Skip to content

Commit 2902294

Browse files
committed
..
1 parent d1f7648 commit 2902294

9 files changed

Lines changed: 251 additions & 6 deletions

File tree

cargo/snk-grid/src/direction.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ impl Direction {
2525
Direction::RIGHT => Point { x: 1, y: 0 },
2626
}
2727
}
28+
29+
pub fn iter() -> impl Iterator<Item = Direction> {
30+
DIRECTIONS.iter().map(|dir| dir.clone())
31+
}
2832
}
2933

3034
pub fn add_direction(a: Point, dir: Direction) -> Point {

cargo/snk-grid/src/grid.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ impl Default for Color {
1414
Color::Empty
1515
}
1616
}
17+
impl Color {
18+
pub fn is_walkable(&self, walkable: Color) -> bool {
19+
*self <= walkable
20+
}
21+
pub fn is_empty(&self) -> bool {
22+
*self == Color::Empty
23+
}
24+
}
1725

1826
pub fn iter_rectangle_fill(width: i8, height: i8) -> impl Iterator<Item = Point> {
1927
(0..height).flat_map(move |y| (0..width).map(move |x| Point { x, y }))
@@ -72,6 +80,14 @@ impl<T: Copy> Grid<T> {
7280
cells,
7381
}
7482
}
83+
84+
pub fn iter_fill(&mut self) -> impl Iterator<Item = Point> {
85+
iter_rectangle_fill(self.width as i8, self.height as i8)
86+
}
87+
88+
pub fn iter_hull(&mut self) -> impl Iterator<Item = Point> {
89+
iter_rectangle_hull(self.width as i8, self.height as i8)
90+
}
7591
}
7692
impl<T: Default + Copy> Grid<T> {
7793
pub fn create_with_default(width: u8, height: u8) -> Grid<T> {
@@ -88,7 +104,14 @@ impl<T: Default + Copy> Grid<T> {
88104

89105
impl Grid<Color> {
90106
pub fn is_walkable(&self, walkable: Color, p: Point) -> bool {
91-
!self.is_inside(p) || self.get(p) <= walkable
107+
self.get_color(p).is_walkable(walkable)
108+
}
109+
pub fn get_color(&self, p: Point) -> Color {
110+
if !self.is_inside(p) {
111+
Color::Empty
112+
} else {
113+
self.get(p)
114+
}
92115
}
93116
}
94117

cargo/snk-grid/src/grid_samples.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,20 @@ pub enum SampleGrid {
2828
OneCave,
2929
Caves,
3030
Realistic,
31-
Labyrinthe,
31+
Labyrinth,
3232
RandomPack,
3333
SolidBlock,
3434
}
35+
pub const SAMPLEGRIDS: [SampleGrid; 8] = [
36+
SampleGrid::Empty,
37+
SampleGrid::OneDot,
38+
SampleGrid::OneCave,
39+
SampleGrid::Caves,
40+
SampleGrid::Realistic,
41+
SampleGrid::Labyrinth,
42+
SampleGrid::RandomPack,
43+
SampleGrid::SolidBlock,
44+
];
3545
pub fn get_grid_sample(g: SampleGrid) -> Grid<Color> {
3646
match g {
3747
SampleGrid::Empty => Grid::<_>::from(
@@ -88,7 +98,7 @@ _ ### #### _
8898
"#,
8999
),
90100

91-
SampleGrid::Labyrinthe => Grid::<_>::from(
101+
SampleGrid::Labyrinth => Grid::<_>::from(
92102
r#"
93103
################################################## #
94104
# #

cargo/snk-grid/src/snake.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ pub struct Snake4 {
1919
body: [Point; 4],
2020
}
2121

22+
impl Snake4 {
23+
pub fn from_points(points: [Point; 4]) -> Self {
24+
Snake4 { body: points }
25+
}
26+
}
27+
2228
impl Snake for Snake4 {
2329
fn get_head(&self) -> Point {
2430
self.body[0]

cargo/snk-js/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub fn log() {
4141
pub fn get_grid_sample(sample_name: String) -> IColorGrid {
4242
snk_grid::grid_samples::get_grid_sample(match &sample_name[..] {
4343
"empty" => SampleGrid::Empty,
44-
"labyrinthe" => SampleGrid::Labyrinthe,
44+
"labyrinthe" => SampleGrid::Labyrinth,
4545
"caves" => SampleGrid::Caves,
4646
"realistic" => SampleGrid::Realistic,
4747
"one-dot" => SampleGrid::OneDot,

cargo/snk-solver/src/cost_to_outside.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{collections::HashSet, usize};
22

33
use snk_grid::{
44
direction::iter_neighbour,
5-
grid::{iter_rectangle_hull, Color, Grid},
5+
grid::{Color, Grid, iter_rectangle_hull},
66
point::Point,
77
};
88

cargo/snk-solver/src/fitness.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use snk_grid::{
2+
direction::Direction,
3+
grid::{Color, Grid, iter_rectangle_fill},
4+
snake::{Snake, Snake4},
5+
};
6+
7+
pub struct SolutionFitness {
8+
pub remaining_color_count: u32,
9+
pub stack_fitness: u32,
10+
pub solution_fitness: u32,
11+
}
12+
13+
pub fn get_solution_fitness(
14+
grid: &Grid<Color>,
15+
snake: Snake4,
16+
solution: Vec<Direction>,
17+
) -> SolutionFitness {
18+
let mut grid = grid.clone();
19+
let mut snake = snake.clone();
20+
let mut stack = Vec::new();
21+
22+
let solution_length = solution.len() as u32;
23+
24+
for direction in solution {
25+
step(&mut grid, &mut snake, &mut stack, direction);
26+
}
27+
28+
let remaining_color_count = iter_rectangle_fill(grid.width as i8, grid.height as i8)
29+
.fold(0, |sum, p| {
30+
sum + if grid.get_color(p).is_empty() { 1 } else { 0 }
31+
});
32+
let stack_fitness = get_stack_fitness(&stack);
33+
let solution_fitness = remaining_color_count * 100_000 + stack_fitness * 1000 + solution_length;
34+
35+
SolutionFitness {
36+
remaining_color_count,
37+
stack_fitness,
38+
solution_fitness,
39+
}
40+
}
41+
42+
// a fitness that count the number of permutation to order the stack
43+
// 0 = perfect
44+
// the bigger fitness is, the worse the stack
45+
pub fn get_stack_fitness(stack: &Vec<Color>) -> u32 {
46+
let mut stack = stack.clone();
47+
let mut fitness = 0;
48+
49+
stack.push(Color::Color4);
50+
51+
for i in 0..(stack.len() - 1) {
52+
while stack[i] > stack[i + 1] {
53+
let mut j = 0;
54+
while stack[i + j] > stack[i + j + 1] {
55+
let tmp = stack[i + j + 1];
56+
stack[i + j + 1] = stack[i + j];
57+
stack[i + j] = tmp;
58+
j += 1;
59+
fitness += 1;
60+
}
61+
}
62+
}
63+
64+
fitness
65+
}
66+
67+
pub fn step(
68+
grid: &mut Grid<Color>,
69+
snake: &mut Snake4,
70+
stack: &mut Vec<Color>,
71+
direction: Direction,
72+
) -> () {
73+
snake.move_snake(direction);
74+
75+
let head = snake.get_head();
76+
77+
let c = grid.get_color(head);
78+
79+
if !c.is_empty() {
80+
grid.set(head, Color::Empty);
81+
stack.push(c);
82+
}
83+
}
84+
85+
#[test]
86+
fn it_should_compute_stack_fitness_for_empty_stack() {
87+
let stack = vec![];
88+
assert_eq!(get_stack_fitness(&stack), 0);
89+
}
90+
91+
#[test]
92+
fn it_should_compute_stack_fitness_for_orderer_stack() {
93+
let stack = vec![Color::Color1, Color::Color2, Color::Color2, Color::Color4];
94+
assert_eq!(get_stack_fitness(&stack), 0);
95+
}
96+
97+
#[test]
98+
fn it_should_compute_stack_fitness_for_unperfect_stack() {
99+
let stack = vec![Color::Color1, Color::Color2, Color::Color1];
100+
assert_eq!(get_stack_fitness(&stack), 1);
101+
}
102+
103+
#[test]
104+
fn it_should_compute_stack_fitness_for_unperfect_stack_2() {
105+
let stack = vec![
106+
Color::Color1,
107+
Color::Color2,
108+
Color::Color1,
109+
Color::Color1,
110+
Color::Color3,
111+
];
112+
assert_eq!(get_stack_fitness(&stack), 2);
113+
114+
let stack = vec![
115+
Color::Color1,
116+
Color::Color3,
117+
Color::Color2,
118+
Color::Color1,
119+
Color::Color1,
120+
];
121+
assert_eq!(get_stack_fitness(&stack), 5);
122+
}

cargo/snk-solver/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// pub mod cave;
22
// pub mod reach_outside;
33
pub mod cost_to_outside;
4+
pub mod fitness;
45
pub mod solver;

cargo/snk-solver/src/solver.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,85 @@ use snk_grid::{
44
snake::Snake4,
55
};
66

7-
pub fn solve(grid: Grid<Color>, snake: Snake4) -> Vec<Direction> {
7+
pub fn solve(grid: &Grid<Color>, snake: &Snake4) -> Vec<Direction> {
88
vec![]
99
}
10+
11+
#[cfg(test)]
12+
mod tests {
13+
use crate::fitness::get_solution_fitness;
14+
15+
use super::*;
16+
use snk_grid::{
17+
grid_samples::{SAMPLEGRIDS, SampleGrid, get_grid_sample},
18+
point::Point,
19+
snake::Snake4,
20+
};
21+
22+
#[test]
23+
fn it_should_found_solution_for_one_dot() {
24+
let grid = get_grid_sample(SampleGrid::OneDot);
25+
let snake = Snake4::from_points([
26+
Point { x: 0, y: -1 },
27+
Point { x: 1, y: -1 },
28+
Point { x: 2, y: -1 },
29+
Point { x: 3, y: -1 },
30+
]);
31+
let solution = solve(&grid, &snake);
32+
33+
let fitness = get_solution_fitness(&grid, snake, solution);
34+
35+
assert_eq!(
36+
fitness.remaining_color_count, 0,
37+
"Remaining color count should be 0"
38+
);
39+
assert_eq!(
40+
fitness.stack_fitness, 0,
41+
"Stack should be perfectly orderer"
42+
);
43+
}
44+
45+
#[test]
46+
fn it_should_found_solution_for_labyrinth() {
47+
let grid = get_grid_sample(SampleGrid::Labyrinth);
48+
let snake = Snake4::from_points([
49+
Point { x: 0, y: -1 },
50+
Point { x: 1, y: -1 },
51+
Point { x: 2, y: -1 },
52+
Point { x: 3, y: -1 },
53+
]);
54+
let solution = solve(&grid, &snake);
55+
56+
let fitness = get_solution_fitness(&grid, snake, solution);
57+
58+
assert_eq!(
59+
fitness.remaining_color_count, 0,
60+
"Remaining color count should be 0"
61+
);
62+
assert_eq!(
63+
fitness.stack_fitness, 0,
64+
"Stack should be perfectly orderer"
65+
);
66+
}
67+
68+
#[test]
69+
fn it_should_found_solution_with_no_remaining_colors() {
70+
for s in SAMPLEGRIDS {
71+
let grid = get_grid_sample(s);
72+
let snake = Snake4::from_points([
73+
Point { x: 0, y: -1 },
74+
Point { x: 1, y: -1 },
75+
Point { x: 2, y: -1 },
76+
Point { x: 3, y: -1 },
77+
]);
78+
let solution = solve(&grid, &snake);
79+
80+
let fitness = get_solution_fitness(&grid, snake, solution);
81+
82+
assert_eq!(
83+
fitness.remaining_color_count, 0,
84+
"Remaining color count should be 0"
85+
);
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)