1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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,
}
}
|