use std::io::{self, Write};
use crate::engine::{GameCore, ChessEngine};
use crate::core::Move10;
use crate::board::PieceMapping;
use crate::rules::checkmate::{CheckmateLogic, Color, GameResult};
fn pid_to_char(pid: u8) -> char {
match pid {
0..=7 => 'p', 8 | 9 => 'r', 10 | 11 => 'n', 12 | 13 => 'b', 14 => 'q', 15 => 'k', 16..=23 => 'P', 24 | 25 => 'R', 26 | 27 => 'N', 28 | 29 => 'B', 30 => 'Q', 31 => 'K', _ => '.', }
}
fn print_board_with_header(mapping: &PieceMapping, occupied: u64, ply: usize, side: Color) {
let turn = ply + 1;
let side_str = match side {
Color::White => "White",
Color::Black => "Black",
};
let king_pid = if side == Color::White { 15 } else { 31 };
let in_check = CheckmateLogic::is_in_check(mapping, occupied, king_pid);
if in_check {
println!("\nTurn {}: {} to move Check!", turn, side_str);
} else {
println!("\nTurn {}: {} to move", turn, side_str);
}
let mut board: [[char; 8]; 8] = [['.'; 8]; 8];
for pid in 0..32 {
if let Some(sq) = mapping.piece_square[pid as usize] {
let rank = (sq >> 3) as usize;
let file = (sq & 7) as usize;
board[rank][file] = pid_to_char(pid as u8);
}
}
for rank in (0..8).rev() {
print!("{} |", rank + 1);
for file in 0..8 {
print!(" {}", board[rank][file]);
}
println!();
}
println!(" ----------------");
println!(" a b c d e f g h\n");
}
fn parse_long_algebraic(input: &str) -> Option<(u8, u8)> {
let bytes = input.trim().as_bytes();
if bytes.len() != 4 {
return None;
}
let sf = bytes[0] as char;
let sr = bytes[1] as char;
let df = bytes[2] as char;
let dr = bytes[3] as char;
let file_from = (sf as u8).wrapping_sub(b'a');
let rank_from = (sr as u8).wrapping_sub(b'1');
let file_to = (df as u8).wrapping_sub(b'a');
let rank_to = (dr as u8).wrapping_sub(b'1');
if file_from < 8 && rank_from < 8 && file_to < 8 && rank_to < 8 {
let src_sq = (rank_from << 3) | file_from;
let dst_sq = (rank_to << 3) | file_to;
Some((src_sq, dst_sq))
} else {
None
}
}
pub fn run_cli() {
let mut engine: GameCore = GameCore::default();
println!("Welcome to Chess CLI!");
println!("Enter moves in ‘long algebraic’ (e.g. e2e4), ‘undo’, or ‘quit’.");
print_board_with_header(
engine.current_mapping(),
engine.current_occupied(),
engine.ply(),
engine.side_to_move(),
);
loop {
print!("Move> ");
io::stdout().flush().unwrap();
let mut input = String::new();
if io::stdin().read_line(&mut input).is_err() {
println!("Error reading input, exiting.");
break;
}
let trimmed = input.trim();
if trimmed.eq_ignore_ascii_case("quit") || trimmed.eq_ignore_ascii_case("exit") {
println!("Goodbye!");
break;
}
if trimmed.eq_ignore_ascii_case("undo") {
if engine.ply() == 0 {
println!("No moves to undo.");
} else {
engine.pop_move();
print_board_with_header(
engine.current_mapping(),
engine.current_occupied(),
engine.ply(),
engine.side_to_move(),
);
}
continue;
}
if trimmed.is_empty() {
continue;
}
match parse_long_algebraic(trimmed) {
Some((src, dst)) => {
let mapping = engine.current_mapping();
if let Some(actual_pid) = mapping.who_on_square(src) {
let side = engine.side_to_move();
let piece_color = if actual_pid < 16 {
Color::White
} else {
Color::Black
};
if piece_color != side {
println!("Illegal: trying to move opponent’s piece.");
continue;
}
let pid_within = actual_pid % 16;
let mv10 = Move10::new(pid_within, dst);
let legal_moves = engine.generate_pseudo_legal_moves();
if !legal_moves.contains(&mv10) {
println!("Move not allowed by piece rules (not pseudo-legal).");
continue;
}
match engine.push_move(mv10) {
Ok(()) => {
let result = engine.game_result();
match result {
GameResult::Ongoing => {
print_board_with_header(
engine.current_mapping(),
engine.current_occupied(),
engine.ply(),
engine.side_to_move(),
);
}
GameResult::Stalemate => {
print_board_with_header(
engine.current_mapping(),
engine.current_occupied(),
engine.ply(),
engine.side_to_move(),
);
println!("\nGame Over: Stalemate. It's a draw.");
break;
}
GameResult::Checkmate(winner) => {
print_board_with_header(
engine.current_mapping(),
engine.current_occupied(),
engine.ply(),
engine.side_to_move(), );
let winner_str = match winner {
Color::White => "White",
Color::Black => "Black",
};
println!("\nGame Over: {} wins by checkmate!", winner_str);
break;
}
}
}
Err(e) => {
println!("Illegal move (leaves king in check): {:?}", e);
}
}
} else {
println!("No piece on source square.");
}
}
None => {
println!("Invalid format. Use e.g. ‘e2e4’.");
}
}
}
}