From 81e869d0579a257bd823f9629a1b4ec3b6a297f9 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Thu, 27 Jun 2019 18:58:04 +0200 Subject: Starting to allow selecting from all of the new moves --- src/game.rs | 34 ++++++++++++++++++++++++++++++---- src/strategy.rs | 35 +++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/game.rs b/src/game.rs index d3d6636..9423e72 100644 --- a/src/game.rs +++ b/src/game.rs @@ -377,8 +377,21 @@ impl GameBoard { (player_index + 1)%2 } + pub fn valid_selects(&self, player_index: usize) -> ArrayVec<[i32; 2]> { + if self.players[player_index].select_moves > 0 { + self.players[player_index].worms + .iter() + .enumerate() + .filter(|(p, _w)| self.players[player_index].active_worm != *p) + .map(|(_p, w)| w.id) + .collect() + } else { + ArrayVec::new() + } + } + pub fn valid_move_commands(&self, player_index: usize) -> ArrayVec<[Command;8]> { - // TODO: This might also involve selects + // TODO: Select and move if let Some(worm) = self.players[player_index].active_worm() { Direction::all() .iter() @@ -395,11 +408,24 @@ impl GameBoard { ArrayVec::new() } } + + pub fn valid_shoot_commands(&self) -> ArrayVec<[Command;24]> { + // TODO: Select and shoot + Direction::all() + .iter() + .map(|d| Command::new(Action::Shoot(*d))) + .collect() + } + + pub fn valid_bomb_commands(&self, player_index: usize) -> Vec { + // TODO: Bombs + // TODO: Select and bomb + unimplemented!("TODO") + } - pub fn valid_shoot_commands(&self, player_index: usize, center: Point2d, weapon_range: u8) -> ArrayVec<[Command;8]> { - // TODO: We might also throw bombs + pub fn sensible_shoot_commands(&self, player_index: usize, center: Point2d, weapon_range: u8) -> ArrayVec<[Command;8]> { let range = weapon_range as i8; - let dir_range = ((weapon_range as f32 + 1.) / 2f32.sqrt()).floor() as i8; + let dir_range = ((f32::from(weapon_range) + 1.) / 2f32.sqrt()).floor() as i8; self.players[GameBoard::opponent(player_index)].worms .iter() diff --git a/src/strategy.rs b/src/strategy.rs index 4c86dfd..46cb2cd 100644 --- a/src/strategy.rs +++ b/src/strategy.rs @@ -131,8 +131,11 @@ fn mcts(node: &mut Node) -> Score { let mut new_state = node.state.clone(); new_state.simulate(commands); let score = rollout(&new_state); - // TODO: This could overshoot, trying to estimate from concluded game - let unexplored = mcts_move_combo(&new_state); + let unexplored = if new_state.outcome == SimulationOutcome::Continue { + mcts_move_combo(&new_state) + } else { + Vec::new() + }; let new_node = Node { state: new_state, @@ -166,7 +169,7 @@ fn mcts_move_combo(state: &GameBoard) -> Vec<[Command; 2]> { let mut result = Vec::with_capacity(player_moves.len() * opponent_moves.len()); for p in &player_moves { for o in &opponent_moves { - result.push([p.clone(), o.clone()]); + result.push([*p, *o]); } } @@ -178,7 +181,7 @@ fn best_player_move(node: &Node) -> Command { .iter() .max_by_key(|(_command, score_sum)| score_sum.avg()) .map(|(command, _score_sum)| *command) - .unwrap_or(Command::new(Action::DoNothing)) + .unwrap_or_else(|| Command::new(Action::DoNothing)) } fn score(state: &GameBoard) -> Score { @@ -202,11 +205,11 @@ fn rollout(state: &GameBoard) -> Score { player_moves .choose(&mut rng) .cloned() - .unwrap_or(Command::new(Action::DoNothing)), + .unwrap_or_else(|| Command::new(Action::DoNothing)), opponent_moves .choose(&mut rng) .cloned() - .unwrap_or(Command::new(Action::DoNothing)), + .unwrap_or_else(|| Command::new(Action::DoNothing)), ]); } @@ -228,29 +231,30 @@ fn choose_one_existing(node: &Node, player_index: usize) -> Command { as i32 }) .map(|(command, _score_sum)| *command) - .unwrap_or(Command::new(Action::DoNothing)) + .unwrap_or_else(|| Command::new(Action::DoNothing)) } fn update(node: &mut Node, commands: [Command; 2], score: Score) { *node.player_score_sums[0] .entry(commands[0]) - .or_insert(ScoreSum::new()) += score; + .or_insert_with(ScoreSum::new) += score; *node.player_score_sums[1] .entry(commands[1]) - .or_insert(ScoreSum::new()) += score; + .or_insert_with(ScoreSum::new) += score; node.score_sum += score; } fn rollout_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 8]> { + // TODO: Have this return one move, chosen randomly? + // TODO: Allow new select / bomb moves if let Some(worm) = state.players[player_index].active_worm() { - let shoots = state.valid_shoot_commands(player_index, worm.position, worm.weapon_range); + let shoots = state.sensible_shoot_commands(player_index, worm.position, worm.weapon_range); if !shoots.is_empty() { return shoots; } - // TODO: More directed destruction movements? state.valid_move_commands(player_index) } else { [Command::new(Action::DoNothing)].into_iter().cloned().collect() @@ -258,11 +262,18 @@ fn rollout_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 8 } fn valid_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 17]> { + // TODO: Move / Dig, Shoot, Bomb, Select to another worm and repeat + // 24 move/digs + // 24 shoots + // 109 bombs (sub those out of range) + // 1 nothing + // TOTAL: 158 possible moves if let Some(worm) = state.players[player_index].active_worm() { - state.valid_shoot_commands(player_index, worm.position, worm.weapon_range) + state.valid_shoot_commands() .iter() .chain(state.valid_move_commands(player_index).iter()) + .chain(state.valid_bomb_commands(player_index).iter()) .cloned() .collect() } else { -- cgit v1.2.3