189 lines
7.2 KiB
Python
189 lines
7.2 KiB
Python
# bustersAgents.py
|
|
# ----------------
|
|
# Licensing Information: You are free to use or extend these projects for
|
|
# educational purposes provided that (1) you do not distribute or publish
|
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
|
#
|
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
|
# The core projects and autograders were primarily created by John DeNero
|
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
|
# Student side autograding was added by Brad Miller, Nick Hay, and
|
|
# Pieter Abbeel (pabbeel@cs.berkeley.edu).
|
|
|
|
|
|
import util
|
|
from game import Agent
|
|
from game import Directions
|
|
from keyboardAgents import KeyboardAgent
|
|
import inference
|
|
import busters
|
|
|
|
class NullGraphics:
|
|
"Placeholder for graphics"
|
|
def initialize(self, state, isBlue = False):
|
|
pass
|
|
def update(self, state):
|
|
pass
|
|
def pause(self):
|
|
pass
|
|
def draw(self, state):
|
|
pass
|
|
def updateDistributions(self, dist):
|
|
pass
|
|
def finish(self):
|
|
pass
|
|
|
|
class KeyboardInference(inference.InferenceModule):
|
|
"""
|
|
Basic inference module for use with the keyboard.
|
|
"""
|
|
def initializeUniformly(self, gameState):
|
|
"Begin with a uniform distribution over ghost positions."
|
|
self.beliefs = util.Counter()
|
|
for p in self.legalPositions: self.beliefs[p] = 1.0
|
|
self.beliefs.normalize()
|
|
|
|
def observe(self, observation, gameState):
|
|
noisyDistance = observation
|
|
emissionModel = busters.getObservationDistribution(noisyDistance)
|
|
pacmanPosition = gameState.getPacmanPosition()
|
|
allPossible = util.Counter()
|
|
for p in self.legalPositions:
|
|
trueDistance = util.manhattanDistance(p, pacmanPosition)
|
|
if emissionModel[trueDistance] > 0:
|
|
allPossible[p] = 1.0
|
|
allPossible.normalize()
|
|
self.beliefs = allPossible
|
|
|
|
def elapseTime(self, gameState):
|
|
pass
|
|
|
|
def getBeliefDistribution(self):
|
|
return self.beliefs
|
|
|
|
|
|
class BustersAgent:
|
|
"An agent that tracks and displays its beliefs about ghost positions."
|
|
|
|
def __init__( self, index = 0, inference = "ExactInference", ghostAgents = None, observeEnable = True, elapseTimeEnable = True):
|
|
inferenceType = util.lookup(inference, globals())
|
|
self.inferenceModules = [inferenceType(a) for a in ghostAgents]
|
|
self.observeEnable = observeEnable
|
|
self.elapseTimeEnable = elapseTimeEnable
|
|
|
|
def registerInitialState(self, gameState):
|
|
"Initializes beliefs and inference modules"
|
|
import __main__
|
|
self.display = __main__._display
|
|
for inference in self.inferenceModules:
|
|
inference.initialize(gameState)
|
|
self.ghostBeliefs = [inf.getBeliefDistribution() for inf in self.inferenceModules]
|
|
self.firstMove = True
|
|
|
|
def observationFunction(self, gameState):
|
|
"Removes the ghost states from the gameState"
|
|
agents = gameState.data.agentStates
|
|
gameState.data.agentStates = [agents[0]] + [None for i in range(1, len(agents))]
|
|
return gameState
|
|
|
|
def getAction(self, gameState):
|
|
"Updates beliefs, then chooses an action based on updated beliefs."
|
|
for index, inf in enumerate(self.inferenceModules):
|
|
if not self.firstMove and self.elapseTimeEnable:
|
|
inf.elapseTime(gameState)
|
|
self.firstMove = False
|
|
if self.observeEnable:
|
|
inf.observeState(gameState)
|
|
self.ghostBeliefs[index] = inf.getBeliefDistribution()
|
|
self.display.updateDistributions(self.ghostBeliefs)
|
|
return self.chooseAction(gameState)
|
|
|
|
def chooseAction(self, gameState):
|
|
"By default, a BustersAgent just stops. This should be overridden."
|
|
return Directions.STOP
|
|
|
|
class BustersKeyboardAgent(BustersAgent, KeyboardAgent):
|
|
"An agent controlled by the keyboard that displays beliefs about ghost positions."
|
|
|
|
def __init__(self, index = 0, inference = "KeyboardInference", ghostAgents = None):
|
|
KeyboardAgent.__init__(self, index)
|
|
BustersAgent.__init__(self, index, inference, ghostAgents)
|
|
|
|
def getAction(self, gameState):
|
|
return BustersAgent.getAction(self, gameState)
|
|
|
|
def chooseAction(self, gameState):
|
|
return KeyboardAgent.getAction(self, gameState)
|
|
|
|
from distanceCalculator import Distancer
|
|
from game import Actions
|
|
from game import Directions
|
|
|
|
class GreedyBustersAgent(BustersAgent):
|
|
"An agent that charges the closest ghost."
|
|
|
|
def registerInitialState(self, gameState):
|
|
"Pre-computes the distance between every two points."
|
|
BustersAgent.registerInitialState(self, gameState)
|
|
self.distancer = Distancer(gameState.data.layout, False)
|
|
|
|
def chooseAction(self, gameState):
|
|
"""
|
|
First computes the most likely position of each ghost that has
|
|
not yet been captured, then chooses an action that brings
|
|
Pacman closer to the closest ghost (according to mazeDistance!).
|
|
|
|
To find the mazeDistance between any two positions, use:
|
|
self.distancer.getDistance(pos1, pos2)
|
|
|
|
To find the successor position of a position after an action:
|
|
successorPosition = Actions.getSuccessor(position, action)
|
|
|
|
livingGhostPositionDistributions, defined below, is a list of
|
|
util.Counter objects equal to the position belief
|
|
distributions for each of the ghosts that are still alive. It
|
|
is defined based on (these are implementation details about
|
|
which you need not be concerned):
|
|
|
|
1) gameState.getLivingGhosts(), a list of booleans, one for each
|
|
agent, indicating whether or not the agent is alive. Note
|
|
that pacman is always agent 0, so the ghosts are agents 1,
|
|
onwards (just as before).
|
|
|
|
2) self.ghostBeliefs, the list of belief distributions for each
|
|
of the ghosts (including ghosts that are not alive). The
|
|
indices into this list should be 1 less than indices into the
|
|
gameState.getLivingGhosts() list.
|
|
"""
|
|
pacmanPosition = gameState.getPacmanPosition()
|
|
legal = [a for a in gameState.getLegalPacmanActions()]
|
|
livingGhosts = gameState.getLivingGhosts()
|
|
livingGhostPositionDistributions = \
|
|
[beliefs for i, beliefs in enumerate(self.ghostBeliefs)
|
|
if livingGhosts[i+1]]
|
|
|
|
def getMaxProbPos(distribution):
|
|
return max([(prob, pos) for pos, prob in distribution.items()])[1]
|
|
|
|
closestGhostPosition = None
|
|
closestGhostDist = float("inf")
|
|
for distribution in livingGhostPositionDistributions:
|
|
ghostPosition = getMaxProbPos(distribution)
|
|
dist = self.distancer.getDistance(pacmanPosition, ghostPosition)
|
|
if dist < closestGhostDist:
|
|
closestGhostPosition = ghostPosition
|
|
closestGhostDist = dist
|
|
|
|
if closestGhostPosition is None:
|
|
return 'Stop'
|
|
|
|
distanceActionTuples = [
|
|
(self.distancer.getDistance(
|
|
Actions.getSuccessor(pacmanPosition, action),
|
|
closestGhostPosition),
|
|
action)
|
|
for action in legal]
|
|
|
|
return min(distanceActionTuples)[1]
|