-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstate.py
117 lines (92 loc) · 4.03 KB
/
state.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
"""
In this file, the class for graph nodes are defined.
It is called state since I thought it suits better for our use case.
"""
from typing import List, Tuple
import numpy as np
UP = 1
DOWN = -1
LEFT = 10
RIGHT = -10
class State:
this_state: np.array
this_dist: int
blank_coord: Tuple[int, int]
is_solvable: bool
def __init__(self,
_2d_curr: List[Tuple[int, int, int]],
_2d_goal: List[Tuple[int, int, int]]):
arr: List[List[List[int, int, int]]] = [[], [], []]
for i in range(3):
for j in range(3):
curr: int = _2d_curr[i][j]
curr_moves: List[int, int, int] = [curr, 0, 0]
arr[i].append(curr_moves)
if curr == 0:
self.blank_coord = j, i
else:
self.find_distance(curr, _2d_goal, i, j, curr_moves)
self.this_state = np.array(arr)
self.set_distance()
self.solution_check(_2d_goal)
def find_distance(self, curr, _2d_goal, i, j, curr_moves):
for k in range(3):
for l in range(3):
if _2d_goal[k][l] == curr:
curr_moves[1] += ((i - k) * UP)
curr_moves[2] += ((j - l) * LEFT)
return
def set_distance(self):
self.this_dist = int(np.absolute(self.this_state.take(1, axis=2).flatten()).sum() +
np.absolute(self.this_state.take(2, axis=2).flatten()).sum() / 10)
def get_distance(self) -> int:
return self.this_dist
def gen_next_state(self, th: int = 0) -> bool:
"""
:param th: In order of shortest distance to longest which should be generated. 0 is the shortest...
"""
next_states: List[Tuple[int, int]] = []
for dirr, ch_x, ch_y in [(1, 0, 1), (-1, 0, -1), (10, 1, 0), (-10, -1, 0)]:
val = self.this_state[self.blank_coord[1] - ch_y][self.blank_coord[0] - ch_x] \
if 2 >= self.blank_coord[1] - ch_y >= 0 and 2 >= self.blank_coord[0] - ch_x >= 0 else None
dist: int = int(abs(val[1]) + abs(val[2]) / 10 - abs(val[1] + ch_y) - abs(val[2] / 10 + ch_x)) \
if val is not None else -100
next_states.append((dist, dirr))
next_states = sorted(next_states, reverse=True, key=lambda x: x[0])
if next_states[th][0] == -100:
return False
self.swap(next_states[th][1])
self.this_dist -= next_states[th][0]
return True
def swap(self, to: int) -> None:
"""
:param to: LEFT, RIGHT, UP OR DOWN... Hint: LEFT is for Blank to shift to left.
"""
b_x = self.blank_coord[0]
b_y = self.blank_coord[1]
to_cell = 1
move_ver = to
move_hor = 0
if to == LEFT or to == RIGHT:
move_hor = int(to / 10)
move_ver = 0
to_cell = 2
self.this_state[b_y - move_ver][b_x - move_hor][to_cell] += to
tmp = np.array(self.this_state[b_y, b_x], copy=True)
self.this_state[b_y, b_x] = self.this_state[b_y - move_ver, b_x - move_hor]
self.this_state[b_y - move_ver, b_x - move_hor] = tmp
self.blank_coord = (b_x - move_hor, b_y - move_ver)
def solution_check(self, solution: List[Tuple[int, int, int]]) -> None:
inversions: int = 0
flat_array: np.array = self.this_state.take(0, axis=2).flatten()
flat_solution = np.array(solution).flatten()
for i in range(8):
for j in range(i, 9):
if flat_array[i] and flat_array[j] and \
np.where(flat_solution == flat_array[i])[0][0] > np.where(flat_solution == flat_array[j])[0][0]:
inversions += 1
self.is_solvable = inversions % 2 == 0
def __lt__(self, other):
if not isinstance(other, State):
return NotImplemented
return self.this_dist < other.this_dist