From a238f66d4f9d6499d00a98a62361f37c78175cfe Mon Sep 17 00:00:00 2001 From: pagoru Date: Fri, 21 Mar 2025 13:58:22 +0100 Subject: [PATCH 1/4] wip --- .gitignore | 1 + Cargo.lock | 7 +++ Cargo.toml | 10 ++++ deno.json | 3 ++ preview/bundle.js | 66 +++++++++-------------- src/lib.rs | 1 + src/open_list.rs | 131 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 177 insertions(+), 42 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/open_list.rs diff --git a/.gitignore b/.gitignore index 428a1df..28de1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea node_modules coverage +target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a6b7534 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "oh_pathfinding" +version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b48c076 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "oh_pathfinding" +version = "0.0.0" +edition = "2024" + +[lib] +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] \ No newline at end of file diff --git a/deno.json b/deno.json index c5da76c..d935744 100644 --- a/deno.json +++ b/deno.json @@ -4,5 +4,8 @@ "exports": "./mod.ts", "imports": { "std/": "https://deno.land/std@0.224.0/" + }, + "tasks": { + "build:lib": "cargo build --release" } } diff --git a/preview/bundle.js b/preview/bundle.js index 415d7df..bec8d5a 100644 --- a/preview/bundle.js +++ b/preview/bundle.js @@ -1,8 +1,9 @@ // src/utils/grid.utils.ts var makeSquare = (layout) => { const maxLength = Math.max(layout.length, ...layout.map((row) => row.length)); - const squareLayout = Array.from({ length: maxLength }, () => - Array(maxLength).fill(null), + const squareLayout = Array.from( + { length: maxLength }, + () => Array(maxLength).fill(null) ); for (let i = 0; i < layout.length; i++) { for (let j = 0; j < layout[i].length; j++) { @@ -13,8 +14,9 @@ var makeSquare = (layout) => { }; var transpose = (matrix) => { const maxCols = Math.max(...matrix.map((row) => row.length)); - const transposed = Array.from({ length: maxCols }, () => - Array(matrix.length).fill(null), + const transposed = Array.from( + { length: maxCols }, + () => Array(matrix.length).fill(null) ); for (let i = 0; i < matrix.length; i++) { for (let j = 0; j < matrix[i].length; j++) { @@ -92,7 +94,7 @@ var findPath = (startPoint, endPoint, grid, config) => { const orthogonalCostMultiplier = config.orthogonalCostMultiplier ?? 1; const maxJumpCost = config.maxJumpCost ?? 5; const maxIterations = config.maxIterations ?? 99999; - const jumpDiagonals = config.jumpDiagonals ?? false; + const jumpBlockedDiagonals = config.jumpBlockedDiagonals ?? false; const index = (point) => { return point.y * grid.height + point.x; }; @@ -110,7 +112,7 @@ var findPath = (startPoint, endPoint, grid, config) => { while (true) { const target = { x: src.x + dirX * jumpDistance, - y: src.y + dirY * jumpDistance, + y: src.y + dirY * jumpDistance }; if (!grid.isWalkable(target)) { break; @@ -125,7 +127,7 @@ var findPath = (startPoint, endPoint, grid, config) => { if (totalCost < visited[targetIndex]) { visited[targetIndex] = totalCost; queue.push( - new PathNode(target, prevNode, totalCost, heuristic(target)), + new PathNode(target, prevNode, totalCost, heuristic(target)) ); } prevPoint = target; @@ -137,24 +139,13 @@ var findPath = (startPoint, endPoint, grid, config) => { }; const addDiagonal = (prevNode, src, srcCost, dirX, dirY) => { const target = { x: src.x + dirX, y: src.y + dirY }; - const moveCost = - srcCost + - (getMoveCostAt(src, target) ?? NOT_REACHED_COST) * diagonalCostMultiplier; + const moveCost = srcCost + (getMoveCostAt(src, target) ?? NOT_REACHED_COST) * diagonalCostMultiplier; const targetHeight = grid.getHeightAt(target); const aux1 = { x: src.x, y: src.y + dirY }; const aux2 = { x: src.x + dirX, y: src.y }; const targetIndex = index(target); - const canJumpDiagonals = - jumpDiagonals || - (grid.isWalkable(aux1) && - grid.isWalkable(aux2) && - targetHeight == grid.getHeightAt(aux1) && - targetHeight == grid.getHeightAt(aux2)); - if ( - grid.isWalkable(target) && - canJumpDiagonals && - moveCost < visited[targetIndex] - ) { + const canJumpDiagonals = jumpBlockedDiagonals || grid.isWalkable(aux1) && grid.isWalkable(aux2) && targetHeight == grid.getHeightAt(aux1) && targetHeight == grid.getHeightAt(aux2); + if (grid.isWalkable(target) && canJumpDiagonals && moveCost < visited[targetIndex]) { visited[targetIndex] = moveCost; queue.push(new PathNode(target, prevNode, moveCost, heuristic(target))); } @@ -253,24 +244,15 @@ var Grid = class _Grid { return new _Grid( this.width, this.height, - new Float32Array(this.heightMatrix), + new Float32Array(this.heightMatrix) ); } inBounds(point) { - return ( - point.x >= 0 && - point.x < this.width && - point.y >= 0 && - point.y < this.height - ); + return point.x >= 0 && point.x < this.width && point.y >= 0 && point.y < this.height; } isWalkable(point) { const heightAt = this.getHeightAt(point); - return ( - this.inBounds(point) && - heightAt !== null && - heightAt !== NON_WALKABLE_HEIGHT - ); + return this.inBounds(point) && heightAt !== null && heightAt !== NON_WALKABLE_HEIGHT; } walkMatrix(callback) { for (let y = 0; y < this.height; y++) { @@ -291,15 +273,15 @@ var Grid = class _Grid { } return matrix; } - findPath( - startPoint, - endPoint, - config = { - maxJumpCost: 5, - travelCosts: void 0, - }, - ) { + findPath(startPoint, endPoint, config = { + maxJumpCost: 5, + travelCosts: void 0 + }) { return findPath(startPoint, endPoint, this, config); } }; -export { Grid, makeSquare, transpose }; +export { + Grid, + makeSquare, + transpose +}; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a9fb459 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +mod open_list; \ No newline at end of file diff --git a/src/open_list.rs b/src/open_list.rs new file mode 100644 index 0000000..80b328d --- /dev/null +++ b/src/open_list.rs @@ -0,0 +1,131 @@ +use std::cmp::Ordering; +use std::fmt::Debug; + +#[derive(Debug)] +pub struct ListNode { + pub value: Value, + pub next: Option>> +} + +#[allow(dead_code)] +impl ListNode { + fn new(value: Value, next: Option>>) -> Self { + ListNode { value, next } + } +} + +pub trait Compare: Debug { + fn compare(&self, a: &Value, b: &Value) -> Ordering; +} + +#[derive(Debug)] +pub struct OpenList { + start: Option>>, + size: usize, + comparator: Box> +} +#[allow(dead_code)] +impl OpenList { + pub fn new(comparator: Box>) -> Self { + OpenList { + start: None, + size: 0, + comparator, + } + } + pub fn push(&mut self, value: Value) { + if self.start.is_none() { + self.start = Some(Box::new(ListNode { value, next: None })); + self.size += 1; + return; + } + + let start = self.start.take().unwrap(); + if self.comparator.compare(&value, &start.value) == Ordering::Less { + self.start = Some(Box::new(ListNode { + value, + next: Some(start), + })); + self.size += 1; + return; + } + + let mut current = Some(start); + let mut prev = &mut self.start; + + while let Some(mut node) = current.take() { + if let Some(next_node) = &node.next { + if self.comparator.compare(&value, &next_node.value) != Ordering::Greater { + node.next = Some(Box::new(ListNode { + value, + next: node.next.take(), + })); + *prev = Some(node); + self.size += 1; + return; + } + } + + current = node.next.take(); + *prev = Some(node); + prev = &mut prev.as_mut().unwrap().next; + } + + *prev = Some(Box::new(ListNode { value, next: None })); + self.size += 1; + } + + pub fn pop (&mut self) -> Result { + match self.start.take() { + None => Err("popping from an empty list".to_string()), + Some(mut popped) => { + self.start = popped.next.take(); + self.size -= 1; + Ok(popped.value) + } + } + } + + pub fn length(&self) -> usize { + self.size + } + + pub fn is_empty(&self) -> bool { + self.start.is_none() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug)] + struct IntComparator; + + impl Compare for IntComparator { + fn compare(&self, a: &i32, b: &i32) -> Ordering { + a.cmp(b) + } + } + + #[test] + fn test_open_list() { + let comparator = Box::new(IntComparator); + let mut list = OpenList::new(comparator); + + assert!(list.is_empty()); + assert_eq!(list.length(), 0); + + list.push(3); + list.push(1); + list.push(4); + + assert!(!list.is_empty()); + assert_eq!(list.length(), 3); + + assert_eq!(list.pop(), Ok(1)); + assert_eq!(list.pop(), Ok(3)); + assert_eq!(list.pop(), Ok(4)); + assert!(list.pop().is_err()); + } +} \ No newline at end of file From 6a9f9f7977f51379f8ef29f3bd0ad068a73deea3 Mon Sep 17 00:00:00 2001 From: pagoru Date: Mon, 24 Mar 2025 00:25:05 +0100 Subject: [PATCH 2/4] wip --- Cargo.lock | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +- src/grid.rs | 66 ++++++++++++++++++++++++++ src/lib.rs | 4 +- src/point.rs | 5 ++ 5 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 src/grid.rs create mode 100644 src/point.rs diff --git a/Cargo.lock b/Cargo.lock index a6b7534..a61733a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,135 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + [[package]] name = "oh_pathfinding" version = "0.0.0" +dependencies = [ + "js-sys", +] + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] diff --git a/Cargo.toml b/Cargo.toml index b48c076..71cf9b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2024" path = "src/lib.rs" crate-type = ["cdylib"] -[dependencies] \ No newline at end of file +[dependencies] +js-sys = "0.3.77" \ No newline at end of file diff --git a/src/grid.rs b/src/grid.rs new file mode 100644 index 0000000..1e31e83 --- /dev/null +++ b/src/grid.rs @@ -0,0 +1,66 @@ +use crate::point::Point; +use js_sys::Float32Array; +use js_sys::Math::{pow, sqrt}; + +#[derive(Debug)] +pub struct Grid { + width: usize, + height: usize, + height_matrix: Float32Array, +} + +impl Grid { + pub fn new(width: usize, height: usize, cost_matrix: Float32Array) -> Self { + Grid { + width, + height, + height_matrix: cost_matrix, + } + } + + pub fn get_height_at(&self, point: &Point) -> Option { + if !self.in_bounds(&point) { + return None; + } + self.height_matrix.at(self.index(point)) + } + pub fn distance(point_a: &Point, point_b: &Point) -> usize { + let x_expo = pow((point_a.x - point_b.x) as f64, 2f64); + let y_expo = pow((point_a.y - point_b.y) as f64, 2f64); + + sqrt(x_expo + y_expo) as usize + } + + pub fn index(&self, point: &Point) -> i32 { + (point.y * self.height + point.x) as i32 + } + + pub fn in_bounds(&self, point: &Point) -> bool { + point.x >= 0 && point.x < self.width && point.y >= 0 && point.y < self.height + } + + pub fn is_walkable(&self, point: &Point) -> bool { + let height_at = self.get_height_at(&point); + self.in_bounds(point) + && height_at.is_some() + //NON WALKABLE HEIGHT + && height_at.unwrap() != 0f32 + } + + pub fn walk_matrix(&self, mut callback: F) + where + F: FnMut(usize, usize, Option), + { + for y in 0..self.height { + for x in 0..self.width { + let index = (y * self.height + x) as i32; + let cost = self.height_matrix.at(index); + callback(x, y, cost); + } + } + } + + pub fn find_path(start_point: Point, end_point: Point) -> Vec { + Vec::new() + } +} diff --git a/src/lib.rs b/src/lib.rs index a9fb459..09aefc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,3 @@ -mod open_list; \ No newline at end of file +mod grid; +mod open_list; +mod point; diff --git a/src/point.rs b/src/point.rs new file mode 100644 index 0000000..bb7bdd6 --- /dev/null +++ b/src/point.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub struct Point { + pub x: usize, + pub y: usize, +} From d9e1c981bc0aafff82ac4d00ae84857d83717035 Mon Sep 17 00:00:00 2001 From: pagoru Date: Mon, 24 Mar 2025 19:47:40 +0100 Subject: [PATCH 3/4] wip --- Cargo.lock | 129 -------------------- Cargo.toml | 5 +- src/find_path_config.rs | 26 ++++ src/finder.rs | 241 ++++++++++++++++++++++++++++++++++++++ src/grid.rs | 64 ++++++---- src/grid/finder.ts | 12 +- src/lib.rs | 144 +++++++++++++++++++++++ src/path_node.rs | 35 ++++++ src/point.rs | 27 ++++- src/types/finder.types.ts | 4 +- src/utils.rs | 37 ++++++ 11 files changed, 561 insertions(+), 163 deletions(-) create mode 100644 src/find_path_config.rs create mode 100644 src/finder.rs create mode 100644 src/path_node.rs create mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a61733a..a6b7534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,135 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "log" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" - [[package]] name = "oh_pathfinding" version = "0.0.0" -dependencies = [ - "js-sys", -] - -[[package]] -name = "once_cell" -version = "1.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" - -[[package]] -name = "proc-macro2" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "syn" -version = "2.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] diff --git a/Cargo.toml b/Cargo.toml index 71cf9b1..6e5f790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,7 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -js-sys = "0.3.77" \ No newline at end of file + +[[bin]] +name = "start" +path = "src/lib.rs" \ No newline at end of file diff --git a/src/find_path_config.rs b/src/find_path_config.rs new file mode 100644 index 0000000..91d350b --- /dev/null +++ b/src/find_path_config.rs @@ -0,0 +1,26 @@ +#[derive(Debug, Clone, Default)] +pub struct FindPathConfig { + pub diagonal_cost_multiplier: f32, + pub orthogonal_cost_multiplier: i32, + pub max_jump_cost: u32, + pub max_iterations: u32, + pub jump_blocked_diagonals: bool, +} + +impl FindPathConfig { + pub fn new( + diagonal_cost_multiplier: Option, + orthogonal_cost_multiplier: Option, + max_jump_cost: Option, + max_iterations: Option, + jump_blocked_diagonals: Option, + ) -> Self { + FindPathConfig { + diagonal_cost_multiplier: diagonal_cost_multiplier.unwrap_or(1.0), + orthogonal_cost_multiplier: orthogonal_cost_multiplier.unwrap_or(1), + max_jump_cost: max_jump_cost.unwrap_or(5), + max_iterations: max_iterations.unwrap_or(99999), + jump_blocked_diagonals: jump_blocked_diagonals.unwrap_or(false), + } + } +} diff --git a/src/finder.rs b/src/finder.rs new file mode 100644 index 0000000..eda8303 --- /dev/null +++ b/src/finder.rs @@ -0,0 +1,241 @@ +use crate::find_path_config::FindPathConfig; +use crate::grid::Grid; +use crate::open_list::{Compare, OpenList}; +use crate::path_node::PathNode; +use crate::point::Point; +use std::cmp::Ordering; +use std::f32; + +#[derive(Debug)] +pub struct PathNodeComparator; + +impl Compare for PathNodeComparator { + fn compare(&self, a: &PathNode, b: &PathNode) -> Ordering { + let a_total = a.cost + a.heuristic_value as usize; + let b_total = b.cost + b.heuristic_value as usize; + + a_total.partial_cmp(&b_total).unwrap_or(Ordering::Equal) + } +} + +pub struct Finder { + not_reached_cost: i32, + visited: Vec, + travel_heuristic: Vec, + queue: OpenList, + + pub start_point: Point, + pub end_point: Point, + pub grid: Grid, + pub config: FindPathConfig, +} + +impl Finder { + pub fn new( + start_point: Point, + end_point: Point, + grid: Grid, + config: Option, + ) -> Self { + let not_reached_cost = 999999; + + let mut visited = vec![0.0; grid.width * grid.height]; + visited.fill(not_reached_cost as f32); + + let mut travel_heuristic = vec![0.0; grid.width * grid.height]; + travel_heuristic.fill(not_reached_cost as f32); + + Finder { + not_reached_cost, + visited, + travel_heuristic, + queue: OpenList::new(Box::new(PathNodeComparator)), + + start_point, + end_point, + grid, + config: config.unwrap_or(FindPathConfig::new(None, None, None, None, None)), + } + } + + fn index(&self, point: &Point) -> u32 { + (point.y * (self.grid.height as isize) + point.x) as u32 + } + + fn get_move_cost_at(&self, src: &Point, dst: &Point) -> Option { + if !self.grid.in_bounds(&src) || !self.grid.in_bounds(&dst) { + return None; + } + let src_height = self + .grid + .get_height_at(&src) + .or(Some(self.not_reached_cost as f32)) + .unwrap_or(0f32); + let dst_height = self + .grid + .get_height_at(&dst) + .or(Some(self.not_reached_cost as f32)) + .unwrap_or(0f32); + + if f32::abs(src_height - dst_height) > self.config.max_jump_cost as f32 { + return None; + } + + Some(1) + } + + fn heuristic(&self, point: &Point) -> f32 { + self.travel_heuristic[self.index(&point) as usize] + } + + fn add_orthogonal_jumps(&mut self, prev_node: &PathNode, dir_x: isize, dir_y: isize) { + let mut jump_distance = 1; + let mut accumulated_cost = 0; + let mut prev_point = prev_node.point.clone(); + + loop { + let target: Point = Point::new( + &prev_node.point.x + dir_x + jump_distance, + &prev_node.point.y + dir_y + jump_distance, + ); + if !self.grid.is_walkable(&target) { + break; + } + let move_cost = match self.get_move_cost_at(&prev_point, &target) { + Some(cost) => cost, + None => break, + }; + + accumulated_cost += (move_cost as i32) * self.config.orthogonal_cost_multiplier; + let target_index = self.index(&target) as i32; + let total_cost = prev_node.cost as i32 + accumulated_cost; + + if total_cost < self.visited[target_index as usize] as i32 { + self.visited[target_index as usize] = total_cost as f32; + self.queue.push(PathNode::new( + self.start_point.clone(), + None, + 0, + self.heuristic(&target), + )) + } + prev_point = target; + jump_distance = jump_distance + 1; + + if accumulated_cost > self.config.max_jump_cost as i32 { + break; + } + } + } + + fn add_diagonal(&mut self, prev_node: &PathNode, dir_x: isize, dir_y: isize) { + let target: Point = Point { + x: prev_node.point.x + dir_x, + y: prev_node.point.y + dir_y, + }; + let move_cost = prev_node.cost + + (self + .get_move_cost_at(&prev_node.point, &target) + .unwrap_or(self.not_reached_cost as usize)) + * self.config.diagonal_cost_multiplier as usize; + let target_height = self.grid.get_height_at(&target); + let aux1: Point = Point { + x: prev_node.point.x, + y: prev_node.point.y + dir_y, + }; + let aux2: Point = Point { + x: prev_node.point.x + dir_x, + y: prev_node.point.y, + }; + let target_index = self.index(&target); + + let can_jump_diagonals = self.config.jump_blocked_diagonals + || (self.grid.is_walkable(&aux1) + && self.grid.is_walkable(&aux2) + && target_height == self.grid.get_height_at(&aux1) + && target_height == self.grid.get_height_at(&aux2)); + + if self.grid.is_walkable(&target) + && can_jump_diagonals + && (move_cost as f32) < self.visited[target_index as usize] + { + self.visited[target_index as usize] = move_cost as f32; + self.queue.push(PathNode::new( + target.clone(), + Some(Box::new(prev_node.clone())), + move_cost, + self.heuristic(&target), + )) + } + } + + fn get_path_from_node(&self, last_node: PathNode) -> Vec { + let mut path: Vec = Vec::new(); + let mut node: Option> = Some(Box::new(last_node)); + loop { + match node { + None => { + break; + } + Some(current_node) => { + path.push(current_node.point); + node = current_node.from + } + } + } + + path.reverse(); + path + } + + pub fn find(&mut self) -> Vec { + if !self.grid.is_walkable(&self.start_point) || !self.grid.is_walkable(&self.end_point) { + return Vec::new(); + } + + self.grid.walk_matrix(|x, y, _cost| { + self.travel_heuristic[y * self.grid.height + x] = self + .grid + .distance(&Point::new(x as isize, y as isize), &self.end_point) + as f32; + }); + + let start_index = self.index(&self.start_point.clone()) as usize; + self.visited[start_index] = 0f32; + self.queue.push(PathNode::new( + self.start_point.clone(), + None, + 0, + self.heuristic(&self.start_point), + )); + + let mut iterations = 0; + + loop { + if self.queue.is_empty() { + break; + } + if iterations > self.config.max_iterations { + return Vec::new(); + } + iterations += 1; + let node = self.queue.pop().unwrap(); + + if node.point.x == self.end_point.x && node.point.y == self.end_point.y { + return self.get_path_from_node(node); + } + + self.add_orthogonal_jumps(&node, 0, -1); + self.add_orthogonal_jumps(&node, 0, 1); + self.add_orthogonal_jumps(&node, -1, 0); + self.add_orthogonal_jumps(&node, 1, 0); + + self.add_diagonal(&node, 1, 1); + self.add_diagonal(&node, -1, 1); + self.add_diagonal(&node, 1, -1); + self.add_diagonal(&node, -1, -1); + } + + Vec::new() + } +} diff --git a/src/grid.rs b/src/grid.rs index 1e31e83..0f4231e 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,16 +1,15 @@ use crate::point::Point; -use js_sys::Float32Array; -use js_sys::Math::{pow, sqrt}; +use crate::utils::make_square; #[derive(Debug)] pub struct Grid { - width: usize, - height: usize, - height_matrix: Float32Array, + pub width: usize, + pub height: usize, + pub height_matrix: Vec, } impl Grid { - pub fn new(width: usize, height: usize, cost_matrix: Float32Array) -> Self { + pub fn new(width: usize, height: usize, cost_matrix: Vec) -> Self { Grid { width, height, @@ -18,25 +17,52 @@ impl Grid { } } + pub fn from(matrix: Vec>) -> Result { + if matrix.get(0).is_none() || matrix.get(0).unwrap().get(0).is_none() { + return Err("Grid matrix cannot be empty!".to_string()); + } + + let mat = make_square(matrix); + let height = mat.len(); + let width = mat.get(0).unwrap().len(); + + if height != width { + return Err("Grid matrix must be square!".to_string()); + } + + let mut cost_matrix = vec![0.0; width * height]; + + for (y, row) in mat.iter().enumerate() { + for (x, &cost) in row.iter().enumerate() { + cost_matrix[y * width + x] = cost as f32 + } + } + + Ok(Grid::new(width, height, cost_matrix)) + } + pub fn get_height_at(&self, point: &Point) -> Option { if !self.in_bounds(&point) { return None; } - self.height_matrix.at(self.index(point)) + Some(self.height_matrix[self.index(point)]) } - pub fn distance(point_a: &Point, point_b: &Point) -> usize { - let x_expo = pow((point_a.x - point_b.x) as f64, 2f64); - let y_expo = pow((point_a.y - point_b.y) as f64, 2f64); + pub fn distance(&self, point_a: &Point, point_b: &Point) -> usize { + let x_expo = i32::pow((point_a.x - point_b.x) as i32, 2); + let y_expo = i32::pow((point_a.y - point_b.y) as i32, 2); - sqrt(x_expo + y_expo) as usize + i32::isqrt(x_expo + y_expo) as usize } - pub fn index(&self, point: &Point) -> i32 { - (point.y * self.height + point.x) as i32 + pub fn index(&self, point: &Point) -> usize { + (point.y * self.height as isize + point.x) as usize } pub fn in_bounds(&self, point: &Point) -> bool { - point.x >= 0 && point.x < self.width && point.y >= 0 && point.y < self.height + point.x >= 0 + && point.x < self.width as isize + && point.y >= 0 + && point.y < self.height as isize } pub fn is_walkable(&self, point: &Point) -> bool { @@ -53,14 +79,10 @@ impl Grid { { for y in 0..self.height { for x in 0..self.width { - let index = (y * self.height + x) as i32; - let cost = self.height_matrix.at(index); - callback(x, y, cost); + let index = y * self.height + x; + let cost = self.height_matrix[index]; + callback(x, y, Some(cost)); } } } - - pub fn find_path(start_point: Point, end_point: Point) -> Vec { - Vec::new() - } } diff --git a/src/grid/finder.ts b/src/grid/finder.ts index 9811619..0ff1d24 100644 --- a/src/grid/finder.ts +++ b/src/grid/finder.ts @@ -1,4 +1,4 @@ -import { Point, FindPathConfig } from "../types/main.ts"; +import { FindPathConfig, Point } from "../types/main.ts"; import { Grid } from "./grid.ts"; import { OpenList } from "./openList.ts"; @@ -112,16 +112,14 @@ export const findPath = ( dirY: number, ) => { const target: Point = { x: src.x + dirX, y: src.y + dirY }; - const moveCost = - srcCost + + const moveCost = srcCost + (getMoveCostAt(src, target) ?? NOT_REACHED_COST) * diagonalCostMultiplier; const targetHeight = grid.getHeightAt(target); const aux1: Point = { x: src.x, y: src.y + dirY }; const aux2: Point = { x: src.x + dirX, y: src.y }; const targetIndex = index(target); - const canJumpDiagonals = - jumpBlockedDiagonals || + const canJumpDiagonals = jumpBlockedDiagonals || (grid.isWalkable(aux1) && grid.isWalkable(aux2) && targetHeight == grid.getHeightAt(aux1) && @@ -143,7 +141,7 @@ export const findPath = ( const visited = new Float32Array(grid.width * grid.height); visited.fill(NOT_REACHED_COST); - config.travelCosts = visited; + // config.travelCosts = visited; // Distance to the end point, from A* const travelHeuristic = new Float32Array(grid.width * grid.height); @@ -152,7 +150,7 @@ export const findPath = ( grid.walkMatrix((x, y) => { travelHeuristic[y * grid.height + x] = grid.distance({ x, y }, endPoint); }); - config.travelHeuristic = travelHeuristic; + // config.travelHeuristic = travelHeuristic; const heuristic = (a: Point): number => { return travelHeuristic[index(a)]; diff --git a/src/lib.rs b/src/lib.rs index 09aefc2..10cb7a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,147 @@ +use crate::finder::Finder; +use crate::grid::Grid; +use crate::point::Point; +use crate::utils::{convert_array, transpose}; + +mod find_path_config; +mod finder; mod grid; mod open_list; +mod path_node; mod point; +mod utils; + +// pub fn find_path( +// start_point: Point, +// end_point: Point, +// grid: Grid, +// config: FindPathConfig, +// ) -> Vec { +// Vec::new() +// } + +pub fn main() { + let matrix = [ + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 7, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 7, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 7, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 7, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 12, 12, 12, 12, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 15, 15, 15, 15, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, + ], + ]; + let layout = transpose(convert_array(matrix)); + + let start = Point::new(19, 17); + let end = Point::new(19, 12); + let grid = Grid::from(layout); + + let mut finder = Finder::new(start, end, grid.unwrap(), None); + + println!( + "[{}]", + finder + .find() + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", ") + ); +} diff --git a/src/path_node.rs b/src/path_node.rs new file mode 100644 index 0000000..1f1c453 --- /dev/null +++ b/src/path_node.rs @@ -0,0 +1,35 @@ +use crate::point::Point; + +pub struct PathNode { + pub point: Point, + pub from: Option>, + pub cost: usize, + pub heuristic_value: f32, +} + +impl PathNode { + pub fn new( + point: Point, + from: Option>, + cost: usize, + heuristic_value: f32, + ) -> Self { + PathNode { + point, + from, + cost, + heuristic_value, + } + } +} + +impl Clone for PathNode { + fn clone(&self) -> Self { + Self { + point: self.point.clone(), + from: self.from.clone(), + cost: self.cost, + heuristic_value: self.heuristic_value, + } + } +} diff --git a/src/point.rs b/src/point.rs index bb7bdd6..c96def3 100644 --- a/src/point.rs +++ b/src/point.rs @@ -1,5 +1,28 @@ +use std::fmt; + #[derive(Debug)] pub struct Point { - pub x: usize, - pub y: usize, + pub x: isize, + pub y: isize, +} + +impl Point { + pub fn new(x: isize, y: isize) -> Self { + Point { x, y } + } +} + +impl Clone for Point { + fn clone(&self) -> Self { + Self { + x: self.x, + y: self.y, + } + } +} + +impl fmt::Display for Point { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } } diff --git a/src/types/finder.types.ts b/src/types/finder.types.ts index d404ce9..c7a138d 100644 --- a/src/types/finder.types.ts +++ b/src/types/finder.types.ts @@ -2,8 +2,6 @@ export type FindPathConfig = { maxJumpCost?: number; orthogonalCostMultiplier?: number; diagonalCostMultiplier?: number; - maxIterations?: number; - travelCosts?: Float32Array; - travelHeuristic?: Float32Array; + maxIterations?: number;; jumpBlockedDiagonals?: boolean; }; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..ce1ee1a --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,37 @@ +pub fn convert_array(arr: [[i32; 32]; 26]) -> Vec> { + arr.iter() + .map(|inner| inner.iter().map(|&x| x as usize).collect()) + .collect() +} +pub fn make_square(layout: Vec>) -> Vec> { + let max_length = layout + .iter() + .map(|row| row.len()) + .max() + .unwrap_or(0) + .max(layout.len()); + + let mut square = vec![vec![0; max_length]; max_length]; + + for (i, row) in layout.iter().enumerate() { + for (j, &val) in row.iter().enumerate() { + square[i][j] = val; + } + } + + square +} + +pub fn transpose(matrix: Vec>) -> Vec> { + let max_cols = matrix.iter().map(|row| row.len()).max().unwrap_or(0); + + let mut transposed = vec![vec![0; matrix.len()]; max_cols]; + + for (i, row) in matrix.iter().enumerate() { + for (j, &val) in row.iter().enumerate() { + transposed[j][i] = val; + } + } + + transposed +} From 53346dbdff81b804aacd7fcf1847818734852f62 Mon Sep 17 00:00:00 2001 From: pagoru Date: Tue, 25 Mar 2025 00:36:06 +0100 Subject: [PATCH 4/4] wip --- deno.lock | 3 +- src/find_path_config.rs | 12 +- src/finder.rs | 92 ++-- src/grid.rs | 11 +- src/grid/openList.ts | 5 +- src/lib.rs | 7 +- src/lib.ts | 912 ++++++++++++++++++++++++++++++++++++++ src/open_list.rs | 213 +++++---- src/path_node.rs | 21 +- src/types/finder.types.ts | 2 +- 10 files changed, 1087 insertions(+), 191 deletions(-) create mode 100644 src/lib.ts diff --git a/deno.lock b/deno.lock index 6147e1b..186e24d 100644 --- a/deno.lock +++ b/deno.lock @@ -1,5 +1,5 @@ { - "version": "3", + "version": "4", "remote": { "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", @@ -41,6 +41,7 @@ "workspace": { "packageJson": { "dependencies": [ + "npm:esbuild@0.23.0", "npm:prettier@3.3.3" ] } diff --git a/src/find_path_config.rs b/src/find_path_config.rs index 91d350b..233208b 100644 --- a/src/find_path_config.rs +++ b/src/find_path_config.rs @@ -1,8 +1,8 @@ #[derive(Debug, Clone, Default)] pub struct FindPathConfig { pub diagonal_cost_multiplier: f32, - pub orthogonal_cost_multiplier: i32, - pub max_jump_cost: u32, + pub orthogonal_cost_multiplier: f32, + pub max_jump_cost: f32, pub max_iterations: u32, pub jump_blocked_diagonals: bool, } @@ -10,15 +10,15 @@ pub struct FindPathConfig { impl FindPathConfig { pub fn new( diagonal_cost_multiplier: Option, - orthogonal_cost_multiplier: Option, - max_jump_cost: Option, + orthogonal_cost_multiplier: Option, + max_jump_cost: Option, max_iterations: Option, jump_blocked_diagonals: Option, ) -> Self { FindPathConfig { diagonal_cost_multiplier: diagonal_cost_multiplier.unwrap_or(1.0), - orthogonal_cost_multiplier: orthogonal_cost_multiplier.unwrap_or(1), - max_jump_cost: max_jump_cost.unwrap_or(5), + orthogonal_cost_multiplier: orthogonal_cost_multiplier.unwrap_or(1.0), + max_jump_cost: max_jump_cost.unwrap_or(5.0), max_iterations: max_iterations.unwrap_or(99999), jump_blocked_diagonals: jump_blocked_diagonals.unwrap_or(false), } diff --git a/src/finder.rs b/src/finder.rs index eda8303..1f59740 100644 --- a/src/finder.rs +++ b/src/finder.rs @@ -3,23 +3,19 @@ use crate::grid::Grid; use crate::open_list::{Compare, OpenList}; use crate::path_node::PathNode; use crate::point::Point; -use std::cmp::Ordering; use std::f32; #[derive(Debug)] pub struct PathNodeComparator; impl Compare for PathNodeComparator { - fn compare(&self, a: &PathNode, b: &PathNode) -> Ordering { - let a_total = a.cost + a.heuristic_value as usize; - let b_total = b.cost + b.heuristic_value as usize; - - a_total.partial_cmp(&b_total).unwrap_or(Ordering::Equal) + fn compare(&self, a: &PathNode, b: &PathNode) -> f32 { + (a.cost + a.heuristic_value) - (b.cost + b.heuristic_value) } } pub struct Finder { - not_reached_cost: i32, + not_reached_cost: f32, visited: Vec, travel_heuristic: Vec, queue: OpenList, @@ -37,13 +33,13 @@ impl Finder { grid: Grid, config: Option, ) -> Self { - let not_reached_cost = 999999; + let not_reached_cost = 999999.0; let mut visited = vec![0.0; grid.width * grid.height]; - visited.fill(not_reached_cost as f32); + visited.fill(not_reached_cost); let mut travel_heuristic = vec![0.0; grid.width * grid.height]; - travel_heuristic.fill(not_reached_cost as f32); + travel_heuristic.fill(not_reached_cost); Finder { not_reached_cost, @@ -58,72 +54,75 @@ impl Finder { } } - fn index(&self, point: &Point) -> u32 { - (point.y * (self.grid.height as isize) + point.x) as u32 + fn index(&self, point: &Point) -> usize { + (point.y * (self.grid.height as isize) + point.x) as usize } - fn get_move_cost_at(&self, src: &Point, dst: &Point) -> Option { + fn get_move_cost_at(&self, src: &Point, dst: &Point) -> Option { if !self.grid.in_bounds(&src) || !self.grid.in_bounds(&dst) { return None; } let src_height = self .grid .get_height_at(&src) - .or(Some(self.not_reached_cost as f32)) - .unwrap_or(0f32); + .unwrap_or(self.not_reached_cost); let dst_height = self .grid .get_height_at(&dst) - .or(Some(self.not_reached_cost as f32)) - .unwrap_or(0f32); - - if f32::abs(src_height - dst_height) > self.config.max_jump_cost as f32 { + .unwrap_or(self.not_reached_cost); + + println!( + "{} {}", + (src_height - dst_height).abs(), + self.config.max_jump_cost + ); + if (src_height - dst_height).abs() > self.config.max_jump_cost { return None; } - Some(1) + Some(1.0) } fn heuristic(&self, point: &Point) -> f32 { - self.travel_heuristic[self.index(&point) as usize] + self.travel_heuristic[self.index(&point)] } fn add_orthogonal_jumps(&mut self, prev_node: &PathNode, dir_x: isize, dir_y: isize) { let mut jump_distance = 1; - let mut accumulated_cost = 0; + let mut accumulated_cost = 0f32; let mut prev_point = prev_node.point.clone(); loop { let target: Point = Point::new( - &prev_node.point.x + dir_x + jump_distance, - &prev_node.point.y + dir_y + jump_distance, + prev_node.point.x + dir_x * jump_distance, + prev_node.point.y + dir_y * jump_distance, ); if !self.grid.is_walkable(&target) { - break; + return; } let move_cost = match self.get_move_cost_at(&prev_point, &target) { Some(cost) => cost, - None => break, + None => return, }; - accumulated_cost += (move_cost as i32) * self.config.orthogonal_cost_multiplier; - let target_index = self.index(&target) as i32; - let total_cost = prev_node.cost as i32 + accumulated_cost; + accumulated_cost += move_cost * self.config.orthogonal_cost_multiplier; + let target_index = self.index(&target); + let total_cost = prev_node.cost + accumulated_cost; - if total_cost < self.visited[target_index as usize] as i32 { - self.visited[target_index as usize] = total_cost as f32; + if total_cost < self.visited[target_index] { + self.visited[target_index] = total_cost; self.queue.push(PathNode::new( self.start_point.clone(), None, - 0, + 0f32, self.heuristic(&target), )) } prev_point = target; jump_distance = jump_distance + 1; - if accumulated_cost > self.config.max_jump_cost as i32 { - break; + if accumulated_cost > self.config.max_jump_cost { + return; } } } @@ -136,8 +135,8 @@ impl Finder { let move_cost = prev_node.cost + (self .get_move_cost_at(&prev_node.point, &target) - .unwrap_or(self.not_reached_cost as usize)) - * self.config.diagonal_cost_multiplier as usize; + .unwrap_or(self.not_reached_cost)) as f32 + * self.config.diagonal_cost_multiplier; let target_height = self.grid.get_height_at(&target); let aux1: Point = Point { x: prev_node.point.x, @@ -157,9 +156,9 @@ impl Finder { if self.grid.is_walkable(&target) && can_jump_diagonals - && (move_cost as f32) < self.visited[target_index as usize] + && move_cost < self.visited[target_index] { - self.visited[target_index as usize] = move_cost as f32; + self.visited[target_index] = move_cost; self.queue.push(PathNode::new( target.clone(), Some(Box::new(prev_node.clone())), @@ -189,23 +188,24 @@ impl Finder { } pub fn find(&mut self) -> Vec { + let empty = Vec::new(); + if !self.grid.is_walkable(&self.start_point) || !self.grid.is_walkable(&self.end_point) { - return Vec::new(); + return empty; } self.grid.walk_matrix(|x, y, _cost| { self.travel_heuristic[y * self.grid.height + x] = self .grid - .distance(&Point::new(x as isize, y as isize), &self.end_point) - as f32; + .distance(&Point::new(x as isize, y as isize), &self.end_point); }); - let start_index = self.index(&self.start_point.clone()) as usize; + let start_index = self.index(&self.start_point.clone()); self.visited[start_index] = 0f32; self.queue.push(PathNode::new( self.start_point.clone(), None, - 0, + 0f32, self.heuristic(&self.start_point), )); @@ -213,10 +213,10 @@ impl Finder { loop { if self.queue.is_empty() { - break; + return empty; } if iterations > self.config.max_iterations { - return Vec::new(); + return empty; } iterations += 1; let node = self.queue.pop().unwrap(); @@ -235,7 +235,5 @@ impl Finder { self.add_diagonal(&node, 1, -1); self.add_diagonal(&node, -1, -1); } - - Vec::new() } } diff --git a/src/grid.rs b/src/grid.rs index 0f4231e..252310b 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -47,15 +47,14 @@ impl Grid { } Some(self.height_matrix[self.index(point)]) } - pub fn distance(&self, point_a: &Point, point_b: &Point) -> usize { - let x_expo = i32::pow((point_a.x - point_b.x) as i32, 2); - let y_expo = i32::pow((point_a.y - point_b.y) as i32, 2); - - i32::isqrt(x_expo + y_expo) as usize + pub fn distance(&self, point_a: &Point, point_b: &Point) -> f32 { + let dx = (point_a.x - point_b.x) as f32; + let dy = (point_a.y - point_b.y) as f32; + (dx * dx + dy * dy).sqrt() } pub fn index(&self, point: &Point) -> usize { - (point.y * self.height as isize + point.x) as usize + (point.y as usize) * self.height + (point.x as usize) } pub fn in_bounds(&self, point: &Point) -> bool { diff --git a/src/grid/openList.ts b/src/grid/openList.ts index c33a3c7..b8f321a 100644 --- a/src/grid/openList.ts +++ b/src/grid/openList.ts @@ -1,4 +1,4 @@ -import { CompFn, ListNode } from "../types/main.ts"; +import type { CompFn, ListNode } from "../types/main.ts"; export class OpenList { private start: ListNode | null; @@ -25,7 +25,8 @@ export class OpenList { } let aux: ListNode = this.start; - while (aux.next !== null && this.comparator(value, aux.next.value) > 0) { + while (aux.next !== null + && this.comparator(value, aux.next.value) > 0) { aux = aux.next; } diff --git a/src/lib.rs b/src/lib.rs index 10cb7a3..0258d68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use crate::finder::Finder; use crate::grid::Grid; use crate::point::Point; use crate::utils::{convert_array, transpose}; +use std::time::Instant; mod find_path_config; mod finder; @@ -21,6 +22,7 @@ mod utils; // } pub fn main() { + let timer = Instant::now(); let matrix = [ [ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -129,8 +131,8 @@ pub fn main() { ]; let layout = transpose(convert_array(matrix)); - let start = Point::new(19, 17); - let end = Point::new(19, 12); + let start = Point::new(3, 3); + let end = Point::new(3, 4); let grid = Grid::from(layout); let mut finder = Finder::new(start, end, grid.unwrap(), None); @@ -144,4 +146,5 @@ pub fn main() { .collect::>() .join(", ") ); + println!("{:?}", timer.elapsed()) } diff --git a/src/lib.ts b/src/lib.ts new file mode 100644 index 0000000..ed92683 --- /dev/null +++ b/src/lib.ts @@ -0,0 +1,912 @@ +import { transpose } from "./utils/grid.utils.ts"; +import { Grid } from "./grid/grid.ts"; + +console.time("test"); +const original = [ + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 7, + 10, + 10, + 10, + 10, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 7, + 10, + 10, + 10, + 10, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 7, + 10, + 10, + 10, + 10, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 7, + 10, + 10, + 10, + 10, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 12, + 12, + 12, + 12, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 15, + 15, + 15, + 15, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + ], +]; + +const start = { + x: 19, + y: 17, +}; +const end = { + x: 19, + y: 12, +}; + +const layout = transpose(original); + +const grid = Grid.from(layout); + +const config = { + // maxJumpCost: 5, + // jumpBlockedDiagonals: Boolean(localStorage.getItem("jumpBlockedDiagonals")), +}; +const path = grid.findPath(start, end, config); + +console.log(path); +console.timeEnd("test"); diff --git a/src/open_list.rs b/src/open_list.rs index 80b328d..3e6845d 100644 --- a/src/open_list.rs +++ b/src/open_list.rs @@ -3,129 +3,126 @@ use std::fmt::Debug; #[derive(Debug)] pub struct ListNode { - pub value: Value, - pub next: Option>> + pub value: Value, + pub next: Option>>, } #[allow(dead_code)] impl ListNode { - fn new(value: Value, next: Option>>) -> Self { - ListNode { value, next } - } + fn new(value: Value, next: Option>>) -> Self { + ListNode { value, next } + } + + fn set_next(&mut self, next: Option>>) { + self.next = next; + } } pub trait Compare: Debug { - fn compare(&self, a: &Value, b: &Value) -> Ordering; + fn compare(&self, a: &Value, b: &Value) -> f32; } #[derive(Debug)] pub struct OpenList { - start: Option>>, - size: usize, - comparator: Box> + start: Option>>, + size: usize, + comparator: Box>, } #[allow(dead_code)] impl OpenList { - pub fn new(comparator: Box>) -> Self { - OpenList { - start: None, - size: 0, - comparator, - } - } - pub fn push(&mut self, value: Value) { - if self.start.is_none() { - self.start = Some(Box::new(ListNode { value, next: None })); - self.size += 1; - return; - } - - let start = self.start.take().unwrap(); - if self.comparator.compare(&value, &start.value) == Ordering::Less { - self.start = Some(Box::new(ListNode { - value, - next: Some(start), - })); - self.size += 1; - return; - } - - let mut current = Some(start); - let mut prev = &mut self.start; - - while let Some(mut node) = current.take() { - if let Some(next_node) = &node.next { - if self.comparator.compare(&value, &next_node.value) != Ordering::Greater { - node.next = Some(Box::new(ListNode { - value, - next: node.next.take(), - })); - *prev = Some(node); - self.size += 1; - return; - } - } - - current = node.next.take(); - *prev = Some(node); - prev = &mut prev.as_mut().unwrap().next; - } - - *prev = Some(Box::new(ListNode { value, next: None })); - self.size += 1; - } - - pub fn pop (&mut self) -> Result { - match self.start.take() { - None => Err("popping from an empty list".to_string()), - Some(mut popped) => { - self.start = popped.next.take(); - self.size -= 1; - Ok(popped.value) - } - } - } - - pub fn length(&self) -> usize { - self.size - } - - pub fn is_empty(&self) -> bool { - self.start.is_none() - } + pub fn new(comparator: Box>) -> Self { + OpenList { + start: None, + size: 0, + comparator, + } + } + pub fn push(&mut self, value: Value) { + if self.start.is_none() { + self.start = Some(Box::new(ListNode::new(value, None))); + self.size += 1; + return; + } + // Compare with the first element + if self.comparator(&value, &self.start.as_ref().unwrap().value) < 0 { + let old_start = self.start.take(); + self.start = Some(Box::new(ListNode::new(value, old_start))); + self.size += 1; + return; + } + + // Traverse to find insertion point + let mut current = &mut self.start; + while let Some(node) = current { + if node.next.is_none() { + // Insert at the end + node.next = Some(Box::new(ListNode::new(value, None))); + self.size += 1; + return; + } + // Compare with next node's value + if self.comparator(&value, &node.next.as_ref().unwrap().value) < 0 { + // Insert after current node + let next = node.next.take(); + node.next = Some(Box::new(ListNode::new(value, next))); + self.size += 1; + return; + } + current = &mut node.next; + } + } + + pub fn pop(&mut self) -> Result { + match self.start.take() { + None => Err("popping from an empty list".to_string()), + Some(popped) => { + self.start = popped.next; + self.size -= 1; + Ok(popped.value) + } + } + } + + pub fn length(&self) -> usize { + self.size + } + + pub fn is_empty(&self) -> bool { + self.start.is_none() + } } #[cfg(test)] mod tests { - use super::*; - - #[derive(Debug)] - struct IntComparator; - - impl Compare for IntComparator { - fn compare(&self, a: &i32, b: &i32) -> Ordering { - a.cmp(b) - } - } - - #[test] - fn test_open_list() { - let comparator = Box::new(IntComparator); - let mut list = OpenList::new(comparator); - - assert!(list.is_empty()); - assert_eq!(list.length(), 0); - - list.push(3); - list.push(1); - list.push(4); - - assert!(!list.is_empty()); - assert_eq!(list.length(), 3); - - assert_eq!(list.pop(), Ok(1)); - assert_eq!(list.pop(), Ok(3)); - assert_eq!(list.pop(), Ok(4)); - assert!(list.pop().is_err()); - } -} \ No newline at end of file + use super::*; + + #[derive(Debug)] + struct IntComparator; + + impl Compare for IntComparator { + fn compare(&self, a: &i32, b: &i32) -> Ordering { + a.cmp(b) + } + } + + #[test] + fn test_open_list() { + let comparator = Box::new(IntComparator); + let mut list = OpenList::new(comparator); + + assert!(list.is_empty()); + assert_eq!(list.length(), 0); + + list.push(3); + list.push(1); + list.push(4); + + assert!(!list.is_empty()); + assert_eq!(list.length(), 3); + + assert_eq!(list.pop(), Ok(1)); + assert_eq!(list.pop(), Ok(3)); + assert_eq!(list.pop(), Ok(4)); + assert!(list.pop().is_err()); + } +} diff --git a/src/path_node.rs b/src/path_node.rs index 1f1c453..b5e57e0 100644 --- a/src/path_node.rs +++ b/src/path_node.rs @@ -1,19 +1,15 @@ use crate::point::Point; +#[derive(Clone)] pub struct PathNode { pub point: Point, pub from: Option>, - pub cost: usize, + pub cost: f32, pub heuristic_value: f32, } impl PathNode { - pub fn new( - point: Point, - from: Option>, - cost: usize, - heuristic_value: f32, - ) -> Self { + pub fn new(point: Point, from: Option>, cost: f32, heuristic_value: f32) -> Self { PathNode { point, from, @@ -22,14 +18,3 @@ impl PathNode { } } } - -impl Clone for PathNode { - fn clone(&self) -> Self { - Self { - point: self.point.clone(), - from: self.from.clone(), - cost: self.cost, - heuristic_value: self.heuristic_value, - } - } -} diff --git a/src/types/finder.types.ts b/src/types/finder.types.ts index c7a138d..9513494 100644 --- a/src/types/finder.types.ts +++ b/src/types/finder.types.ts @@ -2,6 +2,6 @@ export type FindPathConfig = { maxJumpCost?: number; orthogonalCostMultiplier?: number; diagonalCostMultiplier?: number; - maxIterations?: number;; + maxIterations?: number; jumpBlockedDiagonals?: boolean; };