introduce bitloops
This commit is contained in:
@@ -51,7 +51,7 @@ public record Export() {
|
|||||||
|
|
||||||
public record Clued(@Delegate Clues c) {
|
public record Clued(@Delegate Clues c) {
|
||||||
|
|
||||||
public Clued deepCopyGrid() { return new Clued(new Clues(c.lo, c.hi, c.vlo, c.vhi, c.rlo, c.rhi)); }
|
public Clued deepCopyGrid() { return new Clued(new Clues(c.lo, c.hi, c.vlo, c.vhi, c.rlo, c.rhi, c.xlo, c.xhi)); }
|
||||||
String gridToString() {
|
String gridToString() {
|
||||||
var sb = new StringBuilder(INIT);
|
var sb = new StringBuilder(INIT);
|
||||||
forEachSlot((s, _, _) -> {
|
forEachSlot((s, _, _) -> {
|
||||||
@@ -199,7 +199,7 @@ public record Export() {
|
|||||||
|
|
||||||
record Placed(long lemma, int slotKey, int[] cells) {
|
record Placed(long lemma, int slotKey, int[] cells) {
|
||||||
|
|
||||||
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL };
|
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL };
|
||||||
public static final char HORIZONTAL = 'h';
|
public static final char HORIZONTAL = 'h';
|
||||||
static final char VERTICAL = 'v';
|
static final char VERTICAL = 'v';
|
||||||
public int arrowCol() { return SwedishGenerator.IT[Slot.clueIndex(slotKey)].c(); }
|
public int arrowCol() { return SwedishGenerator.IT[Slot.clueIndex(slotKey)].c(); }
|
||||||
|
|||||||
@@ -130,9 +130,8 @@ public final class Masker {
|
|||||||
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
||||||
long lsb = bits & -bits;
|
long lsb = bits & -bits;
|
||||||
int clueIdx = numberOfTrailingZeros(lsb);
|
int clueIdx = numberOfTrailingZeros(lsb);
|
||||||
int v = (grid.vlo & lsb) != 0 ? 1 : 0;
|
int dir = grid.getDir(clueIdx);
|
||||||
int r = (grid.rlo & lsb) != 0 ? 1 : 0;
|
int key = Slot.packSlotKey(clueIdx, dir);
|
||||||
int key = Slot.packSlotKey(clueIdx, (r << 1) | v);
|
|
||||||
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
||||||
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
||||||
if (Slotinfo.increasing(key)) {
|
if (Slotinfo.increasing(key)) {
|
||||||
@@ -173,9 +172,8 @@ public final class Masker {
|
|||||||
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;
|
||||||
int clueIdx = numberOfTrailingZeros(lsb);
|
int clueIdx = numberOfTrailingZeros(lsb);
|
||||||
int v = (grid.vhi & lsb) != 0 ? 1 : 0;
|
int dir = grid.getDir(64 | clueIdx);
|
||||||
int r = (grid.rhi & lsb) != 0 ? 1 : 0;
|
int key = Slot.packSlotKey(64 | clueIdx, dir);
|
||||||
int key = Slot.packSlotKey(64 | clueIdx, (r << 1) | v);
|
|
||||||
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
||||||
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
||||||
if (Slotinfo.increasing(key)) {
|
if (Slotinfo.increasing(key)) {
|
||||||
@@ -226,15 +224,6 @@ public final class Masker {
|
|||||||
// 1. Intersectie
|
// 1. Intersectie
|
||||||
if (((activeSLo[i] & activeSLo[j]) | (activeSHi[i] & activeSHi[j])) != 0) {
|
if (((activeSLo[i] & activeSLo[j]) | (activeSHi[i] & activeSHi[j])) != 0) {
|
||||||
connected = true;
|
connected = true;
|
||||||
} else {
|
|
||||||
// 2. 8-naburigheid van clue cells
|
|
||||||
int ci = activeCIdx[i];
|
|
||||||
int cj = activeCIdx[j];
|
|
||||||
if (cj < 64) {
|
|
||||||
if ((NBR8_PACKED_LO[ci] & (1L << cj)) != 0) connected = true;
|
|
||||||
} else {
|
|
||||||
if ((NBR8_PACKED_HI[ci] & (1L << (cj & 63))) != 0) connected = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
@@ -282,7 +271,7 @@ public final class Masker {
|
|||||||
if ((4 - rci.nbrCount()) + bitCount(rci.n1() & lo_cl) + bitCount(rci.n2() & hi_cl) >= 3) penalty += 400;
|
if ((4 - rci.nbrCount()) + bitCount(rci.n1() & lo_cl) + bitCount(rci.n2() & hi_cl) >= 3) penalty += 400;
|
||||||
boolean h = (cHLo & (1L << clueIdx)) != X;
|
boolean h = (cHLo & (1L << clueIdx)) != X;
|
||||||
boolean v = (cVLo & (1L << clueIdx)) != X;
|
boolean v = (cVLo & (1L << clueIdx)) != X;
|
||||||
if (!h && !v) penalty += 15000;
|
if (!h && !v) penalty += 5000;
|
||||||
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;
|
else penalty += 200;
|
||||||
}
|
}
|
||||||
@@ -293,7 +282,7 @@ public final class Masker {
|
|||||||
boolean h = (cHHi & (1L << clueIdx)) != X;
|
boolean h = (cHHi & (1L << clueIdx)) != X;
|
||||||
boolean v = (cVHi & (1L << clueIdx)) != X;
|
boolean v = (cVHi & (1L << clueIdx)) != X;
|
||||||
if (!h && !v)
|
if (!h && !v)
|
||||||
penalty += 15000;
|
penalty += 5000;
|
||||||
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;
|
else penalty += 200;
|
||||||
}
|
}
|
||||||
@@ -407,7 +396,9 @@ public final class Masker {
|
|||||||
(a.vlo & ~maskLo) | (other.vlo & maskLo),
|
(a.vlo & ~maskLo) | (other.vlo & maskLo),
|
||||||
(a.vhi & ~maskHi) | (other.vhi & maskHi),
|
(a.vhi & ~maskHi) | (other.vhi & maskHi),
|
||||||
(a.rlo & ~maskLo) | (other.rlo & maskLo),
|
(a.rlo & ~maskLo) | (other.rlo & maskLo),
|
||||||
(a.rhi & ~maskHi) | (other.rhi & maskHi));
|
(a.rhi & ~maskHi) | (other.rhi & maskHi),
|
||||||
|
(a.xlo & ~maskLo) | (other.xlo & maskLo),
|
||||||
|
(a.xhi & ~maskHi) | (other.xhi & maskHi));
|
||||||
int guard = 0;
|
int guard = 0;
|
||||||
while (!c.isValid(MIN_LEN) && guard++ < 3) {
|
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), 0);
|
||||||
@@ -513,9 +504,9 @@ public final class Masker {
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class Clues {
|
public static class Clues {
|
||||||
|
|
||||||
long lo, hi, vlo, vhi, rlo, rhi;
|
long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
|
||||||
|
|
||||||
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, 0, 0); }
|
||||||
public static Clued parse(String s) {
|
public static Clued parse(String s) {
|
||||||
var c = createEmpty();
|
var c = createEmpty();
|
||||||
var lines = s.split("\n");
|
var lines = s.split("\n");
|
||||||
@@ -523,7 +514,7 @@ public final class Masker {
|
|||||||
var line = lines[r];
|
var line = lines[r];
|
||||||
for (int col = 0; col < Math.min(line.length(), C); col++) {
|
for (int col = 0; col < Math.min(line.length(), C); col++) {
|
||||||
char ch = line.charAt(col);
|
char ch = line.charAt(col);
|
||||||
if (ch >= '0' && ch <= '3') {
|
if (ch >= '0' && ch <= '4') {
|
||||||
int idx = Grid.offset(r, col);
|
int idx = Grid.offset(r, col);
|
||||||
byte dir = (byte) (ch - '0');
|
byte dir = (byte) (ch - '0');
|
||||||
if ((idx & 64) == 0) c.setClueLo(1L << idx, dir);
|
if ((idx & 64) == 0) c.setClueLo(1L << idx, dir);
|
||||||
@@ -557,6 +548,8 @@ public final class Masker {
|
|||||||
else vlo &= ~mask;
|
else vlo &= ~mask;
|
||||||
if ((idx & 2) != 0) rlo |= mask;
|
if ((idx & 2) != 0) rlo |= mask;
|
||||||
else rlo &= ~mask;
|
else rlo &= ~mask;
|
||||||
|
if ((idx & 4) != 0) xlo |= mask;
|
||||||
|
else xlo &= ~mask;
|
||||||
}
|
}
|
||||||
public void setClueHi(long mask, byte idx) {
|
public void setClueHi(long mask, byte idx) {
|
||||||
hi |= mask;
|
hi |= mask;
|
||||||
@@ -564,16 +557,20 @@ public final class Masker {
|
|||||||
else vhi &= ~mask;
|
else vhi &= ~mask;
|
||||||
if ((idx & 2) != 0) rhi |= mask;
|
if ((idx & 2) != 0) rhi |= mask;
|
||||||
else rhi &= ~mask;
|
else rhi &= ~mask;
|
||||||
|
if ((idx & 4) != 0) xhi |= mask;
|
||||||
|
else xhi &= ~mask;
|
||||||
}
|
}
|
||||||
public void clearClueLo(long mask) {
|
public void clearClueLo(long mask) {
|
||||||
lo &= mask;
|
lo &= mask;
|
||||||
vlo &= mask;
|
vlo &= mask;
|
||||||
rlo &= mask;
|
rlo &= mask;
|
||||||
|
xlo &= mask;
|
||||||
}
|
}
|
||||||
public void clearClueHi(long mask) {
|
public void clearClueHi(long mask) {
|
||||||
hi &= mask;
|
hi &= mask;
|
||||||
vhi &= mask;
|
vhi &= mask;
|
||||||
rhi &= mask;
|
rhi &= mask;
|
||||||
|
xhi &= mask;
|
||||||
}
|
}
|
||||||
public boolean isClueLo(int index) { return ((lo >>> index) & 1L) != X; }
|
public boolean isClueLo(int index) { return ((lo >>> index) & 1L) != X; }
|
||||||
public boolean isClueHi(int index) { return ((hi >>> (index & 63)) & 1L) != X; }
|
public boolean isClueHi(int index) { return ((hi >>> (index & 63)) & 1L) != X; }
|
||||||
@@ -582,8 +579,8 @@ public final class Masker {
|
|||||||
|
|
||||||
public int clueCount() { return bitCount(lo) + bitCount(hi); }
|
public int clueCount() { return bitCount(lo) + bitCount(hi); }
|
||||||
public double similarity(Clues b) {
|
public double similarity(Clues b) {
|
||||||
long matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo)));
|
long matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo) & ~(xlo ^ b.xlo)));
|
||||||
long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi)));
|
long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi) & ~(xhi ^ b.xhi)));
|
||||||
|
|
||||||
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
|
return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED;
|
||||||
}
|
}
|
||||||
@@ -601,14 +598,17 @@ public final class Masker {
|
|||||||
return v.ok;
|
return v.ok;
|
||||||
}
|
}
|
||||||
public void forEachSlot(SlotVisitor visitor) {
|
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 & ~xlo & ~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 & ~xlo & ~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 & ~xlo & 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 l = lo & ~xlo & 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 l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4));
|
||||||
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 & ~xhi & ~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) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3));
|
for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0));
|
||||||
|
for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2));
|
||||||
|
for (var h = hi & ~xhi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3));
|
||||||
|
for (var h = hi & xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 4));
|
||||||
}
|
}
|
||||||
public Clues from(Clues best) {
|
public Clues from(Clues best) {
|
||||||
lo = best.lo;
|
lo = best.lo;
|
||||||
@@ -617,30 +617,34 @@ public final class Masker {
|
|||||||
vhi = best.vhi;
|
vhi = best.vhi;
|
||||||
rlo = best.rlo;
|
rlo = best.rlo;
|
||||||
rhi = best.rhi;
|
rhi = best.rhi;
|
||||||
|
xlo = best.xlo;
|
||||||
|
xhi = best.xhi;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public byte getDir(int index) {
|
public byte getDir(int index) {
|
||||||
if ((index & 64) == 0) {
|
if ((index & 64) == 0) {
|
||||||
int v = (vlo & (1L << index)) != 0 ? 1 : 0;
|
int v = (vlo & (1L << index)) != 0 ? 1 : 0;
|
||||||
int r = (rlo & (1L << index)) != 0 ? 1 : 0;
|
int r = (rlo & (1L << index)) != 0 ? 1 : 0;
|
||||||
return (byte) ((r << 1) | v);
|
int x = (xlo & (1L << index)) != 0 ? 1 : 0;
|
||||||
|
return (byte) ((x << 2) | (r << 1) | v);
|
||||||
} else {
|
} else {
|
||||||
int v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
int v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
||||||
int r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
int r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
||||||
return (byte) ((r << 1) | v);
|
int x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
||||||
|
return (byte) ((x << 2) | (r << 1) | v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Slot(int key, long lo, long hi, DictEntry entry) {
|
public record Slot(int key, long lo, long hi, DictEntry entry) {
|
||||||
|
|
||||||
static final int BIT_FOR_DIR = 2;
|
static final int BIT_FOR_DIR = 3;
|
||||||
static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
|
public 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 length(long lo, long hi) { return bitCount(lo) + bitCount(hi); }
|
||||||
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
||||||
public static int dir(int key) { return key & 3; }
|
public static int dir(int key) { return key & 7; }
|
||||||
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
||||||
public static boolean horiz(int d) { return (d & 1) != 0; }
|
public static boolean horiz(int d) { return (d & 1) != 0 && (d & 4) == 0; }
|
||||||
public static int packSlotKey(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
public static int packSlotKey(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,12 +76,45 @@ public record SwedishGenerator() {
|
|||||||
public int count;
|
public int count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX_0_BASE;
|
public static final long[] OFFSETS_D_IDX;
|
||||||
public static final rci[] IT = Neighbors9x8.IT;
|
public static final rci[] IT = Neighbors9x8.IT;
|
||||||
public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
|
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[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
|
||||||
public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
|
public static final long[] PATH_LO;
|
||||||
public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
|
public static final long[] PATH_HI;
|
||||||
|
|
||||||
|
static {
|
||||||
|
final int NEW_SIZE = SIZE << Masker.Slot.BIT_FOR_DIR;
|
||||||
|
PATH_LO = new long[NEW_SIZE];
|
||||||
|
PATH_HI = new long[NEW_SIZE];
|
||||||
|
OFFSETS_D_IDX = new long[NEW_SIZE];
|
||||||
|
for (int i = 0; i < SIZE; i++) {
|
||||||
|
for (int d = 0; d < 4; d++) {
|
||||||
|
int oldKey = (i << 2) | d;
|
||||||
|
int newKey = (i << Masker.Slot.BIT_FOR_DIR) | d;
|
||||||
|
PATH_LO[newKey] = Neighbors9x8.PATH_LO[oldKey];
|
||||||
|
PATH_HI[newKey] = Neighbors9x8.PATH_HI[oldKey];
|
||||||
|
OFFSETS_D_IDX[newKey] = Neighbors9x8.OFFSET_D_IDX_0_BASE[oldKey];
|
||||||
|
}
|
||||||
|
// Richting 4: Corner Down (Clue op r,c -> Woord op r,c+1 naar beneden)
|
||||||
|
int r = IT[i].r();
|
||||||
|
int c = IT[i].c();
|
||||||
|
int newKey4 = (i << Masker.Slot.BIT_FOR_DIR) | 4;
|
||||||
|
if (c + 1 < C) {
|
||||||
|
long mLo = 0, mHi = 0;
|
||||||
|
for (int ri = r; ri < R; ri++) {
|
||||||
|
int target = Grid.offset(ri, c + 1);
|
||||||
|
if (isLo(target)) mLo |= (1L << target);
|
||||||
|
else mHi |= (1L << (target & 63));
|
||||||
|
}
|
||||||
|
PATH_LO[newKey4] = mLo;
|
||||||
|
PATH_HI[newKey4] = mHi;
|
||||||
|
int first = Grid.offset(r, c + 1);
|
||||||
|
int last = Grid.offset(R - 1, c + 1);
|
||||||
|
OFFSETS_D_IDX[newKey4] = (long)first | ((long)last << 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
|
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);
|
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
|
||||||
|
|
||||||
@@ -112,13 +145,10 @@ public record SwedishGenerator() {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
public byte randint2bitByte() {
|
public byte randint2bitByte() {
|
||||||
var b = (byte) (nextU32() & 3);
|
int r = nextU32() & 7;
|
||||||
if (b == 3) {
|
if (r < 4) return (byte) r;
|
||||||
return 1;
|
if (r == 4) return (byte) 4;
|
||||||
}if (b == 4) {
|
return (byte) (r % 4);
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; }
|
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; }
|
||||||
public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); }
|
public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); }
|
||||||
|
|||||||
@@ -13,78 +13,81 @@ public class ConnectivityTest {
|
|||||||
Rng rng = new Rng(42);
|
Rng rng = new Rng(42);
|
||||||
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
|
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
|
||||||
|
|
||||||
// 1. Maak een masker met één component van clues (bijv. 3 clues naast elkaar)
|
// 1. Maak een masker met één component van clues (bijv. 2 clues die elkaar kruisen)
|
||||||
Clues singleComp = Clues.createEmpty();
|
Clues singleComp = Clues.createEmpty();
|
||||||
// Gebruik offsets die dicht bij elkaar liggen
|
// Clue 1: (0,0) Right. Slot: (0,1), (0,2), (0,3)
|
||||||
int off1 = SwedishGenerator.Grid.offset(1, 1);
|
// Clue 2: (1,2) Up. Slot: (0,2)
|
||||||
int off2 = SwedishGenerator.Grid.offset(1, 2);
|
// Ze zijn NIET 8-naburig, maar wel verbonden via slot op (0,2)
|
||||||
int off3 = SwedishGenerator.Grid.offset(2, 1);
|
singleComp.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1);
|
||||||
|
singleComp.setClueLo(1L << SwedishGenerator.Grid.offset(2,2), (byte)2); // Up van (2,2) naar (1,2), (0,2)
|
||||||
|
|
||||||
singleComp.setClueLo(1L << off1, (byte)1); // Right
|
long fitnessSingle = masker.maskFitness(singleComp, 2);
|
||||||
singleComp.setClueLo(1L << off2, (byte)1); // Right
|
|
||||||
singleComp.setClueLo(1L << off3, (byte)0); // Down
|
|
||||||
|
|
||||||
long fitnessSingle = masker.maskFitness(singleComp, 3);
|
|
||||||
|
|
||||||
// 2. Maak een masker met twee eilandjes van clues
|
// 2. Maak een masker met twee eilandjes van clues
|
||||||
Clues twoIslands = Clues.createEmpty();
|
Clues twoIslands = Clues.createEmpty();
|
||||||
int offA1 = SwedishGenerator.Grid.offset(1, 1);
|
twoIslands.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1);
|
||||||
int offB1 = SwedishGenerator.Grid.offset(6, 6); // Ver weg
|
twoIslands.setClueLo( SwedishGenerator.Grid.offset(7,7) < 64 ? 1L << SwedishGenerator.Grid.offset(7,7) : 0, (byte)1);
|
||||||
|
// Voor de zekerheid checken we of offset(7,7) in lo of hi zit
|
||||||
// We moeten zorgen dat ze elk minstens 1 slot vormen om door isValid(2) te komen
|
int off77 = SwedishGenerator.Grid.offset(7,7);
|
||||||
twoIslands.setClueLo(1L << offA1, (byte)1);
|
if (off77 < 64) twoIslands.setClueLo(1L << off77, (byte)1);
|
||||||
twoIslands.setClueLo(1L << offB1, (byte)1);
|
else twoIslands.setClueHi(1L << (off77 - 64), (byte)1);
|
||||||
|
|
||||||
long fitnessIslands = masker.maskFitness(twoIslands, 2);
|
long fitnessIslands = masker.maskFitness(twoIslands, 2);
|
||||||
|
|
||||||
System.out.println("[DEBUG_LOG] Fitness single component: " + fitnessSingle);
|
System.out.println("[DEBUG_LOG] Fitness single component: " + fitnessSingle);
|
||||||
System.out.println("[DEBUG_LOG] Fitness two islands: " + fitnessIslands);
|
System.out.println("[DEBUG_LOG] Fitness two islands: " + fitnessIslands);
|
||||||
|
|
||||||
// De eilandjes moeten een hogere penalty hebben (als clueCount gelijk is)
|
assertTrue(fitnessIslands > fitnessSingle + 10000, "Islands should have much higher penalty");
|
||||||
Clues twoIslands3 = Clues.createEmpty();
|
|
||||||
twoIslands3.setClueLo(1L << offA1, (byte)1);
|
|
||||||
twoIslands3.setClueLo(1L << offB1, (byte)1);
|
|
||||||
int offB2 = SwedishGenerator.Grid.offset(6, 7);
|
|
||||||
twoIslands3.setClueLo(1L << offB2, (byte)1);
|
|
||||||
|
|
||||||
long fitnessIslands3 = masker.maskFitness(twoIslands3, 3);
|
|
||||||
System.out.println("[DEBUG_LOG] Fitness three clues in two islands: " + fitnessIslands3);
|
|
||||||
|
|
||||||
assertTrue(fitnessIslands3 > fitnessSingle, "Islands should have higher penalty than single component");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testIntersectionConnectivity() {
|
void testPhysicalAdjacency() {
|
||||||
Rng rng = new Rng(42);
|
Rng rng = new Rng(42);
|
||||||
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
|
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
|
||||||
|
|
||||||
// Test of slots die elkaar kruisen als verbonden worden beschouwd,
|
Clues clues = Clues.createEmpty();
|
||||||
// zelfs als de clues niet 8-naburig zijn.
|
// Twee clues naast elkaar, maar slots kruisen niet.
|
||||||
Clues crossing = Clues.createEmpty();
|
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
||||||
// Clue 1: (0,0) naar rechts. Slot op (0,1), (0,2), (0,3)
|
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
||||||
// Clue 2: (1,2) omhoog. Slot op (0,2)
|
clues.setClueLo(1L << SwedishGenerator.Grid.offset(1,1), (byte)1);
|
||||||
// Ze kruisen op (0,2)
|
clues.setClueLo(1L << SwedishGenerator.Grid.offset(2,1), (byte)1);
|
||||||
crossing.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1); // Right
|
|
||||||
crossing.setClueLo(1L << SwedishGenerator.Grid.offset(1,2), (byte)2); // Up
|
|
||||||
|
|
||||||
// Deze twee clues zijn niet 8-naburig (0,0 en 1,2)
|
long fitness = masker.maskFitness(clues, 2);
|
||||||
// Maar hun slots kruisen op (0,2)
|
|
||||||
|
|
||||||
long fitness = masker.maskFitness(crossing, 2);
|
// Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
|
||||||
System.out.println("[DEBUG_LOG] Fitness crossing: " + fitness);
|
System.out.println("[DEBUG_LOG] Fitness physically adjacent: " + fitness);
|
||||||
|
assertTrue(fitness > 20000, "Should have island penalty even if physically adjacent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCornerClueConnectivity() {
|
||||||
|
Rng rng = new Rng(42);
|
||||||
|
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
|
||||||
|
|
||||||
// Als ze als verbonden worden gezien, is er 1 component.
|
Clues clues = Clues.createEmpty();
|
||||||
// Penalty voor connectiviteit zou 0 moeten zijn (bovenop andere penalties).
|
// Clue A: (2,0) Right. Slot: (2,1), (2,2), (2,3), ...
|
||||||
// Als we een derde clue ver weg toevoegen, moet de penalty significant stijgen.
|
// Clue B: (1,2) Corner Down. Word starts at (1,3) en gaat omlaag: (1,3), (2,3), (3,3)...
|
||||||
|
// Ze kruisen op (2,3).
|
||||||
|
clues.setClueLo(1L << SwedishGenerator.Grid.offset(2,0), (byte)1); // Right
|
||||||
|
clues.setClueLo(1L << SwedishGenerator.Grid.offset(1,2), (byte)4); // Corner Down
|
||||||
|
|
||||||
Clues crossingPlusIsland = Clues.createEmpty();
|
long fitness = masker.maskFitness(clues, 2);
|
||||||
crossingPlusIsland.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1);
|
System.out.println("[DEBUG_LOG] Fitness corner clue connected: " + fitness);
|
||||||
crossingPlusIsland.setClueLo(1L << SwedishGenerator.Grid.offset(1,2), (byte)2);
|
|
||||||
crossingPlusIsland.setClueLo(1L << SwedishGenerator.Grid.offset(7,7), (byte)1);
|
|
||||||
|
|
||||||
long fitnessIsland = masker.maskFitness(crossingPlusIsland, 3);
|
// Als ze verbonden zijn, is de penalty voor eilandjes 0.
|
||||||
System.out.println("[DEBUG_LOG] Fitness crossing plus island: " + fitnessIsland);
|
// We vergelijken met een island scenario (2 clues die elkaar NIET raken)
|
||||||
|
Clues island = Clues.createEmpty();
|
||||||
|
int offA = SwedishGenerator.Grid.offset(2,0);
|
||||||
|
if (offA < 64) island.setClueLo(1L << offA, (byte)1);
|
||||||
|
else island.setClueHi(1L << (offA - 64), (byte)1);
|
||||||
|
|
||||||
assertTrue(fitnessIsland > fitness + 10000, "Island should add significant penalty");
|
int offB = SwedishGenerator.Grid.offset(7,7);
|
||||||
|
if (offB < 64) island.setClueLo(1L << offB, (byte)1);
|
||||||
|
else island.setClueHi(1L << (offB - 64), (byte)1);
|
||||||
|
|
||||||
|
long fitnessIsland = masker.maskFitness(island, 2);
|
||||||
|
System.out.println("[DEBUG_LOG] Fitness island: " + fitnessIsland);
|
||||||
|
|
||||||
|
assertTrue(fitnessIsland > fitness + 20000, "Island should add significant penalty compared to connected corner clue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
src/test/java/puzzle/CornerClueTest.java
Normal file
50
src/test/java/puzzle/CornerClueTest.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package puzzle;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import puzzle.Masker.Clues;
|
||||||
|
import puzzle.SwedishGenerator.DictEntry;
|
||||||
|
import puzzle.SwedishGenerator.Slotinfo;
|
||||||
|
|
||||||
|
public class CornerClueTest {
|
||||||
|
@Test
|
||||||
|
void testCornerDownSlot() {
|
||||||
|
Clues clues = Clues.createEmpty();
|
||||||
|
// Clue op (0,0), type 4 (Corner Down)
|
||||||
|
int idx = SwedishGenerator.Grid.offset(0,0);
|
||||||
|
clues.setClueLo(1L << idx, (byte)4);
|
||||||
|
|
||||||
|
assertEquals(4, clues.getDir(idx));
|
||||||
|
|
||||||
|
// Controleer of forEachSlot het slot vindt
|
||||||
|
final boolean[] found = {false};
|
||||||
|
clues.forEachSlot((key, lo, hi) -> {
|
||||||
|
if (Masker.Slot.dir(key) == 4) {
|
||||||
|
found[0] = true;
|
||||||
|
// Woord zou moeten starten op (0,1)
|
||||||
|
int startIdx = SwedishGenerator.Grid.offset(0, 1);
|
||||||
|
assertTrue((lo & (1L << startIdx)) != 0, "Slot should start at (0,1)");
|
||||||
|
// En omlaag gaan
|
||||||
|
int secondIdx = SwedishGenerator.Grid.offset(1, 1);
|
||||||
|
assertTrue((lo & (1L << secondIdx)) != 0, "Slot should continue to (1,1)");
|
||||||
|
|
||||||
|
// Lengte van het slot zou 8 moeten zijn (van rij 0 t/m 7 in kolom 1)
|
||||||
|
assertEquals(8, Masker.Slot.length(lo, hi));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assertTrue(found[0], "Corner Down slot should be found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCornerDownExtraction() {
|
||||||
|
Clues clues = Clues.createEmpty();
|
||||||
|
int idx = SwedishGenerator.Grid.offset(0,0);
|
||||||
|
clues.setClueLo(1L << idx, (byte)4);
|
||||||
|
|
||||||
|
DictEntry[] dict = DictData.DICT.index();
|
||||||
|
Slotinfo[] slots = Masker.slots(clues, dict);
|
||||||
|
|
||||||
|
assertEquals(1, slots.length);
|
||||||
|
assertEquals(4, Masker.Slot.dir(slots[0].key()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user