def __init__(self, fps=6, evolve=0, center=False, blink=False, intro=True, evolveIncrease=0, goalScore=None, musicFile=None, win=None, banner=None, js=None, showInstructions=True, rotDur=4, instructionScreen=None, logScore=True): """ Constructor. Keyword arguments: fps -- The number of frames per second. (default=2) evolve -- Indicates the chance of a maze evolution. (default=0) center -- Indicates whether the image should be centered in Pacman. (default=True) evolveIncrease -- The value to be added to evolve with each step. (default=0) goalScore -- The score at which you win the game or None for infinite gameplay. (default=None) musicFile -- The path to a music file for in-game music. (default=None) win -- The PyGame window or None to initialize. (default=None) banner -- A banner image that is presented to the right of the maze to pad non-square resolutions. (default=None) js -- A joystick object. (default=None) showInstructions -- Indicates whether an instruction screen should be shown. (default=True) rotDur -- The number of rotations that pac-man makes prior to the game. (default=4) instructionScreen -- The path to the instruction screen or None to use the default. (default=None) logScore -- Indicates whether the score should be logged and the percentile score should be calculated. (default=True) """ self.maze = Maze(self, blink=blink, banner=banner) self.pacman = Pacman(self) self.ghosts = [] self.addGhost(n=2) self.frameDur = int(1000. / fps) self.evolve = evolve self.evolveIncrease = evolveIncrease self.center = center self.blink = blink self.intro = intro self.persistentGhosts = True self.goalScore = goalScore self.musicFile = musicFile self.musicFadeout = 4000 self.musicVolume = .5 self._win = win self.js = js self.showInstructions = showInstructions self.rotDur = rotDur self.logScore = logScore if instructionScreen == None: self.instructionScreen = os.path.join(os.path.dirname( infinitemaze.__file__), u'sprites', 'instructions.png') else: self.instructionScreen = instructionScreen
class Game(object): """The game-controller class.""" def __init__(self, fps=6, evolve=0, center=False, blink=False, intro=True, evolveIncrease=0, goalScore=None, musicFile=None, win=None, banner=None, js=None, showInstructions=True, rotDur=4, instructionScreen=None, logScore=True): """ Constructor. Keyword arguments: fps -- The number of frames per second. (default=2) evolve -- Indicates the chance of a maze evolution. (default=0) center -- Indicates whether the image should be centered in Pacman. (default=True) evolveIncrease -- The value to be added to evolve with each step. (default=0) goalScore -- The score at which you win the game or None for infinite gameplay. (default=None) musicFile -- The path to a music file for in-game music. (default=None) win -- The PyGame window or None to initialize. (default=None) banner -- A banner image that is presented to the right of the maze to pad non-square resolutions. (default=None) js -- A joystick object. (default=None) showInstructions -- Indicates whether an instruction screen should be shown. (default=True) rotDur -- The number of rotations that pac-man makes prior to the game. (default=4) instructionScreen -- The path to the instruction screen or None to use the default. (default=None) logScore -- Indicates whether the score should be logged and the percentile score should be calculated. (default=True) """ self.maze = Maze(self, blink=blink, banner=banner) self.pacman = Pacman(self) self.ghosts = [] self.addGhost(n=2) self.frameDur = int(1000. / fps) self.evolve = evolve self.evolveIncrease = evolveIncrease self.center = center self.blink = blink self.intro = intro self.persistentGhosts = True self.goalScore = goalScore self.musicFile = musicFile self.musicFadeout = 4000 self.musicVolume = .5 self._win = win self.js = js self.showInstructions = showInstructions self.rotDur = rotDur self.logScore = logScore if instructionScreen == None: self.instructionScreen = os.path.join(os.path.dirname( infinitemaze.__file__), u'sprites', 'instructions.png') else: self.instructionScreen = instructionScreen def addGhost(self, n=1): """ Populates the maze with ghosts. Keyword arguments: n -- The number of ghosts to add. (default=1) """ for i in range(n): self.ghosts.append(Ghost(self)) def handleEvents(self): """ Handles keyboard and mouse events. Returns: False if the game was aborted, True otherwise. """ xc = (self.maze.width() * self.maze.cellSize()[0])/2 yc = (self.maze.height() * self.maze.cellSize()[1])/2 # Process keypress events for e in pygame.event.get(): # For the PC if e.type == pygame.KEYDOWN: if e.key == pygame.K_ESCAPE: return False elif e.key == pygame.K_UP: self.pacman.setNextDir(u'up') elif e.key == pygame.K_DOWN: self.pacman.setNextDir(u'down') elif e.key == pygame.K_LEFT: self.pacman.setNextDir(u'left') elif e.key == pygame.K_RIGHT: self.pacman.setNextDir(u'right') elif e.key == pygame.K_p: self.wait() # Optionally use the joystick elif self.js != None and e.type == pygame.JOYBUTTONDOWN: if e.button == 8: # start button return False elif self.js != None and e.type == pygame.JOYAXISMOTION: dx = 0 dy = 0 for i in range(self.js.get_numaxes()): if self.js.get_axis(i) == 0: continue if i % 2 == 0: dx = self.js.get_axis(i) else: dy = self.js.get_axis(i) if abs(dx) > abs(dy): if dx < 0: self.pacman.setNextDir(u'left') elif dx > 0: self.pacman.setNextDir(u'right') else: if dy < 0: self.pacman.setNextDir(u'up') elif dy > 0: self.pacman.setNextDir(u'down') # Tapping on the screen also steers the Pacman (for Android) elif e.type == pygame.MOUSEBUTTONDOWN: x, y = e.pos dx = x-xc dy = y-yc if abs(dx) > abs(dy): if dx > 0: self.pacman.setNextDir(u'right') else: self.pacman.setNextDir(u'left') elif dy > 0: self.pacman.setNextDir(u'down') else: self.pacman.setNextDir(u'up') return True def introduction(self): """Shows the introduction animation.""" if self.showInstructions: imgIns = pygame.image.load(self.instructionScreen) for i in range(5): if android != None and android.check_pause(): android.wait_for_resume() self.maze.show() self.win().blit(imgIns, (0,0)) pygame.display.flip() # Process keypress events if not self.wait(roi=True): return False if self.musicFile != None: mixer.music.play() pygame.time.wait(200) i = 0 for dir in [u'up', u'right', u'down', u'left'] * self.rotDur: t0 = pygame.time.get_ticks() self.maze.show() for ghost in self.ghosts: ghost.show(center=self.center) self.pacman.setDir(dir) self.pacman.show(center=self.center) if self.goalScore != None: self.maze.showText(u'Reach %d points!' % self.goalScore) pygame.display.flip() if android != None and android.check_pause(): android.wait_for_resume() i += 1 t1 = pygame.time.get_ticks() if (t1-t0) < self.frameDur: dur = self.frameDur-(t1-t0) print(u'Sleep %d' % dur) pygame.time.wait(dur) return True def launch(self): """ Starts a single game. Returns: True if we should play again, False otherwise. """ self.maze.initWin(self._win) # Initialize music if self.musicFile != None: mixer.music.load(self.musicFile) mixer.music.set_volume(self.musicVolume) if self.intro: if not self.introduction(): return False # And start the main game loop! self.pacman.setDir(u'stop') while True: t0 = pygame.time.get_ticks() # Move pacman and the ghosts. If a ghost and pacman have collided, # we don't move the ghost, because that will cause them to jump # over eachother self.pacman.move() for ghost in self.ghosts: if ghost.getPos() != self.pacman.getPos(): ghost.move() # Next evolve the maze. if random() <= self.evolve and (self.pacman.isMoving() or \ self.blink): self.maze.evolve(style=u'directional') if self.persistentGhosts: # Remove walls where there are ghosts for ghost in self.ghosts: if self.maze.wallAt(ghost.getPos()): self.maze.clearAt(ghost.getPos()) else: # Respawn ghosts that are in a wall _ghosts = [] for ghost in self.ghosts: if self.maze.wallAt(ghost.getPos()): ghost = Ghost(self.maze, self.pacman) _ghosts.append(ghost) self.ghosts = _ghosts # Evolve the evolution! self.evolve += self.evolveIncrease print(u'Evolve %.2f' % self.evolve) # Show the maze, followed by the ghosts, followed by pacman. Pacman # also draws the scoreboard. self.maze.show(center=self.center) for ghost in self.ghosts: ghost.show(center=self.center) self.pacman.show(center=self.center) # Check if we're game over for ghost in self.ghosts: if ghost.getPos() == self.pacman.getPos(): if self.musicFile != None: mixer.music.fadeout(self.musicFadeout) self.maze.gameOver() return True # Sleep to pad the end of the frame t1 = pygame.time.get_ticks() if (t1-t0) < self.frameDur: dur = self.frameDur-(t1-t0) print(u'Sleep %d' % dur) pygame.time.wait(dur) pygame.display.flip() if android != None and android.check_pause(): android.wait_for_resume() # Handle input events and return if the game was aborted if not self.handleEvents(): if self.musicFile != None: mixer.music.fadeout(self.musicFadeout) return False # Check if we've won if self.goalScore != None and self.getScore() >= self.goalScore: if self.musicFile != None: mixer.music.fadeout(self.musicFadeout) self.maze.gameWon() return True def getScore(self): """ Returns: The game score. """ return self.pacman.getScore() def wait(self, roi=False): """ Pauzes until the screen is tapped or a key is pressed. Keyword arguments: roi -- Indicates whether the screen has regions of interest, i.e. buttons that are hardcoded in the image. This is used for the instruction screen. (default=False) """ # First flush for e in pygame.event.get(): pass # And then wait while True: for e in pygame.event.get(): if e.type == pygame.KEYDOWN: if e.key == pygame.K_ESCAPE: return False return True elif e.type == pygame.MOUSEBUTTONDOWN: if roi: x, y = e.pos if x >= 656 and y >= 28 and x <= 720 and y <= 92: print(u'Turning off music') self.musicFile = None elif x >= 656 and y >= 128 and x <= 720 and y <= 192: return False return True elif self.js != None and e.type == pygame.JOYBUTTONDOWN: if e.button == 8: # start button return False return True if android != None and android.check_pause(): android.wait_for_resume() def win(self): """ Return: The pygame window. """ return self.maze.win