diff --git a/store/src/board.rs b/store/src/board.rs index ccbe329..0fba2d6 100644 --- a/store/src/board.rs +++ b/store/src/board.rs @@ -598,6 +598,20 @@ impl Board { core::array::from_fn(|i| i + min) } + /// Returns cumulative white-checker counts: result[i] = # white checkers in fields 1..=i. + /// result[0] = 0. + pub fn white_checker_cumulative(&self) -> [u8; 25] { + let mut cum = [0u8; 25]; + let mut total = 0u8; + for (i, &count) in self.positions.iter().enumerate() { + if count > 0 { + total += count as u8; + } + cum[i + 1] = total; + } + cum + } + pub fn move_checker(&mut self, color: &Color, cmove: CheckerMove) -> Result<(), Error> { self.remove_checker(color, cmove.from)?; self.add_checker(color, cmove.to)?; diff --git a/store/src/training_common.rs b/store/src/training_common.rs index 57094a9..6a5b537 100644 --- a/store/src/training_common.rs +++ b/store/src/training_common.rs @@ -3,7 +3,6 @@ use std::cmp::{max, min}; use std::fmt::{Debug, Display, Formatter}; -use crate::board::Board; use crate::{CheckerMove, Dice, GameEvent, GameState}; use serde::{Deserialize, Serialize}; @@ -221,10 +220,11 @@ pub fn get_valid_actions(game_state: &GameState) -> anyhow::Result anyhow::Result anyhow::Result anyhow::Result { - let dice = &state.dice; - let board = &state.board; - - if color == &crate::Color::Black { - // Moves are already 'white', so we don't mirror them - white_checker_moves_to_trictrac_action( - move1, - move2, - // &move1.clone().mirror(), - // &move2.clone().mirror(), - dice, - &board.clone().mirror(), - ) - // .map(|a| a.mirror()) + // Moves are always in White's coordinate system. For Black, mirror the board first. + let cum = if color == &crate::Color::Black { + state.board.mirror().white_checker_cumulative() } else { - white_checker_moves_to_trictrac_action(move1, move2, dice, board) - } + state.board.white_checker_cumulative() + }; + white_checker_moves_to_trictrac_action(move1, move2, &state.dice, &cum) } fn white_checker_moves_to_trictrac_action( move1: &CheckerMove, move2: &CheckerMove, dice: &Dice, - board: &Board, + cum: &[u8; 25], ) -> anyhow::Result { let to1 = move1.get_to(); let to2 = move2.get_to(); @@ -321,11 +313,21 @@ fn white_checker_moves_to_trictrac_action( } let dice_order = diff_move1 == dice.values.0 as usize; - let checker1 = board.get_field_checker(&crate::Color::White, from1) as usize; - let mut tmp_board = board.clone(); - // should not raise an error for a valid action - tmp_board.move_checker(&crate::Color::White, *move1)?; - let checker2 = tmp_board.get_field_checker(&crate::Color::White, from2) as usize; + // cum[i] = # white checkers in fields 1..=i (precomputed by the caller). + // checker1 is the ordinal of the last checker at from1. + let checker1 = cum[from1] as usize; + // checker2 is the ordinal on the board after move1 (removed from from1, added to to1). + // Adjust the cumulative in O(1) without cloning the board. + let checker2 = { + let mut c = cum[from2]; + if from1 > 0 && from2 >= from1 { + c -= 1; // one checker was removed from from1, shifting later ordinals down + } + if from1 > 0 && to1 > 0 && from2 >= to1 { + c += 1; // one checker was added at to1, shifting later ordinals up + } + c as usize + }; Ok(TrictracAction::Move { dice_order, checker1,