-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlevel.py
210 lines (170 loc) · 6.69 KB
/
level.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
import pygame
import math
import action_bar as ab
# Constants to define the scale of our tiles and screen
DIVIDER_WIDTH = 1
CELL_WIDTH = 20
CELL_HEIGHT = 20
CELL_COUNT_X = 25
CELL_COUNT_Y = 25
# Hard-coded start and destination coordinates
START_NODE = (3, 4)
DESTINATION_NODE = (20, 20)
# Color scheme constants
# noinspection PyArgumentList
CELL_COLOR_EMPTY = pygame.Color(255, 255, 255)
# noinspection PyArgumentList
CELL_COLOR_WALL = pygame.Color(60, 60, 60)
# noinspection PyArgumentList
CELL_COLOR_EXPLORED = pygame.Color(31, 107, 200)
# noinspection PyArgumentList
CELL_COLOR_START = pygame.Color(107, 170, 37)
# noinspection PyArgumentList
CELL_COLOR_DESTINATION = pygame.Color(209, 38, 38)
# noinspection PyArgumentList
CELL_COLOR_CLOUD = pygame.Color(173, 216, 230)
class Level(object):
def __init__(self):
self.cells = []
for x in range(0, CELL_COUNT_X):
column = []
for y in range(0, CELL_COUNT_Y):
# Calculate the coordinates of the top left corner of the cell
window_x = x * (CELL_WIDTH + DIVIDER_WIDTH)
window_y = y * (CELL_HEIGHT + DIVIDER_WIDTH) + ab.ACTION_BAR_HEIGHT
# Create a cell with a set size and location
rect = pygame.Rect(window_x, window_y, CELL_WIDTH, CELL_HEIGHT)
c = Cell(rect, x, y, False)
# If (x,y) matches START_NODE, set cell as start node
if (x, y) == START_NODE:
c.set_start(True)
self.start = c
# If (x,y) matches DESTINATION_NODE, set cell as destination node
if (x, y) == DESTINATION_NODE:
c.set_destination(True)
self.destination = c
column.append(c)
self.cells.append(column)
self.set_neighbors()
def set_wall(self, wall: "Cell"):
if wall is not None and not wall.is_start and not wall.is_destination and not wall.is_wall:
wall.set_wall(True)
for column in self.cells: # remove wall from adjacency matrix of all cells
for cell in column:
if wall in cell.neighbors:
cell.neighbors.pop(wall)
def set_neighbors(self):
# Set neighbor cells of all cells
for column in self.cells:
for cell in column:
self.define_neighbors(cell)
def get_cell(self, x, y):
return self.cells[x][y]
def get_cell_from_window(self, window_x, window_y):
for column in self.cells:
for cell in column:
if cell.rect.collidepoint(window_x, window_y):
return cell
def clear_walls(self):
for column in self.cells:
for cell in column:
cell.set_wall(False)
cell.set_explored(False)
cell.set_cloud(False)
def clear_explored(self):
for column in self.cells:
for cell in column:
cell.set_explored(False)
cell.set_cloud(False)
cell.set_g(math.inf)
cell.set_f(None)
def render(self, surface: pygame.Surface):
rects = []
# Render all cells onto surface
for column in self.cells:
for cell in column:
rect = pygame.draw.rect(surface, cell.color, cell.rect)
rects.append(rect)
# Return list of areas on the window that were drawn to
return rects
def define_neighbors(self, cell):
# Fills default adjacency list with 8 adjacent cells and distances, handling border cases
x = cell.x
y = cell.y
if cell.is_wall:
cell.neighbors = {}
return
if x != 0:
cell.add_neighbor(self.cells[x - 1][y], 1)
if y != 0:
cell.add_neighbor(self.cells[x - 1][y - 1], math.sqrt(2))
if y != CELL_COUNT_Y - 1:
cell.add_neighbor(self.cells[x - 1][y + 1], math.sqrt(2))
if x != CELL_COUNT_X - 1:
cell.add_neighbor(self.cells[x + 1][y], 1)
if y != 0:
cell.add_neighbor(self.cells[x + 1][y - 1], math.sqrt(2))
if y != CELL_COUNT_Y - 1:
cell.add_neighbor(self.cells[x + 1][y + 1], math.sqrt(2))
if y != 0:
cell.add_neighbor(self.cells[x][y - 1], 1)
if y != CELL_COUNT_Y - 1:
cell.add_neighbor(self.cells[x][y + 1], 1)
class Cell(object):
def __init__(self, rect: pygame.Rect, x, y, is_wall: bool):
self.x = x
self.y = y
self.rect = rect
self.color = CELL_COLOR_WALL if is_wall else CELL_COLOR_EMPTY
self.neighbors = {}
self.is_wall = is_wall
self.is_destination = False
self.is_start = False
self.is_explored = False
self.g = math.inf # distance from starting node to cell, updated by a* algorithm
self.f = None # total cost from starting node to cell, = g(cell) + h(cell), updated by a* algorithm
def add_neighbor(self, neighbor: "Cell", weight):
if not neighbor.is_wall and neighbor not in self.neighbors:
self.neighbors[neighbor] = weight
def set_wall(self, value: bool):
if self.is_wall == value:
return
if value:
self.neighbors = {}
self.is_wall = value
self.color = CELL_COLOR_WALL if value else CELL_COLOR_EMPTY
def set_destination(self, value: bool):
if self.is_destination == value:
return
self.is_destination = True
self.color = CELL_COLOR_DESTINATION if value else CELL_COLOR_EMPTY
def set_start(self, value: bool):
if self.is_start == value:
return
self.is_start = value
self.color = CELL_COLOR_START if value else CELL_COLOR_EMPTY
def set_explored(self, value: bool):
if self.is_destination or self.is_start or self.is_wall:
return
if self.is_explored == value:
return
self.is_explored = value
self.color = CELL_COLOR_EXPLORED if value else CELL_COLOR_EMPTY
def set_cloud(self, value: bool):
if self.is_destination or self.is_start or self.is_wall:
return
self.color = CELL_COLOR_CLOUD if value else CELL_COLOR_EMPTY
def set_g(self, value):
self.g = value
def set_f(self, value):
self.f = value
def get_g(self):
return self.g
def get_f(self):
return self.f
def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"
def __repr__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"
def __lt__(self, other):
return self.f < other.f # used in ordering heap