refactor(core): make KlondikeInstruction the move currency
Remove the (from, to, count) tuple as an internal move-passing wrapper. Game logic now stays in KlondikeInstruction space end to end: - Add GameState::apply_instruction, the native apply path. move_cards becomes a thin pile-coordinate adapter that converts to an instruction and delegates, so move bookkeeping (validation, score/recycle history, undo snapshot) lives in one place instead of being duplicated. - next_auto_complete_move matches DstFoundation directly instead of projecting every candidate to pile coordinates. - proptests and the storage round-trip test apply instructions directly rather than round-tripping instruction -> tuple -> move_cards. The single instruction -> pile decode is renamed instruction_to_highlight -> instruction_to_piles and kept in core: decoding a tableau run length needs upstream pile-stack types core does not re-export, so relocating it would duplicate the logic across engine and wasm. The two rendering edges (engine hint highlight, wasm debug move list) call this one decoder; the engine's hint_piles is a thin delegation to it. Also includes the CardEntityIndex render-side index and a SelectionPlugin init_resource fix so update_selection_highlight no longer panics in test harnesses that omit CardPlugin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -506,7 +506,7 @@ mod tests {
|
||||
/// will cause at least one pile to disagree.
|
||||
#[test]
|
||||
fn game_state_v4_mid_game_round_trip() {
|
||||
use solitaire_core::KlondikePile;
|
||||
use solitaire_core::KlondikeInstruction;
|
||||
use solitaire_core::game_state::GameState;
|
||||
|
||||
let path = gs_path("v4_mid_game");
|
||||
@@ -524,11 +524,13 @@ mod tests {
|
||||
|
||||
// Execute the first available DstTableau or DstFoundation move so the
|
||||
// instruction history contains a type other than RotateStock.
|
||||
let moves = gs.possible_instructions();
|
||||
if let Some((from, to, count)) = moves.iter().copied().find(|(_, to, _)| {
|
||||
matches!(to, KlondikePile::Tableau(_) | KlondikePile::Foundation(_))
|
||||
if let Some(instruction) = gs.possible_instructions().into_iter().find(|i| {
|
||||
matches!(
|
||||
i,
|
||||
KlondikeInstruction::DstTableau(_) | KlondikeInstruction::DstFoundation(_)
|
||||
)
|
||||
}) {
|
||||
let _ = gs.move_cards(from, to, count);
|
||||
let _ = gs.apply_instruction(instruction);
|
||||
}
|
||||
|
||||
// Undo once: verifies that `undo_count` is persisted and that the
|
||||
|
||||
Reference in New Issue
Block a user