Editorial illustration: Shuffling through a replaceable contract

Shuffling through a replaceable contract

The problem I needed to solve

I did not want shuffling hidden inside dealing. If another game later needed a constrained opening, I wanted to replace the ordering strategy while retaining the rest of the core.

Implementation in this snapshot

FisherYatesShuffleStrategy implements IShuffleStrategy. Its loop moves backward through the collection, chooses a position between zero and the current index, and swaps the two elements.

The design decision and its cost

The constructor accepts an optional seed. That is useful for reproducing a logical path; I do not equate System.Random with cryptographic randomness or an anti-cheat guarantee.

Current boundaries

Reproducibility depends on more than the seed. Input order and the number of calls matter too. Without holding those constant, apparently similar runs can produce different orders.

Reproducing an order for investigation

Investigating a dealing issue requires more than saying that the deck was shuffled. The input order and random generator state matter too. FisherYatesShuffleStrategy accepts an optional seed and creates Random in its constructor. Two fresh strategy instances with the same seed and identical inputs allow investigation of a reproducible logical path. That is useful for explaining a scenario; it does not establish that Dalo has complete runtime reporting or match saving.

The loop moves from the list’s end toward its beginning and calls random.Next(i + 1). Adding one makes the current position an eligible choice: an item sometimes stays where it is, which is part of the algorithm. Swaps modify the supplied IList; no new collection is returned. Callers must expect their input to change. If they need the original order for comparison, they must preserve it before Shuffle, rather than after the list has been modified.

Shuffle is generic rather than card-specific, so the same contract can reorder any suitable collection. The cost of that independence is that it knows nothing about Joker opening hands or valid deck composition. Reusing one strategy instance also continues the existing Random sequence. A fixed seed does not reset the generator after each call. Reproducing an issue requires accounting for the order and number of calls as well.

What this stage taught me

This contract became useful for Joker: avoiding opening pairs was a different problem. Ordinary shuffling remained available without absorbing a game-specific rule.

Explore Dalo

Latest devlogs

Editorial illustration: Pasur launch and telemetry in the shared structure

Pasur launch and telemetry in the shared structure

Adding Pasur needs both a correct mode handoff and valid public telemetry.

Read article
Editorial illustration: Presenting selection and captures in Pasur

Presenting selection and captures in Pasur

Pasur motion should explain which hand card was played and which table cards were captured.

Read article
Editorial illustration: Pasur bot decisions from capture options

Pasur bot decisions from capture options

My teammate’s Pasur bot chooses among legal options without inspecting hidden opposing cards.

Read article
Dalo