feat(engine): Android polish sweep + hint button + watch replay

Draw-Three waste fan: slot.saturating_sub(1) was a constant shift that
hid slot-0 even when the pile had fewer cards than visible. Fixed to
slot.saturating_sub(rendered_len.saturating_sub(visible)) so small piles
fan correctly and only a genuine buffer card gets hidden. New regression
test covers the small-pile case.

Android toast: game-over "press D / N" message now shows touch-friendly
copy ("Tap the stock...") on Android via cfg gate.

Onboarding: SLIDE_COUNT drops from 3 to 2 on Android so first-time
users skip the keyboard-shortcuts slide (irrelevant on touchscreen).
spawn_slide dispatch is gated identically.

Hint button: added HintButton to the HUD action bar (order 4, between
Help and Modes). Clicking it triggers the async solver hint — same path
as the H key — via optional resources so headless tests stay clean.
All button-order and tooltip tests updated for the new 7-button bar.

Watch Replay: win-summary modal now shows a "Watch Replay" secondary
button alongside "Play Again". It loads the most recent entry from
ReplayHistoryResource and hands it to start_replay_playback, dismissing
the modal. Falls back to an info toast when the replay or playback
plugin is unavailable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-11 20:28:20 -07:00
parent 3110702c74
commit 2b01f741b4
5 changed files with 214 additions and 43 deletions
+16 -1
View File
@@ -41,7 +41,13 @@ use crate::ui_theme::{
// ---------------------------------------------------------------------------
/// Total number of onboarding slides (0-based index goes 0..SLIDE_COUNT-1).
///
/// Android omits the keyboard-shortcuts slide (index 2) because there is no
/// physical keyboard on a touchscreen device, dropping the count to 2.
#[cfg(not(target_os = "android"))]
const SLIDE_COUNT: u8 = 3;
#[cfg(target_os = "android")]
const SLIDE_COUNT: u8 = 2;
// ---------------------------------------------------------------------------
// Components (private — never re-exported)
@@ -276,6 +282,8 @@ fn spawn_slide(commands: &mut Commands, index: u8, font_res: Option<&FontResourc
match index {
0 => spawn_slide_welcome(commands, font_res),
1 => spawn_slide_how_to_play(commands, font_res),
// Slide 2 (keyboard shortcuts) is desktop-only; Android has no keyboard.
#[cfg(not(target_os = "android"))]
2 => spawn_slide_hotkeys(commands, font_res),
_ => spawn_slide_welcome(commands, font_res),
}
@@ -664,8 +672,15 @@ mod tests {
// -----------------------------------------------------------------------
#[test]
#[cfg(not(target_os = "android"))]
fn slide_count_constant_is_three() {
assert_eq!(SLIDE_COUNT, 3, "SLIDE_COUNT must be 3");
assert_eq!(SLIDE_COUNT, 3, "SLIDE_COUNT must be 3 on desktop");
}
#[test]
#[cfg(target_os = "android")]
fn slide_count_constant_is_two_on_android() {
assert_eq!(SLIDE_COUNT, 2, "SLIDE_COUNT must be 2 on Android (no keyboard slide)");
}
#[test]