-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGameState.py
More file actions
153 lines (123 loc) · 5.54 KB
/
Copy pathGameState.py
File metadata and controls
153 lines (123 loc) · 5.54 KB
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
import random
from copy import deepcopy
"""
Class representing the current state of the current game session.
Note that a game may consists of multiple game sessions, i.e. of multiple puzzles.
"""
class GameState:
no_rows = 5
no_cols = 5
"""
Constructor
lights_on: a list of list of integers, each with the value 0 or 1, representing the initial state of the light
bulbs (0=Off, 1=On).
"""
def __init__(self, lights_on):
self.__initial_lights_on = deepcopy(lights_on)
self.__lights_on = lights_on
self.__shortest_solution = None
self.__no_taken_steps = 0
self.__initial_shortest_solution = None
self.__initial_shortest_solution_no_steps = None
"""
Returns true if and only if the game is won.
"""
def is_winning(self):
return all(not any(map(bool, lights_row)) for lights_row in self.__lights_on)
"""
Switches the light bulb in position (x, y).
As a consequence, the state of the light bulbs adjacent to the switched bulb is toggled too.
If this step is not part of the currently known shortest path to a winning state, then the shortest_solution is
deleted. Else, it is updated, with the current step being removed from it.
Note that the positions are indexed from 0.
X: the row of the light bulb to switch.
y; the column of the light bulb to switch.
"""
def switch_light(self, x, y):
assert 0 <= x < GameState.no_rows and 0 <= y < GameState.no_cols
self.__no_taken_steps = self.__no_taken_steps + 1
self.__toggle_light(x, y)
if x > 0:
self.__toggle_light(x - 1, y)
if x + 1 < GameState.no_rows:
self.__toggle_light(x + 1, y)
if y > 0:
self.__toggle_light(x, y - 1)
if y + 1 < GameState.no_cols:
self.__toggle_light(x, y + 1)
if self.__shortest_solution is not None:
if self.__shortest_solution[x][y] == 1:
self.__shortest_solution[x][y] = 0.
else:
self.__shortest_solution = None
"""
Informs the GameState about the solutions for the problem from the current state.
Based on the solutions (it can be mathematically proved, that there are 4 of them), the shortest one is selected
and cached.
solutions: a list of solution matrices. Each solution matrix is a list of lists of integers, with a 1 on position
(x, y) if the light bulb on position (x, y) needs to be toggled, and 0 otherwise.
"""
def set_solutions(self, solutions):
shortest_solution_length = GameState.no_cols * GameState.no_rows + 1
for solution in solutions:
assert len(solution) == GameState.no_rows
assert len(solution[0]) == GameState.no_cols
solution_length = sum([toggle for sublist in solution for toggle in sublist])
if solution_length < shortest_solution_length:
shortest_solution_length = solution_length
self.__shortest_solution = solution
if self.__lights_on == self.__initial_lights_on:
self.__initial_shortest_solution = deepcopy(solution)
self.__initial_shortest_solution_no_steps = solution_length
"""
If previously the solutions for the game were specified, then returns a step (x, y) meaning that by switching
the light bulb on position (x, y) the winning state will be 1 step closer. If no solutions are known,
returns None.
"""
def get_hint_for_next_step(self):
if self.__shortest_solution is not None:
solution_vector = [toggle for sublist in self.__shortest_solution for toggle in sublist]
required_steps = [i for i, toggle in enumerate(solution_vector) if toggle == 1]
hinted_step_vector_index = random.choice(required_steps)
hinted_step_row = hinted_step_vector_index / GameState.no_cols
hinted_step_col = hinted_step_vector_index % GameState.no_cols
hinted_step_matrix_index = (hinted_step_row, hinted_step_col)
return hinted_step_matrix_index
else:
return None
"""
Returns true if and only if the shortest solution to the winning state from the current state is known.
Otherwise false.
"""
def has_shortest_solution(self):
return self.__shortest_solution is not None
"""
Resets the game to its initial state.
"""
def reset_to_initial_state(self):
self.__lights_on = deepcopy(self.__initial_lights_on)
self.__shortest_solution = deepcopy(self.__initial_shortest_solution)
self.__no_taken_steps = 0
"""
Returns the total number of steps since the game was started/reset.
"""
def get_total_no_steps(self):
return self.__no_taken_steps
"""
Returns the state of the light bulbs, organized in a list of lists.
"""
def get_lights_state(self):
return deepcopy(self.__lights_on)
"""
Returns true if and only if the shortest solution to the winning state from the initial state is known.
Otherwise false.
"""
def has_known_shortest_solution_from_initial_state(self):
return self.__initial_shortest_solution is not None
"""
Returns the number of steps in the shortest solution from the initial state to the winning state.
"""
def length_of_shortest_solution_from_initial_state(self):
return self.__initial_shortest_solution_no_steps
def __toggle_light(self, x, y):
self.__lights_on[x][y] = 1 - self.__lights_on[x][y]