{"id":"game-project-robot","slug":"robot","title":"Moving the robot","text":"The map is ready, so it's time for the robot. Along with the three tiles of the map, the game now loads its picture:\n\n```asset floor.png\n40x40\n```\n\n```asset wall.png\n40x40\n```\n\n```asset target.png\n40x40\n```\n\n```asset robot.png\n40x40\n```\n\n### Where is the robot?\n\nThe robot moves, and the map never does. So the game keeps the robot's place in an attribute of its own, `self.robot`: a tuple of its column and its row. `(2, 3)` is the square at x = 2 and y = 3. These are squares, not pixels. Pixels are needed only for drawing, and there `x * SIZE` and `y * SIZE` give them.\n\nThe robot gets its place in a new method, `reset`, which `__init__` calls. Everything that changes during a game is set up there, and in step 4 the game calls it again to start over.\n\nYou could also mark the robot in the map with a character of its own, and search the map for it whenever you need its place. That works, but then the map changes on every move, and every move starts with a search. Keeping the map for what never moves and the robot in an attribute keeps both simple.\n\n### Drawing the robot\n\nThe robot picture is transparent around the robot, so when it's drawn on top of a tile, the floor shows through. The order matters: whatever is drawn later covers what was drawn before. So `draw` draws the map first, and the robot after the loops:\n\n```python\nrobot_x, robot_y = self.robot\nself.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n```\n\n### One square per key press\n\nThe robot moves a whole square each time an arrow key is pressed, and a `KEYDOWN` event comes once for each press. (Moving a little on every frame while a key is held down suits smooth movement, but here the robot would race across 60 squares a second.) Each arrow key calls the method `try_move` with a direction: how much x and y change.\n\n| Key | dx | dy |\n|---|---|---|\n| right | 1 | 0 |\n| left | -1 | 0 |\n| up | 0 | -1 |\n| down | 0 | 1 |\n\nUp is -1 because y counts from the top of the window.\n\n### Walls stop the robot\n\n`try_move` works out the square the robot would step on. If it's a wall, `return` ends the method right there and the robot stays where it is. Otherwise the robot gets its new place:\n\n```python\ndef try_move(self, dx, dy):\n    x, y = self.robot\n    new_x = x + dx\n    new_y = y + dy\n    if self.map[new_y][new_x] == \"#\":\n        return\n    self.robot = (new_x, new_y)\n```\n\nNotice the order again: the map is read row first, `self.map[new_y][new_x]`.\n\n### Why the map has walls all around\n\nThe walls around the edge keep the robot on the map. Without them, a robot in the last column that steps right would make the program read a square that isn't there:\n\n```python\nrow = \"#.....\"\nx = 5\nprint(row[x + 1])\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 3, in <module>\n    print(row[x + 1])\n          ~~~^^^^^^^\nIndexError: string index out of range\n```\n\nStepping left from column 0 is worse: `row[-1]` is the last square of the row, so there's no error at all, and the robot would walk out of the window. A wall around the edge means the robot never gets there.\n\nHere's the game so far. In the page you can play it with the arrow keys:\n\n```python\nimport pygame\n\nSIZE = 40\nLEVEL = [\n    \"#########\",\n    \"#...#...#\",\n    \"#.....X.#\",\n    \"#...#...#\",\n    \"#..X#...#\",\n    \"#.......#\",\n    \"#########\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move(0, -1)\n                if event.key == pygame.K_DOWN:\n                    self.try_move(0, 1)\n\n    def try_move(self, dx, dy):\n        x, y = self.robot\n        new_x = x + dx\n        new_y = y + dy\n        if self.map[new_y][new_x] == \"#\":\n            return\n        self.robot = (new_x, new_y)\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/4c9a26b12ad23b9a.png\",\"size\":[360,280],\"title\":\"Box Robot\",\"frames\":70,\"log\":\"fill (0, 0, 0)\\nblit wall.png at (0, 0)\\nblit wall.png at (40, 0)\\nblit wall.png at (80, 0)\\nblit wall.png at (120, 0)\\nblit wall.png at (160, 0)\\nblit wall.png at (200, 0)\\nblit wall.png at (240, 0)\\nblit wall.png at (280, 0)\\nblit wall.png at (320, 0)\\nblit wall.png at (0, 40)\\nblit floor.png at (40, 40)\\nblit floor.png at (80, 40)\\nblit floor.png at (120, 40)\\nblit wall.png at (160, 40)\\nblit floor.png at (200, 40)\\nblit floor.png at (240, 40)\\nblit floor.png at (280, 40)\\nblit wall.png at (320, 40)\\nblit wall.png at (0, 80)\\nblit floor.png at (40, 80)\\nblit floor.png at (80, 80)\\nblit floor.png at (120, 80)\\nblit floor.png at (160, 80)\\nblit floor.png at (200, 80)\\nblit target.png at (240, 80)\\nblit floor.png at (280, 80)\\nblit wall.png at (320, 80)\\nblit wall.png at (0, 120)\\nblit floor.png at (40, 120)\\nblit floor.png at (80, 120)\\nblit floor.png at (120, 120)\\nblit wall.png at (160, 120)\\nblit floor.png at (200, 120)\\nblit floor.png at (240, 120)\\nblit floor.png at (280, 120)\\nblit wall.png at (320, 120)\\nblit wall.png at (0, 160)\\nblit floor.png at (40, 160)\\nblit floor.png at (80, 160)\\nblit target.png at (120, 160)\\nblit wall.png at (160, 160)\\nblit floor.png at (200, 160)\\nblit floor.png at (240, 160)\\nblit floor.png at (280, 160)\\nblit wall.png at (320, 160)\\nblit wall.png at (0, 200)\\nblit floor.png at (40, 200)\\nblit floor.png at (80, 200)\\nblit floor.png at (120, 200)\\nblit floor.png at (160, 200)\\nblit floor.png at (200, 200)\\nblit floor.png at (240, 200)\\nblit floor.png at (280, 200)\\nblit wall.png at (320, 200)\\nblit wall.png at (0, 240)\\nblit wall.png at (40, 240)\\nblit wall.png at (80, 240)\\nblit wall.png at (120, 240)\\nblit wall.png at (160, 240)\\nblit wall.png at (200, 240)\\nblit wall.png at (240, 240)\\nblit wall.png at (280, 240)\\nblit wall.png at (320, 240)\\nblit robot.png at (200, 120)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\nSIZE = 40\\nLEVEL = [\\n    \\\"#########\\\",\\n    \\\"#...#...#\\\",\\n    \\\"#.....X.#\\\",\\n    \\\"#...#...#\\\",\\n    \\\"#..X#...#\\\",\\n    \\\"#.......#\\\",\\n    \\\"#########\\\",\\n]\\n\\n\\nclass BoxGame:\\n    def __init__(self):\\n        pygame.init()\\n        self.load_pictures()\\n        self.map = LEVEL\\n        self.height = len(self.map)\\n        self.width = len(self.map[0])\\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\\n        pygame.display.set_caption(\\\"Box Robot\\\")\\n        self.clock = pygame.time.Clock()\\n        self.reset()\\n\\n    def load_pictures(self):\\n        self.tiles = {\\n            \\\"#\\\": pygame.image.load(\\\"wall.png\\\"),\\n            \\\".\\\": pygame.image.load(\\\"floor.png\\\"),\\n            \\\"X\\\": pygame.image.load(\\\"target.png\\\"),\\n        }\\n        self.robot_picture = pygame.image.load(\\\"robot.png\\\")\\n\\n    def reset(self):\\n        self.robot = (2, 3)\\n\\n    def run(self):\\n        while True:\\n            self.handle_events()\\n            self.draw()\\n            self.clock.tick(60)\\n\\n    def handle_events(self):\\n        for event in pygame.event.get():\\n            if event.type == pygame.QUIT:\\n                exit()\\n            if event.type == pygame.KEYDOWN:\\n                if event.key == pygame.K_LEFT:\\n                    self.try_move(-1, 0)\\n                if event.key == pygame.K_RIGHT:\\n                    self.try_move(1, 0)\\n                if event.key == pygame.K_UP:\\n                    self.try_move(0, -1)\\n                if event.key == pygame.K_DOWN:\\n                    self.try_move(0, 1)\\n\\n    def try_move(self, dx, dy):\\n        x, y = self.robot\\n        new_x = x + dx\\n        new_y = y + dy\\n        if self.map[new_y][new_x] == \\\"#\\\":\\n            return\\n        self.robot = (new_x, new_y)\\n\\n    def draw(self):\\n        self.window.fill((0, 0, 0))\\n        for y in range(self.height):\\n            for x in range(self.width):\\n                square = self.map[y][x]\\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\\n        robot_x, robot_y = self.robot\\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\\n        pygame.display.flip()\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    game = BoxGame()\\n    game.run()\\n\",\"window\":{\"frames\":70,\"events\":[{\"at\":10,\"type\":\"keydown\",\"key\":\"K_RIGHT\"},{\"at\":10,\"type\":\"keyup\",\"key\":\"K_RIGHT\"},{\"at\":20,\"type\":\"keydown\",\"key\":\"K_RIGHT\"},{\"at\":20,\"type\":\"keyup\",\"key\":\"K_RIGHT\"},{\"at\":30,\"type\":\"keydown\",\"key\":\"K_UP\"},{\"at\":30,\"type\":\"keyup\",\"key\":\"K_UP\"},{\"at\":40,\"type\":\"keydown\",\"key\":\"K_RIGHT\"},{\"at\":40,\"type\":\"keyup\",\"key\":\"K_RIGHT\"},{\"at\":50,\"type\":\"keydown\",\"key\":\"K_RIGHT\"},{\"at\":50,\"type\":\"keyup\",\"key\":\"K_RIGHT\"},{\"at\":60,\"type\":\"keydown\",\"key\":\"K_DOWN\"},{\"at\":60,\"type\":\"keyup\",\"key\":\"K_DOWN\"}]},\"assets\":[\"floor.png\",\"wall.png\",\"target.png\",\"robot.png\"]}\n```\n","task":"The up and down arrows don't work yet. Fill in the blanks with the right directions, so that up moves the robot one square up and down one square down.\n","kind":"fill","seed":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"#########\",\n    \"#...#...#\",\n    \"#.....X.#\",\n    \"#...#...#\",\n    \"#..X#...#\",\n    \"#.......#\",\n    \"#########\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move([[0]], [[-1]])\n                if event.key == pygame.K_DOWN:\n                    self.try_move([[0]], [[1]])\n\n    def try_move(self, dx, dy):\n        x, y = self.robot\n        new_x = x + dx\n        new_y = y + dy\n        if self.map[new_y][new_x] == \"#\":\n            return\n        self.robot = (new_x, new_y)\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","solution":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"#########\",\n    \"#...#...#\",\n    \"#.....X.#\",\n    \"#...#...#\",\n    \"#..X#...#\",\n    \"#.......#\",\n    \"#########\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move(0, -1)\n                if event.key == pygame.K_DOWN:\n                    self.try_move(0, 1)\n\n    def try_move(self, dx, dy):\n        x, y = self.robot\n        new_x = x + dx\n        new_y = y + dy\n        if self.map[new_y][new_x] == \"#\":\n            return\n        self.robot = (new_x, new_y)\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","hints":["The first number is the change in x, and the second the change in y. Going up or down, x stays the same.","y counts from the top of the window, so up makes it smaller: up is `(0, -1)` and down is `(0, 1)`."],"tests":[{"name":"Test 1","stdin":"","window":{"frames":25,"events":[{"at":5,"type":"keydown","key":"K_UP"},{"at":5,"type":"keyup","key":"K_UP"},{"at":10,"type":"keydown","key":"K_UP"},{"at":10,"type":"keyup","key":"K_UP"},{"at":15,"type":"keydown","key":"K_UP"},{"at":15,"type":"keyup","key":"K_UP"},{"at":20,"type":"keydown","key":"K_DOWN"},{"at":20,"type":"keyup","key":"K_DOWN"}]},"expectWindow":{"frames":25,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit wall.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit wall.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit floor.png at (120, 80)\nblit floor.png at (160, 80)\nblit floor.png at (200, 80)\nblit target.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit wall.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit wall.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit wall.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit target.png at (120, 160)\nblit wall.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit wall.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (80, 80)","size":[360,280],"title":"Box Robot","image":"lessons/windows/404aea3764f573b1.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":20,"events":[{"at":5,"type":"keydown","key":"K_DOWN"},{"at":5,"type":"keyup","key":"K_DOWN"},{"at":10,"type":"keydown","key":"K_DOWN"},{"at":10,"type":"keyup","key":"K_DOWN"},{"at":15,"type":"keydown","key":"K_RIGHT"},{"at":15,"type":"keyup","key":"K_RIGHT"}]},"expectWindow":{"frames":20,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit wall.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit wall.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit floor.png at (120, 80)\nblit floor.png at (160, 80)\nblit floor.png at (200, 80)\nblit target.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit wall.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit wall.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit wall.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit target.png at (120, 160)\nblit wall.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit wall.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (120, 200)","size":[360,280],"title":"Box Robot","image":"lessons/windows/3ad0853773883708.png"},"expect":"","hidden":true}],"require":[],"forbid":[],"assets":["floor.png","wall.png","target.png","robot.png"],"more":[{"task":"Write the method `try_move(dx, dy)`. It moves the robot one square in the direction (dx, dy), but if there's a wall on that square, the robot stays where it is.\n","kind":"code","seed":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"#########\",\n    \"#...#...#\",\n    \"#.....X.#\",\n    \"#...#...#\",\n    \"#..X#...#\",\n    \"#.......#\",\n    \"#########\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move(0, -1)\n                if event.key == pygame.K_DOWN:\n                    self.try_move(0, 1)\n\n    def try_move(self, dx, dy):\n        # Move the robot one square in the direction (dx, dy), unless there's a wall there.\n        pass\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","solution":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"#########\",\n    \"#...#...#\",\n    \"#.....X.#\",\n    \"#...#...#\",\n    \"#..X#...#\",\n    \"#.......#\",\n    \"#########\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move(0, -1)\n                if event.key == pygame.K_DOWN:\n                    self.try_move(0, 1)\n\n    def try_move(self, dx, dy):\n        x, y = self.robot\n        new_x = x + dx\n        new_y = y + dy\n        if self.map[new_y][new_x] == \"#\":\n            return\n        self.robot = (new_x, new_y)\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","hints":["The robot's place is `self.robot`. Unpack it first: `x, y = self.robot`.","The new square is `x + dx` and `y + dy`. Check it in the map, row first, before you move the robot there.","If `self.map[new_y][new_x] == \"#\"`, `return` and change nothing. Otherwise set `self.robot = (new_x, new_y)`."],"tests":[{"name":"Walking into the middle wall","stdin":"","window":{"frames":70,"events":[{"at":10,"type":"keydown","key":"K_RIGHT"},{"at":10,"type":"keyup","key":"K_RIGHT"},{"at":20,"type":"keydown","key":"K_RIGHT"},{"at":20,"type":"keyup","key":"K_RIGHT"},{"at":30,"type":"keydown","key":"K_UP"},{"at":30,"type":"keyup","key":"K_UP"},{"at":40,"type":"keydown","key":"K_RIGHT"},{"at":40,"type":"keyup","key":"K_RIGHT"},{"at":50,"type":"keydown","key":"K_RIGHT"},{"at":50,"type":"keyup","key":"K_RIGHT"},{"at":60,"type":"keydown","key":"K_DOWN"},{"at":60,"type":"keyup","key":"K_DOWN"}]},"expectWindow":{"frames":70,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit wall.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit wall.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit floor.png at (120, 80)\nblit floor.png at (160, 80)\nblit floor.png at (200, 80)\nblit target.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit wall.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit wall.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit wall.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit target.png at (120, 160)\nblit wall.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit wall.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (200, 120)","size":[360,280],"title":"Box Robot","image":"lessons/windows/4c9a26b12ad23b9a.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":45,"events":[{"at":5,"type":"keydown","key":"K_LEFT"},{"at":5,"type":"keyup","key":"K_LEFT"},{"at":10,"type":"keydown","key":"K_LEFT"},{"at":10,"type":"keyup","key":"K_LEFT"},{"at":15,"type":"keydown","key":"K_LEFT"},{"at":15,"type":"keyup","key":"K_LEFT"},{"at":20,"type":"keydown","key":"K_UP"},{"at":20,"type":"keyup","key":"K_UP"},{"at":25,"type":"keydown","key":"K_UP"},{"at":25,"type":"keyup","key":"K_UP"},{"at":30,"type":"keydown","key":"K_UP"},{"at":30,"type":"keyup","key":"K_UP"},{"at":35,"type":"keydown","key":"K_UP"},{"at":35,"type":"keyup","key":"K_UP"}]},"expectWindow":{"frames":45,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit wall.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit wall.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit floor.png at (120, 80)\nblit floor.png at (160, 80)\nblit floor.png at (200, 80)\nblit target.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit wall.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit wall.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit wall.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit target.png at (120, 160)\nblit wall.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit wall.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (40, 40)","size":[360,280],"title":"Box Robot","image":"lessons/windows/3eee958d86b380c2.png"},"expect":"","hidden":true},{"name":"Test 3","stdin":"","window":{"frames":60,"events":[{"at":5,"type":"keydown","key":"K_DOWN"},{"at":5,"type":"keyup","key":"K_DOWN"},{"at":10,"type":"keydown","key":"K_DOWN"},{"at":10,"type":"keyup","key":"K_DOWN"},{"at":15,"type":"keydown","key":"K_DOWN"},{"at":15,"type":"keyup","key":"K_DOWN"},{"at":20,"type":"keydown","key":"K_RIGHT"},{"at":20,"type":"keyup","key":"K_RIGHT"},{"at":25,"type":"keydown","key":"K_RIGHT"},{"at":25,"type":"keyup","key":"K_RIGHT"},{"at":30,"type":"keydown","key":"K_RIGHT"},{"at":30,"type":"keyup","key":"K_RIGHT"},{"at":35,"type":"keydown","key":"K_RIGHT"},{"at":35,"type":"keyup","key":"K_RIGHT"},{"at":40,"type":"keydown","key":"K_RIGHT"},{"at":40,"type":"keyup","key":"K_RIGHT"},{"at":45,"type":"keydown","key":"K_RIGHT"},{"at":45,"type":"keyup","key":"K_RIGHT"},{"at":50,"type":"keydown","key":"K_RIGHT"},{"at":50,"type":"keyup","key":"K_RIGHT"}]},"expectWindow":{"frames":60,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit wall.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit wall.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit floor.png at (120, 80)\nblit floor.png at (160, 80)\nblit floor.png at (200, 80)\nblit target.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit wall.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit wall.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit wall.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit target.png at (120, 160)\nblit wall.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit wall.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (280, 200)","size":[360,280],"title":"Box Robot","image":"lessons/windows/2c61a6ad46d5b45e.png"},"expect":"","hidden":true}],"require":[],"forbid":[],"assets":["floor.png","wall.png","target.png","robot.png"]},{"task":"This level has gaps in its outer walls, and they're **tunnels**: a robot that walks out of the left edge comes back in at the right edge of the same row, and one that walks out of the top comes in at the bottom. Right now the robot walks out of the window, or the program stops with an `IndexError`.\n\nChange `try_move` so the robot goes through the tunnels. The remainder operator `%` can bring a number that went too far back into range.\n","kind":"code","seed":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"####.####\",\n    \"#.......#\",\n    \"#..#.#..#\",\n    \"....X....\",\n    \"#..#.#..#\",\n    \"#.......#\",\n    \"####.####\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move(0, -1)\n                if event.key == pygame.K_DOWN:\n                    self.try_move(0, 1)\n\n    def try_move(self, dx, dy):\n        x, y = self.robot\n        new_x = x + dx\n        new_y = y + dy\n        if self.map[new_y][new_x] == \"#\":\n            return\n        self.robot = (new_x, new_y)\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","solution":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"####.####\",\n    \"#.......#\",\n    \"#..#.#..#\",\n    \"....X....\",\n    \"#..#.#..#\",\n    \"#.......#\",\n    \"####.####\",\n]\n\n\nclass BoxGame:\n    def __init__(self):\n        pygame.init()\n        self.load_pictures()\n        self.map = LEVEL\n        self.height = len(self.map)\n        self.width = len(self.map[0])\n        self.window = pygame.display.set_mode((self.width * SIZE, self.height * SIZE))\n        pygame.display.set_caption(\"Box Robot\")\n        self.clock = pygame.time.Clock()\n        self.reset()\n\n    def load_pictures(self):\n        self.tiles = {\n            \"#\": pygame.image.load(\"wall.png\"),\n            \".\": pygame.image.load(\"floor.png\"),\n            \"X\": pygame.image.load(\"target.png\"),\n        }\n        self.robot_picture = pygame.image.load(\"robot.png\")\n\n    def reset(self):\n        self.robot = (2, 3)\n\n    def run(self):\n        while True:\n            self.handle_events()\n            self.draw()\n            self.clock.tick(60)\n\n    def handle_events(self):\n        for event in pygame.event.get():\n            if event.type == pygame.QUIT:\n                exit()\n            if event.type == pygame.KEYDOWN:\n                if event.key == pygame.K_LEFT:\n                    self.try_move(-1, 0)\n                if event.key == pygame.K_RIGHT:\n                    self.try_move(1, 0)\n                if event.key == pygame.K_UP:\n                    self.try_move(0, -1)\n                if event.key == pygame.K_DOWN:\n                    self.try_move(0, 1)\n\n    def try_move(self, dx, dy):\n        x, y = self.robot\n        new_x = (x + dx) % self.width\n        new_y = (y + dy) % self.height\n        if self.map[new_y][new_x] == \"#\":\n            return\n        self.robot = (new_x, new_y)\n\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        for y in range(self.height):\n            for x in range(self.width):\n                square = self.map[y][x]\n                self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        robot_x, robot_y = self.robot\n        self.window.blit(self.robot_picture, (robot_x * SIZE, robot_y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","hints":["The map has `self.width` columns, with indexes from 0 to `self.width - 1`. A robot that steps left from column 0 should land in column `self.width - 1`, and one that steps right from the last column in column 0.","In Python, `-1 % 9` is 8 and `9 % 9` is 0: `%` with the width gives exactly the column you want.","Use `%` on both parts of the new square, x with `self.width` and y with `self.height`, before the wall check reads the map."],"tests":[{"name":"Out of the left edge","stdin":"","window":{"frames":25,"events":[{"at":5,"type":"keydown","key":"K_LEFT"},{"at":5,"type":"keyup","key":"K_LEFT"},{"at":10,"type":"keydown","key":"K_LEFT"},{"at":10,"type":"keyup","key":"K_LEFT"},{"at":15,"type":"keydown","key":"K_LEFT"},{"at":15,"type":"keyup","key":"K_LEFT"},{"at":20,"type":"keydown","key":"K_LEFT"},{"at":20,"type":"keyup","key":"K_LEFT"}]},"expectWindow":{"frames":25,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit floor.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit floor.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit wall.png at (120, 80)\nblit floor.png at (160, 80)\nblit wall.png at (200, 80)\nblit floor.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit floor.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit target.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit floor.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit wall.png at (120, 160)\nblit floor.png at (160, 160)\nblit wall.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit floor.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (280, 120)","size":[360,280],"title":"Box Robot","image":"lessons/windows/c2f5e5b56b218caa.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":40,"events":[{"at":5,"type":"keydown","key":"K_RIGHT"},{"at":5,"type":"keyup","key":"K_RIGHT"},{"at":10,"type":"keydown","key":"K_RIGHT"},{"at":10,"type":"keyup","key":"K_RIGHT"},{"at":15,"type":"keydown","key":"K_UP"},{"at":15,"type":"keyup","key":"K_UP"},{"at":20,"type":"keydown","key":"K_UP"},{"at":20,"type":"keyup","key":"K_UP"},{"at":25,"type":"keydown","key":"K_UP"},{"at":25,"type":"keyup","key":"K_UP"},{"at":30,"type":"keydown","key":"K_UP"},{"at":30,"type":"keyup","key":"K_UP"},{"at":35,"type":"keydown","key":"K_UP"},{"at":35,"type":"keyup","key":"K_UP"}]},"expectWindow":{"frames":40,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit floor.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit floor.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit wall.png at (120, 80)\nblit floor.png at (160, 80)\nblit wall.png at (200, 80)\nblit floor.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit floor.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit target.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit floor.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit wall.png at (120, 160)\nblit floor.png at (160, 160)\nblit wall.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit floor.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (160, 200)","size":[360,280],"title":"Box Robot","image":"lessons/windows/36e04528028818c9.png"},"expect":"","hidden":true},{"name":"Test 3","stdin":"","window":{"frames":40,"events":[{"at":5,"type":"keydown","key":"K_RIGHT"},{"at":5,"type":"keyup","key":"K_RIGHT"},{"at":10,"type":"keydown","key":"K_RIGHT"},{"at":10,"type":"keyup","key":"K_RIGHT"},{"at":15,"type":"keydown","key":"K_RIGHT"},{"at":15,"type":"keyup","key":"K_RIGHT"},{"at":20,"type":"keydown","key":"K_RIGHT"},{"at":20,"type":"keyup","key":"K_RIGHT"},{"at":25,"type":"keydown","key":"K_RIGHT"},{"at":25,"type":"keyup","key":"K_RIGHT"},{"at":30,"type":"keydown","key":"K_RIGHT"},{"at":30,"type":"keyup","key":"K_RIGHT"},{"at":35,"type":"keydown","key":"K_RIGHT"},{"at":35,"type":"keyup","key":"K_RIGHT"}]},"expectWindow":{"frames":40,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit floor.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit floor.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit wall.png at (120, 80)\nblit floor.png at (160, 80)\nblit wall.png at (200, 80)\nblit floor.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit floor.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit target.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit floor.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit wall.png at (120, 160)\nblit floor.png at (160, 160)\nblit wall.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit floor.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (0, 120)","size":[360,280],"title":"Box Robot","image":"lessons/windows/1dcbf369010f9971.png"},"expect":"","hidden":true},{"name":"Test 4","stdin":"","window":{"frames":40,"events":[{"at":5,"type":"keydown","key":"K_RIGHT"},{"at":5,"type":"keyup","key":"K_RIGHT"},{"at":10,"type":"keydown","key":"K_RIGHT"},{"at":10,"type":"keyup","key":"K_RIGHT"},{"at":15,"type":"keydown","key":"K_DOWN"},{"at":15,"type":"keyup","key":"K_DOWN"},{"at":20,"type":"keydown","key":"K_DOWN"},{"at":20,"type":"keyup","key":"K_DOWN"},{"at":25,"type":"keydown","key":"K_DOWN"},{"at":25,"type":"keyup","key":"K_DOWN"},{"at":30,"type":"keydown","key":"K_DOWN"},{"at":30,"type":"keyup","key":"K_DOWN"},{"at":35,"type":"keydown","key":"K_DOWN"},{"at":35,"type":"keyup","key":"K_DOWN"}]},"expectWindow":{"frames":40,"log":"fill (0, 0, 0)\nblit wall.png at (0, 0)\nblit wall.png at (40, 0)\nblit wall.png at (80, 0)\nblit wall.png at (120, 0)\nblit floor.png at (160, 0)\nblit wall.png at (200, 0)\nblit wall.png at (240, 0)\nblit wall.png at (280, 0)\nblit wall.png at (320, 0)\nblit wall.png at (0, 40)\nblit floor.png at (40, 40)\nblit floor.png at (80, 40)\nblit floor.png at (120, 40)\nblit floor.png at (160, 40)\nblit floor.png at (200, 40)\nblit floor.png at (240, 40)\nblit floor.png at (280, 40)\nblit wall.png at (320, 40)\nblit wall.png at (0, 80)\nblit floor.png at (40, 80)\nblit floor.png at (80, 80)\nblit wall.png at (120, 80)\nblit floor.png at (160, 80)\nblit wall.png at (200, 80)\nblit floor.png at (240, 80)\nblit floor.png at (280, 80)\nblit wall.png at (320, 80)\nblit floor.png at (0, 120)\nblit floor.png at (40, 120)\nblit floor.png at (80, 120)\nblit floor.png at (120, 120)\nblit target.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit floor.png at (280, 120)\nblit floor.png at (320, 120)\nblit wall.png at (0, 160)\nblit floor.png at (40, 160)\nblit floor.png at (80, 160)\nblit wall.png at (120, 160)\nblit floor.png at (160, 160)\nblit wall.png at (200, 160)\nblit floor.png at (240, 160)\nblit floor.png at (280, 160)\nblit wall.png at (320, 160)\nblit wall.png at (0, 200)\nblit floor.png at (40, 200)\nblit floor.png at (80, 200)\nblit floor.png at (120, 200)\nblit floor.png at (160, 200)\nblit floor.png at (200, 200)\nblit floor.png at (240, 200)\nblit floor.png at (280, 200)\nblit wall.png at (320, 200)\nblit wall.png at (0, 240)\nblit wall.png at (40, 240)\nblit wall.png at (80, 240)\nblit wall.png at (120, 240)\nblit floor.png at (160, 240)\nblit wall.png at (200, 240)\nblit wall.png at (240, 240)\nblit wall.png at (280, 240)\nblit wall.png at (320, 240)\nblit robot.png at (160, 40)","size":[360,280],"title":"Box Robot","image":"lessons/windows/9c39f2dac719e4da.png"},"expect":"","hidden":true}],"require":[],"forbid":[],"assets":["floor.png","wall.png","target.png","robot.png"]}]}