summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-08-10 13:41:02 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-08-10 13:41:02 +0200
commit903a5a1e9a3f5a7029f8fc30b05fff5c9a77eee3 (patch)
tree9bc01531f04f7a048ea429f759c86649b48b042c
parent355bd2f20d4a9cb60cf9c3898fe54a22445266e6 (diff)
Simplified code that doesn't have a strategy cache anymore
-rw-r--r--src/bin/benchmark.rs2
-rw-r--r--src/main.rs10
-rw-r--r--src/strategy/minimax.rs59
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<Node>,
- 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<Command> {
.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;