From 1aeab6da05a0c7b7dad4d06a38b282a82d5e1a51 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Mon, 24 Jun 2019 21:04:27 +0200 Subject: New command types for select and bombs --- src/command.rs | 39 +++++++++++++++++++++++++++++--- src/game.rs | 44 ++++++++++++++++++++---------------- src/main.rs | 4 ++-- src/strategy.rs | 70 ++++++--------------------------------------------------- 4 files changed, 70 insertions(+), 87 deletions(-) (limited to 'src') diff --git a/src/command.rs b/src/command.rs index 81e3d67..1ffdd35 100644 --- a/src/command.rs +++ b/src/command.rs @@ -3,20 +3,53 @@ use crate::geometry::Direction; use crate::geometry::Point2d; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum Command { +pub struct Command { + pub worm: Option, + pub action: Action +} + +impl fmt::Display for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.worm { + Some(worm) => write!(f, "select {};{}", worm, self.action), + None => write!(f, "{}", self.action) + } + } +} + +impl Command { + pub fn with_select(worm: i32, action: Action) -> Command { + Command { + worm: Some(worm), + action + } + } + + pub fn new(action: Action) -> Command { + Command { + worm: None, + action + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Action { Move(Point2d), Dig(Point2d), Shoot(Direction), + Bomb(Point2d), DoNothing, } -impl fmt::Display for Command { +impl fmt::Display for Action { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Command::*; + use Action::*; match self { Move(p) => write!(f, "move {} {}", p.x, p.y), Dig(p) => write!(f, "dig {} {}", p.x, p.y), Shoot(dir) => write!(f, "shoot {}", dir), + Bomb(p) => write!(f, "banana {} {}", p.x, p.y), DoNothing => write!(f, "nothing"), } } diff --git a/src/game.rs b/src/game.rs index 18731a2..a4a4a23 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,5 +1,5 @@ use crate::geometry::*; -use crate::command::Command; +use crate::command::{Command, Action}; use crate::json; mod player; @@ -136,9 +136,13 @@ impl GameBoard { } pub fn simulate(&mut self, moves: [Command; 2]) { - self.simulate_moves(moves); - self.simulate_digs(moves); - self.simulate_shoots(moves); + let actions = [moves[0].action, moves[1].action]; + + // TODO: simulate_select + self.simulate_moves(actions); + self.simulate_digs(actions); + // TODO: simulate_bombs + self.simulate_shoots(actions); for player in &mut self.players { player.clear_dead_worms(); @@ -156,9 +160,9 @@ impl GameBoard { }; } - fn simulate_moves(&mut self, moves: [Command; 2]) { - match moves { - [Command::Move(p1), Command::Move(p2)] if p1.x == p2.x && p1.y == p2.y => { + fn simulate_moves(&mut self, actions: [Action; 2]) { + match actions { + [Action::Move(p1), Action::Move(p2)] if p1.x == p2.x && p1.y == p2.y => { // TODO: Get this from some sort of config rather let damage = 20; @@ -174,8 +178,8 @@ impl GameBoard { } }, _ => { - for player_index in 0..moves.len() { - if let Command::Move(p) = moves[player_index] { + for player_index in 0..actions.len() { + if let Action::Move(p) = actions[player_index] { debug_assert_eq!(Some(false), self.map.at(p), "Movement target wasn't empty, ({}, {})", p.x, p.y); self.players[player_index].moves_score += 5; @@ -207,12 +211,12 @@ impl GameBoard { } } - fn simulate_digs(&mut self, moves: [Command; 2]) { - for player_index in 0..moves.len() { - if let Command::Dig(p) = moves[player_index] { + fn simulate_digs(&mut self, actions: [Action; 2]) { + for player_index in 0..actions.len() { + if let Action::Dig(p) = actions[player_index] { debug_assert!( Some(true) == self.map.at(p) || - (player_index == 1 && moves[0] == Command::Dig(p)), + (player_index == 1 && actions[0] == Action::Dig(p)), "Tried to dig through air, ({}, {})", p.x, p.y ); debug_assert!{ @@ -228,9 +232,9 @@ impl GameBoard { } } - fn simulate_shoots(&mut self, moves: [Command; 2]) { - 'players_loop: for player_index in 0..moves.len() { - if let Command::Shoot(dir) = moves[player_index] { + fn simulate_shoots(&mut self, actions: [Action; 2]) { + 'players_loop: for player_index in 0..actions.len() { + if let Action::Shoot(dir) = actions[player_index] { if let Some(worm) = self.players[player_index].active_worm() { let (center, weapon_range, weapon_damage) = { (worm.position, worm.weapon_range, worm.weapon_damage) @@ -294,6 +298,7 @@ impl GameBoard { } pub fn valid_move_commands(&self, player_index: usize) -> ArrayVec<[Command;8]> { + // TODO: This might also involve selects if let Some(worm) = self.players[player_index].active_worm() { Direction::all() .iter() @@ -301,8 +306,8 @@ impl GameBoard { .map(|d| worm.position + d) .filter(|p| !self.occupied_cells.contains(p)) .filter_map(|p| match self.map.at(p) { - Some(false) => Some(Command::Move(p)), - Some(true) => Some(Command::Dig(p)), + Some(false) => Some(Command::new(Action::Move(p))), + Some(true) => Some(Command::new(Action::Dig(p))), _ => None, }) .collect() @@ -312,6 +317,7 @@ impl GameBoard { } pub fn valid_shoot_commands(&self, player_index: usize, center: Point2d, weapon_range: u8) -> ArrayVec<[Command;8]> { + // TODO: We might also throw bombs let range = weapon_range as i8; let dir_range = ((weapon_range as f32 + 1.) / 2f32.sqrt()).floor() as i8; @@ -349,7 +355,7 @@ impl GameBoard { self.map.at(center + diff * distance) != Some(false) && !self.players[player_index].worms.iter().any(|w| w.position == center + diff * distance)) }) - .map(|(dir, _range)| Command::Shoot(dir)) + .map(|(dir, _range)| Command::new(Action::Shoot(dir))) .collect() } } diff --git a/src/main.rs b/src/main.rs index b898fb5..de2940d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::path::Path; use time::{Duration, PreciseTime}; -use steam_powered_wyrm::command::Command; +use steam_powered_wyrm::command::{Command, Action}; use steam_powered_wyrm::strategy::choose_move; use steam_powered_wyrm::json; use steam_powered_wyrm::game; @@ -39,7 +39,7 @@ fn main() { }, Err(e) => { eprintln!("WARN: State file could not be parsed: {}", e); - Command::DoNothing + Command::new(Action::DoNothing) } }; println!("C;{};{}", round_number, command); diff --git a/src/strategy.rs b/src/strategy.rs index dd5bece..31a1bee 100644 --- a/src/strategy.rs +++ b/src/strategy.rs @@ -1,4 +1,4 @@ -use crate::command::Command; +use crate::command::{Command, Action}; use crate::game::{GameBoard, SimulationOutcome}; use crate::geometry::*; @@ -179,7 +179,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::DoNothing) + .unwrap_or(Command::new(Action::DoNothing)) } fn score(state: &GameBoard) -> Score { @@ -203,11 +203,11 @@ fn rollout(state: &GameBoard) -> Score { player_moves .choose(&mut rng) .cloned() - .unwrap_or(Command::DoNothing), + .unwrap_or(Command::new(Action::DoNothing)), opponent_moves .choose(&mut rng) .cloned() - .unwrap_or(Command::DoNothing), + .unwrap_or(Command::new(Action::DoNothing)), ]); } @@ -229,7 +229,7 @@ fn choose_one_existing(node: &Node, player_index: usize) -> Command { as i32 }) .map(|(command, _score_sum)| *command) - .unwrap_or(Command::DoNothing) + .unwrap_or(Command::new(Action::DoNothing)) } fn update(node: &mut Node, commands: [Command; 2], score: Score) { @@ -242,62 +242,6 @@ fn update(node: &mut Node, commands: [Command; 2], score: Score) { node.score_sum += score; } -// fn heuristic_moves(state: &GameBoard, player_index: usize) -> Vec { -// let worm = state.players[player_index].active_worm(); - -// let shoots = state -// .valid_shoot_commands(player_index, worm.position, worm.weapon_range); - -// let closest_powerup = state.powerups -// .iter() -// .min_by_key(|p| (p.position - worm.position).walking_distance()); - -// let average_player_position = Point2d { -// x: state.players[player_index].worms -// .iter() -// .map(|w| w.position.x) -// .sum::() / state.players[player_index].worms.len() as i8, -// y: state.players[player_index].worms -// .iter() -// .map(|w| w.position.y) -// .sum::() / state.players[player_index].worms.len() as i8 -// }; - -// let closest_opponent = state.players[GameBoard::opponent(player_index)].worms -// .iter() -// .min_by_key(|w| (w.position - average_player_position).walking_distance()); - -// let mut commands = if !shoots.is_empty() { -// // we're in combat now. Feel free to move anywhere. -// let moves = state.valid_move_commands(player_index); -// moves.iter().chain(shoots.iter()).cloned().collect() -// } else if let Some(powerup) = closest_powerup { -// // there are powerups! Let's go grab the closest one. -// moves_towards(state, player_index, powerup.position) -// } else if let Some(opponent) = closest_opponent { -// // we're not currently in combat. Let's go find the closest worm. -// moves_towards(state, player_index, opponent.position) -// } else { -// // this shouldn't happen -// debug_assert!(false, "No valid heuristic moves"); -// vec!() -// }; -// commands.push(Command::DoNothing); -// commands -// } - -// fn moves_towards(state: &GameBoard, player_index: usize, to: Point2d) -> Vec { -// let distance = (to - state.players[player_index].active_worm().position).walking_distance(); -// state.valid_move_commands(player_index) -// .iter() -// .filter(|c| match c { -// Command::Move(p) | Command::Dig(p) => (to - *p).walking_distance() < distance, -// _ => false -// }) -// .cloned() -// .collect() -// } - fn rollout_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 8]> { if let Some(worm) = state.players[player_index].active_worm() { @@ -310,7 +254,7 @@ fn rollout_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 8 // TODO: More directed destruction movements? state.valid_move_commands(player_index) } else { - [Command::DoNothing].iter().cloned().collect() + [Command::new(Action::DoNothing)].into_iter().cloned().collect() } } @@ -323,6 +267,6 @@ fn valid_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 17] .cloned() .collect() } else { - [Command::DoNothing].iter().cloned().collect() + [Command::new(Action::DoNothing)].into_iter().cloned().collect() } } -- cgit v1.2.3