diff options
Diffstat (limited to 'src/claude-player.rs')
| -rw-r--r-- | src/claude-player.rs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/claude-player.rs b/src/claude-player.rs new file mode 100644 index 0000000..4590c12 --- /dev/null +++ b/src/claude-player.rs @@ -0,0 +1,79 @@ +use std::{collections::HashMap, time::Duration}; + +use crate::{board::{GameBoard, Move, PieceType, Turn}, player::{CacheEntry, MinimaxPlayer}}; + +/// Sheep that start the game: on the board plus in hand. +const TOTAL_SHEEP: u32 = 20; + +/// A computer player that searches with minimax for `time_limit` per move and scores positions with `evaluate`. +pub struct ClaudePlayer +{ + pub time_limit: Duration, + cache: HashMap<(GameBoard, Turn, usize), CacheEntry> +} + +impl Default for ClaudePlayer +{ + fn default() -> Self + { + Self { time_limit: Duration::from_secs(1), cache: HashMap::new() } + } +} + +impl ClaudePlayer +{ + pub fn new(time_limit: Duration) -> Self + { + Self { time_limit, cache: HashMap::new() } + } +} + +impl MinimaxPlayer for ClaudePlayer +{ + /// Scored from the tigers' side. In order of weight: + /// - dead sheep, since five of them wins the game for the tigers; + /// - jumps the tigers could make right now (sheep left hanging); + /// - tigers boxed in with no move at all, since trapping every tiger wins for the sheep; + /// - tiger mobility in general, since the sheep win by squeezing it to zero. + fn evaluate(&mut self, board: &GameBoard) -> f32 + { + let dead = TOTAL_SHEEP - board.sheep_count() - board.sheep_left(); + + let tiger_moves = board.get_moves(Turn::Tiger); + let jumps = tiger_moves.iter().filter(|m| is_jump(**m)).count(); + + let mut trapped = 0; + for y in 0..5 + { + for x in 0..5 + { + if matches!(board.get((x, y)), PieceType::Tiger) + && !tiger_moves.iter().any(|m| matches!(m, Move::MoveTiger { from, .. } if *from == (x, y))) + { + trapped += 1; + } + } + } + + 100.0 * dead as f32 + + 20.0 * jumps as f32 + - 15.0 * trapped as f32 + + 2.0 * tiger_moves.len() as f32 + } + + fn time_limit(&self) -> Duration { self.time_limit } + + fn cache(&mut self) -> Option<&mut dyn crate::player::ScoreCache> { + Some(&mut self.cache) + } +} + +/// True for a tiger move of two points, which always jumps (and kills) a sheep. +fn is_jump(mv: Move) -> bool +{ + match mv + { + Move::MoveTiger { from: (fx, fy), to: (tx, ty) } => fx.abs_diff(tx) == 2 || fy.abs_diff(ty) == 2, + _ => false, + } +} |
