@@ -4,9 +4,9 @@ use crate::astar_snake::get_snake_path;
4
4
use crate :: grid:: { get_distance, Point , WalkableGrid , DIRECTIONS } ;
5
5
use crate :: snake:: Snake ;
6
6
7
- pub fn can_snake_reach_outside ( grid : & WalkableGrid , snake : & Snake ) -> bool {
7
+ pub fn can_snake_reach_outside ( grid : & WalkableGrid , snake : & [ Point ] ) -> bool {
8
8
let mut open_list: Vec < Snake > = Vec :: new ( ) ;
9
- open_list. push ( snake. clone ( ) ) ;
9
+ open_list. push ( snake. to_vec ( ) ) ;
10
10
11
11
let mut close_list: HashSet < Snake > = HashSet :: new ( ) ;
12
12
@@ -51,6 +51,59 @@ pub fn can_snake_reach_outside(grid: &WalkableGrid, snake: &Snake) -> bool {
51
51
false
52
52
}
53
53
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
+
54
107
pub fn get_path_to_eat_all (
55
108
grid : & WalkableGrid ,
56
109
snake : & [ Point ] ,
@@ -69,15 +122,13 @@ pub fn get_path_to_eat_all(
69
122
let head = path[ 0 ] ;
70
123
71
124
let mut best_route: Option < Vec < Point > > = None ;
125
+ let mut best_target_unescapable: Option < Point > = None ;
72
126
73
127
cells_to_eat. sort_by ( |a, b| get_distance ( a, & head) . cmp ( & get_distance ( b, & head) ) ) ;
74
128
75
- for p in cells_to_eat. iter ( ) {
76
- if path. contains ( & p) {
77
- continue ;
78
- }
129
+ let snake = & path[ 0 ..snake_length] ;
79
130
80
- let snake = & path [ 0 ..snake_length ] ;
131
+ for p in cells_to_eat . iter ( ) {
81
132
let max_weight = match best_route. as_ref ( ) {
82
133
None => usize:: MAX ,
83
134
Some ( path) => path. len ( ) - snake_length,
@@ -86,11 +137,23 @@ pub fn get_path_to_eat_all(
86
137
let res = get_snake_path ( |c| grid. is_cell_walkable ( c) , snake, p, max_weight) ;
87
138
88
139
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
+ }
94
157
}
95
158
}
96
159
}
@@ -103,6 +166,35 @@ pub fn get_path_to_eat_all(
103
166
sub_path. truncate ( sub_path. len ( ) - snake_length) ;
104
167
sub_path. append ( & mut path) ;
105
168
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
+ //
106
198
} else {
107
199
panic ! ( "impossible to path to cell to eat" ) ;
108
200
}
0 commit comments