Hello World!

12 Days of Python – Day 11

This is Day 11 of 12 Days of Python.  This is the eleventh in a series of posts where we use Python and the Pygame module to draw a Christmas scene. Each day we add something else to the scene, and we show a new aspect of Python or Pygame. We’re also having a giveaway, where you can win a Kindle Touch!

On Day 1, we made a Pygame window and used the Pygame draw functions to draw Santa using lines circles and polygons. On Day 2, we made Santa’s arm wave. On Day 3, we added a calendar. On Day 4 we added code to cross off the days on the calendar. On Day 5, we added a background. On Day 6, we added a Christmas tree (using recursion). On Day 7, we drew some Christmas lights. On Day 8, we added a picture on the wall behind Santa. On Day 9, we added some presents.  On Day 10, we added a window.

For Day 11 we’re going to make the lights blink.  These are the fancy new lights that can change color, so we’ll make them blink by changing the color of each light randomly.

To do that, we added a couple new functions:  render_light() and update_lights().  We also modified the draw_a_light() function.  It now calls render_light() and also adds the lights to a list.  We then iterate through the list in the update_lights() function and change their colors.  In the main while loop, we call the update_lights() function once every 30 frames.

# santa_11.py
# This is the code for Day 11 of "12 Days of Python",
# by the authors of "Hello World! Computer Programming for Kids and Other Beginners".

import pygame, sys, math, datetime, random

lights = []

def render_light(surface, pos):
    pygame.draw.circle(surface, pygame.color.THECOLORS[random.choice(pygame.color.THECOLORS.keys())], pos, 6)

def update_lights():
    global updatedLights, lights
    for i in lights:
        render_light(updatedLights, i)

def draw_a_light(surface, pos):
    global lights
    render_light(surface, pos)
    lights.append(pos)

def rotatePointAroundCenter(point, center, degrees):
    displacement = (point[0] - center[0], point[1] - center[1])
    radians = math.radians(degrees)
    newPoint = [0,0]
    newPoint[0] = displacement[0] * math.cos(radians) + displacement[1] * math.sin(radians)
    newPoint[1] = displacement[1] * math.cos(radians) - displacement[0] * math.sin(radians)
    return newPoint

def draw_a_present(surface, (x,y)):
    (w,h) = random.choice([(100, 100), (50, 50), (75, 75)])
    present = pygame.surface.Surface((w,h))
    present.fill(pygame.color.THECOLORS[random.choice(pygame.color.THECOLORS.keys())])
    spotcolor = pygame.color.THECOLORS[random.choice(pygame.color.THECOLORS.keys())]
    for i in range(20):
        spotpos = (random.randint(0, 100), random.randint(0,100))
        pygame.draw.circle(present, spotcolor, spotpos, 10)
        pygame.draw.circle(present, (0,0,0), spotpos, 10, 2)
    presentrect= present.get_rect()
    presentrect.bottom = y
    presentrect.centerx = x
    surface.blit(present, presentrect)

def draw_a_tree(surface, leaf_size, (x,y), is_trunk=True):
    if is_trunk:
        rect = pygame.rect.Rect((x - 25, y - 30), (50,60))
        pygame.draw.rect(surface, (102,68,34), rect)
        draw_a_tree(surface, leaf_size, (x, y-30), False)
    elif leaf_size <= 2:
        return
    else:
        pygame.draw.polygon(surface, (0,200,0),((x-leaf_size/2,y),(x+leaf_size/2,y),(x,y-(2*leaf_size/3))))

        draw_a_tree(surface, 3*leaf_size/4, (x, y-leaf_size/3), False)
        if leaf_size > 30:
            light_rect=pygame.rect.Rect((0,0), (1,1))
            light_rect.width = leaf_size
            light_rect.height = 2*leaf_size/3
            light_rect.bottom = y
            light_rect.centerx = x
            pygame.draw.arc(surface, (0,0,0), light_rect, math.radians(-143), math.radians(74-110), 2)
            draw_a_light(surface, (x,y))
            draw_a_light(surface, (x - leaf_size/4, y-leaf_size/20))
            draw_a_light(surface, (x + leaf_size/4, y-leaf_size/20))

pygame.init()

pygame.display.set_caption("12 Days of Python")

screen = pygame.display.set_mode((640,480))
santa = pygame.surface.Surface((640,480)).convert_alpha()
santa.fill((0,0,0,0))
screen.fill((0,0,0))

santasArm = pygame.surface.Surface((640,480)).convert_alpha()
santasArm.fill((0,0,0,0))

updatedLights= pygame.surface.Surface((640,480)).convert_alpha()
updatedLights.fill((0,0,0,0))

background = pygame.surface.Surface((640,480))
background.fill((255,0,0))

#---BACKGROUND DRAW
pygame.draw.polygon(background, (148,123,90), ((0,480), (100,320), (540, 320), (640, 480)))
pygame.draw.polygon(background, (139,89,49), ((0,0), (100,0), (100,320), (0,480)))
pygame.draw.polygon(background, (139,89,49), ((640,0), (540,0), (540,320), (640,480)))
pygame.draw.polygon(background, (102,68,34), ((100,0) ,(540, 0), (540, 320), (100, 320)))
calendar = pygame.surface.Surface((189,150))
calendar.fill((255,255,255))

tree = pygame.surface.Surface((640,480)).convert_alpha()
tree.fill((0,0,0,0))
draw_a_tree(tree, 200, (100, 360))

#---ARTWORK DRAW
artwork = pygame.surface.Surface((640,480))
artwork.fill((0,0,0))

for i in range (100):
    width = random.randint(0, 250)
    height = random.randint(0, 100)
    top = random.randint(0, 400)
    left = random.randint(0, 500)
    color_name = random.choice(pygame.color.THECOLORS.keys())
    color = pygame.color.THECOLORS[color_name]
    line_width = random.randint(6, 9)
    pygame.draw.rect(artwork, color, [left, top, width, height], line_width)
artwork = pygame.transform.scale(artwork, (160, 120))

#---WINDOW DRAW
pygame.draw.polygon(background, (0,0,0), [(550,200),(630,280),(630,80),(550,60)])
pygame.draw.line(background,(0,80,0),(590,70),(590,240),10)
pygame.draw.line(background,(0,80,0),(550,130),(630,180),10)
pygame.draw.lines(background, (0,80,0), True, [(550,200),(630,280),(630,80),(550,60)], 10)

#---PRESENT DRAW
presents = pygame.surface.Surface((640,480)).convert_alpha()
presents.fill((0,0,0,0))
draw_a_present(presents, (500,400))
draw_a_present(presents, (460,430))
draw_a_present(presents, (70,420))
draw_a_present(presents, (130,440))

#---LIGHTS DRAW
pygame.draw.arc(background, (0,0,0), pygame.rect.Rect((0,-30),(320, 60)), -math.pi, 0, 5)
pygame.draw.arc(background, (0,0,0), pygame.rect.Rect((320,-30),(320, 60)), -math.pi, 0, 5)
draw_a_light(background, (6,6))
draw_a_light(background, (80,25))
draw_a_light(background, (160,30))
draw_a_light(background, (240, 25))
draw_a_light(background, (320, 6))
draw_a_light(background, (400,25))
draw_a_light(background, (480,30))
draw_a_light(background, (560, 25))
draw_a_light(background, (634, 6))
#---CALENDAR DRAW
for i in range(0, 189, 27):
    pygame.draw.line(calendar, (0,0,0), (i,30), (i,150), 2)
for i in range(30, 151, 24):
    pygame.draw.line(calendar, (0,0,0), (0, i), (189, i), 2)

cal_head_font = pygame.font.Font(None, 26)
cal_head_surface = cal_head_font.render("DECEMBER", True, (0,0,0))
cal_head_rect = cal_head_surface.get_rect()
cal_head_rect.center = (94.5, 15)

cal_num_font = pygame.font.Font(None, 20)

