{"id":"pygame-many-images","slug":"many-images","title":"Centering and many images","text":"An image knows its own size: `robot.get_width()` and `robot.get_height()` give it in pixels. The window has the same methods. They're handy for placing images without typing their sizes into the program.\n\n```asset robot.png\n40x40\n```\n\n### Centering an image\n\nThe center of a 400 x 300 window is (200, 150). To put the robot's center there, its top-left corner goes half of the robot's width to the left and half of its height up from the center. The same thing in one calculation: the space left over next to the image, divided in two.\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((400, 300))\nrobot = pygame.image.load(\"robot.png\")\n\nx = (window.get_width() - robot.get_width()) // 2\ny = (window.get_height() - robot.get_height()) // 2\nprint(x, y)\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(robot, (x, y))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/34479b2024c78de3.png\",\"size\":[400,300],\"title\":\"pygame window\",\"frames\":30,\"log\":\"fill (0, 0, 0)\\nblit robot.png at (180, 130)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((400, 300))\\nrobot = pygame.image.load(\\\"robot.png\\\")\\n\\nx = (window.get_width() - robot.get_width()) // 2\\ny = (window.get_height() - robot.get_height()) // 2\\nprint(x, y)\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    window.blit(robot, (x, y))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[]},\"assets\":[\"robot.png\"]}\n```\n\n```output\n180 130\n```\n\nThe program calculates the position once, before the main loop, since it never changes. With `/` the position would be `180.0`. pygame accepts that too, but it drops the fraction, and with `//` you see the whole number it really uses. And if you change the window's size or the image, the robot stays in the center without any other change.\n\n### Many images with a loop\n\nA loop draws many images with a few lines of code. This one draws a row of eight coins, one in the middle of each 40-pixel-wide column of the window:\n\n```asset coin.png\n24x24\n```\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\ncoin = pygame.image.load(\"coin.png\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    for i in range(8):\n        window.blit(coin, (8 + i * 40, 20))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/29d5d063a5e03e5b.png\",\"size\":[320,240],\"title\":\"pygame window\",\"frames\":30,\"log\":\"fill (0, 0, 0)\\nblit coin.png at (8, 20)\\nblit coin.png at (48, 20)\\nblit coin.png at (88, 20)\\nblit coin.png at (128, 20)\\nblit coin.png at (168, 20)\\nblit coin.png at (208, 20)\\nblit coin.png at (248, 20)\\nblit coin.png at (288, 20)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\ncoin = pygame.image.load(\\\"coin.png\\\")\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    for i in range(8):\\n        window.blit(coin, (8 + i * 40, 20))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[]},\"assets\":[\"robot.png\",\"coin.png\"]}\n```\n\nThe window as text has a line for each coin, from `blit coin.png at (8, 20)` to `blit coin.png at (288, 20)`, in the order they were drawn.\n\nTwo loops, one inside the other, fill a grid. The window is 8 tiles of 40 pixels wide and 6 high, so these loops cover it with floor tiles, row by row from the top, each row from left to right:\n\n```asset floor.png\n40x40\n```\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nfloor = pygame.image.load(\"floor.png\")\nrobot = pygame.image.load(\"robot.png\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    for row in range(6):\n        for column in range(8):\n            window.blit(floor, (column * 40, row * 40))\n    window.blit(robot, (120, 80))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/458093168acdb2ab.png\",\"size\":[320,240],\"title\":\"pygame window\",\"frames\":30,\"log\":\"fill (0, 0, 0)\\nblit floor.png at (0, 0)\\nblit floor.png at (40, 0)\\nblit floor.png at (80, 0)\\nblit floor.png at (120, 0)\\nblit floor.png at (160, 0)\\nblit floor.png at (200, 0)\\nblit floor.png at (240, 0)\\nblit floor.png at (280, 0)\\nblit floor.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 floor.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 floor.png at (240, 80)\\nblit floor.png at (280, 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 floor.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 (0, 160)\\nblit floor.png at (40, 160)\\nblit floor.png at (80, 160)\\nblit floor.png at (120, 160)\\nblit floor.png at (160, 160)\\nblit floor.png at (200, 160)\\nblit floor.png at (240, 160)\\nblit floor.png at (280, 160)\\nblit floor.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 robot.png at (120, 80)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\nfloor = pygame.image.load(\\\"floor.png\\\")\\nrobot = pygame.image.load(\\\"robot.png\\\")\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    for row in range(6):\\n        for column in range(8):\\n            window.blit(floor, (column * 40, row * 40))\\n    window.blit(robot, (120, 80))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[]},\"assets\":[\"robot.png\",\"coin.png\",\"floor.png\"]}\n```\n\n### Random places\n\nTo scatter coins in random places, choose the places once, before the main loop, and keep them in a list. If you chose them inside the main loop, the coins would jump to new places on every frame.\n\n```python\nimport random\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\ncoin = pygame.image.load(\"coin.png\")\n\nplaces = []\nfor i in range(12):\n    x = random.randint(0, 320 - 24)\n    y = random.randint(0, 240 - 24)\n    places.append((x, y))\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    for place in places:\n        window.blit(coin, place)\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/771ec58f27d592cf.png\",\"size\":[320,240],\"title\":\"pygame window\",\"frames\":30,\"log\":\"fill (0, 0, 0)\\nblit coin.png at (68, 145)\\nblit coin.png at (32, 65)\\nblit coin.png at (60, 126)\\nblit coin.png at (230, 120)\\nblit coin.png at (194, 201)\\nblit coin.png at (107, 24)\\nblit coin.png at (249, 7)\\nblit coin.png at (199, 110)\\nblit coin.png at (1, 178)\\nblit coin.png at (228, 68)\\nblit coin.png at (117, 151)\\nblit coin.png at (52, 81)\",\"ended\":\"closed\",\"code\":\"import random\\nimport pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\ncoin = pygame.image.load(\\\"coin.png\\\")\\n\\nplaces = []\\nfor i in range(12):\\n    x = random.randint(0, 320 - 24)\\n    y = random.randint(0, 240 - 24)\\n    places.append((x, y))\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    for place in places:\\n        window.blit(coin, place)\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[]},\"assets\":[\"robot.png\",\"coin.png\",\"floor.png\"]}\n```\n\nThe site starts `random` the same way in every run of a window, so here the coins land in the same places each time you press Play. On your own computer they'd land somewhere new in every run. That's also why no challenge here asks for random places: the checks couldn't know where your coins went.\n","task":"The window is 400 x 300. Fill in the blanks so the program draws the monster exactly in the center of the window, whatever the sizes of the window and the image. The monster happens to be square, so its width and height are the same number; the check also looks at which of them you used for x and which for y.\n","kind":"fill","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((400, 300))\nmonster = pygame.image.load(\"monster.png\")\n\nx = (window.get_width() - monster.[[get_width]]()) // 2\ny = (window.get_height() - monster.[[get_height]]()) // 2\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(monster, (x, y))\n    pygame.display.flip()\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((400, 300))\nmonster = pygame.image.load(\"monster.png\")\n\nx = (window.get_width() - monster.get_width()) // 2\ny = (window.get_height() - monster.get_height()) // 2\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(monster, (x, y))\n    pygame.display.flip()\n","hints":["The space left over next to the monster is the window's width minus the monster's width. Half of it goes on each side.","The image has the same methods as the window: `monster.get_width()` and `monster.get_height()`. Widths go with x, heights with y."],"tests":[{"name":"Test 1","stdin":"","window":{"frames":10,"events":[]},"expectWindow":{"frames":10,"log":"fill (0, 0, 0)\nblit monster.png at (180, 130)","size":[400,300],"title":"pygame window","image":"lessons/windows/f2a0827be31dca5e.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":4,"events":[]},"expectWindow":{"frames":4,"log":"fill (0, 0, 0)\nblit monster.png at (180, 130)","size":[400,300],"title":"pygame window","image":"lessons/windows/f2a0827be31dca5e.png"},"expect":"","hidden":true}],"require":[{"pattern":"get_width\\(\\)\\s*-\\s*monster\\.get_width\\(\\)","message":"x goes across the window, so it uses widths: the window's width minus the monster's width, `monster.get_width()`."},{"pattern":"get_height\\(\\)\\s*-\\s*monster\\.get_height\\(\\)","message":"y goes down the window, so it uses heights: the window's height minus the monster's height, `monster.get_height()`."}],"forbid":[],"assets":["monster.png"],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"places = []\nfor row in range(2):\n    for column in range(3):\n        places.append((column * 40, row * 40))\nprint(len(places))\nprint(places[0], places[2])\nprint(places[3], places[-1])\n","solution":"places = []\nfor row in range(2):\n    for column in range(3):\n        places.append((column * 40, row * 40))\nprint(len(places))\nprint(places[0], places[2])\nprint(places[3], places[-1])\n","hints":["The inner loop goes through all three columns for each of the two rows, so the list gets 2 × 3 points.","The points come row by row: first the three of row 0, with y = 0, then the three of row 1, with y = 40.","`places[2]` is the last point of the first row, and `places[3]` the first point of the second row."],"tests":[{"name":"Output","stdin":"","expect":"6\n(0, 0) (80, 0)\n(0, 40) (80, 40)","hidden":false}],"require":[],"forbid":[],"lines":["6","(0, 0) (80, 0)","(0, 40) (80, 40)"]},{"task":"Draw a room in a 320 x 240 window, which is 8 tiles wide and 6 tiles high (each tile is 40 x 40). The tiles along the four edges of the window are walls, and all the others are floor. Draw the tiles row by row from the top, each row from left to right, with two loops, one inside the other (`for` or `while`). Then draw the robot in the room at (120, 80).\n","kind":"code","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nwall = pygame.image.load(\"wall.png\")\nfloor = pygame.image.load(\"floor.png\")\nrobot = pygame.image.load(\"robot.png\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    # Draw the tiles here, then the robot.\n    pygame.display.flip()\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nwall = pygame.image.load(\"wall.png\")\nfloor = pygame.image.load(\"floor.png\")\nrobot = pygame.image.load(\"robot.png\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    for row in range(6):\n        for column in range(8):\n            if row == 0 or row == 5 or column == 0 or column == 7:\n                window.blit(wall, (column * 40, row * 40))\n            else:\n                window.blit(floor, (column * 40, row * 40))\n    window.blit(robot, (120, 80))\n    pygame.display.flip()\n","hints":["Start from the lesson's grid of floor tiles: an outer loop over the 6 rows and an inner loop over the 8 columns. Tile (column, row) goes to `(column * 40, row * 40)`.","A tile is on an edge when its row is the first or the last one (0 or 5), or its column is the first or the last one (0 or 7).","In the inner loop, an `if` with that condition draws `wall`, and its `else` draws `floor`."],"tests":[{"name":"Test 1","stdin":"","window":{"frames":10,"events":[]},"expectWindow":{"frames":10,"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 (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 wall.png at (280, 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 floor.png at (240, 80)\nblit wall.png at (280, 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 floor.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit wall.png at (280, 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 floor.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit wall.png at (280, 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 robot.png at (120, 80)","size":[320,240],"title":"pygame window","image":"lessons/windows/7cb13e226a8a32b9.png"},"expect":"","hidden":false},{"name":"Test 2","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 (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 wall.png at (280, 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 floor.png at (240, 80)\nblit wall.png at (280, 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 floor.png at (160, 120)\nblit floor.png at (200, 120)\nblit floor.png at (240, 120)\nblit wall.png at (280, 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 floor.png at (160, 160)\nblit floor.png at (200, 160)\nblit floor.png at (240, 160)\nblit wall.png at (280, 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 robot.png at (120, 80)","size":[320,240],"title":"pygame window","image":"lessons/windows/7cb13e226a8a32b9.png"},"expect":"","hidden":true}],"require":[{"pattern":"\\b(?:for|while)\\b","min":4,"message":"Draw the tiles with two loops, one inside the other: an outer loop over the rows, and in it an inner loop over the columns. (With the main loop and its event loop, the program then has four loops.)"}],"forbid":[],"assets":["wall.png","floor.png","robot.png"]}]}