From c5d6a6db801f06124c320611415c61235cd87f9b Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 9 Jan 2026 01:08:25 +0100 Subject: [PATCH] Gather data --- src/main/java/puzzle/SwedishGenerator.java | 67 +++++-------- src/test/java/puzzle/ExportFormatTest.java | 94 +++++++++++++++++++ src/test/java/puzzle/MainTest.java | 8 +- .../java/puzzle/SwedishGeneratorTest.java | 40 ++++---- 4 files changed, 140 insertions(+), 69 deletions(-) create mode 100644 src/test/java/puzzle/ExportFormatTest.java diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 4d6dc5a..53f558f 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -85,11 +85,11 @@ public record SwedishGenerator(int[] buff) { Bit seen, char[] pattern, IntList[] intListBuffer, - long[] undoBuffer) { + int[] undoBuffer) { public Context() { this(new int[SIZE], new int[SIZE], new int[SIZE], new int[SIZE], new Bit(), new char[MAX_WORD_LENGTH], new IntList[MAX_WORD_LENGTH], - new long[2048]); + new int[2048]); } void setPatter(char[] chars) { System.arraycopy(chars, 0, this.pattern, 0, chars.length); } } @@ -342,16 +342,15 @@ public record SwedishGenerator(int[] buff) { public int clueC() { return (key >> 4) & 15; } public int dir() { return key & 15; } public boolean horiz() { return horiz(key); } - public int r(int i) { return Grid.r(offset(packedPos, i)); } public int pos(int i) { return offset(packedPos, i); } - public int c(int i) { return Grid.c(offset(packedPos, i)); } public static boolean horiz(int key) { return ((key & 15) & 1) == 0; } public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); } } - static void undoPlace(Grid grid, long[] undoBuffer, int offset, int n) { - for (var i = 0; i < n; i++) { - long v = undoBuffer[offset + i]; - grid.clear((int)v); + static void undoPlace(Grid grid, Slot s, int mask) { + for (var i = 0; i < s.len(); i++) { + if ((mask & (1L << i)) != 0) { + grid.clear(s.pos(i)); + } } } @FunctionalInterface @@ -739,49 +738,26 @@ public record SwedishGenerator(int[] buff) { for (var i = 0; i < s.len(); i++) cross += (cellCount[s.pos(i)] - 1); return cross * 10 + s.len(); } - static int placeWord1(Grid grid, Slot s, Lemma w, long[] undoBuffer, int offset) { - int n = 0; - for (var i = 0; i < s.len(); i++) { - int r = s.r(i), c = s.c(i); - char cur = grid.getCharAt(r, c); - var ch = w.charAt(i); - if (cur == C_DASH) { - undoBuffer[offset + n] = ((long) r << 16) | ((long) c << 8); - n++; - grid.setCharAt(r, c, ch); - } else { - if (cur != ch) { - for (var j = 0; j < n; j++) { - long v = undoBuffer[offset + j]; - grid.clear((int) (v >> 16) & 0xFF, (int) (v >> 8) & 0xFF); - } - return -1; - } - } - } - return n; - } - static int placeWord(Grid grid, Slot s, Lemma w, long[] undoBuffer, int offset) { - int n = 0; + static int placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) { + int mask = 0; for (var i = 0; i < s.len(); i++) { int idx = s.pos(i); char cur = grid.getCharAt(idx); var ch = w.charAt(i); if (cur == C_DASH) { - undoBuffer[offset + n] = idx; - n++; + mask |= (1 << i); grid.setCharAt(idx, ch); - } else { - if (cur != ch) { - for (var j = 0; j < n; j++) { - long v = undoBuffer[offset + j]; - grid.clear((int) v); + } else if (cur != ch) { + for (var j = 0; j < i; j++) { + if ((mask & (1 << j)) != 0) { + grid.clear(s.pos(j)); } - return -1; } + return -1; } } - return n; + undoBuffer[offset] = mask; + return 1; } public FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex, @@ -886,7 +862,6 @@ public record SwedishGenerator(int[] buff) { var entry = dictIndex[patLen]; var pat = new char[patLen]; patternForSlot(grid, s, pat); - int undoOffset = depth * SIZE; if (pick.info.indices != null && pick.info.indices.length > 0) { var idxs = pick.info.indices; var L = idxs.length; @@ -909,7 +884,7 @@ public record SwedishGenerator(int[] buff) { } if (!match) continue; - int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, undoOffset); + int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, depth); if (nPlaced < 0) continue; used.set(w.index()); @@ -919,7 +894,7 @@ public record SwedishGenerator(int[] buff) { assigned.remove(k); used.clear(w.index); - undoPlace(grid, ctx.undoBuffer, undoOffset, nPlaced); + undoPlace(grid, s, ctx.undoBuffer[depth]); } stats.backtracks++; return false; @@ -948,7 +923,7 @@ public record SwedishGenerator(int[] buff) { } if (!match) continue; - int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, undoOffset); + int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, depth); if (nPlaced < 0) continue; used.set(w.index()); @@ -958,7 +933,7 @@ public record SwedishGenerator(int[] buff) { assigned.remove(k); used.clear(w.index); - undoPlace(grid, ctx.undoBuffer, undoOffset, nPlaced); + undoPlace(grid, s, ctx.undoBuffer[depth]); } stats.backtracks++; diff --git a/src/test/java/puzzle/ExportFormatTest.java b/src/test/java/puzzle/ExportFormatTest.java new file mode 100644 index 0000000..ea3055b --- /dev/null +++ b/src/test/java/puzzle/ExportFormatTest.java @@ -0,0 +1,94 @@ +package puzzle; + +import org.junit.jupiter.api.Test; +import puzzle.ExportFormat.ExportedPuzzle; +import puzzle.ExportFormat.Rewards; +import puzzle.Main.PuzzleResult; +import puzzle.SwedishGenerator.FillResult; +import puzzle.SwedishGenerator.Grid; +import puzzle.SwedishGenerator.Lemma; + +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.*; + +public class ExportFormatTest { + + @Test + void testExportFormatFromFilled() { + var swe = new SwedishGenerator(); + var grid = SwedishGenerator.makeEmptyGrid(); + + // Place a '2' (right) at (0,0) + grid.setCharAt(0, 0, '2'); + // This creates a slot starting at (0,1) + + var clueMap = new HashMap(); + // key = (r << 8) | (c << 4) | d + int key = (0 << 8) | (0 << 4) | 2; + Lemma lemma = new Lemma("TEST", 1, "A test word"); + clueMap.put(key, lemma); + + // Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4) + grid.setCharAt(0, 1, 'T'); + grid.setCharAt(0, 2, 'E'); + grid.setCharAt(0, 3, 'S'); + grid.setCharAt(0, 4, 'T'); + // Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH + grid.setCharAt(0, 5, '1'); + + var fillResult = new FillResult(true, grid, clueMap, null); + var puzzleResult = new PuzzleResult(swe, null, null, fillResult); + + Rewards rewards = new Rewards(10, 5, 1); + ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 2, rewards); + + assertNotNull(exported); + assertEquals(2, exported.difficulty()); + assertEquals(rewards, exported.rewards()); + + // Check words + assertEquals(1, exported.words().length); + var w = exported.words()[0]; + assertEquals("TEST", w.word()); + assertEquals("h", w.direction()); + + // The bounding box should include (0,0) for the arrow and (0,1)-(0,4) for the word. + // minR=0, maxR=0, minC=0, maxC=4 + // startRow = 0 - minR = 0 + // startCol = 1 - minC = 1 + assertEquals(0, w.startRow()); + assertEquals(1, w.startCol()); + assertEquals(0, w.arrowRow()); + assertEquals(0, w.arrowCol()); + + // Check gridv2 + // It should be 1 row, containing "2TEST" -> but letters are mapped, digits are not explicitly in letterAt. + // Wait, look at exportFormatFromFilled logic: + // row.append(letterAt.getOrDefault(pack(r, c), '#')); + // letterAt only contains letters from placed words. + // arrow cells are NOT in letterAt unless they are also part of a word (unlikely). + // So (0,0) should be '#' + assertEquals(1, exported.gridv2().size()); + assertEquals("#TEST", exported.gridv2().get(0)); + } + + @Test + void testExportFormatEmpty() { + var swe = new SwedishGenerator(); + var grid = SwedishGenerator.makeEmptyGrid(); + var fillResult = new FillResult(true, grid, new HashMap<>(), null); + var puzzleResult = new PuzzleResult(swe, null, null, fillResult); + + ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 1, new Rewards(0,0,0)); + + assertNotNull(exported); + assertEquals(0, exported.words().length); + // Should return full grid with '#' + assertEquals(SwedishGenerator.R, exported.gridv2().size()); + for (String row : exported.gridv2()) { + assertEquals(SwedishGenerator.C, row.length()); + assertTrue(row.matches("#+")); + } + } +} diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 1777b94..02819b3 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -39,10 +39,10 @@ public class MainTest { assertEquals(1, slots.size()); var s = slots.get(0); assertEquals(8, s.len()); - assertEquals(0, s.r(0)); - assertEquals(1, s.c(0)); - assertEquals(0, s.r(1)); - assertEquals(2, s.c(1)); + assertEquals(0, SwedishGenerator.Grid.r(s.pos(0))); + assertEquals(1, SwedishGenerator.Grid.c(s.pos(0))); + assertEquals(0, SwedishGenerator.Grid.r(s.pos(1))); + assertEquals(2, SwedishGenerator.Grid.c(s.pos(1))); } @Test diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 8eee2b9..c7809ab 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -145,12 +145,12 @@ public class SwedishGeneratorTest { assertEquals(3, s.clueC()); assertEquals(5, s.dir()); assertFalse(s.horiz()); - assertEquals(2, s.r(0)); - assertEquals(3, s.r(1)); - assertEquals(4, s.r(2)); - assertEquals(5, s.c(0)); - assertEquals(5, s.c(1)); - assertEquals(5, s.c(2)); + assertEquals(2, Grid.r(s.pos(0))); + assertEquals(3, Grid.r(s.pos(1))); + assertEquals(4, Grid.r(s.pos(2))); + assertEquals(5, Grid.c(s.pos(0))); + assertEquals(5, Grid.c(s.pos(1))); + assertEquals(5, Grid.c(s.pos(2))); assertTrue(Slot.horiz(2)); // right assertFalse(Slot.horiz(3)); // down @@ -261,26 +261,27 @@ public class SwedishGeneratorTest { // Slot at (0,0) length 3, horizontal (right) // key = (r << 8) | (c << 4) | d. Here we just need a valid slot for placeWord. // r(i) and c(i) are used by placeWord. - var packedPos = ((long) Grid.offset(0, 0)) | (((long) Grid.offset(0, 1)) << 7) | (((long) Grid.offset(0, 2)) << 14); - var s = Slot.from(0, packedPos, 3); - var w1 = new Lemma("ABC", 1, "test"); - var undoBuffer = new long[10]; + var packedPos = ((long) Grid.offset(0, 0)) | (((long) Grid.offset(0, 1)) << 7) | (((long) Grid.offset(0, 2)) << 14); + var s = Slot.from(0, packedPos, 3); + var w1 = new Lemma("ABC", 1, "test"); + var undoBuffer = new int[10]; // 1. Successful placement in empty grid int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0); - assertEquals(3, placed); + assertEquals(1, placed); assertEquals('A', grid.getCharAt(0, 0)); assertEquals('B', grid.getCharAt(0, 1)); assertEquals('C', grid.getCharAt(0, 2)); + assertEquals(0b111L, undoBuffer[0]); // 2. Successful placement with partial overlap (same characters) - // Clear grid first or use another slot. Let's just verify it works if we place it again. - placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0); - assertEquals(0, placed); // 0 new characters placed + placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 1); + assertEquals(1, placed); + assertEquals(0L, undoBuffer[1]); // 0 new characters placed // 3. Conflict: place "ABD" where "ABC" is var w2 = new Lemma("ABD", 1, "conflict"); - placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 0); + placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2); assertEquals(-1, placed); // Verify grid is unchanged (still "ABC") assertEquals('A', grid.getCharAt(0, 0)); @@ -290,7 +291,7 @@ public class SwedishGeneratorTest { // 4. Partial placement then conflict (rollback) grid = SwedishGenerator.makeEmptyGrid(); grid.setCharAt(0, 2, 'X'); // Conflict at the end - placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0); + placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 3); assertEquals(-1, placed); // Verify grid is still empty (except for 'X') assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 0)); @@ -305,14 +306,15 @@ public class SwedishGeneratorTest { var packedPos = ((long) Grid.offset(0, 1)) | (((long) Grid.offset(0, 2)) << 7); var s = Slot.from((0 << 8) | (1 << 4) | 2, packedPos, 2); var w = new Lemma("AZ", 1, "A to Z"); - var undoBuffer = new long[10]; + var undoBuffer = new int[10]; var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0); - assertEquals(2, placed); + assertEquals(1, placed); assertEquals('A', grid.getCharAt(0, 1)); assertEquals('Z', grid.getCharAt(0, 2)); + assertEquals(0b11L, undoBuffer[0]); - SwedishGenerator.undoPlace(grid, undoBuffer, 0, placed); + SwedishGenerator.undoPlace(grid, s, undoBuffer[0]); assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 1)); assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 2)); }