6.2: Source Code to Simulate
- Page ID
- 13591
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)This source code can be downloaded from http://invpy.com/simulate.py. If you get any error messages, look at the line number that is mentioned in the error message and check your code for any typos. You can also copy and paste your code into the web form at http://invpy.com/diff/simulate to see if the differences between your code and the code in the book.
You can download the four sound files that this program uses from:
- http://invpy.com/beep1.ogg
- http://invpy.com/beep2.ogg
- http://invpy.com/beep3.ogg
- http://invpy.com/beep4.ogg
# Simulate (a Simon clone) # By Al Sweigart al@inventwithpython.com # http://inventwithpython.com/pygame # Released under a "Simplified BSD" license import random, sys, time, pygame from pygame.locals import * FPS = 30 WINDOWWIDTH = 640 WINDOWHEIGHT = 480 FLASHSPEED = 500 # in milliseconds FLASHDELAY = 200 # in milliseconds BUTTONSIZE = 200 BUTTONGAPSIZE = 20 TIMEOUT = 4 # seconds before game over if no button is pushed. # R G B 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) # Rect objects for each of the four buttons 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 FPSCLOCK, DISPLAYSURF, BASICFONT, BEEP1, BEEP2, BEEP3, BEEP4 pygame.init() FPSCLOCK = pygame.time.Clock() DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('Simulate') BASICFONT = pygame.font.Font('freesansbold.ttf', 16) infoSurf = BASICFONT.render('Match the pattern by clicking on the button or using the Q, W, A, S keys.', 1, DARKGRAY) infoRect = infoSurf.get_rect() infoRect.topleft = (10, WINDOWHEIGHT - 25) # load the sound files BEEP1 = pygame.mixer.Sound('beep1.ogg') BEEP2 = pygame.mixer.Sound('beep2.ogg') BEEP3 = pygame.mixer.Sound('beep3.ogg') BEEP4 = pygame.mixer.Sound('beep4.ogg') # Initialize some variables for a new game pattern = [] # stores the pattern of colors currentStep = 0 # the color the player must push next lastClickTime = 0 # timestamp of the player's last button push score = 0 # when False, the pattern is playing. when True, waiting for the player to click a colored button: waitingForInput = False while True: # main game loop clickedButton = None # button that was clicked (set to YELLOW, RED, GREEN, or BLUE) DISPLAYSURF.fill(bgColor) drawButtons() scoreSurf = BASICFONT.render('Score: ' + str(score), 1, WHITE) scoreRect = scoreSurf.get_rect() scoreRect.topleft = (WINDOWWIDTH - 100, 10) DISPLAYSURF.blit(scoreSurf, scoreRect) DISPLAYSURF.blit(infoSurf, infoRect) checkForQuit() for event in pygame.event.get(): # event handling loop if event.type == MOUSEBUTTONUP: mousex, mousey = event.pos clickedButton = getButtonClicked(mousex, mousey) elif event.type == KEYDOWN: if event.key == K_q: clickedButton = YELLOW elif event.key == K_w: clickedButton = BLUE elif event.key == K_a: clickedButton = RED elif event.key == K_s: clickedButton = GREEN if not waitingForInput: # play the pattern pygame.display.update() pygame.time.wait(1000) pattern.append(random.choice((YELLOW, BLUE, RED, GREEN))) for button in pattern: flashButtonAnimation(button) pygame.time.wait(FLASHDELAY) waitingForInput = True else: # wait for the player to enter buttons if clickedButton and clickedButton == pattern[currentStep]: # pushed the correct button flashButtonAnimation(clickedButton) currentStep += 1 lastClickTime = time.time() if currentStep == len(pattern): # pushed the last button in the pattern changeBackgroundAnimation() score += 1 waitingForInput = False currentStep = 0 # reset back to first step elif (clickedButton and clickedButton != pattern[currentStep]) or (currentStep != 0 and time.time() - TIMEOUT > lastClickTime): # pushed the incorrect button, or has timed out gameOverAnimation() # reset the variables for a new game: pattern = [] currentStep = 0 waitingForInput = False score = 0 pygame.time.wait(1000) changeBackgroundAnimation() pygame.display.update() FPSCLOCK.tick(FPS) def terminate(): pygame.quit() sys.exit() def checkForQuit(): for event in pygame.event.get(QUIT): # get all the QUIT events terminate() # terminate if any QUIT events are present for event in pygame.event.get(KEYUP): # get all the KEYUP events if event.key == K_ESCAPE: terminate() # terminate if the KEYUP event was for the Esc key pygame.event.post(event) # put the other KEYUP event objects back def flashButtonAnimation(color, animationSpeed=50): if color == YELLOW: sound = BEEP1 flashColor = BRIGHTYELLOW rectangle = YELLOWRECT elif color == BLUE: sound = BEEP2 flashColor = BRIGHTBLUE rectangle = BLUERECT elif color == RED: sound = BEEP3 flashColor = BRIGHTRED rectangle = REDRECT elif color == GREEN: sound = BEEP4 flashColor = BRIGHTGREEN rectangle = GREENRECT origSurf = DISPLAYSURF.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)): # animation loop for alpha in range(start, end, animationSpeed * step): checkForQuit() DISPLAYSURF.blit(origSurf, (0, 0)) flashSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(flashSurf, rectangle.topleft) pygame.display.update() FPSCLOCK.tick(FPS) DISPLAYSURF.blit(origSurf, (0, 0)) def drawButtons(): pygame.draw.rect(DISPLAYSURF, YELLOW, YELLOWRECT) pygame.draw.rect(DISPLAYSURF, BLUE, BLUERECT) pygame.draw.rect(DISPLAYSURF, RED, REDRECT) pygame.draw.rect(DISPLAYSURF, GREEN, GREENRECT) 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): # animation loop checkForQuit() DISPLAYSURF.fill(bgColor) newBgSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(newBgSurf, (0, 0)) drawButtons() # redraw the buttons on top of the tint pygame.display.update() FPSCLOCK.tick(FPS) bgColor = newBgColor def gameOverAnimation(color=WHITE, animationSpeed=50): # play all beeps at once, then flash the background origSurf = DISPLAYSURF.copy() flashSurf = pygame.Surface(DISPLAYSURF.get_size()) flashSurf = flashSurf.convert_alpha() BEEP1.play() # play all four beeps at the same time, roughly. BEEP2.play() BEEP3.play() BEEP4.play() r, g, b = color for i in range(3): # do the flash 3 times for start, end, step in ((0, 255, 1), (255, 0, -1)): # The first iteration in this loop sets the following for loop # to go from 0 to 255, the second from 255 to 0. for alpha in range(start, end, animationSpeed * step): # animation loop # alpha means transparency. 255 is opaque, 0 is invisible checkForQuit() flashSurf.fill((r, g, b, alpha)) DISPLAYSURF.blit(origSurf, (0, 0)) DISPLAYSURF.blit(flashSurf, (0, 0)) drawButtons() pygame.display.update() FPSCLOCK.tick(FPS) def getButtonClicked(x, y): if YELLOWRECT.collidepoint( (x, y) ): return YELLOW elif BLUERECT.collidepoint( (x, y) ): return BLUE elif REDRECT.collidepoint( (x, y) ): return RED elif GREENRECT.collidepoint( (x, y) ): return GREEN return None if __name__ == '__main__': main()