package main import ( "bufio" "bytes" "encoding/json" "flag" "log" "math/rand/v2" "os" "path/filepath" "sort" "time" ) type wordList struct { Words map[int][]string `json:"words"` } type shufflePattern struct { Index map[int][]int `json:"index"` } func main() { dictFile := flag.String("dict", "./dict/en_GB.dic", "dictionary of word to prep") outDir := flag.String("out", "./site/assets/data", "output directory") flag.Parse() r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), uint64(time.Now().UnixNano()))) words := wordList{ Words: make(map[int][]string), } if err := scanSuitableWords(*dictFile, func(word string) { if len(word) >= 4 && len(word) <= 6 { words.Words[len(word)] = append(words.Words[len(word)], word) } }); err != nil { log.Fatal(err) } for k, word := range words.Words { log.Printf("Found %d words of length %v", len(word), k) sort.Strings(word) } var wordData bytes.Buffer if err := json.NewEncoder(&wordData).Encode(words); err != nil { log.Fatal(err) } if err := os.WriteFile(filepath.Join(*outDir, "words.json"), wordData.Bytes(), 0644); err != nil { log.Fatal(err) } // Generate a shuffle pattern shp := shufflePattern{Index: make(map[int][]int)} for k := range words.Words { pattern := make([]int, len(words.Words[k])) for i := range words.Words[k] { pattern[i] = i } for x := 4; x < r.IntN(8)+4; x++ { r.Shuffle(len(pattern), func(i, j int) { pattern[i], pattern[j] = pattern[j], pattern[i] }) } // TODO: shuffle shp.Index[k] = pattern } var patternData bytes.Buffer if err := json.NewEncoder(&patternData).Encode(shp); err != nil { log.Fatal(err) } if err := os.WriteFile(filepath.Join(*outDir, "shuffle_pattern.json"), patternData.Bytes(), 0644); err != nil { log.Fatal(err) } } func scanSuitableWords(dictFile string, withWord func(word string)) error { f, err := os.Open(dictFile) if err != nil { return err } defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { isSuitable := true breakpoint := len(scanner.Text()) for i, r := range scanner.Text() { if r == '/' { breakpoint = i break } if r < 'a' || r > 'z' { isSuitable = false break } } if isSuitable { word := scanner.Text()[:breakpoint] withWord(word) } else { } } return scanner.Err() }