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}