Compare commits
No commits in common. "0b65bdbf881b881df4de98ed43964b259e2ae965" and "58f83ea985f72be3bcca3f2f90811d70160de685" have entirely different histories.
0b65bdbf88
...
58f83ea985
10 changed files with 47 additions and 107 deletions
|
|
@ -7,7 +7,7 @@
|
||||||
"waiting_for_opponent": "Waiting for opponent…",
|
"waiting_for_opponent": "Waiting for opponent…",
|
||||||
"your_turn_roll": "Your turn — roll the dice",
|
"your_turn_roll": "Your turn — roll the dice",
|
||||||
"hold_or_go": "Hold or Go?",
|
"hold_or_go": "Hold or Go?",
|
||||||
"select_move": "Move a checker ({{ n }} of 2)",
|
"select_move": "Select move {{ n }} of 2",
|
||||||
"your_turn": "Your turn",
|
"your_turn": "Your turn",
|
||||||
"opponent_turn": "Opponent's turn",
|
"opponent_turn": "Opponent's turn",
|
||||||
"room_label": "Room: {{ id }}",
|
"room_label": "Room: {{ id }}",
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"waiting_for_opponent": "En attente de l'adversaire…",
|
"waiting_for_opponent": "En attente de l'adversaire…",
|
||||||
"your_turn_roll": "À votre tour — lancez les dés",
|
"your_turn_roll": "À votre tour — lancez les dés",
|
||||||
"hold_or_go": "Tenir ou s'en aller ?",
|
"hold_or_go": "Tenir ou s'en aller ?",
|
||||||
"select_move": "Déplacez une dame ({{ n }} sur 2)",
|
"select_move": "Sélectionner le coup {{ n }} sur 2",
|
||||||
"your_turn": "Votre tour",
|
"your_turn": "Votre tour",
|
||||||
"opponent_turn": "Tour de l'adversaire",
|
"opponent_turn": "Tour de l'adversaire",
|
||||||
"room_label": "Salle : {{ id }}",
|
"room_label": "Salle : {{ id }}",
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ use crate::components::{ConnectingScreen, GameScreen, LoginScreen};
|
||||||
use crate::i18n::I18nContextProvider;
|
use crate::i18n::I18nContextProvider;
|
||||||
use crate::trictrac::backend::TrictracBackend;
|
use crate::trictrac::backend::TrictracBackend;
|
||||||
use crate::trictrac::bot_local::bot_decide;
|
use crate::trictrac::bot_local::bot_decide;
|
||||||
use crate::trictrac::types::{
|
use crate::trictrac::types::{GameDelta, JanEntry, PlayerAction, ScoredEvent, SerTurnStage, ViewState};
|
||||||
GameDelta, JanEntry, PlayerAction, ScoredEvent, SerTurnStage, ViewState,
|
|
||||||
};
|
|
||||||
use trictrac_store::CheckerMove;
|
use trictrac_store::CheckerMove;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
@ -196,9 +194,7 @@ pub fn App() -> impl IntoView {
|
||||||
if remote_config.is_none() {
|
if remote_config.is_none() {
|
||||||
loop {
|
loop {
|
||||||
let restart = run_local_bot_game(screen, &mut cmd_rx, pending).await;
|
let restart = run_local_bot_game(screen, &mut cmd_rx, pending).await;
|
||||||
if !restart {
|
if !restart { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pending.update(|q| q.clear());
|
pending.update(|q| q.clear());
|
||||||
screen.set(Screen::Login { error: None });
|
screen.set(Screen::Login { error: None });
|
||||||
|
|
@ -332,12 +328,8 @@ async fn run_local_bot_game(
|
||||||
let mut vs = ViewState::default_with_names("You", "Bot");
|
let mut vs = ViewState::default_with_names("You", "Bot");
|
||||||
for cmd in backend.drain_commands() {
|
for cmd in backend.drain_commands() {
|
||||||
match cmd {
|
match cmd {
|
||||||
BackendCommand::ResetViewState => {
|
BackendCommand::ResetViewState => { vs = backend.get_view_state().clone(); }
|
||||||
vs = backend.get_view_state().clone();
|
BackendCommand::Delta(delta) => { vs.apply_delta(&delta); }
|
||||||
}
|
|
||||||
BackendCommand::Delta(delta) => {
|
|
||||||
vs.apply_delta(&delta);
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -448,21 +440,15 @@ fn compute_scored_event(prev: &ViewState, next: &ViewState, player_id: u16) -> O
|
||||||
&& prev.active_mp_player == Some(player_id)
|
&& prev.active_mp_player == Some(player_id)
|
||||||
{
|
{
|
||||||
// My own roll: positive totals are mine.
|
// My own roll: positive totals are mine.
|
||||||
next.dice_jans
|
next.dice_jans.iter().filter(|e| e.total > 0).cloned().collect()
|
||||||
.iter()
|
} else if next.active_mp_player == Some(player_id)
|
||||||
.filter(|e| e.total > 0)
|
&& prev.active_mp_player != Some(player_id)
|
||||||
.cloned()
|
{
|
||||||
.collect()
|
|
||||||
} else if next.active_mp_player == Some(player_id) && prev.active_mp_player != Some(player_id) {
|
|
||||||
// Opponent just moved: negative totals (their penalty) are scored for me.
|
// Opponent just moved: negative totals (their penalty) are scored for me.
|
||||||
next.dice_jans
|
next.dice_jans
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|e| e.total < 0)
|
.filter(|e| e.total < 0)
|
||||||
.map(|e| JanEntry {
|
.map(|e| JanEntry { total: -e.total, points_per: -e.points_per, ..e.clone() })
|
||||||
total: -e.total,
|
|
||||||
points_per: -e.points_per,
|
|
||||||
..e.clone()
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -510,10 +496,7 @@ fn push_or_show(
|
||||||
});
|
});
|
||||||
// Animation belongs to the buffered confirmation step; clear it on the
|
// Animation belongs to the buffered confirmation step; clear it on the
|
||||||
// fallback live state so it doesn't fire again after the queue drains.
|
// fallback live state so it doesn't fire again after the queue drains.
|
||||||
screen.set(Screen::Playing(GameUiState {
|
screen.set(Screen::Playing(GameUiState { last_moves: None, ..new_state }));
|
||||||
last_moves: None,
|
|
||||||
..new_state
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
// No pause: show scoring directly on the live state.
|
// No pause: show scoring directly on the live state.
|
||||||
screen.set(Screen::Playing(GameUiState {
|
screen.set(Screen::Playing(GameUiState {
|
||||||
|
|
@ -536,7 +519,8 @@ fn infer_pause_reason(prev: &ViewState, next: &ViewState, player_id: u16) -> Opt
|
||||||
return Some(PauseReason::AfterOpponentRoll);
|
return Some(PauseReason::AfterOpponentRoll);
|
||||||
}
|
}
|
||||||
// Was at HoldOrGoChoice, now Move, opponent still active → opponent went.
|
// Was at HoldOrGoChoice, now Move, opponent still active → opponent went.
|
||||||
if prev.turn_stage == SerTurnStage::HoldOrGoChoice && next.turn_stage == SerTurnStage::Move
|
if prev.turn_stage == SerTurnStage::HoldOrGoChoice
|
||||||
|
&& next.turn_stage == SerTurnStage::Move
|
||||||
{
|
{
|
||||||
return Some(PauseReason::AfterOpponentGo);
|
return Some(PauseReason::AfterOpponentGo);
|
||||||
}
|
}
|
||||||
|
|
@ -550,18 +534,14 @@ fn infer_pause_reason(prev: &ViewState, next: &ViewState, player_id: u16) -> Opt
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::trictrac::types::{PlayerScore, SerStage, SerTurnStage};
|
use crate::trictrac::types::{PlayerScore, SerStage, SerTurnStage};
|
||||||
|
|
||||||
fn score() -> PlayerScore {
|
fn score() -> PlayerScore {
|
||||||
PlayerScore {
|
PlayerScore { name: String::new(), points: 0, holes: 0, can_bredouille: false }
|
||||||
name: String::new(),
|
|
||||||
points: 0,
|
|
||||||
holes: 0,
|
|
||||||
can_bredouille: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vs(dice: (u8, u8), turn_stage: SerTurnStage, active: Option<u16>) -> ViewState {
|
fn vs(dice: (u8, u8), turn_stage: SerTurnStage, active: Option<u16>) -> ViewState {
|
||||||
|
|
@ -574,7 +554,6 @@ mod tests {
|
||||||
dice,
|
dice,
|
||||||
dice_jans: Vec::new(),
|
dice_jans: Vec::new(),
|
||||||
dice_moves: (CheckerMove::default(), CheckerMove::default()),
|
dice_moves: (CheckerMove::default(), CheckerMove::default()),
|
||||||
message: "".into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -582,30 +561,21 @@ mod tests {
|
||||||
fn dice_change_is_after_roll() {
|
fn dice_change_is_after_roll() {
|
||||||
let prev = vs((0, 0), SerTurnStage::RollDice, Some(1));
|
let prev = vs((0, 0), SerTurnStage::RollDice, Some(1));
|
||||||
let next = vs((3, 5), SerTurnStage::Move, Some(1));
|
let next = vs((3, 5), SerTurnStage::Move, Some(1));
|
||||||
assert_eq!(
|
assert_eq!(infer_pause_reason(&prev, &next, 0), Some(PauseReason::AfterOpponentRoll));
|
||||||
infer_pause_reason(&prev, &next, 0),
|
|
||||||
Some(PauseReason::AfterOpponentRoll)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hold_to_move_is_after_go() {
|
fn hold_to_move_is_after_go() {
|
||||||
let prev = vs((3, 5), SerTurnStage::HoldOrGoChoice, Some(1));
|
let prev = vs((3, 5), SerTurnStage::HoldOrGoChoice, Some(1));
|
||||||
let next = vs((3, 5), SerTurnStage::Move, Some(1));
|
let next = vs((3, 5), SerTurnStage::Move, Some(1));
|
||||||
assert_eq!(
|
assert_eq!(infer_pause_reason(&prev, &next, 0), Some(PauseReason::AfterOpponentGo));
|
||||||
infer_pause_reason(&prev, &next, 0),
|
|
||||||
Some(PauseReason::AfterOpponentGo)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn turn_switch_is_after_move() {
|
fn turn_switch_is_after_move() {
|
||||||
let prev = vs((3, 5), SerTurnStage::Move, Some(1));
|
let prev = vs((3, 5), SerTurnStage::Move, Some(1));
|
||||||
let next = vs((3, 5), SerTurnStage::RollDice, Some(0));
|
let next = vs((3, 5), SerTurnStage::RollDice, Some(0));
|
||||||
assert_eq!(
|
assert_eq!(infer_pause_reason(&prev, &next, 0), Some(PauseReason::AfterOpponentMove));
|
||||||
infer_pause_reason(&prev, &next, 0),
|
|
||||||
Some(PauseReason::AfterOpponentMove)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
let i18n = use_i18n();
|
let i18n = use_i18n();
|
||||||
|
|
||||||
let vs = state.view_state.clone();
|
let vs = state.view_state.clone();
|
||||||
let message = format!("{}", vs.message);
|
|
||||||
let player_id = state.player_id;
|
let player_id = state.player_id;
|
||||||
let is_my_turn = vs.active_mp_player == Some(player_id);
|
let is_my_turn = vs.active_mp_player == Some(player_id);
|
||||||
let is_move_stage = is_my_turn
|
let is_move_stage = is_my_turn
|
||||||
|
|
@ -352,9 +351,6 @@ pub fn GameScreen(state: GameUiState) -> impl IntoView {
|
||||||
|
|
||||||
// ── Player score (below board) ────────────────────────────────────
|
// ── Player score (below board) ────────────────────────────────────
|
||||||
<PlayerScorePanel score=my_score is_you=true />
|
<PlayerScorePanel score=my_score is_you=true />
|
||||||
<div>
|
|
||||||
{format!("{message}")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
// ── Game-over overlay ─────────────────────────────────────────────
|
// ── Game-over overlay ─────────────────────────────────────────────
|
||||||
{stage_is_ended.then(|| {
|
{stage_is_ended.then(|| {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ pub fn LoginScreen(error: Option<String>) -> impl IntoView {
|
||||||
|
|
||||||
<h1 class="login-title">"Trictrac"</h1>
|
<h1 class="login-title">"Trictrac"</h1>
|
||||||
<p class="login-subtitle">
|
<p class="login-subtitle">
|
||||||
<em>"Une interprétation numérique"</em>
|
<em>"Jeu de trictrac"</em>
|
||||||
|
" — "
|
||||||
|
<em>"XVIII" <sup>"e"</sup> " siècle"</em>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="login-ornament">"✦"</div>
|
<div class="login-ornament">"✦"</div>
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,6 @@ impl BackEndArchitecture<PlayerAction, GameDelta, ViewState> for TrictracBackend
|
||||||
moves: (m1, m2),
|
moves: (m1, m2),
|
||||||
};
|
};
|
||||||
if self.game.validate(&event) {
|
if self.game.validate(&event) {
|
||||||
self.game.debug_message = format!("Event {:?} validated", event);
|
|
||||||
let _ = self.game.consume(&event);
|
let _ = self.game.consume(&event);
|
||||||
self.drive_automatic_stages();
|
self.drive_automatic_stages();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub fn bot_decide(game: &GameState) -> Option<PlayerAction> {
|
||||||
}
|
}
|
||||||
match game.turn_stage {
|
match game.turn_stage {
|
||||||
TurnStage::RollDice => Some(PlayerAction::Roll),
|
TurnStage::RollDice => Some(PlayerAction::Roll),
|
||||||
TurnStage::HoldOrGoChoice => Some(PlayerAction::Mark),
|
TurnStage::HoldOrGoChoice => Some(PlayerAction::Go),
|
||||||
TurnStage::Move => {
|
TurnStage::Move => {
|
||||||
let rules = MoveRules::new(&Color::Black, &game.board, game.dice);
|
let rules = MoveRules::new(&Color::Black, &game.board, game.dice);
|
||||||
let sequences = rules.get_possible_moves_sequences(true, vec![]);
|
let sequences = rules.get_possible_moves_sequences(true, vec![]);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ pub struct ViewState {
|
||||||
pub dice_jans: Vec<JanEntry>,
|
pub dice_jans: Vec<JanEntry>,
|
||||||
/// Last two checker moves played; default when no move has occurred yet.
|
/// Last two checker moves played; default when no move has occurred yet.
|
||||||
pub dice_moves: (CheckerMove, CheckerMove),
|
pub dice_moves: (CheckerMove, CheckerMove),
|
||||||
pub message: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One scoring event from a dice roll.
|
/// One scoring event from a dice roll.
|
||||||
|
|
@ -71,23 +70,12 @@ impl ViewState {
|
||||||
turn_stage: SerTurnStage::RollDice,
|
turn_stage: SerTurnStage::RollDice,
|
||||||
active_mp_player: None,
|
active_mp_player: None,
|
||||||
scores: [
|
scores: [
|
||||||
PlayerScore {
|
PlayerScore { name: host_name.to_string(), points: 0, holes: 0, can_bredouille: false },
|
||||||
name: host_name.to_string(),
|
PlayerScore { name: guest_name.to_string(), points: 0, holes: 0, can_bredouille: false },
|
||||||
points: 0,
|
|
||||||
holes: 0,
|
|
||||||
can_bredouille: false,
|
|
||||||
},
|
|
||||||
PlayerScore {
|
|
||||||
name: guest_name.to_string(),
|
|
||||||
points: 0,
|
|
||||||
holes: 0,
|
|
||||||
can_bredouille: false,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
dice: (0, 0),
|
dice: (0, 0),
|
||||||
dice_jans: Vec::new(),
|
dice_jans: Vec::new(),
|
||||||
dice_moves: (CheckerMove::default(), CheckerMove::default()),
|
dice_moves: (CheckerMove::default(), CheckerMove::default()),
|
||||||
message: "".into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,21 +86,25 @@ impl ViewState {
|
||||||
/// Convert a store `GameState` to a `ViewState`.
|
/// Convert a store `GameState` to a `ViewState`.
|
||||||
/// `host_store_id` and `guest_store_id` are the trictrac `PlayerId`s assigned
|
/// `host_store_id` and `guest_store_id` are the trictrac `PlayerId`s assigned
|
||||||
/// to the host (mp player 0) and guest (mp player 1) respectively.
|
/// to the host (mp player 0) and guest (mp player 1) respectively.
|
||||||
pub fn from_game_state(gs: &GameState, host_store_id: u64, guest_store_id: u64) -> Self {
|
pub fn from_game_state(
|
||||||
|
gs: &GameState,
|
||||||
|
host_store_id: u64,
|
||||||
|
guest_store_id: u64,
|
||||||
|
) -> Self {
|
||||||
let board_vec = gs.board.to_vec();
|
let board_vec = gs.board.to_vec();
|
||||||
let board: [i8; 24] = board_vec.try_into().expect("board is always 24 fields");
|
let board: [i8; 24] = board_vec.try_into().expect("board is always 24 fields");
|
||||||
|
|
||||||
let stage = match gs.stage {
|
let stage = match gs.stage {
|
||||||
Stage::PreGame => SerStage::PreGame,
|
Stage::PreGame => SerStage::PreGame,
|
||||||
Stage::InGame => SerStage::InGame,
|
Stage::InGame => SerStage::InGame,
|
||||||
Stage::Ended => SerStage::Ended,
|
Stage::Ended => SerStage::Ended,
|
||||||
};
|
};
|
||||||
let turn_stage = match gs.turn_stage {
|
let turn_stage = match gs.turn_stage {
|
||||||
TurnStage::RollDice => SerTurnStage::RollDice,
|
TurnStage::RollDice => SerTurnStage::RollDice,
|
||||||
TurnStage::RollWaiting => SerTurnStage::RollWaiting,
|
TurnStage::RollWaiting => SerTurnStage::RollWaiting,
|
||||||
TurnStage::MarkPoints => SerTurnStage::MarkPoints,
|
TurnStage::MarkPoints => SerTurnStage::MarkPoints,
|
||||||
TurnStage::HoldOrGoChoice => SerTurnStage::HoldOrGoChoice,
|
TurnStage::HoldOrGoChoice => SerTurnStage::HoldOrGoChoice,
|
||||||
TurnStage::Move => SerTurnStage::Move,
|
TurnStage::Move => SerTurnStage::Move,
|
||||||
TurnStage::MarkAdvPoints => SerTurnStage::MarkAdvPoints,
|
TurnStage::MarkAdvPoints => SerTurnStage::MarkAdvPoints,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -133,12 +125,7 @@ impl ViewState {
|
||||||
holes: p.holes,
|
holes: p.holes,
|
||||||
can_bredouille: p.can_bredouille,
|
can_bredouille: p.can_bredouille,
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| PlayerScore {
|
.unwrap_or_else(|| PlayerScore { name: String::new(), points: 0, holes: 0, can_bredouille: false })
|
||||||
name: String::new(),
|
|
||||||
points: 0,
|
|
||||||
holes: 0,
|
|
||||||
can_bredouille: false,
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// is_double for scoring: dice show the same value (both dice identical).
|
// is_double for scoring: dice show the same value (both dice identical).
|
||||||
|
|
@ -147,16 +134,13 @@ impl ViewState {
|
||||||
|
|
||||||
// Build JanEntry list from the PossibleJans map.
|
// Build JanEntry list from the PossibleJans map.
|
||||||
let empty_move = CheckerMove::new(0, 0).unwrap_or_default();
|
let empty_move = CheckerMove::new(0, 0).unwrap_or_default();
|
||||||
let mut dice_jans: Vec<JanEntry> = gs
|
let mut dice_jans: Vec<JanEntry> = gs.dice_jans
|
||||||
.dice_jans
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(jan, moves)| {
|
.map(|(jan, moves)| {
|
||||||
// HelplessMan: is_double = true only when *both* dice are unplayable
|
// HelplessMan: is_double = true only when *both* dice are unplayable
|
||||||
// (the moves list contains a single (empty, empty) sentinel).
|
// (the moves list contains a single (empty, empty) sentinel).
|
||||||
let is_double = if *jan == Jan::HelplessMan {
|
let is_double = if *jan == Jan::HelplessMan {
|
||||||
moves
|
moves.first().map(|&(m1, m2)| m1 == empty_move && m2 == empty_move)
|
||||||
.first()
|
|
||||||
.map(|&(m1, m2)| m1 == empty_move && m2 == empty_move)
|
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
} else {
|
} else {
|
||||||
dice_are_double
|
dice_are_double
|
||||||
|
|
@ -186,7 +170,6 @@ impl ViewState {
|
||||||
dice: (gs.dice.values.0, gs.dice.values.1),
|
dice: (gs.dice.values.0, gs.dice.values.1),
|
||||||
dice_jans,
|
dice_jans,
|
||||||
dice_moves: gs.dice_moves,
|
dice_moves: gs.dice_moves,
|
||||||
message: gs.get_debug_message(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ pub struct GameState {
|
||||||
roll_first: bool,
|
roll_first: bool,
|
||||||
// NOTE: add to a Setting struct if other fields needed
|
// NOTE: add to a Setting struct if other fields needed
|
||||||
pub schools_enabled: bool,
|
pub schools_enabled: bool,
|
||||||
pub debug_message: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// implement Display trait
|
// implement Display trait
|
||||||
|
|
@ -120,7 +119,6 @@ impl Default for GameState {
|
||||||
dice_jans: PossibleJans::default(),
|
dice_jans: PossibleJans::default(),
|
||||||
roll_first: true,
|
roll_first: true,
|
||||||
schools_enabled: false,
|
schools_enabled: false,
|
||||||
debug_message: "".into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -149,11 +147,6 @@ impl GameState {
|
||||||
game
|
game
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_debug_message(&self) -> String {
|
|
||||||
// format!("{:?}", self.history.last())
|
|
||||||
format!("{:?}", self.debug_message)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mirror(&self) -> GameState {
|
pub fn mirror(&self) -> GameState {
|
||||||
let mirrored_active_player = if self.active_player_id == 1 { 2 } else { 1 };
|
let mirrored_active_player = if self.active_player_id == 1 { 2 } else { 1 };
|
||||||
let mut mirrored_players = HashMap::new();
|
let mut mirrored_players = HashMap::new();
|
||||||
|
|
@ -178,7 +171,6 @@ impl GameState {
|
||||||
dice_jans: self.dice_jans.mirror(),
|
dice_jans: self.dice_jans.mirror(),
|
||||||
roll_first: self.roll_first,
|
roll_first: self.roll_first,
|
||||||
schools_enabled: self.schools_enabled,
|
schools_enabled: self.schools_enabled,
|
||||||
debug_message: self.debug_message.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -602,9 +594,8 @@ impl GameState {
|
||||||
dice_points: (0, 0),
|
dice_points: (0, 0),
|
||||||
dice_moves: (CheckerMove::default(), CheckerMove::default()),
|
dice_moves: (CheckerMove::default(), CheckerMove::default()),
|
||||||
dice_jans: PossibleJans::default(),
|
dice_jans: PossibleJans::default(),
|
||||||
roll_first: false, // Assume not first roll
|
roll_first: false, // Assume not first roll
|
||||||
schools_enabled: false, // Assume disabled
|
schools_enabled: false, // Assume disabled
|
||||||
debug_message: "".into(), // Assume disabled
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -955,15 +955,15 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
state.board.set_positions(
|
state.board.set_positions(
|
||||||
&Color::Black,
|
&Color::White,
|
||||||
[
|
[
|
||||||
10, 0, 0, 0, -1, 0, 2, 0, 0, 0, 1, 2, 0, -1, -1, 0, 2, 0, 0, 0, 0, 0, 0, -10,
|
6, 0, 0, 0, 0, 0, 2, 2, 1, 2, 0, 2, 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
state.dice.values = (4, 1);
|
state.dice.values = (3, 3);
|
||||||
let moves = (
|
let moves = (
|
||||||
CheckerMove::new(15, 14).unwrap().mirror(),
|
CheckerMove::new(14, 11).unwrap(),
|
||||||
CheckerMove::new(14, 10).unwrap().mirror(),
|
CheckerMove::new(14, 11).unwrap(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(MoveError::OpponentCanFillQuarter),
|
Err(MoveError::OpponentCanFillQuarter),
|
||||||
|
|
@ -1277,7 +1277,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert!(!state.moves_possible(&moves));
|
assert!(!state.moves_possible(&moves));
|
||||||
|
|
||||||
// Chaned moves: can't rest on a field occupied by one opponent's checker
|
|
||||||
state.board.set_positions(
|
state.board.set_positions(
|
||||||
&Color::White,
|
&Color::White,
|
||||||
[
|
[
|
||||||
|
|
@ -1289,7 +1288,7 @@ mod tests {
|
||||||
CheckerMove::new(10, 15).unwrap(),
|
CheckerMove::new(10, 15).unwrap(),
|
||||||
CheckerMove::new(15, 20).unwrap(),
|
CheckerMove::new(15, 20).unwrap(),
|
||||||
);
|
);
|
||||||
assert!(!state.moves_possible(&moves));
|
assert!(state.moves_possible(&moves));
|
||||||
|
|
||||||
// black moves
|
// black moves
|
||||||
let state = MoveRules::new(&Color::Black, &Board::default(), Dice::default());
|
let state = MoveRules::new(&Color::Black, &Board::default(), Dice::default());
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue