1use super::time_management::TimeManagement;
2use crate::engine::game::Game;
3use crate::misc::types::*;
4use cozy_chess::{util, Board, Color};
5use log::{error, info};
6use std::{
7 io::stdin,
8 str::{FromStr, SplitWhitespace},
9};
10
11pub struct Cli {
14 game: Game,
15 tm: TimeManagement,
16}
17
18impl Cli {
19 pub fn new() -> Cli {
21 Cli {
22 game: Default::default(),
23 tm: TimeManagement::default(),
24 }
25 }
26
27 pub fn execute(&mut self) {
29 loop {
30 let mut input = String::new();
31 stdin().read_line(&mut input).unwrap();
32 let mut input_bak = input.clone();
33 input_bak.pop();
34 let input_bak_str = input_bak.as_str();
35 let mut words = input.split_whitespace();
36
37 match words.next() {
38 Some(command) => {
39 let args = words;
40 info!("| {input_bak_str}");
41 match command {
42 "uci" => {
43 self.send_id();
44 self.send_options();
45 self.send_uci_ok();
46 }
47
48 "isready" => {
49 self.send_ready_ok();
50 }
51
52 "position" => {
53 self.position(args);
54 }
55
56 "go" => {
57 self.go(args);
58 }
59
60 "quit" => return,
61
62 _ => continue,
63 }
64 }
65 None => continue,
66 }
67 }
68 }
69
70 fn position(&mut self, mut args: SplitWhitespace) {
72 while let Some(cmd) = args.next() {
73 match cmd {
74 "fen" => {
75 let mut fen: String = "".to_string();
76 for i in 0..6 {
77 match args.next() {
78 Some(s) => {
79 fen = fen + s + " ";
80 if i == 5 {
81 match s.parse::<MoveNumber>() {
83 Ok(n) => self.game.move_number = n,
84 Err(_) => error!("No move number in FEN"),
85 }
86 }
87 }
88 None => {
89 error!("No FEN found");
90 return;
91 }
92 }
93 }
94 fen = fen.trim_end().to_string();
95 match Board::from_str(fen.as_str()) {
96 Ok(b) => self.game.board = b,
97 Err(e) => {
98 error!("FEN not valid: {e}");
99 return;
100 }
101 }
102 }
103
104 "startpos" => {
105 self.game = Game::default();
106 }
107
108 "moves" => loop {
109 match args.next() {
110 Some(move_string) => {
111 match util::parse_uci_move(&self.game.board, move_string) {
112 Ok(m) => {
113 info!("Move: {move_string}");
114 self.game.game_history.inc(&self.game.board);
115 self.game.board.play_unchecked(m);
116 if self.game.board.side_to_move() == Color::Black {
117 self.game.move_number += 1;
118 }
119 }
120 Err(_) => {
121 error!("Illegal move");
122 return;
123 }
124 }
125 }
126 None => return,
127 }
128 },
129
130 _ => break,
131 }
132 }
133 }
134
135 fn go(&mut self, mut args: SplitWhitespace) {
137 while let Some(cmd) = args.next() {
138 match cmd {
139 "searchmoves" => {}
140
141 "ponder" => {}
142
143 "wtime" => match args.next() {
144 Some(arg) => match arg.parse() {
145 Ok(a) => self.tm.white_time = a,
146 Err(_) => break,
147 },
148 None => break,
149 },
150
151 "btime" => match args.next() {
152 Some(arg) => match arg.parse() {
153 Ok(a) => self.tm.black_time = a,
154 Err(_) => break,
155 },
156 None => break,
157 },
158
159 "winc" => match args.next() {
160 Some(arg) => match arg.parse() {
161 Ok(a) => self.tm.white_inc = a,
162 Err(_) => break,
163 },
164 None => break,
165 },
166
167 "binc" => match args.next() {
168 Some(arg) => match arg.parse() {
169 Ok(a) => self.tm.black_inc = a,
170 Err(_) => break,
171 },
172 None => break,
173 },
174
175 "movestogo" => match args.next() {
176 Some(arg) => match arg.parse() {
177 Ok(a) => self.tm.moves_to_go = a,
178 Err(_) => break,
179 },
180 None => break,
181 },
182
183 "depth" => match args.next() {
184 Some(arg) => match arg.parse() {
185 Ok(a) => self.game.max_depth = a,
186 Err(_) => break,
187 },
188 None => break,
189 },
190
191 "nodes" => {}
192
193 "mate" => {}
194
195 "movetime" => match args.next() {
196 Some(arg) => match arg.parse::<u64>() {
197 Ok(a) => {
198 self.game.move_time = a * 9 / 10;
199 }
200 Err(_) => break,
201 },
202 None => break,
203 },
204
205 _ => break,
206 }
207 }
208 self.tm.set_game_time(&mut self.game);
209 self.get_move_from_engine();
210 }
211
212 fn get_move_from_engine(&mut self) {
214 match self.game.find_move() {
215 Some(m) => {
216 let result_uci = util::display_uci_move(&self.game.board, m);
217 self.game.game_history.inc(&self.game.board);
218 self.game.board.play_unchecked(m);
219 let result = format!("bestmove {result_uci}");
220 info!("{} nodes examined.", self.game.node_count);
221 self.send_string(result.as_str());
222 }
223 None => error!("No valid move found"),
224 }
225 }
226
227 fn send_id(&self) {
229 self.send_string("id name C4-E5 Chess");
230 self.send_string("id author Eugen Lindorfer");
231 }
232
233 fn send_options(&self) {
235 self.send_string("option"); }
237
238 fn send_uci_ok(&self) {
240 self.send_string("uciok");
241 }
242
243 fn send_ready_ok(&self) {
245 self.send_string("readyok");
246 }
247
248 fn send_string(&self, s: &str) {
250 println!("{s}");
251 info!("| {s}");
252 }
253}
254
255impl Default for Cli {
256 fn default() -> Self {
257 Self::new()
258 }
259}