- commit
- 50495ad15465aa27199b6e671c2c3ba9f76b20ce
- parent
- f3b28a82f0cf39badc1579bbfff5db05d9165572
- author
- Dami <dami.etoile@gmail.com>
| M | daily.go | 69 | ++++++++++++++++++++++-------- |
| M | vhs/ | 2 | +- |
| M | vhs/ | 7 | ++- |
@@ -1,35 +1,68 @@
11 package main22 33 import (4- "fmt"4+ "hash/fnv"55 "math/rand/v2"66 "time"77 88 "github.com/FelineStateMachine/puzzletea/game"9+ "github.com/FelineStateMachine/puzzletea/hashiwokakero"10+ "github.com/FelineStateMachine/puzzletea/hitori"11+ "github.com/FelineStateMachine/puzzletea/lightsout"912 "github.com/FelineStateMachine/puzzletea/namegen"13+ "github.com/FelineStateMachine/puzzletea/nonogram"14+ "github.com/FelineStateMachine/puzzletea/sudoku"15+ "github.com/FelineStateMachine/puzzletea/takuzu"16+ "github.com/FelineStateMachine/puzzletea/wordsearch"1017 )1118 12-// dailyRNG returns a deterministic RNG seeded from the given date.13-// The same calendar day always produces the same seed, ensuring14-// reproducible daily puzzles regardless of the time of day.15-func dailyRNG(today time.Time) *rand.Rand {16- y, m, d := today.Date()17- seed := uint64(y)*10000 + uint64(m)*100 + uint64(d)19+// dailyEntry pairs a SeededSpawner with metadata for the eligible daily pool.20+type dailyEntry struct {21+ spawner game.SeededSpawner22+ gameType string23+ mode string24+}25+26+// eligibleDailyModes is the curated pool of quick (<10 min) modes for daily rotation.27+var eligibleDailyModes = []dailyEntry{28+ {nonogram.NewMode("Standard", "10x10 grid, ~67% filled. Classic size, dense hints.", 10, 10, 0.67), "Nonogram", "Standard"},29+ {nonogram.NewMode("Classic", "10x10 grid, ~52% filled. The typical nonogram experience.", 10, 10, 0.52), "Nonogram", "Classic"},30+ {sudoku.NewMode("Easy", "38\u201344 clues. Naked Singles.", 38), "Sudoku", "Easy"},31+ {sudoku.NewMode("Medium", "32\u201337 clues. Hidden Pairs / Pointing.", 32), "Sudoku", "Medium"},32+ {takuzu.NewMode("Medium", "8\u00d78 grid, ~40% clues. Larger grid, moderate deduction.", 8, 0.40), "Takuzu", "Medium"},33+ {takuzu.NewMode("Tricky", "10\u00d710 grid, ~38% clues. Uniqueness rule needed.", 10, 0.38), "Takuzu", "Tricky"},34+ {hashiwokakero.NewMode("Easy 9x9", "9x9 grid with 12-16 islands.", 9, 9, 12, 16), "Hashiwokakero", "Easy 9x9"},35+ {hashiwokakero.NewMode("Medium 7x7", "7x7 grid with 12-15 islands.", 7, 7, 12, 15), "Hashiwokakero", "Medium 7x7"},36+ {hitori.NewMode("Easy", "6\u00d76 grid, straightforward logic.", 6, 0.32), "Hitori", "Easy"},37+ {hitori.NewMode("Medium", "8\u00d78 grid, moderate challenge.", 8, 0.30), "Hitori", "Medium"},38+ {lightsout.NewMode("Medium", "5x5 grid", 5, 5), "Lights Out", "Medium"},39+ {lightsout.NewMode("Hard", "7x7 grid", 7, 7), "Lights Out", "Hard"},40+ {wordsearch.NewMode("Easy 10x10", "Find 6 words in a 10x10 grid.", 10, 10, 6, 3, 5, []wordsearch.Direction{wordsearch.Right, wordsearch.Down, wordsearch.DownRight}), "Word Search", "Easy 10x10"},41+}42+43+// dailySeed returns a deterministic int64 seed derived from the date.44+func dailySeed(date time.Time) uint64 {45+ dateStr := date.Format("2006-01-02")46+ h := fnv.New64a()47+ h.Write([]byte(dateStr))48+ return h.Sum64()49+}50+51+// dailyRNG creates a deterministic RNG seeded from the given date.52+func dailyRNG(date time.Time) *rand.Rand {53+ seed := dailySeed(date)1854 return rand.New(rand.NewPCG(seed, seed))1955 }2056 21-// dailyName builds the unique identifier for today's daily puzzle.22-// Format: "Daily <Mon DD YY> <adjective-noun>"23-func dailyName(today time.Time, rng *rand.Rand) string {24- datePart := today.Format("Jan _2 06")25- namePart := namegen.GenerateSeeded(rng)26- return fmt.Sprintf("Daily %s %s", datePart, namePart)57+// dailyName generates the daily puzzle name in the format:58+// "Daily Feb 14 26 - amber-falcon"59+func dailyName(date time.Time, rng *rand.Rand) string {60+ return "Daily " + date.Format("Jan _2 06") + " - " + namegen.GenerateSeeded(rng)2761 }2862 29-// dailyMode picks a random game category and mode using the provided RNG.30-// Returns the SeededSpawner, the canonical game type name, and the mode title.63+// dailyMode selects the daily mode from the eligible pool using the seeded RNG.64+// Returns the spawner, game type name, and mode title.3165 func dailyMode(rng *rand.Rand) (game.SeededSpawner, string, string) {32- cat := GameCategories[rng.IntN(len(GameCategories))].(game.Category)33- mode := cat.Modes[rng.IntN(len(cat.Modes))].(game.Mode)34- return mode.(game.SeededSpawner), cat.Name, mode.Title()66+ entry := eligibleDailyModes[rng.IntN(len(eligibleDailyModes))]67+ return entry.spawner, entry.gameType, entry.mode3568 }@@ -26,16 +26,19 @@ Sleep 500ms
2626 Enter2727 Sleep 2s2828 29-# Browse main menu29+# Browse main menu (Daily Puzzle, Generate, Continue, Quit)3030 Down3131 Sleep 500ms3232 Down3333 Sleep 500ms34+Down35+Sleep 500ms36+Up3437 Up3538 Up3639 Sleep 500ms3740 38-# Select "Generate"41+# Select "Generate" (second item)3942 Down4043 Enter4144 Sleep 1s