introduce bitloops
This commit is contained in:
@@ -42,23 +42,29 @@ public final class Masker {
|
|||||||
if (num == 0) return -1;
|
if (num == 0) return -1;
|
||||||
|
|
||||||
int start = rng.randint0_SIZE() % num;
|
int start = rng.randint0_SIZE() % num;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (int i = 0; i < num; i++) {
|
for (int i = 0; i < num; i++) {
|
||||||
int idx = activeCIdx[(start + i) % num];
|
int idx = activeCIdx[(start + i) % num];
|
||||||
int dir = grid.getDir(idx);
|
int dir = grid.getDir(idx);
|
||||||
int key = Slot.packSlotKey(idx, dir);
|
int key = Slot.packSlotKey(idx, dir);
|
||||||
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
||||||
long hLo = sLo & grid.lo, hHi = sHi & grid.hi;
|
long hLo = sLo & grid.lo, hHi = sHi & grid.hi;
|
||||||
if (Slotinfo.increasing(key)) {
|
if (Slotinfo.increasing(key)) {
|
||||||
if (hLo != X) { sLo &= (1L << numberOfTrailingZeros(hLo)) - 1; sHi = 0; }
|
if (hLo != X) {
|
||||||
else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
sLo &= (1L << numberOfTrailingZeros(hLo)) - 1;
|
||||||
|
sHi = 0;
|
||||||
|
} else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
||||||
} else {
|
} else {
|
||||||
if (hHi != X) { sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1); sLo = 0; }
|
if (hHi != X) {
|
||||||
else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1);
|
||||||
|
sLo = 0;
|
||||||
|
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
||||||
}
|
}
|
||||||
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
||||||
for (int j = 0; j < n; j++) if (bitCount(sLo & activeSLo[j]) + bitCount(sHi & activeSHi[j]) > 1) return idx;
|
for (int j = 0; j < n; j++) if (bitCount(sLo & activeSLo[j]) + bitCount(sHi & activeSHi[j]) > 1) return idx;
|
||||||
activeSLo[n] = sLo; activeSHi[n] = sHi; n++;
|
activeSLo[n] = sLo;
|
||||||
|
activeSHi[n] = sHi;
|
||||||
|
n++;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -315,16 +321,15 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (numClues > 0) {
|
if (numClues > 0) {
|
||||||
int maxReached = 0;
|
int maxReached = 0;
|
||||||
long totalReachedLo = 0, totalReachedHi = 0;
|
long totalReachedLo = 0, totalReachedHi = 0;
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (int i = 0; i < numClues; i++) {
|
||||||
if (i < 64) { if ((totalReachedLo & (1L << i)) != 0) continue; }
|
if (i < 64) { if ((totalReachedLo & (1L << i)) != 0) continue; } else { if ((totalReachedHi & (1L << (i - 64))) != 0) continue; }
|
||||||
else { if ((totalReachedHi & (1L << (i - 64))) != 0) continue; }
|
|
||||||
|
|
||||||
long currentReachedLo = (i < 64) ? (1L << i) : 0;
|
long currentReachedLo = (i < 64) ? (1L << i) : 0;
|
||||||
long currentReachedHi = (i >= 64) ? (1L << (i - 64)) : 0;
|
long currentReachedHi = (i >= 64) ? (1L << (i - 64)) : 0;
|
||||||
stack[0] = i;
|
stack[0] = i;
|
||||||
int sp = 1;
|
int sp = 1;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (sp > 0) {
|
while (sp > 0) {
|
||||||
int cur = stack[--sp];
|
int cur = stack[--sp];
|
||||||
@@ -334,16 +339,16 @@ public final class Masker {
|
|||||||
while (nLo != 0) {
|
while (nLo != 0) {
|
||||||
long lsb = nLo & -nLo;
|
long lsb = nLo & -nLo;
|
||||||
int idx = numberOfTrailingZeros(lsb);
|
int idx = numberOfTrailingZeros(lsb);
|
||||||
currentReachedLo |= lsb;
|
currentReachedLo |= lsb;
|
||||||
stack[sp++] = idx;
|
stack[sp++] = idx;
|
||||||
nLo &= ~lsb;
|
nLo &= ~lsb;
|
||||||
}
|
}
|
||||||
while (nHi != 0) {
|
while (nHi != 0) {
|
||||||
long lsb = nHi & -nHi;
|
long lsb = nHi & -nHi;
|
||||||
int idx = 64 | numberOfTrailingZeros(lsb);
|
int idx = 64 | numberOfTrailingZeros(lsb);
|
||||||
currentReachedHi |= lsb;
|
currentReachedHi |= lsb;
|
||||||
stack[sp++] = idx;
|
stack[sp++] = idx;
|
||||||
nHi &= ~lsb;
|
nHi &= ~lsb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (count > maxReached) maxReached = count;
|
if (count > maxReached) maxReached = count;
|
||||||
@@ -378,17 +383,17 @@ public final class Masker {
|
|||||||
else penalty += 1000;
|
else penalty += 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
long nclLo = ~lo_cl & MASK_LO;
|
long nclLo = ~lo_cl & MASK_LO;
|
||||||
long nclHi = ~hi_cl & MASK_HI;
|
long nclHi = ~hi_cl & MASK_HI;
|
||||||
long hNbrLo = (nclLo >> 8) | (nclLo << 8) | (nclHi << 56);
|
long hNbrLo = (nclLo >> 8) | (nclLo << 8) | (nclHi << 56);
|
||||||
long hNbrHi = (nclHi >> 8) | (nclLo >> 56);
|
long hNbrHi = (nclHi >> 8) | (nclLo >> 56);
|
||||||
long vNbrLo = ((nclLo & ~0x0101010101010101L) >> 1) | ((nclLo & ~0x8080808080808080L) << 1);
|
long vNbrLo = ((nclLo & ~0x0101010101010101L) >> 1) | ((nclLo & ~0x8080808080808080L) << 1);
|
||||||
long vNbrHi = ((nclHi & ~0x01L) >> 1) | ((nclHi & ~0x80L) << 1);
|
long vNbrHi = ((nclHi & ~0x01L) >> 1) | ((nclHi & ~0x80L) << 1);
|
||||||
|
|
||||||
penalty += bitCount(nclLo & ~cHLo & hNbrLo) * 800;
|
//penalty += bitCount(nclLo & ~cHLo & hNbrLo) * 800;
|
||||||
penalty += bitCount(nclLo & ~cVLo & vNbrLo) * 800;
|
//penalty += bitCount(nclLo & ~cVLo & vNbrLo) * 800;
|
||||||
penalty += bitCount(nclHi & ~cHHi & hNbrHi) * 800;
|
//penalty += bitCount(nclHi & ~cHHi & hNbrHi) * 800;
|
||||||
penalty += bitCount(nclHi & ~cVHi & vNbrHi) * 800;
|
//penalty += bitCount(nclHi & ~cVHi & vNbrHi) * 800;
|
||||||
|
|
||||||
return penalty;
|
return penalty;
|
||||||
}
|
}
|
||||||
@@ -424,7 +429,7 @@ public final class Masker {
|
|||||||
|
|
||||||
public Clues mutate(Clues c) {
|
public Clues mutate(Clues c) {
|
||||||
var bytes = MUTATE_RI[rng.randint0_SIZE()];
|
var bytes = MUTATE_RI[rng.randint0_SIZE()];
|
||||||
for (int k = 0, ri; k < 4; k++) {
|
for (int k = 0, ri; k < 6; k++) {
|
||||||
ri = bytes[rng.randint0_624()];
|
ri = bytes[rng.randint0_624()];
|
||||||
if (c.notClue(ri)) { // ADD
|
if (c.notClue(ri)) { // ADD
|
||||||
byte d = rng.randomClueDir();
|
byte d = rng.randomClueDir();
|
||||||
@@ -433,17 +438,31 @@ public final class Masker {
|
|||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
c.setClueLo(1L << ri, d);
|
c.setClueLo(1L << ri, d);
|
||||||
if (!isValid(c, MIN_LEN)) c.clearClueLo(~(1L << ri));
|
if (!isValid(c, MIN_LEN)) c.clearClueLo(~(1L << ri));
|
||||||
|
else continue;
|
||||||
} else {
|
} else {
|
||||||
c.setClueHi(1L << (ri & 63), d);
|
c.setClueHi(1L << (ri & 63), d);
|
||||||
if (!isValid(c, MIN_LEN)) c.clearClueHi(~(1L << (ri & 63)));
|
if (!isValid(c, MIN_LEN)) c.clearClueHi(~(1L << (ri & 63)));
|
||||||
|
else continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // HAS CLUE
|
} else { // HAS CLUE
|
||||||
var op = rng.randomClueDir();
|
var op = rng.randomClueDir();
|
||||||
if (op < 2) { // REMOVE
|
if (op < 2) { // REMOVE
|
||||||
if (isLo(ri)) c.clearClueLo(~(1L << ri));
|
byte oldD = c.getDir(ri);
|
||||||
else c.clearClueHi(~(1L << (ri & 63)));
|
if (isLo(ri)) {
|
||||||
} else if (op < 5) { // CHANGE DIRECTION
|
c.clearClueLo(~(1L << ri));
|
||||||
|
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
||||||
|
else continue;
|
||||||
|
} else {
|
||||||
|
c.clearClueHi(~(1L << (ri & 63)));
|
||||||
|
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
||||||
|
else continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if (isLo(ri)) c.clearClueLo(~(1L << ri));
|
||||||
|
else c.clearClueHi(~(1L << (ri & 63)));*/
|
||||||
|
}
|
||||||
|
if (op < 4) { // CHANGE DIRECTION
|
||||||
byte d = rng.randomClueDir();
|
byte d = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d);
|
int key = Slot.packSlotKey(ri, d);
|
||||||
if (c.hasRoomForClue(key)) {
|
if (c.hasRoomForClue(key)) {
|
||||||
@@ -451,28 +470,29 @@ public final class Masker {
|
|||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
c.setClueLo(1L << ri, d);
|
c.setClueLo(1L << ri, d);
|
||||||
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
||||||
|
else continue;
|
||||||
} else {
|
} else {
|
||||||
c.setClueHi(1L << (ri & 63), d);
|
c.setClueHi(1L << (ri & 63), d);
|
||||||
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
||||||
|
else continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // MOVE
|
} // MOVE
|
||||||
int nri = bytes[rng.randint0_624()];
|
int nri = bytes[rng.randint0_624()];
|
||||||
if (c.notClue(nri)) {
|
if (c.notClue(nri)) {
|
||||||
byte d = c.getDir(ri);
|
byte d = c.getDir(ri);
|
||||||
int nkey = Slot.packSlotKey(nri, d);
|
int nkey = Slot.packSlotKey(nri, d);
|
||||||
if (c.hasRoomForClue(nkey)) {
|
if (c.hasRoomForClue(nkey)) {
|
||||||
if (isLo(ri)) c.clearClueLo(~(1L << ri));
|
if (isLo(ri)) c.clearClueLo(~(1L << ri));
|
||||||
else c.clearClueHi(~(1L << (ri & 63)));
|
else c.clearClueHi(~(1L << (ri & 63)));
|
||||||
if (isLo(nri)) c.setClueLo(1L << nri, d);
|
if (isLo(nri)) c.setClueLo(1L << nri, d);
|
||||||
else c.setClueHi(1L << (nri & 63), d);
|
else c.setClueHi(1L << (nri & 63), d);
|
||||||
if (!isValid(c, MIN_LEN)) {
|
if (!isValid(c, MIN_LEN)) {
|
||||||
if (isLo(nri)) c.clearClueLo(~(1L << nri));
|
if (isLo(nri)) c.clearClueLo(~(1L << nri));
|
||||||
else c.clearClueHi(~(1L << (nri & 63)));
|
else c.clearClueHi(~(1L << (nri & 63)));
|
||||||
if (isLo(ri)) c.setClueLo(1L << ri, d);
|
if (isLo(ri)) c.setClueLo(1L << ri, d);
|
||||||
else c.setClueHi(1L << (ri & 63), d);
|
else c.setClueHi(1L << (ri & 63), d);
|
||||||
}
|
} else continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,38 +689,50 @@ public final class Masker {
|
|||||||
if (((xhi & rhi) & hi) != X) return 64 | numberOfTrailingZeros((xhi & rhi) & hi);
|
if (((xhi & rhi) & hi) != X) return 64 | numberOfTrailingZeros((xhi & rhi) & hi);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (long bits = lo; bits != X; bits &= bits - 1) {
|
for (long bits = lo; bits != X; bits &= bits - 1) {
|
||||||
int idx = numberOfTrailingZeros(bits);
|
int idx = numberOfTrailingZeros(bits);
|
||||||
int dir = getDir(idx);
|
int dir = getDir(idx);
|
||||||
int key = Slot.packSlotKey(idx, dir);
|
int key = Slot.packSlotKey(idx, dir);
|
||||||
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
||||||
long hLo = sLo & lo, hHi = sHi & hi;
|
long hLo = sLo & lo, hHi = sHi & hi;
|
||||||
if (Slotinfo.increasing(key)) {
|
if (Slotinfo.increasing(key)) {
|
||||||
if (hLo != X) { sLo &= (1L << numberOfTrailingZeros(hLo)) - 1; sHi = 0; }
|
if (hLo != X) {
|
||||||
else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
sLo &= (1L << numberOfTrailingZeros(hLo)) - 1;
|
||||||
|
sHi = 0;
|
||||||
|
} else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
||||||
} else {
|
} else {
|
||||||
if (hHi != X) { sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1); sLo = 0; }
|
if (hHi != X) {
|
||||||
else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1);
|
||||||
|
sLo = 0;
|
||||||
|
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
||||||
}
|
}
|
||||||
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
||||||
for (int i = 0; i < n; i++) if (bitCount(sLo & slo[i]) + bitCount(sHi & shi[i]) > 1) return idx;
|
for (int i = 0; i < n; i++) if (bitCount(sLo & slo[i]) + bitCount(sHi & shi[i]) > 1) return idx;
|
||||||
slo[n] = sLo; shi[n] = sHi; n++;
|
slo[n] = sLo;
|
||||||
|
shi[n] = sHi;
|
||||||
|
n++;
|
||||||
}
|
}
|
||||||
for (long bits = hi; bits != X; bits &= bits - 1) {
|
for (long bits = hi; bits != X; bits &= bits - 1) {
|
||||||
int idx = 64 | numberOfTrailingZeros(bits);
|
int idx = 64 | numberOfTrailingZeros(bits);
|
||||||
int dir = getDir(idx);
|
int dir = getDir(idx);
|
||||||
int key = Slot.packSlotKey(idx, dir);
|
int key = Slot.packSlotKey(idx, dir);
|
||||||
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
||||||
long hLo = sLo & lo, hHi = sHi & hi;
|
long hLo = sLo & lo, hHi = sHi & hi;
|
||||||
if (Slotinfo.increasing(key)) {
|
if (Slotinfo.increasing(key)) {
|
||||||
if (hLo != X) { sLo &= (1L << numberOfTrailingZeros(hLo)) - 1; sHi = 0; }
|
if (hLo != X) {
|
||||||
else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
sLo &= (1L << numberOfTrailingZeros(hLo)) - 1;
|
||||||
|
sHi = 0;
|
||||||
|
} else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
||||||
} else {
|
} else {
|
||||||
if (hHi != X) { sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1); sLo = 0; }
|
if (hHi != X) {
|
||||||
else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
sHi &= -(1L << (63 - numberOfLeadingZeros(hHi)) << 1);
|
||||||
|
sLo = 0;
|
||||||
|
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
||||||
}
|
}
|
||||||
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
||||||
for (int i = 0; i < n; i++) if (bitCount(sLo & slo[i]) + bitCount(sHi & shi[i]) > 1) return idx;
|
for (int i = 0; i < n; i++) if (bitCount(sLo & slo[i]) + bitCount(sHi & shi[i]) > 1) return idx;
|
||||||
slo[n] = sLo; shi[n] = sHi; n++;
|
slo[n] = sLo;
|
||||||
|
shi[n] = sHi;
|
||||||
|
n++;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public record SwedishGenerator() {
|
|||||||
public static final int MAX_WORD_LENGTH = Config.PUZZLE_ROWS;
|
public static final int MAX_WORD_LENGTH = Config.PUZZLE_ROWS;
|
||||||
public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
|
public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
|
||||||
public static final int MIN_LEN = 2;//Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
|
public static final int MIN_LEN = 2;//Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
|
||||||
public static final int MAX_TRIES_PER_SLOT = 1200;//Config.MAX_TRIES_PER_SLOT;
|
public static final int MAX_TRIES_PER_SLOT = 700;//Config.MAX_TRIES_PER_SLOT;
|
||||||
public static final int STACK_SIZE = 128;
|
public static final int STACK_SIZE = 128;
|
||||||
public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;// (long) SIZE_MIN_1 - 0L + 1L
|
public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;// (long) SIZE_MIN_1 - 0L + 1L
|
||||||
public static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624;//624L - 0L + 1L;
|
public static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624;//624L - 0L + 1L;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import module java.base;
|
import module java.base;
|
||||||
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import puzzle.Export.Clued;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static puzzle.Masker.Clues;
|
import static puzzle.Masker.Clues;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
@@ -9,109 +11,158 @@ import static puzzle.Masker.Slot;
|
|||||||
|
|
||||||
public class MaskerCluesTest {
|
public class MaskerCluesTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSimilarity() {
|
void testValidRandomMask() {
|
||||||
Clues a = Clues.createEmpty();
|
Rng rng = new Rng(42);
|
||||||
a.setClueLo(1L << 0, (byte) 1);
|
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
|
||||||
a.setClueLo(1L << 10, (byte) 0);
|
for (int i = 0; i < 200; i++) {
|
||||||
|
for (int j = 19; j < 24; j++) {
|
||||||
|
var clues = masker.randomMask(j);
|
||||||
|
assertTrue(clues.isValid(MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void testValidMutate() {
|
||||||
|
Rng rng = new Rng(42);
|
||||||
|
var cache = Clues.createEmpty();
|
||||||
|
Masker masker = new Masker(rng, new int[STACK_SIZE], cache);
|
||||||
|
double sim = 0.0;
|
||||||
|
double simCount = 0.0;
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
for (int j = 19; j < 24; j++) {
|
||||||
|
var clues = masker.randomMask(j);
|
||||||
|
val orig = cache.from(clues);
|
||||||
|
simCount++;
|
||||||
|
masker.mutate(clues);
|
||||||
|
sim += orig.similarity(clues);
|
||||||
|
assertTrue(clues.isValid(MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("Average similarity: " + sim / simCount);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void testCross() {
|
||||||
|
Rng rng = new Rng(42);
|
||||||
|
var cache = Clues.createEmpty();
|
||||||
|
Masker masker = new Masker(rng, new int[STACK_SIZE], cache);
|
||||||
|
double sim = 0.0;
|
||||||
|
double simCount = 0.0;
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
for (int j = 19; j < 24; j++) {
|
||||||
|
var clues = masker.randomMask(j);
|
||||||
|
var clues2 = masker.randomMask(j);
|
||||||
|
simCount++;
|
||||||
|
var cross = masker.crossover(clues, clues2);
|
||||||
|
sim += Math.max(cross.similarity(clues), cross.similarity(clues2));
|
||||||
|
assertTrue(cross.isValid(MIN_LEN), "Mask should be valid for length \n" + new Clued(cross).gridToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("Average similarity: " + sim / simCount);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void testSimilarity() {
|
||||||
|
Clues a = Clues.createEmpty();
|
||||||
|
a.setClueLo(1L << 0, (byte) 1);
|
||||||
|
a.setClueLo(1L << 10, (byte) 0);
|
||||||
|
|
||||||
Clues b = Clues.createEmpty();
|
Clues b = Clues.createEmpty();
|
||||||
b.setClueLo(1L << 0, (byte) 1);
|
b.setClueLo(1L << 0, (byte) 1);
|
||||||
b.setClueLo(1L << 10, (byte) 0);
|
b.setClueLo(1L << 10, (byte) 0);
|
||||||
|
|
||||||
// Identity
|
// Identity
|
||||||
assertEquals(1.0, a.similarity(b), 0.001);
|
assertEquals(1.0, a.similarity(b), 0.001);
|
||||||
|
|
||||||
// Different direction
|
// Different direction
|
||||||
Clues c = Clues.createEmpty();
|
Clues c = Clues.createEmpty();
|
||||||
c.setClueLo(1L << 0, (byte) 0);
|
c.setClueLo(1L << 0, (byte) 0);
|
||||||
c.setClueLo(1L << 10, (byte) 0);
|
c.setClueLo(1L << 10, (byte) 0);
|
||||||
assertTrue(a.similarity(c) < 1.0);
|
assertTrue(a.similarity(c) < 1.0);
|
||||||
|
|
||||||
// Completely different
|
// Completely different
|
||||||
Clues d = Clues.createEmpty();
|
Clues d = Clues.createEmpty();
|
||||||
// Matching empty cells count towards similarity.
|
// Matching empty cells count towards similarity.
|
||||||
// a has 2 clues, d has 0. They match on 70 empty cells.
|
// a has 2 clues, d has 0. They match on 70 empty cells.
|
||||||
assertEquals(70.0 / 72.0, a.similarity(d), 0.001);
|
assertEquals(70.0 / 72.0, a.similarity(d), 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testIsValid() {
|
void testIsValid() {
|
||||||
Clues g = Clues.createEmpty();
|
Clues g = Clues.createEmpty();
|
||||||
assertTrue(g.isValid(MIN_LEN));
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
// Valid clue: Right from (0,0) in 9x8 grid. Length is 8.
|
// Valid clue: Right from (0,0) in 9x8 grid. Length is 8.
|
||||||
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
|
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
|
||||||
assertTrue(g.isValid(MIN_LEN));
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
// Invalid clue: Right from (0,7) in 9x8 grid. Length is 1 (too short if MIN_LEN >= 2).
|
// Invalid clue: Right from (0,7) in 9x8 grid. Length is 1 (too short if MIN_LEN >= 2).
|
||||||
Clues g2 = Clues.createEmpty();
|
Clues g2 = Clues.createEmpty();
|
||||||
g2.setClueLo(1L << Masker.offset(0, 7), (byte) 1);
|
g2.setClueLo(1L << Masker.offset(0, 7), (byte) 1);
|
||||||
assertFalse(g2.isValid(MIN_LEN));
|
assertFalse(g2.isValid(MIN_LEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testHasRoomForClue() {
|
void testHasRoomForClue() {
|
||||||
Clues g = Clues.createEmpty();
|
Clues g = Clues.createEmpty();
|
||||||
|
|
||||||
// Room for Right clue at (0,0) (length 8)
|
// Room for Right clue at (0,0) (length 8)
|
||||||
assertTrue(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
assertTrue(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
||||||
|
|
||||||
// No room for Right clue at (0,8) (length 0 < MIN_LEN)
|
// No room for Right clue at (0,8) (length 0 < MIN_LEN)
|
||||||
assertFalse(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 8), 1)));
|
assertFalse(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 8), 1)));
|
||||||
|
|
||||||
// Blocked room
|
// Blocked room
|
||||||
// Let's place a clue that leaves only 1 cell for another clue.
|
// Let's place a clue that leaves only 1 cell for another clue.
|
||||||
g.setClueLo(1L << Masker.offset(0, 2), (byte) 1);
|
g.setClueLo(1L << Masker.offset(0, 2), (byte) 1);
|
||||||
// Now Right at (0,0) only has (0,1) available -> length 1 < MIN_LEN (which is 2)
|
// Now Right at (0,0) only has (0,1) available -> length 1 < MIN_LEN (which is 2)
|
||||||
assertFalse(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
assertFalse(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
||||||
|
|
||||||
// But enough room
|
// But enough room
|
||||||
g.clearClueLo(0L);
|
g.clearClueLo(0L);
|
||||||
g.setClueLo(1L << Masker.offset(0, 3), (byte) 1);
|
g.setClueLo(1L << Masker.offset(0, 3), (byte) 1);
|
||||||
// Now Right at (0,0) has (0,1), (0,2) -> length 2 == MIN_LEN
|
// Now Right at (0,0) has (0,1), (0,2) -> length 2 == MIN_LEN
|
||||||
assertTrue(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
assertTrue(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testIntersectionConstraint() {
|
void testIntersectionConstraint() {
|
||||||
Clues g = Clues.createEmpty();
|
Clues g = Clues.createEmpty();
|
||||||
|
|
||||||
// Clue 1: (0,0) Right. Slot cells: (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8)
|
// Clue 1: (0,0) Right. Slot cells: (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8)
|
||||||
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
|
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
|
||||||
|
|
||||||
// Clue 2: (1,2) Up. Slot cells: (0,2)
|
// Clue 2: (1,2) Up. Slot cells: (0,2)
|
||||||
// Intersection is exactly 1 cell (0,2). Valid.
|
// Intersection is exactly 1 cell (0,2). Valid.
|
||||||
g.setClueLo(1L << Masker.offset(2, 2), (byte) 2);
|
g.setClueLo(1L << Masker.offset(2, 2), (byte) 2);
|
||||||
assertTrue(g.isValid(MIN_LEN));
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
// Clue 3: (1,1) Right. Slot cells: (1,2), (1,3), ...
|
// Clue 3: (1,1) Right. Slot cells: (1,2), (1,3), ...
|
||||||
// No intersection with Clue 1 or 2. Valid.
|
// No intersection with Clue 1 or 2. Valid.
|
||||||
g.setClueLo(1L << Masker.offset(1, 1), (byte) 1);
|
g.setClueLo(1L << Masker.offset(1, 1), (byte) 1);
|
||||||
assertTrue(g.isValid(MIN_LEN));
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
// Now create a violation: two slots sharing 2 cells.
|
// Now create a violation: two slots sharing 2 cells.
|
||||||
// We can do this with Corner Down and another clue.
|
// We can do this with Corner Down and another clue.
|
||||||
// Clue A: (0,0) Corner Down. Starts at (0,1) goes down: (0,1), (1,1), (2,1), (3,1), ...
|
// Clue A: (0,0) Corner Down. Starts at (0,1) goes down: (0,1), (1,1), (2,1), (3,1), ...
|
||||||
// Clue B: (0,2) Corner Down Left. Starts at (0,1) goes down: (0,1), (1,1), (2,1), ...
|
// Clue B: (0,2) Corner Down Left. Starts at (0,1) goes down: (0,1), (1,1), (2,1), ...
|
||||||
// They share MANY cells starting from (0,1).
|
// They share MANY cells starting from (0,1).
|
||||||
|
|
||||||
Clues g3 = Clues.createEmpty();
|
Clues g3 = Clues.createEmpty();
|
||||||
g3.setClueLo(1L << Masker.offset(0, 0), (byte) 4); // Corner Down
|
g3.setClueLo(1L << Masker.offset(0, 0), (byte) 4); // Corner Down
|
||||||
g3.setClueLo(1L << Masker.offset(0, 2), (byte) 5); // Corner Down Left
|
g3.setClueLo(1L << Masker.offset(0, 2), (byte) 5); // Corner Down Left
|
||||||
assertFalse(g3.isValid(MIN_LEN));
|
assertFalse(g3.isValid(MIN_LEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInvalidDirectionBits() {
|
void testInvalidDirectionBits() {
|
||||||
Clues g = Clues.createEmpty();
|
Clues g = Clues.createEmpty();
|
||||||
// Dir 6 (x=1, r=1, v=0) is invalid
|
// Dir 6 (x=1, r=1, v=0) is invalid
|
||||||
g.setClueLo(1L << 0, (byte) 6);
|
g.setClueLo(1L << 0, (byte) 6);
|
||||||
assertFalse(g.isValid(MIN_LEN));
|
assertFalse(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
// Dir 7 (x=1, r=1, v=1) is invalid
|
// Dir 7 (x=1, r=1, v=1) is invalid
|
||||||
Clues g2 = Clues.createEmpty();
|
Clues g2 = Clues.createEmpty();
|
||||||
g2.setClueLo(1L << 0, (byte) 7);
|
g2.setClueLo(1L << 0, (byte) 7);
|
||||||
assertFalse(g2.isValid(MIN_LEN));
|
assertFalse(g2.isValid(MIN_LEN));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user