Skip to main content

weiss_core/state/
game.rs

1use std::collections::VecDeque;
2
3use serde::{Deserialize, Serialize};
4
5use crate::db::CardId;
6use crate::effects::ReplacementSpec;
7use crate::error::StateError;
8use crate::util::Rng64;
9
10use super::{
11    AttackContext, CardInstance, CardInstanceId, ChoiceState, CostPaymentState, DerivedAttackState,
12    EncoreRequest, GrantedAbilityInstance, ModifierInstance, PendingTrigger, Phase, PlayerState,
13    PriorityState, RevealHistory, StackItem, StackOrderState, TargetSelectionState, TimingWindow,
14    TriggerOrderState,
15};
16
17/// Terminal outcome for an episode.
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum TerminalResult {
20    /// A player won the episode.
21    Win {
22        /// Winning player seat (0 or 1).
23        winner: u8,
24    },
25    /// Both players reached a draw condition.
26    Draw,
27    /// Episode ended due to reaching a tick/decision limit.
28    Timeout,
29}
30
31/// Turn-level state tracking.
32#[derive(Clone, Debug, Hash)]
33pub struct TurnState {
34    /// Seat whose turn it currently is.
35    pub active_player: u8,
36    /// Seat that started the game.
37    pub starting_player: u8,
38    /// Turn counter (starting at 0 for mulligan).
39    pub turn_number: u32,
40    /// Current phase within the turn.
41    pub phase: Phase,
42    /// Per-seat mulligan completion flags.
43    pub mulligan_done: [bool; 2],
44    /// Per-seat packed mulligan selections.
45    pub mulligan_selected: [u64; 2],
46    /// Whether the main phase has been passed.
47    pub main_passed: bool,
48    /// Whether a main-phase move has already been used this turn.
49    pub main_move_used: bool,
50    /// Decision counter for the episode.
51    pub decision_count: u32,
52    /// Tick counter for the episode.
53    pub tick_count: u32,
54    /// Active attack context, if any.
55    pub attack: Option<AttackContext>,
56    /// Counter used to advance attack subphases deterministically.
57    pub attack_subphase_count: u8,
58    /// Seat that must level up next, if any.
59    pub pending_level_up: Option<u8>,
60    /// Queue of encore requests to resolve.
61    pub encore_queue: Vec<EncoreRequest>,
62    /// Seat currently choosing encore, if any.
63    pub encore_step_player: Option<u8>,
64    /// Pending triggers awaiting ordering/resolution.
65    pub pending_triggers: Vec<PendingTrigger>,
66    /// Whether `pending_triggers` is already sorted for resolution.
67    pub pending_triggers_sorted: bool,
68    /// Current timing window for priority/auto effects.
69    pub active_window: Option<TimingWindow>,
70    /// Whether end-phase window has been processed.
71    pub end_phase_window_done: bool,
72    /// Whether end-phase discard has been processed.
73    pub end_phase_discard_done: bool,
74    /// Whether end-phase climax cleanup has been processed.
75    pub end_phase_climax_done: bool,
76    /// Whether end-phase general cleanup has been processed.
77    pub end_phase_cleanup_done: bool,
78    /// Whether encore window has been processed.
79    pub encore_window_done: bool,
80    /// Per-seat pending loss flags.
81    pub pending_losses: [bool; 2],
82    /// Seat currently taking damage, if any.
83    pub damage_resolution_target: Option<u8>,
84    /// Nested cost payment depth (re-entrancy guard).
85    pub cost_payment_depth: u8,
86    /// Pending cleanup operations for the resolution zone.
87    pub pending_resolution_cleanup: Vec<(u8, CardInstanceId)>,
88    /// Per-seat flag to disable auto-encore.
89    pub cannot_use_auto_encore: [bool; 2],
90    /// Active rule overrides for this turn.
91    pub rule_overrides: Vec<crate::effects::RuleOverrideKind>,
92    /// Runtime granted abilities active this turn.
93    pub granted_abilities: Vec<GrantedAbilityInstance>,
94    /// Next grant id allocator.
95    pub next_grant_id: u64,
96    /// Internal step counter within the current phase.
97    pub phase_step: u8,
98    /// Whether attack-phase begin effects have run.
99    pub attack_phase_begin_done: bool,
100    /// Whether attack declaration legality checks have run.
101    pub attack_decl_check_done: bool,
102    /// Whether encore begin effects have run.
103    pub encore_begin_done: bool,
104    /// Active trigger ordering prompt, if any.
105    pub trigger_order: Option<TriggerOrderState>,
106    /// Active choice prompt, if any.
107    pub choice: Option<ChoiceState>,
108    /// Active target selection prompt, if any.
109    pub target_selection: Option<TargetSelectionState>,
110    /// Active cost payment state, if any.
111    pub pending_cost: Option<CostPaymentState>,
112    /// Active priority window, if any.
113    pub priority: Option<PriorityState>,
114    /// Effect stack items awaiting resolution.
115    pub stack: Vec<StackItem>,
116    /// Pending stack groups awaiting an ordering decision.
117    pub pending_stack_groups: VecDeque<StackOrderState>,
118    /// Active stack ordering prompt, if any.
119    pub stack_order: Option<StackOrderState>,
120    /// Cached derived attack state, if any.
121    pub derived_attack: Option<DerivedAttackState>,
122    /// Next trigger id allocator.
123    pub next_trigger_id: u32,
124    /// Next trigger group id allocator.
125    pub next_trigger_group_id: u32,
126    /// Next choice id allocator.
127    pub next_choice_id: u32,
128    /// Next stack group id allocator.
129    pub next_stack_group_id: u32,
130    /// Next damage event id allocator.
131    pub next_damage_event_id: u32,
132    /// Next effect instance id allocator.
133    pub next_effect_instance_id: u32,
134    /// Whether the end phase is currently pending execution.
135    pub end_phase_pending: bool,
136}
137
138/// Complete game state for an environment.
139#[derive(Clone, Debug, Hash)]
140pub struct GameState {
141    /// Per-seat player state.
142    pub players: [PlayerState; 2],
143    /// Per-seat reveal history.
144    pub reveal_history: [RevealHistory; 2],
145    /// Turn-level state.
146    pub turn: TurnState,
147    /// Deterministic RNG state.
148    pub rng: Rng64,
149    /// Active modifier instances.
150    pub modifiers: Vec<ModifierInstance>,
151    /// Next modifier id allocator.
152    pub next_modifier_id: u32,
153    /// Active replacement specs.
154    pub replacements: Vec<ReplacementSpec>,
155    /// Insertion order counter for replacements.
156    pub next_replacement_insertion: u32,
157    /// Terminal result for the episode, if any.
158    pub terminal: Option<TerminalResult>,
159}
160
161impl GameState {
162    /// Build a new game state with the given decks and seed.
163    pub fn new(
164        deck_a: Vec<CardId>,
165        deck_b: Vec<CardId>,
166        seed: u64,
167        starting_player: u8,
168    ) -> Result<Self, StateError> {
169        if starting_player > 1 {
170            return Err(StateError::InvalidStartingPlayer {
171                got: starting_player,
172            });
173        }
174        if deck_a.len() != crate::encode::MAX_DECK {
175            return Err(StateError::DeckLength {
176                owner: 0,
177                got: deck_a.len(),
178                expected: crate::encode::MAX_DECK,
179            });
180        }
181        if deck_b.len() != crate::encode::MAX_DECK {
182            return Err(StateError::DeckLength {
183                owner: 1,
184                got: deck_b.len(),
185                expected: crate::encode::MAX_DECK,
186            });
187        }
188        let rng = Rng64::new(seed);
189        let mut next_instance_id: CardInstanceId = 1;
190        let deck_a = Self::build_deck(deck_a, 0, &mut next_instance_id);
191        let deck_b = Self::build_deck(deck_b, 1, &mut next_instance_id);
192        Ok(Self {
193            players: [PlayerState::new(deck_a), PlayerState::new(deck_b)],
194            reveal_history: [RevealHistory::new(), RevealHistory::new()],
195            turn: TurnState {
196                active_player: starting_player,
197                starting_player,
198                turn_number: 0,
199                phase: Phase::Mulligan,
200                mulligan_done: [false; 2],
201                mulligan_selected: [0; 2],
202                main_passed: false,
203                main_move_used: false,
204                decision_count: 0,
205                tick_count: 0,
206                attack: None,
207                attack_subphase_count: 0,
208                pending_level_up: None,
209                encore_queue: Vec::new(),
210                encore_step_player: None,
211                pending_triggers: Vec::new(),
212                pending_triggers_sorted: true,
213                trigger_order: None,
214                choice: None,
215                target_selection: None,
216                pending_cost: None,
217                priority: None,
218                stack: Vec::new(),
219                pending_stack_groups: VecDeque::new(),
220                stack_order: None,
221                derived_attack: None,
222                next_trigger_id: 1,
223                next_trigger_group_id: 1,
224                next_choice_id: 1,
225                next_stack_group_id: 1,
226                next_damage_event_id: 1,
227                next_effect_instance_id: 1,
228                active_window: None,
229                end_phase_window_done: false,
230                end_phase_discard_done: false,
231                end_phase_climax_done: false,
232                end_phase_cleanup_done: false,
233                encore_window_done: false,
234                pending_losses: [false; 2],
235                damage_resolution_target: None,
236                cost_payment_depth: 0,
237                pending_resolution_cleanup: Vec::new(),
238                cannot_use_auto_encore: [false; 2],
239                rule_overrides: Vec::new(),
240                granted_abilities: Vec::new(),
241                next_grant_id: 1,
242                phase_step: 0,
243                attack_phase_begin_done: false,
244                attack_decl_check_done: false,
245                encore_begin_done: false,
246                end_phase_pending: false,
247            },
248            rng,
249            modifiers: Vec::new(),
250            next_modifier_id: 1,
251            replacements: Vec::new(),
252            next_replacement_insertion: 1,
253            terminal: None,
254        })
255    }
256
257    /// Compatibility helper for test/bench scaffolding.
258    pub fn new_or_panic(
259        deck_a: Vec<CardId>,
260        deck_b: Vec<CardId>,
261        seed: u64,
262        starting_player: u8,
263    ) -> Self {
264        Self::new(deck_a, deck_b, seed, starting_player).expect("GameState::new_or_panic failed")
265    }
266
267    fn build_deck(
268        deck: Vec<CardId>,
269        owner: u8,
270        next_instance_id: &mut CardInstanceId,
271    ) -> Vec<CardInstance> {
272        deck.into_iter()
273            .map(|id| {
274                let instance_id = *next_instance_id;
275                *next_instance_id = next_instance_id.wrapping_add(1);
276                CardInstance::new(id, owner, instance_id)
277            })
278            .collect()
279    }
280}