From eeae90a886f060c2004ec57e6dbd1a5a13588053 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 14 Jan 2026 03:46:36 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/Export.java | 4 ++-- src/main/java/puzzle/Main.java | 2 +- src/main/java/puzzle/SwedishGenerator.java | 15 +++++++-------- src/test/java/puzzle/ExportFormatTest.java | 12 ++++++------ src/test/java/puzzle/MainTest.java | 6 ++---- src/test/java/puzzle/SwedishGeneratorTest.java | 2 +- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index cf3ce54..5d2a502 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -38,7 +38,7 @@ public record Export() { } } - public record Clued(Clues mask) { + public record Clued(@Delegate Clues mask) { String gridToString() { var sb = new StringBuilder(); @@ -190,7 +190,7 @@ public record Export() { var g = filled().grid(); var placed = new ArrayList(); var clueMap = filled().clueMap(); - g.grid().forEachSlot((int key, long lo, long hi) -> { + mask.forEachSlot((int key, long lo, long hi) -> { var word = clueMap[key]; if (word != 0L) { placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi), word)); diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index a22b750..b0fe7f3 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -359,7 +359,7 @@ public class Main { val stack = new int[STACK_SIZE]; var swe = new SwedishGenerator(rng, stack); var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5))); - var filled = fillMask(rng, mask.toGrid(), dict.index()); + var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index()); TOTAL_NODES.addAndGet(filled.stats().nodes); TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks); diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index ab7a94c..ae8d2f1 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -272,6 +272,10 @@ public record SwedishGenerator(Rng rng, int[] stack) { } return grid; } + public void forEachSlot(SlotVisitor visitor) { + for (var l = lo; l != X; l &= l - 1) processSlot(this, visitor, Long.numberOfTrailingZeros(l)); + for (var h = hi; h != X; h &= h - 1) processSlot(this, visitor, 64 | Long.numberOfTrailingZeros(h)); + } } static record Grid(byte[] g, long lo, long hi) { @@ -291,10 +295,6 @@ public record SwedishGenerator(Rng rng, int[] stack) { boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); } - void forEachSlot(SlotVisitor visitor) { - for (var l = lo; l != X; l &= l - 1) processSlot(this, visitor, Long.numberOfTrailingZeros(l)); - for (var h = hi; h != X; h &= h - 1) processSlot(this, visitor, 64 | Long.numberOfTrailingZeros(h)); - } void undoPlace(long maskLo, long maskHi) { for (long b = maskLo; b != 0; b &= b - 1) clearletter(Long.numberOfTrailingZeros(b)); for (long b = maskHi; b != 0; b &= b - 1) clearletter(64 | Long.numberOfTrailingZeros(b)); @@ -401,7 +401,7 @@ public record SwedishGenerator(Rng rng, int[] stack) { public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; } } - private static void processSlot(Grid grid, SlotVisitor visitor, int idx) { + private static void processSlot(Clues grid, SlotVisitor visitor, int idx) { int key = Slot.packSlotDir(idx, grid.digitAt(idx)); // 0..3 long rayLo = PATH_LO[key]; @@ -443,7 +443,7 @@ public record SwedishGenerator(Rng rng, int[] stack) { //if ((rayLo | rayHi) == 0L) throw new RuntimeException() } - static Slot[] extractSlots(Grid grid) { + static Slot[] extractSlots(Clues grid) { var slots = new Slot[grid.clueCount()]; int[] N = new int[]{ 0 }; grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi)); @@ -910,7 +910,7 @@ public record SwedishGenerator(Rng rng, int[] stack) { for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]); } - public static FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex) { + public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask, DictEntry[] dictIndex) { val multiThreaded = Thread.currentThread().getName().contains("pool"); val NO_LOG = (!Main.VERBOSE || multiThreaded); val grid = mask; @@ -919,7 +919,6 @@ public record SwedishGenerator(Rng rng, int[] stack) { val bitset = new long[2500]; val undo = new long[64]; - val slots = extractSlots(grid); val TOTAL = slots.length; val slotScores = new int[TOTAL]; diff --git a/src/test/java/puzzle/ExportFormatTest.java b/src/test/java/puzzle/ExportFormatTest.java index eb0db2b..ba023d4 100644 --- a/src/test/java/puzzle/ExportFormatTest.java +++ b/src/test/java/puzzle/ExportFormatTest.java @@ -2,6 +2,7 @@ package puzzle; import lombok.val; import org.junit.jupiter.api.Test; +import puzzle.Export.Clued; import puzzle.Export.Gridded; import puzzle.Export.Placed; import puzzle.Export.Rewards; @@ -27,7 +28,7 @@ public class ExportFormatTest { @Test void testExportFormatFromFilled() { - var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]); + var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]); val clues = Clues.createEmpty(); // Place a RIGHT clue at (0,0) @@ -35,8 +36,7 @@ public class ExportFormatTest { // This creates a slot starting at (0,1) // Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH clues.setClue(Grid.offset(0, 5), CLUE_LEFT); - var grid = clues.toGrid(); - + var grid = clues.toGrid(); var clueMap = new long[300]; // key = (cellIndex << 2) | (direction) @@ -48,10 +48,9 @@ public class ExportFormatTest { grid.setLetter(Grid.offset(0, 2), (byte) 'E'); grid.setLetter(Grid.offset(0, 3), (byte) 'S'); grid.setLetter(Grid.offset(0, 4), (byte) 'T'); - var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats(0, 0, 0, 0)); - var puzzleResult = new PuzzleResult(swe, null, null, fillResult); + var puzzleResult = new PuzzleResult(swe, null, new Clued(clues), fillResult); var rewards = new Rewards(10, 5, 1); var exported = puzzleResult.exportFormatFromFilled(2, rewards); @@ -90,8 +89,9 @@ public class ExportFormatTest { void testExportFormatEmpty() { var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]); var grid = Grid.createEmpty(); + val clues = Clues.createEmpty(); var fillResult = new FillResult(true, new Gridded(grid), new long[300], new FillStats(0, 0, 0, 0)); - var puzzleResult = new PuzzleResult(swe, null, null, fillResult); + var puzzleResult = new PuzzleResult(swe, null, new Clued(clues), fillResult); var exported = puzzleResult.exportFormatFromFilled(1, new Rewards(0, 0, 0)); diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index dcdfd0b..a1acc16 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -40,7 +40,7 @@ public class MainTest { grid.setLetter(OFF_0_1, LETTER_A); grid.setLetter(OFF_0_2, LETTER_B); - var slots = extractSlots(grid); + var slots = extractSlots(clues); assertEquals(1, slots.length); var s = slots[0]; assertEquals(8, s.length()); @@ -64,10 +64,8 @@ public class MainTest { void testForEachSlot() { var clues = Clues.createEmpty(); clues.setClue(OFF_0_0, CLUE_RIGHT); - var grid = clues.toGrid(); - var count = new AtomicInteger(0); - grid.forEachSlot((key, lo, hi) -> { + clues.forEachSlot((key, lo, hi) -> { count.incrementAndGet(); assertEquals(8, Long.bitCount(lo) + Long.bitCount(hi)); assertEquals(0, Grid.r(Long.numberOfTrailingZeros(lo))); diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 6181c65..c64ac36 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -258,7 +258,7 @@ public class SwedishGeneratorTest { var grid = clues.toGrid(); - var slots = extractSlots(grid); + var slots = extractSlots(clues); assertEquals(1, slots.length); var s = slots[0];