getting right starter files
This commit is contained in:
+79
-68
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -22,63 +22,65 @@ import sys
|
|||||||
import projectParams
|
import projectParams
|
||||||
import random
|
import random
|
||||||
random.seed(0)
|
random.seed(0)
|
||||||
try:
|
try:
|
||||||
from pacman import GameState
|
from pacman import GameState
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 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))
|
||||||
@@ -149,8 +154,8 @@ def readFile(path, root=""):
|
|||||||
|
|
||||||
# TODO: use these
|
# TODO: use these
|
||||||
ERROR_HINT_MAP = {
|
ERROR_HINT_MAP = {
|
||||||
'q1': {
|
'q1': {
|
||||||
"<type 'exceptions.IndexError'>": """
|
"<type 'exceptions.IndexError'>": """
|
||||||
We noticed that your project threw an IndexError on q1.
|
We noticed that your project threw an IndexError on q1.
|
||||||
While many things may cause this, it may have been from
|
While many things may cause this, it may have been from
|
||||||
assuming a certain number of successors from a state space
|
assuming a certain number of successors from a state space
|
||||||
@@ -158,9 +163,9 @@ ERROR_HINT_MAP = {
|
|||||||
state. Try making your code more general (no hardcoded indices)
|
state. Try making your code more general (no hardcoded indices)
|
||||||
and submit again!
|
and submit again!
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
'q3': {
|
'q3': {
|
||||||
"<type 'exceptions.AttributeError'>": """
|
"<type 'exceptions.AttributeError'>": """
|
||||||
We noticed that your project threw an AttributeError on q3.
|
We noticed that your project threw an AttributeError on q3.
|
||||||
While many things may cause this, it may have been from assuming
|
While many things may cause this, it may have been from assuming
|
||||||
a certain size or structure to the state space. For example, if you have
|
a certain size or structure to the state space. For example, if you have
|
||||||
@@ -169,11 +174,12 @@ ERROR_HINT_MAP = {
|
|||||||
making your code more general and submit again!
|
making your code more general and submit again!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
@@ -246,7 +255,7 @@ def getTestSubdirs(testParser, testRoot, questionToGrade):
|
|||||||
# evaluate student code
|
# evaluate student code
|
||||||
def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MAP,
|
def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MAP,
|
||||||
edxOutput=False, muteOutput=False, gsOutput=False,
|
edxOutput=False, muteOutput=False, gsOutput=False,
|
||||||
printTestCase=False, questionToGrade=None, display=None):
|
printTestCase=False, questionToGrade=None, display=None):
|
||||||
# imports of testbench code. note that the testClasses import must follow
|
# imports of testbench code. note that the testClasses import must follow
|
||||||
# the import of student code due to dependencies
|
# the import of student code due to dependencies
|
||||||
import testParser
|
import testParser
|
||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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'
|
||||||
@@ -53,13 +57,13 @@ class Directions:
|
|||||||
WEST = 'West'
|
WEST = 'West'
|
||||||
STOP = 'Stop'
|
STOP = 'Stop'
|
||||||
|
|
||||||
LEFT = {NORTH: WEST,
|
LEFT = {NORTH: WEST,
|
||||||
SOUTH: EAST,
|
SOUTH: EAST,
|
||||||
EAST: NORTH,
|
EAST: NORTH,
|
||||||
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,8 +344,8 @@ 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)
|
||||||
|
|
||||||
@@ -335,30 +355,34 @@ class Actions:
|
|||||||
x_int, y_int = int(x + 0.5), int(y + 0.5)
|
x_int, y_int = int(x + 0.5), int(y + 0.5)
|
||||||
|
|
||||||
# In between grid points, all agents must continue straight
|
# In between grid points, all agents must continue straight
|
||||||
if (abs(x - x_int) + abs(y - y_int) > Actions.TOLERANCE):
|
if (abs(x - x_int) + abs(y - y_int) > Actions.TOLERANCE):
|
||||||
return [config.getDirection()]
|
return [config.getDirection()]
|
||||||
|
|
||||||
for dir, vec in Actions._directionsAsList:
|
for dir, vec in Actions._directionsAsList:
|
||||||
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
|
||||||
|
|||||||
+35
-23
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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
|
||||||
|
|||||||
+218
-213
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -23,83 +23,88 @@ 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,
|
|
||||||
gsOutput=False, edxOutput=False, muteOutput=False):
|
|
||||||
"""
|
|
||||||
Defines the grading scheme for a project
|
|
||||||
projectName: project name
|
|
||||||
questionsAndMaxesDict: a list of (question name, max points per question)
|
|
||||||
"""
|
|
||||||
self.questions = [el[0] for el in questionsAndMaxesList]
|
|
||||||
self.maxes = dict(questionsAndMaxesList)
|
|
||||||
self.points = Counter()
|
|
||||||
self.messages = dict([(q, []) for q in self.questions])
|
|
||||||
self.project = projectName
|
|
||||||
self.start = time.localtime()[1:6]
|
|
||||||
self.sane = True # Sanity checks
|
|
||||||
self.currentQuestion = None # Which question we're grading
|
|
||||||
self.edxOutput = edxOutput
|
|
||||||
self.gsOutput = gsOutput # GradeScope output
|
|
||||||
self.mute = muteOutput
|
|
||||||
self.prereqs = defaultdict(set)
|
|
||||||
|
|
||||||
#print('Autograder transcript for %s' % self.project)
|
def __init__(self, projectName, questionsAndMaxesList,
|
||||||
print('Starting on %d-%d at %d:%02d:%02d' % self.start)
|
gsOutput=False, edxOutput=False, muteOutput=False):
|
||||||
|
"""
|
||||||
|
Defines the grading scheme for a project
|
||||||
|
projectName: project name
|
||||||
|
questionsAndMaxesDict: a list of (question name, max points per question)
|
||||||
|
"""
|
||||||
|
self.questions = [el[0] for el in questionsAndMaxesList]
|
||||||
|
self.maxes = dict(questionsAndMaxesList)
|
||||||
|
self.points = Counter()
|
||||||
|
self.messages = dict([(q, []) for q in self.questions])
|
||||||
|
self.project = projectName
|
||||||
|
self.start = time.localtime()[1:6]
|
||||||
|
self.sane = True # Sanity checks
|
||||||
|
self.currentQuestion = None # Which question we're grading
|
||||||
|
self.edxOutput = edxOutput
|
||||||
|
self.gsOutput = gsOutput # GradeScope output
|
||||||
|
self.mute = muteOutput
|
||||||
|
self.prereqs = defaultdict(set)
|
||||||
|
|
||||||
def addPrereq(self, question, prereq):
|
# print 'Autograder transcript for %s' % self.project
|
||||||
self.prereqs[question].add(prereq)
|
print('Starting on %d-%d at %d:%02d:%02d' % self.start)
|
||||||
|
|
||||||
def grade(self, gradingModule, exceptionMap = {}, bonusPic = False):
|
def addPrereq(self, question, prereq):
|
||||||
"""
|
self.prereqs[question].add(prereq)
|
||||||
Grades each question
|
|
||||||
gradingModule: the module with all the grading functions (pass in with sys.modules[__name__])
|
|
||||||
"""
|
|
||||||
|
|
||||||
completedQuestions = set([])
|
def grade(self, gradingModule, exceptionMap={}, bonusPic=False):
|
||||||
for q in self.questions:
|
"""
|
||||||
print('\nQuestion %s' % q)
|
Grades each question
|
||||||
print('=' * (9 + len(q)))
|
gradingModule: the module with all the grading functions (pass in with sys.modules[__name__])
|
||||||
print
|
"""
|
||||||
self.currentQuestion = q
|
|
||||||
|
|
||||||
incompleted = self.prereqs[q].difference(completedQuestions)
|
completedQuestions = set([])
|
||||||
if len(incompleted) > 0:
|
for q in self.questions:
|
||||||
prereq = incompleted.pop()
|
print('\nQuestion %s' % q)
|
||||||
print(
|
print('=' * (9 + len(q)))
|
||||||
"""*** NOTE: Make sure to complete Question %s before working on Question %s,
|
print()
|
||||||
|
self.currentQuestion = q
|
||||||
|
|
||||||
|
incompleted = self.prereqs[q].difference(completedQuestions)
|
||||||
|
if len(incompleted) > 0:
|
||||||
|
prereq = incompleted.pop()
|
||||||
|
print("""*** 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:
|
||||||
try:
|
util.mutePrint()
|
||||||
util.TimeoutFunction(getattr(gradingModule, q),1800)(self) # Call the question's function
|
try:
|
||||||
#TimeoutFunction(getattr(gradingModule, q),1200)(self) # Call the question's function
|
util.TimeoutFunction(getattr(gradingModule, q), 1800)(
|
||||||
except Exception as inst:
|
self) # Call the question's function
|
||||||
self.addExceptionMessage(q, inst, traceback)
|
# TimeoutFunction(getattr(gradingModule, q),1200)(self) # Call the question's function
|
||||||
self.addErrorHints(exceptionMap, inst, q[1])
|
except Exception as inst:
|
||||||
except:
|
self.addExceptionMessage(q, inst, traceback)
|
||||||
self.fail('FAIL: Terminated with a string exception.')
|
self.addErrorHints(exceptionMap, inst, q[1])
|
||||||
finally:
|
except:
|
||||||
if self.mute: util.unmutePrint()
|
self.fail('FAIL: Terminated with a string exception.')
|
||||||
|
finally:
|
||||||
|
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("\nProvisional grades\n==================")
|
||||||
|
|
||||||
print('\nFinished at %d:%02d:%02d' % time.localtime()[3:6])
|
for q in self.questions:
|
||||||
print("\nProvisional grades\n==================")
|
print('Question %s: %d/%d' % (q, self.points[q], self.maxes[q]))
|
||||||
|
print('------------------')
|
||||||
for q in self.questions:
|
print('Total: %d/%d' %
|
||||||
print('Question %s: %d/%d' % (q, self.points[q], self.maxes[q]))
|
(self.points.totalCount(), sum(self.maxes.values())))
|
||||||
print('------------------')
|
if bonusPic and self.points.totalCount() == 25:
|
||||||
print('Total: %d/%d' % (self.points.totalCount(), sum(self.maxes.values())))
|
print("""
|
||||||
if bonusPic and self.points.totalCount() == 25:
|
|
||||||
print("""
|
|
||||||
|
|
||||||
ALL HAIL GRANDPAC.
|
ALL HAIL GRANDPAC.
|
||||||
LONG LIVE THE GHOSTBUSTING KING.
|
LONG LIVE THE GHOSTBUSTING KING.
|
||||||
@@ -131,115 +136,116 @@ class Grades:
|
|||||||
@@@@@@@@@@@@@@@@@@
|
@@@@@@@@@@@@@@@@@@
|
||||||
|
|
||||||
""")
|
""")
|
||||||
print("""
|
print("""
|
||||||
Your grades are NOT yet registered. To register your grades, make sure
|
Your grades are NOT yet registered. To register your grades, make sure
|
||||||
to follow your instructor's guidelines to receive credit on your project.
|
to follow your instructor's guidelines to receive credit on your project.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if self.edxOutput:
|
if self.edxOutput:
|
||||||
self.produceOutput()
|
self.produceOutput()
|
||||||
if self.gsOutput:
|
if self.gsOutput:
|
||||||
self.produceGradeScopeOutput()
|
self.produceGradeScopeOutput()
|
||||||
|
|
||||||
def addExceptionMessage(self, q, inst, traceback):
|
def addExceptionMessage(self, q, inst, traceback):
|
||||||
"""
|
"""
|
||||||
Method to format the exception message, this is more complicated because
|
Method to format the exception message, this is more complicated because
|
||||||
we need to cgi.escape the traceback but wrap the exception in a <pre> tag
|
we need to cgi.escape the traceback but wrap the exception in a <pre> tag
|
||||||
"""
|
"""
|
||||||
self.fail('FAIL: Exception raised: %s' % inst)
|
self.fail('FAIL: Exception raised: %s' % inst)
|
||||||
self.addMessage('')
|
self.addMessage('')
|
||||||
for line in traceback.format_exc().split('\n'):
|
for line in traceback.format_exc().split('\n'):
|
||||||
self.addMessage(line)
|
self.addMessage(line)
|
||||||
|
|
||||||
def addErrorHints(self, exceptionMap, errorInstance, questionNum):
|
def addErrorHints(self, exceptionMap, errorInstance, questionNum):
|
||||||
typeOf = str(type(errorInstance))
|
typeOf = str(type(errorInstance))
|
||||||
questionName = 'q' + questionNum
|
questionName = 'q' + questionNum
|
||||||
errorHint = ''
|
errorHint = ''
|
||||||
|
|
||||||
# question specific error hints
|
# question specific error hints
|
||||||
if exceptionMap.get(questionName):
|
if exceptionMap.get(questionName):
|
||||||
questionMap = exceptionMap.get(questionName)
|
questionMap = exceptionMap.get(questionName)
|
||||||
if (questionMap.get(typeOf)):
|
if (questionMap.get(typeOf)):
|
||||||
errorHint = questionMap.get(typeOf)
|
errorHint = questionMap.get(typeOf)
|
||||||
# fall back to general error messages if a question specific
|
# fall back to general error messages if a question specific
|
||||||
# one does not exist
|
# one does not exist
|
||||||
if (exceptionMap.get(typeOf)):
|
if (exceptionMap.get(typeOf)):
|
||||||
errorHint = exceptionMap.get(typeOf)
|
errorHint = exceptionMap.get(typeOf)
|
||||||
|
|
||||||
# dont include the HTML if we have no error hint
|
# dont include the HTML if we have no error hint
|
||||||
if not errorHint:
|
if not errorHint:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
for line in errorHint.split('\n'):
|
for line in errorHint.split('\n'):
|
||||||
self.addMessage(line)
|
self.addMessage(line)
|
||||||
|
|
||||||
def produceGradeScopeOutput(self):
|
def produceGradeScopeOutput(self):
|
||||||
out_dct = {}
|
out_dct = {}
|
||||||
|
|
||||||
# total of entire submission
|
# total of entire submission
|
||||||
total_possible = sum(self.maxes.values())
|
total_possible = sum(self.maxes.values())
|
||||||
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 = []
|
||||||
for name in self.questions:
|
for name in self.questions:
|
||||||
test_out = {}
|
test_out = {}
|
||||||
# test name
|
# test name
|
||||||
test_out['name'] = name
|
test_out['name'] = name
|
||||||
# test score
|
# test score
|
||||||
test_out['score'] = self.points[name]
|
test_out['score'] = self.points[name]
|
||||||
test_out['max_score'] = self.maxes[name]
|
test_out['max_score'] = self.maxes[name]
|
||||||
# others
|
# others
|
||||||
is_correct = self.points[name] >= self.maxes[name]
|
is_correct = self.points[name] >= self.maxes[name]
|
||||||
test_out['output'] = " Question {num} ({points}/{max}) {correct}".format(
|
test_out['output'] = " Question {num} ({points}/{max}) {correct}".format(
|
||||||
num=(name[1] if len(name) == 2 else name),
|
num=(name[1] if len(name) == 2 else name),
|
||||||
points=test_out['score'],
|
points=test_out['score'],
|
||||||
max=test_out['max_score'],
|
max=test_out['max_score'],
|
||||||
correct=('X' if not is_correct else ''),
|
correct=('X' if not is_correct else ''),
|
||||||
)
|
)
|
||||||
test_out['tags'] = []
|
test_out['tags'] = []
|
||||||
tests_out.append(test_out)
|
tests_out.append(test_out)
|
||||||
out_dct['tests'] = tests_out
|
out_dct['tests'] = tests_out
|
||||||
|
|
||||||
# file output
|
# file output
|
||||||
with open('gradescope_response.json', 'w') as outfile:
|
with open('gradescope_response.json', 'w') as outfile:
|
||||||
json.dump(out_dct, outfile)
|
json.dump(out_dct, outfile)
|
||||||
return
|
return
|
||||||
|
|
||||||
def produceOutput(self):
|
def produceOutput(self):
|
||||||
edxOutput = open('edx_response.html', 'w')
|
edxOutput = open('edx_response.html', 'w')
|
||||||
edxOutput.write("<div>")
|
edxOutput.write("<div>")
|
||||||
|
|
||||||
# first sum
|
# first sum
|
||||||
total_possible = sum(self.maxes.values())
|
total_possible = sum(self.maxes.values())
|
||||||
total_score = sum(self.points.values())
|
total_score = sum(self.points.values())
|
||||||
checkOrX = '<span class="incorrect"/>'
|
checkOrX = '<span class="incorrect"/>'
|
||||||
if (total_score >= total_possible):
|
if (total_score >= total_possible):
|
||||||
checkOrX = '<span class="correct"/>'
|
checkOrX = '<span class="correct"/>'
|
||||||
header = """
|
header = """
|
||||||
<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)
|
||||||
|
|
||||||
for q in self.questions:
|
for q in self.questions:
|
||||||
if len(q) == 2:
|
if len(q) == 2:
|
||||||
name = q[1]
|
name = q[1]
|
||||||
else:
|
else:
|
||||||
name = q
|
name = q
|
||||||
checkOrX = '<span class="incorrect"/>'
|
checkOrX = '<span class="incorrect"/>'
|
||||||
if (self.points[q] >= self.maxes[q]):
|
if (self.points[q] >= self.maxes[q]):
|
||||||
checkOrX = '<span class="correct"/>'
|
checkOrX = '<span class="correct"/>'
|
||||||
#messages = '\n<br/>\n'.join(self.messages[q])
|
#messages = '\n<br/>\n'.join(self.messages[q])
|
||||||
messages = "<pre>%s</pre>" % '\n'.join(self.messages[q])
|
messages = "<pre>%s</pre>" % '\n'.join(self.messages[q])
|
||||||
output = """
|
output = """
|
||||||
<div class="test">
|
<div class="test">
|
||||||
<section>
|
<section>
|
||||||
<div class="shortform">
|
<div class="shortform">
|
||||||
@@ -250,74 +256,73 @@ 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()
|
||||||
edxOutput = open('edx_grade', 'w')
|
edxOutput = open('edx_grade', 'w')
|
||||||
edxOutput.write(str(self.points.totalCount()))
|
edxOutput.write(str(self.points.totalCount()))
|
||||||
edxOutput.close()
|
edxOutput.close()
|
||||||
|
|
||||||
def fail(self, message, raw=False):
|
def fail(self, message, raw=False):
|
||||||
"Sets sanity check bit to false and outputs a message"
|
"Sets sanity check bit to false and outputs a message"
|
||||||
self.sane = False
|
self.sane = False
|
||||||
self.assignZeroCredit()
|
self.assignZeroCredit()
|
||||||
self.addMessage(message, raw)
|
self.addMessage(message, raw)
|
||||||
|
|
||||||
def assignZeroCredit(self):
|
def assignZeroCredit(self):
|
||||||
self.points[self.currentQuestion] = 0
|
self.points[self.currentQuestion] = 0
|
||||||
|
|
||||||
def addPoints(self, amt):
|
def addPoints(self, amt):
|
||||||
self.points[self.currentQuestion] += amt
|
self.points[self.currentQuestion] += amt
|
||||||
|
|
||||||
def deductPoints(self, amt):
|
def deductPoints(self, amt):
|
||||||
self.points[self.currentQuestion] -= amt
|
self.points[self.currentQuestion] -= amt
|
||||||
|
|
||||||
def assignFullCredit(self, message="", raw=False):
|
|
||||||
self.points[self.currentQuestion] = self.maxes[self.currentQuestion]
|
|
||||||
if message != "":
|
|
||||||
self.addMessage(message, raw)
|
|
||||||
|
|
||||||
def addMessage(self, message, raw=False):
|
|
||||||
if not raw:
|
|
||||||
# We assume raw messages, formatted for HTML, are printed separately
|
|
||||||
if self.mute: util.unmutePrint()
|
|
||||||
print('*** ' + message)
|
|
||||||
if self.mute: util.mutePrint()
|
|
||||||
message = cgi.escape(message)
|
|
||||||
self.messages[self.currentQuestion].append(message)
|
|
||||||
|
|
||||||
def addMessageToEmail(self, message):
|
|
||||||
print("WARNING**** addMessageToEmail is deprecated %s" % message)
|
|
||||||
for line in message.split('\n'):
|
|
||||||
pass
|
|
||||||
#print('%%% ' + line + ' %%%')
|
|
||||||
#self.messages[self.currentQuestion].append(line)
|
|
||||||
|
|
||||||
|
def assignFullCredit(self, message="", raw=False):
|
||||||
|
self.points[self.currentQuestion] = self.maxes[self.currentQuestion]
|
||||||
|
if message != "":
|
||||||
|
self.addMessage(message, raw)
|
||||||
|
|
||||||
|
def addMessage(self, message, raw=False):
|
||||||
|
if not raw:
|
||||||
|
# We assume raw messages, formatted for HTML, are printed separately
|
||||||
|
if self.mute:
|
||||||
|
util.unmutePrint()
|
||||||
|
print('*** ' + message)
|
||||||
|
if self.mute:
|
||||||
|
util.mutePrint()
|
||||||
|
message = cgi.escape(message)
|
||||||
|
self.messages[self.currentQuestion].append(message)
|
||||||
|
|
||||||
|
def addMessageToEmail(self, message):
|
||||||
|
print("WARNING**** addMessageToEmail is deprecated %s" % message)
|
||||||
|
for line in message.split('\n'):
|
||||||
|
pass
|
||||||
|
# print '%%% ' + line + ' %%%'
|
||||||
|
# self.messages[self.currentQuestion].append(line)
|
||||||
|
|
||||||
|
|
||||||
class Counter(dict):
|
class Counter(dict):
|
||||||
"""
|
|
||||||
Dict with default 0
|
|
||||||
"""
|
|
||||||
def __getitem__(self, idx):
|
|
||||||
try:
|
|
||||||
return dict.__getitem__(self, idx)
|
|
||||||
except KeyError:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def totalCount(self):
|
|
||||||
"""
|
"""
|
||||||
Returns the sum of counts for all keys.
|
Dict with default 0
|
||||||
"""
|
"""
|
||||||
return sum(self.values())
|
|
||||||
|
|
||||||
|
def __getitem__(self, idx):
|
||||||
|
try:
|
||||||
|
return dict.__getitem__(self, idx)
|
||||||
|
except KeyError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def totalCount(self):
|
||||||
|
"""
|
||||||
|
Returns the sum of counts for all keys.
|
||||||
|
"""
|
||||||
|
return sum(self.values())
|
||||||
|
|||||||
+202
-143
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -209,14 +217,14 @@ class PacmanGraphics:
|
|||||||
refresh()
|
refresh()
|
||||||
|
|
||||||
def drawAgentObjects(self, state):
|
def drawAgentObjects(self, state):
|
||||||
self.agentImages = [] # (agentState, image)
|
self.agentImages = [] # (agentState, image)
|
||||||
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,43 +420,48 @@ 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
|
||||||
pos = (xNum, yNum)
|
pos = (xNum, yNum)
|
||||||
screen = self.to_screen(pos)
|
screen = self.to_screen(pos)
|
||||||
screen2 = self.to_screen2(pos)
|
screen2 = self.to_screen2(pos)
|
||||||
@@ -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
|
||||||
|
|||||||
+92
-43
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -33,17 +33,21 @@ _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']
|
||||||
else:
|
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
|
||||||
@@ -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,63 +280,71 @@ 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']:
|
||||||
return
|
return
|
||||||
if event.keycode in [37, 101]: # LEFT ARROW (win / x)
|
if event.keycode in [37, 101]: # LEFT ARROW (win / x)
|
||||||
event.char = 'a'
|
event.char = 'a'
|
||||||
if event.keycode in [38, 99]: # UP ARROW
|
if event.keycode in [38, 99]: # UP ARROW
|
||||||
event.char = 'w'
|
event.char = 'w'
|
||||||
if event.keycode in [39, 102]: # RIGHT ARROW
|
if event.keycode in [39, 102]: # RIGHT ARROW
|
||||||
event.char = 'd'
|
event.char = 'd'
|
||||||
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,29 +352,34 @@ 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 = []
|
||||||
current_x, current_y = _canvas.coords(object)[0:2] # first point
|
current_x, current_y = _canvas.coords(object)[0:2] # first point
|
||||||
for coord in _canvas.coords(object):
|
for coord in _canvas.coords(object):
|
||||||
if horiz:
|
if horiz:
|
||||||
inc = x - current_x
|
inc = x - current_x
|
||||||
else:
|
else:
|
||||||
@@ -347,16 +391,19 @@ 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 = []
|
||||||
for coord in _canvas.coords(object):
|
for coord in _canvas.coords(object):
|
||||||
if horiz:
|
if horiz:
|
||||||
inc = x
|
inc = x
|
||||||
else:
|
else:
|
||||||
@@ -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()
|
||||||
|
|||||||
+28
-17
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -16,27 +16,28 @@ 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.
|
||||||
"""
|
"""
|
||||||
# NOTE: Arrow keys also work.
|
# NOTE: Arrow keys also work.
|
||||||
WEST_KEY = 'a'
|
WEST_KEY = 'a'
|
||||||
EAST_KEY = 'd'
|
EAST_KEY = 'd'
|
||||||
NORTH_KEY = 'w'
|
NORTH_KEY = 'w'
|
||||||
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,27 +60,36 @@ 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.
|
||||||
"""
|
"""
|
||||||
# NOTE: Arrow keys also work.
|
# NOTE: Arrow keys also work.
|
||||||
WEST_KEY = 'j'
|
WEST_KEY = 'j'
|
||||||
EAST_KEY = "l"
|
EAST_KEY = "l"
|
||||||
NORTH_KEY = 'i'
|
NORTH_KEY = 'i'
|
||||||
SOUTH_KEY = 'k'
|
SOUTH_KEY = 'k'
|
||||||
STOP_KEY = 'u'
|
STOP_KEY = 'u'
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%. % %.%
|
|
||||||
% %%%%% % %%% %%% %%%%%%% % %
|
|
||||||
% % % % % % % %
|
|
||||||
%%%%% %%%%% %%% % % % %%% %%%%% % %%%
|
|
||||||
% % % % % % % % % % % % %
|
|
||||||
% %%% % % % %%% %%%%% %%% % %%% %%% %
|
|
||||||
% % % % % % % % %
|
|
||||||
%%% %%%%%%%%% %%%%%%% %%% %%% % % % %
|
|
||||||
% % % % % % %
|
|
||||||
% % %%%%% % %%% % % %%% % %%% %%% % %
|
|
||||||
% % % % % % % % % % % % % %
|
|
||||||
% % % %%%%%%% % %%%%%%%%% %%% % %%% %
|
|
||||||
% % % % % % % % % %
|
|
||||||
%%% %%% % %%%%% %%%%% %%% %%% %%%%% %
|
|
||||||
% % % % % % % % %
|
|
||||||
% % % % % % %%% %%% %%% % % % % % %
|
|
||||||
% % % % % %% % % % % % % % % %
|
|
||||||
% % %%%%% % %%% %%% % %%% %%% %%%%%
|
|
||||||
% % % % % % % % % % %
|
|
||||||
% %%% % % % %%% %%% %%%%%%%%% % %%%
|
|
||||||
% % % % % % %
|
|
||||||
% %%% %%%%%%%%%%%%%%%%%%%%% % % %%% %
|
|
||||||
% % % %
|
|
||||||
% % % %%%%% %%% % % % % %%%%%%%%%%%%%
|
|
||||||
% % % % % % % % % % % %
|
|
||||||
% % %%% %%% % % % %%%%%%%%% %%% % % %
|
|
||||||
% % % % % % %P % % % % % %
|
|
||||||
% %%% %%% %%% % %%% % % %%%%% % %%%%%
|
|
||||||
% % % % % % % %
|
|
||||||
%%% % %%%%% %%%%% %%% %%% % %%% % %%%
|
|
||||||
% % % % % % % % % % % % % % %
|
|
||||||
% % %%% % % % % %%%%%%%%% % % % % % %
|
|
||||||
% % % %
|
|
||||||
% % % %%% %%% %%%%%%% %%% %%% %%% %
|
|
||||||
%.% % % % % .%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% % % % % % % %
|
|
||||||
% %%%%%%% % %%% % %%% %%% %%%%%%% % %
|
|
||||||
% % % % % % % %
|
|
||||||
%%%%% %%%%% %%% % % % %%% %%%%% % %%%
|
|
||||||
% % % % % % % % % % % % % %
|
|
||||||
% %%% % % % %%% %%%%% %%% % %%% %%% %
|
|
||||||
% % % % % % % % %
|
|
||||||
%%% %%%%%%%%% %%%%%%% %%% %%% % % % %
|
|
||||||
% % % % % % %
|
|
||||||
% % %%%%% % %%% % % %%% % %%% %%% % %
|
|
||||||
% % % % % % % % % % % % % %
|
|
||||||
% % % %%%%%%% % %%%%%%%%% %%% % %%% %
|
|
||||||
% % % % % % % % % %
|
|
||||||
%%% %%% % %%%%% %%%%% %%% %%% %%%%% %
|
|
||||||
% % % % % % % % % % % %
|
|
||||||
% % % % % %%% %%% %%% %%% % % % % % %
|
|
||||||
% % % % % % % % %
|
|
||||||
%%% %%%%%%% % % %%%%% %%% % %%% %%%%%
|
|
||||||
% % % % % % % % % %
|
|
||||||
%%%%% % % %%%%%%%%% %%%%%%%%%%% % %%%
|
|
||||||
% % % % % % % % %
|
|
||||||
% %%% %%%%% %%%%%%%%% %%%%% % % %%% %
|
|
||||||
% % % % % % %
|
|
||||||
% % % %%%%% %%% % % % % %%%%%%%%%%%%%
|
|
||||||
% % % % % % % % % % % %
|
|
||||||
% % %%% %%% % % % %%%%%%%%% %%% % % %
|
|
||||||
% % % % % % % % % % % % %
|
|
||||||
% %%% %%% %%%%% %%% % % %%%%% % %%%%%
|
|
||||||
% % % % % % % % %
|
|
||||||
%%% % %%%%% %%%%% %%% %%% % %%% % %%%
|
|
||||||
% % % % % % % % % % % % % % %
|
|
||||||
% % %%% % % % % %%%%%%%%% % % % % % %
|
|
||||||
% % % % % %
|
|
||||||
% % % % %%% %%% %%%%%%% %%% %%% %%% %
|
|
||||||
%.% % % % % % % % P%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%.%.........%% G % o%%%%.....%
|
|
||||||
%.%.%%%%%%%.%%%%%% %%%%%%%.%%.%
|
|
||||||
%............%...%............%
|
|
||||||
%%%%%...%%%.. ..%.%...%.%%%
|
|
||||||
%o%%%.%%%%%.%%%%%%%.%%%.%.%%%%%
|
|
||||||
% ..........Po...%...%. o%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%.....%.................%.....%
|
|
||||||
%.%%%.%.%%%.%%%%%%%.%%%.%.....%
|
|
||||||
%.%...%.%......%......%.%.....%
|
|
||||||
%...%%%.%.%%%%.%.%%%%...%%%...%
|
|
||||||
%%%.%.%.%.%......%..%.%...%.%%%
|
|
||||||
%...%.%%%.%.%%% %%%.%.%%%.%...%
|
|
||||||
%.%%%.......% %.......%%%.%
|
|
||||||
%...%.%%%%%.%%%%%%%.%.%%%.%...%
|
|
||||||
%%%.%...%.%....%....%.%...%.%%%
|
|
||||||
%...%%%.%.%%%%.%.%%%%.%.%%%...%
|
|
||||||
%.......%......%......%.....%.%
|
|
||||||
%.....%.%%%.%%%%%%%.%%%.%.%%%.%
|
|
||||||
%.....%........P....%...%.....%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%
|
|
||||||
%. . . . . % %
|
|
||||||
% % %
|
|
||||||
%. . . . . %G%
|
|
||||||
% % %
|
|
||||||
%. . . . . % %
|
|
||||||
% % %
|
|
||||||
%. . . . . % %
|
|
||||||
% P %G%
|
|
||||||
%. . . . . % %
|
|
||||||
% % %
|
|
||||||
%. . . . . % %
|
|
||||||
% % %
|
|
||||||
%%%%%%%%%%%%%%
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
% P %
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
%. %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
%%%%%%
|
|
||||||
%....%
|
|
||||||
% %%.%
|
|
||||||
% %%.%
|
|
||||||
%.P .%
|
|
||||||
%.%%%%
|
|
||||||
%....%
|
|
||||||
%%%%%%
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%. % % % %.%
|
|
||||||
% % % %%%%%% %%%%%%% % %
|
|
||||||
% % % % % %
|
|
||||||
%%%%% %%%%% %%% %% %%%%% % %%%
|
|
||||||
% % % % % % % % %
|
|
||||||
% %%% % % % %%%%%%%% %%% %%% %
|
|
||||||
% % %% % % % %
|
|
||||||
%%% % %%%%%%% %%%% %%% % % % %
|
|
||||||
% % %% % % %
|
|
||||||
% % %%%%% % %%%% % %%% %%% % %
|
|
||||||
% % % % % % %%% %
|
|
||||||
%. %P%%%%% % %%% % .%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% P%
|
|
||||||
% %%%%%%%%%%%%%%%%%%% %%% %%%%%%%% %
|
|
||||||
% %% % % %%% %%% %% ... %
|
|
||||||
% %% % % % % %%%% %%%%%%%%% %% %%%%%
|
|
||||||
% %% % % % % % %% %% %% ... %
|
|
||||||
% %% % % % % % %%%% %%% %%%%%% %
|
|
||||||
% % % % % % %% %%%%%%%% ... %
|
|
||||||
% %% % % %%%%%%%% %% %% %%%%%
|
|
||||||
% %% % %% %%%%%%%%% %% ... %
|
|
||||||
% %%%%%% %%%%%%% %% %%%%%% %
|
|
||||||
%%%%%% % %%%% %% % ... %
|
|
||||||
% %%%%%% %%%%% % %% %% %%%%%
|
|
||||||
% %%%%%% % %%%%% %% %
|
|
||||||
% %%%%%% %%%%%%%%%%% %% %% %
|
|
||||||
%%%%%%%%%% %%%%%% %
|
|
||||||
%. %%%%%%%%%%%%%%%% ...... %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% P%
|
|
||||||
% %%%%%%%%%%%%%%%%%%%%%%% %%%%%%%% %
|
|
||||||
% %% % % %%%%%%% %% %
|
|
||||||
% %% % % % % %%%% %%%%%%%%% %% %%%%%
|
|
||||||
% %% % % % % %% %% %
|
|
||||||
% %% % % % % % %%%% %%% %%%%%% %
|
|
||||||
% % % % % % %% %%%%%%%% %
|
|
||||||
% %% % % %%%%%%%% %% %% %%%%%
|
|
||||||
% %% % %% %%%%%%%%% %% %
|
|
||||||
% %%%%%% %%%%%%% %% %%%%%% %
|
|
||||||
%%%%%% % %%%% %% % %
|
|
||||||
% %%%%%% %%%%% % %% %% %%%%%
|
|
||||||
% %%%%%% % %%%%% %% %
|
|
||||||
% %%%%%% %%%%%%%%%%% %% %% %
|
|
||||||
%%%%%%%%%% %%%%%% %
|
|
||||||
%. %%%%%%%%%%%%%%%% %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%.% ....%% G %%%%%% o%%.%
|
|
||||||
%.%o%%%%%%%.%%%%%%% %%%%%.%
|
|
||||||
% %%%.%%%%%.%%%%%%%.%%%.%.%%%.%
|
|
||||||
% ..........Po...%.........%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% P%
|
|
||||||
% %%%%%%%%%%%%%%%%%%% %%% %%%%%%%% %
|
|
||||||
% %% % % %%% %%% %%GG %
|
|
||||||
% %% % % % % %%%% %%%%%%%%% %% %%%%%
|
|
||||||
% %% % % % % % %%GG %% %
|
|
||||||
% %% % % % % % %%%%% %%% %%%%%% %
|
|
||||||
% %% % % % % %% %%%%%%%%% %
|
|
||||||
% %% % % %%%%%%%% %% %% %%%%%
|
|
||||||
% %% % %% %%%%%%%%% %% %
|
|
||||||
% %%% %% %%%%%%% %% %%%%%% %
|
|
||||||
%%%%%% % % %% %% %
|
|
||||||
% %%%%%% %% %% %% %% %%%%%
|
|
||||||
% %%%%%% % %%%%% %% %
|
|
||||||
% %%%% %%%%% %%%%%% %
|
|
||||||
%%%%%%%% % %%%%%% %
|
|
||||||
%. %%%%%%%%%%%%%%%% %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%............%%%%%............%
|
|
||||||
%%%.%...%%%.........%.%...%.%%%
|
|
||||||
%...%%%.%.%%%%.%.%%%%%%.%%%...%
|
|
||||||
%.%.....%......%......%.....%.%
|
|
||||||
%.%%%.%%%%%.%%%%%%%.%%%.%.%%%%%
|
|
||||||
%.....%........P....%...%.....%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%...%.........%%...%
|
|
||||||
%.%.%.%%%%%%%%%%.%.%
|
|
||||||
%..................%
|
|
||||||
%%%%%%%%.%.%%%%%%%P%
|
|
||||||
%%%%%%%%....... %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% P%
|
|
||||||
% % %
|
|
||||||
% % %
|
|
||||||
% % %
|
|
||||||
% % %
|
|
||||||
% % %
|
|
||||||
% % % %
|
|
||||||
% % % %
|
|
||||||
% % % %
|
|
||||||
% % % %
|
|
||||||
% % % %
|
|
||||||
% % % %
|
|
||||||
% % % %
|
|
||||||
%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%
|
|
||||||
% % %
|
|
||||||
% % %
|
|
||||||
% % %
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
% %
|
|
||||||
%. %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%..................%
|
|
||||||
%..................%
|
|
||||||
%........P.........%
|
|
||||||
%..................%
|
|
||||||
%..................%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
% %% % % %
|
|
||||||
% %%%%%% % %%%%%% %
|
|
||||||
%%%%%% P % %
|
|
||||||
% % %%%%%% %% %%%%%
|
|
||||||
% %%%% % % %
|
|
||||||
% %%% %%% % %
|
|
||||||
%%%%%%%%%% %%%%%% %
|
|
||||||
%. %% %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
%%%%%%%%%
|
|
||||||
%.. % G %
|
|
||||||
%%% %%%%%
|
|
||||||
% %
|
|
||||||
%%%%%%% %
|
|
||||||
% %
|
|
||||||
% %%%%% %
|
|
||||||
% % %
|
|
||||||
%%%%% % %
|
|
||||||
% %o%
|
|
||||||
% %%%%%%%
|
|
||||||
% .%
|
|
||||||
%%%%%%%.%
|
|
||||||
%Po .%
|
|
||||||
%%%%%%%%%
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%. ...P .%
|
|
||||||
%.%%.%%.%%.%%.%% %.%
|
|
||||||
% %% %..... %.%
|
|
||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
%%%%%%%%%%
|
|
||||||
%. P%
|
|
||||||
%%%%%%%%%%
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
%%%%%
|
|
||||||
%.P %
|
|
||||||
%%% %
|
|
||||||
%. %
|
|
||||||
%%%%%
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
%%%%%%%%
|
|
||||||
%. .%
|
|
||||||
% P %
|
|
||||||
% %%%% %
|
|
||||||
% % %
|
|
||||||
% % %%%%
|
|
||||||
%.% .%
|
|
||||||
%%%%%%%%
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
%%%%%%%
|
|
||||||
% P%
|
|
||||||
% %%% %
|
|
||||||
% % %
|
|
||||||
%% %%
|
|
||||||
%. %%%%
|
|
||||||
%%%%%%%
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
%%%%%%%%%
|
|
||||||
% G %...%
|
|
||||||
%%%%%%% %
|
|
||||||
%Po %
|
|
||||||
%.%%.%%.%
|
|
||||||
%.%%....%
|
|
||||||
%%%%%%%%%
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
%%%%%%%%%
|
|
||||||
%.. ..%
|
|
||||||
%%%%.%% %
|
|
||||||
% P %
|
|
||||||
%.%% %%.%
|
|
||||||
%.%. .%
|
|
||||||
%%%%%%%%%
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
%. ..% %
|
|
||||||
%.%%.%%.%%.%%.%% % %
|
|
||||||
% P % %
|
|
||||||
%%%%%%%%%%%%%%%%%% %
|
|
||||||
%..... %
|
|
||||||
%%%%%%%%%%%%%%%%%%%%
|
|
||||||
+178
@@ -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
|
||||||
@@ -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
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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,37 +226,37 @@ 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.
|
||||||
"""
|
"""
|
||||||
if prevState != None: # Initial state
|
if prevState != None: # Initial state
|
||||||
self.data = GameStateData(prevState.data)
|
self.data = GameStateData(prevState.data)
|
||||||
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,20 +561,24 @@ 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:
|
||||||
pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs
|
agentOpts['numTraining'] = options.numTraining
|
||||||
|
pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs
|
||||||
args['pacman'] = pacman
|
args['pacman'] = pacman
|
||||||
|
|
||||||
# Don't display training games
|
# Don't display training games
|
||||||
@@ -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 )")
|
||||||
|
|||||||
+20
-9
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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()
|
||||||
|
|||||||
+4
-4
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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))
|
|
||||||
@@ -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
+8
-10
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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'))
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -4,7 +4,7 @@
|
|||||||
# educational purposes provided that (1) you do not distribute or publish
|
# educational purposes provided that (1) you do not distribute or publish
|
||||||
# solutions, (2) you retain this notice, and (3) you provide clear
|
# solutions, (2) you retain this notice, and (3) you provide clear
|
||||||
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
|
||||||
#
|
#
|
||||||
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
|
||||||
# The core projects and autograders were primarily created by John DeNero
|
# The core projects and autograders were primarily created by John DeNero
|
||||||
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||||
@@ -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
@@ -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"
|
|
||||||
@@ -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)]"
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
max_points: "3"
|
max_points: "4"
|
||||||
class: "PassAllTestsQuestion"
|
class: "PartialCreditQuestion"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# This is the solution file for test_cases/q1/grade-agent.test.
|
||||||
|
# File intentionally blank.
|
||||||
@@ -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)]"
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
"""
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
"""
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
"""
|
|
||||||
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
|
||||||
"""
|
|
||||||
@@ -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"
|
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
|
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
@@ -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"
|
||||||
@@ -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
Reference in New Issue
Block a user