-
Notifications
You must be signed in to change notification settings - Fork 0
/
state.py
161 lines (128 loc) · 5.71 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
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
from map import Map
class State:
def __init__(self, santa: tuple, gifts=[]):
self.santa = santa
self.gifts = gifts
def __eq__(self, other):
return self.gifts == other.gifts and self.santa == other.santa
def __str__(self):
return '"santa at: ' + str(self.santa) + ' gifts at: ' + str(self.gifts) + '"'
def __repr__(self):
return self.__str__()
def __hash__(self):
h = hash(self.santa)
for i in self.gifts:
h += hash(i)
return h
@staticmethod
def successor(state: 'State', map_object: Map) -> list[tuple['State', tuple, int]]:
map_array = map_object.map
points = map_object.points
w, h = map_object.w, map_object.h
next_states = []
santa_y, santa_x = state.santa[0], state.santa[1]
def try_move_santa(y: int, x: int):
""" Tries to move santa and push gifts and saves new state in next_states array. """
# Checking diagonal movement
if x * y != 0:
raise Exception('Diagonal moving is not allowed.')
# Checking bounds
if map_object.check_out_of_bounds(santa_y + y, santa_x + x):
return
# Checking blocks
if map_object.is_block(santa_y + y, santa_x + x):
return
# Checking if there is a gift around
if (santa_y + y, santa_x + x) not in state.gifts: # There is no gifts around
next_states.append((
State((santa_y + y, santa_x + x), state.gifts.copy()),
(y, x),
max(int(map_array[santa_y + y][santa_x + x]), int(map_array[santa_y][santa_x]))
))
else: # There is a gift around
# gift not on bound condition
if (y == -1 and santa_y != 1) or (y == 1 and santa_y != h - 2) or \
(x == -1 and santa_x != 1) or (x == 1 and santa_x != w - 2):
# If gift is on a point
if (santa_y + y, santa_x + x) in points:
return
# if there is block or another gift behind the gift
r2y, r2x = santa_y + 2 * y, santa_x + 2 * x
if map_object.is_block(r2y, r2x) or ((r2y, r2x) in state.gifts):
return
# Moving gift
new_gifts = state.gifts.copy()
new_gifts.remove((santa_y + y, santa_x + x))
new_gifts.append((r2y, r2x))
next_states.append((
State((santa_y + y, santa_x + x), new_gifts),
(y, x),
max(int(map_array[santa_y + y][santa_x + x]), int(map_array[santa_y][santa_x]))
))
try_move_santa(1, 0)
try_move_santa(0, 1)
try_move_santa(-1, 0)
try_move_santa(0, -1)
return next_states
@staticmethod
def predecessor(state: 'State', map_object: Map) -> list[tuple['State', tuple, int]]:
next_states = []
santa_y, santa_x = state.santa[0], state.santa[1]
def try_move_santa(y: int, x: int):
""" Tries to move the santa in possible direction. """
# Checking diagonal movement
if x * y != 0:
raise Exception('Diagonal moving is not allowed.')
# Checking bounds
if map_object.check_out_of_bounds(santa_y + y, santa_x + x):
return
# Checking blocks
if map_object.is_block(santa_y + y, santa_x + x):
return
# If there is a gift forward
if (santa_y + y, santa_x + x) in state.gifts:
return
# Just moving and not pulling gift
next_states.append((
State((santa_y + y, santa_x + x), state.gifts.copy()),
(y, x),
max(int(map_object.map[santa_y + y][santa_x + x]), int(map_object.map[santa_y][santa_x]))
))
# If there is a gift backward
if (santa_y - y, santa_x - x) in state.gifts:
# Pulling gift
new_gifts = state.gifts.copy()
new_gifts.remove((santa_y - y, santa_x - x))
new_gifts.append((santa_y, santa_x))
next_states.append((
State((santa_y + y, santa_x + x), new_gifts),
(y, x),
max(int(map_object.map[santa_y + y][santa_x + x]), int(map_object.map[santa_y][santa_x]))
))
try_move_santa(1, 0)
try_move_santa(0, 1)
try_move_santa(-1, 0)
try_move_santa(0, -1)
return next_states
def apply_action(self, action: tuple[int, int]):
"""
Apply the action to the current state to get the next state.
:param action: The action to be applied (a tuple representing the movement direction).
:return: The next state after applying the action.
"""
new_santa_y = self.santa[0] + action[0]
new_santa_x = self.santa[1] + action[1]
if (new_santa_y, new_santa_x) in self.gifts:
# If there's a gift, move it as well
gift_idx = self.gifts.index((new_santa_y, new_santa_x))
new_gifts = self.gifts[:]
new_gifts[gift_idx] = (new_santa_y + action[0], new_santa_x + action[1])
return State((new_santa_y, new_santa_x), new_gifts)
else:
return State((new_santa_y, new_santa_x), self.gifts[:])
@staticmethod
def is_goal(state: 'State', points: list[tuple]):
for gift in state.gifts:
if gift not in points:
return False
return True