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
哈哈哈