Skip to content

Instantly share code, notes, and snippets.

@onevcat
Created December 10, 2018 03:38
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save onevcat/987f107119f55c55b49e04e80bbe1319 to your computer and use it in GitHub Desktop.
Save onevcat/987f107119f55c55b49e04e80bbe1319 to your computer and use it in GitHub Desktop.
//Solution goes in Sources
public struct Card: CustomStringConvertible {
enum Suit: String {
case spade = "♤", heart = "♡", club = "♧", diamond = "♢"
// It is not the standard Texas poker rule. All suits are considered equal.
// However, there is a test case to "brake tide by suit", in which "heart" is the largest.
var value: Int {
switch self {
case .spade: return 3
case .heart: return 2
case .club: return 1
case .diamond: return 0
}
}
}
enum Rank: String {
case A, K, Q, J
case _10 = "10"
case _9 = "9"
case _8 = "8"
case _7 = "7"
case _6 = "6"
case _5 = "5"
case _4 = "4"
case _3 = "3"
case _2 = "2"
var number: Int {
switch self {
case .A: return 14
case .K: return 13
case .Q: return 12
case .J: return 11
default: return Int(rawValue)!
}
}
}
let rank: Rank
let suit: Suit
init?(rawCard: String) {
guard rawCard.count == 2 || rawCard.count == 3 else { return nil }
guard let s = Suit(rawValue: String(rawCard.last!)) else { return nil }
guard let number = Rank(rawValue: String(rawCard.dropLast())) else { return nil }
rank = number
suit = s
}
public var description: String {
return "\(rank.rawValue)\(suit.rawValue)"
}
}
extension Card: Comparable {
public static func < (lhs: Card, rhs: Card) -> Bool {
if lhs.rank < rhs.rank { return true }
if lhs.rank > rhs.rank { return false }
return lhs.suit < rhs.suit
}
}
extension Card.Rank: Comparable {
static func < (lhs: Card.Rank, rhs: Card.Rank) -> Bool {
return lhs.number < rhs.number
}
}
extension Card.Suit: Comparable {
static func < (lhs: Card.Suit, rhs: Card.Suit) -> Bool {
return lhs.value < rhs.value
}
}
typealias Hand = String
class PokerHand: CustomStringConvertible {
var rank: HandRank!
let cards: [Card]
init?(_ hand: Hand) {
let rawCards = hand.split(separator: " ")
guard rawCards.count == 5 else { return nil }
cards = rawCards.compactMap { Card(rawCard: String($0)) }
guard cards.count == 5 else { return nil }
if isStraightFlush {
rank = .straightFlush(leadingCardInStraight)
} else if let leading = kind(of: 4) {
rank = .fourOfAKind(leading)
} else if let leading = kind(of: 3), let _ = kind(of: 2) {
rank = .fullHouse(leading)
} else if flush {
rank = .flush(cards)
} else if straight {
rank = .straight(leadingCardInStraight)
} else if let leading = kind(of: 3) {
rank = .threeOfAKind(leading, cards.filter { $0.rank != leading.rank })
} else if let leading = twoPairs() {
rank = .twoPair(leading.0, leading.1, cards.filter { ($0.rank != leading.0.rank) && ($0.rank != leading.1.rank) }.first! )
} else if let leading = kind(of: 2) {
rank = .onePair(leading, cards.filter { $0.rank != leading.rank })
} else {
rank = .highCard(cards)
}
}
var description: Hand {
return cards
.map { $0.description }
.joined(separator: " ")
}
lazy var sortedCard: [Card] = { return cards.sorted() }()
var maxCard: Card { return sortedCard[4] }
lazy var straight: Bool = {
let ranks = sortedCard.map { $0.rank }
if ranks == [._2, ._3, ._4, ._5, .A] {
return true
}
let maxDiff = ranks.max()!.number - ranks.min()!.number
return maxDiff == 4 && Set(ranks).count == 5
}()
var leadingCardInStraight: Card {
if let _ = (cards.first { $0.rank == ._2 }) {
// Use A as 1, in A,2,3,4,5. Sorted cards will be 2345A. Return 5 as the leading card.
return sortedCard.dropLast().last!
} else {
return cards.max()!
}
}
lazy var flush: Bool = {
return Set(cards.map { $0.suit }).count == 1
}()
func kind(of n: Int, input: [Card]? = nil) -> Card? {
for card in input ?? cards {
let sameRanks = cards.filter { $0.rank == card.rank }
if sameRanks.count == n {
return card
}
}
return nil
}
func twoPairs() -> (Card, Card)? {
guard let lower = kind(of: 2, input: sortedCard),
let higher = kind(of: 2, input: sortedCard.reversed()) else
{
return nil
}
if lower.rank == higher.rank {
return nil
}
return (higher, lower)
}
}
extension PokerHand {
var isStraightFlush: Bool {
return straight && flush
}
}
extension PokerHand: Comparable {
static func == (lhs: PokerHand, rhs: PokerHand) -> Bool {
return lhs.rank == rhs.rank
}
static func < (lhs: PokerHand, rhs: PokerHand) -> Bool {
return lhs.rank < rhs.rank
}
}
extension Array: Comparable where Element == Card {
public static func < (lhs: Array<Element>, rhs: Array<Element>) -> Bool {
return comparedLess(lhs: lhs, rhs: rhs, withSuit: false)
}
public static func > (lhs: Array<Element>, rhs: Array<Element>) -> Bool {
return comparedGreater(lhs: lhs, rhs: rhs, withSuit: false)
}
static func comparedLess(lhs: Array<Element>, rhs: Array<Element>, withSuit: Bool) -> Bool {
guard lhs.count == rhs.count else { fatalError() }
guard lhs.count != 0 else { return false }
let zipped = zip(lhs.sorted(), rhs.sorted()).reversed()
for v in zipped {
if v.0.rank < v.1.rank { return true }
if v.0.rank > v.1.rank { return false }
}
if withSuit {
let first = zipped.first!
return first.0.suit < first.1.suit
}
return false
}
static func comparedGreater(lhs: Array<Element>, rhs: Array<Element>, withSuit: Bool) -> Bool {
guard lhs.count == rhs.count else { fatalError() }
guard lhs.count != 0 else { return false }
let zipped = zip(lhs.sorted(), rhs.sorted()).reversed()
for v in zipped {
if v.0.rank > v.1.rank { return true }
if v.0.rank < v.1.rank { return false }
}
if withSuit {
let first = zipped.first!
return first.0.suit > first.1.suit
}
return false
}
}
enum HandRank: Comparable {
static func < (lhs: HandRank, rhs: HandRank) -> Bool {
if lhs.rankLevel < rhs.rankLevel { return true }
if lhs.rankLevel > rhs.rankLevel { return false }
switch (lhs, rhs) {
case (.highCard(let lCards), .highCard(let rCards)):
return Array.comparedLess(lhs: lCards, rhs: rCards, withSuit: true)
case (.onePair(let lLeading, let lCards), .onePair(let rLeading, let rCards)):
if lLeading.rank < rLeading.rank { return true }
if lLeading.rank > rLeading.rank { return false }
if lCards < rCards { return true }
if lCards > rCards { return false }
return lLeading.suit < rLeading.suit
case (.twoPair(let lHigherLeading, let lLowerLeading, let lLeft),
.twoPair(let rHigherLeading, let rLowerLeading, let rLeft)):
if lHigherLeading.rank < rHigherLeading.rank { return true }
if lHigherLeading.rank > rHigherLeading.rank { return false }
if lLowerLeading.rank < rLowerLeading.rank { return true }
if lLowerLeading.rank > rLowerLeading.rank { return false }
if lLeft.rank < rLeft.rank { return true }
if lLeft.rank > rLeft.rank { return false }
return lHigherLeading.suit < rHigherLeading.suit
case (.threeOfAKind(let lLeading, let lCards), .threeOfAKind(let rLeading, let rCards)):
if lLeading.rank < rLeading.rank { return true }
if lLeading.rank > rLeading.rank { return false }
if lCards < rCards { return true }
if lCards > rCards { return false }
return lLeading.suit < rLeading.suit
case (.straight(let l), .straight(let r)):
return l < r
case (.flush(let l), .flush(let r)):
return Array.comparedLess(lhs: l, rhs: r, withSuit: true)
case (.fullHouse(let lLeading), .fullHouse(let rLeading)):
return lLeading < rLeading
case (.fourOfAKind(let lLeading), .fourOfAKind(let rLeading)):
return lLeading < rLeading
case (.straightFlush(let lLeading), .straightFlush(let rLeading)):
return lLeading < rLeading
default: fatalError()
}
}
case highCard([Card]) // High cards
case onePair(Card, [Card]) // Pair leading, then high card
case twoPair(Card, Card, Card) // Pair leading, pair leading, then high card
case threeOfAKind(Card, [Card]) // Three leading, then left high cards
case straight(Card) // Leading card
case flush([Card]) // Compared each cards
case fullHouse(Card) // three leading cards
case fourOfAKind(Card) // leading card
case straightFlush(Card) // leading card
var rankLevel: Int {
switch self {
case .highCard: return 0
case .onePair: return 1
case .twoPair: return 2
case .threeOfAKind: return 3
case .straight: return 4
case .flush: return 5
case .fullHouse: return 6
case .fourOfAKind: return 7
case .straightFlush: return 8
}
}
}
func bestHand(_ inputHands: [Hand]) -> Hand? {
guard let best = inputHands.compactMap(PokerHand.init).max() else {
return nil
}
return best.description
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment