snake/
snake.rs

1extern crate rand;
2extern crate evco;
3
4use std::fmt;
5use std::ops::Rem;
6use rand::{OsRng, Rng, Rand};
7use std::collections::VecDeque;
8
9use evco::gp::*;
10use evco::gp::tree::*;
11
12#[derive(PartialEq, Eq, Clone, Copy, Debug)]
13pub enum TurnDirection {
14    Left,
15    Ahead,
16    Right,
17}
18
19impl Rand for TurnDirection {
20    fn rand<R: Rng>(r: &mut R) -> TurnDirection {
21        match r.next_u32() % 3 {
22            0 => TurnDirection::Left,
23            1 => TurnDirection::Ahead,
24            2 => TurnDirection::Right,
25            _ => unreachable!(),
26        }
27    }
28}
29
30#[derive(PartialEq, Eq, Clone, Copy, Debug)]
31pub enum CompassDirection {
32    North,
33    East,
34    South,
35    West,
36}
37
38impl CompassDirection {
39    fn variants() -> &'static [CompassDirection] {
40        static VARIANTS: &'static [CompassDirection] = &[CompassDirection::North,
41                                                         CompassDirection::East,
42                                                         CompassDirection::South,
43                                                         CompassDirection::West];
44        VARIANTS
45    }
46}
47
48impl Rand for CompassDirection {
49    fn rand<R: Rng>(r: &mut R) -> CompassDirection {
50        match r.next_u32() % 4 {
51            0 => CompassDirection::North,
52            1 => CompassDirection::East,
53            2 => CompassDirection::South,
54            3 => CompassDirection::West,
55            _ => unreachable!(),
56        }
57    }
58}
59
60#[derive(PartialEq, Eq, Clone, Copy, Debug)]
61pub struct Vector {
62    x: isize,
63    y: isize,
64}
65
66impl Vector {
67    fn neighbour(&self, direction: &CompassDirection) -> Vector {
68        match *direction {
69            CompassDirection::North => {
70                Vector {
71                    x: self.x,
72                    y: self.y - 1,
73                }
74            }
75            CompassDirection::East => {
76                Vector {
77                    x: self.x + 1,
78                    y: self.y,
79                }
80            }
81            CompassDirection::South => {
82                Vector {
83                    x: self.x,
84                    y: self.y + 1,
85                }
86            }
87            CompassDirection::West => {
88                Vector {
89                    x: self.x - 1,
90                    y: self.y,
91                }
92            }
93        }
94    }
95
96    fn direction_to(&self, possible_neighbour: Vector) -> Option<CompassDirection> {
97        for direction in CompassDirection::variants() {
98            let neighbour = self.neighbour(direction);
99            if neighbour == possible_neighbour {
100                return Some(*direction);
101            }
102        }
103        None
104    }
105}
106
107#[derive(PartialEq, Eq, Clone, Debug)]
108pub struct SnakeEnvironment {
109    pub size: Vector,
110    pub food: Vector,
111    pub snake: Vec<Vector>,
112}
113
114impl SnakeEnvironment {
115    fn turn_to_compass_direction(&self, turn_direction: TurnDirection) -> CompassDirection {
116        let snake_current_compass_direction = self.snake[1].direction_to(self.snake[0]).unwrap();
117        let directions = CompassDirection::variants();
118        let index: isize =
119            directions.iter().position(|&r| r == snake_current_compass_direction).unwrap() as isize;
120        let new_compass_direction_index = match turn_direction {
121            TurnDirection::Left => (index + 3).rem(4),
122            TurnDirection::Ahead => index,
123            TurnDirection::Right => (index + 5).rem(4),
124        };
125        directions[new_compass_direction_index as usize]
126    }
127
128    fn sense_danger(&self, turn_direction: TurnDirection) -> bool {
129        let compass_direction = self.turn_to_compass_direction(turn_direction);
130        let cell_in_direction = self.snake[0].neighbour(&compass_direction);
131        (cell_in_direction.x < 0 || cell_in_direction.y < 0 ||
132         cell_in_direction.x >= self.size.x || cell_in_direction.y >= self.size.y ||
133         self.snake.contains(&cell_in_direction))
134    }
135
136    fn sense_food(&self, turn_direction: TurnDirection) -> bool {
137        let compass_direction = self.turn_to_compass_direction(turn_direction);
138        let cell_in_direction = self.snake[0].neighbour(&compass_direction);
139        self.food == cell_in_direction
140    }
141
142    fn perform_movement(&mut self, turn_direction: TurnDirection) {
143        let compass_direction = self.turn_to_compass_direction(turn_direction);
144        let old_head = self.snake[0];
145        self.snake.pop();
146        self.snake.insert(0, old_head.neighbour(&compass_direction));
147    }
148}
149
150#[derive(Clone, Debug)]
151pub enum SnakeTree {
152    IfDanger(TurnDirection, BoxTree<SnakeTree>, BoxTree<SnakeTree>),
153    IfFood(TurnDirection, BoxTree<SnakeTree>, BoxTree<SnakeTree>),
154    Move(TurnDirection),
155}
156
157use SnakeTree::*;
158
159impl Tree for SnakeTree {
160    type Environment = SnakeEnvironment;
161    type Action = TurnDirection;
162
163    fn branch<R: Rng>(tg: &mut TreeGen<R>, current_depth: usize) -> BoxTree<Self> {
164        let direction = TurnDirection::rand(tg);
165        let true_ = Self::child(tg, current_depth + 1);
166        let false_ = Self::child(tg, current_depth + 1);
167        if tg.gen() {
168            IfDanger(direction, true_, false_).into()
169        } else {
170            IfFood(direction, true_, false_).into()
171        }
172    }
173
174    fn leaf<R: Rng>(tg: &mut TreeGen<R>, _: usize) -> BoxTree<Self> {
175        Move(TurnDirection::rand(tg)).into()
176    }
177
178    fn count_children(&mut self) -> usize {
179        match *self {
180            IfDanger(_, _, _) |
181            IfFood(_, _, _) => 2,
182            Move(_) => 0,
183        }
184    }
185
186    fn children(&self) -> Vec<&BoxTree<Self>> {
187        match *self {
188            IfDanger(_, ref left_, ref right_) |
189            IfFood(_, ref left_, ref right_) => vec![left_, right_],
190            Move(_) => vec![],
191        }
192    }
193
194    fn children_mut(&mut self) -> Vec<&mut BoxTree<Self>> {
195        match *self {
196            IfDanger(_, ref mut left_, ref mut right_) |
197            IfFood(_, ref mut left_, ref mut right_) => vec![left_, right_],
198            Move(_) => vec![],
199        }
200    }
201
202    fn evaluate(&self, env: &Self::Environment) -> Self::Action {
203        match *self {
204            IfDanger(direction, ref left_, ref right_) => {
205                if env.sense_danger(direction) {
206                    left_.evaluate(env)
207                } else {
208                    right_.evaluate(env)
209                }
210            }
211            IfFood(direction, ref left_, ref right_) => {
212                if env.sense_food(direction) {
213                    left_.evaluate(env)
214                } else {
215                    right_.evaluate(env)
216                }
217            }
218            Move(direction) => direction,
219        }
220    }
221}
222
223impl fmt::Display for SnakeTree {
224    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225        let mut stack: VecDeque<&Self> = VecDeque::new();
226        let mut depth_stack: VecDeque<usize> = VecDeque::new();
227        stack.push_back(self);
228        depth_stack.push_back(0);
229        while let (Some(node), Some(depth)) = (stack.pop_back(), depth_stack.pop_back()) {
230            for _ in 0..depth {
231                write!(f, "    ")?;
232            }
233            match *node {
234                IfDanger(direction, _, _) => write!(f, "IfDanger({:?})", direction)?,
235                IfFood(direction, _, _) => write!(f, "IfFood({:?})", direction)?,
236                Move(direction) => write!(f, "Move({:?})", direction)?,
237            }
238            write!(f, "\n")?;
239
240            let mut children = node.children();
241            children.reverse();
242            for child in children {
243                stack.push_back(child);
244                depth_stack.push_back(depth + 1);
245            }
246        }
247        Ok(())
248    }
249}
250
251fn main() {
252    let mut rng = OsRng::new().unwrap();
253    let mut tree_gen = TreeGen::full(&mut rng, 1, 4);
254
255    let mut indv1: Individual<SnakeTree> = Individual::new(&mut tree_gen);
256    let mut indv2: Individual<SnakeTree> = Individual::new(&mut tree_gen);
257
258    let mut rng = OsRng::new().unwrap();
259    let crossover = Crossover::one_point();
260    println!("---");
261    println!("{}", indv1);
262    println!("{}", indv2);
263    println!("---");
264    crossover.mate(&mut indv1, &mut indv2, &mut rng);
265    println!("{}", indv1);
266    println!("{}", indv2);
267    println!("---");
268
269    println!("{}", indv1);
270    let mut mutate_rng = OsRng::new().unwrap();
271    let mut tree_gen = TreeGen::full(&mut mutate_rng, 1, 2);
272    let mutation = Mutation::uniform();
273    mutation.mutate(&mut indv1, &mut tree_gen);
274    println!("{}", indv1);
275
276    let mut env = SnakeEnvironment {
277        size: Vector { x: 10, y: 10 },
278        food: Vector { x: 9, y: 9 },
279        snake: vec![Vector { x: 3, y: 3 }, Vector { x: 4, y: 3 }],
280    };
281
282    for _ in 0..200 {
283        let indv: Individual<SnakeTree> = Individual::new(&mut tree_gen);
284
285        let mut score1 = 0;
286        let mut score2 = 0;
287        let mut tick = 100;
288        while tick > 0 {
289            //println!("{:?} {:?}", tick, env.snake);
290            let move_ = indv.tree.evaluate(&env);
291            if env.sense_danger(move_) {
292                break;
293            }
294            if env.sense_food(move_) {
295                score2 += 1;
296                tick += 100;
297                env.food = Vector {
298                    x: rng.gen_range(0, 11),
299                    y: rng.gen_range(0, 11),
300                };
301            }
302            env.perform_movement(move_);
303            score1 += 1;
304            tick -= 1;
305        }
306        if score2 >= 1 {
307            println!("lived={:?} ate={:?}", score1, score2);
308            println!("{}", indv);
309        }
310    }
311}