board_game_traits/
lib.rs

1//! Traits for abstract game position representations.
2//!
3//! General game-agnostic tools and engines can be built on this module
4//! Represents any 2-player sequential, deterministic, perfect-information game. This includes many popular games such as chess, go, xiangqi, othello, connect four and tic-tac-toe.
5
6use self::Color::*;
7use std::fmt;
8use std::hash;
9use std::ops;
10
11/// Represents a player's color.
12#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
13pub enum Color {
14    White = 0,
15    Black = 1,
16}
17
18impl ops::Not for Color {
19    type Output = Color;
20
21    #[inline]
22    fn not(self) -> Self {
23        match self {
24            White => Black,
25            Black => White,
26        }
27    }
28}
29
30impl fmt::Display for Color {
31    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
32        fmt.write_str(match *self {
33            White => "White",
34            Black => "Black",
35        })
36    }
37}
38
39impl Color {
40    /// Returns the color's discriminant. 0 for white, 1 for black.
41    /// # Examples
42    /// ```rust
43    /// use board_game_traits::Color;
44    /// assert_eq!(Color::White.disc(), 0);
45    /// assert_eq!(Color::Black.disc(), 1);
46    /// ```
47    #[inline]
48    pub fn disc(self) -> usize {
49        self as u16 as usize
50    }
51
52    /// Returns the color's multiplier. -1 for black, 1 for white.
53    /// # Examples
54    /// ```rust
55    /// use board_game_traits::Color;
56    /// assert_eq!(Color::White.multiplier(), 1);
57    /// assert_eq!(Color::Black.multiplier(), -1);
58    /// ```
59    #[inline]
60    pub fn multiplier(self) -> isize {
61        self as u16 as isize * -2 + 1
62    }
63}
64
65/// The result of a game after it has finished.
66#[derive(PartialEq, Eq, Clone, Debug, Copy)]
67pub enum GameResult {
68    WhiteWin = 0,
69    BlackWin = 1,
70    Draw = 2,
71}
72
73impl GameResult {
74    /// Returns WhiteWin for white, BlackWin for black
75    #[inline]
76    pub fn win_by(color: Color) -> Self {
77        match color {
78            White => Self::WhiteWin,
79            Black => Self::BlackWin,
80        }
81    }
82}
83
84impl ops::Not for GameResult {
85    type Output = Self;
86    #[inline]
87    fn not(self) -> Self {
88        match self {
89            GameResult::WhiteWin => GameResult::BlackWin,
90            GameResult::BlackWin => GameResult::WhiteWin,
91            GameResult::Draw => GameResult::Draw,
92        }
93    }
94}
95
96/// The simplest abstract representation of a game position. Together, the provided methods encode all the rules of the game.
97pub trait Position: Sized {
98    /// The type for moves in the game.
99    type Move: Eq + Clone + fmt::Debug;
100    /// The type for a reverse move in the game.
101    type ReverseMove;
102    /// Optional Settings when initializing the position.
103    type Settings: Default;
104
105    /// Returns the starting position for the game. This function always produces identical values.
106    #[inline]
107    fn start_position() -> Self {
108        Self::start_position_with_settings(&Self::Settings::default())
109    }
110
111    /// Returns the starting position for the game with the given settings.
112    fn start_position_with_settings(settings: &Self::Settings) -> Self;
113
114    /// Returns the side to move for the current position.
115    fn side_to_move(&self) -> Color;
116
117    /// Generates all legal moves for the side to move, and extends the provided data structure with them.
118    fn generate_moves<E: Extend<Self::Move>>(&self, moves: &mut E);
119
120    /// Checks if a move is legal in the current position.
121    /// Enables minimax algorithms to use the killer-move heuristic in their search.
122    fn move_is_legal(&self, mv: Self::Move) -> bool {
123        let mut moves = vec![];
124        self.generate_moves(&mut moves);
125        moves.contains(&mv)
126    }
127
128    /// Plays a move in the position. Also returns an ReverseMove do take the move back.
129    ///
130    /// Doing and then undoing a move always restores the position to exactly the same state.
131    fn do_move(&mut self, mv: Self::Move) -> Self::ReverseMove;
132
133    /// Reverse a move made by `do_move`.
134    ///
135    /// Doing and then undoing a move always restores the position to exactly the same state.
136    fn reverse_move(&mut self, mv: Self::ReverseMove);
137
138    /// Returns the result if the game is decided, otherwise returns None.
139    /// If the winning player always plays the last move (as in chess), implementations are allowed
140    /// to only return a win when the losing player is to move.
141    fn game_result(&self) -> Option<GameResult>;
142}
143
144/// A game position that also includes a heuristic static evaluation function.
145/// Enables the use of many game-playing algorithms, such as minimax.
146pub trait EvalPosition: Position + PartialEq + Clone {
147    /// A fast, static evaluation of the current position.
148    /// Returns a number between -100 and 100, where 0.0 is a draw, positive number means better for white, and negative number means better for black.
149    fn static_eval(&self) -> f32;
150}
151
152/// An extended game representation, which includes many additional methods to help game-playing algorithms search more effectively.
153pub trait ExtendedPosition: EvalPosition {
154    /// The type for a reverse null move
155    type ReverseNullMove;
156
157    /// A representation of the position that can be hashed. Can be Self, or unit if no hashing is desired.
158    type HashPosition: hash::Hash + Eq;
159
160    fn hash_position(&self) -> Self::HashPosition;
161
162    /// Generates only the "active" moves in a position, and appends them to the provided vector. These are moves that radically change the static evaluation of a position, e.g. captures or promotions in chess.
163    /// Search algorithms may recursively search all active moves, so eventually, no moves will be appended.
164    /// Required for search algorithms to use quiescence search.
165    fn active_moves(&self, moves: &mut Vec<Self::Move>);
166
167    fn null_move_is_available(&self) -> bool;
168
169    /// Does a passing "null move".
170    /// A null move is an empty which just transfers the turn to the other player. Enables the null move reduction heuristic.
171    fn do_null_move(&mut self) -> Self::ReverseNullMove;
172
173    /// Reverses a passing "null move".
174    fn reverse_null_move(&mut self, reverse_move: Self::ReverseNullMove);
175
176    /// Returns an estimate for the average branch factor of the game.
177    /// Helps search algorithms guide pruning and time management.
178    const BRANCH_FACTOR: u64 = 20;
179}