-
Notifications
You must be signed in to change notification settings - Fork 0
/
play.py
213 lines (184 loc) · 8.17 KB
/
play.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
import pygame
import color as Color
import numpy as np
import space
LEGEND = ["U - Move top clockwise", "D - Move bottom clockwise", "L - Move left side clockwise",
"R - Move right side clockwise", "F - Move front side clockwise", "B - Move back side clockwise", "Hold SHIFT key to move anti-clockwise"]
def play(env, fps=60, callback=None, scramble=None):
""" Allows to play the game using keyboard
To play the game use:
import rubiks_cube as cube
env = cube.make()
env.play()
Arguments:
env: instance of rubicks_cube.py
Rubiks Cube Environment
fps: int
Frames Per Second, Maximum number of steps of the environment
executed every second. Default is 30
callback: lambda or None
Callback is a function which will be executed after every step.
Input:
prev_obs: numpy array of the cube's colors
previous observation, observation before performing action
obs: numpy array of the cube's colors
observation after performing action
action: Enum of Moves inside space.py (integers 0 to 11)
action that was executed
reward: float norm (numbers between 0 and 1)
reward that was received
env_done: bool (True - environment finished)
whether the environment is done or not
scramble: list of moves or None
This allows environment to be reset by custom moves.
"""
pygame.init()
env.reset(scramble)
cube_position = init_cube_position(env)
# Switch to fullscreen mode
# screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
screen = pygame.display.set_mode((1500, 1000))
screen_width = screen.get_width()
screen_height = screen.get_height()
pygame.display.set_caption("Rubics Cube")
pattern = False
clock = pygame.time.Clock()
# Fill background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill(Color.WHITE_RGB)
# Set Font
font = pygame.font.SysFont("comicsansms", 24)
number_solved = 0
env_done = True
while not pattern:
if env_done:
env_done = False
obs = env.reset(scramble)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pattern = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pattern = True
else:
action = key_pressed(env, event, pygame)
if action is not None:
prev_obs = np.copy(obs)
obs, reward, env_done = env.step(action)
number_solved = number_solved + \
1 if env_done else number_solved # If cube is solved add one
if callback is not None:
callback(prev_obs, obs, action, reward, env_done)
screen.blit(background, (0, 0)) # clear screen
draw_cube(env, pygame, screen, cube_position)
draw_text(screen, font, "Time solved: " + str(number_solved),
screen_width - 100, 50, "RIGHT")
draw_multiple_lines(screen, font, LEGEND,
screen_width - 100, 100, "RIGHT", 50)
pygame.display.flip()
clock.tick(fps)
pygame.quit()
def draw_cube(env, pygame, screen, cube_position):
""" Draw the cube on the screen """
for side in range(env.num_faces):
for row in range(env.num_pieces_per_row):
for col in range(env.num_pieces_per_col):
draw_piece(pygame, screen, env.cube_colors[side][row][col], [
cube_position[side][row][col][0], cube_position[side][row][col][1], env.piece_size, env.piece_size])
def draw_piece(pygame, screen, color, location):
""" Draw piece to the screen with color and location
Colors: 0 - White, 1 - Green, 2 - Red, 3 - Blue, 4 - Orange, 5 - Yellow
"""
if color == 0:
pygame.draw.rect(screen, Color.WHITE_RGB, location)
elif color == 1:
pygame.draw.rect(screen, Color.GREEN_RGB, location)
elif color == 2:
pygame.draw.rect(screen, Color.RED_RGB, location)
elif color == 3:
pygame.draw.rect(screen, Color.BLUE_RGB, location)
elif color == 4:
pygame.draw.rect(screen, Color.ORANGE_RGB, location)
else:
pygame.draw.rect(screen, Color.YELLOW_RGB, location)
pygame.draw.rect(screen, Color.BLACK_RGB, location, 2)
def draw_text(screen, font, text, location_x, location_y, justify):
""" Render text on the screen """
line = font.render(text, True, Color.BLUE_RGB)
if justify == "LEFT":
screen.blit(line, (location_x, location_y))
elif justify == "RIGHT":
screen.blit(line, (location_x - line.get_width(), location_y))
elif justify == "CENTER":
screen.blit(line, (location_x - (line.get_width() / 2.0), location_y))
else:
# Default justify to the LEFT
screen.blit(line, (location_x, location_y))
def draw_multiple_lines(screen, font, text_list, location_x, location_y, justify, line_spacing):
""" Render text with multiple lines on the screen """
space_between_lines = 0
for sentence in text_list:
draw_text(screen, font, sentence, location_x,
location_y + space_between_lines, justify)
space_between_lines += line_spacing
def key_pressed(env, event, pygame):
""" Check if the key pressed is valid """
if event.key == pygame.K_u and pygame.key.get_mods() & pygame.KMOD_SHIFT:
return space.Moves.Ui
if event.key == pygame.K_u:
return space.Moves.U
if event.key == pygame.K_d and pygame.key.get_mods() & pygame.KMOD_SHIFT:
return space.Moves.Di
if event.key == pygame.K_d:
return space.Moves.D
if event.key == pygame.K_l and pygame.key.get_mods() & pygame.KMOD_SHIFT:
return space.Moves.Li
if event.key == pygame.K_l:
return space.Moves.L
if event.key == pygame.K_r and pygame.key.get_mods() & pygame.KMOD_SHIFT:
return space.Moves.Ri
if event.key == pygame.K_r:
return space.Moves.R
if event.key == pygame.K_f and pygame.key.get_mods() & pygame.KMOD_SHIFT:
return space.Moves.Fi
if event.key == pygame.K_f:
return space.Moves.F
if event.key == pygame.K_b and pygame.key.get_mods() & pygame.KMOD_SHIFT:
return space.Moves.Bi
if event.key == pygame.K_b:
return space.Moves.B
return None
def init_cube_position(env):
""" Initialize positions of each piece of the cube """
cube_position = np.full((env.num_faces, env.num_pieces_per_row,
env.num_pieces_per_col, env.position_array_row_size), 0)
piece_size = env.piece_size
column_position = piece_size
row_position = piece_size
face_column_position = env.num_pieces_per_col * \
piece_size # First face has one face offset
face_row_position = 0
for side in range(env.num_faces):
for row in range(env.num_pieces_per_row):
for col in range(env.num_pieces_per_col):
cube_position[side][row][col][0] = column_position + \
face_column_position
cube_position[side][row][col][1] = row_position + \
face_row_position
column_position += piece_size
row_position += piece_size
column_position = piece_size
row_position = piece_size
column_position = piece_size
face_column_position += env.num_pieces_per_col * piece_size
if side == 0:
face_row_position += env.num_pieces_per_row * \
piece_size # face offset in horizontal direction
face_column_position = 0
if side == 4:
face_column_position = env.num_pieces_per_col * \
piece_size # face offset in horizontal direction
face_row_position += env.num_pieces_per_row * \
piece_size # face offset in vertical direction
return cube_position