clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

feat/elo-difficulty
puzzletea / games / spellpuzzle / lexicon.go
lexicon.go 2.2K
1package spellpuzzle23import (4 _ "embed"5 "strings"6)78//go:embed twl06.txt9var rawLexicon string1011type letterCounts [26]uint81213type lexiconWord struct {14 Text string15 Counts letterCounts16}1718var (19 allWords []lexiconWord20 wordsByLength map[int][]lexiconWord21 exactWordSet map[string]struct{}22 seedWordsByLength map[int][]string23)2425func init() {26 allWords = make([]lexiconWord, 0, 96000)27 wordsByLength = map[int][]lexiconWord{}28 exactWordSet = map[string]struct{}{}29 seedWordsByLength = map[int][]string{}3031 lines := strings.Split(rawLexicon, "\n")32 for _, line := range lines {33 word := strings.ToUpper(strings.TrimSpace(line))34 if word == "" {35 continue36 }37 if !isAlphaWord(word) {38 continue39 }40 entry := lexiconWord{41 Text: word,42 Counts: countLetters(word),43 }44 allWords = append(allWords, entry)45 wordsByLength[len(word)] = append(wordsByLength[len(word)], entry)46 exactWordSet[word] = struct{}{}47 if len(word) >= 3 && len(word) <= 9 {48 seedWordsByLength[len(word)] = append(seedWordsByLength[len(word)], word)49 }50 }51}5253func isAlphaWord(word string) bool {54 for _, r := range word {55 if r < 'A' || r > 'Z' {56 return false57 }58 }59 return true60}6162func countLetters(word string) letterCounts {63 var counts letterCounts64 for _, r := range word {65 counts[r-'A']++66 }67 return counts68}6970func canSpellWord(word string, bankCounts letterCounts) bool {71 counts := countLetters(word)72 for i, want := range counts {73 if want > bankCounts[i] {74 return false75 }76 }77 return true78}7980func spellableWords(bank string, minLen int) []string {81 counts := countLetters(bank)82 result := make([]string, 0, 64)83 seen := make(map[string]struct{})84 for _, word := range allWords {85 if len(word.Text) < minLen || len(word.Text) > len(bank) {86 continue87 }88 if !canSpellEntry(word, counts) {89 continue90 }91 if _, ok := seen[word.Text]; ok {92 continue93 }94 seen[word.Text] = struct{}{}95 result = append(result, word.Text)96 }97 return result98}99100func canSpellEntry(word lexiconWord, counts letterCounts) bool {101 for i, want := range word.Counts {102 if want > counts[i] {103 return false104 }105 }106 return true107}108109func isExactWord(word string) bool {110 _, ok := exactWordSet[word]111 return ok112}