TroglodyteDerivations commited on
Commit
8bd26ad
·
verified ·
1 Parent(s): 593f436

Upload 3 files

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +377 -0
  3. demo.mp4 +3 -0
  4. requirements.txt +1 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ demo.mp4 filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import heapq
3
+ import math
4
+ from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
5
+ QHBoxLayout, QPushButton, QLabel, QSpinBox,
6
+ QComboBox, QMessageBox, QFrame)
7
+ from PyQt5.QtCore import Qt, QTimer, pyqtSignal
8
+ from PyQt5.QtGui import QPainter, QColor, QPen, QFont
9
+
10
+ class Node:
11
+ def __init__(self, x, y, walkable=True):
12
+ self.x = x
13
+ self.y = y
14
+ self.walkable = walkable
15
+ self.g = 0 # Cost from start to this node
16
+ self.h = 0 # Heuristic cost to end
17
+ self.f = 0 # Total cost (g + h)
18
+ self.parent = None
19
+
20
+ def __lt__(self, other):
21
+ return self.f < other.f
22
+
23
+ class AStarGame(QMainWindow):
24
+ def __init__(self):
25
+ super().__init__()
26
+ self.grid_size = 20
27
+ self.cell_size = 30
28
+ self.grid = []
29
+ self.start_node = None
30
+ self.end_node = None
31
+ self.path = []
32
+ self.open_set = []
33
+ self.closed_set = set()
34
+ self.is_running = False
35
+ self.is_paused = False
36
+ self.speed = 100 # ms
37
+ self.timer = QTimer()
38
+ self.timer.timeout.connect(self.step_algorithm)
39
+ self.current_mode = "start" # start, end, obstacle, erase
40
+ self.init_ui()
41
+ self.init_grid()
42
+
43
+ def init_ui(self):
44
+ self.setWindowTitle("A* Pathfinding Algorithm Game")
45
+ self.setFixedSize(self.grid_size * self.cell_size + 250,
46
+ self.grid_size * self.cell_size + 50)
47
+
48
+ # Central widget
49
+ central_widget = QWidget()
50
+ self.setCentralWidget(central_widget)
51
+
52
+ # Main layout
53
+ main_layout = QHBoxLayout()
54
+ central_widget.setLayout(main_layout)
55
+
56
+ # Grid widget
57
+ self.grid_widget = GridWidget(self)
58
+ main_layout.addWidget(self.grid_widget)
59
+
60
+ # Control panel
61
+ control_panel = QFrame()
62
+ control_panel.setFrameStyle(QFrame.Box)
63
+ control_panel.setFixedWidth(200)
64
+ control_layout = QVBoxLayout()
65
+ control_panel.setLayout(control_layout)
66
+
67
+ # Mode selection
68
+ mode_label = QLabel("Mode:")
69
+ mode_label.setFont(QFont("Arial", 12, QFont.Bold))
70
+ control_layout.addWidget(mode_label)
71
+
72
+ self.mode_combo = QComboBox()
73
+ self.mode_combo.addItems(["Start Point", "End Point", "Obstacles", "Erase"])
74
+ self.mode_combo.currentIndexChanged.connect(self.change_mode)
75
+ control_layout.addWidget(self.mode_combo)
76
+
77
+ # Algorithm controls
78
+ control_layout.addSpacing(20)
79
+ algo_label = QLabel("Algorithm Controls:")
80
+ algo_label.setFont(QFont("Arial", 12, QFont.Bold))
81
+ control_layout.addWidget(algo_label)
82
+
83
+ self.start_button = QPushButton("Start")
84
+ self.start_button.clicked.connect(self.start_algorithm)
85
+ control_layout.addWidget(self.start_button)
86
+
87
+ self.pause_button = QPushButton("Pause")
88
+ self.pause_button.clicked.connect(self.pause_algorithm)
89
+ self.pause_button.setEnabled(False)
90
+ control_layout.addWidget(self.pause_button)
91
+
92
+ self.reset_button = QPushButton("Reset")
93
+ self.reset_button.clicked.connect(self.reset_grid)
94
+ control_layout.addWidget(self.reset_button)
95
+
96
+ self.clear_button = QPushButton("Clear All")
97
+ self.clear_button.clicked.connect(self.clear_all)
98
+ control_layout.addWidget(self.clear_button)
99
+
100
+ # Speed control
101
+ control_layout.addSpacing(20)
102
+ speed_label = QLabel("Speed:")
103
+ speed_label.setFont(QFont("Arial", 12, QFont.Bold))
104
+ control_layout.addWidget(speed_label)
105
+
106
+ speed_layout = QHBoxLayout()
107
+ self.speed_spin = QSpinBox()
108
+ self.speed_spin.setRange(10, 500)
109
+ self.speed_spin.setValue(self.speed)
110
+ self.speed_spin.valueChanged.connect(self.change_speed)
111
+ speed_layout.addWidget(self.speed_spin)
112
+ speed_layout.addWidget(QLabel("ms"))
113
+ control_layout.addLayout(speed_layout)
114
+
115
+ # Instructions
116
+ control_layout.addSpacing(20)
117
+ instructions_label = QLabel("Instructions:")
118
+ instructions_label.setFont(QFont("Arial", 12, QFont.Bold))
119
+ control_layout.addWidget(instructions_label)
120
+
121
+ instructions = QLabel(
122
+ "1. Set start and end points\n"
123
+ "2. Add obstacles\n"
124
+ "3. Click Start to find path\n"
125
+ "4. Watch the algorithm work!"
126
+ )
127
+ instructions.setWordWrap(True)
128
+ control_layout.addWidget(instructions)
129
+
130
+ # Status
131
+ control_layout.addSpacing(20)
132
+ self.status_label = QLabel("Status: Ready")
133
+ self.status_label.setFont(QFont("Arial", 10))
134
+ control_layout.addWidget(self.status_label)
135
+
136
+ control_layout.addStretch()
137
+ main_layout.addWidget(control_panel)
138
+
139
+ def init_grid(self):
140
+ self.grid = []
141
+ for y in range(self.grid_size):
142
+ row = []
143
+ for x in range(self.grid_size):
144
+ row.append(Node(x, y))
145
+ self.grid.append(row)
146
+
147
+ # Set default start and end
148
+ self.start_node = self.grid[2][2]
149
+ self.end_node = self.grid[self.grid_size-3][self.grid_size-3]
150
+ self.update()
151
+
152
+ def change_mode(self, index):
153
+ modes = ["start", "end", "obstacle", "erase"]
154
+ self.current_mode = modes[index]
155
+
156
+ def change_speed(self, value):
157
+ self.speed = value
158
+ if self.timer.isActive():
159
+ self.timer.setInterval(self.speed)
160
+
161
+ def start_algorithm(self):
162
+ if not self.start_node or not self.end_node:
163
+ QMessageBox.warning(self, "Warning", "Please set both start and end points.")
164
+ return
165
+
166
+ self.is_running = True
167
+ self.is_paused = False
168
+ self.path = []
169
+ self.open_set = []
170
+ self.closed_set = set()
171
+
172
+ # Initialize start node
173
+ self.start_node.g = 0
174
+ self.start_node.h = self.heuristic(self.start_node, self.end_node)
175
+ self.start_node.f = self.start_node.g + self.start_node.h
176
+
177
+ heapq.heappush(self.open_set, (self.start_node.f, self.start_node))
178
+
179
+ self.start_button.setEnabled(False)
180
+ self.pause_button.setEnabled(True)
181
+ self.reset_button.setEnabled(False)
182
+ self.status_label.setText("Status: Running")
183
+
184
+ self.timer.start(self.speed)
185
+
186
+ def pause_algorithm(self):
187
+ if self.is_running:
188
+ if self.is_paused:
189
+ self.timer.start(self.speed)
190
+ self.pause_button.setText("Pause")
191
+ self.status_label.setText("Status: Running")
192
+ else:
193
+ self.timer.stop()
194
+ self.pause_button.setText("Resume")
195
+ self.status_label.setText("Status: Paused")
196
+ self.is_paused = not self.is_paused
197
+
198
+ def reset_grid(self):
199
+ self.timer.stop()
200
+ self.is_running = False
201
+ self.is_paused = False
202
+
203
+ for row in self.grid:
204
+ for node in row:
205
+ node.g = 0
206
+ node.h = 0
207
+ node.f = 0
208
+ node.parent = None
209
+
210
+ self.path = []
211
+ self.open_set = []
212
+ self.closed_set = set()
213
+
214
+ self.start_button.setEnabled(True)
215
+ self.pause_button.setEnabled(False)
216
+ self.reset_button.setEnabled(True)
217
+ self.pause_button.setText("Pause")
218
+ self.status_label.setText("Status: Ready")
219
+
220
+ self.update()
221
+
222
+ def clear_all(self):
223
+ self.reset_grid()
224
+ self.init_grid()
225
+
226
+ def step_algorithm(self):
227
+ if not self.open_set:
228
+ self.timer.stop()
229
+ self.is_running = False
230
+ self.status_label.setText("Status: No path found!")
231
+ self.start_button.setEnabled(True)
232
+ self.pause_button.setEnabled(False)
233
+ self.reset_button.setEnabled(True)
234
+ return
235
+
236
+ # Get node with lowest f score
237
+ current = heapq.heappop(self.open_set)[1]
238
+
239
+ # Check if we reached the end
240
+ if current == self.end_node:
241
+ self.timer.stop()
242
+ self.is_running = False
243
+ self.reconstruct_path(current)
244
+ self.status_label.setText("Status: Path found!")
245
+ self.start_button.setEnabled(True)
246
+ self.pause_button.setEnabled(False)
247
+ self.reset_button.setEnabled(True)
248
+ return
249
+
250
+ self.closed_set.add(current)
251
+
252
+ # Check neighbors
253
+ for neighbor in self.get_neighbors(current):
254
+ if neighbor in self.closed_set or not neighbor.walkable:
255
+ continue
256
+
257
+ tentative_g = current.g + self.distance(current, neighbor)
258
+
259
+ if tentative_g < neighbor.g or neighbor not in [n[1] for n in self.open_set]:
260
+ neighbor.parent = current
261
+ neighbor.g = tentative_g
262
+ neighbor.h = self.heuristic(neighbor, self.end_node)
263
+ neighbor.f = neighbor.g + neighbor.h
264
+
265
+ if neighbor not in [n[1] for n in self.open_set]:
266
+ heapq.heappush(self.open_set, (neighbor.f, neighbor))
267
+
268
+ self.update()
269
+
270
+ def get_neighbors(self, node):
271
+ neighbors = []
272
+ for dx, dy in [(0, -1), (1, 0), (0, 1), (-1, 0), (-1, -1), (1, -1), (-1, 1), (1, 1)]:
273
+ x, y = node.x + dx, node.y + dy
274
+ if 0 <= x < self.grid_size and 0 <= y < self.grid_size:
275
+ neighbors.append(self.grid[y][x])
276
+ return neighbors
277
+
278
+ def distance(self, node_a, node_b):
279
+ # Euclidean distance
280
+ dx = node_a.x - node_b.x
281
+ dy = node_a.y - node_b.y
282
+ return math.sqrt(dx*dx + dy*dy)
283
+
284
+ def heuristic(self, node_a, node_b):
285
+ # Manhattan distance
286
+ return abs(node_a.x - node_b.x) + abs(node_a.y - node_b.y)
287
+
288
+ def reconstruct_path(self, current):
289
+ self.path = []
290
+ while current:
291
+ self.path.append(current)
292
+ current = current.parent
293
+ self.path.reverse()
294
+
295
+ def handle_click(self, x, y):
296
+ if self.is_running:
297
+ return
298
+
299
+ node = self.grid[y][x]
300
+
301
+ if self.current_mode == "start":
302
+ if node != self.end_node and node.walkable:
303
+ self.start_node = node
304
+ elif self.current_mode == "end":
305
+ if node != self.start_node and node.walkable:
306
+ self.end_node = node
307
+ elif self.current_mode == "obstacle":
308
+ if node != self.start_node and node != self.end_node:
309
+ node.walkable = False
310
+ elif self.current_mode == "erase":
311
+ node.walkable = True
312
+ if node == self.start_node:
313
+ self.start_node = None
314
+ elif node == self.end_node:
315
+ self.end_node = None
316
+
317
+ self.update()
318
+
319
+ class GridWidget(QWidget):
320
+ def __init__(self, game):
321
+ super().__init__()
322
+ self.game = game
323
+ self.setMouseTracking(True)
324
+
325
+ def mousePressEvent(self, event):
326
+ if event.button() == Qt.LeftButton:
327
+ x = event.x() // self.game.cell_size
328
+ y = event.y() // self.game.cell_size
329
+ if 0 <= x < self.game.grid_size and 0 <= y < self.game.grid_size:
330
+ self.game.handle_click(x, y)
331
+
332
+ def paintEvent(self, event):
333
+ painter = QPainter(self)
334
+ painter.setRenderHint(QPainter.Antialiasing)
335
+
336
+ # Draw grid
337
+ for y in range(self.game.grid_size):
338
+ for x in range(self.game.grid_size):
339
+ node = self.game.grid[y][x]
340
+ rect = (x * self.game.cell_size, y * self.game.cell_size,
341
+ self.game.cell_size, self.game.cell_size)
342
+
343
+ # Background
344
+ if node == self.game.start_node:
345
+ painter.fillRect(*rect, QColor(0, 200, 0)) # Green for start
346
+ elif node == self.game.end_node:
347
+ painter.fillRect(*rect, QColor(200, 0, 0)) # Red for end
348
+ elif not node.walkable:
349
+ painter.fillRect(*rect, QColor(100, 100, 100)) # Gray for obstacles
350
+ elif node in self.game.path:
351
+ painter.fillRect(*rect, QColor(0, 0, 200)) # Blue for path
352
+ elif node in [n[1] for n in self.game.open_set]:
353
+ painter.fillRect(*rect, QColor(200, 200, 100)) # Yellow for open set
354
+ elif node in self.game.closed_set:
355
+ painter.fillRect(*rect, QColor(200, 150, 150)) # Light red for closed set
356
+ else:
357
+ painter.fillRect(*rect, QColor(255, 255, 255)) # White for empty
358
+
359
+ # Grid lines
360
+ painter.setPen(QPen(QColor(200, 200, 200), 1))
361
+ painter.drawRect(*rect)
362
+
363
+ # Display cost values if algorithm is running
364
+ if self.game.is_running and node.g > 0:
365
+ painter.setPen(QPen(QColor(0, 0, 0), 1))
366
+ painter.setFont(QFont("Arial", 8))
367
+ painter.drawText(rect[0] + 2, rect[1] + 12, f"g:{node.g:.1f}")
368
+ painter.drawText(rect[0] + 2, rect[1] + 24, f"h:{node.h:.1f}")
369
+
370
+ def main():
371
+ app = QApplication(sys.argv)
372
+ game = AStarGame()
373
+ game.show()
374
+ sys.exit(app.exec_())
375
+
376
+ if __name__ == "__main__":
377
+ main()
demo.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fc99692407729ae2d501c082e39f803ff7a752f3da0adcd33c1d097bb809a149
3
+ size 10099542
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ PyQt5