Skip to content

Commit d554f54

Browse files
committed
.
1 parent 5e31927 commit d554f54

File tree

5 files changed

+136
-26
lines changed

5 files changed

+136
-26
lines changed

packages/demo/demo.rust.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ import { grid, snake } from "./sample";
2929

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

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-
// }
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+
38+
for (let j = i + snakeLength; j--; ) {
39+
highlightCell(path[j].x, path[j].y, "#123bde");
40+
}
3741
};
3842

3943
const input = document.createElement("input") as any;

packages/solver-r/src/grid.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ impl WalkableGrid {
6868
pub fn is_inside(&self, p: &Point) -> bool {
6969
self.grid.is_inside(p)
7070
}
71+
pub fn get_cell(&self, p: &Point) -> Cell {
72+
self.grid.get_cell(p)
73+
}
7174
}
7275

7376
pub const DIRECTION_RIGHT: Point = Point { x: 1, y: 0 };

packages/solver-r/src/lib.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,18 @@ pub fn ieat_free_cells(grid: &IGrid, snake: ISnake) -> Vec<IPoint> {
186186

187187
let (free_cells, _) = get_free_cells(&grid.grid, Cell::Color1);
188188

189-
let mut to_eat: HashSet<Point> = HashSet::new();
190-
to_eat.insert(Point { x: 6, y: 6 });
191-
to_eat.insert(Point { x: 5, y: 0 });
192-
193-
let path = get_path_to_eat_all(&grid, &snake, &to_eat);
194-
// let path = get_path_to_eat_all(&grid, &snake, &free_cells);
189+
let cells_to_eat = {
190+
let mut cells = free_cells.clone();
191+
cells.retain(|p| grid.get_cell(p) == Cell::Color1);
192+
cells
193+
};
194+
195+
// let mut to_eat: HashSet<Point> = HashSet::new();
196+
// to_eat.insert(Point { x: 6, y: 6 });
197+
// to_eat.insert(Point { x: 5, y: 0 });
198+
199+
// let path = get_path_to_eat_all(&grid, &snake, &to_eat);
200+
let path = get_path_to_eat_all(&grid, &snake, &cells_to_eat);
195201

196202
path.iter().map(IPoint::from).collect()
197203
}

packages/solver-r/src/snake_walk.rs

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use crate::astar_snake::get_snake_path;
44
use crate::grid::{get_distance, Point, WalkableGrid, DIRECTIONS};
55
use crate::snake::Snake;
66

