mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-09-14 08:18:41 +00:00
Delete the byte-oriented KVStore/VersionedStore abstraction and the DynamoDB/memory KV backends. Add a generic typed store (DocStore[T] with Provider/Collection/Typed) persisting each value as a flattened native Mongo document (storedDoc[T] via bson inline) — no value envelope. - MongoDB is the only runtime backend; memory kept for tests/local. - All modules + deploynotify use typed stores; persisted structs carry bson tags == json names (incl. nested lolschedule/wordle types). - lolschedule wraps its array/scalar values in named structs. - migrate-dynamo-to-mongo writes the flattened shape via Typed[bson.M] with wrap rules; Scan/--dry-run/--verify retained. Verified: go vet/build clean; full go test green hermetically and in-container vs real Mongo 7 + DynamoDB Local (storage integration + migrator e2e).
78 lines
2.4 KiB
Go
78 lines
2.4 KiB
Go
// Package wordle implements the classic 5-letter word-guess game, scored
|
|
// letter-by-letter green/yellow/grey.
|
|
package wordle
|
|
|
|
// WordLength is wordle's fixed 5. Exposed so render.go and tests can reuse it
|
|
// without magic numbers.
|
|
const WordLength = 5
|
|
|
|
// LetterResult labels a single guessed letter's state. Values are part of
|
|
// the stored game's JSON shape: "correct" | "partial" | "wrong".
|
|
const (
|
|
ResultCorrect = "correct"
|
|
ResultPartial = "partial"
|
|
ResultWrong = "wrong"
|
|
)
|
|
|
|
// LetterScore is the shape stored per guess (nested in GameState). bson tags
|
|
// mirror the json names so migrated docs (which keep the original JSON keys)
|
|
// read back verbatim.
|
|
type LetterScore struct {
|
|
Letter string `json:"letter" bson:"letter"`
|
|
Result string `json:"result" bson:"result"`
|
|
}
|
|
|
|
// CompareWords scores guess against target letter-by-letter. Both are assumed
|
|
// lowercase a-z and exactly WordLength long; callers validate via
|
|
// validateGuess before reaching here.
|
|
//
|
|
// Two-pass marking is required to handle duplicate letters correctly:
|
|
// - pass 1: positional matches → "correct"; consume those slots from the
|
|
// target's available pool.
|
|
// - pass 2: remaining guess letters → "partial" if still in the pool (and
|
|
// consume), else "wrong".
|
|
//
|
|
// Example: target "abbey", guess "babes" →
|
|
//
|
|
// b@0 partial, a@1 partial, b@2 correct, e@3 correct, s@4 wrong.
|
|
func CompareWords(guess, target string) []LetterScore {
|
|
out := make([]LetterScore, WordLength)
|
|
pool := make([]byte, 0, WordLength)
|
|
|
|
// Pass 1 — positional matches.
|
|
for i := 0; i < WordLength; i++ {
|
|
if guess[i] == target[i] {
|
|
out[i] = LetterScore{Letter: string(guess[i]), Result: ResultCorrect}
|
|
} else {
|
|
pool = append(pool, target[i])
|
|
}
|
|
}
|
|
|
|
// Pass 2 — partial matches against the remaining-pool, with consumption.
|
|
for i := 0; i < WordLength; i++ {
|
|
if out[i].Result == ResultCorrect {
|
|
continue
|
|
}
|
|
idx := indexOfByte(pool, guess[i])
|
|
if idx >= 0 {
|
|
pool = append(pool[:idx], pool[idx+1:]...)
|
|
out[i] = LetterScore{Letter: string(guess[i]), Result: ResultPartial}
|
|
} else {
|
|
out[i] = LetterScore{Letter: string(guess[i]), Result: ResultWrong}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// indexOfByte returns the first index of c in s, or -1.
|
|
// (bytes.IndexByte gives the same answer; inlined to keep this file
|
|
// dependency-free since the scoring algorithm is the whole point.)
|
|
func indexOfByte(s []byte, c byte) int {
|
|
for i, b := range s {
|
|
if b == c {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|