- commit
- fb793ef69726e5d279757061353472fd0243b4ca
- parent
- 7471ee00ebe17f1659a5ad8a7600f262b2495b81
- author
- Dami <dami.etoile@gmail.com>
| M | fillomino/ | 32 | +++++++++++++++++++---------- |
| M | fillomino/ | 14 | ++++++++++++ |
| M | fillomino/ | 12 | ----------- |
| M | fillomino/ | 21 | ------------------- |
| M | fillomino/ | 7 | ------ |
| M | hitori/ | 1 | + |
| M | shikaku/ | 1 | - |
| M | shikaku/ | 34 | ++++++++++++++++++++++++++++++ |
| M | shikaku/ | 7 | ------ |
@@ -8,20 +8,22 @@ import (
88 )99 1010 type Save struct {11- Width int `json:"width"`12- Height int `json:"height"`13- State string `json:"state"`14- Provided string `json:"provided"`15- ModeTitle string `json:"mode_title"`11+ Width int `json:"width"`12+ Height int `json:"height"`13+ State string `json:"state"`14+ Provided string `json:"provided"`15+ ModeTitle string `json:"mode_title"`16+ MaxCellValue int `json:"max_cell_value,omitempty"`1617 }1718 1819 func (m Model) GetSave() ([]byte, error) {1920 save := Save{20- Width: m.width,21- Height: m.height,22- State: encodeGrid(m.grid),23- Provided: serializeProvided(m.provided),24- ModeTitle: m.modeTitle,21+ Width: m.width,22+ Height: m.height,23+ State: encodeGrid(m.grid),24+ Provided: serializeProvided(m.provided),25+ ModeTitle: m.modeTitle,26+ MaxCellValue: m.maxCellValue,2527 }2628 data, err := json.Marshal(save)2729 if err != nil {@@ -61,12 +63,20 @@ func ImportModel(data []byte) (*Model, error) {
6163 cursor: game.Cursor{},6264 keys: DefaultKeyMap,6365 modeTitle: save.ModeTitle,64- maxCellValue: max(9, maxCellValue(state)),66+ maxCellValue: restoredMaxCellValue(save.MaxCellValue, state),6567 }6668 m.recompute()6769 return m, nil6870 }6971 72+func restoredMaxCellValue(savedMax int, state grid) int {73+ if savedMax > 0 {74+ return savedMax75+ }76+77+ return max(1, maxCellValue(state))78+}79+7080 func initialGridForImport(state grid, provided [][]bool) grid {7181 initial := newGrid(len(state[0]), len(state))7282 for y := range len(state) {@@ -164,6 +164,20 @@ func TestSaveRoundTrip(t *testing.T) {
164164 if !loaded.provided[0][0] || !loaded.provided[0][2] || !loaded.provided[1][1] || !loaded.provided[2][2] {165165 t.Fatal("expected provided mask to persist")166166 }167+ if got, want := loaded.maxCellValue, 3; got != want {168+ t.Fatalf("loaded maxCellValue = %d, want %d", got, want)169+ }170+}171+172+func TestImportLegacySaveRestoresDigitCapFromState(t *testing.T) {173+ loaded, err := ImportModel([]byte(`{"width":3,"height":3,"state":"2 . 1\n. 3 .\n2 . 1","provided":"#.#\n.#.\n#.#","mode_title":"Test"}`))174+ if err != nil {175+ t.Fatal(err)176+ }177+178+ if got, want := loaded.maxCellValue, 3; got != want {179+ t.Fatalf("loaded maxCellValue = %d, want %d", got, want)180+ }167181 }168182 169183 func TestCellViewConflictedCursorIsDistinct(t *testing.T) {@@ -96,18 +96,6 @@ func firstUnused(used [][]bool) (point, bool) {
9696 return point{}, false9797 }9898 99-func remainingUnused(used [][]bool) int {100- count := 0101- for y := range len(used) {102- for x := range len(used[y]) {103- if !used[y][x] {104- count++105- }106- }107- }108- return count109-}110-11199 func rectangleCandidates(used [][]bool, start point, maxRegion int, rng *rand.Rand) []region {112100 height := len(used)113101 width := len(used[0])@@ -3,7 +3,6 @@ package fillomino
33 import (44 "errors"55 "math"6- "slices"76 )87 98 var errInvalidGridEncoding = errors.New("invalid fillomino grid encoding")@@ -254,23 +253,3 @@ func isPartialGridValid(g grid) bool {
254253 255254 return true256255 }257-258-func regionCellsAt(g grid, start point) []point {259- value := g[start.y][start.x]260- if value == 0 {261- return nil262- }263-264- visited := make([][]bool, len(g))265- for y := range len(g) {266- visited[y] = make([]bool, len(g[y]))267- }268- comp := buildComponent(g, start, visited)269- slices.SortFunc(comp.cells, func(a, b point) int {270- if a.y != b.y {271- return a.y - b.y272- }273- return a.x - b.x274- })275- return comp.cells276-}@@ -200,13 +200,6 @@ func horizontalEdge(g grid, x, y int) bool {
200200 }201201 }202202 203-func verticalEdge(g grid, x, y int) rune {204- if hasVerticalEdge(g, x, y) {205- return '│'206- }207- return ' '208-}209-210203 func hasVerticalEdge(g grid, x, y int) bool {211204 width := len(g[0])212205 switch {@@ -44,7 +44,6 @@ func ImportModel(data []byte) (*Model, error) {
4444 Clues: s.Clues,4545 Rectangles: s.Rectangles,4646 }47- p.autoPlaceForcedRectangles()4847 4948 return &Model{5049 puzzle: p,@@ -457,6 +457,40 @@ func TestSaveLoadRoundTrip(t *testing.T) {
457457 t.Fatal("expected error for invalid JSON")458458 }459459 })460+461+ t.Run("import preserves saved rectangles without auto-placement", func(t *testing.T) {462+ original := &Model{463+ puzzle: Puzzle{464+ Width: 3,465+ Height: 2,466+ Clues: []Clue{467+ {ID: 0, X: 0, Y: 0, Value: 4},468+ {ID: 1, X: 2, Y: 1, Value: 2},469+ },470+ Rectangles: []Rectangle{471+ {ClueID: 1, X: 2, Y: 0, W: 1, H: 2},472+ },473+ },474+ keys: DefaultKeyMap,475+ modeTitle: "Test Mode",476+ }477+478+ data, err := original.GetSave()479+ if err != nil {480+ t.Fatal(err)481+ }482+483+ got, err := ImportModel(data)484+ if err != nil {485+ t.Fatal(err)486+ }487+ if len(got.puzzle.Rectangles) != 1 {488+ t.Fatalf("rectangle count = %d, want 1", len(got.puzzle.Rectangles))489+ }490+ if r := got.puzzle.FindRectangleForClue(0); r != nil {491+ t.Fatalf("unexpected auto-placed rectangle for clue 0: %+v", *r)492+ }493+ })460494 }461495 462496 func TestDeleteReplacesForcedRectangle(t *testing.T) {@@ -137,13 +137,6 @@ func horizontalEdge(m Model, preview *Rectangle, x, y int) bool {
137137 }138138 }139139 140-func verticalEdge(m Model, preview *Rectangle, x, y int) rune {141- if hasVerticalEdge(m, preview, x, y) {142- return '│'143- }144- return ' '145-}146-147140 func hasVerticalEdge(m Model, preview *Rectangle, x, y int) bool {148141 switch {149142 case x <= 0, x >= m.puzzle.Width: