introduce bitloops

This commit is contained in:
mike
2026-01-13 01:17:08 +01:00
parent 6119722867
commit 81ae0aa84c
3 changed files with 59 additions and 76 deletions

View File

@@ -4,7 +4,6 @@ import lombok.Getter;
import lombok.val;
import precomp.Neighbors9x8;
import precomp.Neighbors9x8.rci;
import puzzle.Export.Bit;
import puzzle.Export.Bit1029;
import puzzle.Export.DictEntryDTO;
import puzzle.Export.Gridded;
@@ -53,7 +52,6 @@ public record SwedishGenerator(Rng rng) {
static final int MAX_WORD_LENGTH = C <= R ? C : R;
static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
static final int MIN_LEN = Config.MIN_LEN;
static final int MIN_LEN7 = Config.MIN_LEN * 7;
static final int MAX_TRIES_PER_SLOT = Config.MAX_TRIES_PER_SLOT;
static final char C_DASH = '\0';
static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH;
@@ -66,15 +64,15 @@ public record SwedishGenerator(Rng rng) {
// 0b01
// 0b10
static final byte B0 = (byte) 0;
static final byte B64 = (byte) 64;
static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX_0_BASE;
static final rci[] IT = Neighbors9x8.IT;
static final int[][] MUTATE_RI = new int[SIZE][625];
static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
static final long[] PATH_LO = Neighbors9x8.PATH_LO;
static final long[] PATH_HI = Neighbors9x8.PATH_HI;
static final byte B0 = (byte) 0;
static final byte B64 = (byte) 64;
static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX_0_BASE;
static final rci[] IT = Neighbors9x8.IT;
static final int[][] MUTATE_RI = new int[SIZE][625];
static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
static final long[] PATH_LO = Neighbors9x8.PATH_LO;
static final long[] PATH_HI = Neighbors9x8.PATH_HI;
static {
for (int i = 0; i < SIZE; i++) {
@@ -116,11 +114,8 @@ public record SwedishGenerator(Rng rng) {
static final class Context {
final Bit covH2 = new Bit();
final Bit covV2 = new Bit();
final int[] cellCount = new int[SIZE];
final int[] stack = new int[SIZE];
final Bit seen = new Bit();
long pattern;
final long[] undo = new long[128];
final long[] bitset = new long[2500];
@@ -149,7 +144,8 @@ public record SwedishGenerator(Rng rng) {
var range = (long) max - (long) min + 1L;
return (byte) (min + (u % range));
}
int randint2bit() { return nextU32() & 3; }
int randint2bit() { return nextU32() & 3; }
byte randint2bitByte() { return (byte) (nextU32() & 3); }
int randint(int min, int max) {
var u = (nextU32() & 0xFFFFFFFFL);
var range = (long) max - (long) min + 1L;
@@ -227,7 +223,7 @@ public record SwedishGenerator(Rng rng) {
}
}
static record DictEntry(long[] words, long[][] posBitsets) { }
static record DictEntry(long[] words, long[][] posBitsets, int numlong) { }
static int LEMMA_COUNTER = 0;
@@ -240,7 +236,7 @@ public record SwedishGenerator(Rng rng) {
static long pack(int index, byte[] b) { return pack(b) | ((long) index << 40); }
static long pack(byte[] b) {
long w = 0;
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & ~64) << (i * 5);
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & 63) << (i * 5);
return w;
}
static public long from(String word) { return pack(LEMMA_COUNTER++, word.getBytes(US_ASCII)); }
@@ -295,7 +291,7 @@ public record SwedishGenerator(Rng rng) {
}
}
}
return new DictEntry(words, bitsets);
return new DictEntry(words, bitsets, (words.length + 63) >>> 6);
}).toArray(DictEntry[]::new),
Arrays.stream(index).mapToInt(i -> i.words().size()).sum());
}
@@ -330,7 +326,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 = (idx << 2) | d;
int key = Slot.packSlotDir(idx, d);
long rayLo = PATH_LO[key];
long rayHi = PATH_HI[key];
@@ -341,9 +337,8 @@ public record SwedishGenerator(Rng rng) {
// slice ray to stop before first clue, depending on direction monotonicity
// right/down => increasing indices; up/left => decreasing indices
boolean increasing = Slot.increasing(d);
if (increasing) {
if (Slot.increasing(d)) {
// first clue is lowest index among hits (lo first, then hi)
if (hitsLo != 0) {
long stop = 1L << Long.numberOfTrailingZeros(hitsLo);
@@ -369,7 +364,7 @@ public record SwedishGenerator(Rng rng) {
}
if ((rayLo | rayHi) != 0) {
visitor.visit(Slot.packSlotDir(idx, d), rayLo, rayHi);
visitor.visit(key, rayLo, rayHi);
}
}
@@ -380,11 +375,8 @@ public record SwedishGenerator(Rng rng) {
}
long maskFitness(Grid grid) {
var ctx = CTX.get();
var covH = ctx.covH2;
var covV = ctx.covV2;
covH.clear();
covV.clear();
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);
boolean hasSlots = false;
@@ -413,8 +405,13 @@ public record SwedishGenerator(Rng rng) {
}
if ((rLo | rHi) != 0) {
hasSlots = true;
if (Slot.horiz(d)) covH.or(rLo, rHi);
else covV.or(rLo, rHi);
if (Slot.horiz(d)) {
cHLo |= rLo;
cHHi |= rHi;
} else {
cVLo |= rLo;
cVHi |= rHi;
}
if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else {
penalty += 25000;
@@ -485,18 +482,25 @@ public record SwedishGenerator(Rng rng) {
}
}
for (int i = 0; i < 65; i += 64) {
long bits = (i == 0 ? ~lo_cl : (~hi_cl & 0xFFL));
for (; bits != X; bits &= bits - 1) {
int clueIdx = i + Long.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;
var h = covH.get(clueIdx);
var v = covV.get(clueIdx);
if (!h && !v) penalty += 1500;
else if (h && v) { /* ok */ } else if (h | v) penalty += 200;
else penalty += 600;
}
for (long bits = ~lo_cl; bits != X; bits &= bits - 1) {
int clueIdx = Long.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;
boolean h = (cHLo & (1L << clueIdx)) != 0;
boolean v = (cVLo & (1L << clueIdx)) != 0;
if (!h && !v) penalty += 1500;
else if (h && v) { /* ok */ } else if (h | v) penalty += 200;
else penalty += 600;
}
for (long bits = ~hi_cl & 0xFFL; bits != X; bits &= bits - 1) {
int clueIdx = Long.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;
boolean h = (cHHi & (1L << clueIdx)) != 0;
boolean v = (cVHi & (1L << clueIdx)) != 0;
if (!h && !v) penalty += 1500;
else if (h && v) { /* ok */ } else if (h | v) penalty += 200;
else penalty += 600;
}
return penalty;
@@ -507,9 +511,9 @@ public record SwedishGenerator(Rng rng) {
for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) {
idx = rng.randint(0, SIZE_MIN_1);
if (g.isClue(idx)) continue;
int d_idx = rng.randint2bit();
var d_idx = rng.randint2bitByte();
if (g.hasRoomForClue(OFFSETS_D_IDX[d_idx | idx << 2])) {
g.setClue(idx, (byte) d_idx);
g.setClue(idx, d_idx);
placed++;
}
}
@@ -522,9 +526,9 @@ public record SwedishGenerator(Rng rng) {
for (var k = 0; k < 4; k++) {
ri = bytes[rng.randint(0, 624)];
if (!g.clueless(ri)) {
int d_idx = rng.randint2bit();
var d_idx = rng.randint2bitByte();
val packed = OFFSETS_D_IDX[d_idx | ri << 2];
if (g.hasRoomForClue(packed)) g.setClue(ri, (byte) d_idx);
if (g.hasRoomForClue(packed)) g.setClue(ri, d_idx);
}
}
return g;
@@ -555,7 +559,7 @@ public record SwedishGenerator(Rng rng) {
out.lo = bo0;
out.hi = bo1;
for (var lo = out.lo; lo != X; lo &= lo - 1L) clearClues(out, Long.numberOfTrailingZeros(lo));
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearClues(out, 64 + Long.numberOfTrailingZeros(hi));
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); }
@@ -735,11 +739,11 @@ public record SwedishGenerator(Rng rng) {
return new CandidateInfo(null, entry.words.length);
}
int numLongs = (entry.words.length + 63) >>> 6;
int numLongs = entry.numlong;
long[] res = ctx.bitset;
boolean first = true;
for (int i = 0, len = Lemma.usedCharsInPattern(pattern); i < len; i++) {
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)];
@@ -771,15 +775,15 @@ public record SwedishGenerator(Rng rng) {
return new CandidateInfo(indices, count);
}
static int candidateCountForPattern(Context ctx, DictEntry entry) {
static int candidateCountForPattern(Context ctx, DictEntry entry, int lenb) {
long pattern = ctx.pattern;
if (pattern == X) return entry.words.length;
int numLongs = (entry.words.length + 63) >>> 6;
int numLongs = entry.numlong;
long[] res = ctx.bitset;
boolean first = true;
for (int i = 0, len = Lemma.usedCharsInPattern(pattern); i < len; i++) {
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)];
@@ -859,7 +863,7 @@ public record SwedishGenerator(Rng rng) {
var entry = dictIndex[s.len()];
if (entry == null) return PICK_NOT_DONE;
ctx.pattern = patternForSlot(grid, s);
int count = candidateCountForPattern(ctx, entry);
int count = candidateCountForPattern(ctx, entry, s.len());
if (count == 0) return PICK_NOT_DONE;
if (best == null