def __init__( self, prevState = None ): ## This initializes the GameState object. This is important as it also specifies the self.data object which is derived from the GameStateData class. """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData()
def __init__( self, prevState = None ): ###PLEASE NOTE THIS THAT THE __init__ method is here and this is where GameState() starts """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData()
def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData()
def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] else: # Initial state self.data = GameStateData() self.data.ghostDistances = []
def __init__( self, prevState = None ): ###PLEASE NOTE THIS THAT THE __init__ method is here and this is where GameState() starts """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) ##This statement imports the GameStateData object from the GameStateData class in game.py. This object contains a data packet documenting the state of the game. else: self.data = GameStateData() """
def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] self.numMoves = prevState.numMoves; self.maxMoves = prevState.maxMoves; else: # Initial state self.data = GameStateData() self.numMoves = 0; self.maxMoves = -1;
def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.blueTeam = prevState.blueTeam self.redTeam = prevState.redTeam self.teams = prevState.teams self.agentDistances = prevState.agentDistances else: self.data = GameStateData() self.agentDistances = []
def initPlot(self, xmin, ymin, width, height): if graphicsUtils._canvas is not None: graphicsUtils.clear_screen() # Initialize GameStateData with blank board with axes self.width = width self.height = height self.xShift = -(xmin-1) self.yShift = -(ymin-1) self.line = None self.zoom = min(30.0/self.width, 20.0/self.height) self.gridSize = graphicsDisplay.DEFAULT_GRID_SIZE * self.zoom # fullRow = ['%']*self.width # row = ((self.width-1)/2)*[' '] + ['%'] + ((self.width-1)/2)*[' '] # boardText = ((self.height-1)/2)*[row] + [fullRow] + ((self.height-1)/2)*[row] numSpaces = self.width-1 numSpacesLeft = self.xShift numSpacesRight = numSpaces-numSpacesLeft numRows = self.height numRowsBelow = self.yShift numRowsAbove = numRows-1-numRowsBelow fullRow = ['%']*self.width if numSpacesLeft < 0: row = [' ']*self.width else: row = numSpacesLeft*[' '] + ['%'] + numSpacesRight*[' '] boardText = numRowsAbove*[row] + [fullRow] + numRowsBelow*[row] layout = Layout(boardText) self.blankGameState = GameStateData() self.blankGameState.initialize(layout, 0) self.initialize(self.blankGameState) title = 'Pacman Plot' graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh()
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def updateState(self, actions): """ Returns the successor state after the actions of all active agents. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('it is terminal state now.') self.data.clear() for agentIndex in range(len(actions)): if self.data._eaten[agentIndex] > 0: BombermanRules.applyAction(self, actions[agentIndex], agentIndex) self.minusOneFrame() for counter, position, power, index in self.data.bombs: if counter == self.getFramesUntilEnd(): self.bombExplode(self.data.bombs, position, power) if index >= 0: self.getAgentState(index).recoverABomb() self.data.bombs = [ b for b in self.data.bombs if (b[0] != self.getFramesUntilEnd()) ] self.updateBombScore() self.updateMapScore() self.data.score = self.data._eaten[0] def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] return BombermanRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action, force=False): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board #state.data._eaten = [False for i in range(state.getNumAgents())] if state.data._eaten[agentIndex] > 0: BombermanRules.applyAction(state, action, agentIndex) if force or (agentIndex == state.getNumAgents() - 1): state.minusOneFrame() for counter, position, power, index in state.data.bombs: if counter == state.getFramesUntilEnd(): state.bombExplode(state.data.bombs, position, power) state.getAgentState(index).recoverABomb() state.data.bombs = [ b for b in state.data.bombs if (b[0] != state.getFramesUntilEnd()) ] state.updateBombScore() state.updateMapScore() # Time passes state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around # Resolve multi-agent effects #GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getAgentPosition(self, agentIndex): return self.data.agentStates[agentIndex].getPosition() def getAgentState(self, agentIndex): return self.data.agentStates[agentIndex] def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def getMapScore(self, x, y=None): if y is None: try: x, y = x except: raise 'incomprehensible coordinates' return self.data.MapScore[x][y] def getBombScore(self, x, y=None): if y is None: try: x, y = x except: raise 'incomprehensible coordinates' return self.data.BombScore[x][y] def hasWall(self, x, y): return self.data.map.isWall((x, y)) def isLose(self): return (self.getNumAgents() == 1 and self.data._eaten[0] == 0) or (self.data._eaten.count(0) == self.getNumAgents()) def isWin(self): return (self.getNumAgents() != 1 and self.data._eaten.count(0) is self.getNumAgents() - 1) def getFramesUntilEnd(self): return self.data.FramesUntilEnd def minusOneFrame(self): self.data.FramesUntilEnd = self.data.FramesUntilEnd - 1 return self.data.FramesUntilEnd def layABomb(self, agentIndex, pos): self.data._bombLaid.append(pos) self.data.map.add_bomb(pos) self.getAgentState(agentIndex).minusABomb() self.data.bombs.append( (self.data.FramesUntilEnd - BOMB_DURATION, pos, self.getAgentState(agentIndex).getBombPower(), agentIndex)) def bombExplode(self, bombs, position, power): x_int, y_int = position if not self.data.map.isBomb(position): return self.data._bombExplode.append(position) self.data.map.remove_object(position) fired = [] for i in range(len(self.data._fire)): fired += self.data._fire[i] if not position in fired: self.checkDie(position) self.data._fire[0].append(position) fired.append(position) for vec in [ v for dir, v in Actions._directionsAsList if (not dir in [Actions.LAY, Directions.STOP]) ]: isbreak = False i = 0 dx, dy = vec next_y, next_x = y_int, x_int while not isbreak and i < power: i = i + 1 next_y = int(next_y + dy) next_x = int(next_x + dx) pos = (next_x, next_y) if pos in fired: continue if self.data.map.isEmpty(pos): self.checkDie(pos) self.data._fire[i].append(pos) fired.append(pos) elif self.data.map.isBlock(pos): isbreak = True self.data._blockBroken.append(pos) res = self.data.map.remove_object(pos) self.data._fire[i].append(pos) fired.append(pos) if res != None: self.data._itemDrop.append((next_x, next_y, res)) elif self.data.map.isWall(pos): isbreak = True elif self.data.map.isItem(pos): self.data._itemEaten.append(pos) self.data.map.remove_object(pos) self.data._fire[i].append(pos) fired.append(pos) self.checkDie(pos) elif self.data.map.isBomb(pos): self.checkDie(pos) self.data._fire[i].append(pos) fired.append(pos) bombSweep = [(idx, bomb) for idx, bomb in enumerate(bombs) if (pos in bomb) and ( bomb[0] < self.data.FramesUntilEnd - int(BOMB_DURATION / 10))] if len(bombSweep) is 1: bombs[bombSweep[0][0]] = ( self.data.FramesUntilEnd - int(BOMB_DURATION / 10), ) + bombSweep[0][1][1:] def checkDie(self, position): x, y = position for index, agent in enumerate(self.data.agentStates): if self.data._eaten[index] is 0: continue sx, sy = agent.getPosition() sx, sy = round(sx), round(sy) if manhattanDistance(position, (sx, sy)) <= 0.5: self.data._eaten[index] -= 1 agent.configuration = agent.start def updateBombScore(self): # The change to the BombScore: self.data.BombScore.data = [[0 for y in range(self.data.map.height)] for x in range(self.data.map.width)] for counter, pos, power, index in self.data.bombs: score = self.calBombScore(counter) self.data.BombScore[pos[0]][pos[1]] += score isbreak = False for vec in [ v for dir, v in Actions._directionsAsList if (not dir in [Actions.LAY, Directions.STOP]) ]: isbreak = False i = 0 dx, dy = vec next_x, next_y = pos while not isbreak and i < power: i += 1 next_x = int(next_x + dx) next_y = int(next_y + dy) if self.data.map.isBlock( (next_x, next_y)) or self.data.map.isWall( (next_x, next_y)): isbreak = True else: self.data.BombScore[next_x][next_y] += score - i / 5.0 def updateMapScore(self): self.data.MapScore.data = [[0 for y in range(self.data.map.height)] for x in range(self.data.map.width)] for x in range(self.data.map.width): for y in range(self.data.map.height): if not self.data.map.isBlocked((x, y)) or self.data.map.isBomb( (x, y)): main = [ self.data.map.isBlocked((row, col)) for row, col in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)] ] second = [ self.data.map.isBlocked((row, col)) for row, col in [(x + 1, y + 1), (x - 1, y + 1), (x + 1, y - 1), (x - 1, y - 1)] ] self.data.MapScore[x][y] = (main.count(True) * 1 + second.count(True) * 0.4) if main.count(True) == 4: self.data.MapScore[x][y] = 100 if self.data.map.isBomb((x, y)): self.data.MapScore[x][y] += 1 def calBombScore(self, counter): if self.getFramesUntilEnd() - 1 == counter: # next frame would explode return 100 return 3 * (BOMB_DURATION - (self.getFramesUntilEnd() - counter)) def getTotalLives(self, indexes): return sum([self.data._eaten[index] for index in indexes]) ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, layout, numAgents=1000, timeout=3000, life=5): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents, timeout, life, BOMB_DURATION)
class GameState: def getLegalActions(self, agentIndex=0): if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
class GameState: explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions( self, agentIndex=0 ): if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') state = GameState(self) if agentIndex == 0: state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: GhostRules.applyAction( state, action, agentIndex ) if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) GhostRules.checkDeath( state, agentIndex ) state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): return self.generateSuccessor( 0, action ) def getPacmanState( self ): return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getGhostStates( self ): return self.data.agentStates[1:] def getGhostState( self, agentIndex ): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return float(self.data.score) def getCapsules(self): return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): return self.data.food def getWalls(self): return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose( self ): return self.data._lose def isWin( self ): return self.data._win def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): return hasattr(other, 'data') and self.data == other.data def __hash__( self ): return hash( self.data ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): self.data.initialize(layout, numGhostAgents)
class GameState: """ Un GameState (Estado del juego) especifica el estado completo del juego, incluyendo la comida, capsulas, configuraciones del agente y cambios en el puntaje. Los GameStates son utilizados por el objeto Game para caputar el estado acutal del juego y puede ser utilizado por los agentes para razonar sobre el juego. Mucha de la información en un GameState está almacenada en un objeto GameStateData. Recomendamos fuertemente que accedas a esa información por medio de los métodos descritos debajo que en lugar de utilizar el objeto GameStateData directamente. Observa que en Pacman clásico, Pacman siempre es el agente 0. """ ############################################################## # Metodos de acceso: usa estos para acceder a GameStateData # ############################################################ # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions(self, agentIndex=0): """ Retorna las acciones legales del agente especificado """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman está en movimiento return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Retorna el estado sucesor después de que el agente especificado realiza la acción """ # Verifica que los sucesores existen if self.isWin() or self.isLose(): raise Exception( 'No se puede generar un sucesor de un estado terminal') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Genera el estado sucesor después del movimiento de Pacman especificado """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Regresa un objeto AgentState para Pacman (en game.py) state.pos gives da la posición actual state.direction da la dirección del vector de viaje """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return float(self.data.score) def getCapsules(self): """ Retorna una lista de posiciones (x,y) de las capsulas restantes. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Regresa un Grid de variables booleanas que indican el estado de la comida. Los Grids pueden ser accesados utilizando la notación de lista, asi que para verificar si hay comida en (x,y), simplemente llama currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Retorna un Grid de variables booleanas que indican la posición de las paredes. Los Grids pueden ser accesados utilizando la notación de lista, asi que para verificar si hay una pared en (x,y), simplemente llama walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win ##################################################### # Métodos auxiliares: # # No deberías llamar a estos métodos directamente # ##################################################### def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange # Pacman observes neighboring squares state.getObservation() return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def isLose(self): return self.data._lose def isWin(self): return self.data._win ################################ # Additions for Hunters Pacman # ################################ def getObservation(self): x, y = self.getPacmanPosition() adjacent = [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] for x, y in adjacent: self.data.observedPositions[x][y] = True return {(x, y): self.squareInfo(x, y) for x, y in adjacent} def squareInfo(self, x, y): if self.hasWall(x, y): return WALL_STRING elif self.data.layout.redWalls[x][y]: return RED_WALL_STRING elif self.data.layout.blueWalls[x][y]: return BLUE_WALL_STRING else: return EMPTY_SQUARE_STRING def getObservedPositions(self): return self.data.observedPositions def getHouseWalls(self, house): return layout.buildHouseAroundCenter(*house) def getPossibleHouses(self): return layout.pickPossibleLocations(self.data.layout.width, self.data.layout.height) def getEvidence(self): evidence = {} possible = { w for h in self.getPossibleHouses() for w in self.getHouseWalls(h) } for pos in self.getObservedPositions().asList(): if pos in possible: evidence[pos] = self.squareInfo(*pos) return evidence ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.numMoves = prevState.numMoves self.maxMoves = prevState.maxMoves else: # Initial state self.data = GameStateData() self.numMoves = 0 self.maxMoves = -1 def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(str(self)) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) def getGhostPosition(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex] def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y]
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception("Can't generate a successor of a terminal state.") # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange # Pacman observes neighboring squares state.getObservation() return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def isLose(self): return self.data._lose def isWin(self): return self.data._win ################################ # Additions for Hunters Pacman # ################################ def getObservation(self): x, y = self.getPacmanPosition() adjacent = [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] for x, y in adjacent: self.data.observedPositions[x][y] = True return {(x, y): self.squareInfo(x, y) for x, y in adjacent} def squareInfo(self, x, y): if self.hasWall(x, y): return WALL_STRING elif self.data.layout.redWalls[x][y]: return RED_WALL_STRING elif self.data.layout.blueWalls[x][y]: return BLUE_WALL_STRING else: return EMPTY_SQUARE_STRING def getObservedPositions(self): return self.data.observedPositions def getHouseWalls(self, house): return layout.buildHouseAroundCenter(*house) def getPossibleHouses(self): return layout.pickPossibleLocations(self.data.layout.width, self.data.layout.height) def getEvidence(self): evidence = {} possible = {w for h in self.getPossibleHouses() for w in self.getHouseWalls(h)} for pos in self.getObservedPositions().asList(): if pos in possible: evidence[pos] = self.squareInfo(*pos) return evidence ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.numMoves = prevState.numMoves self.maxMoves = prevState.maxMoves else: # Initial state self.data = GameStateData() self.numMoves = 0 self.maxMoves = -1 def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(str(self)) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) def getGhostPosition(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex] def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y]
class GameState: """A GameState specifies the full game state. Full game state includes the food, capsules, agent configurations, and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. """ #################################################### # Accessor methods: use these to access state data # #################################################### def get_legal_actions(self, agent_index=0): """Return the legal actions for the agent specified.""" return AgentRules.get_legal_actions(self, agent_index) def generate_successor(self, agent_index, action): """Return the successor after the specified agent takes the action. Returns a GameState object """ # Copy current state state = GameState(self) # Find appropriate rules for the agent AgentRules.apply_action(state, action, agent_index) AgentRules.check_death(state, agent_index) AgentRules.decrement_timer(state.data.agent_states[agent_index]) # Book keeping state.data._agent_moved = agent_index state.data.score += state.data.score_change state.data.timeleft = self.data.timeleft - 1 return state def get_agent_state(self, index): """Return the state (game.AgentState) of agent with given index.""" return self.data.agent_states[index] def get_agent_position(self, index): """Attempt to get agent position. Return a location tuple if the agent with the given index is observable; if the agent is unobservable, return None. """ agent_state = self.data.agent_states[index] ret = agent_state.get_position() if ret: return tuple(int(x) for x in ret) return ret def get_num_agents(self): """Return number of agents.""" return len(self.data.agent_states) def get_score(self): """Return a number corresponding to the current score.""" return self.data.score def get_red_food(self): """Return a matrix of food on the red team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to red (meaning red is protecting it, blue is trying to eat it). """ return half_grid(self.data.food, red=True) def get_blue_food(self): """Return a matrix of food on the blue team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to blue (meaning blue is protecting it, red is trying to eat it). """ return half_grid(self.data.food, red=False) def get_red_capsules(self): """Return list of capsule positions (x,y) on the red team's side.""" return half_list(self.data.capsules, self.data.food, red=True) def get_blue_capsules(self): """Retun list of capsule positions (x,y) on the blue team's side.""" return half_list(self.data.capsules, self.data.food, red=False) def get_walls(self): """Return a matrix of wall positions.""" return self.data.layout.walls def has_food(self, x, y): """Return true if the location (x,y) has food. This is regardless of whether it's blue team food or red team food. """ return self.data.food[x][y] def has_wall(self, x, y): """Return true if (x,y) has a wall, false otherwise.""" return self.data.layout.walls[x][y] def is_over(self): """Return if game is over.""" return self.data._win def get_red_team_indices(self): """Return a list of agent indices for the agents on the red team.""" return self.red_team[:] def get_blue_team_indices(self): """Return a list of agent indices for the agents on the blue team.""" return self.blue_team[:] def is_on_red_team(self, agent_index): """Return true if the agent is on the red team.""" return self.teams[agent_index] def get_agent_distances(self): """Return a noisy distance to each agent.""" if 'agent_distances' in dir(self): return self.agent_distances else: return None def get_distance_prob(self, true_distance, noisy_distance): """Return the probability of noisy_distance given true_distance.""" if noisy_distance - true_distance in SONAR_NOISE_VALUES: return 1.0 / SONAR_NOISE_RANGE else: return 0 def get_initial_agent_position(self, agent_index): """Return the initial position of an agent.""" return self.data.layout.agent_positions[agent_index][1] def get_capsules(self): """Return a list of positions (x,y) of the remaining capsules.""" return self.data.capsules ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prev_state=None): """Generate a new state by copying information from its predecessor.""" if prev_state is not None: # Initial state self.data = GameStateData(prev_state.data) self.blue_team = prev_state.blue_team self.red_team = prev_state.red_team self.data.timeleft = prev_state.data.timeleft self.teams = prev_state.teams self.agent_distances = prev_state.agent_distances else: self.data = GameStateData() self.agent_distances = [] def deep_copy(self): """Return a deep copy of this state.""" state = GameState(self) state.data = self.data.deep_copy() state.data.timeleft = self.data.timeleft state.blue_team = self.blue_team[:] state.red_team = self.red_team[:] state.teams = self.teams[:] state.agent_distances = self.agent_distances[:] return state def make_observation(self, index): """Make observation for agent with given index. Return a GameState instance. """ state = self.deep_copy() # Adds the sonar signal pos = state.get_agent_position(index) n = state.get_num_agents() distances = [ noisy_distance(pos, state.get_agent_position(i)) for i in range(n) ] state.agent_distances = distances # Remove states of distant opponents if index in self.blue_team: team = self.blue_team other_team = self.red_team else: other_team = self.blue_team team = self.red_team for enemy in other_team: seen = False enemy_pos = state.get_agent_position(enemy) for teammate in team: if ((util.manhattan_distance( enemy_pos, state.get_agent_position(teammate)) <= SIGHT_RANGE)): seen = True if not seen: state.data.agent_states[enemy].configuration = None return state def __eq__(self, other): """Allow two states to be compared.""" if other is None: return False return self.data == other.data def __hash__(self): """Allow states to be keys of dictionaries.""" return int(hash(self.data)) def __str__(self): """Allow conversion to string.""" return str(self.data) def initialize(self, layout, num_agents): """Create an initial game state from a layout array (see layout.py).""" self.data.initialize(layout, num_agents) positions = [a.configuration for a in self.data.agent_states] self.blue_team = [ i for i, p in enumerate(positions) if not self.is_red(p) ] self.red_team = [i for i, p in enumerate(positions) if self.is_red(p)] self.teams = [self.is_red(p) for p in positions] # This is usually 60 (always 60 with random maps) # However, if layout map is specified otherwise, it could be less global TOTAL_FOOD TOTAL_FOOD = layout.total_food def is_red(self, config_or_pos): """Determine if position in on the red side of the board.""" width = self.data.layout.width if isinstance(config_or_pos, tuple): return config_or_pos[0] < width / 2 else: return config_or_pos.position[0] < width / 2
class GameState(object): """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### ghostDirections = {} def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Check food eaten GhostRules.checkFoodEaten ( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange p = state.getPacmanPosition() state.data.ghostDistances = [getNoisyDistance(p, state.getGhostPosition(i)) for i in range(1,state.getNumAgents())] state.ghostPositions = self.ghostPositions = [self.getGhostPosition(i) for i in range(1, self.getNumAgents())] a = 0 for i in range(1, self.getNumAgents()): self.ghostDirections[a] = (state.data.agentStates[i].configuration.getDirection()) a += 1 if agentIndex == self.getNumAgents() - 1: state.numMoves += 1 return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getNumAgents( self ): try: return len( self.getGhostPositions() )+1 except: return len( self.data.agentStates ) def getScore( self ): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] ############################## # Additions for Busters Pacman # ############################## def getLivingGhosts(self): """ Returns a list of booleans indicating which ghosts are not yet captured. The first entry (a placeholder for Pacman's index) is always False. """ return self.livingGhosts def getDistanceNearestFood(self): """ Returns the distance to the nearest food """ if(self.getNumFood() > 0): minDistance = 900000 pacmanPosition = self.getPacmanPosition() for i in range(self.data.layout.width): for j in range(self.data.layout.height): if self.hasFood(i, j): foodPosition = i, j distance = util.manhattanDistance(pacmanPosition, foodPosition) if distance < minDistance: minDistance = distance return minDistance else: return None def getDistanceNearestGhost(self, x, y): positions = self.getGhostPositions() livingGhosts = self.getLivingGhosts()[1:] # Remove Pacman from list of ghosts ghostCount = [] ghostPositions = [] for i in range(len(positions)): # Store only the ghosts marked as True and their positions in the above lists if livingGhosts[i] == True: ghostCount.append(livingGhosts[i]) ghostPositions.append(positions[i]) if len(ghostCount) > 0: # Check that there are still some ghosts alive objectPosition = (x, y) distances = [] for j in ghostPositions: ghostPosition = j[0],j[1] distance = util.manhattanDistance(objectPosition, ghostPosition) if distance >= 0: distances.append(distance) minDistance = min(distances) closestGhost = ghostPositions[distances.index(minDistance)] else: minDistance = math.inf closestGhost = (-1,-1) return minDistance, closestGhost def getGhostPositions(self): return self.ghostPositions def getGhostDirections(self): return self.ghostDirections def setGhostNotLiving(self, index): self.livingGhosts[index] = False def isLose( self ): return self.maxMoves > 0 and self.numMoves >= self.maxMoves def isWin( self ): return self.livingGhosts.count(True) == 0 def getNoisyGhostDistances(self): """ Returns a noisy distance to each ghost. """ return self.data.ghostDistances ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = prevState.data.deepCopy() self.livingGhosts = prevState.livingGhosts[:] self.ghostPositions = prevState.ghostPositions[:] self.numMoves = prevState.numMoves; self.maxMoves = prevState.maxMoves; else: # Initial state self.data = GameStateData() self.numMoves = 0; self.maxMoves = -1; self.data.ghostDistances = [] def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() state.data.ghostDistances = self.data.ghostDistances return state def __eq__( self, other ): """ Allows two states to be compared. """ if (other): return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( str( self ) ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) self.data.ghostDistances = [getNoisyDistance(self.getPacmanPosition(), self.getGhostPosition(i)) for i in range(1, self.getNumAgents())] self.ghostPositions = [self.getGhostPosition(i) for i in range(1, self.getNumAgents())] self.livingGhosts = [False] + [True for i in range(len(self.getGhostPositions()))] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostDirection( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostDirection") return self.data.agentStates[agentIndex].getDirection() def getGhostState( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex]
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ return AgentRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Copy current state state = GameState(self) # Find appropriate rules for the agent AgentRules.applyAction( state, action, agentIndex ) AgentRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getAgentState(self, index): return self.data.agentStates[index] def getAgentPosition(self, index): " Note: this could return None if the agent is unobservable " agentState = self.data.agentStates[index] return agentState.getPosition() def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getRedFood(self): return halfGrid(self.data.food, red = True) def getBlueFood(self): return halfGrid(self.data.food, red = False) def getWalls(self): """ Just like getFood but for walls """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isOver( self ): return self.data._win def getRedTeamIndices(self): return self.redTeam[:] def getBlueTeamIndices(self): return self.blueTeam[:] def isOnRedTeam(self, agentIndex): return self.teams[agentIndex] def getAgentDistances(self): "Returns a noisy distance to each agent." if 'agentDistances' in dir(self) : return self.agentDistances else: return None ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.blueTeam = prevState.blueTeam self.redTeam = prevState.redTeam self.teams = prevState.teams self.agentDistances = prevState.agentDistances else: self.data = GameStateData() self.agentDistances = [] def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() state.blueTeam = self.blueTeam[:] state.redTeam = self.redTeam[:] state.teams = self.teams[:] state.agentDistances = self.agentDistances[:] return state def makeObservation(self, index): state = self.deepCopy() # Adds the sonar signal pos = state.getAgentPosition(index) n = state.getNumAgents() distances = [noisyDistance(pos, state.getAgentPosition(i)) for i in range(n)] state.agentDistances = distances # Remove states of distant opponents if index in self.blueTeam: team = self.blueTeam otherTeam = self.redTeam else: otherTeam = self.blueTeam team = self.redTeam for enemy in otherTeam: seen = False enemyPos = state.getAgentPosition(enemy) for teammate in team: if util.manhattanDistance(enemyPos, state.getAgentPosition(teammate)) <= SIGHT_RANGE: seen = True if not seen: state.data.agentStates[enemy].configuration = None return state def __eq__( self, other ): """ Allows two states to be compared. """ if other == None: return False return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return int(hash( self.data )) def __str__( self ): return str(self.data) def initialize( self, layout, numAgents): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents) positions = [a.configuration for a in self.data.agentStates] self.blueTeam = [i for i,p in enumerate(positions) if not self.isRed(p)] self.redTeam = [i for i,p in enumerate(positions) if self.isRed(p)] self.teams = [self.isRed(p) for p in positions] def isRed(self, configOrPos): width = self.data.layout.width if type(configOrPos) == type( (0,0) ): return configOrPos[0] < width / 2 else: return configOrPos.pos[0] < width / 2
class GameState: """A GameState specifies the full game state. Full game state includes the food, capsules, agent configurations, and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had get_legal_actions # called explored = set() @staticmethod def get_and_reset_explored(): """Return a copy of the explored set and reset the original.""" tmp = GameState.explored.copy() GameState.explored = set() return tmp def get_legal_actions(self, agent_index=0): """Return the legal actions for the agent specified.""" if self.is_win() or self.is_lose(): return [] if agent_index == 0: # Pacman is moving return PacmanRules.get_legal_actions(self) else: return GhostRules.get_legal_actions(self, agent_index) def generate_successor(self, agent_index, action): """Generate the successor state after specified agent takes action.""" # Check that successors exist if self.is_win() or self.is_lose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agent_index == 0: # Pacman is moving state.data._eaten = [False for i in range(state.get_num_agents())] PacmanRules.apply_action(state, action) else: # A ghost is moving GhostRules.apply_action(state, action, agent_index) # Time passes if agent_index == 0: # Penalty for waiting around state.data.score_change += -TIME_PENALTY else: GhostRules.decrement_timer(state.data.agent_states[agent_index]) # Resolve multi-agent effects GhostRules.check_death(state, agent_index) # Book keeping state.data._agent_moved = agent_index state.data.score += state.data.score_change GameState.explored.add(self) GameState.explored.add(state) return state def get_legal_pacman_actions(self): """Return legal actions for pacman (convience function). (convenience function that calls get_legal_actions with pacman's index) """ return self.get_legal_actions(0) def generate_pacman_successor(self, action): """Generate successor state after the specified pacman action. (convenience function that calls generate_successor with pacman's index) """ return self.generate_successor(0, action) def get_pacman_state(self): """Return an game.AgentState object for pacman. state.position gives the current position state.direction gives the travel vector """ return self.data.agent_states[0].copy() def get_pacman_position(self): """Return pacman's position.""" return self.data.agent_states[0].get_position() def get_ghost_states(self): """Return list of game.AgentState objects for all ghosts.""" return self.data.agent_states[1:] def get_ghost_state(self, agent_index): """Return game.AgentState object for specified ghost. :raise IndexError if invalid agent_index. """ if agent_index == 0 or agent_index >= self.get_num_agents(): raise IndexError("Invalid index passed to get_ghost_state") return self.data.agent_states[agent_index] def get_ghost_position(self, agent_index): """Return position of specified ghost. :raise IndexError if invalid agent_index. """ return self.get_ghost_state(agent_index).get_position() def get_ghost_positions(self): """Return list of ghost positions for all ghosts.""" return [s.get_position() for s in self.get_ghost_states()] def get_num_agents(self): """Return total number of agents (pacman + ghosts).""" return len(self.data.agent_states) def get_score(self): """Return score of the state.""" return float(self.data.score) def get_capsules(self): """Return a list of positions (x,y) of the remaining capsules.""" return self.data.capsules def get_num_food(self): """Return number of food pellets in the game state.""" return self.data.food.count() def get_food(self): """Return a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call current_food = state.get_food() if current_food[x][y] == True: ... """ return self.data.food def get_walls(self): """Return a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.get_walls() if walls[x][y] == True: ... """ return self.data.layout.walls def has_food(self, x, y): """Return whether there is food at (x,y).""" return self.data.food[x][y] def has_wall(self, x, y): """Return whether there is a wall at (x,y).""" return self.data.layout.walls[x][y] def is_lose(self): """Return if the state is a losing one.""" return self.data._lose def is_win(self): """Return if the state is a winning one.""" return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prev_state=None): """Generate a new state by copying information from its predecessor.""" if prev_state is not None: # Initial state self.data = GameStateData(prev_state.data) else: self.data = GameStateData() def deep_copy(self): """Return a deep copy of the state.""" state = GameState(self) state.data = self.data.deep_copy() return state def __eq__(self, other): """Allow two states to be compared.""" return hasattr(other, 'data') and self.data == other.data def __hash__(self): """Allow states to be keys of dictionaries / set entries.""" return hash(self.data) def __str__(self): """Return string representation of state.""" return str(self.data) def initialize(self, layout, num_ghost_agents=1000): """Create an initial game state from a layout array (see layout.py).""" self.data.initialize(layout, num_ghost_agents)
def __init__(self, prev_state=None): """Generate a new state by copying information from its predecessor.""" if prev_state is not None: # Initial state self.data = GameStateData(prev_state.data) else: self.data = GameStateData()
class GameState: """ A GameState specifies the full game state, including the food, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that Green House Worker is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return GHworkerRules.getLegalActions( self ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] GHworkerRules.applyAction( state, action ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getLegalGHworkerActions( self ): return self.getLegalActions( 0 ) def generateGHworkerSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getGHworkerState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getGHworkerPosition( self ): return self.data.agentStates[0].getPosition() def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getNumFood( self ): print "getNumFood" print self.data.food return self.data.food.count() def getNumGHs( self ): print "getNumGHs" print self.data._foodEaten return self.data._foodEaten.count() def getFood(self): """ Returns a Grid of boolean food indicator variables (strawberries!) Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose( self ): return self.data._lose def isWin( self ): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( self.data ) def __str__( self ): return str(self.data) def initialize( self, layout): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout)
class GameState: def getLegalActions(self, agentIndex=0): if self.isWin() or self.isLose(): return [] if agentIndex == 0: return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): if self.isWin() or self.isLose(): raise Exception("Can\'t generate a successor of a terminal state.") state = GameState(self) if agentIndex == 0: state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: GhostRules.applyAction(state, action, agentIndex) if agentIndex == 0: state.data.scoreChange += 0 else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) GhostRules.checkDeath(state, agentIndex) state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): return self.generateSuccessor(0, action) def getPacmanState(self): return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise BaseException("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise BaseException("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def getCapsules(self): return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): return self.data.food def getWalls(self): return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win def __init__(self, prevState=None): if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): if other == None: return self.data == other return self.data == other.data def __hash__(self): return hash(str(self)) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): self.data.initialize(layout, numGhostAgents)
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(self): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) ghostPosition = state.getGhostPosition(agentIndex) state.explorePositionSurrounding(ghostPosition, agentIndex) state.data.ghostDistances[agentIndex - 1] = self.getGhostDistance(agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) #print(self.getBestSectors(5)) # Book keeping state.data._agentMoved = agentIndex state.visibleData._agentMoved = agentIndex state.visibleData.fitness += self.fitnessfunction() state.data.score += state.data.scoreChange state.visibleData.score += state.data.scoreChange state.data.fitness += self.fitnessfunction() state.data.fitnessChange += self.fitnessfunction() state.visibleData.previousExploredCount = self.countExploredTiles() GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getPacmanPositionIfVisible(self): pacman_position = self.getPacmanPosition() for ghost_position in self.getGhostPositions(): withinXBound = ghost_position[0] - self.ghostVisionDistance <= pacman_position[0] and\ ghost_position[0] + self.ghostVisionDistance >= pacman_position[0] withinYBound = ghost_position[1] - self.ghostVisionDistance <= pacman_position[1] and\ ghost_position[1] + self.ghostVisionDistance >= pacman_position[1] withinBound = withinXBound and withinYBound if withinBound: return self.getPacmanPosition() return None def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def fitnessfunction(self): sightingPoint = 1000 points = 0 maxTime = self.data.maxTime currentTime = self.data.timeRemaining timeRatio = currentTime / maxTime if self.getPacmanPositionIfVisible() != None: points += sightingPoint * timeRatio #exploration points explorationPoint = 10 * timeRatio ##fix unexplored count points += explorationPoint * (self.countExploredTiles() - self.visibleData.previousExploredCount) return points def countExploredTiles(self): j = 0 for i in range(len(self.visibleData.unexplored)): if self.visibleData.unexplored[i] == False: j += 1 return j def getGhostDistance(self, ghost_index): pacman_position = self.getPacmanPosition() ghost_position = self.data.agentStates[ghost_index].getPosition() ghostDistance = manhattanDistance(pacman_position, ghost_position) return ghostDistance def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return float(self.data.score) def getFitness(self): return self.data.fitness def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def getBestSectors(self, topCount): maxTime = self.data.maxTime currentTime = self.data.timeRemaining timeRatio = currentTime / maxTime * 1.0 rowCount = self.data.sectorrow colCount = self.data.sectorcol PacManPoint = 2 * (1 - (timeRatio + 0.1)) print(timeRatio) print(PacManPoint) ghostPoint = -2 if topCount > rowCount * colCount: topCount = rowCount * colCount y = self.getWalls().__getheight__() x = self.getWalls().__getwidth__() sectorWidth = math.floor(x / colCount) sectorHeight = math.floor(y / rowCount) #how you compute a sector number sector = (math.floor(x / sectorWidth) + 1) * (math.floor(y / sectorHeight) + 1) #now count where each ghost, pac is #pacman pacX, pacY = self.data.agentStates[0].getPosition() pacSector, pacMinSec, pacMaxSec = self.getSectorPosition( pacX, pacY, sectorWidth, sectorHeight) ghostSector = {} sectorScore = {} sectorMax = {} sectorMin = {} sectorCount = 0 for row in range(rowCount): for col in range(colCount): #min x_min = col * sectorWidth y_min = row * sectorHeight x_max = x_min + sectorWidth y_max = y_min + sectorHeight if y_max >= y: y_max = y - 1 if x_max >= x: x_max = x - 1 sectorMin[sectorCount + 1] = (x_min, y_min) sectorMax[sectorCount + 1] = (x_max, y_max) sectorCount += 1 #populate based on sector count for i in range(rowCount * colCount): ghostSector[i + 1] = 0 sectorScore[i + 1] = 0 #get sector per ghosts for ghost in range(len(self.data.agentStates) - 1): gX, gY = self.data.agentStates[ghost + 1].getPosition() sector, sec_min, sec_max = self.getSectorPosition( gX, gY, sectorWidth, sectorHeight) ghostSector[sector] += 1 sectorScore[pacSector] += PacManPoint queue = [] for i in range(colCount * rowCount): sectorScore[i + 1] += ghostSector[i + 1] * ghostPoint if i + 1 != pacSector: sectorScore[i + 1] += 0.5 * random.random() queue.append((sectorScore[i + 1], i + 1)) topSectorsPairs = sorted(queue, key=lambda tup: tup[0], reverse=True) topSectors = [] for i in range(len(topSectorsPairs)): topSectors.append(topSectorsPairs[i][1]) topSubSectors = [] for s in range(topCount): topSubSectors.append(topSectors[s]) topSectors = topSubSectors return topSectors, [sectorMin[i] for i in topSectors ], [sectorMax[i] for i in topSectors] def getSectorPosition(self, x, y, sectorWidth, sectorHeight): xcal = math.floor(x / sectorWidth) ycal = math.floor(y / sectorHeight) if xcal == math.floor(x / sectorWidth) and xcal > 1: xcal -= 1 if ycal == math.floor(y / sectorHeight) and ycal > 1: ycal -= 1 return (xcal + 1) * (ycal + 1), (xcal, ycal), (xcal + sectorWidth, ycal + sectorHeight) def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win "adding method to help figure out how much ghost has explored" def getExplored(self): #print(type(self.visibleData.layout.unexplored)) return self.visibleData.layout.unexplored def getToExplore(self): return self.visibleData.layout.unexploredAccessible def resetExplored(self): self.visibleData.layout.resUn() ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.visibleData = GameStateData(prevState.visibleData) self.visibleData.agentStates = self.data.agentStates self.visibleData.ghostDistances = self.data.ghostDistances self.ghostVisionDistance = prevState.ghostVisionDistance else: self.data = GameStateData() self.visibleData = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data and \ hasattr(other, 'visibleData') and self.visibleData == other.visibleData def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, fullLayout, ghostVisionDistance, numGhostAgents=1000, sectorrow=2, sectorcol=3): """ Creates an initial game state from a layout array (see layout.py). """ self.ghostVisionDistance = ghostVisionDistance self.data.initialize(fullLayout, numGhostAgents, sectorrow, sectorcol) self.visibleData.initialize(fullLayout, numGhostAgents, sectorrow, sectorcol) def explorePositionSurrounding(self, position, agentIndex): orig_x, orig_y = position width = self.visibleData.layout.width height = self.visibleData.layout.height min_x = int(max(0, orig_x - self.ghostVisionDistance)) max_x = int(min(width, orig_x + self.ghostVisionDistance + 1)) min_y = int(max(0, orig_y - self.ghostVisionDistance)) max_y = int(min(height, orig_y + self.ghostVisionDistance + 1)) brmin_x = (min_x > 0) brmax_x = (max_x + 1 < width) brmin_y = (min_y > 0) brmax_y = (max_y + 1 < height) self.visibleData._visited = self.visibleData._visible[agentIndex - 1] self.visibleData._visible[agentIndex - 1] = [] for y in range(min_y, max_y): borderymin = (brmin_y and y == min_y) borderymax = (brmax_y and y == max_y - 1) for x in range(min_x, max_x): self.visibleData.food[x][y] = self.data.food[x][y] if (x, y) in self.data.capsules and ( x, y) not in self.visibleData.capsules: self.visibleData.capsules.append((x, y)) elif (x, y) in self.visibleData.capsules and ( x, y) not in self.data.capsules: self.visibleData.capsules.remove((x, y)) self.visibleData.layout.walls[x][y] = self.data.layout.walls[ x][y] self.visibleData.layout.food[x][y] = self.data.layout.food[x][ y] self.visibleData.layout.unexplored[x][y] = False self.visibleData.layout.unexploredRepeat[x][y] = False self.visibleData.layout.unexploredAccessible[x][y] = False #if self.visibleData.unexplored[x][y]: self.visibleData._visible[agentIndex - 1].append((x, y)) if (x, y) in self.visibleData._visited: self.visibleData._visited.remove((x, y)) if (brmin_x and x == min_x and self.visibleData.layout.unexploredRepeat[x - 1][y] and not (self.visibleData.layout.walls[x][y])): self.visibleData.layout.unexploredAccessible[x - 1][y] = True if (brmax_x and x == max_x - 1 and self.visibleData.layout.unexploredRepeat[x + 1][y] and not (self.visibleData.layout.walls[x][y])): self.visibleData.layout.unexploredAccessible[x + 1][y] = True if (borderymin and self.visibleData.layout.unexploredRepeat[x][y - 1] and not (self.visibleData.layout.walls[x][y])): self.visibleData.layout.unexploredAccessible[x][y - 1] = True if (borderymax and self.visibleData.layout.unexploredRepeat[x][y + 1] and not (self.visibleData.layout.walls[x][y])): self.visibleData.layout.unexploredAccessible[x][y + 1] = True
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ return AgentRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state (a GameState object) after the specified agent takes the action. """ # Copy current state state = GameState(self) # Find appropriate rules for the agent AgentRules.applyAction(state, action, agentIndex) AgentRules.checkDeath(state, agentIndex) AgentRules.decrementTimer(state.data.agentStates[agentIndex]) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange state.data.timeleft = self.data.timeleft - 1 return state def getAgentState(self, index): return self.data.agentStates[index] def getAgentPosition(self, index): """ Returns a location tuple if the agent with the given index is observable; if the agent is unobservable, returns None. """ agentState = self.data.agentStates[index] ret = agentState.getPosition() if ret: return tuple(int(x) for x in ret) return ret def getNumAgents(self): return len(self.data.agentStates) def getScore(self): """ Returns a number corresponding to the current score. """ return self.data.score def getRedFood(self): """ Returns a matrix of food that corresponds to the food on the red team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to red (meaning red is protecting it, blue is trying to eat it). """ return halfGrid(self.data.food, red=True) def getBlueFood(self): """ Returns a matrix of food that corresponds to the food on the blue team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to blue (meaning blue is protecting it, red is trying to eat it). """ return halfGrid(self.data.food, red=False) def getRedCapsules(self): return halfList(self.data.capsules, self.data.food, red=True) def getBlueCapsules(self): return halfList(self.data.capsules, self.data.food, red=False) def getWalls(self): """ Just like getFood but for walls """ return self.data.layout.walls def hasFood(self, x, y): """ Returns true if the location (x,y) has food, regardless of whether it's blue team food or red team food. """ return self.data.food[x][y] def hasWall(self, x, y): """ Returns true if (x,y) has a wall, false otherwise. """ return self.data.layout.walls[x][y] def isOver(self): return self.data._win def getRedTeamIndices(self): """ Returns a list of agent index numbers for the agents on the red team. """ return self.redTeam[:] def getBlueTeamIndices(self): """ Returns a list of the agent index numbers for the agents on the blue team. """ return self.blueTeam[:] def isOnRedTeam(self, agentIndex): """ Returns true if the agent with the given agentIndex is on the red team. """ return self.teams[agentIndex] def getAgentDistances(self): """ Returns a noisy distance to each agent. """ if 'agentDistances' in dir(self) : return self.agentDistances else: return None def getDistanceProb(self, trueDistance, noisyDistance): "Returns the probability of a noisy distance given the true distance" if noisyDistance - trueDistance in SONAR_NOISE_VALUES: return 1.0 / SONAR_NOISE_RANGE else: return 0 def getInitialAgentPosition(self, agentIndex): "Returns the initial position of an agent." return self.data.layout.agentPositions[agentIndex][1] def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.blueTeam = prevState.blueTeam self.redTeam = prevState.redTeam self.data.timeleft = prevState.data.timeleft self.teams = prevState.teams self.agentDistances = prevState.agentDistances else: self.data = GameStateData() self.agentDistances = [] def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() state.data.timeleft = self.data.timeleft state.blueTeam = self.blueTeam[:] state.redTeam = self.redTeam[:] state.teams = self.teams[:] state.agentDistances = self.agentDistances[:] return state def makeObservation(self, index): state = self.deepCopy() # Adds the sonar signal pos = state.getAgentPosition(index) n = state.getNumAgents() distances = [noisyDistance(pos, state.getAgentPosition(i)) for i in range(n)] state.agentDistances = distances # Remove states of distant opponents if index in self.blueTeam: team = self.blueTeam otherTeam = self.redTeam else: otherTeam = self.blueTeam team = self.redTeam for enemy in otherTeam: seen = False enemyPos = state.getAgentPosition(enemy) for teammate in team: if util.manhattanDistance(enemyPos, state.getAgentPosition(teammate)) <= SIGHT_RANGE: seen = True if not seen: state.data.agentStates[enemy].configuration = None return state def __eq__(self, other): """ Allows two states to be compared. """ if other == None: return False return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return int(hash(self.data)) def __str__(self): return str(self.data) def initialize(self, layout, numAgents): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents) positions = [a.configuration for a in self.data.agentStates] self.blueTeam = [i for i, p in enumerate(positions) if not self.isRed(p)] self.redTeam = [i for i, p in enumerate(positions) if self.isRed(p)] self.teams = [self.isRed(p) for p in positions] def isRed(self, configOrPos): width = self.data.layout.width if type(configOrPos) == type((0, 0)): return configOrPos[0] < width / 2 else: return configOrPos.pos[0] < width / 2
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange p = state.getPacmanPosition() state.data.ghostDistances = [getNoisyDistance(p, state.getGhostPosition(i)) for i in range(1,state.getNumAgents())] if agentIndex == self.getNumAgents() - 1: state.numMoves += 1 return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def setAgentPosition(self, pos, agentIndex): self.data.agentStates[agentIndex].setPosition(pos) def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] ############################## # Additions for Busters Pacman # ############################## def getLivingGhosts(self): """ Returns a list of booleans indicating which ghosts are not yet captured. The first entry (a placeholder for Pacman's index) is always False. """ return self.livingGhosts def setGhostNotLiving(self, index): self.livingGhosts[index] = False def isLose( self ): return self.data._lose def isWin( self ): return self.data._win """ def isLose( self ): return self.maxMoves > 0 and self.numMoves >= self.maxMoves def isWin( self ): return self.livingGhosts.count(True) == 0 """ def getNoisyGhostDistances(self): """ Returns a noisy distance to each ghost. """ return self.data.ghostDistances def generateStateObservationReward(self, agent, ghostAgents, action): gameStateCopy = self.deepCopy() nextGameState = gameStateCopy.generateSuccessor(0, action) for (i, ghostAgent) in zip(range(1, len(ghostAgents)+1), ghostAgents): if nextGameState.isWin() or nextGameState.isLose(): break nextGameState = nextGameState.generateSuccessor( i, ghostAgent.getAction(nextGameState)) reward = nextGameState.getScore() - self.getScore() if agent.obsOnStopOnly: if action == Directions.STOP: observation = nextGameState.getNoisyGhostDistances() else: observation = [None] else: observation = nextGameState.getNoisyGhostDistances() return (nextGameState, tuple(observation), reward) ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] self.numMoves = prevState.numMoves; self.maxMoves = prevState.maxMoves; else: # Initial state self.data = GameStateData() self.numMoves = 0; self.maxMoves = -1; self.data.ghostDistances = [] def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() state.data.ghostDistances = self.data.ghostDistances return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( str( self ) ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) self.livingGhosts = [False] + [True for i in range(numGhostAgents)] self.data.ghostDistances = [getNoisyDistance(self.getPacmanPosition(), self.getGhostPosition(i)) for i in range(1, self.getNumAgents())] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex]
class GameState: """ A 【GameState】 specifies the full game state, including the food, capsules胶囊, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a 【GameStateData】 object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions( self, agentIndex=0 ): """ 传入agentIndex,返回其当前可选actions Returns the legal actions for the agent specified. """ return AgentRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ 返回某个agent,在当前gameState,执行完某个action, 之后的gameState(是个整个地图的网格数据,当敌方在范围5内,会显示对方 ) Returns the successor state (a GameState object) after the specified agent takes the action. """ # Copy current state state = GameState(self) # Find appropriate rules for the agent通过游戏规则更新gamestate AgentRules.applyAction( state, action, agentIndex ) AgentRules.checkDeath(state, agentIndex) AgentRules.decrementTimer(state.data.agentStates[agentIndex]) # Book keeping 记账,更新“谁移动过”,“当前分数”,“所剩时间” state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange state.data.timeleft = self.data.timeleft - 1 return state def getAgentState(self, index): """ 传入一个agentIndex,返回其当前状态。若不在范围内,会返回身份,但其余值为none。 格式为:当前身份,位置,运动方向 Ghost: (x,y)=(9.0, 13.0), East Ghost: None Pacman: (x,y)=(19.0, 6.0), West """ return self.data.agentStates[index] def getAgentPosition(self, index): """ 如果当前传入的agent是可见的,那么返回其坐标(元组);不可见,返回None Returns a location tuple if the agent with the given index is observable; if the agent is unobservable, returns None. """ agentState = self.data.agentStates[index] ret = agentState.getPosition() if ret: return tuple(int(x) for x in ret) return ret def getNumAgents( self ): """ 返回当前游戏中agent的数量,值一直是固定的4 """ return len( self.data.agentStates ) def getScore( self ): """ 返回当前己方的分数? Returns a number corresponding to the current score. """ return self.data.score def getRedFood(self): """ 返回红方要保护的豆,矩阵,里面全是T/F Returns a matrix of food that corresponds to the food on the red team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to red (meaning red is protecting it, blue is trying to eat it). """ return halfGrid(self.data.food, red = True) def getBlueFood(self): """ 返回蓝方要保护的豆,矩阵,里面全是T/F Returns a matrix of food that corresponds to the food on the blue team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to blue (meaning blue is protecting it, red is trying to eat it). """ return halfGrid(self.data.food, red = False) def getRedCapsules(self): """ 返回红方要保护的“变身胶囊” """ return halfList(self.data.capsules, self.data.food, red = True) def getBlueCapsules(self): """ 返回蓝方要保护的“变身胶囊” """ return halfList(self.data.capsules, self.data.food, red = False) def getWalls(self): """ 返回一个“墙”矩阵,全是T/F Just like getFood but for walls """ return self.data.layout.walls def hasFood(self, x, y): """ 判断具体某个点是否有食物,有ture,无false Returns true if the location (x,y) has food, regardless of whether it's blue team food or red team food. """ return self.data.food[x][y] def hasWall(self, x, y): """ 判断具体某个点是否有“墙” Returns true if (x,y) has a wall, false otherwise. """ return self.data.layout.walls[x][y] def isOver( self ): """ 判断游戏是否结束,有什么用?游戏结束后,这个方法也执行不了。否则一直返回false """ return self.data._win def getRedTeamIndices(self): """ 返回红队的agent索引值,格式为:[0, 2] Returns a list of agent index numbers for the agents on the red team. """ return self.redTeam[:] def getBlueTeamIndices(self): """ Returns a list of the agent index numbers for the agents on the blue team. """ return self.blueTeam[:] def isOnRedTeam(self, agentIndex): """ 判断某个indice是否属于红队 Returns true if the agent with the given agentIndex is on the red team. """ return self.teams[agentIndex] def getAgentDistances(self): """ 返回每个agent到当前agent的粗糙距离list, 格式为:[4, 32, -3, 29],对应着到0-3 agentIndices的距离。 Returns a noisy distance to each agent. """ if 'agentDistances' in dir(self) : return self.agentDistances else: return None def getDistanceProb(self, trueDistance, noisyDistance): """ 传入实际距离和粗糙距离,计算粗糙距离的“可信度”。 怎么用?如果我知道真实距离,我要粗糙距离干什么用? Returns the probability of a noisy distance given the true distance """ if noisyDistance - trueDistance in SONAR_NOISE_VALUES: return 1.0/SONAR_NOISE_RANGE else: return 0 def getInitialAgentPosition(self, agentIndex): """ 根据index获取该agent的出生位置,对于同一个地图,该值是固定的。 Returns the initial position of an agent. """ return self.data.layout.agentPositions[agentIndex][1] def getCapsules(self): """ 返回剩余的“超级胶囊”的坐标list Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.blueTeam = prevState.blueTeam self.redTeam = prevState.redTeam self.data.timeleft = prevState.data.timeleft self.teams = prevState.teams self.agentDistances = prevState.agentDistances else: self.data = GameStateData() self.agentDistances = [] def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() state.data.timeleft = self.data.timeleft state.blueTeam = self.blueTeam[:] state.redTeam = self.redTeam[:] state.teams = self.teams[:] state.agentDistances = self.agentDistances[:] return state def makeObservation(self, index): state = self.deepCopy() # Adds the sonar signal pos = state.getAgentPosition(index) n = state.getNumAgents() distances = [noisyDistance(pos, state.getAgentPosition(i)) for i in range(n)] state.agentDistances = distances # Remove states of distant opponents if index in self.blueTeam: team = self.blueTeam otherTeam = self.redTeam else: otherTeam = self.blueTeam team = self.redTeam for enemy in otherTeam: seen = False enemyPos = state.getAgentPosition(enemy) for teammate in team: if util.manhattanDistance(enemyPos, state.getAgentPosition(teammate)) <= SIGHT_RANGE: seen = True if not seen: state.data.agentStates[enemy].configuration = None return state def __eq__( self, other ): """ Allows two states to be compared. """ if other == None: return False return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return int(hash( self.data )) def __str__( self ): return str(self.data) def initialize( self, layout, numAgents): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents) positions = [a.configuration for a in self.data.agentStates] self.blueTeam = [i for i,p in enumerate(positions) if not self.isRed(p)] self.redTeam = [i for i,p in enumerate(positions) if self.isRed(p)] self.teams = [self.isRed(p) for p in positions] #This is usually 60 (always 60 with random maps) #However, if layout map is specified otherwise, it could be less global TOTAL_FOOD TOTAL_FOOD = layout.totalFood def isRed(self, configOrPos): width = self.data.layout.width if type(configOrPos) == type( (0,0) ): return configOrPos[0] < width / 2 else: return configOrPos.pos[0] < width / 2
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, action ): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose() or self.eventQueue.isEmpty(): raise Exception('Can\'t generate a successor of a terminal state.') time, event = self.eventQueue.peek() assert event.isAgentMove(), 'Can only generate successors of a state where an agent is about to move' state = self.makeAgentMove(action) state.resolveEventsUntilAgentEvent() # Book keeping GameState.explored.add(self.data) GameState.explored.add(state.data) return state def makeAgentMove( self, action ): # Copy current state state = GameState(self) time, event = state.eventQueue.pop() agentIndex = event.getAgentIndex() agentState = state.data.agentStates[agentIndex] state.data.time = time delay = agentState.powers.timestepsBetweenMoves state.registerEventWithDelay(event, delay) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) state.data.scoreChange -= TIME_PENALTY # Penalty for waiting around else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) GhostRules.decrementTimer(agentState) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) if action == Directions.LASER: GhostRules.checkLaserShot(state,agentIndex) if action == Directions.BLAST: GhostRules.checkBlast(state,agentIndex) # Book keeping state.data._agentMoved = agentIndex # Note: It is important that the following value accurately # reflects when Pacman will make the next move, even if the # speed changes (such as a speed-up power pellet). Otherwise # the graphics will do weird things. state.data._timeTillAgentMovesAgain = delay state.data._action = action state.data.score += state.data.scoreChange return state def runEvent( self ): # Check that successors exist if self.eventQueue.isEmpty(): raise Exception('Can\'t run an event of a terminal state.') time, event = self.eventQueue.pop() assert not event.isAgentMove(), 'Can\'t run an AgentMoveEvent' self.data.time = time event.trigger(self) return event def getNextEvent( self ): _, event = self.eventQueue.peek() return event def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getPacmanDirection(self): return self.data.agentStates[0].getDirection() def getGhostStates( self ): return self.data.agentStates[1:] def getGhostState( self, agentIndex ): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNextAgentIndex(self): for time, event in self.eventQueue.getSortedTimesAndEvents(): if event.isAgentMove(): return event.getAgentIndex() assert False, "No more moves can be made" def getAgentMoveTime(self, agentIndex): for time, event in self.eventQueue.getSortedTimesAndEvents(): if event.isAgentMove(): if event.getAgentIndex() == agentIndex: return time assert False, "No more moves can be made by agent " + str(agentIndex) def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return float(self.data.score) def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.walls[x][y] def isLose( self ): return self.data._lose def isWin( self ): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.eventQueue = prevState.eventQueue.deepCopy() else: self.data = GameStateData() self.eventQueue = EventQueue() def resolveEventsUntilAgentEvent(self): # Resolve any events until the next agent event while not self.eventQueue.isEmpty(): time, event = self.eventQueue.peek() if event.isAgentMove(): return else: self.runEvent() def registerEventWithDelay(self, event, delay): self.eventQueue.registerEventAtTime(event, self.data.time + delay) def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() # Event queue has already been copied in the constructor return state def __eq__( self, other ): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data \ and hasattr(other, 'eventQueue') and self.eventQueue == other.eventQueue \ def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( (self.data, self.eventQueue) ) def __str__( self ): return str(self.data) def initialize( self, layout, pacmanPowers, ghostPowers, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, pacmanPowers, ghostPowers, numGhostAgents) numAgents = self.getNumAgents() for i in range(numAgents): self.registerEventWithDelay(AgentMoveEvent(i), i) self.registerEventWithDelay(WallTimerEvent(), 1)
class GameState: explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return float(self.data.score) def getCapsules(self): return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): return self.data.food def getWalls(self): return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win def __init__(self, prevState=None): if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
class CornerMaz: # static variable keeps track of which states have had getLegalActions called explored = set() def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = CornerMaz(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def isLose(self): return self.data._lose def isWin(self): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
class PacmanPlot(PacmanGraphics): def __init__(self, x=None, y=None, zoom=1.0, frameTime=0.0): """ Create and dispaly a pacman plot figure. If both x and y are provided, plot the points (x[i],y[i]) for all i in len(x). This will draw on the existing pacman window (clearing it first) or create a new one if no window exists. x: array or list of N scalar values. Default=None, in which case no points will be plotted y: array or list of N scalar values. Default=None, in which case no points will be plotted """ super(PacmanPlot, self).__init__(zoom, frameTime) if x is None or y is None: width = 23 height = 23 xmin = -(width-1)/2+1 ymin = -(height-1)/2+1 self.initPlot(xmin, ymin, width, height) else: self.plot(x,y) def initPlot(self, xmin, ymin, width, height): if graphicsUtils._canvas is not None: graphicsUtils.clear_screen() # Initialize GameStateData with blank board with axes self.width = width self.height = height self.xShift = -(xmin-1) self.yShift = -(ymin-1) self.line = None self.zoom = min(30.0/self.width, 20.0/self.height) self.gridSize = graphicsDisplay.DEFAULT_GRID_SIZE * self.zoom # fullRow = ['%']*self.width # row = ((self.width-1)/2)*[' '] + ['%'] + ((self.width-1)/2)*[' '] # boardText = ((self.height-1)/2)*[row] + [fullRow] + ((self.height-1)/2)*[row] numSpaces = self.width-1 numSpacesLeft = self.xShift numSpacesRight = numSpaces-numSpacesLeft numRows = self.height numRowsBelow = self.yShift numRowsAbove = numRows-1-numRowsBelow fullRow = ['%']*self.width if numSpacesLeft < 0: row = [' ']*self.width else: row = numSpacesLeft*[' '] + ['%'] + numSpacesRight*[' '] boardText = numRowsAbove*[row] + [fullRow] + numRowsBelow*[row] layout = Layout(boardText) self.blankGameState = GameStateData() self.blankGameState.initialize(layout, 0) self.initialize(self.blankGameState) title = 'Pacman Plot' graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh() def plot(self, x, y, weights=None, title='Pacman Plot'): """ Plot the input values x with their corresponding output values y (either true or predicted). Also, plot the linear regression line if weights are given; assuming h_w(x) = weights[0]*x + weights[1]. This will draw on the existing pacman window (clearing it first) or create a new one if no window exists. x: array or list of N scalar values. y: array or list of N scalar values. weights: array or list of 2 values (or if just one value, the bias weight is assumed to be zero). If None, no line is drawn. Default: None """ if np.array(x).size == 0: return if isinstance(x[0], np.ndarray): # Scrape the first element of each data point x = [data[0] for data in x] xmin = int(math.floor(min(x))) ymin = int(math.floor(min(y))) xmax = int(math.ceil(max(x))) ymax = int(math.ceil(max(y))) width = xmax-xmin+3 height = ymax-ymin+3 self.initPlot(xmin, ymin, width, height) gameState = self.blankGameState.deepCopy() gameState.agentStates = [] # Add ghost at each point for (px,py) in zip(x,y): point = (px+self.xShift, py+self.yShift) gameState.agentStates.append( AgentState( Configuration( point, Directions.STOP), False) ) # self.initialize(gameState) graphicsUtils.clear_screen() self.infoPane = InfoPane(gameState.layout, self.gridSize) self.drawStaticObjects(gameState) self.drawAgentObjects(gameState) graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh() def setWeights(self, weights): pass def takeControl(self): """ Give full control to the window. Blocks current thread. Program will exit when window is closed. """ mainloop()
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return float(self.data.score) def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win ############################################# # Elena methods : # # Metodos anhadidos para funcionalidad q # ############################################# """ FOOD """ def getDistanceNearestFood(self): """ Returns the distance to the nearest food """ if (self.getNumFood() > 0): minDistance = 900000 pacmanPosition = self.getPacmanPosition() for i in range(self.data.layout.width): for j in range(self.data.layout.height): if self.hasFood(i, j): foodPosition = i, j distance = util.manhattanDistance( pacmanPosition, foodPosition) if distance < minDistance: minDistance = distance return minDistance else: return None def getPositionNearestFood(self): if not self.getFoodPositions(): return None else: return min(self.getFoodPositions()) def getFoodPositions(self): """ Elena: returns an array of food positions """ foodPositionArray = [] for i in range(self.data.layout.width): for j in range(self.data.layout.height): if self.hasFood(i, j): foodPositionArray.append((i, j)) return foodPositionArray """ GHOSTS """ def getLivingGhosts(self): """ Returns a list of booleans indicating which ghosts are not yet captured. The first entry (a placeholder for Pacman's index) is always False. """ return self.livingGhosts def getPositionNearestGhost(self): if all(v is None for v in self.data.ghostDistances): return None else: livingGhosts = self.getLivingGhosts() return self.getGhostPositions()[self.data.ghostDistances.index( min(value for value in self.data.ghostDistances if value is not None))] def getGhostDirections(self): return self.ghostDirections def setGhostNotLiving(self, index): self.livingGhosts[index] = False """ WALLS: POR DEFINIR """ ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() return state def __eq__(self, other): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(self.data) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
def __init__(self, prevState=None): if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData()
class PacmanPlotLP(PacmanGraphics): def __init__(self, constraints=[], infeasiblePoints=[], feasiblePoints=[], optimalPoint=None, costVector=None, zoom=1.0, frameTime=0.0): """ Create and dispaly a pacman plot figure. This will draw on the existing pacman window (clearing it first) or create a new one if no window exists. constraints: list of inequality constraints, where each constraint w1*x + w2*y <= b is represented as a tuple ((w1, w2), b) infeasiblePoints (food): list of points where each point is a tuple (x, y) feasiblePoints (power): list of points where each point is a tuple (x, y) optimalPoint (pacman): optimal point as a tuple (x, y) costVector (shading): cost vector represented as a tuple (c1, c2), where cost is c1*x + c2*x """ super(PacmanPlotLP, self).__init__(zoom, frameTime) xmin = 100000 ymin = 100000 xmax = -100000 ymax = -100000 for point in feasiblePoints: if point[0] < xmin: xmin = point[0] if point[0] > xmax: xmax = point[0] if point[1] < ymin: ymin = point[1] if point[1] > ymax: ymax = point[1] if len(feasiblePoints) == 0: for point in infeasiblePoints: if point[0] < xmin: xmin = point[0] if point[0] > xmax: xmax = point[0] if point[1] < ymin: ymin = point[1] if point[1] > ymax: ymax = point[1] xmin = int(math.floor(xmin)) - 3 ymin = int(math.floor(ymin)) - 3 xmax = int(math.ceil(xmax)) + 3 ymax = int(math.ceil(ymax)) + 3 width = xmax - xmin + 1 height = ymax - ymin + 1 # p = feasiblePoints[2] # print("p={}".format(p)) # print("feasible={}".format(self.pointFeasible(p, constraints))) # g = self.cartesianToLayout(xmin, ymin, xmax, ymax, p) # print("g={}".format(g)) # gr = (int(round(g[0])), int(round(g[1]))) # p2 = self.layoutToCartesian(xmin, ymin, xmax, ymax, gr) # print("p2={}".format(p2)) # print("p2 feasible={}".format(self.pointFeasible(p2, constraints))) layoutLists = self.blankLayoutLists(width, height) self.addInfeasibleGhosts(layoutLists, constraints, xmin, ymin, xmax, ymax) layoutLists = self.changeBorderGhostsToWall(layoutLists) for point in infeasiblePoints: self.addCartesianPointToLayout(layoutLists, point, '.', xmin, ymin, xmax, ymax) for point in feasiblePoints: self.addCartesianPointToLayout(layoutLists, point, 'o', xmin, ymin, xmax, ymax) if optimalPoint is not None: self.addCartesianPointToLayout(layoutLists, optimalPoint, 'P', xmin, ymin, xmax, ymax) if graphicsUtils._canvas is not None: graphicsUtils.clear_screen() # Initialize GameStateData with blank board with axes self.width = width self.height = height self.zoom = min(30.0 / self.width, 20.0 / self.height) self.gridSize = graphicsDisplay.DEFAULT_GRID_SIZE * self.zoom maxNumGhosts = 10000 layout = Layout(layoutLists) self.blankGameState = GameStateData() self.blankGameState.initialize(layout, maxNumGhosts) self.initialize(self.blankGameState) title = 'Pacman Plot LP' graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh() if costVector is not None: self.shadeCost(layoutLists, constraints, costVector, feasiblePoints, xmin, ymin, xmax, ymax) def takeControl(self): """ Give full control to the window. Blocks current thread. Program will exit when window is closed. """ mainloop() def pointCost(self, costVector, point): return costVector[0] * point[0] + costVector[1] * point[1] def shadeCost(self, layout, constraints, costVector, feasiblePoints, xmin, ymin, xmax, ymax): baseColor = [1.0, 0.0, 0.0] costs = [self.pointCost(costVector, point) for point in feasiblePoints] minCost = min(costs) maxCost = max(costs) costSpan = maxCost - minCost allFeasiblePoints = self.getFeasibleLayoutPoints( layout, constraints, xmin, ymin, xmax, ymax) # The feasible points themselves may have been gridded to infeasible grid points, # but we want to make sure they are shaded too. #cornerPoints = [self.cartesianToLayout(xmin, ymin, xmax, ymax, point) for point in feasiblePoints] cornerPoints = self.getLayoutPointsWithSymbol(layout, ('o', 'P')) gridPointsToShade = cornerPoints + allFeasiblePoints for gridPoint in gridPointsToShade: point = self.layoutToCartesian(xmin, ymin, xmax, ymax, gridPoint) relativeCost = (self.pointCost(costVector, point) - minCost) * 1.0 / costSpan # Whoops our grid points are flipped top-bottom from what to_screen expects screenPos = self.to_screen( (gridPoint[0], len(layout) - gridPoint[1] - 1)) cellColor = [ 0.25 + 0.5 * relativeCost * channel for channel in baseColor ] graphicsUtils.square(screenPos, 0.5 * self.gridSize, color=graphicsUtils.formatColor(*cellColor), filled=1, behind=2) graphicsUtils.refresh() def layoutToCartesian(self, xmin, ymin, xmax, ymax, point): xnew = point[0] + xmin ynew = (ymax - ymin) - point[1] + ymin return (xnew, ynew) def cartesianToLayout(self, xmin, ymin, xmax, ymax, point): xnew = point[0] - xmin ynew = (ymax - ymin) - (point[1] - ymin) return (xnew, ynew) def printLayout(self, layout): print('-' * (len(layout[0]) + 2)) for row in layout: print('|' + ''.join(row) + '|') print('-' * (len(layout[0]) + 2)) def blankLayoutLists(self, width, height): layout = [] for _ in range(height): row = [' '] * width layout.append(row) return layout def roundPoint(self, p): return (int(round(p[0])), int(round(p[1]))) def setLayoutWall(self, layout, point): self.setLayoutPoint(layout, point, '%') def getLayoutPointsWithSymbol(self, layout, symbolSet): points = [] for gy in range(len(layout)): for gx in range(len(layout[0])): if layout[gy][gx] in symbolSet: points.append((gx, gy)) return points def getLayoutSymbol(self, layout, point): point = self.roundPoint(point) if point[0] >= 0 and point[0] < len( layout[0]) and point[1] >= 0 and point[1] < len(layout): return layout[point[1]][point[0]] return None def setLayoutPoint(self, layout, point, symbol): point = self.roundPoint(point) if point[0] >= 0 and point[0] < len( layout[0]) and point[1] >= 0 and point[1] < len(layout): layout[point[1]][point[0]] = symbol return True return False def distance(self, p1, p2): vec = (p2[0] - p1[0], p2[1] - p1[1]) vecLen = math.sqrt(vec[0]**2 + vec[1]**2) return vecLen def addLineToLayout(self, layout, p1, p2): radius = 1 STEPS_PER_UNIT = 10 fullVec = (p2[0] - p1[0], p2[1] - p1[1]) fullVecLen = math.sqrt(fullVec[0]**2 + fullVec[1]**2) stepVec = (fullVec[0] / fullVecLen / STEPS_PER_UNIT, fullVec[1] / fullVecLen / STEPS_PER_UNIT) numSteps = int(math.ceil(fullVecLen) * STEPS_PER_UNIT) self.setLayoutWall(layout, p1) point = p1 while point != p2: # Generate four connected points deltas = [(-1, 0), (0, 1), (1, 0), (0, -1)] nextPoints = [(point[0] + delta[0], point[1] + delta[1]) for delta in deltas] distances = [ self.distance(nextPoint, p2) for nextPoint in nextPoints ] minDist = distances[0] minIndex = 0 for i, dist in enumerate(distances): if dist < minDist: minDist = dist minIndex = i #print("distances={}, minIndex={}".format(distances, minIndex)) point = nextPoints[minIndex] self.setLayoutWall(layout, point) #print("p1={}, point={}, p2={}".format(p1, point, p2)) #for n in range(1, numSteps+1): # x = p1[0] + n*stepVec[0] # y = p1[1] + n*stepVec[1] # self.setLayoutWall(layout, (x,y)) def getCartesianSymbol(self, layout, point, xmin, ymin, xmax, ymax): point = self.cartesianToLayout(xmin, ymin, xmax, ymax, point) return self.getLayoutSymbol(layout, point) def addCartesianPointToLayout(self, layout, point, symbol, xmin, ymin, xmax, ymax): point = self.cartesianToLayout(xmin, ymin, xmax, ymax, point) return self.setLayoutPoint(layout, point, symbol) def addCartesianLineToLayout(self, layout, w1, w2, b, xmin, ymin, xmax, ymax): (p1, p2) = lineBoxIntersection(w1, w2, b, xmin, ymin, xmax, ymax) p1 = self.cartesianToLayout(xmin, ymin, xmax, ymax, p1) p2 = self.cartesianToLayout(xmin, ymin, xmax, ymax, p2) self.addLineToLayout(layout, p1, p2) def pointFeasible(self, point, constraints): EPSILON = 1e-6 for constraint in constraints: if constraint[0][0] * point[0] + constraint[0][1] * point[ 1] > constraint[1] + EPSILON: #print("Infeasible: point={}, constraint={}".format(point, constraint)) #print("\t{}*{} + {}*{} = {}".format(constraint[0][0], point[0], constraint[0][1], point[1], constraint[0][0]*point[0] + constraint[0][1]*point[1])) return False return True def getFeasibleLayoutPoints(self, layout, constraints, xmin, ymin, xmax, ymax): height = len(layout) width = len(layout[0]) layoutPoints = [] for gy in range(height): for gx in range(width): point = self.layoutToCartesian(xmin, ymin, xmax, ymax, (gx, gy)) if self.pointFeasible(point, constraints): layoutPoints.append((gx, gy)) return layoutPoints def addInfeasibleGhosts(self, layout, constraints, xmin, ymin, xmax, ymax): numGhosts = 0 height = len(layout) width = len(layout[0]) for gy in range(height): for gx in range(width): point = self.layoutToCartesian(xmin, ymin, xmax, ymax, (gx, gy)) if not self.pointFeasible(point, constraints): self.setLayoutPoint(layout, (gx, gy), 'G') numGhosts += 1 return numGhosts def isSymbolNeighbor(self, layout, point, symbols): deltas = [(-1, -1), (-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1)] neighborSymbols = [ self.getLayoutSymbol(layout, (point[0] + delta[0], point[1] + delta[1])) for delta in deltas ] for symbol in symbols: if symbol in neighborSymbols: return True return False def changeBorderGhostsToWall(self, layout): width = len(layout[0]) height = len(layout) newLayout = self.blankLayoutLists(width, height) for gy in range(height): for gx in range(width): symbol = self.getLayoutSymbol(layout, (gx, gy)) newLayout[gy][gx] = symbol if symbol == 'G': if self.isSymbolNeighbor(layout, (gx, gy), (' ', 'o', 'P')): newLayout[gy][gx] = '%' return newLayout
class PacmanPlot(PacmanGraphics): def __init__(self, x=None, y=None, zoom=1.0, frameTime=0.0): """ Create and dispaly a pacman plot figure. If both x and y are provided, plot the points (x[i],y[i]) for all i in len(x). This will draw on the existing pacman window (clearing it first) or create a new one if no window exists. x: array or list of N scalar values. Default=None, in which case no points will be plotted y: array or list of N scalar values. Default=None, in which case no points will be plotted """ super(PacmanPlot, self).__init__(zoom, frameTime) if x is None or y is None: width = 23 height = 23 xmin = -(width - 1) / 2 + 1 ymin = -(height - 1) / 2 + 1 self.initPlot(xmin, ymin, width, height) else: self.plot(x, y) def initPlot(self, xmin, ymin, width, height): if graphicsUtils._canvas is not None: graphicsUtils.clear_screen() # Initialize GameStateData with blank board with axes self.width = width self.height = height self.xShift = -(xmin - 1) self.yShift = -(ymin - 1) self.line = None self.zoom = min(30.0 / self.width, 20.0 / self.height) self.gridSize = graphicsDisplay.DEFAULT_GRID_SIZE * self.zoom # fullRow = ['%']*self.width # row = ((self.width-1)/2)*[' '] + ['%'] + ((self.width-1)/2)*[' '] # boardText = ((self.height-1)/2)*[row] + [fullRow] + ((self.height-1)/2)*[row] numSpaces = self.width - 1 numSpacesLeft = self.xShift numSpacesRight = numSpaces - numSpacesLeft numRows = self.height numRowsBelow = self.yShift numRowsAbove = numRows - 1 - numRowsBelow fullRow = ['%'] * self.width if numSpacesLeft < 0: row = [' '] * self.width else: row = numSpacesLeft * [' '] + ['%'] + numSpacesRight * [' '] boardText = numRowsAbove * [row] + [fullRow] + numRowsBelow * [row] layout = Layout(boardText) self.blankGameState = GameStateData() self.blankGameState.initialize(layout, 0) self.initialize(self.blankGameState) title = 'Pacman Plot' graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh() def plot(self, x, y, weights=None, title='Pacman Plot'): """ Plot the input values x with their corresponding output values y (either true or predicted). Also, plot the linear regression line if weights are given; assuming h_w(x) = weights[0]*x + weights[1]. This will draw on the existing pacman window (clearing it first) or create a new one if no window exists. x: array or list of N scalar values. y: array or list of N scalar values. weights: array or list of 2 values (or if just one value, the bias weight is assumed to be zero). If None, no line is drawn. Default: None """ if np.array(x).size == 0: return if isinstance(x[0], np.ndarray): # Scrape the first element of each data point x = [data[0] for data in x] xmin = int(math.floor(min(x))) ymin = int(math.floor(min(y))) xmax = int(math.ceil(max(x))) ymax = int(math.ceil(max(y))) width = xmax - xmin + 3 height = ymax - ymin + 3 self.initPlot(xmin, ymin, width, height) gameState = self.blankGameState.deepCopy() gameState.agentStates = [] # Add ghost at each point for (px, py) in zip(x, y): point = (px + self.xShift, py + self.yShift) gameState.agentStates.append( AgentState(Configuration(point, Directions.STOP), False)) # self.initialize(gameState) graphicsUtils.clear_screen() self.infoPane = InfoPane(gameState.layout, self.gridSize) self.drawStaticObjects(gameState) self.drawAgentObjects(gameState) graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh() def setWeights(self, weights): pass def takeControl(self): """ Give full control to the window. Blocks current thread. Program will exit when window is closed. """ mainloop()
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange p = state.getPacmanPosition() if agentIndex == self.getNumAgents() - 1: state.numMoves += 1 return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] ############################## # Additions for Busters Pacman # ############################## def getLivingGhosts(self): """ Returns a list of booleans indicating which ghosts are not yet captured. The first entry (a placeholder for Pacman's index) is always False. """ return self.livingGhosts def setGhostNotLiving(self, index): self.livingGhosts[index] = False def isLose( self ): return self.maxMoves > 0 and self.numMoves >= self.maxMoves def isWin( self ): return self.livingGhosts.count(True) == 0 def getTrueWallPerception(self,location): p = [] x,y = location for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: dx, dy = Actions.directionToVector(action) nextx, nexty = int(x + dx), int(y + dy) p.append( ( action,self.hasWall( nextx, nexty ) ) ) return tuple(p) def getNoisyWalls(self): position = self.getPacmanPosition() perception = [] for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: x,y = position dx, dy = Actions.directionToVector(action) nextx, nexty = int(x + dx), int(y + dy) isWall = self.hasWall(nextx,nexty) if util.flipCoin(EPSILON): isWall = not isWall perception.append( ( action, isWall ) ) return tuple(perception) def getBoardWidth(self): return self.getWalls().width def getBoardHeight(self): return self.getWalls().height ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] self.numMoves = prevState.numMoves; self.maxMoves = prevState.maxMoves; else: # Initial state self.data = GameStateData() self.numMoves = 0; self.maxMoves = -1; def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( str( self ) ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) self.livingGhosts = [False] + [True for i in range(numGhostAgents)] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex]
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ return AgentRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Copy current state state = GameState(self) # Find appropriate rules for the agent AgentRules.applyAction(state, action, agentIndex) AgentRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getAgentState(self, index): return self.data.agentStates[index] def getAgentPosition(self, index): " Note: this could return None if the agent is unobservable " agentState = self.data.agentStates[index] return agentState.getPosition() def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def getRedFood(self): return halfGrid(self.data.food, red=True) def getBlueFood(self): return halfGrid(self.data.food, red=False) def getWalls(self): """ Just like getFood but for walls """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isOver(self): return self.data._win def getRedTeamIndices(self): return self.redTeam[:] def getBlueTeamIndices(self): return self.blueTeam[:] def isOnRedTeam(self, agentIndex): return self.teams[agentIndex] def getAgentDistances(self): "Returns a noisy distance to each agent." if 'agentDistances' in dir(self): return self.agentDistances else: return None ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.blueTeam = prevState.blueTeam self.redTeam = prevState.redTeam self.teams = prevState.teams self.agentDistances = prevState.agentDistances else: self.data = GameStateData() self.agentDistances = [] def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() state.blueTeam = self.blueTeam[:] state.redTeam = self.redTeam[:] state.teams = self.teams[:] state.agentDistances = self.agentDistances[:] return state def makeObservation(self, index): state = self.deepCopy() # Adds the sonar signal pos = state.getAgentPosition(index) n = state.getNumAgents() distances = [ noisyDistance(pos, state.getAgentPosition(i)) for i in range(n) ] state.agentDistances = distances # Remove states of distant opponents if index in self.blueTeam: team = self.blueTeam otherTeam = self.redTeam else: otherTeam = self.blueTeam team = self.redTeam for enemy in otherTeam: seen = False enemyPos = state.getAgentPosition(enemy) for teammate in team: if util.manhattanDistance( enemyPos, state.getAgentPosition(teammate)) <= SIGHT_RANGE: seen = True if not seen: state.data.agentStates[enemy].configuration = None return state def __eq__(self, other): """ Allows two states to be compared. """ if other == None: return False return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return int(hash(self.data)) def __str__(self): return str(self.data) def initialize(self, layout, numAgents): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents) positions = [a.configuration for a in self.data.agentStates] self.blueTeam = [ i for i, p in enumerate(positions) if not self.isRed(p) ] self.redTeam = [i for i, p in enumerate(positions) if self.isRed(p)] self.teams = [self.isRed(p) for p in positions] def isRed(self, configOrPos): width = self.data.layout.width if type(configOrPos) == type((0, 0)): return configOrPos[0] < width / 2 else: return configOrPos.pos[0] < width / 2
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def getResult(self, agentIndex, action): """ Returns the state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a result of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange p = state.getPacmanPosition() state.data.ghostDistances = [ getNoisyDistance(p, state.getGhostPosition(i)) for i in range(1, state.getNumAgents()) ] if agentIndex == self.getNumAgents() - 1: state.numMoves += 1 return state def getLegalPacmanActions(self): return self.getLegalActions(0) def getPacmanResult(self, action): """ Generates the result state after the specified pacman action """ return self.getResult(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] ############################## # Additions for Busters Pacman # ############################## def getLivingGhosts(self): """ Returns a list of booleans indicating which ghosts are not yet captured. The first entry (a placeholder for Pacman's index) is always False. """ return self.livingGhosts def setGhostNotLiving(self, index): self.livingGhosts[index] = False def isLose(self): return self.maxMoves > 0 and self.numMoves >= self.maxMoves def isWin(self): return self.livingGhosts.count(True) == 0 def getNoisyGhostDistances(self): """ Returns a noisy distance to each ghost. """ return self.data.ghostDistances ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] self.numMoves = prevState.numMoves self.maxMoves = prevState.maxMoves else: # Initial state self.data = GameStateData() self.numMoves = 0 self.maxMoves = -1 self.data.ghostDistances = [] def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() state.data.ghostDistances = self.data.ghostDistances return state def __eq__(self, other): """ Allows two states to be compared. """ if other is None: return False return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(str(self)) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) self.livingGhosts = [False] + [True for i in range(numGhostAgents)] self.data.ghostDistances = [ getNoisyDistance(self.getPacmanPosition(), self.getGhostPosition(i)) for i in range(1, self.getNumAgents()) ] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex]
def __init__(self, constraints=[], infeasiblePoints=[], feasiblePoints=[], optimalPoint=None, costVector=None, zoom=1.0, frameTime=0.0): """ Create and dispaly a pacman plot figure. This will draw on the existing pacman window (clearing it first) or create a new one if no window exists. constraints: list of inequality constraints, where each constraint w1*x + w2*y <= b is represented as a tuple ((w1, w2), b) infeasiblePoints (food): list of points where each point is a tuple (x, y) feasiblePoints (power): list of points where each point is a tuple (x, y) optimalPoint (pacman): optimal point as a tuple (x, y) costVector (shading): cost vector represented as a tuple (c1, c2), where cost is c1*x + c2*x """ super(PacmanPlotLP, self).__init__(zoom, frameTime) xmin = 100000 ymin = 100000 xmax = -100000 ymax = -100000 for point in feasiblePoints: if point[0] < xmin: xmin = point[0] if point[0] > xmax: xmax = point[0] if point[1] < ymin: ymin = point[1] if point[1] > ymax: ymax = point[1] if len(feasiblePoints) == 0: for point in infeasiblePoints: if point[0] < xmin: xmin = point[0] if point[0] > xmax: xmax = point[0] if point[1] < ymin: ymin = point[1] if point[1] > ymax: ymax = point[1] xmin = int(math.floor(xmin)) - 3 ymin = int(math.floor(ymin)) - 3 xmax = int(math.ceil(xmax)) + 3 ymax = int(math.ceil(ymax)) + 3 width = xmax - xmin + 1 height = ymax - ymin + 1 # p = feasiblePoints[2] # print("p={}".format(p)) # print("feasible={}".format(self.pointFeasible(p, constraints))) # g = self.cartesianToLayout(xmin, ymin, xmax, ymax, p) # print("g={}".format(g)) # gr = (int(round(g[0])), int(round(g[1]))) # p2 = self.layoutToCartesian(xmin, ymin, xmax, ymax, gr) # print("p2={}".format(p2)) # print("p2 feasible={}".format(self.pointFeasible(p2, constraints))) layoutLists = self.blankLayoutLists(width, height) self.addInfeasibleGhosts(layoutLists, constraints, xmin, ymin, xmax, ymax) layoutLists = self.changeBorderGhostsToWall(layoutLists) for point in infeasiblePoints: self.addCartesianPointToLayout(layoutLists, point, '.', xmin, ymin, xmax, ymax) for point in feasiblePoints: self.addCartesianPointToLayout(layoutLists, point, 'o', xmin, ymin, xmax, ymax) if optimalPoint is not None: self.addCartesianPointToLayout(layoutLists, optimalPoint, 'P', xmin, ymin, xmax, ymax) if graphicsUtils._canvas is not None: graphicsUtils.clear_screen() # Initialize GameStateData with blank board with axes self.width = width self.height = height self.zoom = min(30.0 / self.width, 20.0 / self.height) self.gridSize = graphicsDisplay.DEFAULT_GRID_SIZE * self.zoom maxNumGhosts = 10000 layout = Layout(layoutLists) self.blankGameState = GameStateData() self.blankGameState.initialize(layout, maxNumGhosts) self.initialize(self.blankGameState) title = 'Pacman Plot LP' graphicsUtils.changeText(self.infoPane.scoreText, title) graphicsUtils.refresh() if costVector is not None: self.shadeCost(layoutLists, constraints, costVector, feasiblePoints, xmin, ymin, xmax, ymax)
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def updateState(self, actions): """ Returns the successor state after the actions of all active agents. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('it is terminal state now.') self.data.clear() for agentIndex in range(len(actions)): if self.data._eaten[agentIndex] > 0: BombermanRules.applyAction( self, actions[agentIndex], agentIndex ) self.minusOneFrame() for counter,position,power,index in self.data.bombs: if counter == self.getFramesUntilEnd(): self.bombExplode(self.data.bombs,position,power) if index >= 0: self.getAgentState(index).recoverABomb() self.data.bombs = [b for b in self.data.bombs if (b[0] != self.getFramesUntilEnd())] self.updateBombScore() self.updateMapScore() self.data.score = self.data._eaten[0] def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] return BombermanRules.getLegalActions( self , agentIndex) def generateSuccessor( self, agentIndex, action , force = False): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board #state.data._eaten = [False for i in range(state.getNumAgents())] if state.data._eaten[agentIndex] > 0: BombermanRules.applyAction( state, action, agentIndex ) if force or ( agentIndex == state.getNumAgents() - 1): state.minusOneFrame() for counter,position,power,index in state.data.bombs: if counter == state.getFramesUntilEnd(): state.bombExplode(state.data.bombs,position,power) state.getAgentState(index).recoverABomb() state.data.bombs = [b for b in state.data.bombs if (b[0] != state.getFramesUntilEnd())] state.updateBombScore() state.updateMapScore() # Time passes state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around # Resolve multi-agent effects #GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getAgentPosition( self, agentIndex ): return self.data.agentStates[agentIndex].getPosition() def getAgentState(self, agentIndex): return self.data.agentStates[agentIndex] def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getMapScore(self,x,y=None): if y is None: try : x,y = x except: raise 'incomprehensible coordinates' return self.data.MapScore[x][y] def getBombScore(self,x,y=None): if y is None: try : x,y = x except: raise 'incomprehensible coordinates' return self.data.BombScore[x][y] def hasWall(self, x, y): return self.data.map.isWall((x,y)) def isLose( self ): return (self.getNumAgents() == 1 and self.data._eaten[0] == 0) or (self.data._eaten.count(0) == self.getNumAgents()) def isWin( self ): return ( self.getNumAgents()!=1 and self.data._eaten.count(0) is self.getNumAgents()-1 ) def getFramesUntilEnd(self ): return self.data.FramesUntilEnd def minusOneFrame( self ): self.data.FramesUntilEnd = self.data.FramesUntilEnd - 1 return self.data.FramesUntilEnd def layABomb(self,agentIndex,pos): self.data._bombLaid.append(pos) self.data.map.add_bomb(pos) self.getAgentState(agentIndex).minusABomb() self.data.bombs.append( (self.data.FramesUntilEnd - BOMB_DURATION , pos, self.getAgentState(agentIndex).getBombPower() , agentIndex ) ) def bombExplode(self,bombs, position, power): x_int, y_int = position if not self.data.map.isBomb(position): return self.data._bombExplode.append(position) self.data.map.remove_object(position) fired = [] for i in range(len(self.data._fire)): fired += self.data._fire[i] if not position in fired: self.checkDie(position) self.data._fire[0].append(position) fired.append(position) for vec in [ v for dir, v in Actions._directionsAsList if ( not dir in [ Actions.LAY ,Directions.STOP])]: isbreak = False i = 0 dx, dy = vec next_y, next_x = y_int,x_int while not isbreak and i < power: i=i+1 next_y = int(next_y + dy) next_x = int(next_x + dx) pos = (next_x,next_y) if pos in fired: continue if self.data.map.isEmpty(pos): self.checkDie(pos) self.data._fire[i].append(pos) fired.append(pos) elif self.data.map.isBlock(pos): isbreak = True self.data._blockBroken.append(pos) res = self.data.map.remove_object(pos) self.data._fire[i].append(pos) fired.append(pos) if res != None: self.data._itemDrop.append((next_x,next_y,res)) elif self.data.map.isWall(pos): isbreak = True elif self.data.map.isItem(pos): self.data._itemEaten.append(pos) self.data.map.remove_object(pos) self.data._fire[i].append(pos) fired.append(pos) self.checkDie(pos) elif self.data.map.isBomb(pos): self.checkDie(pos) self.data._fire[i].append(pos) fired.append(pos) bombSweep = [(idx,bomb) for idx,bomb in enumerate(bombs) if (pos in bomb ) and (bomb[0] < self.data.FramesUntilEnd-int(BOMB_DURATION/10)) ] if len(bombSweep) is 1: bombs[bombSweep[0][0]] = (self.data.FramesUntilEnd-int(BOMB_DURATION/10),)+bombSweep[0][1][1:] def checkDie(self,position): x,y = position for index,agent in enumerate(self.data.agentStates): if self.data._eaten[index] is 0 : continue sx,sy = agent.getPosition() sx,sy = round(sx),round(sy) if manhattanDistance(position,(sx,sy)) <= 0.5: self.data._eaten[index] -= 1 agent.configuration = agent.start def updateBombScore(self): # The change to the BombScore: self.data.BombScore.data = [[0 for y in range(self.data.map.height)] for x in range(self.data.map.width)] for counter, pos, power, index in self.data.bombs: score = self.calBombScore(counter) self.data.BombScore[pos[0]][pos[1]] += score isbreak = False for vec in [v for dir, v in Actions._directionsAsList if ( not dir in [ Actions.LAY ,Directions.STOP])]: isbreak = False i = 0 dx,dy = vec next_x,next_y = pos while not isbreak and i < power: i += 1 next_x = int(next_x+dx) next_y = int(next_y+dy) if self.data.map.isBlock((next_x,next_y)) or self.data.map.isWall((next_x,next_y)): isbreak = True else : self.data.BombScore[next_x][next_y] += score-i/5.0 def updateMapScore(self): self.data.MapScore.data = [[0 for y in range(self.data.map.height)] for x in range(self.data.map.width)] for x in range(self.data.map.width): for y in range(self.data.map.height): if not self.data.map.isBlocked((x,y)) or self.data.map.isBomb((x,y)): main = [self.data.map.isBlocked((row,col)) for row,col in [(x+1,y),(x-1,y),(x,y+1),(x,y-1)]] second = [self.data.map.isBlocked((row,col)) for row,col in [(x+1,y+1),(x-1,y+1),(x+1,y-1),(x-1,y-1)]] self.data.MapScore[x][y] = ( main.count(True)*1 + second.count(True)*0.4 ) if main.count(True) == 4: self.data.MapScore[x][y] = 100 if self.data.map.isBomb((x,y)): self.data.MapScore[x][y] += 1 def calBombScore(self, counter): if self.getFramesUntilEnd() - 1 == counter: # next frame would explode return 100 return 3*(BOMB_DURATION - (self.getFramesUntilEnd() - counter)) def getTotalLives(self, indexes): return sum([self.data._eaten[index] for index in indexes]) ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( self.data ) def __str__( self ): return str(self.data) def initialize( self, layout, numAgents=1000 , timeout = 3000, life = 5 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents, timeout, life , BOMB_DURATION)
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ return AgentRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state (a GameState object) after the specified agent takes the action. """ # Copy current state state = GameState(self) # Find appropriate rules for the agent AgentRules.applyAction(state, action, agentIndex) AgentRules.checkDeath(state, agentIndex) AgentRules.decrementTimer(state.data.agentStates[agentIndex]) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange state.data.timeleft = self.data.timeleft - 1 return state def getAgentState(self, index): return self.data.agentStates[index] def getAgentPosition(self, index): """ Returns a location tuple if the agent with the given index is observable; if the agent is unobservable, returns None. """ agentState = self.data.agentStates[index] ret = agentState.getPosition() if ret: return tuple(int(x) for x in ret) return ret def getNumAgents(self): return len(self.data.agentStates) def getScore(self): """ Returns a number corresponding to the current score. """ return self.data.score def getRedFood(self): """ Returns a matrix of food that corresponds to the food on the red team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to red (meaning red is protecting it, blue is trying to eat it). """ return halfGrid(self.data.food, red=True) def getBlueFood(self): """ Returns a matrix of food that corresponds to the food on the blue team's side. For the matrix m, m[x][y]=true if there is food in (x,y) that belongs to blue (meaning blue is protecting it, red is trying to eat it). """ return halfGrid(self.data.food, red=False) def getRedCapsules(self): return halfList(self.data.capsules, self.data.food, red=True) def getBlueCapsules(self): return halfList(self.data.capsules, self.data.food, red=False) def getWalls(self): """ Just like getFood but for walls """ return self.data.layout.walls def hasFood(self, x, y): """ Returns true if the location (x,y) has food, regardless of whether it's blue team food or red team food. """ return self.data.food[x][y] def hasWall(self, x, y): """ Returns true if (x,y) has a wall, false otherwise. """ return self.data.layout.walls[x][y] def isOver(self): return self.data._win def getRedTeamIndices(self): """ Returns a list of agent index numbers for the agents on the red team. """ return self.redTeam[:] def getBlueTeamIndices(self): """ Returns a list of the agent index numbers for the agents on the blue team. """ return self.blueTeam[:] def isOnRedTeam(self, agentIndex): """ Returns true if the agent with the given agentIndex is on the red team. """ return self.teams[agentIndex] def getAgentDistances(self): """ Returns a noisy distance to each agent. """ if 'agentDistances' in dir(self): return self.agentDistances else: return None def getDistanceProb(self, trueDistance, noisyDistance): "Returns the probability of a noisy distance given the true distance" if noisyDistance - trueDistance in SONAR_NOISE_VALUES: return 1.0 / SONAR_NOISE_RANGE else: return 0 def getInitialAgentPosition(self, agentIndex): "Returns the initial position of an agent." return self.data.layout.agentPositions[agentIndex][1] def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.blueTeam = prevState.blueTeam self.redTeam = prevState.redTeam self.data.timeleft = prevState.data.timeleft self.teams = prevState.teams self.agentDistances = prevState.agentDistances else: self.data = GameStateData() self.agentDistances = [] def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() state.data.timeleft = self.data.timeleft state.blueTeam = self.blueTeam[:] state.redTeam = self.redTeam[:] state.teams = self.teams[:] state.agentDistances = self.agentDistances[:] return state def makeObservation(self, index): state = self.deepCopy() # Adds the sonar signal pos = state.getAgentPosition(index) n = state.getNumAgents() distances = [ noisyDistance(pos, state.getAgentPosition(i)) for i in range(n) ] state.agentDistances = distances # Remove states of distant opponents if index in self.blueTeam: team = self.blueTeam otherTeam = self.redTeam else: otherTeam = self.blueTeam team = self.redTeam for enemy in otherTeam: seen = False enemyPos = state.getAgentPosition(enemy) for teammate in team: if util.manhattanDistance( enemyPos, state.getAgentPosition(teammate)) <= SIGHT_RANGE: seen = True if not seen: state.data.agentStates[enemy].configuration = None return state def __eq__(self, other): """ Allows two states to be compared. """ if other == None: return False return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return int(hash(self.data)) def __str__(self): return str(self.data) def initialize(self, layout, numAgents): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numAgents) positions = [a.configuration for a in self.data.agentStates] self.blueTeam = [ i for i, p in enumerate(positions) if not self.isRed(p) ] self.redTeam = [i for i, p in enumerate(positions) if self.isRed(p)] self.teams = [self.isRed(p) for p in positions] # This is usually 60 (always 60 with random maps) # However, if layout map is specified otherwise, it could be less global TOTAL_FOOD TOTAL_FOOD = layout.totalFood def isRed(self, configOrPos): width = self.data.layout.width if type(configOrPos) == type((0, 0)): return configOrPos[0] < width / 2 else: return configOrPos.pos[0] < width / 2
def __init__(self, layout, agents, display_class, turn=AgentRole.Mr_X): self.gameState = GameState(GameStateData(layout, agents)) self.turn = turn # create a new <type>+Display object self.display = display_class(self.gameState.data) self.MOD = len(agents) # number of players
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange GameState.explored.add(self) GameState.explored.add(state) return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getGhostStates( self ): return self.data.agentStates[1:] def getGhostState( self, agentIndex ): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return float(self.data.score) def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): count = 0 for x in self.data.food: for e in x: if e: count += 1 return count def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose( self ): return self.data._lose def isWin( self ): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( self.data ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
class GameState:##this is called by initState = GameState() in ClassicGameRules.newGame """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() #REFA01 def getAndResetExplored(): ##this function passes a copy of the explored GameState object to the tmp variable, resets the same object with an empty set. It then returns the tmp tmp = GameState.explored.copy() #[REFA01] This is a copy reference to the explored variable which contains an empty set. GameState.explored = set() #This re-equipps the explored variable with an empty set. return tmp getAndResetExplored = staticmethod(getAndResetExplored) """ So, as we can see from usage of staticmethod, we don't have any access to what the class is- it's basically just a function, called syntactically like a method, but without access to the object and it's internals (fields and another methods) """ def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getGhostStates( self ): return self.data.agentStates[1:] def getGhostState( self, agentIndex ): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose( self ): return self.data._lose def isWin( self ): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): ###PLEASE NOTE THIS THAT THE __init__ method is here and this is where GameState() starts """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) ##This statement imports the GameStateData object from the GameStateData class in game.py. This object contains a data packet documenting the state of the game. else: self.data = GameStateData() """ self._foodEaten = None self._capsuleEaten = None self._agentMoved = None self._lose = False self._win = False self.scoreChange = 0 """ def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( self.data ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). This is based on: self.width = len(layoutText[0])## measures the width of the first element in the layoutText list, which was passed in by the 'tryToLoad' function by initiating the Layout object in the function call below. Open one of the layout files in the 'layouts' folder to understand how the layout works. self.height= len(layoutText) ## measures the hight of the layout by counting the length of the array [line1,line2,line3,line4] self.walls = Grid(self.width, self.height, False) ##self.walls contains an object that contains the Grid's width, height and a 2-dimenstional array called self.data that looks something like this:[[False, False, False,False],[False, False, False,False],...] self.food = Grid(self.width, self.height, False) ## same as the line before self.capsules = [] self.agentPositions = [] self.numGhosts = 0 self.processLayoutText(layoutText) ## This function takes the symbols contained in the layout files and turns them into coordinates which are then appended or assigned to the several variables that have been initialised in the code above. This happens according to the (x,y) convention used in the 2-dimensonal array that was defined in the Games class. self.layoutText = layoutText --> The initialize function then calls the self.data object's initialize function. "self.data" contains the GameStateData objec. GameStateData contains an initialize function itself which creates an initial game state from a layout array (see layout.py) (see above). self.food = layout.food.copy() self.capsules = layout.capsules[:] self.layout = layout self.score = 0 self.scoreChange = 0 self.agentStates = [] numGhosts = 0 for isPacman, pos in layout.agentPositions: if not isPacman: if numGhosts == numGhostAgents: continue # Max ghosts reached already else: numGhosts += 1 self.agentStates.append( AgentState( Configuration( pos, Directions.STOP), isPacman) ) self._eaten = [False for a in self.agentStates] """ self.data.initialize(layout, numGhostAgents) ##self.data is defined in the Grid() class of game.py REF112.It creates an initial game state from a layout array (see layout.py).
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### # static variable keeps track of which states have had getLegalActions called explored = set() def getAndResetExplored(): tmp = GameState.explored.copy() GameState.explored = set() return tmp getAndResetExplored = staticmethod(getAndResetExplored) def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ # GameState.explored.add(self) if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose() or self.eventQueue.isEmpty(): raise Exception('Can\'t generate a successor of a terminal state.') time, event = self.eventQueue.peek() assert event.isAgentMove( ), 'Can only generate successors of a state where an agent is about to move' state = self.makeAgentMove(action) state.resolveEventsUntilAgentEvent() # Book keeping GameState.explored.add(self.data) GameState.explored.add(state.data) return state def makeAgentMove(self, action): # Copy current state state = GameState(self) time, event = state.eventQueue.pop() agentIndex = event.getAgentIndex() agentState = state.data.agentStates[agentIndex] state.data.time = time delay = agentState.powers.timestepsBetweenMoves state.registerEventWithDelay(event, delay) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) state.data.scoreChange -= TIME_PENALTY # Penalty for waiting around else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) GhostRules.decrementTimer(agentState) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) if action == Directions.LASER: GhostRules.checkLaserShot(state, agentIndex) if action == Directions.BLAST: GhostRules.checkBlast(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex # Note: It is important that the following value accurately # reflects when Pacman will make the next move, even if the # speed changes (such as a speed-up power pellet). Otherwise # the graphics will do weird things. state.data._timeTillAgentMovesAgain = delay state.data._action = action state.data.score += state.data.scoreChange return state def runEvent(self): # Check that successors exist if self.eventQueue.isEmpty(): raise Exception('Can\'t run an event of a terminal state.') time, event = self.eventQueue.pop() assert not event.isAgentMove(), 'Can\'t run an AgentMoveEvent' self.data.time = time event.trigger(self) return event def getNextEvent(self): _, event = self.eventQueue.peek() return event def getLegalPacmanActions(self): return self.getLegalActions(0) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getPacmanDirection(self): return self.data.agentStates[0].getDirection() def getGhostStates(self): return self.data.agentStates[1:] def getGhostState(self, agentIndex): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise Exception("Invalid index passed to getGhostState") return self.data.agentStates[agentIndex] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise Exception("Pacman's index passed to getGhostPosition") return self.data.agentStates[agentIndex].getPosition() def getGhostPositions(self): return [s.getPosition() for s in self.getGhostStates()] def getNextAgentIndex(self): for time, event in self.eventQueue.getSortedTimesAndEvents(): if event.isAgentMove(): return event.getAgentIndex() assert False, "No more moves can be made" def getAgentMoveTime(self, agentIndex): for time, event in self.eventQueue.getSortedTimesAndEvents(): if event.isAgentMove(): if event.getAgentIndex() == agentIndex: return time assert False, "No more moves can be made by agent " + str(agentIndex) def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return float(self.data.score) def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is a wall at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.walls[x][y] def isLose(self): return self.data._lose def isWin(self): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) self.eventQueue = prevState.eventQueue.deepCopy() else: self.data = GameStateData() self.eventQueue = EventQueue() def resolveEventsUntilAgentEvent(self): # Resolve any events until the next agent event while not self.eventQueue.isEmpty(): time, event = self.eventQueue.peek() if event.isAgentMove(): return else: self.runEvent() def registerEventWithDelay(self, event, delay): self.eventQueue.registerEventAtTime(event, self.data.time + delay) def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() # Event queue has already been copied in the constructor return state def __eq__(self, other): """ Allows two states to be compared. """ return hasattr(other, 'data') and self.data == other.data \ and hasattr(other, 'eventQueue') and self.eventQueue == other.eventQueue \ def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash((self.data, self.eventQueue)) def __str__(self): return str(self.data) def initialize(self, layout, pacmanPowers, ghostPowers, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, pacmanPowers, ghostPowers, numGhostAgents) numAgents = self.getNumAgents() for i in range(numAgents): self.registerEventWithDelay(AgentMoveEvent(i), i) self.registerEventWithDelay(WallTimerEvent(), 1)
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. Note that in classic Pacman, Pacman is always agent 0. In SLAM inference, you will not have access to any GameState objects. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange p = state.getPacmanPosition() state.data.ghostDistances = [getNoisyDistance(p, state.getGhostPosition(i)) for i in range(1,state.getNumAgents())] if agentIndex == self.getNumAgents() - 1: state.numMoves += 1 return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def getLegalPositions(self): """ Returns all positions that need to be considered for mapping (i.e., both walls and walkable positions) """ l = [p for p in self.getWalls().asList() + self.getWalls().asList(False)] return l def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose( self ): """ Not used by default for SLAM. """ return self.maxMoves > 0 and self.numMoves >= self.maxMoves def isWin( self ): """ Not used for SLAM. """ return False ############################## # Additions for Slam Pacman # ############################## def getNoisyRangeMeasurements(self): """ Return a noisy measurement in each of the four directions as a tuple (N, E, S, W). """ x, y = self.getPacmanPosition() currentX = x currentY = y while not self.hasWall(currentX, currentY): currentY += 1 N = getNoisyDistance((x,y), (currentX, currentY)) currentX = x currentY = y while not self.hasWall(currentX, currentY): currentX += 1 E = getNoisyDistance((x,y), (currentX, currentY)) currentX = x currentY = y while not self.hasWall(currentX, currentY): currentY -= 1 S = getNoisyDistance((x,y), (currentX, currentY)) currentX = x currentY = y while not self.hasWall(currentX, currentY): currentX -= 1 W = getNoisyDistance((x,y), (currentX, currentY)) return (N, E, S, W) ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] self.numMoves = prevState.numMoves; self.maxMoves = prevState.maxMoves; else: # Initial state self.data = GameStateData() self.numMoves = 0; self.maxMoves = -1; self.data.ghostDistances = [] def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() state.data.ghostDistances = self.data.ghostDistances return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( str( self ) ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) self.livingGhosts = [False] + [True for i in range(numGhostAgents)] self.data.ghostDistances = [getNoisyDistance(self.getPacmanPosition(), self.getGhostPosition(i)) for i in range(1, self.getNumAgents())] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex]
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. We strongly suggest that you access that data via the accessor methods below rather than referring to the GameStateData object directly. Note that in classic Pacman, Pacman is always agent 0. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions( self, agentIndex=0 ): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions( self ) else: return GhostRules.getLegalActions( self, agentIndex ) def generateSuccessor( self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction( state, action ) else: # A ghost is moving GhostRules.applyAction( state, action, agentIndex ) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) # Resolve multi-agent effects GhostRules.checkDeath( state, agentIndex ) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange return state def getLegalPacmanActions( self ): return self.getLegalActions( 0 ) def generatePacmanSuccessor( self, action ): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor( 0, action ) def getPacmanState( self ): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition( self ): return self.data.agentStates[0].getPosition() def getGhostStates( self ): return self.data.agentStates[1:] def getGhostState( self, agentIndex ): if agentIndex == 0 or agentIndex >= self.getNumAgents(): raise "Invalid index passed to getGhostState" return self.data.agentStates[agentIndex] def getGhostPosition( self, agentIndex ): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getNumAgents( self ): return len( self.data.agentStates ) def getScore( self ): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood( self ): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose( self ): return self.data._lose def isWin( self ): return self.data._win ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__( self, prevState = None ): """ Generates a new state by copying information from its predecessor. """ if prevState != None: # Initial state self.data = GameStateData(prevState.data) else: self.data = GameStateData() def deepCopy( self ): state = GameState( self ) state.data = self.data.deepCopy() return state def __eq__( self, other ): """ Allows two states to be compared. """ return self.data == other.data def __hash__( self ): """ Allows states to be keys of dictionaries. """ return hash( str( self ) ) def __str__( self ): return str(self.data) def initialize( self, layout, numGhostAgents=1000 ): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents)
class GameState: """ A GameState specifies the full game state, including the food, capsules, agent configurations and score changes. GameStates are used by the Game object to capture the actual state of the game and can be used by agents to reason about the game. Much of the information in a GameState is stored in a GameStateData object. Note that in classic Pacman, Pacman is always agent 0. In SLAM inference, you will not have access to any GameState objects. """ #################################################### # Accessor methods: use these to access state data # #################################################### def getLegalActions(self, agentIndex=0): """ Returns the legal actions for the agent specified. """ if self.isWin() or self.isLose(): return [] if agentIndex == 0: # Pacman is moving return PacmanRules.getLegalActions(self) else: return GhostRules.getLegalActions(self, agentIndex) def generateSuccessor(self, agentIndex, action): """ Returns the successor state after the specified agent takes the action. """ # Check that successors exist if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') # Copy current state state = GameState(self) # Let agent's logic deal with its action's effects on the board if agentIndex == 0: # Pacman is moving state.data._eaten = [False for i in range(state.getNumAgents())] PacmanRules.applyAction(state, action) else: # A ghost is moving GhostRules.applyAction(state, action, agentIndex) # Time passes if agentIndex == 0: state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around else: GhostRules.decrementTimer(state.data.agentStates[agentIndex]) # Resolve multi-agent effects GhostRules.checkDeath(state, agentIndex) # Book keeping state.data._agentMoved = agentIndex state.data.score += state.data.scoreChange p = state.getPacmanPosition() state.data.ghostDistances = [ getNoisyDistance(p, state.getGhostPosition(i)) for i in range(1, state.getNumAgents()) ] if agentIndex == self.getNumAgents() - 1: state.numMoves += 1 return state def getLegalPacmanActions(self): return self.getLegalActions(0) def generatePacmanSuccessor(self, action): """ Generates the successor state after the specified pacman move """ return self.generateSuccessor(0, action) def getPacmanState(self): """ Returns an AgentState object for pacman (in game.py) state.pos gives the current position state.direction gives the travel vector """ return self.data.agentStates[0].copy() def getPacmanPosition(self): return self.data.agentStates[0].getPosition() def getNumAgents(self): return len(self.data.agentStates) def getScore(self): return self.data.score def getCapsules(self): """ Returns a list of positions (x,y) of the remaining capsules. """ return self.data.capsules def getNumFood(self): return self.data.food.count() def getFood(self): """ Returns a Grid of boolean food indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call currentFood = state.getFood() if currentFood[x][y] == True: ... """ return self.data.food def getWalls(self): """ Returns a Grid of boolean wall indicator variables. Grids can be accessed via list notation, so to check if there is food at (x,y), just call walls = state.getWalls() if walls[x][y] == True: ... """ return self.data.layout.walls def getLegalPositions(self): """ Returns all positions that need to be considered for mapping (i.e., both walls and walkable positions) """ l = [ p for p in self.getWalls().asList() + self.getWalls().asList(False) ] return l def hasFood(self, x, y): return self.data.food[x][y] def hasWall(self, x, y): return self.data.layout.walls[x][y] def isLose(self): """ Not used by default for SLAM. """ return self.maxMoves > 0 and self.numMoves >= self.maxMoves def isWin(self): """ Not used for SLAM. """ return False ############################## # Additions for Slam Pacman # ############################## def getNoisyRangeMeasurements(self): """ Return a noisy measurement in each of the four directions as a tuple (N, E, S, W). """ x, y = self.getPacmanPosition() currentX = x currentY = y while not self.hasWall(currentX, currentY): currentY += 1 N = getNoisyDistance((x, y), (currentX, currentY)) currentX = x currentY = y while not self.hasWall(currentX, currentY): currentX += 1 E = getNoisyDistance((x, y), (currentX, currentY)) currentX = x currentY = y while not self.hasWall(currentX, currentY): currentY -= 1 S = getNoisyDistance((x, y), (currentX, currentY)) currentX = x currentY = y while not self.hasWall(currentX, currentY): currentX -= 1 W = getNoisyDistance((x, y), (currentX, currentY)) return (N, E, S, W) ############################################# # Helper methods: # # You shouldn't need to call these directly # ############################################# def __init__(self, prevState=None): """ Generates a new state by copying information from its predecessor. """ if prevState != None: self.data = GameStateData(prevState.data) self.livingGhosts = prevState.livingGhosts[:] self.numMoves = prevState.numMoves self.maxMoves = prevState.maxMoves else: # Initial state self.data = GameStateData() self.numMoves = 0 self.maxMoves = -1 self.data.ghostDistances = [] def deepCopy(self): state = GameState(self) state.data = self.data.deepCopy() state.data.ghostDistances = self.data.ghostDistances return state def __eq__(self, other): """ Allows two states to be compared. """ return self.data == other.data def __hash__(self): """ Allows states to be keys of dictionaries. """ return hash(str(self)) def __str__(self): return str(self.data) def initialize(self, layout, numGhostAgents=1000): """ Creates an initial game state from a layout array (see layout.py). """ self.data.initialize(layout, numGhostAgents) self.livingGhosts = [False] + [True for i in range(numGhostAgents)] self.data.ghostDistances = [ getNoisyDistance(self.getPacmanPosition(), self.getGhostPosition(i)) for i in range(1, self.getNumAgents()) ] def getGhostPosition(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex].getPosition() def getGhostState(self, agentIndex): if agentIndex == 0: raise "Pacman's index passed to getGhostPosition" return self.data.agentStates[agentIndex]