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}