1use std::str::FromStr;
2
3use crate::{Board, ChessMove, Color, Square};
4
5#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
7pub enum GameState {
8 #[default]
10 Ongoing,
11 Checkmates(Color),
13 Stalemate,
15 DrawAccepted,
17 DrawDeclared,
19 Resigns(Color),
21}
22
23impl GameState {
24 pub fn is_ongoing(&self) -> bool {
26 matches!(self, GameState::Ongoing)
27 }
28
29 pub fn is_finish(&self) -> bool {
31 !matches!(self, GameState::Ongoing)
32 }
33}
34
35#[derive(Clone, Eq, PartialEq, Default, Debug)]
39pub struct Chess {
40 pub(crate) board: Board,
41 pub(crate) square_focused: Option<Square>,
42 pub(crate) offer_draw: bool,
43 pub(crate) state: GameState,
44 pub(crate) history: Vec<String>,
45}
46
47impl Chess {
48 pub fn new(board: Board) -> Self {
50 Chess {
51 board,
52 square_focused: None,
53 offer_draw: false,
54 history: vec![],
55 state: GameState::Ongoing,
56 }
57 }
58
59 pub fn history(&self) -> Vec<String> {
63 self.history.clone()
64 }
65
66 pub fn undo(&mut self) {
82 if let Some(fen) = self.history.pop() {
83 self.board = Board::from_str(fen.as_str()).expect("valid fen from history");
84 }
85 }
86
87 pub fn reset(&mut self) {
89 self.board = Board::default();
90 self.offer_draw = false;
91 self.square_focused = None;
92 self.history = vec![];
93 self.state = GameState::Ongoing;
94 }
95
96 pub fn state(&mut self) -> GameState {
98 self.state
99 }
100
101 pub fn play(&mut self, from: Square, to: Square) {
103 let m = ChessMove::new(from, to);
104 if self.board.is_legal(m) {
105 self.history.push(self.board.to_string());
106 self.board.update(m);
107 if self.offer_draw {
108 self.offer_draw = false;
109 }
110 self.state = self.board.state();
111 }
112 self.square_focused = None;
113 }
114
115 pub fn offer_draw(&mut self) {
119 self.offer_draw = true;
120 }
121
122 pub fn accept_draw(&mut self) {
124 self.state = GameState::DrawAccepted;
125 }
126
127 pub fn can_declare_draw(&self) -> bool {
129 let t = self.history.len();
130 if t >= 8 {
131 let fen_boards = [
132 self.board
133 .to_string()
134 .split_once(' ')
135 .unwrap()
136 .0
137 .to_string(),
138 self.history[t - 4].split_once(' ').unwrap().0.to_string(),
139 self.history[t - 8].split_once(' ').unwrap().0.to_string(),
140 ];
141 if fen_boards[0] == fen_boards[1] && fen_boards[1] == fen_boards[2] {
142 return true;
143 }
144 if self.board.halfmoves() >= 100 {
145 return true;
146 }
147 }
148 false
149 }
150
151 pub fn declare_draw(&mut self) {
153 self.state = GameState::DrawDeclared;
154 }
155
156 pub fn resign(&mut self, color: Color) {
158 self.state = GameState::Resigns(color);
159 }
160}