introduce bitloops

This commit is contained in:
mike
2026-01-17 04:18:35 +01:00
parent 81ea708345
commit 47b33af09d
8 changed files with 680 additions and 676 deletions

View File

@@ -17,11 +17,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.stream.IntStream;
import static java.lang.Long.*;
import static java.lang.Long.numberOfTrailingZeros;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -43,77 +40,48 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
* java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt]
*/
@SuppressWarnings("ALL")
public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
public class SwedishGenerator {
record CandidateInfo(int[] indices, int count) { }
//@formatter:off
@FunctionalInterface interface SlotVisitor { void visit(int key, long lo, long hi); }
//@formatter:on
static final long GT_1_OFFSET_53_BIT = 0x3E00000000000000L;
static final long X = 0L;
static final int LOG_EVERY_MS = 200;
static final int BAR_LEN = 22;
static final int C = Config.PUZZLE_COLS;
static final double CROSS_R = (C - 1) / 2.0;
static final int R = Config.PUZZLE_ROWS;
static final double CROSS_C = (R - 1) / 2.0;
static final int SIZE = C * R;// ~18
static final int SIZE_MIN_1 = SIZE - 1;// ~18
static final double SIZED = (double) SIZE;// ~18
static final long MASK_LO = (SIZE >= 64) ? -1L : (1L << SIZE) - 1;
static final long MASK_HI = (SIZE <= 64) ? 0L : (SIZE >= 128 ? -1L : (1L << (SIZE - 64)) - 1);
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 MAX_TRIES_PER_SLOT = Config.MAX_TRIES_PER_SLOT;
static final int STACK_SIZE = 64;
static final char C_DASH = '\0';
static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH;
static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L;
static final long RANGE_0_624 = 624L - 0L + 1L;
//72 << 3;
static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1;
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
public static final long GT_1_OFFSET_53_BIT = 0x3E00000000000000L;
public static final long X = 0L;
public static final int LOG_EVERY_MS = 200;
public static final int BAR_LEN = 22;
public static final int C = Config.PUZZLE_COLS;
public static final double CROSS_R = (C - 1) / 2.0;
public static final int R = Config.PUZZLE_ROWS;
public static final double CROSS_C = (R - 1) / 2.0;
public static final int SIZE = C * R;// ~18
public static final int SIZE_MIN_1 = SIZE - 1;// ~18
public static final double SIZED = (double) SIZE;// ~18
public static final long MASK_LO = (SIZE >= 64) ? -1L : (1L << SIZE) - 1;
public static final long MASK_HI = (SIZE <= 64) ? 0L : (SIZE >= 128 ? -1L : (1L << (SIZE - 64)) - 1);
public static final int MAX_WORD_LENGTH = C <= R ? C : R;
public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
public static final int MIN_LEN = Config.MIN_LEN;
public static final int MAX_TRIES_PER_SLOT = Config.MAX_TRIES_PER_SLOT;
public static final int STACK_SIZE = 64;
public static final char C_DASH = '\0';
public static final byte DASH = (byte) C_DASH;
public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L;
public static final long RANGE_0_624 = 624L - 0L + 1L;
public static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1;
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
@AllArgsConstructor
static class Pick {
public static class Pick {
Slotinfo slot;
int[] indices;
int count;
public Slotinfo slot;
public int[] indices;
public int count;
}
// 0b11
//0b00
// 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 {
for (int i = 0; i < 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++) {
val ti = IT[i];
MUTATE_RI[i][k++] = Grid.offset(clamp(ti.r() + dr1 + dr2, 0, R - 1),
clamp(ti.c() + dc1 + dc2, 0, C - 1));
}
}
}
static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX_0_BASE;
public static final rci[] IT = Neighbors9x8.IT;
public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
public static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
@RequiredArgsConstructor
@Getter
@@ -128,7 +96,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
}
public static record FillResult(boolean ok,
Gridded grid,
@Delegate FillStats stats) {
static public long calcSimpel(Slotinfo[] slots) {
@@ -145,15 +113,15 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
}
}
static final class Rng {
public static final class Rng {
@Getter private int x;
Rng(int seed) {
public Rng(int seed) {
var s = seed;
if (s == 0) s = 1;
this.x = s;
}
int nextU32() {
public int nextU32() {
var y = x;
y ^= (y << 13);
y ^= (y >>> 17);
@@ -161,99 +129,24 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
x = y;
return y;
}
int randint2bit() { return nextU32() & 3; }
byte randint2bitByte() { return (byte) (nextU32() & 3); }
int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max - 0L + 1L))); }
int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); }
int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); }
double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); }
public int randint2bit() { return nextU32() & 3; }
public byte randint2bitByte() { return (byte) (nextU32() & 3); }
public int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max - 0L + 1L))); }
public int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); }
public int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); }
public double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
public int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); }
}
@AllArgsConstructor
static class Clues {
public static class Grid {
long lo, hi, vlo, vhi, rlo, rhi;
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); }
boolean cluelessLo(int idx) {
if (!isClueLo(idx)) return false;
clearClueLo(~(1L << idx));
return true;
}
boolean cluelessHi(int idx) {
if (!isClueHi(idx)) return false;
clearClueHi(~(1L << (idx & 63)));
return true;
}
public boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
public void setClueLo(long mask, byte idx) {
lo |= mask;
if ((idx & 1) != 0) vlo |= mask;
else vlo &= ~mask;
if ((idx & 2) != 0) rlo |= mask;
else rlo &= ~mask;
}
public void setClueHi(long mask, byte idx) {
hi |= mask;
if ((idx & 1) != 0) vhi |= mask;
else vhi &= ~mask;
if ((idx & 2) != 0) rhi |= mask;
else rhi &= ~mask;
}
void clearClueLo(long mask) {
lo &= mask;
vlo &= mask;
rlo &= mask;
}
void clearClueHi(long mask) {
hi &= mask;
vhi &= mask;
rhi &= mask;
}
public boolean isClueLo(int index) { return ((lo >>> index) & 1L) != X; }
public boolean isClueHi(int index) { return ((hi >>> (index & 63)) & 1L) != X; }
public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
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)));
long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi)));
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
}
public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); }
public void forEachSlot(SlotVisitor visitor) {
for (var l = lo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1));
for (var l = lo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 0));
for (var l = lo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2));
for (var l = lo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3));
for (var h = hi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1));
for (var h = hi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0));
for (var h = hi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2));
for (var h = hi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3));
}
public Clues from(Clues best) {
lo = best.lo;
hi = best.hi;
vlo = best.vlo;
vhi = best.vhi;
rlo = best.rlo;
rhi = best.rhi;
return this;
}
public final byte[] g;
public long lo, hi;
public static int offset(int r, int c) { return r | (c << 3); }
}
@AllArgsConstructor
static class Grid {
final byte[] g;
public long lo, hi;
static int offset(int r, int c) { return r | (c << 3); }
}
static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
public static interface Lemma {
@@ -343,399 +236,15 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
for (var n = 1; n < arr.length; n++) if (arr[n].assign.w != X) k++;
return k;
}
public static boolean increasing(int dir) { return (dir & 2) == 0; }
}
static record Slot(int key, long lo, long hi, DictEntry entry) {
static final int BIT_FOR_DIR = 2;
static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
public static int length(long lo, long hi) { return bitCount(lo) + bitCount(hi); }
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
public static int dir(int key) { return key & 3; }
public static boolean increasing(int dir) { return (dir & 2) == 0; }
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
public static boolean horiz(int d) { return (d & 1) != 0; }
public static int packSlotKey(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
}
// slice ray to stop before first clue, depending on direction monotonicity
// 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];
// only consider clue cells
long hitsLo = rayLo & c.lo;
long hitsHi = rayHi & c.hi;
if (hitsHi != X) {
int msb = 63 - numberOfLeadingZeros(hitsHi);
long stop = 1L << msb;
rayHi &= ~((stop << 1) - 1); // keep bits > stop
rayLo = 0; // lo indices are below stop
} else if (hitsLo != X) {
int msb = 63 - numberOfLeadingZeros(hitsLo);
long stop = 1L << msb;
rayLo &= ~((stop << 1) - 1);
}
visitor.visit(key, rayLo, rayHi);
}
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;
if (hitsLo != X) {
long stop = 1L << numberOfTrailingZeros(hitsLo);
rayLo &= (stop - 1);
rayHi = 0; // any hi is beyond the stop
} else if (hitsHi != X) {
long stop = 1L << numberOfTrailingZeros(hitsHi);
// keep all lo (lo indices are < any hi index), but cut hi below stop
rayHi &= (stop - 1);
}
visitor.visit(key, rayLo, rayHi);
}
static Slot[] extractSlots(Clues grid, DictEntry[] index) {
var slots = new Slot[grid.clueCount()];
int[] N = new int[]{ 0 };
grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)]));
return slots;
}
/// does not modify the grid
long maskFitness(final Clues grid, int clueSize) {
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() - clueSize)) * 16000L);
boolean hasSlots = false;
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits;
int clueIdx = numberOfTrailingZeros(lsb);
int v = (grid.vlo & lsb) != 0 ? 1 : 0;
int r = (grid.rlo & lsb) != 0 ? 1 : 0;
int key = Slot.packSlotKey(clueIdx, (r << 1) | v);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slot.increasing(key)) {
if (hLo != X) {
rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1);
rHi = 0;
} else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); }
} else {
if (hHi != X) {
int msb = 63 - numberOfLeadingZeros(hHi);
rHi &= ~((1L << msb << 1) - 1);
rLo = 0;
} else if (hLo != X) {
int msb = 63 - numberOfLeadingZeros(hLo);
rLo &= ~((1L << msb << 1) - 1);
}
}
if ((rLo | rHi) != X) {
hasSlots = true;
if (Slot.horiz(key)) {
cHLo |= rLo;
cHHi |= rHi;
} else {
cVLo |= rLo;
cVHi |= rHi;
}
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else {
penalty += 25000;
}
}
for (long bits = hi_cl; bits != X; bits &= bits - 1) {
long lsb = bits & -bits;
int clueIdx = numberOfTrailingZeros(lsb);
int v = (grid.vhi & lsb) != 0 ? 1 : 0;
int r = (grid.rhi & lsb) != 0 ? 1 : 0;
int key = Slot.packSlotKey(64 | clueIdx, (r << 1) | v);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slot.increasing(key)) {
if (hLo != X) {
rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1);
rHi = 0;
} else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); }
} else {
if (hHi != X) {
int msb = 63 - numberOfLeadingZeros(hHi);
rHi &= ~((1L << msb << 1) - 1);
rLo = 0;
} else if (hLo != X) {
int msb = 63 - numberOfLeadingZeros(hLo);
rLo &= ~((1L << msb << 1) - 1);
}
}
if ((rLo | rHi) != X) {
hasSlots = true;
if (Slot.horiz(key)) {
cHLo |= rLo;
cHHi |= rHi;
} else {
cVLo |= rLo;
cVHi |= rHi;
}
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
} else {
penalty += 25000;
}
}
if (!hasSlots) return 1_000_000_000L;
long seenLo = X, seenHi = X;
// loop over beide helften
for (int base = 0, size, sp, cur; base <= 64; base += 64) {
long clueMask = (base == 0) ? lo_cl : hi_cl;
long seenMask = (base == 0) ? seenLo : seenHi;
// "unseen clues" in deze helft
for (long bits = clueMask & ~seenMask, nLo, nHi; bits != X; bits &= bits - 1) {
int clueIdx = base | numberOfTrailingZeros(bits);
// start nieuwe component
size = 0;
stack[0] = clueIdx;
sp = 1;
// mark seen
if ((clueIdx & 64) == 0) seenLo |= 1L << clueIdx;
else seenHi |= 1L << (clueIdx & 63);
// flood fill / bfs
while (sp > 0) {
cur = stack[--sp];
size++;
// neighbors als 2x long masks
nLo = NBR8_PACKED_LO[cur];
nHi = NBR8_PACKED_HI[cur];
// filter: alleen clues, en nog niet seen
nLo &= lo_cl & ~seenLo;
nHi &= hi_cl & ~seenHi;
// push lo-neighbors
while (nLo != X) {
long lsb = nLo & -nLo;
int nidx = numberOfTrailingZeros(nLo); // 0..63
seenLo |= lsb;
stack[sp++] = nidx;
nLo &= nLo - 1;
}
// push hi-neighbors
while (nHi != X) {
long lsb = nHi & -nHi;
int nidx = 64 | numberOfTrailingZeros(nHi); // 64..127
seenHi |= lsb;
stack[sp++] = nidx;
nHi &= nHi - 1;
}
}
if (size >= 2) penalty += (size - 1L) * 120L;
}
}
for (long bits = ~lo_cl; 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 += 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 = 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;
if (!h && !v) penalty += 1500;
else if (h && v) { /* ok */ } else if (h | v) penalty += 200;
else penalty += 600;
}
return penalty;
}
Clues randomMask(final int clueSize) {
var g = Clues.createEmpty();
for (int placed = 0, guard = 0, ri; placed < clueSize && guard < 4000; guard++) {
ri = rng.randint0_SIZE();
if (isLo(ri)) {
if (g.isClueLo(ri)) continue;
var d_idx = rng.randint2bitByte();
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) {
g.setClueLo(1L << ri, d_idx);
placed++;
}
} else {
if (g.isClueHi(ri)) continue;
var d_idx = rng.randint2bitByte();
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) {
g.setClueHi(1L << (ri & 63), d_idx);
placed++;
}
}
}
return g;
}
static boolean isLo(int n) { return (n & 64) == 0; }
Clues mutate(Clues c) {
var bytes = MUTATE_RI[rng.randint0_SIZE()];
for (int k = 0, ri; k < 4; k++) {
ri = bytes[rng.randint0_624()];
if (isLo(ri)) {
if (!c.cluelessLo(ri)) {
var d_idx = rng.randint2bitByte();
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueLo(1L << ri, d_idx);
}
} else {
if (!c.cluelessHi(ri)) {
var d_idx = rng.randint2bitByte();
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueHi(1L << (ri & 63), d_idx);
}
}
}
return c;
}
Clues crossover(Clues a, Clues other) {
var theta = rng.nextFloat() * Math.PI;
var nc = Math.cos(theta);
var nr = Math.sin(theta);
long maskLo = 0, maskHi = 0;
for (var rci : IT) {
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
int i = rci.i();
if ((i & 64) == 0) maskLo |= (1L << i);
else maskHi |= (1L << (i - 64));
}
}
var c = new Clues(
(a.lo & ~maskLo) | (other.lo & maskLo),
(a.hi & ~maskHi) | (other.hi & maskHi),
(a.vlo & ~maskLo) | (other.vlo & maskLo),
(a.vhi & ~maskHi) | (other.vhi & maskHi),
(a.rlo & ~maskLo) | (other.rlo & maskLo),
(a.rhi & ~maskHi) | (other.rhi & maskHi));
for (var l = c.lo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 0);
for (var l = c.lo & ~c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 1);
for (var l = c.lo & c.rlo & ~c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 2);
for (var l = c.lo & c.rlo & c.vlo; l != X; l &= l - 1) clearCluesLo(c, numberOfTrailingZeros(l), 3);
for (var h = c.hi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, numberOfTrailingZeros(h), 0);
for (var h = c.hi & ~c.rhi & c.vhi; h != X; h &= h - 1) clearCluesHi(c, numberOfTrailingZeros(h), 1);
for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (numberOfTrailingZeros(h)), 2);
for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) clearCluesHi(c, (numberOfTrailingZeros(h)), 3);
return c;
}
public static void clearCluesLo(Clues out, int idx, int d) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, d)])) out.clearClueLo(~(1L << idx)); }
public static void clearCluesHi(Clues out, int idx, int d) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(64 | idx, d)])) out.clearClueHi(~(1L << idx)); }
Clues hillclimb(Clues start, int clue_size, int limit) {
var best = start;
var bestF = maskFitness(best, clue_size);
var fails = 0;
while (fails < limit) {
cache.from(best);
var cand = mutate(best);
var f = maskFitness(cand, clue_size);
if (f < bestF) {
best = cand;
bestF = f;
fails = 0;
} else {
best.from(cache);
fails++;
}
}
return best;
}
public Clues generateMask(int clueSize, int popSize, int gens, int offspring) {
class GridAndFit {
Clues grid;
long fite = -1;
GridAndFit(Clues grid) { this.grid = grid; }
long fit() {
if (fite == -1) this.fite = maskFitness(grid, clueSize);
return this.fite;
}
}
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize);
var pop = new ArrayList<GridAndFit>();
for (var i = 0; i < popSize; i++) {
if (Thread.currentThread().isInterrupted()) return null;
pop.add(new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180)));
}
for (var gen = 0; gen < gens; gen++) {
if (Thread.currentThread().isInterrupted()) break;
var children = new ArrayList<GridAndFit>();
for (var k = 0; k < offspring; k++) {
if (Thread.currentThread().isInterrupted()) break;
var p1 = pop.get(rng.randint(pop.size() - 1));
var p2 = pop.get(rng.randint(pop.size() - 1));
var child = crossover(p1.grid, p2.grid);
children.add(new GridAndFit(hillclimb(child, clueSize, 70)));
}
pop.addAll(children);
pop.sort(Comparator.comparingLong(GridAndFit::fit));
var next = new ArrayList<GridAndFit>();
for (var cand : pop) {
if (next.size() >= offspring) break;
var ok = true;
for (var kept : next) {
if (cand.grid.similarity(kept.grid) > 0.92) {
ok = false;
break;
}
}
if (ok) next.add(cand);
}
pop = next;
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop.get(0).fit());
}
if (pop.isEmpty()) return null;
GridAndFit best = pop.get(0);
for (int i = 1; i < pop.size(); i++) {
var x = pop.get(i);
if (x.fit() < best.fit()) best = x;
}
return best.grid;
}
static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) {
public static boolean isLo(int n) { return (n & 64) == 0; }
public static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) {
if (((lo & glo) | (hi & ghi)) == X) return 0;
long p = 0;
int n = 0;
if (Slot.increasing(key)) {
if (Slotinfo.increasing(key)) {
for (long b = lo & glo; b != X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
int i = bitCount(lo & ((1L << idx) - 1));
@@ -762,15 +271,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
}
return p;
}
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);
return cross * 10 + Slot.length(lo, hi);
}
static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) {
public static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) {
final long glo = grid.lo, ghi = grid.hi;
if (Slot.increasing(key)) {
if (Slotinfo.increasing(key)) {
for (long b = lo & glo; b != X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
if (g[idx] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
@@ -823,7 +326,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
}
/// pattern cannot be X
static int[] candidateInfoForPattern(long[] res, long pattern, long[][] posBitsets, int numLongs) {
public static int[] candidateInfoForPattern(long[] res, long pattern, long[][] posBitsets, int numLongs) {
System.arraycopy(posBitsets[(int) (pattern & 0xFF) - 1], 0, res, 0, numLongs);
for (long p = pattern >>> 8; p != X; p >>>= 8) {
long[] bs = posBitsets[(int) (p & 0xFF) - 1];
@@ -841,7 +344,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
return indices;
}
/// pattern cannot be X
static int candidateCountForPattern(final long[] res, final long pattern, final long[][] posBitsets, final int numLongs) {
public static int candidateCountForPattern(final long[] res, final long pattern, final long[][] posBitsets, final int numLongs) {
System.arraycopy(posBitsets[(int) (pattern & 0xFF) - 1], 0, res, 0, numLongs);
for (long p = pattern >>> 8; p != X; p >>>= 8) {
long[] bs = posBitsets[(int) (p & 0xFF) - 1];
@@ -853,20 +356,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
return count;
}
static Slotinfo[] scoreSlots(int[] slotScores, Slot[] slots) {
val count = new byte[SIZE];
Slotinfo[] 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 (int i = 0; i < slots.length; i++) {
var slot = slots[i];
slotScores[i] = slotScore(count, slot.lo, slot.hi);
slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScores[i], new Assign(), slot.entry);
}
return slotInfo;
}
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
final Grid grid,
final boolean multiThreaded) {
@@ -907,7 +396,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
}
boolean placeWord(final int key, final long lo, final long hi, final long w) {
int idx;
if (Slot.increasing(key)) {
if (Slotinfo.increasing(key)) {
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;
int bcLo = bitCount(lo);
for (long b = hi & ghi; b != X; b &= b - 1)
@@ -1059,7 +548,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
// final progress line
grid.lo = solver.glo;
grid.hi = solver.ghi;
var res = new FillResult(ok, new Gridded(grid),
var res = new FillResult(ok,
new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
if (!multiThreaded) {
System.out.print("\r" + Strings.padRight("", 120) + "\r");