|
|
|
|
@@ -51,7 +51,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
long stop = 1L << msb;
|
|
|
|
|
rayLo &= -(stop << 1);
|
|
|
|
|
}
|
|
|
|
|
// if (Long.bitCount(rayLo) + Long.bitCount(rayHi) > 1)
|
|
|
|
|
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
|
|
|
|
visitor.visit(key, rayLo, rayHi);
|
|
|
|
|
}
|
|
|
|
|
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
|
|
|
|
|
@@ -69,11 +69,11 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
// keep all lo (lo indices are < any hi index), but cut hi below stop
|
|
|
|
|
rayHi &= (stop - 1);
|
|
|
|
|
}
|
|
|
|
|
// if (Long.bitCount(rayLo) + Long.bitCount(rayHi) > 1)
|
|
|
|
|
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
|
|
|
|
visitor.visit(key, rayLo, rayHi);
|
|
|
|
|
}
|
|
|
|
|
public static Slot[] extractSlots(Clues grid, DictEntry[] index) {
|
|
|
|
|
var slots = new ArrayList<Slot>(grid.clueCount());
|
|
|
|
|
var slots = new ArrayList<Slot>(grid.clueCount());
|
|
|
|
|
grid.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, Objects.requireNonNull(index[Slot.length(lo, hi)]))));
|
|
|
|
|
return slots.toArray(Slot[]::new);
|
|
|
|
|
}
|
|
|
|
|
@@ -108,7 +108,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
long lo_cl = grid.lo, hi_cl = grid.hi;
|
|
|
|
|
long penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
|
|
|
|
|
boolean hasSlots = false;
|
|
|
|
|
|
|
|
|
|
if (!grid.isValid(2)) return 1_000_000_000L;
|
|
|
|
|
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
|
|
|
|
long lsb = bits & -bits;
|
|
|
|
|
int clueIdx = numberOfTrailingZeros(lsb);
|
|
|
|
|
@@ -133,11 +133,13 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
if ((rLo | rHi) != X) {
|
|
|
|
|
hasSlots = true;
|
|
|
|
|
if (Slot.horiz(key)) {
|
|
|
|
|
cHLo2 |= (cHLo & rLo); cHHi2 |= (cHHi & rHi);
|
|
|
|
|
cHLo2 |= (cHLo & rLo);
|
|
|
|
|
cHHi2 |= (cHHi & rHi);
|
|
|
|
|
cHLo |= rLo;
|
|
|
|
|
cHHi |= rHi;
|
|
|
|
|
} else {
|
|
|
|
|
cVLo2 |= (cVLo & rLo); cVHi2 |= (cVHi & rHi);
|
|
|
|
|
cVLo2 |= (cVLo & rLo);
|
|
|
|
|
cVHi2 |= (cVHi & rHi);
|
|
|
|
|
cVLo |= rLo;
|
|
|
|
|
cVHi |= rHi;
|
|
|
|
|
}
|
|
|
|
|
@@ -168,11 +170,13 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
if ((rLo | rHi) != X) {
|
|
|
|
|
hasSlots = true;
|
|
|
|
|
if (Slot.horiz(key)) {
|
|
|
|
|
cHLo2 |= (cHLo & rLo); cHHi2 |= (cHHi & rHi);
|
|
|
|
|
cHLo2 |= (cHLo & rLo);
|
|
|
|
|
cHHi2 |= (cHHi & rHi);
|
|
|
|
|
cHLo |= rLo;
|
|
|
|
|
cHHi |= rHi;
|
|
|
|
|
} else {
|
|
|
|
|
cVLo2 |= (cVLo & rLo); cVHi2 |= (cVHi & rHi);
|
|
|
|
|
cVLo2 |= (cVLo & rLo);
|
|
|
|
|
cVHi2 |= (cVHi & rHi);
|
|
|
|
|
cVLo |= rLo;
|
|
|
|
|
cVHi |= rHi;
|
|
|
|
|
}
|
|
|
|
|
@@ -248,8 +252,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
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 ? cHLo2 : cVLo2) & (1L << clueIdx)) != X) penalty += 600;
|
|
|
|
|
else if (h && v) { /* ok */ } else if (((h ? cHLo2 : cVLo2) & (1L << clueIdx)) != X) penalty += 600;
|
|
|
|
|
else penalty += 200;
|
|
|
|
|
}
|
|
|
|
|
for (long bits = ~hi_cl & MASK_HI; bits != X; bits &= bits - 1) {
|
|
|
|
|
@@ -259,8 +262,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
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 ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 600;
|
|
|
|
|
else if (h && v) { /* ok */ } else if (((h ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 600;
|
|
|
|
|
else penalty += 200;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -275,16 +277,20 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
if (isLo(ri)) {
|
|
|
|
|
if (g.isClueLo(ri)) continue;
|
|
|
|
|
var d_idx = rng.randint2bitByte();
|
|
|
|
|
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) {
|
|
|
|
|
int key = Slot.packSlotKey(ri, d_idx);
|
|
|
|
|
if (g.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
|
|
|
|
g.setClueLo(1L << ri, d_idx);
|
|
|
|
|
placed++;
|
|
|
|
|
if (g.isValid(MIN_LEN)) placed++;
|
|
|
|
|
else g.clearClueLo(~(1L << ri));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (g.isClueHi(ri)) continue;
|
|
|
|
|
var d_idx = rng.randint2bitByte();
|
|
|
|
|
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) {
|
|
|
|
|
int key = Slot.packSlotKey(ri, d_idx);
|
|
|
|
|
if (g.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
|
|
|
|
g.setClueHi(1L << (ri & 63), d_idx);
|
|
|
|
|
placed++;
|
|
|
|
|
if (g.isValid(MIN_LEN)) placed++;
|
|
|
|
|
else g.clearClueHi(~(1L << (ri & 63)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -296,16 +302,56 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
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);
|
|
|
|
|
if (c.notClue(ri)) { // ADD
|
|
|
|
|
byte d = rng.randint2bitByte();
|
|
|
|
|
int key = Slot.packSlotKey(ri, d);
|
|
|
|
|
if (c.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
|
|
|
|
if (isLo(ri)) {
|
|
|
|
|
c.setClueLo(1L << ri, d);
|
|
|
|
|
if (!c.isValid(MIN_LEN)) c.clearClueLo(~(1L << ri));
|
|
|
|
|
} else {
|
|
|
|
|
c.setClueHi(1L << (ri & 63), d);
|
|
|
|
|
if (!c.isValid(MIN_LEN)) c.clearClueHi(~(1L << (ri & 63)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // HAS CLUE
|
|
|
|
|
int op = rng.randint(10);
|
|
|
|
|
if (op < 2) { // REMOVE
|
|
|
|
|
if (isLo(ri)) c.clearClueLo(~(1L << ri));
|
|
|
|
|
else c.clearClueHi(~(1L << (ri & 63)));
|
|
|
|
|
} else if (op < 5) { // CHANGE DIRECTION
|
|
|
|
|
byte d = rng.randint2bitByte();
|
|
|
|
|
int key = Slot.packSlotKey(ri, d);
|
|
|
|
|
if (c.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
|
|
|
|
byte oldD = c.getDir(ri);
|
|
|
|
|
if (isLo(ri)) {
|
|
|
|
|
c.setClueLo(1L << ri, d);
|
|
|
|
|
if (!c.isValid(MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
|
|
|
|
} else {
|
|
|
|
|
c.setClueHi(1L << (ri & 63), d);
|
|
|
|
|
if (!c.isValid(MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // MOVE
|
|
|
|
|
int nri = bytes[rng.randint0_624()];
|
|
|
|
|
if (c.notClue(nri)) {
|
|
|
|
|
byte d = c.getDir(ri);
|
|
|
|
|
int nkey = Slot.packSlotKey(nri, d);
|
|
|
|
|
if (c.hasRoomForClue(nkey, OFFSETS_D_IDX[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 (!c.isValid(MIN_LEN)) {
|
|
|
|
|
if (isLo(nri)) c.clearClueLo(~(1L << nri));
|
|
|
|
|
else c.clearClueHi(~(1L << (nri & 63)));
|
|
|
|
|
if (isLo(ri)) c.setClueLo(1L << ri, d);
|
|
|
|
|
else c.setClueHi(1L << (ri & 63), d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} 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;
|
|
|
|
|
}
|
|
|
|
|
@@ -330,21 +376,29 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
(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);
|
|
|
|
|
int guard = 0;
|
|
|
|
|
while (!c.isValid(MIN_LEN) && guard++ < 3) {
|
|
|
|
|
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 clearCluesLo(Clues out, int idx, int d) {
|
|
|
|
|
int key = Slot.packSlotKey(idx, d);
|
|
|
|
|
if (!out.hasRoomForClue(key, OFFSETS_D_IDX[key])) 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)); }
|
|
|
|
|
public static void clearCluesHi(Clues out, int idx, int d) {
|
|
|
|
|
int key = Slot.packSlotKey(64 | idx, d);
|
|
|
|
|
if (!out.hasRoomForClue(key, OFFSETS_D_IDX[key])) out.clearClueHi(~(1L << idx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Clues hillclimb(Clues start, int clue_size, int limit) {
|
|
|
|
|
var best = start;
|
|
|
|
|
@@ -457,7 +511,13 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
clearClueHi(~(1L << (idx & 63)));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
public boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
|
|
|
|
public boolean hasRoomForClue(int key, long packed) {
|
|
|
|
|
if (packed == X || !notClue(packed & 0x7FL) || !notClue((packed >>> 7) & 0x7FL)) return false;
|
|
|
|
|
final boolean[] res = {false};
|
|
|
|
|
if (Slotinfo.increasing(key)) processSlot(this, (k, lo, hi) -> res[0] = true, key);
|
|
|
|
|
else processSlotRev(this, (k, lo, hi) -> res[0] = true, key);
|
|
|
|
|
return res[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setClueLo(long mask, byte idx) {
|
|
|
|
|
lo |= mask;
|
|
|
|
|
@@ -496,6 +556,18 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
|
|
|
|
|
}
|
|
|
|
|
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); }
|
|
|
|
|
public boolean isValid(int minLen) {
|
|
|
|
|
class ValidationVisitor implements SlotVisitor {
|
|
|
|
|
boolean ok = true;
|
|
|
|
|
@Override
|
|
|
|
|
public void visit(int key, long lo, long hi) {
|
|
|
|
|
if (bitCount(lo) + bitCount(hi) < minLen) ok = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ValidationVisitor v = new ValidationVisitor();
|
|
|
|
|
forEachSlot(v);
|
|
|
|
|
return v.ok;
|
|
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
|
@@ -515,6 +587,17 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
|
|
|
|
|
rhi = best.rhi;
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
return (byte) ((r << 1) | v);
|
|
|
|
|
} else {
|
|
|
|
|
int v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
|
|
|
|
int r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
|
|
|
|
return (byte) ((r << 1) | v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public record Slot(int key, long lo, long hi, DictEntry entry) {
|
|
|
|
|
|