refactor: replace local DrawMode with upstream klondike::DrawStockConfig (#82)

DrawMode was a 1:1 mirror of klondike::DrawStockConfig (DrawOne/DrawThree).
Delete it and use the upstream type everywhere; re-export DrawStockConfig from
solitaire_core. config_for assigns draw_stock directly and draw_mode() returns
session.config().inner.draw_stock.

Serde is unchanged — DrawStockConfig serialises to the same "DrawOne"/"DrawThree"
named variants, so persisted game_state.json / replay JSON stay byte-compatible
(no migration). Field/method/variable names containing draw_mode are unchanged.

35 files, mechanical type swap across all crates. Implemented via a multi-agent
workflow (core → per-crate consumers → verify). cargo test --workspace and
clippy --workspace --all-targets -- -D warnings green.

Closes #82

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-11 16:01:11 -07:00
parent d045781119
commit 5c992cbdca
35 changed files with 257 additions and 274 deletions
+24 -24
View File
@@ -18,7 +18,7 @@ use bevy::sprite::Anchor;
use bevy::window::WindowResized;
use solitaire_core::{Foundation, KlondikePile, Tableau};
use solitaire_core::card::{Card, Rank, Suit};
use solitaire_core::{DrawMode, game_state::GameState};
use solitaire_core::{DrawStockConfig, game_state::GameState};
use crate::animation_plugin::{CARD_ANIM_Z_LIFT, CardAnim, EffectiveSlideDuration};
use crate::card_animation::CardAnimation;
@@ -789,8 +789,8 @@ fn sync_cards(
// and its rank/suit peek behind the incoming card.
let waste_buffer_id: Option<Card> = {
let visible = match game.draw_mode() {
DrawMode::DrawOne => 1_usize,
DrawMode::DrawThree => 3_usize,
DrawStockConfig::DrawOne => 1_usize,
DrawStockConfig::DrawThree => 3_usize,
};
let waste_cards = game.waste_cards();
(waste_cards.len() > visible)
@@ -958,8 +958,8 @@ fn card_positions(game: &GameState, layout: &Layout) -> Vec<((Card, bool), Vec2,
// shows up to 3 fanned in X (matching the standard Klondike presentation).
let render_start = if is_waste {
let visible = match game.draw_mode() {
DrawMode::DrawOne => 1_usize,
DrawMode::DrawThree => 3_usize,
DrawStockConfig::DrawOne => 1_usize,
DrawStockConfig::DrawThree => 3_usize,
};
// Render one extra card so that the card sliding off the waste
// during a draw animation is still present in the world at z=0
@@ -972,7 +972,7 @@ fn card_positions(game: &GameState, layout: &Layout) -> Vec<((Card, bool), Vec2,
let mut y_offset = 0.0_f32;
let rendered_len = cards[render_start..].len();
for (slot, (card, face_up)) in cards[render_start..].iter().enumerate() {
let x_offset = if is_waste && matches!(game.draw_mode(), DrawMode::DrawThree) {
let x_offset = if is_waste && matches!(game.draw_mode(), DrawStockConfig::DrawThree) {
// When len > visible, slot 0 is a hidden buffer card kept at
// x=0 to prevent a flash during the draw tween. When len ≤
// visible (small pile), every card is visible and should fan
@@ -2566,7 +2566,7 @@ mod tests {
#[test]
fn card_positions_includes_all_52_cards_at_game_start() {
// At game start waste is empty, so all 52 cards are across stock + tableau.
let g = GameState::new(42, solitaire_core::DrawMode::DrawOne);
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
assert_eq!(positions.len(), 52);
@@ -2574,8 +2574,8 @@ mod tests {
#[test]
fn waste_draw_one_only_renders_top_card() {
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawOne);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawOne);
// Draw 3 cards so the waste pile has 3 cards.
for _ in 0..3 {
let _ = g.draw();
@@ -2612,8 +2612,8 @@ mod tests {
#[test]
fn waste_draw_three_renders_up_to_three_fanned_cards() {
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawThree);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawThree);
// 5 draw() calls in Draw-Three mode accumulates multiple waste cards.
for _ in 0..5 {
let _ = g.draw();
@@ -2666,8 +2666,8 @@ mod tests {
// Regression: slot.saturating_sub(1) always hid slot-0 even when the
// pile was too small to have a buffer card, collapsing 2 visible cards
// onto x=0 instead of fanning them.
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawThree);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawThree);
// Draw exactly once — in Draw-Three mode with a full stock this gives
// 3 waste cards (still ≤ visible=3, so no hidden buffer needed).
let _ = g.draw();
@@ -2709,8 +2709,8 @@ mod tests {
/// top card so that hiding it (`Visibility::Hidden`) leaves no visible gap.
#[test]
fn waste_draw_one_buffer_card_at_same_xy_as_top() {
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawOne);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawOne);
// Draw 3 times so the waste pile has 3 cards and the buffer exists.
for _ in 0..3 {
let _ = g.draw();
@@ -2740,7 +2740,7 @@ mod tests {
#[test]
fn card_positions_tableau_cards_are_fanned_downward() {
let g = GameState::new(42, solitaire_core::DrawMode::DrawOne);
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
@@ -3083,7 +3083,7 @@ mod tests {
#[test]
fn facedown_cards_use_tighter_fan_than_uniform_faceup_fan() {
let g = GameState::new(42, solitaire_core::DrawMode::DrawOne);
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
@@ -3533,7 +3533,7 @@ mod tests {
#[test]
fn stock_card_count_helper_reads_zero_for_empty_stock() {
let g = GameState::new(42, solitaire_core::DrawMode::DrawOne);
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let mut g_empty_stock = g.clone();
g_empty_stock.set_test_stock_cards(Vec::new());
assert_eq!(stock_card_count(&g_empty_stock), 0);
@@ -3805,8 +3805,8 @@ mod tests {
#[test]
fn waste_pile_cards_have_strictly_increasing_z() {
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawThree);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawThree);
for _ in 0..5 {
let _ = g.draw();
}
@@ -3849,8 +3849,8 @@ mod tests {
/// offsets or flips the fan direction is caught immediately.
#[test]
fn waste_cards_do_not_overlap_stock_column_on_portrait() {
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawThree);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawThree);
for _ in 0..5 {
let _ = g.draw();
}
@@ -3885,8 +3885,8 @@ mod tests {
#[test]
fn waste_pile_draw_one_cards_have_distinct_z() {
use solitaire_core::DrawMode;
let mut g = GameState::new(42, DrawMode::DrawOne);
use solitaire_core::DrawStockConfig;
let mut g = GameState::new(42, DrawStockConfig::DrawOne);
for _ in 0..3 {
let _ = g.draw();
}