From 4585c1f2ebce63d6390b3320e7a9c40f0db24325 Mon Sep 17 00:00:00 2001 From: mike Date: Sat, 17 Jan 2026 01:15:03 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/Main.java | 4 +- src/main/java/puzzle/SwedishGenerator.java | 243 ++++++++++++--------- src/test/java/puzzle/MainTest.java | 19 +- 3 files changed, 146 insertions(+), 120 deletions(-) diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index d78b8bf..d2c628f 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -388,8 +388,8 @@ public class Main { var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring); if (mask == null) return null; - - var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); + val multiThreaded = Thread.currentThread().getName().contains("pool"); + var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid(), multiThreaded); 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 194dc1c..c638af3 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -22,6 +22,8 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Locale; import java.util.stream.IntStream; +import static java.lang.Long.*; +import static java.lang.Long.numberOfTrailingZeros; import static java.nio.charset.StandardCharsets.US_ASCII; /** @@ -142,8 +144,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { stats.simplicity = k == 0 ? 0 : stats.simplicity / k; } } - public int wordCount() { - int k = 0; + public int wordCount(int k) { for (var n = 1; n < clueMap.length; n++) if (clueMap[n] != X) k++; return k; } @@ -220,23 +221,23 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } - public int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); } + public int clueCount() { return bitCount(lo) + bitCount(hi); } public double similarity(Clues b) { long matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo))); long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi))); - return (Long.bitCount(matchLo & MASK_LO) + Long.bitCount(matchHi & MASK_HI)) / SIZED; + return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED; } public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); } public void forEachSlot(SlotVisitor visitor) { - for (var l = lo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 1)); - for (var l = lo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 0)); - for (var l = lo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 2)); - for (var l = lo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 3)); - for (var h = hi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | Long.numberOfTrailingZeros(h), 1)); - for (var h = hi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | Long.numberOfTrailingZeros(h), 0)); - for (var h = hi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | Long.numberOfTrailingZeros(h)), 2)); - for (var h = hi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | Long.numberOfTrailingZeros(h)), 3)); + for (var l = lo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1)); + for (var l = lo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 0)); + for (var l = lo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2)); + for (var l = lo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3)); + for (var h = hi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1)); + for (var h = hi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0)); + for (var h = hi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2)); + for (var h = hi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3)); } public Clues from(Clues best) { lo = best.lo; @@ -275,7 +276,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111); } static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); } static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); } - static int length(long word) { return ((63 - Long.numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; } + static int length(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; } static ThreadLocal BYTES = ThreadLocal.withInitial(() -> new byte[MAX_WORD_LENGTH]); public static String asWord(long word) { val len = Lemma.length(word); @@ -338,7 +339,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { static final int BIT_FOR_DIR = 2; 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 length(long lo, long hi) { return bitCount(lo) + 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; } @@ -357,12 +358,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { long hitsHi = rayHi & c.hi; if (hitsHi != X) { - int msb = 63 - Long.numberOfLeadingZeros(hitsHi); + int msb = 63 - numberOfLeadingZeros(hitsHi); long stop = 1L << msb; rayHi &= ~((stop << 1) - 1); // keep bits > stop rayLo = 0; // lo indices are below stop } else if (hitsLo != X) { - int msb = 63 - Long.numberOfLeadingZeros(hitsLo); + int msb = 63 - numberOfLeadingZeros(hitsLo); long stop = 1L << msb; rayLo &= ~((stop << 1) - 1); } @@ -376,11 +377,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { long hitsHi = rayHi & c.hi; if (hitsLo != X) { - long stop = 1L << Long.numberOfTrailingZeros(hitsLo); + long stop = 1L << numberOfTrailingZeros(hitsLo); rayLo &= (stop - 1); rayHi = 0; // any hi is beyond the stop } else if (hitsHi != X) { - long stop = 1L << Long.numberOfTrailingZeros(hitsHi); + long stop = 1L << numberOfTrailingZeros(hitsHi); // keep all lo (lo indices are < any hi index), but cut hi below stop rayHi &= (stop - 1); } @@ -405,7 +406,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (long bits = lo_cl; bits != X; bits &= bits - 1) { long lsb = bits & -bits; - int clueIdx = Long.numberOfTrailingZeros(lsb); + int clueIdx = numberOfTrailingZeros(lsb); int v = (grid.vlo & lsb) != 0 ? 1 : 0; int r = (grid.rlo & lsb) != 0 ? 1 : 0; int key = Slot.packSlotKey(clueIdx, (r << 1) | v); @@ -413,16 +414,16 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { long hLo = rLo & lo_cl, hHi = rHi & hi_cl; if (Slot.increasing(key)) { if (hLo != X) { - rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1); + rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1); rHi = 0; - } else if (hHi != X) { rHi &= ((1L << Long.numberOfTrailingZeros(hHi)) - 1); } + } else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); } } else { if (hHi != X) { - int msb = 63 - Long.numberOfLeadingZeros(hHi); + int msb = 63 - numberOfLeadingZeros(hHi); rHi &= ~((1L << msb << 1) - 1); rLo = 0; } else if (hLo != X) { - int msb = 63 - Long.numberOfLeadingZeros(hLo); + int msb = 63 - numberOfLeadingZeros(hLo); rLo &= ~((1L << msb << 1) - 1); } } @@ -435,14 +436,14 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { cVLo |= rLo; cVHi |= rHi; } - if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000; + if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; } else { penalty += 25000; } } for (long bits = hi_cl; bits != X; bits &= bits - 1) { long lsb = bits & -bits; - int clueIdx = Long.numberOfTrailingZeros(lsb); + int clueIdx = numberOfTrailingZeros(lsb); int v = (grid.vhi & lsb) != 0 ? 1 : 0; int r = (grid.rhi & lsb) != 0 ? 1 : 0; int key = Slot.packSlotKey(64 | clueIdx, (r << 1) | v); @@ -450,16 +451,16 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { long hLo = rLo & lo_cl, hHi = rHi & hi_cl; if (Slot.increasing(key)) { if (hLo != X) { - rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1); + rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1); rHi = 0; - } else if (hHi != X) { rHi &= ((1L << Long.numberOfTrailingZeros(hHi)) - 1); } + } else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); } } else { if (hHi != X) { - int msb = 63 - Long.numberOfLeadingZeros(hHi); + int msb = 63 - numberOfLeadingZeros(hHi); rHi &= ~((1L << msb << 1) - 1); rLo = 0; } else if (hLo != X) { - int msb = 63 - Long.numberOfLeadingZeros(hLo); + int msb = 63 - numberOfLeadingZeros(hLo); rLo &= ~((1L << msb << 1) - 1); } } @@ -472,7 +473,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { cVLo |= rLo; cVHi |= rHi; } - if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000; + if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; } else { penalty += 25000; } @@ -488,7 +489,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { // "unseen clues" in deze helft for (long bits = clueMask & ~seenMask, nLo, nHi; bits != X; bits &= bits - 1) { - int clueIdx = base | Long.numberOfTrailingZeros(bits); + int clueIdx = base | numberOfTrailingZeros(bits); // start nieuwe component size = 0; @@ -515,7 +516,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { // push lo-neighbors while (nLo != X) { long lsb = nLo & -nLo; - int nidx = Long.numberOfTrailingZeros(nLo); // 0..63 + int nidx = numberOfTrailingZeros(nLo); // 0..63 seenLo |= lsb; stack[sp++] = nidx; @@ -526,7 +527,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { // push hi-neighbors while (nHi != X) { long lsb = nHi & -nHi; - int nidx = 64 | Long.numberOfTrailingZeros(nHi); // 64..127 + int nidx = 64 | numberOfTrailingZeros(nHi); // 64..127 seenHi |= lsb; stack[sp++] = nidx; @@ -540,9 +541,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } for (long bits = ~lo_cl; bits != X; bits &= bits - 1) { - int clueIdx = Long.numberOfTrailingZeros(bits); + int clueIdx = numberOfTrailingZeros(bits); var rci = IT[clueIdx]; - if ((4 - rci.nbrCount()) + Long.bitCount(rci.n1() & lo_cl) + Long.bitCount(rci.n2() & hi_cl) >= 3) penalty += 400; + if ((4 - rci.nbrCount()) + bitCount(rci.n1() & lo_cl) + bitCount(rci.n2() & hi_cl) >= 3) penalty += 400; boolean h = (cHLo & (1L << clueIdx)) != X; boolean v = (cVLo & (1L << clueIdx)) != X; if (!h && !v) penalty += 1500; @@ -550,9 +551,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { else penalty += 600; } for (long bits = ~hi_cl & 0xFFL; bits != X; bits &= bits - 1) { - int clueIdx = Long.numberOfTrailingZeros(bits); + int clueIdx = numberOfTrailingZeros(bits); var rci = IT[64 | clueIdx]; - if ((4 - rci.nbrCount()) + Long.bitCount(rci.n1() & lo_cl) + Long.bitCount(rci.n2() & hi_cl) >= 3) penalty += 400; + if ((4 - rci.nbrCount()) + bitCount(rci.n1() & lo_cl) + bitCount(rci.n2() & hi_cl) >= 3) penalty += 400; boolean h = (cHHi & (1L << clueIdx)) != X; boolean v = (cVHi & (1L << clueIdx)) != X; if (!h && !v) penalty += 1500; @@ -629,14 +630,14 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { (a.rlo & ~maskLo) | (other.rlo & maskLo), (a.rhi & ~maskHi) | (other.rhi & maskHi)); - for (var l = c.lo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 0); - for (var l = c.lo & ~c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 1); - for (var l = c.lo & c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 2); - for (var l = c.lo & c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, Long.numberOfTrailingZeros(l), 3); - for (var h = c.hi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, Long.numberOfTrailingZeros(h), 0); - for (var h = c.hi & ~c.rhi & c.vhi; h != X; h &= h - 1) clearCluesHi(c, Long.numberOfTrailingZeros(h), 1); - for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (Long.numberOfTrailingZeros(h)), 2); - for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (Long.numberOfTrailingZeros(h)), 3); + for (var l = c.lo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 0); + for (var l = c.lo & ~c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 1); + for (var l = c.lo & c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 2); + for (var l = c.lo & c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 3); + for (var h = c.hi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, numberOfTrailingZeros(h), 0); + for (var h = c.hi & ~c.rhi & c.vhi; h != X; h &= h - 1) clearCluesHi(c, numberOfTrailingZeros(h), 1); + for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (numberOfTrailingZeros(h)), 2); + for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (numberOfTrailingZeros(h)), 3); return c; } public static void clearCluesLo(Clues out, int idx, int d) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, d)])) out.clearClueLo(~(1L << idx)); } @@ -728,26 +729,26 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { long p = 0; if (Slot.increasing(key)) { for (long b = lo & glo; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - int i = Long.bitCount(lo & ((1L << idx) - 1)); + int idx = numberOfTrailingZeros(b); + int i = bitCount(lo & ((1L << idx) - 1)); p |= ((long) (i * 26 + g[idx])) << (i << 3); } - int offset = Long.bitCount(lo); + int offset = bitCount(lo); for (long b = hi & ghi; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - int i = offset + Long.bitCount(hi & ((1L << idx) - 1)); + int idx = numberOfTrailingZeros(b); + int i = offset + bitCount(hi & ((1L << idx) - 1)); p |= ((long) (i * 26 + g[64 | idx])) << (i << 3); } } else { - int offset = Long.bitCount(hi); + int offset = bitCount(hi); for (long b = hi & ghi; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - int i = Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))); + int idx = numberOfTrailingZeros(b); + int i = bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))); p |= ((long) (i * 26 + g[64 | idx])) << (i << 3); } for (long b = lo & glo; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - int i = offset + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))); + int idx = numberOfTrailingZeros(b); + int i = offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))); p |= ((long) (i * 26 + g[idx])) << (i << 3); } } @@ -755,56 +756,56 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } static int slotScore(byte[] count, long lo, long hi) { int cross = 0; - for (long b = lo; b != X; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1); - for (long b = hi; b != X; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); + for (long b = lo; b != X; b &= b - 1) cross += (count[numberOfTrailingZeros(b)] - 1); + for (long b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1); return cross * 10 + Slot.length(lo, hi); } static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) { final long glo = grid.lo, ghi = grid.hi; if (Slot.increasing(key)) { for (long b = lo & glo; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - if (g[idx] != Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1)))) return false; + int idx = numberOfTrailingZeros(b); + if (g[idx] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false; } - int bcLo = Long.bitCount(lo); + int bcLo = bitCount(lo); for (long b = hi & ghi; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - if (g[64 | idx] != Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1)))) return false; + int idx = numberOfTrailingZeros(b); + if (g[64 | idx] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false; } long maskLo = lo & ~glo, maskHi = hi & ~ghi; if ((maskLo | maskHi) != X) { for (long b = maskLo; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - g[idx] = Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1))); + int idx = numberOfTrailingZeros(b); + g[idx] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1))); } for (long b = maskHi; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - g[64 | idx] = Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1))); + int idx = numberOfTrailingZeros(b); + g[64 | idx] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1))); } grid.lo |= maskLo; grid.hi |= maskHi; } } else { - int bcHi = Long.bitCount(hi); + int bcHi = bitCount(hi); for (long b = hi & ghi; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - if (g[64 | idx] != Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; + int idx = numberOfTrailingZeros(b); + if (g[64 | idx] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; } for (long b = lo & glo; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - if (g[idx] != Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; + int idx = numberOfTrailingZeros(b); + if (g[idx] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; } long maskLo = lo & ~glo, maskHi = hi & ~ghi; if ((maskLo | maskHi) != X) { for (long b = maskHi; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - g[64 | idx] = Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); + int idx = numberOfTrailingZeros(b); + g[64 | idx] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); } for (long b = maskLo; b != X; b &= b - 1) { - int idx = Long.numberOfTrailingZeros(b); - g[idx] = Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); + int idx = numberOfTrailingZeros(b); + g[idx] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); } grid.lo |= maskLo; grid.hi |= maskHi; @@ -828,19 +829,19 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } p >>>= 8; } else { - p >>>= (Long.numberOfTrailingZeros(p) & ~7); + p >>>= (numberOfTrailingZeros(p) & ~7); } } int count = 0; - for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]); + for (int k = 0; k < numLongs; k++) count += bitCount(res[k]); int[] indices = new int[count]; int ki = 0; for (int k = 0; k < numLongs; k++) { long w = res[k]; while (w != X) { - int t = Long.numberOfTrailingZeros(w); + int t = numberOfTrailingZeros(w); indices[ki++] = (k << 6) | t; w &= w - 1; } @@ -864,20 +865,20 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } p >>>= 8; } else { - p >>>= (Long.numberOfTrailingZeros(p) & ~7); + p >>>= (numberOfTrailingZeros(p) & ~7); } } int count = 0; - for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]); + for (int k = 0; k < numLongs; k++) count += bitCount(res[k]); return count; } static void scoreSlots(int[] slotScores, Slot[] slots) { val count = new byte[SIZE]; for (var s : slots) { - for (long b = s.lo; b != X; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++; - for (long b = s.hi; b != X; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++; + for (long b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++; + for (long b = s.hi; b != X; b &= b - 1) count[64 | numberOfTrailingZeros(b)]++; } for (int i = 0; i < slots.length; i++) { var slot = slots[i]; @@ -885,14 +886,13 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } } - 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; - val used = new long[2048]; - val assigned = new long[CLUE_INDEX_MAX_SIZE]; - val bitset = new long[2500]; - val g = grid.g; + public static FillResult fillMask(final Rng rng,final Slot[] slots,final Grid mask, final boolean multiThreaded) { + val NO_LOG = (!Main.VERBOSE || multiThreaded); + val grid = mask; + val used = new long[2048]; + val assigned = new long[CLUE_INDEX_MAX_SIZE]; + val bitset = new long[2500]; + val g = grid.g; val TOTAL = slots.length; val slotScores = new int[TOTAL]; @@ -907,7 +907,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { long nodes; long backtracks; int lastMRV; - long lastLog = t0; + long lastLog = t0, glo = grid.lo, ghi = grid.hi; Pick current = CARRIER; void renderProgress() { var now = System.currentTimeMillis(); @@ -930,12 +930,44 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { System.out.print("\r" + Strings.padRight(msg, 120)); System.out.flush(); } + boolean placeWord(final int key, final long lo, final long hi, final long w) { + int idx; + if (Slot.increasing(key)) { + for (long b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false; + int bcLo = bitCount(lo); + for (long b = hi & ghi; b != X; b &= b - 1) + if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false; + + long maskLo = lo & ~glo, maskHi = hi & ~ghi; + if ((maskLo | maskHi) != X) { + for (long b = maskLo; b != X; b &= b - 1) g[idx = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1))); + for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1))); + glo |= maskLo; + ghi |= maskHi; + } + } else { + int bcHi = bitCount(hi); + for (long b = hi & ghi; b != X; b &= b - 1) + if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; + for (long b = lo & glo; b != X; b &= b - 1) + if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; + + long maskLo = lo & ~glo, maskHi = hi & ~ghi; + if ((maskLo | maskHi) != X) { + for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); + for (long b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); + glo |= maskLo; + ghi |= maskHi; + } + } + return true; + } void chooseMRV() { Slot best = null; for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) { var s = slots[i]; if (assigned[s.key] != X) continue; - var pattern = patternForSlot(grid.lo, grid.hi, g, s.key, s.lo, s.hi); + 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); @@ -957,7 +989,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { current = PICK_DONE; return; } - var pattern = patternForSlot(grid.lo, grid.hi, g, best.key, best.lo, best.hi); + var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi); var index = best.entry; current = CARRIER; current.slot = best; @@ -1004,9 +1036,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var w = entry.words[idx]; var lemIdx = Lemma.unpackIndex(w); if (Bit1029.get(used, lemIdx)) continue; - low = grid.lo; - top = grid.hi; - if (!placeWord(grid, g, k, slo, shi, w)) continue; + low = glo; + top = ghi; + if (!placeWord(k, slo, shi, w)) continue; Bit1029.set(used, lemIdx); assigned[k] = w; @@ -1015,8 +1047,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { assigned[k] = X; Bit1029.clear(used, lemIdx); - grid.lo = low; - grid.hi = top; + glo = low; + ghi = top; } backtracks++; return false; @@ -1031,9 +1063,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var w = entry.words[idxInArray]; var lemIdx = Lemma.unpackIndex(w); if (Bit1029.get(used, lemIdx)) continue; - low = grid.lo; - top = grid.hi; - if (!placeWord(grid, g, k, slo, shi, w)) continue; + low = glo; + top = ghi; + if (!placeWord(k, slo, shi, w)) continue; Bit1029.set(used, lemIdx); assigned[k] = w; @@ -1042,8 +1074,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { assigned[k] = X; Bit1029.clear(used, lemIdx); - grid.lo = low; - grid.hi = top; + glo = low; + ghi = top; } backtracks++; @@ -1056,7 +1088,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { if (!NO_LOG) solver.renderProgress(); var ok = solver.backtrack(0); // final progress line - + grid.lo = solver.glo; + grid.hi = solver.ghi; var res = new FillResult(ok, new Gridded(grid), assigned, new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV)); if (!multiThreaded) { @@ -1068,7 +1101,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { System.out.println( String.format(Locale.ROOT, "[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs", - res.wordCount(), TOTAL, res.nodes(), res.backtracks(), res.lastMRV(), res.seconds() + res.wordCount(0), TOTAL, res.nodes(), res.backtracks(), res.lastMRV(), res.seconds() ) ); } diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index e43b7d5..4c19de7 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -180,9 +180,9 @@ public class MainTest { 128L, 422762372923520L, 192L); - var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); + var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid(), false); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); - Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed"); + Assertions.assertEquals(18, filled.wordCount(0), "Number of assigned words changed"); Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282])); Assertions.assertEquals(-1L, filled.grid().grid().lo); Assertions.assertEquals(255L, filled.grid().grid().hi); @@ -201,7 +201,7 @@ public class MainTest { foundSeed = seed; System.out.println("[DEBUG_LOG] Seed found: " + seed); System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().stats().simplicity); - System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().wordCount()); + System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().wordCount(0)); System.out.println("[DEBUG_LOG] Grid:"); System.out.println(res.filled().grid().renderHuman(res.clues().c())); System.out.println(res.filled().grid().gridToString(res.clues().c())); @@ -214,15 +214,8 @@ public class MainTest { Assertions.assertTrue(res.filled().ok(), "Puzzle generation failed (not ok)"); Assertions.assertEquals(12348, foundSeed, "Found seed changed"); } - boolean isLetter(byte b) { return (b & 64) != 0; } - @Test - public void testIsLetterA() { - assertTrue(isLetter((byte) 'A')); - } - - @Test - public void testIsLetterZ() { - assertTrue(isLetter((byte) 'Z')); - } + boolean isLetter(byte b) { return (b & 64) != 0; } + @Test public void testIsLetterA() { assertTrue(isLetter((byte) 'A')); } + @Test public void testIsLetterZ() { assertTrue(isLetter((byte) 'Z')); } }