-
Notifications
You must be signed in to change notification settings - Fork 0
/
puzzle.py
264 lines (172 loc) · 6.53 KB
/
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
import random
import copy
import heapq
import time
random.seed(314)
state_x,state_y = 3,3
goal_state = [
[None,1, 2],
[3, 4, 5],
[6, 7, 8]
]
class Node:
parent = None
state = None
empty_location_x = None
empty_location_y = None
g_hat = None
h_hat = None
def __init__(self,state,parent=None):
self.parent = parent
self.state = state
self.h_hat = self.get_h_hat()
if parent:
self.g_hat = parent.g_hat+1
else:
self.g_hat = 0
self.f_hat = self.h_hat + self.g_hat
for i_y,i in enumerate(state):
for j_x,j in enumerate(i):
if j==None:
self.empty_location_x = j_x
self.empty_location_y = i_y
def __lt__(self,other):
return True
def __gt__(self,other):
return True
def show(self):
for i in range(state_y):
for j in range(state_x):
print(self.state[i][j], end=' ')
print()
print()
def show_score(self):
print("--------------")
print("g_hat:" + str(self.g_hat))
print("h_hat:" + str(self.h_hat))
print("f_hat:" + str(self.f_hat))
def get_h_hat(self):
manhattan_distance = 0
for y,row in enumerate(self.state):
for x, item in enumerate(row):
for g_y,g_row in enumerate(goal_state):
for g_x, g_item in enumerate(g_row):
if not(item is None )and item==g_item :
manhattan_distance = abs(x-g_x) + abs(y-g_y) + manhattan_distance
return manhattan_distance
def generate_children(self):
children = []
directions_x_y = [
[-1,0],
[ 1,0],
[ 0,1],
[ 0,-1],
]
for i in directions_x_y:
destination_x,destination_y = self.empty_location_x+i[0],self.empty_location_y+i[1]
if not(0<=destination_x<=2 and 0<=destination_y<=2):
continue
child_state = copy.deepcopy(self.state)
child_state[self.empty_location_y][self.empty_location_x] = self.state[destination_y][destination_x]
child_state[destination_y][destination_x] = None
child = Node(child_state,self)
children.append(child)
return children
def is_equal(self,target):
return self.state == target.state
def where(self,node_list):
for ind,i in enumerate(node_list):
if self.is_equal(i):
return ind
return -1
def is_in(self,node_list):
for i in node_list:
if self.is_equal(i):
return True
return False
def node_index(self,node_list):
for ind,i in enumerate(node_list):
if self.is_equal(i):
return ind
raise ValueError("指定の要素が配列に存在するような実装になっていることを確認してください。")
def where_heap(self,node_list):
for ind,i in enumerate(node_list):
if self.is_equal(i[1]):
return ind
return -1
def is_in_heap(self,node_list):
for i in node_list:
if self.is_equal(i[1]):
return True
return False
def node_index_heap(self,node_list):
for ind,i in enumerate(node_list):
if self.is_equal(i[1]):
return ind
raise ValueError("指定の要素が配列に存在するような実装になっていることを確認してください。")
def a_star(puzzle):
goal_node = Node(goal_state)
parent = Node(puzzle)
print("------問題------")
parent.show()
open_list = []
closed_list = []
heapq.heappush(open_list, (parent.f_hat,parent))
while open_list:
_,head = heapq.heappop(open_list)
if head.is_equal(goal_node):
answer = head
break
children = head.generate_children()
for i in children:
if not(i.is_in_heap(open_list)) and not(i.is_in(closed_list)):
heapq.heappush(open_list, (i.f_hat,i))
elif i.is_in_heap(open_list):
ind = i.node_index_heap(open_list)
if open_list[ind][1].f_hat > i.f_hat:
open_list.pop(ind)
heapq.heapify(open_list)
heapq.heappush(open_list, (i.f_hat,i))
elif i.is_in(closed_list):
ind = i.node_index(closed_list)
if closed_list[ind].f_hat > i.f_hat:
# closed_list.pop(ind)
# heapq.heappush(open_list, (i.f_hat,i))
raise ValueError("ヒューリスティック関数に矛盾性があるかもしれません")
closed_list.append(head)
print("------探索完了-------")
print("経路コスト:" + str(answer.g_hat))
print()
return answer
def main():
answer = None
# 100個の初期状態を生成
puzzles = [generate_random_puzzle(move_count) for move_count in range(1, 101)]
for ind,puzzle in enumerate(puzzles):
print("-----何問目:" + str(ind) + "-----")
a_star(puzzle)
def generate_random_puzzle(move_count):
puzzle = copy.deepcopy(goal_state)
# 空タイルを指定した回数ランダムに移動させる
moves = [(0, 1), (0, -1), (1, 0), (-1, 0)] # 右、左、下、上に移動するための座標の変化量
for _ in range(move_count):
possible_moves = []
empty_row, empty_col = None, None
# 空タイルの位置を検索
for i in range(3):
for j in range(3):
if puzzle[i][j] is None:
empty_row, empty_col = i, j
break
# 空タイルの周囲に移動可能なタイルの位置を取得
for dx, dy in moves:
new_row, new_col = empty_row + dx, empty_col + dy
if 0 <= new_row < 3 and 0 <= new_col < 3:
possible_moves.append((new_row, new_col))
# ランダムに移動先を選択して空タイルを移動させる
new_row, new_col = random.choice(possible_moves)
puzzle[empty_row][empty_col] = puzzle[new_row][new_col]
puzzle[new_row][new_col] = None
return puzzle
if __name__=="__main__":
main()