diff --git a/cmd/transpile/category.go b/cmd/transpile/category.go index 2310862..450f5bc 100644 --- a/cmd/transpile/category.go +++ b/cmd/transpile/category.go @@ -4,15 +4,126 @@ import ( "os" "flag" "fmt" + "log" + "path/filepath" + "hash/fnv" + "encoding/binary" + "encoding/json" + "encoding/hex" + "strconv" + "math/rand" ) + +type PuzzleEntry struct { + Id string + Points int + Puzzle Puzzle +} + + +func HashHex(input ...string) (string) { + hasher := fnv.New64() + for _, s := range input { + fmt.Fprintln(hasher, s) + } + return hex.EncodeToString(hasher.Sum(nil)) +} + + +func PrngOfStrings(input ...string) (*rand.Rand) { + hasher := fnv.New64() + for _, s := range input { + fmt.Fprint(hasher, s, "\n") + } + seed := binary.BigEndian.Uint64(hasher.Sum(nil)) + source := rand.NewSource(int64(seed)) + return rand.New(source) +} + + +func ParsePuzzle(puzzlePath string, seed string) (*Puzzle, error) { + puzzleFd, err := os.Open(puzzlePath) + if err != nil { + return nil, err + } + defer puzzleFd.Close() + + puzzle, err := ParseMoth(puzzleFd) + if err != nil { + return nil, err + } + + return puzzle, nil +} + + +func ParseCategory(categoryPath string, seed string) ([]PuzzleEntry, error) { + categoryFd, err := os.Open(categoryPath) + if err != nil { + return nil, err + } + defer categoryFd.Close() + + puzzleDirs, err := categoryFd.Readdirnames(0) + if err != nil { + return nil, err + } + + puzzleEntries := make([]PuzzleEntry, 0, len(puzzleDirs)) + for _, puzzleDir := range puzzleDirs { + puzzlePath := filepath.Join(categoryPath, puzzleDir, "puzzle.moth") + puzzleSeed := fmt.Sprintf("%s/%s", seed, puzzleDir) + + points, err := strconv.Atoi(puzzleDir) + if err != nil { + log.Printf("Skipping %s: %v", puzzlePath, err) + continue + } + + puzzle, err := ParsePuzzle(puzzlePath, puzzleSeed) + if err != nil { + log.Printf("Skipping %s: %v", puzzlePath, err) + continue + } + + prng := PrngOfStrings(puzzleSeed) + idBytes := make([]byte, 16) + prng.Read(idBytes) + id := hex.EncodeToString(idBytes) + puzzleEntry := PuzzleEntry{ + Id: id, + Puzzle: *puzzle, + Points: points, + } + puzzleEntries = append(puzzleEntries, puzzleEntry) + } + + return puzzleEntries, nil +} + + func main() { + // XXX: We need a way to pass in "only run this one point value puzzle" + flag.Parse() + baseSeedString := os.Getenv("SEED") for _, dirname := range flag.Args() { - f, _ := os.Open(dirname) - defer f.Close() - names, _ := f.Readdirnames(0) - fmt.Print(names) + categoryName := filepath.Base(dirname) + categorySeed := fmt.Sprintf("%s/%s", baseSeedString, categoryName) + puzzles, err := ParseCategory(dirname, categorySeed) + if err != nil { + log.Print(err) + continue + } + + jpuzzles, err := json.MarshalIndent(puzzles, "", " ") + if err != nil { + log.Print(err) + continue + } + + fmt.Println(string(jpuzzles)) } -} \ No newline at end of file +}