summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-06-24 21:04:27 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-06-24 21:04:27 +0200
commit1aeab6da05a0c7b7dad4d06a38b282a82d5e1a51 (patch)
treede76a41b53f4ba383bda118dfb97bd2ff53f7a23
parent6d6cb9e39c4ec3f113833042f1b7bc8082c5d650 (diff)
New command types for select and bombs
-rw-r--r--src/command.rs39
-rw-r--r--src/game.rs44
-rw-r--r--src/main.rs4
-rw-r--r--src/strategy.rs70
-rw-r--r--tests/official-runner-matching.rs11
5 files changed, 76 insertions, 92 deletions
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<i32>,
+ 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<i8>),
Dig(Point2d<i8>),
Shoot(Direction),
+ Bomb(Point2d<i8>),
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<i8>, 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<Command> {
-// 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::<i8>() / state.players[player_index].worms.len() as i8,
-// y: state.players[player_index].worms
-// .iter()
-// .map(|w| w.position.y)
-// .sum::<i8>() / 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<i8>) -> Vec<Command> {
-// 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()
}
}
diff --git a/tests/official-runner-matching.rs b/tests/official-runner-matching.rs
index 19cfdeb..8a0320f 100644
--- a/tests/official-runner-matching.rs
+++ b/tests/official-runner-matching.rs
@@ -1,6 +1,6 @@
use steam_powered_wyrm::json;
use steam_powered_wyrm::game::*;
-use steam_powered_wyrm::command::Command;
+use steam_powered_wyrm::command::{Command, Action};
use steam_powered_wyrm::geometry::*;
use std::path::Path;
@@ -101,14 +101,14 @@ fn read_move(csv_line: &[String]) -> Command {
match csv_line[1].as_ref() {
"move" => {
let (x, y) = read_xy_pair(&csv_line[2]);
- Command::Move(Point2d::new(x, y))
+ Command::new(Action::Move(Point2d::new(x, y)))
},
"dig" => {
let (x, y) = read_xy_pair(&csv_line[2]);
- Command::Dig(Point2d::new(x, y))
+ Command::new(Action::Dig(Point2d::new(x, y)))
},
"nothing" => {
- Command::DoNothing
+ Command::new(Action::DoNothing)
},
"shoot" => {
use steam_powered_wyrm::geometry::Direction::*;
@@ -124,8 +124,9 @@ fn read_move(csv_line: &[String]) -> Command {
"shoot NW" => NorthWest,
_ => panic!("Unknown shoot direction: {}", csv_line[2])
};
- Command::Shoot(dir)
+ Command::new(Action::Shoot(dir))
},
+ // TODO: Parsing of new commands
x => {
panic!("Unknown command {}", x);
}