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 80 81 82 83 84 85
//! This module holds the [`Game`] trait. use crate::search::{Search, Value}; /// This trait can be implemented on any type that represents the state of a game. Doing so requires /// implementations that inform the trait of which moves each player can [`Game::perform`] and how /// well each player is doing in any particular state. In return, this trait will provide a method /// that calculates the [`Game::best_move`] the current player can make. /// /// The const generic in [`Game<N>`] represents the number of players in this game. pub trait Game<const N: usize> { /// The type of the moves that players can [`Game::perform`]. type Move; /// The number of moves to look ahead when searching the [`Game::best_move`]. const DEPTH: u32; /// Returns the index of the player whose turn it is. fn turn(&self) -> usize; /// Evaluates the game's state and returns the relative value for each of the players, /// corresponding to their index according to [`Game::turn`]. /// /// For two-player games, the sum of the values is usually 0: if the second player of a game of /// tic-tac-toe is the winner, the result of this method could be \[-1, 1\]. fn value(&self) -> [Value; N]; /// Returns a [`Vec`] of moves that the current player (see [`Game::turn`]) can /// [`Game::perform`]. fn moves(&self) -> Vec<Self::Move>; /// Performs the given [`Game::Move`], changing the game's state. fn perform(&mut self, m: &Self::Move); /// Reverts the given [`Game::Move`]. This is the dual of [`Game::perform`]: [`Game::revert`] /// after [`Game::perform`] with identical moves should have no effect on game's state. fn revert(&mut self, m: &Self::Move); /// Returns the best [`Game::Move`] for the current player (see [`Game::turn`]), according to /// the calculated [`Game::value`] within the next [`Game::DEPTH`] moves, or [`None`] if the /// current player cannot perform any move. /// /// # Examples /// /// ``` /// # use rival::games::TicTacToe; /// # use rival::cache::WithCache; /// # use rival::game::Game; /// # /// # fn test() -> Option<()> { /// let mut game = TicTacToe::new(); /// /// // Play the entire game /// while !game.moves().is_empty() { /// let m = game.best_move()?; /// game.perform(&m); /// } /// /// // Neither of the players won /// assert_eq!(game.value(), [0, 0]); /// # /// # None /// # } /// ``` fn best_move(&mut self) -> Option<Self::Move> { self.max_n(Self::DEPTH, &mut [Value::MIN; N]).best } /// Indicates whether the current evaluation of the game state will not change much within /// subsequent moves. /// /// If the balance of the game is likely to shift drastically within the next move -- such as /// when a queen in chess is under attack -- this method should return `false`, which will /// trigger the algorithm to continue searching even if the search [`Game::DEPTH`] is exceeded. /// This can improve the performance of the computer player, but it is not necessary to /// implement this method. /// /// # Termination /// /// Keep in mind that searching the [`Game::best_move`] might take forever if this function /// returns `false` too often. fn quiet(&self) -> bool { true } }