@@ -4,9 +4,9 @@ use crate::astar_snake::get_snake_path;
44use crate :: grid:: { get_distance, Point , WalkableGrid , DIRECTIONS } ;
55use 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+
54107pub 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 }
0 commit comments