introduce bitloops

This commit is contained in:
mike
2026-01-17 18:39:19 +01:00
parent 9367833407
commit 8ff9d661e3
5 changed files with 81 additions and 96 deletions

View File

@@ -212,7 +212,7 @@ public record Export() {
public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) { public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) {
public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) { public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) {
val meta = Meta.readRecord(Meta.shardKey(l), Lemma.unpackIndex(l)); val meta = Meta.readRecord(Meta.shardKey(l), Lemma.unpackShardIndex(l));
this(Lemma.asWord(l), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, this(Lemma.asWord(l), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed,
meta.simpel(), meta.clues()); meta.simpel(), meta.clues());
} }
@@ -228,7 +228,7 @@ public record Export() {
for (var n = 1; n < slots.length; n++) { for (var n = 1; n < slots.length; n++) {
if (slots[n].assign().w != X) { if (slots[n].assign().w != X) {
k++; k++;
simpel += Meta.readRecord(Meta.shardKey(slots[n].assign().w), Lemma.unpackIndex(slots[n].assign().w)).simpel();//.simpel(Lemma.unpackIndex(slots[n].assign().w)); simpel += Meta.readRecord(Meta.shardKey(slots[n].assign().w), Lemma.unpackShardIndex(slots[n].assign().w)).simpel();
} }
} }
simpel = k == 0 ? 0 : simpel / k; simpel = k == 0 ? 0 : simpel / k;

View File

@@ -413,7 +413,7 @@ public class Main {
if (mask == null) return null; if (mask == null) return null;
val multiThreaded = Thread.currentThread().getName().contains("pool"); val multiThreaded = Thread.currentThread().getName().contains("pool");
var slots = Masker.extractSlots(mask, dict.index()); var slots = Masker.extractSlots(mask, dict.index());
val slotInfo = Masker.scoreSlots(new int[slots.length], slots); val slotInfo = Masker.scoreSlots(slots);
var grid = mask.toGrid(); var grid = mask.toGrid();
var filled = fillMask(rng, slotInfo, grid, multiThreaded); var filled = fillMask(rng, slotInfo, grid, multiThreaded);

View File

@@ -75,7 +75,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)])); grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)]));
return slots; return slots;
} }
public static Slotinfo[] scoreSlots(int[] slotScores, Slot[] slots) { public static Slotinfo[] scoreSlots(Slot[] slots) {
val count = new byte[SwedishGenerator.SIZE]; val count = new byte[SwedishGenerator.SIZE];
Slotinfo[] slotInfo = new Slotinfo[slots.length]; Slotinfo[] slotInfo = new Slotinfo[slots.length];
for (var s : slots) { for (var s : slots) {
@@ -84,8 +84,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
} }
for (int i = 0; i < slots.length; i++) { for (int i = 0; i < slots.length; i++) {
var slot = slots[i]; var slot = slots[i];
slotScores[i] = slotScore(count, slot.lo, slot.hi); slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScore(count, slot.lo, slot.hi), new Assign(), slot.entry);
slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScores[i], new Assign(), slot.entry);
} }
return slotInfo; return slotInfo;
} }
@@ -115,16 +114,14 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
if (hLo != X) { if (hLo != X) {
rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1); rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1);
rHi = 0; rHi = 0;
} else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); } } else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
} else { } else if (hHi != X) {
if (hHi != X) { int msb = 63 - numberOfLeadingZeros(hHi);
int msb = 63 - numberOfLeadingZeros(hHi); rHi &= -(1L << msb << 1);
rHi &= -(1L << msb << 1); rLo = 0;
rLo = 0; } else if (hLo != X) {
} else if (hLo != X) { int msb = 63 - numberOfLeadingZeros(hLo);
int msb = 63 - numberOfLeadingZeros(hLo); rLo &= -(1L << msb << 1);
rLo &= -(1L << msb << 1);
}
} }
if ((rLo | rHi) != X) { if ((rLo | rHi) != X) {
hasSlots = true; hasSlots = true;
@@ -136,9 +133,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
cVHi |= rHi; cVHi |= rHi;
} }
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else { } else penalty += 25000;
penalty += 25000;
}
} }
for (long bits = hi_cl; bits != X; bits &= bits - 1) { for (long bits = hi_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits; long lsb = bits & -bits;
@@ -152,16 +147,14 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
if (hLo != X) { if (hLo != X) {
rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1); rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1);
rHi = 0; rHi = 0;
} else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); } } else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
} else { } else if (hHi != X) {
if (hHi != X) { int msb = 63 - numberOfLeadingZeros(hHi);
int msb = 63 - numberOfLeadingZeros(hHi); rHi &= -(1L << msb << 1);
rHi &= -(1L << msb << 1); rLo = 0;
rLo = 0; } else if (hLo != X) {
} else if (hLo != X) { int msb = 63 - numberOfLeadingZeros(hLo);
int msb = 63 - numberOfLeadingZeros(hLo); rLo &= -(1L << msb << 1);
rLo &= -(1L << msb << 1);
}
} }
if ((rLo | rHi) != X) { if ((rLo | rHi) != X) {
hasSlots = true; hasSlots = true;
@@ -173,9 +166,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
cVHi |= rHi; cVHi |= rHi;
} }
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else { } else penalty += 25000;
penalty += 25000;
}
} }
if (!hasSlots) return 1_000_000_000L; if (!hasSlots) return 1_000_000_000L;
@@ -297,11 +288,9 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
var d_idx = rng.randint2bitByte(); var d_idx = rng.randint2bitByte();
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueLo(1L << ri, d_idx); if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueLo(1L << ri, d_idx);
} }
} else { } else if (!c.cluelessHi(ri)) {
if (!c.cluelessHi(ri)) { var d_idx = rng.randint2bitByte();
var d_idx = rng.randint2bitByte(); if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueHi(1L << (ri & 63), d_idx);
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueHi(1L << (ri & 63), d_idx);
}
} }
} }
@@ -315,13 +304,12 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
var nr = Math.sin(theta); var nr = Math.sin(theta);
long maskLo = 0, maskHi = 0; long maskLo = 0, maskHi = 0;
for (var rci : IT) { for (var rci : IT)
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) { if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
int i = rci.i(); int i = rci.i();
if ((i & 64) == 0) maskLo |= (1L << i); if ((i & 64) == 0) maskLo |= (1L << i);
else maskHi |= (1L << (i - 64)); else maskHi |= (1L << (i - 64));
} }
}
var c = new Clues( var c = new Clues(
(a.lo & ~maskLo) | (other.lo & maskLo), (a.lo & ~maskLo) | (other.lo & maskLo),
(a.hi & ~maskHi) | (other.hi & maskHi), (a.hi & ~maskHi) | (other.hi & maskHi),
@@ -403,12 +391,11 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
for (var cand : pop) { for (var cand : pop) {
if (next.size() >= offspring) break; if (next.size() >= offspring) break;
var ok = true; var ok = true;
for (var kept : next) { for (var kept : next)
if (cand.grid.similarity(kept.grid) > 0.92) { if (cand.grid.similarity(kept.grid) > 0.92) {
ok = false; ok = false;
break; break;
} }
}
if (ok) next.add(cand); if (ok) next.add(cand);
} }
pop = next; pop = next;
@@ -428,12 +415,6 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
public static class Clues { public static class Clues {
long lo, hi, vlo, vhi, rlo, rhi; long lo, hi, vlo, vhi, rlo, rhi;
public long lo() { return lo; }
public long hi() { return hi; }
public long vlo() { return vlo; }
public long vhi() { return vhi; }
public long rlo() { return rlo; }
public long rhi() { return rhi; }
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); } public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); }
public static Clues parse(String s) { public static Clues parse(String s) {

View File

@@ -31,7 +31,7 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
* java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt] * java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt]
*/ */
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
public class SwedishGenerator { public record SwedishGenerator() {
public static final long GT_1_OFFSET_53_BIT = 0x3E00000000000000L; public static final long GT_1_OFFSET_53_BIT = 0x3E00000000000000L;
public static final long X = 0L; public static final long X = 0L;
@@ -160,8 +160,9 @@ public class SwedishGenerator {
for (int i = 0, bi = 0; i < len * 5; bi++, i += 5) b[bi] = (byte) (((word >>> i) & 31) | 64); for (int i = 0, bi = 0; i < len * 5; bi++, i += 5) b[bi] = (byte) (((word >>> i) & 31) | 64);
return new String(b, 0, 0, len); return new String(b, 0, 0, len);
} }
static int unpackIndex(long w) { return (int) (w >>> 40); } static int unpackIndex(long w) { return (int) (w >>> 40); }
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); } static int unpackShardIndex(long w) { return (int) (w >>> 43); }
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
} }
public static record Dict(DictEntry[] index, int length) { } public static record Dict(DictEntry[] index, int length) { }
@@ -284,35 +285,36 @@ public class SwedishGenerator {
System.out.print("\r" + padRight(msg, 120)); System.out.print("\r" + padRight(msg, 120));
System.out.flush(); System.out.flush();
} }
boolean placeWord(final int key, final long lo, final long hi, final long w) { boolean placeWordDec(final long lo, final long hi, final long w) {
int idx; int idx;
if (Slotinfo.increasing(key)) { int bcHi = bitCount(hi);
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; for (long b = hi & ghi; b != X; b &= b - 1)
int bcLo = bitCount(lo); if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false;
for (long b = hi & ghi; b != X; b &= b - 1) for (long b = lo & glo; b != X; b &= b - 1)
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false; 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; long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) { 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, bitCount(hi & ~((1L << idx) | ((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))); 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; glo |= maskLo;
ghi |= maskHi; ghi |= maskHi;
} }
} else { return true;
int bcHi = bitCount(hi); }
for (long b = hi & ghi; b != X; b &= b - 1) boolean placeWordInc(final long lo, final long hi, final long w) {
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; int idx;
for (long b = lo & glo; b != X; b &= b - 1) 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;
if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((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; long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != X) { 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 = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((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)))); 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; glo |= maskLo;
ghi |= maskHi; ghi |= maskHi;
}
} }
return true; return true;
} }
@@ -345,21 +347,15 @@ public class SwedishGenerator {
} }
var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi); var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi);
var index = best.entry; var index = best.entry;
current = CARRIER; current = CARRIER;
current.slot = best; current.slot = best;
current.count = index.length; current.count = index.length;
if (pattern == X) { current.indices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
current.indices = null;
return;
}
current.indices = candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
} }
boolean backtrack(int depth) { boolean backtrack(int depth) {
if (Thread.currentThread().isInterrupted()) return false; if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false;
nodes++; nodes++;
if (20_000 > 0 && (System.currentTimeMillis() - t0) > 20_000) return false;
chooseMRV(); chooseMRV();
var pick = current; var pick = current;
if (pick == PICK_DONE) return true; if (pick == PICK_DONE) return true;
@@ -372,7 +368,7 @@ public class SwedishGenerator {
if (!NO_LOG) renderProgress(); if (!NO_LOG) renderProgress();
val s = pick.slot; val s = pick.slot;
val k = s.key; val inc = Slotinfo.increasing(s.key);
val slo = s.lo; val slo = s.lo;
val shi = s.hi; val shi = s.hi;
val entry = s.entry; val entry = s.entry;
@@ -392,10 +388,14 @@ public class SwedishGenerator {
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
top = ghi; top = ghi;
if (!placeWord(k, slo, shi, w)) continue; if (inc) {
if (!placeWordInc(slo, shi, w)) continue;
} else {
if (!placeWordDec(slo, shi, w)) continue;
}
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
s.assign.w = w; s.assign.w = w;
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
s.assign.w = X; s.assign.w = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lemIdx);
@@ -417,10 +417,14 @@ public class SwedishGenerator {
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
top = ghi; top = ghi;
if (!placeWord(k, slo, shi, w)) continue; if (inc) {
if (!placeWordInc(slo, shi, w)) continue;
} else {
if (!placeWordDec(slo, shi, w)) continue;
}
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
s.assign.w = w; s.assign.w = w;
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
s.assign.w = X; s.assign.w = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lemIdx);

View File

@@ -198,7 +198,7 @@ public class MainTest {
"222 3"); "222 3");
Assertions.assertEquals(20, mask.clueCount()); Assertions.assertEquals(20, mask.clueCount());
var slots = Masker.extractSlots(mask, dict.index()); var slots = Masker.extractSlots(mask, dict.index());
val slotInfo = Masker.scoreSlots(new int[slots.length], slots); val slotInfo = Masker.scoreSlots(slots);
var grid = mask.toGrid(); var grid = mask.toGrid();
// var filled = fillMask(rng, slotInfo, grid, false); // var filled = fillMask(rng, slotInfo, grid, false);
// val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0)); // val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0));
@@ -216,7 +216,7 @@ public class MainTest {
" 1 2\n" + " 1 2\n" +
"21 22 3"); "21 22 3");
var slots = Masker.extractSlots(mask, dict.index()); var slots = Masker.extractSlots(mask, dict.index());
val slotInfo = Masker.scoreSlots(new int[slots.length], slots); val slotInfo = Masker.scoreSlots(slots);
var grid = mask.toGrid(); var grid = mask.toGrid();
var filled = fillMask(rng, slotInfo, grid, false); var filled = fillMask(rng, slotInfo, grid, false);
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");