dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

commit af9d05daeaa8146c205e35118cb4586ee009fdaf
parent 36d49c03cf9ddaded59c0bb53c8eb0d4327b3982
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Thu,  7 Dec 2023 15:52:39 -0500

split pot

Diffstat:
Mpkg/web/handlers/poker/poker.go | 68++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mpkg/web/handlers/poker/poker_test.go | 49+++++++++++++++++++++++++++++++++++++++++++++++++
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) + } + }) + } +}