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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
// // This file is part of zero_sum. // // zero_sum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // zero_sum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with zero_sum. If not, see <http://www.gnu.org/licenses/>. // // Copyright 2016-2017 Chris Foster // use std::fmt::Display; use std::hash::Hash; use ply::Ply; use resolution::Resolution; /// The state of the game. /// /// This should represent everything that makes up a single moment of the game, i.e. in chess, /// this would be the board and all of its pieces, the turn number, etc. /// /// However, if the implementor of this trait does store data that changes or increments every turn, /// like a turn number, it is recommended to implement `Hash` manually and to exclude that data from /// the hash, perhaps simplifying it into the next player to move. This is in order to allow /// the struct to benefit from certain search optimization techniques -- primarily a transposition /// table. /// /// # Example /// /// For tic-tac-toe, we might have: /// /// ```rust /// # extern crate zero_sum; /// # use std::hash::{Hash, Hasher}; /// # use zero_sum::{Ply, Resolution, State}; /// # #[derive(Clone, Copy, Eq, Hash, PartialEq)] /// enum Mark { X, O } /// # #[derive(Clone, Debug, Hash, PartialEq)] /// struct Move { /* ... */ } /// enum End { /* ... */ } /// /// # #[derive(Clone, Eq, PartialEq)] /// struct Board([Option<Mark>; 9], u8); // The board and the turn number /// /// impl State for Board { /// type Ply = Move; /// type Resolution = End; /// /// fn get_ply_count(&self) -> usize { /// self.1 as usize /// } /// /// fn execute_ply(&mut self, ply: Option<&Move>) -> Result<(), String> { /// // ... /// # Ok(()) /// } /// /// fn revert_ply(&mut self, ply: Option<&Move>) -> Result<(), String> { /// // ... /// # Ok(()) /// } /// /// fn check_resolution(&self) -> Option<End> { /// // ... /// # None /// } /// } /// /// impl Hash for Board { /// fn hash<H>(&self, state: &mut H) where H: Hasher { /// self.0.hash(state); /// if self.1 % 2 == 0 { /// Mark::X.hash(state); /// } else { /// Mark::O.hash(state); /// } /// } /// } /// # impl Ply for Move { } /// # impl Resolution for End { fn get_winner(&self) -> Option<u8> { None } fn is_draw(&self) -> bool { false } } /// # impl std::fmt::Display for Move { fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result { Ok(()) } } /// # impl std::fmt::Display for Board { fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result { Ok(()) } } /// # fn main() { } /// ``` pub trait State: Clone + Display + Eq + Hash + PartialEq { type Ply: Ply; type Resolution: Resolution; /// Returns the number of plies that have passed in the game. fn get_ply_count(&self) -> usize; /// Executes the given ply on this state. Pass `None` to execute a null move. fn execute_ply(&mut self, ply: Option<&Self::Ply>) -> Result<(), String>; /// Reverts the given ply from the state. Pass `None` to revert a null move. fn revert_ply(&mut self, ply: Option<&Self::Ply>) -> Result<(), String>; /// Returns `None` if the game has not reached a conclusion. fn check_resolution(&self) -> Option<Self::Resolution>; /// Returns true if the state is in a good place to allow the null move search optimization. /// This is optional to implement, returning a default of `false`. fn null_move_allowed(&self) -> bool { false } /// Executes each ply in `plies` on the result of the previous ply. fn execute_plies(&mut self, plies: &[Self::Ply]) -> Result<(), String> { for ply in plies { if let Err(error) = self.execute_ply(Some(ply)) { return Err(format!("Error executing plies: {}, {}", ply, error)); } } Ok(()) } }