3e11e9e79a
Closes the last solver-on-main-thread hot path. The synchronous
v0.17.0 hint flow called solitaire_core::solver::try_solve_from_state
inline on every H press; median latency was ~2 ms but pathological
positions hit the SolverConfig::default() cap at ~120 ms — a visible
input stall on the same frame the player presses H.
Mirrors the d489e7a PendingNewGameSeed pattern. New module
pending_hint.rs holds:
- PendingHintTask resource carrying an Option<HintTask> with
handle: Task<HintTaskOutput> plus move_count_at_spawn for
staleness detection.
- HintTaskOutput enum: SolverMove { from, to } when the verdict
is Winnable + a first_move; NeedsHeuristic when the solver
returns Unwinnable or Inconclusive.
- poll_pending_hint_task system: polls the task each frame and
surfaces the result via the now-public emit_hint_visuals (or
runs find_heuristic_hint on the live state for the
NeedsHeuristic branch). Discards the result when
GameState.move_count has advanced past move_count_at_spawn.
- drop_pending_hint_on_state_change system: any
StateChangedEvent drops the in-flight task. Cooperatively
cancels via Bevy's Task Drop at the next await point.
- PendingHintTask::spawn implements cancel-on-replace — a fresh
H press while a previous task is in flight overwrites the
handle, dropping the prior task.
input_plugin changes:
- handle_keyboard_hint becomes a thin spawn point. Snapshots
the live state, asks the solver via PendingHintTask::spawn,
returns. No card-entity query, no event writers for the
hint visual / toast — the polling system owns those.
- emit_hint_visuals promoted to pub so pending_hint can call it.
- find_heuristic_hint extracted as a pub helper for the
NeedsHeuristic poll path.
- InputPlugin registers PendingHintTask + the two new systems.
drop-on-state-change is chained .before() poll so a move
applied this frame cancels any in-flight task before its
result can be surfaced.
Tests:
- input_plugin: pressing_h_spawns_pending_hint_task (1) — pins
the H-key wiring at one-frame granularity.
- pending_hint: winnable_solver_emits_hint_after_async_completes,
state_change_drops_in_flight_task,
second_spawn_drops_first_in_flight_task (3) — drives the
AsyncComputeTaskPool with a wall-clock-bounded loop mirroring
the winnable_seed_search_* template.
- Removed two now-stale synchronous tests
(hint_uses_solver_when_winnable,
hint_falls_back_to_heuristic_when_solver_inconclusive) — the
behaviours they pinned now live in pending_hint::tests at the
correct layer.
Workspace: 1168 passing tests / 0 failing, was 1166 (net +2:
removed 2 stale, added 4 new). cargo clippy --workspace
--all-targets -- -D warnings clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
158 lines
6.4 KiB
Rust
158 lines
6.4 KiB
Rust
//! Bevy integration layer for Solitaire Quest.
|
|
|
|
pub mod assets;
|
|
pub mod card_animation;
|
|
pub mod achievement_plugin;
|
|
pub mod animation_plugin;
|
|
pub mod auto_complete_plugin;
|
|
pub mod audio_plugin;
|
|
pub mod card_plugin;
|
|
pub mod font_plugin;
|
|
pub mod feedback_anim_plugin;
|
|
pub mod challenge_plugin;
|
|
pub mod cursor_plugin;
|
|
pub mod daily_challenge_plugin;
|
|
pub mod events;
|
|
pub mod game_plugin;
|
|
pub mod help_plugin;
|
|
pub mod home_plugin;
|
|
pub mod hud_plugin;
|
|
pub mod leaderboard_plugin;
|
|
pub mod input_plugin;
|
|
pub mod layout;
|
|
pub mod onboarding_plugin;
|
|
pub mod pause_plugin;
|
|
pub mod pending_hint;
|
|
pub mod profile_plugin;
|
|
pub mod radial_menu;
|
|
pub mod replay_overlay;
|
|
pub mod replay_playback;
|
|
pub mod settings_plugin;
|
|
pub mod progress_plugin;
|
|
pub mod resources;
|
|
pub mod selection_plugin;
|
|
pub mod splash_plugin;
|
|
pub mod stats_plugin;
|
|
pub mod sync_plugin;
|
|
pub mod table_plugin;
|
|
pub mod theme;
|
|
pub mod time_attack_plugin;
|
|
pub mod ui_focus;
|
|
pub mod ui_modal;
|
|
pub mod ui_theme;
|
|
pub mod ui_tooltip;
|
|
pub mod weekly_goals_plugin;
|
|
pub mod win_summary_plugin;
|
|
|
|
pub use assets::{
|
|
populate_embedded_default_theme, register_theme_asset_sources, AssetSourcesPlugin,
|
|
DEFAULT_THEME_MANIFEST_URL, USER_THEMES,
|
|
};
|
|
pub use theme::{
|
|
set_theme, ActiveTheme, CardTheme, CardThemeLoader, ThemeEntry, ThemePlugin, ThemeRegistry,
|
|
ThemeRegistryPlugin,
|
|
};
|
|
pub use achievement_plugin::{AchievementPlugin, AchievementsResource, AchievementsScreen};
|
|
pub use challenge_plugin::{
|
|
challenge_progress_label, ChallengeAdvancedEvent, ChallengePlugin, CHALLENGE_UNLOCK_LEVEL,
|
|
};
|
|
pub use daily_challenge_plugin::{
|
|
DailyChallengeCompletedEvent, DailyChallengePlugin, DailyChallengeResource,
|
|
};
|
|
pub use progress_plugin::{LevelUpEvent, ProgressPlugin, ProgressResource, ProgressUpdate};
|
|
pub use weekly_goals_plugin::{WeeklyGoalCompletedEvent, WeeklyGoalsPlugin};
|
|
pub use animation_plugin::{ActiveToast, AnimationPlugin, CardAnim, ToastEntity, ToastQueue};
|
|
pub use card_animation::{
|
|
CardAnimation, CardAnimationPlugin, MotionCurve, WinCascadePlugin,
|
|
retarget_animation, sample_curve, compute_duration, cascade_delay, micro_vary,
|
|
HoverState, InputBuffer, BufferedInput,
|
|
win_scatter_targets, WIN_CASCADE_INTERVAL_SECS, DEAL_INTERVAL_SECS,
|
|
MIN_DURATION_SECS, MAX_DURATION_SECS,
|
|
AnimationChain,
|
|
AnimationTuning, InputPlatform,
|
|
FrameTimeDiagnostics, DIAG_WINDOW_SIZE,
|
|
};
|
|
pub use feedback_anim_plugin::{
|
|
deal_stagger_delay, deal_stagger_secs_for_speed, foundation_flourish_scale, shake_offset,
|
|
settle_scale, FeedbackAnimPlugin, FoundationFlourish, FoundationMarkerFlourish, SettleAnim,
|
|
ShakeAnim,
|
|
};
|
|
pub use auto_complete_plugin::AutoCompletePlugin;
|
|
pub use audio_plugin::{AudioPlugin, AudioState, SoundLibrary};
|
|
pub use card_plugin::{
|
|
CardEntity, CardImageSet, CardLabel, CardPlugin, HintHighlight, HintHighlightTimer,
|
|
RightClickHighlight, RightClickHighlightTimer,
|
|
};
|
|
pub use font_plugin::{FontPlugin, FontResource};
|
|
pub use cursor_plugin::CursorPlugin;
|
|
pub use events::{
|
|
AchievementUnlockedEvent, CardFaceRevealedEvent, CardFlippedEvent, DrawRequestEvent,
|
|
ForfeitEvent, ForfeitRequestEvent, FoundationCompletedEvent, GameWonEvent, HelpRequestEvent,
|
|
HintVisualEvent, InfoToastEvent, ManualSyncRequestEvent, MoveRejectedEvent, MoveRequestEvent,
|
|
NewGameRequestEvent, PauseRequestEvent, StartChallengeRequestEvent,
|
|
StartDailyChallengeRequestEvent, StartTimeAttackRequestEvent, StartZenRequestEvent,
|
|
StateChangedEvent, SyncCompleteEvent, ToggleAchievementsRequestEvent,
|
|
ToggleLeaderboardRequestEvent, ToggleProfileRequestEvent, ToggleSettingsRequestEvent,
|
|
ToggleStatsRequestEvent, UndoRequestEvent, WinStreakMilestoneEvent, XpAwardedEvent,
|
|
};
|
|
pub use game_plugin::{
|
|
ConfirmNewGameScreen, GameMutation, GameOverScreen, GamePlugin, GameStatePath, RecordingReplay,
|
|
ReplayPath,
|
|
};
|
|
pub use help_plugin::{HelpPlugin, HelpScreen};
|
|
pub use home_plugin::{HomePlugin, HomeScreen};
|
|
pub use hud_plugin::{
|
|
streak_flourish_scale, ActionButton, HelpButton, HudAutoComplete, HudPlugin, MenuButton,
|
|
MenuOption, MenuPopover, ModeOption, ModesButton, ModesPopover, NewGameButton, PauseButton,
|
|
StreakFlourish, UndoButton,
|
|
};
|
|
pub use leaderboard_plugin::{LeaderboardPlugin, LeaderboardResource, LeaderboardScreen};
|
|
pub use input_plugin::InputPlugin;
|
|
pub use onboarding_plugin::{OnboardingPlugin, OnboardingScreen};
|
|
pub use pause_plugin::{ForfeitConfirmScreen, PausePlugin, PauseScreen, PausedResource};
|
|
pub use profile_plugin::{ProfilePlugin, ProfileScreen};
|
|
pub use radial_menu::{
|
|
legal_destinations_for_card, radial_anchor_for_index, radial_hovered_index, RadialIcon,
|
|
RadialMenuPlugin, RightClickRadialState, Z_RADIAL_MENU,
|
|
};
|
|
pub use replay_overlay::{
|
|
ReplayOverlayBannerText, ReplayOverlayPlugin, ReplayOverlayProgressText, ReplayOverlayRoot,
|
|
ReplayStopButton, Z_REPLAY_OVERLAY,
|
|
};
|
|
pub use replay_playback::{
|
|
start_replay_playback, stop_replay_playback, ReplayPlaybackPlugin, ReplayPlaybackState,
|
|
REPLAY_COMPLETION_LINGER_SECS, REPLAY_MOVE_INTERVAL_SECS,
|
|
};
|
|
pub use settings_plugin::{
|
|
PendingWindowGeometry, SettingsChangedEvent, SettingsPlugin, SettingsResource, SettingsScreen,
|
|
SFX_STEP, WINDOW_GEOMETRY_DEBOUNCE_SECS,
|
|
};
|
|
pub use layout::{compute_layout, Layout, LayoutResource};
|
|
pub use resources::{DragState, GameStateResource, HintCycleIndex, SettingsScrollPos, SyncStatus, SyncStatusResource};
|
|
pub use selection_plugin::{
|
|
KeyboardDragState, SelectionHighlight, SelectionPlugin, SelectionState,
|
|
};
|
|
pub use splash_plugin::{SplashAge, SplashPlugin, SplashRoot};
|
|
pub use stats_plugin::{
|
|
format_replay_caption, LatestReplayPath, ReplayHistoryResource, ReplayNextButton,
|
|
ReplayPrevButton, ReplaySelectorCaption, SelectedReplayIndex, StatsPlugin, StatsResource,
|
|
StatsScreen, StatsUpdate, WatchReplayButton,
|
|
};
|
|
pub use sync_plugin::{SyncPlugin, SyncProviderResource};
|
|
pub use ui_focus::{Disabled, FocusGroup, Focusable, FocusedButton, UiFocusPlugin};
|
|
pub use ui_modal::{
|
|
spawn_modal, spawn_modal_actions, spawn_modal_body_text, spawn_modal_button,
|
|
spawn_modal_header, ButtonVariant, ModalActions, ModalBody, ModalButton, ModalCard,
|
|
ModalHeader, ModalScrim, UiModalPlugin,
|
|
};
|
|
pub use ui_tooltip::{Tooltip, UiTooltipPlugin};
|
|
pub use table_plugin::{
|
|
BackgroundImageSet, HintPileHighlight, PileMarker, TableBackground, TablePlugin,
|
|
};
|
|
pub use time_attack_plugin::{
|
|
TimeAttackEndedEvent, TimeAttackPlugin, TimeAttackResource, TIME_ATTACK_DURATION_SECS,
|
|
};
|
|
pub use win_summary_plugin::{
|
|
format_win_time, ScreenShakeResource, SessionAchievements, WinSummaryPending, WinSummaryPlugin,
|
|
};
|