mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-18 00:15:36 +00:00
/wordle and /loldle now send the wordle and loldle BotFather games in an unlimited mode: each player gets their own word or champion, saved after every guess, with a New game button. Cards the bot did not record as unlimited, including the daily cards, play the daily puzzle. The helper commands keep working on the player's round. /loldledaily mirrors /wordledaily: one champion per day at 07:00 ICT, subscribe and unsubscribe, the morning recap and the live group results. The daily and unlimited machinery is shared in util/guessgame, the old in-chat wordle module is removed, and a one-time startup migration carries each player's stats and unfinished round over without changing old data.
72 lines
2.1 KiB
Go
72 lines
2.1 KiB
Go
package wordledaily
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/tiennm99/tiennm99bot/internal/modules/util/guessgame"
|
|
"github.com/tiennm99/tiennm99bot/internal/modules/wordle/wordlist"
|
|
)
|
|
|
|
// Rejections of a guess, mapped to API errors in http_api.go and to chat
|
|
// replies in command.go.
|
|
var (
|
|
errWordEmpty = errors.New("wordledaily: no word")
|
|
errWordLength = errors.New("wordledaily: word must be 5 letters")
|
|
errWordUnknown = errors.New("wordledaily: not in the word list")
|
|
)
|
|
|
|
// rules is Wordle for the guessgame engine. Marks are one byte per letter:
|
|
// c (correct), p (present elsewhere) or w (wrong).
|
|
type rules struct {
|
|
dict map[string]struct{}
|
|
}
|
|
|
|
func (rules) Label() string { return "Wordle Daily" }
|
|
func (rules) MaxGuesses() int { return maxGuesses }
|
|
|
|
// Judge accepts a dictionary word and scores it against answer.
|
|
func (r rules) Judge(input, answer string, _ []guessgame.Guess) (guessgame.Guess, error) {
|
|
v := wordlist.Validate(r.dict, input)
|
|
switch v.Reason {
|
|
case wordlist.ReasonEmpty:
|
|
return guessgame.Guess{}, errWordEmpty
|
|
case wordlist.ReasonLength:
|
|
return guessgame.Guess{}, errWordLength
|
|
case wordlist.ReasonUnknown:
|
|
return guessgame.Guess{}, errWordUnknown
|
|
}
|
|
return guessgame.Guess{Word: v.Word, Marks: marksOf(wordlist.Compare(v.Word, answer))}, nil
|
|
}
|
|
|
|
func (rules) Marker(mark byte) string {
|
|
switch mark {
|
|
case 'c':
|
|
return wordlist.Marker(wordlist.ResultCorrect)
|
|
case 'p':
|
|
return wordlist.Marker(wordlist.ResultPartial)
|
|
}
|
|
return wordlist.Marker(wordlist.ResultWrong)
|
|
}
|
|
|
|
func (rules) AnswerText(answer string) string { return strings.ToUpper(answer) }
|
|
|
|
// marksOf encodes scored letters as c/p/w, the stored and served form.
|
|
func marksOf(scores []wordlist.LetterScore) string {
|
|
var b strings.Builder
|
|
for _, sc := range scores {
|
|
switch sc.Result {
|
|
case wordlist.ResultCorrect:
|
|
b.WriteByte('c')
|
|
case wordlist.ResultPartial:
|
|
b.WriteByte('p')
|
|
default:
|
|
b.WriteByte('w')
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// emojiRow renders stored marks as the spoiler-free share row.
|
|
func emojiRow(marks string) string { return guessgame.EmojiRow(rules{}, marks) }
|