1
0
mirror of https://github.com/Luzifer/codingame-solutions.git synced 2024-09-18 23:23:00 +00:00
codingame-solutions/finished/community_texas-holdem.go

350 lines
7.8 KiB
Go

package main
import (
"bufio"
"fmt"
"os"
"sort"
"strings"
)
const (
WINNING_UNKNOWN uint = iota
WINNING_HIGH_CARD
WINNING_PAIR
WINNING_TWO_PAIR
WINNING_THREE_OF_A_KIND
WINNING_STRAIGHT
WINNING_FLUSH
WINNING_FULL_HOUSE
WINNING_FOUR_OF_A_KIND
WINNING_STRAIGHT_FLUSH
)
var (
winningOutputs = map[uint]string{
WINNING_HIGH_CARD: "HIGH_CARD",
WINNING_PAIR: "PAIR",
WINNING_TWO_PAIR: "TWO_PAIR",
WINNING_THREE_OF_A_KIND: "THREE_OF_A_KIND",
WINNING_STRAIGHT: "STRAIGHT",
WINNING_FLUSH: "FLUSH",
WINNING_FULL_HOUSE: "FULL_HOUSE",
WINNING_FOUR_OF_A_KIND: "FOUR_OF_A_KIND",
WINNING_STRAIGHT_FLUSH: "STRAIGHT_FLUSH",
}
handChecks = map[uint]handCheck{
WINNING_HIGH_CARD: checkHighCard,
WINNING_PAIR: checkPair,
WINNING_TWO_PAIR: checkTwoPair,
WINNING_THREE_OF_A_KIND: checkThreeOfAKind,
WINNING_STRAIGHT: checkStraight,
WINNING_FLUSH: checkFlush,
WINNING_FULL_HOUSE: checkFullHouse,
WINNING_FOUR_OF_A_KIND: checkFourOfAKind,
WINNING_STRAIGHT_FLUSH: checkStraightFlush,
}
cardValues = map[byte]int{'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}
)
type card struct{ Value, Suit byte }
type handCheck func(hand) (bool, string)
type hand []card
func parseHand(player, board string) hand {
all := strings.Split(player, " ")
all = append(all, strings.Split(board, " ")...)
out := hand{}
for i := 0; i < 7; i++ {
out = append(out, card{all[i][0], all[i][1]})
}
return out
}
type cardValueList []byte
func (c cardValueList) Len() int { return len(c) }
func (c cardValueList) Less(i, j int) bool { return cardValues[c[i]] > cardValues[c[j]] }
func (c cardValueList) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Buffer(make([]byte, 1000000), 1000000)
scanner.Scan()
holeCardsPlayer1 := scanner.Text()
scanner.Scan()
holeCardsPlayer2 := scanner.Text()
scanner.Scan()
communityCards := scanner.Text()
fmt.Fprintln(os.Stderr, "+-------- DBG -------+")
fmt.Fprintf(os.Stderr, "| P1: %s |\n| P2: %s |\n| CO: %s |\n", holeCardsPlayer1, holeCardsPlayer2, communityCards)
fmt.Fprintln(os.Stderr, "+-------- DBG -------+\n")
fmt.Println(executeChecks(holeCardsPlayer1, holeCardsPlayer2, communityCards))
}
func executeChecks(holeCardsPlayer1, holeCardsPlayer2, communityCards string) string {
player1Hand := parseHand(holeCardsPlayer1, communityCards)
player2Hand := parseHand(holeCardsPlayer2, communityCards)
player1Result := WINNING_UNKNOWN
player2Result := WINNING_UNKNOWN
var player1Out, player2Out string
for w, hc := range handChecks {
if ok, out := hc(player1Hand); ok && w > player1Result {
player1Result = w
player1Out = out
}
if ok, out := hc(player2Hand); ok && w > player2Result {
player2Result = w
player2Out = out
}
}
switch {
case player1Result > player2Result:
return fmt.Sprintf("1 %s %s", winningOutputs[player1Result], player1Out)
case player2Result > player1Result:
return fmt.Sprintf("2 %s %s", winningOutputs[player2Result], player2Out)
case player2Result == player1Result:
switch compareResultOutputHighCard(player1Out, player2Out) {
case 1:
return fmt.Sprintf("1 %s %s", winningOutputs[player1Result], player1Out)
case 2:
return fmt.Sprintf("2 %s %s", winningOutputs[player2Result], player2Out)
}
}
return "DRAW"
}
func compareResultOutputHighCard(player1, player2 string) int {
for i := 0; i < 5; i++ {
switch {
case cardValues[player1[i]] > cardValues[player2[i]]:
return 1
case cardValues[player1[i]] < cardValues[player2[i]]:
return 2
}
}
return 0
}
func (h hand) OrderCards() []byte {
cvs := []byte{}
for _, c := range h {
cvs = append(cvs, c.Value)
}
sort.Sort(cardValueList(cvs))
return cvs
}
func (h hand) CountValues() map[byte]int {
out := map[byte]int{}
for _, c := range h {
if _, ok := out[c.Value]; !ok {
out[c.Value] = 0
}
out[c.Value]++
}
return out
}
func (h hand) CountSuits() map[byte]int {
out := map[byte]int{}
for _, c := range h {
if _, ok := out[c.Suit]; !ok {
out[c.Suit] = 0
}
out[c.Suit]++
}
return out
}
func cardOutput(cardValues []byte, winning []byte) string {
kickers := []byte{}
for _, c := range cardValues {
found := false
for _, w := range winning {
if w == c {
found = true
}
}
if !found {
kickers = append(kickers, c)
}
}
sort.Sort(cardValueList(kickers))
return strings.Join(cardValuesToList(winning...), "") + strings.Join(cardValuesToList(kickers[0:5-len(winning)]...), "")
}
func cardValuesToList(vals ...byte) []string {
out := []string{}
for _, b := range vals {
out = append(out, string(b))
}
return out
}
func checkHighCard(cards hand) (bool, string) { return true, cardOutput(cards.OrderCards(), []byte{}) }
func checkPair(cards hand) (bool, string) {
for val, count := range cards.CountValues() {
if count == 2 {
return true, cardOutput(cards.OrderCards(), []byte{val, val})
}
}
return false, ""
}
func checkTwoPair(cards hand) (bool, string) {
var foundPairs = []byte{}
for val, count := range cards.CountValues() {
if count == 2 {
foundPairs = append(foundPairs, val)
}
}
if len(foundPairs) < 2 {
return false, ""
}
sort.Sort(cardValueList(foundPairs))
pairs := []byte{foundPairs[0], foundPairs[0], foundPairs[1], foundPairs[1]}
return true, cardOutput(cards.OrderCards(), pairs)
}
func checkThreeOfAKind(cards hand) (bool, string) {
for val, count := range cards.CountValues() {
if count == 3 {
return true, cardOutput(cards.OrderCards(), []byte{val, val, val})
}
}
return false, ""
}
func checkStraight(cards hand) (bool, string) {
order := cards.OrderCards()
if order[0] == 'A' { // Special case: Despite having value 14 an Ace can have value 1
order = append(order, 'A')
}
straightLength := 1
for i := 1; i < len(order); i++ {
if cardValues[order[i-1]] == cardValues[order[i]]+1 || (cardValues[order[i-1]] == 2 && order[i] == 'A') {
straightLength++
} else {
straightLength = 1
}
if straightLength == 5 {
return true, cardOutput(cards.OrderCards(), order[i-4:i+1])
}
}
return false, ""
}
func checkFlush(cards hand) (bool, string) {
var fiver byte
for suit, count := range cards.CountSuits() {
if count >= 5 {
fiver = suit
}
}
if fiver == 0x0 {
return false, ""
}
matchedSuitValues := []byte{}
for _, c := range cards {
if c.Suit == fiver {
matchedSuitValues = append(matchedSuitValues, c.Value)
}
}
sort.Sort(cardValueList(matchedSuitValues))
return true, cardOutput(cards.OrderCards(), matchedSuitValues[0:5])
}
func checkFullHouse(cards hand) (bool, string) {
var (
foundPairs = []byte{}
foundThreeOfAKind = []byte{}
)
for val, count := range cards.CountValues() {
if count == 2 {
foundPairs = append(foundPairs, val)
} else if count == 3 {
foundThreeOfAKind = append(foundThreeOfAKind, val)
}
}
if len(foundPairs) == 0 || len(foundThreeOfAKind) == 0 {
return false, ""
}
sort.Sort(cardValueList(foundPairs))
winning := []byte{foundThreeOfAKind[0], foundThreeOfAKind[0], foundThreeOfAKind[0], foundPairs[0], foundPairs[0]}
return true, cardOutput(cards.OrderCards(), winning)
}
func checkFourOfAKind(cards hand) (bool, string) {
for val, count := range cards.CountValues() {
if count == 4 {
return true, cardOutput(cards.OrderCards(), []byte{val, val, val, val})
}
}
return false, ""
}
func checkStraightFlush(cards hand) (bool, string) {
var fiver byte
for suit, count := range cards.CountSuits() {
if count >= 5 {
fiver = suit
}
}
if fiver == 0x0 {
return false, ""
}
matchedCards := hand{}
for i := range cards {
c := cards[i]
if c.Suit == fiver {
matchedCards = append(matchedCards, c)
}
}
return checkStraight(matchedCards)
}