# Simulate (A Simon Clone) # http://inventwithpython.com/blog # By Al Sweigart al@inventwithpython.com import random import time import pygame import sys from pygame.locals import * FPS = 30 WINDOWWIDTH = 640 WINDOWHEIGHT = 480 FLASHSPEED = 0.5 FLASHDELAY = 0.2 BUTTONSIZE = 200 BUTTONGAPSIZE = 20 TIMEOUT = 4 WHITE = (255, 255, 255) BLACK = (0, 0, 0) BRIGHTRED = (255, 0, 0) RED = (155, 0, 0) BRIGHTGREEN = (0, 255, 0) GREEN = (0, 155, 0) BRIGHTBLUE = (0, 0, 255) BLUE = (0, 0, 155) BRIGHTYELLOW = (255, 255, 0) YELLOW = (155, 155, 0) DARKGRAY = (40, 40, 40) BGCOLOR = BLACK XMARGIN = int((WINDOWWIDTH - (2 * BUTTONSIZE) - BUTTONGAPSIZE) / 2) YMARGIN = int((WINDOWHEIGHT - (2 * BUTTONSIZE) - BUTTONGAPSIZE) / 2) YELLOWRECT = pygame.Rect(XMARGIN, YMARGIN, BUTTONSIZE, BUTTONSIZE) BLUERECT = pygame.Rect(XMARGIN + BUTTONSIZE + BUTTONGAPSIZE, YMARGIN, BUTTONSIZE, BUTTONSIZE) REDRECT = pygame.Rect(XMARGIN, YMARGIN + BUTTONSIZE + BUTTONGAPSIZE, BUTTONSIZE, BUTTONSIZE) GREENRECT = pygame.Rect(XMARGIN + BUTTONSIZE + BUTTONGAPSIZE, YMARGIN + BUTTONSIZE + BUTTONGAPSIZE, BUTTONSIZE, BUTTONSIZE) def main(): global MAINCLOCK, MAINSURF, FONT, BEEP1, BEEP2, BEEP3, BEEP4 pygame.init() MAINCLOCK = pygame.time.Clock() MAINSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('Simulate') FONT = pygame.font.Font('freesansbold.ttf', 16) BEEP1 = pygame.mixer.Sound('beep1.ogg') BEEP2 = pygame.mixer.Sound('beep2.ogg') BEEP3 = pygame.mixer.Sound('beep3.ogg') BEEP4 = pygame.mixer.Sound('beep4.ogg') pattern = [] currentStep = 0 waitingForInput = False lastClickTime = 0 score = 0 while True: clicked = None MAINSURF.fill(BGCOLOR) drawButtons() scoreSurf = FONT.render('Score: ' + str(score), 1, WHITE) scoreRect = scoreSurf.get_rect() scoreRect.topleft = (WINDOWWIDTH - 100, 10) MAINSURF.blit(scoreSurf, scoreRect) infoSurf = FONT.render('Match the pattern by clicking on the button or using the 8, 9, 5, 6 keys.', 1, DARKGRAY) infoRect = infoSurf.get_rect() infoRect.topleft = (10, WINDOWHEIGHT - 25) MAINSURF.blit(infoSurf, infoRect) if not waitingForInput: pygame.display.update() time.sleep(1) pattern.append(random.choice((YELLOW, BLUE, RED, GREEN))) # Play the pattern. for button in pattern: flashButtonAnimation(button) time.sleep(FLASHDELAY) waitingForInput = True # Handle any events. for event in pygame.event.get(): if event.type == QUIT: terminate() if event.type == MOUSEMOTION: mousex, mousey = event.pos if event.type == MOUSEBUTTONUP: mousex, mousey = event.pos clicked = getButtonClicked(mousex, mousey) if event.type == KEYDOWN: if event.key == K_ESCAPE: terminate() if event.key == K_q or event.key == K_8: clicked = YELLOW if event.key == K_w or event.key == K_9: clicked = BLUE if event.key == K_a or event.key == K_5: clicked = RED if event.key == K_s or event.key == K_6: clicked = GREEN if waitingForInput: if clicked and clicked == pattern[currentStep]: # pushed the correct button flashButtonAnimation(clicked) currentStep += 1 lastClickTime = time.time() if currentStep == len(pattern): # pushed the last button changeBackgroundAnimation() score += 1 waitingForInput = False currentStep = 0 elif (clicked and clicked != pattern[currentStep]) or (currentStep != 0 and time.time() - TIMEOUT > lastClickTime): # pushed the incorrect button, or has timed out gameOverAnimation() pattern = [] currentStep = 0 waitingForInput = False score = 0 time.sleep(1) pygame.display.update() MAINCLOCK.tick(FPS) def terminate(): pygame.quit() sys.exit() def flashButtonAnimation(color, animationSpeed=50): if color == YELLOW: sound = BEEP1 flashColor = BRIGHTYELLOW rectangle = YELLOWRECT if color == BLUE: sound = BEEP2 flashColor = BRIGHTBLUE rectangle = BLUERECT if color == RED: sound = BEEP3 flashColor = BRIGHTRED rectangle = REDRECT if color == GREEN: sound = BEEP4 flashColor = BRIGHTGREEN rectangle = GREENRECT origSurf = MAINSURF.copy() flashSurf = pygame.Surface((BUTTONSIZE, BUTTONSIZE)) flashSurf = flashSurf.convert_alpha() r, g, b = flashColor sound.play() for start, end, step in ((0, 255, 1), (255, 0, -1)): for alpha in range(start, end, animationSpeed * step): checkForQuit() flashSurf.fill((r, g, b, alpha)) MAINSURF.blit(origSurf, (0, 0)) MAINSURF.blit(flashSurf, rectangle.topleft) pygame.display.update() MAINCLOCK.tick(FPS) MAINSURF.blit(origSurf, (0, 0)) def changeBackgroundAnimation(animationSpeed=40): global BGCOLOR newBgColor = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) newBgSurf = pygame.Surface((WINDOWWIDTH, WINDOWHEIGHT)) newBgSurf = newBgSurf.convert_alpha() r, g, b = newBgColor for alpha in range(0, 255, animationSpeed): checkForQuit() MAINSURF.fill(BGCOLOR) newBgSurf.fill((r, g, b, alpha)) MAINSURF.blit(newBgSurf, (0, 0)) drawButtons() pygame.display.update() MAINCLOCK.tick(FPS) BGCOLOR = newBgColor def gameOverAnimation(color=WHITE, animationSpeed=50): # play all beeps at once, then flash the background origSurf = MAINSURF.copy() flashSurf = pygame.Surface(MAINSURF.get_size()) flashSurf = flashSurf.convert_alpha() BEEP1.play() BEEP2.play() BEEP3.play() BEEP4.play() r, g, b = color for i in range(3): for start, end, step in ((0, 255, 1), (255, 0, -1)): for alpha in range(start, end, animationSpeed * step): checkForQuit() flashSurf.fill((r, g, b, alpha)) MAINSURF.blit(origSurf, (0, 0)) MAINSURF.blit(flashSurf, (0, 0)) drawButtons() pygame.display.update() MAINCLOCK.tick(FPS) MAINSURF.blit(origSurf, (0, 0)) def drawButtons(): pygame.draw.rect(MAINSURF, YELLOW, YELLOWRECT) pygame.draw.rect(MAINSURF, BLUE, BLUERECT) pygame.draw.rect(MAINSURF, RED, REDRECT) pygame.draw.rect(MAINSURF, GREEN, GREENRECT) def getButtonClicked(x, y): if YELLOWRECT.collidepoint( (x, y) ): return YELLOW if BLUERECT.collidepoint( (x, y) ): return BLUE if REDRECT.collidepoint( (x, y) ): return RED if GREENRECT.collidepoint( (x, y) ): return GREEN return None def checkForQuit(): for event in pygame.event.get(QUIT): if event.type == QUIT: terminate() if __name__ == '__main__': main()