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
use std::cmp::{max, Ordering};

use crate::ai::minimax::Heuristic;
use crate::ai::solver::SolverHeuristic;
use crate::board::Board;
use crate::games::ataxx::{AtaxxBoard, Tiles};

#[derive(Debug)]
pub struct AtaxxTileHeuristic {
    tile_factor: i32,
    surface_factor: i32,
}

impl AtaxxTileHeuristic {
    pub fn new(tile_factor: i32, surface_factor: i32) -> Self {
        AtaxxTileHeuristic {
            tile_factor,
            surface_factor,
        }
    }

    pub fn greedy() -> Self {
        AtaxxTileHeuristic {
            tile_factor: 1,
            surface_factor: 0,
        }
    }
}

impl Default for AtaxxTileHeuristic {
    fn default() -> Self {
        AtaxxTileHeuristic {
            tile_factor: 100,
            surface_factor: 10,
        }
    }
}

impl AtaxxTileHeuristic {
    fn player_score(&self, board: &AtaxxBoard, tiles: Tiles) -> i32 {
        let tile_count = tiles.count() as i32;
        let surface_area = (tiles.copy_targets(board.size()) & board.free_tiles()).count() as i32;

        self.tile_factor * tile_count + self.surface_factor * surface_area
    }
}

impl Heuristic<AtaxxBoard> for AtaxxTileHeuristic {
    type V = i32;

    fn value(&self, board: &AtaxxBoard, length: u32) -> Self::V {
        if board.is_done() {
            // return near-max values for wins/draws/losses
            SolverHeuristic.value(board, length).to_i32()
        } else {
            let (next, other) = board.tiles_pov();
            self.player_score(board, next) - self.player_score(board, other)
        }
    }

    fn merge(old: Self::V, new: Self::V) -> (Self::V, Ordering) {
        (max(old, new), new.cmp(&old))
    }
}