Advertisement

← poker-tutor

Sources/PokerCore/Chips.swift

2.7 KB Download file
import Foundation

/// A chip you can pick up and put in the pot.
public enum Chip: Int, CaseIterable, Sendable, Identifiable, Comparable {
    case twentyFive = 25
    case fifty = 50
    case hundred = 100
    case fiveHundred = 500
    case thousand = 1_000
    case fiveThousand = 5_000
    case tenThousand = 10_000

    public var value: Int { rawValue }
    public var id: Int { rawValue }

    /// What is printed on the face. Anything from a thousand up is written the way it is said.
    public var label: String {
        value >= 1_000 ? "\(value / 1_000)K" : "\(value)"
    }

    public static func < (a: Chip, b: Chip) -> Bool { a.rawValue < b.rawValue }
}

/// Keeps every bet at the table to a whole number of chips.
///
/// Nobody at a real table bets 437. They push out four hundreds, a twenty-five and a... no, they
/// push out 450, because that is what the chips in front of them make. The opponents round the
/// same way the player's chip tray does, so one side of the table cannot bet in denominations the
/// other side has never seen.
public enum ChipRounding {

    /// The smallest chip still worth having at this blind level.
    ///
    /// Real tournaments colour the small chips up and take them off the table as the blinds climb,
    /// which is why late-stage bets are round thousands rather than round twenty-fives. This is the
    /// same idea in one line: the smallest chip in play is the largest one that still divides a
    /// small blind sensibly.
    public static func smallestChip(bigBlind: Int) -> Chip {
        let ceiling = max(Chip.twentyFive.value, bigBlind / 4)
        return Chip.allCases.last { $0.value <= ceiling } ?? .twentyFive
    }

    /// Rounds a bet to whole chips without ever making it illegal.
    ///
    /// Legality wins over tidiness every time: a bet rounded below the minimum raise would be
    /// rejected by the engine and quietly turned into a check, which would change how the
    /// opponents play rather than just how their bets look.
    public static func round(_ amount: Int, bigBlind: Int, minimum: Int, maximum: Int) -> Int {
        guard maximum > 0, minimum <= maximum else { return amount }

        // All in is exact — a stack is whatever it is, and shoving it is always legal.
        if amount >= maximum { return maximum }

        let unit = smallestChip(bigBlind: bigBlind).value
        var rounded = ((amount + unit / 2) / unit) * unit

        if rounded < minimum {
            // Round up to the first whole number of chips that clears the minimum.
            rounded = ((minimum + unit - 1) / unit) * unit
        }
        if rounded > maximum { return maximum }
        return max(rounded, minimum)
    }
}