From 916b5716268d5f2a6c13561e9c61118ab7df9eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Del=20Solar?= Date: Fri, 22 Nov 2024 00:41:04 -0500 Subject: [PATCH] final touchups. --- montecarlo/montecarlo.go | 33 ++++++++++++++++++++++----------- montecarlo/stats/heatmap.go | 20 +++++++++++--------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/montecarlo/montecarlo.go b/montecarlo/montecarlo.go index 35bbf415..9e286c5e 100644 --- a/montecarlo/montecarlo.go +++ b/montecarlo/montecarlo.go @@ -75,9 +75,10 @@ type LogIteration struct { // LogPlay is a single play. type LogPlay struct { - Play string `json:"play" yaml:"play"` - Rack string `json:"rack" yaml:"rack"` - Pts int `json:"pts" yaml:"pts"` + Play string `json:"play" yaml:"play"` + Leave string `json:"leave" yaml:"leave"` + Rack string `json:"rack" yaml:"rack"` + Pts int `json:"pts" yaml:"pts"` // Leftover is the equity of the leftover tiles at the end of the sim. Leftover float64 `json:"left,omitempty" yaml:"left,omitempty"` // Although this is a recursive structure we don't really use it @@ -655,7 +656,9 @@ func (s *Simmer) simSingleIteration(ctx context.Context, plies, thread int, iter continue } if s.logStream != nil { - logPlay = LogPlay{Play: simmedPlay.play.ShortDescription(), + logPlay = LogPlay{ + Play: g.Board().MoveDescriptionWithPlaythrough(simmedPlay.play), + Leave: simmedPlay.play.LeaveString(), Rack: simmedPlay.play.FullRack(), Bingo: simmedPlay.play.BingoPlayed(), Pts: simmedPlay.play.Score()} @@ -685,7 +688,12 @@ func (s *Simmer) simSingleIteration(ctx context.Context, plies, thread int, iter s.nodeCount.Add(1) // log.Debug().Msgf("Score is now %v", s.game.Score()) if s.logStream != nil { - plyChild = LogPlay{Play: bestPlay.ShortDescription(), Rack: bestPlay.FullRack(), Pts: bestPlay.Score(), Bingo: bestPlay.BingoPlayed()} + plyChild = LogPlay{ + Play: g.Board().MoveDescriptionWithPlaythrough(bestPlay), + Leave: bestPlay.LeaveString(), + Rack: bestPlay.FullRack(), + Pts: bestPlay.Score(), + Bingo: bestPlay.BingoPlayed()} } if ply == plies-2 || ply == plies-1 { // It's either OUR last turn or OPP's last turn. @@ -798,7 +806,7 @@ func (s *Simmer) printStats() string { func (s *Simmer) EquityStats() string { var ss strings.Builder s.sortPlaysByWinRate(false) - fmt.Fprintf(&ss, "%-20s%-9s%-16s%-16s\n", "Play", "Score", "Win%", "Equity") + fmt.Fprintf(&ss, "%-20s%-14s%-9s%-16s%-16s\n", "Play", "Leave", "Score", "Win%", "Equity") s.simmedPlays.RLock() defer s.simmedPlays.RUnlock() for _, play := range s.simmedPlays.plays { @@ -809,8 +817,9 @@ func (s *Simmer) EquityStats() string { ignore = "❌" } - fmt.Fprintf(&ss, "%-20s%-9d%-16s%-16s%s\n", + fmt.Fprintf(&ss, "%-20s%-14s%-9d%-16s%-16s%s\n", s.origGame.Board().MoveDescriptionWithPlaythrough(play.play), + play.play.LeaveString(), play.play.Score(), wpStats, eqStats, ignore) } fmt.Fprintf(&ss, "Iterations: %d (intervals are 99%% confidence, ❌ marks plays cut off early)\n", s.iterationCount.Load()) @@ -828,11 +837,13 @@ func (s *Simmer) ScoreDetails() string { if ply%2 == 0 { who = "Opponent" } - stats += fmt.Sprintf("**Ply %d (%s)**\n%-20s%8s%8s%8s%8s%8s\n%s\n", - ply+1, who, "Play", "Win%", "Mean", "Stdev", "Bingo %", "Iters", strings.Repeat("-", 60)) + stats += fmt.Sprintf("**Ply %d (%s)**\n%-20s%-14s%8s%8s%8s%8s%8s\n%s\n", + ply+1, who, "Play", "Leave", "Win%", "Mean", "Stdev", "Bingo %", "Iters", strings.Repeat("-", 75)) for _, play := range s.simmedPlays.plays { - stats += fmt.Sprintf("%-20s%8.2f%8.3f%8.3f%8.3f%8d\n", - s.origGame.Board().MoveDescriptionWithPlaythrough(play.play), 100.0*play.winPctStats.Mean(), + stats += fmt.Sprintf("%-20s%-14s%8.2f%8.3f%8.3f%8.3f%8d\n", + s.origGame.Board().MoveDescriptionWithPlaythrough(play.play), + play.play.LeaveString(), + 100.0*play.winPctStats.Mean(), play.scoreStats[ply].Mean(), play.scoreStats[ply].Stdev(), 100.0*play.bingoStats[ply].Mean(), play.scoreStats[ply].Iterations()) } diff --git a/montecarlo/stats/heatmap.go b/montecarlo/stats/heatmap.go index 91e5566c..4a165768 100644 --- a/montecarlo/stats/heatmap.go +++ b/montecarlo/stats/heatmap.go @@ -231,7 +231,7 @@ func playStatsStr(nextPlayList []*nextPlay, desc string, maxToDisplay int, total } } if showBingos { - fmt.Fprintf(&ss, "Bingo percentage: %.2f%%\n", float64(bingos*100)/float64(totalPlayCount)) + fmt.Fprintf(&ss, "Bingo probability: %.2f%%\n", float64(bingos*100)/float64(totalPlayCount)) } return ss.String() } @@ -274,7 +274,7 @@ func (st *SimStats) CalculatePlayStats(play string) (string, error) { oppResponsesList := sortedPlayListByScore(oppResponses) - ss.WriteString(playStatsStr(oppResponsesList, "Opponent's highest scoring plays", 10, totalOppResponses, false)) + ss.WriteString(playStatsStr(oppResponsesList, "### Opponent's highest scoring plays", 10, totalOppResponses, false)) ss.WriteString("\n\n") maxToDisplay := 15 @@ -282,10 +282,10 @@ func (st *SimStats) CalculatePlayStats(play string) (string, error) { oppResponsesList = sortedPlayList(oppResponses) ourNextPlaysList := sortedPlayList(ourNextPlays) - ss.WriteString(playStatsStr(oppResponsesList, "Opponent's next play", maxToDisplay, totalOppResponses, true)) - ss.WriteString("\n\n") + ss.WriteString(playStatsStr(oppResponsesList, "### Opponent's next play", maxToDisplay, totalOppResponses, true)) + ss.WriteString("\n") - ss.WriteString(playStatsStr(ourNextPlaysList, "Our next plays", maxToDisplay, totalOurNextPlays, true)) + ss.WriteString(playStatsStr(ourNextPlaysList, "### Our follow-up play", maxToDisplay, totalOurNextPlays, true)) ss.WriteString("\n") st.oppHist = histogram.Hist(15, oppScores) @@ -333,14 +333,16 @@ func (st *SimStats) CalculateTileStats() (string, error) { } } } - - fmt.Fprintf(&ss, " Tile %% Opponent racks with at least one of these\n") + fmt.Fprintf(&ss, "Note: these tile counts are obtained by simulation and thus are not mathematically exact.\n") + fmt.Fprintf(&ss, " Tile %% chance in opp rack Expected # in opp rack\n") for ml := range tilemapping.MaxAlphabetSize { if ct, ok := oppTileAtLeastCount[tilemapping.MachineLetter(ml)]; ok { - fmt.Fprintf(&ss, " %s %.2f\n", + fmt.Fprintf(&ss, " %s %.2f%% %.2f\n", tilemapping.MachineLetter(ml).UserVisible(st.game.Alphabet(), false), - float64(ct*100)/float64(numRacks)) + float64(ct*100)/float64(numRacks), + float64(oppTileRawCount[tilemapping.MachineLetter(ml)])/float64(numRacks), + ) } }