Skip to content

Commit

Permalink
added DijkstraAlgorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
abdelaziz-mahdy committed Nov 29, 2023
1 parent 18862b1 commit ddf80b5
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 41 deletions.
51 changes: 19 additions & 32 deletions lib/algorithm/a_star.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:path_finding/algorithm/algorithm.dart';
import 'package:path_finding/algorithm/node/path_node.dart';
import 'package:path_finding/models/models.dart';

class AStarAlgorithm implements Algorithm {
Expand Down Expand Up @@ -77,39 +78,30 @@ class AStarAlgorithm implements Algorithm {

return AlgorithmResult(changes, null);
}
}

AlgorithmPath constructPath(AStarNode? endNode) {
if (endNode == null) {
throw Exception("Did not find end path");
}
final List<int> rows = [];
final List<int> columns = [];
var currentNode = endNode;

while (currentNode.cameFrom != null) {
rows.insert(0, currentNode.row);
columns.insert(0, currentNode.column);
currentNode = currentNode.cameFrom!;
@override
String name = "A*";
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Algorithm && other.name == name;
}

return AlgorithmPath(rows, columns);
@override
int get hashCode => name.hashCode;
}

class AStarNode {
final int row;
final int column;
BlockState blockState;
AStarNode? cameFrom;
class AStarNode extends PathNode {
double gScore;
int hScore;
double fScore;
BlockState blockState;

AStarNode(this.row, this.column, this.blockState)
: cameFrom = null,
gScore = double.infinity,
AStarNode(int row, int column, this.blockState)
: gScore = double.infinity,
hScore = 0,
fScore = 0;
fScore = 0,
super(row, column);

void calculateFScore(AStarNode endNode) {
hScore = calculateHScore(endNode);
Expand All @@ -124,21 +116,16 @@ class AStarNode {

List<AStarNode> getNeighbors(
List<List<AStarNode>> grid, int rows, int columns) {
final List<AStarNode> neighbors = [];
final neighbors = <AStarNode>[];
if (row > 0) neighbors.add(grid[row - 1][column]);
if (row < rows - 1) neighbors.add(grid[row + 1][column]);
if (column > 0) neighbors.add(grid[row][column - 1]);
if (column < columns - 1) neighbors.add(grid[row][column + 1]);

// Filter out wall nodes if necessary
return neighbors
.where((node) => node.blockState != BlockState.wall)
.toList();
}

@override
bool operator ==(covariant AStarNode other) {
return other.row == row && other.column == column;
}

@override
int get hashCode => row.hashCode ^ column.hashCode;
// Implement other methods specific to AStarNode
}
37 changes: 36 additions & 1 deletion lib/algorithm/algorithm.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
import 'package:path_finding/algorithm/algorithm_path.dart';
import 'package:path_finding/models/algorithm_result.dart';
import 'package:path_finding/models/block_state.dart';

import 'node/path_node.dart';

abstract class Algorithm {
Algorithm();
String name;
Algorithm({required this.name});

AlgorithmResult execute(List<List<BlockState>> matrix);

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Algorithm && other.name == name;
}

@override
int get hashCode => name.hashCode;
}

AlgorithmPath constructPath(PathNode? endNode) {
if (endNode == null) {
throw Exception("End node not found in path");
}

final List<int> rows = [];
final List<int> columns = [];
var currentNode = endNode;

while (currentNode.cameFrom != null) {
rows.insert(0, currentNode.row);
columns.insert(0, currentNode.column);
currentNode = currentNode.cameFrom!;
}

// Insert the start node's coordinates
rows.insert(0, currentNode.row);
columns.insert(0, currentNode.column);

return AlgorithmPath(rows, columns);
}
File renamed without changes.
114 changes: 114 additions & 0 deletions lib/algorithm/dijkstra.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import 'package:path_finding/algorithm/algorithm.dart';
import 'package:path_finding/algorithm/node/path_node.dart';
import 'package:path_finding/models/models.dart';

class DijkstraAlgorithm implements Algorithm {
DijkstraAlgorithm() : super();

@override
AlgorithmResult execute(List<List<BlockState>> matrix) {
final rows = matrix.length;
final columns = matrix[0].length;
final changes = <Change>[];

final grid = List.generate(
rows,
(row) => List.generate(
columns,
(col) => DijkstraNode(row, col, matrix[row][col]),
),
);

DijkstraNode? startNode;
DijkstraNode? endNode;
for (var row = 0; row < rows; row++) {
for (var col = 0; col < columns; col++) {
final blockState = matrix[row][col];
if (blockState == BlockState.start) {
startNode = grid[row][col];
} else if (blockState == BlockState.end) {
endNode = grid[row][col];
}
}
}

if (startNode == null || endNode == null) {
throw Exception("Start and End nodes need to be set");
}
startNode.distance = 0;

final openSet = [startNode];
final closedSet = <DijkstraNode>[];

while (openSet.isNotEmpty) {
final currentNode =
openSet.reduce((a, b) => a.distance < b.distance ? a : b);

if (currentNode == endNode) {
final path = constructPath(currentNode);
return AlgorithmResult(changes, path);
}

openSet.remove(currentNode);
closedSet.add(currentNode);
changes
.add(Change(currentNode.row, currentNode.column, BlockState.visited));

for (final neighbor in currentNode.getNeighbors(grid, rows, columns)) {
if (closedSet.contains(neighbor) ||
neighbor.blockState == BlockState.wall) {
continue;
}

final newDistance = currentNode.distance + 1;
if (newDistance < neighbor.distance) {
neighbor.cameFrom = currentNode;
neighbor.distance = newDistance;
}

if (!openSet.contains(neighbor)) {
openSet.add(neighbor);
changes
.add(Change(neighbor.row, neighbor.column, BlockState.visited));
}
}
}

return AlgorithmResult(changes, null);
}

@override
String name = "Dijkstra";

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Algorithm && other.name == name;
}

@override
int get hashCode => name.hashCode;
}

class DijkstraNode extends PathNode {
int distance;
BlockState blockState;

DijkstraNode(int row, int column, this.blockState)
: distance = 2147483647, // Maximum value for 32-bit integer
super(row, column);

List<DijkstraNode> getNeighbors(
List<List<DijkstraNode>> grid, int rows, int columns) {
final neighbors = <DijkstraNode>[];
if (row > 0) neighbors.add(grid[row - 1][column]);
if (row < rows - 1) neighbors.add(grid[row + 1][column]);
if (column > 0) neighbors.add(grid[row][column - 1]);
if (column < columns - 1) neighbors.add(grid[row][column + 1]);

// Filter out wall nodes if necessary
return neighbors
.where((node) => node.blockState != BlockState.wall)
.toList();
}
}
9 changes: 9 additions & 0 deletions lib/algorithm/node/path_node.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
abstract class PathNode {
final int row;
final int column;
PathNode? cameFrom;

PathNode(this.row, this.column);

// You can add other common methods or properties here if needed
}
2 changes: 2 additions & 0 deletions lib/controllers/controller.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:path_finding/algorithm/a_star.dart';
import 'package:path_finding/models/models.dart';

class GridController {
Expand All @@ -15,6 +16,7 @@ class GridController {
// Private constructor
GridController._internal() {
_onInit();

}

// Replace RxBool with ValueNotifier
Expand Down
36 changes: 31 additions & 5 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:path_finding/algorithm/a_star.dart';
import 'package:path_finding/algorithm/algorithm.dart';
import 'package:path_finding/algorithm/dijkstra.dart';
import 'package:path_finding/controllers/controller.dart';
import 'package:path_finding/widgets/grid.dart';
import 'package:path_finding/models/models.dart';
Expand Down Expand Up @@ -34,12 +36,12 @@ class MyHomePage extends StatefulWidget {
final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
String _selectedAction = 'none';

Algorithm _algorithm = AStarAlgorithm();
void _handleAction(String action) {
setState(() {
_selectedAction = _selectedAction == action ? 'none' : action;
Expand Down Expand Up @@ -76,6 +78,31 @@ class _MyHomePageState extends State<MyHomePage> {
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
actions: [
DropdownButton<Algorithm>(
value: _algorithm,
items: [
DropdownMenuItem<Algorithm>(
value: DijkstraAlgorithm(),
child: const Text('Dijkstra'),
),
DropdownMenuItem<Algorithm>(
value: AStarAlgorithm(),
child: const Text('A*'),
),
],
onChanged: (Algorithm? value) {
if (value != null) {
setState(() {
_algorithm = value;
});
}
},
),
SizedBox(
width: 100,
)
],
),
body: Center(
child: Grid(
Expand Down Expand Up @@ -142,8 +169,7 @@ class _MyHomePageState extends State<MyHomePage> {

void _startAlgorithm() {
final controller = GridController();
AlgorithmResult result =
AStarAlgorithm().execute(controller.getMatrixValues());
AlgorithmResult result = _algorithm.execute(controller.getMatrixValues());
controller.applyAlgorithmResult(result);
}
}
Expand Down Expand Up @@ -185,10 +211,10 @@ class ActionButtonWidget extends StatelessWidget {

return FloatingActionButton(
onPressed: () => handleAction(action),
backgroundColor: backgroundColor,
child: icon != null
? Icon(icon, color: textStyle.color)
: Text(label, style: textStyle),
backgroundColor: backgroundColor,
);
}
}
2 changes: 1 addition & 1 deletion lib/models/algorithm_result.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:path_finding/models/algorithm_path.dart';
import 'package:path_finding/algorithm/algorithm_path.dart';
import 'package:path_finding/models/change.dart';

class AlgorithmResult {
Expand Down
2 changes: 1 addition & 1 deletion lib/models/models.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export 'algorithm_path.dart';
export '../algorithm/algorithm_path.dart';
export 'algorithm_result.dart';
export 'block_state.dart';
export 'change.dart';
Expand Down
16 changes: 16 additions & 0 deletions macos/Podfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
PODS:
- FlutterMacOS (1.0.0)

DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)

EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral

SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24

PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367

COCOAPODS: 1.12.1
Loading

0 comments on commit ddf80b5

Please sign in to comment.