mirror of
https://github.com/Luzifer/codingame-solutions.git
synced 2024-12-23 11:11:20 +00:00
350 lines
7.8 KiB
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)
|
||
|
}
|