summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-06-28 21:20:37 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-06-28 21:20:37 +0200
commita07cbfd67849b98a881f930e12e07c429e604ac4 (patch)
tree4b4ad859c169d6366ca2e6e91d0ef09e5986396f
parente20e7f0a9029d33c67869951371cc03965127b31 (diff)
updated active worm in update
This might change weirdly because of the select move.
-rw-r--r--src/game.rs14
-rw-r--r--src/json.rs70
-rw-r--r--src/main.rs60
-rw-r--r--src/strategy.rs79
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<Bomb>
+ pub banana_bombs: Option<Bomb>,
}
#[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<OpponentWorm>,
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<usize> {
+ 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<Node>, start_time: &PreciseTime, max_time: Duration) -> (Command, Node) {
+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 {
state: state.clone(),
@@ -18,21 +23,21 @@ pub fn choose_move(state: &GameBoard, previous_root: Option<Node>, 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<Node>, 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<Command> {
// 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<Command> {
- 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()
}