7-
pub fn can_snake_reach_outside(grid: &WalkableGrid, snake: &Snake) -> bool {
7+
pub fn can_snake_reach_outside(grid: &WalkableGrid, snake: &[Point]) -> bool {
88
let mut open_list: Vec<Snake> = Vec::new();
9-
open_list.push(snake.clone());
9+
open_list.push(snake.to_vec());
1010

1111
let mut close_list: HashSet<Snake> = HashSet::new();
1212

@@ -51,6 +51,59 @@ pub fn can_snake_reach_outside(grid: &WalkableGrid, snake: &Snake) -> bool {
5151
false
5252
}
5353

54+
pub fn get_snake_path_to_outside(grid: &WalkableGrid, snake: &[Point]) -> Option<Vec<Point>> {
55+
let snake_length = snake.len();
56+
57+
let mut open_list: Vec<Snake> = Vec::new();
58+
open_list.push(snake.to_vec());
59+
60+
let mut close_list: HashSet<Snake> = HashSet::new();
61+
62+
while let Some(path) = open_list.pop() {
63+
for dir in DIRECTIONS {
64+
let next_head = Point {
65+
x: path[0].x + dir.x,
66+
y: path[0].y + dir.y,
67+
};
68+
69+
let head_collide_with_body = (0..snake_length).any(|i| path[i] == next_head);
70+
71+
if head_collide_with_body {
72+
continue;
73+
}
74+
75+
if !grid.is_inside(&next_head) {
76+
let mut path = path.clone();
77+
path.insert(0, next_head);
78+
return Some(path);
79+
}
80+
81+
if !grid.is_cell_walkable(&next_head) {
82+
continue;
83+
}
84+
85+
let next_path = {
86+
let mut path = path.clone();
87+
path.insert(0, next_head);
88+
path
89+
};
90+
91+
let next_snake = &next_path[0..snake_length];
92+
93+
if close_list.contains(next_snake) {
94+
continue;
95+
}
96+
97+
open_list.push(next_path);
98+
}
99+
100+
let snake = &path[0..snake_length];
101+
close_list.insert(snake.to_vec());
102+
}
103+
104+
None
105+
}
106+
54107
pub fn get_path_to_eat_all(
55108
grid: &WalkableGrid,
56109
snake: &[Point],
@@ -69,15 +122,13 @@ pub fn get_path_to_eat_all(
69122
let head = path[0];
70123

71124
let mut best_route: Option<Vec<Point>> = None;
125+
let mut best_target_unescapable: Option<Point> = None;
72126

73127
cells_to_eat.sort_by(|a, b| get_distance(a, &head).cmp(&get_distance(b, &head)));
74128

75-
for p in cells_to_eat.iter() {
76-
if path.contains(&p) {
77-
continue;
78-
}
129+
let snake = &path[0..snake_length];
79130

80-
let snake = &path[0..snake_length];
131+
for p in cells_to_eat.iter() {
81132
let max_weight = match best_route.as_ref() {
82133
None => usize::MAX,
83134
Some(path) => path.len() - snake_length,
@@ -86,11 +137,23 @@ pub fn get_path_to_eat_all(
86137
let res = get_snake_path(|c| grid.is_cell_walkable(c), snake, p, max_weight);
87138

88139
if let Some(sub_path) = res {
89-
if match best_route.as_ref() {
90-
None => true,
91-
Some(r) => sub_path.len() < r.len(),
92-
} {
93-
best_route = Some(sub_path);
140+
//
141+
// is it the route better yet ?
142+
let sub_path_is_better =
143+
best_route.as_ref().is_none_or(|r| sub_path.len() < r.len());
144+
if sub_path_is_better {
145+
//
146+
// ensure this does not lead to a position where the snake is stuck
147+
let next_snake = &sub_path[0..snake_length];
148+
if can_snake_reach_outside(grid, next_snake) {
149+
best_route = Some(sub_path);
150+
} else {
151+
// let's retain only the first target unescapable
152+
// as the cells_to_eat list is sorted by distance, it should be the closest
153+
if best_target_unescapable.is_none() {
154+
best_target_unescapable = Some(**p);
155+
}
156+
}
94157
}
95158
}
96159
}
@@ -103,6 +166,35 @@ pub fn get_path_to_eat_all(
103166
sub_path.truncate(sub_path.len() - snake_length);
104167
sub_path.append(&mut path);
105168
path = sub_path;
169+
} else if let Some(p) = best_target_unescapable {
170+
// let's got to the outside
171+
// and check again
172+
173+
let mut path_to_outside = get_snake_path_to_outside(grid, snake).unwrap();
174+
let outside_direction = {
175+
if path_to_outside[0].y < 0 {
176+
Point { x: 0, y: -1 }
177+
} else if path_to_outside[0].y >= grid.grid.height as i8 {
178+
Point { x: 0, y: 1 }
179+
} else if path_to_outside[0].x < 0 {
180+
Point { x: -1, y: 0 }
181+
} else if path_to_outside[0].x >= grid.grid.width as i8 {
182+
Point { x: 1, y: 0 }
183+
} else {
184+
panic!("not outside");
185+
}
186+
};
187+
for _ in 0..snake_length {
188+
let p = Point {
189+
x: path_to_outside[0].x + outside_direction.x,
190+
y: path_to_outside[0].y + outside_direction.y,
191+
};
192+
path_to_outside.insert(0, p);
193+
}
194+
195+
log::info!("unescapable {:?} ", p);
196+
panic!("impossible to path to cell to eat");
197+
//
106198
} else {
107199
panic!("impossible to path to cell to eat");
108200
}

packages/types/__fixtures__/grid.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,21 @@ export const closedO = createFromAscii(`
8080
`);
8181
export const tunnels = createFromAscii(`
8282
83-
### ### ###
84-
#.# #.# #.#
85-
#.# ### # #
83+
### ### ### ####
84+
#.# #.# #.# #..#
85+
#.# ### # # # #
8686
`);
8787
export const line = createFromAscii(`
8888
8989
#######
9090
.. #
9191
##### #
9292
`);
93+
export const trap = createFromAscii(`
94+
.#
95+
###
96+
97+
`);
9398

9499
const createRandom = (width: number, height: number, emptyP: number) => {
95100
const grid = createEmptyGrid(width, height);

0 commit comments

Comments
 (0)