Last active
June 24, 2021 23:56
-
-
Save loganwright/8c0f9a1203f5c6743b4e9b2452784233 to your computer and use it in GitHub Desktop.
binary-cards-probability
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// setup | |
enum Value: String, CaseIterable, Equatable { | |
case ace, | |
two, | |
three, | |
four, | |
five, | |
six, | |
seven, | |
eight, | |
nine, | |
ten, | |
jack, | |
queen, | |
king | |
} | |
enum Suit: String, CaseIterable, Equatable { | |
case spades, | |
hearts, | |
diamonds, | |
clubs | |
} | |
struct Card: Equatable { | |
let value: Value | |
let suit: Suit | |
} | |
let deck = Suit.allCases.flatMap { suit in | |
Value.allCases.map { value in | |
Card(value: value, suit: suit) | |
} | |
} | |
precondition(deck.count == 52) | |
/// subdivisions | |
typealias Question = (Card) -> Bool | |
struct SubdividedDeck { | |
let label: String | |
let left: [Card] | |
let right: [Card] | |
init(_ label: String, _ question: Question) { | |
self.label = label | |
let (left, right) = deck.makeGroups(sortWith: question) | |
self.left = left | |
self.right = right | |
} | |
} | |
extension Array where Element == Card { | |
func pickRandom() -> Card { | |
assert(!self.isEmpty) | |
return randomElement()! | |
} | |
func makeGroups(sortWith: Question) -> ([Card], [Card]) { | |
var a = [Card]() | |
var b = [Card]() | |
forEach { card in | |
if sortWith(card) { | |
a.append(card) | |
} else { | |
b.append(card) | |
} | |
} | |
return (a, b) | |
} | |
} | |
/// round | |
enum Result: Equatable { | |
case win, loss | |
} | |
extension Array where Element == Result { | |
var winPercentage: Double { | |
let wins = filter { $0 == .win } .count | |
let total = self.count | |
return Double(wins) / Double(total) | |
} | |
} | |
/// options | |
let isRed = SubdividedDeck("isRed") { card in | |
switch card.suit { | |
case .diamonds, .hearts: | |
return true | |
default: | |
return false | |
} | |
} | |
let isFace = SubdividedDeck("isFace") { card in | |
switch card.value { | |
case .jack, .queen, .king: | |
return true | |
default: | |
return false | |
} | |
} | |
let isAceOfSpades = SubdividedDeck("isAce") { card in | |
card.suit == .spades && card.value == .ace | |
} | |
/// | |
extension SubdividedDeck { | |
func play() -> Result { | |
let prize = deck.pickRandom() | |
let choicePool = choicePool(containing: prize) | |
let guess = choicePool.pickRandom() | |
return guess == prize ? .win : .loss | |
} | |
func choicePool(containing prize: Card) -> [Card] { | |
left.contains(prize) ? left : right | |
} | |
} | |
/// prepare | |
let tests = [ | |
isRed, | |
isFace, | |
isAceOfSpades, | |
] | |
var results: [String: [Result]] = [:] | |
tests.forEach { results[$0.label] = [] } | |
/// simulate | |
let numberOfCycles = 10_000_000 | |
for _ in 1...numberOfCycles { | |
tests.forEach { test in | |
let result = test.play() | |
results[test.label]!.append(result) | |
} | |
} | |
/// results | |
tests.forEach { test in | |
let probability = results[test.label]!.winPercentage | |
print("\(test.label): \(probability)") | |
} | |
print("") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment