commit af9d05daeaa8146c205e35118cb4586ee009fdaf
parent 36d49c03cf9ddaded59c0bb53c8eb0d4327b3982
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Thu, 7 Dec 2023 15:52:39 -0500
split pot
Diffstat:
2 files changed, 101 insertions(+), 16 deletions(-)
diff --git a/pkg/web/handlers/poker/poker.go b/pkg/web/handlers/poker/poker.go
@@ -9,8 +9,9 @@ import (
"fmt"
"github.com/chehsunliu/poker"
"github.com/labstack/echo"
- "math"
+ "github.com/sirupsen/logrus"
"net/http"
+ "sort"
"strconv"
"strings"
"sync"
@@ -125,8 +126,15 @@ type PokerGame struct {
IsGameOver atomic.Bool
}
-func (g *Ongoing) computeWinners() (winner *PokerPlayer, winnerHand string) {
- var minScore int32 = math.MaxInt32
+type GameResult struct {
+ HandScore int32
+ Players []*PokerPlayer
+}
+
+func (g *Ongoing) computeWinners() (winner []GameResult) {
+ // [(123, p1, p2), (3234, p3)]
+ // [(123, p1), (3234, p2, p3), (4000, p4), (5000, p5), (6000, p6)]
+ m := make(map[int32][]*PokerPlayer)
for _, p := range g.Players {
p.CardsMtx.RLock()
@@ -144,15 +152,21 @@ func (g *Ongoing) computeWinners() (winner *PokerPlayer, winnerHand string) {
poker.NewCard(cardToPokerCard(playerCard2)),
}
e := poker.Evaluate(hand)
+ if _, ok := m[e]; !ok {
+ m[e] = make([]*PokerPlayer, 0)
+ }
+ m[e] = append(m[e], p)
// TODO: handle split pot
// TODO: handle all-in side pots
- if e < minScore {
- winner = p
- minScore = e
- winnerHand = poker.RankString(e)
- }
}
- return winner, winnerHand
+
+ arr := make([]GameResult, 0)
+ for k, v := range m {
+ arr = append(arr, GameResult{HandScore: k, Players: v})
+ }
+ sort.Slice(arr, func(i, j int) bool { return arr[i].HandScore < arr[j].HandScore })
+
+ return arr
}
func (g *Ongoing) AddEvent(evts ...PokerEvent) {
@@ -489,7 +503,7 @@ OUTER:
func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
roomTopic := "room_" + roomID
roomLogsTopic := "room_" + roomID + "_logs"
- var winner *PokerPlayer
+ var winners []GameResult
var winnerHand string
g.incrDealerIdx()
@@ -669,15 +683,14 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
}
}
- winner, winnerHand = g.Ongoing.computeWinners()
+ winners = g.Ongoing.computeWinners()
END:
- if winner == nil {
+ if len(winners) == 0 {
for _, p := range g.Ongoing.Players {
if !p.Folded {
- winner = p
- winnerHand = "Only player alive"
+ winners = append(winners, GameResult{HandScore: -1, Players: []*PokerPlayer{p}})
break
}
}
@@ -687,7 +700,30 @@ END:
mainPot := g.Ongoing.MainPot
g.Ongoing.MainPotMtx.RUnlock()
- winner.Cash += mainPot
+ winnersStr := ""
+ // TODO: handle all-ins
+ if len(winners) == 0 {
+ logrus.Error("winners has len 0")
+ } else if len(winners) == 1 && len(winners[0].Players) == 1 {
+ // Everyone fold but 1 player
+ winnerHand = "Only player alive"
+ winners[0].Players[0].Cash += mainPot
+ winnersStr += winners[0].Players[0].Username
+ } else if len(winners[0].Players) == 1 && winners[0].Players[0].Cash > 0 {
+ // Only 1 player win and is not all-in
+ winnerHand = poker.RankString(winners[0].HandScore)
+ winners[0].Players[0].Cash += mainPot
+ winnersStr += winners[0].Players[0].Username
+ } else if len(winners[0].Players) > 1 {
+ // Multiple winners, split pot
+ nb := len(winners[0].Players)
+ piece := mainPot / nb // TODO: fix this
+ for _, p := range winners[0].Players {
+ p.Cash += piece
+ winnersStr += p.Username
+ }
+ winnerHand = poker.RankString(winners[0].HandScore)
+ }
g.Ongoing.MainPotMtx.Lock()
g.Ongoing.MainPot = 0
@@ -707,7 +743,7 @@ END:
}
g.IsGameDone.Store(true)
- PokerPubSub.Pub(roomTopic, GameIsDoneEvent{DeckStr: strings.Join(g.Ongoing.Deck, ""), Winner: winner.Username, WinnerHand: winnerHand})
+ PokerPubSub.Pub(roomTopic, GameIsDoneEvent{DeckStr: strings.Join(g.Ongoing.Deck, ""), Winner: winnersStr, WinnerHand: winnerHand})
// Wait a minimum of X seconds before allowing a new game
time.Sleep(MinTimeAfterGame * time.Second)
diff --git a/pkg/web/handlers/poker/poker_test.go b/pkg/web/handlers/poker/poker_test.go
@@ -2,6 +2,7 @@ package poker
import (
"reflect"
+ "sync"
"testing"
)
@@ -28,3 +29,51 @@ func Test_reorderPlayers(t *testing.T) {
})
}
}
+
+func TestOngoing_computeWinners(t *testing.T) {
+ type fields struct {
+ Deck []string
+ Players []*PokerPlayer
+ Events []PokerEvent
+ EventsMtx sync.RWMutex
+ LogEvents []LogEvent
+ LogEventsMtx sync.RWMutex
+ CommunityCards []string
+ WaitTurnEvent PokerWaitTurnEvent
+ WaitTurnEventMtx sync.RWMutex
+ MainPot int
+ MainPotMtx sync.RWMutex
+ }
+ tests := []struct {
+ name string
+ fields fields
+ wantWinner *PokerPlayer
+ wantWinnerHand string
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ g := &Ongoing{
+ Deck: tt.fields.Deck,
+ Players: tt.fields.Players,
+ Events: tt.fields.Events,
+ EventsMtx: tt.fields.EventsMtx,
+ LogEvents: tt.fields.LogEvents,
+ LogEventsMtx: tt.fields.LogEventsMtx,
+ CommunityCards: tt.fields.CommunityCards,
+ WaitTurnEvent: tt.fields.WaitTurnEvent,
+ WaitTurnEventMtx: tt.fields.WaitTurnEventMtx,
+ MainPot: tt.fields.MainPot,
+ MainPotMtx: tt.fields.MainPotMtx,
+ }
+ gotWinner, gotWinnerHand := g.computeWinners()
+ if !reflect.DeepEqual(gotWinner, tt.wantWinner) {
+ t.Errorf("computeWinners() gotWinner = %v, want %v", gotWinner, tt.wantWinner)
+ }
+ if gotWinnerHand != tt.wantWinnerHand {
+ t.Errorf("computeWinners() gotWinnerHand = %v, want %v", gotWinnerHand, tt.wantWinnerHand)
+ }
+ })
+ }
+}