This commit is contained in:
mike
2026-01-13 20:03:19 +01:00
parent 81ae0aa84c
commit 6ab6791d9c
6 changed files with 114 additions and 83 deletions

View File

@@ -31,8 +31,6 @@ import static java.nio.charset.StandardCharsets.*;
public record SwedishGenerator(Rng rng) {
record CandidateInfo(int[] indices, int count) { }
// static final CandidateInfo[] CANDIDATES = IntStream.range(0, 10192 << 2).mapToObj(CandidateInfo::new).toArray(CandidateInfo[]::new);
//@formatter:off
@FunctionalInterface interface SlotVisitor { void visit(int key, long lo, long hi); }
@@ -100,22 +98,35 @@ public record SwedishGenerator(Rng rng) {
public static record FillResult(boolean ok,
Gridded grid,
Map<Integer, Long> clueMap,
long[] clueMap,
FillStats stats) {
public void calcSimpel() {
if (ok) {
clueMap.forEach((k, v) -> stats.simplicity += Lemma.simpel(v));
stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size();
int k = 0;
for (var n = 1; n < clueMap.length; n++) {
if (clueMap[n] != 0L) {
k++;
stats.simplicity += Lemma.simpel(clueMap[n]);
}
}
stats.simplicity = k == 0 ? 0 : stats.simplicity / k;
}
}
public int wordCount() {
int k = 0;
for (var n = 1; n < clueMap.length; n++) {
if (clueMap[n] != 0L) {
k++;
}
}
return k;
}
}
static final class Context {
final int[] cellCount = new int[SIZE];
final int[] stack = new int[SIZE];
final int[] stack = new int[SIZE];
long pattern;
final long[] undo = new long[128];
final long[] bitset = new long[2500];
@@ -245,7 +256,6 @@ public record SwedishGenerator(Rng rng) {
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 usedCharsInPattern(long p) { return ((63 - Long.numberOfLeadingZeros(p & LETTER_MASK)) / 5) + 1; }
public static String asWord(long word) {
var b = new byte[Lemma.length(word)];
for (var i = 0; i < b.length; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64);
@@ -325,8 +335,7 @@ public record SwedishGenerator(Rng rng) {
}
private static void processSlot(Grid grid, SlotVisitor visitor, int idx) {
int d = grid.digitAt(idx); // 0..3
int key = Slot.packSlotDir(idx, d);
int key = Slot.packSlotDir(idx, grid.digitAt(idx)); // 0..3
long rayLo = PATH_LO[key];
long rayHi = PATH_HI[key];
@@ -338,7 +347,7 @@ public record SwedishGenerator(Rng rng) {
// slice ray to stop before first clue, depending on direction monotonicity
// right/down => increasing indices; up/left => decreasing indices
if (Slot.increasing(d)) {
if (Slot.increasing(key)) {
// first clue is lowest index among hits (lo first, then hi)
if (hitsLo != 0) {
long stop = 1L << Long.numberOfTrailingZeros(hitsLo);
@@ -375,7 +384,7 @@ public record SwedishGenerator(Rng rng) {
}
long maskFitness(Grid grid) {
var ctx = CTX.get();
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
long lo_cl = grid.lo, hi_cl = grid.hi;
long penalty = (((long) Math.abs(grid.clueCount() - TARGET_CLUES)) * 16000L);
@@ -384,11 +393,10 @@ public record SwedishGenerator(Rng rng) {
for (int i = 0; i < 65; i += 64) {
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
int clueIdx = i | Long.numberOfTrailingZeros(bits);
int d = grid.digitAt(clueIdx);
int key = (clueIdx << 2) | d;
int key = Slot.packSlotDir(clueIdx, grid.digitAt(clueIdx));
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slot.increasing(d)) {
if (Slot.increasing(key)) {
if (hLo != 0) {
rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1);
rHi = 0;
@@ -405,7 +413,7 @@ public record SwedishGenerator(Rng rng) {
}
if ((rLo | rHi) != 0) {
hasSlots = true;
if (Slot.horiz(d)) {
if (Slot.horiz(key)) {
cHLo |= rLo;
cHHi |= rHi;
} else {
@@ -420,7 +428,7 @@ public record SwedishGenerator(Rng rng) {
}
if (!hasSlots) return 1_000_000_000L;
var ctx = CTX.get();
var stack = ctx.stack;
long seenLo = 0L, seenHi = 0L;
@@ -512,7 +520,7 @@ public record SwedishGenerator(Rng rng) {
idx = rng.randint(0, SIZE_MIN_1);
if (g.isClue(idx)) continue;
var d_idx = rng.randint2bitByte();
if (g.hasRoomForClue(OFFSETS_D_IDX[d_idx | idx << 2])) {
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(idx, d_idx)])) {
g.setClue(idx, d_idx);
placed++;
}
@@ -527,7 +535,7 @@ public record SwedishGenerator(Rng rng) {
ri = bytes[rng.randint(0, 624)];
if (!g.clueless(ri)) {
var d_idx = rng.randint2bitByte();
val packed = OFFSETS_D_IDX[d_idx | ri << 2];
val packed = OFFSETS_D_IDX[Slot.packSlotDir(ri, d_idx)];
if (g.hasRoomForClue(packed)) g.setClue(ri, d_idx);
}
}
@@ -562,7 +570,7 @@ public record SwedishGenerator(Rng rng) {
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearClues(out, 64 | Long.numberOfTrailingZeros(hi));
return out;
}
public static void clearClues(Grid out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[(out.digitAt(idx)) | (idx << 2)])) out.clearClue(idx); }
public static void clearClues(Grid out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(idx, out.digitAt(idx))])) out.clearClue(idx); }
Grid hillclimb(Grid start, int limit) {
var best = start;
@@ -644,26 +652,38 @@ public record SwedishGenerator(Rng rng) {
if (s.increasing()) {
for (long b = s.lo & ~grid.lo; b != 0; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b);
int i = Long.bitCount(s.lo & ((1L << idx) - 1));
p |= ((long) (grid.g[idx] & 31)) << (i * 5);
int val = grid.g[idx] & 31;
if (val != 0) {
int i = Long.bitCount(s.lo & ((1L << idx) - 1));
p |= ((long) (i * 26 + val)) << (i << 3);
}
}
int offset = Long.bitCount(s.lo);
for (long b = s.hi & ~grid.hi; b != 0; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b);
int i = offset + Long.bitCount(s.hi & ((1L << idx) - 1));
p |= ((long) (grid.g[64 | idx] & 31)) << (i * 5);
int val = grid.g[64 | idx] & 31;
if (val != 0) {
int i = offset + Long.bitCount(s.hi & ((1L << idx) - 1));
p |= ((long) (i * 26 + val)) << (i << 3);
}
}
} else {
int offset = Long.bitCount(s.hi);
for (long b = s.hi & ~grid.hi; b != 0; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b);
int i = Long.bitCount(s.hi & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (grid.g[64 | idx] & 31)) << (i * 5);
int val = grid.g[64 | idx] & 31;
if (val != 0) {
int i = Long.bitCount(s.hi & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (i * 26 + val)) << (i << 3);
}
}
for (long b = s.lo & ~grid.lo; b != 0; b &= b - 1) {
int idx = Long.numberOfTrailingZeros(b);
int i = offset + Long.bitCount(s.lo & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (grid.g[idx] & 31)) << (i * 5);
int val = grid.g[idx] & 31;
if (val != 0) {
int i = offset + Long.bitCount(s.lo & ~((1L << idx) | ((1L << idx) - 1)));
p |= ((long) (i * 26 + val)) << (i << 3);
}
}
}
return p;
@@ -743,16 +763,19 @@ public record SwedishGenerator(Rng rng) {
long[] res = ctx.bitset;
boolean first = true;
for (int i = 0, len = lenb/*Lemma.usedCharsInPattern(pattern)*/; i < len; i++) {
int val = (int) ((pattern >>> (i * 5)) & 31);
if (val != 0) {
long[] bs = entry.posBitsets[i * 26 + (val - 1)];
for (long p = pattern; p != 0; ) {
int combined = (int) (p & 0xFF);
if (combined != 0) {
long[] bs = entry.posBitsets[combined - 1];
if (first) {
System.arraycopy(bs, 0, res, 0, numLongs);
first = false;
} else {
for (int k = 0; k < numLongs; k++) res[k] &= bs[k];
}
p >>>= 8;
} else {
p >>>= (Long.numberOfTrailingZeros(p) & ~7);
}
}
@@ -783,16 +806,19 @@ public record SwedishGenerator(Rng rng) {
long[] res = ctx.bitset;
boolean first = true;
for (int i = 0, len = lenb /*Lemma.usedCharsInPattern(pattern)*/; i < len; i++) {
int val = (int) ((pattern >>> (i * 5)) & 31);
if (val != 0) {
long[] bs = entry.posBitsets[i * 26 + (val - 1)];
for (long p = pattern; p != 0; ) {
int combined = (int) (p & 0xFF);
if (combined != 0) {
long[] bs = entry.posBitsets[combined - 1];
if (first) {
System.arraycopy(bs, 0, res, 0, numLongs);
first = false;
} else {
for (int k = 0; k < numLongs; k++) res[k] &= bs[k];
}
p >>>= 8;
} else {
p >>>= (Long.numberOfTrailingZeros(p) & ~7);
}
}
@@ -803,7 +829,7 @@ public record SwedishGenerator(Rng rng) {
return count;
}
//72 << 3;
static final int BIGG = 581 + 1;
static final int BIGG = (288 | 3) + 1;
public FillResult fillMask(Grid mask, DictEntry[] dictIndex,
int timeLimitMs) {
val multiThreaded = Thread.currentThread().getName().contains("pool");
@@ -812,8 +838,7 @@ public record SwedishGenerator(Rng rng) {
long[] assigned = new long[BIGG];
val ctx = CTX.get();
val count = ctx.cellCount;
Arrays.fill(count, 0, SIZE, 0);
val count = new int[SIZE];
val slots = extractSlots(grid);
for (var s : slots) {
@@ -912,7 +937,7 @@ public record SwedishGenerator(Rng rng) {
for (var t = 0; t < tries; t++) {
double r = rng.nextFloat();
int idxInArray = (int) (r * r * r * L);
int idxInArray = (int) (r * r * r * (L - 1));
var idx = idxs[idxInArray];
var w = entry.words[idx];
var lemIdx = Lemma.unpackIndex(w);
@@ -942,7 +967,7 @@ public record SwedishGenerator(Rng rng) {
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
for (var t = 0; t < tries; t++) {
double r = rng.nextFloat();
int idxInArray = (int) (r * r * r * N);
int idxInArray = (int) (r * r * r * (N - 1));
var w = entry.words[idxInArray];
var lemIdx = Lemma.unpackIndex(w);
if (used.get(lemIdx)) continue;
@@ -975,20 +1000,14 @@ public record SwedishGenerator(Rng rng) {
}
stats.seconds = (System.currentTimeMillis() - t0) / 1000.0;
Map<Integer, Long> lemmaMap = new HashMap<>();
for (var i = 0; i < assigned.length; i++) {
if (assigned[i] != X) {
lemmaMap.put(i, assigned[i]);
}
}
var res = new FillResult(ok, new Gridded(grid), lemmaMap, stats);
var res = new FillResult(ok, new Gridded(grid), assigned, stats);
// print a final progress line
if (Main.VERBOSE && !multiThreaded) {
System.out.println(
String.format(Locale.ROOT,
"[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs",
lemmaMap.size(), TOTAL, stats.nodes, stats.backtracks, stats.lastMRV, stats.seconds
res.wordCount(), TOTAL, stats.nodes, stats.backtracks, stats.lastMRV, stats.seconds
)
);
}