-
Notifications
You must be signed in to change notification settings - Fork 0
/
classdef.py
278 lines (237 loc) · 11.6 KB
/
classdef.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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# script for all the classes
# this handles the drawing as well as the button presses for all the pygame window
# imports
import pygame
import random
import math
# colors
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
INACTIVE_BUTTON = BLACK
ACTIVE_BUTTON = GREEN
BUTTON_TEXT_COL = WHITE
FRINGE = (0, 82, 204) # actually this is not the fringe but the unopened nodes in open
# constants
COST = 5 # the cost to travel from a node to the other
class Node(object):
# class definition for the nodes
def __init__(self, x=0, y=0, dim=0, gridx=0, gridy=0):
self.x = x
self.y = y
self.dim = dim
self.col = WHITE
self.gridx = gridx
self.gridy = gridy
self.neighbours = list() # list of all the neighbours
self.gcost = 10000000000 # any high value ie. infinity
self.hcost = None # heuristic cost
self.fcost = None # addition of gcost & hcost
self.prev = None # this represents the parent node that has to be traversed to get to this node
def setStart(self):
self.col = GREEN
self.gcost = 0
def setEnd(self):
self.col = RED
self.hcost = 0
def setWall(self):
if self.col == WHITE:
self.col = BLACK
def clear(self):
# resets a node
self.col = WHITE
self.gcost = 10000000000
self.hcost = None
self.fcost = None
def calcGcost(self):
return self.gcost + COST # returns the gcost of any neighbour node
def calcHcost(self, end):
return math.sqrt((end.x - self.x) ** 2 + (end.y - self.y) ** 2) # we are using euclidian distance as heuristic function
def getNeighbours(self, mainGrid, delay):
for row in range(-1, 2):
for col in range(-1, 2):
if (row == 0 and col == 0) or (abs(row) == 1 and abs(col) == 1): # doesn't go diagonally
continue
if self.gridy + row >= 0 and self.gridy + row < len(mainGrid.grid) and self.gridx + col >= 0 and self.gridx + col < len(mainGrid.grid[0]):
evaluating = mainGrid.grid[self.gridy + row][self.gridx + col] # the neighbour we are evaluating
if evaluating in mainGrid.closed or evaluating.col == BLACK:
continue
self.neighbours.append(evaluating)
if evaluating != mainGrid.end:
evaluating.col = FRINGE
pygame.time.delay(delay)
def getNodePos(self):
return (self.x, self.y, self.dim, self.dim)
def draw(self, win):
if self.col == WHITE:
pygame.draw.rect(win, BLACK, self.getNodePos(), 1) # we are drawing them as black to get the grid border effect
elif self.col == (255, 255, 51):
pygame.draw.rect(win, self.col, self.getNodePos())
pygame.draw.rect(win, BLACK, self.getNodePos(), 1) # drawing the border seperately for 'A E S T H E T I C S'
else:
pygame.draw.rect(win, self.col, self.getNodePos())
class Grid(object):
# class definition for the grid
def __init__(self, x, y, width, height, num):
self.x = x # x, y is the position of the first node in grid
self.y = y
self.width = width
self.height = height
self.num = num # number on nodes in grid. It dictates the width of the nodes
# grid modes
self.drawingWalls = True # toggle for if the user is drawing walls
self.drawingStart = False # toggle for if the user is drawing start node
self.drawingEnd = False # toggle for if the user is drawing end node
self.drawingErase = False # toggle for if the user is erasing walls
self.vizStarted = False # toggle for if the visualization has already started
self.nodeDim = self.width // self.num # dimentions of each node
self.grid = self.createGrid()
# creating the buttons
self.setStartButton = pygame.Rect(10, 10, 150, 40)
self.setEndButton = pygame.Rect(10, 90, 150, 40)
self.setWallButton = pygame.Rect(10, 170, 150, 40)
self.setEraseButton = pygame.Rect(10, 250, 150, 40)
self.randMazeButton = pygame.Rect(10, 330, 150, 40)
self.clearButton = pygame.Rect(10, 410, 150, 40)
self.startVizButton = pygame.Rect(10, 530, 150, 60)
# for the pathfinder algorithm
self.start = None
self.end = None
self.open = list()
self.closed = list()
def createGrid(self):
return [[Node((col * self.nodeDim) + self.x, (row * self.nodeDim) + self.y, self.nodeDim, col, row) for col in range(self.num)] for row in range(self.height // self.nodeDim)] # returns a grid based on the number of nodes
def makeRandMaze(self):
# makes a random maze
self.clearGrid()
self.clearState()
for row in range(len(self.grid)):
for col in range(len(self.grid[0])):
if bool(random.getrandbits(1)):
self.grid[row][col].setWall()
def clearGrid(self):
# clears the grid
for row in range(len(self.grid)):
for col in range(len(self.grid[0])):
self.grid[row][col].clear()
self.start = None
self.end = None
def getPos(self):
return (self.x, self.y, self.width, self.height) # returns the position of the whole grid
def draw(self, win, buttonFont):
for row in range(self.height // self.nodeDim):
for col in range(self.num):
self.grid[row][col].draw(win)
self.drawButtons(win, buttonFont) # draws the buttons
def drawButtons(self, win, buttonFont):
if self.drawingStart:
pygame.draw.rect(win, ACTIVE_BUTTON, self.setStartButton) # drawing the start button
else:
pygame.draw.rect(win, INACTIVE_BUTTON, self.setStartButton) # drawing the start button
startButton = buttonFont.render('Draw start', 1, BUTTON_TEXT_COL)
win.blit(startButton, (self.setStartButton.left + 30, self.setStartButton.top + 11))
if self.drawingEnd:
pygame.draw.rect(win, ACTIVE_BUTTON, self.setEndButton) # drawing the end button
else:
pygame.draw.rect(win, INACTIVE_BUTTON, self.setEndButton) # drawing the end button
endButton = buttonFont.render('Draw end', 1, BUTTON_TEXT_COL)
win.blit(endButton, (self.setEndButton.left + 35, self.setEndButton.top + 11))
if self.drawingWalls:
pygame.draw.rect(win, ACTIVE_BUTTON, self.setWallButton) # drawing the wall button
else:
pygame.draw.rect(win, INACTIVE_BUTTON, self.setWallButton) # drawing the wall button
wallButton = buttonFont.render('Draw walls', 1, BUTTON_TEXT_COL)
win.blit(wallButton, (self.setWallButton.left + 35, self.setWallButton.top + 11))
if self.drawingErase:
pygame.draw.rect(win, ACTIVE_BUTTON, self.setEraseButton) # drawing the erase button
else:
pygame.draw.rect(win, INACTIVE_BUTTON, self.setEraseButton) # drawing the erase button
eraseButton = buttonFont.render('Erase', 1, BUTTON_TEXT_COL)
win.blit(eraseButton, (self.setEraseButton.left + 52, self.setEraseButton.top + 11))
pygame.draw.rect(win, INACTIVE_BUTTON, self.randMazeButton) # drawing the random maze button
mazeButton = buttonFont.render('Random maze', 1, BUTTON_TEXT_COL)
win.blit(mazeButton, (self.randMazeButton.left + 15, self.randMazeButton.top + 11))
pygame.draw.rect(win, INACTIVE_BUTTON, self.clearButton) # drawing the clear grid button
clearGridButton = buttonFont.render('Clear Grid', 1, BUTTON_TEXT_COL)
win.blit(clearGridButton, (self.clearButton.left + 29, self.clearButton.top + 11))
pygame.draw.rect(win, INACTIVE_BUTTON, self.startVizButton) # drawing the startviz button
vizButton = buttonFont.render('Start Viz', 1, BUTTON_TEXT_COL)
win.blit(vizButton, (self.startVizButton.left + 35, self.startVizButton.top + 20))
def clickOnGrid(self, mousePos):
# function to handle the drawing
if mousePos[1] // self.nodeDim >= len(self.grid) or (mousePos[0] - self.x) // self.nodeDim >= len(self.grid[0]): # to stay in bounds of the grid list
return
if mousePos[0] < self.x: # checking if the click is done on the grid
self.clickOnSidebar(mousePos)
return
if self.vizStarted:
return # cannot draw on screen if the viz has already started
node = self.getGridPos(mousePos)
# proceeding according to the grid state
if self.drawingStart:
if node.col == WHITE:
if self.start is not None:
self.start.clear() # clears if another node for the same purpose exists
node.setStart()
self.start = node
elif self.drawingEnd:
if node.col == WHITE:
if self.end is not None:
self.end.clear() # clears if another node for the same purpose exists
node.setEnd()
self.end = node
elif self.drawingWalls:
node.setWall()
elif self.drawingErase:
if node == self.start:
self.start = None
elif node == self.end:
self.end = None
node.clear()
def getGridPos(self, mousePos):
# this translates the mouse click to a node in the array
return self.grid[mousePos[1] // self.nodeDim][(mousePos[0] - self.x) // self.nodeDim]
def clickOnSidebar(self, mousePos):
# checks which button from the sidebar has been pressed
if self.buttonClick(mousePos, self.setStartButton):
if not self.drawingStart and not self.vizStarted:
self.clearState()
self.drawingStart = True
elif self.buttonClick(mousePos, self.setEndButton):
if not self.drawingEnd and not self.vizStarted:
self.clearState()
self.drawingEnd = True
elif self.buttonClick(mousePos, self.setWallButton):
if not self.drawingWalls and not self.vizStarted:
self.clearState()
self.drawingWalls = True
elif self.buttonClick(mousePos, self.setEraseButton):
if not self.drawingErase and not self.vizStarted:
self.clearState()
self.drawingErase = True
elif self.buttonClick(mousePos, self.randMazeButton) and not self.vizStarted:
self.makeRandMaze()
elif self.buttonClick(mousePos, self.clearButton) and not self.vizStarted:
self.clearGrid()
self.clearState()
self.drawingWalls = True # after clearing the grid ut seems natural to default to drawing walls
elif self.buttonClick(mousePos, self.startVizButton) and not self.vizStarted and not (self.start is None or self.end is None):
self.clearState()
self.vizStarted = True # we need to start the vizualization here
self.start.hcost = self.start.calcHcost(self.end)
def clearState(self):
# clears the state of the grid
self.drawingStart = False
self.drawingEnd = False
self.drawingWalls = False
self.drawingErase = False
def buttonClick(self, mousePos, button):
# returns if a button has been pressed
return (mousePos[0] >= button.left and mousePos[0] <= button.right) and (mousePos[1] >= button.top and mousePos[1] <= button.bottom)
class Choice(object):
# class for getting the user's choice
def __init__(self):
self.algo = None
self.nodes = None
self.speed = None