extern crate rand; extern crate evco; use std::fmt; use std::ops::Rem; use rand::{OsRng, Rng, Rand}; use std::collections::VecDeque; use evco::gp::*; use evco::gp::tree::*; #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum TurnDirection { Left, Ahead, Right, } impl Rand for TurnDirection { fn rand(r: &mut R) -> TurnDirection { match r.next_u32() % 3 { 0 => TurnDirection::Left, 1 => TurnDirection::Ahead, 2 => TurnDirection::Right, _ => unreachable!(), } } } #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum CompassDirection { North, East, South, West, } impl CompassDirection { fn variants() -> &'static [CompassDirection] { static VARIANTS: &'static [CompassDirection] = &[CompassDirection::North, CompassDirection::East, CompassDirection::South, CompassDirection::West]; VARIANTS } } impl Rand for CompassDirection { fn rand(r: &mut R) -> CompassDirection { match r.next_u32() % 4 { 0 => CompassDirection::North, 1 => CompassDirection::East, 2 => CompassDirection::South, 3 => CompassDirection::West, _ => unreachable!(), } } } #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct Vector { x: isize, y: isize, } impl Vector { fn neighbour(&self, direction: &CompassDirection) -> Vector { match *direction { CompassDirection::North => { Vector { x: self.x, y: self.y - 1, } } CompassDirection::East => { Vector { x: self.x + 1, y: self.y, } } CompassDirection::South => { Vector { x: self.x, y: self.y + 1, } } CompassDirection::West => { Vector { x: self.x - 1, y: self.y, } } } } fn direction_to(&self, possible_neighbour: Vector) -> Option { for direction in CompassDirection::variants() { let neighbour = self.neighbour(direction); if neighbour == possible_neighbour { return Some(*direction); } } None } } #[derive(PartialEq, Eq, Clone, Debug)] pub struct SnakeEnvironment { pub size: Vector, pub food: Vector, pub snake: Vec, } impl SnakeEnvironment { fn turn_to_compass_direction(&self, turn_direction: TurnDirection) -> CompassDirection { let snake_current_compass_direction = self.snake[1].direction_to(self.snake[0]).unwrap(); let directions = CompassDirection::variants(); let index: isize = directions.iter().position(|&r| r == snake_current_compass_direction).unwrap() as isize; let new_compass_direction_index = match turn_direction { TurnDirection::Left => (index + 3).rem(4), TurnDirection::Ahead => index, TurnDirection::Right => (index + 5).rem(4), }; directions[new_compass_direction_index as usize] } fn sense_danger(&self, turn_direction: TurnDirection) -> bool { let compass_direction = self.turn_to_compass_direction(turn_direction); let cell_in_direction = self.snake[0].neighbour(&compass_direction); (cell_in_direction.x < 0 || cell_in_direction.y < 0 || cell_in_direction.x >= self.size.x || cell_in_direction.y >= self.size.y || self.snake.contains(&cell_in_direction)) } fn sense_food(&self, turn_direction: TurnDirection) -> bool { let compass_direction = self.turn_to_compass_direction(turn_direction); let cell_in_direction = self.snake[0].neighbour(&compass_direction); self.food == cell_in_direction } fn perform_movement(&mut self, turn_direction: TurnDirection) { let compass_direction = self.turn_to_compass_direction(turn_direction); let old_head = self.snake[0]; self.snake.pop(); self.snake.insert(0, old_head.neighbour(&compass_direction)); } } #[derive(Clone, Debug)] pub enum SnakeTree { IfDanger(TurnDirection, BoxTree, BoxTree), IfFood(TurnDirection, BoxTree, BoxTree), Move(TurnDirection), } use SnakeTree::*; impl Tree for SnakeTree { type Environment = SnakeEnvironment; type Action = TurnDirection; fn branch(tg: &mut TreeGen, current_depth: usize) -> BoxTree { let direction = TurnDirection::rand(tg); let true_ = Self::child(tg, current_depth + 1); let false_ = Self::child(tg, current_depth + 1); if tg.gen() { IfDanger(direction, true_, false_).into() } else { IfFood(direction, true_, false_).into() } } fn leaf(tg: &mut TreeGen, _: usize) -> BoxTree { Move(TurnDirection::rand(tg)).into() } fn count_children(&mut self) -> usize { match *self { IfDanger(_, _, _) | IfFood(_, _, _) => 2, Move(_) => 0, } } fn children(&self) -> Vec<&BoxTree> { match *self { IfDanger(_, ref left_, ref right_) | IfFood(_, ref left_, ref right_) => vec![left_, right_], Move(_) => vec![], } } fn children_mut(&mut self) -> Vec<&mut BoxTree> { match *self { IfDanger(_, ref mut left_, ref mut right_) | IfFood(_, ref mut left_, ref mut right_) => vec![left_, right_], Move(_) => vec![], } } fn evaluate(&self, env: &Self::Environment) -> Self::Action { match *self { IfDanger(direction, ref left_, ref right_) => { if env.sense_danger(direction) { left_.evaluate(env) } else { right_.evaluate(env) } } IfFood(direction, ref left_, ref right_) => { if env.sense_food(direction) { left_.evaluate(env) } else { right_.evaluate(env) } } Move(direction) => direction, } } } impl fmt::Display for SnakeTree { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut stack: VecDeque<&Self> = VecDeque::new(); let mut depth_stack: VecDeque = VecDeque::new(); stack.push_back(self); depth_stack.push_back(0); while let (Some(node), Some(depth)) = (stack.pop_back(), depth_stack.pop_back()) { for _ in 0..depth { write!(f, " ")?; } match *node { IfDanger(direction, _, _) => write!(f, "IfDanger({:?})", direction)?, IfFood(direction, _, _) => write!(f, "IfFood({:?})", direction)?, Move(direction) => write!(f, "Move({:?})", direction)?, } write!(f, "\n")?; let mut children = node.children(); children.reverse(); for child in children { stack.push_back(child); depth_stack.push_back(depth + 1); } } Ok(()) } } fn main() { let mut rng = OsRng::new().unwrap(); let mut tree_gen = TreeGen::full(&mut rng, 1, 4); let mut indv1: Individual = Individual::new(&mut tree_gen); let mut indv2: Individual = Individual::new(&mut tree_gen); let mut rng = OsRng::new().unwrap(); let crossover = Crossover::one_point(); println!("---"); println!("{}", indv1); println!("{}", indv2); println!("---"); crossover.mate(&mut indv1, &mut indv2, &mut rng); println!("{}", indv1); println!("{}", indv2); println!("---"); println!("{}", indv1); let mut mutate_rng = OsRng::new().unwrap(); let mut tree_gen = TreeGen::full(&mut mutate_rng, 1, 2); let mutation = Mutation::uniform(); mutation.mutate(&mut indv1, &mut tree_gen); println!("{}", indv1); let mut env = SnakeEnvironment { size: Vector { x: 10, y: 10 }, food: Vector { x: 9, y: 9 }, snake: vec![Vector { x: 3, y: 3 }, Vector { x: 4, y: 3 }], }; for _ in 0..200 { let indv: Individual = Individual::new(&mut tree_gen); let mut score1 = 0; let mut score2 = 0; let mut tick = 100; while tick > 0 { //println!("{:?} {:?}", tick, env.snake); let move_ = indv.tree.evaluate(&env); if env.sense_danger(move_) { break; } if env.sense_food(move_) { score2 += 1; tick += 100; env.food = Vector { x: rng.gen_range(0, 11), y: rng.gen_range(0, 11), }; } env.perform_movement(move_); score1 += 1; tick -= 1; } if score2 >= 1 { println!("lived={:?} ate={:?}", score1, score2); println!("{}", indv); } } }