getting right starter files

This commit is contained in:
Arjun Patel
2019-02-20 15:24:24 -08:00
parent 48d11417be
commit ade293719f
358 changed files with 8770 additions and 4878 deletions
+1 -1
View File
@@ -1 +1 @@
v1.001 v1.004
+68 -57
View File
@@ -29,56 +29,58 @@ except:
# register arguments and set default values # register arguments and set default values
def readCommand(argv): def readCommand(argv):
parser = optparse.OptionParser(description = 'Run public tests on student code') parser = optparse.OptionParser(
parser.set_defaults(generateSolutions=False, edxOutput=False, gsOutput=False, muteOutput=False, printTestCase=False, noGraphics=False) description='Run public tests on student code')
parser.set_defaults(generateSolutions=False, edxOutput=False, gsOutput=False,
muteOutput=False, printTestCase=False, noGraphics=False)
parser.add_option('--test-directory', parser.add_option('--test-directory',
dest = 'testRoot', dest='testRoot',
default = 'test_cases', default='test_cases',
help = 'Root test directory which contains subdirectories corresponding to each question') help='Root test directory which contains subdirectories corresponding to each question')
parser.add_option('--student-code', parser.add_option('--student-code',
dest = 'studentCode', dest='studentCode',
default = projectParams.STUDENT_CODE_DEFAULT, default=projectParams.STUDENT_CODE_DEFAULT,
help = 'comma separated list of student code files') help='comma separated list of student code files')
parser.add_option('--code-directory', parser.add_option('--code-directory',
dest = 'codeRoot', dest='codeRoot',
default = "", default="",
help = 'Root directory containing the student and testClass code') help='Root directory containing the student and testClass code')
parser.add_option('--test-case-code', parser.add_option('--test-case-code',
dest = 'testCaseCode', dest='testCaseCode',
default = projectParams.PROJECT_TEST_CLASSES, default=projectParams.PROJECT_TEST_CLASSES,
help = 'class containing testClass classes for this project') help='class containing testClass classes for this project')
parser.add_option('--generate-solutions', parser.add_option('--generate-solutions',
dest = 'generateSolutions', dest='generateSolutions',
action = 'store_true', action='store_true',
help = 'Write solutions generated to .solution file') help='Write solutions generated to .solution file')
parser.add_option('--edx-output', parser.add_option('--edx-output',
dest = 'edxOutput', dest='edxOutput',
action = 'store_true', action='store_true',
help = 'Generate edX output files') help='Generate edX output files')
parser.add_option('--gradescope-output', parser.add_option('--gradescope-output',
dest = 'gsOutput', dest='gsOutput',
action = 'store_true', action='store_true',
help = 'Generate GradeScope output files') help='Generate GradeScope output files')
parser.add_option('--mute', parser.add_option('--mute',
dest = 'muteOutput', dest='muteOutput',
action = 'store_true', action='store_true',
help = 'Mute output from executing tests') help='Mute output from executing tests')
parser.add_option('--print-tests', '-p', parser.add_option('--print-tests', '-p',
dest = 'printTestCase', dest='printTestCase',
action = 'store_true', action='store_true',
help = 'Print each test case before running them.') help='Print each test case before running them.')
parser.add_option('--test', '-t', parser.add_option('--test', '-t',
dest = 'runTest', dest='runTest',
default = None, default=None,
help = 'Run one particular test. Relative to test root.') help='Run one particular test. Relative to test root.')
parser.add_option('--question', '-q', parser.add_option('--question', '-q',
dest = 'gradeQuestion', dest='gradeQuestion',
default = None, default=None,
help = 'Grade one particular question.') help='Grade one particular question.')
parser.add_option('--no-graphics', parser.add_option('--no-graphics',
dest = 'noGraphics', dest='noGraphics',
action = 'store_true', action='store_true',
help = 'No graphics display for pacman games.') help='No graphics display for pacman games.')
(options, args) = parser.parse_args(argv) (options, args) = parser.parse_args(argv)
return options return options
@@ -107,14 +109,15 @@ def setModuleName(module, filename):
for i in dir(module): for i in dir(module):
o = getattr(module, i) o = getattr(module, i)
if hasattr(o, '__file__'): continue if hasattr(o, '__file__'):
continue
if type(o) == functionType: if type(o) == functionType:
setattr(o, '__file__', filename) setattr(o, '__file__', filename)
elif type(o) == classType: elif type(o) == classType:
setattr(o, '__file__', filename) setattr(o, '__file__', filename)
# TODO: assign member __file__'s? # TODO: assign member __file__'s?
#print(i, type(o)) # print i, type(o)
#from cStringIO import StringIO #from cStringIO import StringIO
@@ -126,12 +129,14 @@ def loadModuleString(moduleSource):
#f = StringIO(moduleCodeDict[k]) #f = StringIO(moduleCodeDict[k])
#tmp = imp.load_module(k, f, k, (".py", "r", imp.PY_SOURCE)) #tmp = imp.load_module(k, f, k, (".py", "r", imp.PY_SOURCE))
tmp = imp.new_module(k) tmp = imp.new_module(k)
exec(moduleCodeDict[k] in tmp.__dict__) exec(moduleCodeDict[k], tmp.__dict__)
setModuleName(tmp, k) setModuleName(tmp, k)
return tmp return tmp
import py_compile import py_compile
def loadModuleFile(moduleName, filePath): def loadModuleFile(moduleName, filePath):
with open(filePath, 'r') as f: with open(filePath, 'r') as f:
return imp.load_module(moduleName, f, "%s.py" % moduleName, (".py", "r", imp.PY_SOURCE)) return imp.load_module(moduleName, f, "%s.py" % moduleName, (".py", "r", imp.PY_SOURCE))
@@ -174,6 +179,7 @@ ERROR_HINT_MAP = {
import pprint import pprint
def splitStrings(d): def splitStrings(d):
d2 = dict(d) d2 = dict(d)
for k in d: for k in d:
@@ -215,14 +221,15 @@ def runTest(testName, moduleDict, printTestCase=False, display=None):
printTest(testDict, solutionDict) printTest(testDict, solutionDict)
# This is a fragile hack to create a stub grades object # This is a fragile hack to create a stub grades object
grades = grading.Grades(projectParams.PROJECT_NAME, [(None,0)]) grades = grading.Grades(projectParams.PROJECT_NAME, [(None, 0)])
testCase.execute(grades, moduleDict, solutionDict) testCase.execute(grades, moduleDict, solutionDict)
# returns all the tests you need to run in order to run question # returns all the tests you need to run in order to run question
def getDepends(testParser, testRoot, question): def getDepends(testParser, testRoot, question):
allDeps = [question] allDeps = [question]
questionDict = testParser.TestParser(os.path.join(testRoot, question, 'CONFIG')).parse() questionDict = testParser.TestParser(
os.path.join(testRoot, question, 'CONFIG')).parse()
if 'depends' in questionDict: if 'depends' in questionDict:
depends = questionDict['depends'].split() depends = questionDict['depends'].split()
for d in depends: for d in depends:
@@ -232,11 +239,13 @@ def getDepends(testParser, testRoot, question):
# get list of questions to grade # get list of questions to grade
def getTestSubdirs(testParser, testRoot, questionToGrade): def getTestSubdirs(testParser, testRoot, questionToGrade):
problemDict = testParser.TestParser(os.path.join(testRoot, 'CONFIG')).parse() problemDict = testParser.TestParser(
os.path.join(testRoot, 'CONFIG')).parse()
if questionToGrade != None: if questionToGrade != None:
questions = getDepends(testParser, testRoot, questionToGrade) questions = getDepends(testParser, testRoot, questionToGrade)
if len(questions) > 1: if len(questions) > 1:
print('Note: due to dependencies, the following tests will be run: %s' % ' '.join(questions)) print('Note: due to dependencies, the following tests will be run: %s' %
' '.join(questions))
return questions return questions
if 'order' in problemDict: if 'order' in problemDict:
return problemDict['order'].split() return problemDict['order'].split()
@@ -263,14 +272,16 @@ def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MA
continue continue
# create a question object # create a question object
questionDict = testParser.TestParser(os.path.join(subdir_path, 'CONFIG')).parse() questionDict = testParser.TestParser(
os.path.join(subdir_path, 'CONFIG')).parse()
questionClass = getattr(testClasses, questionDict['class']) questionClass = getattr(testClasses, questionDict['class'])
question = questionClass(questionDict, display) question = questionClass(questionDict, display)
questionDicts[q] = questionDict questionDicts[q] = questionDict
# load test cases into question # load test cases into question
tests = filter(lambda t: re.match('[^#~.].*\.test\Z', t), os.listdir(subdir_path)) tests = [t for t in os.listdir(
tests = map(lambda t: re.match('(.*)\.test\Z', t).group(1), tests) subdir_path) if re.match('[^#~.].*\.test\Z', t)]
tests = [re.match('(.*)\.test\Z', t).group(1) for t in tests]
for t in sorted(tests): for t in sorted(tests):
test_file = os.path.join(subdir_path, '%s.test' % t) test_file = os.path.join(subdir_path, '%s.test' % t)
solution_file = os.path.join(subdir_path, '%s.solution' % t) solution_file = os.path.join(subdir_path, '%s.solution' % t)
@@ -281,6 +292,7 @@ def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MA
testDict['test_out_file'] = test_out_file testDict['test_out_file'] = test_out_file
testClass = getattr(projectTestClasses, testDict['class']) testClass = getattr(projectTestClasses, testDict['class'])
testCase = testClass(question, testDict) testCase = testClass(question, testDict)
def makefun(testCase, solution_file): def makefun(testCase, solution_file):
if generateSolutions: if generateSolutions:
# write solution file to disk # write solution file to disk
@@ -308,11 +320,10 @@ def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MA
for prereq in questionDicts[q].get('depends', '').split(): for prereq in questionDicts[q].get('depends', '').split():
grades.addPrereq(q, prereq) grades.addPrereq(q, prereq)
grades.grade(sys.modules[__name__], bonusPic = projectParams.BONUS_PIC) grades.grade(sys.modules[__name__], bonusPic=projectParams.BONUS_PIC)
return grades.points return grades.points
def getDisplay(graphicsByDefault, options=None): def getDisplay(graphicsByDefault, options=None):
graphics = graphicsByDefault graphics = graphicsByDefault
if options is not None and options.noGraphics: if options is not None and options.noGraphics:
@@ -327,8 +338,6 @@ def getDisplay(graphicsByDefault, options=None):
return textDisplay.NullGraphics() return textDisplay.NullGraphics()
if __name__ == '__main__': if __name__ == '__main__':
options = readCommand(sys.argv) options = readCommand(sys.argv)
if options.generateSolutions: if options.generateSolutions:
@@ -344,15 +353,17 @@ if __name__ == '__main__':
moduleDict = {} moduleDict = {}
for cp in codePaths: for cp in codePaths:
moduleName = re.match('.*?([^/]*)\.py', cp).group(1) moduleName = re.match('.*?([^/]*)\.py', cp).group(1)
moduleDict[moduleName] = loadModuleFile(moduleName, os.path.join(options.codeRoot, cp)) moduleDict[moduleName] = loadModuleFile(
moduleName, os.path.join(options.codeRoot, cp))
moduleName = re.match('.*?([^/]*)\.py', options.testCaseCode).group(1) moduleName = re.match('.*?([^/]*)\.py', options.testCaseCode).group(1)
moduleDict['projectTestClasses'] = loadModuleFile(moduleName, os.path.join(options.codeRoot, options.testCaseCode)) moduleDict['projectTestClasses'] = loadModuleFile(
moduleName, os.path.join(options.codeRoot, options.testCaseCode))
if options.runTest != None: if options.runTest != None:
runTest(options.runTest, moduleDict, printTestCase=options.printTestCase, display=getDisplay(True, options)) runTest(options.runTest, moduleDict, printTestCase=options.printTestCase,
display=getDisplay(True, options))
else: else:
evaluate(options.generateSolutions, options.testRoot, moduleDict, evaluate(options.generateSolutions, options.testRoot, moduleDict,
gsOutput=options.gsOutput, gsOutput=options.gsOutput,
edxOutput=options.edxOutput, muteOutput=options.muteOutput, printTestCase=options.printTestCase, edxOutput=options.edxOutput, muteOutput=options.muteOutput, printTestCase=options.printTestCase,
questionToGrade=options.gradeQuestion, display=getDisplay(options.gradeQuestion!=None, options)) questionToGrade=options.gradeQuestion, display=getDisplay(options.gradeQuestion != None, options))
-21
View File
@@ -1,21 +0,0 @@
python pacman.py
python pacman.py --layout testMaze --pacman GoWestAgent
python pacman.py --layout tinyMaze --pacman GoWestAgent
python pacman.py -h
python pacman.py -l tinyMaze -p SearchAgent -a fn=tinyMazeSearch
python pacman.py -l tinyMaze -p SearchAgent
python pacman.py -l mediumMaze -p SearchAgent
python pacman.py -l bigMaze -z .5 -p SearchAgent
python pacman.py -l mediumMaze -p SearchAgent -a fn=bfs
python pacman.py -l bigMaze -p SearchAgent -a fn=bfs -z .5
python eightpuzzle.py
python pacman.py -l mediumMaze -p SearchAgent -a fn=ucs
python pacman.py -l mediumDottedMaze -p StayEastSearchAgent
python pacman.py -l mediumScaryMaze -p StayWestSearchAgent
python pacman.py -l bigMaze -z .5 -p SearchAgent -a fn=astar,heuristic=manhattanHeuristic
python pacman.py -l tinyCorners -p SearchAgent -a fn=bfs,prob=CornersProblem
python pacman.py -l mediumCorners -p SearchAgent -a fn=bfs,prob=CornersProblem
python pacman.py -l mediumCorners -p AStarCornersAgent -z 0.5
python pacman.py -l testSearch -p AStarFoodSearchAgent
python pacman.py -l trickySearch -p AStarFoodSearchAgent
python pacman.py -l bigSearch -p ClosestDotSearchAgent -z .5
-281
View File
@@ -1,281 +0,0 @@
# eightpuzzle.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 search
import random
# Module Classes
class EightPuzzleState:
"""
The Eight Puzzle is described in the course textbook on
page 64.
This class defines the mechanics of the puzzle itself. The
task of recasting this puzzle as a search problem is left to
the EightPuzzleSearchProblem class.
"""
def __init__( self, numbers ):
"""
Constructs a new eight puzzle from an ordering of numbers.
numbers: a list of integers from 0 to 8 representing an
instance of the eight puzzle. 0 represents the blank
space. Thus, the list
[1, 0, 2, 3, 4, 5, 6, 7, 8]
represents the eight puzzle:
-------------
| 1 | | 2 |
-------------
| 3 | 4 | 5 |
-------------
| 6 | 7 | 8 |
------------
The configuration of the puzzle is stored in a 2-dimensional
list (a list of lists) 'cells'.
"""
self.cells = []
numbers = numbers[:] # Make a copy so as not to cause side-effects.
numbers.reverse()
for row in range( 3 ):
self.cells.append( [] )
for col in range( 3 ):
self.cells[row].append( numbers.pop() )
if self.cells[row][col] == 0:
self.blankLocation = row, col
def isGoal( self ):
"""
Checks to see if the puzzle is in its goal state.
-------------
| | 1 | 2 |
-------------
| 3 | 4 | 5 |
-------------
| 6 | 7 | 8 |
-------------
>>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]).isGoal()
True
>>> EightPuzzleState([1, 0, 2, 3, 4, 5, 6, 7, 8]).isGoal()
False
"""
current = 0
for row in range( 3 ):
for col in range( 3 ):
if current != self.cells[row][col]:
return False
current += 1
return True
def legalMoves( self ):
"""
Returns a list of legal moves from the current state.
Moves consist of moving the blank space up, down, left or right.
These are encoded as 'up', 'down', 'left' and 'right' respectively.
>>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]).legalMoves()
['down', 'right']
"""
moves = []
row, col = self.blankLocation
if(row != 0):
moves.append('up')
if(row != 2):
moves.append('down')
if(col != 0):
moves.append('left')
if(col != 2):
moves.append('right')
return moves
def result(self, move):
"""
Returns a new eightPuzzle with the current state and blankLocation
updated based on the provided move.
The move should be a string drawn from a list returned by legalMoves.
Illegal moves will raise an exception, which may be an array bounds
exception.
NOTE: This function *does not* change the current object. Instead,
it returns a new object.
"""
row, col = self.blankLocation
if(move == 'up'):
newrow = row - 1
newcol = col
elif(move == 'down'):
newrow = row + 1
newcol = col
elif(move == 'left'):
newrow = row
newcol = col - 1
elif(move == 'right'):
newrow = row
newcol = col + 1
else:
raise "Illegal Move"
# Create a copy of the current eightPuzzle
newPuzzle = EightPuzzleState([0, 0, 0, 0, 0, 0, 0, 0, 0])
newPuzzle.cells = [values[:] for values in self.cells]
# And update it to reflect the move
newPuzzle.cells[row][col] = self.cells[newrow][newcol]
newPuzzle.cells[newrow][newcol] = self.cells[row][col]
newPuzzle.blankLocation = newrow, newcol
return newPuzzle
# Utilities for comparison and display
def __eq__(self, other):
"""
Overloads '==' such that two eightPuzzles with the same configuration
are equal.
>>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]) == \
EightPuzzleState([1, 0, 2, 3, 4, 5, 6, 7, 8]).result('left')
True
"""
for row in range( 3 ):
if self.cells[row] != other.cells[row]:
return False
return True
def __hash__(self):
return hash(str(self.cells))
def __getAsciiString(self):
"""
Returns a display string for the maze
"""
lines = []
horizontalLine = ('-' * (13))
lines.append(horizontalLine)
for row in self.cells:
rowLine = '|'
for col in row:
if col == 0:
col = ' '
rowLine = rowLine + ' ' + col.__str__() + ' |'
lines.append(rowLine)
lines.append(horizontalLine)
return '\n'.join(lines)
def __str__(self):
return self.__getAsciiString()
# TODO: Implement The methods in this class
class EightPuzzleSearchProblem(search.SearchProblem):
"""
Implementation of a SearchProblem for the Eight Puzzle domain
Each state is represented by an instance of an eightPuzzle.
"""
def __init__(self,puzzle):
"Creates a new EightPuzzleSearchProblem which stores search information."
self.puzzle = puzzle
def getStartState(self):
return puzzle
def isGoalState(self,state):
return state.isGoal()
def getSuccessors(self,state):
"""
Returns list of (successor, action, stepCost) pairs where
each succesor is either left, right, up, or down
from the original state and the cost is 1.0 for each
"""
succ = []
for a in state.legalMoves():
succ.append((state.result(a), a, 1))
return succ
def getCostOfActions(self, actions):
"""
actions: A list of actions to take
This method returns the total cost of a particular sequence of actions. The sequence must
be composed of legal moves
"""
return len(actions)
EIGHT_PUZZLE_DATA = [[1, 0, 2, 3, 4, 5, 6, 7, 8],
[1, 7, 8, 2, 3, 4, 5, 6, 0],
[4, 3, 2, 7, 0, 5, 1, 6, 8],
[5, 1, 3, 4, 0, 2, 6, 7, 8],
[1, 2, 5, 7, 6, 8, 0, 4, 3],
[0, 3, 1, 6, 8, 2, 7, 5, 4]]
def loadEightPuzzle(puzzleNumber):
"""
puzzleNumber: The number of the eight puzzle to load.
Returns an eight puzzle object generated from one of the
provided puzzles in EIGHT_PUZZLE_DATA.
puzzleNumber can range from 0 to 5.
>>> print(loadEightPuzzle(0))
-------------
| 1 | | 2 |
-------------
| 3 | 4 | 5 |
-------------
| 6 | 7 | 8 |
-------------
"""
return EightPuzzleState(EIGHT_PUZZLE_DATA[puzzleNumber])
def createRandomEightPuzzle(moves=100):
"""
moves: number of random moves to apply
Creates a random eight puzzle by applying
a series of 'moves' random moves to a solved
puzzle.
"""
puzzle = EightPuzzleState([0,1,2,3,4,5,6,7,8])
for i in range(moves):
# Execute a random legal move
puzzle = puzzle.result(random.sample(puzzle.legalMoves(), 1)[0])
return puzzle
if __name__ == '__main__':
puzzle = createRandomEightPuzzle(25)
print('A random puzzle:')
print(puzzle)
problem = EightPuzzleSearchProblem(puzzle)
path = search.breadthFirstSearch(problem)
print('BFS found a path of %d moves: %s' % (len(path), str(path)))
curr = puzzle
i = 1
for a in path:
curr = curr.result(a)
print('After %d move%s: %s' % (i, ("", "s")[i>1], a))
print(curr)
input("Press return for the next state...") # wait for key stroke
i += 1
+149 -100
View File
@@ -21,7 +21,8 @@
# For more info, see http://inst.eecs.berkeley.edu/~cs188/sp09/pacman.html # For more info, see http://inst.eecs.berkeley.edu/~cs188/sp09/pacman.html
from util import * from util import *
import time, os import time
import os
import traceback import traceback
import sys import sys
@@ -29,6 +30,7 @@ import sys
# Parts worth reading # # Parts worth reading #
####################### #######################
class Agent: class Agent:
""" """
An agent must define a getAction method, but may also define the An agent must define a getAction method, but may also define the
@@ -36,6 +38,7 @@ class Agent:
def registerInitialState(self, state): # inspects the starting state def registerInitialState(self, state): # inspects the starting state
""" """
def __init__(self, index=0): def __init__(self, index=0):
self.index = index self.index = index
@@ -46,6 +49,7 @@ class Agent:
""" """
raiseNotDefined() raiseNotDefined()
class Directions: class Directions:
NORTH = 'North' NORTH = 'North'
SOUTH = 'South' SOUTH = 'South'
@@ -59,7 +63,7 @@ class Directions:
WEST: SOUTH, WEST: SOUTH,
STOP: STOP} STOP: STOP}
RIGHT = dict([(y,x) for x, y in LEFT.items()]) RIGHT = dict([(y, x) for x, y in list(LEFT.items())])
REVERSE = {NORTH: SOUTH, REVERSE = {NORTH: SOUTH,
SOUTH: NORTH, SOUTH: NORTH,
@@ -67,6 +71,7 @@ class Directions:
WEST: EAST, WEST: EAST,
STOP: STOP} STOP: STOP}
class Configuration: class Configuration:
""" """
A Configuration holds the (x,y) coordinate of a character, along with its A Configuration holds the (x,y) coordinate of a character, along with its
@@ -87,11 +92,12 @@ class Configuration:
return self.direction return self.direction
def isInteger(self): def isInteger(self):
x,y = self.pos x, y = self.pos
return x == int(x) and y == int(y) return x == int(x) and y == int(y)
def __eq__(self, other): def __eq__(self, other):
if other == None: return False if other == None:
return False
return (self.pos == other.pos and self.direction == other.direction) return (self.pos == other.pos and self.direction == other.direction)
def __hash__(self): def __hash__(self):
@@ -110,33 +116,35 @@ class Configuration:
Actions are movement vectors. Actions are movement vectors.
""" """
x, y= self.pos x, y = self.pos
dx, dy = vector dx, dy = vector
direction = Actions.vectorToDirection(vector) direction = Actions.vectorToDirection(vector)
if direction == Directions.STOP: if direction == Directions.STOP:
direction = self.direction # There is no stop direction direction = self.direction # There is no stop direction
return Configuration((x + dx, y+dy), direction) return Configuration((x + dx, y+dy), direction)
class AgentState: class AgentState:
""" """
AgentStates hold the state of an agent (configuration, speed, scared, etc). AgentStates hold the state of an agent (configuration, speed, scared, etc).
""" """
def __init__( self, startConfiguration, isPacman ): def __init__(self, startConfiguration, isPacman):
self.start = startConfiguration self.start = startConfiguration
self.configuration = startConfiguration self.configuration = startConfiguration
self.isPacman = isPacman self.isPacman = isPacman
self.scaredTimer = 0 self.scaredTimer = 0
# state below potentially used for contest only
self.numCarrying = 0 self.numCarrying = 0
self.numReturned = 0 self.numReturned = 0
def __str__( self ): def __str__(self):
if self.isPacman: if self.isPacman:
return "Pacman: " + str( self.configuration ) return "Pacman: " + str(self.configuration)
else: else:
return "Ghost: " + str( self.configuration ) return "Ghost: " + str(self.configuration)
def __eq__( self, other ): def __eq__(self, other):
if other == None: if other == None:
return False return False
return self.configuration == other.configuration and self.scaredTimer == other.scaredTimer return self.configuration == other.configuration and self.scaredTimer == other.scaredTimer
@@ -144,8 +152,8 @@ class AgentState:
def __hash__(self): def __hash__(self):
return hash(hash(self.configuration) + 13 * hash(self.scaredTimer)) return hash(hash(self.configuration) + 13 * hash(self.scaredTimer))
def copy( self ): def copy(self):
state = AgentState( self.start, self.isPacman ) state = AgentState(self.start, self.isPacman)
state.configuration = self.configuration state.configuration = self.configuration
state.scaredTimer = self.scaredTimer state.scaredTimer = self.scaredTimer
state.numCarrying = self.numCarrying state.numCarrying = self.numCarrying
@@ -153,12 +161,14 @@ class AgentState:
return state return state
def getPosition(self): def getPosition(self):
if self.configuration == None: return None if self.configuration == None:
return None
return self.configuration.getPosition() return self.configuration.getPosition()
def getDirection(self): def getDirection(self):
return self.configuration.getDirection() return self.configuration.getDirection()
class Grid: class Grid:
""" """
A 2-dimensional array of objects backed by a list of lists. Data is accessed A 2-dimensional array of objects backed by a list of lists. Data is accessed
@@ -167,13 +177,16 @@ class Grid:
The __str__ method constructs an output that is oriented like a pacman board. The __str__ method constructs an output that is oriented like a pacman board.
""" """
def __init__(self, width, height, initialValue=False, bitRepresentation=None): def __init__(self, width, height, initialValue=False, bitRepresentation=None):
if initialValue not in [False, True]: raise Exception('Grids can only contain booleans') if initialValue not in [False, True]:
raise Exception('Grids can only contain booleans')
self.CELLS_PER_INT = 30 self.CELLS_PER_INT = 30
self.width = width self.width = width
self.height = height self.height = height
self.data = [[initialValue for y in range(height)] for x in range(width)] self.data = [[initialValue for y in range(
height)] for x in range(width)]
if bitRepresentation: if bitRepresentation:
self._unpackBits(bitRepresentation) self._unpackBits(bitRepresentation)
@@ -184,12 +197,14 @@ class Grid:
self.data[key] = item self.data[key] = item
def __str__(self): def __str__(self):
out = [[str(self.data[x][y])[0] for x in range(self.width)] for y in range(self.height)] out = [[str(self.data[x][y])[0] for x in range(self.width)]
for y in range(self.height)]
out.reverse() out.reverse()
return '\n'.join([''.join(x) for x in out]) return '\n'.join([''.join(x) for x in out])
def __eq__(self, other): def __eq__(self, other):
if other == None: return False if other == None:
return False
return self.data == other.data return self.data == other.data
def __hash__(self): def __hash__(self):
@@ -216,14 +231,15 @@ class Grid:
g.data = self.data g.data = self.data
return g return g
def count(self, item =True ): def count(self, item=True):
return sum([x.count(item) for x in self.data]) return sum([x.count(item) for x in self.data])
def asList(self, key = True): def asList(self, key=True):
list = [] list = []
for x in range(self.width): for x in range(self.width):
for y in range(self.height): for y in range(self.height):
if self[x][y] == key: list.append( (x,y) ) if self[x][y] == key:
list.append((x, y))
return list return list
def packBits(self): def packBits(self):
@@ -246,7 +262,7 @@ class Grid:
return tuple(bits) return tuple(bits)
def _cellIndexToPosition(self, index): def _cellIndexToPosition(self, index):
x = index // self.height x = index / self.height
y = index % self.height y = index % self.height
return x, y return x, y
@@ -257,14 +273,16 @@ class Grid:
cell = 0 cell = 0
for packed in bits: for packed in bits:
for bit in self._unpackInt(packed, self.CELLS_PER_INT): for bit in self._unpackInt(packed, self.CELLS_PER_INT):
if cell == self.width * self.height: break if cell == self.width * self.height:
break
x, y = self._cellIndexToPosition(cell) x, y = self._cellIndexToPosition(cell)
self[x][y] = bit self[x][y] = bit
cell += 1 cell += 1
def _unpackInt(self, packed, size): def _unpackInt(self, packed, size):
bools = [] bools = []
if packed < 0: raise ValueError("must be a positive integer") if packed < 0:
raise ValueError("must be a positive integer")
for i in range(size): for i in range(size):
n = 2 ** (self.CELLS_PER_INT - i - 1) n = 2 ** (self.CELLS_PER_INT - i - 1)
if packed >= n: if packed >= n:
@@ -274,28 +292,30 @@ class Grid:
bools.append(False) bools.append(False)
return bools return bools
def reconstituteGrid(bitRep): def reconstituteGrid(bitRep):
if type(bitRep) is not type((1,2)): if type(bitRep) is not type((1, 2)):
return bitRep return bitRep
width, height = bitRep[:2] width, height = bitRep[:2]
return Grid(width, height, bitRepresentation= bitRep[2:]) return Grid(width, height, bitRepresentation=bitRep[2:])
#################################### ####################################
# Parts you shouldn't have to read # # Parts you shouldn't have to read #
#################################### ####################################
class Actions: class Actions:
""" """
A collection of static methods for manipulating move actions. A collection of static methods for manipulating move actions.
""" """
# Directions # Directions
_directions = {Directions.NORTH: (0, 1), _directions = {Directions.WEST: (-1, 0),
Directions.SOUTH: (0, -1), Directions.STOP: (0, 0),
Directions.EAST: (1, 0), Directions.EAST: (1, 0),
Directions.WEST: (-1, 0), Directions.NORTH: (0, 1),
Directions.STOP: (0, 0)} Directions.SOUTH: (0, -1)}
_directionsAsList = _directions.items() _directionsAsList = [('West', (-1, 0)), ('Stop', (0, 0)), ('East', (1, 0)), ('North', (0, 1)), ('South', (0, -1))]
TOLERANCE = .001 TOLERANCE = .001
@@ -324,7 +344,7 @@ class Actions:
return Directions.STOP return Directions.STOP
vectorToDirection = staticmethod(vectorToDirection) vectorToDirection = staticmethod(vectorToDirection)
def directionToVector(direction, speed = 1.0): def directionToVector(direction, speed=1.0):
dx, dy = Actions._directions[direction] dx, dy = Actions._directions[direction]
return (dx * speed, dy * speed) return (dx * speed, dy * speed)
directionToVector = staticmethod(directionToVector) directionToVector = staticmethod(directionToVector)
@@ -342,23 +362,27 @@ class Actions:
dx, dy = vec dx, dy = vec
next_y = y_int + dy next_y = y_int + dy
next_x = x_int + dx next_x = x_int + dx
if not walls[next_x][next_y]: possible.append(dir) if not walls[next_x][next_y]:
possible.append(dir)
return possible return possible
getPossibleActions = staticmethod(getPossibleActions) getPossibleActions = staticmethod(getPossibleActions)
def getLegalNeighbors(position, walls): def getLegalNeighbors(position, walls):
x,y = position x, y = position
x_int, y_int = int(x + 0.5), int(y + 0.5) x_int, y_int = int(x + 0.5), int(y + 0.5)
neighbors = [] neighbors = []
for dir, vec in Actions._directionsAsList: for dir, vec in Actions._directionsAsList:
dx, dy = vec dx, dy = vec
next_x = x_int + dx next_x = x_int + dx
if next_x < 0 or next_x == walls.width: continue if next_x < 0 or next_x == walls.width:
continue
next_y = y_int + dy next_y = y_int + dy
if next_y < 0 or next_y == walls.height: continue if next_y < 0 or next_y == walls.height:
if not walls[next_x][next_y]: neighbors.append((next_x, next_y)) continue
if not walls[next_x][next_y]:
neighbors.append((next_x, next_y))
return neighbors return neighbors
getLegalNeighbors = staticmethod(getLegalNeighbors) getLegalNeighbors = staticmethod(getLegalNeighbors)
@@ -368,18 +392,17 @@ class Actions:
return (x + dx, y + dy) return (x + dx, y + dy)
getSuccessor = staticmethod(getSuccessor) getSuccessor = staticmethod(getSuccessor)
class GameStateData:
"""
""" class GameStateData:
def __init__( self, prevState = None ):
def __init__(self, prevState=None):
""" """
Generates a new data packet by copying information from its predecessor. Generates a new data packet by copying information from its predecessor.
""" """
if prevState != None: if prevState != None:
self.food = prevState.food.shallowCopy() self.food = prevState.food.shallowCopy()
self.capsules = prevState.capsules[:] self.capsules = prevState.capsules[:]
self.agentStates = self.copyAgentStates( prevState.agentStates ) self.agentStates = self.copyAgentStates(prevState.agentStates)
self.layout = prevState.layout self.layout = prevState.layout
self._eaten = prevState._eaten self._eaten = prevState._eaten
self.score = prevState.score self.score = prevState.score
@@ -392,8 +415,8 @@ class GameStateData:
self._win = False self._win = False
self.scoreChange = 0 self.scoreChange = 0
def deepCopy( self ): def deepCopy(self):
state = GameStateData( self ) state = GameStateData(self)
state.food = self.food.deepCopy() state.food = self.food.deepCopy()
state.layout = self.layout.deepCopy() state.layout = self.layout.deepCopy()
state._agentMoved = self._agentMoved state._agentMoved = self._agentMoved
@@ -402,40 +425,45 @@ class GameStateData:
state._capsuleEaten = self._capsuleEaten state._capsuleEaten = self._capsuleEaten
return state return state
def copyAgentStates( self, agentStates ): def copyAgentStates(self, agentStates):
copiedStates = [] copiedStates = []
for agentState in agentStates: for agentState in agentStates:
copiedStates.append( agentState.copy() ) copiedStates.append(agentState.copy())
return copiedStates return copiedStates
def __eq__( self, other ): def __eq__(self, other):
""" """
Allows two states to be compared. Allows two states to be compared.
""" """
if other == None: return False if other == None:
return False
# TODO Check for type of other # TODO Check for type of other
if not self.agentStates == other.agentStates: return False if not self.agentStates == other.agentStates:
if not self.food == other.food: return False return False
if not self.capsules == other.capsules: return False if not self.food == other.food:
if not self.score == other.score: return False return False
if not self.capsules == other.capsules:
return False
if not self.score == other.score:
return False
return True return True
def __hash__( self ): def __hash__(self):
""" """
Allows states to be keys of dictionaries. Allows states to be keys of dictionaries.
""" """
for i, state in enumerate( self.agentStates ): for i, state in enumerate(self.agentStates):
try: try:
int(hash(state)) int(hash(state))
except TypeError as e: except TypeError as e:
print(e) print(e)
#hash(state) # hash(state)
return int((hash(tuple(self.agentStates)) + 13*hash(self.food) + 113* hash(tuple(self.capsules)) + 7 * hash(self.score)) % 1048575 ) return int((hash(tuple(self.agentStates)) + 13*hash(self.food) + 113 * hash(tuple(self.capsules)) + 7 * hash(self.score)) % 1048575)
def __str__( self ): def __str__(self):
width, height = self.layout.width, self.layout.height width, height = self.layout.width, self.layout.height
map = Grid(width, height) map = Grid(width, height)
if type(self.food) == type((1,2)): if type(self.food) == type((1, 2)):
self.food = reconstituteGrid(self.food) self.food = reconstituteGrid(self.food)
for x in range(width): for x in range(width):
for y in range(height): for y in range(height):
@@ -443,21 +471,23 @@ class GameStateData:
map[x][y] = self._foodWallStr(food[x][y], walls[x][y]) map[x][y] = self._foodWallStr(food[x][y], walls[x][y])
for agentState in self.agentStates: for agentState in self.agentStates:
if agentState == None: continue if agentState == None:
if agentState.configuration == None: continue continue
x,y = [int( i ) for i in nearestPoint( agentState.configuration.pos )] if agentState.configuration == None:
continue
x, y = [int(i) for i in nearestPoint(agentState.configuration.pos)]
agent_dir = agentState.configuration.direction agent_dir = agentState.configuration.direction
if agentState.isPacman: if agentState.isPacman:
map[x][y] = self._pacStr( agent_dir ) map[x][y] = self._pacStr(agent_dir)
else: else:
map[x][y] = self._ghostStr( agent_dir ) map[x][y] = self._ghostStr(agent_dir)
for x, y in self.capsules: for x, y in self.capsules:
map[x][y] = 'o' map[x][y] = 'o'
return str(map) + ("\nScore: %d\n" % self.score) return str(map) + ("\nScore: %d\n" % self.score)
def _foodWallStr( self, hasFood, hasWall ): def _foodWallStr(self, hasFood, hasWall):
if hasFood: if hasFood:
return '.' return '.'
elif hasWall: elif hasWall:
@@ -465,7 +495,7 @@ class GameStateData:
else: else:
return ' ' return ' '
def _pacStr( self, dir ): def _pacStr(self, dir):
if dir == Directions.NORTH: if dir == Directions.NORTH:
return 'v' return 'v'
if dir == Directions.SOUTH: if dir == Directions.SOUTH:
@@ -474,7 +504,7 @@ class GameStateData:
return '>' return '>'
return '<' return '<'
def _ghostStr( self, dir ): def _ghostStr(self, dir):
return 'G' return 'G'
if dir == Directions.NORTH: if dir == Directions.NORTH:
return 'M' return 'M'
@@ -484,7 +514,7 @@ class GameStateData:
return '3' return '3'
return 'E' return 'E'
def initialize( self, layout, numGhostAgents ): def initialize(self, layout, numGhostAgents):
""" """
Creates an initial game state from a layout array (see layout.py). Creates an initial game state from a layout array (see layout.py).
""" """
@@ -499,23 +529,28 @@ class GameStateData:
numGhosts = 0 numGhosts = 0
for isPacman, pos in layout.agentPositions: for isPacman, pos in layout.agentPositions:
if not isPacman: if not isPacman:
if numGhosts == numGhostAgents: continue # Max ghosts reached already if numGhosts == numGhostAgents:
else: numGhosts += 1 continue # Max ghosts reached already
self.agentStates.append( AgentState( Configuration( pos, Directions.STOP), isPacman) ) else:
numGhosts += 1
self.agentStates.append(AgentState(
Configuration(pos, Directions.STOP), isPacman))
self._eaten = [False for a in self.agentStates] self._eaten = [False for a in self.agentStates]
try: try:
import boinc import boinc
_BOINC_ENABLED = True _BOINC_ENABLED = True
except: except:
_BOINC_ENABLED = False _BOINC_ENABLED = False
class Game: class Game:
""" """
The Game manages the control flow, soliciting actions from agents. The Game manages the control flow, soliciting actions from agents.
""" """
def __init__( self, agents, display, rules, startingIndex=0, muteAgents=False, catchExceptions=False ): def __init__(self, agents, display, rules, startingIndex=0, muteAgents=False, catchExceptions=False):
self.agentCrashed = False self.agentCrashed = False
self.agents = agents self.agents = agents
self.display = display self.display = display
@@ -537,9 +572,10 @@ class Game:
else: else:
return self.rules.getProgress(self) return self.rules.getProgress(self)
def _agentCrash( self, agentIndex, quiet=False): def _agentCrash(self, agentIndex, quiet=False):
"Helper method for handling agent crashes" "Helper method for handling agent crashes"
if not quiet: traceback.print_exc() if not quiet:
traceback.print_exc()
self.gameOver = True self.gameOver = True
self.agentCrashed = True self.agentCrashed = True
self.rules.agentCrash(self, agentIndex) self.rules.agentCrash(self, agentIndex)
@@ -548,7 +584,8 @@ class Game:
OLD_STDERR = None OLD_STDERR = None
def mute(self, agentIndex): def mute(self, agentIndex):
if not self.muteAgents: return if not self.muteAgents:
return
global OLD_STDOUT, OLD_STDERR global OLD_STDOUT, OLD_STDERR
import io import io
OLD_STDOUT = sys.stdout OLD_STDOUT = sys.stdout
@@ -557,21 +594,21 @@ class Game:
sys.stderr = self.agentOutput[agentIndex] sys.stderr = self.agentOutput[agentIndex]
def unmute(self): def unmute(self):
if not self.muteAgents: return if not self.muteAgents:
return
global OLD_STDOUT, OLD_STDERR global OLD_STDOUT, OLD_STDERR
# Revert stdout/stderr to originals # Revert stdout/stderr to originals
sys.stdout = OLD_STDOUT sys.stdout = OLD_STDOUT
sys.stderr = OLD_STDERR sys.stderr = OLD_STDERR
def run(self):
def run( self ):
""" """
Main control loop for game play. Main control loop for game play.
""" """
self.display.initialize(self.state.data) self.display.initialize(self.state.data)
self.numMoves = 0 self.numMoves = 0
###self.display.initialize(self.state.makeObservation(1).data) # self.display.initialize(self.state.makeObservation(1).data)
# inform learning agents of the game start # inform learning agents of the game start
for i in range(len(self.agents)): for i in range(len(self.agents)):
agent = self.agents[i] agent = self.agents[i]
@@ -587,14 +624,16 @@ class Game:
self.mute(i) self.mute(i)
if self.catchExceptions: if self.catchExceptions:
try: try:
timed_func = TimeoutFunction(agent.registerInitialState, int(self.rules.getMaxStartupTime(i))) timed_func = TimeoutFunction(
agent.registerInitialState, int(self.rules.getMaxStartupTime(i)))
try: try:
start_time = time.time() start_time = time.time()
timed_func(self.state.deepCopy()) timed_func(self.state.deepCopy())
time_taken = time.time() - start_time time_taken = time.time() - start_time
self.totalAgentTimes[i] += time_taken self.totalAgentTimes[i] += time_taken
except TimeoutFunctionException: except TimeoutFunctionException:
print("Agent %d ran out of time on startup!" % i, file=sys.stderr) print("Agent %d ran out of time on startup!" %
i, file=sys.stderr)
self.unmute() self.unmute()
self.agentTimeout = True self.agentTimeout = True
self._agentCrash(i, quiet=True) self._agentCrash(i, quiet=True)
@@ -605,11 +644,11 @@ class Game:
return return
else: else:
agent.registerInitialState(self.state.deepCopy()) agent.registerInitialState(self.state.deepCopy())
## TODO: could this exceed the total time # TODO: could this exceed the total time
self.unmute() self.unmute()
agentIndex = self.startingIndex agentIndex = self.startingIndex
numAgents = len( self.agents ) numAgents = len(self.agents)
while not self.gameOver: while not self.gameOver:
# Fetch the next agent # Fetch the next agent
@@ -617,11 +656,12 @@ class Game:
move_time = 0 move_time = 0
skip_action = False skip_action = False
# Generate an observation of the state # Generate an observation of the state
if 'observationFunction' in dir( agent ): if 'observationFunction' in dir(agent):
self.mute(agentIndex) self.mute(agentIndex)
if self.catchExceptions: if self.catchExceptions:
try: try:
timed_func = TimeoutFunction(agent.observationFunction, int(self.rules.getMoveTimeout(agentIndex))) timed_func = TimeoutFunction(agent.observationFunction, int(
self.rules.getMoveTimeout(agentIndex)))
try: try:
start_time = time.time() start_time = time.time()
observation = timed_func(self.state.deepCopy()) observation = timed_func(self.state.deepCopy())
@@ -634,7 +674,8 @@ class Game:
self.unmute() self.unmute()
return return
else: else:
observation = agent.observationFunction(self.state.deepCopy()) observation = agent.observationFunction(
self.state.deepCopy())
self.unmute() self.unmute()
else: else:
observation = self.state.deepCopy() observation = self.state.deepCopy()
@@ -644,14 +685,16 @@ class Game:
self.mute(agentIndex) self.mute(agentIndex)
if self.catchExceptions: if self.catchExceptions:
try: try:
timed_func = TimeoutFunction(agent.getAction, int(self.rules.getMoveTimeout(agentIndex)) - int(move_time)) timed_func = TimeoutFunction(agent.getAction, int(
self.rules.getMoveTimeout(agentIndex)) - int(move_time))
try: try:
start_time = time.time() start_time = time.time()
if skip_action: if skip_action:
raise TimeoutFunctionException() raise TimeoutFunctionException()
action = timed_func( observation ) action = timed_func(observation)
except TimeoutFunctionException: except TimeoutFunctionException:
print("Agent %d timed out on a single move!" % agentIndex, file=sys.stderr) print("Agent %d timed out on a single move!" %
agentIndex, file=sys.stderr)
self.agentTimeout = True self.agentTimeout = True
self._agentCrash(agentIndex, quiet=True) self._agentCrash(agentIndex, quiet=True)
self.unmute() self.unmute()
@@ -661,18 +704,21 @@ class Game:
if move_time > self.rules.getMoveWarningTime(agentIndex): if move_time > self.rules.getMoveWarningTime(agentIndex):
self.totalAgentTimeWarnings[agentIndex] += 1 self.totalAgentTimeWarnings[agentIndex] += 1
print("Agent %d took too long to make a move! This is warning %d" % (agentIndex, self.totalAgentTimeWarnings[agentIndex]), file=sys.stderr) print("Agent %d took too long to make a move! This is warning %d" % (
agentIndex, self.totalAgentTimeWarnings[agentIndex]), file=sys.stderr)
if self.totalAgentTimeWarnings[agentIndex] > self.rules.getMaxTimeWarnings(agentIndex): if self.totalAgentTimeWarnings[agentIndex] > self.rules.getMaxTimeWarnings(agentIndex):
print("Agent %d exceeded the maximum number of warnings: %d" % (agentIndex, self.totalAgentTimeWarnings[agentIndex]), file=sys.stderr) print("Agent %d exceeded the maximum number of warnings: %d" % (
agentIndex, self.totalAgentTimeWarnings[agentIndex]), file=sys.stderr)
self.agentTimeout = True self.agentTimeout = True
self._agentCrash(agentIndex, quiet=True) self._agentCrash(agentIndex, quiet=True)
self.unmute() self.unmute()
return return
self.totalAgentTimes[agentIndex] += move_time self.totalAgentTimes[agentIndex] += move_time
#print("Agent: %d, time: %f, total: %f" % (agentIndex, move_time, self.totalAgentTimes[agentIndex])) # print "Agent: %d, time: %f, total: %f" % (agentIndex, move_time, self.totalAgentTimes[agentIndex])
if self.totalAgentTimes[agentIndex] > self.rules.getMaxTotalTime(agentIndex): if self.totalAgentTimes[agentIndex] > self.rules.getMaxTotalTime(agentIndex):
print("Agent %d ran out of time! (time: %1.2f)" % (agentIndex, self.totalAgentTimes[agentIndex]), file=sys.stderr) print("Agent %d ran out of time! (time: %1.2f)" % (
agentIndex, self.totalAgentTimes[agentIndex]), file=sys.stderr)
self.agentTimeout = True self.agentTimeout = True
self._agentCrash(agentIndex, quiet=True) self._agentCrash(agentIndex, quiet=True)
self.unmute() self.unmute()
@@ -687,42 +733,45 @@ class Game:
self.unmute() self.unmute()
# Execute the action # Execute the action
self.moveHistory.append( (agentIndex, action) ) self.moveHistory.append((agentIndex, action))
if self.catchExceptions: if self.catchExceptions:
try: try:
self.state = self.state.generateSuccessor( agentIndex, action ) self.state = self.state.generateSuccessor(
agentIndex, action)
except Exception as data: except Exception as data:
self.mute(agentIndex) self.mute(agentIndex)
self._agentCrash(agentIndex) self._agentCrash(agentIndex)
self.unmute() self.unmute()
return return
else: else:
self.state = self.state.generateSuccessor( agentIndex, action ) self.state = self.state.generateSuccessor(agentIndex, action)
# Change the display # Change the display
self.display.update( self.state.data ) self.display.update(self.state.data)
###idx = agentIndex - agentIndex % 2 + 1 ###idx = agentIndex - agentIndex % 2 + 1
###self.display.update( self.state.makeObservation(idx).data ) ###self.display.update( self.state.makeObservation(idx).data )
# Allow for game specific conditions (winning, losing, etc.) # Allow for game specific conditions (winning, losing, etc.)
self.rules.process(self.state, self) self.rules.process(self.state, self)
# Track progress # Track progress
if agentIndex == numAgents + 1: self.numMoves += 1 if agentIndex == numAgents + 1:
self.numMoves += 1
# Next agent # Next agent
agentIndex = ( agentIndex + 1 ) % numAgents agentIndex = (agentIndex + 1) % numAgents
if _BOINC_ENABLED: if _BOINC_ENABLED:
boinc.set_fraction_done(self.getProgress()) boinc.set_fraction_done(self.getProgress())
# inform a learning agent of the game result # inform a learning agent of the game result
for agentIndex, agent in enumerate(self.agents): for agentIndex, agent in enumerate(self.agents):
if "final" in dir( agent ) : if "final" in dir(agent):
try: try:
self.mute(agentIndex) self.mute(agentIndex)
agent.final( self.state ) agent.final(self.state)
self.unmute() self.unmute()
except Exception as data: except Exception as data:
if not self.catchExceptions: raise data if not self.catchExceptions:
raise
self._agentCrash(agentIndex) self._agentCrash(agentIndex)
self.unmute() self.unmute()
return return
+34 -22
View File
@@ -19,63 +19,75 @@ import random
from util import manhattanDistance from util import manhattanDistance
import util import util
class GhostAgent( Agent ):
def __init__( self, index ): class GhostAgent(Agent):
def __init__(self, index):
self.index = index self.index = index
def getAction( self, state ): def getAction(self, state):
dist = self.getDistribution(state) dist = self.getDistribution(state)
if len(dist) == 0: if len(dist) == 0:
return Directions.STOP return Directions.STOP
else: else:
return util.chooseFromDistribution( dist ) return util.chooseFromDistribution(dist)
def getDistribution(self, state): def getDistribution(self, state):
"Returns a Counter encoding a distribution over actions from the provided state." "Returns a Counter encoding a distribution over actions from the provided state."
util.raiseNotDefined() util.raiseNotDefined()
class RandomGhost( GhostAgent ):
class RandomGhost(GhostAgent):
"A ghost that chooses a legal action uniformly at random." "A ghost that chooses a legal action uniformly at random."
def getDistribution( self, state ):
def getDistribution(self, state):
dist = util.Counter() dist = util.Counter()
for a in state.getLegalActions( self.index ): dist[a] = 1.0 for a in state.getLegalActions(self.index):
dist[a] = 1.0
dist.normalize() dist.normalize()
return dist return dist
class DirectionalGhost( GhostAgent ):
class DirectionalGhost(GhostAgent):
"A ghost that prefers to rush Pacman, or flee when scared." "A ghost that prefers to rush Pacman, or flee when scared."
def __init__( self, index, prob_attack=0.8, prob_scaredFlee=0.8 ):
def __init__(self, index, prob_attack=0.8, prob_scaredFlee=0.8):
self.index = index self.index = index
self.prob_attack = prob_attack self.prob_attack = prob_attack
self.prob_scaredFlee = prob_scaredFlee self.prob_scaredFlee = prob_scaredFlee
def getDistribution( self, state ): def getDistribution(self, state):
# Read variables from state # Read variables from state
ghostState = state.getGhostState( self.index ) ghostState = state.getGhostState(self.index)
legalActions = state.getLegalActions( self.index ) legalActions = state.getLegalActions(self.index)
pos = state.getGhostPosition( self.index ) pos = state.getGhostPosition(self.index)
isScared = ghostState.scaredTimer > 0 isScared = ghostState.scaredTimer > 0
speed = 1 speed = 1
if isScared: speed = 0.5 if isScared:
speed = 0.5
actionVectors = [Actions.directionToVector( a, speed ) for a in legalActions] actionVectors = [Actions.directionToVector(
newPositions = [( pos[0]+a[0], pos[1]+a[1] ) for a in actionVectors] a, speed) for a in legalActions]
newPositions = [(pos[0]+a[0], pos[1]+a[1]) for a in actionVectors]
pacmanPosition = state.getPacmanPosition() pacmanPosition = state.getPacmanPosition()
# Select best actions given the state # Select best actions given the state
distancesToPacman = [manhattanDistance( pos, pacmanPosition ) for pos in newPositions] distancesToPacman = [manhattanDistance(
pos, pacmanPosition) for pos in newPositions]
if isScared: if isScared:
bestScore = max( distancesToPacman ) bestScore = max(distancesToPacman)
bestProb = self.prob_scaredFlee bestProb = self.prob_scaredFlee
else: else:
bestScore = min( distancesToPacman ) bestScore = min(distancesToPacman)
bestProb = self.prob_attack bestProb = self.prob_attack
bestActions = [action for action, distance in zip( legalActions, distancesToPacman ) if distance == bestScore] bestActions = [action for action, distance in zip(
legalActions, distancesToPacman) if distance == bestScore]
# Construct distribution # Construct distribution
dist = util.Counter() dist = util.Counter()
for a in bestActions: dist[a] = bestProb / len(bestActions) for a in bestActions:
for a in legalActions: dist[a] += ( 1-bestProb ) / len(legalActions) dist[a] = bestProb / len(bestActions)
for a in legalActions:
dist[a] += (1-bestProb) / len(legalActions)
dist.normalize() dist.normalize()
return dist return dist
+36 -31
View File
@@ -23,8 +23,10 @@ import pdb
from collections import defaultdict from collections import defaultdict
import util import util
class Grades: class Grades:
"A data structure for project grades, along with formatting code to display them" "A data structure for project grades, along with formatting code to display them"
def __init__(self, projectName, questionsAndMaxesList, def __init__(self, projectName, questionsAndMaxesList,
gsOutput=False, edxOutput=False, muteOutput=False): gsOutput=False, edxOutput=False, muteOutput=False):
""" """
@@ -45,13 +47,13 @@ class Grades:
self.mute = muteOutput self.mute = muteOutput
self.prereqs = defaultdict(set) self.prereqs = defaultdict(set)
#print('Autograder transcript for %s' % self.project) # print 'Autograder transcript for %s' % self.project
print('Starting on %d-%d at %d:%02d:%02d' % self.start) print('Starting on %d-%d at %d:%02d:%02d' % self.start)
def addPrereq(self, question, prereq): def addPrereq(self, question, prereq):
self.prereqs[question].add(prereq) self.prereqs[question].add(prereq)
def grade(self, gradingModule, exceptionMap = {}, bonusPic = False): def grade(self, gradingModule, exceptionMap={}, bonusPic=False):
""" """
Grades each question Grades each question
gradingModule: the module with all the grading functions (pass in with sys.modules[__name__]) gradingModule: the module with all the grading functions (pass in with sys.modules[__name__])
@@ -61,35 +63,37 @@ class Grades:
for q in self.questions: for q in self.questions:
print('\nQuestion %s' % q) print('\nQuestion %s' % q)
print('=' * (9 + len(q))) print('=' * (9 + len(q)))
print print()
self.currentQuestion = q self.currentQuestion = q
incompleted = self.prereqs[q].difference(completedQuestions) incompleted = self.prereqs[q].difference(completedQuestions)
if len(incompleted) > 0: if len(incompleted) > 0:
prereq = incompleted.pop() prereq = incompleted.pop()
print( print("""*** NOTE: Make sure to complete Question %s before working on Question %s,
"""*** NOTE: Make sure to complete Question %s before working on Question %s,
*** because Question %s builds upon your answer for Question %s. *** because Question %s builds upon your answer for Question %s.
""" % (prereq, q, q, prereq)) """ % (prereq, q, q, prereq))
continue continue
if self.mute: util.mutePrint() if self.mute:
util.mutePrint()
try: try:
util.TimeoutFunction(getattr(gradingModule, q),1800)(self) # Call the question's function util.TimeoutFunction(getattr(gradingModule, q), 1800)(
#TimeoutFunction(getattr(gradingModule, q),1200)(self) # Call the question's function self) # Call the question's function
# TimeoutFunction(getattr(gradingModule, q),1200)(self) # Call the question's function
except Exception as inst: except Exception as inst:
self.addExceptionMessage(q, inst, traceback) self.addExceptionMessage(q, inst, traceback)
self.addErrorHints(exceptionMap, inst, q[1]) self.addErrorHints(exceptionMap, inst, q[1])
except: except:
self.fail('FAIL: Terminated with a string exception.') self.fail('FAIL: Terminated with a string exception.')
finally: finally:
if self.mute: util.unmutePrint() if self.mute:
util.unmutePrint()
if self.points[q] >= self.maxes[q]: if self.points[q] >= self.maxes[q]:
completedQuestions.add(q) completedQuestions.add(q)
print('\n### Question %s: %d/%d ###\n' % (q, self.points[q], self.maxes[q])) print('\n### Question %s: %d/%d ###\n' %
(q, self.points[q], self.maxes[q]))
print('\nFinished at %d:%02d:%02d' % time.localtime()[3:6]) print('\nFinished at %d:%02d:%02d' % time.localtime()[3:6])
print("\nProvisional grades\n==================") print("\nProvisional grades\n==================")
@@ -97,7 +101,8 @@ class Grades:
for q in self.questions: for q in self.questions:
print('Question %s: %d/%d' % (q, self.points[q], self.maxes[q])) print('Question %s: %d/%d' % (q, self.points[q], self.maxes[q]))
print('------------------') print('------------------')
print('Total: %d/%d' % (self.points.totalCount(), sum(self.maxes.values()))) print('Total: %d/%d' %
(self.points.totalCount(), sum(self.maxes.values())))
if bonusPic and self.points.totalCount() == 25: if bonusPic and self.points.totalCount() == 25:
print(""" print("""
@@ -181,7 +186,8 @@ to follow your instructor's guidelines to receive credit on your project.
total_score = sum(self.points.values()) total_score = sum(self.points.values())
out_dct['score'] = total_score out_dct['score'] = total_score
out_dct['max_score'] = total_possible out_dct['max_score'] = total_possible
out_dct['output'] = "Total score (%d / %d)" % (total_score, total_possible) out_dct['output'] = "Total score (%d / %d)" % (
total_score, total_possible)
# individual tests # individual tests
tests_out = [] tests_out = []
@@ -223,9 +229,9 @@ to follow your instructor's guidelines to receive credit on your project.
<h3> <h3>
Total score ({total_score} / {total_possible}) Total score ({total_score} / {total_possible})
</h3> </h3>
""".format(total_score = total_score, """.format(total_score=total_score,
total_possible = total_possible, total_possible=total_possible,
checkOrX = checkOrX checkOrX=checkOrX
) )
edxOutput.write(header) edxOutput.write(header)
@@ -250,14 +256,14 @@ to follow your instructor's guidelines to receive credit on your project.
</div> </div>
</section> </section>
</div> </div>
""".format(q = name, """.format(q=name,
max = self.maxes[q], max=self.maxes[q],
messages = messages, messages=messages,
checkOrX = checkOrX, checkOrX=checkOrX,
points = self.points[q] points=self.points[q]
) )
# print("*** output for Question %s " % q[1]) # print "*** output for Question %s " % q[1]
# print(output) # print output
edxOutput.write(output) edxOutput.write(output)
edxOutput.write("</div>") edxOutput.write("</div>")
edxOutput.close() edxOutput.close()
@@ -288,9 +294,11 @@ to follow your instructor's guidelines to receive credit on your project.
def addMessage(self, message, raw=False): def addMessage(self, message, raw=False):
if not raw: if not raw:
# We assume raw messages, formatted for HTML, are printed separately # We assume raw messages, formatted for HTML, are printed separately
if self.mute: util.unmutePrint() if self.mute:
util.unmutePrint()
print('*** ' + message) print('*** ' + message)
if self.mute: util.mutePrint() if self.mute:
util.mutePrint()
message = cgi.escape(message) message = cgi.escape(message)
self.messages[self.currentQuestion].append(message) self.messages[self.currentQuestion].append(message)
@@ -298,17 +306,15 @@ to follow your instructor's guidelines to receive credit on your project.
print("WARNING**** addMessageToEmail is deprecated %s" % message) print("WARNING**** addMessageToEmail is deprecated %s" % message)
for line in message.split('\n'): for line in message.split('\n'):
pass pass
#print('%%% ' + line + ' %%%') # print '%%% ' + line + ' %%%'
#self.messages[self.currentQuestion].append(line) # self.messages[self.currentQuestion].append(line)
class Counter(dict): class Counter(dict):
""" """
Dict with default 0 Dict with default 0
""" """
def __getitem__(self, idx): def __getitem__(self, idx):
try: try:
return dict.__getitem__(self, idx) return dict.__getitem__(self, idx)
@@ -320,4 +326,3 @@ class Counter(dict):
Returns the sum of counts for all keys. Returns the sum of counts for all keys.
""" """
return sum(self.values()) return sum(self.values())
+189 -130
View File
@@ -13,7 +13,8 @@
from graphicsUtils import * from graphicsUtils import *
import math, time import math
import time
from game import Directions from game import Directions
########################### ###########################
@@ -25,60 +26,61 @@ from game import Directions
DEFAULT_GRID_SIZE = 30.0 DEFAULT_GRID_SIZE = 30.0
INFO_PANE_HEIGHT = 35 INFO_PANE_HEIGHT = 35
BACKGROUND_COLOR = formatColor(0,0,0) BACKGROUND_COLOR = formatColor(0, 0, 0)
WALL_COLOR = formatColor(0.0/255.0, 51.0/255.0, 255.0/255.0) WALL_COLOR = formatColor(0.0/255.0, 51.0/255.0, 255.0/255.0)
INFO_PANE_COLOR = formatColor(.4,.4,0) INFO_PANE_COLOR = formatColor(.4, .4, 0)
SCORE_COLOR = formatColor(.9, .9, .9) SCORE_COLOR = formatColor(.9, .9, .9)
PACMAN_OUTLINE_WIDTH = 2 PACMAN_OUTLINE_WIDTH = 2
PACMAN_CAPTURE_OUTLINE_WIDTH = 4 PACMAN_CAPTURE_OUTLINE_WIDTH = 4
GHOST_COLORS = [] GHOST_COLORS = []
GHOST_COLORS.append(formatColor(.9,0,0)) # Red GHOST_COLORS.append(formatColor(.9, 0, 0)) # Red
GHOST_COLORS.append(formatColor(0,.3,.9)) # Blue GHOST_COLORS.append(formatColor(0, .3, .9)) # Blue
GHOST_COLORS.append(formatColor(.98,.41,.07)) # Orange GHOST_COLORS.append(formatColor(.98, .41, .07)) # Orange
GHOST_COLORS.append(formatColor(.1,.75,.7)) # Green GHOST_COLORS.append(formatColor(.1, .75, .7)) # Green
GHOST_COLORS.append(formatColor(1.0,0.6,0.0)) # Yellow GHOST_COLORS.append(formatColor(1.0, 0.6, 0.0)) # Yellow
GHOST_COLORS.append(formatColor(.4,0.13,0.91)) # Purple GHOST_COLORS.append(formatColor(.4, 0.13, 0.91)) # Purple
TEAM_COLORS = GHOST_COLORS[:2] TEAM_COLORS = GHOST_COLORS[:2]
GHOST_SHAPE = [ GHOST_SHAPE = [
( 0, 0.3 ), (0, 0.3),
( 0.25, 0.75 ), (0.25, 0.75),
( 0.5, 0.3 ), (0.5, 0.3),
( 0.75, 0.75 ), (0.75, 0.75),
( 0.75, -0.5 ), (0.75, -0.5),
( 0.5, -0.75 ), (0.5, -0.75),
(-0.5, -0.75 ), (-0.5, -0.75),
(-0.75, -0.5 ), (-0.75, -0.5),
(-0.75, 0.75 ), (-0.75, 0.75),
(-0.5, 0.3 ), (-0.5, 0.3),
(-0.25, 0.75 ) (-0.25, 0.75)
] ]
GHOST_SIZE = 0.65 GHOST_SIZE = 0.65
SCARED_COLOR = formatColor(1,1,1) SCARED_COLOR = formatColor(1, 1, 1)
GHOST_VEC_COLORS = [colorToVector(c) for c in GHOST_COLORS] GHOST_VEC_COLORS = list(map(colorToVector, GHOST_COLORS))
PACMAN_COLOR = formatColor(255.0/255.0,255.0/255.0,61.0/255) PACMAN_COLOR = formatColor(255.0/255.0, 255.0/255.0, 61.0/255)
PACMAN_SCALE = 0.5 PACMAN_SCALE = 0.5
#pacman_speed = 0.25 #pacman_speed = 0.25
# Food # Food
FOOD_COLOR = formatColor(1,1,1) FOOD_COLOR = formatColor(1, 1, 1)
FOOD_SIZE = 0.1 FOOD_SIZE = 0.1
# Laser # Laser
LASER_COLOR = formatColor(1,0,0) LASER_COLOR = formatColor(1, 0, 0)
LASER_SIZE = 0.02 LASER_SIZE = 0.02
# Capsule graphics # Capsule graphics
CAPSULE_COLOR = formatColor(1,1,1) CAPSULE_COLOR = formatColor(1, 1, 1)
CAPSULE_SIZE = 0.25 CAPSULE_SIZE = 0.25
# Drawing walls # Drawing walls
WALL_RADIUS = 0.15 WALL_RADIUS = 0.15
class InfoPane: class InfoPane:
def __init__(self, layout, gridSize): def __init__(self, layout, gridSize):
self.gridSize = gridSize self.gridSize = gridSize
@@ -89,21 +91,22 @@ class InfoPane:
self.textColor = PACMAN_COLOR self.textColor = PACMAN_COLOR
self.drawPane() self.drawPane()
def toScreen(self, pos, y = None): def toScreen(self, pos, y=None):
""" """
Translates a point relative from the bottom left of the info pane. Translates a point relative from the bottom left of the info pane.
""" """
if y == None: if y == None:
x,y = pos x, y = pos
else: else:
x = pos x = pos
x = self.gridSize + x # Margin x = self.gridSize + x # Margin
y = self.base + y y = self.base + y
return x,y return x, y
def drawPane(self): def drawPane(self):
self.scoreText = text( self.toScreen(0, 0 ), self.textColor, "SCORE: 0", "Times", self.fontSize, "bold") self.scoreText = text(self.toScreen(
0, 0), self.textColor, "SCORE: 0", "Times", self.fontSize, "bold")
def initializeGhostDistances(self, distances): def initializeGhostDistances(self, distances):
self.ghostDistanceText = [] self.ghostDistanceText = []
@@ -115,7 +118,8 @@ class InfoPane:
size = 10 size = 10
for i, d in enumerate(distances): for i, d in enumerate(distances):
t = text( self.toScreen(self.width//2 + self.width//8 * i, 0), GHOST_COLORS[i+1], d, "Times", size, "bold") t = text(self.toScreen(self.width/2 + self.width/8 * i, 0),
GHOST_COLORS[i+1], d, "Times", size, "bold")
self.ghostDistanceText.append(t) self.ghostDistanceText.append(t)
def updateScore(self, score): def updateScore(self, score):
@@ -123,12 +127,16 @@ class InfoPane:
def setTeam(self, isBlue): def setTeam(self, isBlue):
text = "RED TEAM" text = "RED TEAM"
if isBlue: text = "BLUE TEAM" if isBlue:
self.teamText = text( self.toScreen(300, 0 ), self.textColor, text, "Times", self.fontSize, "bold") text = "BLUE TEAM"
self.teamText = text(self.toScreen(
300, 0), self.textColor, text, "Times", self.fontSize, "bold")
def updateGhostDistances(self, distances): def updateGhostDistances(self, distances):
if len(distances) == 0: return if len(distances) == 0:
if 'ghostDistanceText' not in dir(self): self.initializeGhostDistances(distances) return
if 'ghostDistanceText' not in dir(self):
self.initializeGhostDistances(distances)
else: else:
for i, d in enumerate(distances): for i, d in enumerate(distances):
changeText(self.ghostDistanceText[i], d) changeText(self.ghostDistanceText[i], d)
@@ -165,7 +173,7 @@ class PacmanGraphics:
def checkNullDisplay(self): def checkNullDisplay(self):
return False return False
def initialize(self, state, isBlue = False): def initialize(self, state, isBlue=False):
self.isBlue = isBlue self.isBlue = isBlue
self.startGraphics(state) self.startGraphics(state)
@@ -193,11 +201,11 @@ class PacmanGraphics:
distx = [] distx = []
dist.append(distx) dist.append(distx)
for y in range(walls.height): for y in range(walls.height):
( screen_x, screen_y ) = self.to_screen( (x, y) ) (screen_x, screen_y) = self.to_screen((x, y))
block = square( (screen_x, screen_y), block = square((screen_x, screen_y),
0.5 * self.gridSize, 0.5 * self.gridSize,
color = BACKGROUND_COLOR, color=BACKGROUND_COLOR,
filled = 1, behind=2) filled=1, behind=2)
distx.append(block) distx.append(block)
self.distributionImages = dist self.distributionImages = dist
@@ -213,10 +221,10 @@ class PacmanGraphics:
for index, agent in enumerate(state.agentStates): for index, agent in enumerate(state.agentStates):
if agent.isPacman: if agent.isPacman:
image = self.drawPacman(agent, index) image = self.drawPacman(agent, index)
self.agentImages.append( (agent, image) ) self.agentImages.append((agent, image))
else: else:
image = self.drawGhost(agent, index) image = self.drawGhost(agent, index)
self.agentImages.append( (agent, image) ) self.agentImages.append((agent, image))
refresh() refresh()
def swapImages(self, agentIndex, newState): def swapImages(self, agentIndex, newState):
@@ -224,20 +232,22 @@ class PacmanGraphics:
Changes an image from a ghost to a pacman or vis versa (for capture) Changes an image from a ghost to a pacman or vis versa (for capture)
""" """
prevState, prevImage = self.agentImages[agentIndex] prevState, prevImage = self.agentImages[agentIndex]
for item in prevImage: remove_from_screen(item) for item in prevImage:
remove_from_screen(item)
if newState.isPacman: if newState.isPacman:
image = self.drawPacman(newState, agentIndex) image = self.drawPacman(newState, agentIndex)
self.agentImages[agentIndex] = (newState, image ) self.agentImages[agentIndex] = (newState, image)
else: else:
image = self.drawGhost(newState, agentIndex) image = self.drawGhost(newState, agentIndex)
self.agentImages[agentIndex] = (newState, image ) self.agentImages[agentIndex] = (newState, image)
refresh() refresh()
def update(self, newState): def update(self, newState):
agentIndex = newState._agentMoved agentIndex = newState._agentMoved
agentState = newState.agentStates[agentIndex] agentState = newState.agentStates[agentIndex]
if self.agentImages[agentIndex][0].isPacman != agentState.isPacman: self.swapImages(agentIndex, agentState) if self.agentImages[agentIndex][0].isPacman != agentState.isPacman:
self.swapImages(agentIndex, agentState)
prevState, prevImage = self.agentImages[agentIndex] prevState, prevImage = self.agentImages[agentIndex]
if agentState.isPacman: if agentState.isPacman:
self.animatePacman(agentState, prevState, prevImage) self.animatePacman(agentState, prevState, prevImage)
@@ -279,14 +289,14 @@ class PacmanGraphics:
width = PACMAN_CAPTURE_OUTLINE_WIDTH width = PACMAN_CAPTURE_OUTLINE_WIDTH
return [circle(screen_point, PACMAN_SCALE * self.gridSize, return [circle(screen_point, PACMAN_SCALE * self.gridSize,
fillColor = fillColor, outlineColor = outlineColor, fillColor=fillColor, outlineColor=outlineColor,
endpoints = endpoints, endpoints=endpoints,
width = width)] width=width)]
def getEndpoints(self, direction, position=(0,0)): def getEndpoints(self, direction, position=(0, 0)):
x, y = position x, y = position
pos = x - int(x) + y - int(y) pos = x - int(x) + y - int(y)
width = 30 + 80 * math.sin(math.pi* pos) width = 30 + 80 * math.sin(math.pi * pos)
delta = width / 2 delta = width / 2
if (direction == 'West'): if (direction == 'West'):
@@ -301,7 +311,7 @@ class PacmanGraphics:
def movePacman(self, position, direction, image): def movePacman(self, position, direction, image):
screenPosition = self.to_screen(position) screenPosition = self.to_screen(position)
endpoints = self.getEndpoints( direction, position ) endpoints = self.getEndpoints(direction, position)
r = PACMAN_SCALE * self.gridSize r = PACMAN_SCALE * self.gridSize
moveCircle(image[0], screenPosition, r, endpoints) moveCircle(image[0], screenPosition, r, endpoints)
refresh() refresh()
@@ -317,13 +327,15 @@ class PacmanGraphics:
fx, fy = self.getPosition(prevPacman) fx, fy = self.getPosition(prevPacman)
px, py = self.getPosition(pacman) px, py = self.getPosition(pacman)
frames = 4.0 frames = 4.0
for i in range(1,int(frames) + 1): for i in range(1, int(frames) + 1):
pos = px*i/frames + fx*(frames-i)/frames, py*i/frames + fy*(frames-i)/frames pos = px*i/frames + fx * \
(frames-i)/frames, py*i/frames + fy*(frames-i)/frames
self.movePacman(pos, self.getDirection(pacman), image) self.movePacman(pos, self.getDirection(pacman), image)
refresh() refresh()
sleep(abs(self.frameTime) / frames) sleep(abs(self.frameTime) / frames)
else: else:
self.movePacman(self.getPosition(pacman), self.getDirection(pacman), image) self.movePacman(self.getPosition(pacman),
self.getDirection(pacman), image)
refresh() refresh()
def getGhostColor(self, ghost, ghostIndex): def getGhostColor(self, ghost, ghostIndex):
@@ -335,13 +347,14 @@ class PacmanGraphics:
def drawGhost(self, ghost, agentIndex): def drawGhost(self, ghost, agentIndex):
pos = self.getPosition(ghost) pos = self.getPosition(ghost)
dir = self.getDirection(ghost) dir = self.getDirection(ghost)
(screen_x, screen_y) = (self.to_screen(pos) ) (screen_x, screen_y) = (self.to_screen(pos))
coords = [] coords = []
for (x, y) in GHOST_SHAPE: for (x, y) in GHOST_SHAPE:
coords.append((x*self.gridSize*GHOST_SIZE + screen_x, y*self.gridSize*GHOST_SIZE + screen_y)) coords.append((x*self.gridSize*GHOST_SIZE + screen_x,
y*self.gridSize*GHOST_SIZE + screen_y))
colour = self.getGhostColor(ghost, agentIndex) colour = self.getGhostColor(ghost, agentIndex)
body = polygon(coords, colour, filled = 1) body = polygon(coords, colour, filled=1)
WHITE = formatColor(1.0, 1.0, 1.0) WHITE = formatColor(1.0, 1.0, 1.0)
BLACK = formatColor(0.0, 0.0, 0.0) BLACK = formatColor(0.0, 0.0, 0.0)
@@ -355,10 +368,14 @@ class PacmanGraphics:
dx = 0.2 dx = 0.2
if dir == 'West': if dir == 'West':
dx = -0.2 dx = -0.2
leftEye = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE) leftEye = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y -
rightEye = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE) self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE)
leftPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK) rightEye = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y -
rightPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK) self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2, WHITE, WHITE)
leftPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y -
self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK)
rightPupil = circle((screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y -
self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08, BLACK, BLACK)
ghostImageParts = [] ghostImageParts = []
ghostImageParts.append(body) ghostImageParts.append(body)
ghostImageParts.append(leftEye) ghostImageParts.append(leftEye)
@@ -369,7 +386,7 @@ class PacmanGraphics:
return ghostImageParts return ghostImageParts
def moveEyes(self, pos, dir, eyes): def moveEyes(self, pos, dir, eyes):
(screen_x, screen_y) = (self.to_screen(pos) ) (screen_x, screen_y) = (self.to_screen(pos))
dx = 0 dx = 0
dy = 0 dy = 0
if dir == 'North': if dir == 'North':
@@ -380,10 +397,14 @@ class PacmanGraphics:
dx = 0.2 dx = 0.2
if dir == 'West': if dir == 'West':
dx = -0.2 dx = -0.2
moveCircle(eyes[0],(screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2) moveCircle(eyes[0], (screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx/1.5), screen_y -
moveCircle(eyes[1],(screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2) self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2)
moveCircle(eyes[2],(screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08) moveCircle(eyes[1], (screen_x+self.gridSize*GHOST_SIZE*(0.3+dx/1.5), screen_y -
moveCircle(eyes[3],(screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y-self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08) self.gridSize*GHOST_SIZE*(0.3-dy/1.5)), self.gridSize*GHOST_SIZE*0.2)
moveCircle(eyes[2], (screen_x+self.gridSize*GHOST_SIZE*(-0.3+dx), screen_y -
self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08)
moveCircle(eyes[3], (screen_x+self.gridSize*GHOST_SIZE*(0.3+dx), screen_y -
self.gridSize*GHOST_SIZE*(0.3-dy)), self.gridSize*GHOST_SIZE*0.08)
def moveGhost(self, ghost, ghostIndex, prevGhost, ghostImageParts): def moveGhost(self, ghost, ghostIndex, prevGhost, ghostImageParts):
old_x, old_y = self.to_screen(self.getPosition(prevGhost)) old_x, old_y = self.to_screen(self.getPosition(prevGhost))
@@ -399,40 +420,45 @@ class PacmanGraphics:
else: else:
color = GHOST_COLORS[ghostIndex] color = GHOST_COLORS[ghostIndex]
edit(ghostImageParts[0], ('fill', color), ('outline', color)) edit(ghostImageParts[0], ('fill', color), ('outline', color))
self.moveEyes(self.getPosition(ghost), self.getDirection(ghost), ghostImageParts[-4:]) self.moveEyes(self.getPosition(ghost),
self.getDirection(ghost), ghostImageParts[-4:])
refresh() refresh()
def getPosition(self, agentState): def getPosition(self, agentState):
if agentState.configuration == None: return (-1000, -1000) if agentState.configuration == None:
return (-1000, -1000)
return agentState.getPosition() return agentState.getPosition()
def getDirection(self, agentState): def getDirection(self, agentState):
if agentState.configuration == None: return Directions.STOP if agentState.configuration == None:
return Directions.STOP
return agentState.configuration.getDirection() return agentState.configuration.getDirection()
def finish(self): def finish(self):
end_graphics() end_graphics()
def to_screen(self, point): def to_screen(self, point):
( x, y ) = point (x, y) = point
#y = self.height - y #y = self.height - y
x = (x + 1)*self.gridSize x = (x + 1)*self.gridSize
y = (self.height - y)*self.gridSize y = (self.height - y)*self.gridSize
return ( x, y ) return (x, y)
# Fixes some TK issue with off-center circles # Fixes some TK issue with off-center circles
def to_screen2(self, point): def to_screen2(self, point):
( x, y ) = point (x, y) = point
#y = self.height - y #y = self.height - y
x = (x + 1)*self.gridSize x = (x + 1)*self.gridSize
y = (self.height - y)*self.gridSize y = (self.height - y)*self.gridSize
return ( x, y ) return (x, y)
def drawWalls(self, wallMatrix): def drawWalls(self, wallMatrix):
wallColor = WALL_COLOR wallColor = WALL_COLOR
for xNum, x in enumerate(wallMatrix): for xNum, x in enumerate(wallMatrix):
if self.capture and (xNum * 2) < wallMatrix.width: wallColor = TEAM_COLORS[0] if self.capture and (xNum * 2) < wallMatrix.width:
if self.capture and (xNum * 2) >= wallMatrix.width: wallColor = TEAM_COLORS[1] wallColor = TEAM_COLORS[0]
if self.capture and (xNum * 2) >= wallMatrix.width:
wallColor = TEAM_COLORS[1]
for yNum, cell in enumerate(x): for yNum, cell in enumerate(x):
if cell: # There's a wall here if cell: # There's a wall here
@@ -453,66 +479,90 @@ class PacmanGraphics:
# NE quadrant # NE quadrant
if (not nIsWall) and (not eIsWall): if (not nIsWall) and (not eIsWall):
# inner circle # inner circle
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (0,91), 'arc') circle(screen2, WALL_RADIUS * self.gridSize,
wallColor, wallColor, (0, 91), 'arc')
if (nIsWall) and (not eIsWall): if (nIsWall) and (not eIsWall):
# vertical line # vertical line
line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor) line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen,
(self.gridSize*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor)
if (not nIsWall) and (eIsWall): if (not nIsWall) and (eIsWall):
# horizontal line # horizontal line
line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor) line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen,
(self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor)
if (nIsWall) and (eIsWall) and (not neIsWall): if (nIsWall) and (eIsWall) and (not neIsWall):
# outer circle # outer circle
circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (180,271), 'arc') circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)),
line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor) WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (180, 271), 'arc')
line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5))), wallColor) line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(-1)*WALL_RADIUS)),
add(screen, (self.gridSize*0.5+1, self.gridSize*(-1)*WALL_RADIUS)), wallColor)
line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)),
add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(-0.5))), wallColor)
# NW quadrant # NW quadrant
if (not nIsWall) and (not wIsWall): if (not nIsWall) and (not wIsWall):
# inner circle # inner circle
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (90,181), 'arc') circle(screen2, WALL_RADIUS * self.gridSize,
wallColor, wallColor, (90, 181), 'arc')
if (nIsWall) and (not wIsWall): if (nIsWall) and (not wIsWall):
# vertical line # vertical line
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor) line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen,
(self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5)-1)), wallColor)
if (not nIsWall) and (wIsWall): if (not nIsWall) and (wIsWall):
# horizontal line # horizontal line
line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5)-1, self.gridSize*(-1)*WALL_RADIUS)), wallColor) line(add(screen, (0, self.gridSize*(-1)*WALL_RADIUS)), add(screen,
(self.gridSize*(-0.5)-1, self.gridSize*(-1)*WALL_RADIUS)), wallColor)
if (nIsWall) and (wIsWall) and (not nwIsWall): if (nIsWall) and (wIsWall) and (not nwIsWall):
# outer circle # outer circle
circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (270,361), 'arc') circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS)),
line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(-1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5), self.gridSize*(-1)*WALL_RADIUS)), wallColor) WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (270, 361), 'arc')
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5))), wallColor) line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(-1)*WALL_RADIUS)),
add(screen, (self.gridSize*(-0.5), self.gridSize*(-1)*WALL_RADIUS)), wallColor)
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-2)*WALL_RADIUS+1)),
add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(-0.5))), wallColor)
# SE quadrant # SE quadrant
if (not sIsWall) and (not eIsWall): if (not sIsWall) and (not eIsWall):
# inner circle # inner circle
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (270,361), 'arc') circle(screen2, WALL_RADIUS * self.gridSize,
wallColor, wallColor, (270, 361), 'arc')
if (sIsWall) and (not eIsWall): if (sIsWall) and (not eIsWall):
# vertical line # vertical line
line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor) line(add(screen, (self.gridSize*WALL_RADIUS, 0)), add(screen,
(self.gridSize*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor)
if (not sIsWall) and (eIsWall): if (not sIsWall) and (eIsWall):
# horizontal line # horizontal line
line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5+1, self.gridSize*(1)*WALL_RADIUS)), wallColor) line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen,
(self.gridSize*0.5+1, self.gridSize*(1)*WALL_RADIUS)), wallColor)
if (sIsWall) and (eIsWall) and (not seIsWall): if (sIsWall) and (eIsWall) and (not seIsWall):
# outer circle # outer circle
circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (90,181), 'arc') circle(add(screen2, (self.gridSize*2*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)),
line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*0.5, self.gridSize*(1)*WALL_RADIUS)), wallColor) WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (90, 181), 'arc')
line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)), add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5))), wallColor) line(add(screen, (self.gridSize*2*WALL_RADIUS-1, self.gridSize*(1)*WALL_RADIUS)),
add(screen, (self.gridSize*0.5, self.gridSize*(1)*WALL_RADIUS)), wallColor)
line(add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)),
add(screen, (self.gridSize*WALL_RADIUS, self.gridSize*(0.5))), wallColor)
# SW quadrant # SW quadrant
if (not sIsWall) and (not wIsWall): if (not sIsWall) and (not wIsWall):
# inner circle # inner circle
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (180,271), 'arc') circle(screen2, WALL_RADIUS * self.gridSize,
wallColor, wallColor, (180, 271), 'arc')
if (sIsWall) and (not wIsWall): if (sIsWall) and (not wIsWall):
# vertical line # vertical line
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor) line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, 0)), add(screen,
(self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5)+1)), wallColor)
if (not sIsWall) and (wIsWall): if (not sIsWall) and (wIsWall):
# horizontal line # horizontal line
line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5)-1, self.gridSize*(1)*WALL_RADIUS)), wallColor) line(add(screen, (0, self.gridSize*(1)*WALL_RADIUS)), add(screen,
(self.gridSize*(-0.5)-1, self.gridSize*(1)*WALL_RADIUS)), wallColor)
if (sIsWall) and (wIsWall) and (not swIsWall): if (sIsWall) and (wIsWall) and (not swIsWall):
# outer circle # outer circle
circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)), WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (0,91), 'arc') circle(add(screen2, (self.gridSize*(-2)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS)),
line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(1)*WALL_RADIUS)), add(screen, (self.gridSize*(-0.5), self.gridSize*(1)*WALL_RADIUS)), wallColor) WALL_RADIUS * self.gridSize-1, wallColor, wallColor, (0, 91), 'arc')
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)), add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5))), wallColor) line(add(screen, (self.gridSize*(-2)*WALL_RADIUS+1, self.gridSize*(1)*WALL_RADIUS)),
add(screen, (self.gridSize*(-0.5), self.gridSize*(1)*WALL_RADIUS)), wallColor)
line(add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(2)*WALL_RADIUS-1)),
add(screen, (self.gridSize*(-1)*WALL_RADIUS, self.gridSize*(0.5))), wallColor)
def isWall(self, x, y, walls): def isWall(self, x, y, walls):
if x < 0 or y < 0: if x < 0 or y < 0:
@@ -521,43 +571,45 @@ class PacmanGraphics:
return False return False
return walls[x][y] return walls[x][y]
def drawFood(self, foodMatrix ): def drawFood(self, foodMatrix):
foodImages = [] foodImages = []
color = FOOD_COLOR color = FOOD_COLOR
for xNum, x in enumerate(foodMatrix): for xNum, x in enumerate(foodMatrix):
if self.capture and (xNum * 2) <= foodMatrix.width: color = TEAM_COLORS[0] if self.capture and (xNum * 2) <= foodMatrix.width:
if self.capture and (xNum * 2) > foodMatrix.width: color = TEAM_COLORS[1] color = TEAM_COLORS[0]
if self.capture and (xNum * 2) > foodMatrix.width:
color = TEAM_COLORS[1]
imageRow = [] imageRow = []
foodImages.append(imageRow) foodImages.append(imageRow)
for yNum, cell in enumerate(x): for yNum, cell in enumerate(x):
if cell: # There's food here if cell: # There's food here
screen = self.to_screen((xNum, yNum )) screen = self.to_screen((xNum, yNum))
dot = circle( screen, dot = circle(screen,
FOOD_SIZE * self.gridSize, FOOD_SIZE * self.gridSize,
outlineColor = color, fillColor = color, outlineColor=color, fillColor=color,
width = 1) width=1)
imageRow.append(dot) imageRow.append(dot)
else: else:
imageRow.append(None) imageRow.append(None)
return foodImages return foodImages
def drawCapsules(self, capsules ): def drawCapsules(self, capsules):
capsuleImages = {} capsuleImages = {}
for capsule in capsules: for capsule in capsules:
( screen_x, screen_y ) = self.to_screen(capsule) (screen_x, screen_y) = self.to_screen(capsule)
dot = circle( (screen_x, screen_y), dot = circle((screen_x, screen_y),
CAPSULE_SIZE * self.gridSize, CAPSULE_SIZE * self.gridSize,
outlineColor = CAPSULE_COLOR, outlineColor=CAPSULE_COLOR,
fillColor = CAPSULE_COLOR, fillColor=CAPSULE_COLOR,
width = 1) width=1)
capsuleImages[capsule] = dot capsuleImages[capsule] = dot
return capsuleImages return capsuleImages
def removeFood(self, cell, foodImages ): def removeFood(self, cell, foodImages):
x, y = cell x, y = cell
remove_from_screen(foodImages[x][y]) remove_from_screen(foodImages[x][y])
def removeCapsule(self, cell, capsuleImages ): def removeCapsule(self, cell, capsuleImages):
x, y = cell x, y = cell
remove_from_screen(capsuleImages[(x, y)]) remove_from_screen(capsuleImages[(x, y)])
@@ -570,12 +622,13 @@ class PacmanGraphics:
self.clearExpandedCells() self.clearExpandedCells()
self.expandedCells = [] self.expandedCells = []
for k, cell in enumerate(cells): for k, cell in enumerate(cells):
screenPos = self.to_screen( cell) screenPos = self.to_screen(cell)
cellColor = formatColor(*[(n-k) * c * .5 / n + .25 for c in baseColor]) cellColor = formatColor(
*[(n-k) * c * .5 / n + .25 for c in baseColor])
block = square(screenPos, block = square(screenPos,
0.5 * self.gridSize, 0.5 * self.gridSize,
color = cellColor, color=cellColor,
filled = 1, behind=2) filled=1, behind=2)
self.expandedCells.append(block) self.expandedCells.append(block)
if self.frameTime < 0: if self.frameTime < 0:
refresh() refresh()
@@ -585,36 +638,38 @@ class PacmanGraphics:
for cell in self.expandedCells: for cell in self.expandedCells:
remove_from_screen(cell) remove_from_screen(cell)
def updateDistributions(self, distributions): def updateDistributions(self, distributions):
"Draws an agent's belief distributions" "Draws an agent's belief distributions"
# copy all distributions so we don't change their state # copy all distributions so we don't change their state
distributions = map(lambda x: x.copy(), distributions) distributions = [x.copy() for x in distributions]
if self.distributionImages == None: if self.distributionImages == None:
self.drawDistributions(self.previousState) self.drawDistributions(self.previousState)
for x in range(len(self.distributionImages)): for x in range(len(self.distributionImages)):
for y in range(len(self.distributionImages[0])): for y in range(len(self.distributionImages[0])):
image = self.distributionImages[x][y] image = self.distributionImages[x][y]
weights = [dist[ (x,y) ] for dist in distributions] weights = [dist[(x, y)] for dist in distributions]
if sum(weights) != 0: if sum(weights) != 0:
pass pass
# Fog of war # Fog of war
color = [0.0,0.0,0.0] color = [0.0, 0.0, 0.0]
colors = GHOST_VEC_COLORS[1:] # With Pacman colors = GHOST_VEC_COLORS[1:] # With Pacman
if self.capture: colors = GHOST_VEC_COLORS if self.capture:
colors = GHOST_VEC_COLORS
for weight, gcolor in zip(weights, colors): for weight, gcolor in zip(weights, colors):
color = [min(1.0, c + 0.95 * g * weight ** .3) for c,g in zip(color, gcolor)] color = [min(1.0, c + 0.95 * g * weight ** .3)
for c, g in zip(color, gcolor)]
changeColor(image, formatColor(*color)) changeColor(image, formatColor(*color))
refresh() refresh()
class FirstPersonPacmanGraphics(PacmanGraphics): class FirstPersonPacmanGraphics(PacmanGraphics):
def __init__(self, zoom = 1.0, showGhosts = True, capture = False, frameTime=0): def __init__(self, zoom=1.0, showGhosts=True, capture=False, frameTime=0):
PacmanGraphics.__init__(self, zoom, frameTime=frameTime) PacmanGraphics.__init__(self, zoom, frameTime=frameTime)
self.showGhosts = showGhosts self.showGhosts = showGhosts
self.capture = capture self.capture = capture
def initialize(self, state, isBlue = False): def initialize(self, state, isBlue=False):
self.isBlue = isBlue self.isBlue = isBlue
PacmanGraphics.startGraphics(self, state) PacmanGraphics.startGraphics(self, state)
@@ -654,6 +709,7 @@ class FirstPersonPacmanGraphics(PacmanGraphics):
else: else:
return PacmanGraphics.getPosition(self, ghostState) return PacmanGraphics.getPosition(self, ghostState)
def add(x, y): def add(x, y):
return (x[0] + y[0], x[1] + y[1]) return (x[0] + y[0], x[1] + y[1])
@@ -669,11 +725,14 @@ POSTSCRIPT_OUTPUT_DIR = 'frames'
FRAME_NUMBER = 0 FRAME_NUMBER = 0
import os import os
def saveFrame(): def saveFrame():
"Saves the current graphical output as a postscript file" "Saves the current graphical output as a postscript file"
global SAVE_POSTSCRIPT, FRAME_NUMBER, POSTSCRIPT_OUTPUT_DIR global SAVE_POSTSCRIPT, FRAME_NUMBER, POSTSCRIPT_OUTPUT_DIR
if not SAVE_POSTSCRIPT: return if not SAVE_POSTSCRIPT:
if not os.path.exists(POSTSCRIPT_OUTPUT_DIR): os.mkdir(POSTSCRIPT_OUTPUT_DIR) return
if not os.path.exists(POSTSCRIPT_OUTPUT_DIR):
os.mkdir(POSTSCRIPT_OUTPUT_DIR)
name = os.path.join(POSTSCRIPT_OUTPUT_DIR, 'frame_%08d.ps' % FRAME_NUMBER) name = os.path.join(POSTSCRIPT_OUTPUT_DIR, 'frame_%08d.ps' % FRAME_NUMBER)
FRAME_NUMBER += 1 FRAME_NUMBER += 1
writePostscript(name) # writes the current canvas writePostscript(name) # writes the current canvas
+80 -31
View File
@@ -33,11 +33,14 @@ _canvas_col = None # Current colour (set to black below)
_canvas_tsize = 12 _canvas_tsize = 12
_canvas_tserifs = 0 _canvas_tserifs = 0
def formatColor(r, g, b): def formatColor(r, g, b):
return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255)) return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255))
def colorToVector(color): def colorToVector(color):
return list(map(lambda x: int(x, 16) / 256.0, [color[1:3], color[3:5], color[5:7]])) return [int(x, 16) / 256.0 for x in [color[1:3], color[3:5], color[5:7]]]
if _Windows: if _Windows:
_canvas_tfonts = ['times new roman', 'lucida console'] _canvas_tfonts = ['times new roman', 'lucida console']
@@ -45,6 +48,7 @@ else:
_canvas_tfonts = ['times', 'lucidasans-24'] _canvas_tfonts = ['times', 'lucidasans-24']
pass # XXX need defaults here pass # XXX need defaults here
def sleep(secs): def sleep(secs):
global _root_window global _root_window
if _root_window == None: if _root_window == None:
@@ -54,6 +58,7 @@ def sleep(secs):
_root_window.after(int(1000 * secs), _root_window.quit) _root_window.after(int(1000 * secs), _root_window.quit)
_root_window.mainloop() _root_window.mainloop()
def begin_graphics(width=640, height=480, color=formatColor(0, 0, 0), title=None): def begin_graphics(width=640, height=480, color=formatColor(0, 0, 0), title=None):
global _root_window, _canvas, _canvas_x, _canvas_y, _canvas_xs, _canvas_ys, _bg_color global _root_window, _canvas, _canvas_x, _canvas_y, _canvas_xs, _canvas_ys, _bg_color
@@ -85,32 +90,37 @@ def begin_graphics(width=640, height=480, color=formatColor(0, 0, 0), title=None
raise raise
# Bind to key-down and key-up events # Bind to key-down and key-up events
_root_window.bind( "<KeyPress>", _keypress ) _root_window.bind("<KeyPress>", _keypress)
_root_window.bind( "<KeyRelease>", _keyrelease ) _root_window.bind("<KeyRelease>", _keyrelease)
_root_window.bind( "<FocusIn>", _clear_keys ) _root_window.bind("<FocusIn>", _clear_keys)
_root_window.bind( "<FocusOut>", _clear_keys ) _root_window.bind("<FocusOut>", _clear_keys)
_root_window.bind( "<Button-1>", _leftclick ) _root_window.bind("<Button-1>", _leftclick)
_root_window.bind( "<Button-2>", _rightclick ) _root_window.bind("<Button-2>", _rightclick)
_root_window.bind( "<Button-3>", _rightclick ) _root_window.bind("<Button-3>", _rightclick)
_root_window.bind( "<Control-Button-1>", _ctrl_leftclick) _root_window.bind("<Control-Button-1>", _ctrl_leftclick)
_clear_keys() _clear_keys()
_leftclick_loc = None _leftclick_loc = None
_rightclick_loc = None _rightclick_loc = None
_ctrl_leftclick_loc = None _ctrl_leftclick_loc = None
def _leftclick(event): def _leftclick(event):
global _leftclick_loc global _leftclick_loc
_leftclick_loc = (event.x, event.y) _leftclick_loc = (event.x, event.y)
def _rightclick(event): def _rightclick(event):
global _rightclick_loc global _rightclick_loc
_rightclick_loc = (event.x, event.y) _rightclick_loc = (event.x, event.y)
def _ctrl_leftclick(event): def _ctrl_leftclick(event):
global _ctrl_leftclick_loc global _ctrl_leftclick_loc
_ctrl_leftclick_loc = (event.x, event.y) _ctrl_leftclick_loc = (event.x, event.y)
def wait_for_click(): def wait_for_click():
while True: while True:
global _leftclick_loc global _leftclick_loc
@@ -130,16 +140,21 @@ def wait_for_click():
return val, 'ctrl_left' return val, 'ctrl_left'
sleep(0.05) sleep(0.05)
def draw_background(): def draw_background():
corners = [(0,0), (0, _canvas_ys), (_canvas_xs, _canvas_ys), (_canvas_xs, 0)] corners = [(0, 0), (0, _canvas_ys),
polygon(corners, _bg_color, fillColor=_bg_color, filled=True, smoothed=False) (_canvas_xs, _canvas_ys), (_canvas_xs, 0)]
polygon(corners, _bg_color, fillColor=_bg_color,
filled=True, smoothed=False)
def _destroy_window(event=None): def _destroy_window(event=None):
sys.exit(0) sys.exit(0)
# global _root_window # global _root_window
# _root_window.destroy() # _root_window.destroy()
# _root_window = None # _root_window = None
#print("DESTROY") # print "DESTROY"
def end_graphics(): def end_graphics():
global _root_window, _canvas, _mouse_enabled global _root_window, _canvas, _mouse_enabled
@@ -156,30 +171,37 @@ def end_graphics():
_mouse_enabled = 0 _mouse_enabled = 0
_clear_keys() _clear_keys()
def clear_screen(background=None): def clear_screen(background=None):
global _canvas_x, _canvas_y global _canvas_x, _canvas_y
_canvas.delete('all') _canvas.delete('all')
draw_background() draw_background()
_canvas_x, _canvas_y = 0, _canvas_ys _canvas_x, _canvas_y = 0, _canvas_ys
def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1): def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1):
c = [] c = []
for coord in coords: for coord in coords:
c.append(coord[0]) c.append(coord[0])
c.append(coord[1]) c.append(coord[1])
if fillColor == None: fillColor = outlineColor if fillColor == None:
if filled == 0: fillColor = "" fillColor = outlineColor
poly = _canvas.create_polygon(c, outline=outlineColor, fill=fillColor, smooth=smoothed, width=width) if filled == 0:
fillColor = ""
poly = _canvas.create_polygon(
c, outline=outlineColor, fill=fillColor, smooth=smoothed, width=width)
if behind > 0: if behind > 0:
_canvas.tag_lower(poly, behind) # Higher should be more visible _canvas.tag_lower(poly, behind) # Higher should be more visible
return poly return poly
def square(pos, r, color, filled=1, behind=0): def square(pos, r, color, filled=1, behind=0):
x, y = pos x, y = pos
coords = [(x - r, y - r), (x + r, y - r), (x + r, y + r), (x - r, y + r)] coords = [(x - r, y - r), (x + r, y - r), (x + r, y + r), (x - r, y + r)]
return polygon(coords, color, color, filled, 0, behind=behind) return polygon(coords, color, color, filled, 0, behind=behind)
def circle(pos, r, outlineColor, fillColor=None, endpoints=None, style='pieslice', width=2):
def circle(pos, r, outlineColor, fillColor, endpoints=None, style='pieslice', width=2):
x, y = pos x, y = pos
x0, x1 = x - r - 1, x + r x0, x1 = x - r - 1, x + r
y0, y1 = y - r - 1, y + r y0, y1 = y - r - 1, y + r
@@ -187,20 +209,23 @@ def circle(pos, r, outlineColor, fillColor=None, endpoints=None, style='pieslice
e = [0, 359] e = [0, 359]
else: else:
e = list(endpoints) e = list(endpoints)
while e[0] > e[1]: e[1] = e[1] + 360 while e[0] > e[1]:
e[1] = e[1] + 360
return _canvas.create_arc(x0, y0, x1, y1, outline=outlineColor, fill=fillColor or outlineColor, return _canvas.create_arc(x0, y0, x1, y1, outline=outlineColor, fill=fillColor,
extent=e[1] - e[0], start=e[0], style=style, width=width) extent=e[1] - e[0], start=e[0], style=style, width=width)
def image(pos, file="../../blueghost.gif"): def image(pos, file="../../blueghost.gif"):
x, y = pos x, y = pos
# img = PhotoImage(file=file) # img = PhotoImage(file=file)
return _canvas.create_image(x, y, image = tkinter.PhotoImage(file=file), anchor = tkinter.NW) return _canvas.create_image(x, y, image=tkinter.PhotoImage(file=file), anchor=tkinter.NW)
def refresh(): def refresh():
_canvas.update_idletasks() _canvas.update_idletasks()
def moveCircle(id, pos, r, endpoints=None): def moveCircle(id, pos, r, endpoints=None):
global _canvas_x, _canvas_y global _canvas_x, _canvas_y
@@ -213,7 +238,8 @@ def moveCircle(id, pos, r, endpoints=None):
e = [0, 359] e = [0, 359]
else: else:
e = list(endpoints) e = list(endpoints)
while e[0] > e[1]: e[1] = e[1] + 360 while e[0] > e[1]:
e[1] = e[1] + 360
if os.path.isfile('flag'): if os.path.isfile('flag'):
edit(id, ('extent', e[1] - e[0])) edit(id, ('extent', e[1] - e[0]))
@@ -221,23 +247,28 @@ def moveCircle(id, pos, r, endpoints=None):
edit(id, ('start', e[0]), ('extent', e[1] - e[0])) edit(id, ('start', e[0]), ('extent', e[1] - e[0]))
move_to(id, x0, y0) move_to(id, x0, y0)
def edit(id, *args): def edit(id, *args):
_canvas.itemconfigure(id, **dict(args)) _canvas.itemconfigure(id, **dict(args))
def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor="nw"): def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor="nw"):
global _canvas_x, _canvas_y global _canvas_x, _canvas_y
x, y = pos x, y = pos
font = (font, str(size), style) font = (font, str(size), style)
return _canvas.create_text(x, y, fill=color, text=contents, font=font, anchor=anchor) return _canvas.create_text(x, y, fill=color, text=contents, font=font, anchor=anchor)
def changeText(id, newText, font=None, size=12, style='normal'): def changeText(id, newText, font=None, size=12, style='normal'):
_canvas.itemconfigure(id, text=newText) _canvas.itemconfigure(id, text=newText)
if font != None: if font != None:
_canvas.itemconfigure(id, font=(font, '-%d' % size, style)) _canvas.itemconfigure(id, font=(font, '-%d' % size, style))
def changeColor(id, newColor): def changeColor(id, newColor):
_canvas.itemconfigure(id, fill=newColor) _canvas.itemconfigure(id, fill=newColor)
def line(here, there, color=formatColor(0, 0, 0), width=2): def line(here, there, color=formatColor(0, 0, 0), width=2):
x0, y0 = here[0], here[1] x0, y0 = here[0], here[1]
x1, y1 = there[0], there[1] x1, y1 = there[0], there[1]
@@ -249,29 +280,33 @@ def line(here, there, color=formatColor(0, 0, 0), width=2):
# We bind to key-down and key-up events. # We bind to key-down and key-up events.
_keysdown = {} _keysdown = {}
_keyswaiting = {} _keyswaiting = {}
# This holds an unprocessed key release. We delay key releases by up to # This holds an unprocessed key release. We delay key releases by up to
# one call to keys_pressed() to get round a problem with auto repeat. # one call to keys_pressed() to get round a problem with auto repeat.
_got_release = None _got_release = None
def _keypress(event): def _keypress(event):
global _got_release global _got_release
#remap_arrows(event) # remap_arrows(event)
_keysdown[event.keysym] = 1 _keysdown[event.keysym] = 1
_keyswaiting[event.keysym] = 1 _keyswaiting[event.keysym] = 1
# print(event.char, event.keycode) # print event.char, event.keycode
_got_release = None _got_release = None
def _keyrelease(event): def _keyrelease(event):
global _got_release global _got_release
#remap_arrows(event) # remap_arrows(event)
try: try:
del _keysdown[event.keysym] del _keysdown[event.keysym]
except: except:
pass pass
_got_release = 1 _got_release = 1
def remap_arrows(event): def remap_arrows(event):
# TURN ARROW PRESSES INTO LETTERS (SHOULD BE IN KEYBOARD AGENT) # TURN ARROW PRESSES INTO LETTERS (SHOULD BE IN KEYBOARD AGENT)
if event.char in ['a', 's', 'd', 'w']: if event.char in ['a', 's', 'd', 'w']:
@@ -285,27 +320,31 @@ def remap_arrows(event):
if event.keycode in [40, 104]: # DOWN ARROW if event.keycode in [40, 104]: # DOWN ARROW
event.char = 's' event.char = 's'
def _clear_keys(event=None): def _clear_keys(event=None):
global _keysdown, _got_release, _keyswaiting global _keysdown, _got_release, _keyswaiting
_keysdown = {} _keysdown = {}
_keyswaiting = {} _keyswaiting = {}
_got_release = None _got_release = None
def keys_pressed(d_o_e=lambda arg: _root_window.dooneevent(arg), def keys_pressed(d_o_e=lambda arg: _root_window.dooneevent(arg),
d_w=tkinter._tkinter.DONT_WAIT): d_w=tkinter._tkinter.DONT_WAIT):
d_o_e(d_w) d_o_e(d_w)
if _got_release: if _got_release:
d_o_e(d_w) d_o_e(d_w)
return _keysdown.keys() return list(_keysdown.keys())
def keys_waiting(): def keys_waiting():
global _keyswaiting global _keyswaiting
keys = _keyswaiting.keys() keys = list(_keyswaiting.keys())
_keyswaiting = {} _keyswaiting = {}
return keys return keys
# Block for a list of keys... # Block for a list of keys...
def wait_for_keys(): def wait_for_keys():
keys = [] keys = []
while keys == []: while keys == []:
@@ -313,24 +352,29 @@ def wait_for_keys():
sleep(0.05) sleep(0.05)
return keys return keys
def remove_from_screen(x, def remove_from_screen(x,
d_o_e=lambda arg: _root_window.dooneevent(arg), d_o_e=lambda arg: _root_window.dooneevent(arg),
d_w=tkinter._tkinter.DONT_WAIT): d_w=tkinter._tkinter.DONT_WAIT):
_canvas.delete(x) _canvas.delete(x)
d_o_e(d_w) d_o_e(d_w)
def _adjust_coords(coord_list, x, y): def _adjust_coords(coord_list, x, y):
for i in range(0, len(coord_list), 2): for i in range(0, len(coord_list), 2):
coord_list[i] = coord_list[i] + x coord_list[i] = coord_list[i] + x
coord_list[i + 1] = coord_list[i + 1] + y coord_list[i + 1] = coord_list[i + 1] + y
return coord_list return coord_list
def move_to(object, x, y=None, def move_to(object, x, y=None,
d_o_e=lambda arg: _root_window.dooneevent(arg), d_o_e=lambda arg: _root_window.dooneevent(arg),
d_w=tkinter._tkinter.DONT_WAIT): d_w=tkinter._tkinter.DONT_WAIT):
if y is None: if y is None:
try: x, y = x try:
except: raise 'incomprehensible coordinates' x, y = x
except:
raise Exception('incomprehensible coordinates')
horiz = True horiz = True
newCoords = [] newCoords = []
@@ -347,12 +391,15 @@ def move_to(object, x, y=None,
_canvas.coords(object, *newCoords) _canvas.coords(object, *newCoords)
d_o_e(d_w) d_o_e(d_w)
def move_by(object, x, y=None, def move_by(object, x, y=None,
d_o_e=lambda arg: _root_window.dooneevent(arg), d_o_e=lambda arg: _root_window.dooneevent(arg),
d_w=tkinter._tkinter.DONT_WAIT, lift=False): d_w=tkinter._tkinter.DONT_WAIT, lift=False):
if y is None: if y is None:
try: x, y = x try:
except: raise Exception('incomprehensible coordinates') x, y = x
except:
raise Exception('incomprehensible coordinates')
horiz = True horiz = True
newCoords = [] newCoords = []
@@ -370,14 +417,16 @@ def move_by(object, x, y=None,
if lift: if lift:
_canvas.tag_raise(object) _canvas.tag_raise(object)
def writePostscript(filename): def writePostscript(filename):
"Writes the current canvas to a postscript file." "Writes the current canvas to a postscript file."
psfile = open(filename, 'w') psfile = file(filename, 'w')
psfile.write(_canvas.postscript(pageanchor='sw', psfile.write(_canvas.postscript(pageanchor='sw',
y='0.c', y='0.c',
x='0.c')) x='0.c'))
psfile.close() psfile.close()
ghost_shape = [ ghost_shape = [
(0, - 0.5), (0, - 0.5),
(0.25, - 0.75), (0.25, - 0.75),
@@ -390,7 +439,7 @@ ghost_shape = [
(- 0.75, - 0.75), (- 0.75, - 0.75),
(- 0.5, - 0.5), (- 0.5, - 0.5),
(- 0.25, - 0.75) (- 0.25, - 0.75)
] ]
if __name__ == '__main__': if __name__ == '__main__':
begin_graphics() begin_graphics()
+23 -12
View File
@@ -16,6 +16,7 @@ from game import Agent
from game import Directions from game import Directions
import random import random
class KeyboardAgent(Agent): class KeyboardAgent(Agent):
""" """
An agent controlled by the keyboard. An agent controlled by the keyboard.
@@ -27,16 +28,16 @@ class KeyboardAgent(Agent):
SOUTH_KEY = 's' SOUTH_KEY = 's'
STOP_KEY = 'q' STOP_KEY = 'q'
def __init__( self, index = 0 ): def __init__(self, index=0):
self.lastMove = Directions.STOP self.lastMove = Directions.STOP
self.index = index self.index = index
self.keys = [] self.keys = []
def getAction( self, state): def getAction(self, state):
from graphicsUtils import keys_waiting from graphicsUtils import keys_waiting
from graphicsUtils import keys_pressed from graphicsUtils import keys_pressed
keys = list(keys_waiting()) + list(keys_pressed()) keys = keys_waiting() + keys_pressed()
if keys != []: if keys != []:
self.keys = keys self.keys = keys
@@ -48,7 +49,8 @@ class KeyboardAgent(Agent):
if self.lastMove in legal: if self.lastMove in legal:
move = self.lastMove move = self.lastMove
if (self.STOP_KEY in self.keys) and Directions.STOP in legal: move = Directions.STOP if (self.STOP_KEY in self.keys) and Directions.STOP in legal:
move = Directions.STOP
if move not in legal: if move not in legal:
move = random.choice(legal) move = random.choice(legal)
@@ -58,12 +60,17 @@ class KeyboardAgent(Agent):
def getMove(self, legal): def getMove(self, legal):
move = Directions.STOP move = Directions.STOP
if (self.WEST_KEY in self.keys or 'Left' in self.keys) and Directions.WEST in legal: move = Directions.WEST if (self.WEST_KEY in self.keys or 'Left' in self.keys) and Directions.WEST in legal:
if (self.EAST_KEY in self.keys or 'Right' in self.keys) and Directions.EAST in legal: move = Directions.EAST move = Directions.WEST
if (self.NORTH_KEY in self.keys or 'Up' in self.keys) and Directions.NORTH in legal: move = Directions.NORTH if (self.EAST_KEY in self.keys or 'Right' in self.keys) and Directions.EAST in legal:
if (self.SOUTH_KEY in self.keys or 'Down' in self.keys) and Directions.SOUTH in legal: move = Directions.SOUTH move = Directions.EAST
if (self.NORTH_KEY in self.keys or 'Up' in self.keys) and Directions.NORTH in legal:
move = Directions.NORTH
if (self.SOUTH_KEY in self.keys or 'Down' in self.keys) and Directions.SOUTH in legal:
move = Directions.SOUTH
return move return move
class KeyboardAgent2(KeyboardAgent): class KeyboardAgent2(KeyboardAgent):
""" """
A second agent controlled by the keyboard. A second agent controlled by the keyboard.
@@ -77,8 +84,12 @@ class KeyboardAgent2(KeyboardAgent):
def getMove(self, legal): def getMove(self, legal):
move = Directions.STOP move = Directions.STOP
if (self.WEST_KEY in self.keys) and Directions.WEST in legal: move = Directions.WEST if (self.WEST_KEY in self.keys) and Directions.WEST in legal:
if (self.EAST_KEY in self.keys) and Directions.EAST in legal: move = Directions.EAST move = Directions.WEST
if (self.NORTH_KEY in self.keys) and Directions.NORTH in legal: move = Directions.NORTH if (self.EAST_KEY in self.keys) and Directions.EAST in legal:
if (self.SOUTH_KEY in self.keys) and Directions.SOUTH in legal: move = Directions.SOUTH move = Directions.EAST
if (self.NORTH_KEY in self.keys) and Directions.NORTH in legal:
move = Directions.NORTH
if (self.SOUTH_KEY in self.keys) and Directions.SOUTH in legal:
move = Directions.SOUTH
return move return move
+39 -25
View File
@@ -20,6 +20,7 @@ from functools import reduce
VISIBILITY_MATRIX_CACHE = {} VISIBILITY_MATRIX_CACHE = {}
class Layout: class Layout:
""" """
A Layout manages the static information about the game board. A Layout manages the static information about the game board.
@@ -27,7 +28,7 @@ class Layout:
def __init__(self, layoutText): def __init__(self, layoutText):
self.width = len(layoutText[0]) self.width = len(layoutText[0])
self.height= len(layoutText) self.height = len(layoutText)
self.walls = Grid(self.width, self.height, False) self.walls = Grid(self.width, self.height, False)
self.food = Grid(self.width, self.height, False) self.food = Grid(self.width, self.height, False)
self.capsules = [] self.capsules = []
@@ -45,41 +46,46 @@ class Layout:
global VISIBILITY_MATRIX_CACHE global VISIBILITY_MATRIX_CACHE
if reduce(str.__add__, self.layoutText) not in VISIBILITY_MATRIX_CACHE: if reduce(str.__add__, self.layoutText) not in VISIBILITY_MATRIX_CACHE:
from game import Directions from game import Directions
vecs = [(-0.5,0), (0.5,0),(0,-0.5),(0,0.5)] vecs = [(-0.5, 0), (0.5, 0), (0, -0.5), (0, 0.5)]
dirs = [Directions.NORTH, Directions.SOUTH, Directions.WEST, Directions.EAST] dirs = [Directions.NORTH, Directions.SOUTH,
vis = Grid(self.width, self.height, {Directions.NORTH:set(), Directions.SOUTH:set(), Directions.EAST:set(), Directions.WEST:set(), Directions.STOP:set()}) Directions.WEST, Directions.EAST]
vis = Grid(self.width, self.height, {Directions.NORTH: set(), Directions.SOUTH: set(
), Directions.EAST: set(), Directions.WEST: set(), Directions.STOP: set()})
for x in range(self.width): for x in range(self.width):
for y in range(self.height): for y in range(self.height):
if self.walls[x][y] == False: if self.walls[x][y] == False:
for vec, direction in zip(vecs, dirs): for vec, direction in zip(vecs, dirs):
dx, dy = vec dx, dy = vec
nextx, nexty = x + dx, y + dy nextx, nexty = x + dx, y + dy
while (nextx + nexty) != int(nextx) + int(nexty) or not self.walls[int(nextx)][int(nexty)] : while (nextx + nexty) != int(nextx) + int(nexty) or not self.walls[int(nextx)][int(nexty)]:
vis[x][y][direction].add((nextx, nexty)) vis[x][y][direction].add((nextx, nexty))
nextx, nexty = x + dx, y + dy nextx, nexty = x + dx, y + dy
self.visibility = vis self.visibility = vis
VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)] = vis VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)] = vis
else: else:
self.visibility = VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)] self.visibility = VISIBILITY_MATRIX_CACHE[reduce(
str.__add__, self.layoutText)]
def isWall(self, pos): def isWall(self, pos):
x, col = pos x, col = pos
return self.walls[x][col] return self.walls[x][col]
def getRandomLegalPosition(self): def getRandomLegalPosition(self):
x = random.choice(range(self.width)) x = random.choice(list(range(self.width)))
y = random.choice(range(self.height)) y = random.choice(list(range(self.height)))
while self.isWall( (x, y) ): while self.isWall((x, y)):
x = random.choice(range(self.width)) x = random.choice(list(range(self.width)))
y = random.choice(range(self.height)) y = random.choice(list(range(self.height)))
return (x,y) return (x, y)
def getRandomCorner(self): def getRandomCorner(self):
poses = [(1,1), (1, self.height - 2), (self.width - 2, 1), (self.width - 2, self.height - 2)] poses = [(1, 1), (1, self.height - 2), (self.width - 2, 1),
(self.width - 2, self.height - 2)]
return random.choice(poses) return random.choice(poses)
def getFurthestCorner(self, pacPos): def getFurthestCorner(self, pacPos):
poses = [(1,1), (1, self.height - 2), (self.width - 2, 1), (self.width - 2, self.height - 2)] poses = [(1, 1), (1, self.height - 2), (self.width - 2, 1),
(self.width - 2, self.height - 2)]
dist, pos = max([(manhattanDistance(p, pacPos), p) for p in poses]) dist, pos = max([(manhattanDistance(p, pacPos), p) for p in poses])
return pos return pos
@@ -112,7 +118,7 @@ class Layout:
layoutChar = layoutText[maxY - y][x] layoutChar = layoutText[maxY - y][x]
self.processLayoutChar(x, y, layoutChar) self.processLayoutChar(x, y, layoutChar)
self.agentPositions.sort() self.agentPositions.sort()
self.agentPositions = [ ( i == 0, pos) for i, pos in self.agentPositions] self.agentPositions = [(i == 0, pos) for i, pos in self.agentPositions]
def processLayoutChar(self, x, y, layoutChar): def processLayoutChar(self, x, y, layoutChar):
if layoutChar == '%': if layoutChar == '%':
@@ -122,29 +128,37 @@ class Layout:
elif layoutChar == 'o': elif layoutChar == 'o':
self.capsules.append((x, y)) self.capsules.append((x, y))
elif layoutChar == 'P': elif layoutChar == 'P':
self.agentPositions.append( (0, (x, y) ) ) self.agentPositions.append((0, (x, y)))
elif layoutChar in ['G']: elif layoutChar in ['G']:
self.agentPositions.append( (1, (x, y) ) ) self.agentPositions.append((1, (x, y)))
self.numGhosts += 1 self.numGhosts += 1
elif layoutChar in ['1', '2', '3', '4']: elif layoutChar in ['1', '2', '3', '4']:
self.agentPositions.append( (int(layoutChar), (x,y))) self.agentPositions.append((int(layoutChar), (x, y)))
self.numGhosts += 1 self.numGhosts += 1
def getLayout(name, back = 2):
def getLayout(name, back=2):
if name.endswith('.lay'): if name.endswith('.lay'):
layout = tryToLoad('layouts/' + name) layout = tryToLoad('layouts/' + name)
if layout == None: layout = tryToLoad(name) if layout == None:
layout = tryToLoad(name)
else: else:
layout = tryToLoad('layouts/' + name + '.lay') layout = tryToLoad('layouts/' + name + '.lay')
if layout == None: layout = tryToLoad(name + '.lay') if layout == None:
layout = tryToLoad(name + '.lay')
if layout == None and back >= 0: if layout == None and back >= 0:
curdir = os.path.abspath('.') curdir = os.path.abspath('.')
os.chdir('..') os.chdir('..')
layout = getLayout(name, back -1) layout = getLayout(name, back - 1)
os.chdir(curdir) os.chdir(curdir)
return layout return layout
def tryToLoad(fullname): def tryToLoad(fullname):
if(not os.path.exists(fullname)): return None if(not os.path.exists(fullname)):
return None
f = open(fullname) f = open(fullname)
try: return Layout([line.strip() for line in f]) try:
finally: f.close() return Layout([line.strip() for line in f])
finally:
f.close()
-37
View File
@@ -1,37 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%. % %.%
% %%%%% % %%% %%% %%%%%%% % %
% % % % % % % %
%%%%% %%%%% %%% % % % %%% %%%%% % %%%
% % % % % % % % % % % % %
% %%% % % % %%% %%%%% %%% % %%% %%% %
% % % % % % % % %
%%% %%%%%%%%% %%%%%%% %%% %%% % % % %
% % % % % % %
% % %%%%% % %%% % % %%% % %%% %%% % %
% % % % % % % % % % % % % %
% % % %%%%%%% % %%%%%%%%% %%% % %%% %
% % % % % % % % % %
%%% %%% % %%%%% %%%%% %%% %%% %%%%% %
% % % % % % % % %
% % % % % % %%% %%% %%% % % % % % %
% % % % % %% % % % % % % % % %
% % %%%%% % %%% %%% % %%% %%% %%%%%
% % % % % % % % % % %
% %%% % % % %%% %%% %%%%%%%%% % %%%
% % % % % % %
% %%% %%%%%%%%%%%%%%%%%%%%% % % %%% %
% % % %
% % % %%%%% %%% % % % % %%%%%%%%%%%%%
% % % % % % % % % % % %
% % %%% %%% % % % %%%%%%%%% %%% % % %
% % % % % % %P % % % % % %
% %%% %%% %%% % %%% % % %%%%% % %%%%%
% % % % % % % %
%%% % %%%%% %%%%% %%% %%% % %%% % %%%
% % % % % % % % % % % % % % %
% % %%% % % % % %%%%%%%%% % % % % % %
% % % %
% % % %%% %%% %%%%%%% %%% %%% %%% %
%.% % % % % .%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-37
View File
@@ -1,37 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% % % % % % % %
% %%%%%%% % %%% % %%% %%% %%%%%%% % %
% % % % % % % %
%%%%% %%%%% %%% % % % %%% %%%%% % %%%
% % % % % % % % % % % % % %
% %%% % % % %%% %%%%% %%% % %%% %%% %
% % % % % % % % %
%%% %%%%%%%%% %%%%%%% %%% %%% % % % %
% % % % % % %
% % %%%%% % %%% % % %%% % %%% %%% % %
% % % % % % % % % % % % % %
% % % %%%%%%% % %%%%%%%%% %%% % %%% %
% % % % % % % % % %
%%% %%% % %%%%% %%%%% %%% %%% %%%%% %
% % % % % % % % % % % %
% % % % % %%% %%% %%% %%% % % % % % %
% % % % % % % % %
%%% %%%%%%% % % %%%%% %%% % %%% %%%%%
% % % % % % % % % %
%%%%% % % %%%%%%%%% %%%%%%%%%%% % %%%
% % % % % % % % %
% %%% %%%%% %%%%%%%%% %%%%% % % %%% %
% % % % % % %
% % % %%%%% %%% % % % % %%%%%%%%%%%%%
% % % % % % % % % % % %
% % %%% %%% % % % %%%%%%%%% %%% % % %
% % % % % % % % % % % % %
% %%% %%% %%%%% %%% % % %%%%% % %%%%%
% % % % % % % % %
%%% % %%%%% %%%%% %%% %%% % %%% % %%%
% % % % % % % % % % % % % % %
% % %%% % % % % %%%%%%%%% % % % % % %
% % % % % %
% % % % %%% %%% %%%%%%% %%% %%% %%% %
%.% % % % % % % % P%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-8
View File
@@ -1,8 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%.%.........%% G % o%%%%.....%
%.%.%%%%%%%.%%%%%% %%%%%%%.%%.%
%............%...%............%
%%%%%...%%%.. ..%.%...%.%%%
%o%%%.%%%%%.%%%%%%%.%%%.%.%%%%%
% ..........Po...%...%. o%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-15
View File
@@ -1,15 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%.....%.................%.....%
%.%%%.%.%%%.%%%%%%%.%%%.%.....%
%.%...%.%......%......%.%.....%
%...%%%.%.%%%%.%.%%%%...%%%...%
%%%.%.%.%.%......%..%.%...%.%%%
%...%.%%%.%.%%% %%%.%.%%%.%...%
%.%%%.......% %.......%%%.%
%...%.%%%%%.%%%%%%%.%.%%%.%...%
%%%.%...%.%....%....%.%...%.%%%
%...%%%.%.%%%%.%.%%%%.%.%%%...%
%.......%......%......%.....%.%
%.....%.%%%.%%%%%%%.%%%.%.%%%.%
%.....%........P....%...%.....%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-14
View File
@@ -1,14 +0,0 @@
%%%%%%%%%%%%%%
%. . . . . % %
% % %
%. . . . . %G%
% % %
%. . . . . % %
% % %
%. . . . . % %
% P %G%
%. . . . . % %
% % %
%. . . . . % %
% % %
%%%%%%%%%%%%%%
-11
View File
@@ -1,11 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% %
% P %
% %
% %
% %
%. %
%%%%%%%%%%%%%%%%%%%%%
-8
View File
@@ -1,8 +0,0 @@
%%%%%%
%....%
% %%.%
% %%.%
%.P .%
%.%%%%
%....%
%%%%%%
-14
View File
@@ -1,14 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%. % % % %.%
% % % %%%%%% %%%%%%% % %
% % % % % %
%%%%% %%%%% %%% %% %%%%% % %%%
% % % % % % % % %
% %%% % % % %%%%%%%% %%% %%% %
% % %% % % % %
%%% % %%%%%%% %%%% %%% % % % %
% % %% % % %
% % %%%%% % %%%% % %%% %%% % %
% % % % % % %%% %
%. %P%%%%% % %%% % .%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-18
View File
@@ -1,18 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% P%
% %%%%%%%%%%%%%%%%%%% %%% %%%%%%%% %
% %% % % %%% %%% %% ... %
% %% % % % % %%%% %%%%%%%%% %% %%%%%
% %% % % % % % %% %% %% ... %
% %% % % % % % %%%% %%% %%%%%% %
% % % % % % %% %%%%%%%% ... %
% %% % % %%%%%%%% %% %% %%%%%
% %% % %% %%%%%%%%% %% ... %
% %%%%%% %%%%%%% %% %%%%%% %
%%%%%% % %%%% %% % ... %
% %%%%%% %%%%% % %% %% %%%%%
% %%%%%% % %%%%% %% %
% %%%%%% %%%%%%%%%%% %% %% %
%%%%%%%%%% %%%%%% %
%. %%%%%%%%%%%%%%%% ...... %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-18
View File
@@ -1,18 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% P%
% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% %
% %% % % %%%%%%% %% %
% %% % % % % %%%% %%%%%%%%% %% %%%%%
% %% % % % % %% %% %
% %% % % % % % %%%% %%% %%%%%% %
% % % % % % %% %%%%%%%% %
% %% % % %%%%%%%% %% %% %%%%%
% %% % %% %%%%%%%%% %% %
% %%%%%% %%%%%%% %% %%%%%% %
%%%%%% % %%%% %% % %
% %%%%%% %%%%% % %% %% %%%%%
% %%%%%% % %%%%% %% %
% %%%%%% %%%%%%%%%%% %% %% %
%%%%%%%%%% %%%%%% %
%. %%%%%%%%%%%%%%%% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-6
View File
@@ -1,6 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%.% ....%% G %%%%%% o%%.%
%.%o%%%%%%%.%%%%%%% %%%%%.%
% %%%.%%%%%.%%%%%%%.%%%.%.%%%.%
% ..........Po...%.........%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-18
View File
@@ -1,18 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% P%
% %%%%%%%%%%%%%%%%%%% %%% %%%%%%%% %
% %% % % %%% %%% %%GG %
% %% % % % % %%%% %%%%%%%%% %% %%%%%
% %% % % % % % %%GG %% %
% %% % % % % % %%%%% %%% %%%%%% %
% %% % % % % %% %%%%%%%%% %
% %% % % %%%%%%%% %% %% %%%%%
% %% % %% %%%%%%%%% %% %
% %%% %% %%%%%%% %% %%%%%% %
%%%%%% % % %% %% %
% %%%%%% %% %% %% %% %%%%%
% %%%%%% % %%%%% %% %
% %%%% %%%%% %%%%%% %
%%%%%%%% % %%%%%% %
%. %%%%%%%%%%%%%%%% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-8
View File
@@ -1,8 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%............%%%%%............%
%%%.%...%%%.........%.%...%.%%%
%...%%%.%.%%%%.%.%%%%%%.%%%...%
%.%.....%......%......%.....%.%
%.%%%.%%%%%.%%%%%%%.%%%.%.%%%%%
%.....%........P....%...%.....%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-7
View File
@@ -1,7 +0,0 @@
%%%%%%%%%%%%%%%%%%%%
%...%.........%%...%
%.%.%.%%%%%%%%%%.%.%
%..................%
%%%%%%%%.%.%%%%%%%P%
%%%%%%%%....... %
%%%%%%%%%%%%%%%%%%%%
-23
View File
@@ -1,23 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% P%
% % %
% % %
% % %
% % %
% % %
% % % %
% % % %
% % % %
% % % %
% % % %
% % % %
% % % %
%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%
% % %
% % %
% % %
% %
% %
% %
%. %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-7
View File
@@ -1,7 +0,0 @@
%%%%%%%%%%%%%%%%%%%%
%..................%
%..................%
%........P.........%
%..................%
%..................%
%%%%%%%%%%%%%%%%%%%%
-10
View File
@@ -1,10 +0,0 @@
%%%%%%%%%%%%%%%%%%%%%%
% %% % % %
% %%%%%% % %%%%%% %
%%%%%% P % %
% % %%%%%% %% %%%%%
% %%%% % % %
% %%% %%% % %
%%%%%%%%%% %%%%%% %
%. %% %
%%%%%%%%%%%%%%%%%%%%%%
-15
View File
@@ -1,15 +0,0 @@
%%%%%%%%%
%.. % G %
%%% %%%%%
% %
%%%%%%% %
% %
% %%%%% %
% % %
%%%%% % %
% %o%
% %%%%%%%
% .%
%%%%%%%.%
%Po .%
%%%%%%%%%
-5
View File
@@ -1,5 +0,0 @@
%%%%%%%%%%%%%%%%%%%%
%. ...P .%
%.%%.%%.%%.%%.%% %.%
% %% %..... %.%
%%%%%%%%%%%%%%%%%%%%
-3
View File
@@ -1,3 +0,0 @@
%%%%%%%%%%
%. P%
%%%%%%%%%%
-5
View File
@@ -1,5 +0,0 @@
%%%%%
%.P %
%%% %
%. %
%%%%%
-8
View File
@@ -1,8 +0,0 @@
%%%%%%%%
%. .%
% P %
% %%%% %
% % %
% % %%%%
%.% .%
%%%%%%%%
-7
View File
@@ -1,7 +0,0 @@
%%%%%%%
% P%
% %%% %
% % %
%% %%
%. %%%%
%%%%%%%
-7
View File
@@ -1,7 +0,0 @@
%%%%%%%%%
% G %...%
%%%%%%% %
%Po %
%.%%.%%.%
%.%%....%
%%%%%%%%%
-7
View File
@@ -1,7 +0,0 @@
%%%%%%%%%
%.. ..%
%%%%.%% %
% P %
%.%% %%.%
%.%. .%
%%%%%%%%%
-7
View File
@@ -1,7 +0,0 @@
%%%%%%%%%%%%%%%%%%%%
%. ..% %
%.%%.%%.%%.%%.%% % %
% P % %
%%%%%%%%%%%%%%%%%% %
%..... %
%%%%%%%%%%%%%%%%%%%%
+178
View File
@@ -0,0 +1,178 @@
# multiAgents.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).
from util import manhattanDistance
from game import Directions
import random, util
from game import Agent
class ReflexAgent(Agent):
"""
A reflex agent chooses an action at each choice point by examining
its alternatives via a state evaluation function.
The code below is provided as a guide. You are welcome to change
it in any way you see fit, so long as you don't touch our method
headers.
"""
def getAction(self, gameState):
"""
You do not need to change this method, but you're welcome to.
getAction chooses among the best options according to the evaluation function.
Just like in the previous project, getAction takes a GameState and returns
some Directions.X for some X in the set {NORTH, SOUTH, WEST, EAST, STOP}
"""
# Collect legal moves and successor states
legalMoves = gameState.getLegalActions()
# Choose one of the best actions
scores = [self.evaluationFunction(gameState, action) for action in legalMoves]
bestScore = max(scores)
bestIndices = [index for index in range(len(scores)) if scores[index] == bestScore]
chosenIndex = random.choice(bestIndices) # Pick randomly among the best
"Add more of your code here if you want to"
return legalMoves[chosenIndex]
def evaluationFunction(self, currentGameState, action):
"""
Design a better evaluation function here.
The evaluation function takes in the current and proposed successor
GameStates (pacman.py) and returns a number, where higher numbers are better.
The code below extracts some useful information from the state, like the
remaining food (newFood) and Pacman position after moving (newPos).
newScaredTimes holds the number of moves that each ghost will remain
scared because of Pacman having eaten a power pellet.
Print out these variables to see what you're getting, then combine them
to create a masterful evaluation function.
"""
# Useful information you can extract from a GameState (pacman.py)
successorGameState = currentGameState.generatePacmanSuccessor(action)
newPos = successorGameState.getPacmanPosition()
newFood = successorGameState.getFood()
newGhostStates = successorGameState.getGhostStates()
newScaredTimes = [ghostState.scaredTimer for ghostState in newGhostStates]
"*** YOUR CODE HERE ***"
return successorGameState.getScore()
def scoreEvaluationFunction(currentGameState):
"""
This default evaluation function just returns the score of the state.
The score is the same one displayed in the Pacman GUI.
This evaluation function is meant for use with adversarial search agents
(not reflex agents).
"""
return currentGameState.getScore()
class MultiAgentSearchAgent(Agent):
"""
This class provides some common elements to all of your
multi-agent searchers. Any methods defined here will be available
to the MinimaxPacmanAgent, AlphaBetaPacmanAgent & ExpectimaxPacmanAgent.
You *do not* need to make any changes here, but you can if you want to
add functionality to all your adversarial search agents. Please do not
remove anything, however.
Note: this is an abstract class: one that should not be instantiated. It's
only partially specified, and designed to be extended. Agent (game.py)
is another abstract class.
"""
def __init__(self, evalFn = 'scoreEvaluationFunction', depth = '2'):
self.index = 0 # Pacman is always agent index 0
self.evaluationFunction = util.lookup(evalFn, globals())
self.depth = int(depth)
class MinimaxAgent(MultiAgentSearchAgent):
"""
Your minimax agent (question 2)
"""
def getAction(self, gameState):
"""
Returns the minimax action from the current gameState using self.depth
and self.evaluationFunction.
Here are some method calls that might be useful when implementing minimax.
gameState.getLegalActions(agentIndex):
Returns a list of legal actions for an agent
agentIndex=0 means Pacman, ghosts are >= 1
gameState.generateSuccessor(agentIndex, action):
Returns the successor game state after an agent takes an action
gameState.getNumAgents():
Returns the total number of agents in the game
gameState.isWin():
Returns whether or not the game state is a winning state
gameState.isLose():
Returns whether or not the game state is a losing state
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
class AlphaBetaAgent(MultiAgentSearchAgent):
"""
Your minimax agent with alpha-beta pruning (question 3)
"""
def getAction(self, gameState):
"""
Returns the minimax action using self.depth and self.evaluationFunction
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
class ExpectimaxAgent(MultiAgentSearchAgent):
"""
Your expectimax agent (question 4)
"""
def getAction(self, gameState):
"""
Returns the expectimax action using self.depth and self.evaluationFunction
All ghosts should be modeled as choosing uniformly at random from their
legal moves.
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def betterEvaluationFunction(currentGameState):
"""
Your extreme ghost-hunting, pellet-nabbing, food-gobbling, unstoppable
evaluation function (question 5).
DESCRIPTION: <write something here so we know what you did>
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
# Abbreviation
better = betterEvaluationFunction
+578
View File
@@ -0,0 +1,578 @@
# multiagentTestClasses.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).
# A minimax tree which interfaces like gameState
# state.getNumAgents()
# state.isWin()
# state.isLose()
# state.generateSuccessor(agentIndex, action)
# state.getScore()
# used by multiAgents.scoreEvaluationFunction, which is the default
#
import testClasses
import json
from collections import defaultdict
from pprint import PrettyPrinter
pp = PrettyPrinter()
from game import Agent
from pacman import GameState
from ghostAgents import RandomGhost, DirectionalGhost
import random
import math
import traceback
import sys
import os
import layout
import pacman
import autograder
# import grading
VERBOSE = False
class MultiagentTreeState(object):
def __init__(self, problem, state):
self.problem = problem
self.state = state
def generateSuccessor(self, agentIndex, action):
if VERBOSE:
print("generateSuccessor(%s, %s, %s) -> %s" % (self.state, agentIndex,
action, self.problem.stateToSuccessorMap[self.state][action]))
successor = self.problem.stateToSuccessorMap[self.state][action]
self.problem.generatedStates.add(successor)
return MultiagentTreeState(self.problem, successor)
def getScore(self):
if VERBOSE:
print("getScore(%s) -> %s" %
(self.state, self.problem.evaluation[self.state]))
if self.state not in self.problem.evaluation:
raise Exception(
'getScore() called on non-terminal state or before maximum depth achieved.')
return float(self.problem.evaluation[self.state])
def getLegalActions(self, agentIndex=0):
if VERBOSE:
print("getLegalActions(%s) -> %s" %
(self.state, self.problem.stateToActions[self.state]))
# if len(self.problem.stateToActions[self.state]) == 0:
# print "WARNING: getLegalActions called on leaf state %s" % (self.state,)
return list(self.problem.stateToActions[self.state])
def isWin(self):
if VERBOSE:
print("isWin(%s) -> %s" %
(self.state, self.state in self.problem.winStates))
return self.state in self.problem.winStates
def isLose(self):
if VERBOSE:
print("isLose(%s) -> %s" %
(self.state, self.state in self.problem.loseStates))
return self.state in self.problem.loseStates
def getNumAgents(self):
if VERBOSE:
print("getNumAgents(%s) -> %s" %
(self.state, self.problem.numAgents))
return self.problem.numAgents
class MultiagentTreeProblem(object):
def __init__(self, numAgents, startState, winStates, loseStates, successors, evaluation):
self.startState = MultiagentTreeState(self, startState)
self.numAgents = numAgents
self.winStates = winStates
self.loseStates = loseStates
self.evaluation = evaluation
self.successors = successors
self.reset()
self.stateToSuccessorMap = defaultdict(dict)
self.stateToActions = defaultdict(list)
for state, action, nextState in successors:
self.stateToActions[state].append(action)
self.stateToSuccessorMap[state][action] = nextState
def reset(self):
self.generatedStates = set([self.startState.state])
def parseTreeProblem(testDict):
numAgents = int(testDict["num_agents"])
startState = testDict["start_state"]
winStates = set(testDict["win_states"].split(" "))
loseStates = set(testDict["lose_states"].split(" "))
successors = []
evaluation = {}
for line in testDict["evaluation"].split('\n'):
tokens = line.split()
if len(tokens) == 2:
state, value = tokens
evaluation[state] = float(value)
else:
raise Exception("[parseTree] Bad evaluation line: |%s|" % (line,))
for line in testDict["successors"].split('\n'):
tokens = line.split()
if len(tokens) == 3:
state, action, nextState = tokens
successors.append((state, action, nextState))
else:
raise Exception("[parseTree] Bad successor line: |%s|" % (line,))
return MultiagentTreeProblem(numAgents, startState, winStates, loseStates, successors, evaluation)
def run(lay, layName, pac, ghosts, disp, nGames=1, name='games'):
"""
Runs a few games and outputs their statistics.
"""
starttime = time.time()
print('*** Running %s on' % name, layName, '%d time(s).' % nGames)
games = pacman.runGames(lay, pac, ghosts, disp,
nGames, False, catchExceptions=True, timeout=120)
print('*** Finished running %s on' % name, layName,
'after %d seconds.' % (time.time() - starttime))
stats = {'time': time.time() - starttime, 'wins': [g.state.isWin() for g in games].count(True), 'games': games, 'scores': [g.state.getScore() for g in games],
'timeouts': [g.agentTimeout for g in games].count(True), 'crashes': [g.agentCrashed for g in games].count(True)}
print('*** Won %d out of %d games. Average score: %f ***' %
(stats['wins'], len(games), sum(stats['scores']) * 1.0 / len(games)))
return stats
class GradingAgent(Agent):
def __init__(self, seed, studentAgent, optimalActions, altDepthActions, partialPlyBugActions):
# save student agent and actions of refernce agents
self.studentAgent = studentAgent
self.optimalActions = optimalActions
self.altDepthActions = altDepthActions
self.partialPlyBugActions = partialPlyBugActions
# create fields for storing specific wrong actions
self.suboptimalMoves = []
self.wrongStatesExplored = -1
# boolean vectors represent types of implementation the student could have
self.actionsConsistentWithOptimal = [
True for i in range(len(optimalActions[0]))]
self.actionsConsistentWithAlternativeDepth = [
True for i in range(len(altDepthActions[0]))]
self.actionsConsistentWithPartialPlyBug = [
True for i in range(len(partialPlyBugActions[0]))]
# keep track of elapsed moves
self.stepCount = 0
self.seed = seed
def registerInitialState(self, state):
if 'registerInitialState' in dir(self.studentAgent):
self.studentAgent.registerInitialState(state)
random.seed(self.seed)
def getAction(self, state):
GameState.getAndResetExplored()
studentAction = (self.studentAgent.getAction(state),
len(GameState.getAndResetExplored()))
optimalActions = self.optimalActions[self.stepCount]
altDepthActions = self.altDepthActions[self.stepCount]
partialPlyBugActions = self.partialPlyBugActions[self.stepCount]
studentOptimalAction = False
curRightStatesExplored = False
for i in range(len(optimalActions)):
if studentAction[0] in optimalActions[i][0]:
studentOptimalAction = True
else:
self.actionsConsistentWithOptimal[i] = False
if studentAction[1] == int(optimalActions[i][1]):
curRightStatesExplored = True
if not curRightStatesExplored and self.wrongStatesExplored < 0:
self.wrongStatesExplored = 1
for i in range(len(altDepthActions)):
if studentAction[0] not in altDepthActions[i]:
self.actionsConsistentWithAlternativeDepth[i] = False
for i in range(len(partialPlyBugActions)):
if studentAction[0] not in partialPlyBugActions[i]:
self.actionsConsistentWithPartialPlyBug[i] = False
if not studentOptimalAction:
self.suboptimalMoves.append(
(state, studentAction[0], optimalActions[0][0][0]))
self.stepCount += 1
random.seed(self.seed + self.stepCount)
return optimalActions[0][0][0]
def getSuboptimalMoves(self):
return self.suboptimalMoves
def getWrongStatesExplored(self):
return self.wrongStatesExplored
def checkFailure(self):
"""
Return +n if have n suboptimal moves.
Return -1 if have only off by one depth moves.
Return 0 otherwise.
"""
if self.wrongStatesExplored > 0:
return -3
if self.actionsConsistentWithOptimal.count(True) > 0:
return 0
elif self.actionsConsistentWithPartialPlyBug.count(True) > 0:
return -2
elif self.actionsConsistentWithAlternativeDepth.count(True) > 0:
return -1
else:
return len(self.suboptimalMoves)
class PolyAgent(Agent):
def __init__(self, seed, multiAgents, ourPacOptions, depth):
# prepare our pacman agents
solutionAgents, alternativeDepthAgents, partialPlyBugAgents = self.construct_our_pacs(
multiAgents, ourPacOptions)
for p in solutionAgents:
p.depth = depth
for p in partialPlyBugAgents:
p.depth = depth
for p in alternativeDepthAgents[:2]:
p.depth = max(1, depth - 1)
for p in alternativeDepthAgents[2:]:
p.depth = depth + 1
self.solutionAgents = solutionAgents
self.alternativeDepthAgents = alternativeDepthAgents
self.partialPlyBugAgents = partialPlyBugAgents
# prepare fields for storing the results
self.optimalActionLists = []
self.alternativeDepthLists = []
self.partialPlyBugLists = []
self.seed = seed
self.stepCount = 0
def select(self, list, indices):
"""
Return a sublist of elements given by indices in list.
"""
return [list[i] for i in indices]
def construct_our_pacs(self, multiAgents, keyword_dict):
pacs_without_stop = [multiAgents.StaffMultiAgentSearchAgent(
**keyword_dict) for i in range(3)]
keyword_dict['keepStop'] = 'True'
pacs_with_stop = [multiAgents.StaffMultiAgentSearchAgent(
**keyword_dict) for i in range(3)]
keyword_dict['usePartialPlyBug'] = 'True'
partial_ply_bug_pacs = [
multiAgents.StaffMultiAgentSearchAgent(**keyword_dict)]
keyword_dict['keepStop'] = 'False'
partial_ply_bug_pacs = partial_ply_bug_pacs + \
[multiAgents.StaffMultiAgentSearchAgent(**keyword_dict)]
for pac in pacs_with_stop + pacs_without_stop + partial_ply_bug_pacs:
pac.verbose = False
ourpac = [pacs_with_stop[0], pacs_without_stop[0]]
alternative_depth_pacs = self.select(
pacs_with_stop + pacs_without_stop, [1, 4, 2, 5])
return (ourpac, alternative_depth_pacs, partial_ply_bug_pacs)
def registerInitialState(self, state):
for agent in self.solutionAgents + self.alternativeDepthAgents:
if 'registerInitialState' in dir(agent):
agent.registerInitialState(state)
random.seed(self.seed)
def getAction(self, state):
# survey agents
GameState.getAndResetExplored()
optimalActionLists = []
for agent in self.solutionAgents:
optimalActionLists.append((agent.getBestPacmanActions(
state)[0], len(GameState.getAndResetExplored())))
alternativeDepthLists = [agent.getBestPacmanActions(
state)[0] for agent in self.alternativeDepthAgents]
partialPlyBugLists = [agent.getBestPacmanActions(
state)[0] for agent in self.partialPlyBugAgents]
# record responses
self.optimalActionLists.append(optimalActionLists)
self.alternativeDepthLists.append(alternativeDepthLists)
self.partialPlyBugLists.append(partialPlyBugLists)
self.stepCount += 1
random.seed(self.seed + self.stepCount)
return optimalActionLists[0][0][0]
def getTraces(self):
# return traces from individual agents
return (self.optimalActionLists, self.alternativeDepthLists, self.partialPlyBugLists)
class PacmanGameTreeTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(PacmanGameTreeTest, self).__init__(question, testDict)
self.seed = int(self.testDict['seed'])
self.alg = self.testDict['alg']
self.layout_text = self.testDict['layout']
self.layout_name = self.testDict['layoutName']
self.depth = int(self.testDict['depth'])
self.max_points = int(self.testDict['max_points'])
def execute(self, grades, moduleDict, solutionDict):
# load student code and staff code solutions
multiAgents = moduleDict['multiAgents']
studentAgent = getattr(multiAgents, self.alg)(depth=self.depth)
allActions = [json.loads(x)
for x in solutionDict['optimalActions'].split('\n')]
altDepthActions = [json.loads(
x) for x in solutionDict['altDepthActions'].split('\n')]
partialPlyBugActions = [json.loads(
x) for x in solutionDict['partialPlyBugActions'].split('\n')]
# set up game state and play a game
random.seed(self.seed)
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
pac = GradingAgent(self.seed, studentAgent, allActions,
altDepthActions, partialPlyBugActions)
# check return codes and assign grades
disp = self.question.getDisplay()
stats = run(lay, self.layout_name, pac, [DirectionalGhost(
i + 1) for i in range(2)], disp, name=self.alg)
if stats['timeouts'] > 0:
self.addMessage('Agent timed out on smallClassic. No credit')
return self.testFail(grades)
if stats['crashes'] > 0:
self.addMessage('Agent crashed on smallClassic. No credit')
return self.testFail(grades)
code = pac.checkFailure()
if code == 0:
return self.testPass(grades)
elif code == -3:
if pac.getWrongStatesExplored() >= 0:
self.addMessage('Bug: Wrong number of states expanded.')
return self.testFail(grades)
else:
return self.testPass(grades)
elif code == -2:
self.addMessage('Bug: Partial Ply Bug')
return self.testFail(grades)
elif code == -1:
self.addMessage('Bug: Search depth off by 1')
return self.testFail(grades)
elif code > 0:
moves = pac.getSuboptimalMoves()
state, studentMove, optMove = random.choice(moves)
self.addMessage('Bug: Suboptimal moves')
self.addMessage('State:%s\nStudent Move:%s\nOptimal Move:%s' % (
state, studentMove, optMove))
return self.testFail(grades)
def writeList(self, handle, name, list):
handle.write('%s: """\n' % name)
for l in list:
handle.write('%s\n' % json.dumps(l))
handle.write('"""\n')
def writeSolution(self, moduleDict, filePath):
# load module, set seed, create ghosts and macman, run game
multiAgents = moduleDict['multiAgents']
random.seed(self.seed)
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
if self.alg == 'ExpectimaxAgent':
ourPacOptions = {'expectimax': 'True'}
elif self.alg == 'AlphaBetaAgent':
ourPacOptions = {'alphabeta': 'True'}
else:
ourPacOptions = {}
pac = PolyAgent(self.seed, multiAgents, ourPacOptions, self.depth)
disp = self.question.getDisplay()
run(lay, self.layout_name, pac, [DirectionalGhost(
i + 1) for i in range(2)], disp, name=self.alg)
(optimalActions, altDepthActions, partialPlyBugActions) = pac.getTraces()
# recover traces and record to file
handle = open(filePath, 'w')
self.writeList(handle, 'optimalActions', optimalActions)
self.writeList(handle, 'altDepthActions', altDepthActions)
self.writeList(handle, 'partialPlyBugActions', partialPlyBugActions)
handle.close()
class GraphGameTreeTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(GraphGameTreeTest, self).__init__(question, testDict)
self.problem = parseTreeProblem(testDict)
self.alg = self.testDict['alg']
self.diagram = self.testDict['diagram'].split('\n')
self.depth = int(self.testDict['depth'])
def solveProblem(self, multiAgents):
self.problem.reset()
studentAgent = getattr(multiAgents, self.alg)(depth=self.depth)
action = studentAgent.getAction(self.problem.startState)
generated = self.problem.generatedStates
return action, " ".join([str(s) for s in sorted(generated)])
def addDiagram(self):
self.addMessage('Tree:')
for line in self.diagram:
self.addMessage(line)
def execute(self, grades, moduleDict, solutionDict):
multiAgents = moduleDict['multiAgents']
goldAction = solutionDict['action']
goldGenerated = solutionDict['generated']
action, generated = self.solveProblem(multiAgents)
fail = False
if action != goldAction:
self.addMessage('Incorrect move for depth=%s' % (self.depth,))
self.addMessage(
' Student move: %s\n Optimal move: %s' % (action, goldAction))
fail = True
if generated != goldGenerated:
self.addMessage(
'Incorrect generated nodes for depth=%s' % (self.depth,))
self.addMessage(' Student generated nodes: %s\n Correct generated nodes: %s' % (
generated, goldGenerated))
fail = True
if fail:
self.addDiagram()
return self.testFail(grades)
else:
return self.testPass(grades)
def writeSolution(self, moduleDict, filePath):
multiAgents = moduleDict['multiAgents']
action, generated = self.solveProblem(multiAgents)
with open(filePath, 'w') as handle:
handle.write('# This is the solution file for %s.\n' % self.path)
handle.write('action: "%s"\n' % (action,))
handle.write('generated: "%s"\n' % (generated,))
return True
import time
from util import TimeoutFunction
class EvalAgentTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(EvalAgentTest, self).__init__(question, testDict)
self.layoutName = testDict['layoutName']
self.agentName = testDict['agentName']
self.ghosts = eval(testDict['ghosts'])
self.maxTime = int(testDict['maxTime'])
self.seed = int(testDict['randomSeed'])
self.numGames = int(testDict['numGames'])
self.scoreMinimum = int(
testDict['scoreMinimum']) if 'scoreMinimum' in testDict else None
self.nonTimeoutMinimum = int(
testDict['nonTimeoutMinimum']) if 'nonTimeoutMinimum' in testDict else None
self.winsMinimum = int(
testDict['winsMinimum']) if 'winsMinimum' in testDict else None
self.scoreThresholds = [int(s) for s in testDict.get(
'scoreThresholds', '').split()]
self.nonTimeoutThresholds = [int(s) for s in testDict.get(
'nonTimeoutThresholds', '').split()]
self.winsThresholds = [int(s) for s in testDict.get(
'winsThresholds', '').split()]
self.maxPoints = sum([len(t) for t in [
self.scoreThresholds, self.nonTimeoutThresholds, self.winsThresholds]])
self.agentArgs = testDict.get('agentArgs', '')
def execute(self, grades, moduleDict, solutionDict):
startTime = time.time()
agentType = getattr(moduleDict['multiAgents'], self.agentName)
agentOpts = pacman.parseAgentArgs(
self.agentArgs) if self.agentArgs != '' else {}
agent = agentType(**agentOpts)
lay = layout.getLayout(self.layoutName, 3)
disp = self.question.getDisplay()
random.seed(self.seed)
games = pacman.runGames(lay, agent, self.ghosts, disp, self.numGames,
False, catchExceptions=True, timeout=self.maxTime)
totalTime = time.time() - startTime
stats = {'time': totalTime, 'wins': [g.state.isWin() for g in games].count(True),
'games': games, 'scores': [g.state.getScore() for g in games],
'timeouts': [g.agentTimeout for g in games].count(True), 'crashes': [g.agentCrashed for g in games].count(True)}
averageScore = sum(stats['scores']) / float(len(stats['scores']))
nonTimeouts = self.numGames - stats['timeouts']
wins = stats['wins']
def gradeThreshold(value, minimum, thresholds, name):
points = 0
passed = (minimum == None) or (value >= minimum)
if passed:
for t in thresholds:
if value >= t:
points += 1
return (passed, points, value, minimum, thresholds, name)
results = [gradeThreshold(averageScore, self.scoreMinimum, self.scoreThresholds, "average score"),
gradeThreshold(nonTimeouts, self.nonTimeoutMinimum,
self.nonTimeoutThresholds, "games not timed out"),
gradeThreshold(wins, self.winsMinimum, self.winsThresholds, "wins")]
totalPoints = 0
for passed, points, value, minimum, thresholds, name in results:
if minimum == None and len(thresholds) == 0:
continue
# print passed, points, value, minimum, thresholds, name
totalPoints += points
if not passed:
assert points == 0
self.addMessage(
"%s %s (fail: below minimum value %s)" % (value, name, minimum))
else:
self.addMessage("%s %s (%s of %s points)" %
(value, name, points, len(thresholds)))
if minimum != None:
self.addMessage(" Grading scheme:")
self.addMessage(" < %s: fail" % (minimum,))
if len(thresholds) == 0 or minimum != thresholds[0]:
self.addMessage(" >= %s: 0 points" % (minimum,))
for idx, threshold in enumerate(thresholds):
self.addMessage(" >= %s: %s points" %
(threshold, idx+1))
elif len(thresholds) > 0:
self.addMessage(" Grading scheme:")
self.addMessage(" < %s: 0 points" % (thresholds[0],))
for idx, threshold in enumerate(thresholds):
self.addMessage(" >= %s: %s points" %
(threshold, idx+1))
if any([not passed for passed, _, _, _, _, _ in results]):
totalPoints = 0
return self.testPartial(grades, totalPoints, self.maxPoints)
def writeSolution(self, moduleDict, filePath):
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
handle.write('# File intentionally blank.\n')
handle.close()
return True
+187 -133
View File
@@ -45,13 +45,19 @@ from game import Directions
from game import Actions from game import Actions
from util import nearestPoint from util import nearestPoint
from util import manhattanDistance from util import manhattanDistance
import util, layout import util
import sys, types, time, random, os import layout
import sys
import types
import time
import random
import os
################################################### ###################################################
# YOUR INTERFACE TO THE PACMAN WORLD: A GameState # # YOUR INTERFACE TO THE PACMAN WORLD: A GameState #
################################################### ###################################################
class GameState: class GameState:
""" """
A GameState specifies the full game state, including the food, capsules, A GameState specifies the full game state, including the food, capsules,
@@ -73,30 +79,33 @@ class GameState:
# static variable keeps track of which states have had getLegalActions called # static variable keeps track of which states have had getLegalActions called
explored = set() explored = set()
def getAndResetExplored(): def getAndResetExplored():
tmp = GameState.explored.copy() tmp = GameState.explored.copy()
GameState.explored = set() GameState.explored = set()
return tmp return tmp
getAndResetExplored = staticmethod(getAndResetExplored) getAndResetExplored = staticmethod(getAndResetExplored)
def getLegalActions( self, agentIndex=0 ): def getLegalActions(self, agentIndex=0):
""" """
Returns the legal actions for the agent specified. Returns the legal actions for the agent specified.
""" """
# GameState.explored.add(self) # GameState.explored.add(self)
if self.isWin() or self.isLose(): return [] if self.isWin() or self.isLose():
return []
if agentIndex == 0: # Pacman is moving if agentIndex == 0: # Pacman is moving
return PacmanRules.getLegalActions( self ) return PacmanRules.getLegalActions(self)
else: else:
return GhostRules.getLegalActions( self, agentIndex ) return GhostRules.getLegalActions(self, agentIndex)
def generateSuccessor( self, agentIndex, action): def generateSuccessor(self, agentIndex, action):
""" """
Returns the successor state after the specified agent takes the action. Returns the successor state after the specified agent takes the action.
""" """
# Check that successors exist # Check that successors exist
if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.') if self.isWin() or self.isLose():
raise Exception('Can\'t generate a successor of a terminal state.')
# Copy current state # Copy current state
state = GameState(self) state = GameState(self)
@@ -104,18 +113,18 @@ class GameState:
# Let agent's logic deal with its action's effects on the board # Let agent's logic deal with its action's effects on the board
if agentIndex == 0: # Pacman is moving if agentIndex == 0: # Pacman is moving
state.data._eaten = [False for i in range(state.getNumAgents())] state.data._eaten = [False for i in range(state.getNumAgents())]
PacmanRules.applyAction( state, action ) PacmanRules.applyAction(state, action)
else: # A ghost is moving else: # A ghost is moving
GhostRules.applyAction( state, action, agentIndex ) GhostRules.applyAction(state, action, agentIndex)
# Time passes # Time passes
if agentIndex == 0: if agentIndex == 0:
state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around
else: else:
GhostRules.decrementTimer( state.data.agentStates[agentIndex] ) GhostRules.decrementTimer(state.data.agentStates[agentIndex])
# Resolve multi-agent effects # Resolve multi-agent effects
GhostRules.checkDeath( state, agentIndex ) GhostRules.checkDeath(state, agentIndex)
# Book keeping # Book keeping
state.data._agentMoved = agentIndex state.data._agentMoved = agentIndex
@@ -124,16 +133,16 @@ class GameState:
GameState.explored.add(state) GameState.explored.add(state)
return state return state
def getLegalPacmanActions( self ): def getLegalPacmanActions(self):
return self.getLegalActions( 0 ) return self.getLegalActions(0)
def generatePacmanSuccessor( self, action ): def generatePacmanSuccessor(self, action):
""" """
Generates the successor state after the specified pacman move Generates the successor state after the specified pacman move
""" """
return self.generateSuccessor( 0, action ) return self.generateSuccessor(0, action)
def getPacmanState( self ): def getPacmanState(self):
""" """
Returns an AgentState object for pacman (in game.py) Returns an AgentState object for pacman (in game.py)
@@ -142,18 +151,18 @@ class GameState:
""" """
return self.data.agentStates[0].copy() return self.data.agentStates[0].copy()
def getPacmanPosition( self ): def getPacmanPosition(self):
return self.data.agentStates[0].getPosition() return self.data.agentStates[0].getPosition()
def getGhostStates( self ): def getGhostStates(self):
return self.data.agentStates[1:] return self.data.agentStates[1:]
def getGhostState( self, agentIndex ): def getGhostState(self, agentIndex):
if agentIndex == 0 or agentIndex >= self.getNumAgents(): if agentIndex == 0 or agentIndex >= self.getNumAgents():
raise Exception("Invalid index passed to getGhostState") raise Exception("Invalid index passed to getGhostState")
return self.data.agentStates[agentIndex] return self.data.agentStates[agentIndex]
def getGhostPosition( self, agentIndex ): def getGhostPosition(self, agentIndex):
if agentIndex == 0: if agentIndex == 0:
raise Exception("Pacman's index passed to getGhostPosition") raise Exception("Pacman's index passed to getGhostPosition")
return self.data.agentStates[agentIndex].getPosition() return self.data.agentStates[agentIndex].getPosition()
@@ -161,10 +170,10 @@ class GameState:
def getGhostPositions(self): def getGhostPositions(self):
return [s.getPosition() for s in self.getGhostStates()] return [s.getPosition() for s in self.getGhostStates()]
def getNumAgents( self ): def getNumAgents(self):
return len( self.data.agentStates ) return len(self.data.agentStates)
def getScore( self ): def getScore(self):
return float(self.data.score) return float(self.data.score)
def getCapsules(self): def getCapsules(self):
@@ -173,7 +182,7 @@ class GameState:
""" """
return self.data.capsules return self.data.capsules
def getNumFood( self ): def getNumFood(self):
return self.data.food.count() return self.data.food.count()
def getFood(self): def getFood(self):
@@ -206,10 +215,10 @@ class GameState:
def hasWall(self, x, y): def hasWall(self, x, y):
return self.data.layout.walls[x][y] return self.data.layout.walls[x][y]
def isLose( self ): def isLose(self):
return self.data._lose return self.data._lose
def isWin( self ): def isWin(self):
return self.data._win return self.data._win
############################################# #############################################
@@ -217,7 +226,7 @@ class GameState:
# You shouldn't need to call these directly # # You shouldn't need to call these directly #
############################################# #############################################
def __init__( self, prevState = None ): def __init__(self, prevState=None):
""" """
Generates a new state by copying information from its predecessor. Generates a new state by copying information from its predecessor.
""" """
@@ -226,28 +235,28 @@ class GameState:
else: else:
self.data = GameStateData() self.data = GameStateData()
def deepCopy( self ): def deepCopy(self):
state = GameState( self ) state = GameState(self)
state.data = self.data.deepCopy() state.data = self.data.deepCopy()
return state return state
def __eq__( self, other ): def __eq__(self, other):
""" """
Allows two states to be compared. Allows two states to be compared.
""" """
return hasattr(other, 'data') and self.data == other.data return hasattr(other, 'data') and self.data == other.data
def __hash__( self ): def __hash__(self):
""" """
Allows states to be keys of dictionaries. Allows states to be keys of dictionaries.
""" """
return hash( self.data ) return hash(self.data)
def __str__( self ): def __str__(self):
return str(self.data) return str(self.data)
def initialize( self, layout, numGhostAgents=1000 ): def initialize(self, layout, numGhostAgents=1000):
""" """
Creates an initial game state from a layout array (see layout.py). Creates an initial game state from a layout array (see layout.py).
""" """
@@ -259,22 +268,25 @@ class GameState:
# You shouldn't need to look through the code in this section of the file. # # You shouldn't need to look through the code in this section of the file. #
############################################################################ ############################################################################
SCARED_TIME = 40 # Moves ghosts are scared SCARED_TIME = 40 # Moves ghosts are scared
COLLISION_TOLERANCE = 0.7 # How close ghosts must be to Pacman to kill COLLISION_TOLERANCE = 0.7 # How close ghosts must be to Pacman to kill
TIME_PENALTY = 1 # Number of points lost each round TIME_PENALTY = 1 # Number of points lost each round
class ClassicGameRules: class ClassicGameRules:
""" """
These game rules manage the control flow of a game, deciding when These game rules manage the control flow of a game, deciding when
and how the game starts and ends. and how the game starts and ends.
""" """
def __init__(self, timeout=30): def __init__(self, timeout=30):
self.timeout = timeout self.timeout = timeout
def newGame( self, layout, pacmanAgent, ghostAgents, display, quiet = False, catchExceptions=False): def newGame(self, layout, pacmanAgent, ghostAgents, display, quiet=False, catchExceptions=False):
agents = [pacmanAgent] + ghostAgents[:layout.getNumGhosts()] agents = [pacmanAgent] + ghostAgents[:layout.getNumGhosts()]
initState = GameState() initState = GameState()
initState.initialize( layout, len(ghostAgents) ) initState.initialize(layout, len(ghostAgents))
game = Game(agents, display, self, catchExceptions=catchExceptions) game = Game(agents, display, self, catchExceptions=catchExceptions)
game.state = initState game.state = initState
self.initialState = initState.deepCopy() self.initialState = initState.deepCopy()
@@ -285,15 +297,19 @@ class ClassicGameRules:
""" """
Checks to see whether it is time to end the game. Checks to see whether it is time to end the game.
""" """
if state.isWin(): self.win(state, game) if state.isWin():
if state.isLose(): self.lose(state, game) self.win(state, game)
if state.isLose():
self.lose(state, game)
def win( self, state, game ): def win(self, state, game):
if not self.quiet: print("Pacman emerges victorious! Score: %d" % state.data.score) if not self.quiet:
print("Pacman emerges victorious! Score: %d" % state.data.score)
game.gameOver = True game.gameOver = True
def lose( self, state, game ): def lose(self, state, game):
if not self.quiet: print("Pacman died! Score: %d" % state.data.score) if not self.quiet:
print("Pacman died! Score: %d" % state.data.score)
game.gameOver = True game.gameOver = True
def getProgress(self, game): def getProgress(self, game):
@@ -320,44 +336,46 @@ class ClassicGameRules:
def getMaxTimeWarnings(self, agentIndex): def getMaxTimeWarnings(self, agentIndex):
return 0 return 0
class PacmanRules: class PacmanRules:
""" """
These functions govern how pacman interacts with his environment under These functions govern how pacman interacts with his environment under
the classic game rules. the classic game rules.
""" """
PACMAN_SPEED=1 PACMAN_SPEED = 1
def getLegalActions( state ): def getLegalActions(state):
""" """
Returns a list of possible actions. Returns a list of possible actions.
""" """
return Actions.getPossibleActions( state.getPacmanState().configuration, state.data.layout.walls ) return Actions.getPossibleActions(state.getPacmanState().configuration, state.data.layout.walls)
getLegalActions = staticmethod( getLegalActions ) getLegalActions = staticmethod(getLegalActions)
def applyAction( state, action ): def applyAction(state, action):
""" """
Edits the state to reflect the results of the action. Edits the state to reflect the results of the action.
""" """
legal = PacmanRules.getLegalActions( state ) legal = PacmanRules.getLegalActions(state)
if action not in legal: if action not in legal:
raise Exception("Illegal action " + str(action)) raise Exception("Illegal action " + str(action))
pacmanState = state.data.agentStates[0] pacmanState = state.data.agentStates[0]
# Update Configuration # Update Configuration
vector = Actions.directionToVector( action, PacmanRules.PACMAN_SPEED ) vector = Actions.directionToVector(action, PacmanRules.PACMAN_SPEED)
pacmanState.configuration = pacmanState.configuration.generateSuccessor( vector ) pacmanState.configuration = pacmanState.configuration.generateSuccessor(
vector)
# Eat # Eat
next = pacmanState.configuration.getPosition() next = pacmanState.configuration.getPosition()
nearest = nearestPoint( next ) nearest = nearestPoint(next)
if manhattanDistance( nearest, next ) <= 0.5 : if manhattanDistance(nearest, next) <= 0.5:
# Remove food # Remove food
PacmanRules.consume( nearest, state ) PacmanRules.consume(nearest, state)
applyAction = staticmethod( applyAction ) applyAction = staticmethod(applyAction)
def consume( position, state ): def consume(position, state):
x,y = position x, y = position
# Eat food # Eat food
if state.data.food[x][y]: if state.data.food[x][y]:
state.data.scoreChange += 10 state.data.scoreChange += 10
@@ -370,70 +388,76 @@ class PacmanRules:
state.data.scoreChange += 500 state.data.scoreChange += 500
state.data._win = True state.data._win = True
# Eat capsule # Eat capsule
if( position in state.getCapsules() ): if(position in state.getCapsules()):
state.data.capsules.remove( position ) state.data.capsules.remove(position)
state.data._capsuleEaten = position state.data._capsuleEaten = position
# Reset all ghosts' scared timers # Reset all ghosts' scared timers
for index in range( 1, len( state.data.agentStates ) ): for index in range(1, len(state.data.agentStates)):
state.data.agentStates[index].scaredTimer = SCARED_TIME state.data.agentStates[index].scaredTimer = SCARED_TIME
consume = staticmethod( consume ) consume = staticmethod(consume)
class GhostRules: class GhostRules:
""" """
These functions dictate how ghosts interact with their environment. These functions dictate how ghosts interact with their environment.
""" """
GHOST_SPEED=1.0 GHOST_SPEED = 1.0
def getLegalActions( state, ghostIndex ):
def getLegalActions(state, ghostIndex):
""" """
Ghosts cannot stop, and cannot turn around unless they Ghosts cannot stop, and cannot turn around unless they
reach a dead end, but can turn 90 degrees at intersections. reach a dead end, but can turn 90 degrees at intersections.
""" """
conf = state.getGhostState( ghostIndex ).configuration conf = state.getGhostState(ghostIndex).configuration
possibleActions = Actions.getPossibleActions( conf, state.data.layout.walls ) possibleActions = Actions.getPossibleActions(
reverse = Actions.reverseDirection( conf.direction ) conf, state.data.layout.walls)
reverse = Actions.reverseDirection(conf.direction)
if Directions.STOP in possibleActions: if Directions.STOP in possibleActions:
possibleActions.remove( Directions.STOP ) possibleActions.remove(Directions.STOP)
if reverse in possibleActions and len( possibleActions ) > 1: if reverse in possibleActions and len(possibleActions) > 1:
possibleActions.remove( reverse ) possibleActions.remove(reverse)
return possibleActions return possibleActions
getLegalActions = staticmethod( getLegalActions ) getLegalActions = staticmethod(getLegalActions)
def applyAction( state, action, ghostIndex): def applyAction(state, action, ghostIndex):
legal = GhostRules.getLegalActions( state, ghostIndex ) legal = GhostRules.getLegalActions(state, ghostIndex)
if action not in legal: if action not in legal:
raise Exception("Illegal ghost action " + str(action)) raise Exception("Illegal ghost action " + str(action))
ghostState = state.data.agentStates[ghostIndex] ghostState = state.data.agentStates[ghostIndex]
speed = GhostRules.GHOST_SPEED speed = GhostRules.GHOST_SPEED
if ghostState.scaredTimer > 0: speed /= 2.0 if ghostState.scaredTimer > 0:
vector = Actions.directionToVector( action, speed ) speed /= 2.0
ghostState.configuration = ghostState.configuration.generateSuccessor( vector ) vector = Actions.directionToVector(action, speed)
applyAction = staticmethod( applyAction ) ghostState.configuration = ghostState.configuration.generateSuccessor(
vector)
applyAction = staticmethod(applyAction)
def decrementTimer( ghostState): def decrementTimer(ghostState):
timer = ghostState.scaredTimer timer = ghostState.scaredTimer
if timer == 1: if timer == 1:
ghostState.configuration.pos = nearestPoint( ghostState.configuration.pos ) ghostState.configuration.pos = nearestPoint(
ghostState.scaredTimer = max( 0, timer - 1 ) ghostState.configuration.pos)
decrementTimer = staticmethod( decrementTimer ) ghostState.scaredTimer = max(0, timer - 1)
decrementTimer = staticmethod(decrementTimer)
def checkDeath( state, agentIndex): def checkDeath(state, agentIndex):
pacmanPosition = state.getPacmanPosition() pacmanPosition = state.getPacmanPosition()
if agentIndex == 0: # Pacman just moved; Anyone can kill him if agentIndex == 0: # Pacman just moved; Anyone can kill him
for index in range( 1, len( state.data.agentStates ) ): for index in range(1, len(state.data.agentStates)):
ghostState = state.data.agentStates[index] ghostState = state.data.agentStates[index]
ghostPosition = ghostState.configuration.getPosition() ghostPosition = ghostState.configuration.getPosition()
if GhostRules.canKill( pacmanPosition, ghostPosition ): if GhostRules.canKill(pacmanPosition, ghostPosition):
GhostRules.collide( state, ghostState, index ) GhostRules.collide(state, ghostState, index)
else: else:
ghostState = state.data.agentStates[agentIndex] ghostState = state.data.agentStates[agentIndex]
ghostPosition = ghostState.configuration.getPosition() ghostPosition = ghostState.configuration.getPosition()
if GhostRules.canKill( pacmanPosition, ghostPosition ): if GhostRules.canKill(pacmanPosition, ghostPosition):
GhostRules.collide( state, ghostState, agentIndex ) GhostRules.collide(state, ghostState, agentIndex)
checkDeath = staticmethod( checkDeath ) checkDeath = staticmethod(checkDeath)
def collide( state, ghostState, agentIndex): def collide(state, ghostState, agentIndex):
if ghostState.scaredTimer > 0: if ghostState.scaredTimer > 0:
state.data.scoreChange += 200 state.data.scoreChange += 200
GhostRules.placeGhost(state, ghostState) GhostRules.placeGhost(state, ghostState)
@@ -444,36 +468,40 @@ class GhostRules:
if not state.data._win: if not state.data._win:
state.data.scoreChange -= 500 state.data.scoreChange -= 500
state.data._lose = True state.data._lose = True
collide = staticmethod( collide ) collide = staticmethod(collide)
def canKill( pacmanPosition, ghostPosition ): def canKill(pacmanPosition, ghostPosition):
return manhattanDistance( ghostPosition, pacmanPosition ) <= COLLISION_TOLERANCE return manhattanDistance(ghostPosition, pacmanPosition) <= COLLISION_TOLERANCE
canKill = staticmethod( canKill ) canKill = staticmethod(canKill)
def placeGhost(state, ghostState): def placeGhost(state, ghostState):
ghostState.configuration = ghostState.start ghostState.configuration = ghostState.start
placeGhost = staticmethod( placeGhost ) placeGhost = staticmethod(placeGhost)
############################# #############################
# FRAMEWORK TO START A GAME # # FRAMEWORK TO START A GAME #
############################# #############################
def default(str): def default(str):
return str + ' [Default: %default]' return str + ' [Default: %default]'
def parseAgentArgs(str): def parseAgentArgs(str):
if str == None: return {} if str == None:
return {}
pieces = str.split(',') pieces = str.split(',')
opts = {} opts = {}
for p in pieces: for p in pieces:
if '=' in p: if '=' in p:
key, val = p.split('=') key, val = p.split('=')
else: else:
key,val = p, 1 key, val = p, 1
opts[key] = val opts[key] = val
return opts return opts
def readCommand( argv ):
def readCommand(argv):
""" """
Processes the command used to run pacman from the command line. Processes the command used to run pacman from the command line.
""" """
@@ -491,18 +519,21 @@ def readCommand( argv ):
parser.add_option('-n', '--numGames', dest='numGames', type='int', parser.add_option('-n', '--numGames', dest='numGames', type='int',
help=default('the number of GAMES to play'), metavar='GAMES', default=1) help=default('the number of GAMES to play'), metavar='GAMES', default=1)
parser.add_option('-l', '--layout', dest='layout', parser.add_option('-l', '--layout', dest='layout',
help=default('the LAYOUT_FILE from which to load the map layout'), help=default(
'the LAYOUT_FILE from which to load the map layout'),
metavar='LAYOUT_FILE', default='mediumClassic') metavar='LAYOUT_FILE', default='mediumClassic')
parser.add_option('-p', '--pacman', dest='pacman', parser.add_option('-p', '--pacman', dest='pacman',
help=default('the agent TYPE in the pacmanAgents module to use'), help=default(
'the agent TYPE in the pacmanAgents module to use'),
metavar='TYPE', default='KeyboardAgent') metavar='TYPE', default='KeyboardAgent')
parser.add_option('-t', '--textGraphics', action='store_true', dest='textGraphics', parser.add_option('-t', '--textGraphics', action='store_true', dest='textGraphics',
help='Display output as text only', default=False) help='Display output as text only', default=False)
parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics', parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics',
help='Generate minimal output and no graphics', default=False) help='Generate minimal output and no graphics', default=False)
parser.add_option('-g', '--ghosts', dest='ghost', parser.add_option('-g', '--ghosts', dest='ghost',
help=default('the ghost agent TYPE in the ghostAgents module to use'), help=default(
metavar = 'TYPE', default='RandomGhost') 'the ghost agent TYPE in the ghostAgents module to use'),
metavar='TYPE', default='RandomGhost')
parser.add_option('-k', '--numghosts', type='int', dest='numGhosts', parser.add_option('-k', '--numghosts', type='int', dest='numGhosts',
help=default('The maximum number of ghosts to use'), default=4) help=default('The maximum number of ghosts to use'), default=4)
parser.add_option('-z', '--zoom', type='float', dest='zoom', parser.add_option('-z', '--zoom', type='float', dest='zoom',
@@ -513,7 +544,7 @@ def readCommand( argv ):
help='Writes game histories to a file (named by the time they were played)', default=False) help='Writes game histories to a file (named by the time they were played)', default=False)
parser.add_option('--replay', dest='gameToReplay', parser.add_option('--replay', dest='gameToReplay',
help='A recorded game file (pickle) to replay', default=None) help='A recorded game file (pickle) to replay', default=None)
parser.add_option('-a','--agentArgs',dest='agentArgs', parser.add_option('-a', '--agentArgs', dest='agentArgs',
help='Comma separated values sent to agent. e.g. "opt1=val1,opt2,opt3=val3"') help='Comma separated values sent to agent. e.g. "opt1=val1,opt2,opt3=val3"')
parser.add_option('-x', '--numTraining', dest='numTraining', type='int', parser.add_option('-x', '--numTraining', dest='numTraining', type='int',
help=default('How many episodes are training (suppresses output)'), default=0) help=default('How many episodes are training (suppresses output)'), default=0)
@@ -530,19 +561,23 @@ def readCommand( argv ):
args = dict() args = dict()
# Fix the random seed # Fix the random seed
if options.fixRandomSeed: random.seed('cs188') if options.fixRandomSeed:
random.seed('cs188')
# Choose a layout # Choose a layout
args['layout'] = layout.getLayout( options.layout ) args['layout'] = layout.getLayout(options.layout)
if args['layout'] == None: raise Exception("The layout " + options.layout + " cannot be found") if args['layout'] == None:
raise Exception("The layout " + options.layout + " cannot be found")
# Choose a Pacman agent # Choose a Pacman agent
noKeyboard = options.gameToReplay == None and (options.textGraphics or options.quietGraphics) noKeyboard = options.gameToReplay == None and (
options.textGraphics or options.quietGraphics)
pacmanType = loadAgent(options.pacman, noKeyboard) pacmanType = loadAgent(options.pacman, noKeyboard)
agentOpts = parseAgentArgs(options.agentArgs) agentOpts = parseAgentArgs(options.agentArgs)
if options.numTraining > 0: if options.numTraining > 0:
args['numTraining'] = options.numTraining args['numTraining'] = options.numTraining
if 'numTraining' not in agentOpts: agentOpts['numTraining'] = options.numTraining if 'numTraining' not in agentOpts:
agentOpts['numTraining'] = options.numTraining
pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs
args['pacman'] = pacman args['pacman'] = pacman
@@ -553,7 +588,7 @@ def readCommand( argv ):
# Choose a ghost agent # Choose a ghost agent
ghostType = loadAgent(options.ghost, noKeyboard) ghostType = loadAgent(options.ghost, noKeyboard)
args['ghosts'] = [ghostType( i+1 ) for i in range( options.numGhosts )] args['ghosts'] = [ghostType(i+1) for i in range(options.numGhosts)]
# Choose a display format # Choose a display format
if options.quietGraphics: if options.quietGraphics:
@@ -565,7 +600,8 @@ def readCommand( argv ):
args['display'] = textDisplay.PacmanGraphics() args['display'] = textDisplay.PacmanGraphics()
else: else:
import graphicsDisplay import graphicsDisplay
args['display'] = graphicsDisplay.PacmanGraphics(options.zoom, frameTime = options.frameTime) args['display'] = graphicsDisplay.PacmanGraphics(
options.zoom, frameTime=options.frameTime)
args['numGames'] = options.numGames args['numGames'] = options.numGames
args['record'] = options.record args['record'] = options.record
args['catchExceptions'] = options.catchExceptions args['catchExceptions'] = options.catchExceptions
@@ -575,15 +611,18 @@ def readCommand( argv ):
if options.gameToReplay != None: if options.gameToReplay != None:
print('Replaying recorded game %s.' % options.gameToReplay) print('Replaying recorded game %s.' % options.gameToReplay)
import pickle import pickle
f = open(options.gameToReplay, 'rb') f = open(options.gameToReplay)
try: recorded = pickle.load(f) try:
finally: f.close() recorded = pickle.load(f)
finally:
f.close()
recorded['display'] = args['display'] recorded['display'] = args['display']
replayGame(**recorded) replayGame(**recorded)
sys.exit(0) sys.exit(0)
return args return args
def loadAgent(pacman, nographics): def loadAgent(pacman, nographics):
# Looks through all pythonPath Directories for the right module, # Looks through all pythonPath Directories for the right module,
pythonPathStr = os.path.expandvars("$PYTHONPATH") pythonPathStr = os.path.expandvars("$PYTHONPATH")
@@ -594,8 +633,10 @@ def loadAgent(pacman, nographics):
pythonPathDirs.append('.') pythonPathDirs.append('.')
for moduleDir in pythonPathDirs: for moduleDir in pythonPathDirs:
if not os.path.isdir(moduleDir): continue if not os.path.isdir(moduleDir):
moduleNames = [f for f in os.listdir(moduleDir) if f.endswith('gents.py')] continue
moduleNames = [f for f in os.listdir(
moduleDir) if f.endswith('gents.py')]
for modulename in moduleNames: for modulename in moduleNames:
try: try:
module = __import__(modulename[:-3]) module = __import__(modulename[:-3])
@@ -603,36 +644,42 @@ def loadAgent(pacman, nographics):
continue continue
if pacman in dir(module): if pacman in dir(module):
if nographics and modulename == 'keyboardAgents.py': if nographics and modulename == 'keyboardAgents.py':
raise Exception('Using the keyboard requires graphics (not text display)') raise Exception(
'Using the keyboard requires graphics (not text display)')
return getattr(module, pacman) return getattr(module, pacman)
raise Exception('The agent ' + pacman + ' is not specified in any *Agents.py.') raise Exception('The agent ' + pacman +
' is not specified in any *Agents.py.')
def replayGame( layout, actions, display ):
import pacmanAgents, ghostAgents def replayGame(layout, actions, display):
import pacmanAgents
import ghostAgents
rules = ClassicGameRules() rules = ClassicGameRules()
agents = [pacmanAgents.GreedyAgent()] + [ghostAgents.RandomGhost(i+1) for i in range(layout.getNumGhosts())] agents = [pacmanAgents.GreedyAgent()] + [ghostAgents.RandomGhost(i+1)
game = rules.newGame( layout, agents[0], agents[1:], display ) for i in range(layout.getNumGhosts())]
game = rules.newGame(layout, agents[0], agents[1:], display)
state = game.state state = game.state
display.initialize(state.data) display.initialize(state.data)
for action in actions: for action in actions:
# Execute the action # Execute the action
state = state.generateSuccessor( *action ) state = state.generateSuccessor(*action)
# Change the display # Change the display
display.update( state.data ) display.update(state.data)
# Allow for game specific conditions (winning, losing, etc.) # Allow for game specific conditions (winning, losing, etc.)
rules.process(state, game) rules.process(state, game)
display.finish() display.finish()
def runGames( layout, pacman, ghosts, display, numGames, record, numTraining = 0, catchExceptions=False, timeout=30 ):
def runGames(layout, pacman, ghosts, display, numGames, record, numTraining=0, catchExceptions=False, timeout=30):
import __main__ import __main__
__main__.__dict__['_display'] = display __main__.__dict__['_display'] = display
rules = ClassicGameRules(timeout) rules = ClassicGameRules(timeout)
games = [] games = []
for i in range( numGames ): for i in range(numGames):
beQuiet = i < numTraining beQuiet = i < numTraining
if beQuiet: if beQuiet:
# Suppress output and graphics # Suppress output and graphics
@@ -642,14 +689,18 @@ def runGames( layout, pacman, ghosts, display, numGames, record, numTraining = 0
else: else:
gameDisplay = display gameDisplay = display
rules.quiet = False rules.quiet = False
game = rules.newGame( layout, pacman, ghosts, gameDisplay, beQuiet, catchExceptions) game = rules.newGame(layout, pacman, ghosts,
gameDisplay, beQuiet, catchExceptions)
game.run() game.run()
if not beQuiet: games.append(game) if not beQuiet:
games.append(game)
if record: if record:
import time, pickle import time
fname = ('recorded-game-%d' % (i + 1)) + '-'.join([str(t) for t in time.localtime()[1:6]]) import pickle
f = open(fname, 'wb') fname = ('recorded-game-%d' % (i + 1)) + \
'-'.join([str(t) for t in time.localtime()[1:6]])
f = file(fname, 'w')
components = {'layout': layout, 'actions': game.moveHistory} components = {'layout': layout, 'actions': game.moveHistory}
pickle.dump(components, f) pickle.dump(components, f)
f.close() f.close()
@@ -657,14 +708,17 @@ def runGames( layout, pacman, ghosts, display, numGames, record, numTraining = 0
if (numGames-numTraining) > 0: if (numGames-numTraining) > 0:
scores = [game.state.getScore() for game in games] scores = [game.state.getScore() for game in games]
wins = [game.state.isWin() for game in games] wins = [game.state.isWin() for game in games]
winRate = wins.count(True)/ float(len(wins)) winRate = wins.count(True) / float(len(wins))
print('Average Score:', sum(scores) / float(len(scores))) print('Average Score:', sum(scores) / float(len(scores)))
print('Scores: ', ', '.join([str(score) for score in scores])) print('Scores: ', ', '.join([str(score) for score in scores]))
print('Win Rate: %d/%d (%.2f)' % (wins.count(True), len(wins), winRate)) print('Win Rate: %d/%d (%.2f)' %
print('Record: ', ', '.join([ ['Loss', 'Win'][int(w)] for w in wins])) (wins.count(True), len(wins), winRate))
print('Record: ', ', '.join(
[['Loss', 'Win'][int(w)] for w in wins]))
return games return games
if __name__ == '__main__': if __name__ == '__main__':
""" """
The main function called when pacman.py is run The main function called when pacman.py is run
@@ -676,8 +730,8 @@ if __name__ == '__main__':
> python pacman.py --help > python pacman.py --help
""" """
args = readCommand( sys.argv[1:] ) # Get game components based on input args = readCommand(sys.argv[1:]) # Get game components based on input
runGames( **args ) runGames(**args)
# import cProfile # import cProfile
# cProfile.run("runGames( **args )") # cProfile.run("runGames( **args )")
+19 -8
View File
@@ -18,20 +18,27 @@ import random
import game import game
import util import util
class LeftTurnAgent(game.Agent): class LeftTurnAgent(game.Agent):
"An agent that turns left at every opportunity" "An agent that turns left at every opportunity"
def getAction(self, state): def getAction(self, state):
legal = state.getLegalPacmanActions() legal = state.getLegalPacmanActions()
current = state.getPacmanState().configuration.direction current = state.getPacmanState().configuration.direction
if current == Directions.STOP: current = Directions.NORTH if current == Directions.STOP:
current = Directions.NORTH
left = Directions.LEFT[current] left = Directions.LEFT[current]
if left in legal: return left if left in legal:
if current in legal: return current return left
if Directions.RIGHT[current] in legal: return Directions.RIGHT[current] if current in legal:
if Directions.LEFT[left] in legal: return Directions.LEFT[left] return current
if Directions.RIGHT[current] in legal:
return Directions.RIGHT[current]
if Directions.LEFT[left] in legal:
return Directions.LEFT[left]
return Directions.STOP return Directions.STOP
class GreedyAgent(Agent): class GreedyAgent(Agent):
def __init__(self, evalFn="scoreEvaluation"): def __init__(self, evalFn="scoreEvaluation"):
self.evaluationFunction = util.lookup(evalFn, globals()) self.evaluationFunction = util.lookup(evalFn, globals())
@@ -40,13 +47,17 @@ class GreedyAgent(Agent):
def getAction(self, state): def getAction(self, state):
# Generate candidate actions # Generate candidate actions
legal = state.getLegalPacmanActions() legal = state.getLegalPacmanActions()
if Directions.STOP in legal: legal.remove(Directions.STOP) if Directions.STOP in legal:
legal.remove(Directions.STOP)
successors = [(state.generateSuccessor(0, action), action) for action in legal] successors = [(state.generateSuccessor(0, action), action)
scored = [(self.evaluationFunction(state), action) for state, action in successors] for action in legal]
scored = [(self.evaluationFunction(state), action)
for state, action in successors]
bestScore = max(scored)[0] bestScore = max(scored)[0]
bestActions = [pair[1] for pair in scored if pair[0] == bestScore] bestActions = [pair[1] for pair in scored if pair[0] == bestScore]
return random.choice(bestActions) return random.choice(bestActions)
def scoreEvaluation(state): def scoreEvaluation(state):
return state.getScore() return state.getScore()
+3 -3
View File
@@ -12,7 +12,7 @@
# Pieter Abbeel (pabbeel@cs.berkeley.edu). # Pieter Abbeel (pabbeel@cs.berkeley.edu).
STUDENT_CODE_DEFAULT = 'searchAgents.py,search.py' STUDENT_CODE_DEFAULT = 'multiAgents.py'
PROJECT_TEST_CLASSES = 'searchTestClasses.py' PROJECT_TEST_CLASSES = 'multiagentTestClasses.py'
PROJECT_NAME = 'Project 1: Search' PROJECT_NAME = 'Project 2: Multiagent search'
BONUS_PIC = False BONUS_PIC = False
-119
View File
@@ -1,119 +0,0 @@
# search.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).
"""
In search.py, you will implement generic search algorithms which are called by
Pacman agents (in searchAgents.py).
"""
import util
class SearchProblem:
"""
This class outlines the structure of a search problem, but doesn't implement
any of the methods (in object-oriented terminology: an abstract class).
You do not need to change anything in this class, ever.
"""
def getStartState(self):
"""
Returns the start state for the search problem.
"""
util.raiseNotDefined()
def isGoalState(self, state):
"""
state: Search state
Returns True if and only if the state is a valid goal state.
"""
util.raiseNotDefined()
def getSuccessors(self, state):
"""
state: Search state
For a given state, this should return a list of triples, (successor,
action, stepCost), where 'successor' is a successor to the current
state, 'action' is the action required to get there, and 'stepCost' is
the incremental cost of expanding to that successor.
"""
util.raiseNotDefined()
def getCostOfActions(self, actions):
"""
actions: A list of actions to take
This method returns the total cost of a particular sequence of actions.
The sequence must be composed of legal moves.
"""
util.raiseNotDefined()
def tinyMazeSearch(problem):
"""
Returns a sequence of moves that solves tinyMaze. For any other maze, the
sequence of moves will be incorrect, so only use this for tinyMaze.
"""
from game import Directions
s = Directions.SOUTH
w = Directions.WEST
return [s, s, w, s, w, w, s, w]
def depthFirstSearch(problem):
"""
Search the deepest nodes in the search tree first.
Your search algorithm needs to return a list of actions that reaches the
goal. Make sure to implement a graph search algorithm.
To get started, you might want to try some of these simple commands to
understand the search problem that is being passed in:
print("Start:", problem.getStartState())
print("Is the start a goal?", problem.isGoalState(problem.getStartState()))
print("Start's successors:", problem.getSuccessors(problem.getStartState()))
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def breadthFirstSearch(problem):
"""Search the shallowest nodes in the search tree first."""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def uniformCostSearch(problem):
"""Search the node of least total cost first."""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def nullHeuristic(state, problem=None):
"""
A heuristic function estimates the cost from the current state to the nearest
goal in the provided SearchProblem. This heuristic is trivial.
"""
return 0
def aStarSearch(problem, heuristic=nullHeuristic):
"""Search the node that has the lowest combined cost and heuristic first."""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
# Abbreviations
bfs = breadthFirstSearch
dfs = depthFirstSearch
astar = aStarSearch
ucs = uniformCostSearch
-542
View File
@@ -1,542 +0,0 @@
# searchAgents.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).
"""
This file contains all of the agents that can be selected to control Pacman. To
select an agent, use the '-p' option when running pacman.py. Arguments can be
passed to your agent using '-a'. For example, to load a SearchAgent that uses
depth first search (dfs), run the following command:
> python pacman.py -p SearchAgent -a fn=depthFirstSearch
Commands to invoke other search strategies can be found in the project
description.
Please only change the parts of the file you are asked to. Look for the lines
that say
"*** YOUR CODE HERE ***"
The parts you fill in start about 3/4 of the way down. Follow the project
description for details.
Good luck and happy searching!
"""
from game import Directions
from game import Agent
from game import Actions
import util
import time
import search
class GoWestAgent(Agent):
"An agent that goes West until it can't."
def getAction(self, state):
"The agent receives a GameState (defined in pacman.py)."
if Directions.WEST in state.getLegalPacmanActions():
return Directions.WEST
else:
return Directions.STOP
#######################################################
# This portion is written for you, but will only work #
# after you fill in parts of search.py #
#######################################################
class SearchAgent(Agent):
"""
This very general search agent finds a path using a supplied search
algorithm for a supplied search problem, then returns actions to follow that
path.
As a default, this agent runs DFS on a PositionSearchProblem to find
location (1,1)
Options for fn include:
depthFirstSearch or dfs
breadthFirstSearch or bfs
Note: You should NOT change any code in SearchAgent
"""
def __init__(self, fn='depthFirstSearch', prob='PositionSearchProblem', heuristic='nullHeuristic'):
# Warning: some advanced Python magic is employed below to find the right functions and problems
# Get the search function from the name and heuristic
if fn not in dir(search):
raise AttributeError(fn + ' is not a search function in search.py.')
func = getattr(search, fn)
if 'heuristic' not in func.__code__.co_varnames:
print('[SearchAgent] using function ' + fn)
self.searchFunction = func
else:
if heuristic in globals().keys():
heur = globals()[heuristic]
elif heuristic in dir(search):
heur = getattr(search, heuristic)
else:
raise AttributeError(heuristic + ' is not a function in searchAgents.py or search.py.')
print('[SearchAgent] using function %s and heuristic %s' % (fn, heuristic))
# Note: this bit of Python trickery combines the search algorithm and the heuristic
self.searchFunction = lambda x: func(x, heuristic=heur)
# Get the search problem type from the name
if prob not in globals().keys() or not prob.endswith('Problem'):
raise AttributeError(prob + ' is not a search problem type in SearchAgents.py.')
self.searchType = globals()[prob]
print('[SearchAgent] using problem type ' + prob)
def registerInitialState(self, state):
"""
This is the first time that the agent sees the layout of the game
board. Here, we choose a path to the goal. In this phase, the agent
should compute the path to the goal and store it in a local variable.
All of the work is done in this method!
state: a GameState object (pacman.py)
"""
if self.searchFunction == None: raise Exception("No search function provided for SearchAgent")
starttime = time.time()
problem = self.searchType(state) # Makes a new search problem
self.actions = self.searchFunction(problem) # Find a path
totalCost = problem.getCostOfActions(self.actions)
print('Path found with total cost of %d in %.1f seconds' % (totalCost, time.time() - starttime))
if '_expanded' in dir(problem): print('Search nodes expanded: %d' % problem._expanded)
def getAction(self, state):
"""
Returns the next action in the path chosen earlier (in
registerInitialState). Return Directions.STOP if there is no further
action to take.
state: a GameState object (pacman.py)
"""
if 'actionIndex' not in dir(self): self.actionIndex = 0
i = self.actionIndex
self.actionIndex += 1
if i < len(self.actions):
return self.actions[i]
else:
return Directions.STOP
class PositionSearchProblem(search.SearchProblem):
"""
A search problem defines the state space, start state, goal test, successor
function and cost function. This search problem can be used to find paths
to a particular point on the pacman board.
The state space consists of (x,y) positions in a pacman game.
Note: this search problem is fully specified; you should NOT change it.
"""
def __init__(self, gameState, costFn = lambda x: 1, goal=(1,1), start=None, warn=True, visualize=True):
"""
Stores the start and goal.
gameState: A GameState object (pacman.py)
costFn: A function from a search state (tuple) to a non-negative number
goal: A position in the gameState
"""
self.walls = gameState.getWalls()
self.startState = gameState.getPacmanPosition()
if start != None: self.startState = start
self.goal = goal
self.costFn = costFn
self.visualize = visualize
if warn and (gameState.getNumFood() != 1 or not gameState.hasFood(*goal)):
print('Warning: this does not look like a regular search maze')
# For display purposes
self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE
def getStartState(self):
return self.startState
def isGoalState(self, state):
isGoal = state == self.goal
# For display purposes only
if isGoal and self.visualize:
self._visitedlist.append(state)
import __main__
if '_display' in dir(__main__):
if 'drawExpandedCells' in dir(__main__._display): #@UndefinedVariable
__main__._display.drawExpandedCells(self._visitedlist) #@UndefinedVariable
return isGoal
def getSuccessors(self, state):
"""
Returns successor states, the actions they require, and a cost of 1.
As noted in search.py:
For a given state, this should return a list of triples,
(successor, action, stepCost), where 'successor' is a
successor to the current state, 'action' is the action
required to get there, and 'stepCost' is the incremental
cost of expanding to that successor
"""
successors = []
for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
x,y = state
dx, dy = Actions.directionToVector(action)
nextx, nexty = int(x + dx), int(y + dy)
if not self.walls[nextx][nexty]:
nextState = (nextx, nexty)
cost = self.costFn(nextState)
successors.append( ( nextState, action, cost) )
# Bookkeeping for display purposes
self._expanded += 1 # DO NOT CHANGE
if state not in self._visited:
self._visited[state] = True
self._visitedlist.append(state)
return successors
def getCostOfActions(self, actions):
"""
Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999.
"""
if actions == None: return 999999
x,y= self.getStartState()
cost = 0
for action in actions:
# Check figure out the next state and see whether its' legal
dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy)
if self.walls[x][y]: return 999999
cost += self.costFn((x,y))
return cost
class StayEastSearchAgent(SearchAgent):
"""
An agent for position search with a cost function that penalizes being in
positions on the West side of the board.
The cost function for stepping into a position (x,y) is 1/2^x.
"""
def __init__(self):
self.searchFunction = search.uniformCostSearch
costFn = lambda pos: .5 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(state, costFn, (1, 1), None, False)
class StayWestSearchAgent(SearchAgent):
"""
An agent for position search with a cost function that penalizes being in
positions on the East side of the board.
The cost function for stepping into a position (x,y) is 2^x.
"""
def __init__(self):
self.searchFunction = search.uniformCostSearch
costFn = lambda pos: 2 ** pos[0]
self.searchType = lambda state: PositionSearchProblem(state, costFn)
def manhattanHeuristic(position, problem, info={}):
"The Manhattan distance heuristic for a PositionSearchProblem"
xy1 = position
xy2 = problem.goal
return abs(xy1[0] - xy2[0]) + abs(xy1[1] - xy2[1])
def euclideanHeuristic(position, problem, info={}):
"The Euclidean distance heuristic for a PositionSearchProblem"
xy1 = position
xy2 = problem.goal
return ( (xy1[0] - xy2[0]) ** 2 + (xy1[1] - xy2[1]) ** 2 ) ** 0.5
#####################################################
# This portion is incomplete. Time to write code! #
#####################################################
class CornersProblem(search.SearchProblem):
"""
This search problem finds paths through all four corners of a layout.
You must select a suitable state space and successor function
"""
def __init__(self, startingGameState):
"""
Stores the walls, pacman's starting position and corners.
"""
self.walls = startingGameState.getWalls()
self.startingPosition = startingGameState.getPacmanPosition()
top, right = self.walls.height-2, self.walls.width-2
self.corners = ((1,1), (1,top), (right, 1), (right, top))
for corner in self.corners:
if not startingGameState.hasFood(*corner):
print('Warning: no food in corner ' + str(corner))
self._expanded = 0 # DO NOT CHANGE; Number of search nodes expanded
# Please add any code here which you would like to use
# in initializing the problem
"*** YOUR CODE HERE ***"
def getStartState(self):
"""
Returns the start state (in your state space, not the full Pacman state
space)
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def isGoalState(self, state):
"""
Returns whether this search state is a goal state of the problem.
"""
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def getSuccessors(self, state):
"""
Returns successor states, the actions they require, and a cost of 1.
As noted in search.py:
For a given state, this should return a list of triples, (successor,
action, stepCost), where 'successor' is a successor to the current
state, 'action' is the action required to get there, and 'stepCost'
is the incremental cost of expanding to that successor
"""
successors = []
for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
# Add a successor state to the successor list if the action is legal
# Here's a code snippet for figuring out whether a new position hits a wall:
# x,y = currentPosition
# dx, dy = Actions.directionToVector(action)
# nextx, nexty = int(x + dx), int(y + dy)
# hitsWall = self.walls[nextx][nexty]
"*** YOUR CODE HERE ***"
self._expanded += 1 # DO NOT CHANGE
return successors
def getCostOfActions(self, actions):
"""
Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999. This is implemented for you.
"""
if actions == None: return 999999
x,y= self.startingPosition
for action in actions:
dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy)
if self.walls[x][y]: return 999999
return len(actions)
def cornersHeuristic(state, problem):
"""
A heuristic for the CornersProblem that you defined.
state: The current search state
(a data structure you chose in your search problem)
problem: The CornersProblem instance for this layout.
This function should always return a number that is a lower bound on the
shortest path from the state to a goal of the problem; i.e. it should be
admissible (as well as consistent).
"""
corners = problem.corners # These are the corner coordinates
walls = problem.walls # These are the walls of the maze, as a Grid (game.py)
"*** YOUR CODE HERE ***"
return 0 # Default to trivial solution
class AStarCornersAgent(SearchAgent):
"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
def __init__(self):
self.searchFunction = lambda prob: search.aStarSearch(prob, cornersHeuristic)
self.searchType = CornersProblem
class FoodSearchProblem:
"""
A search problem associated with finding the a path that collects all of the
food (dots) in a Pacman game.
A search state in this problem is a tuple ( pacmanPosition, foodGrid ) where
pacmanPosition: a tuple (x,y) of integers specifying Pacman's position
foodGrid: a Grid (see game.py) of either True or False, specifying remaining food
"""
def __init__(self, startingGameState):
self.start = (startingGameState.getPacmanPosition(), startingGameState.getFood())
self.walls = startingGameState.getWalls()
self.startingGameState = startingGameState
self._expanded = 0 # DO NOT CHANGE
self.heuristicInfo = {} # A dictionary for the heuristic to store information
def getStartState(self):
return self.start
def isGoalState(self, state):
return state[1].count() == 0
def getSuccessors(self, state):
"Returns successor states, the actions they require, and a cost of 1."
successors = []
self._expanded += 1 # DO NOT CHANGE
for direction in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]:
x,y = state[0]
dx, dy = Actions.directionToVector(direction)
nextx, nexty = int(x + dx), int(y + dy)
if not self.walls[nextx][nexty]:
nextFood = state[1].copy()
nextFood[nextx][nexty] = False
successors.append( ( ((nextx, nexty), nextFood), direction, 1) )
return successors
def getCostOfActions(self, actions):
"""Returns the cost of a particular sequence of actions. If those actions
include an illegal move, return 999999"""
x,y= self.getStartState()[0]
cost = 0
for action in actions:
# figure out the next state and see whether it's legal
dx, dy = Actions.directionToVector(action)
x, y = int(x + dx), int(y + dy)
if self.walls[x][y]:
return 999999
cost += 1
return cost
class AStarFoodSearchAgent(SearchAgent):
"A SearchAgent for FoodSearchProblem using A* and your foodHeuristic"
def __init__(self):
self.searchFunction = lambda prob: search.aStarSearch(prob, foodHeuristic)
self.searchType = FoodSearchProblem
def foodHeuristic(state, problem):
"""
Your heuristic for the FoodSearchProblem goes here.
This heuristic must be consistent to ensure correctness. First, try to come
up with an admissible heuristic; almost all admissible heuristics will be
consistent as well.
If using A* ever finds a solution that is worse uniform cost search finds,
your heuristic is *not* consistent, and probably not admissible! On the
other hand, inadmissible or inconsistent heuristics may find optimal
solutions, so be careful.
The state is a tuple ( pacmanPosition, foodGrid ) where foodGrid is a Grid
(see game.py) of either True or False. You can call foodGrid.asList() to get
a list of food coordinates instead.
If you want access to info like walls, capsules, etc., you can query the
problem. For example, problem.walls gives you a Grid of where the walls
are.
If you want to *store* information to be reused in other calls to the
heuristic, there is a dictionary called problem.heuristicInfo that you can
use. For example, if you only want to count the walls once and store that
value, try: problem.heuristicInfo['wallCount'] = problem.walls.count()
Subsequent calls to this heuristic can access
problem.heuristicInfo['wallCount']
"""
position, foodGrid = state
"*** YOUR CODE HERE ***"
return 0
class ClosestDotSearchAgent(SearchAgent):
"Search for all food using a sequence of searches"
def registerInitialState(self, state):
self.actions = []
currentState = state
while(currentState.getFood().count() > 0):
nextPathSegment = self.findPathToClosestDot(currentState) # The missing piece
self.actions += nextPathSegment
for action in nextPathSegment:
legal = currentState.getLegalActions()
if action not in legal:
t = (str(action), str(currentState))
raise Exception('findPathToClosestDot returned an illegal move: %s!\n%s' % t)
currentState = currentState.generateSuccessor(0, action)
self.actionIndex = 0
print('Path found with cost %d.' % len(self.actions))
def findPathToClosestDot(self, gameState):
"""
Returns a path (a list of actions) to the closest dot, starting from
gameState.
"""
# Here are some useful elements of the startState
startPosition = gameState.getPacmanPosition()
food = gameState.getFood()
walls = gameState.getWalls()
problem = AnyFoodSearchProblem(gameState)
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
class AnyFoodSearchProblem(PositionSearchProblem):
"""
A search problem for finding a path to any food.
This search problem is just like the PositionSearchProblem, but has a
different goal test, which you need to fill in below. The state space and
successor function do not need to be changed.
The class definition above, AnyFoodSearchProblem(PositionSearchProblem),
inherits the methods of the PositionSearchProblem.
You can use this search problem to help you fill in the findPathToClosestDot
method.
"""
def __init__(self, gameState):
"Stores information from the gameState. You don't need to change this."
# Store the food for later reference
self.food = gameState.getFood()
# Store info for the PositionSearchProblem (no need to change this)
self.walls = gameState.getWalls()
self.startState = gameState.getPacmanPosition()
self.costFn = lambda x: 1
self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE
def isGoalState(self, state):
"""
The state is Pacman's position. Fill this in with a goal test that will
complete the problem definition.
"""
x,y = state
"*** YOUR CODE HERE ***"
util.raiseNotDefined()
def mazeDistance(point1, point2, gameState):
"""
Returns the maze distance between any two points, using the search functions
you have already built. The gameState can be any game state -- Pacman's
position in that state is ignored.
Example usage: mazeDistance( (2,4), (5,6), gameState)
This might be a useful helper function for your ApproximateSearchAgent.
"""
x1, y1 = point1
x2, y2 = point2
walls = gameState.getWalls()
assert not walls[x1][y1], 'point1 is a wall: ' + str(point1)
assert not walls[x2][y2], 'point2 is a wall: ' + str(point2)
prob = PositionSearchProblem(gameState, start=point1, goal=point2, warn=False, visualize=False)
return len(search.bfs(prob))
-823
View File
@@ -1,823 +0,0 @@
# searchTestClasses.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 sys
import re
import testClasses
import textwrap
# import project specific code
import layout
import pacman
from search import SearchProblem
# helper function for printing solutions in solution files
def wrap_solution(solution):
if type(solution) == type([]):
return '\n'.join(textwrap.wrap(' '.join(solution)))
else:
return str(solution)
def followAction(state, action, problem):
for successor1, action1, cost1 in problem.getSuccessors(state):
if action == action1: return successor1
return None
def followPath(path, problem):
state = problem.getStartState()
states = [state]
for action in path:
state = followAction(state, action, problem)
states.append(state)
return states
def checkSolution(problem, path):
state = problem.getStartState()
for action in path:
state = followAction(state, action, problem)
return problem.isGoalState(state)
# Search problem on a plain graph
class GraphSearch(SearchProblem):
# Read in the state graph; define start/end states, edges and costs
def __init__(self, graph_text):
self.expanded_states = []
lines = graph_text.split('\n')
r = re.match('start_state:(.*)', lines[0])
if r == None:
print("Broken graph:")
print('"""%s"""' % graph_text)
raise Exception("GraphSearch graph specification start_state not found or incorrect on line 0")
self.start_state = r.group(1).strip()
r = re.match('goal_states:(.*)', lines[1])
if r == None:
print("Broken graph:")
print('"""%s"""' % graph_text)
raise Exception("GraphSearch graph specification goal_states not found or incorrect on line 1")
goals = r.group(1).split()
self.goals = [str.strip(g) for g in goals]
self.successors = {}
all_states = set()
self.orderedSuccessorTuples = []
for l in lines[2:]:
if len(l.split()) == 3:
start, action, next_state = l.split()
cost = 1
elif len(l.split()) == 4:
start, action, next_state, cost = l.split()
else:
print("Broken graph:")
print('"""%s"""' % graph_text)
raise Exception("Invalid line in GraphSearch graph specification on line:" + l)
cost = float(cost)
self.orderedSuccessorTuples.append((start, action, next_state, cost))
all_states.add(start)
all_states.add(next_state)
if start not in self.successors:
self.successors[start] = []
self.successors[start].append((next_state, action, cost))
for s in all_states:
if s not in self.successors:
self.successors[s] = []
# Get start state
def getStartState(self):
return self.start_state
# Check if a state is a goal state
def isGoalState(self, state):
return state in self.goals
# Get all successors of a state
def getSuccessors(self, state):
self.expanded_states.append(state)
return list(self.successors[state])
# Calculate total cost of a sequence of actions
def getCostOfActions(self, actions):
total_cost = 0
state = self.start_state
for a in actions:
successors = self.successors[state]
match = False
for (next_state, action, cost) in successors:
if a == action:
state = next_state
total_cost += cost
match = True
if not match:
print('invalid action sequence')
sys.exit(1)
return total_cost
# Return a list of all states on which 'getSuccessors' was called
def getExpandedStates(self):
return self.expanded_states
def __str__(self):
print(self.successors)
edges = ["%s %s %s %s" % t for t in self.orderedSuccessorTuples]
return \
"""start_state: %s
goal_states: %s
%s""" % (self.start_state, " ".join(self.goals), "\n".join(edges))
def parseHeuristic(heuristicText):
heuristic = {}
for line in heuristicText.split('\n'):
tokens = line.split()
if len(tokens) != 2:
print("Broken heuristic:")
print('"""%s"""' % heuristicText)
raise Exception("GraphSearch heuristic specification broken at tokens:" + str(tokens))
state, h = tokens
heuristic[state] = float(h)
def graphHeuristic(state, problem=None):
if state in heuristic:
return heuristic[state]
else:
import pprint
pp = pprint.PrettyPrinter(indent=4)
print("Heuristic:")
pp.pprint(heuristic)
raise Exception("Graph heuristic called with invalid state: " + str(state))
return graphHeuristic
class GraphSearchTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(GraphSearchTest, self).__init__(question, testDict)
self.graph_text = testDict['graph']
self.alg = testDict['algorithm']
self.diagram = testDict['diagram']
self.exactExpansionOrder = testDict.get('exactExpansionOrder', 'True').lower() == "true"
if 'heuristic' in testDict:
self.heuristic = parseHeuristic(testDict['heuristic'])
else:
self.heuristic = None
# Note that the return type of this function is a tripple:
# (solution, expanded states, error message)
def getSolInfo(self, search):
alg = getattr(search, self.alg)
problem = GraphSearch(self.graph_text)
if self.heuristic != None:
solution = alg(problem, self.heuristic)
else:
solution = alg(problem)
if type(solution) != type([]):
return None, None, 'The result of %s must be a list. (Instead, it is %s)' % (self.alg, type(solution))
return solution, problem.getExpandedStates(), None
# Run student code. If an error message is returned, print error and return false.
# If a good solution is returned, printn the solution and return true; otherwise,
# print both the correct and student's solution and return false.
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
gold_solution = [str.split(solutionDict['solution']), str.split(solutionDict['rev_solution'])]
gold_expanded_states = [str.split(solutionDict['expanded_states']), str.split(solutionDict['rev_expanded_states'])]
solution, expanded_states, error = self.getSolInfo(search)
if error != None:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('\t%s' % error)
return False
if solution in gold_solution and (not self.exactExpansionOrder or expanded_states in gold_expanded_states):
grades.addMessage('PASS: %s' % self.path)
grades.addMessage('\tsolution:\t\t%s' % solution)
grades.addMessage('\texpanded_states:\t%s' % expanded_states)
return True
else:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('\tgraph:')
for line in self.diagram.split('\n'):
grades.addMessage('\t %s' % (line,))
grades.addMessage('\tstudent solution:\t\t%s' % solution)
grades.addMessage('\tstudent expanded_states:\t%s' % expanded_states)
grades.addMessage('')
grades.addMessage('\tcorrect solution:\t\t%s' % gold_solution[0])
grades.addMessage('\tcorrect expanded_states:\t%s' % gold_expanded_states[0])
grades.addMessage('\tcorrect rev_solution:\t\t%s' % gold_solution[1])
grades.addMessage('\tcorrect rev_expanded_states:\t%s' % gold_expanded_states[1])
return False
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# open file and write comments
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
handle.write('# This solution is designed to support both right-to-left\n')
handle.write('# and left-to-right implementations.\n')
# write forward solution
solution, expanded_states, error = self.getSolInfo(search)
if error != None: raise Exception("Error in solution code: %s" % error)
handle.write('solution: "%s"\n' % ' '.join(solution))
handle.write('expanded_states: "%s"\n' % ' '.join(expanded_states))
# reverse and write backwards solution
search.REVERSE_PUSH = not search.REVERSE_PUSH
solution, expanded_states, error = self.getSolInfo(search)
if error != None: raise Exception("Error in solution code: %s" % error)
handle.write('rev_solution: "%s"\n' % ' '.join(solution))
handle.write('rev_expanded_states: "%s"\n' % ' '.join(expanded_states))
# clean up
search.REVERSE_PUSH = not search.REVERSE_PUSH
handle.close()
return True
class PacmanSearchTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(PacmanSearchTest, self).__init__(question, testDict)
self.layout_text = testDict['layout']
self.alg = testDict['algorithm']
self.layoutName = testDict['layoutName']
# TODO: sensible to have defaults like this?
self.leewayFactor = float(testDict.get('leewayFactor', '1'))
self.costFn = eval(testDict.get('costFn', 'None'))
self.searchProblemClassName = testDict.get('searchProblemClass', 'PositionSearchProblem')
self.heuristicName = testDict.get('heuristic', None)
def getSolInfo(self, search, searchAgents):
alg = getattr(search, self.alg)
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
start_state = pacman.GameState()
start_state.initialize(lay, 0)
problemClass = getattr(searchAgents, self.searchProblemClassName)
problemOptions = {}
if self.costFn != None:
problemOptions['costFn'] = self.costFn
problem = problemClass(start_state, **problemOptions)
heuristic = getattr(searchAgents, self.heuristicName) if self.heuristicName != None else None
if heuristic != None:
solution = alg(problem, heuristic)
else:
solution = alg(problem)
if type(solution) != type([]):
return None, None, 'The result of %s must be a list. (Instead, it is %s)' % (self.alg, type(solution))
from game import Directions
dirs = Directions.LEFT.keys()
if [el in dirs for el in solution].count(False) != 0:
return None, None, 'Output of %s must be a list of actions from game.Directions' % self.alg
expanded = problem._expanded
return solution, expanded, None
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
gold_solution = [str.split(solutionDict['solution']), str.split(solutionDict['rev_solution'])]
gold_expanded = max(int(solutionDict['expanded_nodes']), int(solutionDict['rev_expanded_nodes']))
solution, expanded, error = self.getSolInfo(search, searchAgents)
if error != None:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('%s' % error)
return False
# FIXME: do we want to standardize test output format?
if solution not in gold_solution:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('Solution not correct.')
grades.addMessage('\tstudent solution length: %s' % len(solution))
grades.addMessage('\tstudent solution:\n%s' % wrap_solution(solution))
grades.addMessage('')
grades.addMessage('\tcorrect solution length: %s' % len(gold_solution[0]))
grades.addMessage('\tcorrect (reversed) solution length: %s' % len(gold_solution[1]))
grades.addMessage('\tcorrect solution:\n%s' % wrap_solution(gold_solution[0]))
grades.addMessage('\tcorrect (reversed) solution:\n%s' % wrap_solution(gold_solution[1]))
return False
if expanded > self.leewayFactor * gold_expanded and expanded > gold_expanded + 1:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('Too many node expanded; are you expanding nodes twice?')
grades.addMessage('\tstudent nodes expanded: %s' % expanded)
grades.addMessage('')
grades.addMessage('\tcorrect nodes expanded: %s (leewayFactor %s)' % (gold_expanded, self.leewayFactor))
return False
grades.addMessage('PASS: %s' % self.path)
grades.addMessage('\tpacman layout:\t\t%s' % self.layoutName)
grades.addMessage('\tsolution length: %s' % len(solution))
grades.addMessage('\tnodes expanded:\t\t%s' % expanded)
return True
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# open file and write comments
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
handle.write('# This solution is designed to support both right-to-left\n')
handle.write('# and left-to-right implementations.\n')
handle.write('# Number of nodes expanded must be with a factor of %s of the numbers below.\n' % self.leewayFactor)
# write forward solution
solution, expanded, error = self.getSolInfo(search, searchAgents)
if error != None: raise Exception("Error in solution code: %s" % error)
handle.write('solution: """\n%s\n"""\n' % wrap_solution(solution))
handle.write('expanded_nodes: "%s"\n' % expanded)
# write backward solution
search.REVERSE_PUSH = not search.REVERSE_PUSH
solution, expanded, error = self.getSolInfo(search, searchAgents)
if error != None: raise Exception("Error in solution code: %s" % error)
handle.write('rev_solution: """\n%s\n"""\n' % wrap_solution(solution))
handle.write('rev_expanded_nodes: "%s"\n' % expanded)
# clean up
search.REVERSE_PUSH = not search.REVERSE_PUSH
handle.close()
return True
from game import Actions
def getStatesFromPath(start, path):
"Returns the list of states visited along the path"
vis = [start]
curr = start
for a in path:
x,y = curr
dx, dy = Actions.directionToVector(a)
curr = (int(x + dx), int(y + dy))
vis.append(curr)
return vis
class CornerProblemTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(CornerProblemTest, self).__init__(question, testDict)
self.layoutText = testDict['layout']
self.layoutName = testDict['layoutName']
def solution(self, search, searchAgents):
lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')])
gameState = pacman.GameState()
gameState.initialize(lay, 0)
problem = searchAgents.CornersProblem(gameState)
path = search.bfs(problem)
gameState = pacman.GameState()
gameState.initialize(lay, 0)
visited = getStatesFromPath(gameState.getPacmanPosition(), path)
top, right = gameState.getWalls().height-2, gameState.getWalls().width-2
missedCorners = [p for p in ((1,1), (1,top), (right, 1), (right, top)) if p not in visited]
return path, missedCorners
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
gold_length = int(solutionDict['solution_length'])
solution, missedCorners = self.solution(search, searchAgents)
if type(solution) != type([]):
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('The result must be a list. (Instead, it is %s)' % type(solution))
return False
if len(missedCorners) != 0:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('Corners missed: %s' % missedCorners)
return False
if len(solution) != gold_length:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('Optimal solution not found.')
grades.addMessage('\tstudent solution length:\n%s' % len(solution))
grades.addMessage('')
grades.addMessage('\tcorrect solution length:\n%s' % gold_length)
return False
grades.addMessage('PASS: %s' % self.path)
grades.addMessage('\tpacman layout:\t\t%s' % self.layoutName)
grades.addMessage('\tsolution length:\t\t%s' % len(solution))
return True
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# open file and write comments
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
print("Solving problem", self.layoutName)
print(self.layoutText)
path, _ = self.solution(search, searchAgents)
length = len(path)
print("Problem solved")
handle.write('solution_length: "%s"\n' % length)
handle.close()
# template = """class: "HeuristicTest"
#
# heuristic: "foodHeuristic"
# searchProblemClass: "FoodSearchProblem"
# layoutName: "Test %s"
# layout: \"\"\"
# %s
# \"\"\"
# """
#
# for i, (_, _, l) in enumerate(doneTests + foodTests):
# f = open("food_heuristic_%s.test" % (i+1), "w")
# f.write(template % (i+1, "\n".join(l)))
# f.close()
class HeuristicTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(HeuristicTest, self).__init__(question, testDict)
self.layoutText = testDict['layout']
self.layoutName = testDict['layoutName']
self.searchProblemClassName = testDict['searchProblemClass']
self.heuristicName = testDict['heuristic']
def setupProblem(self, searchAgents):
lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')])
gameState = pacman.GameState()
gameState.initialize(lay, 0)
problemClass = getattr(searchAgents, self.searchProblemClassName)
problem = problemClass(gameState)
state = problem.getStartState()
heuristic = getattr(searchAgents, self.heuristicName)
return problem, state, heuristic
def checkHeuristic(self, heuristic, problem, state, solutionCost):
h0 = heuristic(state, problem)
if solutionCost == 0:
if h0 == 0:
return True, ''
else:
return False, 'Heuristic failed H(goal) == 0 test'
if h0 < 0:
return False, 'Heuristic failed H >= 0 test'
if not h0 > 0:
return False, 'Heuristic failed non-triviality test'
if not h0 <= solutionCost:
return False, 'Heuristic failed admissibility test'
for succ, action, stepCost in problem.getSuccessors(state):
h1 = heuristic(succ, problem)
if h1 < 0: return False, 'Heuristic failed H >= 0 test'
if h0 - h1 > stepCost: return False, 'Heuristic failed consistency test'
return True, ''
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
solutionCost = int(solutionDict['solution_cost'])
problem, state, heuristic = self.setupProblem(searchAgents)
passed, message = self.checkHeuristic(heuristic, problem, state, solutionCost)
if not passed:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('%s' % message)
return False
else:
grades.addMessage('PASS: %s' % self.path)
return True
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# open file and write comments
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
print("Solving problem", self.layoutName, self.heuristicName)
print(self.layoutText)
problem, _, heuristic = self.setupProblem(searchAgents)
path = search.astar(problem, heuristic)
cost = problem.getCostOfActions(path)
print("Problem solved")
handle.write('solution_cost: "%s"\n' % cost)
handle.close()
return True
class HeuristicGrade(testClasses.TestCase):
def __init__(self, question, testDict):
super(HeuristicGrade, self).__init__(question, testDict)
self.layoutText = testDict['layout']
self.layoutName = testDict['layoutName']
self.searchProblemClassName = testDict['searchProblemClass']
self.heuristicName = testDict['heuristic']
self.basePoints = int(testDict['basePoints'])
self.thresholds = [int(t) for t in testDict['gradingThresholds'].split()]
def setupProblem(self, searchAgents):
lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')])
gameState = pacman.GameState()
gameState.initialize(lay, 0)
problemClass = getattr(searchAgents, self.searchProblemClassName)
problem = problemClass(gameState)
state = problem.getStartState()
heuristic = getattr(searchAgents, self.heuristicName)
return problem, state, heuristic
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
problem, _, heuristic = self.setupProblem(searchAgents)
path = search.astar(problem, heuristic)
expanded = problem._expanded
if not checkSolution(problem, path):
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('\tReturned path is not a solution.')
grades.addMessage('\tpath returned by astar: %s' % expanded)
return False
grades.addPoints(self.basePoints)
points = 0
for threshold in self.thresholds:
if expanded <= threshold:
points += 1
grades.addPoints(points)
if points >= len(self.thresholds):
grades.addMessage('PASS: %s' % self.path)
else:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('\texpanded nodes: %s' % expanded)
grades.addMessage('\tthresholds: %s' % self.thresholds)
return True
def writeSolution(self, moduleDict, filePath):
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
handle.write('# File intentionally blank.\n')
handle.close()
return True
# template = """class: "ClosestDotTest"
#
# layoutName: "Test %s"
# layout: \"\"\"
# %s
# \"\"\"
# """
#
# for i, (_, _, l) in enumerate(foodTests):
# f = open("closest_dot_%s.test" % (i+1), "w")
# f.write(template % (i+1, "\n".join(l)))
# f.close()
class ClosestDotTest(testClasses.TestCase):
def __init__(self, question, testDict):
super(ClosestDotTest, self).__init__(question, testDict)
self.layoutText = testDict['layout']
self.layoutName = testDict['layoutName']
def solution(self, searchAgents):
lay = layout.Layout([l.strip() for l in self.layoutText.split('\n')])
gameState = pacman.GameState()
gameState.initialize(lay, 0)
path = searchAgents.ClosestDotSearchAgent().findPathToClosestDot(gameState)
return path
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
gold_length = int(solutionDict['solution_length'])
solution = self.solution(searchAgents)
if type(solution) != type([]):
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('\tThe result must be a list. (Instead, it is %s)' % type(solution))
return False
if len(solution) != gold_length:
grades.addMessage('FAIL: %s' % self.path)
grades.addMessage('Closest dot not found.')
grades.addMessage('\tstudent solution length:\n%s' % len(solution))
grades.addMessage('')
grades.addMessage('\tcorrect solution length:\n%s' % gold_length)
return False
grades.addMessage('PASS: %s' % self.path)
grades.addMessage('\tpacman layout:\t\t%s' % self.layoutName)
grades.addMessage('\tsolution length:\t\t%s' % len(solution))
return True
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# open file and write comments
handle = open(filePath, 'w')
handle.write('# This is the solution file for %s.\n' % self.path)
print("Solving problem", self.layoutName)
print(self.layoutText)
length = len(self.solution(searchAgents))
print("Problem solved")
handle.write('solution_length: "%s"\n' % length)
handle.close()
return True
class CornerHeuristicSanity(testClasses.TestCase):
def __init__(self, question, testDict):
super(CornerHeuristicSanity, self).__init__(question, testDict)
self.layout_text = testDict['layout']
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
game_state = pacman.GameState()
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
game_state.initialize(lay, 0)
problem = searchAgents.CornersProblem(game_state)
start_state = problem.getStartState()
h0 = searchAgents.cornersHeuristic(start_state, problem)
succs = problem.getSuccessors(start_state)
# cornerConsistencyA
for succ in succs:
h1 = searchAgents.cornersHeuristic(succ[0], problem)
if h0 - h1 > 1:
grades.addMessage('FAIL: inconsistent heuristic')
return False
heuristic_cost = searchAgents.cornersHeuristic(start_state, problem)
true_cost = float(solutionDict['cost'])
# cornerNontrivial
if heuristic_cost == 0:
grades.addMessage('FAIL: must use non-trivial heuristic')
return False
# cornerAdmissible
if heuristic_cost > true_cost:
grades.addMessage('FAIL: Inadmissible heuristic')
return False
path = solutionDict['path'].split()
states = followPath(path, problem)
heuristics = []
for state in states:
heuristics.append(searchAgents.cornersHeuristic(state, problem))
for i in range(0, len(heuristics) - 1):
h0 = heuristics[i]
h1 = heuristics[i+1]
# cornerConsistencyB
if h0 - h1 > 1:
grades.addMessage('FAIL: inconsistent heuristic')
return False
# cornerPosH
if h0 < 0 or h1 <0:
grades.addMessage('FAIL: non-positive heuristic')
return False
# cornerGoalH
if heuristics[len(heuristics) - 1] != 0:
grades.addMessage('FAIL: heuristic non-zero at goal')
return False
grades.addMessage('PASS: heuristic value less than true cost at start state')
return True
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# write comment
handle = open(filePath, 'w')
handle.write('# In order for a heuristic to be admissible, the value\n')
handle.write('# of the heuristic must be less at each state than the\n')
handle.write('# true cost of the optimal path from that state to a goal.\n')
# solve problem and write solution
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
start_state = pacman.GameState()
start_state.initialize(lay, 0)
problem = searchAgents.CornersProblem(start_state)
solution = search.astar(problem, searchAgents.cornersHeuristic)
handle.write('cost: "%d"\n' % len(solution))
handle.write('path: """\n%s\n"""\n' % wrap_solution(solution))
handle.close()
return True
class CornerHeuristicPacman(testClasses.TestCase):
def __init__(self, question, testDict):
super(CornerHeuristicPacman, self).__init__(question, testDict)
self.layout_text = testDict['layout']
def execute(self, grades, moduleDict, solutionDict):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
total = 0
true_cost = float(solutionDict['cost'])
thresholds = [int(x) for x in solutionDict['thresholds'].split()]
game_state = pacman.GameState()
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
game_state.initialize(lay, 0)
problem = searchAgents.CornersProblem(game_state)
start_state = problem.getStartState()
if searchAgents.cornersHeuristic(start_state, problem) > true_cost:
grades.addMessage('FAIL: Inadmissible heuristic')
return False
path = search.astar(problem, searchAgents.cornersHeuristic)
print("path:", path)
print("path length:", len(path))
cost = problem.getCostOfActions(path)
if cost > true_cost:
grades.addMessage('FAIL: Inconsistent heuristic')
return False
expanded = problem._expanded
points = 0
for threshold in thresholds:
if expanded <= threshold:
points += 1
grades.addPoints(points)
if points >= len(thresholds):
grades.addMessage('PASS: Heuristic resulted in expansion of %d nodes' % expanded)
else:
grades.addMessage('FAIL: Heuristic resulted in expansion of %d nodes' % expanded)
return True
def writeSolution(self, moduleDict, filePath):
search = moduleDict['search']
searchAgents = moduleDict['searchAgents']
# write comment
handle = open(filePath, 'w')
handle.write('# This solution file specifies the length of the optimal path\n')
handle.write('# as well as the thresholds on number of nodes expanded to be\n')
handle.write('# used in scoring.\n')
# solve problem and write solution
lay = layout.Layout([l.strip() for l in self.layout_text.split('\n')])
start_state = pacman.GameState()
start_state.initialize(lay, 0)
problem = searchAgents.CornersProblem(start_state)
solution = search.astar(problem, searchAgents.cornersHeuristic)
handle.write('cost: "%d"\n' % len(solution))
handle.write('path: """\n%s\n"""\n' % wrap_solution(solution))
handle.write('thresholds: "2000 1600 1200"\n')
handle.close()
return True
File diff suppressed because one or more lines are too long
+7 -9
View File
@@ -61,6 +61,7 @@ class PassAllTestsQuestion(Question):
else: else:
grades.assignFullCredit() grades.assignFullCredit()
class ExtraCreditPassAllTestsQuestion(Question): class ExtraCreditPassAllTestsQuestion(Question):
def __init__(self, questionDict, display): def __init__(self, questionDict, display):
Question.__init__(self, questionDict, display) Question.__init__(self, questionDict, display)
@@ -92,11 +93,12 @@ class HackedPartialCreditQuestion(Question):
for testCase, f in self.testCases: for testCase, f in self.testCases:
testResult = f(grades) testResult = f(grades)
if "points" in testCase.testDict: if "points" in testCase.testDict:
if testResult: points += float(testCase.testDict["points"]) if testResult:
points += float(testCase.testDict["points"])
else: else:
passed = passed and testResult passed = passed and testResult
## FIXME: Below terrible hack to match q3's logic # FIXME: Below terrible hack to match q3's logic
if int(points) == self.maxPoints and not passed: if int(points) == self.maxPoints and not passed:
grades.assignZeroCredit() grades.assignZeroCredit()
else: else:
@@ -116,6 +118,7 @@ class Q6PartialCreditQuestion(Question):
if False in results: if False in results:
grades.assignZeroCredit() grades.assignZeroCredit()
class PartialCreditQuestion(Question): class PartialCreditQuestion(Question):
"""Fails any test which returns False, otherwise doesn't effect the grades object. """Fails any test which returns False, otherwise doesn't effect the grades object.
Partial credit tests will add the required points.""" Partial credit tests will add the required points."""
@@ -130,7 +133,6 @@ class PartialCreditQuestion(Question):
return False return False
class NumberPassedQuestion(Question): class NumberPassedQuestion(Question):
"""Grade is the number of test cases passed.""" """Grade is the number of test cases passed."""
@@ -138,9 +140,6 @@ class NumberPassedQuestion(Question):
grades.addPoints([f(grades) for _, f in self.testCases].count(True)) grades.addPoints([f(grades) for _, f in self.testCases].count(True))
# Template modeling a generic test case # Template modeling a generic test case
class TestCase(object): class TestCase(object):
@@ -186,13 +185,13 @@ class TestCase(object):
return False return False
# This should really be question level? # This should really be question level?
#
def testPartial(self, grades, points, maxPoints): def testPartial(self, grades, points, maxPoints):
grades.addPoints(points) grades.addPoints(points)
extraCredit = max(0, points - maxPoints) extraCredit = max(0, points - maxPoints)
regularCredit = points - extraCredit regularCredit = points - extraCredit
grades.addMessage('%s: %s (%s of %s points)' % ("PASS" if points >= maxPoints else "FAIL", self.path, regularCredit, maxPoints)) grades.addMessage('%s: %s (%s of %s points)' % (
"PASS" if points >= maxPoints else "FAIL", self.path, regularCredit, maxPoints))
if extraCredit > 0: if extraCredit > 0:
grades.addMessage('EXTRA CREDIT: %s points' % (extraCredit,)) grades.addMessage('EXTRA CREDIT: %s points' % (extraCredit,))
@@ -203,4 +202,3 @@ class TestCase(object):
def addMessage(self, message): def addMessage(self, message):
self.messages.extend(message.split('\n')) self.messages.extend(message.split('\n'))
+1
View File
@@ -15,6 +15,7 @@
import re import re
import sys import sys
class TestParser(object): class TestParser(object):
def __init__(self, path): def __init__(self, path):
+1 -1
View File
@@ -1 +1 @@
order: "q1 q2 q3 q4 q5 q6 q7 q8" order: "q1 q2 q3 q4 q5"
@@ -1,3 +1,2 @@
max_points: "0"
class: "PartialCreditQuestion" class: "PartialCreditQuestion"
max_points: "4"
depends: "q4"
+11
View File
@@ -0,0 +1,11 @@
class: "EvalAgentTest"
agentName: "ContestAgent"
layoutName: "contestClassic"
maxTime: "180"
numGames: "5"
scoreThresholds: "2500 2900"
randomSeed: "0"
ghosts: "[DirectionalGhost(1), DirectionalGhost(2), DirectionalGhost(3)]"
+2 -2
View File
@@ -1,2 +1,2 @@
max_points: "3" max_points: "4"
class: "PassAllTestsQuestion" class: "PartialCreditQuestion"
+2
View File
@@ -0,0 +1,2 @@
# This is the solution file for test_cases/q1/grade-agent.test.
# File intentionally blank.
+18
View File
@@ -0,0 +1,18 @@
class: "EvalAgentTest"
agentName: "ReflexAgent"
layoutName: "openClassic"
maxTime: "120"
numGames: "10"
nonTimeoutMinimum: "10"
scoreThresholds: "500 1000"
winsMinimum: "1"
winsThresholds: "5 10"
randomSeed: "0"
ghosts: "[RandomGhost(1)]"
-7
View File
@@ -1,7 +0,0 @@
# This is the solution file for test_cases/q1/graph_backtrack.test.
# This solution is designed to support both right-to-left
# and left-to-right implementations.
solution: "1:A->C 0:C->G"
expanded_states: "A D C"
rev_solution: "1:A->C 0:C->G"
rev_expanded_states: "A B C"
-32
View File
@@ -1,32 +0,0 @@
class: "GraphSearchTest"
algorithm: "depthFirstSearch"
diagram: """
B
^
|
*A --> C --> G
|
V
D
A is the start state, G is the goal. Arrows mark
possible state transitions. This tests whether
you extract the sequence of actions correctly even
if your search backtracks. If you fail this, your
nodes are not correctly tracking the sequences of
actions required to reach them.
"""
# The following section specifies the search problem and the solution.
# The graph is specified by first the set of start states, followed by
# the set of goal states, and lastly by the state transitions which are
# of the form:
# <start state> <actions> <end state> <cost>
graph: """
start_state: A
goal_states: G
A 0:A->B B 1.0
A 1:A->C C 2.0
A 2:A->D D 4.0
C 0:C->G G 8.0
"""
-7
View File
@@ -1,7 +0,0 @@
# This is the solution file for test_cases/q1/graph_bfs_vs_dfs.test.
# This solution is designed to support both right-to-left
# and left-to-right implementations.
solution: "2:A->D 0:D->G"
expanded_states: "A D"
rev_solution: "0:A->B 0:B->D 0:D->G"
rev_expanded_states: "A B D"
-30
View File
@@ -1,30 +0,0 @@
# Graph where BFS finds the optimal solution but DFS does not
class: "GraphSearchTest"
algorithm: "depthFirstSearch"
diagram: """
/-- B
| ^
| |
| *A -->[G]
| | ^
| V |
\-->D ----/
A is the start state, G is the goal. Arrows
mark possible transitions
"""
# The following section specifies the search problem and the solution.
# The graph is specified by first the set of start states, followed by
# the set of goal states, and lastly by the state transitions which are
# of the form:
# <start state> <actions> <end state> <cost>
graph: """
start_state: A
goal_states: G
A 0:A->B B 1.0
A 1:A->G G 2.0
A 2:A->D D 4.0
B 0:B->D D 8.0
D 0:D->G G 16.0
"""
-7
View File
@@ -1,7 +0,0 @@
# This is the solution file for test_cases/q1/graph_infinite.test.
# This solution is designed to support both right-to-left
# and left-to-right implementations.
solution: "0:A->B 1:B->C 1:C->G"
expanded_states: "A B C"
rev_solution: "0:A->B 1:B->C 1:C->G"
rev_expanded_states: "A B C"
-30
View File
@@ -1,30 +0,0 @@
# Graph where natural action choice leads to an infinite loop
class: "GraphSearchTest"
algorithm: "depthFirstSearch"
diagram: """
B <--> C
^ /|
| / |
V / V
*A<-/ [G]
A is the start state, G is the goal. Arrows mark
possible state transitions.
"""
# The following section specifies the search problem and the solution.
# The graph is specified by first the set of start states, followed by
# the set of goal states, and lastly by the state transitions which are
# of the form:
# <start state> <actions> <end state> <cost>
graph: """
start_state: A
goal_states: G
A 0:A->B B 1.0
B 0:B->A A 2.0
B 1:B->C C 4.0
C 0:C->A A 8.0
C 1:C->G G 16.0
C 2:C->B B 32.0
"""
-7
View File
@@ -1,7 +0,0 @@
# This is the solution file for test_cases/q1/graph_manypaths.test.
# This solution is designed to support both right-to-left
# and left-to-right implementations.
solution: "2:A->B2 0:B2->C 0:C->D 2:D->E2 0:E2->F 0:F->G"
expanded_states: "A B2 C D E2 F"
rev_solution: "0:A->B1 0:B1->C 0:C->D 0:D->E1 0:E1->F 0:F->G"
rev_expanded_states: "A B1 C D E1 F"
-39
View File
@@ -1,39 +0,0 @@
class: "GraphSearchTest"
algorithm: "depthFirstSearch"
diagram: """
B1 E1
^ \ ^ \
/ V / V
*A --> C --> D --> F --> [G]
\ ^ \ ^
V / V /
B2 E2
A is the start state, G is the goal. Arrows mark
possible state transitions. This graph has multiple
paths to the goal, where nodes with the same state
are added to the fringe multiple times before they
are expanded.
"""
# The following section specifies the search problem and the solution.
# The graph is specified by first the set of start states, followed by
# the set of goal states, and lastly by the state transitions which are
# of the form:
# <start state> <actions> <end state> <cost>
graph: """
start_state: A
goal_states: G
A 0:A->B1 B1 1.0
A 1:A->C C 2.0
A 2:A->B2 B2 4.0
B1 0:B1->C C 8.0
B2 0:B2->C C 16.0
C 0:C->D D 32.0
D 0:D->E1 E1 64.0
D 1:D->F F 128.0
D 2:D->E2 E2 256.0
E1 0:E1->F F 512.0
E2 0:E2->F F 1024.0
F 0:F->G G 2048.0
"""
-40
View File
@@ -1,40 +0,0 @@
# This is the solution file for test_cases/q1/pacman_1.test.
# This solution is designed to support both right-to-left
# and left-to-right implementations.
# Number of nodes expanded must be with a factor of 1.0 of the numbers below.
solution: """
West West West West West West West West West West West West West West
West West West West West West West West West West West West West West
West West West West West South South South South South South South
South South East East East North North North North North North North
East East South South South South South South East East North North
North North North North East East South South South South East East
North North East East East East East East East East South South South
East East East East East East East South South South South South South
South West West West West West West West West West West West West West
West West West West South West West West West West West West West West
"""
expanded_nodes: "146"
rev_solution: """
South South West West West West South South East East East East South
South West West West West South South East East East East South South
West West West West South South South East North East East East South
South South West West West West West West West North North North North
North North North North West West West West West West West North North
North East East East East South East East East North North North West
West North North West West West West West West West West West West
West West West West West West West West West West West West West West
South South South South South South South South South East East East
North North North North North North North East East South South South
South South South East East North North North North North North East
East South South South South East East North North North North East
East East East East South South West West West South South East East
East South South West West West West West West South South West West
West West West South West West West West West South South East East
East East East East East North East East East East East North North
East East East East East East North East East East East East South
South West West West South West West West West West West South South
West West West West West South West West West West West West West West
West
"""
rev_expanded_nodes: "269"
-27
View File
@@ -1,27 +0,0 @@
# This is a basic depth first search test
class: "PacmanSearchTest"
algorithm: "depthFirstSearch"
# The following specifies the layout to be used
layoutName: "mediumMaze"
layout: """
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% P%
% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% %
% %% % % %%%%%%% %% %
% %% % % % % %%%% %%%%%%%%% %% %%%%%
% %% % % % % %% %% %
% %% % % % % % %%%% %%% %%%%%% %
% % % % % % %% %%%%%%%% %
% %% % % %%%%%%%% %% %% %%%%%
% %% % %% %%%%%%%%% %% %
% %%%%%% %%%%%%% %% %%%%%% %
%%%%%% % %%%% %% % %
% %%%%%% %%%%% % %% %% %%%%%
% %%%%%% % %%%%% %% %
% %%%%%% %%%%%%%%%%% %% %% %
%%%%%%%%%% %%%%%% %
%. %%%%%%%%%%%%%%%% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
"""
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/0-eval-function-lose-states-1.test.
action: "Left"
generated: "lose1 lose2 root"
@@ -0,0 +1,30 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
diagram: """
root
/ \
lose1 lose2
1 0
If your algorithm is returning a different
action, make sure you are calling the
evaluation function on losing states.
"""
num_agents: "2"
start_state: "root"
win_states: ""
lose_states: "lose1 lose2"
successors: """
root Left lose1
root Right lose2
"""
evaluation: """
lose1 1.0
lose2 0.0
"""
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/0-eval-function-lose-states-2.test.
action: "Right"
generated: "lose1 lose2 root"
@@ -0,0 +1,30 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
diagram: """
root
/ \
lose1 lose2
0 1
If your algorithm is returning a different
action, make sure you are calling the
evaluation function on losing states.
"""
num_agents: "2"
start_state: "root"
win_states: ""
lose_states: "lose1 lose2"
successors: """
root Left lose1
root Right lose2
"""
evaluation: """
lose1 0.0
lose2 1.0
"""
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/0-eval-function-win-states-1.test.
action: "Left"
generated: "root win1 win2"
@@ -0,0 +1,30 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
diagram: """
root
/ \
win1 win2
1 0
If your algorithm is returning a different
action, make sure you are calling the
evaluation function on winning states.
"""
num_agents: "2"
start_state: "root"
win_states: "win1 win2"
lose_states: ""
successors: """
root Left win1
root Right win2
"""
evaluation: """
win1 1.0
win2 0.0
"""
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/0-eval-function-win-states-2.test.
action: "Right"
generated: "root win1 win2"
@@ -0,0 +1,30 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
diagram: """
root
/ \
win1 win2
0 1
If your algorithm is returning a different
action, make sure you are calling the
evaluation function on winning states.
"""
num_agents: "2"
start_state: "root"
win_states: "win1 win2"
lose_states: ""
successors: """
root Left win1
root Right win2
"""
evaluation: """
win1 0.0
win2 1.0
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/0-lecture-6-tree.test.
action: "Center"
generated: "A B C D E F G H I max min1 min2 min3"
+50
View File
@@ -0,0 +1,50 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
# Tree from lecture 6 slides
diagram: """
max
/-/ | \--\
/ | \
/ | \
min1 min2 min3
/|\ /|\ /|\
/ | \ / | \ / | \
A B C D E F G H I
3 12 8 5 4 6 14 1 11
"""
num_agents: "2"
start_state: "max"
win_states: "A B C D E F G H I"
lose_states: ""
successors: """
max Left min1
max Center min2
max Right min3
min1 Left A
min1 Center B
min1 Right C
min2 Left D
min2 Center E
min2 Right F
min3 Left G
min3 Center H
min3 Right I
"""
evaluation: """
A 3.0
B 12.0
C 8.0
D 5.0
E 4.0
F 6.0
G 14.0
H 1.0
I 11.0
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/0-small-tree.test.
action: "pacLeft"
generated: "A B C D deeper minLeft minRight root"
+36
View File
@@ -0,0 +1,36 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "3"
diagram: """
root
/ \
minLeft minRight
/ \ / \
A B C deeper
4 3 2 |
D
1000
"""
num_agents: "2"
start_state: "root"
win_states: "A C"
lose_states: "B D"
successors: """
root pacLeft minLeft
root pacRight minRight
minLeft gLeft A
minLeft gRight B
minRight gLeft C
minRight gRight deeper
deeper pacLeft D
"""
evaluation: """
A 4.0
B 3.0
C 2.0
D 1000.0
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-1-minmax.test.
action: "Left"
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
+47
View File
@@ -0,0 +1,47 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "3"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
c1 c2 cx
/ \ / \ |
d1 d2 d3 d4 dx
-3 -9 10 6 -3.01
a - max
b - min
c - max
Note that the minimax value of b1 is -3.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
cx Down dx
"""
evaluation: """
d1 -3.0
d2 -9.0
d3 10.0
d4 6.0
dx -3.01
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-2-minmax.test.
action: "Right"
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
+47
View File
@@ -0,0 +1,47 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "3"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
c1 c2 cx
/ \ / \ |
d1 d2 d3 d4 dx
-3 -9 10 6 -2.99
a - max
b - min
c - max
Note that the minimax value of b1 is -3.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
cx Down dx
"""
evaluation: """
d1 -3.0
d2 -9.0
d3 10.0
d4 6.0
dx -2.99
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-3-minmax.test.
action: "Left"
generated: "a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
+47
View File
@@ -0,0 +1,47 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "3"
diagram: """
/-----a------\
/ \
/ \
b1 b2
| / \
cx c3 c4
| / \ / \
dx d5 d6 d7 d8
4.01 4 -7 0 5
a - max
b - min
c - max
Note that the minimax value of b2 is 4.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 d5 d6 d7 d8 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Down cx
b2 Left c3
b2 Right c4
c3 Left d5
c3 Right d6
c4 Left d7
c4 Right d8
cx Down dx
"""
evaluation: """
d5 4.0
d6 -7.0
d7 0.0
d8 5.0
dx 4.01
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-4-minmax.test.
action: "Right"
generated: "a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
+47
View File
@@ -0,0 +1,47 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "3"
diagram: """
/-----a------\
/ \
/ \
b1 b2
| / \
cx c3 c4
| / \ / \
dx d5 d6 d7 d8
3.99 4 -7 0 5
a - max
b - min
c - max
Note that the minimax value of b2 is 4.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 d5 d6 d7 d8 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Down cx
b2 Left c3
b2 Right c4
c3 Left d5
c3 Right d6
c4 Left d7
c4 Right d8
cx Down dx
"""
evaluation: """
d5 4.0
d6 -7.0
d7 0.0
d8 5.0
dx 3.99
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-5-minmax.test.
action: "Right"
generated: "A B C D E F G H Z a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
+75
View File
@@ -0,0 +1,75 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "4"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
c1 c2 cx
/ \ / \ |
d1 d2 d3 d4 dx
/ \ / \ / \ / \ |
A B C D E F G H Z
-3 13 5 9 10 3 -6 8 3.01
a - max
b - min
c - max
d - min
Note the minimax value of b1 is 3.
"""
num_agents: "2"
start_state: "a"
win_states: "A B C D E F G H I J K L M N O P Z"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
c3 Left d5
c3 Right d6
c4 Left d7
c4 Right d8
cx Down dx
d1 Left A
d1 Right B
d2 Left C
d2 Right D
d3 Left E
d3 Right F
d4 Left G
d4 Right H
d5 Left I
d5 Right J
d6 Left K
d6 Right L
d7 Left M
d7 Right N
d8 Left O
d8 Right P
dx Down Z
"""
evaluation: """
A -3.0
B 13.0
C 5.0
D 9.0
E 10.0
F 3.0
G -6.0
H 8.0
Z 3.01
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-6-minmax.test.
action: "Left"
generated: "A B C D E F G H Z a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
+75
View File
@@ -0,0 +1,75 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "4"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
c1 c2 cx
/ \ / \ |
d1 d2 d3 d4 dx
/ \ / \ / \ / \ |
A B C D E F G H Z
-3 13 5 9 10 3 -6 8 2.99
a - max
b - min
c - max
d - min
Note the minimax value of b1 is 3.
"""
num_agents: "2"
start_state: "a"
win_states: "A B C D E F G H I J K L M N O P Z"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
c3 Left d5
c3 Right d6
c4 Left d7
c4 Right d8
cx Down dx
d1 Left A
d1 Right B
d2 Left C
d2 Right D
d3 Left E
d3 Right F
d4 Left G
d4 Right H
d5 Left I
d5 Right J
d6 Left K
d6 Right L
d7 Left M
d7 Right N
d8 Left O
d8 Right P
dx Down Z
"""
evaluation: """
A -3.0
B 13.0
C 5.0
D 9.0
E 10.0
F 3.0
G -6.0
H 8.0
Z 2.99
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-7-minmax.test.
action: "Left"
generated: "I J K L M N O P Z a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
+75
View File
@@ -0,0 +1,75 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "4"
diagram: """
/-----a------\
/ \
/ \
b1 b2
| / \
cx c3 c4
| / \ / \
dx d5 d6 d7 d8
| / \ / \ / \ / \
Z I J K L M N O P
-1.99 -1 -9 4 7 2 5 -3 -2
a - max
b - min
c - min
d - max
Note that the minimax value of b2 is -2
"""
num_agents: "3"
start_state: "a"
win_states: "A B C D E F G H I J K L M N O P Z"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Down cx
b2 Left c3
b2 Right c4
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
c3 Left d5
c3 Right d6
c4 Left d7
c4 Right d8
cx Down dx
d1 Left A
d1 Right B
d2 Left C
d2 Right D
d3 Left E
d3 Right F
d4 Left G
d4 Right H
d5 Left I
d5 Right J
d6 Left K
d6 Right L
d7 Left M
d7 Right N
d8 Left O
d8 Right P
dx Down Z
"""
evaluation: """
I -1.0
J -9.0
K 4.0
L 7.0
M 2.0
N 5.0
O -3.0
P -2.0
Z -1.99
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/1-8-minmax.test.
action: "Right"
generated: "I J K L M N O P Z a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
+75
View File
@@ -0,0 +1,75 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "4"
diagram: """
/-----a------\
/ \
/ \
b1 b2
| / \
cx c3 c4
| / \ / \
dx d5 d6 d7 d8
| / \ / \ / \ / \
Z I J K L M N O P
-2.01 -1 -9 4 7 2 5 -3 -2
a - max
b - min
c - min
d - max
Note that the minimax value of b2 is -2.01
"""
num_agents: "3"
start_state: "a"
win_states: "A B C D E F G H I J K L M N O P Z"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Down cx
b2 Left c3
b2 Right c4
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
c3 Left d5
c3 Right d6
c4 Left d7
c4 Right d8
cx Down dx
d1 Left A
d1 Right B
d2 Left C
d2 Right D
d3 Left E
d3 Right F
d4 Left G
d4 Right H
d5 Left I
d5 Right J
d6 Left K
d6 Right L
d7 Left M
d7 Right N
d8 Left O
d8 Right P
dx Down Z
"""
evaluation: """
I -1.0
J -9.0
K 4.0
L 7.0
M 2.0
N 5.0
O -3.0
P -2.0
Z -2.01
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/2-1a-vary-depth.test.
action: "Left"
generated: "a b1 b2 c1 c2 cx"
+52
View File
@@ -0,0 +1,52 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "1"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
-4 c1 c2 9 cx -4.01
/ \ / \ |
d1 d2 d3 d4 dx
-3 -9 10 6 -4.01
a - max
b - min
c - max
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
The values next to c1, c2, and cx are the values of the evaluation function, not
necessarily the correct minimax backup.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
cx Down dx
"""
evaluation: """
c1 -4.0
c2 9.0
cx -4.01
d1 -3.0
d2 -9.0
d3 10.0
d4 6.0
dx -4.01
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/2-1b-vary-depth.test.
action: "Left"
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
+52
View File
@@ -0,0 +1,52 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
-4 c1 c2 9 cx -4.01
/ \ / \ |
d1 d2 d3 d4 dx
-3 -9 10 6 -4.01
a - max
b - min
c - max
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
The values next to c1, c2, and cx are the values of the evaluation function, not
necessarily the correct minimax backup.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
cx Down dx
"""
evaluation: """
c1 -4.0
c2 9.0
cx -4.01
d1 -3.0
d2 -9.0
d3 10.0
d4 6.0
dx -4.01
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/2-2a-vary-depth.test.
action: "Right"
generated: "a b1 b2 c1 c2 cx"
+52
View File
@@ -0,0 +1,52 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "1"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
-4 c1 c2 9 cx -3.99
/ \ / \ |
d1 d2 d3 d4 dx
-3 -9 10 6 -3.99
a - max
b - min
c - max
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
The values next to c1, c2, and cx are the values of the evaluation function, not
necessarily the correct minimax backup.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
cx Down dx
"""
evaluation: """
c1 -4.0
c2 9.0
cx -3.99
d1 -3.0
d2 -9.0
d3 10.0
d4 6.0
dx -3.99
"""
+3
View File
@@ -0,0 +1,3 @@
# This is the solution file for test_cases/q2/2-2b-vary-depth.test.
action: "Left"
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
+52
View File
@@ -0,0 +1,52 @@
class: "GraphGameTreeTest"
alg: "MinimaxAgent"
depth: "2"
diagram: """
/-----a------\
/ \
/ \
b1 b2
/ \ |
-4 c1 c2 9 cx -3.99
/ \ / \ |
d1 d2 d3 d4 dx
-3 -9 10 6 -3.99
a - max
b - min
c - max
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
The values next to c1, c2, and cx are the values of the evaluation function, not
necessarily the correct minimax backup.
"""
num_agents: "2"
start_state: "a"
win_states: "d1 d2 d3 d4 dx"
lose_states: ""
successors: """
a Left b1
a Right b2
b1 Left c1
b1 Right c2
b2 Down cx
c1 Left d1
c1 Right d2
c2 Left d3
c2 Right d4
cx Down dx
"""
evaluation: """
c1 -4.0
c2 9.0
cx -3.99
d1 -3.0
d2 -9.0
d3 10.0
d4 6.0
dx -3.99
"""

Some files were not shown because too many files have changed in this diff Show More