From 28f448d1784337b3b15e1165e4a1ad750e5e5554 Mon Sep 17 00:00:00 2001 From: mike Date: Tue, 20 Jan 2026 02:45:16 +0100 Subject: [PATCH] introduce bitloops --- src/main/generated-sources/puzzle/Config.java | 16 ++ src/main/java/puzzle/Main.java | 2 +- src/main/java/puzzle/Masker.java | 142 +++++++++++++----- src/main/java/puzzle/SwedishGenerator.java | 2 +- 4 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 src/main/generated-sources/puzzle/Config.java diff --git a/src/main/generated-sources/puzzle/Config.java b/src/main/generated-sources/puzzle/Config.java new file mode 100644 index 0000000..d10450e --- /dev/null +++ b/src/main/generated-sources/puzzle/Config.java @@ -0,0 +1,16 @@ +package puzzle; + +/** + * Generated constants from pom.xml during build via templating-maven-plugin. + */ +public final class Config { + public static final int CLUE_SIZE = 4; + public static final int MIN_LEN = 2; + public static final int MAX_TRIES_PER_SLOT = 1000; + public static final int MAX_LEN = 8; + public static final int PUZZLE_ROWS = 8; + public static final int PUZZLE_COLS = 9; + public static final int PUZZLE_SIZE = PUZZLE_ROWS*PUZZLE_COLS; + public static final int MAX_WORD_LENGTH = PUZZLE_ROWS; + public static final int MAX_WORD_LENGTH_MIN_1 = PUZZLE_ROWS-1; +} diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index d63599b..a846520 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -34,7 +34,7 @@ public class Main { @NoArgsConstructor public static class Opts { - static int SSIZE = 23; + static int SSIZE = 20; public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis()); public int clueSize = SSIZE; public int pop = SSIZE * 2; diff --git a/src/main/java/puzzle/Masker.java b/src/main/java/puzzle/Masker.java index e188b18..b39b45c 100644 --- a/src/main/java/puzzle/Masker.java +++ b/src/main/java/puzzle/Masker.java @@ -28,13 +28,45 @@ public final class Masker { } public boolean isValid(Clues c, int minLen) { - return c.isValid(minLen, activeSLo, activeSHi); + return findOffendingClue(c, minLen) == -1; } - + + public int findOffendingClue(Clues grid, int minLen) { + if (((grid.xlo & grid.rlo) & grid.lo) != X) return numberOfTrailingZeros((grid.xlo & grid.rlo) & grid.lo); + if (((grid.xhi & grid.rhi) & grid.hi) != X) return 64 | numberOfTrailingZeros((grid.xhi & grid.rhi) & grid.hi); + + int num = 0; + for (long bits = grid.lo; bits != X; bits &= bits - 1) activeCIdx[num++] = numberOfTrailingZeros(bits); + for (long bits = grid.hi; bits != X; bits &= bits - 1) activeCIdx[num++] = 64 | numberOfTrailingZeros(bits); + + if (num == 0) return -1; + + int start = rng.randint0_SIZE() % num; + int n = 0; + for (int i = 0; i < num; i++) { + int idx = activeCIdx[(start + i) % num]; + int dir = grid.getDir(idx); + int key = Slot.packSlotKey(idx, dir); + long sLo = PATH_LO[key], sHi = PATH_HI[key]; + long hLo = sLo & grid.lo, hHi = sHi & grid.hi; + if (Slotinfo.increasing(key)) { + if (hLo != X) { sLo &= (1L << numberOfTrailingZeros(hLo)) - 1; sHi = 0; } + else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; } + } else { + if (hHi != X) { sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1); sLo = 0; } + else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); } + } + if (bitCount(sLo) + bitCount(sHi) < minLen) return idx; + for (int j = 0; j < n; j++) if (bitCount(sLo & activeSLo[j]) + bitCount(sHi & activeSHi[j]) > 1) return idx; + activeSLo[n] = sLo; activeSHi[n] = sHi; n++; + } + return -1; + } + public void cleanup(Clues c, int minLen) { int guard = 0; while (guard++ < 50) { - int offending = c.findOffendingClue(minLen, activeSLo, activeSHi); + int offending = findOffendingClue(c, minLen); if (offending == -1) break; if ((offending & 64) == 0) c.clearClueLo(~(1L << offending)); else c.clearClueHi(~(1L << (offending & 63))); @@ -199,6 +231,8 @@ public final class Masker { cVHi |= rHi; } if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; + int wordLen = bitCount(rLo) + bitCount(rHi); + if (wordLen > 5) penalty += (wordLen - 5) * 2000; } else penalty += 25000; } for (long bits = hi_cl; bits != X; bits &= bits - 1) { @@ -221,12 +255,12 @@ public final class Masker { int msb = 63 - numberOfLeadingZeros(hLo); rLo &= -(1L << msb << 1); } - + activeCIdx[numClues] = 64 | clueIdx; activeSLo[numClues] = rLo; activeSHi[numClues] = rHi; numClues++; - + if ((rLo | rHi) != X) { hasSlots = true; if (Slot.horiz(key)) { @@ -241,10 +275,22 @@ public final class Masker { cVHi |= rHi; } if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; + int wordLen = bitCount(rLo) + bitCount(rHi); + if (wordLen > 5) penalty += (wordLen - 5) * 2000; } else penalty += 25000; } if (!hasSlots) return 1_000_000_000L; + + int[] rCount = new int[8]; + int[] cCount = new int[9]; + for (int i = 0; i < numClues; i++) { + int idx = activeCIdx[i]; + rCount[idx & 7]++; + cCount[idx >> 3]++; + } + for (int rc : rCount) if (rc < 2) penalty += (2 - rc) * 4000; + for (int cc : cCount) if (cc < 2) penalty += (2 - cc) * 4000; // Connectiviteitscheck for (int i = 0; i < numClues; i++) { @@ -269,44 +315,56 @@ public final class Masker { } if (numClues > 0) { - long reachedLo = 1L, reachedHi = 0L; - stack[0] = 0; - int sp = 1; - while (sp > 0) { - int cur = stack[--sp]; - long nLo = adjLo[cur] & ~reachedLo; - long nHi = adjHi[cur] & ~reachedHi; - while (nLo != 0) { - long lsb = nLo & -nLo; - int idx = numberOfTrailingZeros(lsb); - reachedLo |= lsb; - stack[sp++] = idx; - nLo &= ~lsb; - } - while (nHi != 0) { - long lsb = nHi & -nHi; - int idx = 64 | numberOfTrailingZeros(lsb); - reachedHi |= lsb; - stack[sp++] = idx; - nHi &= ~lsb; + int maxReached = 0; + long totalReachedLo = 0, totalReachedHi = 0; + for (int i = 0; i < numClues; i++) { + if (i < 64) { if ((totalReachedLo & (1L << i)) != 0) continue; } + else { if ((totalReachedHi & (1L << (i - 64))) != 0) continue; } + + long currentReachedLo = (i < 64) ? (1L << i) : 0; + long currentReachedHi = (i >= 64) ? (1L << (i - 64)) : 0; + stack[0] = i; + int sp = 1; + int count = 0; + while (sp > 0) { + int cur = stack[--sp]; + count++; + long nLo = adjLo[cur] & ~currentReachedLo; + long nHi = adjHi[cur] & ~currentReachedHi; + while (nLo != 0) { + long lsb = nLo & -nLo; + int idx = numberOfTrailingZeros(lsb); + currentReachedLo |= lsb; + stack[sp++] = idx; + nLo &= ~lsb; + } + while (nHi != 0) { + long lsb = nHi & -nHi; + int idx = 64 | numberOfTrailingZeros(lsb); + currentReachedHi |= lsb; + stack[sp++] = idx; + nHi &= ~lsb; + } } + if (count > maxReached) maxReached = count; + totalReachedLo |= currentReachedLo; + totalReachedHi |= currentReachedHi; } - int count = bitCount(reachedLo) + bitCount(reachedHi); - if (count < numClues) { - penalty += (numClues - count) * 4000; + if (maxReached < numClues) { + penalty += (numClues - maxReached) * 4000; penalty += 20000; } } - + for (long bits = ~lo_cl & MASK_LO; bits != X; bits &= bits - 1) { int clueIdx = numberOfTrailingZeros(bits); var rci = IT[clueIdx]; 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 += 5000; - else if (h && v) { /* ok */ } else if (((h ? cHLo2 : cVLo2) & (1L << clueIdx)) != X) penalty += 600; - else penalty += 200; + if (!h && !v) penalty += 2000; + else if (h && v) { /* ok */ } else if (((h ? cHLo2 : cVLo2) & (1L << clueIdx)) != X) penalty += 1000; + else penalty += 1000; } for (long bits = ~hi_cl & MASK_HI; bits != X; bits &= bits - 1) { int clueIdx = numberOfTrailingZeros(bits); @@ -315,11 +373,23 @@ public final class Masker { boolean h = (cHHi & (1L << clueIdx)) != X; boolean v = (cVHi & (1L << clueIdx)) != X; if (!h && !v) - penalty += 5000; - else if (h && v) { /* ok */ } else if (((h ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 600; - else penalty += 200; + penalty += 2000; + else if (h && v) { /* ok */ } else if (((h ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 1000; + else penalty += 1000; } - + + long nclLo = ~lo_cl & MASK_LO; + long nclHi = ~hi_cl & MASK_HI; + long hNbrLo = (nclLo >> 8) | (nclLo << 8) | (nclHi << 56); + long hNbrHi = (nclHi >> 8) | (nclLo >> 56); + long vNbrLo = ((nclLo & ~0x0101010101010101L) >> 1) | ((nclLo & ~0x8080808080808080L) << 1); + long vNbrHi = ((nclHi & ~0x01L) >> 1) | ((nclHi & ~0x80L) << 1); + + penalty += bitCount(nclLo & ~cHLo & hNbrLo) * 800; + penalty += bitCount(nclLo & ~cVLo & vNbrLo) * 800; + penalty += bitCount(nclHi & ~cHHi & hNbrHi) * 800; + penalty += bitCount(nclHi & ~cVHi & vNbrHi) * 800; + return penalty; } diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index fc1fbbe..7e9ecc2 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -40,7 +40,7 @@ public record SwedishGenerator() { public static final int MAX_WORD_LENGTH = Config.PUZZLE_ROWS; public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1; public static final int MIN_LEN = 2;//Neighbors9x8.MIN_LEN;//Config.MIN_LEN; - public static final int MAX_TRIES_PER_SLOT = 700;//Config.MAX_TRIES_PER_SLOT; + public static final int MAX_TRIES_PER_SLOT = 1200;//Config.MAX_TRIES_PER_SLOT; public static final int STACK_SIZE = 128; public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;// (long) SIZE_MIN_1 - 0L + 1L public static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624;//624L - 0L + 1L;