refactor(core,engine,wasm): canonical FOUNDATIONS/TABLEAUS consts; adopt upstream SUITS/RANKS
Review findings 1+2 (Quat-underuse lens, 2026-07-06):
- solitaire_core gains pub const FOUNDATIONS / TABLEAUS — the canonical
iteration source for the upstream pile enums (upstream klondike has no
Foundation::ALL, and inherent impls cannot be added to foreign types).
Deletes three identical private const-fn copies (radial_menu,
table_plugin, input_plugin) and the hand-enumerated variants in
card_plugin::sync::all_cards and solitaire_wasm.
- Hand-rolled [Suit; 4] / [Rank; 13] arrays replaced with upstream
Suit::SUITS / Rank::RANKS. The order-sensitive CardImageSet indexing
is re-keyed through canonical card_plugin::{suit_index, rank_index}
helpers that match upstream order, with regression tests asserting
the correspondence — one ordering everywhere instead of three
divergent local ones.
Net -177 lines. No behaviour change; all consumers go through the
canonical helpers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+21
-69
@@ -19,7 +19,7 @@
|
||||
//! is the contract.
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use solitaire_core::{Foundation, KlondikeInstruction, KlondikePile, Tableau};
|
||||
use solitaire_core::{KlondikeInstruction, KlondikePile};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solitaire_core::{Card, Deck, Rank, Suit};
|
||||
use solitaire_core::error::MoveError;
|
||||
@@ -145,21 +145,10 @@ impl ReplayPlayer {
|
||||
let pile_cards = |t: KlondikePile| -> Vec<CardSnapshot> {
|
||||
self.game.pile(t).iter().map(CardSnapshot::from).collect()
|
||||
};
|
||||
let foundations: [Vec<CardSnapshot>; 4] = [
|
||||
pile_cards(KlondikePile::Foundation(Foundation::Foundation1)),
|
||||
pile_cards(KlondikePile::Foundation(Foundation::Foundation2)),
|
||||
pile_cards(KlondikePile::Foundation(Foundation::Foundation3)),
|
||||
pile_cards(KlondikePile::Foundation(Foundation::Foundation4)),
|
||||
];
|
||||
let tableaus: [Vec<CardSnapshot>; 7] = [
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau1)),
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau2)),
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau3)),
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau4)),
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau5)),
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau6)),
|
||||
pile_cards(KlondikePile::Tableau(Tableau::Tableau7)),
|
||||
];
|
||||
let foundations: [Vec<CardSnapshot>; 4] = solitaire_core::FOUNDATIONS
|
||||
.map(|f| pile_cards(KlondikePile::Foundation(f)));
|
||||
let tableaus: [Vec<CardSnapshot>; 7] =
|
||||
solitaire_core::TABLEAUS.map(|t| pile_cards(KlondikePile::Tableau(t)));
|
||||
StateSnapshot {
|
||||
step_idx: self.step_idx,
|
||||
total_steps: self.moves.len(),
|
||||
@@ -353,21 +342,9 @@ fn legal_moves_for_game(game: &GameState) -> Vec<DebugMove> {
|
||||
fn invariant_report_for_game(game: &GameState, legal_moves: &[DebugMove]) -> DebugInvariantReport {
|
||||
let stock = game.stock_cards();
|
||||
let waste = game.waste_cards();
|
||||
let foundations = [
|
||||
game.pile(KlondikePile::Foundation(Foundation::Foundation1)),
|
||||
game.pile(KlondikePile::Foundation(Foundation::Foundation2)),
|
||||
game.pile(KlondikePile::Foundation(Foundation::Foundation3)),
|
||||
game.pile(KlondikePile::Foundation(Foundation::Foundation4)),
|
||||
];
|
||||
let tableaus = [
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau1)),
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau2)),
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau3)),
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau4)),
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau5)),
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau6)),
|
||||
game.pile(KlondikePile::Tableau(Tableau::Tableau7)),
|
||||
];
|
||||
let foundations =
|
||||
solitaire_core::FOUNDATIONS.map(|f| game.pile(KlondikePile::Foundation(f)));
|
||||
let tableaus = solitaire_core::TABLEAUS.map(|t| game.pile(KlondikePile::Tableau(t)));
|
||||
|
||||
let mut seen: std::collections::HashSet<Card> = std::collections::HashSet::new();
|
||||
let mut duplicate_cards = Vec::new();
|
||||
@@ -488,21 +465,9 @@ impl SolitaireGame {
|
||||
.iter()
|
||||
.map(CardSnapshot::from)
|
||||
.collect(),
|
||||
foundations: [
|
||||
cards(KlondikePile::Foundation(Foundation::Foundation1)),
|
||||
cards(KlondikePile::Foundation(Foundation::Foundation2)),
|
||||
cards(KlondikePile::Foundation(Foundation::Foundation3)),
|
||||
cards(KlondikePile::Foundation(Foundation::Foundation4)),
|
||||
],
|
||||
tableaus: [
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau1)),
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau2)),
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau3)),
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau4)),
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau5)),
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau6)),
|
||||
cards(KlondikePile::Tableau(Tableau::Tableau7)),
|
||||
],
|
||||
foundations: solitaire_core::FOUNDATIONS
|
||||
.map(|f| cards(KlondikePile::Foundation(f))),
|
||||
tableaus: solitaire_core::TABLEAUS.map(|t| cards(KlondikePile::Tableau(t))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,34 +478,21 @@ impl SolitaireGame {
|
||||
let slot: u8 = s["foundation-".len()..]
|
||||
.parse()
|
||||
.map_err(|_| format!("bad pile: {s}"))?;
|
||||
if slot >= 4 {
|
||||
return Err(format!("foundation slot out of range: {slot}"));
|
||||
}
|
||||
Ok(KlondikePile::Foundation(match slot {
|
||||
0 => Foundation::Foundation1,
|
||||
1 => Foundation::Foundation2,
|
||||
2 => Foundation::Foundation3,
|
||||
3 => Foundation::Foundation4,
|
||||
_ => return Err(format!("foundation slot out of range: {slot}")),
|
||||
}))
|
||||
let foundation = solitaire_core::FOUNDATIONS
|
||||
.get(slot as usize)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("foundation slot out of range: {slot}"))?;
|
||||
Ok(KlondikePile::Foundation(foundation))
|
||||
}
|
||||
_ if s.starts_with("tableau-") => {
|
||||
let col: usize = s["tableau-".len()..]
|
||||
.parse()
|
||||
.map_err(|_| format!("bad pile: {s}"))?;
|
||||
if col >= 7 {
|
||||
return Err(format!("tableau col out of range: {col}"));
|
||||
}
|
||||
Ok(KlondikePile::Tableau(match col {
|
||||
0 => Tableau::Tableau1,
|
||||
1 => Tableau::Tableau2,
|
||||
2 => Tableau::Tableau3,
|
||||
3 => Tableau::Tableau4,
|
||||
4 => Tableau::Tableau5,
|
||||
5 => Tableau::Tableau6,
|
||||
6 => Tableau::Tableau7,
|
||||
_ => return Err(format!("tableau col out of range: {col}")),
|
||||
}))
|
||||
let tableau = solitaire_core::TABLEAUS
|
||||
.get(col)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("tableau col out of range: {col}"))?;
|
||||
Ok(KlondikePile::Tableau(tableau))
|
||||
}
|
||||
_ => Err(format!("unknown pile: {s}")),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user