ucui 0.1.3

A minimal UCI engine frontend experiment
use std::num::NonZeroU32;

use shakmaty::{Chess, Color, Move, Position};

use crate::util::san_format_move;

#[derive(Clone)]
pub struct Turn<'a> {
    game: Chess,
    move_list: &'a Vec<Move>,
    start_turn: Color,
    index: usize,
    sep_move: String,
    sep_outcome: String,
    bother_outcome: bool,
}

impl<'a> Turn<'a> {
    pub fn new(game: Chess, move_list: &'a Vec<Move>) -> Self {
        let start_turn = game.turn();
        Turn {
            move_list,
            game,
            start_turn,
            index: 0,
            sep_move: String::from("\t"),
            sep_outcome: String::from("\n\t"),
            bother_outcome: true,
        }
    }

    pub fn seps(self, sep0: String, sep1: String) -> Self {
        Turn {
            sep_move: sep0,
            sep_outcome: sep1,
            ..self
        }
    }
    pub fn without_outcome(self) -> Self {
        Turn {
            bother_outcome: false,
            ..self
        }
    }

    pub fn with_outcome(&self, line: &str) -> String {
        match (self.game.outcome(), self.bother_outcome) {
            (Some(outcome), true) => format!("{}{}{}", line, self.sep_outcome, outcome),
            _ => String::from(line),
        }
    }

    fn get_moves(&self) -> (NonZeroU32, Option<&Move>, Option<&Move>) {
        let fullmoves = self.game.fullmoves();

        match (self.start_turn, self.index) {
            (Color::Black, 0) => (fullmoves, None, self.move_list.get(self.index)),
            (_, _) => (
                fullmoves,
                self.move_list.get(self.index),
                self.move_list.get(self.index + 1),
            ),
        }
    }

    pub fn format_move(&self) -> String {
        let (n, wm, bm) = self.get_moves();
        match (wm, bm) {
            (Some(w), Some(b)) => {
                let game = self.game.clone();
                if !game.is_legal(w) {
                    return format!("{:>3}. !legal {}", n, w);
                }
                let np = game.play(w).expect("turn move should be OK");
                self.with_outcome(&format!(
                    "{:>3}. {}{}{}",
                    n,
                    san_format_move(&self.game, w, false),
                    self.sep_move,
                    san_format_move(&np, b, false)
                ))
            }
            (Some(w), None) => self.with_outcome(&format!(
                "{:>3}. {}",
                n,
                san_format_move(&self.game, w, false)
            )),
            (None, Some(b)) => self.with_outcome(&format!(
                "{:>3}... {}",
                n,
                san_format_move(&self.game, b, false)
            )),
            _ => self.with_outcome(""),
        }
    }

    pub fn step(&mut self) -> bool {
        if self.game.outcome().is_some() {
            false
        } else {
            let cloned = self.clone();
            let (_, wm, bm) = cloned.get_moves();
            let mut game = self.game.clone();
            match (wm, bm) {
                (_, None) => false,
                (Some(w), Some(b)) => {
                    game = match game.play(w) {
                        Err(e) => {
                            log::error!("[white move error] {e}");
                            return false;
                        }
                        Ok(game) => game,
                    };
                    game = match game.play(b) {
                        Err(e) => {
                            log::error!("[black move error] {e}");
                            return false;
                        }
                        Ok(game) => game,
                    };
                    self.game = game;
                    self.index += 2;
                    true
                }
                (None, Some(b)) => {
                    game = match game.play(b) {
                        Err(e) => {
                            log::error!("[black move error] {e}");
                            return false;
                        }
                        Ok(game) => game,
                    };
                    self.game = game;
                    self.index += 1;
                    true
                }
            }
        }
    }
}