{"id":"pygame-techniques-shapes","slug":"title-and-shapes","title":"The title and shapes","text":"A window's title bar says \"pygame window\" until the program gives it a name of its own with `pygame.display.set_caption`. Call it once, after opening the window:\n\n```python\npygame.display.set_caption(\"Coin Catcher\")\n```\n\nThe page shows the title in the window's title bar. (The checks compare the drawing, not the title.)\n\n### Drawing shapes\n\nNot everything has to be an image. The module `pygame.draw` draws shapes straight into the window. Each function takes the surface to draw on first and the color second:\n\n- `pygame.draw.rect(window, color, (x, y, width, height))` draws a rectangle with its top-left corner at (x, y).\n- `pygame.draw.circle(window, color, (x, y), radius)` draws a circle with its **center** at (x, y). Note the difference: an image's position is its top-left corner, but a circle's is its center.\n- `pygame.draw.polygon(window, color, points)` draws a shape with corners at the points of the list, such as a triangle.\n- `pygame.draw.line(window, color, start, end, width)` draws a line from one point to another, `width` pixels thick.\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"A house\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((120, 180, 255))\n    pygame.draw.rect(window, (60, 160, 60), (0, 180, 320, 60))\n    pygame.draw.rect(window, (200, 120, 80), (100, 110, 100, 80))\n    pygame.draw.polygon(window, (150, 40, 40), [(90, 110), (150, 60), (210, 110)])\n    pygame.draw.rect(window, (90, 60, 30), (140, 150, 24, 40))\n    pygame.draw.circle(window, (255, 220, 0), (270, 50), 25)\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/ee4a3c3af6208fcd.png\",\"size\":[320,240],\"title\":\"A house\",\"frames\":30,\"log\":\"fill (120, 180, 255)\\nrect (60, 160, 60) (0, 180, 320, 60)\\nrect (200, 120, 80) (100, 110, 100, 80)\\npolygon (150, 40, 40) through (90, 110), (150, 60), (210, 110)\\nrect (90, 60, 30) (140, 150, 24, 40)\\ncircle (255, 220, 0) at (270, 50) radius 25\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\npygame.display.set_caption(\\\"A house\\\")\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((120, 180, 255))\\n    pygame.draw.rect(window, (60, 160, 60), (0, 180, 320, 60))\\n    pygame.draw.rect(window, (200, 120, 80), (100, 110, 100, 80))\\n    pygame.draw.polygon(window, (150, 40, 40), [(90, 110), (150, 60), (210, 110)])\\n    pygame.draw.rect(window, (90, 60, 30), (140, 150, 24, 40))\\n    pygame.draw.circle(window, (255, 220, 0), (270, 50), 25)\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[]}}\n```\n\nThe shapes are part of the window as text, one line for each, in the order they were drawn:\n\n```text\nfill (120, 180, 255)\nrect (60, 160, 60) (0, 180, 320, 60)\nrect (200, 120, 80) (100, 110, 100, 80)\npolygon (150, 40, 40) through (90, 110), (150, 60), (210, 110)\nrect (90, 60, 30) (140, 150, 24, 40)\ncircle (255, 220, 0) at (270, 50) radius 25\n```\n\nThe order matters here as it does with images: the ground is drawn before the house, and the door after the house, so that it's in front of the wall.\n\n### Outlines and lines\n\nA rectangle or a circle is filled by default. With one more number, the line width, you get only its outline. Here a game board of tic-tac-toe is drawn with lines and outlines:\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"Tic-tac-toe\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((255, 255, 255))\n    pygame.draw.line(window, (0, 0, 0), (130, 30), (130, 210), 4)\n    pygame.draw.line(window, (0, 0, 0), (190, 30), (190, 210), 4)\n    pygame.draw.line(window, (0, 0, 0), (70, 90), (250, 90), 4)\n    pygame.draw.line(window, (0, 0, 0), (70, 150), (250, 150), 4)\n    pygame.draw.circle(window, (0, 0, 220), (160, 120), 22, 5)\n    pygame.draw.line(window, (220, 0, 0), (82, 42), (118, 78), 5)\n    pygame.draw.line(window, (220, 0, 0), (118, 42), (82, 78), 5)\n    pygame.draw.rect(window, (0, 0, 0), (60, 20, 200, 200), 2)\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/e1bfc2175ab28998.png\",\"size\":[320,240],\"title\":\"Tic-tac-toe\",\"frames\":30,\"log\":\"fill (255, 255, 255)\\nline (0, 0, 0) from (130, 30) to (130, 210), width 4\\nline (0, 0, 0) from (190, 30) to (190, 210), width 4\\nline (0, 0, 0) from (70, 90) to (250, 90), width 4\\nline (0, 0, 0) from (70, 150) to (250, 150), width 4\\ncircle (0, 0, 220) at (160, 120) radius 22, width 5\\nline (220, 0, 0) from (82, 42) to (118, 78), width 5\\nline (220, 0, 0) from (118, 42) to (82, 78), width 5\\nrect (0, 0, 0) (60, 20, 200, 200), width 2\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\npygame.display.set_caption(\\\"Tic-tac-toe\\\")\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((255, 255, 255))\\n    pygame.draw.line(window, (0, 0, 0), (130, 30), (130, 210), 4)\\n    pygame.draw.line(window, (0, 0, 0), (190, 30), (190, 210), 4)\\n    pygame.draw.line(window, (0, 0, 0), (70, 90), (250, 90), 4)\\n    pygame.draw.line(window, (0, 0, 0), (70, 150), (250, 150), 4)\\n    pygame.draw.circle(window, (0, 0, 220), (160, 120), 22, 5)\\n    pygame.draw.line(window, (220, 0, 0), (82, 42), (118, 78), 5)\\n    pygame.draw.line(window, (220, 0, 0), (118, 42), (82, 78), 5)\\n    pygame.draw.rect(window, (0, 0, 0), (60, 20, 200, 200), 2)\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[]}}\n```\n\nShapes move just like images: take their positions from variables and change the variables between frames. A ball can just as well be a `pygame.draw.circle` whose center moves.\n","task":"Fill in the blanks, so the program gives the window the title `Night`, draws a moon (a circle) and the dark ground (a rectangle).\n","kind":"fill","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.[[set_caption]](\"Night\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((10, 10, 50))\n    pygame.draw.[[circle]](window, (240, 240, 200), (250, 60), 30)\n    pygame.draw.[[rect]](window, (40, 40, 40), (0, 200, 320, 40))\n    pygame.display.flip()\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"Night\")\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((10, 10, 50))\n    pygame.draw.circle(window, (240, 240, 200), (250, 60), 30)\n    pygame.draw.rect(window, (40, 40, 40), (0, 200, 320, 40))\n    pygame.display.flip()\n","hints":["The title is set with a function of `pygame.display`. The moon has a center and a radius; the ground has a corner, a width and a height.","The functions are `set_caption`, `circle` and `rect`."],"tests":[{"name":"Test 1","stdin":"","window":{"frames":10,"events":[]},"expectWindow":{"frames":10,"log":"fill (10, 10, 50)\ncircle (240, 240, 200) at (250, 60) radius 30\nrect (40, 40, 40) (0, 200, 320, 40)","size":[320,240],"title":"Night","image":"lessons/windows/d9f3337848346703.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":3,"events":[]},"expectWindow":{"frames":3,"log":"fill (10, 10, 50)\ncircle (240, 240, 200) at (250, 60) radius 30\nrect (40, 40, 40) (0, 200, 320, 40)","size":[320,240],"title":"Night","image":"lessons/windows/d9f3337848346703.png"},"expect":"","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Draw a target in a black 320 x 240 window: five filled circles, all centered at (160, 120), with the radii 100, 80, 60, 40 and 20. They are red, `(220, 0, 0)`, and white, `(255, 255, 255)`, in turns, starting with red for the biggest. Draw them from the biggest to the smallest with a loop, so that each smaller circle is in front of the bigger ones.\n","kind":"code","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"Target\")\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    pygame.draw.circle(window, (220, 0, 0), (160, 120), 100)\n    pygame.display.flip()\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"Target\")\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(5):\n        if i % 2 == 0:\n            color = (220, 0, 0)\n        else:\n            color = (255, 255, 255)\n        pygame.draw.circle(window, color, (160, 120), 100 - i * 20)\n    pygame.display.flip()\n","hints":["The radius gets 20 smaller each time. A loop over `range(5)` gives the numbers 0 to 4.","Circle number `i` has the radius `100 - i * 20`. With a `range(100, 0, -20)` loop, you get the radii directly.","The color depends on whether the circle's number is even or odd: `i % 2 == 0` is red."],"tests":[{"name":"Test 1","stdin":"","window":{"frames":3,"events":[]},"expectWindow":{"frames":3,"log":"fill (0, 0, 0)\ncircle (220, 0, 0) at (160, 120) radius 100\ncircle (255, 255, 255) at (160, 120) radius 80\ncircle (220, 0, 0) at (160, 120) radius 60\ncircle (255, 255, 255) at (160, 120) radius 40\ncircle (220, 0, 0) at (160, 120) radius 20","size":[320,240],"title":"Target","image":"lessons/windows/246bac55b3a093e7.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":9,"events":[]},"expectWindow":{"frames":9,"log":"fill (0, 0, 0)\ncircle (220, 0, 0) at (160, 120) radius 100\ncircle (255, 255, 255) at (160, 120) radius 80\ncircle (220, 0, 0) at (160, 120) radius 60\ncircle (255, 255, 255) at (160, 120) radius 40\ncircle (220, 0, 0) at (160, 120) radius 20","size":[320,240],"title":"Target","image":"lessons/windows/246bac55b3a093e7.png"},"expect":"","hidden":true}],"require":[{"pattern":"\\bfor\\b","message":"Draw the circles with a loop."},{"pattern":"draw\\.circle\\s*\\(","max":1,"message":"Use one `pygame.draw.circle` call, inside the loop."}],"forbid":[]},{"task":"Draw a loading bar that fills up. In each frame, first draw the bar: a green, `(0, 200, 0)`, filled rectangle at (22, 102) that is 36 high and `width` pixels wide. Then draw the frame around it: a white outline, line width 2, of the rectangle (20, 100, 280, 40). The bar's width is 0 on the first frame and grows by 4 after each frame, until it's 276, the whole inside of the frame. After that it stays 276. The window is black.\n","kind":"code","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"Loading\")\n\nwidth = 0\nclock = pygame.time.Clock()\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    pygame.display.flip()\n\n    clock.tick(60)\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\npygame.display.set_caption(\"Loading\")\n\nwidth = 0\nclock = pygame.time.Clock()\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    pygame.draw.rect(window, (0, 200, 0), (22, 102, width, 36))\n    pygame.draw.rect(window, (255, 255, 255), (20, 100, 280, 40), 2)\n    pygame.display.flip()\n\n    if width < 276:\n        width += 4\n    clock.tick(60)\n","hints":["The bar is `pygame.draw.rect(window, (0, 200, 0), (22, 102, width, 36))`. The frame is a rectangle too, with a fifth argument, the line width 2.","Draw the bar first and the frame second, as the task says, and grow `width` after `flip`.","Grow it only while it's less than 276: `if width < 276:`."],"tests":[{"name":"Test 1","stdin":"","window":{"frames":40,"events":[]},"expectWindow":{"frames":40,"log":"fill (0, 0, 0)\nrect (0, 200, 0) (22, 102, 156, 36)\nrect (255, 255, 255) (20, 100, 280, 40), width 2","size":[320,240],"title":"Loading","image":"lessons/windows/90a1865d75679bd2.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":100,"events":[]},"expectWindow":{"frames":100,"log":"fill (0, 0, 0)\nrect (0, 200, 0) (22, 102, 276, 36)\nrect (255, 255, 255) (20, 100, 280, 40), width 2","size":[320,240],"title":"Loading","image":"lessons/windows/bdae3949b5ddc226.png"},"expect":"","hidden":true},{"name":"Test 3","stdin":"","window":{"frames":30,"events":[]},"expectWindow":{"frames":30,"log":"fill (0, 0, 0)\nrect (0, 200, 0) (22, 102, 116, 36)\nrect (255, 255, 255) (20, 100, 280, 40), width 2","size":[320,240],"title":"Loading","image":"lessons/windows/d7a10b0c748669f4.png"},"expect":"","hidden":true}],"require":[],"forbid":[]}]}