clone

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

chore/app-model-decomp
puzzletea / games / takuzu / export.go
export.go 2.2K
1package takuzu23import (4 "encoding/json"5 "fmt"6 "strings"78 "github.com/FelineStateMachine/puzzletea/game"9)1011// Save represents the serialized state of a Takuzu game.12type Save struct {13 Size int `json:"size"`14 State string `json:"state"`15 Provided string `json:"provided"`16 ModeTitle string `json:"mode_title"`17}1819func (m Model) GetSave() ([]byte, error) {20 save := Save{21 Size: m.size,22 State: m.grid.String(),23 Provided: serializeProvided(m.provided),24 ModeTitle: m.modeTitle,25 }26 data, err := json.Marshal(save)27 if err != nil {28 return nil, fmt.Errorf("failed to marshal takuzu save: %w", err)29 }30 return data, nil31}3233// ImportModel reconstructs a Model from saved JSON data.34func ImportModel(data []byte) (*Model, error) {35 var s Save36 if err := json.Unmarshal(data, &s); err != nil {37 return nil, fmt.Errorf("failed to unmarshal takuzu save: %w", err)38 }39 if s.Size <= 0 {40 return nil, fmt.Errorf("invalid grid size in save: %d", s.Size)41 }4243 g := newGrid(state(s.State))44 provided := deserializeProvided(s.Provided, s.Size)4546 initial := g.clone()47 for y := range s.Size {48 for x := range s.Size {49 if !provided[y][x] {50 initial[y][x] = emptyCell51 }52 }53 }5455 m := &Model{56 size: s.Size,57 grid: g,58 initialGrid: initial,59 provided: provided,60 cursor: game.Cursor{X: 0, Y: 0},61 keys: DefaultKeyMap,62 modeTitle: s.ModeTitle,63 }64 m.solved = m.checkSolved()65 return m, nil66}6768// serializeProvided encodes the provided matrix as a string of '.' (not provided) and '#' (provided).69func serializeProvided(p [][]bool) string {70 var b strings.Builder71 for y, row := range p {72 for _, v := range row {73 if v {74 b.WriteByte('#')75 } else {76 b.WriteByte('.')77 }78 }79 if y < len(p)-1 {80 b.WriteByte('\n')81 }82 }83 return b.String()84}8586// deserializeProvided decodes a provided-matrix string back into [][]bool.87func deserializeProvided(s string, size int) [][]bool {88 rows := strings.Split(s, "\n")89 p := make([][]bool, size)90 for y := range size {91 p[y] = make([]bool, size)92 if y < len(rows) {93 for x := range size {94 if x < len(rows[y]) && rows[y][x] == '#' {95 p[y][x] = true96 }97 }98 }99 }100 return p101}