Skip to content

Commit b6740b5

Browse files
committed
...
1 parent badef7d commit b6740b5

File tree

5 files changed

+309
-41
lines changed

5 files changed

+309
-41
lines changed

cargo/snk-grid/src/direction.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ pub fn iter_neighbour(p: Point) -> impl Iterator<Item = Point> {
7676
iter_directions().map(move |dir| add_direction(p, dir))
7777
}
7878

79+
pub fn sub_direction(a: Point, b: Point) -> Direction {
80+
let x = a.x - b.x;
81+
let y = a.y - b.y;
82+
match (x, y) {
83+
(0, 1) => Direction::UP,
84+
(0, -1) => Direction::DOWN,
85+
(-1, 0) => Direction::LEFT,
86+
(1, 0) => Direction::RIGHT,
87+
_ => panic!("Invalid direction"),
88+
}
89+
}
90+
7991
#[test]
8092
fn it_should_iter_direction() {
8193
let directions: HashSet<_> = iter_directions().collect();
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
use snk_grid::{
2+
color::Color,
3+
direction::{Direction, iter_directions},
4+
grid::Grid,
5+
grid_samples::get_grid_sample,
6+
point::{Point, get_distance},
7+
snake::{Snake, Snake4, snake_will_self_collide},
8+
};
9+
use std::collections::{BinaryHeap, HashMap};
10+
11+
use crate::cost::Cost;
12+
13+
#[derive(Clone, Debug)]
14+
struct Node {
15+
pub snake: Snake4,
16+
pub cost: Cost,
17+
pub f: Cost,
18+
pub path: Vec<Direction>,
19+
}
20+
21+
impl Eq for Node {}
22+
impl PartialEq for Node {
23+
fn eq(&self, other: &Self) -> bool {
24+
self.path == other.path
25+
}
26+
}
27+
impl Ord for Node {
28+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
29+
other.f.cmp(&self.f)
30+
}
31+
}
32+
impl PartialOrd for Node {
33+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
34+
Some(self.cmp(other))
35+
}
36+
}
37+
38+
// TODO: when the snake have moved at least it's length, it's useless to come back to a cell of it's body
39+
// -> we could likely get away with moving cell by cell rather than the whole snake
40+
pub fn get_snake_path(
41+
grid: &Grid<Color>,
42+
from: &Snake4,
43+
to: Point,
44+
max_cost: Cost,
45+
) -> Option<(Vec<Direction>, Cost)> {
46+
let mut open_list: BinaryHeap<Node> = BinaryHeap::new();
47+
let mut close_list: HashMap<Snake4, Cost> = HashMap::new();
48+
49+
open_list.push(Node {
50+
snake: from.clone(),
51+
cost: Cost::zero(),
52+
f: Cost::zero(),
53+
path: Vec::new(),
54+
});
55+
56+
let mut loop_count = 0;
57+
58+
while let Some(node) = open_list.pop() {
59+
loop_count += 1;
60+
61+
if loop_count > 20_000 {
62+
panic!("loop_count exceeded")
63+
}
64+
65+
{
66+
let head = node.snake.get_head();
67+
68+
if to == head {
69+
return Some((node.path, node.cost));
70+
}
71+
}
72+
73+
for dir in iter_directions() {
74+
// log::info!(
75+
// " - snake {:?} dir {:?}, {:?}",
76+
// node.snake,
77+
// dir,
78+
// snake_will_self_collide(&node.snake, dir)
79+
// );
80+
81+
if snake_will_self_collide(&node.snake, dir) {
82+
continue;
83+
}
84+
let snake = node.snake.clone_and_move(dir);
85+
let head = snake.get_head();
86+
87+
if !grid.is_inside_margin(head, 2) {
88+
continue;
89+
}
90+
91+
let cost = node.cost + grid.get_color(head).into();
92+
let distance = get_distance(head, to);
93+
94+
let f = cost + Cost::from(Color::Empty) * (distance as u64);
95+
if f > max_cost {
96+
continue;
97+
}
98+
99+
if let Some(last_cost) = close_list.get(&snake)
100+
&& *last_cost <= cost
101+
{
102+
continue;
103+
}
104+
105+
close_list.insert(snake.clone(), node.cost);
106+
107+
let mut path = node.path.clone();
108+
path.push(dir);
109+
open_list.push(Node {
110+
snake,
111+
cost,
112+
f,
113+
path,
114+
});
115+
}
116+
}
117+
118+
None
119+
}
120+
121+
#[test]
122+
fn it_should_find_simple_path() {
123+
let snake = Snake4::from_points([
124+
Point { x: 0, y: 0 },
125+
Point { x: 1, y: 0 },
126+
Point { x: 2, y: 0 },
127+
Point { x: 3, y: 0 },
128+
]);
129+
let grid = Grid::<_>::from(
130+
r#"
131+
_ _
132+
_ _
133+
_ _
134+
_ _
135+
"#,
136+
);
137+
let (path, cost) = get_snake_path(&grid, &snake, Point { x: 0, y: 3 }, Cost::max()).unwrap();
138+
139+
assert_eq!(
140+
path,
141+
vec![
142+
//
143+
Direction::DOWN,
144+
Direction::DOWN,
145+
Direction::DOWN,
146+
]
147+
)
148+
}
149+
150+
#[test]
151+
fn it_should_find_path_out_of_labyrinth() {
152+
let snake = Snake4::from_points([
153+
Point { x: 0, y: -1 },
154+
Point { x: 1, y: -1 },
155+
Point { x: 2, y: -1 },
156+
Point { x: 3, y: -1 },
157+
]);
158+
let grid = get_grid_sample(snk_grid::grid_samples::SampleGrid::Labyrinth);
159+
160+
assert_eq!(grid.get_color(Point { x: 1, y: 5 }), Color::Color1);
161+
162+
let (path, cost) = get_snake_path(&grid, &snake, Point { x: 1, y: 5 }, Cost::max()).unwrap();
163+
164+
println!("{:?} {:?}", path, cost);
165+
166+
assert!(cost < Cost::from(Color::Color1) * 2)
167+
}
168+
169+
#[test]
170+
fn it_should_not_self_collide() {
171+
let snake = Snake4::from_points([
172+
Point { x: 0, y: 1 },
173+
Point { x: 1, y: 1 },
174+
Point { x: 2, y: 1 },
175+
Point { x: 3, y: 1 },
176+
]);
177+
let grid = Grid::<_>::from(
178+
r#"
179+
#########
180+
.#
181+
#########
182+
"#,
183+
);
184+
185+
assert_eq!(grid.get_color(Point { x: 7, y: 1 }), Color::Color1);
186+
187+
let (path, cost) = get_snake_path(&grid, &snake, Point { x: 7, y: 1 }, Cost::max()).unwrap();
188+
189+
println!("{:?} {:?}", path, cost);
190+
191+
assert!(cost < Cost::from(Color::Color4));
192+
assert!(cost > Cost::from(Color::Color1) + Cost::from(Color::Empty) * 5);
193+
}

0 commit comments

Comments
 (0)