chess_turn_engine/lib.rs
1//! # Chess turn engine
2//! Turn engine fully implements all chess rules so it could be used to play
3//! chess or to make chess simulations.
4//!
5//! Engine provides a list of available and playable turns after each turn
6//! is played. Any turn from the list can be played.
7//! Any played turn can be undone.
8//!
9//! Input for the engine are chess turns written in algebraic chess notation
10//! format. But for the simplicity sake, one can use `AvailableTurn` struct to
11//! transform simple layman's turn *Move piece from square A to square B* into a
12//! chess notation turn.
13//!
14//! ## Display
15//! Chess engine provides a basic set of tools to print out the board state in
16//! ASCII format. Few options are available for printing out the board state.
17//! Turn history can be printed out. Captured pieces can be printed out.
18//! ### `ViewMode::SimpleAscii` - Quite simple display format
19//! #### Legend
20//! - `b`: Black player
21//! - `w`: White player
22//! - `P`: Pawn
23//! - `N`: Knight
24//! - `B`: Bishop
25//! - `R`: Rook
26//! - `K`: King
27//! - `Q`: Queen
28//!
29//! #### Example
30//! ```text
31//! 8 bR bN bB bQ bK bB bN bR
32//! 7 bP bP bP bP bP bP bP bP
33//! 6 - + - + - + - +
34//! 5 + - + - + - + -
35//! 4 - + - + - + - +
36//! 3 + - + - + - + -
37//! 2 wP wP wP wP wP wP wP wP
38//! 1 wR wN wB wQ wK wB wN wR
39//! a b c d e f g h
40//! ```
41//! ### `ViewMode::FancyTui` - Colorful terminal ASCII display format
42//! Note: *colors are not visible in documentation pages*
43//!
44//! #### Example
45//! ```text
46//! 8 ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
47//! 7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
48//! 6
49//! 5
50//! 4
51//! 3
52//! 2 ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
53//! 1 ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
54//! a b c d e f g h
55//! ```
56//! ## Example with six straightforward turns played
57//! ```
58//! # use chess_turn_engine::{
59//! # ChessTurnEngine, DisplayOption, Setup, ViewMode, Gamestate
60//! # };
61//! #
62//! let mut cte = ChessTurnEngine::new(Setup::Normal).unwrap();
63//! let turns = ["h4", "Na6", "h5", "b5", "d3", "g6"];
64//!
65//! turns.iter().for_each(|turn| {
66//! cte.play_turn(turn);
67//! });
68//!
69//! for turn in cte.available_turns() {
70//! println!("{}", turn);
71//! }
72//!
73//! // Output:
74//! // AvailableTurn (src: h5, dst: h6, piece: Pawn, captured: None, turn: h6
75//! // AvailableTurn (src: h5, dst: g6, piece: Pawn, captured: Some("Pawn"), turn: hxg6
76//! // AvailableTurn (src: d3, dst: d4, piece: Pawn, captured: None, turn: d4
77//! // AvailableTurn (src: a2, dst: a3, piece: Pawn, captured: None, turn: a3
78//! // AvailableTurn (src: a2, dst: a4, piece: Pawn, captured: None, turn: a4
79//! // AvailableTurn (src: b2, dst: b3, piece: Pawn, captured: None, turn: b3
80//! // AvailableTurn (src: b2, dst: b4, piece: Pawn, captured: None, turn: b4
81//! // AvailableTurn (src: c2, dst: c3, piece: Pawn, captured: None, turn: c3
82//! // AvailableTurn (src: c2, dst: c4, piece: Pawn, captured: None, turn: c4
83//! // AvailableTurn (src: e2, dst: e3, piece: Pawn, captured: None, turn: e3
84//! // AvailableTurn (src: e2, dst: e4, piece: Pawn, captured: None, turn: e4
85//! // AvailableTurn (src: f2, dst: f3, piece: Pawn, captured: None, turn: f3
86//! // AvailableTurn (src: f2, dst: f4, piece: Pawn, captured: None, turn: f4
87//! // AvailableTurn (src: g2, dst: g3, piece: Pawn, captured: None, turn: g3
88//! // AvailableTurn (src: g2, dst: g4, piece: Pawn, captured: None, turn: g4
89//! // AvailableTurn (src: b1, dst: c3, piece: Knight, captured: None, turn: Nc3
90//! // AvailableTurn (src: b1, dst: a3, piece: Knight, captured: None, turn: Na3
91//! // AvailableTurn (src: b1, dst: d2, piece: Knight, captured: None, turn: Nd2
92//! // AvailableTurn (src: c1, dst: d2, piece: Bishop, captured: None, turn: Bd2
93//! // AvailableTurn (src: c1, dst: e3, piece: Bishop, captured: None, turn: Be3
94//! // AvailableTurn (src: c1, dst: f4, piece: Bishop, captured: None, turn: Bf4
95//! // AvailableTurn (src: c1, dst: g5, piece: Bishop, captured: None, turn: Bg5
96//! // AvailableTurn (src: c1, dst: h6, piece: Bishop, captured: None, turn: Bh6
97//! // AvailableTurn (src: d1, dst: d2, piece: Queen, captured: None, turn: Qd2
98//! // AvailableTurn (src: e1, dst: d2, piece: King, captured: None, turn: Kd2
99//! // AvailableTurn (src: g1, dst: h3, piece: Knight, captured: None, turn: Nh3
100//! // AvailableTurn (src: g1, dst: f3, piece: Knight, captured: None, turn: Nf3
101//! // AvailableTurn (src: h1, dst: h2, piece: Rook, captured: None, turn: Rh2
102//! // AvailableTurn (src: h1, dst: h3, piece: Rook, captured: None, turn: Rh3
103//! // AvailableTurn (src: h1, dst: h4, piece: Rook, captured: None, turn: Rh4
104//! ```
105//! ## Example with two turns played
106//! ```
107//! # use chess_turn_engine::{
108//! # ChessTurnEngine, DisplayOption, Setup, ViewMode, Gamestate
109//! # };
110//! #
111//! let mut cte = ChessTurnEngine::new(Setup::Normal).unwrap();
112//! assert!(cte.play_turn("d4").is_ok()); // Play "d4", starting turn
113//!
114//! // Play a random turn for the black player
115//! let mut next_random_turn =
116//! String::from(cte.available_turns().first().unwrap().get_turn());
117//! assert!(cte.play_turn(&next_random_turn).is_ok());
118//!
119//! // Play "a3" turn using only info about the source and destination squares
120//! for turn in cte.available_turns() {
121//! if turn.src != String::from("a2") && turn.dst != String::from("a3") {
122//! continue;
123//! }
124//! next_random_turn = String::from(turn.get_turn());
125//! break;
126//! }
127//! assert!(cte.play_turn(&next_random_turn).is_ok());
128//! assert_eq!(cte.gamestate(), Gamestate::Ongoing);
129//!
130//! cte.display_on_screen(DisplayOption::BoardView(ViewMode::SimpleAscii));
131//! // 8 bR bN bB bQ bK bB bN bR
132//! // 7 bP bP bP - bP bP bP bP
133//! // 6 - + - + - + - +
134//! // 5 + - + bP + - + -
135//! // 4 - + - wP - + - +
136//! // 3 wP - + - + - + -
137//! // 2 - wP wP + wP wP wP wP
138//! // 1 wR wN wB wQ wK wB wN wR
139//! // a b c d e f g h
140//! ```
141
142mod game;
143
144pub use game::availableturn::AvailableTurn;
145pub use game::game_error::GameError;
146pub use game::gamestate::Gamestate;
147use game::Game;
148
149/// Game engine
150pub struct ChessTurnEngine {
151 /// Game
152 game: Game,
153}
154
155/// View modes for `BoardView`
156#[derive(Copy, Clone)]
157pub enum ViewMode {
158 /// Display board in plain ASCII format
159 SimpleAscii,
160
161 /// Display board in colorful ASCII format for terminal purposes
162 FancyTui,
163}
164
165/// Display option
166#[derive(Copy, Clone)]
167pub enum DisplayOption {
168 /// Display board state
169 BoardView(ViewMode),
170
171 /// Display turn history in plain ASCII format
172 TurnHistory,
173
174 /// Display capture history in plain ASCII format
175 CaptureHistory,
176}
177
178/// Chessboard setup
179#[derive(Copy, Clone)]
180pub enum Setup {
181 /// **Default** board setup
182 Normal,
183
184 /// **Custom setup** (experimental usage)
185 ///
186 /// Note: *Castling turns are not possible in custom setups*
187 ///
188 /// Format for the custom setup is:
189 /// ```(`Square`,`Player`,`Piece` )+```
190 /// - `Square`
191 /// - Must be specified with lowercase letters: e.g. `a1`
192 /// - `Player`
193 /// - `w` for White
194 /// - `b` for Black
195 /// - `Piece`
196 /// - `P`: Pawn
197 /// - `R`: Rook
198 /// - `B`: Bishop
199 /// - `N`: Knight
200 /// - `Q`: Queen
201 /// - `K`: King - Note: *game always must have two kings*
202 ///
203 /// Below is an example of how default chess setup looks like:
204 ///
205 /// ```
206 /// let default_setup =
207 /// "a1,w,R b1,w,N c1,w,B d1,w,Q e1,w,K f1,w,B g1,w,N h1,w,R \
208 /// a2,w,P b2,w,P c2,w,P d2,w,P e2,w,P f2,w,P g2,w,P h2,w,P \
209 /// a7,b,P b7,b,P c7,b,P d7,b,P e7,b,P f7,b,P g7,b,P h7,b,P \
210 /// a8,b,R b8,b,N c8,b,B d8,b,Q e8,b,K f8,b,B g8,b,N h8,b,R";
211 /// ```
212 Custom(&'static str),
213}
214
215impl ChessTurnEngine {
216 /// Create and setup a new game
217 ///
218 /// # Arguments
219 ///
220 /// * `setup` - Game setup
221 pub fn new(setup: Setup) -> Result<ChessTurnEngine, &'static str> {
222 let setup = match setup {
223 Setup::Normal => game::NORMAL_SETUP,
224 Setup::Custom(setup) => setup,
225 };
226
227 let game = Game::new(setup)?;
228 Ok(ChessTurnEngine { game })
229 }
230
231 /// Provide a displayable `String` of the chessboard
232 ///
233 /// # Arguments
234 ///
235 /// * `opt` - Display option
236 pub fn display(&self, opt: DisplayOption) -> String {
237 self.game.display(opt)
238 }
239
240 /// Display chessboard on the screen
241 ///
242 /// # Arguments
243 ///
244 /// * `opt` - Display option
245 pub fn display_on_screen(&self, opt: DisplayOption) {
246 println!("{}", self.game.display(opt));
247 }
248
249 /// Play provided turn.
250 ///
251 /// # Return value
252 /// - `Ok` - Latest game status.
253 /// - `Err` - `GameError` in case turn is not playable.
254 ///
255 /// # Arguments
256 ///
257 /// * `turn` - Turn provided in chess notation format
258 pub fn play_turn(&mut self, turn: &str) -> Result<Gamestate, GameError> {
259 self.game.play_turn(turn)
260 }
261
262 /// Get game status
263 pub fn gamestate(&self) -> Gamestate {
264 self.game.gamestate()
265 }
266
267 /// Undo turn and restore previous board state
268 pub fn undo_turn(&mut self) -> Result<(), GameError> {
269 self.game.undo_turn()
270 }
271
272 /// Get list of available turns
273 pub fn available_turns(&self) -> &Vec<AvailableTurn> {
274 self.game.available_turns()
275 }
276}