From a07cbfd67849b98a881f930e12e07c429e604ac4 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Fri, 28 Jun 2019 21:20:37 +0200 Subject: updated active worm in update This might change weirdly because of the select move. --- src/game.rs | 14 ++++------ src/json.rs | 70 ++++++++++++++++++++++++++++++-------------------- src/main.rs | 60 ++++++++++++++++++++++--------------------- src/strategy.rs | 79 ++++++++++++++++++++++++++++++++------------------------- 4 files changed, 123 insertions(+), 100 deletions(-) diff --git a/src/game.rs b/src/game.rs index 7c6b649..e3caf4e 100644 --- a/src/game.rs +++ b/src/game.rs @@ -129,7 +129,6 @@ impl GameBoard { // TODO: How to update opponent worm bombs? } } - self.players[0].moves_score = json.my_player.score - json.my_player.health_score(); self.players[1].moves_score = json.opponents[0].score - json.opponents[0].health_score(); @@ -156,12 +155,9 @@ impl GameBoard { for player in &mut self.players { player.clear_dead_worms(); - player.next_active_worm(); } - debug_assert_eq!( - json.active_worm_index().unwrap(), - self.players[0].active_worm - ); + self.players[0].active_worm = json.active_worm_index().unwrap_or(0); + self.players[1].active_worm = json.opponent_active_worm_index().unwrap_or(0); self.round += 1; debug_assert_eq!(json.current_round, self.round); @@ -508,7 +504,7 @@ impl GameBoard { Some(worm.id) }; - let mut result = Vec::with_capacity(11*11-12); + let mut result = Vec::with_capacity(11 * 11 - 12); for y in worm.position.y - 5..=worm.position.y + 5 { for x in worm.position.x - 5..=worm.position.x + 5 { @@ -516,12 +512,12 @@ impl GameBoard { if (worm.position - target).magnitude_squared() < 36 { result.push(Command { worm: select, - action: Action::Bomb(target) + action: Action::Bomb(target), }); } } } - + result } None => Vec::new(), diff --git a/src/json.rs b/src/json.rs index 798c567..86b35ea 100644 --- a/src/json.rs +++ b/src/json.rs @@ -53,7 +53,7 @@ pub struct PlayerWorm { pub digging_range: u32, pub movement_range: u32, pub weapon: Weapon, - pub banana_bombs: Option + pub banana_bombs: Option, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] @@ -61,6 +61,7 @@ pub struct PlayerWorm { pub struct Opponent { pub id: i32, pub score: i32, + pub current_worm_id: i32, pub worms: Vec, pub remaining_worm_selections: u8, } @@ -159,7 +160,7 @@ pub struct Bomb { pub damage: i32, pub range: u8, pub count: u8, - pub damage_radius: u8 + pub damage_radius: u8, } impl State { @@ -170,6 +171,14 @@ impl State { .filter(|w| w.health > 0) .position(|w| w.id == self.current_worm_id) } + + pub fn opponent_active_worm_index(&self) -> Option { + self.opponents[0] + .worms + .iter() + .filter(|w| w.health > 0) + .position(|w| w.id == self.opponents[0].current_worm_id) + } } #[cfg(test)] @@ -234,6 +243,7 @@ mod test { { "id": 2, "score": 100, + "currentWormId": 3, "remainingWormSelections": 2, "worms": [ { @@ -328,39 +338,43 @@ mod test { score: 100, health: 300, remaining_worm_selections: 1, - worms: vec![PlayerWorm { - id: 1, - health: 100, - position: Position { x: 24, y: 29 }, - weapon: Weapon { - damage: 1, - range: 3, + worms: vec![ + PlayerWorm { + id: 1, + health: 100, + position: Position { x: 24, y: 29 }, + weapon: Weapon { + damage: 1, + range: 3, + }, + digging_range: 1, + movement_range: 1, + banana_bombs: Some(Bomb { + damage: 20, + range: 5, + count: 3, + damage_radius: 2, + }), }, - digging_range: 1, - movement_range: 1, - banana_bombs: Some(Bomb { - damage: 20, - range: 5, - count: 3, - damage_radius: 2 - }), - }, PlayerWorm { - id: 2, - health: 150, - position: Position { x: 1, y: 16 }, - weapon: Weapon { - damage: 1, - range: 3, + PlayerWorm { + id: 2, + health: 150, + position: Position { x: 1, y: 16 }, + weapon: Weapon { + damage: 1, + range: 3, + }, + digging_range: 1, + movement_range: 1, + banana_bombs: None, }, - digging_range: 1, - movement_range: 1, - banana_bombs: None, - }], + ], }, opponents: vec![Opponent { id: 2, score: 100, remaining_worm_selections: 2, + current_worm_id: 3, worms: vec![OpponentWorm { id: 1, health: 100, diff --git a/src/main.rs b/src/main.rs index de2940d..00b96cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,44 +4,46 @@ use std::path::Path; use time::{Duration, PreciseTime}; -use steam_powered_wyrm::command::{Command, Action}; -use steam_powered_wyrm::strategy::choose_move; -use steam_powered_wyrm::json; +use steam_powered_wyrm::command::{Action, Command}; use steam_powered_wyrm::game; +use steam_powered_wyrm::json; +use steam_powered_wyrm::strategy::choose_move; fn main() { - let max_time = Duration::milliseconds(950); + let max_time = Duration::milliseconds(900); let mut game_board = None; let mut strategy_cache = None; for line in stdin().lock().lines() { let start_time = PreciseTime::now(); - + let round_number = line.expect("Failed to read line from stdin: {}"); - - let command = - match json::read_state_from_json_file(&Path::new(&format!("./rounds/{}/state.json", round_number))) { - Ok(json_state) => { - match &mut game_board { - None => { - let new_board = game::GameBoard::new(json_state); - let (command, cache) = choose_move(&new_board, strategy_cache, &start_time, max_time); - strategy_cache = Some(cache); - game_board = Some(new_board); - command - }, - Some(game_board) => { - game_board.update(json_state); - let (command, cache) = choose_move(&game_board, strategy_cache, &start_time, max_time); - strategy_cache = Some(cache); - command - } - } - }, - Err(e) => { - eprintln!("WARN: State file could not be parsed: {}", e); - Command::new(Action::DoNothing) + + let command = match json::read_state_from_json_file(&Path::new(&format!( + "./rounds/{}/state.json", + round_number + ))) { + Ok(json_state) => match &mut game_board { + None => { + let new_board = game::GameBoard::new(json_state); + let (command, cache) = + choose_move(&new_board, strategy_cache, &start_time, max_time); + strategy_cache = Some(cache); + game_board = Some(new_board); + command + } + Some(game_board) => { + game_board.update(json_state); + let (command, cache) = + choose_move(&game_board, strategy_cache, &start_time, max_time); + strategy_cache = Some(cache); + command } - }; + }, + Err(e) => { + eprintln!("WARN: State file could not be parsed: {}", e); + Command::new(Action::DoNothing) + } + }; println!("C;{};{}", round_number, command); } } diff --git a/src/strategy.rs b/src/strategy.rs index 502e9f2..e2bf71d 100644 --- a/src/strategy.rs +++ b/src/strategy.rs @@ -1,4 +1,4 @@ -use crate::command::{Command, Action}; +use crate::command::{Action, Command}; use crate::game::{GameBoard, SimulationOutcome}; use std::cmp; @@ -9,7 +9,12 @@ use time::{Duration, PreciseTime}; use rand; use rand::prelude::*; -pub fn choose_move(state: &GameBoard, previous_root: Option, start_time: &PreciseTime, max_time: Duration) -> (Command, Node) { +pub fn choose_move( + state: &GameBoard, + previous_root: Option, + start_time: &PreciseTime, + max_time: Duration, +) -> (Command, Node) { let mut root_node = match previous_root { None => Node { state: state.clone(), @@ -18,21 +23,21 @@ pub fn choose_move(state: &GameBoard, previous_root: Option, start_time: & unexplored: mcts_move_combo(state), children: HashMap::new(), }, - Some(mut node) => { - node.children.drain() - .map(|(_k, n)| n) - .find(|n| n.state == *state) - .unwrap_or_else(|| { - eprintln!("Previous round did not appear in the cache"); - Node { - state: state.clone(), - score_sum: ScoreSum::new(), - player_score_sums: [HashMap::new(), HashMap::new()], - unexplored: mcts_move_combo(state), - children: HashMap::new(), - } - }) - } + Some(mut node) => node + .children + .drain() + .map(|(_k, n)| n) + .find(|n| n.state == *state) + .unwrap_or_else(|| { + eprintln!("Previous round did not appear in the cache"); + Node { + state: state.clone(), + score_sum: ScoreSum::new(), + player_score_sums: [HashMap::new(), HashMap::new()], + unexplored: mcts_move_combo(state), + children: HashMap::new(), + } + }), }; while start_time.to(PreciseTime::now()) < max_time { @@ -51,8 +56,10 @@ pub fn choose_move(state: &GameBoard, previous_root: Option, start_time: & let chosen_command = best_player_move(&root_node); - root_node.children.retain(|[c1, _], _| *c1 == chosen_command); - + root_node + .children + .retain(|[c1, _], _| *c1 == chosen_command); + (chosen_command, root_node) } @@ -162,9 +169,9 @@ fn mcts(node: &mut Node) -> Score { fn mcts_move_combo(state: &GameBoard) -> Vec<[Command; 2]> { let player_moves = valid_moves(state, 0); let opponent_moves = valid_moves(state, 1); - debug_assert!(player_moves.len() > 0, "No player moves"); - debug_assert!(player_moves.len() > 0, "No opponent moves"); - + debug_assert!(!player_moves.is_empty(), "No player moves"); + debug_assert!(!opponent_moves.is_empty(), "No opponent moves"); + let mut result = Vec::with_capacity(player_moves.len() * opponent_moves.len()); for p in &player_moves { for o in &opponent_moves { @@ -189,7 +196,7 @@ fn score(state: &GameBoard) -> Score { SimulationOutcome::PlayerWon(0) => 500., SimulationOutcome::PlayerWon(1) => -500., _ => (state.players[0].score() - state.players[1].score()) as f32, - } + }, } } @@ -245,26 +252,30 @@ fn update(node: &mut Node, commands: [Command; 2], score: Score) { fn rollout_moves(state: &GameBoard, player_index: usize) -> Vec { // TODO: Have this return one move, chosen randomly? - // TODO: Allow new select / bomb moves - if let Some(worm) = state.players[player_index].active_worm() { + // TODO: Heuristic based move + valid_moves(state, player_index) + // + // if let Some(worm) = state.players[player_index].active_worm() { - let shoots = state.sensible_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.into_iter().collect(); - } + // if !shoots.is_empty() { + // return shoots.into_iter().collect(); + // } - state.valid_move_commands(player_index).into_iter().collect() - } else { - [Command::new(Action::DoNothing)].into_iter().cloned().collect() - } + // state.valid_move_commands(player_index).into_iter().collect() + // } else { + // [Command::new(Action::DoNothing)].into_iter().cloned().collect() + // } } fn valid_moves(state: &GameBoard, player_index: usize) -> Vec { - state.valid_shoot_commands(player_index) + state + .valid_shoot_commands(player_index) .iter() .chain(state.valid_move_commands(player_index).iter()) .chain(state.valid_bomb_commands(player_index).iter()) + .chain([Command::new(Action::DoNothing)].iter()) .cloned() .collect() } -- cgit v1.2.3