← poker-tutor
import Foundation
/// Deterministic random source (SplitMix64). Every hand records its seed, so any hand
/// the user played can be replayed card-for-card by the history browser and by tests.
public struct SeededRandom: RandomNumberGenerator, Sendable {
private var state: UInt64
public let seed: UInt64
public init(seed: UInt64) {
self.seed = seed
self.state = seed &+ 0x9E3779B97F4A7C15
}
public mutating func next() -> UInt64 {
state = state &+ 0x9E3779B97F4A7C15
var z = state
z = (z ^ (z >> 30)) &* 0xBF58476D1CE4E5B9
z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
return z ^ (z >> 31)
}
}
/// A shuffled 52-card deck that deals from the top.
public struct Deck: Sendable {
private var cards: [Card]
private var position: Int = 0
public init(rng: inout SeededRandom) {
cards = Card.all
// Fisher-Yates against our own generator so the order depends only on the seed.
for i in stride(from: cards.count - 1, to: 0, by: -1) {
let j = Int(rng.next() % UInt64(i + 1))
cards.swapAt(i, j)
}
}
/// A deck with a fixed order, for tests that need exact cards.
public init(stacked: [Card]) {
let remaining = Card.all.filter { c in !stacked.contains(c) }
cards = stacked + remaining
}
public var remaining: Int { cards.count - position }
public mutating func deal() -> Card {
precondition(position < cards.count, "deck exhausted")
defer { position += 1 }
return cards[position]
}
public mutating func deal(_ count: Int) -> [Card] {
(0..<count).map { _ in deal() }
}
/// Discards a card face down, as a real dealer does before each community street.
public mutating func burn() {
_ = deal()
}
}