diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/Pacman.iml b/.idea/Pacman.iml new file mode 100644 index 0000000..86d1115 --- /dev/null +++ b/.idea/Pacman.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7ba73c2 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e4c734e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/doc/202002422003-黄结伦-实验1.md b/doc/202002422003-黄结伦-实验1.md new file mode 100644 index 0000000..a623fd3 --- /dev/null +++ b/doc/202002422003-黄结伦-实验1.md @@ -0,0 +1,541 @@ +# Pacman 实验1 + +## 一.实验目的 + +​ 1.加深对经典的DFS,BFS,一致代价搜索,A*搜索的理解,并掌握其初步的使用方法 + +## 二.实验原理 + +### 1.宽度优先搜索 + +​ **宽度优先搜索**(bread-first search)是简单搜索策略,先扩展根节点,接着扩展根节点的所有后继,然后再扩展它们的后继,以此类推。其伪代码如下![image-20221021212311445](D:\AI\Subject\Pacman\doc\image-20221021212311445.png) + +​ 下图显示了一个简单二叉树的搜索过程 + +![image-20221021212559638](D:\AI\Subject\Pacman\doc\image-20221021212559638.png) + +### 2.深度优先搜索 + +​ **深度优先搜索**(depth-first search)总是扩展搜索树的当前边缘结点集中最深的节点。搜索很快推进到搜索树的最深层,那里的结点没有后继。当那些结点扩展完之后,就从边缘结点集中去掉,然后搜索算法回溯到下一个还有未扩展后继的深度稍浅的结点。其伪代码如下![image-20221021212636227](D:\AI\Subject\Pacman\doc\image-20221021212636227.png) + +​ 下图显示了一个简单的二叉树搜索过程 + +![image-20221021212936449](D:\AI\Subject\Pacman\doc\image-20221021212936449.png) + +### 3.一致代价搜索 + +​ **一致代价搜索**总是扩展路径消耗最小的节点N。N点的路径消耗等于前一节点N-1的路径消耗加上N-1到N节点的路径消耗。其伪代码如下 + +![image-20221021213229986](D:\AI\Subject\Pacman\doc\image-20221021213229986.png) + +​ 下图举了一个简单的例子说明一致代价搜索 + +![image-20221021213316266](D:\AI\Subject\Pacman\doc\image-20221021213316266.png) + +### 4.A*搜索 + +​ **A*搜索**对结点的评估结合了g(n),即到达此结点已经花费的代价,和A(n),从该结点到目标结点所花代价: + +​ `f(n)=g(n)+h(n)` + +​ 由于g(n)是从开始结点到结点n的路径代价,而A(n)是从结点n到目标结点的最小代价路径的估计值,因此 + +​ ` f(n)= 经过结点n的最小代价解的估计代价` + +​ 这样,如果我们想要找到最小代价的解,首先扩展g(n)+ A(n)值最小的结点是合理的.可以发现这个策略不仅仅合理:假设启发式函数H(n)满足特定的条件,A*搜索既是完备的也是最优的.算法与一致代价搜索类似,除了A*使用g+h而不是g. + +​ 下图举了一个简单的例子说明A*搜索的过程 + +![image-20221021213939136](D:\AI\Subject\Pacman\doc\image-20221021213939136.png) + + + +## 三.实验内容 + +​ 1.完成search文件夹中search.py中的depthFirstSearch,breadFirstSearch,uniformCostSearch,aStarSearch四个函数。 + +在编写完代码后,可在终端中的search目录下执行`python2 .\autograder.py` 命令,即可查看是否通过! + +## 四.实验结果 + +​ 搜索算法中四个函数的具体实现如下 + +### 1.depthFirstSearch + +```python +def depthFirstSearch(problem): + """ + Search the deepest nodes in the search tree first. + + Your search algorithm needs to return a list of actions that reaches the + goal. Make sure to implement a graph search algorithm. + + To get started, you might want to try some of these simple commands to + understand the search problem that is being passed in: + + print "Start:", problem.getStartState() + print "Is the start a goal?", problem.isGoalState(problem.getStartState()) + print "Start's successors:", problem.getSuccessors(problem.getStartState()) + """ + "*** YOUR CODE HERE ***" + visited_node = [] + myStack= util.Stack() + actions = [] + s = problem.getStartState() + if problem.isGoalState(s): + return actions + myStack.push((s, actions)) + while not myStack.isEmpty(): + state = myStack.pop() + if state[0] in visited_node: + continue + visited_node.append(state[0]) + actions = state[1] + if (problem.isGoalState(state[0])): + return actions + for successor in problem.getSuccessors(state[0]): + child_state = successor[0] + action = successor[1] + sub_action = list(actions) + if not child_state in visited_node: + sub_action.append(action) + myStack.push((child_state, sub_action)) + return actions + util.raiseNotDefined() +``` + +### 2.breadFirstSearch + +```python +def breadthFirstSearch(problem): + """Search the shallowest nodes in the search tree first.""" + "*** YOUR CODE HERE ***" + visited_node = [] + myQueue = util.Queue() + actions = [] + s = problem.getStartState() + if problem.isGoalState(s): + return actions + myQueue.push((s, actions)) + while not myQueue.isEmpty(): + state = myQueue.pop() + if state[0] in visited_node: + continue + visited_node.append(state[0]) + actions = state[1] + if (problem.isGoalState(state[0])): + return actions + for successor in problem.getSuccessors(state[0]): + child_state = successor[0] + action = successor[1] + sub_action = list(actions) + if not child_state in visited_node: + sub_action.append(action) + myQueue.push((child_state, sub_action)) + return actions + util.raiseNotDefined() +``` + +### 3.uniformCostSearch + +```python +def uniformCostSearch(problem): + """Search the node of least total cost first.""" + "*** YOUR CODE HERE ***" + visited_node = [] + mypriorityQueue = util.PriorityQueue() + actions = [] + s = problem.getStartState() + if problem.isGoalState(s): + return actions + mypriorityQueue.push((s, actions), 0) + while not mypriorityQueue.isEmpty(): + state = mypriorityQueue.pop() + if state[0] in visited_node: + continue + visited_node.append(state[0]) + actions = state[1] + if (problem.isGoalState(state[0])): + return actions + for successor in problem.getSuccessors(state[0]): + child_state = successor[0] + action = successor[1] + sub_action = list(actions) + if not child_state in visited_node: + sub_action.append(action) + mypriorityQueue.push((child_state, sub_action), problem.getCostOfActions(sub_action)) + return actions + util.raiseNotDefined() +``` + +### 4.aStarSearch + +```python +def aStarSearch(problem, heuristic=nullHeuristic): + """Search the node that has the lowest combined cost and heuristic first.""" + "*** YOUR CODE HERE ***" + visited_node = [] + mypriorityQueue = util.PriorityQueue() + actions = [] + s = problem.getStartState() + if problem.isGoalState(s): + return actions + mypriorityQueue.push((s, actions), 0) + while not mypriorityQueue.isEmpty(): + state = mypriorityQueue.pop() + if state[0] in visited_node: + continue + visited_node.append(state[0]) + actions = state[1] + if (problem.isGoalState(state[0])): + return actions + for successor in problem.getSuccessors(state[0]): + child_state = successor[0] + action = successor[1] + sub_action = list(actions) + if not child_state in visited_node: + sub_action.append(action) + mypriorityQueue.push((child_state, sub_action),heuristic(child_state, problem) + + problem.getCostOfActions(sub_action)) + return actions + util.raiseNotDefined() +``` + +​ 在写完这四个函数后,在终端中输入 `python2 .\autograder.py` + +![image-20221022153157435](D:\AI\Subject\Pacman\doc\image-20221022153157435.png) + +​ 等到一段时间后,可以得到测试结果 + +![image-20221022153238236](D:\AI\Subject\Pacman\doc\image-20221022153238236.png) + +​ 测试通过! + +## 五.附加实验 + +​ 在完成了search实验的四个搜索算法的基础上,进一步完成附加实验,包括[Corners Problem: Representation](http://ai.berkeley.edu/search.html#Q5),[Corners Problem: Heuristic](http://ai.berkeley.edu/search.html#Q6),[Eating All The Dots: Heuristic](http://ai.berkeley.edu/search.html#Q7),[Suboptimal Search](http://ai.berkeley.edu/search.html#Q8). + +### 1.Corner Problem + +​ 本关主要是实现一个找到所有角落的启发式函数,实现思路为先获取地图的大小,然后记录每个后继的坐标,然后通过比较判断是否为地图角落,实现代码如下 + +```python + def __init__(self, startingGameState): + """ + Stores the walls, pacman's starting position and corners. + """ + self.walls = startingGameState.getWalls() + self.startingPosition = startingGameState.getPacmanPosition() + top, right = self.walls.height-2, self.walls.width-2 + self.corners = ((1,1), (1,top), (right, 1), (right, top)) + for corner in self.corners: + if not startingGameState.hasFood(*corner): + print 'Warning: no food in corner ' + str(corner) + self._expanded = 0 # DO NOT CHANGE; Number of search nodes expanded + # Please add any code here which you would like to use + # in initializing the problem + "*** YOUR CODE HERE ***" + self.top = top + self.right = right +``` + +```python + def getStartState(self): + """ + Returns the start state (in your state space, not the full Pacman state + space) + """ + "*** YOUR CODE HERE ***" + startState = (self.startingPosition,[]) + return startState + util.raiseNotDefined() +``` + +```python + def isGoalState(self, state): + """ + Returns whether this search state is a goal state of the problem. + """ + "*** YOUR CODE HERE ***" + result = state[1] + if state[0] in self.corners: + if state[0] not in result: + result.append(state[0]) + if (len(result) == 4): + return True + else: + return False + util.raiseNotDefined() +``` + +```python + def getSuccessors(self, state): + """ + Returns successor states, the actions they require, and a cost of 1. + + As noted in search.py: + For a given state, this should return a list of triples, (successor, + action, stepCost), where 'successor' is a successor to the current + state, 'action' is the action required to get there, and 'stepCost' + is the incremental cost of expanding to that successor + """ + + successors = [] + for action in [Directions.NORTH, Directions.SOUTH, Directions.EAST, Directions.WEST]: + # Add a successor state to the successor list if the action is legal + # Here's a code snippet for figuring out whether a new position hits a wall: + # x,y = currentPosition + # dx, dy = Actions.directionToVector(action) + # nextx, nexty = int(x + dx), int(y + dy) + # hitsWall = self.walls[nextx][nexty] + + "*** YOUR CODE HERE ***" + x, y = state[0] + dx, dy = Actions.directionToVector(action) + nextx, nexty = int(x + dx), int(y + dy) + visited = list(state[1]) + if not self.walls[nextx][nexty]: + nextState = (nextx, nexty) + cost = 1 + if nextState in self.corners and nextState not in visited: + visited.append(nextState) + successors.append(((nextState, visited), action, cost)) + self._expanded += 1 # DO NOT CHANGE + return successors +``` + + + +### 2.Corners Heuristic + +​ 本关实现一个走到所有角落的最短路径的启发式函数,为之后的搜索作准备,实现思路为通过last记录四个角落,然后依次计算所有后继至四个角落的曼哈顿距离,求和之后取最小值,该后继作为下一步,实现代码如下 + +```python +def cornersHeuristic(state, problem): + """ + A heuristic for the CornersProblem that you defined. + + state: The current search state + (a data structure you chose in your search problem) + + problem: The CornersProblem instance for this layout. + + This function should always return a number that is a lower bound on the + shortest path from the state to a goal of the problem; i.e. it should be + admissible (as well as consistent). + """ + corners = problem.corners # These are the corner coordinates + walls = problem.walls # These are the walls of the maze, as a Grid (game.py) + + "*** YOUR CODE HERE ***" + visited = state[1] + now_state = state[0] + Heuristic = 0 + last = [] + if (problem.isGoalState(state)): + return 0 + for i in corners: + if i not in visited: + last.append(i) + pos = now_state + cost = 999999 + while len(last) != 0: + for i in last: + if cost > (abs(pos[0] - i[0]) + abs(pos[1] - i[1])): + min_con = i + cost = (abs(pos[0] - i[0]) + abs(pos[1] - i[1])) + Heuristic += cost + pos = min_con + cost = 999999 + last.remove(min_con) + return Heuristic +``` + + + +### 3.Food Heuristic + +​ 本关实现一个新的搜索食物的启发式函数,使找到所有食物的路径最小。实现思路为先找到代价最大的食物,然后根据当前位置与最大代价的食物的相对位置,计算出已经吃掉的食物数量,先将代价小的食物都吃掉,最后再吃代价大的食物,即为最短路径。实现代码如下, + +```python +def foodHeuristic(state, problem): + """ + Your heuristic for the FoodSearchProblem goes here. + + This heuristic must be consistent to ensure correctness. First, try to come + up with an admissible heuristic; almost all admissible heuristics will be + consistent as well. + + If using A* ever finds a solution that is worse uniform cost search finds, + your heuristic is *not* consistent, and probably not admissible! On the + other hand, inadmissible or inconsistent heuristics may find optimal + solutions, so be careful. + + The state is a tuple ( pacmanPosition, foodGrid ) where foodGrid is a Grid + (see game.py) of either True or False. You can call foodGrid.asList() to get + a list of food coordinates instead. + + If you want access to info like walls, capsules, etc., you can query the + problem. For example, problem.walls gives you a Grid of where the walls + are. + + If you want to *store* information to be reused in other calls to the + heuristic, there is a dictionary called problem.heuristicInfo that you can + use. For example, if you only want to count the walls once and store that + value, try: problem.heuristicInfo['wallCount'] = problem.walls.count() + Subsequent calls to this heuristic can access + problem.heuristicInfo['wallCount'] + """ + position, foodGrid = state + "*** YOUR CODE HERE ***" + lsFoodGrid = foodGrid.asList() + last = list(lsFoodGrid) + Heuristic = 0 + cost = 0 + max_con = position + for i in last: + if cost < (abs(position[0] - i[0]) + abs(position[1] - i[1])): + max_con = i + cost = (abs(position[0] - i[0]) + abs(position[1] - i[1])) + Heuristic = cost + diff = position[0] - max_con[0] + count = 0 + for i in last: + if diff > 0: + if position[0] < i[0]: + count += 1 + if diff < 0: + if position[0] > i[0]: + count += 1 + if diff == 0: + if position[0] != i[0]: + count += 1 + return Heuristic + count +``` + + + +### 4.Suboptimal Search + +本关实现一个次优搜索,在保证吃掉所有食物的条件下,在较短的时间内找到一个比较好的路径。实现思路为使用优先队列和AnyFoodSearchProblem类中实现的函数完成本关,实现代码如下, + +```python +class ClosestDotSearchAgent(SearchAgent): + "Search for all food using a sequence of searches" + def registerInitialState(self, state): + self.actions = [] + currentState = state + while(currentState.getFood().count() > 0): + nextPathSegment = self.findPathToClosestDot(currentState) # The missing piece + self.actions += nextPathSegment + for action in nextPathSegment: + legal = currentState.getLegalActions() + if action not in legal: + t = (str(action), str(currentState)) + raise Exception, 'findPathToClosestDot returned an illegal move: %s!\n%s' % t + currentState = currentState.generateSuccessor(0, action) + self.actionIndex = 0 + print 'Path found with cost %d.' % len(self.actions) + + def findPathToClosestDot(self, gameState): + """ + Returns a path (a list of actions) to the closest dot, starting from + gameState. + """ + # Here are some useful elements of the startState + startPosition = gameState.getPacmanPosition() + food = gameState.getFood() + walls = gameState.getWalls() + problem = AnyFoodSearchProblem(gameState) + + "*** YOUR CODE HERE ***" + Result = [] + Visited = [] + Queue = util.PriorityQueue() + startState = (problem.getStartState(),[],0) + Queue.push(startState,startState[2]) + while not Queue.isEmpty(): + (state,path,cost) = Queue.pop() + if problem.isGoalState(state): + Result = path + break + if state not in Visited: + Visited.append(state) + for currentState,currentPath,currentCost in problem.getSuccessors(state): + newPath = path + [currentPath] + newCost = cost + currentCost + newState = (currentState,newPath,newCost) + Queue.push(newState,newCost) + return Result + util.raiseNotDefined() + +class AnyFoodSearchProblem(PositionSearchProblem): + """ + A search problem for finding a path to any food. + + This search problem is just like the PositionSearchProblem, but has a + different goal test, which you need to fill in below. The state space and + successor function do not need to be changed. + + The class definition above, AnyFoodSearchProblem(PositionSearchProblem), + inherits the methods of the PositionSearchProblem. + + You can use this search problem to help you fill in the findPathToClosestDot + method. + """ + + def __init__(self, gameState): + "Stores information from the gameState. You don't need to change this." + # Store the food for later reference + self.food = gameState.getFood() + + # Store info for the PositionSearchProblem (no need to change this) + self.walls = gameState.getWalls() + self.startState = gameState.getPacmanPosition() + self.costFn = lambda x: 1 + self._visited, self._visitedlist, self._expanded = {}, [], 0 # DO NOT CHANGE + + def isGoalState(self, state): + """ + The state is Pacman's position. Fill this in with a goal test that will + complete the problem definition. + """ + x,y = state + + "*** YOUR CODE HERE ***" + + foodGrid = self.food + if foodGrid[x][y] == True or foodGrid.count() == 0: + return True + else: + return False + util.raiseNotDefined() + + + +def mazeDistance(point1, point2, gameState): + """ + Returns the maze distance between any two points, using the search functions + you have already built. The gameState can be any game state -- Pacman's + position in that state is ignored. + + Example usage: mazeDistance( (2,4), (5,6), gameState) + + This might be a useful helper function for your ApproximateSearchAgent. + """ + x1, y1 = point1 + x2, y2 = point2 + walls = gameState.getWalls() + assert not walls[x1][y1], 'point1 is a wall: ' + str(point1) + assert not walls[x2][y2], 'point2 is a wall: ' + str(point2) + prob = PositionSearchProblem(gameState, start=point1, goal=point2, warn=False, visualize=False) + return len(search.bfs(prob)) +``` + + + +## 六.实验总结 + +​ 学会了四个经典的搜索算法的用法,在前期的算法课中虽然学习过DFS,BFS,aStar等搜索算法,但是之前的考试都考背诵代码,都没有真正的理解这些算法的过程。Pacman实验将算法的过程通过吃豆人的移动与路径的选择表现出来,让人眼前一亮,也让人更加深刻的理解这些算法。 + + \ No newline at end of file diff --git a/doc/202002422003-黄结伦-实验2.md b/doc/202002422003-黄结伦-实验2.md new file mode 100644 index 0000000..1c74a48 --- /dev/null +++ b/doc/202002422003-黄结伦-实验2.md @@ -0,0 +1,257 @@ +# Pacman 实验2 + +## 一.实验目的 + +​ 1.通过实验理解零和游戏的博弈过程,学会简单的评估函数的设计 + +​ 2.掌握极小极大法和alpha-beta剪枝 + +## 二.实验原理 + +### 1.极小极大法 + +​ 在零和博弈中,玩家均会在可选的选项中选择将其N步后优势最大化或者令对手优势最小化的选择。将双方决策过程视作一颗决策树,若决策树某一层均为己方决策依据状态(即接下来是己方进行动作),则己方必定会选择使得己方收益最大化的路径,将该层称为MAX层。若决策树某一层均为对手决策依据状态(即接下来是对手进行动作),则对手必定会选择使得己方收益最小化的路径,将该层成为MIN层。由此,一个极小极大决策树将包含max节点(MAX层中的节点)、min节点(MIN层中的节点)和终止节点(博弈终止状态节点或N步时的状态节点)。每个节点对应的预期收益成为该节点的minimax值。 +​ 对于终止结点, minimax值等于直接对局面的估值。对于max结点,由于max节点所选择的动作将会由己方给定,因此选择minimax值最大的子结点的值作为max结点的值。对于min结点,则选择minimax值最小的子结点的值作为min结点的值。 +极小极大算法过程可描述如下: + +构建决策树; + 1.将评估函数应用于叶子结点; + 2.自底向上计算每个结点的minimax值; + 3.从根结点选择minimax值最大的分支,作为行动策略。 +minimax计算流程如下: + +​ 1.如果节点是终止节点:应用估值函数求值; +​ 2.如果节点是max节点:找到每个子节点的值,将其中最大的子节点值作为该节点的值; +​ 3.如果节点时min节点:找到每个子节点的值,将其中最小的子节点值作为该节点的值。 + +其伪代码如下, + +![image-20221021214532214](D:\AI\Subject\Pacman\doc\image-20221021214532214.png) + +### 2.alpah-beta剪枝 + +​ 举个简单的例子说明**alpha-beta剪枝**的过程 + +![image-20221021214643168](D:\AI\Subject\Pacman\doc\image-20221021214643168.png) + +​ 其伪代码如下, + +![image-20221021214709128](D:\AI\Subject\Pacman\doc\image-20221021214709128.png) + +## 三.实验内容 + +​ 1.完成multiagent文件夹中的evaluationFunction,MinimaxAgent,AlphaBetaAgent函数。 + +​ 在编写完代码后,可在终端中的multiagent目录下执行`python2 .\autograder.py` 命令,即可查看是否通过! + +## 四.实验结果 + +​ 博弈算法中的三个函数(类)的具体实现如下 + +### 1.evaluationFunction + +```python + def evaluationFunction(self, currentGameState, action): + """ + Design a better evaluation function here. + + The evaluation function takes in the current and proposed successor + GameStates (pacman.py) and returns a number, where higher numbers are better. + + The code below extracts some useful information from the state, like the + remaining food (newFood) and Pacman position after moving (newPos). + newScaredTimes holds the number of moves that each ghost will remain + scared because of Pacman having eaten a power pellet. + + Print out these variables to see what you're getting, then combine them + to create a masterful evaluation function. + """ + # Useful information you can extract from a GameState (pacman.py) + successorGameState = currentGameState.generatePacmanSuccessor(action) + newPos = successorGameState.getPacmanPosition() + newFood = successorGameState.getFood() + newGhostStates = successorGameState.getGhostStates() + newScaredTimes = [ghostState.scaredTimer for ghostState in newGhostStates] + #many ghosts + "*** YOUR CODE HERE ***" + GhostPos = successorGameState.getGhostPositions() + x_pacman,y_pacman = newPos + failedDist = min([(abs(each[0]- x_pacman) + abs(each[1]-y_pacman)) for each in GhostPos]) + if failedDist != 0 and failedDist < 4: + ghostScore = -11 / failedDist + else : + ghostScore = 0 + nearestFood = float('inf') + width = newFood.width + height = newFood.height + if failedDist >= 2: + dx = [1,0,-1,0] + dy = [0,1,0,-1] + List = [] + d = {} + List.append(newPos) + d.update({(x_pacman,y_pacman) : 1}) + while List: + tempPos = List[0] + List.pop(0) + temp_x,temp_y = tempPos + if newFood[temp_x][temp_y]: + nearestFood = min(nearestFood,(abs(temp_x - x_pacman) + abs(temp_y - y_pacman))) + break + for i in range(len(dx)): + x = temp_x + dx[i] + y = temp_y + dy[i] + if 0 <= x < width and 0 <= y < height: + tempPos =(x,y) + if tempPos not in d: + d[tempPos] = 1 + List.append(tempPos) + if nearestFood != float('inf'): + foodScore = 10 / nearestFood + else : + foodScore = 0 + return successorGameState.getScore() + foodScore + ghostScore +``` + +### 2.MinimaxAgent + +```python +class MinimaxAgent(MultiAgentSearchAgent): + """ + Your minimax agent (question 2) + """ + def getAction(self, gameState): + """ + Returns the minimax action from the current gameState using self.depth + and self.evaluationFunction. + + Here are some method calls that might be useful when implementing minimax. + + gameState.getLegalActions(agentIndex): + Returns a list of legal actions for an agent + agentIndex=0 means Pacman, ghosts are >= 1 + + gameState.generateSuccessor(agentIndex, action): + Returns the successor game state after an agent takes an action + + gameState.getNumAgents(): + Returns the total number of agents in the game + + gameState.isWin(): + Returns whether or not the game state is a winning state + + gameState.isLose(): + Returns whether or not the game state is a losing state + """ + "*** YOUR CODE HERE ***" + + def gameOver(gameState): + return gameState.isWin() or gameState.isLose() + #Be different with me + def min_value(gameState, depth, ghost): + value = float('inf') + if gameOver(gameState): + return self.evaluationFunction(gameState) + for action in gameState.getLegalActions(ghost): + if ghost == gameState.getNumAgents() - 1: + value = min(value, max_value(gameState.generateSuccessor(ghost, action), depth)) + else: + value = min(value, min_value(gameState.generateSuccessor(ghost, action), depth, + ghost + 1)) + return value + + + def max_value(gameState, depth): + value = float('-inf') + depth = depth + 1 + #Be different with me + if depth == self.depth or gameOver(gameState): + return self.evaluationFunction(gameState) + for action in gameState.getLegalActions(0): + value = max(value, min_value(gameState.generateSuccessor(0, action), depth, 1)) + return value + nextAction = gameState.getLegalActions(0) + Max = float('-inf') + Result = None + + for action in nextAction: + if (action != "stop"): + depth = 0 + value = min_value(gameState.generateSuccessor(0, action), depth, 1) + if (value > Max): + Max = value + Result = action + return Result + util.raiseNotDefined() +``` + +### 3.AlphaBetaAgent + +```python +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 ***" + ghostIndex = [i for i in range(1,gameState.getNumAgents())] + def gameOver(state,depth): + return state.isWin() or state.isLose() or depth == self.depth + def min_value(state,depth,ghost,alpha,beta): + if gameOver(state,depth): + return self.evaluationFunction(state) + value = float('inf') + for action in state.getLegalActions(ghost): + if ghost == ghostIndex[-1]: + value = min(value,max_value(state.generateSuccessor(ghost,action),depth+1,alpha,beta)) + else: + value = min(value,min_value(state.generateSuccessor(ghost,action),depth,ghost+1,alpha,beta)) + if value < alpha: + return value + beta = min(beta,value) + return value + def max_value(state,depth,alpha,beta): + if gameOver(state,depth): + return self.evaluationFunction(state) + value = float('-inf') + for action in state.getLegalActions(0): + if action == 'stop': + continue + value = max(value,min_value(state.generateSuccessor(0,action),depth,1,alpha,beta)) + if value > beta: + return value + alpha = max(value,alpha) + return value + def function(state): + value = float('-inf') + actions = None + alpha = float('-inf') + beta = float('inf') + for action in state.getLegalActions(0): + if action == 'stop': + continue + tmpValue = min_value(state.generateSuccessor(0,action),0,1,alpha,beta) + if value < tmpValue: + value = tmpValue + actions = action + alpha = max(value,alpha) + return actions + return function(gameState) + util.raiseNotDefined() +``` + + + +​ 测试过程与结果如下 + +![image-20221022153803006](D:\AI\Subject\Pacman\doc\image-20221022153803006.png) + +![image-20221022153822732](D:\AI\Subject\Pacman\doc\image-20221022153822732.png) + +## 五.实验总结 + +​ 博弈算法在AI课程中第一次接触,虽然博弈过程是一个十分玄妙的过程,但是计算机人用其大智慧,将这个过程转化为一个可以用数学公式描述的过程,而这种抽象化的思维能力是一个及其重要的能力。通过Pacman的实验,加深了我对alpha-beta剪枝的原理与过程。 \ No newline at end of file diff --git a/doc/202002422003_黄结伦_实验1.pdf b/doc/202002422003_黄结伦_实验1.pdf new file mode 100644 index 0000000..41974e1 Binary files /dev/null and b/doc/202002422003_黄结伦_实验1.pdf differ diff --git a/doc/202002422003_黄结伦_实验2.pdf b/doc/202002422003_黄结伦_实验2.pdf new file mode 100644 index 0000000..a213cae Binary files /dev/null and b/doc/202002422003_黄结伦_实验2.pdf differ diff --git a/doc/image-20221021212311445.png b/doc/image-20221021212311445.png new file mode 100644 index 0000000..ecf52be Binary files /dev/null and b/doc/image-20221021212311445.png differ diff --git a/doc/image-20221021212559638.png b/doc/image-20221021212559638.png new file mode 100644 index 0000000..6fc6f11 Binary files /dev/null and b/doc/image-20221021212559638.png differ diff --git a/doc/image-20221021212636227.png b/doc/image-20221021212636227.png new file mode 100644 index 0000000..d99274d Binary files /dev/null and b/doc/image-20221021212636227.png differ diff --git a/doc/image-20221021212936449.png b/doc/image-20221021212936449.png new file mode 100644 index 0000000..bff341c Binary files /dev/null and b/doc/image-20221021212936449.png differ diff --git a/doc/image-20221021213229986.png b/doc/image-20221021213229986.png new file mode 100644 index 0000000..b7f95a9 Binary files /dev/null and b/doc/image-20221021213229986.png differ diff --git a/doc/image-20221021213316266.png b/doc/image-20221021213316266.png new file mode 100644 index 0000000..039dc9f Binary files /dev/null and b/doc/image-20221021213316266.png differ diff --git a/doc/image-20221021213939136.png b/doc/image-20221021213939136.png new file mode 100644 index 0000000..a5b4c09 Binary files /dev/null and b/doc/image-20221021213939136.png differ diff --git a/doc/image-20221021214532214.png b/doc/image-20221021214532214.png new file mode 100644 index 0000000..b4cfa0d Binary files /dev/null and b/doc/image-20221021214532214.png differ diff --git a/doc/image-20221021214643168.png b/doc/image-20221021214643168.png new file mode 100644 index 0000000..8b5c894 Binary files /dev/null and b/doc/image-20221021214643168.png differ diff --git a/doc/image-20221021214709128.png b/doc/image-20221021214709128.png new file mode 100644 index 0000000..1a4f77a Binary files /dev/null and b/doc/image-20221021214709128.png differ diff --git a/doc/image-20221022153157435.png b/doc/image-20221022153157435.png new file mode 100644 index 0000000..5139366 Binary files /dev/null and b/doc/image-20221022153157435.png differ diff --git a/doc/image-20221022153238236.png b/doc/image-20221022153238236.png new file mode 100644 index 0000000..7398ce0 Binary files /dev/null and b/doc/image-20221022153238236.png differ diff --git a/doc/image-20221022153803006.png b/doc/image-20221022153803006.png new file mode 100644 index 0000000..4f28e7e Binary files /dev/null and b/doc/image-20221022153803006.png differ diff --git a/doc/image-20221022153822732.png b/doc/image-20221022153822732.png new file mode 100644 index 0000000..ef1fcc4 Binary files /dev/null and b/doc/image-20221022153822732.png differ diff --git a/doc/实验报告模板.pdf b/doc/实验报告模板.pdf new file mode 100644 index 0000000..8cdb2e1 Binary files /dev/null and b/doc/实验报告模板.pdf differ diff --git a/multiagent/multiagent/autograder.pyc b/multiagent/multiagent/autograder.pyc new file mode 100644 index 0000000..da84be1 Binary files /dev/null and b/multiagent/multiagent/autograder.pyc differ diff --git a/multiagent/multiagent/game.pyc b/multiagent/multiagent/game.pyc new file mode 100644 index 0000000..3699c34 Binary files /dev/null and b/multiagent/multiagent/game.pyc differ diff --git a/multiagent/multiagent/ghostAgents.pyc b/multiagent/multiagent/ghostAgents.pyc new file mode 100644 index 0000000..c0b3d99 Binary files /dev/null and b/multiagent/multiagent/ghostAgents.pyc differ diff --git a/multiagent/multiagent/grading.pyc b/multiagent/multiagent/grading.pyc new file mode 100644 index 0000000..8f231f8 Binary files /dev/null and b/multiagent/multiagent/grading.pyc differ diff --git a/multiagent/multiagent/graphicsDisplay.pyc b/multiagent/multiagent/graphicsDisplay.pyc new file mode 100644 index 0000000..a0f0b63 Binary files /dev/null and b/multiagent/multiagent/graphicsDisplay.pyc differ diff --git a/multiagent/multiagent/graphicsUtils.pyc b/multiagent/multiagent/graphicsUtils.pyc new file mode 100644 index 0000000..91bd2b2 Binary files /dev/null and b/multiagent/multiagent/graphicsUtils.pyc differ diff --git a/multiagent/multiagent/keyboardAgents.pyc b/multiagent/multiagent/keyboardAgents.pyc new file mode 100644 index 0000000..135f307 Binary files /dev/null and b/multiagent/multiagent/keyboardAgents.pyc differ diff --git a/multiagent/multiagent/layout.pyc b/multiagent/multiagent/layout.pyc new file mode 100644 index 0000000..8f45b28 Binary files /dev/null and b/multiagent/multiagent/layout.pyc differ diff --git a/multiagent/multiagent/multiAgents.pyc b/multiagent/multiagent/multiAgents.pyc new file mode 100644 index 0000000..e170839 Binary files /dev/null and b/multiagent/multiagent/multiAgents.pyc differ diff --git a/multiagent/multiagent/multiagentTestClasses.pyc b/multiagent/multiagent/multiagentTestClasses.pyc new file mode 100644 index 0000000..77b31bc Binary files /dev/null and b/multiagent/multiagent/multiagentTestClasses.pyc differ diff --git a/multiagent/multiagent/pacman.pyc b/multiagent/multiagent/pacman.pyc new file mode 100644 index 0000000..c7bff85 Binary files /dev/null and b/multiagent/multiagent/pacman.pyc differ diff --git a/multiagent/multiagent/pacmanAgents.pyc b/multiagent/multiagent/pacmanAgents.pyc new file mode 100644 index 0000000..4b09f53 Binary files /dev/null and b/multiagent/multiagent/pacmanAgents.pyc differ diff --git a/multiagent/multiagent/projectParams.pyc b/multiagent/multiagent/projectParams.pyc new file mode 100644 index 0000000..2e4c8e8 Binary files /dev/null and b/multiagent/multiagent/projectParams.pyc differ diff --git a/multiagent/multiagent/testClasses.pyc b/multiagent/multiagent/testClasses.pyc new file mode 100644 index 0000000..5e0fd0a Binary files /dev/null and b/multiagent/multiagent/testClasses.pyc differ diff --git a/multiagent/multiagent/testParser.pyc b/multiagent/multiagent/testParser.pyc new file mode 100644 index 0000000..6127ca6 Binary files /dev/null and b/multiagent/multiagent/testParser.pyc differ diff --git a/multiagent/multiagent/textDisplay.pyc b/multiagent/multiagent/textDisplay.pyc new file mode 100644 index 0000000..9f58a50 Binary files /dev/null and b/multiagent/multiagent/textDisplay.pyc differ diff --git a/multiagent/multiagent/util.pyc b/multiagent/multiagent/util.pyc new file mode 100644 index 0000000..762e132 Binary files /dev/null and b/multiagent/multiagent/util.pyc differ diff --git a/project1.rar b/project1.rar new file mode 100644 index 0000000..7b88c9f Binary files /dev/null and b/project1.rar differ diff --git a/project2.rar b/project2.rar new file mode 100644 index 0000000..faf0a6c Binary files /dev/null and b/project2.rar differ diff --git a/search/grading.pyc b/search/grading.pyc new file mode 100644 index 0000000..d147ad5 Binary files /dev/null and b/search/grading.pyc differ diff --git a/search/projectParams.pyc b/search/projectParams.pyc new file mode 100644 index 0000000..6255abc Binary files /dev/null and b/search/projectParams.pyc differ diff --git a/search/searchTestClasses.pyc b/search/searchTestClasses.pyc new file mode 100644 index 0000000..3116b9e Binary files /dev/null and b/search/searchTestClasses.pyc differ diff --git a/search/testClasses.pyc b/search/testClasses.pyc new file mode 100644 index 0000000..290a003 Binary files /dev/null and b/search/testClasses.pyc differ diff --git a/search/testParser.pyc b/search/testParser.pyc new file mode 100644 index 0000000..4608e42 Binary files /dev/null and b/search/testParser.pyc differ diff --git a/search/textDisplay.pyc b/search/textDisplay.pyc new file mode 100644 index 0000000..83c9ff7 Binary files /dev/null and b/search/textDisplay.pyc differ