Skip to content

Commit 9635976

Browse files
committed
commit inicial
1 parent 1231834 commit 9635976

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

eightpuzzle.py

+281
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# eightpuzzle.py
2+
# --------------
3+
# Licensing Information: You are free to use or extend these projects for
4+
# educational purposes provided that (1) you do not distribute or publish
5+
# solutions, (2) you retain this notice, and (3) you provide clear
6+
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
7+
#
8+
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
9+
# The core projects and autograders were primarily created by John DeNero
10+
11+
# Student side autograding was added by Brad Miller, Nick Hay, and
12+
# Pieter Abbeel ([email protected]).
13+
14+
15+
import search
16+
import random
17+
18+
# Module Classes
19+
20+
class EightPuzzleState:
21+
"""
22+
The Eight Puzzle is described in the course textbook on
23+
page 64.
24+
25+
This class defines the mechanics of the puzzle itself. The
26+
task of recasting this puzzle as a search problem is left to
27+
the EightPuzzleSearchProblem class.
28+
"""
29+
30+
def __init__( self, numbers ):
31+
"""
32+
Constructs a new eight puzzle from an ordering of numbers.
33+
34+
numbers: a list of integers from 0 to 8 representing an
35+
instance of the eight puzzle. 0 represents the blank
36+
space. Thus, the list
37+
38+
[1, 0, 2, 3, 4, 5, 6, 7, 8]
39+
40+
represents the eight puzzle:
41+
-------------
42+
| 1 | | 2 |
43+
-------------
44+
| 3 | 4 | 5 |
45+
-------------
46+
| 6 | 7 | 8 |
47+
------------
48+
49+
The configuration of the puzzle is stored in a 2-dimensional
50+
list (a list of lists) 'cells'.
51+
"""
52+
self.cells = []
53+
numbers = numbers[:] # Make a copy so as not to cause side-effects.
54+
numbers.reverse()
55+
for row in range( 3 ):
56+
self.cells.append( [] )
57+
for col in range( 3 ):
58+
self.cells[row].append( numbers.pop() )
59+
if self.cells[row][col] == 0:
60+
self.blankLocation = row, col
61+
62+
def isGoal( self ):
63+
"""
64+
Checks to see if the puzzle is in its goal state.
65+
66+
-------------
67+
| | 1 | 2 |
68+
-------------
69+
| 3 | 4 | 5 |
70+
-------------
71+
| 6 | 7 | 8 |
72+
-------------
73+
74+
>>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]).isGoal()
75+
True
76+
77+
>>> EightPuzzleState([1, 0, 2, 3, 4, 5, 6, 7, 8]).isGoal()
78+
False
79+
"""
80+
current = 0
81+
for row in range( 3 ):
82+
for col in range( 3 ):
83+
if current != self.cells[row][col]:
84+
return False
85+
current += 1
86+
return True
87+
88+
def legalMoves( self ):
89+
"""
90+
Returns a list of legal moves from the current state.
91+
92+
Moves consist of moving the blank space up, down, left or right.
93+
These are encoded as 'up', 'down', 'left' and 'right' respectively.
94+
95+
>>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]).legalMoves()
96+
['down', 'right']
97+
"""
98+
moves = []
99+
row, col = self.blankLocation
100+
if(row != 0):
101+
moves.append('up')
102+
if(row != 2):
103+
moves.append('down')
104+
if(col != 0):
105+
moves.append('left')
106+
if(col != 2):
107+
moves.append('right')
108+
return moves
109+
110+
def result(self, move):
111+
"""
112+
Returns a new eightPuzzle with the current state and blankLocation
113+
updated based on the provided move.
114+
115+
The move should be a string drawn from a list returned by legalMoves.
116+
Illegal moves will raise an exception, which may be an array bounds
117+
exception.
118+
119+
NOTE: This function *does not* change the current object. Instead,
120+
it returns a new object.
121+
"""
122+
row, col = self.blankLocation
123+
if(move == 'up'):
124+
newrow = row - 1
125+
newcol = col
126+
elif(move == 'down'):
127+
newrow = row + 1
128+
newcol = col
129+
elif(move == 'left'):
130+
newrow = row
131+
newcol = col - 1
132+
elif(move == 'right'):
133+
newrow = row
134+
newcol = col + 1
135+
else:
136+
raise "Illegal Move"
137+
138+
# Create a copy of the current eightPuzzle
139+
newPuzzle = EightPuzzleState([0, 0, 0, 0, 0, 0, 0, 0, 0])
140+
newPuzzle.cells = [values[:] for values in self.cells]
141+
# And update it to reflect the move
142+
newPuzzle.cells[row][col] = self.cells[newrow][newcol]
143+
newPuzzle.cells[newrow][newcol] = self.cells[row][col]
144+
newPuzzle.blankLocation = newrow, newcol
145+
146+
return newPuzzle
147+
148+
# Utilities for comparison and display
149+
def __eq__(self, other):
150+
"""
151+
Overloads '==' such that two eightPuzzles with the same configuration
152+
are equal.
153+
154+
>>> EightPuzzleState([0, 1, 2, 3, 4, 5, 6, 7, 8]) == \
155+
EightPuzzleState([1, 0, 2, 3, 4, 5, 6, 7, 8]).result('left')
156+
True
157+
"""
158+
for row in range( 3 ):
159+
if self.cells[row] != other.cells[row]:
160+
return False
161+
return True
162+
163+
def __hash__(self):
164+
return hash(str(self.cells))
165+
166+
def __getAsciiString(self):
167+
"""
168+
Returns a display string for the maze
169+
"""
170+
lines = []
171+
horizontalLine = ('-' * (13))
172+
lines.append(horizontalLine)
173+
for row in self.cells:
174+
rowLine = '|'
175+
for col in row:
176+
if col == 0:
177+
col = ' '
178+
rowLine = rowLine + ' ' + col.__str__() + ' |'
179+
lines.append(rowLine)
180+
lines.append(horizontalLine)
181+
return '\n'.join(lines)
182+
183+
def __str__(self):
184+
return self.__getAsciiString()
185+
186+
# TODO: Implement The methods in this class
187+
188+
class EightPuzzleSearchProblem(search.SearchProblem):
189+
"""
190+
Implementation of a SearchProblem for the Eight Puzzle domain
191+
192+
Each state is represented by an instance of an eightPuzzle.
193+
"""
194+
def __init__(self,puzzle):
195+
"Creates a new EightPuzzleSearchProblem which stores search information."
196+
self.puzzle = puzzle
197+
198+
def getStartState(self):
199+
return puzzle
200+
201+
def isGoalState(self,state):
202+
return state.isGoal()
203+
204+
def getSuccessors(self,state):
205+
"""
206+
Returns list of (successor, action, stepCost) pairs where
207+
each succesor is either left, right, up, or down
208+
from the original state and the cost is 1.0 for each
209+
"""
210+
succ = []
211+
for a in state.legalMoves():
212+
succ.append((state.result(a), a, 1))
213+
return succ
214+
215+
def getCostOfActions(self, actions):
216+
"""
217+
actions: A list of actions to take
218+
219+
This method returns the total cost of a particular sequence of actions. The sequence must
220+
be composed of legal moves
221+
"""
222+
return len(actions)
223+
224+
EIGHT_PUZZLE_DATA = [[1, 0, 2, 3, 4, 5, 6, 7, 8],
225+
[1, 7, 8, 2, 3, 4, 5, 6, 0],
226+
[4, 3, 2, 7, 0, 5, 1, 6, 8],
227+
[5, 1, 3, 4, 0, 2, 6, 7, 8],
228+
[1, 2, 5, 7, 6, 8, 0, 4, 3],
229+
[0, 3, 1, 6, 8, 2, 7, 5, 4]]
230+
231+
def loadEightPuzzle(puzzleNumber):
232+
"""
233+
puzzleNumber: The number of the eight puzzle to load.
234+
235+
Returns an eight puzzle object generated from one of the
236+
provided puzzles in EIGHT_PUZZLE_DATA.
237+
238+
puzzleNumber can range from 0 to 5.
239+
240+
>>> print loadEightPuzzle(0)
241+
-------------
242+
| 1 | | 2 |
243+
-------------
244+
| 3 | 4 | 5 |
245+
-------------
246+
| 6 | 7 | 8 |
247+
-------------
248+
"""
249+
return EightPuzzleState(EIGHT_PUZZLE_DATA[puzzleNumber])
250+
251+
def createRandomEightPuzzle(moves=100):
252+
"""
253+
moves: number of random moves to apply
254+
255+
Creates a random eight puzzle by applying
256+
a series of 'moves' random moves to a solved
257+
puzzle.
258+
"""
259+
puzzle = EightPuzzleState([0,1,2,3,4,5,6,7,8])
260+
for i in range(moves):
261+
# Execute a random legal move
262+
puzzle = puzzle.result(random.sample(puzzle.legalMoves(), 1)[0])
263+
return puzzle
264+
265+
if __name__ == '__main__':
266+
puzzle = createRandomEightPuzzle(25)
267+
print('A random puzzle:')
268+
print(puzzle)
269+
270+
problem = EightPuzzleSearchProblem(puzzle)
271+
path = search.breadthFirstSearch(problem)
272+
print('BFS found a path of %d moves: %s' % (len(path), str(path)))
273+
curr = puzzle
274+
i = 1
275+
for a in path:
276+
curr = curr.result(a)
277+
print('After %d move%s: %s' % (i, ("", "s")[i>1], a))
278+
print(curr)
279+
280+
raw_input("Press return for the next state...") # wait for key stroke
281+
i += 1

0 commit comments

Comments
 (0)