fix(bot): raise error on empty get_legal_actions

This commit is contained in:
Henri Bourcereau 2026-02-27 18:08:21 +01:00
parent 6840d371fc
commit 72eb60f322
3 changed files with 27 additions and 25 deletions

View file

@ -57,7 +57,7 @@ pub mod ffi {
/// Legal action indices for `player_idx` in [0, 513]. /// Legal action indices for `player_idx` in [0, 513].
/// Returns an empty vector when it is not that player's turn. /// Returns an empty vector when it is not that player's turn.
fn get_legal_actions(self: &TricTracEngine, player_idx: u64) -> Vec<u64>; fn get_legal_actions(self: &TricTracEngine, player_idx: u64) -> Result<Vec<u64>>;
/// Human-readable description of an action index. /// Human-readable description of an action index.
fn action_to_string(self: &TricTracEngine, player_idx: u64, action_idx: u64) -> String; fn action_to_string(self: &TricTracEngine, player_idx: u64, action_idx: u64) -> String;
@ -123,21 +123,16 @@ impl TricTracEngine {
self.game_state.active_player_id - 1 self.game_state.active_player_id - 1
} }
fn get_legal_actions(&self, player_idx: u64) -> Vec<u64> { fn get_legal_actions(&self, player_idx: u64) -> anyhow::Result<Vec<u64>> {
if player_idx != self.current_player_idx() { if player_idx != self.current_player_idx() {
return vec![]; return Ok(vec![]);
} }
if player_idx == 0 { if player_idx == 0 {
get_valid_action_indices(&self.game_state) get_valid_action_indices(&self.game_state)
.into_iter() .map(|v| v.into_iter().map(|i| i as u64).collect())
.map(|i| i as u64)
.collect()
} else { } else {
let mirror = self.game_state.mirror(); let mirror = self.game_state.mirror();
get_valid_action_indices(&mirror) get_valid_action_indices(&mirror).map(|v| v.into_iter().map(|i| i as u64).collect())
.into_iter()
.map(|i| i as u64)
.collect()
} }
} }
@ -184,8 +179,10 @@ impl TricTracEngine {
fn apply_dice_roll(&mut self, dice: ffi::DicePair) -> anyhow::Result<()> { fn apply_dice_roll(&mut self, dice: ffi::DicePair) -> anyhow::Result<()> {
if self.game_state.turn_stage != TurnStage::RollWaiting { if self.game_state.turn_stage != TurnStage::RollWaiting {
anyhow::bail!("apply_dice_roll: not in RollWaiting stage (currently {:?})", anyhow::bail!(
self.game_state.turn_stage); "apply_dice_roll: not in RollWaiting stage (currently {:?})",
self.game_state.turn_stage
);
} }
let player_id = self.game_state.active_player_id; let player_id = self.game_state.active_player_id;
let dice = Dice { let dice = Dice {
@ -214,10 +211,14 @@ impl TricTracEngine {
self.game_state.consume(&evt); self.game_state.consume(&evt);
Ok(()) Ok(())
} }
Some(_) => anyhow::bail!("apply_action: action {} is not valid in current state", Some(_) => anyhow::bail!(
action_idx), "apply_action: action {} is not valid in current state",
None => anyhow::bail!("apply_action: could not build event from action index {}", action_idx
action_idx), ),
None => anyhow::bail!(
"apply_action: could not build event from action index {}",
action_idx
),
} }
} }
} }

View file

@ -43,10 +43,10 @@ impl TricTrac {
fn get_legal_actions(&self, player_idx: u64) -> Vec<usize> { fn get_legal_actions(&self, player_idx: u64) -> Vec<usize> {
if player_idx == self.current_player_idx() { if player_idx == self.current_player_idx() {
if player_idx == 0 { if player_idx == 0 {
get_valid_action_indices(&self.game_state) get_valid_action_indices(&self.game_state).unwrap()
} else { } else {
let mirror = self.game_state.mirror(); let mirror = self.game_state.mirror();
get_valid_action_indices(&mirror) get_valid_action_indices(&mirror).unwrap()
} }
} else { } else {
vec![] vec![]

View file

@ -195,7 +195,7 @@ impl TrictracAction {
} }
/// Obtient les actions valides pour l'état de jeu actuel /// Obtient les actions valides pour l'état de jeu actuel
pub fn get_valid_actions(game_state: &GameState) -> Vec<TrictracAction> { pub fn get_valid_actions(game_state: &GameState) -> anyhow::Result<Vec<TrictracAction>> {
use crate::TurnStage; use crate::TurnStage;
let mut valid_actions = Vec::new(); let mut valid_actions = Vec::new();
@ -246,9 +246,9 @@ pub fn get_valid_actions(game_state: &GameState) -> Vec<TrictracAction> {
} }
if valid_actions.is_empty() { if valid_actions.is_empty() {
panic!("empty valid_actions for state {game_state}"); anyhow::bail!("empty valid_actions for state {game_state}");
} }
valid_actions Ok(valid_actions)
} }
fn checker_moves_to_trictrac_action( fn checker_moves_to_trictrac_action(
@ -336,11 +336,12 @@ fn white_checker_moves_to_trictrac_action(
} }
/// Retourne les indices des actions valides /// Retourne les indices des actions valides
pub fn get_valid_action_indices(game_state: &GameState) -> Vec<usize> { pub fn get_valid_action_indices(game_state: &GameState) -> anyhow::Result<Vec<usize>> {
get_valid_actions(game_state) let actions = get_valid_actions(game_state)?;
Ok(actions
.into_iter() .into_iter()
.map(|action| action.to_action_index()) .map(|action| action.to_action_index())
.collect() .collect())
} }
/// Sélectionne une action valide aléatoire /// Sélectionne une action valide aléatoire
@ -349,7 +350,7 @@ pub fn sample_valid_action(game_state: &GameState) -> Option<TrictracAction> {
let valid_actions = get_valid_actions(game_state); let valid_actions = get_valid_actions(game_state);
let mut rng = rng(); let mut rng = rng();
valid_actions.choose(&mut rng).cloned() valid_actions.unwrap().choose(&mut rng).cloned()
} }
#[cfg(test)] #[cfg(test)]