From db860196b7c35928c33411ece776033a26412e2f Mon Sep 17 00:00:00 2001 From: janhavi-khare Date: Wed, 22 Apr 2026 20:05:43 +0530 Subject: [PATCH 1/7] Added project folder and README Added comprehensive README for the Dynamic Escape Route Planner project, detailing features, algorithms, project structure, and usage instructions. --- .../README.md | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 Team 12 - Dynamic Escape Route Planner/README.md diff --git a/Team 12 - Dynamic Escape Route Planner/README.md b/Team 12 - Dynamic Escape Route Planner/README.md new file mode 100644 index 000000000..c40edc1e8 --- /dev/null +++ b/Team 12 - Dynamic Escape Route Planner/README.md @@ -0,0 +1,171 @@ +# πŸ—ΊοΈ Dynamic City Escape Route Planner + +> A pathfinding simulator that navigates an agent through a procedurally generated city grid to a safe exit, dynamically replanning its route as disasters strike in real time. + +--- + +## πŸ“½οΈ Demo Video + +**[β–Ά Watch Demo](YOUR_VIDEO_LINK_HERE)** + +--- + +## 🧩 Problem Statement + +In emergency scenarios β€” fires, floods, structural collapses β€” static evacuation routes fail the moment a road becomes impassable. This project models the real-world challenge of **dynamic pathfinding under uncertainty**: an agent must find the lowest-cost path from a start position `S` to an exit `E` on a city grid, but the environment is not static. Disasters spawn on the planned route mid-execution, blocking cells and forcing the agent to recompute a new optimal path from its current position β€” all without backtracking to the origin. + +The core question this project answers is: **how quickly and efficiently can a search algorithm adapt its route when the world changes beneath it?** + +The simulation supports four search strategies β€” A\*, Dijkstra, Greedy Best-First Search, and BFS β€” allowing direct comparison of how each handles the tension between exploration efficiency and path optimality in a changing environment. + +--- + +## ✨ Features + +- Procedurally generated city map (streets, alleys, parks, buildings) on every run +- Four selectable search algorithms with step-by-step and full-simulation modes +- Real-time disaster spawning (Fire πŸ”₯, Barricade 🚧, Water πŸ’§, Traffic πŸš—) directly on the planned path +- Instant replanning from the agent's current position when the route is blocked +- Visual frontier/explored node highlighting so you can watch the algorithm think +- Accumulated movement cost tracking across reroutes + +--- + +## πŸ”¬ Algorithms Supported + +| # | Algorithm | Strategy | Optimal? | Notes | +|---|-----------|----------|----------|-------| +| 0 | **A\*** | `f = g + h` | βœ… Yes | Chebyshev-based heuristic; tie-breaks by deeper g-cost | +| 1 | **Dijkstra** | `f = g` | βœ… Yes | Exhaustive; no heuristic | +| 2 | **Greedy Best-First** | `f = h` | ❌ No | Fast but ignores terrain cost | +| 3 | **BFS** | `f = steps` | Unweighted | Ignores terrain; uniform edge cost of 1 | + +The heuristic used for A\* and Greedy is an **admissible Chebyshev variant** tuned for 8-directional movement with asymmetric edge costs (orthogonal = 2, diagonal = 3): + +``` +h(n) = 3 Γ— min(dx, dy) + 2 Γ— |dx - dy| +``` + +--- + +## πŸ™οΈ Grid Cell Types + +| Symbol | Meaning | Traversal Cost | +|--------|---------|----------------| +| `S` | Start position | 1 | +| `E` | Exit / Safe zone | 1 | +| `C` | Corridor / Road | 1 (configurable) | +| `R` | Park / Plaza | 15 | +| `T` | Traffic jam | 20 | +| `X` | Building (wall) | ∞ β€” impassable | +| `F` | Fire | ∞ β€” impassable | +| `B` | Barricade | ∞ β€” impassable | +| `W` | Water / Flood | ∞ β€” impassable | + +Move costs are **additive**: diagonal moves cost `2 + terrain`, cardinal moves cost `1 + terrain`. + +--- + +## πŸ—‚οΈ Data Structures + +### `PriorityQueue` β€” Open List +The frontier of nodes awaiting exploration. Each `Node` stores `(x, y, g-cost, f-priority)`. The queue is ordered by `f`; ties are broken by preferring nodes with a **higher g-cost** (deeper in the search tree), which empirically reduces total nodes expanded and speeds up A\* significantly. + +### `int[][] dist` β€” Cost Table +A 2D array tracking the best known `g`-cost to reach every grid cell. Initialised to `Integer.MAX_VALUE`. When a cheaper path to a cell is found, its entry is updated and a new `Node` is enqueued β€” the stale entry is discarded via the `visited` guard. + +### `int[][] px / py` β€” Parent Pointer Arrays +Two parallel 2D arrays storing the row and column of each cell's predecessor on the best-known path. Used at termination to reconstruct the full route by walking backwards from `E` to `S`. + +### `boolean[][] visited` β€” Closed List +A 2D boolean array marking cells that have been **finalized** (popped from the priority queue and fully processed). Prevents re-expansion of already-settled nodes, ensuring each cell is relaxed at most once per search. + +### `List finalPath` +An `ArrayList` holding the reconstructed path as `[row, col]` pairs, stored **goal β†’ start** (reversed after `buildPath()`). During simulation, the agent consumes this list from the tail, moving one step per tick. + +### `Set frontierSet / exploredSet` +Two `HashSet` instances keyed by `"row,col"` strings. Used exclusively for **UI rendering** β€” they let the frontend colour frontier and explored cells without walking the full priority queue each frame. + +### `Queue` β€” BFS Reachability Check +A plain `LinkedList`-backed `Queue` used in `isReachable()` for a lightweight connectivity check before committing to a disaster spawn. Ensures a disaster is never placed if it would permanently disconnect the agent from the exit. + +--- + +## πŸ“ Project Structure + +``` +β”œβ”€β”€ PathfindingLogic.java Core algorithm engine β€” grid generation, A*/Dijkstra/ +β”‚ Greedy/BFS, simulation loop, disaster spawning, replanning +β”œβ”€β”€ Grid.java Entry point / original prototype β€” static 4Γ—4 grid demo +β”‚ of A* with controlled obstacle injection +β”œβ”€β”€ GridServer.java Plain Java HTTP server β€” exposes pathfinding as REST API +β”‚ (GET /api/state, POST /api/start|step|reset) +└── index.html Browser UI β€” connects to the Java REST API, renders the + live grid, path, metrics, and incident log +``` + +--- + +## πŸš€ Running the Project + +### Prerequisites +- Java 11 or higher (uses `com.sun.net.httpserver` β€” no extra dependencies) +- A modern browser (Chrome, Firefox, Edge) + +### Start the server +```bash +javac GridServer.java PathfindingLogic.java +java GridServer +``` + +You should see: +``` +╔══════════════════════════════════════╗ +β•‘ GridServer running on port 8080 β•‘ +β•‘ Open index.html in your browser β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• +``` + +### Open the UI +Open `index.html` directly in your browser β€” no additional web server needed. + +The status bar at the bottom turns **green** when the Java server is connected. + +--- + +## πŸ” Simulation Flow + +``` +generateCityMap() + β”‚ + β–Ό +startSimulation(algo, disasterRate) + β”‚ instantly runs selected algorithm to find initial path + β–Ό +loop: simulateStep() + β”œβ”€β”€ maybe spawn disaster ON the planned path (max 3 active) + β”‚ └── reachability check β†’ revert if goal becomes disconnected + β”œβ”€β”€ if disaster spawned β†’ return SIMULATION_REROUTING + β”‚ └── recalculatePathFromAgent() β†’ full re-run from current pos + β”œβ”€β”€ move agent one step along finalPath + └── if agent == goal β†’ return SIMULATION_REACHED +``` + +--- + +## βš™οΈ Configuration + +Tune these in `PathfindingLogic.java`: + +```java +private int corridorCost = 1; // Cost to traverse a road cell +private int disasterRate; // % chance per step of a disaster spawning (0–100) +``` + +--- + +## πŸ‘₯ Authors + +Janhavi Khare +Kavya Thacker +Rujuta Walavalkar From ee8d22389f0248e92b63da0bf07f8c00ce5c2a02 Mon Sep 17 00:00:00 2001 From: janhavi-khare Date: Wed, 22 Apr 2026 20:07:51 +0530 Subject: [PATCH 2/7] Format authors list in README.md --- Team 12 - Dynamic Escape Route Planner/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Team 12 - Dynamic Escape Route Planner/README.md b/Team 12 - Dynamic Escape Route Planner/README.md index c40edc1e8..68f3f411a 100644 --- a/Team 12 - Dynamic Escape Route Planner/README.md +++ b/Team 12 - Dynamic Escape Route Planner/README.md @@ -166,6 +166,6 @@ private int disasterRate; // % chance per step of a disaster spawning (0 ## πŸ‘₯ Authors -Janhavi Khare -Kavya Thacker -Rujuta Walavalkar +Janhavi Khare, +Kavya Thacker, +Rujuta Walavalkar, From 227883ca04af429152a897c08369db528f42e931 Mon Sep 17 00:00:00 2001 From: janhavi-khare Date: Wed, 22 Apr 2026 20:08:19 +0530 Subject: [PATCH 3/7] Update README.md --- Team 12 - Dynamic Escape Route Planner/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Team 12 - Dynamic Escape Route Planner/README.md b/Team 12 - Dynamic Escape Route Planner/README.md index 68f3f411a..59dd0ec63 100644 --- a/Team 12 - Dynamic Escape Route Planner/README.md +++ b/Team 12 - Dynamic Escape Route Planner/README.md @@ -168,4 +168,4 @@ private int disasterRate; // % chance per step of a disaster spawning (0 Janhavi Khare, Kavya Thacker, -Rujuta Walavalkar, +Rujuta Walavalkar From 56649ecf4a47afbb01b6495a796f8083e2e5a44a Mon Sep 17 00:00:00 2001 From: janhavi-khare Date: Wed, 22 Apr 2026 20:12:04 +0530 Subject: [PATCH 4/7] Uploaded files --- .../Grid.java | 450 ++++++++++++++++++ .../PathfindingLogic.java | 410 ++++++++++++++++ 2 files changed, 860 insertions(+) create mode 100644 Team 12 - Dynamic Escape Route Planner/Grid.java create mode 100644 Team 12 - Dynamic Escape Route Planner/PathfindingLogic.java diff --git a/Team 12 - Dynamic Escape Route Planner/Grid.java b/Team 12 - Dynamic Escape Route Planner/Grid.java new file mode 100644 index 000000000..3a7abbdb9 --- /dev/null +++ b/Team 12 - Dynamic Escape Route Planner/Grid.java @@ -0,0 +1,450 @@ +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.*; + +public class Grid extends JFrame { + + private PathfindingLogic logic; + + // UI Colors + private final Color COLOR_BG = new Color(30, 30, 30); + private final Color COLOR_START = new Color(46, 204, 113); + private final Color COLOR_END = new Color(231, 76, 60); + private final Color COLOR_PATH = new Color(52, 152, 219); + private final Color COLOR_EXPLORED = new Color(155, 89, 182, 100); + private final Color COLOR_FRONTIER = new Color(241, 196, 15, 100); + + private char currentBrush = 'S'; + + private GridPanel gridPanel; + + // Controls + private JComboBox algoCombo; + private JSlider speedSlider; + private JSlider spinDisaster; + private JComboBox pointCombo; + private JLabel lblTotalCost; + + // Animation state + private Timer timer; + private boolean isAnimating = false; + private boolean isSimulationMode = false; + + public Grid() { + super("Dynamic City Pathfinding Simulator"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLayout(new BorderLayout()); + getContentPane().setBackground(COLOR_BG); + + logic = new PathfindingLogic(21, 31); + + gridPanel = new GridPanel(); + + JPanel container = new JPanel(new GridBagLayout()); + container.setBackground(COLOR_BG); + container.add(gridPanel); + + JScrollPane scrollPane = new JScrollPane(container); + scrollPane.setBorder(null); + scrollPane.getViewport().setBackground(COLOR_BG); + add(scrollPane, BorderLayout.CENTER); + + JPanel controlPanel = createControlPanel(); + add(controlPanel, BorderLayout.EAST); + + add(createLegendPanel(), BorderLayout.SOUTH); + + pack(); + setLocationRelativeTo(null); + if(getWidth() > Toolkit.getDefaultToolkit().getScreenSize().width) { + setSize(Toolkit.getDefaultToolkit().getScreenSize().width - 100, getHeight()); + } + } + + private JPanel createControlPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBackground(new Color(40, 40, 40)); + panel.setBorder(new EmptyBorder(15, 15, 15, 15)); + + addLabel(panel, "Algorithm:"); + algoCombo = new JComboBox<>(new String[]{"A* Search", "Dijkstra's", "Greedy Best-First", "Breadth-First (BFS)"}); + algoCombo.setMaximumSize(new Dimension(200, 30)); + panel.add(algoCombo); + panel.add(Box.createRigidArea(new Dimension(0, 15))); + + addLabel(panel, "Animation Delay (ms):"); + speedSlider = new JSlider(1, 100, 50); + speedSlider.setBackground(new Color(40, 40, 40)); + speedSlider.setForeground(Color.WHITE); + speedSlider.setMaximumSize(new Dimension(200, 40)); + speedSlider.addChangeListener(e -> { + if (timer != null) timer.setDelay(speedSlider.getValue()); + }); + panel.add(speedSlider); + panel.add(Box.createRigidArea(new Dimension(0, 15))); + + addLabel(panel, "Set Position:"); + pointCombo = new JComboBox<>(new String[]{"Start (S)", "End (E)"}); + pointCombo.setMaximumSize(new Dimension(200, 30)); + pointCombo.addActionListener(e -> currentBrush = pointCombo.getSelectedIndex() == 0 ? 'S' : 'E'); + panel.add(pointCombo); + panel.add(Box.createRigidArea(new Dimension(0, 15))); + + addLabel(panel, "Disaster Rate:"); + spinDisaster = new JSlider(0, 50, 5); + spinDisaster.setBackground(new Color(40, 40, 40)); + spinDisaster.setForeground(Color.WHITE); + spinDisaster.setMaximumSize(new Dimension(200, 40)); + panel.add(spinDisaster); + panel.add(Box.createRigidArea(new Dimension(0, 20))); + + lblTotalCost = new JLabel("Total Cost: N/A"); + lblTotalCost.setForeground(new Color(241, 196, 15)); + lblTotalCost.setAlignmentX(Component.CENTER_ALIGNMENT); + lblTotalCost.setFont(lblTotalCost.getFont().deriveFont(Font.BOLD, 14f)); + panel.add(lblTotalCost); + panel.add(Box.createRigidArea(new Dimension(0, 20))); + + JButton btnFindPath = new JButton("Find Path"); + btnFindPath.setAlignmentX(Component.CENTER_ALIGNMENT); + btnFindPath.addActionListener(e -> startAlgorithm()); + panel.add(btnFindPath); + panel.add(Box.createRigidArea(new Dimension(0, 10))); + + JButton btnSimulate = new JButton("Init Simulation"); + btnSimulate.setAlignmentX(Component.CENTER_ALIGNMENT); + btnSimulate.addActionListener(e -> startSimulation()); + panel.add(btnSimulate); + panel.add(Box.createRigidArea(new Dimension(0, 10))); + + JButton btnNext = new JButton("Next Step"); + btnNext.setAlignmentX(Component.CENTER_ALIGNMENT); + btnNext.addActionListener(e -> { + if (isSimulationMode) handleSimulationTick(); + else JOptionPane.showMessageDialog(this, "Click 'Init Simulation' first!"); + }); + panel.add(btnNext); + panel.add(Box.createRigidArea(new Dimension(0, 10))); + + JButton btnClearPath = new JButton("Clear Path"); + btnClearPath.setAlignmentX(Component.CENTER_ALIGNMENT); + btnClearPath.addActionListener(e -> clearPath()); + panel.add(btnClearPath); + panel.add(Box.createRigidArea(new Dimension(0, 10))); + + JButton btnCity = new JButton("Generate City Map"); + btnCity.setAlignmentX(Component.CENTER_ALIGNMENT); + btnCity.addActionListener(e -> generateCityMap()); + panel.add(btnCity); + panel.add(Box.createRigidArea(new Dimension(0, 10))); + + JButton btnClearWalls = new JButton("Reset City"); + btnClearWalls.setAlignmentX(Component.CENTER_ALIGNMENT); + btnClearWalls.addActionListener(e -> { + stopAnimation(); + logic.clearDisasters(); + clearPath(); + gridPanel.repaint(); + }); + panel.add(btnClearWalls); + + return panel; + } + + private JPanel createLegendPanel() { + JPanel legend = new JPanel(); + legend.setBackground(COLOR_BG); + legend.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 10)); + + legend.add(createLegendItem("Street", new Color(45, 45, 45))); + legend.add(createLegendItem("Building", new Color(60, 60, 70))); + legend.add(createLegendItem("Park", new Color(34, 139, 34))); + legend.add(createLegendItem("Fire", new Color(230, 80, 0))); + legend.add(createLegendItem("Flood", new Color(40, 120, 200))); + legend.add(createLegendItem("Fallen Bldg", new Color(90, 60, 40))); + legend.add(createLegendItem("Traffic", new Color(100, 100, 100))); + legend.add(createLegendItem("Path", COLOR_PATH)); + legend.add(createLegendItem("Agent", Color.CYAN)); + + return legend; + } + + private JPanel createLegendItem(String labelText, Color color) { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); + panel.setBackground(COLOR_BG); + + JPanel colorBox = new JPanel(); + colorBox.setPreferredSize(new Dimension(15, 15)); + colorBox.setBackground(color); + colorBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + + JLabel label = new JLabel(labelText); + label.setForeground(Color.WHITE); + label.setFont(label.getFont().deriveFont(12f)); + + panel.add(colorBox); + panel.add(label); + + return panel; + } + + private void addLabel(JPanel panel, String text) { + JLabel label = new JLabel(text); + label.setForeground(Color.WHITE); + label.setAlignmentX(Component.CENTER_ALIGNMENT); + panel.add(label); + } + + private void generateCityMap() { + stopAnimation(); + logic.generateCityMap(); + lblTotalCost.setText("Total Cost: N/A"); + gridPanel.repaint(); + } + + private void clearPath() { + stopAnimation(); + logic.clearPath(); + lblTotalCost.setText("Total Cost: N/A"); + gridPanel.repaint(); + } + + private void stopAnimation() { + if (timer != null) timer.stop(); + isAnimating = false; + isSimulationMode = false; + } + + private void startAlgorithm() { + clearPath(); + if (!logic.hasStartAndGoal()) { + JOptionPane.showMessageDialog(this, "Need Start and End positions!"); + return; + } + + int algo = algoCombo.getSelectedIndex(); + logic.startAlgorithm(algo); + + isAnimating = true; + isSimulationMode = false; + timer = new Timer(speedSlider.getValue(), e -> handleAlgorithmTick()); + timer.start(); + } + + private void startSimulation() { + clearPath(); + if (!logic.hasStartAndGoal()) { + JOptionPane.showMessageDialog(this, "Need Start and End positions!"); + return; + } + + int algo = algoCombo.getSelectedIndex(); + int dRate = spinDisaster.getValue(); + + logic.startSimulation(algo, dRate); + + isSimulationMode = true; + isAnimating = false; + if (timer != null) timer.stop(); + gridPanel.repaint(); + } + + private void handleAlgorithmTick() { + PathfindingLogic.Status status = logic.step(); + gridPanel.repaint(); + + if (status == PathfindingLogic.Status.FOUND) { + timer.stop(); + isAnimating = false; + int cost = logic.getCalculatedCost(); + int steps = logic.getFinalPath() != null ? logic.getFinalPath().size() - 1 : 0; + lblTotalCost.setText("Total Cost: " + cost); + JOptionPane.showMessageDialog(this, "Goal Reached!\nTotal Cost: " + cost + "\nPath Length (steps): " + steps); + } else if (status == PathfindingLogic.Status.NO_PATH) { + timer.stop(); + isAnimating = false; + lblTotalCost.setText("Total Cost: No Path"); + JOptionPane.showMessageDialog(this, "No path found!"); + } + } + + private void handleSimulationTick() { + PathfindingLogic.Status status = logic.simulateStep(); + gridPanel.paintImmediately(0, 0, gridPanel.getWidth(), gridPanel.getHeight()); + + if (status == PathfindingLogic.Status.SIMULATION_REACHED) { + isSimulationMode = false; + int cost = logic.getCalculatedCost(); + lblTotalCost.setText("Total Cost: " + cost); + JOptionPane.showMessageDialog(this, "Agent reached the destination!\nTotal Cost: " + cost); + } else if (status == PathfindingLogic.Status.SIMULATION_NO_PATH) { + isSimulationMode = false; + lblTotalCost.setText("Total Cost: No Path"); + JOptionPane.showMessageDialog(this, "Agent is completely blocked by disasters!"); + } else if (status == PathfindingLogic.Status.SIMULATION_REROUTING) { + JOptionPane.showMessageDialog(this, "Disaster! Road blocked, rerouting...", "Warning", JOptionPane.WARNING_MESSAGE); + logic.recalculatePathFromAgent(); + gridPanel.repaint(); + } + } + + private class GridPanel extends JPanel { + private final int CELL_SIZE = 30; + + public GridPanel() { + setPreferredSize(new Dimension(logic.getCols() * CELL_SIZE, logic.getRows() * CELL_SIZE)); + setBackground(COLOR_BG); + + MouseAdapter ma = new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { paintCell(e); } + @Override + public void mouseDragged(MouseEvent e) { paintCell(e); } + }; + addMouseListener(ma); + addMouseMotionListener(ma); + } + + private void paintCell(MouseEvent e) { + if (isAnimating) return; + + int c = e.getX() / CELL_SIZE; + int r = e.getY() / CELL_SIZE; + + if (r >= 0 && r < logic.getRows() && c >= 0 && c < logic.getCols()) { + logic.setCell(r, c, currentBrush); + clearPath(); + repaint(); + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + int rows = logic.getRows(); + int cols = logic.getCols(); + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + int x = c * CELL_SIZE; + int y = r * CELL_SIZE; + char cell = logic.getCell(r, c); + + if (cell == 'X') { + // Building + g2.setColor(new Color(60, 60, 70)); + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + g2.setColor(new Color(40, 40, 50)); + g2.drawRect(x, y, CELL_SIZE, CELL_SIZE); + // Tiny windows to make it look like a building from above + g2.setColor(new Color(220, 220, 150, 150)); + g2.fillRect(x + 5, y + 5, 8, 8); + g2.fillRect(x + 17, y + 5, 8, 8); + g2.fillRect(x + 5, y + 17, 8, 8); + g2.fillRect(x + 17, y + 17, 8, 8); + } else if (cell == 'R') { + // Park / Plaza + g2.setColor(new Color(34, 139, 34)); + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + g2.setColor(new Color(0, 100, 0)); + g2.drawRect(x, y, CELL_SIZE, CELL_SIZE); + } else if (cell == 'C' || cell == 'S' || cell == 'E') { + // Street + g2.setColor(new Color(45, 45, 45)); + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + g2.setColor(new Color(30, 30, 30)); + g2.drawRect(x, y, CELL_SIZE, CELL_SIZE); + + // Dashed road lines + g2.setColor(new Color(255, 200, 0, 100)); // Yellow + boolean up = r > 0 && isStreet(logic.getCell(r-1, c)); + boolean down = r < rows-1 && isStreet(logic.getCell(r+1, c)); + boolean left = c > 0 && isStreet(logic.getCell(r, c-1)); + boolean right = c < cols-1 && isStreet(logic.getCell(r, c+1)); + + if (left && right && !up && !down) { + g2.fillRect(x + CELL_SIZE/4, y + CELL_SIZE/2 - 1, CELL_SIZE/2, 2); + } else if (up && down && !left && !right) { + g2.fillRect(x + CELL_SIZE/2 - 1, y + CELL_SIZE/4, 2, CELL_SIZE/2); + } + } else if (cell == 'F') { + g2.setColor(new Color(230, 80, 0)); // Fire + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + } else if (cell == 'B') { + g2.setColor(new Color(90, 60, 40)); // Building rubble + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + } else if (cell == 'W') { + g2.setColor(new Color(40, 120, 200)); // Flood + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + } else if (cell == 'T') { + g2.setColor(new Color(100, 100, 100)); // Traffic/Rubble + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + } + + String key = r + "," + c; + if (logic.getExploredSet().contains(key)) { + g2.setColor(COLOR_EXPLORED); + g2.fillRect(x, y, CELL_SIZE, CELL_SIZE); + } else if (logic.getFrontierSet().contains(key)) { + g2.setColor(COLOR_FRONTIER); + g2.fillRect(x + CELL_SIZE/4, y + CELL_SIZE/4, CELL_SIZE/2, CELL_SIZE/2); + } + + if (cell == 'S') { + g2.setColor(COLOR_START); + g2.fillOval(x + 5, y + 5, CELL_SIZE - 10, CELL_SIZE - 10); + } else if (cell == 'E') { + g2.setColor(COLOR_END); + g2.fillOval(x + 5, y + 5, CELL_SIZE - 10, CELL_SIZE - 10); + } + } + } + + java.util.List path = logic.getFinalPath(); + if (path != null && path.size() > 1) { + g2.setColor(COLOR_PATH); + g2.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + for (int i = 0; i < path.size() - 1; i++) { + int[] p1 = path.get(i); + int[] p2 = path.get(i+1); + int x1 = p1[1] * CELL_SIZE + CELL_SIZE/2; + int y1 = p1[0] * CELL_SIZE + CELL_SIZE/2; + int x2 = p2[1] * CELL_SIZE + CELL_SIZE/2; + int y2 = p2[0] * CELL_SIZE + CELL_SIZE/2; + g2.drawLine(x1, y1, x2, y2); + } + } + + // Draw Agent + int[] agent = logic.getAgentPos(); + if (agent != null) { + g2.setColor(Color.CYAN); + int ax = agent[1] * CELL_SIZE; + int ay = agent[0] * CELL_SIZE; + g2.fillOval(ax + 6, ay + 6, CELL_SIZE - 12, CELL_SIZE - 12); + g2.setColor(Color.WHITE); + g2.setStroke(new BasicStroke(2)); + g2.drawOval(ax + 6, ay + 6, CELL_SIZE - 12, CELL_SIZE - 12); + } + } + + private boolean isStreet(char c) { + return c == 'C' || c == 'S' || c == 'E' || c == 'F' || c == 'B' || c == 'W' || c == 'T'; + } + } + + public static void main(String[] args) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) {} + + SwingUtilities.invokeLater(() -> { + new Grid().setVisible(true); + }); + } +} diff --git a/Team 12 - Dynamic Escape Route Planner/PathfindingLogic.java b/Team 12 - Dynamic Escape Route Planner/PathfindingLogic.java new file mode 100644 index 000000000..0cd33b332 --- /dev/null +++ b/Team 12 - Dynamic Escape Route Planner/PathfindingLogic.java @@ -0,0 +1,410 @@ +import java.util.*; + +public class PathfindingLogic { + public enum Status { RUNNING, FOUND, NO_PATH, SIMULATING, SIMULATION_REACHED, SIMULATION_NO_PATH, SIMULATION_REROUTING } + + private int rows, cols; + private char[][] grid; + + private PriorityQueue pq; + private int[][] dist, px, py; + private boolean[][] visited; + private List finalPath; + private Set frontierSet = new HashSet<>(); + private Set exploredSet = new HashSet<>(); + private int[] startPos, goalPos, agentPos; + + private int selectedAlgo; + private int corridorCost = 1; + private int roomCost = 5; + private int calculatedCost; + private int accumulatedCost = 0; + private int disasterRate; + private Random random = new Random(); + + static int[][] directions = { + {1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {1, -1}, {-1, 1}, {-1, -1} + }; + + static class Node implements Comparable { + int x, y, cost, priority; + Node(int x, int y, int cost, int priority) { + this.x = x; this.y = y; this.cost = cost; this.priority = priority; + } + public int compareTo(Node other) { + if (this.priority == other.priority) { + // Break ties by preferring paths that have traveled further (higher g-cost) + // This makes A* significantly faster by exploring deeper nodes first! + return Integer.compare(other.cost, this.cost); + } + return Integer.compare(this.priority, other.priority); + } + } + + public PathfindingLogic(int rows, int cols) { + this.rows = rows; + this.cols = cols; + initGrid(); + } + + public void initGrid() { + grid = new char[rows][cols]; + generateCityMap(); + } + + public void setCell(int r, int c, char val) { + if (val == 'S' || val == 'E') { + for (int i=0; i(); + int initialPriority = (algo == 0 || algo == 2) ? heuristic(startPos[0], startPos[1], goalPos) : 0; + + pq.add(new Node(startPos[0], startPos[1], 0, initialPriority)); + dist[startPos[0]][startPos[1]] = 0; + } + + public void startSimulation(int algo, int dRate) { + this.disasterRate = dRate; + startAlgorithm(algo); + + // Instantly find initial path + Status s = Status.RUNNING; + while (s == Status.RUNNING) { + s = internalStep(); + } + if (s == Status.FOUND) { + buildPath(goalPos[0], goalPos[1]); + calculatedCost = calculateTrueCost(); + agentPos = new int[]{startPos[0], startPos[1]}; + } else { + agentPos = null; + } + } + + public Status simulateStep() { + if (agentPos == null || goalPos == null) return Status.SIMULATION_NO_PATH; + if (agentPos[0] == goalPos[0] && agentPos[1] == goalPos[1]) return Status.SIMULATION_REACHED; + + // Count total disasters on the board + int numDisasters = 0; + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + char ch = grid[r][c]; + if (ch == 'F' || ch == 'B' || ch == 'W' || ch == 'T') numDisasters++; + } + } + + boolean disasterSpawned = false; + // Spawn disaster directly ON the planned path (Max 3 allowed) + if (disasterRate > 0 && finalPath != null && finalPath.size() > 2 && numDisasters < 3) { + if (random.nextInt(100) < disasterRate) { + // Pick a random node on the path. finalPath is Goal(0) to Agent(size-1). + int idx = random.nextInt(finalPath.size() - 2) + 1; + int[] p = finalPath.get(idx); + char originalCell = grid[p[0]][p[1]]; + if (originalCell == 'C' || originalCell == 'R') { + int type = random.nextInt(4); + if (type == 0) grid[p[0]][p[1]] = 'F'; + else if (type == 1) grid[p[0]][p[1]] = 'B'; + else if (type == 2) grid[p[0]][p[1]] = 'W'; + else grid[p[0]][p[1]] = 'T'; + + // Check if Goal is still reachable + if (!isReachable(agentPos, goalPos)) { + // Revert disaster if it completely blocks the goal + grid[p[0]][p[1]] = originalCell; + } else { + disasterSpawned = true; + } + } + } + } + + if (disasterSpawned) { + return Status.SIMULATION_REROUTING; + } + + if (finalPath != null && finalPath.size() > 1) { + int[] nextStep = finalPath.get(finalPath.size() - 2); + + int dx = Math.abs(agentPos[0] - nextStep[0]); + int dy = Math.abs(agentPos[1] - nextStep[1]); + int move = (dx + dy == 2) ? 2 : 1; + accumulatedCost += move + getTerrainCost(grid[nextStep[0]][nextStep[1]]); + + agentPos[0] = nextStep[0]; + agentPos[1] = nextStep[1]; + } + + recalculatePathFromAgent(); + + if (agentPos[0] == goalPos[0] && agentPos[1] == goalPos[1]) { + return Status.SIMULATION_REACHED; + } + + if (finalPath == null) { + return Status.SIMULATION_NO_PATH; + } + + return Status.SIMULATING; + } + + public void recalculatePathFromAgent() { + dist = new int[rows][cols]; + px = new int[rows][cols]; + py = new int[rows][cols]; + visited = new boolean[rows][cols]; + frontierSet.clear(); + exploredSet.clear(); + + for (int i = 0; i < rows; i++) { + Arrays.fill(dist[i], Integer.MAX_VALUE); + Arrays.fill(px[i], -1); + Arrays.fill(py[i], -1); + } + + pq = new PriorityQueue<>(); + int initialPriority = (selectedAlgo == 0 || selectedAlgo == 2) ? heuristic(agentPos[0], agentPos[1], goalPos) : 0; + + pq.add(new Node(agentPos[0], agentPos[1], 0, initialPriority)); + dist[agentPos[0]][agentPos[1]] = 0; + + Status s = Status.RUNNING; + while (s == Status.RUNNING) { + s = internalStep(); + } + if (s == Status.FOUND) { + buildPath(goalPos[0], goalPos[1]); + calculatedCost = calculateTrueCost(); + } else { + finalPath = null; + } + } + + public Status step() { + Status s = internalStep(); + if (s == Status.FOUND) { + buildPath(goalPos[0], goalPos[1]); + calculatedCost = calculateTrueCost(); + } + return s; + } + + private Status internalStep() { + if (pq == null || pq.isEmpty()) return Status.NO_PATH; + + Node cur = pq.poll(); + frontierSet.remove(cur.x + "," + cur.y); + + if (visited[cur.x][cur.y]) return Status.RUNNING; + + visited[cur.x][cur.y] = true; + exploredSet.add(cur.x + "," + cur.y); + + if (cur.x == goalPos[0] && cur.y == goalPos[1]) { + return Status.FOUND; + } + + for (int[] d : directions) { + int nx = cur.x + d[0], ny = cur.y + d[1]; + if (isValid(nx, ny)) { + int dx = Math.abs(nx - cur.x); + int dy = Math.abs(ny - cur.y); + int move = (dx + dy == 2) ? 2 : 1; + + int tCost = getTerrainCost(grid[nx][ny]); + if (tCost == Integer.MAX_VALUE) continue; + + int newCost = cur.cost + move + tCost; + if (selectedAlgo == 3) newCost = cur.cost + 1; + + if (newCost < dist[nx][ny]) { + dist[nx][ny] = newCost; + px[nx][ny] = cur.x; + py[nx][ny] = cur.y; + + int priority = 0; + int h = heuristic(nx, ny, goalPos); + + if (selectedAlgo == 0) priority = newCost + h; + else if (selectedAlgo == 1) priority = newCost; + else if (selectedAlgo == 2) priority = h; + else if (selectedAlgo == 3) priority = newCost; + + pq.add(new Node(nx, ny, newCost, priority)); + frontierSet.add(nx + "," + ny); + } + } + } + return Status.RUNNING; + } + + private void buildPath(int ex, int ey) { + finalPath = new ArrayList<>(); + int cx = ex, cy = ey; + while (cx != -1 && cy != -1) { + finalPath.add(new int[]{cx, cy}); + int tx = px[cx][cy]; + int ty = py[cx][cy]; + cx = tx; + cy = ty; + } + } + + private int calculateTrueCost() { + if (finalPath == null || finalPath.size() < 2) return 0; + int total = 0; + for (int i = 0; i < finalPath.size() - 1; i++) { + int[] p1 = finalPath.get(i); + int[] p2 = finalPath.get(i+1); + int dx = Math.abs(p1[0] - p2[0]); + int dy = Math.abs(p1[1] - p2[1]); + int move = (dx + dy == 2) ? 2 : 1; + total += move + getTerrainCost(grid[p1[0]][p1[1]]); + } + return total; + } + + private int[] findPos(char target) { + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == target) return new int[]{i, j}; + } + } + return null; + } + + private boolean isValid(int x, int y) { + if (x < 0 || y < 0 || x >= rows || y >= cols) return false; + char c = grid[x][y]; + return c != 'X' && c != 'B' && c != 'F' && c != 'W'; + } + + private boolean isReachable(int[] start, int[] end) { + boolean[][] vis = new boolean[rows][cols]; + Queue q = new LinkedList<>(); + q.add(start); + vis[start[0]][start[1]] = true; + while(!q.isEmpty()) { + int[] curr = q.poll(); + if (curr[0] == end[0] && curr[1] == end[1]) return true; + for (int[] d : directions) { + int nx = curr[0] + d[0], ny = curr[1] + d[1]; + if (isValid(nx, ny) && !vis[nx][ny]) { + vis[nx][ny] = true; + q.add(new int[]{nx, ny}); + } + } + } + return false; + } + + private int heuristic(int x, int y, int[] goal) { + // Optimal admissible heuristic for 8-way movement where orthogonal=2, diagonal=3 + int dx = Math.abs(x - goal[0]); + int dy = Math.abs(y - goal[1]); + return 3 * Math.min(dx, dy) + 2 * Math.abs(dx - dy); + } + + private int getTerrainCost(char cell) { + if (cell == 'C') return corridorCost; + if (cell == 'R') return 15; + if (cell == 'T') return 20; + if (cell == 'E' || cell == 'S') return 1; + return Integer.MAX_VALUE; + } + + // Getters for rendering + public Set getExploredSet() { return exploredSet; } + public Set getFrontierSet() { return frontierSet; } + public List getFinalPath() { return finalPath; } + public int getCalculatedCost() { return calculatedCost + accumulatedCost; } + public int[] getAgentPos() { return agentPos; } + public boolean hasStartAndGoal() { return findPos('S') != null && findPos('E') != null; } +} From 791c61a61061bed31c578dfa53a540f091602099 Mon Sep 17 00:00:00 2001 From: janhavi-khare Date: Thu, 23 Apr 2026 00:07:46 +0530 Subject: [PATCH 5/7] Added link of video to README --- Team 12 - Dynamic Escape Route Planner/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Team 12 - Dynamic Escape Route Planner/README.md b/Team 12 - Dynamic Escape Route Planner/README.md index 59dd0ec63..83b72af05 100644 --- a/Team 12 - Dynamic Escape Route Planner/README.md +++ b/Team 12 - Dynamic Escape Route Planner/README.md @@ -6,7 +6,7 @@ ## πŸ“½οΈ Demo Video -**[β–Ά Watch Demo](YOUR_VIDEO_LINK_HERE)** +**[β–Ά Watch Demo]((https://drive.google.com/drive/folders/17VGOnwmzpo-PoxPG4b9siuLrlAKB3-UO?usp=sharing))** --- From 23642bf00a4d1ed456c6c8bfce490d0d74431094 Mon Sep 17 00:00:00 2001 From: janhavi-khare Date: Thu, 23 Apr 2026 00:09:59 +0530 Subject: [PATCH 6/7] Added link of video to README --- Team 12 - Dynamic Escape Route Planner/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Team 12 - Dynamic Escape Route Planner/README.md b/Team 12 - Dynamic Escape Route Planner/README.md index 83b72af05..0d2e6e1a6 100644 --- a/Team 12 - Dynamic Escape Route Planner/README.md +++ b/Team 12 - Dynamic Escape Route Planner/README.md @@ -6,7 +6,7 @@ ## πŸ“½οΈ Demo Video -**[β–Ά Watch Demo]((https://drive.google.com/drive/folders/17VGOnwmzpo-PoxPG4b9siuLrlAKB3-UO?usp=sharing))** +(https://drive.google.com/drive/folders/17VGOnwmzpo-PoxPG4b9siuLrlAKB3-UO?usp=sharing) --- From 71db9c49edfda64c578b0531a4036846d8971d56 Mon Sep 17 00:00:00 2001 From: Janhavi Date: Thu, 11 Jun 2026 18:30:02 +0530 Subject: [PATCH 7/7] Update README to simplify project setup instructions Removed outdated server start instructions and prerequisites from README. --- .../README.md | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/Team 12 - Dynamic Escape Route Planner/README.md b/Team 12 - Dynamic Escape Route Planner/README.md index 0d2e6e1a6..de43dd552 100644 --- a/Team 12 - Dynamic Escape Route Planner/README.md +++ b/Team 12 - Dynamic Escape Route Planner/README.md @@ -98,39 +98,15 @@ A plain `LinkedList`-backed `Queue` used in `isReachable()` for a lightweight co β”‚ Greedy/BFS, simulation loop, disaster spawning, replanning β”œβ”€β”€ Grid.java Entry point / original prototype β€” static 4Γ—4 grid demo β”‚ of A* with controlled obstacle injection -β”œβ”€β”€ GridServer.java Plain Java HTTP server β€” exposes pathfinding as REST API -β”‚ (GET /api/state, POST /api/start|step|reset) -└── index.html Browser UI β€” connects to the Java REST API, renders the - live grid, path, metrics, and incident log ``` --- ## πŸš€ Running the Project -### Prerequisites -- Java 11 or higher (uses `com.sun.net.httpserver` β€” no extra dependencies) -- A modern browser (Chrome, Firefox, Edge) - -### Start the server -```bash -javac GridServer.java PathfindingLogic.java -java GridServer -``` - -You should see: ``` -╔══════════════════════════════════════╗ -β•‘ GridServer running on port 8080 β•‘ -β•‘ Open index.html in your browser β•‘ -β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• +Simply run the PathfindingLogic.java ``` - -### Open the UI -Open `index.html` directly in your browser β€” no additional web server needed. - -The status bar at the bottom turns **green** when the Java server is connected. - --- ## πŸ” Simulation Flow