for i in range(1,32):
    cal_num_surface= cal_num_font.render(str(i), True, (0,0,0))
    cal_num_rect = cal_num_surface.get_rect()
    cal_num_rect.center = (((i+3) % 7 * 27) + 13.5,((i+3) // 7 * 24) + 42)
    calendar.blit(cal_num_surface, cal_num_rect)

calendar.blit(cal_head_surface, cal_head_rect)

#---CALENDAR CROSSOUT
today = datetime.datetime.now().day

for i in range(1, today):
    pygame.draw.line(calendar, (0,0,0), (((i+3) % 7 * 27) + 27, ((i + 3) // 7 * 24) + 30),
                (((i + 3) % 7 * 27),  ((i + 3) // 7 * 24)+54), 3   )

#---L ARM DRAW
pygame.draw.polygon(santasArm,(255,0,0),((274,207),(264,197),(243,182),(226,168),
                    (215,154),(212,153),(206,158),(192,155),(183,157),
                    (182,172),(204,190),(219,208),(244,231),(261,245))) #arm n hand
pygame.draw.polygon(santasArm, (255,255,255),((243,182),(242,176),(231,167),
                    (226,168),(218,181),(204,190),(204,200),(212,208),
                    (219,208),(232,197))) #arm fur
santasRotArm = santasArm.subsurface(pygame.rect.Rect((182,153),(92, 92)))
#---R ARM DRAW
pygame.draw.polygon(santa,(255,0,0),((358,191),(405,202),(428,207),(448,212),
                    (457,220),(459,226),(457,232),(444,236),(440,243),(421,237),
                    (399,233),(389,233),(344,236))) #arm n hand
pygame.draw.polygon(santa,(255,255,255),((428,207),(418,197),(406,197),
                    (407,204),(400,215),(401,237),(421,238),(421,223))) #arm fur
#---L LEG DRAW
pygame.draw.polygon(santa,(255,0,0),((270,323),(268,345),(266,352),(305,361),
                    (309,352),(317,326),(319,312),(272,306))) #leg
pygame.draw.polygon(santa,(102,68,34),((301,370),(266,361),(247,356),(231,357),
                    (224,368),(227,380),(245,388),(272,386),(274,390),(295,391))) #boot
pygame.draw.polygon(santa,(255,255,255), ((301,370),(312,355),(303,352),(293,348),
                    (279,346),(268,342),(263,343),(266,361))) #fur
#---R LEG DRAW
pygame.draw.polygon(santa,(255,0,0),((322,326),(330,352),(331,359),(372,350),
                            (369,323),(367,311),(316,314))) #leg
pygame.draw.polygon(santa, (102,68,34),((340,369),(373,361),(396,356),(410,359),
                    (415,375),(400,387),(366,386),(365,392),(347,393),(340,386))) #boot
pygame.draw.polygon(santa,(255,255,255),((373,361),(340,369),(334,368),(326,353),
                    (345,348),(359,347),(375,343)))
#---BODY DRAW
pygame.draw.polygon(santa, (255,255,255), ((234,289),(226,299),(230,316),
                    (281,322),(304,327),(317,326),(333,322),(363,324),
                    (408,304),(410,287),(402,275),(328,208),(281,207))) #vest fur
pygame.draw.polygon(santa, (255,0,0), ((271,188),(245,231),(236,259),(234,272),
                    (235,287),(241,295),(294,302),(300,298),(304,285),
                        (298,217))) #vest 1
pygame.draw.polygon(santa, (255,0,0), ((326,235),(328,286),(330,295),(335,300),
                    (369,297),(401,277),(389,234),(357,192),(334,197))) #vest 2

pygame.draw.circle(santa,(0,0,0),(315,243),4)
pygame.draw.circle(santa,(0,0,0),(314,257),4)
pygame.draw.circle(santa,(0,0,0),(315,274),4)
pygame.draw.circle(santa,(0,0,0),(317,293),4)
#---HEAD DRAW
pygame.draw.polygon(santa, (255,255,255),((278,153),(272,188),
                    (281,211),(300,232),(315,238),(326,232),(346,215),(357,191),
                    (351,151),(278,153))) #beard
pygame.draw.polygon(santa, (255, 218, 185),((282,151),(283,166),(309,178),
                    (315,179),(321,177),(345,166),(346,152),(335,140),(310,133),
                    (289,141))) #head
pygame.draw.polygon(santa, (255,0,0),((266,139),(278,123),
                    (293,112),(315,110),(343,115),(312,85),(260,134))) #hat main
pygame.draw.polygon(santa,(255,255,255),((346,152),(335,140),(310,133),
                    (289,141),(282,151),(270,150),(261,146),(266,139),(278,123),
                    (293,112),(315,110),(343,115),(357,132))) #hat rim
pygame.draw.circle(santa,(255,255,255),(255,149),15) #hat ball
pygame.draw.circle(santa,( 50, 50, 50),(307,151),3) #eye
pygame.draw.circle(santa,( 50, 50, 50),(319,151),3) #eye
pygame.draw.polygon(santa,(255,255,255),((310,140),(301,141),(294,145),(294,149),
                    (303,145),(309,144))) #eyebrow
pygame.draw.polygon(santa,(255,255,255),((315,146),(324,146),(330,150),(331,146),
                    (326,142),(317,140))) #eyebrow
pygame.draw.polygon(santa,(255,255,255),((283,167),(287,177),(296,182),(311,176),
                    (305,169),)) #mustachelet
pygame.draw.polygon(santa,(255,255,255),((344,168),(323,166),(319,176),(336,178))) #mustachelet
pygame.draw.lines(santa, (0,0,0), False, ((307,165),(307,169),(310,173),(321,171),(322,164)),2) #nose
counter = 0
clock = pygame.time.Clock()
while 1:
    clock.tick(30)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    screen.fill((0,0,0))
    #screen.blit(santasArm, (0,0))
    rotatedArm = pygame.transform.rotate(santasRotArm,math.sin(counter/5.0)*10-7)
    rotArmRect = rotatedArm.get_rect()
    rotArmPos = rotatePointAroundCenter((86,73),santasRotArm.get_rect().center,-math.sin(counter/5.0)*10+7)
    if counter % 30 == 0:
        update_lights()
    rotArmPos[0] += 190
    rotArmPos[1] += 180
    rotArmRect.center = rotArmPos
    screen.blit(background, (0,0))
    screen.blit(artwork, (120,50))
    screen.blit(tree, (0,0))
    screen.blit(updatedLights, (0,0))
    screen.blit(presents, (0,0))
    screen.blit(calendar, (400, 20))
    screen.blit(rotatedArm, rotArmRect)
    screen.blit(santa, (0,0))
    pygame.display.flip()
    counter += 1

When you run the program, it looks the same as Day 10, except the lights will all change color once a second.

To try the code out yourself, you can cut-and-paste it from the code window above into your favorite Python editor.

On Day 12, we’ll add the final bit of code.  Check back on Dec 23 to find out what it does!

Warren & Carter