Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions frontend/src/Algorithms/AStar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { getNeighbors } from "./algoFunctions.jsx";


import { RpcProvider, shortString, Contract, cairo, CallData } from "starknet";

async function a_star(grid, startCell, endCell) {

const height = grid.length;
const witdh = grid[0].length;

let tiles = [];
for (let col = 0; col < grid.length; col++) {
for (let row = 0; row < grid[col].length; row++) {
const tile = {id: row * witdh + col, x: col, y: row, is_walkable: !grid[col][row].wall};
tiles.push(tile);
}
}

const provider = new RpcProvider({ nodeUrl: "https://starknet-sepolia.public.blastapi.io/rpc/v0_6" }); // only for starknet-devnet-rs

const JPSClassAt = await provider.getClassAt("0x07921148721b727726ff737f9c65e411c730ab7dd9667f323dd449067daa0c8f");

const jpsContract = new Contract(JPSClassAt.abi, "0x07921148721b727726ff737f9c65e411c730ab7dd9667f323dd449067daa0c8f", provider);
const callData = new CallData(JPSClassAt.abi);

const myCallData = callData.compile("a_star", {
tiles: tiles,
map_width: witdh,
map_height: height,
start: cairo.tuple(startCell.col, startCell.row),
goal: cairo.tuple( endCell.col, endCell.row)
});

let path;
try {
path = await jpsContract.call("jps", myCallData);
} catch(error) {
throw new Error('Contract error: No remaining steps available.');
}

console.log("path", path);
if (path.length == 0) {
throw new Error('Could not find a valid path on the provided map.');
}

const parsedJumpPoints = [];
path.forEach(obj => {
const col = cairo.uint256(obj[0]);
const row = cairo.uint256(obj[1]);
const cell = {
row: row.low,
col: col.low,
start: false,
end: false,
jumpPoint: true
};
parsedJumpPoints.push(cell);
});

// Completa el camino con los puntos intermedios
const completedPath = [];
for (let i = 0; i < parsedJumpPoints.length - 1; i++) {
const currentCell = parsedJumpPoints[i] ;
const nextCell = parsedJumpPoints[i + 1];

// Agrega el punto actual al camino completado
completedPath.push(currentCell);

// Calcula los puntos intermedios en línea recta entre las celdas actual y siguiente
const deltaX = nextCell.col - currentCell.col;
const deltaY = nextCell.row - currentCell.row;
const distance = Math.max(Math.abs(deltaX), Math.abs(deltaY));

for (let j = 1; j < distance; j++) {
const col = parseInt(currentCell.col, 10);
const row = parseInt(currentCell.row, 10);

const x = Math.floor(col + deltaX * (j / distance)).toString();
const y = Math.floor(row + deltaY * (j / distance)).toString();
completedPath.push({ col: x, row: y, start: false, end: false, jumpPoint: false });
}
}
// Agrega la celda final al camino compSletado
completedPath.push(parsedJumpPoints[parsedJumpPoints.length - 1]);
return completedPath;
}

export default a_star;
6 changes: 3 additions & 3 deletions frontend/src/Algorithms/JPS.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ async function jps(grid, startCell, endCell) {
}
}

const provider = new RpcProvider({ nodeUrl: "https://starknet-testnet.public.blastapi.io" }); // only for starknet-devnet-rs
const provider = new RpcProvider({ nodeUrl: "https://starknet-sepolia.public.blastapi.io/rpc/v0_6" }); // only for starknet-devnet-rs

const JPSClassAt = await provider.getClassAt("0x04d12d8652db5311bc38ad36ac82b2f3ae41fcbbb4ae7e991bd368b43e7ab3ae");
const JPSClassAt = await provider.getClassAt("0x07921148721b727726ff737f9c65e411c730ab7dd9667f323dd449067daa0c8f");

