Skip to content

Commit 21e28f1

Browse files
committed
.
1 parent 7ddd020 commit 21e28f1

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

packages/demo/demo.rust_astar.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { createCanvas } from "./canvas";
2+
import "./menu";
3+
import { grid } from "./sample";
4+
5+
(async () => {
6+
const api = await import("@snk/solver-r");
7+
8+
const g = api.IGrid.create(grid.width, grid.height, grid.data);
9+
const path = api.iastar(
10+
g,
11+
api.IPoint.create(-1, 0),
12+
api.IPoint.create(47, 4),
13+
);
14+
15+
{
16+
const { canvas, draw, highlightCell } = createCanvas(g);
17+
document.body.appendChild(canvas);
18+
19+
draw({ width: g.width, height: g.height, data: g.data }, [] as any, []);
20+
21+
if (path)
22+
for (const p of path) {
23+
highlightCell(p.x, p.y);
24+
}
25+
}
26+
})();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { snakeToCells } from "@snk/types/snake";
2+
import { createCanvas } from "./canvas";
3+
import "./menu";
4+
import { grid, snake } from "./sample";
5+
6+
(async () => {
7+
const api = await import("@snk/solver-r");
8+
9+
const g = api.IGrid.create(grid.width, grid.height, grid.data);
10+
const path = api.iastar_snake(
11+
g,
12+
snakeToCells(snake).map((p) => api.IPoint.create(p.x, p.y)),
13+
api.IPoint.create(7, 2),
14+
);
15+
16+
console.log(snakeToCells(snake).map((p) => api.IPoint.create(p.x, p.y)));
17+
18+
{
19+
const { canvas, draw, highlightCell } = createCanvas(g);
20+
document.body.appendChild(canvas);
21+
22+
draw({ width: g.width, height: g.height, data: g.data }, [] as any, []);
23+
24+
console.log(path);
25+
26+
if (path)
27+
for (const p of path) {
28+
highlightCell(p.x, p.y);
29+
}
30+
}
31+
})();

packages/solver-r/src/astar_snake.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use crate::grid::{get_distance, Point, DIRECTIONS};
2+
use crate::snake::Snake;
3+
use std::cmp::Reverse;
4+
use std::collections::{BinaryHeap, HashSet};
5+
6+
struct Node {
7+
h: usize,
8+
path: Vec<Point>,
9+
}
10+
11+
impl Eq for Node {}
12+
impl PartialEq for Node {
13+
fn eq(&self, other: &Self) -> bool {
14+
self.path == other.path
15+
}
16+
}
17+
impl Ord for Node {
18+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
19+
let f1 = self.h + self.path.len();
20+
let f2 = other.h + other.path.len();
21+
f1.cmp(&f2)
22+
}
23+
}
24+
impl PartialOrd for Node {
25+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
26+
Some(self.cmp(other))
27+
}
28+
}
29+
30+
pub fn get_snake_path<F>(mut walkable: F, snake: &Snake, end: &Point) -> Option<Vec<Point>>
31+
where
32+
F: FnMut(&Point) -> bool,
33+
{
34+
let snake_length = snake.len();
35+
36+
let mut open_list = BinaryHeap::new();
37+
let mut close_list: HashSet<Vec<Point>> = HashSet::new();
38+
39+
open_list.push(Reverse(Node {
40+
path: snake.clone(),
41+
h: get_distance(&snake[0], &end) as usize,
42+
}));
43+
44+
while let Some(n) = open_list.pop() {
45+
let node = n.0;
46+
47+
for dir in DIRECTIONS {
48+
let next_head = Point {
49+
x: node.path[0].x + dir.x,
50+
y: node.path[0].y + dir.y,
51+
};
52+
53+
let head_collide_with_body = (0..snake_length).any(|i| node.path[i] == next_head);
54+
55+
if head_collide_with_body {
56+
continue;
57+
}
58+
59+
if &next_head == end {
60+
let mut path = node.path.clone();
61+
path.insert(0, next_head);
62+
return Some(path);
63+
}
64+
65+
if !walkable(&next_head) {
66+
continue;
67+
}
68+
69+
let next_path = {
70+
let mut path = node.path.clone();
71+
path.insert(0, next_head);
72+
path
73+
};
74+
75+
let next_snake = &next_path[0..snake_length];
76+
77+
if close_list.contains(next_snake) {
78+
continue;
79+
}
80+
81+
let h = get_distance(&next_head, &end) as usize;
82+
83+
open_list.push(Reverse(Node { path: next_path, h }));
84+
}
85+
86+
let snake = {
87+
// TODO : avoid cloning the whole vec
88+
let mut s = node.path.clone();
89+
s.truncate(snake_length);
90+
s
91+
};
92+
93+
close_list.insert(snake);
94+
}
95+
96+
None
97+
}
98+
99+
#[test]
100+
fn it_should_find_path() {
101+
let path = get_snake_path(
102+
|_| true,
103+
&vec![Point { x: 0, y: 0 }, Point { x: -1, y: 0 }],
104+
&Point { x: 3, y: 0 },
105+
);
106+
107+
assert_eq!(
108+
path,
109+
Some(vec![
110+
//
111+
Point { x: 3, y: 0 },
112+
Point { x: 2, y: 0 },
113+
Point { x: 1, y: 0 },
114+
Point { x: 0, y: 0 },
115+
Point { x: -1, y: 0 },
116+
])
117+
);
118+
}
119+
120+
#[test]
121+
fn it_should_180_to_find_path() {
122+
let path = get_snake_path(
123+
|_| true,
124+
&vec![Point { x: 0, y: 0 }, Point { x: -1, y: 0 }],
125+
&Point { x: -2, y: 0 },
126+
);
127+
128+
assert_eq!(
129+
path,
130+
Some(vec![
131+
Point { x: -2, y: 0 },
132+
Point { x: -2, y: 1 },
133+
Point { x: -1, y: 1 },
134+
Point { x: 0, y: 1 },
135+
Point { x: 0, y: 0 },
136+
Point { x: -1, y: 0 }
137+
])
138+
);
139+
}

0 commit comments

Comments
 (0)