fixed uci
This commit is contained in:
parent
66cea5a2bf
commit
f1ec0a08d9
5 changed files with 164 additions and 60 deletions
|
|
@ -1,5 +1,6 @@
|
|||
// ... (your use statements)
|
||||
use crate::board::Board;
|
||||
use crate::r#move::Move;
|
||||
use crate::search::minimax::minimax;
|
||||
|
||||
pub struct Engine {
|
||||
|
|
@ -19,10 +20,18 @@ impl Engine {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn setpos(&mut self, fen: &str) {
|
||||
pub fn setpos_fen(&mut self, fen: &str) {
|
||||
self.board = Board::from_fen(fen);
|
||||
}
|
||||
|
||||
pub fn setpos_startpos(&mut self, moves: &[&str]) {
|
||||
self.board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
|
||||
for mv_str in moves {
|
||||
let mv = Move::from_algebraic(mv_str, &self.board);
|
||||
self.board.make_move(mv);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search(&mut self, depth: u8) {
|
||||
let (opt_move, _score) = minimax(&mut self.board, depth);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ pub mod display;
|
|||
pub mod parsing;
|
||||
pub mod search;
|
||||
pub mod engine;
|
||||
pub mod uci;
|
||||
|
|
|
|||
49
src/main.rs
49
src/main.rs
|
|
@ -1,52 +1,7 @@
|
|||
use std::io::{self, BufRead};
|
||||
use chess_engine::engine::Engine;
|
||||
use chess_engine::uci::uci_mainloop;
|
||||
|
||||
fn main() {
|
||||
// Create a new engine instance
|
||||
let mut engine = Engine::new("Yakari".to_string(), "EiSiMo".to_string());
|
||||
|
||||
loop {
|
||||
// Start the main UCI loop
|
||||
for line in io::stdin().lock().lines() {
|
||||
let input = line.unwrap_or_else(|_| "quit".to_string());
|
||||
let tokens: Vec<&str> = input.split_whitespace().collect();
|
||||
|
||||
if tokens.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match tokens[0] {
|
||||
"uci" => {
|
||||
println!("id name {}", engine.name);
|
||||
println!("id author {}", engine.author);
|
||||
println!("uciok");
|
||||
}
|
||||
"isready" => {
|
||||
println!("readyok");
|
||||
}
|
||||
"position" => {
|
||||
// Example: "position startpos moves e2e4 e7e5"
|
||||
// Or: "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||
// You'll need to write a parser for this!
|
||||
// For now, let's just handle the "fen" part simply.
|
||||
if tokens.len() > 1 && tokens[1] == "fen" {
|
||||
let fen = tokens[2..].join(" ");
|
||||
engine.setpos(&fen);
|
||||
}
|
||||
}
|
||||
"go" => {
|
||||
// Example: "go depth 6"
|
||||
// For now, we'll just use the fixed depth from your search function.
|
||||
engine.search(5);
|
||||
}
|
||||
"quit" => {
|
||||
break; // Exit the loop and the program
|
||||
}
|
||||
_ => {
|
||||
// Unknown command, just ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uci_mainloop(&mut engine);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::mem;
|
||||
use crate::board::{Board, Color, PieceType, CASTLING_BK_FLAG, CASTLING_BQ_FLAG, CASTLING_WK_FLAG, CASTLING_WQ_FLAG};
|
||||
use crate::r#move::{Move, MOVE_FLAG_BK_CASTLE, MOVE_FLAG_BQ_CASTLE, MOVE_FLAG_PROMO_B, MOVE_FLAG_PROMO_B_CAP, MOVE_FLAG_PROMO_N, MOVE_FLAG_PROMO_N_CAP, MOVE_FLAG_PROMO_Q, MOVE_FLAG_PROMO_Q_CAP, MOVE_FLAG_PROMO_R, MOVE_FLAG_PROMO_R_CAP, MOVE_FLAG_WK_CASTLE, MOVE_FLAG_WQ_CASTLE, MOVE_FROM_MASK, MOVE_TO_MASK};
|
||||
use crate::board::*;
|
||||
use crate::r#move::*;
|
||||
use crate::square::Square;
|
||||
|
||||
impl Board {
|
||||
|
|
@ -35,27 +35,27 @@ impl Board {
|
|||
match c {
|
||||
'P' => {
|
||||
pieces[PieceType::Pawn as usize][color_idx] |= mask;
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Pawn); // <-- ADDED
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Pawn);
|
||||
}
|
||||
'N' => {
|
||||
pieces[PieceType::Knight as usize][color_idx] |= mask;
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Knight); // <-- ADDED
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Knight);
|
||||
}
|
||||
'B' => {
|
||||
pieces[PieceType::Bishop as usize][color_idx] |= mask;
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Bishop); // <-- ADDED
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Bishop);
|
||||
}
|
||||
'R' => {
|
||||
pieces[PieceType::Rook as usize][color_idx] |= mask;
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Rook); // <-- ADDED
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Rook);
|
||||
}
|
||||
'Q' => {
|
||||
pieces[PieceType::Queen as usize][color_idx] |= mask;
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Queen); // <-- ADDED
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::Queen);
|
||||
}
|
||||
'K' => {
|
||||
pieces[PieceType::King as usize][color_idx] |= mask;
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::King); // <-- ADDED
|
||||
pieces_on_squares[sq as usize] = Some(PieceType::King);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -237,7 +237,6 @@ impl Board {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl Move {
|
||||
/// Converts a square index (0-63) to algebraic notation (e.g., 0 -> "a1", 63 -> "h8").
|
||||
fn square_val_to_alg(val: u16) -> String {
|
||||
|
|
@ -246,6 +245,16 @@ impl Move {
|
|||
format!("{}{}", file, rank)
|
||||
}
|
||||
|
||||
/// Converts algebraic notation (e.g., "a1") to a Square.
|
||||
/// Assumes valid input.
|
||||
fn alg_to_square(alg: &str) -> Square {
|
||||
let file = (alg.as_bytes()[0] - b'a') as u8;
|
||||
let rank = (alg.as_bytes()[1] - b'1') as u8;
|
||||
let sq_index = rank * 8 + file;
|
||||
// This is unsafe, but we assume valid algebraic notation
|
||||
unsafe { mem::transmute::<u8, Square>(sq_index) }
|
||||
}
|
||||
|
||||
/// Converts the move to coordinate notation (e.g., "e2e4", "e7e8q", "e1g1").
|
||||
pub fn to_algebraic(&self) -> String {
|
||||
let flags = self.get_flags();
|
||||
|
|
@ -267,11 +276,77 @@ impl Move {
|
|||
};
|
||||
format!("{}{}{}", from_str, to_str, promo_char)
|
||||
} else {
|
||||
// This covers Quiet, DoublePawn, Capture, EnPassant
|
||||
// This covers Quiet, DoublePawn, Capture, EnPassant, Castles
|
||||
format!("{}{}", from_str, to_str)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// pub fn from_algebraic(s: &str, board: &Board) -> Move {}
|
||||
/// Creates a Move from algebraic notation (e.g., "e2e4") and a board state.
|
||||
/// Assumes the move is valid and legal for the given board state.
|
||||
pub fn from_algebraic(s: &str, board: &Board) -> Move {
|
||||
let from_sq = Self::alg_to_square(&s[0..2]);
|
||||
let to_sq = Self::alg_to_square(&s[2..4]);
|
||||
|
||||
let moving_piece = board.pieces_on_squares[from_sq as usize]
|
||||
.expect("Invalid move: No piece on 'from' square.");
|
||||
|
||||
let is_capture = board.pieces_on_squares[to_sq as usize].is_some();
|
||||
|
||||
// 1. Handle Promotions
|
||||
if s.len() == 5 {
|
||||
let promo_char = s.chars().nth(4).unwrap();
|
||||
match (promo_char, is_capture) {
|
||||
('q', false) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_Q),
|
||||
('n', false) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_N),
|
||||
('r', false) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_R),
|
||||
('b', false) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_B),
|
||||
('q', true) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_Q_CAP),
|
||||
('n', true) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_N_CAP),
|
||||
('r', true) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_R_CAP),
|
||||
('b', true) => return Move::new(from_sq, to_sq, MOVE_FLAG_PROMO_B_CAP),
|
||||
_ => panic!("Invalid promotion character"),
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Handle Castling
|
||||
if moving_piece == PieceType::King {
|
||||
let from_idx = from_sq as u8;
|
||||
let to_idx = to_sq as u8;
|
||||
|
||||
// White King Side: e1g1 (idx 4 -> 6)
|
||||
if from_idx == 4 && to_idx == 6 { return Move::new(from_sq, to_sq, MOVE_FLAG_WK_CASTLE); }
|
||||
// White Queen Side: e1c1 (idx 4 -> 2)
|
||||
if from_idx == 4 && to_idx == 2 { return Move::new(from_sq, to_sq, MOVE_FLAG_WQ_CASTLE); }
|
||||
// Black King Side: e8g8 (idx 60 -> 62)
|
||||
if from_idx == 60 && to_idx == 62 { return Move::new(from_sq, to_sq, MOVE_FLAG_BK_CASTLE); }
|
||||
// Black Queen Side: e8c8 (idx 60 -> 58)
|
||||
if from_idx == 60 && to_idx == 58 { return Move::new(from_sq, to_sq, MOVE_FLAG_BQ_CASTLE); }
|
||||
}
|
||||
|
||||
// 3. Handle Pawn Special Moves
|
||||
if moving_piece == PieceType::Pawn {
|
||||
// Double Pawn Push
|
||||
let rank_diff = (to_sq as i8 - from_sq as i8).abs();
|
||||
if rank_diff == 16 {
|
||||
return Move::new(from_sq, to_sq, MOVE_FLAG_DOUBLE_PAWN);
|
||||
}
|
||||
|
||||
// En Passant
|
||||
// Must be diagonal move, to the en_passant_target square, and not a normal capture
|
||||
if Some(to_sq) == board.en_passant_target && !is_capture {
|
||||
let from_file = from_sq as u8 % 8;
|
||||
let to_file = to_sq as u8 % 8;
|
||||
if from_file != to_file {
|
||||
return Move::new(from_sq, to_sq, MOVE_FLAG_EN_PASSANT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Handle Normal Captures / Quiet Moves
|
||||
if is_capture {
|
||||
Move::new(from_sq, to_sq, MOVE_FLAG_CAPTURE)
|
||||
} else {
|
||||
Move::new(from_sq, to_sq, MOVE_FLAG_QUIET)
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/uci.rs
Normal file
64
src/uci.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// uci.rs
|
||||
|
||||
use std::io::{self, BufRead};
|
||||
use crate::engine::Engine;
|
||||
|
||||
pub fn uci_mainloop(engine: &mut Engine) {
|
||||
loop {
|
||||
// Start the main UCI loop
|
||||
for line in io::stdin().lock().lines() {
|
||||
let input = line.unwrap_or_else(|_| "quit".to_string());
|
||||
let tokens: Vec<&str> = input.split_whitespace().collect();
|
||||
|
||||
if tokens.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match tokens[0] {
|
||||
"uci" => {
|
||||
println!("id name {}", engine.name);
|
||||
println!("id author {}", engine.author);
|
||||
println!("uciok");
|
||||
}
|
||||
"isready" => {
|
||||
println!("readyok");
|
||||
}
|
||||
"ucinewgame" => {
|
||||
// not yet implemented
|
||||
}
|
||||
"position" => {
|
||||
if tokens.len() > 1 {
|
||||
if tokens[1] == "fen" {
|
||||
let fen = tokens[2..].join(" ");
|
||||
engine.setpos_fen(&fen);
|
||||
} else if tokens[1] == "startpos" {
|
||||
// Check explicitly for the "moves" keyword
|
||||
if tokens.len() > 2 && tokens[2] == "moves" {
|
||||
// Command: "position startpos moves e2e4 e7e5 ..."
|
||||
// Pass only the tokens *after* "moves"
|
||||
engine.setpos_startpos(&tokens[3..]);
|
||||
} else {
|
||||
// Command: "position startpos"
|
||||
// Pass an empty slice
|
||||
engine.setpos_startpos(&[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"go" => {
|
||||
// TODO add a lot functionality
|
||||
engine.search(5);
|
||||
}
|
||||
"stop" => {
|
||||
// TODO stop search as soon as possible
|
||||
}
|
||||
"quit" => {
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
// Unknown command, just ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue