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_netwalk.go
parse_netwalk.go 2.6K
1package pdfexport23import (4 "encoding/json"5 "fmt"6 "strings"7)89type netwalkSave struct {10 Size int `json:"size"`11 Masks string `json:"masks"`12 InitialRotations string `json:"initial_rotations"`13 Kinds string `json:"kinds"`14}1516func ParseNetwalkPrintData(saveData []byte) (*NetwalkData, error) {17 if len(strings.TrimSpace(string(saveData))) == 0 {18 return nil, nil19 }2021 var save netwalkSave22 if err := json.Unmarshal(saveData, &save); err != nil {23 return nil, fmt.Errorf("decode netwalk save: %w", err)24 }25 if save.Size <= 0 {26 return nil, nil27 }2829 maskRows, err := parseNetwalkRows(save.Size, save.Masks)30 if err != nil {31 return nil, fmt.Errorf("parse netwalk masks: %w", err)32 }33 rotationRows, err := parseNetwalkRows(save.Size, save.InitialRotations)34 if err != nil {35 return nil, fmt.Errorf("parse netwalk rotations: %w", err)36 }37 kindRows, err := parseNetwalkRows(save.Size, save.Kinds)38 if err != nil {39 return nil, fmt.Errorf("parse netwalk kinds: %w", err)40 }4142 data := &NetwalkData{43 Size: save.Size,44 Masks: make([][]uint8, save.Size),45 Rotations: make([][]uint8, save.Size),46 RootX: -1,47 RootY: -1,48 }4950 for y := 0; y < save.Size; y++ {51 data.Masks[y] = make([]uint8, save.Size)52 data.Rotations[y] = make([]uint8, save.Size)53 for x := 0; x < save.Size; x++ {54 mask, ok := parseHexNibble(maskRows[y][x])55 if !ok {56 return nil, fmt.Errorf("invalid mask value %q at (%d,%d)", maskRows[y][x], x, y)57 }58 if rotationRows[y][x] < '0' || rotationRows[y][x] > '3' {59 return nil, fmt.Errorf("invalid rotation value %q at (%d,%d)", rotationRows[y][x], x, y)60 }61 data.Masks[y][x] = mask62 data.Rotations[y][x] = rotationRows[y][x] - '0'63 if kindRows[y][x] == 'S' {64 data.RootX = x65 data.RootY = y66 }67 }68 }6970 if data.RootX < 0 || data.RootY < 0 {71 return nil, fmt.Errorf("netwalk print data missing root")72 }7374 return data, nil75}7677func parseNetwalkRows(size int, raw string) ([][]byte, error) {78 rows := splitNormalizedLines(raw)79 if len(rows) != size {80 return nil, fmt.Errorf("row count = %d, want %d", len(rows), size)81 }8283 result := make([][]byte, size)84 for y, row := range rows {85 if len(row) != size {86 return nil, fmt.Errorf("row %d width = %d, want %d", y, len(row), size)87 }88 result[y] = []byte(row)89 }90 return result, nil91}9293func parseHexNibble(value byte) (uint8, bool) {94 switch {95 case value >= '0' && value <= '9':96 return value - '0', true97 case value >= 'a' && value <= 'f':98 return 10 + value - 'a', true99 case value >= 'A' && value <= 'F':100 return 10 + value - 'A', true101 default:102 return 0, false103 }104}