diff --git a/projects_registry.json b/projects_registry.json index 9e9d2e2b..943cf35e 100644 --- a/projects_registry.json +++ b/projects_registry.json @@ -618,5 +618,22 @@ "harmonics" ], "path": "math/Fourier-Series-Visualizer/Fourier-Series-Visualizer.py" + }, + { + "name": "Pathfinding Visualizer", + "emoji": "πΊοΈ", + "category": "utilities", + "difficulty": "advanced", + "description": "Visualize Dijkstra's and A* Search algorithms on an interactive grid.", + "keywords": [ + "pathfinding", + "visualizer", + "algorithm", + "dijkstra", + "astar", + "grid", + "search" + ], + "path": "utilities/Pathfinding-Visualizer/Pathfinding-Visualizer.py" } ] \ No newline at end of file diff --git a/utilities/Pathfinding-Visualizer/Pathfinding-Visualizer.py b/utilities/Pathfinding-Visualizer/Pathfinding-Visualizer.py new file mode 100644 index 00000000..65775738 --- /dev/null +++ b/utilities/Pathfinding-Visualizer/Pathfinding-Visualizer.py @@ -0,0 +1,92 @@ +import os +import time + +def clear_screen(): + os.system('cls' if os.name == 'nt' else 'clear') + +def print_grid(grid): + for row in grid: + print(" ".join(row)) + print() + +def get_neighbors(node, rows, cols): + r, c = node + neighbors = [] + if r > 0: neighbors.append((r - 1, c)) + if r < rows - 1: neighbors.append((r + 1, c)) + if c > 0: neighbors.append((r, c - 1)) + if c < cols - 1: neighbors.append((r, c + 1)) + return neighbors + +def run_dijkstra(grid, start, end): + rows, cols = len(grid), len(grid[0]) + distances = { (r, c): float('inf') for r in range(rows) for c in range(cols) } + distances[start] = 0 + previous = { (r, c): None for r in range(rows) for c in range(cols) } + unvisited = [(r, c) for r in range(rows) for c in range(cols)] + + while unvisited: + unvisited.sort(key=lambda node: distances[node]) + current = unvisited.pop(0) + + if distances[current] == float('inf'): + break + + if grid[current[0]][current[1]] == 'β': + continue + + if current == end: + break + + for neighbor in get_neighbors(current, rows, cols): + if neighbor in unvisited and grid[neighbor[0]][neighbor[1]] != 'β': + alt = distances[current] + 1 + if alt < distances[neighbor]: + distances[neighbor] = alt + previous[neighbor] = current + + if neighbor != end and neighbor != start: + grid[neighbor[0]][neighbor[1]] = '.' + clear_screen() + print_grid(grid) + time.sleep(0.05) + + # Reconstruct path + path = [] + curr = end + while previous[curr] is not None: + path.append(curr) + curr = previous[curr] + + for p in reversed(path): + if p != end and p != start: + grid[p[0]][p[1]] = '*' + clear_screen() + print_grid(grid) + time.sleep(0.05) + +def main(): + rows, cols = 10, 20 + grid = [[' ' for _ in range(cols)] for _ in range(rows)] + + start = (1, 1) + end = (8, 18) + + grid[start[0]][start[1]] = 'S' + grid[end[0]][end[1]] = 'E' + + # Add some walls + for i in range(2, 8): + grid[i][6] = 'β' + for i in range(1, 7): + grid[i][12] = 'β' + + print("πΊοΈ Pathfinding Visualizer (Terminal Edition)") + print("S = Start, E = End, β = Wall, . = Visited, * = Path") + input("Press Enter to run Dijkstra's Algorithm...") + + run_dijkstra(grid, start, end) + print("Finished Pathfinding!") + +if __name__ == "__main__": + main() diff --git a/web-app/assets/banners/2048-game.webp b/web-app/assets/banners/2048-game.webp index f4e7851a..4b4daced 100644 Binary files a/web-app/assets/banners/2048-game.webp and b/web-app/assets/banners/2048-game.webp differ diff --git a/web-app/assets/banners/armstrong.webp b/web-app/assets/banners/armstrong.webp index bbef83d2..397e88a0 100644 Binary files a/web-app/assets/banners/armstrong.webp and b/web-app/assets/banners/armstrong.webp differ diff --git a/web-app/assets/banners/binary-search.webp b/web-app/assets/banners/binary-search.webp index 89f26030..b0ca47cd 100644 Binary files a/web-app/assets/banners/binary-search.webp and b/web-app/assets/banners/binary-search.webp differ diff --git a/web-app/assets/banners/blackjack21.webp b/web-app/assets/banners/blackjack21.webp index 62bd6cd8..5e9fe570 100644 Binary files a/web-app/assets/banners/blackjack21.webp and b/web-app/assets/banners/blackjack21.webp differ diff --git a/web-app/assets/banners/bubble-sort.webp b/web-app/assets/banners/bubble-sort.webp index b7925012..15c5688a 100644 Binary files a/web-app/assets/banners/bubble-sort.webp and b/web-app/assets/banners/bubble-sort.webp differ diff --git a/web-app/assets/banners/budget-tracker.webp b/web-app/assets/banners/budget-tracker.webp index 516eac09..97f56747 100644 Binary files a/web-app/assets/banners/budget-tracker.webp and b/web-app/assets/banners/budget-tracker.webp differ diff --git a/web-app/assets/banners/caesar-cipher.webp b/web-app/assets/banners/caesar-cipher.webp index e9c91c42..aab8f6f4 100644 Binary files a/web-app/assets/banners/caesar-cipher.webp and b/web-app/assets/banners/caesar-cipher.webp differ diff --git a/web-app/assets/banners/calculator.webp b/web-app/assets/banners/calculator.webp index d89984c0..fccb8732 100644 Binary files a/web-app/assets/banners/calculator.webp and b/web-app/assets/banners/calculator.webp differ diff --git a/web-app/assets/banners/chess.webp b/web-app/assets/banners/chess.webp index 87ca03ea..b20230f2 100644 Binary files a/web-app/assets/banners/chess.webp and b/web-app/assets/banners/chess.webp differ diff --git a/web-app/assets/banners/coin-flip.webp b/web-app/assets/banners/coin-flip.webp index d5e558de..4c438568 100644 Binary files a/web-app/assets/banners/coin-flip.webp and b/web-app/assets/banners/coin-flip.webp differ diff --git a/web-app/assets/banners/collatz.webp b/web-app/assets/banners/collatz.webp index 35c2b773..796cdc6d 100644 Binary files a/web-app/assets/banners/collatz.webp and b/web-app/assets/banners/collatz.webp differ diff --git a/web-app/assets/banners/color-palette.webp b/web-app/assets/banners/color-palette.webp index 2c7ffd38..cd3cc427 100644 Binary files a/web-app/assets/banners/color-palette.webp and b/web-app/assets/banners/color-palette.webp differ diff --git a/web-app/assets/banners/coordinate-polar-transform.webp b/web-app/assets/banners/coordinate-polar-transform.webp index 11088c13..1290f31a 100644 Binary files a/web-app/assets/banners/coordinate-polar-transform.webp and b/web-app/assets/banners/coordinate-polar-transform.webp differ diff --git a/web-app/assets/banners/derivative-calculator.webp b/web-app/assets/banners/derivative-calculator.webp index 50930531..eaa7362d 100644 Binary files a/web-app/assets/banners/derivative-calculator.webp and b/web-app/assets/banners/derivative-calculator.webp differ diff --git a/web-app/assets/banners/dice-rolling.webp b/web-app/assets/banners/dice-rolling.webp index 0bf89226..6fc8ba37 100644 Binary files a/web-app/assets/banners/dice-rolling.webp and b/web-app/assets/banners/dice-rolling.webp differ diff --git a/web-app/assets/banners/dots-boxes.webp b/web-app/assets/banners/dots-boxes.webp index 4263cbfd..4b777310 100644 Binary files a/web-app/assets/banners/dots-boxes.webp and b/web-app/assets/banners/dots-boxes.webp differ diff --git a/web-app/assets/banners/emoji-memory-game.webp b/web-app/assets/banners/emoji-memory-game.webp index 3ffa9fc1..1b9e8163 100644 Binary files a/web-app/assets/banners/emoji-memory-game.webp and b/web-app/assets/banners/emoji-memory-game.webp differ diff --git a/web-app/assets/banners/fibonacci.webp b/web-app/assets/banners/fibonacci.webp index 939f1083..0be96a47 100644 Binary files a/web-app/assets/banners/fibonacci.webp and b/web-app/assets/banners/fibonacci.webp differ diff --git a/web-app/assets/banners/flames.webp b/web-app/assets/banners/flames.webp index e01dcfe2..a5aa44c8 100644 Binary files a/web-app/assets/banners/flames.webp and b/web-app/assets/banners/flames.webp differ diff --git a/web-app/assets/banners/flappy-game.webp b/web-app/assets/banners/flappy-game.webp index 823725ea..afca434c 100644 Binary files a/web-app/assets/banners/flappy-game.webp and b/web-app/assets/banners/flappy-game.webp differ diff --git a/web-app/assets/banners/fourier-series.webp b/web-app/assets/banners/fourier-series.webp index 1015fd32..54d1336a 100644 Binary files a/web-app/assets/banners/fourier-series.webp and b/web-app/assets/banners/fourier-series.webp differ diff --git a/web-app/assets/banners/hangman.webp b/web-app/assets/banners/hangman.webp index ef82af85..dfcf3b3d 100644 Binary files a/web-app/assets/banners/hangman.webp and b/web-app/assets/banners/hangman.webp differ diff --git a/web-app/assets/banners/math-quiz.webp b/web-app/assets/banners/math-quiz.webp index 1a2fa278..321b10fb 100644 Binary files a/web-app/assets/banners/math-quiz.webp and b/web-app/assets/banners/math-quiz.webp differ diff --git a/web-app/assets/banners/matrix-calculator.webp b/web-app/assets/banners/matrix-calculator.webp index 592124a2..7a6b7a0a 100644 Binary files a/web-app/assets/banners/matrix-calculator.webp and b/web-app/assets/banners/matrix-calculator.webp differ diff --git a/web-app/assets/banners/morse-code.webp b/web-app/assets/banners/morse-code.webp index 11e58c07..a535603f 100644 Binary files a/web-app/assets/banners/morse-code.webp and b/web-app/assets/banners/morse-code.webp differ diff --git a/web-app/assets/banners/number-converter.webp b/web-app/assets/banners/number-converter.webp index 305181d3..dd1fbeb2 100644 Binary files a/web-app/assets/banners/number-converter.webp and b/web-app/assets/banners/number-converter.webp differ diff --git a/web-app/assets/banners/number-guessing.webp b/web-app/assets/banners/number-guessing.webp index e69d8bb6..fb788575 100644 Binary files a/web-app/assets/banners/number-guessing.webp and b/web-app/assets/banners/number-guessing.webp differ diff --git a/web-app/assets/banners/number-sliding-puzzle.webp b/web-app/assets/banners/number-sliding-puzzle.webp index 5a956b72..9557aa06 100644 Binary files a/web-app/assets/banners/number-sliding-puzzle.webp and b/web-app/assets/banners/number-sliding-puzzle.webp differ diff --git a/web-app/assets/banners/pascal-triangle.webp b/web-app/assets/banners/pascal-triangle.webp index fe25b2c6..9e3ec08e 100644 Binary files a/web-app/assets/banners/pascal-triangle.webp and b/web-app/assets/banners/pascal-triangle.webp differ diff --git a/web-app/assets/banners/password-forge.webp b/web-app/assets/banners/password-forge.webp index f74f2bd9..0b8d9678 100644 Binary files a/web-app/assets/banners/password-forge.webp and b/web-app/assets/banners/password-forge.webp differ diff --git a/web-app/assets/banners/pathfinding-visualizer.webp b/web-app/assets/banners/pathfinding-visualizer.webp new file mode 100644 index 00000000..d0297f10 Binary files /dev/null and b/web-app/assets/banners/pathfinding-visualizer.webp differ diff --git a/web-app/assets/banners/prime-analyzer.webp b/web-app/assets/banners/prime-analyzer.webp index a8ec2094..ea879f0c 100644 Binary files a/web-app/assets/banners/prime-analyzer.webp and b/web-app/assets/banners/prime-analyzer.webp differ diff --git a/web-app/assets/banners/productive-pet.webp b/web-app/assets/banners/productive-pet.webp index e2be7d1d..8e7295d5 100644 Binary files a/web-app/assets/banners/productive-pet.webp and b/web-app/assets/banners/productive-pet.webp differ diff --git a/web-app/assets/banners/progress-tracker.webp b/web-app/assets/banners/progress-tracker.webp index ac05ef38..a0722e14 100644 Binary files a/web-app/assets/banners/progress-tracker.webp and b/web-app/assets/banners/progress-tracker.webp differ diff --git a/web-app/assets/banners/progression-recognizer.webp b/web-app/assets/banners/progression-recognizer.webp index 858e2173..5b332d11 100644 Binary files a/web-app/assets/banners/progression-recognizer.webp and b/web-app/assets/banners/progression-recognizer.webp differ diff --git a/web-app/assets/banners/projectile-motion.webp b/web-app/assets/banners/projectile-motion.webp index 83f1c064..76e4e686 100644 Binary files a/web-app/assets/banners/projectile-motion.webp and b/web-app/assets/banners/projectile-motion.webp differ diff --git a/web-app/assets/banners/resume-analyzer.webp b/web-app/assets/banners/resume-analyzer.webp index 1f0c8991..b684b1d9 100644 Binary files a/web-app/assets/banners/resume-analyzer.webp and b/web-app/assets/banners/resume-analyzer.webp differ diff --git a/web-app/assets/banners/reverse-hangman.webp b/web-app/assets/banners/reverse-hangman.webp index a8c65802..841df72d 100644 Binary files a/web-app/assets/banners/reverse-hangman.webp and b/web-app/assets/banners/reverse-hangman.webp differ diff --git a/web-app/assets/banners/rock-paper-scissor.webp b/web-app/assets/banners/rock-paper-scissor.webp index e719f910..b22b8fdf 100644 Binary files a/web-app/assets/banners/rock-paper-scissor.webp and b/web-app/assets/banners/rock-paper-scissor.webp differ diff --git a/web-app/assets/banners/simon-says.webp b/web-app/assets/banners/simon-says.webp index 42862065..a2ca0a14 100644 Binary files a/web-app/assets/banners/simon-says.webp and b/web-app/assets/banners/simon-says.webp differ diff --git a/web-app/assets/banners/snake.webp b/web-app/assets/banners/snake.webp index 7b18632d..f49c5114 100644 Binary files a/web-app/assets/banners/snake.webp and b/web-app/assets/banners/snake.webp differ diff --git a/web-app/assets/banners/spot-the-difference.webp b/web-app/assets/banners/spot-the-difference.webp index 1ed15093..8b4a44aa 100644 Binary files a/web-app/assets/banners/spot-the-difference.webp and b/web-app/assets/banners/spot-the-difference.webp differ diff --git a/web-app/assets/banners/tic-tac-toe.webp b/web-app/assets/banners/tic-tac-toe.webp index 0a1dd612..968be42e 100644 Binary files a/web-app/assets/banners/tic-tac-toe.webp and b/web-app/assets/banners/tic-tac-toe.webp differ diff --git a/web-app/assets/banners/tower-of-hanoi.webp b/web-app/assets/banners/tower-of-hanoi.webp index ffb6c64a..0e5d10af 100644 Binary files a/web-app/assets/banners/tower-of-hanoi.webp and b/web-app/assets/banners/tower-of-hanoi.webp differ diff --git a/web-app/assets/banners/typing-speed-tester.webp b/web-app/assets/banners/typing-speed-tester.webp index 8bd7336e..91a3d5db 100644 Binary files a/web-app/assets/banners/typing-speed-tester.webp and b/web-app/assets/banners/typing-speed-tester.webp differ diff --git a/web-app/assets/banners/unit-converter.webp b/web-app/assets/banners/unit-converter.webp index f1369f50..493eb308 100644 Binary files a/web-app/assets/banners/unit-converter.webp and b/web-app/assets/banners/unit-converter.webp differ diff --git a/web-app/assets/banners/war-card-game.webp b/web-app/assets/banners/war-card-game.webp index 4e2d3889..de4920b2 100644 Binary files a/web-app/assets/banners/war-card-game.webp and b/web-app/assets/banners/war-card-game.webp differ diff --git a/web-app/assets/banners/whack-a-mole.webp b/web-app/assets/banners/whack-a-mole.webp index 28456e0b..ff52ef84 100644 Binary files a/web-app/assets/banners/whack-a-mole.webp and b/web-app/assets/banners/whack-a-mole.webp differ diff --git a/web-app/assets/banners/word-scramble.webp b/web-app/assets/banners/word-scramble.webp index 211bddd8..c9eea753 100644 Binary files a/web-app/assets/banners/word-scramble.webp and b/web-app/assets/banners/word-scramble.webp differ diff --git a/web-app/debug.js b/web-app/debug.js new file mode 100644 index 00000000..98d3d622 --- /dev/null +++ b/web-app/debug.js @@ -0,0 +1,34 @@ +const { chromium } = require('@playwright/test'); + +(async () => { + const browser = await chromium.launch({ headless: true }); + const page = await browser.newPage(); + + page.on('console', msg => console.log('BROWSER CONSOLE:', msg.type(), msg.text())); + page.on('pageerror', err => console.log('BROWSER ERROR:', err.message)); + + console.log("Navigating..."); + await page.goto('http://localhost:8080'); + + console.log("Waiting for search bar..."); + await page.waitForSelector('#searchBar'); + await page.fill('#searchBar', 'pathfinding visualizer'); + + console.log("Waiting for project card..."); + await page.waitForSelector('.project-card:has-text("Pathfinding Visualizer")'); + await page.click('.project-card:has-text("Pathfinding Visualizer")'); + + console.log("Waiting for modal..."); + await page.waitForSelector('#pfGrid'); + + const html = await page.locator('#pfGrid').innerHTML(); + console.log("GRID HTML:", html.substring(0, 500)); + + const count = await page.locator('#pfGrid td').count(); + console.log("CELL COUNT:", count); + + const box = await page.locator('#pfGrid').boundingBox(); + console.log("GRID BOUNDING BOX:", box); + + await browser.close(); +})(); diff --git a/web-app/generate_banners.py b/web-app/generate_banners.py index 8085ff87..29532930 100644 --- a/web-app/generate_banners.py +++ b/web-app/generate_banners.py @@ -537,6 +537,23 @@ def draw_o(ox, oy): for i, h in enumerate(bar_heights): x = 230 + i * 50 v_draw.rectangle([x, 320 - h, x + 30, 320], fill=color_accent) + elif "pathfinding" in n_lower or "visualizer" in n_lower: + # A grid with a path being found + cx, cy = 400, 225 + # draw grid base + for r in range(5): + for c in range(8): + x = cx - 160 + c * 40 + y = cy - 100 + r * 40 + v_draw.rectangle([x, y, x + 35, y + 35], fill=(255,255,255,10), outline=color_accent_dim, width=1) + # draw start and end + v_draw.ellipse([cx - 150, cy - 90, cx - 135, cy - 75], fill=(16, 185, 129)) # Start + v_draw.ellipse([cx + 130, cy + 70, cx + 145, cy + 85], fill=(239, 68, 68)) # End + # draw a path + pts = [(cx - 142, cy - 82), (cx - 102, cy - 82), (cx - 102, cy - 42), (cx - 62, cy - 42), (cx - 62, cy - 2), (cx + 18, cy - 2), (cx + 18, cy + 38), (cx + 58, cy + 38), (cx + 58, cy + 78), (cx + 138, cy + 78)] + v_draw.line(pts, fill=color_accent, width=4, joint="round") + # draw a wall + v_draw.rectangle([cx - 20, cy - 100, cx + 15, cy + 15], fill=color_accent) else: # Default nice abstract waves points = [] @@ -633,6 +650,7 @@ def draw_o(ox, oy): ("Caesar Cipher", "utilities", "caesar-cipher.webp"), ("Unit Converter", "utilities", "unit-converter.webp"), ("Budget Tracker", "utilities", "budget-tracker.webp"), + ("Pathfinding Visualizer", "utilities", "pathfinding-visualizer.webp"), ] # Run generation diff --git a/web-app/index.html b/web-app/index.html index d9c80028..72b3ff3e 100644 --- a/web-app/index.html +++ b/web-app/index.html @@ -875,7 +875,8 @@