-
Notifications
You must be signed in to change notification settings - Fork 0
/
8_puzzle.py
297 lines (244 loc) · 10.7 KB
/
8_puzzle.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
import numpy as np
import sys
# Creating a main class
class PuzzleSolution():
# Creating a puzzle solution class
# Defining and declaring values in the function.
# Specifying the target state and also the iterations along with the name of files to store data
# Basically creating a map with node coordinates to define positions within a matrix.
# Elements are meant to be read column wise.
# init method for the class
def __init__(self, maps):
self.nodesFile = open("Nodes.txt", "w")
self.nodePathFile = open("nodePath.txt", "w")
self.nodesInfoFile = open("NodesInfo.txt", "w")
self.maps = maps
self.goalmaps = np.array([[1,2,3],[4,5,6],[7,8,0]])
self.leftMove = 1
self.upMove = 2
self.rightMove = 3
self.downMove = 4
# Results stored in nodes.txt
def WriteToFile(self, array):
data = ""
for col in range(0, array.shape[1]):
for row in range(0, array.shape[0]):
data = data + str(array[row][col]) + " "
data = data + "\n"
self.nodesFile.write(data)
# Nodes path in nodePath.txt
def WriteToPathFile(self, array):
data = ""
for col in range(0, array.shape[1]):
for row in range(0, array.shape[0]):
data = data + str(array[row][col]) + " "
data = data + "\n"
self.nodePathFile.write(data)
# Map to string conversion
def mapsToString(self, array):
data = str("")
for col in range(0, array.shape[1]):
for row in range(0, array.shape[0]):
data = data + str(array[row][col]) + " "
return data
# blank Tile is denoted by 0
def TileLocation(self):
TileRow, TileCol = np.where(self.maps == 0)
if (len(TileRow) > 0 and len(TileCol) > 0):
return (TileRow[0], TileCol[0])
else:
return (-1, -1)
# Condition for a step to take place or not
def ValidMove(self, TileRow, TileCol, numRows, numCols):
if ((TileRow >= 0 and TileRow < numRows) and (TileCol >= 0 and TileCol < numCols)):
return True
else:
return False
# Function for moving Tile to the left
def ActionMoveLeft(self, TileRow, TileCol):
newmaps = self.maps.copy()
# moving left
var = newmaps[TileRow][TileCol]
newmaps[TileRow][TileCol] = newmaps[TileRow][TileCol - 1]
newmaps[TileRow][TileCol - 1] = var
return newmaps
# Function for moving Tile to the right
def ActionMoveRight(self, TileRow, TileCol):
newmaps = self.maps.copy()
# moving right
var = newmaps[TileRow][TileCol]
newmaps[TileRow][TileCol] = newmaps[TileRow][TileCol + 1]
newmaps[TileRow][TileCol + 1] = var
return newmaps
# Function for moving Tile upwards
def ActionMoveUp(self, TileRow, TileCol):
newmaps = self.maps.copy()
# moving up
var = newmaps[TileRow][TileCol]
newmaps[TileRow][TileCol] = newmaps[TileRow - 1][TileCol]
newmaps[TileRow - 1][TileCol] = var
return newmaps
# Function for moving Tile downwards
def ActionMoveDown(self, TileRow, TileCol):
newmaps = self.maps.copy()
# moving down
var = newmaps[TileRow][TileCol]
newmaps[TileRow][TileCol] = newmaps[TileRow + 1][TileCol]
newmaps[TileRow + 1][TileCol] = var
return newmaps
# Checking target state function
def CheckGoalState(self):
return ((self.maps == self.goalmaps).all() and (self.maps.shape == self.goalmaps.shape))
# comparing maps
def Comparemaps(self, maps1, maps2):
return ((maps1 == maps2).all() and (maps1.shape == maps2.shape))
# update maps with values
def Updatemaps(self, newmaps):
self.maps = newmaps
# Backtracking function to find the path to reach from target to initial
def GeneratePath(self, Node_State_i, numberOfMoves, lastMove):
finalmaps = []
self.Updatemaps(self.goalmaps)
finalmaps.append((self.goalmaps, -1))
if (lastMove == self.leftMove):
newmaps = self.ActionMoveRight(2, 2)
elif (lastMove == self.upMove):
newmaps = self.ActionMoveDown(2, 2)
elif (lastMove == self.rightMove):
newmaps = self.ActionMoveLeft(2, 2)
else:
newmaps = self.ActionMoveUp(2, 2)
numberOfMoves = numberOfMoves - 1
while (len(Node_State_i[numberOfMoves]) > 0):
for index in range(0, len(Node_State_i[numberOfMoves])):
if (self.Comparemaps(newmaps, Node_State_i[numberOfMoves][index][0])):
finalmaps.append((newmaps, lastMove))
self.Updatemaps(newmaps)
(currTileRow, currTileCol) = self.TileLocation()
lastMove = Node_State_i[numberOfMoves][index][1]
# updating newly created maps
if (lastMove == self.leftMove):
newmaps = self.ActionMoveRight(currTileRow, currTileCol)
elif (lastMove == self.upMove):
newmaps = self.ActionMoveDown(currTileRow, currTileCol)
elif (lastMove == self.rightMove):
newmaps = self.ActionMoveLeft(currTileRow, currTileCol)
else:
newmaps = self.ActionMoveUp(currTileRow, currTileCol)
numberOfMoves = numberOfMoves - 1
break
# Reversing the map paths
finalmaps = finalmaps[::-1]
return finalmaps
# BFS logic using the flow chart
def solve(self):
queue = []
queue.append((self.maps, -1, 0, 0))
visitedmaps = {}
Node_State_i = {}
moves = []
prevLevel = -1
lastMove = -1
flag = -1
startIndex = 1
while (len(queue) > 0):
(currmaps, prevMove, Node_Index_i, Parent_Node_Index_i) = queue[0]
self.Updatemaps(currmaps)
stringmaps = self.mapsToString(currmaps)
queue.pop(0)
# writing into nodesInfo.txt and nodes.txt
data = "" + str(startIndex) + " " + str(Parent_Node_Index_i) + "\n"
self.nodesInfoFile.write(data)
self.WriteToFile(currmaps)
# Defining a Maximum amount of steps
if (Node_Index_i > 1000):
lastMove = -1
break
# update Node_State_i
if (prevLevel != Node_Index_i):
Node_State_i[prevLevel] = moves
moves = []
moves.append((currmaps, prevMove))
prevLevel = Node_Index_i
else:
moves.append((currmaps, prevMove))
# Verify if goal state reached or not
if (self.CheckGoalState()):
flag = Node_Index_i
lastMove = prevMove
break
# new nodes visited
if (visitedmaps.get(str(stringmaps)) is None):
# updating new values as visited
visitedmaps[str(stringmaps)] = 1
# Defining the tile location
(currTileRow, currTileCol) = self.TileLocation()
# moving left
if (prevMove != self.rightMove and self.ValidMove(currTileRow, currTileCol - 1, 3, 3)):
leftMovemaps = self.ActionMoveLeft(currTileRow, currTileCol)
# check maps visited or not
if (visitedmaps.get(str(leftMovemaps)) is None):
visitedmaps[str(leftMovemaps)] = 1
queue.append((leftMovemaps, self.leftMove, Node_Index_i + 1, startIndex))
# moving up
if (prevMove != self.downMove and self.ValidMove(currTileRow - 1, currTileCol, 3, 3)):
upMovemaps = self.ActionMoveUp(currTileRow, currTileCol)
# check maps visited or not
if (visitedmaps.get(str(upMovemaps)) is None):
visitedmaps[str(upMovemaps)] = 1
queue.append((upMovemaps, self.upMove, Node_Index_i + 1, startIndex))
# moving right
if (prevMove != self.leftMove and self.ValidMove(currTileRow, currTileCol + 1, 3, 3)):
rightMovemaps = self.ActionMoveRight(currTileRow, currTileCol)
# check maps visited or not
if (visitedmaps.get(str(rightMovemaps)) is None):
visitedmaps[str(rightMovemaps)] = 1
queue.append((rightMovemaps, self.rightMove, Node_Index_i + 1, startIndex))
# moving down
if (prevMove != self.upMove and self.ValidMove(currTileRow + 1, currTileCol, 3, 3)):
downMovemaps = self.ActionMoveDown(currTileRow, currTileCol)
# check maps visited or not
if (visitedmaps.get(str(downMovemaps)) is None):
visitedmaps[str(downMovemaps)] = 1
queue.append((downMovemaps, self.downMove, Node_Index_i + 1, startIndex))
startIndex = startIndex + 1
# backtracking to find the solution
numberOfMoves = flag
lastMoveToReachGoal = lastMove
if (flag == -1):
self.nodesFile.close()
self.nodePathFile.close()
self.nodesInfoFile.close()
return ([], -1)
else:
finalmaps = self.GeneratePath(Node_State_i, numberOfMoves, lastMove)
# write to nodePath file
for array in finalmaps:
self.WriteToPathFile(array[0])
self.nodesFile.close()
self.nodePathFile.close()
self.nodesInfoFile.close()
return (finalmaps, numberOfMoves)
# taking Input
# where I define values to the input matrix you enter, reading the string entered to integer values
if (len(sys.argv) > 1 and len(sys.argv[1]) > 8):
Matrix_pos = str(sys.argv[1])
Matrix_stdx = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
Matrix_stdx[0][0] = int(Matrix_pos[0])
Matrix_stdx[1][0] = int(Matrix_pos[1])
Matrix_stdx[2][0] = int(Matrix_pos[2])
Matrix_stdx[0][1] = int(Matrix_pos[3])
Matrix_stdx[1][1] = int(Matrix_pos[4])
Matrix_stdx[2][1] = int(Matrix_pos[5])
Matrix_stdx[0][2] = int(Matrix_pos[6])
Matrix_stdx[1][2] = int(Matrix_pos[7])
Matrix_stdx[2][2] = int(Matrix_pos[8])
# solving the problem
puzzle = PuzzleSolution(Matrix_stdx)
(array, moves) = puzzle.solve()
if (moves > -1):
print("Puzzle solved with moves: " + str(moves))
else:
print("Puzzle not solved!")
else:
print("Enter test case")