summaryrefslogtreecommitdiff
path: root/src/strategy.rs
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-05-21 13:27:25 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-05-21 13:27:25 +0200
commit63da94f7f1b25eddeb9ffd379f37c1a32e750fdb (patch)
tree83bd2670e948b16b37890e3ee9785f3672e0d32d /src/strategy.rs
parentb93a9c643485c720a0711ddaf90872b7c6f006c8 (diff)
More robust game logic and reasoning
Diffstat (limited to 'src/strategy.rs')
-rw-r--r--src/strategy.rs70
1 files changed, 19 insertions, 51 deletions
diff --git a/src/strategy.rs b/src/strategy.rs
index 16d639b..8d2eea5 100644
--- a/src/strategy.rs
+++ b/src/strategy.rs
@@ -9,6 +9,7 @@ use time::{Duration, PreciseTime};
use rand;
use rand::prelude::*;
+use arrayvec::ArrayVec;
pub fn choose_move(state: &GameBoard, start_time: &PreciseTime, max_time: Duration) -> Command {
let mut root_node = Node {
@@ -110,6 +111,7 @@ fn mcts(node: &mut Node) -> Score {
let mut new_state = node.state.clone();
new_state.simulate(commands);
let score = rollout(&new_state);
+ // TODO: This could overshoot, trying to estimate from concluded game
let unexplored = mcts_move_combo(&new_state);
let new_node = Node {
@@ -219,16 +221,11 @@ fn update(node: &mut Node, commands: [Command; 2], score: Score) {
node.score_sum += score;
}
-// TODO: Remove all invalid moves onto other worms
-
fn heuristic_moves(state: &GameBoard, player_index: usize) -> Vec<Command> {
let worm = state.players[player_index].active_worm();
- let mut shoots = state
- .find_targets(player_index, worm.position, worm.weapon_range)
- .iter()
- .map(|d| Command::Shoot(*d))
- .collect::<Vec<_>>();
+ let shoots = state
+ .valid_shoot_commands(player_index, worm.position, worm.weapon_range);
let closest_powerup = state.powerups
.iter()
@@ -245,30 +242,20 @@ fn heuristic_moves(state: &GameBoard, player_index: usize) -> Vec<Command> {
.sum::<i8>() / state.players[player_index].worms.len() as i8
};
- let closest_opponent = state.players[(player_index + 1) % 2].worms
+ 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 mut moves = Direction::all()
- .iter()
- .map(Direction::as_vec)
- .map(|d| worm.position + d)
- .filter_map(|p| match state.map.at(p) {
- Some(false) => Some(Command::Move(p.x, p.y)),
- Some(true) => Some(Command::Dig(p.x, p.y)),
- _ => None,
- })
- .collect::<Vec<_>>();
- moves.append(&mut shoots);
- moves
+ 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, worm.position, powerup.position)
+ 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, worm.position, opponent.position)
+ moves_towards(state, player_index, opponent.position)
} else {
// this shouldn't happen
debug_assert!(false, "No valid heuristic moves");
@@ -278,46 +265,27 @@ fn heuristic_moves(state: &GameBoard, player_index: usize) -> Vec<Command> {
commands
}
-fn moves_towards(state: &GameBoard, from: Point2d<i8>, to: Point2d<i8>) -> Vec<Command> {
- let distance = (to - from).walking_distance();
- Direction::all()
+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()
- .map(Direction::as_vec)
- .map(|d| from + d)
- .filter(|p| (to - *p).walking_distance() < distance)
- .filter_map(|p| match state.map.at(p) {
- Some(false) => Some(Command::Move(p.x, p.y)),
- Some(true) => Some(Command::Dig(p.x, p.y)),
- _ => None,
+ .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) -> Vec<Command> {
+fn rollout_moves(state: &GameBoard, player_index: usize) -> ArrayVec<[Command; 8]> {
let worm = state.players[player_index].active_worm();
- let shoots = state
- .find_targets(player_index, worm.position, worm.weapon_range)
- .iter()
- .map(|d| Command::Shoot(*d))
- .collect::<Vec<_>>();
+ let shoots = state.valid_shoot_commands(player_index, worm.position, worm.weapon_range);
if !shoots.is_empty() {
return shoots;
}
// TODO: More directed destruction movements?
- let mut moves = Direction::all()
- .iter()
- .map(Direction::as_vec)
- .map(|d| worm.position + d)
- .filter_map(|p| match state.map.at(p) {
- Some(false) => Some(Command::Move(p.x, p.y)),
- Some(true) => Some(Command::Dig(p.x, p.y)),
- _ => None,
- })
- .collect::<Vec<_>>();
-
- moves.push(Command::DoNothing);
- moves
+ state.valid_move_commands(player_index)
}