{"id":"game-project-map","slug":"map","title":"The game map","text":"In this part you put everything from part 13 together into one bigger program: a puzzle game. A robot works in a warehouse. Boxes stand on the floor, some squares of the floor are marked as targets, and the robot's job is to push every box onto a target. It can push a box one square at a time, but it can't pull one, so a box pushed into a corner stays there for good. Puzzles like this are known as **Sokoban**, Japanese for \"warehouse keeper\".\n\nEach step of this module adds a piece to the game: the map in this step, then the robot, then the boxes, and last the move counter, restarting and winning. Every step ends with the whole game so far, and you can play it right in the page.\n\n### Tiles\n\nThe map is made of **tiles**: small square pictures that fit together side by side, like the tiles of a floor. These three are 40 by 40 pixels each:\n\n```asset floor.png\n40x40\n```\n\n```asset wall.png\n40x40\n```\n\n```asset target.png\n40x40\n```\n\n### The map as data\n\nBefore it can draw anything, the program needs to know what the room looks like. That's data, and a list of strings describes it well: one string for each row of squares, one character for each square. Here `#` is a wall, `.` is floor and `X` is a target:\n\n```python\nLEVEL = [\n    \"#########\",\n    \"#...#...#\",\n    \"#.....X.#\",\n    \"#...#...#\",\n    \"#..X#...#\",\n    \"#.......#\",\n    \"#########\",\n]\n\nprint(len(LEVEL), \"rows\")\nprint(len(LEVEL[0]), \"squares in a row\")\nprint(LEVEL[2])\nprint(LEVEL[2][6])\n```\n\n```output\n7 rows\n9 squares in a row\n#.....X.#\nX\n```\n\nA list of strings works like a list of lists here: `LEVEL[2]` is the row at index 2, and `LEVEL[2][6]` is the square at index 6 of that row, a target. Notice the order: **the row comes first**. In the map you write `LEVEL[y][x]`, where y counts the rows from the top and x the squares from the left. pygame does it the other way round: a point in the window is `(x, y)`, x first. Both are normal, so keep an eye on which one you are writing.\n\nKeeping the map as data pays off right away. To change the room, you edit the strings, and the code that draws it stays the same.\n\n### Drawing the map\n\nSquare (x, y) of the map goes x * 40 pixels from the left edge of the window and y * 40 pixels from the top. Two loops, one inside the other, go through the rows from top to bottom and the squares of each row from left to right, and a dictionary gives the tile for each character:\n\n```python\nimport pygame\n\nSIZE = 40\nROOM = [\n    \"######\",\n    \"#..X.#\",\n    \"#....#\",\n    \"######\",\n]\n\npygame.init()\nwindow = pygame.display.set_mode((len(ROOM[0]) * SIZE, len(ROOM) * SIZE))\ntiles = {\n    \"#\": pygame.image.load(\"wall.png\"),\n    \".\": pygame.image.load(\"floor.png\"),\n    \"X\": pygame.image.load(\"target.png\"),\n}\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n    window.fill((0, 0, 0))\n    for y in range(len(ROOM)):\n        for x in range(len(ROOM[y])):\n            square = ROOM[y][x]\n            window.blit(tiles[square], (x * SIZE, y * SIZE))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/07309ac0b16afaa6.png\",\"size\":[240,160],\"title\":\"pygame window\",\"frames\":2,\"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 (0, 40)\\nblit floor.png at (40, 40)\\nblit floor.png at (80, 40)\\nblit target.png at (120, 40)\\nblit floor.png at (160, 40)\\nblit wall.png at (200, 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 wall.png at (200, 80)\\nblit wall.png at (0, 120)\\nblit wall.png at (40, 120)\\nblit wall.png at (80, 120)\\nblit wall.png at (120, 120)\\nblit wall.png at (160, 120)\\nblit wall.png at (200, 120)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\nSIZE = 40\\nROOM = [\\n    \\\"######\\\",\\n    \\\"#..X.#\\\",\\n    \\\"#....#\\\",\\n    \\\"######\\\",\\n]\\n\\npygame.init()\\nwindow = pygame.display.set_mode((len(ROOM[0]) * SIZE, len(ROOM) * SIZE))\\ntiles = {\\n    \\\"#\\\": pygame.image.load(\\\"wall.png\\\"),\\n    \\\".\\\": pygame.image.load(\\\"floor.png\\\"),\\n    \\\"X\\\": pygame.image.load(\\\"target.png\\\"),\\n}\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n    window.fill((0, 0, 0))\\n    for y in range(len(ROOM)):\\n        for x in range(len(ROOM[y])):\\n            square = ROOM[y][x]\\n            window.blit(tiles[square], (x * SIZE, y * SIZE))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":2,\"events\":[]},\"assets\":[\"floor.png\",\"wall.png\",\"target.png\"]}\n```\n\nThe window's size comes from the map too. A map 6 squares wide and 4 high gets a window of 240 by 160 pixels, and a bigger map would get a bigger window without any other change.\n\n### The game as a class\n\nThe game will grow to about a hundred lines, with many parts that use the same data: the map, the pictures, the window, and soon the robot. A class keeps all of it together in one object, and its methods split the work into parts with names. Here is the start of the game, with the real map:\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\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\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\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        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/99546f89324df636.png\",\"size\":[360,280],\"title\":\"Box Robot\",\"frames\":2,\"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)\",\"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\\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\\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\\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        pygame.display.flip()\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    game = BoxGame()\\n    game.run()\\n\",\"window\":{\"frames\":2,\"events\":[]},\"assets\":[\"floor.png\",\"wall.png\",\"target.png\"]}\n```\n\n- `__init__` prepares everything: pygame, the pictures, the map, a window that fits the map, its title and a clock.\n- `load_pictures` puts the tiles in a dictionary, so the picture of a square is `self.tiles[square]`.\n- `run` is the main loop. It handles the events, draws the window and ticks the clock, over and over. Nothing in this puzzle moves by itself, but `self.clock.tick(60)` still keeps the loop from drawing thousands of frames a second for nothing.\n- `handle_events` only closes the window for now. The next step teaches it the arrow keys.\n- `draw` fills the window and draws every square again, on every frame.\n\nCreating the game object doesn't start the game: `game.run()` in the main block does. That keeps the two apart, so a game can be created and its data looked at without starting the loop.\n","task":"The `draw` method has lost the place of each tile. Fill in the blanks so that every square is drawn at its own place in the window.\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\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\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\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        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\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\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\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        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","hints":["Square (x, y) goes x tiles from the left edge and y tiles from the top, and every tile is `SIZE` pixels wide and high.","The point in the window is x first: `(x * SIZE, y * SIZE)`. Fill the first blank with `x * SIZE` and the second with `y * SIZE`."],"tests":[{"name":"Window","stdin":"","window":{"frames":2,"events":[]},"expectWindow":{"frames":2,"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)","size":[360,280],"title":"Box Robot","image":"lessons/windows/99546f89324df636.png"},"expect":"","hidden":false}],"require":[],"forbid":[],"assets":["floor.png","wall.png","target.png"],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"ROOM = [\n    \"######\",\n    \"#..X.#\",\n    \"#.#..#\",\n    \"######\",\n]\nprint(len(ROOM), len(ROOM[0]))\nprint(ROOM[2][2])\nfor y in range(len(ROOM)):\n    for x in range(len(ROOM[y])):\n        if ROOM[y][x] == \"X\":\n            print(\"target at\", x, y)\n","solution":"ROOM = [\n    \"######\",\n    \"#..X.#\",\n    \"#.#..#\",\n    \"######\",\n]\nprint(len(ROOM), len(ROOM[0]))\nprint(ROOM[2][2])\nfor y in range(len(ROOM)):\n    for x in range(len(ROOM[y])):\n        if ROOM[y][x] == \"X\":\n            print(\"target at\", x, y)\n","hints":["`len(ROOM)` is the number of rows, and `len(ROOM[0])` the number of characters in the first row.","`ROOM[2][2]` is row 2 (the third string), and the character at index 2 of it.","The loops go through every square. Only one square is an `X`. Which row is it in, and at which index of that row?"],"tests":[{"name":"Output","stdin":"","expect":"4 6\n#\ntarget at 3 1","hidden":false}],"require":[],"forbid":[],"lines":["4 6","#","target at 3 1"]},{"task":"This level isn't a rectangle. The squares outside its walls are spaces in the map, and nothing should be drawn there, so that the window's black shows through.\n\nWrite the two loops in `draw` that draw the map. Go through the rows from top to bottom and the squares of each row from left to right (the check compares the drawing in this order), and draw the tile of every square that isn't a space.\n","kind":"code","seed":"import pygame\n\nSIZE = 40\nLEVEL = [\n    \"  #####  \",\n    \"###...###\",\n    \"#...X...#\",\n    \"#.#...#.#\",\n    \"#...X...#\",\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\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\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\n    def draw(self):\n        self.window.fill((0, 0, 0))\n        # Draw the map here.\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\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\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\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\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                if square != \" \":\n                    self.window.blit(self.tiles[square], (x * SIZE, y * SIZE))\n        pygame.display.flip()\n\n\nif __name__ == \"__main__\":\n    game = BoxGame()\n    game.run()\n","hints":["The outer loop goes through the rows, `for y in range(self.height):`, and the inner one through the squares of a row, `for x in range(self.width):`.","Inside them, `square = self.map[y][x]` is the character, and its tile goes at `(x * SIZE, y * SIZE)`.","Draw only when `square != \" \"`. The dictionary has no tile for a space, so `self.tiles[\" \"]` would stop the program with a `KeyError`."],"tests":[{"name":"Window","stdin":"","window":{"frames":2,"events":[]},"expectWindow":{"frames":2,"log":"fill (0, 0, 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 (0, 40)\nblit wall.png at (40, 40)\nblit wall.png at (80, 40)\nblit floor.png at (120, 40)\nblit floor.png at (160, 40)\nblit floor.png at (200, 40)\nblit wall.png at (240, 40)\nblit wall.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 target.png at (160, 80)\nblit floor.png at (200, 80)\nblit floor.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 wall.png at (80, 120)\nblit floor.png at (120, 120)\nblit floor.png at (160, 120)\nblit floor.png at (200, 120)\nblit wall.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 floor.png at (120, 160)\nblit target.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 wall.png at (40, 200)\nblit wall.png at (80, 200)\nblit wall.png at (120, 200)\nblit wall.png at (160, 200)\nblit wall.png at (200, 200)\nblit wall.png at (240, 200)\nblit wall.png at (280, 200)\nblit wall.png at (320, 200)","size":[360,240],"title":"Box Robot","image":"lessons/windows/c254bc89d28038bb.png"},"expect":"","hidden":false}],"require":[],"forbid":[],"assets":["floor.png","wall.png","target.png"]}]}