Skip to content

fix(engine): randomize Dig library remainders - #7319

Merged
matthewevans merged 3 commits into
mainfrom
ship/6367-randomize-dig-library-remainders
Aug 13, 2026
Merged

fix(engine): randomize Dig library remainders#7319
matthewevans merged 3 commits into
mainfrom
ship/6367-randomize-dig-library-remainders

Conversation

@matthewevans

@matthewevans matthewevans commented Aug 13, 2026

Copy link
Copy Markdown
Member

Closes #6367.

Preserves typed ordering semantics for dig-like effects and randomizes library remainders when Oracle text requires it.

Summary by CodeRabbit

  • New Features

    • Cards returned to the bottom of the library can now be placed in random order when specified by an effect.
    • Card-ordering instructions are preserved across searches, reveals, pauses, and replacement effects.
    • Parsing and imported card effects now recognize random-order instructions.
  • Bug Fixes

    • Corrected Thassa’s Oracle and similar effects so unchosen cards are randomized as intended while selected cards remain properly positioned.
    • Added coverage for deterministic randomization and preserved ordering.

@matthewevans
matthewevans enabled auto-merge August 13, 2026 00:45
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@matthewevans, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 2 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 46eb867e-0561-4165-813a-011bf09c335e

📥 Commits

Reviewing files that changed from the base of the PR and between 02750b6 and 918682f.

📒 Files selected for processing (1)
  • crates/engine/src/parser/oracle_effect/sequence.rs

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f33f8999-48fb-4194-95bb-9511de424e3d

📥 Commits

Reviewing files that changed from the base of the PR and between 81d6b35 and 02750b6.