const jpsContract = new Contract(JPSClassAt.abi, "0x04d12d8652db5311bc38ad36ac82b2f3ae41fcbbb4ae7e991bd368b43e7ab3ae", provider);
const jpsContract = new Contract(JPSClassAt.abi, "0x07921148721b727726ff737f9c65e411c730ab7dd9667f323dd449067daa0c8f", provider);
const callData = new CallData(JPSClassAt.abi);

const myCallData = callData.compile("jps", {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/utils.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import jps from "./Algorithms/JPS.jsx";
import a_star from "./Algorithms/AStar.jsx";
import random from "./Mazes/Personal.jsx";
import recursiveDivision from "./Mazes/RecursiveDivision.jsx";
import recursiveBacktracking from "./Mazes/RecusiveBacktracking.jsx";
Expand All @@ -7,6 +8,7 @@ import binaryTree from "./Mazes/binaryTree.jsx";

export const algos = {
"Jump Point Search": jps,
"A*": a_star,
};

export const mazes = {
Expand Down
145 changes: 145 additions & 0 deletions src/algorithms/a_star.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use core::array::SpanTrait;
use core::dict::Felt252DictTrait;
use core::nullable::{Nullable, NullableTrait, match_nullable, FromNullableResult};
use core::option::OptionTrait;
use pathfinding::algorithms::jps::{InfoKey, TilesInfo, TilesInfoTrait};
use pathfinding::data_structures::{
map::{Map, MapTrait, convert_position_to_idx, convert_idx_to_position},
min_heap::{MinHeap, MinHeapTrait},
};
use pathfinding::numbers::i64::i64;
use pathfinding::numbers::integer_trait::IntegerTrait;
use pathfinding::utils::constants::{
PARENT_KEY, STATUS_KEY, DISTANCE_KEY, DISTANCE_TO_GOAL_KEY, ESTIMATED_TOTAL_PATH_DISTANCE_KEY
};
use pathfinding::utils::heuristics::manhattan;
use pathfinding::utils::movement::get_movement_direction_coords;

const OPENED: u64 = 1;
const CLOSED: u64 = 2;

trait AStarTrait {
fn find_path(map: Map, start: (u64, u64), goal: (u64, u64)) -> Span<(u64, u64)>;
}

impl AStarDiagOneObstacle of AStarTrait {
fn find_path(map: Map, start: (u64, u64), goal: (u64, u64)) -> Span<(u64, u64)> {
let (sx, sy) = start;
let (gx, gy) = goal;
let mut tiles_info = TilesInfoTrait::new();
let mut open_list: MinHeap<u64> = MinHeapTrait::new();

let goal_id = convert_position_to_idx(map.width, gx, gy);
let start_id = convert_position_to_idx(map.width, sx, sy);
tiles_info.write(start_id, InfoKey::STATUS, OPENED);
tiles_info.write(start_id, InfoKey::ESTIMATIVE_TOTAL_COST, 0);
open_list.add(start_id, 0);

let mut goal_flag = false;
let mut node_id_flag = 0;

loop {
if open_list.len == 0 || goal_flag {
break;
}

let (node_id, node_value) = open_list.poll().unwrap(); // nodo con menor costo de F
let (node_x, node_y) = convert_idx_to_position(map.width, node_id);
tiles_info.write(node_id, InfoKey::STATUS, CLOSED);

if goal_id == node_id {
goal_flag = true;
node_id_flag = node_id;
break;
}

let neighbours = map.get_neighbours(ref tiles_info, node_id);
let mut i = 0;
loop {
if neighbours.len() == i {
break;
}
let n_id = *neighbours.at(i);
let (nx, ny) = convert_idx_to_position(map.width, n_id);
let n_status = tiles_info.read(n_id, InfoKey::STATUS);
let n_status_is_null = match match_nullable(n_status) {
FromNullableResult::Null => true,
FromNullableResult::NotNull(val) => false,
};

let inx = IntegerTrait::<i64>::new(nx, false);
let iny = IntegerTrait::<i64>::new(ny, false);
if (!n_status_is_null && n_status.deref() == CLOSED)
|| !map.is_walkable_at(inx, iny) {
i += 1;
continue;
}

let nh = manhattan(
(IntegerTrait::<i64>::new(nx, false) - IntegerTrait::<i64>::new(sx, false)).mag,
(IntegerTrait::<i64>::new(ny, false) - IntegerTrait::<i64>::new(sy, false)).mag
);
let ng = manhattan(
(IntegerTrait::<i64>::new(nx, false) - IntegerTrait::<i64>::new(gx, false)).mag,
(IntegerTrait::<i64>::new(ny, false) - IntegerTrait::<i64>::new(gy, false)).mag
);
let new_nf = nh + ng;
let nf = tiles_info.read(n_id, InfoKey::ESTIMATIVE_TOTAL_COST);
let nf_int = match match_nullable(nf) {
FromNullableResult::Null => 0,
FromNullableResult::NotNull(val) => nf.deref(),
};
if new_nf < nf_int || (n_status_is_null || n_status.deref() == CLOSED) {
tiles_info.write(n_id, InfoKey::ESTIMATIVE_TOTAL_COST, new_nf);
tiles_info.write(n_id, InfoKey::PARENT, node_id);

if n_status_is_null || n_status.deref() == CLOSED {
open_list.add(n_id, new_nf);
tiles_info.write(n_id, InfoKey::STATUS, OPENED);
}
}
i += 1;
}
};

if goal_flag {
let (node_x, node_y) = convert_idx_to_position(map.width, node_id_flag);
return build_reverse_path_from(map, ref tiles_info, node_id_flag);
} else {
array![].span()
}
}
}

fn build_reverse_path_from(map: Map, ref tiles_info: TilesInfo, grid_id: u64) -> Span<(u64, u64)> {
let (x, y) = convert_idx_to_position(map.width, grid_id);
let mut res = array![grid_id];

let mut parent_id = grid_id;
loop {
let p = tiles_info.read(parent_id, InfoKey::PARENT);
let p_is_null = match match_nullable(p) {
FromNullableResult::Null => true,
FromNullableResult::NotNull(val) => false,
};
if p_is_null {
break;
}
res.append(p.deref());
parent_id = p.deref();
};

let mut i = res.len() - 1;
let mut reverse = array![];
loop {
reverse.append(convert_idx_to_position(map.width, *res.at(i)));
if i == 0 {
break;
}
i -= 1;
};
reverse.span()
}

fn print_span(span: Span<u64>) { // let mut i = 0;s
}
2 changes: 2 additions & 0 deletions src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod pathfinder;

mod algorithms {
mod a_star;
mod jps;
}

Expand All @@ -24,6 +25,7 @@ mod utils {

#[cfg(test)]
mod tests {
mod test_a_star;
// mod test_map;
mod test_jps;
// mod test_heap;
Expand Down
21 changes: 21 additions & 0 deletions src/pathfinder.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ trait IPathfinder<TContractState> {
start: (u64, u64),
goal: (u64, u64)
) -> Span<(u64, u64)>;
fn a_star(
self: @TContractState,
tiles: Span<Tile>,
map_width: u64,
map_height: u64,
start: (u64, u64),
goal: (u64, u64)
) -> Span<(u64, u64)>;
}

#[starknet::contract]
mod Pathfinder {
use pathfinding::algorithms::{a_star::AStarTrait};
use pathfinding::algorithms::{jps::JPSTrait};
use pathfinding::data_structures::{map::{Map, MapTrait}, tile::{Tile, TileTrait}};
use super::IPathfinder;
Expand Down Expand Up @@ -47,5 +56,17 @@ mod Pathfinder {
let map = MapTrait::new(map_height, map_width, tiles);
JPSTrait::find_path(map, start, goal)
}

fn a_star(
self: @ContractState,
tiles: Span<Tile>,
map_width: u64,
map_height: u64,
start: (u64, u64),
goal: (u64, u64)
) -> Span<(u64, u64)> {
let map = MapTrait::new(map_height, map_width, tiles);
AStarTrait::find_path(map, start, goal)
}
}
}
Loading