Created
May 23, 2019 00:26
-
-
Save yangchenyun/103bceb5f764655fbc2b604f3c977e3f to your computer and use it in GitHub Desktop.
Poker hands
This file contains 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
// # Determine Winning Poker Hand | |
// Design a program to determine the winner of a | |
// [poker](https://en.wikipedia.org/wiki/Poker) game. | |
// The order of a card would be described as "23456789TJQKA", the suit of a card | |
// would be described as "HSCD" (**H**eart, **S**pade, **C**lub, **D**iamond). Each card | |
// would be presented by two characters, i.e. "9D", "KC" etc. | |
// You would be given a list of hands as a text file, each line represents a | |
// player's hand. Your program should output the winning hand (or hands if they are | |
// tied.) | |
// The winning rules for hand rankings could be found at:reverse | |
// https://www.cardplayer.com/rules-of-poker/hand-rankings. In the event of a tie: | |
// Highest card wins, and if necessary, the second-highest, third-highest, | |
// fourth-highest and smallest card can be used to break the tie. | |
// Example | |
// ```text | |
// input: | |
// 6C 7C 8C 9C TC | |
// 9D 9H 9S 9C 7D | |
// TD TC TH 7C 7D | |
// output: | |
// 6C 7C 8C 9C TC | |
// ``` | |
// ```text | |
// input: | |
// TD TC TH 7C 7D | |
// TD TC TH 8C 8D | |
// output: | |
// TD TC TH 8C 8D | |
// ``` | |
// Looking for correctness and good code design. | |
package main | |
import ( | |
"fmt" | |
"sort" | |
"strconv" | |
"strings" | |
) | |
var ( | |
example1 = [][]string { | |
{"6C", "7C", "8C", "9C", "TC"}, | |
{"9D", "9H", "9S", "9C", "7D"}, | |
{"TD", "TC", "TH", "7C", "7D"}, | |
} | |
example1output = []string{"6C", "7C", "8C", "9C", "TC"} | |
example2 = [][]string{ | |
{"TD","TC","TH","7C","7D"}, | |
{"TD","TC","TH","8C","8D"}, | |
} | |
example2output = []string {"TD","TC","TH","8C","8D"} | |
example3 = [][]string{ | |
{"TD","TC","TH","8C","8D"}, | |
{"TD","TC","TH","8C","8D"}, | |
} | |
example3output = []string {"TD","TC","TH","8C","8D"} | |
) | |
type Card struct { | |
Suit string | |
Rank int | |
raw string | |
} | |
func (c Card) ToString() string { | |
return c.raw | |
} | |
type Hand []Card | |
func (h Hand) Swap(i, j int) { | |
h[i], h[j] = h[j], h[i] | |
} | |
func (h Hand) Len() int { | |
return len(h) | |
} | |
func (h Hand) Less(i, j int) bool { | |
return h[i].Rank < h[j].Rank | |
} | |
func (h Hand) Flush() bool { | |
for i := 0; i < len(h)-1; i++ { | |
if h[i].Suit != h[i+1].Suit { | |
return false | |
} | |
} | |
return true | |
} | |
func (h Hand) Straight() bool { | |
sort.Sort(h) | |
diff := h[len(h)-1].Rank - h[0].Rank | |
return diff == 4 | |
} | |
func (h Hand) HighCard() Card { | |
sort.Sort(h) | |
return h[len(h)-1] | |
} | |
func (h Hand) Type() Kind { | |
hist := GetSubHands(h) | |
kind := evaluateRankHistogram(hist) | |
flush := h.Flush() | |
straight := h.Straight() | |
if straight && flush { | |
if h.HighCard().Rank == 14 { | |
return RoyalFlush | |
} else { | |
return StraightFlush | |
} | |
} else if straight { | |
return Straight | |
} else if flush { | |
return Flush | |
} | |
return kind | |
} | |
func (h Hand) resolveFlushes(other Hand) (bool, bool) { | |
h = sort.Reverse(h).(Hand) | |
other = sort.Reverse(other).(Hand) | |
for i := 0; i < len(h) && i < len(other); i++ { | |
if h[i].Rank > other[i].Rank { | |
return true, false | |
} | |
if h[i].Rank < other[i].Rank { | |
return false, false | |
} | |
} | |
return false, true | |
} | |
func (h Hand) resolveSubHands(other Hand) (bool, bool) { | |
subHands := GetSubHands(h) | |
otherSubHands := GetSubHands(other) | |
// Special Case Two Pair, put the highest rank pair first | |
if len(subHands) == 3 && len(subHands[0]) == 2 { //2, 2, 1 | |
if subHands[0][0].Rank < subHands[1][0].Rank { | |
subHands[0], subHands[1] = subHands[1], subHands[0] | |
} | |
if otherSubHands[0][0].Rank < otherSubHands[1][0].Rank { | |
otherSubHands[0], otherSubHands[1] = otherSubHands[1], otherSubHands[0] | |
} | |
} | |
for i := 0; i < len(subHands) && i < len(otherSubHands); i++ { | |
if subHands[i][0].Rank > otherSubHands[i][0].Rank { | |
return true, false | |
} | |
if subHands[i][0].Rank < otherSubHands[i][0].Rank { | |
return false, false | |
} | |
} | |
return false, true | |
} | |
func (h Hand) Trumps(other Hand) (bool, bool) { | |
t := h.Type() | |
ot := other.Type() | |
if t < ot { | |
return true, false | |
} | |
if t > ot { | |
return false, false | |
} | |
switch t { | |
case Straight: | |
fallthrough | |
case StraightFlush: | |
fallthrough | |
case HighCard: | |
return h.HighCard().Rank > other.HighCard().Rank, false | |
case Flush: | |
return h.resolveFlushes(other) | |
case FourKind: | |
fallthrough | |
case ThreeKind: | |
fallthrough | |
case FullHouse: | |
fallthrough | |
case TwoPair: | |
fallthrough | |
case OnePair: | |
return h.resolveSubHands(other) | |
} | |
return false, true | |
} | |
func (h Hand) ToString() string { | |
out := make([]string, len(h)) | |
for i, c := range h { | |
out[i] = c.ToString() | |
} | |
return strings.Join(out, " ") | |
} | |
func NewHandFromStringArray(s []string) (Hand) { | |
out := make([]Card, len(s)) | |
for i, h := range s { | |
rank := h[0:1] | |
suit := h[1:2] | |
var r int | |
// "23456789TJQKA" | |
switch rank { | |
case "A": | |
r = 14 // | |
case "T": | |
r = 10 | |
case "J": | |
r = 11 | |
case "Q": | |
r = 12 | |
case "K": | |
r = 13 | |
default: | |
r , _ = strconv.Atoi(rank) | |
} | |
// "HSCD" | |
out[i].Suit = suit | |
out[i].Rank = r | |
out[i].raw = h | |
} | |
return out | |
} | |
type Kind int | |
const ( | |
RoyalFlush Kind = iota | |
StraightFlush | |
FourKind | |
FullHouse | |
Flush | |
Straight | |
ThreeKind | |
TwoPair | |
OnePair | |
HighCard | |
END | |
) | |
func GetSubHands(hand Hand) []Hand { | |
histogram := make(map[int]Hand) | |
for _, c := range hand { | |
histogram[c.Rank] = append(histogram[c.Rank], c) | |
} | |
a := make([]Hand, 0) | |
for _, v := range histogram { | |
a = append(a, v) | |
} | |
sort.Slice(a, func(i, j int) bool { | |
return len(a[i]) > len(a[j]) | |
}) | |
return a | |
} | |
func evaluateSubHand(a Hand) Kind { | |
if len(a) == 4 { | |
return FourKind | |
} | |
if len(a) == 3 { | |
return ThreeKind | |
} | |
if len(a) == 2 { | |
return OnePair | |
} | |
return HighCard | |
} | |
func evaluateRankHistogram(a []Hand) Kind { | |
kinds := make([]Kind, 0) | |
for _, h := range a { | |
k := evaluateSubHand(h) | |
kinds = append(kinds, k) | |
} | |
kind := kinds[0] | |
for i := 1; i < len(kinds); i++ { | |
k := kinds[i] | |
if kind == ThreeKind && k == OnePair { | |
kind = FullHouse | |
} else if kind == OnePair && k == OnePair { | |
kind = TwoPair | |
} | |
} | |
return kind | |
} | |
func main() { | |
winners := make([]Hand, 0) | |
for _, h := range example1 { | |
hand := NewHandFromStringArray(h) | |
if len(winners) == 0 { | |
winners = append(winners, hand) | |
} else { | |
win, tie := hand.Trumps(winners[0]) | |
if win { | |
winners = []Hand{hand} | |
} else if tie { | |
winners = append(winners, hand) | |
} | |
} | |
} | |
output := make([]string, 0) | |
for _, hand := range winners { | |
output = append(output, hand.ToString()) | |
} | |
fmt.Printf("%v=%v", example1output, output) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment