From 903a5a1e9a3f5a7029f8fc30b05fff5c9a77eee3 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Sat, 10 Aug 2019 13:41:02 +0200 Subject: Simplified code that doesn't have a strategy cache anymore --- src/bin/benchmark.rs | 2 +- src/main.rs | 10 ++------- src/strategy/minimax.rs | 59 ++++++++++++++++--------------------------------- 3 files changed, 22 insertions(+), 49 deletions(-) diff --git a/src/bin/benchmark.rs b/src/bin/benchmark.rs index 0cee8d6..9a62aed 100644 --- a/src/bin/benchmark.rs +++ b/src/bin/benchmark.rs @@ -13,7 +13,7 @@ fn main() { match json::read_state_from_json_file(&Path::new(&format!("./tests/example-state.json"))) { Ok(json_state) => { let new_board = game::GameBoard::new(json_state); - let _ = choose_move(&new_board, None, start_time, max_time); + let _ = choose_move(&new_board, start_time, max_time); } Err(e) => { eprintln!("WARN: State file could not be parsed: {}", e); diff --git a/src/main.rs b/src/main.rs index 280df8a..6f3fba5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,6 @@ use steam_powered_wyrm::strategy::choose_move; fn main() { 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(); @@ -25,18 +24,13 @@ fn main() { 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); + let command = choose_move(&new_board, start_time, max_time); 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 + choose_move(&game_board, start_time, max_time) } }, Err(e) => { diff --git a/src/strategy/minimax.rs b/src/strategy/minimax.rs index ce5e574..6c07b07 100644 --- a/src/strategy/minimax.rs +++ b/src/strategy/minimax.rs @@ -7,33 +7,13 @@ use std::collections::HashMap; use std::ops::*; use time::{Duration, PreciseTime}; -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 { - score_sum: ScoreSum::new(), - player_score_sums: [HashMap::new(), HashMap::new()], - unexplored: move_combos(state), - children: HashMap::new(), - }, - Some(mut node) => node - .children - .drain() - .map(|(_k, n)| n) - .find(|_n| false) // TODO: Use the last player / opponent move and worm positions to use this cache. - .unwrap_or_else(|| { - eprintln!("Previous round did not appear in the cache"); - Node { - score_sum: ScoreSum::new(), - player_score_sums: [HashMap::new(), HashMap::new()], - unexplored: move_combos(state), - children: HashMap::new(), - } - }), +// TODO: Cache results from last round based on player / opponent move and worm positions +pub fn choose_move(state: &GameBoard, start_time: PreciseTime, max_time: Duration) -> Command { + let mut root_node = Node { + score_sum: ScoreSum::new(), + player_score_sums: [HashMap::new(), HashMap::new()], + unexplored: move_combos(state), + children: HashMap::new(), }; while start_time.to(PreciseTime::now()) < max_time { @@ -50,13 +30,7 @@ pub fn choose_move( ); } - let chosen_command = best_player_move(&root_node); - - root_node - .children - .retain(|[c1, _], _| *c1 == chosen_command); - - (chosen_command, root_node) + best_player_move(&root_node) } pub struct Node { @@ -243,17 +217,20 @@ fn score(state: &GameBoard) -> Score { let snowballs = state.players[0].snowballs() as f32 - state.players[1].snowballs() as f32; let bombs = state.players[0].bombs() as f32 - state.players[1].bombs() as f32; + // TODO: Calibrate these weightings somehow? Some sort of generate and sort based on playing against each other? + // What about: + // - Creating a list (mins and maxes) + // - Keep adding a new guess, run against all, and sort the list by fitness. + // - Repeat until list has many values + // - Somehow prioritize sticking new items in based on what's going well? Or maximally different? Keep dividing all the ranges in half? const MAX_HEALTH_WEIGHT: f32 = 1.; const TOTAL_HEALTH_WEIGHT: f32 = 1.; const POINTS_WEIGHT: f32 = 0.; const VICTORY_WEIGHT: f32 = 3000.; - const SNOWBALL_WEIGHT: f32 = 100.; const BOMB_WEIGHT: f32 = 100.; - // TODO: Try adding new features here. Something about board position and ammo remaining? Ammo isn't zero sum, so is it right to represent it as such here? - // TODO: Calibrate these weightings somehow? - // TODO: Distance to dirt heatmap? Probably less relevant these days. + // TODO: Try adding new features here. Something about board position? Score { val: max_health * MAX_HEALTH_WEIGHT + total_health * TOTAL_HEALTH_WEIGHT @@ -318,11 +295,13 @@ fn pruned_moves(state: &GameBoard, player_index: usize) -> Vec { .valid_moves(player_index) .into_iter() .filter(|command| { - // TODO: Filtering out of snowball moves to only freeze opponents + // TODO: Some of these filters could be done with better + // performance by running them while generating the list + // of valid moves. // NB: These rules should pass for doing nothing, otherwise // we need some other mechanism for sticking in a do - // nothing option. Unfortunately, sitting in lava is a situation where this prunes all moves currently :( + // nothing option. let idle_opponent_state = sim_with_idle_opponent(*command); let hurt_self = idle_opponent_state.players[player_index].health() < my_starting_health; -- cgit v1.2.3