⛔ Files ignored due to path filters (2)
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__follow_the_lumarets_ir.snap is excluded by !**/*.snap, !**/snapshots/**
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__follow_the_lumarets_lowered.snap is excluded by !**/*.snap, !**/snapshots/**
📒 Files selected for processing (3)
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/parser/oracle_effect/tests.rs
  • crates/engine/src/parser/oracle_ir/ast.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/engine/src/parser/oracle_ir/ast.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs

📝 Walkthrough

Walkthrough

The PR adds DigRestOrder to represent preserved or randomized rest-card ordering. Parser, importer, game-state, resolution, deferred routing, visibility, and test paths now propagate this setting. Library-bound random rest piles use the game RNG.

Changes

Dig rest-ordering

Layer / File(s) Summary
Ordering contracts and parser propagation
crates/engine/src/types/..., crates/engine/src/parser/..., crates/mtgish-import/src/convert/action.rs
Adds DigRestOrder, stores it in serialized state and AST data, and parses random-order clauses into Effect::Dig.
Dig resolution and deferred routing
crates/engine/src/game/effects/..., crates/engine/src/game/engine_resolution_choices.rs, crates/engine/src/game/visibility.rs
Propagates rest_order through immediate and deferred Dig flows. Library-bound rest cards are shuffled only in Random mode.
Fixtures and regression coverage
crates/engine/tests/integration/..., crates/phase-ai/..., crates/server-core/session.rs
Updates existing fixtures to use Preserve and adds Thassa’s Oracle tests for seeded random bottom placement and RNG consumption.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🔵 Low · up to 02750

The PR changes Dig remainder ordering and adds rules-sensitive branching. It is mergeable with owner awareness for the inconsistent or missing rules citations and duplicated shuffle-gating logic, which create bounded verification and maintenance risk but no demonstrated production failure.

Sequence Diagram(s)

sequenceDiagram
  participant OracleParser
  participant DigResolution
  participant GameRng
  participant Library
  OracleParser->>DigResolution: create Dig with rest_order
  DigResolution->>GameRng: shuffle rest cards when order is Random
  GameRng-->>DigResolution: shuffled rest cards
  DigResolution->>Library: place rest cards on the library
Loading

Suggested labels: bug

Suggested reviewers: jacobwoodson, keloide

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: randomizing library remainders for the Dig effect.
Linked Issues check ✅ Passed The changes implement random Dig remainder ordering and add Thassa’s Oracle regression tests for issue #6367.
Out of Scope Changes check ✅ Passed The changes consistently support Dig ordering propagation, parser handling, routing, and regression coverage for the linked issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ship/6367-randomize-dig-library-remainders

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@matthewevans
matthewevans added this pull request to the merge queue Aug 13, 2026
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

Generated for head 918682ff2aabce7443c55934ecea66304b403682.

Parse changes introduced by this PR · 3 card(s), 7 signature(s) (baseline: main 552cf8fa5deb)

🟢 Added (1 signature)

  • 1 card · ➕ ability/WinTheGame · added: WinTheGame (conditional=devotion to Blue ≥ cards in your library)
    • Affected (first 3): Thassa's Oracle

🔴 Removed (2 signatures)

  • 2 cards · ➖ ability/PutAtLibraryPosition · removed: PutAtLibraryPosition (count=Fixed { value: 1 }, position=Top, target=parent target)
    • Affected (first 3): Slimefoot's Survey, Thassa's Oracle
  • 1 card · ➖ ability/PutAtLibraryPosition · removed: PutAtLibraryPosition (count=Fixed { value: 1 }, position=Top, target=tracked set #0)
    • Affected (first 3): Voda Sea Scavenger

🟡 Modified fields (4 signatures)

  • 3 cards · 🔄 ability/Dig · changed field keep_count: 01
    • Affected (first 3): Slimefoot's Survey, Thassa's Oracle, Voda Sea Scavenger
  • 3 cards · 🔄 ability/Dig · changed field rest_to: library
    • Affected (first 3): Slimefoot's Survey, Thassa's Oracle, Voda Sea Scavenger
  • 3 cards · 🔄 ability/Dig · changed field to: library
    • Affected (first 3): Slimefoot's Survey, Thassa's Oracle, Voda Sea Scavenger
  • 3 cards · 🔄 ability/Dig · changed field up_to: true
    • Affected (first 3): Slimefoot's Survey, Thassa's Oracle, Voda Sea Scavenger

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/mtgish-import/src/convert/action.rs (1)

4916-4939: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a CR citation for the new random-order branch.

Each of these four blocks implements a rules distinction: whether the remaining library cards are randomized (DigRestOrder::Random) or not (DigRestOrder::Preserve). None of the four carries its own CR citation for this specific branch; the surrounding doc comments cite CR numbers for the overall disposition-to-Dig shape, not for the order determination.

Add a short comment citing the applicable CR rule (or an honest CR ??? if the rule text does not name this distinction) next to each rest_order computation, consistent with the citation already added in ast.rs for DigFromAmong.rest_order ("CR 400.5 + CR 608.2c").

Based on learnings: "Every code arm implementing a Magic rules requirement must include a CR annotation verified against docs/MagicCompRules.txt; use an honest CR ??? annotation when the rule is absent from the text" (crates/mtgish-import/CLAUDE.md).

Also applies to: 4966-4989, 5103-5126, 5127-5150

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mtgish-import/src/convert/action.rs` around lines 4916 - 4939, Add a
short CR citation comment for the Random/Preserve rest_order determination in
each affected match arm: the shown Dig conversion and the corresponding blocks
around lines 4966, 5103, and 5127. Verify the applicable rule in
docs/MagicCompRules.txt, or use an honest “CR ???” annotation if the distinction
is not specified, placing the comment next to each rest_order computation and
matching the existing DigFromAmong citation style.

Source: Learnings

🧹 Nitpick comments (2)
crates/engine/src/parser/oracle_effect/conditions.rs (1)

4339-4357: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the rest_order fallback alongside rest_destination.

The comment above alt_effect explains why rest_destination prefers the alternative's inline value and falls back to the preceding Dig's value otherwise. It does not explain the equivalent rest_order: alt_rest.map_or(*prev_rest_order, |_| alt_rest_order) line, whose correctness depends on the same reasoning: when alt_rest is None, alt_rest_order is a meaningless default (Preserve) rather than a real alternative-clause value, so falling back to prev_rest_order is required.

Add one sentence to the existing comment covering this field.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine/src/parser/oracle_effect/conditions.rs` around lines 4339 -
4357, Extend the comment immediately above the alt_effect construction to
document rest_order: when alt_rest is None, use prev_rest_order because
alt_rest_order is only meaningful for an alternative inline rest destination and
otherwise defaults to Preserve.
crates/engine/src/game/engine_resolution_choices.rs (1)

760-776: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate shuffle-guard logic between route_rest_partition and the DigChoice direct-push branch.

route_rest_partition (Lines 764-774) and the DigChoice handler's direct-library-push branch (Lines 3380-3386) both implement the identical rule: shuffle the rest pile only when rest_zone/rest_destination is Zone::Library and rest_order == DigRestOrder::Random. The two implementations carry near-identical CR comments. If a future change adjusts one shuffle condition (for example, to cover an additional zone or a different randomization scope), the other site can silently drift out of sync.

Extract a small shared helper, for example fn maybe_randomize_rest(ids: &mut Vec<ObjectId>, rest_zone: Zone, rest_order: DigRestOrder, rng: &mut impl Rng), and call it from both sites.

♻️ Proposed shared helper
+fn maybe_randomize_rest_for_library(
+    ids: &mut Vec<ObjectId>,
+    destination_zone: Zone,
+    rest_order: DigRestOrder,
+    state: &mut GameState,
+) {
+    // CR 400.5: randomize exactly this rest pile when the printed
+    // instruction requires a random-order library placement.
+    if destination_zone == Zone::Library && rest_order == DigRestOrder::Random {
+        ids.shuffle(&mut state.rng);
+    }
+}

Then in route_rest_partition:

-    let mut ordered_ids = rest_ids.to_vec();
-    if rest_zone == Zone::Library && rest_order == DigRestOrder::Random {
-        // CR 400.5 + CR 608.2c: Exact Oracle text requires a randomized
-        // remainder; only this rest pile, not the remainder of the library,
-        // consumes entropy.
-        ordered_ids.shuffle(&mut state.rng);
-    }
+    let mut ordered_ids = rest_ids.to_vec();
+    maybe_randomize_rest_for_library(&mut ordered_ids, rest_zone, rest_order, state);

And in the DigChoice direct-push branch:

-                        Some(Zone::Library) => {
-                            if rest_order == DigRestOrder::Random {
-                                // CR 400.5 + CR 608.2c: Randomize exactly the
-                                // unchosen pile immediately before bottom placement.
-                                unkept.shuffle(&mut state.rng);
-                            }
+                        Some(Zone::Library) => {
+                            maybe_randomize_rest_for_library(&mut unkept, Zone::Library, rest_order, state);

Also applies to: 3362-3396

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/engine/src/game/engine_resolution_choices.rs` around lines 760 - 776,
Extract the shared rest-pile randomization condition from route_rest_partition
and the DigChoice direct-library-push branch into a helper such as
maybe_randomize_rest, accepting mutable ObjectId storage, rest zone/order, and
the RNG. Call this helper from both sites, preserving the existing Library plus
Random condition and its scoped shuffle behavior, and keep the rule’s
explanatory comment in the shared helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/engine/src/parser/oracle_ir/ast.rs`:
- Around line 423-426: Update ContinuationAst::PutRest to retain and parse the
“in a random order” suffix into a #[serde(default)] rest_order: DigRestOrder
field, then thread that value through apply_clause_continuation and
patch_rest_destination_recursively when constructing Effect::Dig, preserving
Preserve as the default for other forms.

In `@crates/engine/src/types/ability.rs`:
- Around line 10636-10652: Update the documentation comments for DigRestOrder to
cite CR 401.4 alongside CR 400.5 and CR 608.2c, while preserving the existing
explanation and enum behavior.

---

Outside diff comments:
In `@crates/mtgish-import/src/convert/action.rs`:
- Around line 4916-4939: Add a short CR citation comment for the Random/Preserve
rest_order determination in each affected match arm: the shown Dig conversion
and the corresponding blocks around lines 4966, 5103, and 5127. Verify the
applicable rule in docs/MagicCompRules.txt, or use an honest “CR ???” annotation
if the distinction is not specified, placing the comment next to each rest_order
computation and matching the existing DigFromAmong citation style.

---

Nitpick comments:
In `@crates/engine/src/game/engine_resolution_choices.rs`:
- Around line 760-776: Extract the shared rest-pile randomization condition from
route_rest_partition and the DigChoice direct-library-push branch into a helper
such as maybe_randomize_rest, accepting mutable ObjectId storage, rest
zone/order, and the RNG. Call this helper from both sites, preserving the
existing Library plus Random condition and its scoped shuffle behavior, and keep
the rule’s explanatory comment in the shared helper.

In `@crates/engine/src/parser/oracle_effect/conditions.rs`:
- Around line 4339-4357: Extend the comment immediately above the alt_effect
construction to document rest_order: when alt_rest is None, use prev_rest_order
because alt_rest_order is only meaningful for an alternative inline rest
destination and otherwise defaults to Preserve.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 072b3b6e-848c-4da2-9724-082e51be58d7

📥 Commits

Reviewing files that changed from the base of the PR and between cc76680 and 81d6b35.

⛔ Files ignored due to path filters (1)
  • crates/engine/src/parser/oracle_ir/snapshots/engine__parser__oracle_ir__snapshot_tests__follow_the_lumarets_ir.snap is excluded by !**/*.snap, !**/snapshots/**
📒 Files selected for processing (34)
  • crates/engine/src/database/hideaway.rs
  • crates/engine/src/game/ability_rw.rs
  • crates/engine/src/game/ability_scan.rs
  • crates/engine/src/game/effects/choose_card.rs
  • crates/engine/src/game/effects/dig.rs
  • crates/engine/src/game/effects/explore.rs
  • crates/engine/src/game/effects/mod.rs
  • crates/engine/src/game/effects/reveal_until.rs
  • crates/engine/src/game/engine_resolution_choices.rs
  • crates/engine/src/game/mana_abilities.rs
  • crates/engine/src/game/visibility.rs
  • crates/engine/src/parser/oracle_effect/conditions.rs
  • crates/engine/src/parser/oracle_effect/imperative.rs
  • crates/engine/src/parser/oracle_effect/lower.rs
  • crates/engine/src/parser/oracle_effect/sequence.rs
  • crates/engine/src/parser/oracle_effect/tests.rs
  • crates/engine/src/parser/oracle_ir/ast.rs
  • crates/engine/src/parser/oracle_tests.rs
  • crates/engine/src/types/ability.rs
  • crates/engine/src/types/game_state.rs
  • crates/engine/tests/integration/cost_zone_pipeline.rs
  • crates/engine/tests/integration/dig_impossible_keep_count.rs
  • crates/engine/tests/integration/dig_rest_pile_stranding_on_etb_pause.rs
  • crates/engine/tests/integration/issue_5996_planetarium_look_cast.rs
  • crates/engine/tests/integration/issue_6367_thassas_oracle.rs
  • crates/engine/tests/integration/main.rs
  • crates/engine/tests/integration/metamorphic_alteration.rs
  • crates/mtgish-import/src/convert/action.rs
  • crates/phase-ai/src/determinize.rs
  • crates/phase-ai/src/features/control.rs
  • crates/phase-ai/src/features/spellslinger_prowess.rs
  • crates/phase-ai/src/features/tests/graveyard_types.rs
  • crates/phase-ai/src/search.rs
  • crates/server-core/src/session.rs

Comment thread crates/engine/src/parser/oracle_ir/ast.rs
Comment thread crates/engine/src/types/ability.rs
@matthewevans
matthewevans removed this pull request from the merge queue due to a manual request Aug 13, 2026
@matthewevans

Copy link
Copy Markdown
Member Author

Review follow-up: I verified this against the repositorys current official CR text. I am not adding CR 401.4: it governs the owners choice to arrange simultaneous library placements in any order, whereas this field implements an exact random-order instruction (no player ordering choice). CR 400.5 establishes the library-order constraint and CR 608.2c requires carrying out the cards random-order instruction, so those remain the accurate annotations.

@matthewevans
matthewevans added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit f616d6f Aug 13, 2026
19 checks passed
@matthewevans
matthewevans deleted the ship/6367-randomize-dig-library-remainders branch August 13, 2026 02:18
This was referenced Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Thassa's Oracle — Puts itself on top of your library

1 participant