refactor: migrate PileType → KlondikePile across core/wasm/engine
Build and Deploy / build-and-push (push) Failing after 1m24s
Build and Deploy / build-and-push (push) Failing after 1m24s
- Replace PileType with typed KlondikePile (Foundation/Tableau variants) throughout solitaire_core, solitaire_wasm, and solitaire_engine; ReplayMove now uses SavedKlondikePile for serialisation stability - Split replay_overlay.rs into replay_overlay/ module (mod, format, input, update, tests) for maintainability - Add klondike dep to solitaire_engine and solitaire_data Cargo.toml - Add TestPileState infrastructure to game_state.rs for engine unit tests - Rebuild solitaire_wasm pkg (js + wasm artefacts updated) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -151,9 +151,9 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::game_plugin::GamePlugin;
|
||||
use crate::table_plugin::TablePlugin;
|
||||
use solitaire_core::card::{Card, Rank, Suit};
|
||||
use klondike::{Foundation, KlondikePile, Tableau};
|
||||
use solitaire_core::card::{Rank, Suit};
|
||||
use solitaire_core::game_state::{DrawMode, GameState};
|
||||
use solitaire_core::pile::PileType;
|
||||
|
||||
fn headless_app() -> App {
|
||||
let mut app = App::new();
|
||||
@@ -166,31 +166,45 @@ mod tests {
|
||||
app
|
||||
}
|
||||
|
||||
/// Build a nearly-won game: one Ace of Clubs in Tableau(0), all other
|
||||
/// tableau piles empty, stock/waste empty, Clubs foundation empty.
|
||||
fn nearly_won_state() -> GameState {
|
||||
let mut g = GameState::new(42, DrawMode::DrawOne);
|
||||
g.piles.get_mut(&PileType::Stock).unwrap().cards.clear();
|
||||
g.piles.get_mut(&PileType::Waste).unwrap().cards.clear();
|
||||
for i in 0..7 {
|
||||
g.piles
|
||||
.get_mut(&PileType::Tableau(i))
|
||||
.unwrap()
|
||||
.cards
|
||||
.clear();
|
||||
fn seeded_state_with_auto_move() -> (GameState, (KlondikePile, KlondikePile)) {
|
||||
let mut g = GameState::new(1, DrawMode::DrawOne);
|
||||
g.set_test_stock_cards(Vec::new());
|
||||
g.set_test_waste_cards(Vec::new());
|
||||
for foundation in [
|
||||
Foundation::Foundation1,
|
||||
Foundation::Foundation2,
|
||||
Foundation::Foundation3,
|
||||
Foundation::Foundation4,
|
||||
] {
|
||||
g.set_test_foundation_cards(foundation, Vec::new());
|
||||
}
|
||||
g.piles
|
||||
.get_mut(&PileType::Tableau(0))
|
||||
.unwrap()
|
||||
.cards
|
||||
.push(Card {
|
||||
id: 99,
|
||||
for tableau in [
|
||||
Tableau::Tableau1,
|
||||
Tableau::Tableau2,
|
||||
Tableau::Tableau3,
|
||||
Tableau::Tableau4,
|
||||
Tableau::Tableau5,
|
||||
Tableau::Tableau6,
|
||||
Tableau::Tableau7,
|
||||
] {
|
||||
g.set_test_tableau_cards(tableau, Vec::new());
|
||||
}
|
||||
g.set_test_tableau_cards(
|
||||
Tableau::Tableau1,
|
||||
vec![solitaire_core::card::Card {
|
||||
id: 7_001,
|
||||
suit: Suit::Clubs,
|
||||
rank: Rank::Ace,
|
||||
face_up: true,
|
||||
});
|
||||
}],
|
||||
);
|
||||
g.is_auto_completable = true;
|
||||
g
|
||||
let expected = (
|
||||
KlondikePile::Tableau(Tableau::Tableau1),
|
||||
KlondikePile::Foundation(Foundation::Foundation1),
|
||||
);
|
||||
assert_eq!(g.next_auto_complete_move(), Some(expected));
|
||||
(g, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -202,8 +216,9 @@ mod tests {
|
||||
#[test]
|
||||
fn detect_activates_when_auto_completable() {
|
||||
let mut app = headless_app();
|
||||
// Install a nearly-won state and fire StateChangedEvent.
|
||||
app.world_mut().resource_mut::<GameStateResource>().0 = nearly_won_state();
|
||||
let mut g = GameState::new(42, DrawMode::DrawOne);
|
||||
g.is_auto_completable = true;
|
||||
app.world_mut().resource_mut::<GameStateResource>().0 = g;
|
||||
app.world_mut().write_message(StateChangedEvent);
|
||||
app.update();
|
||||
|
||||
@@ -213,7 +228,8 @@ mod tests {
|
||||
#[test]
|
||||
fn drive_fires_move_request_when_active() {
|
||||
let mut app = headless_app();
|
||||
app.world_mut().resource_mut::<GameStateResource>().0 = nearly_won_state();
|
||||
let (g, (expected_from, expected_to)) = seeded_state_with_auto_move();
|
||||
app.world_mut().resource_mut::<GameStateResource>().0 = g;
|
||||
app.world_mut().write_message(StateChangedEvent);
|
||||
app.update(); // detect runs, sets active
|
||||
|
||||
@@ -229,16 +245,15 @@ mod tests {
|
||||
let fired: Vec<_> = cursor.read(events).collect();
|
||||
// At least one MoveRequestEvent should have been fired.
|
||||
assert!(!fired.is_empty(), "expected at least one MoveRequestEvent");
|
||||
assert_eq!(fired[0].from, PileType::Tableau(0));
|
||||
// First empty foundation slot wins on a fresh nearly-won board.
|
||||
assert_eq!(fired[0].to, PileType::Foundation(0));
|
||||
assert_eq!(fired[0].from, expected_from);
|
||||
assert_eq!(fired[0].to, expected_to);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drive_deactivates_on_win() {
|
||||
let mut app = headless_app();
|
||||
// Inject a won game state — active should not be set.
|
||||
let mut gs = nearly_won_state();
|
||||
let (mut gs, _) = seeded_state_with_auto_move();
|
||||
gs.is_won = true;
|
||||
app.world_mut().resource_mut::<GameStateResource>().0 = gs;
|
||||
app.world_mut().write_message(StateChangedEvent);
|
||||
|
||||
Reference in New Issue
Block a user