introduce bitloops

This commit is contained in:
mike
2026-01-20 21:19:39 +01:00
parent ddce9addb5
commit b66437bb70
16 changed files with 502 additions and 564 deletions

View File

@@ -45,11 +45,15 @@ public record Export() {
static int INDEX(int r, int cols, int c) { return r * cols + c; }
@AllArgsConstructor
enum Clue {
DOWN(CLUE_DOWN),
RIGHT(CLUE_RIGHT),
UP(CLUE_UP),
LEFT(CLUE_LEFT);
DOWN(CLUE_DOWN, 'B', 'b'),
RIGHT(CLUE_RIGHT, 'A', 'a'),
UP(CLUE_UP, 'C', 'c'),
LEFT(CLUE_LEFT, 'D', 'd'),
NONE(CLUE_LEFT, '?', '?');
final byte dir;
final char slotChar, clueChar;
private static final Clue[] CLUES = new Clue[]{ DOWN, RIGHT, UP, LEFT, NONE, NONE, NONE, NONE, NONE };
public static Clue from(int dir) { return CLUES[dir]; }
}
record Strings() {
@@ -295,7 +299,7 @@ public record Export() {
p.arrowCol() - MIN_C,
p.isReversed(), bytes
)).toArray(WordOut[]::new);
var total = 0.0001d;
var total = 0.0001d;
for (var word : wordsOut) {
total += word.complex();
}

View File

@@ -40,7 +40,6 @@ public class Main {
public int pop = SSIZE * 2;
public int offspring = SSIZE * 3;
public int gens = 600;
public String wordsPath = "nl_score_hints_v4.csv";
public double minSimplicity = 0; // 0 means no limit
public int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
public int tries = threads;
@@ -65,7 +64,6 @@ public class Main {
section("Puzzle Generator");
info("OutputDir : " + OUT_DIR);
info("WordsFile : " + opts.wordsPath);
section("Settings");
printSettings(opts);
@@ -88,7 +86,7 @@ public class Main {
System.out.print(indentLines(res.grid().renderHuman(res.clues().c()), " "));
var exported = res.exportFormatFromFilled(new Rewards(50, 2, 1));
section("Clues");
info("status : generating...");
info("generatedFor : " + exported.words().length);
@@ -146,7 +144,6 @@ public class Main {
System.out.printf(Locale.ROOT, " %-14s: %d%n", "population", o.pop);
System.out.printf(Locale.ROOT, " %-14s: %d%n", "offspring", o.offspring);
System.out.printf(Locale.ROOT, " %-14s: %d%n", "generations", o.gens);
System.out.printf(Locale.ROOT, " %-14s: %s%n", "wordsPath", o.wordsPath);
System.out.printf(Locale.ROOT, " %-14s: %.2f%n", "minSimplicity", o.minSimplicity);
System.out.printf(Locale.ROOT, " %-14s: %d%n", "threads", o.threads);
}
@@ -229,9 +226,6 @@ public class Main {
} else if (a.equals("--tries")) {
out.tries = Integer.parseInt(v);
i++;
} else if (a.equals("--words")) {
out.wordsPath = v;
i++;
} else if (a.equals("--min-simplicity")) {
out.minSimplicity = Double.parseDouble(v);
i++;

View File

@@ -11,51 +11,46 @@ import static puzzle.SwedishGenerator.*;
public final class Masker {
public static final rci[] IT = Neighbors9x8.IT;
private final Rng rng;
private final int[] stack;
private final Clues cache;
private final int[] activeCIdx = new int[SwedishGenerator.SIZE];
private final long[] activeSLo = new long[SwedishGenerator.SIZE];
private final long[] activeSHi = new long[SwedishGenerator.SIZE];
private final long[] adjLo = new long[SwedishGenerator.SIZE];
private final long[] adjHi = new long[SwedishGenerator.SIZE];
private final int[] rCount = new int[8];
private final int[] cCount = new int[9];
private static final long[] NBR_LO = new long[SwedishGenerator.SIZE];
private static final long[] NBR_HI = new long[SwedishGenerator.SIZE];
static {
for (int i = 0; i < SwedishGenerator.SIZE; i++) {
NBR_LO[i] = IT[i].n1();
NBR_HI[i] = IT[i].n2();
}
}
public static final rci[] IT = Neighbors9x8.IT;
private final Rng rng;
private final int[] stack;
private final Clues cache;
private final int[] activeCIdx = new int[SwedishGenerator.SIZE];
private final long[] activeSLo = new long[SwedishGenerator.SIZE];
private final long[] activeSHi = new long[SwedishGenerator.SIZE];
private final long[] adjLo = new long[SwedishGenerator.SIZE];
private final long[] adjHi = new long[SwedishGenerator.SIZE];
private final int[] rCount = new int[8];
private final int[] cCount = new int[9];
private static final long[] NBR_LO = Neighbors9x8.NBR_LO;
private static final long[] NBR_HI = Neighbors9x8.NBR_HI;
public Masker(Rng rng, int[] stack, Clues cache) {
this.rng = rng;
this.stack = stack;
this.cache = cache;
}
public boolean isValid(Clues c, int minLen) {
return findOffendingClue(c, minLen) == -1;
public boolean isValid(Clues c) {
return findOffendingClue(c) == -1;
}
public int findOffendingClue(Clues grid, int minLen) {
public int findOffendingClue(Clues grid) {
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);
var num = 0;
for (var bits = grid.lo; bits != X; bits &= bits - 1) activeCIdx[num++] = numberOfTrailingZeros(bits);
for (var 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);
var start = rng.randint0_SIZE() % num;
var n = 0;
for (var i = 0; i < num; i++) {
var idx = activeCIdx[(start + i) % num];
int dir = grid.getDir(idx);
var 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)) {
@@ -69,8 +64,8 @@ public final class Masker {
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;
if (bitCount(sLo) + bitCount(sHi) < MIN_LEN) return idx;
for (var j = 0; j < n; j++) if (bitCount(sLo & activeSLo[j]) + bitCount(sHi & activeSHi[j]) > 1) return idx;
activeSLo[n] = sLo;
activeSHi[n] = sHi;
n++;
@@ -78,10 +73,10 @@ public final class Masker {
return -1;
}
public void cleanup(Clues c, int minLen) {
int guard = 0;
public void cleanup(Clues c) {
var guard = 0;
while (guard++ < 50) {
int offending = findOffendingClue(c, minLen);
var offending = findOffendingClue(c);
if (offending == -1) break;
if ((offending & 64) == 0) c.clearClueLo(~(1L << offending));
else c.clearClueHi(~(1L << (offending & 63)));
@@ -91,12 +86,12 @@ public final class Masker {
public static final int[][] MUTATE_RI = new int[SwedishGenerator.SIZE][625];
static {
for (int i = 0; i < SwedishGenerator.SIZE; i++) {
int k = 0;
for (int dr1 = -2; dr1 <= 2; dr1++)
for (int dr2 = -2; dr2 <= 2; dr2++)
for (int dc1 = -2; dc1 <= 2; dc1++)
for (int dc2 = -2; dc2 <= 2; dc2++) {
for (var i = 0; i < SwedishGenerator.SIZE; i++) {
var k = 0;
for (var dr1 = -2; dr1 <= 2; dr1++)
for (var dr2 = -2; dr2 <= 2; dr2++)
for (var dc1 = -2; dc1 <= 2; dc1++)
for (var dc2 = -2; dc2 <= 2; dc2++) {
val ti = IT[i];
MUTATE_RI[i][k++] = offset(clamp(ti.r() + dr1 + dr2, 0, R - 1),
clamp(ti.c() + dc1 + dc2, 0, C - 1));
@@ -107,58 +102,58 @@ public final class Masker {
// right/down => increasing indices; up/left => decreasing indices
// first clue is highest index among hits (hi first, then lo)
private static void processSlotRev(Clues c, SlotVisitor visitor, int key) {
long rayLo = PATH_LO[key];
long rayHi = PATH_HI[key];
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
// only consider clue cells
long hitsLo = rayLo & c.lo;
long hitsHi = rayHi & c.hi;
var hitsLo = rayLo & c.lo;
var hitsHi = rayHi & c.hi;
if (hitsHi != X) {
int msb = 63 - numberOfLeadingZeros(hitsHi);
long stop = 1L << msb;
var msb = 63 - numberOfLeadingZeros(hitsHi);
var stop = 1L << msb;
rayHi &= -(stop << 1); // keep bits > stop
rayLo = 0; // lo indices are below stop
} else if (hitsLo != X) {
int msb = 63 - numberOfLeadingZeros(hitsLo);
long stop = 1L << msb;
var msb = 63 - numberOfLeadingZeros(hitsLo);
var stop = 1L << msb;
rayLo &= -(stop << 1);
}
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
visitor.visit(key, rayLo, rayHi);
}
private static boolean validSlotRev(long lo, long hi, int min, int key) {
long rayLo = PATH_LO[key];
long rayHi = PATH_HI[key];
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
// only consider clue cells
long hitsLo = rayLo & lo;
long hitsHi = rayHi & hi;
var hitsLo = rayLo & lo;
var hitsHi = rayHi & hi;
if (hitsHi != X) return (Long.bitCount(rayHi & -(1L << 63 - numberOfLeadingZeros(hitsHi) << 1)) >= min);
else if (hitsLo != X) return (Long.bitCount(rayLo & -(1L << 63 - numberOfLeadingZeros(hitsLo) << 1)) + Long.bitCount(rayHi) >= min);
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
}
private static boolean validSlot(long lo, long hi, int min, int key) {
long rayLo = PATH_LO[key];
long rayHi = PATH_HI[key];
long hitsLo = rayLo & lo;
long hitsHi = rayHi & hi;
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
var hitsLo = rayLo & lo;
var hitsHi = rayHi & hi;
if (hitsLo != X) return (Long.bitCount(rayLo & ((1L << numberOfTrailingZeros(hitsLo)) - 1)) >= min);
else if (hitsHi != X) return (Long.bitCount(rayLo) + Long.bitCount(rayHi & ((1L << numberOfTrailingZeros(hitsHi)) - 1)) >= min);
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
}
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
long rayLo = PATH_LO[key];
long rayHi = PATH_HI[key];
long hitsLo = rayLo & c.lo;
long hitsHi = rayHi & c.hi;
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
var hitsLo = rayLo & c.lo;
var hitsHi = rayHi & c.hi;
if (hitsLo != X) {
long stop = 1L << numberOfTrailingZeros(hitsLo);
var stop = 1L << numberOfTrailingZeros(hitsLo);
rayLo &= (stop - 1);
rayHi = 0;
} else if (hitsHi != X) {
long stop = 1L << numberOfTrailingZeros(hitsHi);
var stop = 1L << numberOfTrailingZeros(hitsHi);
rayHi &= (stop - 1);
}
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
@@ -174,13 +169,13 @@ public final class Masker {
return Masker.scoreSlots(slots);
}
public static Slotinfo[] scoreSlots(Slot[] slots) {
val count = new byte[SwedishGenerator.SIZE];
Slotinfo[] slotInfo = new Slotinfo[slots.length];
val count = new byte[SwedishGenerator.SIZE];
var slotInfo = new Slotinfo[slots.length];
for (var s : slots) {
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 (var b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++;
for (var b = s.hi; b != X; b &= b - 1) count[64 | numberOfTrailingZeros(b)]++;
}
for (int i = 0; i < slots.length; i++) {
for (var i = 0; i < slots.length; i++) {
var slot = slots[i];
slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScore(count, slot.lo, slot.hi), new Assign(), slot.entry,
Math.min(slot.entry.words().length, MAX_TRIES_PER_SLOT));
@@ -188,9 +183,9 @@ public final class Masker {
return slotInfo;
}
public static int slotScore(byte[] count, long lo, long hi) {
int cross = 0;
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);
var cross = 0;
for (var b = lo; b != X; b &= b - 1) cross += (count[numberOfTrailingZeros(b)] - 1);
for (var b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1);
return cross * 10 + Slot.length(lo, hi);
}
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
@@ -200,21 +195,21 @@ public final class Masker {
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
long cHLo2 = 0L, cHHi2 = 0L, cVLo2 = 0L, cVHi2 = 0L;
long lo_cl = grid.lo, hi_cl = grid.hi;
long penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
boolean hasSlots = false;
long lo_cl = grid.lo, hi_cl = grid.hi;
var penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
var hasSlots = false;
/* if (!isValid(grid, 2)) {
throw new RuntimeException("Invalid grid configuration for mask fitness calculation");
//return 1_000_000_000L;
}*/
int numClues = 0;
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits;
int clueIdx = numberOfTrailingZeros(lsb);
var numClues = 0;
for (var bits = lo_cl; bits != X; bits &= bits - 1) {
var lsb = bits & -bits;
var clueIdx = numberOfTrailingZeros(lsb);
int dir = grid.getDir(clueIdx);
int key = Slot.packSlotKey(clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
var key = Slot.packSlotKey(clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slotinfo.increasing(key)) {
if (hLo != X) {
@@ -222,11 +217,11 @@ public final class Masker {
rHi = 0;
} else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
} else if (hHi != X) {
int msb = 63 - numberOfLeadingZeros(hHi);
var msb = 63 - numberOfLeadingZeros(hHi);
rHi &= -(1L << msb << 1);
rLo = 0;
} else if (hLo != X) {
int msb = 63 - numberOfLeadingZeros(hLo);
var msb = 63 - numberOfLeadingZeros(hLo);
rLo &= -(1L << msb << 1);
}
@@ -249,16 +244,16 @@ public final class Masker {
cVHi |= rHi;
}
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
int wordLen = bitCount(rLo) + bitCount(rHi);
var wordLen = bitCount(rLo) + bitCount(rHi);
if (wordLen > 6) penalty += (wordLen - 6) * 1000L;
} else penalty += 25000;
}
for (long bits = hi_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits;
int clueIdx = numberOfTrailingZeros(lsb);
for (var bits = hi_cl; bits != X; bits &= bits - 1) {
var lsb = bits & -bits;
var clueIdx = numberOfTrailingZeros(lsb);
int dir = grid.getDir(64 | clueIdx);
int key = Slot.packSlotKey(64 | clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
var key = Slot.packSlotKey(64 | clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slotinfo.increasing(key)) {
if (hLo != X) {
@@ -266,11 +261,11 @@ public final class Masker {
rHi = 0;
} else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
} else if (hHi != X) {
int msb = 63 - numberOfLeadingZeros(hHi);
var msb = 63 - numberOfLeadingZeros(hHi);
rHi &= -(1L << msb << 1);
rLo = 0;
} else if (hLo != X) {
int msb = 63 - numberOfLeadingZeros(hLo);
var msb = 63 - numberOfLeadingZeros(hLo);
rLo &= -(1L << msb << 1);
}
@@ -293,7 +288,7 @@ public final class Masker {
cVHi |= rHi;
}
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
int wordLen = bitCount(rLo) + bitCount(rHi);
var wordLen = bitCount(rLo) + bitCount(rHi);
if (wordLen > 6) penalty += (wordLen - 6) * 1000L;
} else penalty += 25000;
}
@@ -302,22 +297,22 @@ public final class Masker {
Arrays.fill(rCount, 0);
Arrays.fill(cCount, 0);
for (int i = 0; i < numClues; i++) {
int idx = activeCIdx[i];
for (var i = 0; i < numClues; i++) {
var idx = activeCIdx[i];
rCount[idx & 7]++;
cCount[idx >> 3]++;
}
for (int rc : rCount) if (rc < 2) penalty += (2 - rc) * 4000L;
for (int cc : cCount) if (cc < 2) penalty += (2 - cc) * 4000L;
for (var rc : rCount) if (rc < 2) penalty += (2 - rc) * 4000L;
for (var cc : cCount) if (cc < 2) penalty += (2 - cc) * 4000L;
// Connectiviteitscheck
for (int i = 0; i < numClues; i++) {
for (var i = 0; i < numClues; i++) {
adjLo[i] = 0;
adjHi[i] = 0;
}
for (int i = 0; i < numClues; i++) {
for (int j = i + 1; j < numClues; j++) {
boolean connected = false;
for (var i = 0; i < numClues; i++) {
for (var j = i + 1; j < numClues; j++) {
var connected = false;
// 1. Intersectie
if (((activeSLo[i] & activeSLo[j]) | (activeSHi[i] & activeSHi[j])) != 0) {
connected = true;
@@ -333,31 +328,31 @@ public final class Masker {
}
if (numClues > 0) {
int maxReached = 0;
var maxReached = 0;
long totalReachedLo = 0, totalReachedHi = 0;
for (int i = 0; i < numClues; i++) {
for (var 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;
var currentReachedLo = (i < 64) ? (1L << i) : 0;
var currentReachedHi = (i >= 64) ? (1L << (i - 64)) : 0;
stack[0] = i;
int sp = 1;
int count = 0;
var sp = 1;
var count = 0;
while (sp > 0) {
int cur = stack[--sp];
var cur = stack[--sp];
count++;
long nLo = adjLo[cur] & ~currentReachedLo;
long nHi = adjHi[cur] & ~currentReachedHi;
var nLo = adjLo[cur] & ~currentReachedLo;
var nHi = adjHi[cur] & ~currentReachedHi;
while (nLo != 0) {
long lsb = nLo & -nLo;
int idx = numberOfTrailingZeros(lsb);
var lsb = nLo & -nLo;
var idx = numberOfTrailingZeros(lsb);
currentReachedLo |= lsb;
stack[sp++] = idx;
nLo &= ~lsb;
}
while (nHi != 0) {
long lsb = nHi & -nHi;
int idx = 64 | numberOfTrailingZeros(lsb);
var lsb = nHi & -nHi;
var idx = 64 | numberOfTrailingZeros(lsb);
currentReachedHi |= lsb;
stack[sp++] = idx;
nHi &= ~lsb;
@@ -373,22 +368,22 @@ public final class Masker {
}
}
for (long bits = ~lo_cl & MASK_LO; bits != X; bits &= bits - 1) {
int clueIdx = numberOfTrailingZeros(bits);
for (var bits = ~lo_cl & MASK_LO; bits != X; bits &= bits - 1) {
var 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;
var h = (cHLo & (1L << clueIdx)) != X;
var v = (cVLo & (1L << clueIdx)) != X;
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);
for (var bits = ~hi_cl & MASK_HI; bits != X; bits &= bits - 1) {
var clueIdx = numberOfTrailingZeros(bits);
var rci = IT[64 | clueIdx];
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;
var h = (cHHi & (1L << clueIdx)) != X;
var v = (cVHi & (1L << clueIdx)) != X;
if (!h && !v)
penalty += 2000;
else if (h && v) { /* ok */ } else if (((h ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 1000;
@@ -402,20 +397,23 @@ public final class Masker {
else compHi = lowestOneBit(remHi);
long lastLo, lastHi;
do {
lastLo = compLo; lastHi = compHi;
lastLo = compLo;
lastHi = compHi;
long expandedLo = 0, expandedHi = 0;
for (long bits = compLo; bits != X; bits &= bits - 1) {
int idx = numberOfTrailingZeros(bits);
expandedLo |= NBR_LO[idx]; expandedHi |= NBR_HI[idx];
for (var bits = compLo; bits != X; bits &= bits - 1) {
var idx = numberOfTrailingZeros(bits);
expandedLo |= NBR_LO[idx];
expandedHi |= NBR_HI[idx];
}
for (long bits = compHi; bits != X; bits &= bits - 1) {
int idx = 64 | numberOfTrailingZeros(bits);
expandedLo |= NBR_LO[idx]; expandedHi |= NBR_HI[idx];
for (var bits = compHi; bits != X; bits &= bits - 1) {
var idx = 64 | numberOfTrailingZeros(bits);
expandedLo |= NBR_LO[idx];
expandedHi |= NBR_HI[idx];
}
compLo |= expandedLo & lo_cl;
compHi |= expandedHi & hi_cl;
} while (compLo != lastLo || compHi != lastHi);
int s = bitCount(compLo) + bitCount(compHi);
var s = bitCount(compLo) + bitCount(compHi);
if (s >= 2) penalty += (long) (s - 1) * 520;
remLo &= ~compLo;
remHi &= ~compHi;
@@ -432,19 +430,19 @@ public final class Masker {
if (isLo(ri)) {
if (g.isClueLo(ri)) continue;
var d_idx = rng.randomClueDir();
int key = Slot.packSlotKey(ri, d_idx);
var key = Slot.packSlotKey(ri, d_idx);
if (g.hasRoomForClue(key)) {
g.setClueLo(1L << ri, d_idx);
if (isValid(g, MIN_LEN)) placed++;
if (isValid(g)) placed++;
else g.clearClueLo(~(1L << ri));
}
} else {
if (g.isClueHi(ri)) continue;
var d_idx = rng.randomClueDir();
int key = Slot.packSlotKey(ri, d_idx);
var key = Slot.packSlotKey(ri, d_idx);
if (g.hasRoomForClue(key)) {
g.setClueHi(1L << (ri & 63), d_idx);
if (isValid(g, MIN_LEN)) placed++;
if (isValid(g)) placed++;
else g.clearClueHi(~(1L << (ri & 63)));
}
}
@@ -458,62 +456,59 @@ public final class Masker {
for (int k = 0, ri; k < 6; k++) {
ri = bytes[rng.randint0_624()];
if (c.notClue(ri)) { // ADD
byte d = rng.randomClueDir();
int key = Slot.packSlotKey(ri, d);
var d = rng.randomClueDir();
var key = Slot.packSlotKey(ri, d);
if (c.hasRoomForClue(key)) {
if (isLo(ri)) {
c.setClueLo(1L << ri, d);
if (!isValid(c, MIN_LEN)) c.clearClueLo(~(1L << ri));
if (!isValid(c)) c.clearClueLo(~(1L << ri));
else continue;
} else {
c.setClueHi(1L << (ri & 63), d);
if (!isValid(c, MIN_LEN)) c.clearClueHi(~(1L << (ri & 63)));
if (!isValid(c)) c.clearClueHi(~(1L << (ri & 63)));
else continue;
}
}
} else { // HAS CLUE
var op = rng.randomClueDir();
if (op < 2) { // REMOVE
byte oldD = c.getDir(ri);
var oldD = c.getDir(ri);
if (isLo(ri)) {
c.clearClueLo(~(1L << ri));
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
if (!isValid(c)) c.setClueLo(1L << ri, oldD);
else continue;
} else {
c.clearClueHi(~(1L << (ri & 63)));
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
if (!isValid(c)) c.setClueHi(1L << (ri & 63), oldD);
else continue;
}
/* if (isLo(ri)) c.clearClueLo(~(1L << ri));
else c.clearClueHi(~(1L << (ri & 63)));*/
}
if (op < 4) { // CHANGE DIRECTION
byte d = rng.randomClueDir();
int key = Slot.packSlotKey(ri, d);
var d = rng.randomClueDir();
var key = Slot.packSlotKey(ri, d);
if (c.hasRoomForClue(key)) {
byte oldD = c.getDir(ri);
var oldD = c.getDir(ri);
if (isLo(ri)) {
c.setClueLo(1L << ri, d);
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
if (!isValid(c)) c.setClueLo(1L << ri, oldD);
else continue;
} else {
c.setClueHi(1L << (ri & 63), d);
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
if (!isValid(c)) c.setClueHi(1L << (ri & 63), oldD);
else continue;
}
}
} // MOVE
int nri = bytes[rng.randint0_624()];
var nri = bytes[rng.randint0_624()];
if (c.notClue(nri)) {
byte d = c.getDir(ri);
int nkey = Slot.packSlotKey(nri, d);
var d = c.getDir(ri);
var nkey = Slot.packSlotKey(nri, d);
if (c.hasRoomForClue(nkey)) {
if (isLo(ri)) c.clearClueLo(~(1L << ri));
else c.clearClueHi(~(1L << (ri & 63)));
if (isLo(nri)) c.setClueLo(1L << nri, d);
else c.setClueHi(1L << (nri & 63), d);
if (!isValid(c, MIN_LEN)) {
if (!isValid(c)) {
if (isLo(nri)) c.clearClueLo(~(1L << nri));
else c.clearClueHi(~(1L << (nri & 63)));
if (isLo(ri)) c.setClueLo(1L << ri, d);
@@ -535,7 +530,7 @@ public final class Masker {
long maskLo = 0, maskHi = 0;
for (var rci : IT)
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
int i = rci.i();
var i = rci.i();
if ((i & 64) == 0) maskLo |= (1L << i);
else maskHi |= (1L << (i - 64));
}
@@ -548,7 +543,7 @@ public final class Masker {
(a.rhi & ~maskHi) | (other.rhi & maskHi),
(a.xlo & ~maskLo) | (other.xlo & maskLo),
(a.xhi & ~maskHi) | (other.xhi & maskHi));
cleanup(c, MIN_LEN);
cleanup(c);
return c;
}
@@ -585,36 +580,36 @@ public final class Masker {
}
}
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize);
GridAndFit[] pop = new GridAndFit[popSize];
for (int i = 0; i < popSize; i++) {
var pop = new GridAndFit[popSize];
for (var i = 0; i < popSize; i++) {
if (Thread.currentThread().isInterrupted()) return null;
pop[i] = new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180));
}
for (int gen = 0; gen < gens; gen++) {
for (var gen = 0; gen < gens; gen++) {
if (Thread.currentThread().isInterrupted()) break;
GridAndFit[] children = new GridAndFit[offspring];
int childCount = 0;
for (int k = 0; k < offspring; k++) {
var children = new GridAndFit[offspring];
var childCount = 0;
for (var k = 0; k < offspring; k++) {
if (Thread.currentThread().isInterrupted()) break;
GridAndFit p1 = rng.rand(pop);
GridAndFit p2 = rng.rand(pop);
Clues child = crossover(p1.grid, p2.grid);
var p1 = rng.rand(pop);
var p2 = rng.rand(pop);
var child = crossover(p1.grid, p2.grid);
children[k] = new GridAndFit(hillclimb(child, clueSize, 70));
childCount++;
}
GridAndFit[] combined = new GridAndFit[pop.length + childCount];
var combined = new GridAndFit[pop.length + childCount];
System.arraycopy(pop, 0, combined, 0, pop.length);
System.arraycopy(children, 0, combined, pop.length, childCount);
Arrays.sort(combined, Comparator.comparingLong(GridAndFit::fit));
GridAndFit[] next = new GridAndFit[popSize];
int nextCount = 0;
for (GridAndFit cand : combined) {
var next = new GridAndFit[popSize];
var nextCount = 0;
for (var cand : combined) {
if (nextCount >= popSize) break;
boolean unique = true;
for (int i = 0; i < nextCount; i++) {
var unique = true;
for (var i = 0; i < nextCount; i++) {
if (cand.grid.similarity(next[i].grid) > 0.92) {
unique = false;
break;
@@ -624,10 +619,10 @@ public final class Masker {
}
if (nextCount < popSize) {
for (GridAndFit cand : combined) {
for (var cand : combined) {
if (nextCount >= popSize) break;
boolean alreadyIn = false;
for (int i = 0; i < nextCount; i++) {
var alreadyIn = false;
for (var i = 0; i < nextCount; i++) {
if (cand == next[i]) {
alreadyIn = true;
break;
@@ -641,9 +636,9 @@ public final class Masker {
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop[0].fit());
}
if (pop.length == 0) return null;
GridAndFit best = pop[0];
for (int i = 1; i < pop.length; i++) {
GridAndFit x = pop[i];
var best = pop[0];
for (var i = 1; i < pop.length; i++) {
var x = pop[i];
if (x.fit() < best.fit()) best = x;
}
return best.grid;
@@ -698,8 +693,8 @@ public final class Masker {
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) & ~(xlo ^ b.xlo)));
long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi) & ~(xhi ^ b.xhi)));
var matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo) & ~(xlo ^ b.xlo)));
var matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi) & ~(xhi ^ b.xhi)));
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
}
@@ -733,14 +728,14 @@ public final class Masker {
}
public byte getDir(int index) {
if ((index & 64) == 0) {
int v = (vlo & (1L << index)) != 0 ? 1 : 0;
int r = (rlo & (1L << index)) != 0 ? 1 : 0;
int x = (xlo & (1L << index)) != 0 ? 1 : 0;
var v = (vlo & (1L << index)) != 0 ? 1 : 0;
var r = (rlo & (1L << index)) != 0 ? 1 : 0;
var x = (xlo & (1L << index)) != 0 ? 1 : 0;
return (byte) ((x << 2) | (r << 1) | v);
} else {
int v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
int r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
int x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0;
var v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
var r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
var x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0;
return (byte) ((x << 2) | (r << 1) | v);
}
}

View File

@@ -37,19 +37,19 @@ public class Meta {
// --- Lookup: w -> i using mmap ---
static int findIndexInMapMmap(Path mapFile, long target) throws IOException {
try (FileChannel ch = FileChannel.open(mapFile, StandardOpenOption.READ)) {
MappedByteBuffer mbb = (MappedByteBuffer) ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size()).order(ORDER);
try (var ch = FileChannel.open(mapFile, StandardOpenOption.READ)) {
var mbb = (MappedByteBuffer) ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size()).order(ORDER);
int magic = mbb.getInt(0);
int ver = mbb.getInt(4);
int n = mbb.getInt(8);
var magic = mbb.getInt(0);
var ver = mbb.getInt(4);
var n = mbb.getInt(8);
if (magic != MAP_MAGIC || ver != VERSION) throw new IOException("Bad map file");
int lo = 0, hi = n - 1;
while (lo <= hi) {
int mid = (lo + hi) >>> 1;
int off = 12 + mid * 8;
long key = mbb.getLong(off);
var mid = (lo + hi) >>> 1;
var off = 12 + mid * 8;
var key = mbb.getLong(off);
if (key < target) lo = mid + 1;
else if (key > target) hi = mid - 1;
@@ -61,41 +61,41 @@ public class Meta {
// --- Read record i from shard.data (your format) ---
static ShardLem readRecord(Path shardFile, long w, int i) throws IOException {
try (FileChannel ch = FileChannel.open(shardFile, StandardOpenOption.READ)) {
ByteBuffer hdr = ByteBuffer.allocate(12).order(ORDER);
try (var ch = FileChannel.open(shardFile, StandardOpenOption.READ)) {
var hdr = ByteBuffer.allocate(12).order(ORDER);
ch.read(hdr);
hdr.flip();
int magic = hdr.getInt();
int ver = hdr.getInt();
int n = hdr.getInt();
var magic = hdr.getInt();
var ver = hdr.getInt();
var n = hdr.getInt();
if (magic != SHARD_MAGIC || ver != VERSION) throw new IOException("Bad shard file");
if (i < 0 || i >= n) throw new IndexOutOfBoundsException();
long tableStart = 12L;
long dataStart = 12L + (long) n * 4L;
var tableStart = 12L;
var dataStart = 12L + (long) n * 4L;
int offI = readIntAt(ch, tableStart + (long) i * 4L);
int offIp = (i + 1 < n)
var offI = readIntAt(ch, tableStart + (long) i * 4L);
var offIp = (i + 1 < n)
? readIntAt(ch, tableStart + (long) (i + 1) * 4L)
: (int) (ch.size() - dataStart);
int len = offIp - offI;
ByteBuffer buf = ByteBuffer.allocate(len);
var len = offIp - offI;
var buf = ByteBuffer.allocate(len);
ch.position(dataStart + offI);
ch.read(buf);
buf.flip();
String s = StandardCharsets.UTF_8.decode(buf).toString();
String[] parts = s.split("\t", 3);
var s = StandardCharsets.UTF_8.decode(buf).toString();
var parts = s.split("\t", 3);
int simpel = Integer.parseInt(parts[1]);
String[] clues = GSON.fromJson(parts[2], String[].class);
var simpel = Integer.parseInt(parts[1]);
var clues = GSON.fromJson(parts[2], String[].class);
return new ShardLem(w, simpel, clues);
}
}
static int readIntAt(FileChannel ch, long pos) throws IOException {
ByteBuffer b = ByteBuffer.allocate(4).order(ORDER);
var b = ByteBuffer.allocate(4).order(ORDER);
ch.position(pos);
ch.read(b);
b.flip();
@@ -105,10 +105,10 @@ public class Meta {
// --- Demo main ---
public static ShardLem lookup(long w) {
try {
int i = findIndexInMapMmap(shardMap, Lemma.pack43(w));
var i = findIndexInMapMmap(shardMap, Lemma.pack43(w));
System.out.println("\nQuery: w=" + w + " -> i=" + i);
if (i >= 0) {
ShardLem rec = readRecord(shardData, w, i);
var rec = readRecord(shardData, w, i);
System.out.println(" simpel=" + rec.simpel());
System.out.println(" clues=" + Arrays.toString(rec.clues()));
return rec;

View File

@@ -0,0 +1,6 @@
// file: app/Trigger.java
package puzzle;
import gen.GenerateConst;
@GenerateConst(C = 9, R = 8, packageName = "precomp", className = "Const9x8")
public final class TriggerConstants { }

View File

@@ -0,0 +1,5 @@
package puzzle;
import gen.GenerateNeighbors;
@GenerateNeighbors(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2)
public final class TriggerNeighbors4x3 { }

View File

@@ -3,4 +3,4 @@ package puzzle;
import gen.GenerateNeighbors;
@GenerateNeighbors(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2)
public final class Trigger { }
public final class TriggerNeighbors9x8 { }