From 8fc08caec31b6313c8c22f96ba7bb3bf1bcd4d31 Mon Sep 17 00:00:00 2001 From: Ashlin Jose <141202853+Ash-Jose@users.noreply.github.com> Date: Thu, 13 Nov 2025 23:18:22 +0530 Subject: [PATCH 1/3] Fixed trailing whitespace in the name of Java folder Signed-off-by: Ash Jose --- .../MotivationalQuote/MotivationalQuote.java | 0 {Java => Java}/MotivationalQuote/README.md | 0 {Java => Java}/MotivationalQuote/quotes.txt | 0 .../RandomPasswordGenerator in java/Images/.png | 0 .../RandomPasswordGenerator.java | 0 {Java => Java}/Readme.md | 0 {Java => Java}/Resizerr/BatchResizeImages.java | 0 {Java => Java}/Resizerr/Readme.md | 0 {Java => Java}/Resizerr/input/164948.jpg | Bin .../Resizerr/input/BatchResizeImages.java | 0 .../input/luffy-vs-kaido-one-5120x2880-18361.jpg | Bin {Java => Java}/Resizerr/resize_images.py | 0 {Java => Java}/Text summarizer/Readme.MD | 0 .../Screenshot 2025-10-07 042010.png | Bin .../Text summarizer/Text summarizer.java | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename {Java => Java}/MotivationalQuote/MotivationalQuote.java (100%) rename {Java => Java}/MotivationalQuote/README.md (100%) rename {Java => Java}/MotivationalQuote/quotes.txt (100%) rename {Java => Java}/RandomPasswordGenerator in java/Images/.png (100%) rename {Java => Java}/RandomPasswordGenerator in java/RandomPasswordGenerator.java (100%) rename {Java => Java}/Readme.md (100%) rename {Java => Java}/Resizerr/BatchResizeImages.java (100%) rename {Java => Java}/Resizerr/Readme.md (100%) rename {Java => Java}/Resizerr/input/164948.jpg (100%) rename {Java => Java}/Resizerr/input/BatchResizeImages.java (100%) rename {Java => Java}/Resizerr/input/luffy-vs-kaido-one-5120x2880-18361.jpg (100%) rename {Java => Java}/Resizerr/resize_images.py (100%) rename {Java => Java}/Text summarizer/Readme.MD (100%) rename {Java => Java}/Text summarizer/Screenshot 2025-10-07 042010.png (100%) rename {Java => Java}/Text summarizer/Text summarizer.java (100%) diff --git a/Java /MotivationalQuote/MotivationalQuote.java b/Java/MotivationalQuote/MotivationalQuote.java similarity index 100% rename from Java /MotivationalQuote/MotivationalQuote.java rename to Java/MotivationalQuote/MotivationalQuote.java diff --git a/Java /MotivationalQuote/README.md b/Java/MotivationalQuote/README.md similarity index 100% rename from Java /MotivationalQuote/README.md rename to Java/MotivationalQuote/README.md diff --git a/Java /MotivationalQuote/quotes.txt b/Java/MotivationalQuote/quotes.txt similarity index 100% rename from Java /MotivationalQuote/quotes.txt rename to Java/MotivationalQuote/quotes.txt diff --git a/Java /RandomPasswordGenerator in java/Images/.png b/Java/RandomPasswordGenerator in java/Images/.png similarity index 100% rename from Java /RandomPasswordGenerator in java/Images/.png rename to Java/RandomPasswordGenerator in java/Images/.png diff --git a/Java /RandomPasswordGenerator in java/RandomPasswordGenerator.java b/Java/RandomPasswordGenerator in java/RandomPasswordGenerator.java similarity index 100% rename from Java /RandomPasswordGenerator in java/RandomPasswordGenerator.java rename to Java/RandomPasswordGenerator in java/RandomPasswordGenerator.java diff --git a/Java /Readme.md b/Java/Readme.md similarity index 100% rename from Java /Readme.md rename to Java/Readme.md diff --git a/Java /Resizerr/BatchResizeImages.java b/Java/Resizerr/BatchResizeImages.java similarity index 100% rename from Java /Resizerr/BatchResizeImages.java rename to Java/Resizerr/BatchResizeImages.java diff --git a/Java /Resizerr/Readme.md b/Java/Resizerr/Readme.md similarity index 100% rename from Java /Resizerr/Readme.md rename to Java/Resizerr/Readme.md diff --git a/Java /Resizerr/input/164948.jpg b/Java/Resizerr/input/164948.jpg similarity index 100% rename from Java /Resizerr/input/164948.jpg rename to Java/Resizerr/input/164948.jpg diff --git a/Java /Resizerr/input/BatchResizeImages.java b/Java/Resizerr/input/BatchResizeImages.java similarity index 100% rename from Java /Resizerr/input/BatchResizeImages.java rename to Java/Resizerr/input/BatchResizeImages.java diff --git a/Java /Resizerr/input/luffy-vs-kaido-one-5120x2880-18361.jpg b/Java/Resizerr/input/luffy-vs-kaido-one-5120x2880-18361.jpg similarity index 100% rename from Java /Resizerr/input/luffy-vs-kaido-one-5120x2880-18361.jpg rename to Java/Resizerr/input/luffy-vs-kaido-one-5120x2880-18361.jpg diff --git a/Java /Resizerr/resize_images.py b/Java/Resizerr/resize_images.py similarity index 100% rename from Java /Resizerr/resize_images.py rename to Java/Resizerr/resize_images.py diff --git a/Java /Text summarizer/Readme.MD b/Java/Text summarizer/Readme.MD similarity index 100% rename from Java /Text summarizer/Readme.MD rename to Java/Text summarizer/Readme.MD diff --git a/Java /Text summarizer/Screenshot 2025-10-07 042010.png b/Java/Text summarizer/Screenshot 2025-10-07 042010.png similarity index 100% rename from Java /Text summarizer/Screenshot 2025-10-07 042010.png rename to Java/Text summarizer/Screenshot 2025-10-07 042010.png diff --git a/Java /Text summarizer/Text summarizer.java b/Java/Text summarizer/Text summarizer.java similarity index 100% rename from Java /Text summarizer/Text summarizer.java rename to Java/Text summarizer/Text summarizer.java From ee32f35267d52f8b6cc406400e4cc67700931262 Mon Sep 17 00:00:00 2001 From: Ash-Jose Date: Fri, 14 Nov 2025 05:30:57 +0530 Subject: [PATCH 2/3] Added the feature as proposed in issue #105 Signed-off-by: Ash-Jose --- C++/pathfinding_visualizer/.gitignore | 77 +++++++ C++/pathfinding_visualizer/CMakeLists.txt | 32 +++ C++/pathfinding_visualizer/README.md | 161 +++++++++++++ .../include/Algorithms/AlgorithmWorker.hpp | 34 +++ C++/pathfinding_visualizer/include/Grid.hpp | 51 +++++ .../include/MainWindow.hpp | 52 +++++ C++/pathfinding_visualizer/include/Node.hpp | 33 +++ .../src/Algorithms/AlgorithmWorker.cpp | 212 ++++++++++++++++++ C++/pathfinding_visualizer/src/Grid.cpp | 131 +++++++++++ C++/pathfinding_visualizer/src/MainWindow.cpp | 184 +++++++++++++++ C++/pathfinding_visualizer/src/Node.cpp | 54 +++++ C++/pathfinding_visualizer/src/main.cpp | 19 ++ C++/pathfinding_visualizer/ui/MainWindow.ui | 22 ++ .../random_joke_generator.py | 43 ---- .../random_joke_generator.py | 6 +- 15 files changed, 1064 insertions(+), 47 deletions(-) create mode 100644 C++/pathfinding_visualizer/.gitignore create mode 100644 C++/pathfinding_visualizer/CMakeLists.txt create mode 100644 C++/pathfinding_visualizer/README.md create mode 100644 C++/pathfinding_visualizer/include/Algorithms/AlgorithmWorker.hpp create mode 100644 C++/pathfinding_visualizer/include/Grid.hpp create mode 100644 C++/pathfinding_visualizer/include/MainWindow.hpp create mode 100644 C++/pathfinding_visualizer/include/Node.hpp create mode 100644 C++/pathfinding_visualizer/src/Algorithms/AlgorithmWorker.cpp create mode 100644 C++/pathfinding_visualizer/src/Grid.cpp create mode 100644 C++/pathfinding_visualizer/src/MainWindow.cpp create mode 100644 C++/pathfinding_visualizer/src/Node.cpp create mode 100644 C++/pathfinding_visualizer/src/main.cpp create mode 100644 C++/pathfinding_visualizer/ui/MainWindow.ui delete mode 100644 Python/Random_joke_generator/random_joke_generator.py diff --git a/C++/pathfinding_visualizer/.gitignore b/C++/pathfinding_visualizer/.gitignore new file mode 100644 index 0000000..27cf088 --- /dev/null +++ b/C++/pathfinding_visualizer/.gitignore @@ -0,0 +1,77 @@ +# ---------------------------- +# Build folders +# ---------------------------- +build/ +build-*/ +out/ +bin/ +obj/ + +# Generated Qt / CMake files +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +CTestTestfile.cmake +Makefile +*.moc +*.qrc.dep +*.qm +*.qmake.stash +*.qmake.cache +*.qtc_settings +*.qtc_clangd +.qm +*_automoc.cpp + +# Qt Creator project files +*.user +*.pro.user +*.qmlproject.user +*.tags +*.swp +*.autosave +*.creator +*.creator.user + +# VS Code +.vscode/ +.vscode-ipch/ +*.code-workspace + +# CLion / JetBrains +.idea/ +cmake-build-*/ + +# Windows system files +Thumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# Logs +*.log + +# Temporary files +*.tmp +*.temp +*.bak +*~ +*.cache +*.orig + +# Compiled object files & artifacts +*.o +*.obj +*.dll +*.exe +*.pdb +*.lib +*.a +*.so +*.dylib + +# Python virtual envs (if used in repo) +venv/ +.env/ + +# Ignore test output +test_output/ diff --git a/C++/pathfinding_visualizer/CMakeLists.txt b/C++/pathfinding_visualizer/CMakeLists.txt new file mode 100644 index 0000000..1fede5d --- /dev/null +++ b/C++/pathfinding_visualizer/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.21) +project(pathfinding_visualizer LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) + +# Enable Qt automatic processing +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +find_package(Qt6 REQUIRED COMPONENTS Widgets Core Gui) + +qt_add_executable(pathfinding_visualizer + src/main.cpp + src/MainWindow.cpp + src/Grid.cpp + src/Node.cpp + src/Algorithms/AlgorithmWorker.cpp + + # Headers (needed for AUTOMOC) + include/MainWindow.hpp + include/Grid.hpp + include/Node.hpp + include/Algorithms/AlgorithmWorker.hpp + + # UI + Resources + ui/MainWindow.ui +) + +target_include_directories(pathfinding_visualizer PRIVATE include) + +target_link_libraries(pathfinding_visualizer PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui) diff --git a/C++/pathfinding_visualizer/README.md b/C++/pathfinding_visualizer/README.md new file mode 100644 index 0000000..cec74ec --- /dev/null +++ b/C++/pathfinding_visualizer/README.md @@ -0,0 +1,161 @@ +# Pathfinding Visualizer (Code_Script Module) + +A GUI-based C++17 + Qt6 pathfinding animation tool that visually demonstrates +**BFS**, **Dijkstra**, and **A\*** exploring a 2D grid in real time. +This module is intended for educational use and integration into the Code_Script project. + +--- + +## 📄 Small Description + +The Pathfinding Visualizer is an interactive Qt6 application that displays how classical pathfinding algorithms traverse a grid to find a path between two points. Users can place walls, change algorithms, adjust animation speed, and observe the search progress and final path. + +--- + +## 🎯 Goals + +- Visually demonstrate how BFS, Dijkstra, and A\* explore and find shortest paths. +- Provide an interactive 2D grid where users can toggle walls and set start/target nodes. +- Animate algorithm steps in real-time (visited nodes + final path). +- Maintain a thread-safe architecture using a background worker thread for algorithms. +- Offer adjustable animation speed and algorithm selection via a toolbar UI. +- Serve as a self-contained Code_Script module. + +--- + +## 📁 File Structure + +``` +pathfinding_visualizer/ +│ +├── CMakeLists.txt +├── README.md +├── .gitignore +│ +├── include/ +│ ├── MainWindow.hpp +│ ├── Grid.hpp +│ ├── Node.hpp +│ └── Algorithms/ +│ └── AlgorithmWorker.hpp +│ +├── src/ +│ ├── main.cpp +│ ├── MainWindow.cpp +│ ├── Grid.cpp +│ ├── Node.cpp +│ └── Algorithms/ +│ └── AlgorithmWorker.cpp +│ +├── ui/ +│ └── MainWindow.ui +``` + +> Icons are SVG files, recommended from **Lucide Icons** (MIT licensed). +> Place all SVGs in `ui/icons/` exactly matching the filenames above. + +--- + +## 🛠️ Prerequisites + +### Software Requirements +- **Qt 6.9.3** (or later) + - MinGW 64-bit or MSVC 64-bit build tools installed via Qt Online Installer. +- **CMake 3.16+** +- **C++17 compatible compiler** + - MSYS2 MinGW-w64 (preferred for simplicity) + - or Microsoft Visual C++ (MSVC) +- **VS Code** (recommended) with: + - *CMake Tools* extension + - *C/C++* extension + +### Optional +- Qt Creator (IDE) +- Git (if contributing back to Code_Script) + +--- + +## ▶️ Usage Instructions + +### Running the Application +After building the project, launch: + +./build/pathfinding_visualizer.exe + +This opens the main window containing the 2D grid and the control toolbar. + +--- + +### Grid Interaction + +- **Left Click** on a cell → Toggle Wall (White ↔ Black) +- **Right Click** on a cell → Set Start Node (Green) +- **Shift + Left Click** or **Middle Click** → Set Target Node (Red) + +The grid updates visually using `Node` objects in a `QGraphicsScene`. + +--- + +### Toolbar Controls + +- **Run** (`play.svg`) + Starts the selected algorithm on a background thread (`AlgorithmWorker`). + +- **Reset** (`reset.svg`) + Clears all walls, visited cells, and path markings. Start/Target nodes return to defaults. + +- **Algorithm Selector** + Choose between **BFS**, **Dijkstra**, or **A\***. + +- **Speed Slider** + Controls animation delay (ms per step). + Lower value = faster, higher = slower. + +--- + +### Visualization Colors +- **Start Node** → Green +- **Target Node** → Red +- **Walls** → Black +- **Visited Cells** → Light Blue +- **Final Path** → Yellow + +Updates are triggered by worker-thread signals: +`visit(row,col)` and `pathNode(row,col)`. + +--- + +## 🔧 Build Instructions (Windows — Qt 6.9.3) + +### Requirements +- Qt **6.9.3** + - Either **MinGW 64-bit** or **MSVC 2022 64-bit** Qt build installed +- CMake **3.16+** +- A C++17 compiler +- Optional: VS Code with **CMake Tools** extension + +--- + +### 1️⃣ Configure (MinGW Example) + +cmake -B build -S . -G "MinGW Makefiles" ^ +-DCMAKE_PREFIX_PATH="C:/Qt/6.9.3/mingw_64/lib/cmake" + +### 2️⃣ Build (MinGW) +cmake --build build + +--- + +### 1️⃣ Configure (MSVC Example) + +cmake -B build -S . -G "NMake Makefiles" ` +-DCMAKE_PREFIX_PATH="C:/Qt/6.9.3/msvc2022_64/lib/cmake" + +### 2️⃣ Build (MSVC) +cmake --build build --config Release + +--- + +### 3️⃣ Run + +./build/pathfinding_visualizer.exe diff --git a/C++/pathfinding_visualizer/include/Algorithms/AlgorithmWorker.hpp b/C++/pathfinding_visualizer/include/Algorithms/AlgorithmWorker.hpp new file mode 100644 index 0000000..905ee52 --- /dev/null +++ b/C++/pathfinding_visualizer/include/Algorithms/AlgorithmWorker.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +class AlgorithmWorker : public QObject { + Q_OBJECT +public: + explicit AlgorithmWorker(QObject *parent = nullptr); + ~AlgorithmWorker() override; + +public slots: + void runBFS(const QVector> &grid, const QPoint &start, const QPoint &target, int delayMs); + void runDijkstra(const QVector> &grid, const QPoint &start, const QPoint &target, int delayMs); + void runAStar(const QVector> &grid, const QPoint &start, const QPoint &target, int delayMs); + + void requestAbort(); + +signals: + void visit(int row, int col); + void pathNode(int row, int col); + void status(const QString &msg); + void finished(); + +private: + volatile bool m_abortRequested; + + void sleepMs(int ms) const; + + static inline int manhattan(int r1, int c1, int r2, int c2) { + return qAbs(r1 - r2) + qAbs(c1 - c2); + } +}; diff --git a/C++/pathfinding_visualizer/include/Grid.hpp b/C++/pathfinding_visualizer/include/Grid.hpp new file mode 100644 index 0000000..2529942 --- /dev/null +++ b/C++/pathfinding_visualizer/include/Grid.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +/** + * Grid manages QGraphicsScene and Node items. + * It also handles mouse interactions by installing an event filter on the scene. + * exportModel() returns a copy (QVector) safe to send across threads. + */ +class Node; +class Grid : public QObject { + Q_OBJECT +public: + struct Model { + QVector> grid; // 0 = free, 1 = wall + QPoint start; + QPoint target; + }; + + explicit Grid(int rows, int cols, QObject *parent = nullptr); + ~Grid() override; + + QGraphicsScene* scene() const { return m_scene; } + + Model exportModel() const; + + // Called from GUI thread (slots) + void markVisited(int r, int c); + void markPath(int r, int c); + void reset(); + +protected: + // eventFilter to capture mouse clicks on the scene and translate to grid actions + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + void toggleWallAtScenePos(const QPointF &scenePos); + void setStartAtScenePos(const QPointF &scenePos); + void setTargetAtScenePos(const QPointF &scenePos); + + int m_rows; + int m_cols; + QGraphicsScene *m_scene; + QVector> m_nodes; + + QPoint m_start; + QPoint m_target; +}; diff --git a/C++/pathfinding_visualizer/include/MainWindow.hpp b/C++/pathfinding_visualizer/include/MainWindow.hpp new file mode 100644 index 0000000..58ac82d --- /dev/null +++ b/C++/pathfinding_visualizer/include/MainWindow.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +class Grid; +class AlgorithmWorker; +class QComboBox; +class QSlider; +class QLabel; +class QAction; + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow() override; + +protected: + void keyPressEvent(QKeyEvent *event) override; + +private slots: + void onRun(); + void onReset(); + void onSpeedChanged(int value); + void onAlgoChanged(const QString &name); + + // Slots to receive worker signals (executed in GUI thread) + void handleVisit(int row, int col); + void handlePathNode(int row, int col); + void handleWorkerFinished(); + void handleStatus(const QString &text); + +private: + void createToolbar(); + void startAlgorithmOnWorker(); + + Grid *m_grid; + AlgorithmWorker *m_worker; + QThread *m_workerThread; + + QAction *m_runAction; + QAction *m_resetAction; + QComboBox *m_algoSelector; + QSlider *m_speedSlider; + QLabel *m_statusLabel; + + QString m_currentAlgo; + int m_speedMs; + bool m_isRunning; +}; diff --git a/C++/pathfinding_visualizer/include/Node.hpp b/C++/pathfinding_visualizer/include/Node.hpp new file mode 100644 index 0000000..4c95b8f --- /dev/null +++ b/C++/pathfinding_visualizer/include/Node.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +/** + * Node is a QGraphicsRectItem representing one grid cell. + * It stores row/col indices and visual state. + * + * Note: Node methods must be called from the GUI thread. + */ +class Node : public QGraphicsRectItem { +public: + Node(int row = 0, int col = 0); + + void setVisited(bool v); + void setPath(bool p); + void setWall(bool w); + void reset(); + + void setAsStart(); + void setAsTarget(); + + bool isWall() const { return m_wall; } + int row() const { return m_row; } + int col() const { return m_col; } + +private: + int m_row; + int m_col; + bool m_visited; + bool m_path; + bool m_wall; +}; diff --git a/C++/pathfinding_visualizer/src/Algorithms/AlgorithmWorker.cpp b/C++/pathfinding_visualizer/src/Algorithms/AlgorithmWorker.cpp new file mode 100644 index 0000000..d4f89b1 --- /dev/null +++ b/C++/pathfinding_visualizer/src/Algorithms/AlgorithmWorker.cpp @@ -0,0 +1,212 @@ +#include "Algorithms/AlgorithmWorker.hpp" +#include +#include +#include +#include + +AlgorithmWorker::AlgorithmWorker(QObject *parent) + : QObject(parent), m_abortRequested(false) +{} + +AlgorithmWorker::~AlgorithmWorker() {} + +void AlgorithmWorker::requestAbort() { + m_abortRequested = true; +} + +void AlgorithmWorker::sleepMs(int ms) const { + // Sleep in worker thread + QThread::msleep(static_cast(ms)); +} + +/** + * BFS implementation on a grid represented by QVector> + * 0 = free, 1 = wall + */ +void AlgorithmWorker::runBFS(const QVector> &grid, const QPoint &start, const QPoint &target, int delayMs) { + m_abortRequested = false; + int rows = grid.size(); + if (rows == 0) { emit finished(); return; } + int cols = grid[0].size(); + + std::queue q; + QVector> seen(rows, QVector(cols, false)); + QVector> parent(rows, QVector(cols, QPoint(-1, -1))); + + q.push(start); + seen[start.x()][start.y()] = true; + + const int dr[4] = {1,-1,0,0}; + const int dc[4] = {0,0,1,-1}; + + while (!q.empty() && !m_abortRequested) { + QPoint cur = q.front(); q.pop(); + emit visit(cur.x(), cur.y()); + sleepMs(delayMs); + + if (cur == target) break; + + for (int i = 0; i < 4; ++i) { + int nr = cur.x() + dr[i]; + int nc = cur.y() + dc[i]; + if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue; + if (grid[nr][nc] == 1) continue; + if (seen[nr][nc]) continue; + seen[nr][nc] = true; + parent[nr][nc] = cur; + q.push(QPoint(nr, nc)); + } + } + + if (m_abortRequested) { emit status("Aborted"); emit finished(); return; } + + if (!seen[target.x()][target.y()]) { + emit status("No path found"); + emit finished(); + return; + } + + // Reconstruct path + QVector path; + for (QPoint at = target; at != QPoint(-1, -1); at = parent[at.x()][at.y()]) + path.push_back(at); + std::reverse(path.begin(), path.end()); + for (const QPoint &p : path) { + emit pathNode(p.x(), p.y()); + sleepMs(delayMs); + } + emit finished(); +} + +/** + * Dijkstra (uniform weights for now; ready to accept weights if grid uses >1 values) + */ +void AlgorithmWorker::runDijkstra(const QVector> &grid, const QPoint &start, const QPoint &target, int delayMs) { + m_abortRequested = false; + int rows = grid.size(); + if (rows == 0) { emit finished(); return; } + int cols = grid[0].size(); + + const int INF = std::numeric_limits::max() / 4; + QVector> dist(rows, QVector(cols, INF)); + QVector> parent(rows, QVector(cols, QPoint(-1,-1))); + + using NodeT = std::pair; + auto cmp = [](const NodeT &a, const NodeT &b){ return a.first > b.first; }; + std::priority_queue, decltype(cmp)> pq(cmp); + + dist[start.x()][start.y()] = 0; + pq.push({0, start}); + + const int dr[4] = {1,-1,0,0}; + const int dc[4] = {0,0,1,-1}; + + while (!pq.empty() && !m_abortRequested) { + auto [d, pos] = pq.top(); pq.pop(); + int r = pos.x(), c = pos.y(); + if (d != dist[r][c]) continue; + + emit visit(r, c); + sleepMs(delayMs); + + if (pos == target) break; + + for (int i=0;i<4;++i) { + int nr = r + dr[i], nc = c + dc[i]; + if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue; + if (grid[nr][nc] == 1) continue; + int w = 1; // default weight; if grid[r][c] > 1 treat as weight + int nd = d + w; + if (nd < dist[nr][nc]) { + dist[nr][nc] = nd; + parent[nr][nc] = pos; + pq.push({nd, QPoint(nr,nc)}); + } + } + } + + if (m_abortRequested) { emit status("Aborted"); emit finished(); return; } + + if (dist[target.x()][target.y()] == INF) { + emit status("No path found"); + emit finished(); + return; + } + + QVector path; + for (QPoint at = target; at != QPoint(-1,-1); at = parent[at.x()][at.y()]) + path.push_back(at); + std::reverse(path.begin(), path.end()); + for (const QPoint &p : path) { + emit pathNode(p.x(), p.y()); + sleepMs(delayMs); + } + emit finished(); +} + +/** + * A* with Manhattan heuristic + */ +void AlgorithmWorker::runAStar(const QVector> &grid, const QPoint &start, const QPoint &target, int delayMs) { + m_abortRequested = false; + int rows = grid.size(); + if (rows == 0) { emit finished(); return; } + int cols = grid[0].size(); + + const int INF = std::numeric_limits::max() / 4; + QVector> gscore(rows, QVector(cols, INF)); + QVector> parent(rows, QVector(cols, QPoint(-1,-1))); + + using NodeT = std::pair; // fscore, pos + auto cmp = [](const NodeT &a, const NodeT &b){ return a.first > b.first; }; + std::priority_queue, decltype(cmp)> open(cmp); + + gscore[start.x()][start.y()] = 0; + int f0 = manhattan(start.x(), start.y(), target.x(), target.y()); + open.push({f0, start}); + + const int dr[4] = {1,-1,0,0}; + const int dc[4] = {0,0,1,-1}; + + while (!open.empty() && !m_abortRequested) { + auto [f, pos] = open.top(); open.pop(); + int r = pos.x(), c = pos.y(); + + emit visit(r, c); + sleepMs(delayMs); + + if (pos == target) break; + + for (int i=0;i<4;++i) { + int nr = r + dr[i], nc = c + dc[i]; + if (nr < 0 || nr >= rows || nc < 0 || nc >= cols) continue; + if (grid[nr][nc] == 1) continue; + int tentative = gscore[r][c] + 1; + if (tentative < gscore[nr][nc]) { + parent[nr][nc] = pos; + gscore[nr][nc] = tentative; + int h = manhattan(nr, nc, target.x(), target.y()); + int nf = tentative + h; + open.push({nf, QPoint(nr,nc)}); + } + } + } + + if (m_abortRequested) { emit status("Aborted"); emit finished(); return; } + + if (gscore[target.x()][target.y()] == INF) { + emit status("No path found"); + emit finished(); + return; + } + + QVector path; + for (QPoint at = target; at != QPoint(-1,-1); at = parent[at.x()][at.y()]) + path.push_back(at); + std::reverse(path.begin(), path.end()); + for (const QPoint &p : path) { + emit pathNode(p.x(), p.y()); + sleepMs(delayMs); + } + emit finished(); +} diff --git a/C++/pathfinding_visualizer/src/Grid.cpp b/C++/pathfinding_visualizer/src/Grid.cpp new file mode 100644 index 0000000..f750d75 --- /dev/null +++ b/C++/pathfinding_visualizer/src/Grid.cpp @@ -0,0 +1,131 @@ +#include "Grid.hpp" +#include "Node.hpp" + +#include +#include +#include +#include + +Grid::Grid(int rows, int cols, QObject *parent) + : QObject(parent), m_rows(rows), m_cols(cols), m_scene(new QGraphicsScene(this)) +{ + const int cellSize = 22; + m_nodes.resize(m_rows); + for (int r = 0; r < m_rows; ++r) { + m_nodes[r].resize(m_cols); + for (int c = 0; c < m_cols; ++c) { + Node *n = new Node(r, c); + n->setRect(0,0,cellSize - 1, cellSize - 1); + n->setPos(c * cellSize, r * cellSize); + m_scene->addItem(n); + m_nodes[r][c] = n; + } + } + + m_start = QPoint(0, 0); + m_target = QPoint(m_rows - 1, m_cols - 1); + // mark start/target visuals + m_nodes[m_start.x()][m_start.y()]->setAsStart(); + m_nodes[m_target.x()][m_target.y()]->setAsTarget(); + + // Install this object as an event filter on the scene so we can handle clicks + m_scene->installEventFilter(this); +} + +Grid::~Grid() { + // QGraphicsScene will delete items when destroyed; nothing to free here +} + +Grid::Model Grid::exportModel() const { + Model m; + m.grid = QVector>(m_rows, QVector(m_cols, 0)); + for (int r = 0; r < m_rows; ++r) + for (int c = 0; c < m_cols; ++c) + m.grid[r][c] = m_nodes[r][c]->isWall() ? 1 : 0; + m.start = m_start; + m.target = m_target; + return m; +} + +void Grid::markVisited(int r, int c) { + if (r < 0 || r >= m_rows || c < 0 || c >= m_cols) return; + m_nodes[r][c]->setVisited(true); +} + +void Grid::markPath(int r, int c) { + if (r < 0 || r >= m_rows || c < 0 || c >= m_cols) return; + m_nodes[r][c]->setPath(true); +} + +void Grid::reset() { + for (int r = 0; r < m_rows; ++r) + for (int c = 0; c < m_cols; ++c) + m_nodes[r][c]->reset(); + + // re-mark start/target + m_nodes[m_start.x()][m_start.y()]->setAsStart(); + m_nodes[m_target.x()][m_target.y()]->setAsTarget(); +} + +/** + * eventFilter intercepts scene mouse press events and delegates to handlers. + * Left click: toggle wall + * Right click: set start + * Middle click or Shift+Left: set target + */ +bool Grid::eventFilter(QObject *watched, QEvent *event) { + if (watched == m_scene && event->type() == QEvent::GraphicsSceneMousePress) { + auto *mouseEvent = static_cast(event); + QPointF scenePos = mouseEvent->scenePos(); + if (mouseEvent->button() == Qt::LeftButton && !(mouseEvent->modifiers() & Qt::ShiftModifier)) { + toggleWallAtScenePos(scenePos); + return true; + } else if (mouseEvent->button() == Qt::RightButton) { + setStartAtScenePos(scenePos); + return true; + } else if (mouseEvent->button() == Qt::MiddleButton || (mouseEvent->button() == Qt::LeftButton && (mouseEvent->modifiers() & Qt::ShiftModifier))) { + setTargetAtScenePos(scenePos); + return true; + } + } + return QObject::eventFilter(watched, event); +} + +static QPointF roundToCellTopLeft(const QPointF &pos, int cellSize) { + int c = int(pos.x()) / cellSize; + int r = int(pos.y()) / cellSize; + return QPointF(c * cellSize, r * cellSize); +} + +void Grid::toggleWallAtScenePos(const QPointF &scenePos) { + const int cellSize = 22; + int c = int(scenePos.x()) / cellSize; + int r = int(scenePos.y()) / cellSize; + if (r < 0 || r >= m_rows || c < 0 || c >= m_cols) return; + Node *n = m_nodes[r][c]; + // don't allow changing start/target into walls + if (QPoint(r, c) == m_start || QPoint(r, c) == m_target) return; + n->setWall(!n->isWall()); +} + +void Grid::setStartAtScenePos(const QPointF &scenePos) { + const int cellSize = 22; + int c = int(scenePos.x()) / cellSize; + int r = int(scenePos.y()) / cellSize; + if (r < 0 || r >= m_rows || c < 0 || c >= m_cols) return; + // clear old start + m_nodes[m_start.x()][m_start.y()]->reset(); + m_start = QPoint(r, c); + m_nodes[m_start.x()][m_start.y()]->setAsStart(); +} + +void Grid::setTargetAtScenePos(const QPointF &scenePos) { + const int cellSize = 22; + int c = int(scenePos.x()) / cellSize; + int r = int(scenePos.y()) / cellSize; + if (r < 0 || r >= m_rows || c < 0 || c >= m_cols) return; + // clear old target + m_nodes[m_target.x()][m_target.y()]->reset(); + m_target = QPoint(r, c); + m_nodes[m_target.x()][m_target.y()]->setAsTarget(); +} diff --git a/C++/pathfinding_visualizer/src/MainWindow.cpp b/C++/pathfinding_visualizer/src/MainWindow.cpp new file mode 100644 index 0000000..fd3cb28 --- /dev/null +++ b/C++/pathfinding_visualizer/src/MainWindow.cpp @@ -0,0 +1,184 @@ +#include "MainWindow.hpp" +#include "Grid.hpp" +#include "Algorithms/AlgorithmWorker.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), + m_grid(nullptr), + m_worker(nullptr), + m_workerThread(nullptr), + m_runAction(nullptr), + m_resetAction(nullptr), + m_algoSelector(nullptr), + m_speedSlider(nullptr), + m_statusLabel(nullptr), + m_currentAlgo("A*"), + m_speedMs(40), + m_isRunning(false) +{ + setWindowTitle("Pathfinding Visualizer - Code_Script"); + resize(1280, 720); + + m_grid = new Grid(30, 50, this); + QGraphicsView *view = new QGraphicsView(m_grid->scene(), this); + view->setRenderHint(QPainter::Antialiasing); + setCentralWidget(view); + + createToolbar(); + m_statusLabel = new QLabel("Ready", this); + statusBar()->addWidget(m_statusLabel); + + // Worker and thread + m_worker = new AlgorithmWorker(); + m_workerThread = new QThread(this); + m_worker->moveToThread(m_workerThread); + m_workerThread->start(); + + // Connect worker signals -> main window slots + connect(m_worker, &AlgorithmWorker::visit, this, &MainWindow::handleVisit); + connect(m_worker, &AlgorithmWorker::pathNode, this, &MainWindow::handlePathNode); + connect(m_worker, &AlgorithmWorker::status, this, &MainWindow::handleStatus); + connect(m_worker, &AlgorithmWorker::finished, this, &MainWindow::handleWorkerFinished); + + // Ensure thread quits when window destroyed + connect(this, &QObject::destroyed, [this]() { + if (m_worker) m_worker->requestAbort(); + if (m_workerThread && m_workerThread->isRunning()) { + m_workerThread->quit(); + m_workerThread->wait(); + } + }); +} + +MainWindow::~MainWindow() { + if (m_worker) { + m_worker->requestAbort(); + } + if (m_workerThread && m_workerThread->isRunning()) { + m_workerThread->quit(); + m_workerThread->wait(); + } + delete m_worker; +} + +void MainWindow::createToolbar() { + QToolBar *toolbar = addToolBar("Controls"); + toolbar->setMovable(false); + + m_runAction = toolbar->addAction("Run"); + m_resetAction = toolbar->addAction("Reset"); + + m_algoSelector = new QComboBox(this); + m_algoSelector->addItems({"BFS", "Dijkstra", "A*"}); + toolbar->addWidget(m_algoSelector); + + m_speedSlider = new QSlider(Qt::Horizontal, this); + m_speedSlider->setRange(5, 300); + m_speedSlider->setValue(m_speedMs); + m_speedSlider->setFixedWidth(200); + toolbar->addWidget(m_speedSlider); + + connect(m_runAction, &QAction::triggered, this, &MainWindow::onRun); + connect(m_resetAction, &QAction::triggered, this, &MainWindow::onReset); + connect(m_speedSlider, &QSlider::valueChanged, this, &MainWindow::onSpeedChanged); + connect(m_algoSelector, &QComboBox::currentTextChanged, this, &MainWindow::onAlgoChanged); +} + +void MainWindow::onRun() { + if (m_isRunning) { + // Request abort if already running + m_worker->requestAbort(); + m_statusLabel->setText("Abort requested..."); + return; + } + m_statusLabel->setText("Preparing..."); + startAlgorithmOnWorker(); +} + +void MainWindow::startAlgorithmOnWorker() { + auto model = m_grid->exportModel(); // model.grid is QVector>, start/target are QPoint + // Call the appropriate worker slot via queued connection + if (m_currentAlgo == "BFS") { + QMetaObject::invokeMethod(m_worker, "runBFS", Qt::QueuedConnection, + Q_ARG(QVector>, model.grid), + Q_ARG(QPoint, model.start), + Q_ARG(QPoint, model.target), + Q_ARG(int, m_speedMs)); + } else if (m_currentAlgo == "Dijkstra") { + QMetaObject::invokeMethod(m_worker, "runDijkstra", Qt::QueuedConnection, + Q_ARG(QVector>, model.grid), + Q_ARG(QPoint, model.start), + Q_ARG(QPoint, model.target), + Q_ARG(int, m_speedMs)); + } else { + QMetaObject::invokeMethod(m_worker, "runAStar", Qt::QueuedConnection, + Q_ARG(QVector>, model.grid), + Q_ARG(QPoint, model.start), + Q_ARG(QPoint, model.target), + Q_ARG(int, m_speedMs)); + } + m_isRunning = true; + m_statusLabel->setText("Running " + m_currentAlgo); +} + +void MainWindow::onReset() { + m_worker->requestAbort(); + m_grid->reset(); + m_statusLabel->setText("Grid reset"); + m_isRunning = false; +} + +void MainWindow::onSpeedChanged(int value) { + m_speedMs = value; + m_statusLabel->setText(QString("Speed: %1 ms").arg(m_speedMs)); +} + +void MainWindow::onAlgoChanged(const QString &name) { + m_currentAlgo = name; + m_statusLabel->setText("Algorithm: " + name); +} + +void MainWindow::handleVisit(int row, int col) { + m_grid->markVisited(row, col); +} + +void MainWindow::handlePathNode(int row, int col) { + m_grid->markPath(row, col); +} + +void MainWindow::handleWorkerFinished() { + m_isRunning = false; + m_statusLabel->setText("Finished"); +} + +void MainWindow::handleStatus(const QString &text) { + m_statusLabel->setText(text); +} + +void MainWindow::keyPressEvent(QKeyEvent *event) { + if (!event) return; + if (event->key() == Qt::Key_Space) { + onRun(); + } else if (event->key() == Qt::Key_R) { + onReset(); + } else if (event->key() == Qt::Key_B) { + m_algoSelector->setCurrentText("BFS"); + } else if (event->key() == Qt::Key_D) { + m_algoSelector->setCurrentText("Dijkstra"); + } else if (event->key() == Qt::Key_A) { + m_algoSelector->setCurrentText("A*"); + } else { + QMainWindow::keyPressEvent(event); + } +} diff --git a/C++/pathfinding_visualizer/src/Node.cpp b/C++/pathfinding_visualizer/src/Node.cpp new file mode 100644 index 0000000..9b285d8 --- /dev/null +++ b/C++/pathfinding_visualizer/src/Node.cpp @@ -0,0 +1,54 @@ +#include "Node.hpp" +#include +#include + +Node::Node(int row, int col) + : QGraphicsRectItem(), m_row(row), m_col(col), m_visited(false), m_path(false), m_wall(false) +{ + setBrush(Qt::white); + setPen(QPen(Qt::lightGray)); + setFlag(QGraphicsItem::ItemIsSelectable, false); + setAcceptHoverEvents(false); +} + +void Node::setVisited(bool v) { + m_visited = v; + if (!m_wall) { + if (v) setBrush(QColor(135,206,250)); // light sky blue + else setBrush(Qt::white); + } +} + +void Node::setPath(bool p) { + m_path = p; + if (!m_wall) { + if (p) setBrush(QColor(255,215,0)); // gold + else setBrush(Qt::white); + } +} + +void Node::setWall(bool w) { + m_wall = w; + setBrush(w ? Qt::black : Qt::white); +} + +void Node::reset() { + m_visited = false; + m_path = false; + m_wall = false; + setBrush(Qt::white); +} + +void Node::setAsStart() { + m_visited = false; + m_path = false; + m_wall = false; + setBrush(QColor(0,180,0)); // green +} + +void Node::setAsTarget() { + m_visited = false; + m_path = false; + m_wall = false; + setBrush(QColor(200,0,0)); // red +} diff --git a/C++/pathfinding_visualizer/src/main.cpp b/C++/pathfinding_visualizer/src/main.cpp new file mode 100644 index 0000000..464adb4 --- /dev/null +++ b/C++/pathfinding_visualizer/src/main.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include "MainWindow.hpp" + +// Register meta types used in queued connections +Q_DECLARE_METATYPE(QVector>) + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + qRegisterMetaType>>("QVector>"); + qRegisterMetaType("QPoint"); + + MainWindow w; + w.show(); + return app.exec(); +} diff --git a/C++/pathfinding_visualizer/ui/MainWindow.ui b/C++/pathfinding_visualizer/ui/MainWindow.ui new file mode 100644 index 0000000..ed5a180 --- /dev/null +++ b/C++/pathfinding_visualizer/ui/MainWindow.ui @@ -0,0 +1,22 @@ + + + MainWindow + + + + 0 + 0 + 1280 + 720 + + + + Pathfinding Visualizer + + + + + + + + diff --git a/Python/Random_joke_generator/random_joke_generator.py b/Python/Random_joke_generator/random_joke_generator.py deleted file mode 100644 index c4e2d7a..0000000 --- a/Python/Random_joke_generator/random_joke_generator.py +++ /dev/null @@ -1,43 +0,0 @@ -import tkinter as tk -import requests - -def get_joke(): - """Fetch a random joke from JokeAPI and display it.""" - url = "https://v2.jokeapi.dev/joke/Any" - try: - response = requests.get(url) - data = response.json() - - if data["type"] == "single": - joke = data["joke"] - else: - joke = f"{data['setup']}\n\n{data['delivery']}" - - joke_text.config(state=tk.NORMAL) - joke_text.delete(1.0, tk.END) - joke_text.insert(tk.END, joke) - joke_text.config(state=tk.DISABLED) - except Exception as e: - joke_text.config(state=tk.NORMAL) - joke_text.delete(1.0, tk.END) - joke_text.insert(tk.END, f"Error fetching joke:\n{e}") - joke_text.config(state=tk.DISABLED) - -root = tk.Tk() -root.title("Random Joke Generator") -root.geometry("500x300") -root.config(bg="#fafafa") - -title = tk.Label(root, text="Random Joke Generator", font=("Helvetica", 16, "bold"), bg="#fafafa") -title.pack(pady=10) - -joke_text = tk.Text(root, wrap=tk.WORD, font=("Helvetica", 12), bg="#fff", height=8, width=55) -joke_text.pack(padx=10, pady=10) -joke_text.config(state=tk.DISABLED) - -next_button = tk.Button(root, text="Next Joke", command=get_joke, font=("Helvetica", 12, "bold"), bg="#4CAF50", fg="white") -next_button.pack(pady=10) - -get_joke() - -root.mainloop() diff --git a/Python/random_joke_generator/random_joke_generator.py b/Python/random_joke_generator/random_joke_generator.py index 0271cbe..c4e2d7a 100644 --- a/Python/random_joke_generator/random_joke_generator.py +++ b/Python/random_joke_generator/random_joke_generator.py @@ -23,23 +23,21 @@ def get_joke(): joke_text.insert(tk.END, f"Error fetching joke:\n{e}") joke_text.config(state=tk.DISABLED) -# --- GUI setup --- root = tk.Tk() root.title("Random Joke Generator") root.geometry("500x300") root.config(bg="#fafafa") -title = tk.Label(root, text="😂 Random Joke Generator 😂", font=("Helvetica", 16, "bold"), bg="#fafafa") +title = tk.Label(root, text="Random Joke Generator", font=("Helvetica", 16, "bold"), bg="#fafafa") title.pack(pady=10) joke_text = tk.Text(root, wrap=tk.WORD, font=("Helvetica", 12), bg="#fff", height=8, width=55) joke_text.pack(padx=10, pady=10) joke_text.config(state=tk.DISABLED) -next_button = tk.Button(root, text="Next Joke 👉", command=get_joke, font=("Helvetica", 12, "bold"), bg="#4CAF50", fg="white") +next_button = tk.Button(root, text="Next Joke", command=get_joke, font=("Helvetica", 12, "bold"), bg="#4CAF50", fg="white") next_button.pack(pady=10) -# Load the first joke automatically get_joke() root.mainloop() From d89520cec5b7367540081ef2dd5b81f32618d430 Mon Sep 17 00:00:00 2001 From: Ash-Jose Date: Fri, 14 Nov 2025 05:39:42 +0530 Subject: [PATCH 3/3] Changed readme slightly Signed-off-by: Ash-Jose --- C++/pathfinding_visualizer/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/C++/pathfinding_visualizer/README.md b/C++/pathfinding_visualizer/README.md index cec74ec..91f1b78 100644 --- a/C++/pathfinding_visualizer/README.md +++ b/C++/pathfinding_visualizer/README.md @@ -51,9 +51,6 @@ pathfinding_visualizer/ │ └── MainWindow.ui ``` -> Icons are SVG files, recommended from **Lucide Icons** (MIT licensed). -> Place all SVGs in `ui/icons/` exactly matching the filenames above. - --- ## 🛠️ Prerequisites @@ -98,10 +95,10 @@ The grid updates visually using `Node` objects in a `QGraphicsScene`. ### Toolbar Controls -- **Run** (`play.svg`) +- **Run** Starts the selected algorithm on a background thread (`AlgorithmWorker`). -- **Reset** (`reset.svg`) +- **Reset** Clears all walls, visited cells, and path markings. Start/Target nodes return to defaults. - **Algorithm Selector**