From c1706e1bf726b90888ff59ae9f2f5e4f727700e7 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 14 Jan 2026 11:03:28 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/Export.java | 2 +- src/main/java/puzzle/SwedishGenerator.java | 49 +++++-------------- src/test/java/puzzle/ExportFormatTest.java | 8 +-- src/test/java/puzzle/MainTest.java | 10 ++-- .../java/puzzle/SwedishGeneratorTest.java | 38 +++++++------- 5 files changed, 42 insertions(+), 65 deletions(-) diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 7125dc5..ac34f82 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -120,7 +120,7 @@ public record Export() { var offset = Grid.offset(r, c); if (clues.isClue(offset)) sb.append((char) (48 | clues.digitAt(offset))); - else if (grid.lisLetterAtLo(offset)) + else if (grid.lisLetterAt(offset)) sb.append((char) (64 | grid.letter32At(offset))); else sb.append(' '); diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index ab1303a..870c88e 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -303,30 +303,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { g[idx] = ch; } - void setLetter(int idx, byte ch) { - if ((idx & 64) == 0) - setLetterLo(idx, ch); - else - setLetterHi(idx, ch); - } - - private void clearletterLo(int idx) { - g[idx] = DASH; - - lo &= ~(1L << idx); - } - private void clearletterHi(int idx) { - g[idx] = DASH; - - hi &= ~(1L << (idx & 63)); - } - void undoPlace(long maskLo, long maskHi) { - lo &= ~maskLo; - hi &= ~maskHi; - - /*for (long b = maskLo; b != 0; b &= b - 1) clearletterLo(Long.numberOfTrailingZeros(b)); - for (long b = maskHi; b != 0; b &= b - 1) clearletterHi(64 | Long.numberOfTrailingZeros(b));*/ - } } static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { } @@ -799,7 +775,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (long b = s.hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); return cross * 10 + s.length(); } - static boolean placeWord(Grid grid, Slot s, long w, long[] undoBuffer, int offset) { + static boolean placeWord(Grid grid, Slot s, long w) { if (s.increasing()) { for (long b = s.lo & grid.lo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); @@ -824,8 +800,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { grid.lo |= maskLo; grid.hi |= maskHi; } - undoBuffer[offset << 1] = maskLo; - undoBuffer[(offset << 1) | 1] = maskHi; } else { int bcHi = Long.bitCount(s.hi); for (long b = s.hi & grid.hi; b != 0; b &= b - 1) { @@ -850,8 +824,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { grid.lo |= maskLo; grid.hi |= maskHi; } - undoBuffer[offset << 1] = maskLo; - undoBuffer[(offset << 1) | 1] = maskHi; } return true; } @@ -932,7 +904,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { val used = new Bit1029(); val assigned = new long[CLUE_INDEX_MAX_SIZE]; val bitset = new long[2500]; - val undo = new long[64]; val TOTAL = slots.length; val slotScores = new int[TOTAL]; @@ -1042,8 +1013,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var w = entry.words[idx]; var lemIdx = Lemma.unpackIndex(w); if (used.get(lemIdx)) continue; - - if (!placeWord(grid, s, w, undo, depth)) continue; + val low = grid.lo; + val top = grid.hi; + if (!placeWord(grid, s, w)) continue; used.set(lemIdx); assigned[k] = w; @@ -1052,7 +1024,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { assigned[k] = X; used.clear(lemIdx); - grid.undoPlace(undo[depth << 1], undo[(depth << 1) | 1]); + //grid.undoPlace(undo[depth << 1], undo[(depth << 1) | 1]); + grid.lo = low; + grid.hi = top; } backtracks++; return false; @@ -1067,8 +1041,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var w = entry.words[idxInArray]; var lemIdx = Lemma.unpackIndex(w); if (used.get(lemIdx)) continue; - - if (!placeWord(grid, s, w, undo, depth)) continue; + val low = grid.lo; + val top = grid.hi; + if (!placeWord(grid, s, w)) continue; used.set(lemIdx); assigned[k] = w; @@ -1077,7 +1052,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { assigned[k] = X; used.clear(lemIdx); - grid.undoPlace(undo[depth << 1], undo[(depth << 1) | 1]); + grid.lo = low; + grid.hi = top; + //grid.undoPlace(undo[depth << 1], undo[(depth << 1) | 1]); } backtracks++; diff --git a/src/test/java/puzzle/ExportFormatTest.java b/src/test/java/puzzle/ExportFormatTest.java index cedb914..97af5e8 100644 --- a/src/test/java/puzzle/ExportFormatTest.java +++ b/src/test/java/puzzle/ExportFormatTest.java @@ -45,10 +45,10 @@ public class ExportFormatTest { clueMap[key] = SwedishGeneratorTest.TEST; // Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4) - grid.setLetter(Grid.offset(0, 1), LETTER_T); - grid.setLetter(Grid.offset(0, 2), LETTER_E); - grid.setLetter(Grid.offset(0, 3), LETTER_S); - grid.setLetter(Grid.offset(0, 4), LETTER_T); + grid.setLetterLo(OFF_0_1, LETTER_T); + grid.setLetterLo(OFF_0_2, LETTER_E); + grid.setLetterLo(OFF_0_3, LETTER_S); + grid.setLetterLo(OFF_0_4, LETTER_T); var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats(0, 0, 0, 0)); var puzzleResult = new PuzzleResult(new Clued(clues), fillResult); diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 4a7f745..1710a3a 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -31,6 +31,8 @@ public class MainTest { static final int OFF_0_0 = Grid.offset(0, 0); static final int OFF_0_1 = Grid.offset(0, 1); static final int OFF_0_2 = Grid.offset(0, 2); + static final int OFF_0_3 = Grid.offset(0, 3); + static final int OFF_0_4 = Grid.offset(0, 4); static final int OFF_1_1 = Grid.offset(1, 1); static final int OFF_1_2 = Grid.offset(1, 2); static final int OFF_2_3 = Grid.offset(2, 3); @@ -50,8 +52,8 @@ public class MainTest { var clues = Clues.createEmpty(); clues.setClue(OFF_0_0, CLUE_RIGHT); var grid = clues.toGrid(); - grid.setLetter(OFF_0_1, LETTER_A); - grid.setLetter(OFF_0_2, LETTER_B); + grid.setLetterLo(OFF_0_1, LETTER_A); + grid.setLetterLo(OFF_0_2, LETTER_B); var slots = extractSlots(clues); assertEquals(1, slots.length); @@ -100,8 +102,8 @@ public class MainTest { var grid = clues.toGrid(); // Test set/get - grid.setLetter(OFF_0_0, LETTER_A); - grid.setLetter(OFF_2_3, LETTER_Z); + grid.setLetterLo(OFF_0_0, LETTER_A); + grid.setLetterLo(OFF_2_3, LETTER_Z); Assertions.assertEquals(LETTER_A, grid.letter32At(OFF_0_0)); Assertions.assertEquals(CLUE_UP, clues.digitAt(OFF_1_2)); diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 5efdf53..61da43e 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -78,9 +78,9 @@ public class SwedishGeneratorTest { @Test void testPatternForSlotAllLetters() { var grid = createEmpty(); - grid.setLetter(0, LETTER_A); - grid.setLetter(1, LETTER_B); - grid.setLetter(2, LETTER_C); + grid.setLetterLo(0, LETTER_A); + grid.setLetterLo(1, LETTER_B); + grid.setLetterLo(2, LETTER_C); var slot = Slot.from(18 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); var pattern = patternForSlot(grid, slot); @@ -90,8 +90,8 @@ public class SwedishGeneratorTest { @Test void testPatternForSlotMixed() { var grid = createEmpty(); - grid.setLetter(0, LETTER_A); - grid.setLetter(2, LETTER_C); + grid.setLetterLo(OFF_0_0, LETTER_A); + grid.setLetterLo(2, LETTER_C); var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); var pattern = patternForSlot(grid, slot); @@ -110,7 +110,7 @@ public class SwedishGeneratorTest { @Test void testPatternForSlotSingleLetter() { var grid = createEmpty(); - grid.setLetter(0, LETTER_A); + grid.setLetterLo(OFF_0_0, LETTER_A); var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); var pattern = patternForSlot(grid, slot); @@ -137,7 +137,7 @@ public class SwedishGeneratorTest { @Test void testGrid() { var grid = createEmpty(); - grid.setLetter(OFF_0_0, LETTER_A); + grid.setLetterLo(OFF_0_0, LETTER_A); assertEquals(LETTER_A, grid.letter32At(OFF_0_0)); } @@ -334,22 +334,19 @@ public class SwedishGeneratorTest { var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2); var s = Slot.from(key, lo, 0L); var w1 = ABC; - var undoBuffer = new long[10]; // 1. Successful placement in empty grid - assertTrue(placeWord(grid, s, w1, undoBuffer, 0)); + assertTrue(placeWord(grid, s, w1)); assertEquals(LETTER_A, grid.letter32At(OFF_0_0)); assertEquals(LETTER_B, grid.letter32At(OFF_0_1)); assertEquals(LETTER_C, grid.letter32At(OFF_0_2)); - assertEquals(lo, undoBuffer[0]); // 2. Successful placement with partial overlap (same characters) - assertTrue(placeWord(grid, s, w1, undoBuffer, 1)); - assertEquals(0L, undoBuffer[2]); // 0 new characters placed + assertTrue(placeWord(grid, s, w1)); // 3. Conflict: place "ABD" where "ABC" is var w2 = ABD; - assertFalse(placeWord(grid, s, w2, undoBuffer, 2)); + assertFalse(placeWord(grid, s, w2)); // Verify grid is unchanged (still "ABC") assertEquals(LETTER_A, grid.letter32At(OFF_0_0)); assertEquals(LETTER_B, grid.letter32At(OFF_0_1)); @@ -357,8 +354,8 @@ public class SwedishGeneratorTest { // 4. Partial placement then conflict (rollback) grid = createEmpty(); - grid.setLetter(OFF_0_2, LETTER_X); // Conflict at the end - assertFalse(placeWord(grid, s, w1, undoBuffer, 3)); + grid.setLetterLo(OFF_0_2, LETTER_X); // Conflict at the end + assertFalse(placeWord(grid, s, w1)); // Verify grid is still empty (except for 'X') assertFalse(grid.lisLetterAtLo(OFF_0_0)); assertFalse(grid.lisLetterAtLo(OFF_0_1)); @@ -373,15 +370,16 @@ public class SwedishGeneratorTest { var lo = (1L << OFF_0_1) | (1L << OFF_0_2); var s = Slot.from(key, lo, 0L); var w = AZ; - var undoBuffer = new long[10]; - - var placed = placeWord(grid, s, w, undoBuffer, 0); + val low = grid.lo; + val top = grid.hi; + var placed = placeWord(grid, s, w); assertTrue(placed); assertEquals(LETTER_A, grid.letter32At(OFF_0_1)); assertEquals(LETTER_Z, grid.letter32At(OFF_0_2)); - assertEquals(lo, undoBuffer[0]); - grid.undoPlace(undoBuffer[0], undoBuffer[1]); + grid.hi = top; + grid.lo = low; + //grid.undoPlace(undoBuffer[0], undoBuffer[1]); assertFalse(grid.lisLetterAtLo(OFF_0_1)); assertFalse(grid.lisLetterAtLo(OFF_0_2)); }