mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 16:42:41 +00:00
Adventure — Masterwork Rework: - Expand from 3 generic items to 15 tiered masterwork items across mining (weapon), fishing (armor), and foraging (boots) with per-tier drop rates (5%/4%/3%/2%/1.5%) and location gating - Skill-specific cross-skill bonuses replace flat masterwork check - Auto-equip if better, silent discard for duplicates, inventory for rest - Add !adventure equip command for manual masterwork equipping - Tiered room announcements: T1-2 DM only, T3 quiet, T4-5 full - First-drop detection with special message - Character sheet shows ⭐/⚔️ markers for masterwork/arena gear - Sell protection for special gear in shop Adventure — Arena Combat Log: - Turn-based Dragon Quest style narrative for arena fights - Outcome-first design: roll determines win/loss, log assembled backward - No-repeat flavor text via actionPicker with per-pool tracking - 60 participation XP on arena loss Adventure — Death's Reprieve Fix: - All equipment set to 1 condition instead of death-level degradation Wordle: - Refactored word sourcing and expanded fallback word list - Simplified Wordnik integration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
3.0 KiB
Go
122 lines
3.0 KiB
Go
package plugin
|
|
|
|
import (
|
|
"time"
|
|
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
// LetterResult represents the result of a single letter in a Wordle guess.
|
|
type LetterResult int
|
|
|
|
const (
|
|
LetterAbsent LetterResult = iota // ⬛ not in word
|
|
LetterPresent // 🟨 right letter, wrong position
|
|
LetterCorrect // 🟩 right letter, right position
|
|
)
|
|
|
|
// WordleGuess stores a single guess and its evaluation.
|
|
type WordleGuess struct {
|
|
Word string
|
|
PlayerID id.UserID
|
|
PlayerName string
|
|
Results []LetterResult
|
|
Timestamp time.Time
|
|
}
|
|
|
|
// WordleCategory identifies the language/category of a puzzle.
|
|
type WordleCategory string
|
|
|
|
const (
|
|
WordleCategoryEN WordleCategory = "" // English (default)
|
|
WordleCategoryPT WordleCategory = "pt" // European Portuguese
|
|
WordleCategoryFR WordleCategory = "fr" // French
|
|
WordleCategoryGames WordleCategory = "games" // Video game words (English)
|
|
)
|
|
|
|
// WordlePuzzle holds all state for one day's puzzle.
|
|
type WordlePuzzle struct {
|
|
PuzzleID string // YYYY-MM-DD
|
|
PuzzleNumber int
|
|
RoomID id.RoomID
|
|
Answer string // uppercased
|
|
WordLength int
|
|
MaxGuesses int // always 6
|
|
Category WordleCategory
|
|
Guesses []WordleGuess
|
|
Solved bool
|
|
Failed bool
|
|
StartedAt time.Time
|
|
SolvedAt *time.Time
|
|
LetterStates map[rune]LetterResult // best known state per letter
|
|
}
|
|
|
|
// WordlePlayerStat tracks a player's all-time Wordle stats.
|
|
type WordlePlayerStat struct {
|
|
UserID id.UserID
|
|
DisplayName string
|
|
TotalGuesses int
|
|
PuzzlesPlayed int
|
|
PuzzlesSolved int
|
|
WinningGuesses int
|
|
}
|
|
|
|
// scoreGuess evaluates a guess against the answer using the standard
|
|
// two-pass Wordle algorithm for correct duplicate-letter handling.
|
|
func scoreGuess(guess, answer string) []LetterResult {
|
|
n := len(answer)
|
|
results := make([]LetterResult, n)
|
|
pool := make([]rune, 0, n)
|
|
|
|
guessRunes := []rune(guess)
|
|
answerRunes := []rune(answer)
|
|
|
|
// First pass: mark exact matches (Correct).
|
|
used := make([]bool, n)
|
|
for i := 0; i < n; i++ {
|
|
if guessRunes[i] == answerRunes[i] {
|
|
results[i] = LetterCorrect
|
|
used[i] = true
|
|
}
|
|
}
|
|
|
|
// Build pool of unmatched answer letters.
|
|
for i := 0; i < n; i++ {
|
|
if !used[i] {
|
|
pool = append(pool, answerRunes[i])
|
|
}
|
|
}
|
|
|
|
// Second pass: mark Present or Absent.
|
|
for i := 0; i < n; i++ {
|
|
if results[i] == LetterCorrect {
|
|
continue
|
|
}
|
|
found := false
|
|
for j, r := range pool {
|
|
if guessRunes[i] == r {
|
|
results[i] = LetterPresent
|
|
pool = append(pool[:j], pool[j+1:]...)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
results[i] = LetterAbsent
|
|
}
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
// updateLetterStates updates the keyboard state map with results from a guess.
|
|
// A letter's state only upgrades: Absent → Present → Correct.
|
|
func updateLetterStates(states map[rune]LetterResult, guess string, results []LetterResult) {
|
|
for i, r := range []rune(guess) {
|
|
existing, ok := states[r]
|
|
if !ok || results[i] > existing {
|
|
states[r] = results[i]
|
|
}
|
|
}
|
|
}
|