-
Notifications
You must be signed in to change notification settings - Fork 0
/
general_search.py
101 lines (91 loc) · 4.07 KB
/
general_search.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# format for search taken from project prompt, all functions developed by Tyson Loveless
from heapq import heappush, heappop # for priority queue structure
import puzzle
printing = True
diameter = 31
MAX_QUEUE_SIZE = 0 # keeps track of queue size
TOTAL_EXPANDED = 0 # keeps track of total nodes expanded
# updates maximum depth when puzzle size is changed
def setDiameter():
global diameter
if puzzle.size == 9:
diameter = 31
elif puzzle.size == 16:
diameter = 80
elif puzzle.size == 25:
diameter = 208
else:
diameter = float('inf')
# helper function to print the current state of the puzzle
def printPuzzle(state):
j = 0
for i in range(0, puzzle.edge):
print ' ',
while j < puzzle.edge:
if state[j + puzzle.edge * i] == 0:
print '*',
else:
print state[j + puzzle.edge * i],
print "\t",
j += 1
print ""
j = 0
# expand does the work of expanding a node using the given operators for the problem
# the node is the current node, the operators are the legal moves on the current node
def expand(node, operators):
children = []
blankPosition = node.STATE.index(0) # finds the index of the blank tile (0)
for oper in operators:
child = oper(node, blankPosition) # gets child from position using operator, if illegal child = 0
if child: # if operator was legal move
children.append(child) # append child node to list of nodes
return children
# general form of search function based on project instructions
def search(problem, function):
global diameter
global TOTAL_EXPANDED
global MAX_QUEUE_SIZE
# in case you enter a custom puzzle that is already solved
if problem.GOAL_TEST(problem.INITIAL_STATE.STATE):
print "\n\nThe puzzle isn't mixed up yet!"
return problem.INITIAL_STATE, TOTAL_EXPANDED, MAX_QUEUE_SIZE
nodes = [] # our priority queue
closed = {} # positions that have been marked off as too costly to continue with
heappush(nodes, [float('inf'), problem.INITIAL_STATE]) # root node pushed to pqueue
while True:
if not nodes: # nodes is empty, no soln found, we lose
return 0, 0, 0
# keep track of max queue size
MAX_QUEUE_SIZE = max(MAX_QUEUE_SIZE, nodes.__len__())
cost, node = heappop(nodes) # queue is popped with highest priority first (least cost)
closed[tuple(node.STATE)] = True
if printing:
if function is 1:
print "The best state to expand with a g(n) = %d and h(n) = %d is..." % (node.DEPTH, 0)
elif function is 2:
print "The best state to expand with a g(n) = %d and h(n) = %d is..." % (node.DEPTH, node.mis())
else:
print "The best state to expand with a g(n) = %d and h(n) = %d is..." % (node.DEPTH, node.man())
printPuzzle(node.STATE)
print ' Expanding this node...'
else:
if function is 2:
node.mis()
elif function is 3:
node.man()
# for every child to be expanded, we check if its state has already been visited
# if so, we do nothing, otherwise we push it to our priority queue based on our
# given queueing function heuristics
for child in expand(node, problem.OPERATORS):
if tuple(child.STATE) not in closed:
if child.DEPTH <= diameter:
if function is 1:
heappush(nodes, [child.DEPTH, child])
elif function is 2:
heappush(nodes, [child.DEPTH + child.mis(), child])
else:
heappush(nodes, [child.DEPTH + child.man(), child])
TOTAL_EXPANDED += 1
# success is checked once a node is expanded, to avoid expanding extra nodes
if problem.GOAL_TEST(child.STATE):
return child, TOTAL_EXPANDED, MAX_QUEUE_SIZE