introduce bitloops
This commit is contained in:
@@ -41,6 +41,7 @@ public record Export() {
|
|||||||
static final byte CLUE_UP = 2;
|
static final byte CLUE_UP = 2;
|
||||||
static final byte CLUE_LEFT = 3;
|
static final byte CLUE_LEFT = 3;
|
||||||
static final byte CLUE_LEFT_TOP = 4;
|
static final byte CLUE_LEFT_TOP = 4;
|
||||||
|
static final byte CLUE_RIGHT_TOP = 5;
|
||||||
static int HI(int in) { return in | 64; }
|
static int HI(int in) { return in | 64; }
|
||||||
static char LETTER(int in) { return (char) (in | 64); }
|
static char LETTER(int in) { return (char) (in | 64); }
|
||||||
static char CLUE_CHAR(int s) { return (char) (s | 48); }
|
static char CLUE_CHAR(int s) { return (char) (s | 48); }
|
||||||
@@ -100,13 +101,15 @@ public record Export() {
|
|||||||
for (var l = c.lo & ~c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), DOWN.dir));
|
for (var l = c.lo & ~c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), DOWN.dir));
|
||||||
for (var l = c.lo & ~c.xlo & c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_UP));
|
for (var l = c.lo & ~c.xlo & c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_UP));
|
||||||
for (var l = c.lo & ~c.xlo & c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT));
|
for (var l = c.lo & ~c.xlo & c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT));
|
||||||
for (var l = c.lo & c.xlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP));
|
for (var l = c.lo & c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP));
|
||||||
|
for (var l = c.lo & c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_RIGHT_TOP));
|
||||||
|
|
||||||
for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT));
|
for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT));
|
||||||
for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN));
|
for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN));
|
||||||
for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_UP));
|
for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_UP));
|
||||||
for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT));
|
for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT));
|
||||||
for (var h = c.hi & c.xhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP));
|
for (var h = c.hi & c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP));
|
||||||
|
for (var h = c.hi & c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT_TOP));
|
||||||
|
|
||||||
return stream.build();
|
return stream.build();
|
||||||
}
|
}
|
||||||
@@ -223,7 +226,7 @@ public record Export() {
|
|||||||
|
|
||||||
public static final char HORIZONTAL = 'h';
|
public static final char HORIZONTAL = 'h';
|
||||||
static final char VERTICAL = 'v';
|
static final char VERTICAL = 'v';
|
||||||
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL };
|
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.VERTICAL };
|
||||||
|
|
||||||
public int arrowCol() { return Masker.IT[Slot.clueIndex(slotKey)].c(); }
|
public int arrowCol() { return Masker.IT[Slot.clueIndex(slotKey)].c(); }
|
||||||
public int arrowRow() { return Masker.IT[Slot.clueIndex(slotKey)].r(); }
|
public int arrowRow() { return Masker.IT[Slot.clueIndex(slotKey)].r(); }
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class Main {
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public static class Opts {
|
public static class Opts {
|
||||||
|
|
||||||
static int SSIZE = 24;
|
static int SSIZE = 23;
|
||||||
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
||||||
public int clueSize = SSIZE;
|
public int clueSize = SSIZE;
|
||||||
public int pop = SSIZE * 2;
|
public int pop = SSIZE * 2;
|
||||||
|
|||||||
@@ -27,6 +27,20 @@ public final class Masker {
|
|||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isValid(Clues c, int minLen) {
|
||||||
|
return c.isValid(minLen, activeSLo, activeSHi);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup(Clues c, int minLen) {
|
||||||
|
int guard = 0;
|
||||||
|
while (guard++ < 50) {
|
||||||
|
int offending = c.findOffendingClue(minLen, activeSLo, activeSHi);
|
||||||
|
if (offending == -1) break;
|
||||||
|
if ((offending & 64) == 0) c.clearClueLo(~(1L << offending));
|
||||||
|
else c.clearClueHi(~(1L << (offending & 63)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final int[][] MUTATE_RI = new int[SwedishGenerator.SIZE][625];
|
public static final int[][] MUTATE_RI = new int[SwedishGenerator.SIZE][625];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -105,7 +119,7 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
public static Slot[] extractSlots(Clues grid, DictEntry[] index) {
|
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)]))));
|
grid.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, index[Slot.length(lo, hi)])));
|
||||||
return slots.toArray(Slot[]::new);
|
return slots.toArray(Slot[]::new);
|
||||||
}
|
}
|
||||||
public static Slotinfo[] slots(Clues mask, DictEntry[] index) {
|
public static Slotinfo[] slots(Clues mask, DictEntry[] index) {
|
||||||
@@ -121,7 +135,8 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
for (int i = 0; i < slots.length; i++) {
|
for (int i = 0; i < slots.length; i++) {
|
||||||
var slot = slots[i];
|
var slot = slots[i];
|
||||||
slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScore(count, slot.lo, slot.hi), new Assign(), slot.entry);
|
slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScore(count, slot.lo, slot.hi), new Assign(), slot.entry,
|
||||||
|
Math.min(slot.entry.words().length, MAX_TRIES_PER_SLOT));
|
||||||
}
|
}
|
||||||
return slotInfo;
|
return slotInfo;
|
||||||
}
|
}
|
||||||
@@ -141,7 +156,7 @@ public final class Masker {
|
|||||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||||
long penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
|
long penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
|
||||||
boolean hasSlots = false;
|
boolean hasSlots = false;
|
||||||
if (!grid.isValid(2)) return 1_000_000_000L;
|
if (!isValid(grid, 2)) return 1_000_000_000L;
|
||||||
|
|
||||||
int numClues = 0;
|
int numClues = 0;
|
||||||
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
||||||
@@ -315,20 +330,20 @@ public final class Masker {
|
|||||||
ri = rng.randint0_SIZE();
|
ri = rng.randint0_SIZE();
|
||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
if (g.isClueLo(ri)) continue;
|
if (g.isClueLo(ri)) continue;
|
||||||
var d_idx = rng.randint2bitByte();
|
var d_idx = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d_idx);
|
int key = Slot.packSlotKey(ri, d_idx);
|
||||||
if (g.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
if (g.hasRoomForClue(key)) {
|
||||||
g.setClueLo(1L << ri, d_idx);
|
g.setClueLo(1L << ri, d_idx);
|
||||||
if (g.isValid(MIN_LEN)) placed++;
|
if (isValid(g, MIN_LEN)) placed++;
|
||||||
else g.clearClueLo(~(1L << ri));
|
else g.clearClueLo(~(1L << ri));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (g.isClueHi(ri)) continue;
|
if (g.isClueHi(ri)) continue;
|
||||||
var d_idx = rng.randint2bitByte();
|
var d_idx = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d_idx);
|
int key = Slot.packSlotKey(ri, d_idx);
|
||||||
if (g.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
if (g.hasRoomForClue(key)) {
|
||||||
g.setClueHi(1L << (ri & 63), d_idx);
|
g.setClueHi(1L << (ri & 63), d_idx);
|
||||||
if (g.isValid(MIN_LEN)) placed++;
|
if (isValid(g, MIN_LEN)) placed++;
|
||||||
else g.clearClueHi(~(1L << (ri & 63)));
|
else g.clearClueHi(~(1L << (ri & 63)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,33 +357,33 @@ public final class Masker {
|
|||||||
for (int k = 0, ri; k < 4; k++) {
|
for (int k = 0, ri; k < 4; k++) {
|
||||||
ri = bytes[rng.randint0_624()];
|
ri = bytes[rng.randint0_624()];
|
||||||
if (c.notClue(ri)) { // ADD
|
if (c.notClue(ri)) { // ADD
|
||||||
byte d = rng.randint2bitByte();
|
byte d = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d);
|
int key = Slot.packSlotKey(ri, d);
|
||||||
if (c.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
if (c.hasRoomForClue(key)) {
|
||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
c.setClueLo(1L << ri, d);
|
c.setClueLo(1L << ri, d);
|
||||||
if (!c.isValid(MIN_LEN)) c.clearClueLo(~(1L << ri));
|
if (!isValid(c, MIN_LEN)) c.clearClueLo(~(1L << ri));
|
||||||
} else {
|
} else {
|
||||||
c.setClueHi(1L << (ri & 63), d);
|
c.setClueHi(1L << (ri & 63), d);
|
||||||
if (!c.isValid(MIN_LEN)) c.clearClueHi(~(1L << (ri & 63)));
|
if (!isValid(c, MIN_LEN)) c.clearClueHi(~(1L << (ri & 63)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // HAS CLUE
|
} else { // HAS CLUE
|
||||||
int op = rng.randint(10);
|
var op = rng.randomClueDir();
|
||||||
if (op < 2) { // REMOVE
|
if (op < 2) { // REMOVE
|
||||||
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)));
|
||||||
} else if (op < 5) { // CHANGE DIRECTION
|
} else if (op < 5) { // CHANGE DIRECTION
|
||||||
byte d = rng.randint2bitByte();
|
byte d = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d);
|
int key = Slot.packSlotKey(ri, d);
|
||||||
if (c.hasRoomForClue(key, OFFSETS_D_IDX[key])) {
|
if (c.hasRoomForClue(key)) {
|
||||||
byte oldD = c.getDir(ri);
|
byte oldD = c.getDir(ri);
|
||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
c.setClueLo(1L << ri, d);
|
c.setClueLo(1L << ri, d);
|
||||||
if (!c.isValid(MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
||||||
} else {
|
} else {
|
||||||
c.setClueHi(1L << (ri & 63), d);
|
c.setClueHi(1L << (ri & 63), d);
|
||||||
if (!c.isValid(MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // MOVE
|
} else { // MOVE
|
||||||
@@ -376,12 +391,12 @@ public final class Masker {
|
|||||||
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, OFFSETS_D_IDX[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 (!c.isValid(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);
|
||||||
@@ -417,30 +432,10 @@ public final class Masker {
|
|||||||
(a.rhi & ~maskHi) | (other.rhi & maskHi),
|
(a.rhi & ~maskHi) | (other.rhi & maskHi),
|
||||||
(a.xlo & ~maskLo) | (other.xlo & maskLo),
|
(a.xlo & ~maskLo) | (other.xlo & maskLo),
|
||||||
(a.xhi & ~maskHi) | (other.xhi & maskHi));
|
(a.xhi & ~maskHi) | (other.xhi & maskHi));
|
||||||
int guard = 0;
|
cleanup(c, MIN_LEN);
|
||||||
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;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|
||||||
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) {
|
public Clues hillclimb(Clues start, int clue_size, int limit) {
|
||||||
var best = start;
|
var best = start;
|
||||||
var bestF = maskFitness(best, clue_size);
|
var bestF = maskFitness(best, clue_size);
|
||||||
@@ -498,19 +493,34 @@ public final class Masker {
|
|||||||
System.arraycopy(children, 0, combined, pop.length, childCount);
|
System.arraycopy(children, 0, combined, pop.length, childCount);
|
||||||
Arrays.sort(combined, Comparator.comparingLong(GridAndFit::fit));
|
Arrays.sort(combined, Comparator.comparingLong(GridAndFit::fit));
|
||||||
|
|
||||||
GridAndFit[] next = new GridAndFit[offspring];
|
GridAndFit[] next = new GridAndFit[popSize];
|
||||||
int nextCount = 0;
|
int nextCount = 0;
|
||||||
for (GridAndFit cand : combined) {
|
for (GridAndFit cand : combined) {
|
||||||
if (nextCount >= offspring) break;
|
if (nextCount >= popSize) break;
|
||||||
boolean ok = true;
|
boolean unique = true;
|
||||||
for (int i = 0; i < nextCount; i++)
|
for (int i = 0; i < nextCount; i++) {
|
||||||
if (cand.grid.similarity(next[i].grid) > 0.92) {
|
if (cand.grid.similarity(next[i].grid) > 0.92) {
|
||||||
ok = false;
|
unique = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ok) next[nextCount++] = cand;
|
|
||||||
}
|
}
|
||||||
pop = nextCount == offspring ? next : Arrays.copyOf(next, nextCount);
|
if (unique) next[nextCount++] = cand;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextCount < popSize) {
|
||||||
|
for (GridAndFit cand : combined) {
|
||||||
|
if (nextCount >= popSize) break;
|
||||||
|
boolean alreadyIn = false;
|
||||||
|
for (int i = 0; i < nextCount; i++) {
|
||||||
|
if (cand == next[i]) {
|
||||||
|
alreadyIn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!alreadyIn) next[nextCount++] = cand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pop = nextCount == popSize ? next : Arrays.copyOf(next, nextCount);
|
||||||
|
|
||||||
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop[0].fit());
|
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop[0].fit());
|
||||||
}
|
}
|
||||||
@@ -531,18 +541,7 @@ public final class Masker {
|
|||||||
long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
|
long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
|
||||||
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0, 0, 0); }
|
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0, 0, 0); }
|
||||||
|
|
||||||
public boolean cluelessLo(int idx) {
|
public boolean hasRoomForClue(int key) {
|
||||||
if (!isClueLo(idx)) return false;
|
|
||||||
clearClueLo(~(1L << idx));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean cluelessHi(int idx) {
|
|
||||||
if (!isClueHi(idx)) return false;
|
|
||||||
clearClueHi(~(1L << (idx & 63)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean hasRoomForClue(int key, long packed) {
|
|
||||||
if (packed == X || !notClue(packed & 0x7FL) || !notClue((packed >>> 7) & 0x7FL)) return false;
|
|
||||||
if (Slotinfo.increasing(key)) if (!validSlot(lo, hi, MIN_LEN, key)) return false;
|
if (Slotinfo.increasing(key)) if (!validSlot(lo, hi, MIN_LEN, key)) return false;
|
||||||
return validSlotRev(lo, hi, MIN_LEN, key);
|
return validSlotRev(lo, hi, MIN_LEN, key);
|
||||||
}
|
}
|
||||||
@@ -579,7 +578,6 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
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; }
|
||||||
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 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 int clueCount() { return bitCount(lo) + bitCount(hi); }
|
||||||
@@ -591,18 +589,50 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); }
|
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); }
|
||||||
public boolean isValid(int minLen) {
|
public boolean isValid(int minLen) {
|
||||||
for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 1))) return false;
|
return findOffendingClue(minLen, new long[SwedishGenerator.SIZE], new long[SwedishGenerator.SIZE]) == -1;
|
||||||
for (var l = lo & ~xlo & ~rlo & ~vlo; l != X; l &= l - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 0))) return false;
|
}
|
||||||
for (var l = lo & ~xlo & rlo & ~vlo; l != X; l &= l - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 2))) return false;
|
public boolean isValid(int minLen, long[] slo, long[] shi) {
|
||||||
for (var l = lo & ~xlo & rlo & vlo; l != X; l &= l - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 3))) return false;
|
return findOffendingClue(minLen, slo, shi) == -1;
|
||||||
for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 4))) return false;
|
}
|
||||||
|
public int findOffendingClue(int minLen, long[] slo, long[] shi) {
|
||||||
for (var h = hi & ~xhi & ~rhi & vhi; h != X; h &= h - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1))) return false;
|
if (((xlo & rlo) & lo) != X) return numberOfTrailingZeros((xlo & rlo) & lo);
|
||||||
for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0))) return false;
|
if (((xhi & rhi) & hi) != X) return 64 | numberOfTrailingZeros((xhi & rhi) & hi);
|
||||||
for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2))) return false;
|
int n = 0;
|
||||||
for (var h = hi & ~xhi & rhi & vhi; h != X; h &= h - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3))) return false;
|
for (long bits = lo; bits != X; bits &= bits - 1) {
|
||||||
for (var h = hi & xhi & ~rhi & ~vhi; h != X; h &= h - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 4))) return false;
|
int idx = numberOfTrailingZeros(bits);
|
||||||
return true;
|
int dir = getDir(idx);
|
||||||
|
int key = Slot.packSlotKey(idx, dir);
|
||||||
|
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
||||||
|
long hLo = sLo & lo, hHi = sHi & hi;
|
||||||
|
if (Slotinfo.increasing(key)) {
|
||||||
|
if (hLo != X) { sLo &= (1L << numberOfTrailingZeros(hLo)) - 1; sHi = 0; }
|
||||||
|
else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
||||||
|
} else {
|
||||||
|
if (hHi != X) { 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;
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
for (long bits = hi; bits != X; bits &= bits - 1) {
|
||||||
|
int idx = 64 | numberOfTrailingZeros(bits);
|
||||||
|
int dir = getDir(idx);
|
||||||
|
int key = Slot.packSlotKey(idx, dir);
|
||||||
|
long sLo = PATH_LO[key], sHi = PATH_HI[key];
|
||||||
|
long hLo = sLo & lo, hHi = sHi & hi;
|
||||||
|
if (Slotinfo.increasing(key)) {
|
||||||
|
if (hLo != X) { sLo &= (1L << numberOfTrailingZeros(hLo)) - 1; sHi = 0; }
|
||||||
|
else if (hHi != X) { sHi &= (1L << numberOfTrailingZeros(hHi)) - 1; }
|
||||||
|
} else {
|
||||||
|
if (hHi != X) { 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;
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
public void forEachSlot(SlotVisitor visitor) {
|
public void forEachSlot(SlotVisitor visitor) {
|
||||||
for (var l = lo & ~xlo & ~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));
|
||||||
@@ -610,12 +640,14 @@ public final class Masker {
|
|||||||
for (var l = lo & ~xlo & 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 & ~xlo & 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 l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4));
|
for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4));
|
||||||
|
for (var l = lo & xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 5));
|
||||||
|
|
||||||
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 & ~xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1));
|
||||||
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) 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)), 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) 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));
|
for (var h = hi & xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 4));
|
||||||
|
for (var h = hi & xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 5));
|
||||||
}
|
}
|
||||||
public Clues from(Clues best) {
|
public Clues from(Clues best) {
|
||||||
lo = best.lo;
|
lo = best.lo;
|
||||||
@@ -650,7 +682,7 @@ public final class Masker {
|
|||||||
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 & 7; }
|
public static int dir(int key) { return key & 7; }
|
||||||
public static boolean horiz(int d) { return (d & 1) != 0 && (d & 4) == 0; }
|
public static boolean horiz(int d) { return (d == 1) || (d == 3); }
|
||||||
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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public record SwedishGenerator() {
|
|||||||
x = y;
|
x = y;
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
static final byte[] BYTE = new byte[]{ 0, 1, 2, 3/*,4, 5*/ };
|
static final byte[] BYTE = new byte[]{ 0, 1, 2, 3, 4, 5 };
|
||||||
public byte randomClueDir() { return rand(BYTE); }
|
public byte randomClueDir() { return rand(BYTE); }
|
||||||
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length)))]; }
|
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length)))]; }
|
||||||
public byte rand(byte[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length)))]; }
|
public byte rand(byte[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length)))]; }
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import gen.GenerateNeighbors;
|
import gen.GenerateNeighbors;
|
||||||
@GenerateNeighbors(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 3)
|
@GenerateNeighbors(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2)
|
||||||
public final class Trigger { }
|
public final class Trigger { }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import puzzle.Export.LetterVisit.LetterAt;
|
|||||||
import puzzle.Export.PuzzleResult;
|
import puzzle.Export.PuzzleResult;
|
||||||
import puzzle.Export.Rewards;
|
import puzzle.Export.Rewards;
|
||||||
import puzzle.Main.Opts;
|
import puzzle.Main.Opts;
|
||||||
|
import puzzle.Masker.Slot;
|
||||||
import puzzle.SwedishGenerator.Rng;
|
import puzzle.SwedishGenerator.Rng;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
@@ -20,6 +21,7 @@ import static puzzle.Export.Clue.DOWN;
|
|||||||
import static puzzle.Export.Clue.LEFT;
|
import static puzzle.Export.Clue.LEFT;
|
||||||
import static puzzle.Export.Clue.RIGHT;
|
import static puzzle.Export.Clue.RIGHT;
|
||||||
import static puzzle.Export.Clue.UP;
|
import static puzzle.Export.Clue.UP;
|
||||||
|
import static puzzle.Masker.Slot;
|
||||||
import static puzzle.SwedishGenerator.Dict;
|
import static puzzle.SwedishGenerator.Dict;
|
||||||
import static puzzle.SwedishGenerator.Lemma;
|
import static puzzle.SwedishGenerator.Lemma;
|
||||||
import static puzzle.SwedishGenerator.Slotinfo;
|
import static puzzle.SwedishGenerator.Slotinfo;
|
||||||
@@ -216,8 +218,8 @@ public class MainTest {
|
|||||||
var grid = Slotinfo.grid(slotInfo);
|
var grid = Slotinfo.grid(slotInfo);
|
||||||
var filled = fillMask(rng, slotInfo, grid);
|
var filled = fillMask(rng, slotInfo, grid);
|
||||||
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
||||||
Assertions.assertEquals(13, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
|
Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
|
||||||
Assertions.assertEquals("WAANZIN", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get()));
|
Assertions.assertEquals("VREEMDS", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get()));
|
||||||
Assertions.assertEquals(-1L, grid.lo);
|
Assertions.assertEquals(-1L, grid.lo);
|
||||||
Assertions.assertEquals(-1L, grid.hi);
|
Assertions.assertEquals(-1L, grid.hi);
|
||||||
var g = new Gridded(grid, mask.c());
|
var g = new Gridded(grid, mask.c());
|
||||||
|
|||||||
117
src/test/java/puzzle/MaskerCluesTest.java
Normal file
117
src/test/java/puzzle/MaskerCluesTest.java
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package puzzle;
|
||||||
|
|
||||||
|
import module java.base;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static puzzle.Masker.Clues;
|
||||||
|
import static puzzle.SwedishGenerator.*;
|
||||||
|
import static puzzle.Masker.Slot;
|
||||||
|
|
||||||
|
public class MaskerCluesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimilarity() {
|
||||||
|
Clues a = Clues.createEmpty();
|
||||||
|
a.setClueLo(1L << 0, (byte) 1);
|
||||||
|
a.setClueLo(1L << 10, (byte) 0);
|
||||||
|
|
||||||
|
Clues b = Clues.createEmpty();
|
||||||
|
b.setClueLo(1L << 0, (byte) 1);
|
||||||
|
b.setClueLo(1L << 10, (byte) 0);
|
||||||
|
|
||||||
|
// Identity
|
||||||
|
assertEquals(1.0, a.similarity(b), 0.001);
|
||||||
|
|
||||||
|
// Different direction
|
||||||
|
Clues c = Clues.createEmpty();
|
||||||
|
c.setClueLo(1L << 0, (byte) 0);
|
||||||
|
c.setClueLo(1L << 10, (byte) 0);
|
||||||
|
assertTrue(a.similarity(c) < 1.0);
|
||||||
|
|
||||||
|
// Completely different
|
||||||
|
Clues d = Clues.createEmpty();
|
||||||
|
// Matching empty cells count towards similarity.
|
||||||
|
// a has 2 clues, d has 0. They match on 70 empty cells.
|
||||||
|
assertEquals(70.0 / 72.0, a.similarity(d), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIsValid() {
|
||||||
|
Clues g = Clues.createEmpty();
|
||||||
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
|
// Valid clue: Right from (0,0) in 9x8 grid. Length is 8.
|
||||||
|
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
|
||||||
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
|
// Invalid clue: Right from (0,7) in 9x8 grid. Length is 1 (too short if MIN_LEN >= 2).
|
||||||
|
Clues g2 = Clues.createEmpty();
|
||||||
|
g2.setClueLo(1L << Masker.offset(0, 7), (byte) 1);
|
||||||
|
assertFalse(g2.isValid(MIN_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testHasRoomForClue() {
|
||||||
|
Clues g = Clues.createEmpty();
|
||||||
|
|
||||||
|
// Room for Right clue at (0,0) (length 8)
|
||||||
|
assertTrue(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
||||||
|
|
||||||
|
// No room for Right clue at (0,8) (length 0 < MIN_LEN)
|
||||||
|
assertFalse(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 8), 1)));
|
||||||
|
|
||||||
|
// Blocked room
|
||||||
|
// Let's place a clue that leaves only 1 cell for another clue.
|
||||||
|
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)
|
||||||
|
assertFalse(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
||||||
|
|
||||||
|
// But enough room
|
||||||
|
g.clearClueLo(0L);
|
||||||
|
g.setClueLo(1L << Masker.offset(0, 3), (byte) 1);
|
||||||
|
// Now Right at (0,0) has (0,1), (0,2) -> length 2 == MIN_LEN
|
||||||
|
assertTrue(g.hasRoomForClue(Slot.packSlotKey(Masker.offset(0, 0), 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIntersectionConstraint() {
|
||||||
|
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)
|
||||||
|
g.setClueLo(1L << Masker.offset(0, 0), (byte) 1);
|
||||||
|
|
||||||
|
// Clue 2: (1,2) Up. Slot cells: (0,2)
|
||||||
|
// Intersection is exactly 1 cell (0,2). Valid.
|
||||||
|
g.setClueLo(1L << Masker.offset(2, 2), (byte) 2);
|
||||||
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
|
// Clue 3: (1,1) Right. Slot cells: (1,2), (1,3), ...
|
||||||
|
// No intersection with Clue 1 or 2. Valid.
|
||||||
|
g.setClueLo(1L << Masker.offset(1, 1), (byte) 1);
|
||||||
|
assertTrue(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
|
// Now create a violation: two slots sharing 2 cells.
|
||||||
|
// 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 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).
|
||||||
|
|
||||||
|
Clues g3 = Clues.createEmpty();
|
||||||
|
g3.setClueLo(1L << Masker.offset(0, 0), (byte) 4); // Corner Down
|
||||||
|
g3.setClueLo(1L << Masker.offset(0, 2), (byte) 5); // Corner Down Left
|
||||||
|
assertFalse(g3.isValid(MIN_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInvalidDirectionBits() {
|
||||||
|
Clues g = Clues.createEmpty();
|
||||||
|
// Dir 6 (x=1, r=1, v=0) is invalid
|
||||||
|
g.setClueLo(1L << 0, (byte) 6);
|
||||||
|
assertFalse(g.isValid(MIN_LEN));
|
||||||
|
|
||||||
|
// Dir 7 (x=1, r=1, v=1) is invalid
|
||||||
|
Clues g2 = Clues.createEmpty();
|
||||||
|
g2.setClueLo(1L << 0, (byte) 7);
|
||||||
|
assertFalse(g2.isValid(MIN_LEN));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user