八數碼 A*演算法
阿新 • • 發佈:2019-02-14
from utils import ( PriorityQueue) import copy infinity = float('inf') def best_first_graph_search(problem, f): #定義初始節點 node = Node(problem.initial) node.fvalue=f(node) #如果是最終結果,返回節點 if problem.goal_test(node): return node #frotier是一個順序佇列,從小到大排列,排列比較通過f函式 #如果order是min,最小的先出隊 frontier = PriorityQueue(min, f) #加入節點 frontier.append(node) #print(node.fvalue) #展開的節點 explored = set() #當棧不為空 while frontier: #節點出隊 node = frontier.pop() #print("pop") #node.state.display() #print(node.fvalue) # 如果是最終結果,返回節點 if problem.goal_test(node): return node #加入展開的節點 explored.add(node) #對於出棧的子節點 for child in node.expand(problem): #如果節點沒有展開,並且子節點沒在隊中 if child not in explored and child not in frontier: #子節點入隊 frontier.append(child) #print(child.fvalue) #如果子節點在隊中 elif child in frontier: incumbent = frontier[child] #如果子節點的f值小於隊中節點的f值 if f(child) < f(incumbent): #刪除棧中的節點,子節點入隊 del frontier[incumbent] frontier.append(child) #print("change fvalue",child.state.display(),child.fvalue) return None #a*演算法 f=g+h def astar_search(problem, h=None): h = problem.h return best_first_graph_search(problem, lambda n:h(n) + n.path_cost) class Problem(object): def __init__(self, initial, goal=None): self.initial = initial self.goal = goal def actions(self, state): raise NotImplementedError def result(self, state, action): raise NotImplementedError def goal_test(self, node): return (node.state.board==self.goal.board) def path_cost(self, c, state1, action, state2): return c + 1 def value(self, state): raise NotImplementedError class PuzzleProblem(Problem): def __init__(self, initial=None, goal=None): Problem.__init__(self, initial, goal) self.state=initial self.goal=goal def actions(self, state): x,y=state.location(0) #空格在邊緣時減少活動 action=[(0,1),(0,-1),(1,0),(-1,0)] if (x == 0): action.remove((-1,0)) if (x == 2): action.remove((1, 0)) if (y == 0): action.remove((0, -1)) if (y == 2): action.remove((0, 1)) return list(action) def result(self, state, action): #返回移動空格後的棋盤 x,y=state.location(0) a,b=action n= state.board[x+a][y+b] s=copy.deepcopy(state) s.board[x+a][y+b]=0 s.board[x][y]=n return s def path_cost( self,node): #展開子節點cost+1 return node.path_cost+1 def h(self, now): #當前棋盤每個格到目標棋盤的距離的平方和 if now.state or self.goal: return now.state.distance(self.goal) else: return infinity class Node: def __init__(self, state, parent=None, action=None, path_cost=0): self.state = state self.parent = parent self.action = action self.path_cost = path_cost self.fvalue=0 self.depth = 0 if parent: self.depth = parent.depth + 1 def __repr__(self): return "<Node {}>".format(self.state.board) def __lt__(self, node): #<運算子 佇列排序使用 return self.fvalue < node.fvalue def expand(self, problem): #展開子節點 l = [] for action in problem.actions(self.state): n=self.child_node(problem, action) l.append(n) return l def child_node(self, problem, action): next = problem.result(self.state, action) return Node(next, self, action,problem.path_cost(self)) def solution(self): return self.path() def printsolution(self): l=self.path() print("selution:") for x in range(len(l)- 1, -1, -1): print(" ↓") l[x].state.display() def path(self): #返回父節點路徑list node, path_back = self, [] while node: path_back.append(node) node = node.parent return path_back def __eq__(self, other): #==運算子 return isinstance(other, Node) and self.state.board==other.state.board def __hash__(self): #not in 使用 比較物件Node board=self.state.board sum=0 for x in range(0, 3): for y in range(0, 3): number = board[x][y] sum=sum*10+number return sum class GameState: def __init__(self,board=None,action=None): self.board=board self.action=action def display(state): board = state.board if board: for x in range(0, 3): for y in range(0, 3): print(board[x][y], end=' ') print() def location(state, number): #數字對應座標 for x in range(0, 3): for y in range(0, 3): if state.board[x][y] == number: return (x, y) return (0, 0) def distance(statea, stateb): #棋盤的距離 board = statea.board s = 0 for x in range(0, 3): for y in range(0, 3): number = board[x][y] a, b = stateb.location(number) d = (x - a) ** 2 + (y - b) ** 2 s = s + d return s """Provides some utilities widely used by other modules""" import bisect class Queue: """Queue is an abstract class/interface. There are three types: Stack(): A Last In First Out Queue. FIFOQueue(): A First In First Out Queue. PriorityQueue(order, f): Queue in sorted order (default min-first). Each type supports the following methods and functions: q.append(item) -- add an item to the queue q.extend(items) -- equivalent to: for item in items: q.append(item) q.pop() -- return the top item from the queue len(q) -- number of items in q (also q.__len()) item in q -- does q contain item? Note that isinstance(Stack(), Queue) is false, because we implement stacks as lists. If Python ever gets interfaces, Queue will be an interface.""" def __init__(self): raise NotImplementedError def extend(self, items): for item in items: self.append(item) class PriorityQueue(Queue): """A queue in which the minimum (or maximum) element (as determined by f and order) is returned first. If order is min, the item with minimum f(x) is returned first; if order is max, then it is the item with maximum f(x). Also supports dict-like lookup. 如果是order=MIN,最小f(x)值的item將出隊""" def __init__(self, order=min, f=lambda x: x): self.A = [] self.order = order self.f = f #按順序插入,按f(x)從小到大 def append(self, item): item.fvalue=self.f(item) bisect.insort(self.A, item) #print("push") #item.state.display() def __len__(self): return len(self.A) #order=min輸出最小 否則輸出最大 def pop(self): if self.order == min: return self.A.pop(0) else: return self.A.pop() def __contains__(self, item): return any(item.state.board == pair.state.board for pair in self.A) def __getitem__(self, key): for item in self.A: if item == key: return item def __delitem__(self, key): for item in enumerate(self.A): if item == key: self.A.pop(item) from eightpuzzle import * goal = GameState( [[7,1,6], [5,3,2], [0,8,4]] ) start = GameState([[2,0,3,], [1,8,4,], [7,6,5]] ) goal.action=PuzzleProblem().actions(goal) start.action=PuzzleProblem().actions(start) p=PuzzleProblem(start,goal) result=astar_search(p) result.printsolution() selution: ↓ 2 0 3 1 8 4 7 6 5 ↓ 0 2 3 1 8 4 7 6 5 ↓ 1 2 3 0 8 4 7 6 5 ↓ 1 2 3 8 0 4 7 6 5 ↓ 1 2 3 8 6 4 7 0 5 ↓ 1 2 3 8 6 4 7 5 0 ↓ 1 2 3 8 6 0 7 5 4 ↓ 1 2 3 8 0 6 7 5 4 ↓ 1 2 3 0 8 6 7 5 4 ↓ 1 2 3 7 8 6 0 5 4 ↓ 1 2 3 7 8 6 5 0 4 ↓ 1 2 3 7 0 6 5 8 4 ↓ 1 0 3 7 2 6 5 8 4 ↓ 1 3 0 7 2 6 5 8 4 ↓ 1 3 6 7 2 0 5 8 4 ↓ 1 3 6 7 0 2 5 8 4 ↓ 1 0 6 7 3 2 5 8 4 ↓ 0 1 6 7 3 2 5 8 4 ↓ 7 1 6 0 3 2 5 8 4 ↓ 7 1 6 5 3 2 0 8 4
哈哈哈