380 |
ira |
1 |
#!/usr/bin/env python
|
|
|
2 |
|
|
|
3 |
__author__ = "Ira W. Snyder (devel@irasnyder.com)"
|
|
|
4 |
__copyright__ = "Copyright (c) 2006 Ira W. Snyder (devel@irasnyder.com)"
|
|
|
5 |
__license__ = "GNU GPL v2 (or, at your option, any later version)"
|
|
|
6 |
|
|
|
7 |
# 1) Put the start node N0 on OPEN. Create G with just this node
|
|
|
8 |
# 2) Create the list CLOSED which is empty
|
|
|
9 |
#
|
|
|
10 |
# LOOP:
|
|
|
11 |
#
|
|
|
12 |
# 3) If OPEN is empty, exit with FAILURE
|
|
|
13 |
# 4) Select the first node from OPEN, put it on CLOSED. Call this N
|
|
|
14 |
# 5) If N is a goal node, exit successfully with the solution in G
|
|
|
15 |
# 6) Generate M from the children of N
|
|
|
16 |
# 7) Add anything not in M not in (CLOSED union OPEN) to OPEN
|
|
|
17 |
# 8) Reorder OPEN appropriately
|
|
|
18 |
# 9) goto LOOP
|
|
|
19 |
|
|
|
20 |
from Graph import Graph
|
|
|
21 |
import yapgvb
|
|
|
22 |
|
|
|
23 |
class GraphSearch (object):
|
|
|
24 |
"""Implements a graph search"""
|
|
|
25 |
|
|
|
26 |
def __init__ (self, full_graph, start_node, goal_nodes):
|
|
|
27 |
"""Constructor.
|
|
|
28 |
full_graph: a Graph representing all the knowledge
|
|
|
29 |
start_node: the node to start at (must be in full_graph)
|
|
|
30 |
goal_nodes: a list of nodes to end at"""
|
|
|
31 |
|
|
|
32 |
self.__kb = full_graph
|
|
|
33 |
self.__start_node = start_node
|
|
|
34 |
self.__goal_nodes = goal_nodes
|
|
|
35 |
self.__ordering_func = list.sort
|
|
|
36 |
|
|
|
37 |
def set_ordering_func (self, func):
|
|
|
38 |
"""Set the ordering function to use."""
|
|
|
39 |
self.__ordering_func = func
|
|
|
40 |
|
|
|
41 |
def __find_nearest_child (self, children, already_visited):
|
|
|
42 |
"""Find the child that we came from. This necessitates that
|
|
|
43 |
the list already_visited be sorted in the order of nodes visited"""
|
|
|
44 |
for n in reversed(already_visited):
|
|
|
45 |
if n in children:
|
|
|
46 |
return n
|
|
|
47 |
|
|
|
48 |
# This should never happen
|
|
|
49 |
raise ValueError
|
|
|
50 |
|
|
|
51 |
def search (self):
|
|
|
52 |
|
|
|
53 |
# Create the result graph
|
|
|
54 |
result = Graph ()
|
|
|
55 |
#result.add_vertex (self.__start_node)
|
|
|
56 |
firsttime = True
|
|
|
57 |
counter = 0
|
|
|
58 |
|
|
|
59 |
OPEN = [self.__start_node]
|
|
|
60 |
CLOSED = []
|
|
|
61 |
|
|
|
62 |
while OPEN:
|
|
|
63 |
N = OPEN.pop(0)
|
|
|
64 |
CLOSED.append (N)
|
|
|
65 |
|
|
|
66 |
# Find all possible next paths
|
|
|
67 |
M = self.__kb.get_children (N)
|
|
|
68 |
|
|
|
69 |
# Add the current place to the result graph
|
|
|
70 |
result.add_vertex (N)
|
|
|
71 |
if not firsttime:
|
|
|
72 |
v1 = N
|
|
|
73 |
v2 = self.__find_nearest_child (M, CLOSED)
|
|
|
74 |
|
|
|
75 |
result.add_edge (v1, v2)
|
|
|
76 |
result.set_edge_color (v1, v2, yapgvb.colors.red)
|
|
|
77 |
result.set_edge_label (v1, v2, str(counter))
|
|
|
78 |
|
|
|
79 |
self.__kb.set_edge_color (v1, v2, 'red')
|
|
|
80 |
self.__kb.set_edge_label (v1, v2, str(counter))
|
|
|
81 |
else:
|
|
|
82 |
firsttime = False
|
|
|
83 |
|
|
|
84 |
# Check if we've reached the goal
|
|
|
85 |
if N in self.__goal_nodes:
|
|
|
86 |
return self.__kb #result
|
|
|
87 |
|
382 |
ira |
88 |
#for node in M:
|
|
|
89 |
# if (node not in OPEN) and (node not in CLOSED):
|
|
|
90 |
# OPEN.append (node)
|
380 |
ira |
91 |
|
|
|
92 |
# Sort OPEN appropriately
|
|
|
93 |
# NOTE: no sort algorithm is BFS
|
|
|
94 |
#self.__ordering_func (OPEN)
|
|
|
95 |
|
|
|
96 |
# FIXME: maybe take the "for node in M" loop and
|
|
|
97 |
# FIXME: combine it with the ordering function into
|
|
|
98 |
# FIXME: it's own function that chooses how to add
|
|
|
99 |
# FIXME: the nodes from M into OPEN
|
|
|
100 |
#
|
|
|
101 |
# FIXME: prototype might be: func (M, OPEN, CLOSED)
|
382 |
ira |
102 |
#add_dfs (M, OPEN, CLOSED)
|
|
|
103 |
OPEN = add_bfs (M, OPEN, CLOSED)
|
380 |
ira |
104 |
|
|
|
105 |
counter += 1
|
|
|
106 |
|
|
|
107 |
return 'FAILURE'
|
|
|
108 |
|
|
|
109 |
def add_bfs (M, OPEN, CLOSED):
|
|
|
110 |
for node in M:
|
|
|
111 |
if (node not in OPEN) and (node not in CLOSED):
|
|
|
112 |
OPEN.append (node)
|
|
|
113 |
|
382 |
ira |
114 |
return OPEN
|
|
|
115 |
|
380 |
ira |
116 |
def add_dfs (M, OPEN, CLOSED):
|
|
|
117 |
for node in M:
|
|
|
118 |
if (node not in OPEN) and (node not in CLOSED):
|
|
|
119 |
OPEN.insert (0, node) # insert node at beginning
|
|
|
120 |
|
382 |
ira |
121 |
return OPEN
|
380 |
ira |
122 |
|
382 |
ira |
123 |
|
380 |
ira |
124 |
from Graph import Graph
|
|
|
125 |
from DrawGraph import DrawGraph
|
|
|
126 |
|
|
|
127 |
def main ():
|
|
|
128 |
#vertices = [0, 1, 2, 3, 'goal']
|
|
|
129 |
#edges = ( [0,1], [1,2], [2,3], [3,'goal'] )
|
|
|
130 |
|
|
|
131 |
#vertices = [0, 1, 2, 3, 'goal']
|
|
|
132 |
#edges = ( [0,1], [0,2], [2,3], [2,'goal'] )
|
|
|
133 |
|
|
|
134 |
vertices = [0, 1, 2, 3, 5, 6, 7, 8, 'g1', 'g2']
|
|
|
135 |
edges = ( [0,1], [0,2], [0,8], [2,3], [2,'g2'], [1,5], [1,6], [6,7], [7,'g1'] )
|
|
|
136 |
|
|
|
137 |
g = Graph (vertices, edges)
|
|
|
138 |
s = GraphSearch (g, 0, ['g1', 'g2'])
|
|
|
139 |
result = s.search ()
|
|
|
140 |
|
|
|
141 |
if result != 'FAILURE':
|
|
|
142 |
DrawGraph ('result', result).render_graph ('res.svg') #result.vertices, result.edges.keys ()).render_graph ('res.svg')
|
|
|
143 |
|
|
|
144 |
|
|
|
145 |
if __name__ == '__main__':
|
|
|
146 |
main ()
|