Files
miti99bot/internal/modules/wordle/render.go
T
tiennm99 6ee8118ad9 feat(modules): port wordle module + per-subject locking
Phase 5b of go-port-cloud-run plan. Port 14855-word dictionary
(89 KB, byte-identical to JS source) and four wordle commands
(/wordle, /wordle_new, /wordle_giveup, /wordle_stats).

KV wire-format parity: GameState/Stats JSON match JS shape;
*int64 LastResultAt for null-value compatibility. Two real bugs
caught and fixed: (1) defaultRNG data race in handlers — switched
to math/rand.Intn (mutex-protected package-level); (2) Get→mutate→Put
logical race in groups — added per-subject sync.Mutex map to serialize
access. TTL deferred (Firestore has no expirationTtl equiv — Phase 11 GC).
2026-05-09 09:35:23 +07:00

44 lines
1.0 KiB
Go

package wordle
import (
"strings"
)
// markerFor maps a LetterScore.Result to the NYT-Wordle share emoji.
func markerFor(result string) string {
switch result {
case ResultCorrect:
return "🟩"
case ResultPartial:
return "🟨"
default:
return "⬜"
}
}
// renderGuess formats one guess as the NYT share-pattern: word on one line,
// emoji marker row below.
//
// CRANE
// 🟩🟨⬜🟩🟩
func renderGuess(word string, results []LetterScore) string {
var markers strings.Builder
for _, r := range results {
markers.WriteString(markerFor(r.Result))
}
return strings.ToUpper(word) + "\n" + markers.String()
}
// renderBoard joins all prior guesses, blank-line separated. Used when a
// player asks for `/wordle` mid-round.
func renderBoard(guesses []GuessRecord) string {
if len(guesses) == 0 {
return "No guesses yet. Reply with `/wordle <word>`."
}
rows := make([]string, len(guesses))
for i, g := range guesses {
rows[i] = renderGuess(g.Word, g.Results)
}
return strings.Join(rows, "\n\n")
}