From 5d186ae0baf28977e4c2a51c9c18bac509c0c9f8 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 19 Jan 2026 19:11:31 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/Export.java | 1 - src/main/java/puzzle/SwedishGenerator.java | 139 +++++++----------- src/test/java/puzzle/MainTest.java | 3 +- .../java/puzzle/SwedishGeneratorTest.java | 2 + 4 files changed, 56 insertions(+), 89 deletions(-) diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 404445f..3515dad 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -332,7 +332,6 @@ public record Export() { for (long w = lemma & Lemma.LETTER_MASK; w != 0; w >>>= 5, i++) { pos[i][(int) ((w & 31) - 1)].add(idx); } - // for (i = 0; i < L; i++) entry.pos()[i][Lemma.byteAt(lemma, i) - 1].add(idx); } for (int i = 2; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i); return new Dict(Arrays.stream(index).map(i -> { diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 7e2246a..39c2157 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -43,10 +43,10 @@ public record SwedishGenerator() { public static final int MIN_LEN = 3;//Config.MIN_LEN; public static final int MAX_TRIES_PER_SLOT = 700;//Config.MAX_TRIES_PER_SLOT; public static final int STACK_SIZE = 128; - public static final char C_DASH = '\0'; - public static final byte DASH = (byte) C_DASH; public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L; public static final long RANGE_0_624 = 624L - 0L + 1L; + static final int PICK_NOT_DONE = -1; + static final int PICK_DONE = 0; public static boolean isLo(int n) { return (n & 64) == 0; } interface Bit1029 { @@ -57,40 +57,18 @@ public record SwedishGenerator() { static void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); } } - @AllArgsConstructor - public static class Pick { - - public Slotinfo slot; - public int[] indices; - public int count; - } - //@formatter:off - public static record Dict(DictEntry[] index, int length) { } - @AllArgsConstructor @NoArgsConstructor static class Assign { long w; } + @AllArgsConstructor @NoArgsConstructor static final class Assign { long w; } public static final class FillStats { public double simplicity; } + @AllArgsConstructor public static final class Grid { public final byte[] g; public long lo, hi; } + public static record Dict(DictEntry[] index, int length) { } + public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { } + public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { } //@formatter:on - @AllArgsConstructor - public static class Grid { - - public final byte[] g; - public long lo, hi; - } - public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { } - - public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { } - - public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSETS_D_IDX; - public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO; - public static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI; - - public static final long[] PATH_LO = Neighbors9x8.PATH_LO; - public static final long[] PATH_HI = Neighbors9x8.PATH_HI; - - public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true); - - public static final Pick PICK_NOT_DONE = new Pick(null, null, 0); + public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSETS_D_IDX; + public static final long[] PATH_LO = Neighbors9x8.PATH_LO; + public static final long[] PATH_HI = Neighbors9x8.PATH_HI; public static final class Rng { @@ -111,12 +89,11 @@ public record SwedishGenerator() { public byte randint2bitByte() { int r = nextU32() & 7; if (r == 4) return (byte) 4; - //if (r < 4) return (byte) r; return (byte) (r & 3); } - public T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; } - public T rand(List p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); } - public int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max /*- 0L*/ /*+ 1L*/))); } + public T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length)))]; } + public T rand(List p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size())))); } + public int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max))); } public int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); } public int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); } public double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; } @@ -140,9 +117,7 @@ public record SwedishGenerator() { static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); } public static String asWord(long word, byte[] bytes) { int bi = 0; - for (long w = word & LETTER_MASK; w != 0; w >>>= 5) { - bytes[bi++] = (byte) ((w & 31) | 64); // neem laagste 5 bits - } + for (long w = word & LETTER_MASK; w != 0; w >>>= 5) bytes[bi++] = (byte) ((w & 31) | 64); return new String(bytes, 0, bi, US_ASCII); } static int unpackIndex(long w) { return (int) (w >>> 40); } @@ -164,7 +139,7 @@ public record SwedishGenerator() { lo |= slot.lo; hi |= slot.hi; } - return new Grid(new byte[SIZE], ~lo, ~hi /*& 0xFF*/); + return new Grid(new byte[SIZE], ~lo, ~hi); } } @@ -229,19 +204,18 @@ public record SwedishGenerator() { public static FillResult fillMask(final Rng rng, final Slotinfo[] slots, final Grid grid) { - val used = new long[2048]; - val bitset = new long[2500]; - val g = grid.g; - val TOTAL = slots.length; - val t0 = System.currentTimeMillis(); - val CARRIER = new Pick(null, null, 0); + val used = new long[2048]; + val bitset = new long[2500]; + val g = grid.g; + val TOTAL = slots.length; + val t0 = System.currentTimeMillis(); class Solver { long nodes; long backtracks; - int lastMRV; - long lastLog = t0, glo = grid.lo, ghi = grid.hi; - Pick current = CARRIER; + long glo = grid.lo, ghi = grid.hi; + Slotinfo currentSlot; + int[] currentIndices; boolean placeWordDec(final long lo, final long hi, final long w) { int idx; int bcHi = bitCount(hi); @@ -274,19 +248,17 @@ public record SwedishGenerator() { } return true; } - void chooseMRV() { - Slotinfo best = null; - for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) { + int chooseMRV() { + Slotinfo best = null; + int count2 = -1, bestScore = -1; + for (int i = 0, n = TOTAL; i < n; i++) { var s = slots[i]; if (s.assign.w != X) continue; var pattern = patternForSlot(glo, ghi, g, s.key, s.lo, s.hi); var index = s.entry; - count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); + int count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); - if (count == 0) { - current = PICK_NOT_DONE; - return; - } + if (count == 0) return PICK_NOT_DONE; if (best == null || count < count2 || (count == count2 && s.score > bestScore)) { @@ -296,36 +268,30 @@ public record SwedishGenerator() { if (count <= 1) break; } } - if (best == null) { - current = PICK_DONE; - return; - } + if (best == null) return PICK_DONE; var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi); - var index = best.entry; - current = CARRIER; - current.slot = best; - current.count = index.length; - current.indices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong); + currentSlot = best; + var index = best.entry; + currentIndices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong); + return 1; } boolean backtrack(int depth) { if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false; nodes++; - chooseMRV(); - var pick = current; - if (pick == PICK_DONE) return true; - if (pick.slot == null) { + int status = chooseMRV(); + if (status == PICK_DONE) return true; + if (status == PICK_NOT_DONE) { backtracks++; return false; } - val info = pick.indices; - lastMRV = pick.count; - - val s = pick.slot; - val inc = Slotinfo.increasing(s.key); - val slo = s.lo; - val shi = s.hi; - val entry = s.entry; + val info = currentIndices; + val s = currentSlot; + val inc = Slotinfo.increasing(s.key); + val slo = s.lo; + val shi = s.hi; + val assign = s.assign; + val words = s.entry.words; long low, top; if (info != null && info.length > 0) { var idxs = info; @@ -333,7 +299,7 @@ public record SwedishGenerator() { var tries = Math.min(MAX_TRIES_PER_SLOT, L); for (var t = 0; t < tries; t++) { - var w = entry.words[idxs[rng.biasedIndexPow3(L - 1)]]; + var w = words[idxs[rng.biasedIndexPow3(L - 1)]]; var lemIdx = Lemma.unpackIndex(w); if (Bit1029.get(used, lemIdx)) continue; low = glo; @@ -345,9 +311,9 @@ public record SwedishGenerator() { } Bit1029.set(used, lemIdx); - s.assign.w = w; + assign.w = w; if (backtrack(depth + 1)) return true; - s.assign.w = X; + assign.w = X; Bit1029.clear(used, lemIdx); glo = low; ghi = top; @@ -356,11 +322,11 @@ public record SwedishGenerator() { return false; } - var N = entry.words.length; + var N = words.length; var tries = Math.min(MAX_TRIES_PER_SLOT, N); for (var t = 0; t < tries; t++) { - var w = entry.words[rng.biasedIndexPow3(N - 1)]; + var w = words[rng.biasedIndexPow3(N - 1)]; var lemIdx = Lemma.unpackIndex(w); if (Bit1029.get(used, lemIdx)) continue; low = glo; @@ -372,9 +338,9 @@ public record SwedishGenerator() { } Bit1029.set(used, lemIdx); - s.assign.w = w; + assign.w = w; if (backtrack(depth + 1)) return true; - s.assign.w = X; + assign.w = X; Bit1029.clear(used, lemIdx); glo = low; @@ -391,6 +357,7 @@ public record SwedishGenerator() { grid.lo = solver.glo; grid.hi = solver.ghi; - return new FillResult(ok, solver.nodes, solver.backtracks, solver.lastMRV, System.currentTimeMillis() - t0, new FillStats()); + return new FillResult(ok, solver.nodes, solver.backtracks, solver.currentSlot == null ? 0 : solver.currentSlot.entry.length, System.currentTimeMillis() - t0, + new FillStats()); } } diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index d43bb97..5e23fda 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -57,7 +57,7 @@ public class MainTest { this.tries = 1; this.verbose = false; }}; - final Dict dict = DictData.DICT;//loadDict(opts.wordsPath); + static final Dict dict = DictData.DICT;//loadDict(opts.wordsPath); @Test void testExtractSlots() { @@ -239,7 +239,6 @@ public class MainTest { System.out.println("[DEBUG_LOG] Grid:"); System.out.println(res.grid().renderHuman(res.clues().c())); System.out.println(res.grid().gridToString(res.clues().c())); - System.out.println(res.grid().renderHuman(res.clues().c())); break; } } diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 5f3f895..e6ea1f9 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -19,6 +19,8 @@ import static puzzle.SwedishGeneratorTest.Idx.IDX_0_0; public class SwedishGeneratorTest { + public static final char C_DASH = '\0'; + public static final byte DASH = (byte) C_DASH; static Grid createEmpty() { return new Grid(new byte[SIZE], X, X); } record Context(long[] bitset) {