From dfb4679da836399139436025fffab61c7c0d28fd Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 14 Jan 2026 12:28:07 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/Export.java | 4 +- src/main/java/puzzle/Main.java | 10 +-- src/main/java/puzzle/SwedishGenerator.java | 64 ++++++++++--------- src/test/java/puzzle/MainTest.java | 6 +- .../java/puzzle/SwedishGeneratorTest.java | 9 ++- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index da0ac60..56a8ab0 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -6,6 +6,7 @@ import lombok.experimental.Delegate; import lombok.val; import puzzle.Export.Gridded.Replacar.Cell; import puzzle.SwedishGenerator.Clues; +import puzzle.SwedishGenerator.DictEntry; import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.Grid; import java.util.ArrayList; @@ -197,10 +198,11 @@ public record Export() { var g = filled().grid(); var placed = new ArrayList(); var clueMap = filled().clueMap(); + val entries = new DictEntry[10]; mask.forEachSlot((int key, long lo, long hi) -> { var word = clueMap[key]; if (word != 0L) { - placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi), word)); + placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi, entries[Slot.length(lo, hi)]), word)); } else { System.err.println("Could not find clue for slot: " + key); } diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index 871bc3a..4e73e35 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -43,8 +43,8 @@ public class Main { @NoArgsConstructor public static class Opts { - public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis()); - public int pop = 18; + public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis()); + public int pop = 18; public int gens = 1200; public String wordsPath = "nl_score_hints_v3.csv"; public double minSimplicity = 0; // 0 means no limit @@ -358,10 +358,10 @@ public class Main { } static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) { TOTAL_ATTEMPTS.incrementAndGet(); - var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); - var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5))); + var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); + var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5))); - var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index()); + var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); 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 0ac976f..0ad4540 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -383,18 +383,17 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } } - static record Slot(int key, long lo, long hi) { + static record Slot(int key, long lo, long hi, DictEntry entry) { static final int BIT_FOR_DIR = 2; - static Slot from(int key, long lo, long hi) { return new Slot(key, lo, hi); } - - public int length() { return Long.bitCount(lo) + Long.bitCount(hi); } - public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; } - public static int dir(int key) { return key & 3; } - public static boolean increasing(int dir) { return (dir & 2) == 0; } - public IntStream walk() { return Gridded.walk((byte) key, lo, hi); } - public static boolean horiz(int d) { return (d & 1) != 0; } - public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; } + static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); } + public static int length(long lo, long hi) { return Long.bitCount(lo) + Long.bitCount(hi); } + public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; } + public static int dir(int key) { return key & 3; } + public static boolean increasing(int dir) { return (dir & 2) == 0; } + public IntStream walk() { return Gridded.walk((byte) key, lo, hi); } + public static boolean horiz(int d) { return (d & 1) != 0; } + public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; } } private static void processSlot(Clues grid, SlotVisitor visitor, int idx) { @@ -438,10 +437,10 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { visitor.visit(key, rayLo, rayHi); } - static Slot[] extractSlots(Clues grid) { + static Slot[] extractSlots(Clues grid, DictEntry[] index) { var slots = new Slot[grid.clueCount()]; int[] N = new int[]{ 0 }; - grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi)); + grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)])); return slots; } @@ -764,11 +763,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } return p; } - static int slotScore(byte[] count, Slot s) { + static int slotScore(byte[] count, long lo, long hi) { int cross = 0; - for (long b = s.lo; b != 0; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1); - for (long b = s.hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); - return cross * 10 + s.length(); + for (long b = lo; b != 0; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1); + for (long b = hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); + return cross * 10 + Slot.length(lo, hi); } static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) { final long glo = grid.lo, ghi = grid.hi; @@ -890,10 +889,13 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (long b = s.lo; b != 0; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++; for (long b = s.hi; b != 0; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++; } - for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]); + for (int i = 0; i < slots.length; i++) { + var slot = slots[i]; + slotScores[i] = slotScore(count, slot.lo, slot.hi); + } } - public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask, DictEntry[] dictIndex) { + public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask) { val multiThreaded = Thread.currentThread().getName().contains("pool"); val NO_LOG = (!Main.VERBOSE || multiThreaded); val grid = mask; @@ -943,7 +945,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var s = slots[i]; if (assigned[s.key] != X) continue; var pattern = patternForSlot(grid, s.key, s.lo, s.hi); - var index = dictIndex[s.length()]; + var index = s.entry; count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); if (count == 0) { @@ -965,7 +967,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { return; } var pattern = patternForSlot(grid, best.key, best.lo, best.hi); - var index = dictIndex[best.length()]; + var index = best.entry; current = CARRIER; current.slot = best; current.count = index.length; @@ -992,10 +994,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { lastMRV = pick.count; if (!NO_LOG) renderProgress(); - val s = pick.slot; - val k = s.key; - val entry = dictIndex[s.length()]; - + val s = pick.slot; + val k = s.key; + val slo = s.lo; + val shi = s.hi; + val entry = s.entry; + long low, top; if (info != null && info.length > 0) { var idxs = info; var L = idxs.length; @@ -1009,9 +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; - val low = grid.lo; - val top = grid.hi; - if (!placeWord(grid, k, s.lo, s.hi, w)) continue; + low = grid.lo; + top = grid.hi; + if (!placeWord(grid, k, slo, shi, w)) continue; used.set(lemIdx); assigned[k] = w; @@ -1036,9 +1040,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; - val low = grid.lo; - val top = grid.hi; - if (!placeWord(grid, k, s.lo, s.hi, w)) continue; + low = grid.lo; + top = grid.hi; + if (!placeWord(grid, k, slo, shi, w)) continue; used.set(lemIdx); assigned[k] = w; diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index fb5d428..def2413 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -55,10 +55,10 @@ public class MainTest { grid.setLetterLo(OFF_0_1, LETTER_A); grid.setLetterLo(OFF_0_2, LETTER_B); - var slots = extractSlots(clues); + var slots = extractSlots(clues, dict.index()); assertEquals(1, slots.length); var s = slots[0]; - assertEquals(8, s.length()); + assertEquals(8, Slot.length(s.lo(), s.hi())); var cells = s.walk().toArray(); assertEquals(0, SwedishGenerator.IT[cells[0]].r()); assertEquals(1, SwedishGenerator.IT[cells[0]].c()); @@ -184,7 +184,7 @@ public class MainTest { 128L, 422762372923520L, 192L); - var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index()); + var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed"); Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282])); diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index ff63cfd..b43f1e1 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -280,12 +280,12 @@ public class SwedishGeneratorTest { // This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2) var clues = Clues.createEmpty(); clues.setClue(OFF_0_0, CLUE_RIGHT); - - var slots = extractSlots(clues); + var dict = new Dict(WORDS2); + var slots = extractSlots(clues, dict.index()); assertEquals(1, slots.length); var s = slots[0]; - assertTrue(s.length() >= 2); + assertTrue(Slot.length(s.lo(), s.hi()) >= 2); assertEquals(OFF_0_0, Slot.clueIndex(s.key())); assertEquals(CLUE_RIGHT, Slot.dir(s.key())); } @@ -396,10 +396,9 @@ public class SwedishGeneratorTest { counts[2] = 3; var dict = new Dict(WORDS); var entry5 = dict.index()[5]; - var sScore = Slot.from(0, (1L << 1) | (1L << 2), 0L); // cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3 // score = 3 * 10 + len(2) = 32 - assertEquals(32, slotScore(counts, sScore)); + assertEquals(32, slotScore(counts, (1L << 1) | (1L << 2), 0L)); // 3. Test candidateCountForPattern var ctx = Context.get();