Added problem 2 and solve question 1 reflex agent.
This commit is contained in:
parent
fd8dd8ae35
commit
11dcc491a2
1
p2_multiagent/VERSION
Normal file
1
p2_multiagent/VERSION
Normal file
@ -0,0 +1 @@
|
||||
v1.002
|
351
p2_multiagent/autograder.py
Normal file
351
p2_multiagent/autograder.py
Normal file
@ -0,0 +1,351 @@
|
||||
# autograder.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).
|
||||
|
||||
|
||||
# imports from python standard library
|
||||
import grading
|
||||
import imp
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import projectParams
|
||||
import random
|
||||
random.seed(0)
|
||||
try:
|
||||
from pacman import GameState
|
||||
except:
|
||||
pass
|
||||
|
||||
# register arguments and set default values
|
||||
def readCommand(argv):
|
||||
parser = optparse.OptionParser(description = 'Run public tests on student code')
|
||||
parser.set_defaults(generateSolutions=False, edxOutput=False, muteOutput=False, printTestCase=False, noGraphics=False)
|
||||
parser.add_option('--test-directory',
|
||||
dest = 'testRoot',
|
||||
default = 'test_cases',
|
||||
help = 'Root test directory which contains subdirectories corresponding to each question')
|
||||
parser.add_option('--student-code',
|
||||
dest = 'studentCode',
|
||||
default = projectParams.STUDENT_CODE_DEFAULT,
|
||||
help = 'comma separated list of student code files')
|
||||
parser.add_option('--code-directory',
|
||||
dest = 'codeRoot',
|
||||
default = "",
|
||||
help = 'Root directory containing the student and testClass code')
|
||||
parser.add_option('--test-case-code',
|
||||
dest = 'testCaseCode',
|
||||
default = projectParams.PROJECT_TEST_CLASSES,
|
||||
help = 'class containing testClass classes for this project')
|
||||
parser.add_option('--generate-solutions',
|
||||
dest = 'generateSolutions',
|
||||
action = 'store_true',
|
||||
help = 'Write solutions generated to .solution file')
|
||||
parser.add_option('--edx-output',
|
||||
dest = 'edxOutput',
|
||||
action = 'store_true',
|
||||
help = 'Generate edX output files')
|
||||
parser.add_option('--mute',
|
||||
dest = 'muteOutput',
|
||||
action = 'store_true',
|
||||
help = 'Mute output from executing tests')
|
||||
parser.add_option('--print-tests', '-p',
|
||||
dest = 'printTestCase',
|
||||
action = 'store_true',
|
||||
help = 'Print each test case before running them.')
|
||||
parser.add_option('--test', '-t',
|
||||
dest = 'runTest',
|
||||
default = None,
|
||||
help = 'Run one particular test. Relative to test root.')
|
||||
parser.add_option('--question', '-q',
|
||||
dest = 'gradeQuestion',
|
||||
default = None,
|
||||
help = 'Grade one particular question.')
|
||||
parser.add_option('--no-graphics',
|
||||
dest = 'noGraphics',
|
||||
action = 'store_true',
|
||||
help = 'No graphics display for pacman games.')
|
||||
(options, args) = parser.parse_args(argv)
|
||||
return options
|
||||
|
||||
|
||||
# confirm we should author solution files
|
||||
def confirmGenerate():
|
||||
print 'WARNING: this action will overwrite any solution files.'
|
||||
print 'Are you sure you want to proceed? (yes/no)'
|
||||
while True:
|
||||
ans = sys.stdin.readline().strip()
|
||||
if ans == 'yes':
|
||||
break
|
||||
elif ans == 'no':
|
||||
sys.exit(0)
|
||||
else:
|
||||
print 'please answer either "yes" or "no"'
|
||||
|
||||
|
||||
# TODO: Fix this so that it tracebacks work correctly
|
||||
# Looking at source of the traceback module, presuming it works
|
||||
# the same as the intepreters, it uses co_filename. This is,
|
||||
# however, a readonly attribute.
|
||||
def setModuleName(module, filename):
|
||||
functionType = type(confirmGenerate)
|
||||
classType = type(optparse.Option)
|
||||
|
||||
for i in dir(module):
|
||||
o = getattr(module, i)
|
||||
if hasattr(o, '__file__'): continue
|
||||
|
||||
if type(o) == functionType:
|
||||
setattr(o, '__file__', filename)
|
||||
elif type(o) == classType:
|
||||
setattr(o, '__file__', filename)
|
||||
# TODO: assign member __file__'s?
|
||||
#print i, type(o)
|
||||
|
||||
|
||||
#from cStringIO import StringIO
|
||||
|
||||
def loadModuleString(moduleSource):
|
||||
# Below broken, imp doesn't believe its being passed a file:
|
||||
# ValueError: load_module arg#2 should be a file or None
|
||||
#
|
||||
#f = StringIO(moduleCodeDict[k])
|
||||
#tmp = imp.load_module(k, f, k, (".py", "r", imp.PY_SOURCE))
|
||||
tmp = imp.new_module(k)
|
||||
exec moduleCodeDict[k] in tmp.__dict__
|
||||
setModuleName(tmp, k)
|
||||
return tmp
|
||||
|
||||
import py_compile
|
||||
|
||||
def loadModuleFile(moduleName, filePath):
|
||||
with open(filePath, 'r') as f:
|
||||
return imp.load_module(moduleName, f, "%s.py" % moduleName, (".py", "r", imp.PY_SOURCE))
|
||||
|
||||
|
||||
def readFile(path, root=""):
|
||||
"Read file from disk at specified path and return as string"
|
||||
with open(os.path.join(root, path), 'r') as handle:
|
||||
return handle.read()
|
||||
|
||||
|
||||
#######################################################################
|
||||
# Error Hint Map
|
||||
#######################################################################
|
||||
|
||||
# TODO: use these
|
||||
ERROR_HINT_MAP = {
|
||||
'q1': {
|
||||
"<type 'exceptions.IndexError'>": """
|
||||
We noticed that your project threw an IndexError on q1.
|
||||
While many things may cause this, it may have been from
|
||||
assuming a certain number of successors from a state space
|
||||
or assuming a certain number of actions available from a given
|
||||
state. Try making your code more general (no hardcoded indices)
|
||||
and submit again!
|
||||
"""
|
||||
},
|
||||
'q3': {
|
||||
"<type 'exceptions.AttributeError'>": """
|
||||
We noticed that your project threw an AttributeError on q3.
|
||||
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 line of code assuming that the state is (x, y) and we run your code
|
||||
on a state space with (x, y, z), this error could be thrown. Try
|
||||
making your code more general and submit again!
|
||||
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
import pprint
|
||||
|
||||
def splitStrings(d):
|
||||
d2 = dict(d)
|
||||
for k in d:
|
||||
if k[0:2] == "__":
|
||||
del d2[k]
|
||||
continue
|
||||
if d2[k].find("\n") >= 0:
|
||||
d2[k] = d2[k].split("\n")
|
||||
return d2
|
||||
|
||||
|
||||
def printTest(testDict, solutionDict):
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
print "Test case:"
|
||||
for line in testDict["__raw_lines__"]:
|
||||
print " |", line
|
||||
print "Solution:"
|
||||
for line in solutionDict["__raw_lines__"]:
|
||||
print " |", line
|
||||
|
||||
|
||||
def runTest(testName, moduleDict, printTestCase=False, display=None):
|
||||
import testParser
|
||||
import testClasses
|
||||
for module in moduleDict:
|
||||
setattr(sys.modules[__name__], module, moduleDict[module])
|
||||
|
||||
testDict = testParser.TestParser(testName + ".test").parse()
|
||||
solutionDict = testParser.TestParser(testName + ".solution").parse()
|
||||
test_out_file = os.path.join('%s.test_output' % testName)
|
||||
testDict['test_out_file'] = test_out_file
|
||||
testClass = getattr(projectTestClasses, testDict['class'])
|
||||
|
||||
questionClass = getattr(testClasses, 'Question')
|
||||
question = questionClass({'max_points': 0}, display)
|
||||
testCase = testClass(question, testDict)
|
||||
|
||||
if printTestCase:
|
||||
printTest(testDict, solutionDict)
|
||||
|
||||
# This is a fragile hack to create a stub grades object
|
||||
grades = grading.Grades(projectParams.PROJECT_NAME, [(None,0)])
|
||||
testCase.execute(grades, moduleDict, solutionDict)
|
||||
|
||||
|
||||
# returns all the tests you need to run in order to run question
|
||||
def getDepends(testParser, testRoot, question):
|
||||
allDeps = [question]
|
||||
questionDict = testParser.TestParser(os.path.join(testRoot, question, 'CONFIG')).parse()
|
||||
if 'depends' in questionDict:
|
||||
depends = questionDict['depends'].split()
|
||||
for d in depends:
|
||||
# run dependencies first
|
||||
allDeps = getDepends(testParser, testRoot, d) + allDeps
|
||||
return allDeps
|
||||
|
||||
# get list of questions to grade
|
||||
def getTestSubdirs(testParser, testRoot, questionToGrade):
|
||||
problemDict = testParser.TestParser(os.path.join(testRoot, 'CONFIG')).parse()
|
||||
if questionToGrade != None:
|
||||
questions = getDepends(testParser, testRoot, questionToGrade)
|
||||
if len(questions) > 1:
|
||||
print 'Note: due to dependencies, the following tests will be run: %s' % ' '.join(questions)
|
||||
return questions
|
||||
if 'order' in problemDict:
|
||||
return problemDict['order'].split()
|
||||
return sorted(os.listdir(testRoot))
|
||||
|
||||
|
||||
# evaluate student code
|
||||
def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MAP, edxOutput=False, muteOutput=False,
|
||||
printTestCase=False, questionToGrade=None, display=None):
|
||||
# imports of testbench code. note that the testClasses import must follow
|
||||
# the import of student code due to dependencies
|
||||
import testParser
|
||||
import testClasses
|
||||
for module in moduleDict:
|
||||
setattr(sys.modules[__name__], module, moduleDict[module])
|
||||
|
||||
questions = []
|
||||
questionDicts = {}
|
||||
test_subdirs = getTestSubdirs(testParser, testRoot, questionToGrade)
|
||||
for q in test_subdirs:
|
||||
subdir_path = os.path.join(testRoot, q)
|
||||
if not os.path.isdir(subdir_path) or q[0] == '.':
|
||||
continue
|
||||
|
||||
# create a question object
|
||||
questionDict = testParser.TestParser(os.path.join(subdir_path, 'CONFIG')).parse()
|
||||
questionClass = getattr(testClasses, questionDict['class'])
|
||||
question = questionClass(questionDict, display)
|
||||
questionDicts[q] = questionDict
|
||||
|
||||
# load test cases into question
|
||||
tests = filter(lambda t: re.match('[^#~.].*\.test\Z', t), os.listdir(subdir_path))
|
||||
tests = map(lambda t: re.match('(.*)\.test\Z', t).group(1), tests)
|
||||
for t in sorted(tests):
|
||||
test_file = os.path.join(subdir_path, '%s.test' % t)
|
||||
solution_file = os.path.join(subdir_path, '%s.solution' % t)
|
||||
test_out_file = os.path.join(subdir_path, '%s.test_output' % t)
|
||||
testDict = testParser.TestParser(test_file).parse()
|
||||
if testDict.get("disabled", "false").lower() == "true":
|
||||
continue
|
||||
testDict['test_out_file'] = test_out_file
|
||||
testClass = getattr(projectTestClasses, testDict['class'])
|
||||
testCase = testClass(question, testDict)
|
||||
def makefun(testCase, solution_file):
|
||||
if generateSolutions:
|
||||
# write solution file to disk
|
||||
return lambda grades: testCase.writeSolution(moduleDict, solution_file)
|
||||
else:
|
||||
# read in solution dictionary and pass as an argument
|
||||
testDict = testParser.TestParser(test_file).parse()
|
||||
solutionDict = testParser.TestParser(solution_file).parse()
|
||||
if printTestCase:
|
||||
return lambda grades: printTest(testDict, solutionDict) or testCase.execute(grades, moduleDict, solutionDict)
|
||||
else:
|
||||
return lambda grades: testCase.execute(grades, moduleDict, solutionDict)
|
||||
question.addTestCase(testCase, makefun(testCase, solution_file))
|
||||
|
||||
# Note extra function is necessary for scoping reasons
|
||||
def makefun(question):
|
||||
return lambda grades: question.execute(grades)
|
||||
setattr(sys.modules[__name__], q, makefun(question))
|
||||
questions.append((q, question.getMaxPoints()))
|
||||
|
||||
grades = grading.Grades(projectParams.PROJECT_NAME, questions, edxOutput=edxOutput, muteOutput=muteOutput)
|
||||
if questionToGrade == None:
|
||||
for q in questionDicts:
|
||||
for prereq in questionDicts[q].get('depends', '').split():
|
||||
grades.addPrereq(q, prereq)
|
||||
|
||||
grades.grade(sys.modules[__name__], bonusPic = projectParams.BONUS_PIC)
|
||||
return grades.points
|
||||
|
||||
|
||||
|
||||
def getDisplay(graphicsByDefault, options=None):
|
||||
graphics = graphicsByDefault
|
||||
if options is not None and options.noGraphics:
|
||||
graphics = False
|
||||
if graphics:
|
||||
try:
|
||||
import graphicsDisplay
|
||||
return graphicsDisplay.PacmanGraphics(1, frameTime=.05)
|
||||
except ImportError:
|
||||
pass
|
||||
import textDisplay
|
||||
return textDisplay.NullGraphics()
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
options = readCommand(sys.argv)
|
||||
if options.generateSolutions:
|
||||
confirmGenerate()
|
||||
codePaths = options.studentCode.split(',')
|
||||
# moduleCodeDict = {}
|
||||
# for cp in codePaths:
|
||||
# moduleName = re.match('.*?([^/]*)\.py', cp).group(1)
|
||||
# moduleCodeDict[moduleName] = readFile(cp, root=options.codeRoot)
|
||||
# moduleCodeDict['projectTestClasses'] = readFile(options.testCaseCode, root=options.codeRoot)
|
||||
# moduleDict = loadModuleDict(moduleCodeDict)
|
||||
|
||||
moduleDict = {}
|
||||
for cp in codePaths:
|
||||
moduleName = re.match('.*?([^/]*)\.py', cp).group(1)
|
||||
moduleDict[moduleName] = loadModuleFile(moduleName, os.path.join(options.codeRoot, cp))
|
||||
moduleName = re.match('.*?([^/]*)\.py', options.testCaseCode).group(1)
|
||||
moduleDict['projectTestClasses'] = loadModuleFile(moduleName, os.path.join(options.codeRoot, options.testCaseCode))
|
||||
|
||||
|
||||
if options.runTest != None:
|
||||
runTest(options.runTest, moduleDict, printTestCase=options.printTestCase, display=getDisplay(True, options))
|
||||
else:
|
||||
evaluate(options.generateSolutions, options.testRoot, moduleDict,
|
||||
edxOutput=options.edxOutput, muteOutput=options.muteOutput, printTestCase=options.printTestCase,
|
||||
questionToGrade=options.gradeQuestion, display=getDisplay(options.gradeQuestion!=None, options))
|
729
p2_multiagent/game.py
Normal file
729
p2_multiagent/game.py
Normal file
@ -0,0 +1,729 @@
|
||||
# game.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).
|
||||
|
||||
|
||||
# game.py
|
||||
# -------
|
||||
# Licensing Information: Please do not distribute or publish solutions to this
|
||||
# project. You are free to use and extend these projects for educational
|
||||
# purposes. The Pacman AI projects were developed at UC Berkeley, primarily by
|
||||
# John DeNero (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
|
||||
# For more info, see http://inst.eecs.berkeley.edu/~cs188/sp09/pacman.html
|
||||
|
||||
from util import *
|
||||
import time, os
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
#######################
|
||||
# Parts worth reading #
|
||||
#######################
|
||||
|
||||
class Agent:
|
||||
"""
|
||||
An agent must define a getAction method, but may also define the
|
||||
following methods which will be called if they exist:
|
||||
|
||||
def registerInitialState(self, state): # inspects the starting state
|
||||
"""
|
||||
def __init__(self, index=0):
|
||||
self.index = index
|
||||
|
||||
def getAction(self, state):
|
||||
"""
|
||||
The Agent will receive a GameState (from either {pacman, capture, sonar}.py) and
|
||||
must return an action from Directions.{North, South, East, West, Stop}
|
||||
"""
|
||||
raiseNotDefined()
|
||||
|
||||
class Directions:
|
||||
NORTH = 'North'
|
||||
SOUTH = 'South'
|
||||
EAST = 'East'
|
||||
WEST = 'West'
|
||||
STOP = 'Stop'
|
||||
|
||||
LEFT = {NORTH: WEST,
|
||||
SOUTH: EAST,
|
||||
EAST: NORTH,
|
||||
WEST: SOUTH,
|
||||
STOP: STOP}
|
||||
|
||||
RIGHT = dict([(y,x) for x, y in LEFT.items()])
|
||||
|
||||
REVERSE = {NORTH: SOUTH,
|
||||
SOUTH: NORTH,
|
||||
EAST: WEST,
|
||||
WEST: EAST,
|
||||
STOP: STOP}
|
||||
|
||||
class Configuration:
|
||||
"""
|
||||
A Configuration holds the (x,y) coordinate of a character, along with its
|
||||
traveling direction.
|
||||
|
||||
The convention for positions, like a graph, is that (0,0) is the lower left corner, x increases
|
||||
horizontally and y increases vertically. Therefore, north is the direction of increasing y, or (0,1).
|
||||
"""
|
||||
|
||||
def __init__(self, pos, direction):
|
||||
self.pos = pos
|
||||
self.direction = direction
|
||||
|
||||
def getPosition(self):
|
||||
return (self.pos)
|
||||
|
||||
def getDirection(self):
|
||||
return self.direction
|
||||
|
||||
def isInteger(self):
|
||||
x,y = self.pos
|
||||
return x == int(x) and y == int(y)
|
||||
|
||||
def __eq__(self, other):
|
||||
if other == None: return False
|
||||
return (self.pos == other.pos and self.direction == other.direction)
|
||||
|
||||
def __hash__(self):
|
||||
x = hash(self.pos)
|
||||
y = hash(self.direction)
|
||||
return hash(x + 13 * y)
|
||||
|
||||
def __str__(self):
|
||||
return "(x,y)="+str(self.pos)+", "+str(self.direction)
|
||||
|
||||
def generateSuccessor(self, vector):
|
||||
"""
|
||||
Generates a new configuration reached by translating the current
|
||||
configuration by the action vector. This is a low-level call and does
|
||||
not attempt to respect the legality of the movement.
|
||||
|
||||
Actions are movement vectors.
|
||||
"""
|
||||
x, y= self.pos
|
||||
dx, dy = vector
|
||||
direction = Actions.vectorToDirection(vector)
|
||||
if direction == Directions.STOP:
|
||||
direction = self.direction # There is no stop direction
|
||||
return Configuration((x + dx, y+dy), direction)
|
||||
|
||||
class AgentState:
|
||||
"""
|
||||
AgentStates hold the state of an agent (configuration, speed, scared, etc).
|
||||
"""
|
||||
|
||||
def __init__( self, startConfiguration, isPacman ):
|
||||
self.start = startConfiguration
|
||||
self.configuration = startConfiguration
|
||||
self.isPacman = isPacman
|
||||
self.scaredTimer = 0
|
||||
self.numCarrying = 0
|
||||
self.numReturned = 0
|
||||
|
||||
def __str__( self ):
|
||||
if self.isPacman:
|
||||
return "Pacman: " + str( self.configuration )
|
||||
else:
|
||||
return "Ghost: " + str( self.configuration )
|
||||
|
||||
def __eq__( self, other ):
|
||||
if other == None:
|
||||
return False
|
||||
return self.configuration == other.configuration and self.scaredTimer == other.scaredTimer
|
||||
|
||||
def __hash__(self):
|
||||
return hash(hash(self.configuration) + 13 * hash(self.scaredTimer))
|
||||
|
||||
def copy( self ):
|
||||
state = AgentState( self.start, self.isPacman )
|
||||
state.configuration = self.configuration
|
||||
state.scaredTimer = self.scaredTimer
|
||||
state.numCarrying = self.numCarrying
|
||||
state.numReturned = self.numReturned
|
||||
return state
|
||||
|
||||
def getPosition(self):
|
||||
if self.configuration == None: return None
|
||||
return self.configuration.getPosition()
|
||||
|
||||
def getDirection(self):
|
||||
return self.configuration.getDirection()
|
||||
|
||||
class Grid:
|
||||
"""
|
||||
A 2-dimensional array of objects backed by a list of lists. Data is accessed
|
||||
via grid[x][y] where (x,y) are positions on a Pacman map with x horizontal,
|
||||
y vertical and the origin (0,0) in the bottom left corner.
|
||||
|
||||
The __str__ method constructs an output that is oriented like a pacman board.
|
||||
"""
|
||||
def __init__(self, width, height, initialValue=False, bitRepresentation=None):
|
||||
if initialValue not in [False, True]: raise Exception('Grids can only contain booleans')
|
||||
self.CELLS_PER_INT = 30
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.data = [[initialValue for y in range(height)] for x in range(width)]
|
||||
if bitRepresentation:
|
||||
self._unpackBits(bitRepresentation)
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self.data[i]
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
self.data[key] = item
|
||||
|
||||
def __str__(self):
|
||||
out = [[str(self.data[x][y])[0] for x in range(self.width)] for y in range(self.height)]
|
||||
out.reverse()
|
||||
return '\n'.join([''.join(x) for x in out])
|
||||
|
||||
def __eq__(self, other):
|
||||
if other == None: return False
|
||||
return self.data == other.data
|
||||
|
||||
def __hash__(self):
|
||||
# return hash(str(self))
|
||||
base = 1
|
||||
h = 0
|
||||
for l in self.data:
|
||||
for i in l:
|
||||
if i:
|
||||
h += base
|
||||
base *= 2
|
||||
return hash(h)
|
||||
|
||||
def copy(self):
|
||||
g = Grid(self.width, self.height)
|
||||
g.data = [x[:] for x in self.data]
|
||||
return g
|
||||
|
||||
def deepCopy(self):
|
||||
return self.copy()
|
||||
|
||||
def shallowCopy(self):
|
||||
g = Grid(self.width, self.height)
|
||||
g.data = self.data
|
||||
return g
|
||||
|
||||
def count(self, item =True ):
|
||||
return sum([x.count(item) for x in self.data])
|
||||
|
||||
def asList(self, key = True):
|
||||
list = []
|
||||
for x in range(self.width):
|
||||
for y in range(self.height):
|
||||
if self[x][y] == key: list.append( (x,y) )
|
||||
return list
|
||||
|
||||
def packBits(self):
|
||||
"""
|
||||
Returns an efficient int list representation
|
||||
|
||||
(width, height, bitPackedInts...)
|
||||
"""
|
||||
bits = [self.width, self.height]
|
||||
currentInt = 0
|
||||
for i in range(self.height * self.width):
|
||||
bit = self.CELLS_PER_INT - (i % self.CELLS_PER_INT) - 1
|
||||
x, y = self._cellIndexToPosition(i)
|
||||
if self[x][y]:
|
||||
currentInt += 2 ** bit
|
||||
if (i + 1) % self.CELLS_PER_INT == 0:
|
||||
bits.append(currentInt)
|
||||
currentInt = 0
|
||||
bits.append(currentInt)
|
||||
return tuple(bits)
|
||||
|
||||
def _cellIndexToPosition(self, index):
|
||||
x = index / self.height
|
||||
y = index % self.height
|
||||
return x, y
|
||||
|
||||
def _unpackBits(self, bits):
|
||||
"""
|
||||
Fills in data from a bit-level representation
|
||||
"""
|
||||
cell = 0
|
||||
for packed in bits:
|
||||
for bit in self._unpackInt(packed, self.CELLS_PER_INT):
|
||||
if cell == self.width * self.height: break
|
||||
x, y = self._cellIndexToPosition(cell)
|
||||
self[x][y] = bit
|
||||
cell += 1
|
||||
|
||||
def _unpackInt(self, packed, size):
|
||||
bools = []
|
||||
if packed < 0: raise ValueError, "must be a positive integer"
|
||||
for i in range(size):
|
||||
n = 2 ** (self.CELLS_PER_INT - i - 1)
|
||||
if packed >= n:
|
||||
bools.append(True)
|
||||
packed -= n
|
||||
else:
|
||||
bools.append(False)
|
||||
return bools
|
||||
|
||||
def reconstituteGrid(bitRep):
|
||||
if type(bitRep) is not type((1,2)):
|
||||
return bitRep
|
||||
width, height = bitRep[:2]
|
||||
return Grid(width, height, bitRepresentation= bitRep[2:])
|
||||
|
||||
####################################
|
||||
# Parts you shouldn't have to read #
|
||||
####################################
|
||||
|
||||
class Actions:
|
||||
"""
|
||||
A collection of static methods for manipulating move actions.
|
||||
"""
|
||||
# Directions
|
||||
_directions = {Directions.NORTH: (0, 1),
|
||||
Directions.SOUTH: (0, -1),
|
||||
Directions.EAST: (1, 0),
|
||||
Directions.WEST: (-1, 0),
|
||||
Directions.STOP: (0, 0)}
|
||||
|
||||
_directionsAsList = _directions.items()
|
||||
|
||||
TOLERANCE = .001
|
||||
|
||||
def reverseDirection(action):
|
||||
if action == Directions.NORTH:
|
||||
return Directions.SOUTH
|
||||
if action == Directions.SOUTH:
|
||||
return Directions.NORTH
|
||||
if action == Directions.EAST:
|
||||
return Directions.WEST
|
||||
if action == Directions.WEST:
|
||||
return Directions.EAST
|
||||
return action
|
||||
reverseDirection = staticmethod(reverseDirection)
|
||||
|
||||
def vectorToDirection(vector):
|
||||
dx, dy = vector
|
||||
if dy > 0:
|
||||
return Directions.NORTH
|
||||
if dy < 0:
|
||||
return Directions.SOUTH
|
||||
if dx < 0:
|
||||
return Directions.WEST
|
||||
if dx > 0:
|
||||
return Directions.EAST
|
||||
return Directions.STOP
|
||||
vectorToDirection = staticmethod(vectorToDirection)
|
||||
|
||||
def directionToVector(direction, speed = 1.0):
|
||||
dx, dy = Actions._directions[direction]
|
||||
return (dx * speed, dy * speed)
|
||||
directionToVector = staticmethod(directionToVector)
|
||||
|
||||
def getPossibleActions(config, walls):
|
||||
possible = []
|
||||
x, y = config.pos
|
||||
x_int, y_int = int(x + 0.5), int(y + 0.5)
|
||||
|
||||
# In between grid points, all agents must continue straight
|
||||
if (abs(x - x_int) + abs(y - y_int) > Actions.TOLERANCE):
|
||||
return [config.getDirection()]
|
||||
|
||||
for dir, vec in Actions._directionsAsList:
|
||||
dx, dy = vec
|
||||
next_y = y_int + dy
|
||||
next_x = x_int + dx
|
||||
if not walls[next_x][next_y]: possible.append(dir)
|
||||
|
||||
return possible
|
||||
|
||||
getPossibleActions = staticmethod(getPossibleActions)
|
||||
|
||||
def getLegalNeighbors(position, walls):
|
||||
x,y = position
|
||||
x_int, y_int = int(x + 0.5), int(y + 0.5)
|
||||
neighbors = []
|
||||
for dir, vec in Actions._directionsAsList:
|
||||
dx, dy = vec
|
||||
next_x = x_int + dx
|
||||
if next_x < 0 or next_x == walls.width: continue
|
||||
next_y = y_int + dy
|
||||
if next_y < 0 or next_y == walls.height: continue
|
||||
if not walls[next_x][next_y]: neighbors.append((next_x, next_y))
|
||||
return neighbors
|
||||
getLegalNeighbors = staticmethod(getLegalNeighbors)
|
||||
|
||||
def getSuccessor(position, action):
|
||||
dx, dy = Actions.directionToVector(action)
|
||||
x, y = position
|
||||
return (x + dx, y + dy)
|
||||
getSuccessor = staticmethod(getSuccessor)
|
||||
|
||||
class GameStateData:
|
||||
"""
|
||||
|
||||
"""
|
||||
def __init__( self, prevState = None ):
|
||||
"""
|
||||
Generates a new data packet by copying information from its predecessor.
|
||||
"""
|
||||
if prevState != None:
|
||||
self.food = prevState.food.shallowCopy()
|
||||
self.capsules = prevState.capsules[:]
|
||||
self.agentStates = self.copyAgentStates( prevState.agentStates )
|
||||
self.layout = prevState.layout
|
||||
self._eaten = prevState._eaten
|
||||
self.score = prevState.score
|
||||
|
||||
self._foodEaten = None
|
||||
self._foodAdded = None
|
||||
self._capsuleEaten = None
|
||||
self._agentMoved = None
|
||||
self._lose = False
|
||||
self._win = False
|
||||
self.scoreChange = 0
|
||||
|
||||
def deepCopy( self ):
|
||||
state = GameStateData( self )
|
||||
state.food = self.food.deepCopy()
|
||||
state.layout = self.layout.deepCopy()
|
||||
state._agentMoved = self._agentMoved
|
||||
state._foodEaten = self._foodEaten
|
||||
state._foodAdded = self._foodAdded
|
||||
state._capsuleEaten = self._capsuleEaten
|
||||
return state
|
||||
|
||||
def copyAgentStates( self, agentStates ):
|
||||
copiedStates = []
|
||||
for agentState in agentStates:
|
||||
copiedStates.append( agentState.copy() )
|
||||
return copiedStates
|
||||
|
||||
def __eq__( self, other ):
|
||||
"""
|
||||
Allows two states to be compared.
|
||||
"""
|
||||
if other == None: return False
|
||||
# TODO Check for type of other
|
||||
if not self.agentStates == other.agentStates: return False
|
||||
if not self.food == other.food: return False
|
||||
if not self.capsules == other.capsules: return False
|
||||
if not self.score == other.score: return False
|
||||
return True
|
||||
|
||||
def __hash__( self ):
|
||||
"""
|
||||
Allows states to be keys of dictionaries.
|
||||
"""
|
||||
for i, state in enumerate( self.agentStates ):
|
||||
try:
|
||||
int(hash(state))
|
||||
except TypeError, e:
|
||||
print e
|
||||
#hash(state)
|
||||
return int((hash(tuple(self.agentStates)) + 13*hash(self.food) + 113* hash(tuple(self.capsules)) + 7 * hash(self.score)) % 1048575 )
|
||||
|
||||
def __str__( self ):
|
||||
width, height = self.layout.width, self.layout.height
|
||||
map = Grid(width, height)
|
||||
if type(self.food) == type((1,2)):
|
||||
self.food = reconstituteGrid(self.food)
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
food, walls = self.food, self.layout.walls
|
||||
map[x][y] = self._foodWallStr(food[x][y], walls[x][y])
|
||||
|
||||
for agentState in self.agentStates:
|
||||
if agentState == None: continue
|
||||
if agentState.configuration == None: continue
|
||||
x,y = [int( i ) for i in nearestPoint( agentState.configuration.pos )]
|
||||
agent_dir = agentState.configuration.direction
|
||||
if agentState.isPacman:
|
||||
map[x][y] = self._pacStr( agent_dir )
|
||||
else:
|
||||
map[x][y] = self._ghostStr( agent_dir )
|
||||
|
||||
for x, y in self.capsules:
|
||||
map[x][y] = 'o'
|
||||
|
||||
return str(map) + ("\nScore: %d\n" % self.score)
|
||||
|
||||
def _foodWallStr( self, hasFood, hasWall ):
|
||||
if hasFood:
|
||||
return '.'
|
||||
elif hasWall:
|
||||
return '%'
|
||||
else:
|
||||
return ' '
|
||||
|
||||
def _pacStr( self, dir ):
|
||||
if dir == Directions.NORTH:
|
||||
return 'v'
|
||||
if dir == Directions.SOUTH:
|
||||
return '^'
|
||||
if dir == Directions.WEST:
|
||||
return '>'
|
||||
return '<'
|
||||
|
||||
def _ghostStr( self, dir ):
|
||||
return 'G'
|
||||
if dir == Directions.NORTH:
|
||||
return 'M'
|
||||
if dir == Directions.SOUTH:
|
||||
return 'W'
|
||||
if dir == Directions.WEST:
|
||||
return '3'
|
||||
return 'E'
|
||||
|
||||
def initialize( self, layout, numGhostAgents ):
|
||||
"""
|
||||
Creates an initial game state from a layout array (see layout.py).
|
||||
"""
|
||||
self.food = layout.food.copy()
|
||||
#self.capsules = []
|
||||
self.capsules = layout.capsules[:]
|
||||
self.layout = layout
|
||||
self.score = 0
|
||||
self.scoreChange = 0
|
||||
|
||||
self.agentStates = []
|
||||
numGhosts = 0
|
||||
for isPacman, pos in layout.agentPositions:
|
||||
if not isPacman:
|
||||
if numGhosts == numGhostAgents: continue # Max ghosts reached already
|
||||
else: numGhosts += 1
|
||||
self.agentStates.append( AgentState( Configuration( pos, Directions.STOP), isPacman) )
|
||||
self._eaten = [False for a in self.agentStates]
|
||||
|
||||
try:
|
||||
import boinc
|
||||
_BOINC_ENABLED = True
|
||||
except:
|
||||
_BOINC_ENABLED = False
|
||||
|
||||
class Game:
|
||||
"""
|
||||
The Game manages the control flow, soliciting actions from agents.
|
||||
"""
|
||||
|
||||
def __init__( self, agents, display, rules, startingIndex=0, muteAgents=False, catchExceptions=False ):
|
||||
self.agentCrashed = False
|
||||
self.agents = agents
|
||||
self.display = display
|
||||
self.rules = rules
|
||||
self.startingIndex = startingIndex
|
||||
self.gameOver = False
|
||||
self.muteAgents = muteAgents
|
||||
self.catchExceptions = catchExceptions
|
||||
self.moveHistory = []
|
||||
self.totalAgentTimes = [0 for agent in agents]
|
||||
self.totalAgentTimeWarnings = [0 for agent in agents]
|
||||
self.agentTimeout = False
|
||||
import cStringIO
|
||||
self.agentOutput = [cStringIO.StringIO() for agent in agents]
|
||||
|
||||
def getProgress(self):
|
||||
if self.gameOver:
|
||||
return 1.0
|
||||
else:
|
||||
return self.rules.getProgress(self)
|
||||
|
||||
def _agentCrash( self, agentIndex, quiet=False):
|
||||
"Helper method for handling agent crashes"
|
||||
if not quiet: traceback.print_exc()
|
||||
self.gameOver = True
|
||||
self.agentCrashed = True
|
||||
self.rules.agentCrash(self, agentIndex)
|
||||
|
||||
OLD_STDOUT = None
|
||||
OLD_STDERR = None
|
||||
|
||||
def mute(self, agentIndex):
|
||||
if not self.muteAgents: return
|
||||
global OLD_STDOUT, OLD_STDERR
|
||||
import cStringIO
|
||||
OLD_STDOUT = sys.stdout
|
||||
OLD_STDERR = sys.stderr
|
||||
sys.stdout = self.agentOutput[agentIndex]
|
||||
sys.stderr = self.agentOutput[agentIndex]
|
||||
|
||||
def unmute(self):
|
||||
if not self.muteAgents: return
|
||||
global OLD_STDOUT, OLD_STDERR
|
||||
# Revert stdout/stderr to originals
|
||||
sys.stdout = OLD_STDOUT
|
||||
sys.stderr = OLD_STDERR
|
||||
|
||||
|
||||
def run( self ):
|
||||
"""
|
||||
Main control loop for game play.
|
||||
"""
|
||||
self.display.initialize(self.state.data)
|
||||
self.numMoves = 0
|
||||
|
||||
###self.display.initialize(self.state.makeObservation(1).data)
|
||||
# inform learning agents of the game start
|
||||
for i in range(len(self.agents)):
|
||||
agent = self.agents[i]
|
||||
if not agent:
|
||||
self.mute(i)
|
||||
# this is a null agent, meaning it failed to load
|
||||
# the other team wins
|
||||
print >>sys.stderr, "Agent %d failed to load" % i
|
||||
self.unmute()
|
||||
self._agentCrash(i, quiet=True)
|
||||
return
|
||||
if ("registerInitialState" in dir(agent)):
|
||||
self.mute(i)
|
||||
if self.catchExceptions:
|
||||
try:
|
||||
timed_func = TimeoutFunction(agent.registerInitialState, int(self.rules.getMaxStartupTime(i)))
|
||||
try:
|
||||
start_time = time.time()
|
||||
timed_func(self.state.deepCopy())
|
||||
time_taken = time.time() - start_time
|
||||
self.totalAgentTimes[i] += time_taken
|
||||
except TimeoutFunctionException:
|
||||
print >>sys.stderr, "Agent %d ran out of time on startup!" % i
|
||||
self.unmute()
|
||||
self.agentTimeout = True
|
||||
self._agentCrash(i, quiet=True)
|
||||
return
|
||||
except Exception,data:
|
||||
self._agentCrash(i, quiet=False)
|
||||
self.unmute()
|
||||
return
|
||||
else:
|
||||
agent.registerInitialState(self.state.deepCopy())
|
||||
## TODO: could this exceed the total time
|
||||
self.unmute()
|
||||
|
||||
agentIndex = self.startingIndex
|
||||
numAgents = len( self.agents )
|
||||
|
||||
while not self.gameOver:
|
||||
# Fetch the next agent
|
||||
agent = self.agents[agentIndex]
|
||||
move_time = 0
|
||||
skip_action = False
|
||||
# Generate an observation of the state
|
||||
if 'observationFunction' in dir( agent ):
|
||||
self.mute(agentIndex)
|
||||
if self.catchExceptions:
|
||||
try:
|
||||
timed_func = TimeoutFunction(agent.observationFunction, int(self.rules.getMoveTimeout(agentIndex)))
|
||||
try:
|
||||
start_time = time.time()
|
||||
observation = timed_func(self.state.deepCopy())
|
||||
except TimeoutFunctionException:
|
||||
skip_action = True
|
||||
move_time += time.time() - start_time
|
||||
self.unmute()
|
||||
except Exception,data:
|
||||
self._agentCrash(agentIndex, quiet=False)
|
||||
self.unmute()
|
||||
return
|
||||
else:
|
||||
observation = agent.observationFunction(self.state.deepCopy())
|
||||
self.unmute()
|
||||
else:
|
||||
observation = self.state.deepCopy()
|
||||
|
||||
# Solicit an action
|
||||
action = None
|
||||
self.mute(agentIndex)
|
||||
if self.catchExceptions:
|
||||
try:
|
||||
timed_func = TimeoutFunction(agent.getAction, int(self.rules.getMoveTimeout(agentIndex)) - int(move_time))
|
||||
try:
|
||||
start_time = time.time()
|
||||
if skip_action:
|
||||
raise TimeoutFunctionException()
|
||||
action = timed_func( observation )
|
||||
except TimeoutFunctionException:
|
||||
print >>sys.stderr, "Agent %d timed out on a single move!" % agentIndex
|
||||
self.agentTimeout = True
|
||||
self._agentCrash(agentIndex, quiet=True)
|
||||
self.unmute()
|
||||
return
|
||||
|
||||
move_time += time.time() - start_time
|
||||
|
||||
if move_time > self.rules.getMoveWarningTime(agentIndex):
|
||||
self.totalAgentTimeWarnings[agentIndex] += 1
|
||||
print >>sys.stderr, "Agent %d took too long to make a move! This is warning %d" % (agentIndex, self.totalAgentTimeWarnings[agentIndex])
|
||||
if self.totalAgentTimeWarnings[agentIndex] > self.rules.getMaxTimeWarnings(agentIndex):
|
||||
print >>sys.stderr, "Agent %d exceeded the maximum number of warnings: %d" % (agentIndex, self.totalAgentTimeWarnings[agentIndex])
|
||||
self.agentTimeout = True
|
||||
self._agentCrash(agentIndex, quiet=True)
|
||||
self.unmute()
|
||||
return
|
||||
|
||||
self.totalAgentTimes[agentIndex] += move_time
|
||||
#print "Agent: %d, time: %f, total: %f" % (agentIndex, move_time, self.totalAgentTimes[agentIndex])
|
||||
if self.totalAgentTimes[agentIndex] > self.rules.getMaxTotalTime(agentIndex):
|
||||
print >>sys.stderr, "Agent %d ran out of time! (time: %1.2f)" % (agentIndex, self.totalAgentTimes[agentIndex])
|
||||
self.agentTimeout = True
|
||||
self._agentCrash(agentIndex, quiet=True)
|
||||
self.unmute()
|
||||
return
|
||||
self.unmute()
|
||||
except Exception,data:
|
||||
self._agentCrash(agentIndex)
|
||||
self.unmute()
|
||||
return
|
||||
else:
|
||||
action = agent.getAction(observation)
|
||||
self.unmute()
|
||||
|
||||
# Execute the action
|
||||
self.moveHistory.append( (agentIndex, action) )
|
||||
if self.catchExceptions:
|
||||
try:
|
||||
self.state = self.state.generateSuccessor( agentIndex, action )
|
||||
except Exception,data:
|
||||
self.mute(agentIndex)
|
||||
self._agentCrash(agentIndex)
|
||||
self.unmute()
|
||||
return
|
||||
else:
|
||||
self.state = self.state.generateSuccessor( agentIndex, action )
|
||||
|
||||
# Change the display
|
||||
self.display.update( self.state.data )
|
||||
###idx = agentIndex - agentIndex % 2 + 1
|
||||
###self.display.update( self.state.makeObservation(idx).data )
|
||||
|
||||
# Allow for game specific conditions (winning, losing, etc.)
|
||||
self.rules.process(self.state, self)
|
||||
# Track progress
|
||||
if agentIndex == numAgents + 1: self.numMoves += 1
|
||||
# Next agent
|
||||
agentIndex = ( agentIndex + 1 ) % numAgents
|
||||
|
||||
if _BOINC_ENABLED:
|
||||
boinc.set_fraction_done(self.getProgress())
|
||||
|
||||
# inform a learning agent of the game result
|
||||
for agentIndex, agent in enumerate(self.agents):
|
||||
if "final" in dir( agent ) :
|
||||
try:
|
||||
self.mute(agentIndex)
|
||||
agent.final( self.state )
|
||||
self.unmute()
|
||||
except Exception,data:
|
||||
if not self.catchExceptions: raise
|
||||
self._agentCrash(agentIndex)
|
||||
self.unmute()
|
||||
return
|
||||
self.display.finish()
|
81
p2_multiagent/ghostAgents.py
Normal file
81
p2_multiagent/ghostAgents.py
Normal file
@ -0,0 +1,81 @@
|
||||
# ghostAgents.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 game import Agent
|
||||
from game import Actions
|
||||
from game import Directions
|
||||
import random
|
||||
from util import manhattanDistance
|
||||
import util
|
||||
|
||||
class GhostAgent( Agent ):
|
||||
def __init__( self, index ):
|
||||
self.index = index
|
||||
|
||||
def getAction( self, state ):
|
||||
dist = self.getDistribution(state)
|
||||
if len(dist) == 0:
|
||||
return Directions.STOP
|
||||
else:
|
||||
return util.chooseFromDistribution( dist )
|
||||
|
||||
def getDistribution(self, state):
|
||||
"Returns a Counter encoding a distribution over actions from the provided state."
|
||||
util.raiseNotDefined()
|
||||
|
||||
class RandomGhost( GhostAgent ):
|
||||
"A ghost that chooses a legal action uniformly at random."
|
||||
def getDistribution( self, state ):
|
||||
dist = util.Counter()
|
||||
for a in state.getLegalActions( self.index ): dist[a] = 1.0
|
||||
dist.normalize()
|
||||
return dist
|
||||
|
||||
class DirectionalGhost( GhostAgent ):
|
||||
"A ghost that prefers to rush Pacman, or flee when scared."
|
||||
def __init__( self, index, prob_attack=0.8, prob_scaredFlee=0.8 ):
|
||||
self.index = index
|
||||
self.prob_attack = prob_attack
|
||||
self.prob_scaredFlee = prob_scaredFlee
|
||||
|
||||
def getDistribution( self, state ):
|
||||
# Read variables from state
|
||||
ghostState = state.getGhostState( self.index )
|
||||
legalActions = state.getLegalActions( self.index )
|
||||
pos = state.getGhostPosition( self.index )
|
||||
isScared = ghostState.scaredTimer > 0
|
||||
|
||||
speed = 1
|
||||
if isScared: speed = 0.5
|
||||
|
||||
actionVectors = [Actions.directionToVector( a, speed ) for a in legalActions]
|
||||
newPositions = [( pos[0]+a[0], pos[1]+a[1] ) for a in actionVectors]
|
||||
pacmanPosition = state.getPacmanPosition()
|
||||
|
||||
# Select best actions given the state
|
||||
distancesToPacman = [manhattanDistance( pos, pacmanPosition ) for pos in newPositions]
|
||||
if isScared:
|
||||
bestScore = max( distancesToPacman )
|
||||
bestProb = self.prob_scaredFlee
|
||||
else:
|
||||
bestScore = min( distancesToPacman )
|
||||
bestProb = self.prob_attack
|
||||
bestActions = [action for action, distance in zip( legalActions, distancesToPacman ) if distance == bestScore]
|
||||
|
||||
# Construct distribution
|
||||
dist = util.Counter()
|
||||
for a in bestActions: dist[a] = bestProb / len(bestActions)
|
||||
for a in legalActions: dist[a] += ( 1-bestProb ) / len(legalActions)
|
||||
dist.normalize()
|
||||
return dist
|
282
p2_multiagent/grading.py
Normal file
282
p2_multiagent/grading.py
Normal file
@ -0,0 +1,282 @@
|
||||
# grading.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).
|
||||
|
||||
|
||||
"Common code for autograders"
|
||||
|
||||
import cgi
|
||||
import time
|
||||
import sys
|
||||
import traceback
|
||||
import pdb
|
||||
from collections import defaultdict
|
||||
import util
|
||||
|
||||
class Grades:
|
||||
"A data structure for project grades, along with formatting code to display them"
|
||||
def __init__(self, projectName, questionsAndMaxesList, 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.mute = muteOutput
|
||||
self.prereqs = defaultdict(set)
|
||||
|
||||
#print 'Autograder transcript for %s' % self.project
|
||||
print 'Starting on %d-%d at %d:%02d:%02d' % self.start
|
||||
|
||||
def addPrereq(self, question, prereq):
|
||||
self.prereqs[question].add(prereq)
|
||||
|
||||
def grade(self, gradingModule, exceptionMap = {}, bonusPic = False):
|
||||
"""
|
||||
Grades each question
|
||||
gradingModule: the module with all the grading functions (pass in with sys.modules[__name__])
|
||||
"""
|
||||
|
||||
completedQuestions = set([])
|
||||
for q in self.questions:
|
||||
print '\nQuestion %s' % q
|
||||
print '=' * (9 + len(q))
|
||||
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.
|
||||
""" % (prereq, q, q, prereq)
|
||||
continue
|
||||
|
||||
if self.mute: util.mutePrint()
|
||||
try:
|
||||
util.TimeoutFunction(getattr(gradingModule, q),300)(self) # Call the question's function
|
||||
#TimeoutFunction(getattr(gradingModule, q),1200)(self) # Call the question's function
|
||||
except Exception, inst:
|
||||
self.addExceptionMessage(q, inst, traceback)
|
||||
self.addErrorHints(exceptionMap, inst, q[1])
|
||||
except:
|
||||
self.fail('FAIL: Terminated with a string exception.')
|
||||
finally:
|
||||
if self.mute: util.unmutePrint()
|
||||
|
||||
if self.points[q] >= self.maxes[q]:
|
||||
completedQuestions.add(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=================="
|
||||
|
||||
for q in self.questions:
|
||||
print 'Question %s: %d/%d' % (q, self.points[q], self.maxes[q])
|
||||
print '------------------'
|
||||
print 'Total: %d/%d' % (self.points.totalCount(), sum(self.maxes.values()))
|
||||
if bonusPic and self.points.totalCount() == 25:
|
||||
print """
|
||||
|
||||
ALL HAIL GRANDPAC.
|
||||
LONG LIVE THE GHOSTBUSTING KING.
|
||||
|
||||
--- ---- ---
|
||||
| \ / + \ / |
|
||||
| + \--/ \--/ + |
|
||||
| + + |
|
||||
| + + + |
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
\ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
\ / @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
V \ @@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
\ / @@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
V @@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@
|
||||
/\ @@@@@@@@@@@@@@@@@@@@@@
|
||||
/ \ @@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
/\ / @@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
/ \ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@
|
||||
|
||||
"""
|
||||
print """
|
||||
Your grades are NOT yet registered. To register your grades, make sure
|
||||
to follow your instructor's guidelines to receive credit on your project.
|
||||
"""
|
||||
|
||||
if self.edxOutput:
|
||||
self.produceOutput()
|
||||
|
||||
def addExceptionMessage(self, q, inst, traceback):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
self.fail('FAIL: Exception raised: %s' % inst)
|
||||
self.addMessage('')
|
||||
for line in traceback.format_exc().split('\n'):
|
||||
self.addMessage(line)
|
||||
|
||||
def addErrorHints(self, exceptionMap, errorInstance, questionNum):
|
||||
typeOf = str(type(errorInstance))
|
||||
questionName = 'q' + questionNum
|
||||
errorHint = ''
|
||||
|
||||
# question specific error hints
|
||||
if exceptionMap.get(questionName):
|
||||
questionMap = exceptionMap.get(questionName)
|
||||
if (questionMap.get(typeOf)):
|
||||
errorHint = questionMap.get(typeOf)
|
||||
# fall back to general error messages if a question specific
|
||||
# one does not exist
|
||||
if (exceptionMap.get(typeOf)):
|
||||
errorHint = exceptionMap.get(typeOf)
|
||||
|
||||
# dont include the HTML if we have no error hint
|
||||
if not errorHint:
|
||||
return ''
|
||||
|
||||
for line in errorHint.split('\n'):
|
||||
self.addMessage(line)
|
||||
|
||||
def produceOutput(self):
|
||||
edxOutput = open('edx_response.html', 'w')
|
||||
edxOutput.write("<div>")
|
||||
|
||||
# first sum
|
||||
total_possible = sum(self.maxes.values())
|
||||
total_score = sum(self.points.values())
|
||||
checkOrX = '<span class="incorrect"/>'
|
||||
if (total_score >= total_possible):
|
||||
checkOrX = '<span class="correct"/>'
|
||||
header = """
|
||||
<h3>
|
||||
Total score ({total_score} / {total_possible})
|
||||
</h3>
|
||||
""".format(total_score = total_score,
|
||||
total_possible = total_possible,
|
||||
checkOrX = checkOrX
|
||||
)
|
||||
edxOutput.write(header)
|
||||
|
||||
for q in self.questions:
|
||||
if len(q) == 2:
|
||||
name = q[1]
|
||||
else:
|
||||
name = q
|
||||
checkOrX = '<span class="incorrect"/>'
|
||||
if (self.points[q] == self.maxes[q]):
|
||||
checkOrX = '<span class="correct"/>'
|
||||
#messages = '\n<br/>\n'.join(self.messages[q])
|
||||
messages = "<pre>%s</pre>" % '\n'.join(self.messages[q])
|
||||
output = """
|
||||
<div class="test">
|
||||
<section>
|
||||
<div class="shortform">
|
||||
Question {q} ({points}/{max}) {checkOrX}
|
||||
</div>
|
||||
<div class="longform">
|
||||
{messages}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
""".format(q = name,
|
||||
max = self.maxes[q],
|
||||
messages = messages,
|
||||
checkOrX = checkOrX,
|
||||
points = self.points[q]
|
||||
)
|
||||
# print "*** output for Question %s " % q[1]
|
||||
# print output
|
||||
edxOutput.write(output)
|
||||
edxOutput.write("</div>")
|
||||
edxOutput.close()
|
||||
edxOutput = open('edx_grade', 'w')
|
||||
edxOutput.write(str(self.points.totalCount()))
|
||||
edxOutput.close()
|
||||
|
||||
def fail(self, message, raw=False):
|
||||
"Sets sanity check bit to false and outputs a message"
|
||||
self.sane = False
|
||||
self.assignZeroCredit()
|
||||
self.addMessage(message, raw)
|
||||
|
||||
def assignZeroCredit(self):
|
||||
self.points[self.currentQuestion] = 0
|
||||
|
||||
def addPoints(self, amt):
|
||||
self.points[self.currentQuestion] += amt
|
||||
|
||||
def deductPoints(self, 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
return sum(self.values())
|
||||
|
679
p2_multiagent/graphicsDisplay.py
Normal file
679
p2_multiagent/graphicsDisplay.py
Normal file
@ -0,0 +1,679 @@
|
||||
# graphicsDisplay.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 graphicsUtils import *
|
||||
import math, time
|
||||
from game import Directions
|
||||
|
||||
###########################
|
||||
# GRAPHICS DISPLAY CODE #
|
||||
###########################
|
||||
|
||||
# Most code by Dan Klein and John Denero written or rewritten for cs188, UC Berkeley.
|
||||
# Some code from a Pacman implementation by LiveWires, and used / modified with permission.
|
||||
|
||||
DEFAULT_GRID_SIZE = 30.0
|
||||
INFO_PANE_HEIGHT = 35
|
||||
BACKGROUND_COLOR = formatColor(0,0,0)
|
||||
WALL_COLOR = formatColor(0.0/255.0, 51.0/255.0, 255.0/255.0)
|
||||
INFO_PANE_COLOR = formatColor(.4,.4,0)
|
||||
SCORE_COLOR = formatColor(.9, .9, .9)
|
||||
PACMAN_OUTLINE_WIDTH = 2
|
||||
PACMAN_CAPTURE_OUTLINE_WIDTH = 4
|
||||
|
||||
GHOST_COLORS = []
|
||||
GHOST_COLORS.append(formatColor(.9,0,0)) # Red
|
||||
GHOST_COLORS.append(formatColor(0,.3,.9)) # Blue
|
||||
GHOST_COLORS.append(formatColor(.98,.41,.07)) # Orange
|
||||
GHOST_COLORS.append(formatColor(.1,.75,.7)) # Green
|
||||
GHOST_COLORS.append(formatColor(1.0,0.6,0.0)) # Yellow
|
||||
GHOST_COLORS.append(formatColor(.4,0.13,0.91)) # Purple
|
||||
|
||||
TEAM_COLORS = GHOST_COLORS[:2]
|
||||
|
||||
GHOST_SHAPE = [
|
||||
( 0, 0.3 ),
|
||||
( 0.25, 0.75 ),
|
||||
( 0.5, 0.3 ),
|
||||
( 0.75, 0.75 ),
|
||||
( 0.75, -0.5 ),
|
||||
( 0.5, -0.75 ),
|
||||
(-0.5, -0.75 ),
|
||||
(-0.75, -0.5 ),
|
||||
(-0.75, 0.75 ),
|
||||
(-0.5, 0.3 ),
|
||||
(-0.25, 0.75 )
|
||||
]
|
||||
GHOST_SIZE = 0.65
|
||||
SCARED_COLOR = formatColor(1,1,1)
|
||||
|
||||
GHOST_VEC_COLORS = map(colorToVector, GHOST_COLORS)
|
||||
|
||||
PACMAN_COLOR = formatColor(255.0/255.0,255.0/255.0,61.0/255)
|
||||
PACMAN_SCALE = 0.5
|
||||
#pacman_speed = 0.25
|
||||
|
||||
# Food
|
||||
FOOD_COLOR = formatColor(1,1,1)
|
||||
FOOD_SIZE = 0.1
|
||||
|
||||
# Laser
|
||||
LASER_COLOR = formatColor(1,0,0)
|
||||
LASER_SIZE = 0.02
|
||||
|
||||
# Capsule graphics
|
||||
CAPSULE_COLOR = formatColor(1,1,1)
|
||||
CAPSULE_SIZE = 0.25
|
||||
|
||||
# Drawing walls
|
||||
WALL_RADIUS = 0.15
|
||||
|
||||
class InfoPane:
|
||||
def __init__(self, layout, gridSize):
|
||||
self.gridSize = gridSize
|
||||
self.width = (layout.width) * gridSize
|
||||
self.base = (layout.height + 1) * gridSize
|
||||
self.height = INFO_PANE_HEIGHT
|
||||
self.fontSize = 24
|
||||
self.textColor = PACMAN_COLOR
|
||||
self.drawPane()
|
||||
|
||||
def toScreen(self, pos, y = None):
|
||||
"""
|
||||
Translates a point relative from the bottom left of the info pane.
|
||||
"""
|
||||
if y == None:
|
||||
x,y = pos
|
||||
else:
|
||||
x = pos
|
||||
|
||||
x = self.gridSize + x # Margin
|
||||
y = self.base + y
|
||||
return x,y
|
||||
|
||||
def drawPane(self):
|
||||
self.scoreText = text( self.toScreen(0, 0 ), self.textColor, "SCORE: 0", "Times", self.fontSize, "bold")
|
||||
|
||||
def initializeGhostDistances(self, distances):
|
||||
self.ghostDistanceText = []
|
||||
|
||||
size = 20
|
||||
if self.width < 240:
|
||||
size = 12
|
||||
if self.width < 160:
|
||||
size = 10
|
||||
|
||||
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")
|
||||
self.ghostDistanceText.append(t)
|
||||
|
||||
def updateScore(self, score):
|
||||
changeText(self.scoreText, "SCORE: % 4d" % score)
|
||||
|
||||
def setTeam(self, isBlue):
|
||||
text = "RED TEAM"
|
||||
if isBlue: text = "BLUE TEAM"
|
||||
self.teamText = text( self.toScreen(300, 0 ), self.textColor, text, "Times", self.fontSize, "bold")
|
||||
|
||||
def updateGhostDistances(self, distances):
|
||||
if len(distances) == 0: return
|
||||
if 'ghostDistanceText' not in dir(self): self.initializeGhostDistances(distances)
|
||||
else:
|
||||
for i, d in enumerate(distances):
|
||||
changeText(self.ghostDistanceText[i], d)
|
||||
|
||||
def drawGhost(self):
|
||||
pass
|
||||
|
||||
def drawPacman(self):
|
||||
pass
|
||||
|
||||
def drawWarning(self):
|
||||
pass
|
||||
|
||||
def clearIcon(self):
|
||||
pass
|
||||
|
||||
def updateMessage(self, message):
|
||||
pass
|
||||
|
||||
def clearMessage(self):
|
||||
pass
|
||||
|
||||
|
||||
class PacmanGraphics:
|
||||
def __init__(self, zoom=1.0, frameTime=0.0, capture=False):
|
||||
self.have_window = 0
|
||||
self.currentGhostImages = {}
|
||||
self.pacmanImage = None
|
||||
self.zoom = zoom
|
||||
self.gridSize = DEFAULT_GRID_SIZE * zoom
|
||||
self.capture = capture
|
||||
self.frameTime = frameTime
|
||||
|
||||
def checkNullDisplay(self):
|
||||
return False
|
||||
|
||||
def initialize(self, state, isBlue = False):
|
||||
self.isBlue = isBlue
|
||||
self.startGraphics(state)
|
||||
|
||||
# self.drawDistributions(state)
|
||||
self.distributionImages = None # Initialized lazily
|
||||
self.drawStaticObjects(state)
|
||||
self.drawAgentObjects(state)
|
||||
|
||||
# Information
|
||||
self.previousState = state
|
||||
|
||||
def startGraphics(self, state):
|
||||
self.layout = state.layout
|
||||
layout = self.layout
|
||||
self.width = layout.width
|
||||
self.height = layout.height
|
||||
self.make_window(self.width, self.height)
|
||||
self.infoPane = InfoPane(layout, self.gridSize)
|
||||
self.currentState = layout
|
||||
|
||||
def drawDistributions(self, state):
|
||||
walls = state.layout.walls
|
||||
dist = []
|
||||
for x in range(walls.width):
|
||||
distx = []
|
||||
dist.append(distx)
|
||||
for y in range(walls.height):
|
||||
( screen_x, screen_y ) = self.to_screen( (x, y) )
|
||||
block = square( (screen_x, screen_y),
|
||||
0.5 * self.gridSize,
|
||||
color = BACKGROUND_COLOR,
|
||||
filled = 1, behind=2)
|
||||
distx.append(block)
|
||||
self.distributionImages = dist
|
||||
|
||||
def drawStaticObjects(self, state):
|
||||
layout = self.layout
|
||||
self.drawWalls(layout.walls)
|
||||
self.food = self.drawFood(layout.food)
|
||||
self.capsules = self.drawCapsules(layout.capsules)
|
||||
refresh()
|
||||
|
||||
def drawAgentObjects(self, state):
|
||||
self.agentImages = [] # (agentState, image)
|
||||
for index, agent in enumerate(state.agentStates):
|
||||
if agent.isPacman:
|
||||
image = self.drawPacman(agent, index)
|
||||
self.agentImages.append( (agent, image) )
|
||||
else:
|
||||
image = self.drawGhost(agent, index)
|
||||
self.agentImages.append( (agent, image) )
|
||||
refresh()
|
||||
|
||||
def swapImages(self, agentIndex, newState):
|
||||
"""
|
||||
Changes an image from a ghost to a pacman or vis versa (for capture)
|
||||
"""
|
||||
prevState, prevImage = self.agentImages[agentIndex]
|
||||
for item in prevImage: remove_from_screen(item)
|
||||
if newState.isPacman:
|
||||
image = self.drawPacman(newState, agentIndex)
|
||||
self.agentImages[agentIndex] = (newState, image )
|
||||
else:
|
||||
image = self.drawGhost(newState, agentIndex)
|
||||
self.agentImages[agentIndex] = (newState, image )
|
||||
refresh()
|
||||
|
||||
def update(self, newState):
|
||||
agentIndex = newState._agentMoved
|
||||
agentState = newState.agentStates[agentIndex]
|
||||
|
||||
if self.agentImages[agentIndex][0].isPacman != agentState.isPacman: self.swapImages(agentIndex, agentState)
|
||||
prevState, prevImage = self.agentImages[agentIndex]
|
||||
if agentState.isPacman:
|
||||
self.animatePacman(agentState, prevState, prevImage)
|
||||
else:
|
||||
self.moveGhost(agentState, agentIndex, prevState, prevImage)
|
||||
self.agentImages[agentIndex] = (agentState, prevImage)
|
||||
|
||||
if newState._foodEaten != None:
|
||||
self.removeFood(newState._foodEaten, self.food)
|
||||
if newState._capsuleEaten != None:
|
||||
self.removeCapsule(newState._capsuleEaten, self.capsules)
|
||||
self.infoPane.updateScore(newState.score)
|
||||
if 'ghostDistances' in dir(newState):
|
||||
self.infoPane.updateGhostDistances(newState.ghostDistances)
|
||||
|
||||
def make_window(self, width, height):
|
||||
grid_width = (width-1) * self.gridSize
|
||||
grid_height = (height-1) * self.gridSize
|
||||
screen_width = 2*self.gridSize + grid_width
|
||||
screen_height = 2*self.gridSize + grid_height + INFO_PANE_HEIGHT
|
||||
|
||||
begin_graphics(screen_width,
|
||||
screen_height,
|
||||
BACKGROUND_COLOR,
|
||||
"CS188 Pacman")
|
||||
|
||||
def drawPacman(self, pacman, index):
|
||||
position = self.getPosition(pacman)
|
||||
screen_point = self.to_screen(position)
|
||||
endpoints = self.getEndpoints(self.getDirection(pacman))
|
||||
|
||||
width = PACMAN_OUTLINE_WIDTH
|
||||
outlineColor = PACMAN_COLOR
|
||||
fillColor = PACMAN_COLOR
|
||||
|
||||
if self.capture:
|
||||
outlineColor = TEAM_COLORS[index % 2]
|
||||
fillColor = GHOST_COLORS[index]
|
||||
width = PACMAN_CAPTURE_OUTLINE_WIDTH
|
||||
|
||||
return [circle(screen_point, PACMAN_SCALE * self.gridSize,
|
||||
fillColor = fillColor, outlineColor = outlineColor,
|
||||
endpoints = endpoints,
|
||||
width = width)]
|
||||
|
||||
def getEndpoints(self, direction, position=(0,0)):
|
||||
x, y = position
|
||||
pos = x - int(x) + y - int(y)
|
||||
width = 30 + 80 * math.sin(math.pi* pos)
|
||||
|
||||
delta = width / 2
|
||||
if (direction == 'West'):
|
||||
endpoints = (180+delta, 180-delta)
|
||||
elif (direction == 'North'):
|
||||
endpoints = (90+delta, 90-delta)
|
||||
elif (direction == 'South'):
|
||||
endpoints = (270+delta, 270-delta)
|
||||
else:
|
||||
endpoints = (0+delta, 0-delta)
|
||||
return endpoints
|
||||
|
||||
def movePacman(self, position, direction, image):
|
||||
screenPosition = self.to_screen(position)
|
||||
endpoints = self.getEndpoints( direction, position )
|
||||
r = PACMAN_SCALE * self.gridSize
|
||||
moveCircle(image[0], screenPosition, r, endpoints)
|
||||
refresh()
|
||||
|
||||
def animatePacman(self, pacman, prevPacman, image):
|
||||
if self.frameTime < 0:
|
||||
print 'Press any key to step forward, "q" to play'
|
||||
keys = wait_for_keys()
|
||||
if 'q' in keys:
|
||||
self.frameTime = 0.1
|
||||
if self.frameTime > 0.01 or self.frameTime < 0:
|
||||
start = time.time()
|
||||
fx, fy = self.getPosition(prevPacman)
|
||||
px, py = self.getPosition(pacman)
|
||||
frames = 4.0
|
||||
for i in range(1,int(frames) + 1):
|
||||
pos = px*i/frames + fx*(frames-i)/frames, py*i/frames + fy*(frames-i)/frames
|
||||
self.movePacman(pos, self.getDirection(pacman), image)
|
||||
refresh()
|
||||
sleep(abs(self.frameTime) / frames)
|
||||
else:
|
||||
self.movePacman(self.getPosition(pacman), self.getDirection(pacman), image)
|
||||
refresh()
|
||||
|
||||
def getGhostColor(self, ghost, ghostIndex):
|
||||
if ghost.scaredTimer > 0:
|
||||
return SCARED_COLOR
|
||||
else:
|
||||
return GHOST_COLORS[ghostIndex]
|
||||
|
||||
def drawGhost(self, ghost, agentIndex):
|
||||
pos = self.getPosition(ghost)
|
||||
dir = self.getDirection(ghost)
|
||||
(screen_x, screen_y) = (self.to_screen(pos) )
|
||||
coords = []
|
||||
for (x, y) in GHOST_SHAPE:
|
||||
coords.append((x*self.gridSize*GHOST_SIZE + screen_x, y*self.gridSize*GHOST_SIZE + screen_y))
|
||||
|
||||
colour = self.getGhostColor(ghost, agentIndex)
|
||||
body = polygon(coords, colour, filled = 1)
|
||||
WHITE = formatColor(1.0, 1.0, 1.0)
|
||||
BLACK = formatColor(0.0, 0.0, 0.0)
|
||||
|
||||
dx = 0
|
||||
dy = 0
|
||||
if dir == 'North':
|
||||
dy = -0.2
|
||||
if dir == 'South':
|
||||
dy = 0.2
|
||||
if dir == 'East':
|
||||
dx = 0.2
|
||||
if dir == 'West':
|
||||
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)
|
||||
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)
|
||||
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.append(body)
|
||||
ghostImageParts.append(leftEye)
|
||||
ghostImageParts.append(rightEye)
|
||||
ghostImageParts.append(leftPupil)
|
||||
ghostImageParts.append(rightPupil)
|
||||
|
||||
return ghostImageParts
|
||||
|
||||
def moveEyes(self, pos, dir, eyes):
|
||||
(screen_x, screen_y) = (self.to_screen(pos) )
|
||||
dx = 0
|
||||
dy = 0
|
||||
if dir == 'North':
|
||||
dy = -0.2
|
||||
if dir == 'South':
|
||||
dy = 0.2
|
||||
if dir == 'East':
|
||||
dx = 0.2
|
||||
if dir == 'West':
|
||||
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[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)
|
||||
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):
|
||||
old_x, old_y = self.to_screen(self.getPosition(prevGhost))
|
||||
new_x, new_y = self.to_screen(self.getPosition(ghost))
|
||||
delta = new_x - old_x, new_y - old_y
|
||||
|
||||
for ghostImagePart in ghostImageParts:
|
||||
move_by(ghostImagePart, delta)
|
||||
refresh()
|
||||
|
||||
if ghost.scaredTimer > 0:
|
||||
color = SCARED_COLOR
|
||||
else:
|
||||
color = GHOST_COLORS[ghostIndex]
|
||||
edit(ghostImageParts[0], ('fill', color), ('outline', color))
|
||||
self.moveEyes(self.getPosition(ghost), self.getDirection(ghost), ghostImageParts[-4:])
|
||||
refresh()
|
||||
|
||||
def getPosition(self, agentState):
|
||||
if agentState.configuration == None: return (-1000, -1000)
|
||||
return agentState.getPosition()
|
||||
|
||||
def getDirection(self, agentState):
|
||||
if agentState.configuration == None: return Directions.STOP
|
||||
return agentState.configuration.getDirection()
|
||||
|
||||
def finish(self):
|
||||
end_graphics()
|
||||
|
||||
def to_screen(self, point):
|
||||
( x, y ) = point
|
||||
#y = self.height - y
|
||||
x = (x + 1)*self.gridSize
|
||||
y = (self.height - y)*self.gridSize
|
||||
return ( x, y )
|
||||
|
||||
# Fixes some TK issue with off-center circles
|
||||
def to_screen2(self, point):
|
||||
( x, y ) = point
|
||||
#y = self.height - y
|
||||
x = (x + 1)*self.gridSize
|
||||
y = (self.height - y)*self.gridSize
|
||||
return ( x, y )
|
||||
|
||||
def drawWalls(self, wallMatrix):
|
||||
wallColor = WALL_COLOR
|
||||
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: wallColor = TEAM_COLORS[1]
|
||||
|
||||
for yNum, cell in enumerate(x):
|
||||
if cell: # There's a wall here
|
||||
pos = (xNum, yNum)
|
||||
screen = self.to_screen(pos)
|
||||
screen2 = self.to_screen2(pos)
|
||||
|
||||
# draw each quadrant of the square based on adjacent walls
|
||||
wIsWall = self.isWall(xNum-1, yNum, wallMatrix)
|
||||
eIsWall = self.isWall(xNum+1, yNum, wallMatrix)
|
||||
nIsWall = self.isWall(xNum, yNum+1, wallMatrix)
|
||||
sIsWall = self.isWall(xNum, yNum-1, wallMatrix)
|
||||
nwIsWall = self.isWall(xNum-1, yNum+1, wallMatrix)
|
||||
swIsWall = self.isWall(xNum-1, yNum-1, wallMatrix)
|
||||
neIsWall = self.isWall(xNum+1, yNum+1, wallMatrix)
|
||||
seIsWall = self.isWall(xNum+1, yNum-1, wallMatrix)
|
||||
|
||||
# NE quadrant
|
||||
if (not nIsWall) and (not eIsWall):
|
||||
# inner circle
|
||||
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (0,91), 'arc')
|
||||
if (nIsWall) and (not eIsWall):
|
||||
# vertical line
|
||||
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):
|
||||
# horizontal line
|
||||
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):
|
||||
# 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')
|
||||
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
|
||||
if (not nIsWall) and (not wIsWall):
|
||||
# inner circle
|
||||
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (90,181), 'arc')
|
||||
if (nIsWall) and (not wIsWall):
|
||||
# vertical line
|
||||
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):
|
||||
# horizontal line
|
||||
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):
|
||||
# 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')
|
||||
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
|
||||
if (not sIsWall) and (not eIsWall):
|
||||
# inner circle
|
||||
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (270,361), 'arc')
|
||||
if (sIsWall) and (not eIsWall):
|
||||
# vertical line
|
||||
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):
|
||||
# horizontal line
|
||||
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):
|
||||
# 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')
|
||||
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
|
||||
if (not sIsWall) and (not wIsWall):
|
||||
# inner circle
|
||||
circle(screen2, WALL_RADIUS * self.gridSize, wallColor, wallColor, (180,271), 'arc')
|
||||
if (sIsWall) and (not wIsWall):
|
||||
# vertical line
|
||||
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):
|
||||
# horizontal line
|
||||
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):
|
||||
# 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')
|
||||
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):
|
||||
if x < 0 or y < 0:
|
||||
return False
|
||||
if x >= walls.width or y >= walls.height:
|
||||
return False
|
||||
return walls[x][y]
|
||||
|
||||
def drawFood(self, foodMatrix ):
|
||||
foodImages = []
|
||||
color = FOOD_COLOR
|
||||
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: color = TEAM_COLORS[1]
|
||||
imageRow = []
|
||||
foodImages.append(imageRow)
|
||||
for yNum, cell in enumerate(x):
|
||||
if cell: # There's food here
|
||||
screen = self.to_screen((xNum, yNum ))
|
||||
dot = circle( screen,
|
||||
FOOD_SIZE * self.gridSize,
|
||||
outlineColor = color, fillColor = color,
|
||||
width = 1)
|
||||
imageRow.append(dot)
|
||||
else:
|
||||
imageRow.append(None)
|
||||
return foodImages
|
||||
|
||||
def drawCapsules(self, capsules ):
|
||||
capsuleImages = {}
|
||||
for capsule in capsules:
|
||||
( screen_x, screen_y ) = self.to_screen(capsule)
|
||||
dot = circle( (screen_x, screen_y),
|
||||
CAPSULE_SIZE * self.gridSize,
|
||||
outlineColor = CAPSULE_COLOR,
|
||||
fillColor = CAPSULE_COLOR,
|
||||
width = 1)
|
||||
capsuleImages[capsule] = dot
|
||||
return capsuleImages
|
||||
|
||||
def removeFood(self, cell, foodImages ):
|
||||
x, y = cell
|
||||
remove_from_screen(foodImages[x][y])
|
||||
|
||||
def removeCapsule(self, cell, capsuleImages ):
|
||||
x, y = cell
|
||||
remove_from_screen(capsuleImages[(x, y)])
|
||||
|
||||
def drawExpandedCells(self, cells):
|
||||
"""
|
||||
Draws an overlay of expanded grid positions for search agents
|
||||
"""
|
||||
n = float(len(cells))
|
||||
baseColor = [1.0, 0.0, 0.0]
|
||||
self.clearExpandedCells()
|
||||
self.expandedCells = []
|
||||
for k, cell in enumerate(cells):
|
||||
screenPos = self.to_screen( cell)
|
||||
cellColor = formatColor(*[(n-k) * c * .5 / n + .25 for c in baseColor])
|
||||
block = square(screenPos,
|
||||
0.5 * self.gridSize,
|
||||
color = cellColor,
|
||||
filled = 1, behind=2)
|
||||
self.expandedCells.append(block)
|
||||
if self.frameTime < 0:
|
||||
refresh()
|
||||
|
||||
def clearExpandedCells(self):
|
||||
if 'expandedCells' in dir(self) and len(self.expandedCells) > 0:
|
||||
for cell in self.expandedCells:
|
||||
remove_from_screen(cell)
|
||||
|
||||
|
||||
def updateDistributions(self, distributions):
|
||||
"Draws an agent's belief distributions"
|
||||
# copy all distributions so we don't change their state
|
||||
distributions = map(lambda x: x.copy(), distributions)
|
||||
if self.distributionImages == None:
|
||||
self.drawDistributions(self.previousState)
|
||||
for x in range(len(self.distributionImages)):
|
||||
for y in range(len(self.distributionImages[0])):
|
||||
image = self.distributionImages[x][y]
|
||||
weights = [dist[ (x,y) ] for dist in distributions]
|
||||
|
||||
if sum(weights) != 0:
|
||||
pass
|
||||
# Fog of war
|
||||
color = [0.0,0.0,0.0]
|
||||
colors = GHOST_VEC_COLORS[1:] # With Pacman
|
||||
if self.capture: colors = GHOST_VEC_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)]
|
||||
changeColor(image, formatColor(*color))
|
||||
refresh()
|
||||
|
||||
class FirstPersonPacmanGraphics(PacmanGraphics):
|
||||
def __init__(self, zoom = 1.0, showGhosts = True, capture = False, frameTime=0):
|
||||
PacmanGraphics.__init__(self, zoom, frameTime=frameTime)
|
||||
self.showGhosts = showGhosts
|
||||
self.capture = capture
|
||||
|
||||
def initialize(self, state, isBlue = False):
|
||||
|
||||
self.isBlue = isBlue
|
||||
PacmanGraphics.startGraphics(self, state)
|
||||
# Initialize distribution images
|
||||
walls = state.layout.walls
|
||||
dist = []
|
||||
self.layout = state.layout
|
||||
|
||||
# Draw the rest
|
||||
self.distributionImages = None # initialize lazily
|
||||
self.drawStaticObjects(state)
|
||||
self.drawAgentObjects(state)
|
||||
|
||||
# Information
|
||||
self.previousState = state
|
||||
|
||||
def lookAhead(self, config, state):
|
||||
if config.getDirection() == 'Stop':
|
||||
return
|
||||
else:
|
||||
pass
|
||||
# Draw relevant ghosts
|
||||
allGhosts = state.getGhostStates()
|
||||
visibleGhosts = state.getVisibleGhosts()
|
||||
for i, ghost in enumerate(allGhosts):
|
||||
if ghost in visibleGhosts:
|
||||
self.drawGhost(ghost, i)
|
||||
else:
|
||||
self.currentGhostImages[i] = None
|
||||
|
||||
def getGhostColor(self, ghost, ghostIndex):
|
||||
return GHOST_COLORS[ghostIndex]
|
||||
|
||||
def getPosition(self, ghostState):
|
||||
if not self.showGhosts and not ghostState.isPacman and ghostState.getPosition()[1] > 1:
|
||||
return (-1000, -1000)
|
||||
else:
|
||||
return PacmanGraphics.getPosition(self, ghostState)
|
||||
|
||||
def add(x, y):
|
||||
return (x[0] + y[0], x[1] + y[1])
|
||||
|
||||
|
||||
# Saving graphical output
|
||||
# -----------------------
|
||||
# Note: to make an animated gif from this postscript output, try the command:
|
||||
# convert -delay 7 -loop 1 -compress lzw -layers optimize frame* out.gif
|
||||
# convert is part of imagemagick (freeware)
|
||||
|
||||
SAVE_POSTSCRIPT = False
|
||||
POSTSCRIPT_OUTPUT_DIR = 'frames'
|
||||
FRAME_NUMBER = 0
|
||||
import os
|
||||
|
||||
def saveFrame():
|
||||
"Saves the current graphical output as a postscript file"
|
||||
global SAVE_POSTSCRIPT, FRAME_NUMBER, POSTSCRIPT_OUTPUT_DIR
|
||||
if not SAVE_POSTSCRIPT: 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)
|
||||
FRAME_NUMBER += 1
|
||||
writePostscript(name) # writes the current canvas
|
398
p2_multiagent/graphicsUtils.py
Normal file
398
p2_multiagent/graphicsUtils.py
Normal file
@ -0,0 +1,398 @@
|
||||
# graphicsUtils.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 math
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
import types
|
||||
import Tkinter
|
||||
|
||||
_Windows = sys.platform == 'win32' # True if on Win95/98/NT
|
||||
|
||||
_root_window = None # The root window for graphics output
|
||||
_canvas = None # The canvas which holds graphics
|
||||
_canvas_xs = None # Size of canvas object
|
||||
_canvas_ys = None
|
||||
_canvas_x = None # Current position on canvas
|
||||
_canvas_y = None
|
||||
_canvas_col = None # Current colour (set to black below)
|
||||
_canvas_tsize = 12
|
||||
_canvas_tserifs = 0
|
||||
|
||||
def formatColor(r, g, b):
|
||||
return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255))
|
||||
|
||||
def colorToVector(color):
|
||||
return map(lambda x: int(x, 16) / 256.0, [color[1:3], color[3:5], color[5:7]])
|
||||
|
||||
if _Windows:
|
||||
_canvas_tfonts = ['times new roman', 'lucida console']
|
||||
else:
|
||||
_canvas_tfonts = ['times', 'lucidasans-24']
|
||||
pass # XXX need defaults here
|
||||
|
||||
def sleep(secs):
|
||||
global _root_window
|
||||
if _root_window == None:
|
||||
time.sleep(secs)
|
||||
else:
|
||||
_root_window.update_idletasks()
|
||||
_root_window.after(int(1000 * secs), _root_window.quit)
|
||||
_root_window.mainloop()
|
||||
|
||||
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
|
||||
|
||||
# Check for duplicate call
|
||||
if _root_window is not None:
|
||||
# Lose the window.
|
||||
_root_window.destroy()
|
||||
|
||||
# Save the canvas size parameters
|
||||
_canvas_xs, _canvas_ys = width - 1, height - 1
|
||||
_canvas_x, _canvas_y = 0, _canvas_ys
|
||||
_bg_color = color
|
||||
|
||||
# Create the root window
|
||||
_root_window = Tkinter.Tk()
|
||||
_root_window.protocol('WM_DELETE_WINDOW', _destroy_window)
|
||||
_root_window.title(title or 'Graphics Window')
|
||||
_root_window.resizable(0, 0)
|
||||
|
||||
# Create the canvas object
|
||||
try:
|
||||
_canvas = Tkinter.Canvas(_root_window, width=width, height=height)
|
||||
_canvas.pack()
|
||||
draw_background()
|
||||
_canvas.update()
|
||||
except:
|
||||
_root_window = None
|
||||
raise
|
||||
|
||||
# Bind to key-down and key-up events
|
||||
_root_window.bind( "<KeyPress>", _keypress )
|
||||
_root_window.bind( "<KeyRelease>", _keyrelease )
|
||||
_root_window.bind( "<FocusIn>", _clear_keys )
|
||||
_root_window.bind( "<FocusOut>", _clear_keys )
|
||||
_root_window.bind( "<Button-1>", _leftclick )
|
||||
_root_window.bind( "<Button-2>", _rightclick )
|
||||
_root_window.bind( "<Button-3>", _rightclick )
|
||||
_root_window.bind( "<Control-Button-1>", _ctrl_leftclick)
|
||||
_clear_keys()
|
||||
|
||||
_leftclick_loc = None
|
||||
_rightclick_loc = None
|
||||
_ctrl_leftclick_loc = None
|
||||
|
||||
def _leftclick(event):
|
||||
global _leftclick_loc
|
||||
_leftclick_loc = (event.x, event.y)
|
||||
|
||||
def _rightclick(event):
|
||||
global _rightclick_loc
|
||||
_rightclick_loc = (event.x, event.y)
|
||||
|
||||
def _ctrl_leftclick(event):
|
||||
global _ctrl_leftclick_loc
|
||||
_ctrl_leftclick_loc = (event.x, event.y)
|
||||
|
||||
def wait_for_click():
|
||||
while True:
|
||||
global _leftclick_loc
|
||||
global _rightclick_loc
|
||||
global _ctrl_leftclick_loc
|
||||
if _leftclick_loc != None:
|
||||
val = _leftclick_loc
|
||||
_leftclick_loc = None
|
||||
return val, 'left'
|
||||
if _rightclick_loc != None:
|
||||
val = _rightclick_loc
|
||||
_rightclick_loc = None
|
||||
return val, 'right'
|
||||
if _ctrl_leftclick_loc != None:
|
||||
val = _ctrl_leftclick_loc
|
||||
_ctrl_leftclick_loc = None
|
||||
return val, 'ctrl_left'
|
||||
sleep(0.05)
|
||||
|
||||
def draw_background():
|
||||
corners = [(0,0), (0, _canvas_ys), (_canvas_xs, _canvas_ys), (_canvas_xs, 0)]
|
||||
polygon(corners, _bg_color, fillColor=_bg_color, filled=True, smoothed=False)
|
||||
|
||||
def _destroy_window(event=None):
|
||||
sys.exit(0)
|
||||
# global _root_window
|
||||
# _root_window.destroy()
|
||||
# _root_window = None
|
||||
#print "DESTROY"
|
||||
|
||||
def end_graphics():
|
||||
global _root_window, _canvas, _mouse_enabled
|
||||
try:
|
||||
try:
|
||||
sleep(1)
|
||||
if _root_window != None:
|
||||
_root_window.destroy()
|
||||
except SystemExit, e:
|
||||
print 'Ending graphics raised an exception:', e
|
||||
finally:
|
||||
_root_window = None
|
||||
_canvas = None
|
||||
_mouse_enabled = 0
|
||||
_clear_keys()
|
||||
|
||||
def clear_screen(background=None):
|
||||
global _canvas_x, _canvas_y
|
||||
_canvas.delete('all')
|
||||
draw_background()
|
||||
_canvas_x, _canvas_y = 0, _canvas_ys
|
||||
|
||||
def polygon(coords, outlineColor, fillColor=None, filled=1, smoothed=1, behind=0, width=1):
|
||||
c = []
|
||||
for coord in coords:
|
||||
c.append(coord[0])
|
||||
c.append(coord[1])
|
||||
if fillColor == None: fillColor = outlineColor
|
||||
if filled == 0: fillColor = ""
|
||||
poly = _canvas.create_polygon(c, outline=outlineColor, fill=fillColor, smooth=smoothed, width=width)
|
||||
if behind > 0:
|
||||
_canvas.tag_lower(poly, behind) # Higher should be more visible
|
||||
return poly
|
||||
|
||||
def square(pos, r, color, filled=1, behind=0):
|
||||
x, y = pos
|
||||
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)
|
||||
|
||||
def circle(pos, r, outlineColor, fillColor, endpoints=None, style='pieslice', width=2):
|
||||
x, y = pos
|
||||
x0, x1 = x - r - 1, x + r
|
||||
y0, y1 = y - r - 1, y + r
|
||||
if endpoints == None:
|
||||
e = [0, 359]
|
||||
else:
|
||||
e = list(endpoints)
|
||||
while e[0] > e[1]: e[1] = e[1] + 360
|
||||
|
||||
return _canvas.create_arc(x0, y0, x1, y1, outline=outlineColor, fill=fillColor,
|
||||
extent=e[1] - e[0], start=e[0], style=style, width=width)
|
||||
|
||||
def image(pos, file="../../blueghost.gif"):
|
||||
x, y = pos
|
||||
# img = PhotoImage(file=file)
|
||||
return _canvas.create_image(x, y, image = Tkinter.PhotoImage(file=file), anchor = Tkinter.NW)
|
||||
|
||||
|
||||
def refresh():
|
||||
_canvas.update_idletasks()
|
||||
|
||||
def moveCircle(id, pos, r, endpoints=None):
|
||||
global _canvas_x, _canvas_y
|
||||
|
||||
x, y = pos
|
||||
# x0, x1 = x - r, x + r + 1
|
||||
# y0, y1 = y - r, y + r + 1
|
||||
x0, x1 = x - r - 1, x + r
|
||||
y0, y1 = y - r - 1, y + r
|
||||
if endpoints == None:
|
||||
e = [0, 359]
|
||||
else:
|
||||
e = list(endpoints)
|
||||
while e[0] > e[1]: e[1] = e[1] + 360
|
||||
|
||||
edit(id, ('start', e[0]), ('extent', e[1] - e[0]))
|
||||
move_to(id, x0, y0)
|
||||
|
||||
def edit(id, *args):
|
||||
_canvas.itemconfigure(id, **dict(args))
|
||||
|
||||
def text(pos, color, contents, font='Helvetica', size=12, style='normal', anchor="nw"):
|
||||
global _canvas_x, _canvas_y
|
||||
x, y = pos
|
||||
font = (font, str(size), style)
|
||||
return _canvas.create_text(x, y, fill=color, text=contents, font=font, anchor=anchor)
|
||||
|
||||
def changeText(id, newText, font=None, size=12, style='normal'):
|
||||
_canvas.itemconfigure(id, text=newText)
|
||||
if font != None:
|
||||
_canvas.itemconfigure(id, font=(font, '-%d' % size, style))
|
||||
|
||||
def changeColor(id, newColor):
|
||||
_canvas.itemconfigure(id, fill=newColor)
|
||||
|
||||
def line(here, there, color=formatColor(0, 0, 0), width=2):
|
||||
x0, y0 = here[0], here[1]
|
||||
x1, y1 = there[0], there[1]
|
||||
return _canvas.create_line(x0, y0, x1, y1, fill=color, width=width)
|
||||
|
||||
##############################################################################
|
||||
### Keypress handling ########################################################
|
||||
##############################################################################
|
||||
|
||||
# We bind to key-down and key-up events.
|
||||
|
||||
_keysdown = {}
|
||||
_keyswaiting = {}
|
||||
# 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.
|
||||
_got_release = None
|
||||
|
||||
def _keypress(event):
|
||||
global _got_release
|
||||
#remap_arrows(event)
|
||||
_keysdown[event.keysym] = 1
|
||||
_keyswaiting[event.keysym] = 1
|
||||
# print event.char, event.keycode
|
||||
_got_release = None
|
||||
|
||||
def _keyrelease(event):
|
||||
global _got_release
|
||||
#remap_arrows(event)
|
||||
try:
|
||||
del _keysdown[event.keysym]
|
||||
except:
|
||||
pass
|
||||
_got_release = 1
|
||||
|
||||
def remap_arrows(event):
|
||||
# TURN ARROW PRESSES INTO LETTERS (SHOULD BE IN KEYBOARD AGENT)
|
||||
if event.char in ['a', 's', 'd', 'w']:
|
||||
return
|
||||
if event.keycode in [37, 101]: # LEFT ARROW (win / x)
|
||||
event.char = 'a'
|
||||
if event.keycode in [38, 99]: # UP ARROW
|
||||
event.char = 'w'
|
||||
if event.keycode in [39, 102]: # RIGHT ARROW
|
||||
event.char = 'd'
|
||||
if event.keycode in [40, 104]: # DOWN ARROW
|
||||
event.char = 's'
|
||||
|
||||
def _clear_keys(event=None):
|
||||
global _keysdown, _got_release, _keyswaiting
|
||||
_keysdown = {}
|
||||
_keyswaiting = {}
|
||||
_got_release = None
|
||||
|
||||
def keys_pressed(d_o_e=Tkinter.tkinter.dooneevent,
|
||||
d_w=Tkinter.tkinter.DONT_WAIT):
|
||||
d_o_e(d_w)
|
||||
if _got_release:
|
||||
d_o_e(d_w)
|
||||
return _keysdown.keys()
|
||||
|
||||
def keys_waiting():
|
||||
global _keyswaiting
|
||||
keys = _keyswaiting.keys()
|
||||
_keyswaiting = {}
|
||||
return keys
|
||||
|
||||
# Block for a list of keys...
|
||||
|
||||
def wait_for_keys():
|
||||
keys = []
|
||||
while keys == []:
|
||||
keys = keys_pressed()
|
||||
sleep(0.05)
|
||||
return keys
|
||||
|
||||
def remove_from_screen(x,
|
||||
d_o_e=Tkinter.tkinter.dooneevent,
|
||||
d_w=Tkinter.tkinter.DONT_WAIT):
|
||||
_canvas.delete(x)
|
||||
d_o_e(d_w)
|
||||
|
||||
def _adjust_coords(coord_list, x, y):
|
||||
for i in range(0, len(coord_list), 2):
|
||||
coord_list[i] = coord_list[i] + x
|
||||
coord_list[i + 1] = coord_list[i + 1] + y
|
||||
return coord_list
|
||||
|
||||
def move_to(object, x, y=None,
|
||||
d_o_e=Tkinter.tkinter.dooneevent,
|
||||
d_w=Tkinter.tkinter.DONT_WAIT):
|
||||
if y is None:
|
||||
try: x, y = x
|
||||
except: raise 'incomprehensible coordinates'
|
||||
|
||||
horiz = True
|
||||
newCoords = []
|
||||
current_x, current_y = _canvas.coords(object)[0:2] # first point
|
||||
for coord in _canvas.coords(object):
|
||||
if horiz:
|
||||
inc = x - current_x
|
||||
else:
|
||||
inc = y - current_y
|
||||
horiz = not horiz
|
||||
|
||||
newCoords.append(coord + inc)
|
||||
|
||||
_canvas.coords(object, *newCoords)
|
||||
d_o_e(d_w)
|
||||
|
||||
def move_by(object, x, y=None,
|
||||
d_o_e=Tkinter.tkinter.dooneevent,
|
||||
d_w=Tkinter.tkinter.DONT_WAIT, lift=False):
|
||||
if y is None:
|
||||
try: x, y = x
|
||||
except: raise Exception, 'incomprehensible coordinates'
|
||||
|
||||
horiz = True
|
||||
newCoords = []
|
||||
for coord in _canvas.coords(object):
|
||||
if horiz:
|
||||
inc = x
|
||||
else:
|
||||
inc = y
|
||||
horiz = not horiz
|
||||
|
||||
newCoords.append(coord + inc)
|
||||
|
||||
_canvas.coords(object, *newCoords)
|
||||
d_o_e(d_w)
|
||||
if lift:
|
||||
_canvas.tag_raise(object)
|
||||
|
||||
def writePostscript(filename):
|
||||
"Writes the current canvas to a postscript file."
|
||||
psfile = file(filename, 'w')
|
||||
psfile.write(_canvas.postscript(pageanchor='sw',
|
||||
y='0.c',
|
||||
x='0.c'))
|
||||
psfile.close()
|
||||
|
||||
ghost_shape = [
|
||||
(0, - 0.5),
|
||||
(0.25, - 0.75),
|
||||
(0.5, - 0.5),
|
||||
(0.75, - 0.75),
|
||||
(0.75, 0.5),
|
||||
(0.5, 0.75),
|
||||
(- 0.5, 0.75),
|
||||
(- 0.75, 0.5),
|
||||
(- 0.75, - 0.75),
|
||||
(- 0.5, - 0.5),
|
||||
(- 0.25, - 0.75)
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
begin_graphics()
|
||||
clear_screen()
|
||||
ghost_shape = [(x * 10 + 20, y * 10 + 20) for x, y in ghost_shape]
|
||||
g = polygon(ghost_shape, formatColor(1, 1, 1))
|
||||
move_to(g, (50, 50))
|
||||
circle((150, 150), 20, formatColor(0.7, 0.3, 0.0), endpoints=[15, - 15])
|
||||
sleep(2)
|
84
p2_multiagent/keyboardAgents.py
Normal file
84
p2_multiagent/keyboardAgents.py
Normal file
@ -0,0 +1,84 @@
|
||||
# keyboardAgents.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 game import Agent
|
||||
from game import Directions
|
||||
import random
|
||||
|
||||
class KeyboardAgent(Agent):
|
||||
"""
|
||||
An agent controlled by the keyboard.
|
||||
"""
|
||||
# NOTE: Arrow keys also work.
|
||||
WEST_KEY = 'a'
|
||||
EAST_KEY = 'd'
|
||||
NORTH_KEY = 'w'
|
||||
SOUTH_KEY = 's'
|
||||
STOP_KEY = 'q'
|
||||
|
||||
def __init__( self, index = 0 ):
|
||||
|
||||
self.lastMove = Directions.STOP
|
||||
self.index = index
|
||||
self.keys = []
|
||||
|
||||
def getAction( self, state):
|
||||
from graphicsUtils import keys_waiting
|
||||
from graphicsUtils import keys_pressed
|
||||
keys = keys_waiting() + keys_pressed()
|
||||
if keys != []:
|
||||
self.keys = keys
|
||||
|
||||
legal = state.getLegalActions(self.index)
|
||||
move = self.getMove(legal)
|
||||
|
||||
if move == Directions.STOP:
|
||||
# Try to move in the same direction as before
|
||||
if self.lastMove in legal:
|
||||
move = self.lastMove
|
||||
|
||||
if (self.STOP_KEY in self.keys) and Directions.STOP in legal: move = Directions.STOP
|
||||
|
||||
if move not in legal:
|
||||
move = random.choice(legal)
|
||||
|
||||
self.lastMove = move
|
||||
return move
|
||||
|
||||
def getMove(self, legal):
|
||||
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.EAST_KEY in self.keys or 'Right' in self.keys) and Directions.EAST in legal: 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
|
||||
|
||||
class KeyboardAgent2(KeyboardAgent):
|
||||
"""
|
||||
A second agent controlled by the keyboard.
|
||||
"""
|
||||
# NOTE: Arrow keys also work.
|
||||
WEST_KEY = 'j'
|
||||
EAST_KEY = "l"
|
||||
NORTH_KEY = 'i'
|
||||
SOUTH_KEY = 'k'
|
||||
STOP_KEY = 'u'
|
||||
|
||||
def getMove(self, legal):
|
||||
move = Directions.STOP
|
||||
if (self.WEST_KEY in self.keys) and Directions.WEST in legal: move = Directions.WEST
|
||||
if (self.EAST_KEY in self.keys) and Directions.EAST in legal: 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
|
149
p2_multiagent/layout.py
Normal file
149
p2_multiagent/layout.py
Normal file
@ -0,0 +1,149 @@
|
||||
# layout.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 Grid
|
||||
import os
|
||||
import random
|
||||
|
||||
VISIBILITY_MATRIX_CACHE = {}
|
||||
|
||||
class Layout:
|
||||
"""
|
||||
A Layout manages the static information about the game board.
|
||||
"""
|
||||
|
||||
def __init__(self, layoutText):
|
||||
self.width = len(layoutText[0])
|
||||
self.height= len(layoutText)
|
||||
self.walls = Grid(self.width, self.height, False)
|
||||
self.food = Grid(self.width, self.height, False)
|
||||
self.capsules = []
|
||||
self.agentPositions = []
|
||||
self.numGhosts = 0
|
||||
self.processLayoutText(layoutText)
|
||||
self.layoutText = layoutText
|
||||
self.totalFood = len(self.food.asList())
|
||||
# self.initializeVisibilityMatrix()
|
||||
|
||||
def getNumGhosts(self):
|
||||
return self.numGhosts
|
||||
|
||||
def initializeVisibilityMatrix(self):
|
||||
global VISIBILITY_MATRIX_CACHE
|
||||
if reduce(str.__add__, self.layoutText) not in VISIBILITY_MATRIX_CACHE:
|
||||
from game import Directions
|
||||
vecs = [(-0.5,0), (0.5,0),(0,-0.5),(0,0.5)]
|
||||
dirs = [Directions.NORTH, Directions.SOUTH, 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 y in range(self.height):
|
||||
if self.walls[x][y] == False:
|
||||
for vec, direction in zip(vecs, dirs):
|
||||
dx, dy = vec
|
||||
nextx, nexty = x + dx, y + dy
|
||||
while (nextx + nexty) != int(nextx) + int(nexty) or not self.walls[int(nextx)][int(nexty)] :
|
||||
vis[x][y][direction].add((nextx, nexty))
|
||||
nextx, nexty = x + dx, y + dy
|
||||
self.visibility = vis
|
||||
VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)] = vis
|
||||
else:
|
||||
self.visibility = VISIBILITY_MATRIX_CACHE[reduce(str.__add__, self.layoutText)]
|
||||
|
||||
def isWall(self, pos):
|
||||
x, col = pos
|
||||
return self.walls[x][col]
|
||||
|
||||
def getRandomLegalPosition(self):
|
||||
x = random.choice(range(self.width))
|
||||
y = random.choice(range(self.height))
|
||||
while self.isWall( (x, y) ):
|
||||
x = random.choice(range(self.width))
|
||||
y = random.choice(range(self.height))
|
||||
return (x,y)
|
||||
|
||||
def getRandomCorner(self):
|
||||
poses = [(1,1), (1, self.height - 2), (self.width - 2, 1), (self.width - 2, self.height - 2)]
|
||||
return random.choice(poses)
|
||||
|
||||
def getFurthestCorner(self, pacPos):
|
||||
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])
|
||||
return pos
|
||||
|
||||
def isVisibleFrom(self, ghostPos, pacPos, pacDirection):
|
||||
row, col = [int(x) for x in pacPos]
|
||||
return ghostPos in self.visibility[row][col][pacDirection]
|
||||
|
||||
def __str__(self):
|
||||
return "\n".join(self.layoutText)
|
||||
|
||||
def deepCopy(self):
|
||||
return Layout(self.layoutText[:])
|
||||
|
||||
def processLayoutText(self, layoutText):
|
||||
"""
|
||||
Coordinates are flipped from the input format to the (x,y) convention here
|
||||
|
||||
The shape of the maze. Each character
|
||||
represents a different type of object.
|
||||
% - Wall
|
||||
. - Food
|
||||
o - Capsule
|
||||
G - Ghost
|
||||
P - Pacman
|
||||
Other characters are ignored.
|
||||
"""
|
||||
maxY = self.height - 1
|
||||
for y in range(self.height):
|
||||
for x in range(self.width):
|
||||
layoutChar = layoutText[maxY - y][x]
|
||||
self.processLayoutChar(x, y, layoutChar)
|
||||
self.agentPositions.sort()
|
||||
self.agentPositions = [ ( i == 0, pos) for i, pos in self.agentPositions]
|
||||
|
||||
def processLayoutChar(self, x, y, layoutChar):
|
||||
if layoutChar == '%':
|
||||
self.walls[x][y] = True
|
||||
elif layoutChar == '.':
|
||||
self.food[x][y] = True
|
||||
elif layoutChar == 'o':
|
||||
self.capsules.append((x, y))
|
||||
elif layoutChar == 'P':
|
||||
self.agentPositions.append( (0, (x, y) ) )
|
||||
elif layoutChar in ['G']:
|
||||
self.agentPositions.append( (1, (x, y) ) )
|
||||
self.numGhosts += 1
|
||||
elif layoutChar in ['1', '2', '3', '4']:
|
||||
self.agentPositions.append( (int(layoutChar), (x,y)))
|
||||
self.numGhosts += 1
|
||||
def getLayout(name, back = 2):
|
||||
if name.endswith('.lay'):
|
||||
layout = tryToLoad('layouts/' + name)
|
||||
if layout == None: layout = tryToLoad(name)
|
||||
else:
|
||||
layout = tryToLoad('layouts/' + name + '.lay')
|
||||
if layout == None: layout = tryToLoad(name + '.lay')
|
||||
if layout == None and back >= 0:
|
||||
curdir = os.path.abspath('.')
|
||||
os.chdir('..')
|
||||
layout = getLayout(name, back -1)
|
||||
os.chdir(curdir)
|
||||
return layout
|
||||
|
||||
def tryToLoad(fullname):
|
||||
if(not os.path.exists(fullname)): return None
|
||||
f = open(fullname)
|
||||
try: return Layout([line.strip() for line in f])
|
||||
finally: f.close()
|
7
p2_multiagent/layouts/capsuleClassic.lay
Normal file
7
p2_multiagent/layouts/capsuleClassic.lay
Normal file
@ -0,0 +1,7 @@
|
||||
%%%%%%%%%%%%%%%%%%%
|
||||
%G. G ....%
|
||||
%.% % %%%%%% %.%%.%
|
||||
%.%o% % o% %.o%.%
|
||||
%.%%%.% %%% %..%.%
|
||||
%..... P %..%G%
|
||||
%%%%%%%%%%%%%%%%%%%%
|
9
p2_multiagent/layouts/contestClassic.lay
Normal file
9
p2_multiagent/layouts/contestClassic.lay
Normal file
@ -0,0 +1,9 @@
|
||||
%%%%%%%%%%%%%%%%%%%%
|
||||
%o...%........%...o%
|
||||
%.%%.%.%%..%%.%.%%.%
|
||||
%...... G GG%......%
|
||||
%.%.%%.%% %%%.%%.%.%
|
||||
%.%....% ooo%.%..%.%
|
||||
%.%.%%.% %% %.%.%%.%
|
||||
%o%......P....%....%
|
||||
%%%%%%%%%%%%%%%%%%%%
|
11
p2_multiagent/layouts/mediumClassic.lay
Normal file
11
p2_multiagent/layouts/mediumClassic.lay
Normal file
@ -0,0 +1,11 @@
|
||||
%%%%%%%%%%%%%%%%%%%%
|
||||
%o...%........%....%
|
||||
%.%%.%.%%%%%%.%.%%.%
|
||||
%.%..............%.%
|
||||
%.%.%%.%% %%.%%.%.%
|
||||
%......%G G%......%
|
||||
%.%.%%.%%%%%%.%%.%.%
|
||||
%.%..............%.%
|
||||
%.%%.%.%%%%%%.%.%%.%
|
||||
%....%...P....%...o%
|
||||
%%%%%%%%%%%%%%%%%%%%
|
5
p2_multiagent/layouts/minimaxClassic.lay
Normal file
5
p2_multiagent/layouts/minimaxClassic.lay
Normal file
@ -0,0 +1,5 @@
|
||||
%%%%%%%%%
|
||||
%.P G%
|
||||
% %.%G%%%
|
||||
%G %%%
|
||||
%%%%%%%%%
|
9
p2_multiagent/layouts/openClassic.lay
Normal file
9
p2_multiagent/layouts/openClassic.lay
Normal file
@ -0,0 +1,9 @@
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%.. P .... .... %
|
||||
%.. ... ... ... ... %
|
||||
%.. ... ... ... ... %
|
||||
%.. .... .... G %
|
||||
%.. ... ... ... ... %
|
||||
%.. ... ... ... ... %
|
||||
%.. .... .... o%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%
|
27
p2_multiagent/layouts/originalClassic.lay
Normal file
27
p2_multiagent/layouts/originalClassic.lay
Normal file
@ -0,0 +1,27 @@
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%............%%............%
|
||||
%.%%%%.%%%%%.%%.%%%%%.%%%%.%
|
||||
%o%%%%.%%%%%.%%.%%%%%.%%%%o%
|
||||
%.%%%%.%%%%%.%%.%%%%%.%%%%.%
|
||||
%..........................%
|
||||
%.%%%%.%%.%%%%%%%%.%%.%%%%.%
|
||||
%.%%%%.%%.%%%%%%%%.%%.%%%%.%
|
||||
%......%%....%%....%%......%
|
||||
%%%%%%.%%%%% %% %%%%%.%%%%%%
|
||||
%%%%%%.%%%%% %% %%%%%.%%%%%%
|
||||
%%%%%%.% %.%%%%%%
|
||||
%%%%%%.% %%%% %%%% %.%%%%%%
|
||||
% . %G GG G% . %
|
||||
%%%%%%.% %%%%%%%%%% %.%%%%%%
|
||||
%%%%%%.% %.%%%%%%
|
||||
%%%%%%.% %%%%%%%%%% %.%%%%%%
|
||||
%............%%............%
|
||||
%.%%%%.%%%%%.%%.%%%%%.%%%%.%
|
||||
%.%%%%.%%%%%.%%.%%%%%.%%%%.%
|
||||
%o..%%....... .......%%..o%
|
||||
%%%.%%.%%.%%%%%%%%.%%.%%.%%%
|
||||
%%%.%%.%%.%%%%%%%%.%%.%%.%%%
|
||||
%......%%....%%....%%......%
|
||||
%.%%%%%%%%%%.%%.%%%%%%%%%%.%
|
||||
%.............P............%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
7
p2_multiagent/layouts/smallClassic.lay
Normal file
7
p2_multiagent/layouts/smallClassic.lay
Normal file
@ -0,0 +1,7 @@
|
||||
%%%%%%%%%%%%%%%%%%%%
|
||||
%......%G G%......%
|
||||
%.%%...%% %%...%%.%
|
||||
%.%o.%........%.o%.%
|
||||
%.%%.%.%%%%%%.%.%%.%
|
||||
%........P.........%
|
||||
%%%%%%%%%%%%%%%%%%%%
|
10
p2_multiagent/layouts/testClassic.lay
Normal file
10
p2_multiagent/layouts/testClassic.lay
Normal file
@ -0,0 +1,10 @@
|
||||
%%%%%
|
||||
% . %
|
||||
%.G.%
|
||||
% . %
|
||||
%. .%
|
||||
% %
|
||||
% .%
|
||||
% %
|
||||
%P .%
|
||||
%%%%%
|
5
p2_multiagent/layouts/trappedClassic.lay
Normal file
5
p2_multiagent/layouts/trappedClassic.lay
Normal file
@ -0,0 +1,5 @@
|
||||
%%%%%%%%
|
||||
% P G%
|
||||
%G%%%%%%
|
||||
%.... %
|
||||
%%%%%%%%
|
13
p2_multiagent/layouts/trickyClassic.lay
Normal file
13
p2_multiagent/layouts/trickyClassic.lay
Normal file
@ -0,0 +1,13 @@
|
||||
%%%%%%%%%%%%%%%%%%%%
|
||||
%o...%........%...o%
|
||||
%.%%.%.%%..%%.%.%%.%
|
||||
%.%.....%..%.....%.%
|
||||
%.%.%%.%% %%.%%.%.%
|
||||
%...... GGGG%.%....%
|
||||
%.%....%%%%%%.%..%.%
|
||||
%.%....% oo%.%..%.%
|
||||
%.%....% %%%%.%..%.%
|
||||
%.%...........%..%.%
|
||||
%.%%.%.%%%%%%.%.%%.%
|
||||
%o...%...P....%...o%
|
||||
%%%%%%%%%%%%%%%%%%%%
|
187
p2_multiagent/multiAgents.py
Normal file
187
p2_multiagent/multiAgents.py
Normal file
@ -0,0 +1,187 @@
|
||||
# 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
|
||||
|
||||
print(gameState)
|
||||
print(list(zip(scores, legalMoves)))
|
||||
print("chosenAction", legalMoves[chosenIndex])
|
||||
|
||||
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)
|
||||
# newScaredTimes = [ghostSt.scaredTimer for ghostSt in newGhostStates]
|
||||
|
||||
successorGameState = currentGameState.generatePacmanSuccessor(action)
|
||||
newPos = successorGameState.getPacmanPosition()
|
||||
|
||||
closestGhost = min([manhattanDistance(newPos, ghost.getPosition())
|
||||
for ghost in successorGameState.getGhostStates()])
|
||||
if closestGhost < 2.0:
|
||||
return 0
|
||||
|
||||
if successorGameState.getScore() > currentGameState.getScore():
|
||||
return 1.0
|
||||
|
||||
foodPositions = successorGameState.getFood().asList()
|
||||
closestFoodDist = min([manhattanDistance(newPos, foodPos)
|
||||
for foodPos in foodPositions])
|
||||
|
||||
return 1. / closestFoodDist
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
"*** 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
|
||||
|
529
p2_multiagent/multiagentTestClasses.py
Normal file
529
p2_multiagent/multiagentTestClasses.py
Normal file
@ -0,0 +1,529 @@
|
||||
# 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, math, traceback, sys, os
|
||||
import layout, 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 = map(lambda x: json.loads(x), solutionDict['optimalActions'].split('\n'))
|
||||
altDepthActions = map(lambda x: json.loads(x), solutionDict['altDepthActions'].split('\n'))
|
||||
partialPlyBugActions = map(lambda x: json.loads(x), 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
|
||||
|
||||
|
||||
|
||||
|
684
p2_multiagent/pacman.py
Normal file
684
p2_multiagent/pacman.py
Normal file
@ -0,0 +1,684 @@
|
||||
# pacman.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).
|
||||
|
||||
|
||||
"""
|
||||
Pacman.py holds the logic for the classic pacman game along with the main
|
||||
code to run a game. This file is divided into three sections:
|
||||
|
||||
(i) Your interface to the pacman world:
|
||||
Pacman is a complex environment. You probably don't want to
|
||||
read through all of the code we wrote to make the game runs
|
||||
correctly. This section contains the parts of the code
|
||||
that you will need to understand in order to complete the
|
||||
project. There is also some code in game.py that you should
|
||||
understand.
|
||||
|
||||
(ii) The hidden secrets of pacman:
|
||||
This section contains all of the logic code that the pacman
|
||||
environment uses to decide who can move where, who dies when
|
||||
things collide, etc. You shouldn't need to read this section
|
||||
of code, but you can if you want.
|
||||
|
||||
(iii) Framework to start a game:
|
||||
The final section contains the code for reading the command
|
||||
you use to set up the game, then starting up a new game, along with
|
||||
linking in all the external parts (agent functions, graphics).
|
||||
Check this section out to see all the options available to you.
|
||||
|
||||
To play your first game, type 'python pacman.py' from the command line.
|
||||
The keys are 'a', 's', 'd', and 'w' to move (or arrow keys). Have fun!
|
||||
"""
|
||||
from game import GameStateData
|
||||
from game import Game
|
||||
from game import Directions
|
||||
from game import Actions
|
||||
from util import nearestPoint
|
||||
from util import manhattanDistance
|
||||
import util, layout
|
||||
import sys, types, time, random, os
|
||||
|
||||
###################################################
|
||||
# YOUR INTERFACE TO THE PACMAN WORLD: A GameState #
|
||||
###################################################
|
||||
|
||||
class GameState:
|
||||
"""
|
||||
A GameState specifies the full game state, including the food, capsules,
|
||||
agent configurations and score changes.
|
||||
|
||||
GameStates are used by the Game object to capture the actual state of the game and
|
||||
can be used by agents to reason about the game.
|
||||
|
||||
Much of the information in a GameState is stored in a GameStateData object. We
|
||||
strongly suggest that you access that data via the accessor methods below rather
|
||||
than referring to the GameStateData object directly.
|
||||
|
||||
Note that in classic Pacman, Pacman is always agent 0.
|
||||
"""
|
||||
|
||||
####################################################
|
||||
# Accessor methods: use these to access state data #
|
||||
####################################################
|
||||
|
||||
# static variable keeps track of which states have had getLegalActions called
|
||||
explored = set()
|
||||
def getAndResetExplored():
|
||||
tmp = GameState.explored.copy()
|
||||
GameState.explored = set()
|
||||
return tmp
|
||||
getAndResetExplored = staticmethod(getAndResetExplored)
|
||||
|
||||
def getLegalActions( self, agentIndex=0 ):
|
||||
"""
|
||||
Returns the legal actions for the agent specified.
|
||||
"""
|
||||
# GameState.explored.add(self)
|
||||
if self.isWin() or self.isLose(): return []
|
||||
|
||||
if agentIndex == 0: # Pacman is moving
|
||||
return PacmanRules.getLegalActions( self )
|
||||
else:
|
||||
return GhostRules.getLegalActions( self, agentIndex )
|
||||
|
||||
def generateSuccessor( self, agentIndex, action):
|
||||
"""
|
||||
Returns the successor state after the specified agent takes the action.
|
||||
"""
|
||||
# Check that successors exist
|
||||
if self.isWin() or self.isLose(): raise Exception('Can\'t generate a successor of a terminal state.')
|
||||
|
||||
# Copy current state
|
||||
state = GameState(self)
|
||||
|
||||
# Let agent's logic deal with its action's effects on the board
|
||||
if agentIndex == 0: # Pacman is moving
|
||||
state.data._eaten = [False for i in range(state.getNumAgents())]
|
||||
PacmanRules.applyAction( state, action )
|
||||
else: # A ghost is moving
|
||||
GhostRules.applyAction( state, action, agentIndex )
|
||||
|
||||
# Time passes
|
||||
if agentIndex == 0:
|
||||
state.data.scoreChange += -TIME_PENALTY # Penalty for waiting around
|
||||
else:
|
||||
GhostRules.decrementTimer( state.data.agentStates[agentIndex] )
|
||||
|
||||
# Resolve multi-agent effects
|
||||
GhostRules.checkDeath( state, agentIndex )
|
||||
|
||||
# Book keeping
|
||||
state.data._agentMoved = agentIndex
|
||||
state.data.score += state.data.scoreChange
|
||||
GameState.explored.add(self)
|
||||
GameState.explored.add(state)
|
||||
return state
|
||||
|
||||
def getLegalPacmanActions( self ):
|
||||
return self.getLegalActions( 0 )
|
||||
|
||||
def generatePacmanSuccessor( self, action ):
|
||||
"""
|
||||
Generates the successor state after the specified pacman move
|
||||
"""
|
||||
return self.generateSuccessor( 0, action )
|
||||
|
||||
def getPacmanState( self ):
|
||||
"""
|
||||
Returns an AgentState object for pacman (in game.py)
|
||||
|
||||
state.pos gives the current position
|
||||
state.direction gives the travel vector
|
||||
"""
|
||||
return self.data.agentStates[0].copy()
|
||||
|
||||
def getPacmanPosition( self ):
|
||||
return self.data.agentStates[0].getPosition()
|
||||
|
||||
def getGhostStates( self ):
|
||||
return self.data.agentStates[1:]
|
||||
|
||||
def getGhostState( self, agentIndex ):
|
||||
if agentIndex == 0 or agentIndex >= self.getNumAgents():
|
||||
raise Exception("Invalid index passed to getGhostState")
|
||||
return self.data.agentStates[agentIndex]
|
||||
|
||||
def getGhostPosition( self, agentIndex ):
|
||||
if agentIndex == 0:
|
||||
raise Exception("Pacman's index passed to getGhostPosition")
|
||||
return self.data.agentStates[agentIndex].getPosition()
|
||||
|
||||
def getGhostPositions(self):
|
||||
return [s.getPosition() for s in self.getGhostStates()]
|
||||
|
||||
def getNumAgents( self ):
|
||||
return len( self.data.agentStates )
|
||||
|
||||
def getScore( self ):
|
||||
return float(self.data.score)
|
||||
|
||||
def getCapsules(self):
|
||||
"""
|
||||
Returns a list of positions (x,y) of the remaining capsules.
|
||||
"""
|
||||
return self.data.capsules
|
||||
|
||||
def getNumFood( self ):
|
||||
return self.data.food.count()
|
||||
|
||||
def getFood(self):
|
||||
"""
|
||||
Returns a Grid of boolean food indicator variables.
|
||||
|
||||
Grids can be accessed via list notation, so to check
|
||||
if there is food at (x,y), just call
|
||||
|
||||
currentFood = state.getFood()
|
||||
if currentFood[x][y] == True: ...
|
||||
"""
|
||||
return self.data.food
|
||||
|
||||
def getWalls(self):
|
||||
"""
|
||||
Returns a Grid of boolean wall indicator variables.
|
||||
|
||||
Grids can be accessed via list notation, so to check
|
||||
if there is a wall at (x,y), just call
|
||||
|
||||
walls = state.getWalls()
|
||||
if walls[x][y] == True: ...
|
||||
"""
|
||||
return self.data.layout.walls
|
||||
|
||||
def hasFood(self, x, y):
|
||||
return self.data.food[x][y]
|
||||
|
||||
def hasWall(self, x, y):
|
||||
return self.data.layout.walls[x][y]
|
||||
|
||||
def isLose( self ):
|
||||
return self.data._lose
|
||||
|
||||
def isWin( self ):
|
||||
return self.data._win
|
||||
|
||||
#############################################
|
||||
# Helper methods: #
|
||||
# You shouldn't need to call these directly #
|
||||
#############################################
|
||||
|
||||
def __init__( self, prevState = None ):
|
||||
"""
|
||||
Generates a new state by copying information from its predecessor.
|
||||
"""
|
||||
if prevState != None: # Initial state
|
||||
self.data = GameStateData(prevState.data)
|
||||
else:
|
||||
self.data = GameStateData()
|
||||
|
||||
def deepCopy( self ):
|
||||
state = GameState( self )
|
||||
state.data = self.data.deepCopy()
|
||||
return state
|
||||
|
||||
def __eq__( self, other ):
|
||||
"""
|
||||
Allows two states to be compared.
|
||||
"""
|
||||
return hasattr(other, 'data') and self.data == other.data
|
||||
|
||||
def __hash__( self ):
|
||||
"""
|
||||
Allows states to be keys of dictionaries.
|
||||
"""
|
||||
return hash( self.data )
|
||||
|
||||
def __str__( self ):
|
||||
|
||||
return str(self.data)
|
||||
|
||||
def initialize( self, layout, numGhostAgents=1000 ):
|
||||
"""
|
||||
Creates an initial game state from a layout array (see layout.py).
|
||||
"""
|
||||
self.data.initialize(layout, numGhostAgents)
|
||||
|
||||
############################################################################
|
||||
# THE HIDDEN SECRETS OF PACMAN #
|
||||
# #
|
||||
# You shouldn't need to look through the code in this section of the file. #
|
||||
############################################################################
|
||||
|
||||
SCARED_TIME = 40 # Moves ghosts are scared
|
||||
COLLISION_TOLERANCE = 0.7 # How close ghosts must be to Pacman to kill
|
||||
TIME_PENALTY = 1 # Number of points lost each round
|
||||
|
||||
class ClassicGameRules:
|
||||
"""
|
||||
These game rules manage the control flow of a game, deciding when
|
||||
and how the game starts and ends.
|
||||
"""
|
||||
def __init__(self, timeout=30):
|
||||
self.timeout = timeout
|
||||
|
||||
def newGame( self, layout, pacmanAgent, ghostAgents, display, quiet = False, catchExceptions=False):
|
||||
agents = [pacmanAgent] + ghostAgents[:layout.getNumGhosts()]
|
||||
initState = GameState()
|
||||
initState.initialize( layout, len(ghostAgents) )
|
||||
game = Game(agents, display, self, catchExceptions=catchExceptions)
|
||||
game.state = initState
|
||||
self.initialState = initState.deepCopy()
|
||||
self.quiet = quiet
|
||||
return game
|
||||
|
||||
def process(self, state, game):
|
||||
"""
|
||||
Checks to see whether it is time to end the game.
|
||||
"""
|
||||
if state.isWin(): self.win(state, game)
|
||||
if state.isLose(): self.lose(state, game)
|
||||
|
||||
def win( self, state, game ):
|
||||
if not self.quiet: print "Pacman emerges victorious! Score: %d" % state.data.score
|
||||
game.gameOver = True
|
||||
|
||||
def lose( self, state, game ):
|
||||
if not self.quiet: print "Pacman died! Score: %d" % state.data.score
|
||||
game.gameOver = True
|
||||
|
||||
def getProgress(self, game):
|
||||
return float(game.state.getNumFood()) / self.initialState.getNumFood()
|
||||
|
||||
def agentCrash(self, game, agentIndex):
|
||||
if agentIndex == 0:
|
||||
print "Pacman crashed"
|
||||
else:
|
||||
print "A ghost crashed"
|
||||
|
||||
def getMaxTotalTime(self, agentIndex):
|
||||
return self.timeout
|
||||
|
||||
def getMaxStartupTime(self, agentIndex):
|
||||
return self.timeout
|
||||
|
||||
def getMoveWarningTime(self, agentIndex):
|
||||
return self.timeout
|
||||
|
||||
def getMoveTimeout(self, agentIndex):
|
||||
return self.timeout
|
||||
|
||||
def getMaxTimeWarnings(self, agentIndex):
|
||||
return 0
|
||||
|
||||
class PacmanRules:
|
||||
"""
|
||||
These functions govern how pacman interacts with his environment under
|
||||
the classic game rules.
|
||||
"""
|
||||
PACMAN_SPEED=1
|
||||
|
||||
def getLegalActions( state ):
|
||||
"""
|
||||
Returns a list of possible actions.
|
||||
"""
|
||||
return Actions.getPossibleActions( state.getPacmanState().configuration, state.data.layout.walls )
|
||||
getLegalActions = staticmethod( getLegalActions )
|
||||
|
||||
def applyAction( state, action ):
|
||||
"""
|
||||
Edits the state to reflect the results of the action.
|
||||
"""
|
||||
legal = PacmanRules.getLegalActions( state )
|
||||
if action not in legal:
|
||||
raise Exception("Illegal action " + str(action))
|
||||
|
||||
pacmanState = state.data.agentStates[0]
|
||||
|
||||
# Update Configuration
|
||||
vector = Actions.directionToVector( action, PacmanRules.PACMAN_SPEED )
|
||||
pacmanState.configuration = pacmanState.configuration.generateSuccessor( vector )
|
||||
|
||||
# Eat
|
||||
next = pacmanState.configuration.getPosition()
|
||||
nearest = nearestPoint( next )
|
||||
if manhattanDistance( nearest, next ) <= 0.5 :
|
||||
# Remove food
|
||||
PacmanRules.consume( nearest, state )
|
||||
applyAction = staticmethod( applyAction )
|
||||
|
||||
def consume( position, state ):
|
||||
x,y = position
|
||||
# Eat food
|
||||
if state.data.food[x][y]:
|
||||
state.data.scoreChange += 10
|
||||
state.data.food = state.data.food.copy()
|
||||
state.data.food[x][y] = False
|
||||
state.data._foodEaten = position
|
||||
# TODO: cache numFood?
|
||||
numFood = state.getNumFood()
|
||||
if numFood == 0 and not state.data._lose:
|
||||
state.data.scoreChange += 500
|
||||
state.data._win = True
|
||||
# Eat capsule
|
||||
if( position in state.getCapsules() ):
|
||||
state.data.capsules.remove( position )
|
||||
state.data._capsuleEaten = position
|
||||
# Reset all ghosts' scared timers
|
||||
for index in range( 1, len( state.data.agentStates ) ):
|
||||
state.data.agentStates[index].scaredTimer = SCARED_TIME
|
||||
consume = staticmethod( consume )
|
||||
|
||||
class GhostRules:
|
||||
"""
|
||||
These functions dictate how ghosts interact with their environment.
|
||||
"""
|
||||
GHOST_SPEED=1.0
|
||||
def getLegalActions( state, ghostIndex ):
|
||||
"""
|
||||
Ghosts cannot stop, and cannot turn around unless they
|
||||
reach a dead end, but can turn 90 degrees at intersections.
|
||||
"""
|
||||
conf = state.getGhostState( ghostIndex ).configuration
|
||||
possibleActions = Actions.getPossibleActions( conf, state.data.layout.walls )
|
||||
reverse = Actions.reverseDirection( conf.direction )
|
||||
if Directions.STOP in possibleActions:
|
||||
possibleActions.remove( Directions.STOP )
|
||||
if reverse in possibleActions and len( possibleActions ) > 1:
|
||||
possibleActions.remove( reverse )
|
||||
return possibleActions
|
||||
getLegalActions = staticmethod( getLegalActions )
|
||||
|
||||
def applyAction( state, action, ghostIndex):
|
||||
|
||||
legal = GhostRules.getLegalActions( state, ghostIndex )
|
||||
if action not in legal:
|
||||
raise Exception("Illegal ghost action " + str(action))
|
||||
|
||||
ghostState = state.data.agentStates[ghostIndex]
|
||||
speed = GhostRules.GHOST_SPEED
|
||||
if ghostState.scaredTimer > 0: speed /= 2.0
|
||||
vector = Actions.directionToVector( action, speed )
|
||||
ghostState.configuration = ghostState.configuration.generateSuccessor( vector )
|
||||
applyAction = staticmethod( applyAction )
|
||||
|
||||
def decrementTimer( ghostState):
|
||||
timer = ghostState.scaredTimer
|
||||
if timer == 1:
|
||||
ghostState.configuration.pos = nearestPoint( ghostState.configuration.pos )
|
||||
ghostState.scaredTimer = max( 0, timer - 1 )
|
||||
decrementTimer = staticmethod( decrementTimer )
|
||||
|
||||
def checkDeath( state, agentIndex):
|
||||
pacmanPosition = state.getPacmanPosition()
|
||||
if agentIndex == 0: # Pacman just moved; Anyone can kill him
|
||||
for index in range( 1, len( state.data.agentStates ) ):
|
||||
ghostState = state.data.agentStates[index]
|
||||
ghostPosition = ghostState.configuration.getPosition()
|
||||
if GhostRules.canKill( pacmanPosition, ghostPosition ):
|
||||
GhostRules.collide( state, ghostState, index )
|
||||
else:
|
||||
ghostState = state.data.agentStates[agentIndex]
|
||||
ghostPosition = ghostState.configuration.getPosition()
|
||||
if GhostRules.canKill( pacmanPosition, ghostPosition ):
|
||||
GhostRules.collide( state, ghostState, agentIndex )
|
||||
checkDeath = staticmethod( checkDeath )
|
||||
|
||||
def collide( state, ghostState, agentIndex):
|
||||
if ghostState.scaredTimer > 0:
|
||||
state.data.scoreChange += 200
|
||||
GhostRules.placeGhost(state, ghostState)
|
||||
ghostState.scaredTimer = 0
|
||||
# Added for first-person
|
||||
state.data._eaten[agentIndex] = True
|
||||
else:
|
||||
if not state.data._win:
|
||||
state.data.scoreChange -= 500
|
||||
state.data._lose = True
|
||||
collide = staticmethod( collide )
|
||||
|
||||
def canKill( pacmanPosition, ghostPosition ):
|
||||
return manhattanDistance( ghostPosition, pacmanPosition ) <= COLLISION_TOLERANCE
|
||||
canKill = staticmethod( canKill )
|
||||
|
||||
def placeGhost(state, ghostState):
|
||||
ghostState.configuration = ghostState.start
|
||||
placeGhost = staticmethod( placeGhost )
|
||||
|
||||
#############################
|
||||
# FRAMEWORK TO START A GAME #
|
||||
#############################
|
||||
|
||||
def default(str):
|
||||
return str + ' [Default: %default]'
|
||||
|
||||
def parseAgentArgs(str):
|
||||
if str == None: return {}
|
||||
pieces = str.split(',')
|
||||
opts = {}
|
||||
for p in pieces:
|
||||
if '=' in p:
|
||||
key, val = p.split('=')
|
||||
else:
|
||||
key,val = p, 1
|
||||
opts[key] = val
|
||||
return opts
|
||||
|
||||
def readCommand( argv ):
|
||||
"""
|
||||
Processes the command used to run pacman from the command line.
|
||||
"""
|
||||
from optparse import OptionParser
|
||||
usageStr = """
|
||||
USAGE: python pacman.py <options>
|
||||
EXAMPLES: (1) python pacman.py
|
||||
- starts an interactive game
|
||||
(2) python pacman.py --layout smallClassic --zoom 2
|
||||
OR python pacman.py -l smallClassic -z 2
|
||||
- starts an interactive game on a smaller board, zoomed in
|
||||
"""
|
||||
parser = OptionParser(usageStr)
|
||||
|
||||
parser.add_option('-n', '--numGames', dest='numGames', type='int',
|
||||
help=default('the number of GAMES to play'), metavar='GAMES', default=1)
|
||||
parser.add_option('-l', '--layout', dest='layout',
|
||||
help=default('the LAYOUT_FILE from which to load the map layout'),
|
||||
metavar='LAYOUT_FILE', default='mediumClassic')
|
||||
parser.add_option('-p', '--pacman', dest='pacman',
|
||||
help=default('the agent TYPE in the pacmanAgents module to use'),
|
||||
metavar='TYPE', default='KeyboardAgent')
|
||||
parser.add_option('-t', '--textGraphics', action='store_true', dest='textGraphics',
|
||||
help='Display output as text only', default=False)
|
||||
parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics',
|
||||
help='Generate minimal output and no graphics', default=False)
|
||||
parser.add_option('-g', '--ghosts', dest='ghost',
|
||||
help=default('the ghost agent TYPE in the ghostAgents module to use'),
|
||||
metavar = 'TYPE', default='RandomGhost')
|
||||
parser.add_option('-k', '--numghosts', type='int', dest='numGhosts',
|
||||
help=default('The maximum number of ghosts to use'), default=4)
|
||||
parser.add_option('-z', '--zoom', type='float', dest='zoom',
|
||||
help=default('Zoom the size of the graphics window'), default=1.0)
|
||||
parser.add_option('-f', '--fixRandomSeed', action='store_true', dest='fixRandomSeed',
|
||||
help='Fixes the random seed to always play the same game', default=False)
|
||||
parser.add_option('-r', '--recordActions', action='store_true', dest='record',
|
||||
help='Writes game histories to a file (named by the time they were played)', default=False)
|
||||
parser.add_option('--replay', dest='gameToReplay',
|
||||
help='A recorded game file (pickle) to replay', default=None)
|
||||
parser.add_option('-a','--agentArgs',dest='agentArgs',
|
||||
help='Comma separated values sent to agent. e.g. "opt1=val1,opt2,opt3=val3"')
|
||||
parser.add_option('-x', '--numTraining', dest='numTraining', type='int',
|
||||
help=default('How many episodes are training (suppresses output)'), default=0)
|
||||
parser.add_option('--frameTime', dest='frameTime', type='float',
|
||||
help=default('Time to delay between frames; <0 means keyboard'), default=0.1)
|
||||
parser.add_option('-c', '--catchExceptions', action='store_true', dest='catchExceptions',
|
||||
help='Turns on exception handling and timeouts during games', default=False)
|
||||
parser.add_option('--timeout', dest='timeout', type='int',
|
||||
help=default('Maximum length of time an agent can spend computing in a single game'), default=30)
|
||||
|
||||
options, otherjunk = parser.parse_args(argv)
|
||||
if len(otherjunk) != 0:
|
||||
raise Exception('Command line input not understood: ' + str(otherjunk))
|
||||
args = dict()
|
||||
|
||||
# Fix the random seed
|
||||
if options.fixRandomSeed: random.seed('cs188')
|
||||
|
||||
# Choose a layout
|
||||
args['layout'] = layout.getLayout( options.layout )
|
||||
if args['layout'] == None: raise Exception("The layout " + options.layout + " cannot be found")
|
||||
|
||||
# Choose a Pacman agent
|
||||
noKeyboard = options.gameToReplay == None and (options.textGraphics or options.quietGraphics)
|
||||
pacmanType = loadAgent(options.pacman, noKeyboard)
|
||||
agentOpts = parseAgentArgs(options.agentArgs)
|
||||
if options.numTraining > 0:
|
||||
args['numTraining'] = options.numTraining
|
||||
if 'numTraining' not in agentOpts: agentOpts['numTraining'] = options.numTraining
|
||||
pacman = pacmanType(**agentOpts) # Instantiate Pacman with agentArgs
|
||||
args['pacman'] = pacman
|
||||
|
||||
# Don't display training games
|
||||
if 'numTrain' in agentOpts:
|
||||
options.numQuiet = int(agentOpts['numTrain'])
|
||||
options.numIgnore = int(agentOpts['numTrain'])
|
||||
|
||||
# Choose a ghost agent
|
||||
ghostType = loadAgent(options.ghost, noKeyboard)
|
||||
args['ghosts'] = [ghostType( i+1 ) for i in range( options.numGhosts )]
|
||||
|
||||
# Choose a display format
|
||||
if options.quietGraphics:
|
||||
import textDisplay
|
||||
args['display'] = textDisplay.NullGraphics()
|
||||
elif options.textGraphics:
|
||||
import textDisplay
|
||||
textDisplay.SLEEP_TIME = options.frameTime
|
||||
args['display'] = textDisplay.PacmanGraphics()
|
||||
else:
|
||||
import graphicsDisplay
|
||||
args['display'] = graphicsDisplay.PacmanGraphics(options.zoom, frameTime = options.frameTime)
|
||||
args['numGames'] = options.numGames
|
||||
args['record'] = options.record
|
||||
args['catchExceptions'] = options.catchExceptions
|
||||
args['timeout'] = options.timeout
|
||||
|
||||
# Special case: recorded games don't use the runGames method or args structure
|
||||
if options.gameToReplay != None:
|
||||
print 'Replaying recorded game %s.' % options.gameToReplay
|
||||
import cPickle
|
||||
f = open(options.gameToReplay)
|
||||
try: recorded = cPickle.load(f)
|
||||
finally: f.close()
|
||||
recorded['display'] = args['display']
|
||||
replayGame(**recorded)
|
||||
sys.exit(0)
|
||||
|
||||
return args
|
||||
|
||||
def loadAgent(pacman, nographics):
|
||||
# Looks through all pythonPath Directories for the right module,
|
||||
pythonPathStr = os.path.expandvars("$PYTHONPATH")
|
||||
if pythonPathStr.find(';') == -1:
|
||||
pythonPathDirs = pythonPathStr.split(':')
|
||||
else:
|
||||
pythonPathDirs = pythonPathStr.split(';')
|
||||
pythonPathDirs.append('.')
|
||||
|
||||
for moduleDir in pythonPathDirs:
|
||||
if not os.path.isdir(moduleDir): continue
|
||||
moduleNames = [f for f in os.listdir(moduleDir) if f.endswith('gents.py')]
|
||||
for modulename in moduleNames:
|
||||
try:
|
||||
module = __import__(modulename[:-3])
|
||||
except ImportError:
|
||||
continue
|
||||
if pacman in dir(module):
|
||||
if nographics and modulename == 'keyboardAgents.py':
|
||||
raise Exception('Using the keyboard requires graphics (not text display)')
|
||||
return getattr(module, pacman)
|
||||
raise Exception('The agent ' + pacman + ' is not specified in any *Agents.py.')
|
||||
|
||||
def replayGame( layout, actions, display ):
|
||||
import pacmanAgents, ghostAgents
|
||||
rules = ClassicGameRules()
|
||||
agents = [pacmanAgents.GreedyAgent()] + [ghostAgents.RandomGhost(i+1) for i in range(layout.getNumGhosts())]
|
||||
game = rules.newGame( layout, agents[0], agents[1:], display )
|
||||
state = game.state
|
||||
display.initialize(state.data)
|
||||
|
||||
for action in actions:
|
||||
# Execute the action
|
||||
state = state.generateSuccessor( *action )
|
||||
# Change the display
|
||||
display.update( state.data )
|
||||
# Allow for game specific conditions (winning, losing, etc.)
|
||||
rules.process(state, game)
|
||||
|
||||
display.finish()
|
||||
|
||||
def runGames( layout, pacman, ghosts, display, numGames, record, numTraining = 0, catchExceptions=False, timeout=30 ):
|
||||
import __main__
|
||||
__main__.__dict__['_display'] = display
|
||||
|
||||
rules = ClassicGameRules(timeout)
|
||||
games = []
|
||||
|
||||
for i in range( numGames ):
|
||||
beQuiet = i < numTraining
|
||||
if beQuiet:
|
||||
# Suppress output and graphics
|
||||
import textDisplay
|
||||
gameDisplay = textDisplay.NullGraphics()
|
||||
rules.quiet = True
|
||||
else:
|
||||
gameDisplay = display
|
||||
rules.quiet = False
|
||||
game = rules.newGame( layout, pacman, ghosts, gameDisplay, beQuiet, catchExceptions)
|
||||
game.run()
|
||||
if not beQuiet: games.append(game)
|
||||
|
||||
if record:
|
||||
import time, cPickle
|
||||
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}
|
||||
cPickle.dump(components, f)
|
||||
f.close()
|
||||
|
||||
if (numGames-numTraining) > 0:
|
||||
scores = [game.state.getScore() for game in games]
|
||||
wins = [game.state.isWin() for game in games]
|
||||
winRate = wins.count(True)/ float(len(wins))
|
||||
print 'Average Score:', sum(scores) / float(len(scores))
|
||||
print 'Scores: ', ', '.join([str(score) for score in scores])
|
||||
print 'Win Rate: %d/%d (%.2f)' % (wins.count(True), len(wins), winRate)
|
||||
print 'Record: ', ', '.join([ ['Loss', 'Win'][int(w)] for w in wins])
|
||||
|
||||
return games
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""
|
||||
The main function called when pacman.py is run
|
||||
from the command line:
|
||||
|
||||
> python pacman.py
|
||||
|
||||
See the usage string for more details.
|
||||
|
||||
> python pacman.py --help
|
||||
"""
|
||||
args = readCommand( sys.argv[1:] ) # Get game components based on input
|
||||
runGames( **args )
|
||||
|
||||
# import cProfile
|
||||
# cProfile.run("runGames( **args )")
|
||||
pass
|
52
p2_multiagent/pacmanAgents.py
Normal file
52
p2_multiagent/pacmanAgents.py
Normal file
@ -0,0 +1,52 @@
|
||||
# pacmanAgents.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 pacman import Directions
|
||||
from game import Agent
|
||||
import random
|
||||
import game
|
||||
import util
|
||||
|
||||
class LeftTurnAgent(game.Agent):
|
||||
"An agent that turns left at every opportunity"
|
||||
|
||||
def getAction(self, state):
|
||||
legal = state.getLegalPacmanActions()
|
||||
current = state.getPacmanState().configuration.direction
|
||||
if current == Directions.STOP: current = Directions.NORTH
|
||||
left = Directions.LEFT[current]
|
||||
if left in legal: return left
|
||||
if current in legal: 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
|
||||
|
||||
class GreedyAgent(Agent):
|
||||
def __init__(self, evalFn="scoreEvaluation"):
|
||||
self.evaluationFunction = util.lookup(evalFn, globals())
|
||||
assert self.evaluationFunction != None
|
||||
|
||||
def getAction(self, state):
|
||||
# Generate candidate actions
|
||||
legal = state.getLegalPacmanActions()
|
||||
if Directions.STOP in legal: legal.remove(Directions.STOP)
|
||||
|
||||
successors = [(state.generateSuccessor(0, action), action) for action in legal]
|
||||
scored = [(self.evaluationFunction(state), action) for state, action in successors]
|
||||
bestScore = max(scored)[0]
|
||||
bestActions = [pair[1] for pair in scored if pair[0] == bestScore]
|
||||
return random.choice(bestActions)
|
||||
|
||||
def scoreEvaluation(state):
|
||||
return state.getScore()
|
18
p2_multiagent/projectParams.py
Normal file
18
p2_multiagent/projectParams.py
Normal file
@ -0,0 +1,18 @@
|
||||
# projectParams.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).
|
||||
|
||||
|
||||
STUDENT_CODE_DEFAULT = 'multiAgents.py'
|
||||
PROJECT_TEST_CLASSES = 'multiagentTestClasses.py'
|
||||
PROJECT_NAME = 'Project 2: Multiagent search'
|
||||
BONUS_PIC = False
|
189
p2_multiagent/testClasses.py
Normal file
189
p2_multiagent/testClasses.py
Normal file
@ -0,0 +1,189 @@
|
||||
# testClasses.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 modules from python standard library
|
||||
import inspect
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
# Class which models a question in a project. Note that questions have a
|
||||
# maximum number of points they are worth, and are composed of a series of
|
||||
# test cases
|
||||
class Question(object):
|
||||
|
||||
def raiseNotDefined(self):
|
||||
print 'Method not implemented: %s' % inspect.stack()[1][3]
|
||||
sys.exit(1)
|
||||
|
||||
def __init__(self, questionDict, display):
|
||||
self.maxPoints = int(questionDict['max_points'])
|
||||
self.testCases = []
|
||||
self.display = display
|
||||
|
||||
def getDisplay(self):
|
||||
return self.display
|
||||
|
||||
def getMaxPoints(self):
|
||||
return self.maxPoints
|
||||
|
||||
# Note that 'thunk' must be a function which accepts a single argument,
|
||||
# namely a 'grading' object
|
||||
def addTestCase(self, testCase, thunk):
|
||||
self.testCases.append((testCase, thunk))
|
||||
|
||||
def execute(self, grades):
|
||||
self.raiseNotDefined()
|
||||
|
||||
# Question in which all test cases must be passed in order to receive credit
|
||||
class PassAllTestsQuestion(Question):
|
||||
|
||||
def execute(self, grades):
|
||||
# TODO: is this the right way to use grades? The autograder doesn't seem to use it.
|
||||
testsFailed = False
|
||||
grades.assignZeroCredit()
|
||||
for _, f in self.testCases:
|
||||
if not f(grades):
|
||||
testsFailed = True
|
||||
if testsFailed:
|
||||
grades.fail("Tests failed.")
|
||||
else:
|
||||
grades.assignFullCredit()
|
||||
|
||||
|
||||
# Question in which predict credit is given for test cases with a ``points'' property.
|
||||
# All other tests are mandatory and must be passed.
|
||||
class HackedPartialCreditQuestion(Question):
|
||||
|
||||
def execute(self, grades):
|
||||
# TODO: is this the right way to use grades? The autograder doesn't seem to use it.
|
||||
grades.assignZeroCredit()
|
||||
|
||||
points = 0
|
||||
passed = True
|
||||
for testCase, f in self.testCases:
|
||||
testResult = f(grades)
|
||||
if "points" in testCase.testDict:
|
||||
if testResult: points += float(testCase.testDict["points"])
|
||||
else:
|
||||
passed = passed and testResult
|
||||
|
||||
## FIXME: Below terrible hack to match q3's logic
|
||||
if int(points) == self.maxPoints and not passed:
|
||||
grades.assignZeroCredit()
|
||||
else:
|
||||
grades.addPoints(int(points))
|
||||
|
||||
|
||||
class Q6PartialCreditQuestion(Question):
|
||||
"""Fails any test which returns False, otherwise doesn't effect the grades object.
|
||||
Partial credit tests will add the required points."""
|
||||
|
||||
def execute(self, grades):
|
||||
grades.assignZeroCredit()
|
||||
|
||||
results = []
|
||||
for _, f in self.testCases:
|
||||
results.append(f(grades))
|
||||
if False in results:
|
||||
grades.assignZeroCredit()
|
||||
|
||||
class PartialCreditQuestion(Question):
|
||||
"""Fails any test which returns False, otherwise doesn't effect the grades object.
|
||||
Partial credit tests will add the required points."""
|
||||
|
||||
def execute(self, grades):
|
||||
grades.assignZeroCredit()
|
||||
|
||||
for _, f in self.testCases:
|
||||
if not f(grades):
|
||||
grades.assignZeroCredit()
|
||||
grades.fail("Tests failed.")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class NumberPassedQuestion(Question):
|
||||
"""Grade is the number of test cases passed."""
|
||||
|
||||
def execute(self, grades):
|
||||
grades.addPoints([f(grades) for _, f in self.testCases].count(True))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Template modeling a generic test case
|
||||
class TestCase(object):
|
||||
|
||||
def raiseNotDefined(self):
|
||||
print 'Method not implemented: %s' % inspect.stack()[1][3]
|
||||
sys.exit(1)
|
||||
|
||||
def getPath(self):
|
||||
return self.path
|
||||
|
||||
def __init__(self, question, testDict):
|
||||
self.question = question
|
||||
self.testDict = testDict
|
||||
self.path = testDict['path']
|
||||
self.messages = []
|
||||
|
||||
def __str__(self):
|
||||
self.raiseNotDefined()
|
||||
|
||||
def execute(self, grades, moduleDict, solutionDict):
|
||||
self.raiseNotDefined()
|
||||
|
||||
def writeSolution(self, moduleDict, filePath):
|
||||
self.raiseNotDefined()
|
||||
return True
|
||||
|
||||
# Tests should call the following messages for grading
|
||||
# to ensure a uniform format for test output.
|
||||
#
|
||||
# TODO: this is hairy, but we need to fix grading.py's interface
|
||||
# to get a nice hierarchical project - question - test structure,
|
||||
# then these should be moved into Question proper.
|
||||
def testPass(self, grades):
|
||||
grades.addMessage('PASS: %s' % (self.path,))
|
||||
for line in self.messages:
|
||||
grades.addMessage(' %s' % (line,))
|
||||
return True
|
||||
|
||||
def testFail(self, grades):
|
||||
grades.addMessage('FAIL: %s' % (self.path,))
|
||||
for line in self.messages:
|
||||
grades.addMessage(' %s' % (line,))
|
||||
return False
|
||||
|
||||
# This should really be question level?
|
||||
#
|
||||
def testPartial(self, grades, points, maxPoints):
|
||||
grades.addPoints(points)
|
||||
extraCredit = max(0, points - maxPoints)
|
||||
regularCredit = points - extraCredit
|
||||
|
||||
grades.addMessage('%s: %s (%s of %s points)' % ("PASS" if points >= maxPoints else "FAIL", self.path, regularCredit, maxPoints))
|
||||
if extraCredit > 0:
|
||||
grades.addMessage('EXTRA CREDIT: %s points' % (extraCredit,))
|
||||
|
||||
for line in self.messages:
|
||||
grades.addMessage(' %s' % (line,))
|
||||
|
||||
return True
|
||||
|
||||
def addMessage(self, message):
|
||||
self.messages.extend(message.split('\n'))
|
||||
|
85
p2_multiagent/testParser.py
Normal file
85
p2_multiagent/testParser.py
Normal file
@ -0,0 +1,85 @@
|
||||
# testParser.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 re
|
||||
import sys
|
||||
|
||||
class TestParser(object):
|
||||
|
||||
def __init__(self, path):
|
||||
# save the path to the test file
|
||||
self.path = path
|
||||
|
||||
def removeComments(self, rawlines):
|
||||
# remove any portion of a line following a '#' symbol
|
||||
fixed_lines = []
|
||||
for l in rawlines:
|
||||
idx = l.find('#')
|
||||
if idx == -1:
|
||||
fixed_lines.append(l)
|
||||
else:
|
||||
fixed_lines.append(l[0:idx])
|
||||
return '\n'.join(fixed_lines)
|
||||
|
||||
def parse(self):
|
||||
# read in the test case and remove comments
|
||||
test = {}
|
||||
with open(self.path) as handle:
|
||||
raw_lines = handle.read().split('\n')
|
||||
|
||||
test_text = self.removeComments(raw_lines)
|
||||
test['__raw_lines__'] = raw_lines
|
||||
test['path'] = self.path
|
||||
test['__emit__'] = []
|
||||
lines = test_text.split('\n')
|
||||
i = 0
|
||||
# read a property in each loop cycle
|
||||
while(i < len(lines)):
|
||||
# skip blank lines
|
||||
if re.match('\A\s*\Z', lines[i]):
|
||||
test['__emit__'].append(("raw", raw_lines[i]))
|
||||
i += 1
|
||||
continue
|
||||
m = re.match('\A([^"]*?):\s*"([^"]*)"\s*\Z', lines[i])
|
||||
if m:
|
||||
test[m.group(1)] = m.group(2)
|
||||
test['__emit__'].append(("oneline", m.group(1)))
|
||||
i += 1
|
||||
continue
|
||||
m = re.match('\A([^"]*?):\s*"""\s*\Z', lines[i])
|
||||
if m:
|
||||
msg = []
|
||||
i += 1
|
||||
while(not re.match('\A\s*"""\s*\Z', lines[i])):
|
||||
msg.append(raw_lines[i])
|
||||
i += 1
|
||||
test[m.group(1)] = '\n'.join(msg)
|
||||
test['__emit__'].append(("multiline", m.group(1)))
|
||||
i += 1
|
||||
continue
|
||||
print 'error parsing test file: %s' % self.path
|
||||
sys.exit(1)
|
||||
return test
|
||||
|
||||
|
||||
def emitTestDict(testDict, handle):
|
||||
for kind, data in testDict['__emit__']:
|
||||
if kind == "raw":
|
||||
handle.write(data + "\n")
|
||||
elif kind == "oneline":
|
||||
handle.write('%s: "%s"\n' % (data, testDict[data]))
|
||||
elif kind == "multiline":
|
||||
handle.write('%s: """\n%s\n"""\n' % (data, testDict[data]))
|
||||
else:
|
||||
raise Exception("Bad __emit__")
|
1
p2_multiagent/test_cases/CONFIG
Normal file
1
p2_multiagent/test_cases/CONFIG
Normal file
@ -0,0 +1 @@
|
||||
order: "q1 q2 q3 q4 q5"
|
2
p2_multiagent/test_cases/extra/CONFIG
Normal file
2
p2_multiagent/test_cases/extra/CONFIG
Normal file
@ -0,0 +1,2 @@
|
||||
max_points: "0"
|
||||
class: "PartialCreditQuestion"
|
11
p2_multiagent/test_cases/extra/grade-agent.test
Normal file
11
p2_multiagent/test_cases/extra/grade-agent.test
Normal file
@ -0,0 +1,11 @@
|
||||
class: "EvalAgentTest"
|
||||
|
||||
agentName: "ContestAgent"
|
||||
layoutName: "contestClassic"
|
||||
maxTime: "180"
|
||||
numGames: "5"
|
||||
|
||||
scoreThresholds: "2500 2900"
|
||||
|
||||
randomSeed: "0"
|
||||
ghosts: "[DirectionalGhost(1), DirectionalGhost(2), DirectionalGhost(3)]"
|
2
p2_multiagent/test_cases/q1/CONFIG
Normal file
2
p2_multiagent/test_cases/q1/CONFIG
Normal file
@ -0,0 +1,2 @@
|
||||
max_points: "4"
|
||||
class: "PartialCreditQuestion"
|
2
p2_multiagent/test_cases/q1/grade-agent.solution
Normal file
2
p2_multiagent/test_cases/q1/grade-agent.solution
Normal file
@ -0,0 +1,2 @@
|
||||
# This is the solution file for test_cases/q1/grade-agent.test.
|
||||
# File intentionally blank.
|
18
p2_multiagent/test_cases/q1/grade-agent.test
Normal file
18
p2_multiagent/test_cases/q1/grade-agent.test
Normal file
@ -0,0 +1,18 @@
|
||||
class: "EvalAgentTest"
|
||||
|
||||
agentName: "ReflexAgent"
|
||||
layoutName: "openClassic"
|
||||
maxTime: "120"
|
||||
numGames: "10"
|
||||
|
||||
|
||||
nonTimeoutMinimum: "10"
|
||||
|
||||
scoreThresholds: "500 1000"
|
||||
|
||||
winsMinimum: "1"
|
||||
winsThresholds: "5 10"
|
||||
|
||||
|
||||
randomSeed: "0"
|
||||
ghosts: "[RandomGhost(1)]"
|
3
p2_multiagent/test_cases/q2/0-lecture-6-tree.solution
Normal file
3
p2_multiagent/test_cases/q2/0-lecture-6-tree.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/0-lecture-6-tree.test.
|
||||
action: "Center"
|
||||
generated: "A B C D E F G H I max min1 min2 min3"
|
50
p2_multiagent/test_cases/q2/0-lecture-6-tree.test
Normal file
50
p2_multiagent/test_cases/q2/0-lecture-6-tree.test
Normal file
@ -0,0 +1,50 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
# Tree from lecture 6 slides
|
||||
diagram: """
|
||||
max
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
/ | \
|
||||
min1 min2 min3
|
||||
/|\ /|\ /|\
|
||||
/ | \ / | \ / | \
|
||||
A B C D E F G H I
|
||||
3 12 8 5 4 6 14 1 11
|
||||
"""
|
||||
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "max"
|
||||
win_states: "A B C D E F G H I"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
max Left min1
|
||||
max Center min2
|
||||
max Right min3
|
||||
min1 Left A
|
||||
min1 Center B
|
||||
min1 Right C
|
||||
min2 Left D
|
||||
min2 Center E
|
||||
min2 Right F
|
||||
min3 Left G
|
||||
min3 Center H
|
||||
min3 Right I
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
A 3.0
|
||||
B 12.0
|
||||
C 8.0
|
||||
D 5.0
|
||||
E 4.0
|
||||
F 6.0
|
||||
G 14.0
|
||||
H 1.0
|
||||
I 11.0
|
||||
"""
|
3
p2_multiagent/test_cases/q2/0-small-tree.solution
Normal file
3
p2_multiagent/test_cases/q2/0-small-tree.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/0-small-tree.test.
|
||||
action: "pacLeft"
|
||||
generated: "A B C D deeper minLeft minRight root"
|
36
p2_multiagent/test_cases/q2/0-small-tree.test
Normal file
36
p2_multiagent/test_cases/q2/0-small-tree.test
Normal file
@ -0,0 +1,36 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
root
|
||||
/ \
|
||||
minLeft minRight
|
||||
/ \ / \
|
||||
A B C deeper
|
||||
4 3 2 |
|
||||
D
|
||||
1000
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "root"
|
||||
win_states: "A C"
|
||||
lose_states: "B D"
|
||||
|
||||
successors: """
|
||||
root pacLeft minLeft
|
||||
root pacRight minRight
|
||||
minLeft gLeft A
|
||||
minLeft gRight B
|
||||
minRight gLeft C
|
||||
minRight gRight deeper
|
||||
deeper pacLeft D
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A 4.0
|
||||
B 3.0
|
||||
C 2.0
|
||||
D 1000.0
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-1-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-1-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-1-minmax.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
|
47
p2_multiagent/test_cases/q2/1-1-minmax.test
Normal file
47
p2_multiagent/test_cases/q2/1-1-minmax.test
Normal file
@ -0,0 +1,47 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
c1 c2 cx
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -3.01
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -3.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-2-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-2-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-2-minmax.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
|
47
p2_multiagent/test_cases/q2/1-2-minmax.test
Normal file
47
p2_multiagent/test_cases/q2/1-2-minmax.test
Normal file
@ -0,0 +1,47 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
c1 c2 cx
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -2.99
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -2.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-3-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-3-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-3-minmax.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
|
47
p2_multiagent/test_cases/q2/1-3-minmax.test
Normal file
47
p2_multiagent/test_cases/q2/1-3-minmax.test
Normal file
@ -0,0 +1,47 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
cx c3 c4
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
4.01 4 -7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b2 is 4.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 d5 d6 d7 d8 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Down cx
|
||||
b2 Left c3
|
||||
b2 Right c4
|
||||
c3 Left d5
|
||||
c3 Right d6
|
||||
c4 Left d7
|
||||
c4 Right d8
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d5 4.0
|
||||
d6 -7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
dx 4.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-4-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-4-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-4-minmax.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
|
47
p2_multiagent/test_cases/q2/1-4-minmax.test
Normal file
47
p2_multiagent/test_cases/q2/1-4-minmax.test
Normal file
@ -0,0 +1,47 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
cx c3 c4
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
3.99 4 -7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b2 is 4.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 d5 d6 d7 d8 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Down cx
|
||||
b2 Left c3
|
||||
b2 Right c4
|
||||
c3 Left d5
|
||||
c3 Right d6
|
||||
c4 Left d7
|
||||
c4 Right d8
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d5 4.0
|
||||
d6 -7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
dx 3.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-5-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-5-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-5-minmax.test.
|
||||
action: "Right"
|
||||
generated: "A B C D E F G H Z a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
|
75
p2_multiagent/test_cases/q2/1-5-minmax.test
Normal file
75
p2_multiagent/test_cases/q2/1-5-minmax.test
Normal file
@ -0,0 +1,75 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "4"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
c1 c2 cx
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
/ \ / \ / \ / \ |
|
||||
A B C D E F G H Z
|
||||
-3 13 5 9 10 3 -6 8 3.01
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
d - min
|
||||
|
||||
Note the minimax value of b1 is 3.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "A B C D E F G H I J K L M N O P Z"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
c3 Left d5
|
||||
c3 Right d6
|
||||
c4 Left d7
|
||||
c4 Right d8
|
||||
cx Down dx
|
||||
d1 Left A
|
||||
d1 Right B
|
||||
d2 Left C
|
||||
d2 Right D
|
||||
d3 Left E
|
||||
d3 Right F
|
||||
d4 Left G
|
||||
d4 Right H
|
||||
d5 Left I
|
||||
d5 Right J
|
||||
d6 Left K
|
||||
d6 Right L
|
||||
d7 Left M
|
||||
d7 Right N
|
||||
d8 Left O
|
||||
d8 Right P
|
||||
dx Down Z
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A -3.0
|
||||
B 13.0
|
||||
C 5.0
|
||||
D 9.0
|
||||
E 10.0
|
||||
F 3.0
|
||||
G -6.0
|
||||
H 8.0
|
||||
Z 3.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-6-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-6-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-6-minmax.test.
|
||||
action: "Left"
|
||||
generated: "A B C D E F G H Z a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
|
75
p2_multiagent/test_cases/q2/1-6-minmax.test
Normal file
75
p2_multiagent/test_cases/q2/1-6-minmax.test
Normal file
@ -0,0 +1,75 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "4"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
c1 c2 cx
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
/ \ / \ / \ / \ |
|
||||
A B C D E F G H Z
|
||||
-3 13 5 9 10 3 -6 8 2.99
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
d - min
|
||||
|
||||
Note the minimax value of b1 is 3.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "A B C D E F G H I J K L M N O P Z"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
c3 Left d5
|
||||
c3 Right d6
|
||||
c4 Left d7
|
||||
c4 Right d8
|
||||
cx Down dx
|
||||
d1 Left A
|
||||
d1 Right B
|
||||
d2 Left C
|
||||
d2 Right D
|
||||
d3 Left E
|
||||
d3 Right F
|
||||
d4 Left G
|
||||
d4 Right H
|
||||
d5 Left I
|
||||
d5 Right J
|
||||
d6 Left K
|
||||
d6 Right L
|
||||
d7 Left M
|
||||
d7 Right N
|
||||
d8 Left O
|
||||
d8 Right P
|
||||
dx Down Z
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A -3.0
|
||||
B 13.0
|
||||
C 5.0
|
||||
D 9.0
|
||||
E 10.0
|
||||
F 3.0
|
||||
G -6.0
|
||||
H 8.0
|
||||
Z 2.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-7-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-7-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-7-minmax.test.
|
||||
action: "Left"
|
||||
generated: "I J K L M N O P Z a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
|
75
p2_multiagent/test_cases/q2/1-7-minmax.test
Normal file
75
p2_multiagent/test_cases/q2/1-7-minmax.test
Normal file
@ -0,0 +1,75 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "4"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
cx c3 c4
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
| / \ / \ / \ / \
|
||||
Z I J K L M N O P
|
||||
-1.99 -1 -9 4 7 2 5 -3 -2
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
d - max
|
||||
|
||||
Note that the minimax value of b2 is -2
|
||||
"""
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "A B C D E F G H I J K L M N O P Z"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Down cx
|
||||
b2 Left c3
|
||||
b2 Right c4
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
c3 Left d5
|
||||
c3 Right d6
|
||||
c4 Left d7
|
||||
c4 Right d8
|
||||
cx Down dx
|
||||
d1 Left A
|
||||
d1 Right B
|
||||
d2 Left C
|
||||
d2 Right D
|
||||
d3 Left E
|
||||
d3 Right F
|
||||
d4 Left G
|
||||
d4 Right H
|
||||
d5 Left I
|
||||
d5 Right J
|
||||
d6 Left K
|
||||
d6 Right L
|
||||
d7 Left M
|
||||
d7 Right N
|
||||
d8 Left O
|
||||
d8 Right P
|
||||
dx Down Z
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
I -1.0
|
||||
J -9.0
|
||||
K 4.0
|
||||
L 7.0
|
||||
M 2.0
|
||||
N 5.0
|
||||
O -3.0
|
||||
P -2.0
|
||||
Z -1.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/1-8-minmax.solution
Normal file
3
p2_multiagent/test_cases/q2/1-8-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/1-8-minmax.test.
|
||||
action: "Right"
|
||||
generated: "I J K L M N O P Z a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
|
75
p2_multiagent/test_cases/q2/1-8-minmax.test
Normal file
75
p2_multiagent/test_cases/q2/1-8-minmax.test
Normal file
@ -0,0 +1,75 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "4"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
cx c3 c4
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
| / \ / \ / \ / \
|
||||
Z I J K L M N O P
|
||||
-2.01 -1 -9 4 7 2 5 -3 -2
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
d - max
|
||||
|
||||
Note that the minimax value of b2 is -2.01
|
||||
"""
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "A B C D E F G H I J K L M N O P Z"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Down cx
|
||||
b2 Left c3
|
||||
b2 Right c4
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
c3 Left d5
|
||||
c3 Right d6
|
||||
c4 Left d7
|
||||
c4 Right d8
|
||||
cx Down dx
|
||||
d1 Left A
|
||||
d1 Right B
|
||||
d2 Left C
|
||||
d2 Right D
|
||||
d3 Left E
|
||||
d3 Right F
|
||||
d4 Left G
|
||||
d4 Right H
|
||||
d5 Left I
|
||||
d5 Right J
|
||||
d6 Left K
|
||||
d6 Right L
|
||||
d7 Left M
|
||||
d7 Right N
|
||||
d8 Left O
|
||||
d8 Right P
|
||||
dx Down Z
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
I -1.0
|
||||
J -9.0
|
||||
K 4.0
|
||||
L 7.0
|
||||
M 2.0
|
||||
N 5.0
|
||||
O -3.0
|
||||
P -2.0
|
||||
Z -2.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-1a-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-1a-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-1a-vary-depth.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 cx"
|
52
p2_multiagent/test_cases/q2/2-1a-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-1a-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "1"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
-4 c1 c2 9 cx -4.01
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -4.01
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
|
||||
The values next to c1, c2, and cx are the values of the evaluation function, not
|
||||
necessarily the correct minimax backup.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
c1 -4.0
|
||||
c2 9.0
|
||||
cx -4.01
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -4.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-1b-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-1b-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-1b-vary-depth.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
|
52
p2_multiagent/test_cases/q2/2-1b-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-1b-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
-4 c1 c2 9 cx -4.01
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -4.01
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
|
||||
The values next to c1, c2, and cx are the values of the evaluation function, not
|
||||
necessarily the correct minimax backup.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
c1 -4.0
|
||||
c2 9.0
|
||||
cx -4.01
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -4.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-2a-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-2a-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-2a-vary-depth.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 c1 c2 cx"
|
52
p2_multiagent/test_cases/q2/2-2a-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-2a-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "1"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
-4 c1 c2 9 cx -3.99
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -3.99
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
|
||||
The values next to c1, c2, and cx are the values of the evaluation function, not
|
||||
necessarily the correct minimax backup.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
c1 -4.0
|
||||
c2 9.0
|
||||
cx -3.99
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -3.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-2b-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-2b-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-2b-vary-depth.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 cx d1 d2 d3 d4 dx"
|
52
p2_multiagent/test_cases/q2/2-2b-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-2b-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
-4 c1 c2 9 cx -3.99
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -3.99
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3, but the depth=1 limited value is -4.
|
||||
The values next to c1, c2, and cx are the values of the evaluation function, not
|
||||
necessarily the correct minimax backup.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
c1 -4.0
|
||||
c2 9.0
|
||||
cx -3.99
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -3.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-3a-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-3a-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-3a-vary-depth.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c3 c4 cx"
|
52
p2_multiagent/test_cases/q2/2-3a-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-3a-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "1"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
5.01 cx 8 c3 c4 5
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
5.01 4 -7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is 4, but the depth=1 limited value is 5.
|
||||
The values next to c3, c4, 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 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: """
|
||||
c3 8.0
|
||||
c4 5.0
|
||||
cx 5.01
|
||||
d5 4.0
|
||||
d6 -7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
dx 5.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-3b-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-3b-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-3b-vary-depth.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
|
52
p2_multiagent/test_cases/q2/2-3b-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-3b-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
5.01 cx 8 c3 c4 5
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
5.01 4 -7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is 4, but the depth=1 limited value is 5.
|
||||
The values next to c3, c4, 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 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: """
|
||||
c3 8.0
|
||||
c4 5.0
|
||||
cx 5.01
|
||||
d5 4.0
|
||||
d6 -7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
dx 5.01
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-4a-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-4a-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-4a-vary-depth.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 c3 c4 cx"
|
52
p2_multiagent/test_cases/q2/2-4a-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-4a-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "1"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
4.99 cx 8 c3 c4 5
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
4.99 4 -7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is 4, but the depth=1 limited value is 5.
|
||||
The values next to c3, c4, 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 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: """
|
||||
c3 8.0
|
||||
c4 5.0
|
||||
cx 4.99
|
||||
d5 4.0
|
||||
d6 -7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
dx 4.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-4b-vary-depth.solution
Normal file
3
p2_multiagent/test_cases/q2/2-4b-vary-depth.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-4b-vary-depth.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c3 c4 cx d5 d6 d7 d8 dx"
|
52
p2_multiagent/test_cases/q2/2-4b-vary-depth.test
Normal file
52
p2_multiagent/test_cases/q2/2-4b-vary-depth.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
| / \
|
||||
4.99 cx 8 c3 c4 5
|
||||
| / \ / \
|
||||
dx d5 d6 d7 d8
|
||||
4.99 4 -7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is 4, but the depth=1 limited value is 5.
|
||||
The values next to c3, c4, 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 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: """
|
||||
c3 8.0
|
||||
c4 5.0
|
||||
cx 4.99
|
||||
d5 4.0
|
||||
d6 -7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
dx 4.99
|
||||
"""
|
3
p2_multiagent/test_cases/q2/2-one-ghost-3level.solution
Normal file
3
p2_multiagent/test_cases/q2/2-one-ghost-3level.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/2-one-ghost-3level.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 c3 c4 d1 d2 d3 d4 d5 d6 d7 d8"
|
52
p2_multiagent/test_cases/q2/2-one-ghost-3level.test
Normal file
52
p2_multiagent/test_cases/q2/2-one-ghost-3level.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ / \
|
||||
c1 c2 c3 c4
|
||||
/ \ / \ / \ / \
|
||||
d1 d2 d3 d4 d5 d6 d7 d8
|
||||
3 9 10 6 4 7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 d5 d6 d7 d8"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
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
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d1 3.0
|
||||
d2 9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
d5 4.0
|
||||
d6 7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
"""
|
3
p2_multiagent/test_cases/q2/3-one-ghost-4level.solution
Normal file
3
p2_multiagent/test_cases/q2/3-one-ghost-4level.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/3-one-ghost-4level.test.
|
||||
action: "Left"
|
||||
generated: "A B C D E F G H I J K L M N O P a b1 b2 c1 c2 c3 c4 d1 d2 d3 d4 d5 d6 d7 d8"
|
79
p2_multiagent/test_cases/q2/3-one-ghost-4level.test
Normal file
79
p2_multiagent/test_cases/q2/3-one-ghost-4level.test
Normal file
@ -0,0 +1,79 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "4"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ / \
|
||||
c1 c2 c3 c4
|
||||
/ \ / \ / \ / \
|
||||
d1 d2 d3 d4 d5 d6 d7 d8
|
||||
/ \ / \ / \ / \ / \ / \ / \ / \
|
||||
A B C D E F G H I J K L M N O P
|
||||
3 13 5 9 10 11 6 8 1 0 4 7 12 15 2 14
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
d - min
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "A B C D E F G H I J K L M N O P"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
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
|
||||
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
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A 3.0
|
||||
B 13.0
|
||||
C 5.0
|
||||
D 9.0
|
||||
E 10.0
|
||||
F 11.0
|
||||
G 6.0
|
||||
H 8.0
|
||||
I 1.0
|
||||
J 0.0
|
||||
K 4.0
|
||||
L 7.0
|
||||
M 12.0
|
||||
N 15.0
|
||||
O 2.0
|
||||
P 14.0
|
||||
"""
|
3
p2_multiagent/test_cases/q2/4-two-ghosts-3level.solution
Normal file
3
p2_multiagent/test_cases/q2/4-two-ghosts-3level.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/4-two-ghosts-3level.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 c3 c4 d1 d2 d3 d4 d5 d6 d7 d8"
|
52
p2_multiagent/test_cases/q2/4-two-ghosts-3level.test
Normal file
52
p2_multiagent/test_cases/q2/4-two-ghosts-3level.test
Normal file
@ -0,0 +1,52 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ / \
|
||||
c1 c2 c3 c4
|
||||
/ \ / \ / \ / \
|
||||
d1 d2 d3 d4 d5 d6 d7 d8
|
||||
3 9 10 6 4 7 0 5
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
"""
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 d5 d6 d7 d8"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
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
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d1 3.0
|
||||
d2 9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
d5 4.0
|
||||
d6 7.0
|
||||
d7 0.0
|
||||
d8 5.0
|
||||
"""
|
3
p2_multiagent/test_cases/q2/5-two-ghosts-4level.solution
Normal file
3
p2_multiagent/test_cases/q2/5-two-ghosts-4level.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/5-two-ghosts-4level.test.
|
||||
action: "Left"
|
||||
generated: "A B C D E F G H I J K L M N O P a b1 b2 c1 c2 c3 c4 d1 d2 d3 d4 d5 d6 d7 d8"
|
79
p2_multiagent/test_cases/q2/5-two-ghosts-4level.test
Normal file
79
p2_multiagent/test_cases/q2/5-two-ghosts-4level.test
Normal file
@ -0,0 +1,79 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "4"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ / \
|
||||
c1 c2 c3 c4
|
||||
/ \ / \ / \ / \
|
||||
d1 d2 d3 d4 d5 d6 d7 d8
|
||||
/ \ / \ / \ / \ / \ / \ / \ / \
|
||||
A B C D E F G H I J K L M N O P
|
||||
3 13 5 9 10 11 6 8 1 0 4 7 12 15 2 14
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
d - max
|
||||
"""
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "A B C D E F G H I J K L M N O P"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
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
|
||||
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
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A 3.0
|
||||
B 13.0
|
||||
C 5.0
|
||||
D 9.0
|
||||
E 10.0
|
||||
F 11.0
|
||||
G 6.0
|
||||
H 8.0
|
||||
I 1.0
|
||||
J 0.0
|
||||
K 4.0
|
||||
L 7.0
|
||||
M 12.0
|
||||
N 15.0
|
||||
O 2.0
|
||||
P 14.0
|
||||
"""
|
3
p2_multiagent/test_cases/q2/6-tied-root.solution
Normal file
3
p2_multiagent/test_cases/q2/6-tied-root.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/6-tied-root.test.
|
||||
action: "Left"
|
||||
generated: "A B C max min1 min2"
|
31
p2_multiagent/test_cases/q2/6-tied-root.test
Normal file
31
p2_multiagent/test_cases/q2/6-tied-root.test
Normal file
@ -0,0 +1,31 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
max
|
||||
/ \
|
||||
min1 min2
|
||||
| / \
|
||||
A B C
|
||||
10 10 0
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "max"
|
||||
win_states: "A B"
|
||||
lose_states: "C"
|
||||
|
||||
successors: """
|
||||
max Left min1
|
||||
max Right min2
|
||||
min1 Down A
|
||||
min2 Left B
|
||||
min2 Right C
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A 10.0
|
||||
B 10.0
|
||||
C 0.0
|
||||
"""
|
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/7-1a-check-depth-one-ghost.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 b3 c1 c2 c3"
|
83
p2_multiagent/test_cases/q2/7-1a-check-depth-one-ghost.test
Normal file
83
p2_multiagent/test_cases/q2/7-1a-check-depth-one-ghost.test
Normal file
@ -0,0 +1,83 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "1"
|
||||
|
||||
diagram: """
|
||||
a
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
0 b1 0 b2 b3 8
|
||||
| | |
|
||||
10 c1 0 c2 c3 8
|
||||
| | |
|
||||
0 d1 0 d2 d3 8
|
||||
| | |
|
||||
0 e1 10 e2 e3 8
|
||||
| | |
|
||||
0 f1 0 f2 f3 8
|
||||
| | |
|
||||
g1 g2 g3
|
||||
0 0 8
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
d - min
|
||||
e - max
|
||||
f - min
|
||||
|
||||
At depth 1, the evaluation function is called at level c,
|
||||
so Left should be returned. If your algorithm is returning a
|
||||
different action, check how you implemented your depth.
|
||||
"""
|
||||
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "g1 g2 g3"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Center b2
|
||||
a Right b3
|
||||
b1 Center c1
|
||||
b2 Center c2
|
||||
b3 Center c3
|
||||
c1 Center d1
|
||||
c2 Center d2
|
||||
c3 Center d3
|
||||
d1 Center e1
|
||||
d2 Center e2
|
||||
d3 Center e3
|
||||
e1 Center f1
|
||||
e2 Center f2
|
||||
e3 Center f3
|
||||
f1 Center g1
|
||||
f2 Center g2
|
||||
f3 Center g3
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
b1 0.0
|
||||
b2 0.0
|
||||
b3 8.0
|
||||
c1 10.0
|
||||
c2 0.0
|
||||
c3 8.0
|
||||
d1 0.0
|
||||
d2 0.0
|
||||
d3 8.0
|
||||
e1 0.0
|
||||
e2 10.0
|
||||
e3 8.0
|
||||
f1 0.0
|
||||
f2 0.0
|
||||
f3 8.0
|
||||
g1 0.0
|
||||
g2 0.0
|
||||
g3 8.0
|
||||
"""
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/7-1b-check-depth-one-ghost.test.
|
||||
action: "Center"
|
||||
generated: "a b1 b2 b3 c1 c2 c3 d1 d2 d3 e1 e2 e3"
|
83
p2_multiagent/test_cases/q2/7-1b-check-depth-one-ghost.test
Normal file
83
p2_multiagent/test_cases/q2/7-1b-check-depth-one-ghost.test
Normal file
@ -0,0 +1,83 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
diagram: """
|
||||
a
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
0 b1 0 b2 b3 8
|
||||
| | |
|
||||
10 c1 0 c2 c3 8
|
||||
| | |
|
||||
0 d1 0 d2 d3 8
|
||||
| | |
|
||||
0 e1 10 e2 e3 8
|
||||
| | |
|
||||
0 f1 0 f2 f3 8
|
||||
| | |
|
||||
g1 g2 g3
|
||||
0 0 8
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
d - min
|
||||
e - max
|
||||
f - min
|
||||
|
||||
At depth 2, the evaluation function is called at level e,
|
||||
so Center should be returned. If your algorithm is returning a
|
||||
different action, check how you implemented your depth.
|
||||
"""
|
||||
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "g1 g2 g3"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Center b2
|
||||
a Right b3
|
||||
b1 Center c1
|
||||
b2 Center c2
|
||||
b3 Center c3
|
||||
c1 Center d1
|
||||
c2 Center d2
|
||||
c3 Center d3
|
||||
d1 Center e1
|
||||
d2 Center e2
|
||||
d3 Center e3
|
||||
e1 Center f1
|
||||
e2 Center f2
|
||||
e3 Center f3
|
||||
f1 Center g1
|
||||
f2 Center g2
|
||||
f3 Center g3
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
b1 0.0
|
||||
b2 0.0
|
||||
b3 8.0
|
||||
c1 10.0
|
||||
c2 0.0
|
||||
c3 8.0
|
||||
d1 0.0
|
||||
d2 0.0
|
||||
d3 8.0
|
||||
e1 0.0
|
||||
e2 10.0
|
||||
e3 8.0
|
||||
f1 0.0
|
||||
f2 0.0
|
||||
f3 8.0
|
||||
g1 0.0
|
||||
g2 0.0
|
||||
g3 8.0
|
||||
"""
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/7-1c-check-depth-one-ghost.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 b3 c1 c2 c3 d1 d2 d3 e1 e2 e3 f1 f2 f3 g1 g2 g3"
|
83
p2_multiagent/test_cases/q2/7-1c-check-depth-one-ghost.test
Normal file
83
p2_multiagent/test_cases/q2/7-1c-check-depth-one-ghost.test
Normal file
@ -0,0 +1,83 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
a
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
0 b1 0 b2 b3 8
|
||||
| | |
|
||||
10 c1 0 c2 c3 8
|
||||
| | |
|
||||
0 d1 0 d2 d3 8
|
||||
| | |
|
||||
0 e1 10 e2 e3 8
|
||||
| | |
|
||||
0 f1 0 f2 f3 8
|
||||
| | |
|
||||
g1 g2 g3
|
||||
0 0 8
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
d - min
|
||||
e - max
|
||||
f - min
|
||||
|
||||
At depth 3, the evaluation function is called at level g,
|
||||
so Right should be returned. If your algorithm is returning a
|
||||
different action, check how you implemented your depth.
|
||||
"""
|
||||
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "g1 g2 g3"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Center b2
|
||||
a Right b3
|
||||
b1 Center c1
|
||||
b2 Center c2
|
||||
b3 Center c3
|
||||
c1 Center d1
|
||||
c2 Center d2
|
||||
c3 Center d3
|
||||
d1 Center e1
|
||||
d2 Center e2
|
||||
d3 Center e3
|
||||
e1 Center f1
|
||||
e2 Center f2
|
||||
e3 Center f3
|
||||
f1 Center g1
|
||||
f2 Center g2
|
||||
f3 Center g3
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
b1 0.0
|
||||
b2 0.0
|
||||
b3 8.0
|
||||
c1 10.0
|
||||
c2 0.0
|
||||
c3 8.0
|
||||
d1 0.0
|
||||
d2 0.0
|
||||
d3 8.0
|
||||
e1 0.0
|
||||
e2 10.0
|
||||
e3 8.0
|
||||
f1 0.0
|
||||
f2 0.0
|
||||
f3 8.0
|
||||
g1 0.0
|
||||
g2 0.0
|
||||
g3 8.0
|
||||
"""
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/7-2a-check-depth-two-ghosts.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 b3 c1 c2 c3 d1 d2 d3"
|
110
p2_multiagent/test_cases/q2/7-2a-check-depth-two-ghosts.test
Normal file
110
p2_multiagent/test_cases/q2/7-2a-check-depth-two-ghosts.test
Normal file
@ -0,0 +1,110 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "1"
|
||||
|
||||
diagram: """
|
||||
a
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
0 b1 0 b2 b3 8
|
||||
| | |
|
||||
0 c1 0 c2 c3 8
|
||||
| | |
|
||||
10 d1 0 d2 d3 8
|
||||
| | |
|
||||
0 e1 0 e2 e3 8
|
||||
| | |
|
||||
0 f1 0 f2 f3 8
|
||||
| | |
|
||||
0 g1 10 g2 g3 8
|
||||
| | |
|
||||
0 h1 0 h2 h3 8
|
||||
| | |
|
||||
0 i1 0 i2 i3 8
|
||||
| | |
|
||||
j1 j2 j3
|
||||
0 0 8
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
d - max
|
||||
e - min
|
||||
f - min
|
||||
g - max
|
||||
h - min
|
||||
i - min
|
||||
|
||||
At depth 1, the evaluation function is called at level d,
|
||||
so Left should be returned. If your algorithm is returning a
|
||||
different action, check how you implemented your depth.
|
||||
"""
|
||||
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "j1 j2 j3"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Center b2
|
||||
a Right b3
|
||||
b1 Center c1
|
||||
b2 Center c2
|
||||
b3 Center c3
|
||||
c1 Center d1
|
||||
c2 Center d2
|
||||
c3 Center d3
|
||||
d1 Center e1
|
||||
d2 Center e2
|
||||
d3 Center e3
|
||||
e1 Center f1
|
||||
e2 Center f2
|
||||
e3 Center f3
|
||||
f1 Center g1
|
||||
f2 Center g2
|
||||
f3 Center g3
|
||||
g1 Center h1
|
||||
g2 Center h2
|
||||
g3 Center h3
|
||||
h1 Center i1
|
||||
h2 Center i2
|
||||
h3 Center i3
|
||||
i1 Center j1
|
||||
i2 Center j2
|
||||
i3 Center j3
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
b1 0.0
|
||||
b2 0.0
|
||||
b3 8.0
|
||||
c1 0.0
|
||||
c2 0.0
|
||||
c3 8.0
|
||||
d1 10.0
|
||||
d2 0.0
|
||||
d3 8.0
|
||||
e1 0.0
|
||||
e2 0.0
|
||||
e3 8.0
|
||||
f1 0.0
|
||||
f2 0.0
|
||||
f3 8.0
|
||||
g1 0.0
|
||||
g2 10.0
|
||||
g3 8.0
|
||||
h1 0.0
|
||||
h2 0.0
|
||||
h3 8.0
|
||||
i1 0.0
|
||||
i2 0.0
|
||||
i3 8.0
|
||||
j1 0.0
|
||||
j2 0.0
|
||||
j3 8.0
|
||||
"""
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/7-2b-check-depth-two-ghosts.test.
|
||||
action: "Center"
|
||||
generated: "a b1 b2 b3 c1 c2 c3 d1 d2 d3 e1 e2 e3 f1 f2 f3 g1 g2 g3"
|
110
p2_multiagent/test_cases/q2/7-2b-check-depth-two-ghosts.test
Normal file
110
p2_multiagent/test_cases/q2/7-2b-check-depth-two-ghosts.test
Normal file
@ -0,0 +1,110 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "2"
|
||||
|
||||
diagram: """
|
||||
a
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
0 b1 0 b2 b3 8
|
||||
| | |
|
||||
0 c1 0 c2 c3 8
|
||||
| | |
|
||||
10 d1 0 d2 d3 8
|
||||
| | |
|
||||
0 e1 0 e2 e3 8
|
||||
| | |
|
||||
0 f1 0 f2 f3 8
|
||||
| | |
|
||||
0 g1 10 g2 g3 8
|
||||
| | |
|
||||
0 h1 0 h2 h3 8
|
||||
| | |
|
||||
0 i1 0 i2 i3 8
|
||||
| | |
|
||||
j1 j2 j3
|
||||
0 0 8
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
d - max
|
||||
e - min
|
||||
f - min
|
||||
g - max
|
||||
h - min
|
||||
i - min
|
||||
|
||||
At depth 2, the evaluation function is called at level g,
|
||||
so Center should be returned. If your algorithm is returning
|
||||
a different action, check how you implemented your depth.
|
||||
"""
|
||||
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "j1 j2 j3"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Center b2
|
||||
a Right b3
|
||||
b1 Center c1
|
||||
b2 Center c2
|
||||
b3 Center c3
|
||||
c1 Center d1
|
||||
c2 Center d2
|
||||
c3 Center d3
|
||||
d1 Center e1
|
||||
d2 Center e2
|
||||
d3 Center e3
|
||||
e1 Center f1
|
||||
e2 Center f2
|
||||
e3 Center f3
|
||||
f1 Center g1
|
||||
f2 Center g2
|
||||
f3 Center g3
|
||||
g1 Center h1
|
||||
g2 Center h2
|
||||
g3 Center h3
|
||||
h1 Center i1
|
||||
h2 Center i2
|
||||
h3 Center i3
|
||||
i1 Center j1
|
||||
i2 Center j2
|
||||
i3 Center j3
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
b1 0.0
|
||||
b2 0.0
|
||||
b3 8.0
|
||||
c1 0.0
|
||||
c2 0.0
|
||||
c3 8.0
|
||||
d1 10.0
|
||||
d2 0.0
|
||||
d3 8.0
|
||||
e1 0.0
|
||||
e2 0.0
|
||||
e3 8.0
|
||||
f1 0.0
|
||||
f2 0.0
|
||||
f3 8.0
|
||||
g1 0.0
|
||||
g2 10.0
|
||||
g3 8.0
|
||||
h1 0.0
|
||||
h2 0.0
|
||||
h3 8.0
|
||||
i1 0.0
|
||||
i2 0.0
|
||||
i3 8.0
|
||||
j1 0.0
|
||||
j2 0.0
|
||||
j3 8.0
|
||||
"""
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q2/7-2c-check-depth-two-ghosts.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 b3 c1 c2 c3 d1 d2 d3 e1 e2 e3 f1 f2 f3 g1 g2 g3 h1 h2 h3 i1 i2 i3 j1 j2 j3"
|
110
p2_multiagent/test_cases/q2/7-2c-check-depth-two-ghosts.test
Normal file
110
p2_multiagent/test_cases/q2/7-2c-check-depth-two-ghosts.test
Normal file
@ -0,0 +1,110 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
a
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
0 b1 0 b2 b3 8
|
||||
| | |
|
||||
0 c1 0 c2 c3 8
|
||||
| | |
|
||||
10 d1 0 d2 d3 8
|
||||
| | |
|
||||
0 e1 0 e2 e3 8
|
||||
| | |
|
||||
0 f1 0 f2 f3 8
|
||||
| | |
|
||||
0 g1 10 g2 g3 8
|
||||
| | |
|
||||
0 h1 0 h2 h3 8
|
||||
| | |
|
||||
0 i1 0 i2 i3 8
|
||||
| | |
|
||||
j1 j2 j3
|
||||
0 0 8
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - min
|
||||
d - max
|
||||
e - min
|
||||
f - min
|
||||
g - max
|
||||
h - min
|
||||
i - min
|
||||
|
||||
At depth 3, the evaluation function is called at level j,
|
||||
so Right should be returned. If your algorithm is returning
|
||||
a different action, check how you implemented your depth.
|
||||
"""
|
||||
|
||||
num_agents: "3"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "j1 j2 j3"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Center b2
|
||||
a Right b3
|
||||
b1 Center c1
|
||||
b2 Center c2
|
||||
b3 Center c3
|
||||
c1 Center d1
|
||||
c2 Center d2
|
||||
c3 Center d3
|
||||
d1 Center e1
|
||||
d2 Center e2
|
||||
d3 Center e3
|
||||
e1 Center f1
|
||||
e2 Center f2
|
||||
e3 Center f3
|
||||
f1 Center g1
|
||||
f2 Center g2
|
||||
f3 Center g3
|
||||
g1 Center h1
|
||||
g2 Center h2
|
||||
g3 Center h3
|
||||
h1 Center i1
|
||||
h2 Center i2
|
||||
h3 Center i3
|
||||
i1 Center j1
|
||||
i2 Center j2
|
||||
i3 Center j3
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
b1 0.0
|
||||
b2 0.0
|
||||
b3 8.0
|
||||
c1 0.0
|
||||
c2 0.0
|
||||
c3 8.0
|
||||
d1 10.0
|
||||
d2 0.0
|
||||
d3 8.0
|
||||
e1 0.0
|
||||
e2 0.0
|
||||
e3 8.0
|
||||
f1 0.0
|
||||
f2 0.0
|
||||
f3 8.0
|
||||
g1 0.0
|
||||
g2 10.0
|
||||
g3 8.0
|
||||
h1 0.0
|
||||
h2 0.0
|
||||
h3 8.0
|
||||
i1 0.0
|
||||
i2 0.0
|
||||
i3 8.0
|
||||
j1 0.0
|
||||
j2 0.0
|
||||
j3 8.0
|
||||
"""
|
||||
|
||||
|
444
p2_multiagent/test_cases/q2/8-pacman-game.solution
Normal file
444
p2_multiagent/test_cases/q2/8-pacman-game.solution
Normal file
@ -0,0 +1,444 @@
|
||||
optimalActions: """
|
||||
[[["West", "East"], 59], [["West", "East"], 35]]
|
||||
[[["West"], 190], [["West"], 127]]
|
||||
[[["West"], 190], [["West"], 135]]
|
||||
[[["West", "North"], 120], [["West", "North"], 82]]
|
||||
[[["West"], 77], [["West"], 57]]
|
||||
[[["West", "North"], 143], [["West", "North"], 97]]
|
||||
[[["West"], 155], [["West"], 110]]
|
||||
[[["West"], 40], [["West"], 27]]
|
||||
[[["North"], 64], [["North"], 43]]
|
||||
[[["North"], 85], [["North"], 57]]
|
||||
[[["North"], 106], [["North"], 71]]
|
||||
[[["North"], 97], [["North"], 65]]
|
||||
[[["Stop", "East"], 154], [["East"], 103]]
|
||||
[[["East"], 156], [["East"], 101]]
|
||||
[[["West"], 30], [["West"], 17]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 18], [["East"], 12]]
|
||||
[[["North"], 29], [["North"], 18]]
|
||||
[[["North"], 50], [["North"], 31]]
|
||||
[[["West"], 55], [["West"], 36]]
|
||||
[[["East"], 29], [["East"], 16]]
|
||||
[[["North"], 89], [["North"], 61]]
|
||||
[[["East", "North"], 161], [["East", "North"], 121]]
|
||||
[[["East", "North"], 221], [["East", "North"], 166]]
|
||||
[[["North", "South"], 105], [["North", "South"], 77]]
|
||||
[[["West"], 69], [["West"], 51]]
|
||||
[[["West"], 94], [["West"], 69]]
|
||||
[[["West", "Stop"], 57], [["West"], 42]]
|
||||
[[["West", "Stop", "East"], 69], [["West", "East"], 49]]
|
||||
[[["West", "Stop", "East"], 61], [["West", "East"], 41]]
|
||||
[[["Stop", "East", "South"], 55], [["East", "South"], 37]]
|
||||
[[["Stop", "East", "South"], 28], [["East", "South"], 19]]
|
||||
[[["Stop", "East", "South"], 34], [["East", "South"], 23]]
|
||||
[[["Stop", "East", "South"], 55], [["East", "South"], 37]]
|
||||
[[["Stop", "East", "South"], 55], [["East", "South"], 37]]
|
||||
[[["Stop", "East", "South"], 61], [["East", "South"], 41]]
|
||||
[[["Stop", "East", "South"], 85], [["East", "South"], 57]]
|
||||
[[["Stop", "East", "South"], 64], [["East", "South"], 43]]
|
||||
[[["Stop", "East", "South"], 61], [["East", "South"], 41]]
|
||||
[[["Stop", "East", "South"], 61], [["East", "South"], 41]]
|
||||
[[["Stop", "East", "South"], 85], [["East", "South"], 57]]
|
||||
[[["Stop", "East", "South"], 102], [["East", "South"], 67]]
|
||||
[[["Stop", "South"], 23], [["South"], 13]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 18], [["East"], 12]]
|
||||
[[["East", "North"], 29], [["East", "North"], 18]]
|
||||
[[["East"], 38], [["East"], 22]]
|
||||
[[["North"], 29], [["North"], 18]]
|
||||
[[["North"], 38], [["North"], 22]]
|
||||
[[["East"], 33], [["East"], 22]]
|
||||
[[["East"], 37], [["East"], 18]]
|
||||
[[["East"], 18], [["East"], 12]]
|
||||
[[["East"], 37], [["East"], 26]]
|
||||
[[["East"], 69], [["East"], 41]]
|
||||
[[["East"], 56], [["East"], 26]]
|
||||
[[["East"], 44], [["East"], 29]]
|
||||
[[["North", "South"], 83], [["North", "South"], 52]]
|
||||
[[["East", "North"], 121], [["East", "North"], 74]]
|
||||
[[["East", "North"], 97], [["East", "North"], 73]]
|
||||
[[["North", "South"], 173], [["North", "South"], 130]]
|
||||
[[["West", "East"], 90], [["West", "East"], 66]]
|
||||
[[["West", "Stop", "East"], 161], [["West", "East"], 118]]
|
||||
[[["Stop", "East", "South"], 58], [["East", "South"], 43]]
|
||||
[[["Stop", "East"], 120], [["South"], 85]]
|
||||
[[["East"], 78], [["East"], 45]]
|
||||
[[["West"], 77], [["West"], 42]]
|
||||
[[["South"], 83], [["South"], 48]]
|
||||
[[["South"], 49], [["South"], 37]]
|
||||
[[["South"], 185], [["South"], 104]]
|
||||
[[["South"], 68], [["South"], 41]]
|
||||
[[["West"], 30], [["West"], 18]]
|
||||
[[["West"], 56], [["West"], 29]]
|
||||
[[["West"], 14], [["West"], 10]]
|
||||
[[["West"], 20], [["West"], 14]]
|
||||
[[["West"], 13], [["West"], 9]]
|
||||
[[["West"], 13], [["West"], 9]]
|
||||
[[["West"], 16], [["West"], 12]]
|
||||
[[["West", "North"], 30], [["West", "North"], 20]]
|
||||
[[["West"], 38], [["West"], 23]]
|
||||
[[["West", "Stop", "East", "North"], 70], [["West", "East", "North"], 46]]
|
||||
[[["West", "Stop", "East"], 128], [["West", "East"], 89]]
|
||||
[[["West", "Stop", "East"], 31], [["West", "East"], 20]]
|
||||
[[["Stop", "East", "North"], 69], [["East", "North"], 45]]
|
||||
[[["Stop", "North"], 58], [["North"], 31]]
|
||||
[[["North"], 34], [["North"], 19]]
|
||||
[[["North"], 30], [["North"], 17]]
|
||||
[[["North"], 19], [["North"], 11]]
|
||||
[[["North"], 34], [["North"], 19]]
|
||||
[[["East"], 30], [["East"], 17]]
|
||||
[[["East"], 19], [["East"], 11]]
|
||||
[[["East"], 44], [["East"], 29]]
|
||||
[[["East", "South"], 87], [["East", "South"], 60]]
|
||||
[[["East", "South"], 108], [["East", "South"], 62]]
|
||||
[[["South"], 120], [["South"], 61]]
|
||||
[[["North", "South"], 209], [["North", "South"], 132]]
|
||||
[[["West"], 108], [["West"], 60]]
|
||||
[[["West", "Stop", "East", "South"], 83], [["West", "East", "South"], 61]]
|
||||
[[["West", "Stop", "East", "South"], 90], [["West", "East", "South"], 66]]
|
||||
[[["West", "Stop", "East"], 134], [["West", "East"], 95]]
|
||||
[[["West", "Stop", "East"], 82], [["West", "East"], 55]]
|
||||
[[["Stop", "East", "South"], 142], [["East", "South"], 95]]
|
||||
[[["Stop", "East", "South"], 98], [["East", "South"], 65]]
|
||||
[[["Stop", "East", "South"], 128], [["East", "South"], 86]]
|
||||
[[["Stop", "East", "South"], 82], [["East", "South"], 55]]
|
||||
[[["Stop", "East", "South"], 85], [["East", "South"], 57]]
|
||||
[[["Stop", "East", "South"], 190], [["East", "South"], 127]]
|
||||
[[["Stop", "East", "South"], 158], [["East", "South"], 103]]
|
||||
[[["Stop", "South"], 50], [["South"], 27]]
|
||||
[[["South"], 30], [["South"], 17]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["South"], 15], [["South"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 18], [["East"], 12]]
|
||||
[[["East", "North"], 29], [["East", "North"], 18]]
|
||||
[[["East"], 37], [["East"], 22]]
|
||||
[[["East", "North"], 41], [["East", "North"], 24]]
|
||||
[[["East"], 59], [["East"], 29]]
|
||||
[[["East"], 19], [["East"], 11]]
|
||||
[[["East"], 26], [["East"], 15]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 15], [["East"], 9]]
|
||||
[[["East"], 18], [["East"], 12]]
|
||||
[[["East"], 29], [["East"], 18]]
|
||||
[[["East"], 37], [["East"], 22]]
|
||||
[[["East", "North"], 41], [["East", "North"], 24]]
|
||||
[[["East"], 59], [["East"], 29]]
|
||||
[[["East"], 19], [["East"], 11]]
|
||||
[[["North"], 26], [["North"], 15]]
|
||||
[[["North"], 19], [["North"], 11]]
|
||||
[[["North"], 30], [["North"], 17]]
|
||||
[[["North"], 34], [["North"], 19]]
|
||||
[[["West"], 34], [["West"], 19]]
|
||||
[[["West"], 25], [["West"], 13]]
|
||||
[[["West", "Stop", "East"], 7], [["West", "East"], 3]]
|
||||
"""
|
||||
altDepthActions: """
|
||||
[["West", "East"], ["West", "East"], ["West", "East"], ["West", "East"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West", "North"], ["West", "North"], ["West", "North"], ["West", "North"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West", "North"], ["West", "North"], ["West", "North"], ["West", "North"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["Stop", "North"], ["North"]]
|
||||
[["East"], ["East"], ["Stop", "East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["West"], ["West"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["East", "North"], ["East", "North"], ["East", "North"], ["East", "North"]]
|
||||
[["East", "North"], ["East", "North"], ["East", "North"], ["East", "North"]]
|
||||
[["North", "South"], ["North", "South"], ["North"], ["North"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West"], ["West"], ["West", "Stop"], ["West"]]
|
||||
[["West"], ["West"], ["West", "Stop", "East", "South"], ["West", "East", "South"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "South"], ["South"], ["Stop", "South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["North", "South"], ["North", "South"], ["South"], ["South"]]
|
||||
[["East", "North"], ["East", "North"], ["East", "North"], ["East", "North"]]
|
||||
[["East", "North"], ["East", "North"], ["East", "North"], ["East", "North"]]
|
||||
[["North", "South"], ["North", "South"], ["North"], ["North"]]
|
||||
[["West", "East"], ["West", "East"], ["East"], ["East"]]
|
||||
[["West"], ["West"], ["East"], ["East"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["East"], ["East"]]
|
||||
[["Stop", "East"], ["East"], ["Stop", "East"], ["South"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["West", "East"], ["West", "East"], ["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", "North"], ["West", "North"], ["West", "North"], ["West", "North"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West", "Stop", "East", "North"], ["West", "East", "North"], ["West", "Stop", "East", "North"], ["West", "East", "North"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "North"], ["East", "North"], ["Stop", "East", "North"], ["East", "North"]]
|
||||
[["Stop", "North"], ["North"], ["Stop", "North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East", "South"], ["East", "South"], ["East", "South"], ["East", "South"]]
|
||||
[["East", "South"], ["East", "South"], ["East", "South"], ["East", "South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["North", "South"], ["North", "South"], ["North", "South"], ["North", "South"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West", "Stop", "East", "South"], ["West", "East", "South"], ["West", "Stop", "East", "South"], ["West", "East", "South"]]
|
||||
[["West", "Stop", "East", "South"], ["West", "East", "South"], ["West", "Stop", "East", "South"], ["West", "East", "South"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"], ["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "South"], ["South"], ["Stop", "South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["South"], ["South"], ["South"], ["South"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"], ["East", "North"], ["East", "North"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"], ["East", "North"], ["East", "North"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["East"], ["East"], ["East"], ["East"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["North"], ["North"], ["North"], ["North"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West"], ["West"], ["West"], ["West"]]
|
||||
[["West", "Stop", "East"], ["West", "East"], ["West", "Stop", "East"], ["West", "East"]]
|
||||
"""
|
||||
partialPlyBugActions: """
|
||||
[["West", "East"], ["West", "East"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "North"], ["West", "North"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "North"], ["West", "North"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["East"], ["East"]]
|
||||
[["Stop", "East"], ["East"]]
|
||||
[["West"], ["West"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["West"], ["West"]]
|
||||
[["East"], ["East"]]
|
||||
[["North"], ["North"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["North", "South"], ["North", "South"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "Stop"], ["West"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["East"], ["East"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["North", "South"], ["North", "South"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["North", "South"], ["North", "South"]]
|
||||
[["West", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["West"], ["West"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "North"], ["West", "North"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "Stop", "East", "North"], ["West", "East", "North"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "North"], ["East", "North"]]
|
||||
[["Stop", "North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East", "South"], ["East", "South"]]
|
||||
[["East", "South"], ["East", "South"]]
|
||||
[["South"], ["South"]]
|
||||
[["North", "South"], ["North", "South"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "Stop", "East", "South"], ["West", "East", "South"]]
|
||||
[["West", "Stop", "East", "South"], ["West", "East", "South"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "East", "South"], ["East", "South"]]
|
||||
[["Stop", "South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["South"], ["South"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["East", "North"], ["East", "North"]]
|
||||
[["East"], ["East"]]
|
||||
[["East"], ["East"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["North"], ["North"]]
|
||||
[["West"], ["West"]]
|
||||
[["West"], ["West"]]
|
||||
[["West", "Stop", "East"], ["West", "East"]]
|
||||
"""
|
19
p2_multiagent/test_cases/q2/8-pacman-game.test
Normal file
19
p2_multiagent/test_cases/q2/8-pacman-game.test
Normal file
@ -0,0 +1,19 @@
|
||||
class: "PacmanGameTreeTest"
|
||||
alg: "MinimaxAgent"
|
||||
seed: "0"
|
||||
depth: "2"
|
||||
max_points: "4"
|
||||
|
||||
# The following specifies the layout to be used
|
||||
layoutName: "smallClassic"
|
||||
layout: """
|
||||
%%%%%%%%%%%%%%%%%%%%
|
||||
%......%G G%......%
|
||||
%.%%...%% %%...%%.%
|
||||
%.%o.%........%.o%.%
|
||||
%.%%.%.%%%%%%.%.%%.%
|
||||
%........P.........%
|
||||
%%%%%%%%%%%%%%%%%%%%
|
||||
"""
|
||||
|
||||
|
2
p2_multiagent/test_cases/q2/CONFIG
Normal file
2
p2_multiagent/test_cases/q2/CONFIG
Normal file
@ -0,0 +1,2 @@
|
||||
max_points: "5"
|
||||
class: "PassAllTestsQuestion"
|
3
p2_multiagent/test_cases/q3/0-lecture-6-tree.solution
Normal file
3
p2_multiagent/test_cases/q3/0-lecture-6-tree.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q3/0-lecture-6-tree.test.
|
||||
action: "Center"
|
||||
generated: "A B C D E F G H max min1 min2 min3"
|
50
p2_multiagent/test_cases/q3/0-lecture-6-tree.test
Normal file
50
p2_multiagent/test_cases/q3/0-lecture-6-tree.test
Normal file
@ -0,0 +1,50 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "AlphaBetaAgent"
|
||||
depth: "2"
|
||||
|
||||
# Tree from lecture 6 slides
|
||||
diagram: """
|
||||
max
|
||||
/-/ | \--\
|
||||
/ | \
|
||||
/ | \
|
||||
min1 min2 min3
|
||||
/|\ /|\ /|\
|
||||
/ | \ / | \ / | \
|
||||
A B C D E F G H I
|
||||
3 12 8 5 4 6 14 1 11
|
||||
"""
|
||||
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "max"
|
||||
win_states: "A B C D E F G H I"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
max Left min1
|
||||
max Center min2
|
||||
max Right min3
|
||||
min1 Left A
|
||||
min1 Center B
|
||||
min1 Right C
|
||||
min2 Left D
|
||||
min2 Center E
|
||||
min2 Right F
|
||||
min3 Left G
|
||||
min3 Center H
|
||||
min3 Right I
|
||||
"""
|
||||
|
||||
|
||||
evaluation: """
|
||||
A 3.0
|
||||
B 12.0
|
||||
C 8.0
|
||||
D 5.0
|
||||
E 4.0
|
||||
F 6.0
|
||||
G 14.0
|
||||
H 1.0
|
||||
I 11.0
|
||||
"""
|
3
p2_multiagent/test_cases/q3/0-small-tree.solution
Normal file
3
p2_multiagent/test_cases/q3/0-small-tree.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q3/0-small-tree.test.
|
||||
action: "pacLeft"
|
||||
generated: "A B C minLeft minRight root"
|
36
p2_multiagent/test_cases/q3/0-small-tree.test
Normal file
36
p2_multiagent/test_cases/q3/0-small-tree.test
Normal file
@ -0,0 +1,36 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "AlphaBetaAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
root
|
||||
/ \
|
||||
minLeft minRight
|
||||
/ \ / \
|
||||
A B C deeper
|
||||
4 3 2 |
|
||||
D
|
||||
1000
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "root"
|
||||
win_states: "A C"
|
||||
lose_states: "B D"
|
||||
|
||||
successors: """
|
||||
root pacLeft minLeft
|
||||
root pacRight minRight
|
||||
minLeft gLeft A
|
||||
minLeft gRight B
|
||||
minRight gLeft C
|
||||
minRight gRight deeper
|
||||
deeper pacLeft D
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
A 4.0
|
||||
B 3.0
|
||||
C 2.0
|
||||
D 1000.0
|
||||
"""
|
3
p2_multiagent/test_cases/q3/1-1-minmax.solution
Normal file
3
p2_multiagent/test_cases/q3/1-1-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q3/1-1-minmax.test.
|
||||
action: "Left"
|
||||
generated: "a b1 b2 c1 c2 cx d1 d2 d3 dx"
|
47
p2_multiagent/test_cases/q3/1-1-minmax.test
Normal file
47
p2_multiagent/test_cases/q3/1-1-minmax.test
Normal file
@ -0,0 +1,47 @@
|
||||
class: "GraphGameTreeTest"
|
||||
alg: "AlphaBetaAgent"
|
||||
depth: "3"
|
||||
|
||||
diagram: """
|
||||
/-----a------\
|
||||
/ \
|
||||
/ \
|
||||
b1 b2
|
||||
/ \ |
|
||||
c1 c2 cx
|
||||
/ \ / \ |
|
||||
d1 d2 d3 d4 dx
|
||||
-3 -9 10 6 -3.01
|
||||
|
||||
a - max
|
||||
b - min
|
||||
c - max
|
||||
|
||||
Note that the minimax value of b1 is -3.
|
||||
"""
|
||||
num_agents: "2"
|
||||
|
||||
start_state: "a"
|
||||
win_states: "d1 d2 d3 d4 dx"
|
||||
lose_states: ""
|
||||
|
||||
successors: """
|
||||
a Left b1
|
||||
a Right b2
|
||||
b1 Left c1
|
||||
b1 Right c2
|
||||
b2 Down cx
|
||||
c1 Left d1
|
||||
c1 Right d2
|
||||
c2 Left d3
|
||||
c2 Right d4
|
||||
cx Down dx
|
||||
"""
|
||||
|
||||
evaluation: """
|
||||
d1 -3.0
|
||||
d2 -9.0
|
||||
d3 10.0
|
||||
d4 6.0
|
||||
dx -3.01
|
||||
"""
|
3
p2_multiagent/test_cases/q3/1-2-minmax.solution
Normal file
3
p2_multiagent/test_cases/q3/1-2-minmax.solution
Normal file
@ -0,0 +1,3 @@
|
||||
# This is the solution file for test_cases/q3/1-2-minmax.test.
|
||||
action: "Right"
|
||||
generated: "a b1 b2 c1 c2 cx d1 d2 d3 dx"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user