Skip to content

Commit c6bbfdb

Browse files
committed
.
1 parent 13c5950 commit c6bbfdb

File tree

10 files changed

+206
-138
lines changed

10 files changed

+206
-138
lines changed

packages/demo/demo.rust.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,20 @@ import { grid, snake } from "./sample";
88

99
const g = api.IGrid.create(grid.width, grid.height, grid.data);
1010

11-
const freeCells = api.iget_free_cells(g);
11+
const { canvas, draw, highlightCell } = createCanvas(g);
12+
document.body.appendChild(canvas);
13+
draw({ width: g.width, height: g.height, data: g.data }, snake, []);
1214

1315
api.greet();
1416

17+
const a = performance.now();
1518
const path = api.ieat_free_cells(
1619
g,
1720
snakeToCells(snake).map((p) => api.IPoint.create(p.x, p.y)),
1821
);
22+
console.log(performance.now() - a);
1923

2024
{
21-
const { canvas, draw, highlightCell } = createCanvas(g);
22-
document.body.appendChild(canvas);
23-
2425
const snakeLength = snake.length / 2;
2526

2627
const onChange = () => {
@@ -29,12 +30,6 @@ import { grid, snake } from "./sample";
2930

3031
draw({ width: g.width, height: g.height, data: g.data }, s, []);
3132

32-
for (let i = freeCells.length / 2; i--; ) {
33-
const x = freeCells[i * 2 + 0];
34-
const y = freeCells[i * 2 + 1];
35-
highlightCell(x, y);
36-
}
37-
3833
for (let j = i + snakeLength; j--; ) {
3934
highlightCell(path[j].x, path[j].y, "#123bde");
4035
}

packages/demo/demo.rust_astar_snake.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ import { grid, snake } from "./sample";
1010
const path = api.iastar_snake(
1111
g,
1212
snakeToCells(snake).map((p) => api.IPoint.create(p.x, p.y)),
13-
api.IPoint.create(7, 2),
13+
api.IPoint.create(7, 0),
1414
);
1515

16-
console.log(snakeToCells(snake).map((p) => api.IPoint.create(p.x, p.y)));
17-
1816
{
1917
const { canvas, draw, highlightCell } = createCanvas(g);
2018
document.body.appendChild(canvas);

packages/draw/drawSnake.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export const drawSnake = (
1515
const cells = snakeToCells(snake);
1616

1717
for (let i = 0; i < cells.length; i++) {
18-
const u = (i + 1) * 0.6;
19-
18+
const u = Math.min((i + 1) * 0.6, o.sizeCell * 0.3);
19+
2020
ctx.save();
2121
ctx.fillStyle = o.colorSnake;
2222
ctx.translate(cells[i].x * o.sizeCell + u, cells[i].y * o.sizeCell + u);

packages/solver-r/src/exitable.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ use std::collections::HashSet;
22

33
use crate::grid::{Point, WalkableGrid, DIRECTIONS};
44

5-
pub fn initiate_exitable_with_border(
6-
exitable_cells: &mut HashSet<Point>,
7-
grid: &WalkableGrid,
8-
) -> () {
5+
pub fn propagate_exitable(exitable_cells: &mut HashSet<Point>, grid: &WalkableGrid) -> () {
96
for x in 0..(grid.grid.width as i8) {
107
{
118
let p = Point { x, y: 0 };
@@ -40,9 +37,7 @@ pub fn initiate_exitable_with_border(
4037
}
4138
}
4239
}
43-
}
4440

45-
pub fn propagate_exitable(exitable_cells: &mut HashSet<Point>, grid: &WalkableGrid) -> () {
4641
let mut open_list: Vec<Point> = exitable_cells.iter().map(|p| p.clone()).collect();
4742

4843
while let Some(p) = open_list.pop() {

packages/solver-r/src/free_cells.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use std::collections::HashSet;
2+
3+
use crate::grid::{Cell, Grid, Point};
4+
5+
pub fn get_free_cells(grid: &Grid, walkable: Cell) -> (HashSet<Point>, HashSet<Point>) {
6+
let mut free_cells: HashSet<Point> = HashSet::new();
7+
let mut one_way_cells: HashSet<Point> = HashSet::new();
8+
let mut open_list: HashSet<Point> = HashSet::new();
9+
10+
for x in 0..(grid.width as i8) {
11+
open_list.insert(Point { x, y: 0 });
12+
open_list.insert(Point {
13+
x,
14+
y: (grid.height as i8) - 1,
15+
});
16+
}
17+
for y in 0..(grid.height as i8) {
18+
open_list.insert(Point { x: 0, y });
19+
open_list.insert(Point {
20+
x: (grid.width as i8) - 1,
21+
y,
22+
});
23+
}
24+
open_list.retain(|p| grid.get_cell(&p) <= walkable);
25+
26+
let directions = [
27+
Point { x: 1, y: 0 },
28+
Point { x: -1, y: 0 },
29+
Point { x: 0, y: 1 },
30+
Point { x: 0, y: -1 },
31+
];
32+
33+
while let Some(p) = open_list.iter().next().cloned() {
34+
open_list.remove(&p);
35+
36+
let has_enough_free_exits = {
37+
let mut exit_count = 0;
38+
let mut visited: HashSet<Point> = HashSet::new();
39+
40+
for dir in directions {
41+
let neighbour = Point {
42+
x: p.x + dir.x,
43+
y: p.y + dir.y,
44+
};
45+
46+
if !visited.contains(&neighbour)
47+
&& (free_cells.contains(&neighbour) || !grid.is_inside(&neighbour))
48+
{
49+
visited.insert(neighbour);
50+
exit_count += 1;
51+
}
52+
53+
if grid.is_inside(&neighbour) && grid.get_cell(&neighbour) <= walkable {
54+
for alt in [-1, 1] {
55+
let corner = {
56+
if neighbour.x != 0 {
57+
Point {
58+
x: neighbour.x,
59+
y: neighbour.y + alt,
60+
}
61+
} else {
62+
Point {
63+
x: neighbour.x + alt,
64+
y: neighbour.y,
65+
}
66+
}
67+
};
68+
69+
if !visited.contains(&neighbour)
70+
&& !visited.contains(&corner)
71+
&& (free_cells.contains(&corner) || !grid.is_inside(&corner))
72+
{
73+
visited.insert(neighbour);
74+
visited.insert(corner);
75+
exit_count += 1;
76+
}
77+
}
78+
}
79+
}
80+
81+
exit_count >= 2
82+
};
83+
84+
if has_enough_free_exits {
85+
free_cells.insert(p);
86+
87+
for dir in directions {
88+
let neighbour = Point {
89+
x: p.x + dir.x,
90+
y: p.y + dir.y,
91+
};
92+
93+
if !free_cells.contains(&neighbour)
94+
&& grid.is_inside(&neighbour)
95+
&& grid.get_cell(&neighbour) <= walkable
96+
{
97+
open_list.insert(neighbour);
98+
}
99+
}
100+
} else {
101+
one_way_cells.insert(p);
102+
}
103+
}
104+
105+
one_way_cells.retain(|p| !free_cells.contains(&p));
106+
107+
(free_cells, one_way_cells)
108+
}
109+
110+
#[test]
111+
fn it_should_collect_free_cell() {
112+
let mut grid = Grid::create_empty(2, 2);
113+
114+
grid.set_cell(&Point { x: 1, y: 1 }, Cell::Color2);
115+
116+
let (free_cells, _) = get_free_cells(&grid, Cell::Color1);
117+
118+
assert_eq!(
119+
free_cells,
120+
HashSet::from([
121+
//
122+
Point { x: 0, y: 0 },
123+
Point { x: 0, y: 1 },
124+
Point { x: 1, y: 0 },
125+
])
126+
);
127+
}

packages/solver-r/src/grid.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,28 @@ impl Grid {
6565
pub fn is_inside_margin(&self, p: &Point, m: i8) -> bool {
6666
-m <= p.x && p.x < (self.width as i8) + m && -m <= p.y && p.y < (self.height as i8) + m
6767
}
68+
pub fn iter(&self) -> impl Iterator<Item = Point> {
69+
let mut i = 0;
70+
let width = self.width;
71+
let height = self.height as usize;
72+
std::iter::from_fn(move || {
73+
let p = Point {
74+
x: (i / height) as i8,
75+
y: (i % height) as i8,
76+
};
77+
78+
i += 1;
79+
80+
if p.x >= (width as i8) {
81+
None
82+
} else {
83+
Some(p)
84+
}
85+
})
86+
}
6887
}
6988

89+
#[derive(Clone)]
7090
pub struct WalkableGrid {
7191
pub grid: Grid,
7292
walkable: Cell,
@@ -115,3 +135,17 @@ fn it_should_grid_setter() {
115135

116136
assert_eq!(grid.get_cell(&Point { x: 12, y: 3 }), Cell::Color1);
117137
}
138+
#[test]
139+
fn it_should_iterate() {
140+
let grid = Grid::create_empty(2, 2);
141+
142+
assert_eq!(
143+
grid.iter().collect::<HashSet<_>>(),
144+
HashSet::from([
145+
Point { x: 0, y: 0 },
146+
Point { x: 0, y: 1 },
147+
Point { x: 1, y: 0 },
148+
Point { x: 1, y: 1 },
149+
])
150+
);
151+
}

packages/solver-r/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
mod _test_grid_samples;
12
mod astar;
23
mod astar_snake;
34
mod exitable;
5+
mod free_cells;
46
mod grid;
57
mod snake;
68
mod snake_compact;
@@ -9,14 +11,15 @@ mod solver;
911

1012
use std::collections::HashSet;
1113

14+
use _test_grid_samples::{get_grid_sample, SampleGrid};
1215
use astar::get_path;
1316
use astar_snake::get_snake_path;
14-
use exitable::{initiate_exitable_with_border, propagate_exitable};
17+
use exitable::propagate_exitable;
18+
use free_cells::get_free_cells;
1519
use grid::{Cell, Grid, Point, WalkableGrid};
1620
use js_sys;
1721
use log::info;
1822
use snake_walk::get_path_to_eat_all;
19-
use solver::get_free_cells;
2023
use wasm_bindgen::prelude::*;
2124

2225
#[wasm_bindgen]
@@ -181,7 +184,6 @@ pub fn ieat_free_cells(grid: &IGrid, snake: ISnake) -> Vec<IPoint> {
181184
let snake: Vec<Point> = snake.iter().map(Point::from).collect();
182185

183186
let mut exitable_cells = HashSet::new();
184-
initiate_exitable_with_border(&mut exitable_cells, &grid);
185187
propagate_exitable(&mut exitable_cells, &grid);
186188

187189
let cells_to_eat = {
@@ -197,6 +199,7 @@ pub fn ieat_free_cells(grid: &IGrid, snake: ISnake) -> Vec<IPoint> {
197199
let (path, unreachable) = get_path_to_eat_all(&grid, &snake, &cells_to_eat);
198200

199201
log::info!("unreachable {:?}", unreachable);
202+
log::info!(" {:?}", get_grid_sample(SampleGrid::RandomPack).cells);
200203

201204
path.iter().map(IPoint::from).collect()
202205
}

packages/solver-r/src/snake_walk.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ pub fn get_path_to_eat_all(
246246
}
247247
}
248248

249-
path.reverse();
249+
path.truncate(path.len() - snake_length);
250250

251251
(path, cells_unexitable)
252252
}

0 commit comments

Comments
 (0)