From a269584ad6fffedc55f64e8d65bbf19b0969ec20 Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 9 Jan 2026 00:39:43 +0100 Subject: [PATCH] Gather data --- src/main/java/puzzle/ExportFormat.java | 37 +++++++--------- src/main/java/puzzle/SwedishGenerator.java | 42 ++++++++++++++---- .../java/puzzle/SwedishGeneratorTest.java | 43 +++++++++++++++++++ 3 files changed, 93 insertions(+), 29 deletions(-) diff --git a/src/main/java/puzzle/ExportFormat.java b/src/main/java/puzzle/ExportFormat.java index 186617e..3c5713a 100644 --- a/src/main/java/puzzle/ExportFormat.java +++ b/src/main/java/puzzle/ExportFormat.java @@ -1,6 +1,7 @@ package puzzle; import puzzle.Main.PuzzleResult; +import puzzle.SwedishGenerator.Grid; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -78,11 +79,11 @@ public record ExportFormat() { int maxR = Integer.MIN_VALUE, maxC = Integer.MIN_VALUE; for (var rc : placed) { - for (var r : rc.cells) { - minR = Math.min(minR, r[0]); - minC = Math.min(minC, r[1]); - maxR = Math.max(maxR, r[0]); - maxC = Math.max(maxC, r[1]); + for (var c : rc.cells) { + minR = Math.min(minR, Grid.r(c)); + minC = Math.min(minC, Grid.c(c)); + maxR = Math.max(maxR, Grid.r(c)); + maxC = Math.max(maxC, Grid.c(c)); } minR = Math.min(minR, rc.arrowRow); minC = Math.min(minC, rc.arrowCol); @@ -93,8 +94,8 @@ public record ExportFormat() { // 3) map of only used letter cells (everything else becomes '#') var letterAt = new HashMap(); for (var p : placed) { - for (var rc : p.cells) { - int rr = rc[0], cc = rc[1]; + for (var c : p.cells) { + int rr = Grid.r(c), cc = Grid.c(c); if (inBounds(rr, cc) && g.isLetterAt(rr, cc)) { letterAt.put(pack(rr, cc), g.getCharAt(rr, cc)); } @@ -134,40 +135,34 @@ public record ExportFormat() { int c = s.clueC(); int d = s.dir(); - int[][] cells = new int[s.len()][]; + int[] cells = new int[s.len()]; for (int i = 0; i < s.len(); i++) { - cells[i] = new int[]{ s.r(i), s.c(i) }; + cells[i] = s.pos(i); } // Canonicalize: always output right/down int startRow, startCol, arrowRow, arrowCol; String direction; boolean isReversed = false; - + startRow = Grid.r(cells[0]); + startCol = Grid.c(cells[0]); if (d == 2) { // right -> horizontal direction = HORIZONTAL; - startRow = cells[0][0]; - startCol = cells[0][1]; - arrowRow = r; - arrowCol = c; + + arrowRow = r; + arrowCol = c; } else if (d == 3 || d == 5) { // down or down-bent -> vertical direction = VERTICAL; - startRow = cells[0][0]; - startCol = cells[0][1]; arrowRow = r; arrowCol = c; } else if (d == 4) { // left -> horizontal (REVERSED) direction = HORIZONTAL; isReversed = true; - startRow = cells[0][0]; - startCol = cells[0][1]; arrowRow = r; arrowCol = c; } else if (d == 1) { // up -> vertical (REVERSED) direction = VERTICAL; isReversed = true; - startRow = cells[0][0]; - startCol = cells[0][1]; arrowRow = r; arrowCol = c; } else { @@ -187,7 +182,7 @@ public record ExportFormat() { } private static long pack(int r, int c) { return (((long) r) << 32) ^ (c & 0xFFFFFFFFL); } - private record Placed(Lemma lemma, int startRow, int startCol, String direction, int arrowRow, int arrowCol, int[][] cells, boolean isReversed) { } + private record Placed(Lemma lemma, int startRow, int startCol, String direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) { } public record Rewards(int coins, int stars, int hints) { } diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 60a2d0f..4d6dc5a 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -125,9 +125,13 @@ public record SwedishGenerator(int[] buff) { static int offset(int r, int c) { return r | (c << 3); } Grid deepCopyGrid() { return new Grid(g.clone()); } char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); } + char getCharAt(int pos) { return (char) (g[pos]); } int digitAt(int r, int c) { return g[offset(r, c)] - 48; } byte byteAt(int r, int c) { return g[offset(r, c)]; } void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; } + void setCharAt(int idx, char ch) { g[idx] = (byte) ch; } + void clear(int r, int c) { g[offset(r, c)] = 0; } + void clear(int idx) { g[idx] = 0; } boolean isLetterAt(int r, int c) { return ((g[offset(r, c)] & 64) != 0); } boolean isDigitAt(int r, int c) { return (g[offset(r, c)] & 48) == 48; } boolean isDigitAt(int index) { return (g[index] & 48) == 48; } @@ -339,6 +343,7 @@ public record SwedishGenerator(int[] buff) { 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); } @@ -346,7 +351,7 @@ public record SwedishGenerator(int[] buff) { static void undoPlace(Grid grid, long[] undoBuffer, int offset, int n) { for (var i = 0; i < n; i++) { long v = undoBuffer[offset + i]; - grid.setCharAt((int) (v >> 16) & 0xFF, (int) (v >> 8) & 0xFF, (char) (v & 0xFF)); + grid.clear((int)v); } } @FunctionalInterface @@ -724,32 +729,53 @@ public record SwedishGenerator(int[] buff) { static void patternForSlot(Grid grid, Slot s, char[] pat) { for (var i = 0; i < s.len(); i++) { - var ch = grid.getCharAt(s.r(i), s.c(i)); + var ch = grid.getCharAt(s.pos(i)); pat[i] = isLetter(ch) ? ch : C_DASH; } } static int slotScore(int[] cellCount, Slot s, Grid grid) { var cross = 0; - for (var i = 0; i < s.len(); i++) cross += (cellCount[Grid.offset(s.r(i), s.c(i))] - 1); + for (var i = 0; i < s.len(); i++) cross += (cellCount[s.pos(i)] - 1); return cross * 10 + s.len(); } - - static int placeWord(Grid grid, Slot s, Lemma w, long[] undoBuffer, int offset) { + 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) | (long) 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.setCharAt((int) (v >> 16) & 0xFF, (int) (v >> 8) & 0xFF, (char) (v & 0xFF)); + 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; + 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++; + grid.setCharAt(idx, ch); + } else { + if (cur != ch) { + for (var j = 0; j < n; j++) { + long v = undoBuffer[offset + j]; + grid.clear((int) v); } return -1; } @@ -770,7 +796,7 @@ public record SwedishGenerator(int[] buff) { var ctx = CTX.get(); var cellCount = ctx.cellCount; Arrays.fill(cellCount, 0, SIZE, 0); - for (var s : slots) for (var i = 0; i < s.len(); i++) cellCount[Grid.offset(s.r(i), s.c(i))]++; + for (var s : slots) for (var i = 0; i < s.len(); i++) cellCount[s.pos(i)]++; var t0 = System.currentTimeMillis(); final var lastLog = new AtomicLong(t0); diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index adf93d3..8eee2b9 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -255,6 +255,49 @@ public class SwedishGeneratorTest { assertNotNull(g4); } + @Test + void testPlaceWord() { + var grid = SwedishGenerator.makeEmptyGrid(); + // 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]; + + // 1. Successful placement in empty grid + int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0); + assertEquals(3, placed); + assertEquals('A', grid.getCharAt(0, 0)); + assertEquals('B', grid.getCharAt(0, 1)); + assertEquals('C', grid.getCharAt(0, 2)); + + // 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 + + // 3. Conflict: place "ABD" where "ABC" is + var w2 = new Lemma("ABD", 1, "conflict"); + placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 0); + assertEquals(-1, placed); + // Verify grid is unchanged (still "ABC") + assertEquals('A', grid.getCharAt(0, 0)); + assertEquals('B', grid.getCharAt(0, 1)); + assertEquals('C', grid.getCharAt(0, 2)); + + // 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); + assertEquals(-1, placed); + // Verify grid is still empty (except for 'X') + assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 0)); + assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 1)); + assertEquals('X', grid.getCharAt(0, 2)); + } + @Test void testBacktrackingHelpers() { var grid = SwedishGenerator.makeEmptyGrid();