clone

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

chore/app-model-decomp
puzzletea / export / pdf / parse_hitori.go
parse_hitori.go 2.0K
1package pdfexport23import (4 "encoding/json"5 "fmt"6 "strings"7 "unicode/utf8"8)910type hitoriSave struct {11 Size int `json:"size"`12 Numbers string `json:"numbers"`13}1415func ParseHitoriPrintData(saveData []byte) (*HitoriData, error) {16 if len(strings.TrimSpace(string(saveData))) == 0 {17 return nil, nil18 }1920 var save hitoriSave21 if err := json.Unmarshal(saveData, &save); err != nil {22 return nil, fmt.Errorf("decode hitori save: %w", err)23 }2425 rows := splitNormalizedLines(save.Numbers)26 size := save.Size27 if size <= 0 {28 size = len(rows)29 }30 if size <= 0 {31 return nil, nil32 }3334 numbers := make([][]string, size)35 for y := 0; y < size; y++ {36 numbers[y] = make([]string, size)37 if y >= len(rows) {38 continue39 }4041 rowValues := parseHitoriRowValues(rows[y])42 for x := 0; x < size && x < len(rowValues); x++ {43 numbers[y][x] = rowValues[x]44 }45 }4647 return &HitoriData{48 Size: size,49 Numbers: numbers,50 }, nil51}5253func parseHitoriRowValues(row string) []string {54 row = strings.TrimSpace(row)55 if row == "" {56 return nil57 }5859 if strings.Contains(row, " ") || strings.Contains(row, ",") {60 fields := strings.Fields(strings.ReplaceAll(row, ",", " "))61 if len(fields) > 1 {62 values := make([]string, len(fields))63 for i, field := range fields {64 values[i] = normalizeHitoriToken(field)65 }66 return values67 }68 }6970 runes := []rune(row)71 values := make([]string, len(runes))72 for i, r := range runes {73 values[i] = normalizeHitoriRune(r)74 }75 return values76}7778func normalizeHitoriToken(token string) string {79 token = strings.TrimSpace(token)80 if token == "" || token == "." {81 return ""82 }83 if utf8.RuneCountInString(token) == 1 {84 r, _ := utf8.DecodeRuneInString(token)85 return normalizeHitoriRune(r)86 }87 return token88}8990func normalizeHitoriRune(r rune) string {91 switch {92 case r == '.':93 return ""94 case r >= '0' && r <= '9':95 return string(r)96 default:97 value := int(r - '0')98 if value >= 10 && value <= 35 {99 return fmt.Sprintf("%d", value)100 }101 return string(r)102 }103}