-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPuzzle.py
executable file
·231 lines (203 loc) · 9.37 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
import pygame
import random
import Constants as Const
from Animation import *
# created by MaX-Lo on 15.03.2016
# contains puzzle with methods to create new puzzles, move tiles and so on...
class Puzzle:
def __init__(self):
self.field = None
self.empty_field = None
self.size = Const.SIZE
self.num_at_correct_pos = -1
self.create_puzzle()
while not self.is_solvable():
self.create_puzzle()
self.refresh_num_at_correct_pos()
def create_puzzle(self):
"""
generates the field filled with unique random numbers
"""
# init int array
field = [[0 for _ in range(self.size)] for i in range(self.size)]
empty_field = None
# set array elements with unique random numbers
for i in range(self.size):
for j in range(self.size):
searching_unused_number = True
number = 0
# create random numbers until an unused number is created
while searching_unused_number:
number = random.randint(1, self.size ** 2)
searching_unused_number = False
for k in range(self.size):
for l in range(self.size):
if int(field[k][l]) == number:
searching_unused_number = True
field[i][j] = number
# set array element with highest number 0 -> empty field
for i in range(self.size):
for j in range(self.size):
if field[i][j] == self.size ** 2:
field[i][j] = 0
empty_field = (i, j)
self.field = field
self.empty_field = empty_field
def refresh_num_at_correct_pos(self):
""" checks how many tiles are at their final (correct) position """
number = 1
correct = 0
for i in range(self.size):
for j in range(self.size):
if self.field[j][i] == number:
correct += 1
number += 1
# last field is empty
if number == self.size ** 2:
continue
if self.num_at_correct_pos != correct:
self.num_at_correct_pos = correct
return True
else:
return False
def click(self, mpos, board, blank_screen):
pos0 = (25, 100) # board position on screen
b_pos = (mpos[0] - pos0[0], mpos[1] - pos0[1]) # click position on board
if 0 < b_pos[0] < len(self.field[0]) * 100 and \
0 < b_pos[1] < len(self.field[1]) * 100:
clicked_field = (b_pos[0] / 100, b_pos[1] / 100)
print("click:", clicked_field, " empty: ", self.empty_field)
if clicked_field[0]-1 == self.empty_field[0] and \
clicked_field[1] == self.empty_field[1]:
self.move_tile_left(board, blank_screen)
elif clicked_field[0]+1 == self.empty_field[0] and \
clicked_field[1] == self.empty_field[1]:
self.move_tile_right(board, blank_screen)
elif clicked_field[0] == self.empty_field[0] and \
clicked_field[1]-1 == self.empty_field[1]:
self.move_tile_up(board, blank_screen)
elif clicked_field[0] == self.empty_field[0] and \
clicked_field[1]+1 == self.empty_field[1]:
self.move_tile_down(board, blank_screen)
def move_tile_up(self, board, blank_screen):
if self.empty_field[1] + 1 != self.size:
self.field[self.empty_field[0]][self.empty_field[1]], self.field[self.empty_field[0]][
self.empty_field[1] + 1] = \
self.field[self.empty_field[0]][self.empty_field[1] + 1], self.field[self.empty_field[0]][
self.empty_field[1]]
self.empty_field = (self.empty_field[0], self.empty_field[1] + 1) # getting new empty field
animation_up_linear(board, self) # simple shifting animation
return True
else:
animation_tremble(pygame.display.get_surface(), board, blank_screen)
return False
def move_tile_down(self, board, blank_screen):
if self.empty_field[1] - 1 >= 0:
self.field[self.empty_field[0]][self.empty_field[1]], self.field[self.empty_field[0]][
self.empty_field[1] - 1] = \
self.field[self.empty_field[0]][self.empty_field[1] - 1], self.field[self.empty_field[0]][
self.empty_field[1]]
self.empty_field = (self.empty_field[0], self.empty_field[1] - 1) # getting new empty field
animation_down_linear(board, self) # simple shifting animation
return True
else:
animation_tremble(pygame.display.get_surface(), board, blank_screen)
return False
def move_tile_right(self, board, blank_screen):
if self.empty_field[0] - 1 >= 0:
self.field[self.empty_field[0]][self.empty_field[1]], self.field[self.empty_field[0] - 1][
self.empty_field[1]] = \
self.field[self.empty_field[0] - 1][self.empty_field[1]], self.field[self.empty_field[0]][
self.empty_field[1]]
self.empty_field = (self.empty_field[0] - 1, self.empty_field[1]) # getting new empty field
animation_right_linear(board, self) # simple shifting animation
return True
else:
animation_tremble(pygame.display.get_surface(), board, blank_screen)
return False
def move_tile_left(self, board, blank_screen):
if self.empty_field[0] + 1 != self.size:
self.field[self.empty_field[0]][self.empty_field[1]], self.field[self.empty_field[0] + 1][
self.empty_field[1]] = \
self.field[self.empty_field[0] + 1][self.empty_field[1]], self.field[self.empty_field[0]][
self.empty_field[1]]
self.empty_field = (self.empty_field[0] + 1, self.empty_field[1]) # getting new empty field
animation_left_linear(board, self) # simple shifting animation
return True
else:
animation_tremble(pygame.display.get_surface(), board, blank_screen)
return False
def draw(self, board):
board.fill(Const.LT)
for i in range(self.size):
for j in range(self.size):
if self.field[i][j] == 0:
pygame.draw.rect(board, Const.LT, (i * 100 + 1, j * 100 + 1, 98, 98))
else:
pygame.draw.rect(board, Const.DK, (i * 100 + 1, j * 100 + 1, 98, 98))
font = pygame.font.Font(None, 50)
mytext = font.render(str(self.field[i][j]), 1, Const.WHITE)
textpos = mytext.get_rect(centerx=i * 100 + 50, centery=j * 100 + 50)
board.blit(mytext, textpos)
def draw_colorful(self, board):
for i in range(self.size):
for j in range(self.size):
if self.field[i][j] == 0:
pygame.draw.rect(board, Const.LT, (i * 100 + 1, j * 100 + 1, 98, 98))
else:
red = random.randint(0, 90)
green = random.randint(0, 90)
blue = random.randint(0, 90)
color = (red, green, blue)
pygame.draw.rect(board, color, (i * 100 + 1, j * 100 + 1, 98, 98))
font = pygame.font.Font(None, 50)
mytext = font.render(str(self.field[i][j]), 1, Const.WHITE)
textpos = mytext.get_rect(centerx=i * 100 + 50, centery=j * 100 + 50)
board.blit(mytext, textpos)
def is_solvable(self):
"""
Not every puzzle is solvable therefore this method test the current puzzle.
:return: whether puzzle is solvable
"""
l = []
# writing all numbers row by row into one list (not necessary, but makes the following step easier)
empty_field_row = 0
for i in range(self.size):
for j in range(self.size):
if self.field[j][i] != 0:
l.append(self.field[j][i])
else:
empty_field_row = i + 1
# getting the number of unsorted pairs and row of the empty field
unsorted_pairs = 0
for pos in range(len(l)):
for i in range(0, pos):
if l[pos] < l[i]:
unsorted_pairs += 1
# even size: unsorted_pairs + empty_field_row = even -> solvable
# odd size: unsorted_pairs = even -> solvable
if self.size % 2 == 0: # even
if (unsorted_pairs + empty_field_row) % 2 == 0:
return True
else:
return False
else: # odd
if unsorted_pairs % 2 == 0:
return True
else:
return False
def is_solved(self):
"""
checks wether the puzzle is solved
:return: true if puzzle is solved
"""
number = 1
for i in range(self.size):
for j in range(self.size):
if self.field[j][i] != number:
return False
number += 1
# last field should be empty
if number == self.size ** 2:
return True
return True