introduce bitloops
This commit is contained in:
@@ -1,16 +0,0 @@
|
|||||||
package puzzle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generated constants from pom.xml during build via templating-maven-plugin.
|
|
||||||
*/
|
|
||||||
public final class Config {
|
|
||||||
public static final int CLUE_SIZE = 4;
|
|
||||||
public static final int MIN_LEN = 2;
|
|
||||||
public static final int MAX_TRIES_PER_SLOT = 1000;
|
|
||||||
public static final int MAX_LEN = 8;
|
|
||||||
public static final int PUZZLE_ROWS = 8;
|
|
||||||
public static final int PUZZLE_COLS = 9;
|
|
||||||
public static final int PUZZLE_SIZE = PUZZLE_ROWS*PUZZLE_COLS;
|
|
||||||
public static final int MAX_WORD_LENGTH = PUZZLE_ROWS;
|
|
||||||
public static final int MAX_WORD_LENGTH_MIN_1 = PUZZLE_ROWS-1;
|
|
||||||
}
|
|
||||||
@@ -45,11 +45,15 @@ public record Export() {
|
|||||||
static int INDEX(int r, int cols, int c) { return r * cols + c; }
|
static int INDEX(int r, int cols, int c) { return r * cols + c; }
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
enum Clue {
|
enum Clue {
|
||||||
DOWN(CLUE_DOWN),
|
DOWN(CLUE_DOWN, 'B', 'b'),
|
||||||
RIGHT(CLUE_RIGHT),
|
RIGHT(CLUE_RIGHT, 'A', 'a'),
|
||||||
UP(CLUE_UP),
|
UP(CLUE_UP, 'C', 'c'),
|
||||||
LEFT(CLUE_LEFT);
|
LEFT(CLUE_LEFT, 'D', 'd'),
|
||||||
|
NONE(CLUE_LEFT, '?', '?');
|
||||||
final byte dir;
|
final byte dir;
|
||||||
|
final char slotChar, clueChar;
|
||||||
|
private static final Clue[] CLUES = new Clue[]{ DOWN, RIGHT, UP, LEFT, NONE, NONE, NONE, NONE, NONE };
|
||||||
|
public static Clue from(int dir) { return CLUES[dir]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
record Strings() {
|
record Strings() {
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ public class Main {
|
|||||||
public int pop = SSIZE * 2;
|
public int pop = SSIZE * 2;
|
||||||
public int offspring = SSIZE * 3;
|
public int offspring = SSIZE * 3;
|
||||||
public int gens = 600;
|
public int gens = 600;
|
||||||
public String wordsPath = "nl_score_hints_v4.csv";
|
|
||||||
public double minSimplicity = 0; // 0 means no limit
|
public double minSimplicity = 0; // 0 means no limit
|
||||||
public int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
|
public int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
|
||||||
public int tries = threads;
|
public int tries = threads;
|
||||||
@@ -65,7 +64,6 @@ public class Main {
|
|||||||
|
|
||||||
section("Puzzle Generator");
|
section("Puzzle Generator");
|
||||||
info("OutputDir : " + OUT_DIR);
|
info("OutputDir : " + OUT_DIR);
|
||||||
info("WordsFile : " + opts.wordsPath);
|
|
||||||
|
|
||||||
section("Settings");
|
section("Settings");
|
||||||
printSettings(opts);
|
printSettings(opts);
|
||||||
@@ -146,7 +144,6 @@ public class Main {
|
|||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "population", o.pop);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "population", o.pop);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "offspring", o.offspring);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "offspring", o.offspring);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "generations", o.gens);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "generations", o.gens);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %s%n", "wordsPath", o.wordsPath);
|
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %.2f%n", "minSimplicity", o.minSimplicity);
|
System.out.printf(Locale.ROOT, " %-14s: %.2f%n", "minSimplicity", o.minSimplicity);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "threads", o.threads);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "threads", o.threads);
|
||||||
}
|
}
|
||||||
@@ -229,9 +226,6 @@ public class Main {
|
|||||||
} else if (a.equals("--tries")) {
|
} else if (a.equals("--tries")) {
|
||||||
out.tries = Integer.parseInt(v);
|
out.tries = Integer.parseInt(v);
|
||||||
i++;
|
i++;
|
||||||
} else if (a.equals("--words")) {
|
|
||||||
out.wordsPath = v;
|
|
||||||
i++;
|
|
||||||
} else if (a.equals("--min-simplicity")) {
|
} else if (a.equals("--min-simplicity")) {
|
||||||
out.minSimplicity = Double.parseDouble(v);
|
out.minSimplicity = Double.parseDouble(v);
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -22,40 +22,35 @@ public final class Masker {
|
|||||||
private final long[] adjHi = new long[SwedishGenerator.SIZE];
|
private final long[] adjHi = new long[SwedishGenerator.SIZE];
|
||||||
private final int[] rCount = new int[8];
|
private final int[] rCount = new int[8];
|
||||||
private final int[] cCount = new int[9];
|
private final int[] cCount = new int[9];
|
||||||
private static final long[] NBR_LO = new long[SwedishGenerator.SIZE];
|
private static final long[] NBR_LO = Neighbors9x8.NBR_LO;
|
||||||
private static final long[] NBR_HI = new long[SwedishGenerator.SIZE];
|
private static final long[] NBR_HI = Neighbors9x8.NBR_HI;
|
||||||
static {
|
|
||||||
for (int i = 0; i < SwedishGenerator.SIZE; i++) {
|
|
||||||
NBR_LO[i] = IT[i].n1();
|
|
||||||
NBR_HI[i] = IT[i].n2();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Masker(Rng rng, int[] stack, Clues cache) {
|
public Masker(Rng rng, int[] stack, Clues cache) {
|
||||||
this.rng = rng;
|
this.rng = rng;
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValid(Clues c, int minLen) {
|
public boolean isValid(Clues c) {
|
||||||
return findOffendingClue(c, minLen) == -1;
|
return findOffendingClue(c) == -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int findOffendingClue(Clues grid, int minLen) {
|
public int findOffendingClue(Clues grid) {
|
||||||
if (((grid.xlo & grid.rlo) & grid.lo) != X) return numberOfTrailingZeros((grid.xlo & grid.rlo) & grid.lo);
|
if (((grid.xlo & grid.rlo) & grid.lo) != X) return numberOfTrailingZeros((grid.xlo & grid.rlo) & grid.lo);
|
||||||
if (((grid.xhi & grid.rhi) & grid.hi) != X) return 64 | numberOfTrailingZeros((grid.xhi & grid.rhi) & grid.hi);
|
if (((grid.xhi & grid.rhi) & grid.hi) != X) return 64 | numberOfTrailingZeros((grid.xhi & grid.rhi) & grid.hi);
|
||||||
|
|
||||||
int num = 0;
|
var num = 0;
|
||||||
for (long bits = grid.lo; bits != X; bits &= bits - 1) activeCIdx[num++] = numberOfTrailingZeros(bits);
|
for (var bits = grid.lo; bits != X; bits &= bits - 1) activeCIdx[num++] = numberOfTrailingZeros(bits);
|
||||||
for (long bits = grid.hi; bits != X; bits &= bits - 1) activeCIdx[num++] = 64 | numberOfTrailingZeros(bits);
|
for (var bits = grid.hi; bits != X; bits &= bits - 1) activeCIdx[num++] = 64 | numberOfTrailingZeros(bits);
|
||||||
|
|
||||||
if (num == 0) return -1;
|
if (num == 0) return -1;
|
||||||
|
|
||||||
int start = rng.randint0_SIZE() % num;
|
var start = rng.randint0_SIZE() % num;
|
||||||
int n = 0;
|
var n = 0;
|
||||||
for (int i = 0; i < num; i++) {
|
for (var i = 0; i < num; i++) {
|
||||||
int idx = activeCIdx[(start + i) % num];
|
var idx = activeCIdx[(start + i) % num];
|
||||||
int dir = grid.getDir(idx);
|
int dir = grid.getDir(idx);
|
||||||
int key = Slot.packSlotKey(idx, dir);
|
var 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)) {
|
||||||
@@ -69,8 +64,8 @@ public final class Masker {
|
|||||||
sLo = 0;
|
sLo = 0;
|
||||||
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
} else if (hLo != X) { sLo &= -(1L << (63 - numberOfLeadingZeros(hLo)) << 1); }
|
||||||
}
|
}
|
||||||
if (bitCount(sLo) + bitCount(sHi) < minLen) return idx;
|
if (bitCount(sLo) + bitCount(sHi) < MIN_LEN) return idx;
|
||||||
for (int j = 0; j < n; j++) if (bitCount(sLo & activeSLo[j]) + bitCount(sHi & activeSHi[j]) > 1) return idx;
|
for (var j = 0; j < n; j++) if (bitCount(sLo & activeSLo[j]) + bitCount(sHi & activeSHi[j]) > 1) return idx;
|
||||||
activeSLo[n] = sLo;
|
activeSLo[n] = sLo;
|
||||||
activeSHi[n] = sHi;
|
activeSHi[n] = sHi;
|
||||||
n++;
|
n++;
|
||||||
@@ -78,10 +73,10 @@ public final class Masker {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup(Clues c, int minLen) {
|
public void cleanup(Clues c) {
|
||||||
int guard = 0;
|
var guard = 0;
|
||||||
while (guard++ < 50) {
|
while (guard++ < 50) {
|
||||||
int offending = findOffendingClue(c, minLen);
|
var offending = findOffendingClue(c);
|
||||||
if (offending == -1) break;
|
if (offending == -1) break;
|
||||||
if ((offending & 64) == 0) c.clearClueLo(~(1L << offending));
|
if ((offending & 64) == 0) c.clearClueLo(~(1L << offending));
|
||||||
else c.clearClueHi(~(1L << (offending & 63)));
|
else c.clearClueHi(~(1L << (offending & 63)));
|
||||||
@@ -91,12 +86,12 @@ public final class Masker {
|
|||||||
public static final int[][] MUTATE_RI = new int[SwedishGenerator.SIZE][625];
|
public static final int[][] MUTATE_RI = new int[SwedishGenerator.SIZE][625];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (int i = 0; i < SwedishGenerator.SIZE; i++) {
|
for (var i = 0; i < SwedishGenerator.SIZE; i++) {
|
||||||
int k = 0;
|
var k = 0;
|
||||||
for (int dr1 = -2; dr1 <= 2; dr1++)
|
for (var dr1 = -2; dr1 <= 2; dr1++)
|
||||||
for (int dr2 = -2; dr2 <= 2; dr2++)
|
for (var dr2 = -2; dr2 <= 2; dr2++)
|
||||||
for (int dc1 = -2; dc1 <= 2; dc1++)
|
for (var dc1 = -2; dc1 <= 2; dc1++)
|
||||||
for (int dc2 = -2; dc2 <= 2; dc2++) {
|
for (var dc2 = -2; dc2 <= 2; dc2++) {
|
||||||
val ti = IT[i];
|
val ti = IT[i];
|
||||||
MUTATE_RI[i][k++] = offset(clamp(ti.r() + dr1 + dr2, 0, R - 1),
|
MUTATE_RI[i][k++] = offset(clamp(ti.r() + dr1 + dr2, 0, R - 1),
|
||||||
clamp(ti.c() + dc1 + dc2, 0, C - 1));
|
clamp(ti.c() + dc1 + dc2, 0, C - 1));
|
||||||
@@ -107,58 +102,58 @@ public final class Masker {
|
|||||||
// right/down => increasing indices; up/left => decreasing indices
|
// right/down => increasing indices; up/left => decreasing indices
|
||||||
// first clue is highest index among hits (hi first, then lo)
|
// first clue is highest index among hits (hi first, then lo)
|
||||||
private static void processSlotRev(Clues c, SlotVisitor visitor, int key) {
|
private static void processSlotRev(Clues c, SlotVisitor visitor, int key) {
|
||||||
long rayLo = PATH_LO[key];
|
var rayLo = PATH_LO[key];
|
||||||
long rayHi = PATH_HI[key];
|
var rayHi = PATH_HI[key];
|
||||||
// only consider clue cells
|
// only consider clue cells
|
||||||
long hitsLo = rayLo & c.lo;
|
var hitsLo = rayLo & c.lo;
|
||||||
long hitsHi = rayHi & c.hi;
|
var hitsHi = rayHi & c.hi;
|
||||||
|
|
||||||
if (hitsHi != X) {
|
if (hitsHi != X) {
|
||||||
int msb = 63 - numberOfLeadingZeros(hitsHi);
|
var msb = 63 - numberOfLeadingZeros(hitsHi);
|
||||||
long stop = 1L << msb;
|
var stop = 1L << msb;
|
||||||
rayHi &= -(stop << 1); // keep bits > stop
|
rayHi &= -(stop << 1); // keep bits > stop
|
||||||
rayLo = 0; // lo indices are below stop
|
rayLo = 0; // lo indices are below stop
|
||||||
} else if (hitsLo != X) {
|
} else if (hitsLo != X) {
|
||||||
int msb = 63 - numberOfLeadingZeros(hitsLo);
|
var msb = 63 - numberOfLeadingZeros(hitsLo);
|
||||||
long stop = 1L << msb;
|
var stop = 1L << msb;
|
||||||
rayLo &= -(stop << 1);
|
rayLo &= -(stop << 1);
|
||||||
}
|
}
|
||||||
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
||||||
visitor.visit(key, rayLo, rayHi);
|
visitor.visit(key, rayLo, rayHi);
|
||||||
}
|
}
|
||||||
private static boolean validSlotRev(long lo, long hi, int min, int key) {
|
private static boolean validSlotRev(long lo, long hi, int min, int key) {
|
||||||
long rayLo = PATH_LO[key];
|
var rayLo = PATH_LO[key];
|
||||||
long rayHi = PATH_HI[key];
|
var rayHi = PATH_HI[key];
|
||||||
// only consider clue cells
|
// only consider clue cells
|
||||||
long hitsLo = rayLo & lo;
|
var hitsLo = rayLo & lo;
|
||||||
long hitsHi = rayHi & hi;
|
var hitsHi = rayHi & hi;
|
||||||
|
|
||||||
if (hitsHi != X) return (Long.bitCount(rayHi & -(1L << 63 - numberOfLeadingZeros(hitsHi) << 1)) >= min);
|
if (hitsHi != X) return (Long.bitCount(rayHi & -(1L << 63 - numberOfLeadingZeros(hitsHi) << 1)) >= min);
|
||||||
else if (hitsLo != X) return (Long.bitCount(rayLo & -(1L << 63 - numberOfLeadingZeros(hitsLo) << 1)) + Long.bitCount(rayHi) >= min);
|
else if (hitsLo != X) return (Long.bitCount(rayLo & -(1L << 63 - numberOfLeadingZeros(hitsLo) << 1)) + Long.bitCount(rayHi) >= min);
|
||||||
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
|
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
|
||||||
}
|
}
|
||||||
private static boolean validSlot(long lo, long hi, int min, int key) {
|
private static boolean validSlot(long lo, long hi, int min, int key) {
|
||||||
long rayLo = PATH_LO[key];
|
var rayLo = PATH_LO[key];
|
||||||
long rayHi = PATH_HI[key];
|
var rayHi = PATH_HI[key];
|
||||||
long hitsLo = rayLo & lo;
|
var hitsLo = rayLo & lo;
|
||||||
long hitsHi = rayHi & hi;
|
var hitsHi = rayHi & hi;
|
||||||
|
|
||||||
if (hitsLo != X) return (Long.bitCount(rayLo & ((1L << numberOfTrailingZeros(hitsLo)) - 1)) >= min);
|
if (hitsLo != X) return (Long.bitCount(rayLo & ((1L << numberOfTrailingZeros(hitsLo)) - 1)) >= min);
|
||||||
else if (hitsHi != X) return (Long.bitCount(rayLo) + Long.bitCount(rayHi & ((1L << numberOfTrailingZeros(hitsHi)) - 1)) >= min);
|
else if (hitsHi != X) return (Long.bitCount(rayLo) + Long.bitCount(rayHi & ((1L << numberOfTrailingZeros(hitsHi)) - 1)) >= min);
|
||||||
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
|
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
|
||||||
}
|
}
|
||||||
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
|
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
|
||||||
long rayLo = PATH_LO[key];
|
var rayLo = PATH_LO[key];
|
||||||
long rayHi = PATH_HI[key];
|
var rayHi = PATH_HI[key];
|
||||||
long hitsLo = rayLo & c.lo;
|
var hitsLo = rayLo & c.lo;
|
||||||
long hitsHi = rayHi & c.hi;
|
var hitsHi = rayHi & c.hi;
|
||||||
|
|
||||||
if (hitsLo != X) {
|
if (hitsLo != X) {
|
||||||
long stop = 1L << numberOfTrailingZeros(hitsLo);
|
var stop = 1L << numberOfTrailingZeros(hitsLo);
|
||||||
rayLo &= (stop - 1);
|
rayLo &= (stop - 1);
|
||||||
rayHi = 0;
|
rayHi = 0;
|
||||||
} else if (hitsHi != X) {
|
} else if (hitsHi != X) {
|
||||||
long stop = 1L << numberOfTrailingZeros(hitsHi);
|
var stop = 1L << numberOfTrailingZeros(hitsHi);
|
||||||
rayHi &= (stop - 1);
|
rayHi &= (stop - 1);
|
||||||
}
|
}
|
||||||
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
||||||
@@ -175,12 +170,12 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
public static Slotinfo[] scoreSlots(Slot[] slots) {
|
public static Slotinfo[] scoreSlots(Slot[] slots) {
|
||||||
val count = new byte[SwedishGenerator.SIZE];
|
val count = new byte[SwedishGenerator.SIZE];
|
||||||
Slotinfo[] slotInfo = new Slotinfo[slots.length];
|
var slotInfo = new Slotinfo[slots.length];
|
||||||
for (var s : slots) {
|
for (var s : slots) {
|
||||||
for (long b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++;
|
for (var b = s.lo; b != X; b &= b - 1) count[numberOfTrailingZeros(b)]++;
|
||||||
for (long b = s.hi; b != X; b &= b - 1) count[64 | numberOfTrailingZeros(b)]++;
|
for (var b = s.hi; b != X; b &= b - 1) count[64 | numberOfTrailingZeros(b)]++;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < slots.length; i++) {
|
for (var 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));
|
Math.min(slot.entry.words().length, MAX_TRIES_PER_SLOT));
|
||||||
@@ -188,9 +183,9 @@ public final class Masker {
|
|||||||
return slotInfo;
|
return slotInfo;
|
||||||
}
|
}
|
||||||
public static int slotScore(byte[] count, long lo, long hi) {
|
public static int slotScore(byte[] count, long lo, long hi) {
|
||||||
int cross = 0;
|
var cross = 0;
|
||||||
for (long b = lo; b != X; b &= b - 1) cross += (count[numberOfTrailingZeros(b)] - 1);
|
for (var b = lo; b != X; b &= b - 1) cross += (count[numberOfTrailingZeros(b)] - 1);
|
||||||
for (long b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1);
|
for (var b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1);
|
||||||
return cross * 10 + Slot.length(lo, hi);
|
return cross * 10 + Slot.length(lo, hi);
|
||||||
}
|
}
|
||||||
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
||||||
@@ -201,19 +196,19 @@ public final class Masker {
|
|||||||
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
|
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
|
||||||
long cHLo2 = 0L, cHHi2 = 0L, cVLo2 = 0L, cVHi2 = 0L;
|
long cHLo2 = 0L, cHHi2 = 0L, cVLo2 = 0L, cVHi2 = 0L;
|
||||||
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);
|
var penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
|
||||||
boolean hasSlots = false;
|
var hasSlots = false;
|
||||||
/* if (!isValid(grid, 2)) {
|
/* if (!isValid(grid, 2)) {
|
||||||
throw new RuntimeException("Invalid grid configuration for mask fitness calculation");
|
throw new RuntimeException("Invalid grid configuration for mask fitness calculation");
|
||||||
//return 1_000_000_000L;
|
//return 1_000_000_000L;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
int numClues = 0;
|
var numClues = 0;
|
||||||
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
for (var bits = lo_cl; bits != X; bits &= bits - 1) {
|
||||||
long lsb = bits & -bits;
|
var lsb = bits & -bits;
|
||||||
int clueIdx = numberOfTrailingZeros(lsb);
|
var clueIdx = numberOfTrailingZeros(lsb);
|
||||||
int dir = grid.getDir(clueIdx);
|
int dir = grid.getDir(clueIdx);
|
||||||
int key = Slot.packSlotKey(clueIdx, dir);
|
var key = Slot.packSlotKey(clueIdx, dir);
|
||||||
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)) {
|
||||||
@@ -222,11 +217,11 @@ public final class Masker {
|
|||||||
rHi = 0;
|
rHi = 0;
|
||||||
} else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
|
} else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
|
||||||
} else if (hHi != X) {
|
} else if (hHi != X) {
|
||||||
int msb = 63 - numberOfLeadingZeros(hHi);
|
var msb = 63 - numberOfLeadingZeros(hHi);
|
||||||
rHi &= -(1L << msb << 1);
|
rHi &= -(1L << msb << 1);
|
||||||
rLo = 0;
|
rLo = 0;
|
||||||
} else if (hLo != X) {
|
} else if (hLo != X) {
|
||||||
int msb = 63 - numberOfLeadingZeros(hLo);
|
var msb = 63 - numberOfLeadingZeros(hLo);
|
||||||
rLo &= -(1L << msb << 1);
|
rLo &= -(1L << msb << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,15 +244,15 @@ public final class Masker {
|
|||||||
cVHi |= rHi;
|
cVHi |= rHi;
|
||||||
}
|
}
|
||||||
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
|
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
|
||||||
int wordLen = bitCount(rLo) + bitCount(rHi);
|
var wordLen = bitCount(rLo) + bitCount(rHi);
|
||||||
if (wordLen > 6) penalty += (wordLen - 6) * 1000L;
|
if (wordLen > 6) penalty += (wordLen - 6) * 1000L;
|
||||||
} else penalty += 25000;
|
} else penalty += 25000;
|
||||||
}
|
}
|
||||||
for (long bits = hi_cl; bits != X; bits &= bits - 1) {
|
for (var bits = hi_cl; bits != X; bits &= bits - 1) {
|
||||||
long lsb = bits & -bits;
|
var lsb = bits & -bits;
|
||||||
int clueIdx = numberOfTrailingZeros(lsb);
|
var clueIdx = numberOfTrailingZeros(lsb);
|
||||||
int dir = grid.getDir(64 | clueIdx);
|
int dir = grid.getDir(64 | clueIdx);
|
||||||
int key = Slot.packSlotKey(64 | clueIdx, dir);
|
var key = Slot.packSlotKey(64 | clueIdx, dir);
|
||||||
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)) {
|
||||||
@@ -266,11 +261,11 @@ public final class Masker {
|
|||||||
rHi = 0;
|
rHi = 0;
|
||||||
} else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
|
} else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1);
|
||||||
} else if (hHi != X) {
|
} else if (hHi != X) {
|
||||||
int msb = 63 - numberOfLeadingZeros(hHi);
|
var msb = 63 - numberOfLeadingZeros(hHi);
|
||||||
rHi &= -(1L << msb << 1);
|
rHi &= -(1L << msb << 1);
|
||||||
rLo = 0;
|
rLo = 0;
|
||||||
} else if (hLo != X) {
|
} else if (hLo != X) {
|
||||||
int msb = 63 - numberOfLeadingZeros(hLo);
|
var msb = 63 - numberOfLeadingZeros(hLo);
|
||||||
rLo &= -(1L << msb << 1);
|
rLo &= -(1L << msb << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +288,7 @@ public final class Masker {
|
|||||||
cVHi |= rHi;
|
cVHi |= rHi;
|
||||||
}
|
}
|
||||||
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
|
if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000;
|
||||||
int wordLen = bitCount(rLo) + bitCount(rHi);
|
var wordLen = bitCount(rLo) + bitCount(rHi);
|
||||||
if (wordLen > 6) penalty += (wordLen - 6) * 1000L;
|
if (wordLen > 6) penalty += (wordLen - 6) * 1000L;
|
||||||
} else penalty += 25000;
|
} else penalty += 25000;
|
||||||
}
|
}
|
||||||
@@ -302,22 +297,22 @@ public final class Masker {
|
|||||||
|
|
||||||
Arrays.fill(rCount, 0);
|
Arrays.fill(rCount, 0);
|
||||||
Arrays.fill(cCount, 0);
|
Arrays.fill(cCount, 0);
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (var i = 0; i < numClues; i++) {
|
||||||
int idx = activeCIdx[i];
|
var idx = activeCIdx[i];
|
||||||
rCount[idx & 7]++;
|
rCount[idx & 7]++;
|
||||||
cCount[idx >> 3]++;
|
cCount[idx >> 3]++;
|
||||||
}
|
}
|
||||||
for (int rc : rCount) if (rc < 2) penalty += (2 - rc) * 4000L;
|
for (var rc : rCount) if (rc < 2) penalty += (2 - rc) * 4000L;
|
||||||
for (int cc : cCount) if (cc < 2) penalty += (2 - cc) * 4000L;
|
for (var cc : cCount) if (cc < 2) penalty += (2 - cc) * 4000L;
|
||||||
|
|
||||||
// Connectiviteitscheck
|
// Connectiviteitscheck
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (var i = 0; i < numClues; i++) {
|
||||||
adjLo[i] = 0;
|
adjLo[i] = 0;
|
||||||
adjHi[i] = 0;
|
adjHi[i] = 0;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (var i = 0; i < numClues; i++) {
|
||||||
for (int j = i + 1; j < numClues; j++) {
|
for (var j = i + 1; j < numClues; j++) {
|
||||||
boolean connected = false;
|
var connected = false;
|
||||||
// 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;
|
||||||
@@ -333,31 +328,31 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (numClues > 0) {
|
if (numClues > 0) {
|
||||||
int maxReached = 0;
|
var maxReached = 0;
|
||||||
long totalReachedLo = 0, totalReachedHi = 0;
|
long totalReachedLo = 0, totalReachedHi = 0;
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (var i = 0; i < numClues; i++) {
|
||||||
if (i < 64) { if ((totalReachedLo & (1L << i)) != 0) continue; } else { if ((totalReachedHi & (1L << (i - 64))) != 0) continue; }
|
if (i < 64) { if ((totalReachedLo & (1L << i)) != 0) continue; } else { if ((totalReachedHi & (1L << (i - 64))) != 0) continue; }
|
||||||
|
|
||||||
long currentReachedLo = (i < 64) ? (1L << i) : 0;
|
var currentReachedLo = (i < 64) ? (1L << i) : 0;
|
||||||
long currentReachedHi = (i >= 64) ? (1L << (i - 64)) : 0;
|
var currentReachedHi = (i >= 64) ? (1L << (i - 64)) : 0;
|
||||||
stack[0] = i;
|
stack[0] = i;
|
||||||
int sp = 1;
|
var sp = 1;
|
||||||
int count = 0;
|
var count = 0;
|
||||||
while (sp > 0) {
|
while (sp > 0) {
|
||||||
int cur = stack[--sp];
|
var cur = stack[--sp];
|
||||||
count++;
|
count++;
|
||||||
long nLo = adjLo[cur] & ~currentReachedLo;
|
var nLo = adjLo[cur] & ~currentReachedLo;
|
||||||
long nHi = adjHi[cur] & ~currentReachedHi;
|
var nHi = adjHi[cur] & ~currentReachedHi;
|
||||||
while (nLo != 0) {
|
while (nLo != 0) {
|
||||||
long lsb = nLo & -nLo;
|
var lsb = nLo & -nLo;
|
||||||
int idx = numberOfTrailingZeros(lsb);
|
var 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;
|
var lsb = nHi & -nHi;
|
||||||
int idx = 64 | numberOfTrailingZeros(lsb);
|
var idx = 64 | numberOfTrailingZeros(lsb);
|
||||||
currentReachedHi |= lsb;
|
currentReachedHi |= lsb;
|
||||||
stack[sp++] = idx;
|
stack[sp++] = idx;
|
||||||
nHi &= ~lsb;
|
nHi &= ~lsb;
|
||||||
@@ -373,22 +368,22 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long bits = ~lo_cl & MASK_LO; bits != X; bits &= bits - 1) {
|
for (var bits = ~lo_cl & MASK_LO; bits != X; bits &= bits - 1) {
|
||||||
int clueIdx = numberOfTrailingZeros(bits);
|
var clueIdx = numberOfTrailingZeros(bits);
|
||||||
var rci = IT[clueIdx];
|
var rci = IT[clueIdx];
|
||||||
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;
|
var h = (cHLo & (1L << clueIdx)) != X;
|
||||||
boolean v = (cVLo & (1L << clueIdx)) != X;
|
var v = (cVLo & (1L << clueIdx)) != X;
|
||||||
if (!h && !v) penalty += 2000;
|
if (!h && !v) penalty += 2000;
|
||||||
else if (h && v) { /* ok */ } else if (((h ? cHLo2 : cVLo2) & (1L << clueIdx)) != X) penalty += 1000;
|
else if (h && v) { /* ok */ } else if (((h ? cHLo2 : cVLo2) & (1L << clueIdx)) != X) penalty += 1000;
|
||||||
else penalty += 1000;
|
else penalty += 1000;
|
||||||
}
|
}
|
||||||
for (long bits = ~hi_cl & MASK_HI; bits != X; bits &= bits - 1) {
|
for (var bits = ~hi_cl & MASK_HI; bits != X; bits &= bits - 1) {
|
||||||
int clueIdx = numberOfTrailingZeros(bits);
|
var clueIdx = numberOfTrailingZeros(bits);
|
||||||
var rci = IT[64 | clueIdx];
|
var rci = IT[64 | clueIdx];
|
||||||
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 = (cHHi & (1L << clueIdx)) != X;
|
var h = (cHHi & (1L << clueIdx)) != X;
|
||||||
boolean v = (cVHi & (1L << clueIdx)) != X;
|
var v = (cVHi & (1L << clueIdx)) != X;
|
||||||
if (!h && !v)
|
if (!h && !v)
|
||||||
penalty += 2000;
|
penalty += 2000;
|
||||||
else if (h && v) { /* ok */ } else if (((h ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 1000;
|
else if (h && v) { /* ok */ } else if (((h ? cHHi2 : cVHi2) & (1L << clueIdx)) != X) penalty += 1000;
|
||||||
@@ -402,20 +397,23 @@ public final class Masker {
|
|||||||
else compHi = lowestOneBit(remHi);
|
else compHi = lowestOneBit(remHi);
|
||||||
long lastLo, lastHi;
|
long lastLo, lastHi;
|
||||||
do {
|
do {
|
||||||
lastLo = compLo; lastHi = compHi;
|
lastLo = compLo;
|
||||||
|
lastHi = compHi;
|
||||||
long expandedLo = 0, expandedHi = 0;
|
long expandedLo = 0, expandedHi = 0;
|
||||||
for (long bits = compLo; bits != X; bits &= bits - 1) {
|
for (var bits = compLo; bits != X; bits &= bits - 1) {
|
||||||
int idx = numberOfTrailingZeros(bits);
|
var idx = numberOfTrailingZeros(bits);
|
||||||
expandedLo |= NBR_LO[idx]; expandedHi |= NBR_HI[idx];
|
expandedLo |= NBR_LO[idx];
|
||||||
|
expandedHi |= NBR_HI[idx];
|
||||||
}
|
}
|
||||||
for (long bits = compHi; bits != X; bits &= bits - 1) {
|
for (var bits = compHi; bits != X; bits &= bits - 1) {
|
||||||
int idx = 64 | numberOfTrailingZeros(bits);
|
var idx = 64 | numberOfTrailingZeros(bits);
|
||||||
expandedLo |= NBR_LO[idx]; expandedHi |= NBR_HI[idx];
|
expandedLo |= NBR_LO[idx];
|
||||||
|
expandedHi |= NBR_HI[idx];
|
||||||
}
|
}
|
||||||
compLo |= expandedLo & lo_cl;
|
compLo |= expandedLo & lo_cl;
|
||||||
compHi |= expandedHi & hi_cl;
|
compHi |= expandedHi & hi_cl;
|
||||||
} while (compLo != lastLo || compHi != lastHi);
|
} while (compLo != lastLo || compHi != lastHi);
|
||||||
int s = bitCount(compLo) + bitCount(compHi);
|
var s = bitCount(compLo) + bitCount(compHi);
|
||||||
if (s >= 2) penalty += (long) (s - 1) * 520;
|
if (s >= 2) penalty += (long) (s - 1) * 520;
|
||||||
remLo &= ~compLo;
|
remLo &= ~compLo;
|
||||||
remHi &= ~compHi;
|
remHi &= ~compHi;
|
||||||
@@ -432,19 +430,19 @@ public final class Masker {
|
|||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
if (g.isClueLo(ri)) continue;
|
if (g.isClueLo(ri)) continue;
|
||||||
var d_idx = rng.randomClueDir();
|
var d_idx = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d_idx);
|
var key = Slot.packSlotKey(ri, d_idx);
|
||||||
if (g.hasRoomForClue(key)) {
|
if (g.hasRoomForClue(key)) {
|
||||||
g.setClueLo(1L << ri, d_idx);
|
g.setClueLo(1L << ri, d_idx);
|
||||||
if (isValid(g, MIN_LEN)) placed++;
|
if (isValid(g)) 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.randomClueDir();
|
var d_idx = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d_idx);
|
var key = Slot.packSlotKey(ri, d_idx);
|
||||||
if (g.hasRoomForClue(key)) {
|
if (g.hasRoomForClue(key)) {
|
||||||
g.setClueHi(1L << (ri & 63), d_idx);
|
g.setClueHi(1L << (ri & 63), d_idx);
|
||||||
if (isValid(g, MIN_LEN)) placed++;
|
if (isValid(g)) placed++;
|
||||||
else g.clearClueHi(~(1L << (ri & 63)));
|
else g.clearClueHi(~(1L << (ri & 63)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,62 +456,59 @@ public final class Masker {
|
|||||||
for (int k = 0, ri; k < 6; 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();
|
var d = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d);
|
var key = Slot.packSlotKey(ri, d);
|
||||||
if (c.hasRoomForClue(key)) {
|
if (c.hasRoomForClue(key)) {
|
||||||
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)) c.clearClueLo(~(1L << ri));
|
||||||
else continue;
|
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)) c.clearClueHi(~(1L << (ri & 63)));
|
||||||
else continue;
|
else continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // HAS CLUE
|
} else { // HAS CLUE
|
||||||
var op = rng.randomClueDir();
|
var op = rng.randomClueDir();
|
||||||
if (op < 2) { // REMOVE
|
if (op < 2) { // REMOVE
|
||||||
byte oldD = c.getDir(ri);
|
var oldD = c.getDir(ri);
|
||||||
if (isLo(ri)) {
|
if (isLo(ri)) {
|
||||||
c.clearClueLo(~(1L << ri));
|
c.clearClueLo(~(1L << ri));
|
||||||
if (!isValid(c, MIN_LEN)) c.setClueLo(1L << ri, oldD);
|
if (!isValid(c)) c.setClueLo(1L << ri, oldD);
|
||||||
else continue;
|
else continue;
|
||||||
} else {
|
} else {
|
||||||
c.clearClueHi(~(1L << (ri & 63)));
|
c.clearClueHi(~(1L << (ri & 63)));
|
||||||
if (!isValid(c, MIN_LEN)) c.setClueHi(1L << (ri & 63), oldD);
|
if (!isValid(c)) c.setClueHi(1L << (ri & 63), oldD);
|
||||||
else continue;
|
else continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if (isLo(ri)) c.clearClueLo(~(1L << ri));
|
|
||||||
else c.clearClueHi(~(1L << (ri & 63)));*/
|
|
||||||
}
|
}
|
||||||
if (op < 4) { // CHANGE DIRECTION
|
if (op < 4) { // CHANGE DIRECTION
|
||||||
byte d = rng.randomClueDir();
|
var d = rng.randomClueDir();
|
||||||
int key = Slot.packSlotKey(ri, d);
|
var key = Slot.packSlotKey(ri, d);
|
||||||
if (c.hasRoomForClue(key)) {
|
if (c.hasRoomForClue(key)) {
|
||||||
byte oldD = c.getDir(ri);
|
var oldD = c.getDir(ri);
|
||||||
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)) c.setClueLo(1L << ri, oldD);
|
||||||
else continue;
|
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)) c.setClueHi(1L << (ri & 63), oldD);
|
||||||
else continue;
|
else continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // MOVE
|
} // MOVE
|
||||||
int nri = bytes[rng.randint0_624()];
|
var nri = bytes[rng.randint0_624()];
|
||||||
if (c.notClue(nri)) {
|
if (c.notClue(nri)) {
|
||||||
byte d = c.getDir(ri);
|
var d = c.getDir(ri);
|
||||||
int nkey = Slot.packSlotKey(nri, d);
|
var 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)) {
|
||||||
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);
|
||||||
@@ -535,7 +530,7 @@ public final class Masker {
|
|||||||
long maskLo = 0, maskHi = 0;
|
long maskLo = 0, maskHi = 0;
|
||||||
for (var rci : IT)
|
for (var rci : IT)
|
||||||
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
|
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
|
||||||
int i = rci.i();
|
var i = rci.i();
|
||||||
if ((i & 64) == 0) maskLo |= (1L << i);
|
if ((i & 64) == 0) maskLo |= (1L << i);
|
||||||
else maskHi |= (1L << (i - 64));
|
else maskHi |= (1L << (i - 64));
|
||||||
}
|
}
|
||||||
@@ -548,7 +543,7 @@ 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));
|
||||||
cleanup(c, MIN_LEN);
|
cleanup(c);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,36 +580,36 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize);
|
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize);
|
||||||
GridAndFit[] pop = new GridAndFit[popSize];
|
var pop = new GridAndFit[popSize];
|
||||||
for (int i = 0; i < popSize; i++) {
|
for (var i = 0; i < popSize; i++) {
|
||||||
if (Thread.currentThread().isInterrupted()) return null;
|
if (Thread.currentThread().isInterrupted()) return null;
|
||||||
pop[i] = new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180));
|
pop[i] = new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int gen = 0; gen < gens; gen++) {
|
for (var gen = 0; gen < gens; gen++) {
|
||||||
if (Thread.currentThread().isInterrupted()) break;
|
if (Thread.currentThread().isInterrupted()) break;
|
||||||
GridAndFit[] children = new GridAndFit[offspring];
|
var children = new GridAndFit[offspring];
|
||||||
int childCount = 0;
|
var childCount = 0;
|
||||||
for (int k = 0; k < offspring; k++) {
|
for (var k = 0; k < offspring; k++) {
|
||||||
if (Thread.currentThread().isInterrupted()) break;
|
if (Thread.currentThread().isInterrupted()) break;
|
||||||
GridAndFit p1 = rng.rand(pop);
|
var p1 = rng.rand(pop);
|
||||||
GridAndFit p2 = rng.rand(pop);
|
var p2 = rng.rand(pop);
|
||||||
Clues child = crossover(p1.grid, p2.grid);
|
var child = crossover(p1.grid, p2.grid);
|
||||||
children[k] = new GridAndFit(hillclimb(child, clueSize, 70));
|
children[k] = new GridAndFit(hillclimb(child, clueSize, 70));
|
||||||
childCount++;
|
childCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
GridAndFit[] combined = new GridAndFit[pop.length + childCount];
|
var combined = new GridAndFit[pop.length + childCount];
|
||||||
System.arraycopy(pop, 0, combined, 0, pop.length);
|
System.arraycopy(pop, 0, combined, 0, pop.length);
|
||||||
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[popSize];
|
var next = new GridAndFit[popSize];
|
||||||
int nextCount = 0;
|
var nextCount = 0;
|
||||||
for (GridAndFit cand : combined) {
|
for (var cand : combined) {
|
||||||
if (nextCount >= popSize) break;
|
if (nextCount >= popSize) break;
|
||||||
boolean unique = true;
|
var unique = true;
|
||||||
for (int i = 0; i < nextCount; i++) {
|
for (var i = 0; i < nextCount; i++) {
|
||||||
if (cand.grid.similarity(next[i].grid) > 0.92) {
|
if (cand.grid.similarity(next[i].grid) > 0.92) {
|
||||||
unique = false;
|
unique = false;
|
||||||
break;
|
break;
|
||||||
@@ -624,10 +619,10 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextCount < popSize) {
|
if (nextCount < popSize) {
|
||||||
for (GridAndFit cand : combined) {
|
for (var cand : combined) {
|
||||||
if (nextCount >= popSize) break;
|
if (nextCount >= popSize) break;
|
||||||
boolean alreadyIn = false;
|
var alreadyIn = false;
|
||||||
for (int i = 0; i < nextCount; i++) {
|
for (var i = 0; i < nextCount; i++) {
|
||||||
if (cand == next[i]) {
|
if (cand == next[i]) {
|
||||||
alreadyIn = true;
|
alreadyIn = true;
|
||||||
break;
|
break;
|
||||||
@@ -641,9 +636,9 @@ public final class Masker {
|
|||||||
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());
|
||||||
}
|
}
|
||||||
if (pop.length == 0) return null;
|
if (pop.length == 0) return null;
|
||||||
GridAndFit best = pop[0];
|
var best = pop[0];
|
||||||
for (int i = 1; i < pop.length; i++) {
|
for (var i = 1; i < pop.length; i++) {
|
||||||
GridAndFit x = pop[i];
|
var x = pop[i];
|
||||||
if (x.fit() < best.fit()) best = x;
|
if (x.fit() < best.fit()) best = x;
|
||||||
}
|
}
|
||||||
return best.grid;
|
return best.grid;
|
||||||
@@ -698,8 +693,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) & ~(xlo ^ b.xlo)));
|
var 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) & ~(xhi ^ b.xhi)));
|
var 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;
|
||||||
}
|
}
|
||||||
@@ -733,14 +728,14 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
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;
|
var v = (vlo & (1L << index)) != 0 ? 1 : 0;
|
||||||
int r = (rlo & (1L << index)) != 0 ? 1 : 0;
|
var r = (rlo & (1L << index)) != 0 ? 1 : 0;
|
||||||
int x = (xlo & (1L << index)) != 0 ? 1 : 0;
|
var x = (xlo & (1L << index)) != 0 ? 1 : 0;
|
||||||
return (byte) ((x << 2) | (r << 1) | v);
|
return (byte) ((x << 2) | (r << 1) | v);
|
||||||
} else {
|
} else {
|
||||||
int v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
var v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
||||||
int r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
var r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
||||||
int x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
var x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0;
|
||||||
return (byte) ((x << 2) | (r << 1) | v);
|
return (byte) ((x << 2) | (r << 1) | v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,19 +37,19 @@ public class Meta {
|
|||||||
|
|
||||||
// --- Lookup: w -> i using mmap ---
|
// --- Lookup: w -> i using mmap ---
|
||||||
static int findIndexInMapMmap(Path mapFile, long target) throws IOException {
|
static int findIndexInMapMmap(Path mapFile, long target) throws IOException {
|
||||||
try (FileChannel ch = FileChannel.open(mapFile, StandardOpenOption.READ)) {
|
try (var ch = FileChannel.open(mapFile, StandardOpenOption.READ)) {
|
||||||
MappedByteBuffer mbb = (MappedByteBuffer) ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size()).order(ORDER);
|
var mbb = (MappedByteBuffer) ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size()).order(ORDER);
|
||||||
|
|
||||||
int magic = mbb.getInt(0);
|
var magic = mbb.getInt(0);
|
||||||
int ver = mbb.getInt(4);
|
var ver = mbb.getInt(4);
|
||||||
int n = mbb.getInt(8);
|
var n = mbb.getInt(8);
|
||||||
if (magic != MAP_MAGIC || ver != VERSION) throw new IOException("Bad map file");
|
if (magic != MAP_MAGIC || ver != VERSION) throw new IOException("Bad map file");
|
||||||
|
|
||||||
int lo = 0, hi = n - 1;
|
int lo = 0, hi = n - 1;
|
||||||
while (lo <= hi) {
|
while (lo <= hi) {
|
||||||
int mid = (lo + hi) >>> 1;
|
var mid = (lo + hi) >>> 1;
|
||||||
int off = 12 + mid * 8;
|
var off = 12 + mid * 8;
|
||||||
long key = mbb.getLong(off);
|
var key = mbb.getLong(off);
|
||||||
|
|
||||||
if (key < target) lo = mid + 1;
|
if (key < target) lo = mid + 1;
|
||||||
else if (key > target) hi = mid - 1;
|
else if (key > target) hi = mid - 1;
|
||||||
@@ -61,41 +61,41 @@ public class Meta {
|
|||||||
|
|
||||||
// --- Read record i from shard.data (your format) ---
|
// --- Read record i from shard.data (your format) ---
|
||||||
static ShardLem readRecord(Path shardFile, long w, int i) throws IOException {
|
static ShardLem readRecord(Path shardFile, long w, int i) throws IOException {
|
||||||
try (FileChannel ch = FileChannel.open(shardFile, StandardOpenOption.READ)) {
|
try (var ch = FileChannel.open(shardFile, StandardOpenOption.READ)) {
|
||||||
ByteBuffer hdr = ByteBuffer.allocate(12).order(ORDER);
|
var hdr = ByteBuffer.allocate(12).order(ORDER);
|
||||||
ch.read(hdr);
|
ch.read(hdr);
|
||||||
hdr.flip();
|
hdr.flip();
|
||||||
int magic = hdr.getInt();
|
var magic = hdr.getInt();
|
||||||
int ver = hdr.getInt();
|
var ver = hdr.getInt();
|
||||||
int n = hdr.getInt();
|
var n = hdr.getInt();
|
||||||
if (magic != SHARD_MAGIC || ver != VERSION) throw new IOException("Bad shard file");
|
if (magic != SHARD_MAGIC || ver != VERSION) throw new IOException("Bad shard file");
|
||||||
if (i < 0 || i >= n) throw new IndexOutOfBoundsException();
|
if (i < 0 || i >= n) throw new IndexOutOfBoundsException();
|
||||||
|
|
||||||
long tableStart = 12L;
|
var tableStart = 12L;
|
||||||
long dataStart = 12L + (long) n * 4L;
|
var dataStart = 12L + (long) n * 4L;
|
||||||
|
|
||||||
int offI = readIntAt(ch, tableStart + (long) i * 4L);
|
var offI = readIntAt(ch, tableStart + (long) i * 4L);
|
||||||
int offIp = (i + 1 < n)
|
var offIp = (i + 1 < n)
|
||||||
? readIntAt(ch, tableStart + (long) (i + 1) * 4L)
|
? readIntAt(ch, tableStart + (long) (i + 1) * 4L)
|
||||||
: (int) (ch.size() - dataStart);
|
: (int) (ch.size() - dataStart);
|
||||||
|
|
||||||
int len = offIp - offI;
|
var len = offIp - offI;
|
||||||
ByteBuffer buf = ByteBuffer.allocate(len);
|
var buf = ByteBuffer.allocate(len);
|
||||||
ch.position(dataStart + offI);
|
ch.position(dataStart + offI);
|
||||||
ch.read(buf);
|
ch.read(buf);
|
||||||
buf.flip();
|
buf.flip();
|
||||||
|
|
||||||
String s = StandardCharsets.UTF_8.decode(buf).toString();
|
var s = StandardCharsets.UTF_8.decode(buf).toString();
|
||||||
String[] parts = s.split("\t", 3);
|
var parts = s.split("\t", 3);
|
||||||
|
|
||||||
int simpel = Integer.parseInt(parts[1]);
|
var simpel = Integer.parseInt(parts[1]);
|
||||||
String[] clues = GSON.fromJson(parts[2], String[].class);
|
var clues = GSON.fromJson(parts[2], String[].class);
|
||||||
return new ShardLem(w, simpel, clues);
|
return new ShardLem(w, simpel, clues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int readIntAt(FileChannel ch, long pos) throws IOException {
|
static int readIntAt(FileChannel ch, long pos) throws IOException {
|
||||||
ByteBuffer b = ByteBuffer.allocate(4).order(ORDER);
|
var b = ByteBuffer.allocate(4).order(ORDER);
|
||||||
ch.position(pos);
|
ch.position(pos);
|
||||||
ch.read(b);
|
ch.read(b);
|
||||||
b.flip();
|
b.flip();
|
||||||
@@ -105,10 +105,10 @@ public class Meta {
|
|||||||
// --- Demo main ---
|
// --- Demo main ---
|
||||||
public static ShardLem lookup(long w) {
|
public static ShardLem lookup(long w) {
|
||||||
try {
|
try {
|
||||||
int i = findIndexInMapMmap(shardMap, Lemma.pack43(w));
|
var i = findIndexInMapMmap(shardMap, Lemma.pack43(w));
|
||||||
System.out.println("\nQuery: w=" + w + " -> i=" + i);
|
System.out.println("\nQuery: w=" + w + " -> i=" + i);
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
ShardLem rec = readRecord(shardData, w, i);
|
var rec = readRecord(shardData, w, i);
|
||||||
System.out.println(" simpel=" + rec.simpel());
|
System.out.println(" simpel=" + rec.simpel());
|
||||||
System.out.println(" clues=" + Arrays.toString(rec.clues()));
|
System.out.println(" clues=" + Arrays.toString(rec.clues()));
|
||||||
return rec;
|
return rec;
|
||||||
|
|||||||
6
src/main/java/puzzle/TriggerConstants.java
Normal file
6
src/main/java/puzzle/TriggerConstants.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// file: app/Trigger.java
|
||||||
|
package puzzle;
|
||||||
|
|
||||||
|
import gen.GenerateConst;
|
||||||
|
@GenerateConst(C = 9, R = 8, packageName = "precomp", className = "Const9x8")
|
||||||
|
public final class TriggerConstants { }
|
||||||
5
src/main/java/puzzle/TriggerNeighbors4x3.java
Normal file
5
src/main/java/puzzle/TriggerNeighbors4x3.java
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package puzzle;
|
||||||
|
|
||||||
|
import gen.GenerateNeighbors;
|
||||||
|
@GenerateNeighbors(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2)
|
||||||
|
public final class TriggerNeighbors4x3 { }
|
||||||
@@ -3,4 +3,4 @@ package puzzle;
|
|||||||
|
|
||||||
import gen.GenerateNeighbors;
|
import gen.GenerateNeighbors;
|
||||||
@GenerateNeighbors(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2)
|
@GenerateNeighbors(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2)
|
||||||
public final class Trigger { }
|
public final class TriggerNeighbors9x8 { }
|
||||||
@@ -15,9 +15,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.LongConsumer;
|
|
||||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
import static puzzle.SwedishGenerator.*;
|
|
||||||
public class BuildClueAndSimpelIndex {
|
public class BuildClueAndSimpelIndex {
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import module java.base;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
import puzzle.Masker.Clues;
|
import puzzle.Masker.Clues;
|
||||||
import puzzle.SwedishGenerator.Rng;
|
import puzzle.SwedishGenerator.Rng;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static precomp.Const9x8.OFF_0_0;
|
||||||
|
import static precomp.Const9x8.OFF_1_1;
|
||||||
|
import static precomp.Const9x8.OFF_1_2;
|
||||||
|
import static precomp.Const9x8.OFF_2_0;
|
||||||
|
import static precomp.Const9x8.OFF_2_1;
|
||||||
|
import static precomp.Const9x8.OFF_2_2;
|
||||||
|
import static precomp.Const9x8.OFF_3_1;
|
||||||
|
import static precomp.Const9x8.OFF_7_7;
|
||||||
import static puzzle.SwedishGenerator.STACK_SIZE;
|
import static puzzle.SwedishGenerator.STACK_SIZE;
|
||||||
|
|
||||||
public class ConnectivityTest {
|
public class ConnectivityTest {
|
||||||
@@ -19,19 +26,19 @@ public class ConnectivityTest {
|
|||||||
// Clue 1: (0,0) Right. Slot: (0,1), (0,2), (0,3)
|
// Clue 1: (0,0) Right. Slot: (0,1), (0,2), (0,3)
|
||||||
// Clue 2: (1,2) Up. Slot: (0,2)
|
// Clue 2: (1,2) Up. Slot: (0,2)
|
||||||
// Ze zijn NIET 8-naburig, maar wel verbonden via slot op (0,2)
|
// Ze zijn NIET 8-naburig, maar wel verbonden via slot op (0,2)
|
||||||
singleComp.setClueLo(1L << Masker.offset(0, 0), (byte)1);
|
singleComp.setClueLo(1L << OFF_0_0, (byte) 1);
|
||||||
singleComp.setClueLo(1L << Masker.offset(2, 2), (byte)2); // Up van (2,2) naar (1,2), (0,2)
|
singleComp.setClueLo(1L << OFF_2_2, (byte) 2); // Up van (2,2) naar (1,2), (0,2)
|
||||||
|
|
||||||
long fitnessSingle = masker.maskFitness(singleComp, 2);
|
long fitnessSingle = masker.maskFitness(singleComp, 2);
|
||||||
|
|
||||||
// 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();
|
||||||
twoIslands.setClueLo(1L << Masker.offset(0, 0), (byte)1);
|
twoIslands.setClueLo(1L << OFF_0_0, (byte) 1);
|
||||||
twoIslands.setClueLo(Masker.offset(7, 7) < 64 ? 1L << Masker.offset(7, 7) : 0, (byte)1);
|
twoIslands.setClueLo(OFF_7_7 < 64 ? 1L << OFF_7_7 : 0, (byte) 1);
|
||||||
// Voor de zekerheid checken we of offset(7,7) in lo of hi zit
|
// Voor de zekerheid checken we of offset(7,7) in lo of hi zit
|
||||||
int off77 = Masker.offset(7, 7);
|
int off77 = OFF_7_7;
|
||||||
if (off77 < 64) twoIslands.setClueLo(1L << off77, (byte)1);
|
if (off77 < 64) twoIslands.setClueLo(1L << off77, (byte) 1);
|
||||||
else twoIslands.setClueHi(1L << (off77 - 64), (byte)1);
|
else twoIslands.setClueHi(1L << (off77 - 64), (byte) 1);
|
||||||
|
|
||||||
long fitnessIslands = masker.maskFitness(twoIslands, 2);
|
long fitnessIslands = masker.maskFitness(twoIslands, 2);
|
||||||
|
|
||||||
@@ -50,8 +57,8 @@ public class ConnectivityTest {
|
|||||||
// Twee clues naast elkaar, maar slots kruisen niet.
|
// Twee clues naast elkaar, maar slots kruisen niet.
|
||||||
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
||||||
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
||||||
clues.setClueLo(1L << Masker.offset(1, 1), (byte)1);
|
clues.setClueLo(1L << OFF_1_1, (byte) 1);
|
||||||
clues.setClueLo(1L << Masker.offset(2, 1), (byte)1);
|
clues.setClueLo(1L << OFF_2_1, (byte) 1);
|
||||||
|
|
||||||
long fitness = masker.maskFitness(clues, 2);
|
long fitness = masker.maskFitness(clues, 2);
|
||||||
// Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
|
// Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
|
||||||
@@ -61,15 +68,13 @@ public class ConnectivityTest {
|
|||||||
// Twee clues naast elkaar, maar slots kruisen niet.
|
// Twee clues naast elkaar, maar slots kruisen niet.
|
||||||
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
||||||
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
||||||
clues2.setClueLo(1L << Masker.offset(1, 1), (byte)1);
|
clues2.setClueLo(1L << OFF_1_1, (byte) 1);
|
||||||
clues2.setClueLo(1L << Masker.offset(3, 1), (byte)1);
|
clues2.setClueLo(1L << OFF_3_1, (byte) 1);
|
||||||
long fitness2 = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty()).maskFitness(clues2, 2);
|
long fitness2 = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty()).maskFitness(clues2, 2);
|
||||||
// Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
|
// Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
|
||||||
System.out.println("[DEBUG_LOG] Fitness physically adjacent: " + fitness2);
|
System.out.println("[DEBUG_LOG] Fitness physically adjacent: " + fitness2);
|
||||||
assertTrue(fitness2 > 20000, "Should have island penalty even if physically adjacent");
|
assertTrue(fitness2 > 20000, "Should have island penalty even if physically adjacent");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -81,8 +86,8 @@ public class ConnectivityTest {
|
|||||||
// Clue A: (2,0) Right. Slot: (2,1), (2,2), (2,3), ...
|
// Clue A: (2,0) Right. Slot: (2,1), (2,2), (2,3), ...
|
||||||
// Clue B: (1,2) Corner Down. Word starts at (1,3) en gaat omlaag: (1,3), (2,3), (3,3)...
|
// 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).
|
// Ze kruisen op (2,3).
|
||||||
clues.setClueLo(1L << Masker.offset(2, 0), (byte)1); // Right
|
clues.setClueLo(1L << OFF_2_0, (byte) 1); // Right
|
||||||
clues.setClueLo(1L << Masker.offset(1, 2), (byte)4); // Corner Down
|
clues.setClueLo(1L << OFF_1_2, (byte) 4); // Corner Down
|
||||||
|
|
||||||
long fitness = masker.maskFitness(clues, 2);
|
long fitness = masker.maskFitness(clues, 2);
|
||||||
System.out.println("[DEBUG_LOG] Fitness corner clue connected: " + fitness);
|
System.out.println("[DEBUG_LOG] Fitness corner clue connected: " + fitness);
|
||||||
@@ -90,13 +95,13 @@ public class ConnectivityTest {
|
|||||||
// Als ze verbonden zijn, is de penalty voor eilandjes 0.
|
// Als ze verbonden zijn, is de penalty voor eilandjes 0.
|
||||||
// We vergelijken met een island scenario (2 clues die elkaar NIET raken)
|
// We vergelijken met een island scenario (2 clues die elkaar NIET raken)
|
||||||
Clues island = Clues.createEmpty();
|
Clues island = Clues.createEmpty();
|
||||||
int offA = Masker.offset(2, 0);
|
int offA = OFF_2_0;
|
||||||
if (offA < 64) island.setClueLo(1L << offA, (byte)1);
|
if (offA < 64) island.setClueLo(1L << offA, (byte) 1);
|
||||||
else island.setClueHi(1L << (offA - 64), (byte)1);
|
else island.setClueHi(1L << (offA - 64), (byte) 1);
|
||||||
|
|
||||||
int offB = Masker.offset(7, 7);
|
int offB = OFF_7_7;
|
||||||
if (offB < 64) island.setClueLo(1L << offB, (byte)1);
|
if (offB < 64) island.setClueLo(1L << offB, (byte) 1);
|
||||||
else island.setClueHi(1L << (offB - 64), (byte)1);
|
else island.setClueHi(1L << (offB - 64), (byte) 1);
|
||||||
|
|
||||||
long fitnessIsland = masker.maskFitness(island, 2);
|
long fitnessIsland = masker.maskFitness(island, 2);
|
||||||
System.out.println("[DEBUG_LOG] Fitness island: " + fitnessIsland);
|
System.out.println("[DEBUG_LOG] Fitness island: " + fitnessIsland);
|
||||||
|
|||||||
@@ -1,33 +1,34 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import module java.base;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
import puzzle.Masker.Clues;
|
import puzzle.Masker.Clues;
|
||||||
import puzzle.SwedishGenerator.DictEntry;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import puzzle.SwedishGenerator.Slotinfo;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static precomp.Const9x8.OFF_0_0;
|
||||||
|
import static precomp.Const9x8.OFF_0_1;
|
||||||
|
import static precomp.Const9x8.OFF_1_0;
|
||||||
|
import static precomp.Const9x8.OFF_1_1;
|
||||||
|
|
||||||
public class CornerClueTest {
|
public class CornerClueTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCornerDownSlot() {
|
void testCornerDownSlot() {
|
||||||
Clues clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
// Clue op (0,0), type 4 (Corner Down)
|
// Clue op (0,0), type 4 (Corner Down)
|
||||||
int idx = Masker.offset(0, 0);
|
var idx = OFF_0_0;
|
||||||
clues.setClueLo(1L << idx, (byte)4);
|
clues.setClueLo(1L << idx, (byte) 4);
|
||||||
|
|
||||||
assertEquals(4, clues.getDir(idx));
|
assertEquals(4, clues.getDir(idx));
|
||||||
|
|
||||||
// Controleer of forEachSlot het slot vindt
|
// Controleer of forEachSlot het slot vindt
|
||||||
final boolean[] found = {false};
|
final var found = new boolean[]{ false };
|
||||||
clues.forEachSlot((key, lo, hi) -> {
|
clues.forEachSlot((key, lo, hi) -> {
|
||||||
if (Masker.Slot.dir(key) == 4) {
|
if (Masker.Slot.dir(key) == 4) {
|
||||||
found[0] = true;
|
found[0] = true;
|
||||||
// Woord zou moeten starten op (0,1)
|
// Woord zou moeten starten op (0,1)
|
||||||
int startIdx = Masker.offset(0, 1);
|
assertTrue((lo & (1L << OFF_0_1)) != 0, "Slot should start at (0,1)");
|
||||||
assertTrue((lo & (1L << startIdx)) != 0, "Slot should start at (0,1)");
|
|
||||||
// En omlaag gaan
|
// En omlaag gaan
|
||||||
int secondIdx = Masker.offset(1, 1);
|
assertTrue((lo & (1L << OFF_1_1)) != 0, "Slot should continue to (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)
|
// Lengte van het slot zou 8 moeten zijn (van rij 0 t/m 7 in kolom 1)
|
||||||
assertEquals(8, Masker.Slot.length(lo, hi));
|
assertEquals(8, Masker.Slot.length(lo, hi));
|
||||||
@@ -38,12 +39,11 @@ public class CornerClueTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCornerDownExtraction() {
|
void testCornerDownExtraction() {
|
||||||
Clues clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
int idx = Masker.offset(0, 0);
|
clues.setClueLo(1L << OFF_0_0, (byte) 4);
|
||||||
clues.setClueLo(1L << idx, (byte)4);
|
|
||||||
|
|
||||||
DictEntry[] dict = DictData.DICT.index();
|
var dict = DictData.DICT.index();
|
||||||
Slotinfo[] slots = Masker.slots(clues, dict);
|
var slots = Masker.slots(clues, dict);
|
||||||
|
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
assertEquals(4, Masker.Slot.dir(slots[0].key()));
|
assertEquals(4, Masker.Slot.dir(slots[0].key()));
|
||||||
@@ -51,25 +51,23 @@ public class CornerClueTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCornerDownLeftSlot() {
|
void testCornerDownLeftSlot() {
|
||||||
Clues clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
// Clue op (0,1), type 5 (Corner Down Left)
|
// Clue op (0,1), type 5 (Corner Down Left)
|
||||||
// Should result in word starting at (0,0) going down.
|
// Should result in word starting at (0,0) going down.
|
||||||
int idx = Masker.offset(0, 1);
|
var idx = OFF_0_1;
|
||||||
clues.setClueLo(1L << idx, (byte)5);
|
clues.setClueLo(1L << idx, (byte) 5);
|
||||||
|
|
||||||
assertEquals(5, clues.getDir(idx));
|
assertEquals(5, clues.getDir(idx));
|
||||||
|
|
||||||
// Controleer of forEachSlot het slot vindt
|
// Controleer of forEachSlot het slot vindt
|
||||||
final boolean[] found = {false};
|
final var found = new boolean[]{ false };
|
||||||
clues.forEachSlot((key, lo, hi) -> {
|
clues.forEachSlot((key, lo, hi) -> {
|
||||||
if (Masker.Slot.dir(key) == 5) {
|
if (Masker.Slot.dir(key) == 5) {
|
||||||
found[0] = true;
|
found[0] = true;
|
||||||
// Woord zou moeten starten op (0,0)
|
// Woord zou moeten starten op (0,0)
|
||||||
int startIdx = Masker.offset(0, 0);
|
assertTrue((lo & (1L << OFF_0_0)) != 0, "Slot should start at (0,0)");
|
||||||
assertTrue((lo & (1L << startIdx)) != 0, "Slot should start at (0,0)");
|
|
||||||
// En omlaag gaan
|
// En omlaag gaan
|
||||||
int secondIdx = Masker.offset(1, 0);
|
assertTrue((lo & (1L << OFF_1_0)) != 0, "Slot should continue to (1,0)");
|
||||||
assertTrue((lo & (1L << secondIdx)) != 0, "Slot should continue to (1,0)");
|
|
||||||
|
|
||||||
// Lengte van het slot zou 8 moeten zijn (van rij 0 t/m 7 in kolom 0)
|
// Lengte van het slot zou 8 moeten zijn (van rij 0 t/m 7 in kolom 0)
|
||||||
assertEquals(8, Masker.Slot.length(lo, hi));
|
assertEquals(8, Masker.Slot.length(lo, hi));
|
||||||
@@ -80,12 +78,11 @@ public class CornerClueTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCornerDownLeftExtraction() {
|
void testCornerDownLeftExtraction() {
|
||||||
Clues clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
int idx = Masker.offset(0, 1);
|
clues.setClueLo(1L << OFF_0_1, (byte) 5);
|
||||||
clues.setClueLo(1L << idx, (byte)5);
|
|
||||||
|
|
||||||
DictEntry[] dict = DictData.DICT.index();
|
var dict = DictData.DICT.index();
|
||||||
Slotinfo[] slots = Masker.slots(clues, dict);
|
var slots = Masker.slots(clues, dict);
|
||||||
|
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
assertEquals(5, Masker.Slot.dir(slots[0].key()));
|
assertEquals(5, Masker.Slot.dir(slots[0].key()));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package puzzle;
|
|||||||
|
|
||||||
import module java.base;
|
import module java.base;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import puzzle.Export.Clued;
|
import puzzle.Export.Clued;
|
||||||
import puzzle.Export.Gridded;
|
import puzzle.Export.Gridded;
|
||||||
@@ -16,6 +17,10 @@ import puzzle.SwedishGeneratorTest.Idx;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static precomp.Const9x8.OFF_0_1;
|
||||||
|
import static precomp.Const9x8.OFF_0_2;
|
||||||
|
import static precomp.Const9x8.OFF_0_3;
|
||||||
|
import static precomp.Const9x8.OFF_0_4;
|
||||||
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.SwedishGenerator.C;
|
import static puzzle.SwedishGenerator.C;
|
||||||
@@ -24,10 +29,6 @@ import static puzzle.SwedishGenerator.FillStats;
|
|||||||
import static puzzle.SwedishGenerator.R;
|
import static puzzle.SwedishGenerator.R;
|
||||||
import static puzzle.Masker.Slot;
|
import static puzzle.Masker.Slot;
|
||||||
import static puzzle.GridBuilder.placeWord;
|
import static puzzle.GridBuilder.placeWord;
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_1;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_2;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_3;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_4;
|
|
||||||
import static puzzle.SwedishGeneratorTest.TEST;
|
import static puzzle.SwedishGeneratorTest.TEST;
|
||||||
|
|
||||||
public class ExportFormatTest {
|
public class ExportFormatTest {
|
||||||
@@ -104,17 +105,14 @@ public class ExportFormatTest {
|
|||||||
assertTrue(row.matches("#+"));
|
assertTrue(row.matches("#+"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Path shardKey(long word) {
|
|
||||||
return Path.of("src/main/generated-sources/puzzle").resolve(Lemma.unpackSize(word) + 1 + ".idx");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testShardToClue() {
|
void testShardToClue() {
|
||||||
for (int length = 2; length <= 8; length++) {
|
for (var length = 2; length <= 8; length++) {
|
||||||
val entry = DictData.DICT.index()[length];
|
val entry = DictData.DICT.index()[length];
|
||||||
if (entry == null) continue;
|
if (entry == null) continue;
|
||||||
val words = entry.words();
|
val words = entry.words();
|
||||||
for (int i = 0; i < Math.min(words.length, 5); i++) {
|
for (var i = 0; i < Math.min(words.length, 5); i++) {
|
||||||
val wordVal = words[i];
|
val wordVal = words[i];
|
||||||
val word = Lemma.asWord(wordVal, Export.BYTES.get());
|
val word = Lemma.asWord(wordVal, Export.BYTES.get());
|
||||||
val clueRec = Meta.lookup(wordVal);
|
val clueRec = Meta.lookup(wordVal);
|
||||||
@@ -130,24 +128,10 @@ public class ExportFormatTest {
|
|||||||
@Test
|
@Test
|
||||||
void testSpecificWords() {
|
void testSpecificWords() {
|
||||||
// These words are known to be in the CSV and likely in the dictionary
|
// These words are known to be in the CSV and likely in the dictionary
|
||||||
String[] testWords = { "EEN", "NAAR", "IEDEREEN" };
|
var testWords = new String[]{ "EEN", "NAAR", "IEDEREEN" };
|
||||||
for (String wStr : testWords) {
|
for (var wStr : testWords) {
|
||||||
long w = Lemma.from(wStr);
|
var w = Lemma.from(wStr);
|
||||||
int L = wStr.length();
|
|
||||||
var entry = DictData.DICT.index()[L];
|
|
||||||
if (entry == null) continue;
|
|
||||||
|
|
||||||
// Find index of word in entry
|
|
||||||
int idx = -1;
|
|
||||||
long[] words = entry.words();
|
|
||||||
for (int i = 0; i < words.length; i++) {
|
|
||||||
if (Lemma.asWord(words[i], Export.BYTES.get()).equals(wStr)) {
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx != -1) {
|
|
||||||
val clueRec = Meta.lookup(w);
|
val clueRec = Meta.lookup(w);
|
||||||
assertNotNull(clueRec);
|
assertNotNull(clueRec);
|
||||||
assertEquals(wStr, Lemma.asWord(clueRec.w(), Export.BYTES.get()));
|
assertEquals(wStr, Lemma.asWord(clueRec.w(), Export.BYTES.get()));
|
||||||
@@ -168,5 +152,4 @@ public class ExportFormatTest {
|
|||||||
assertTrue(clueRec.clues().length > 0);
|
assertTrue(clueRec.clues().length > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ import puzzle.SwedishGenerator.Rng;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static precomp.Const9x8.OFF_0_0;
|
||||||
|
import static precomp.Const9x8.OFF_0_1;
|
||||||
|
import static precomp.Const9x8.OFF_0_2;
|
||||||
|
import static precomp.Const9x8.OFF_1_1;
|
||||||
|
import static precomp.Const9x8.OFF_1_2;
|
||||||
|
import static precomp.Const9x8.OFF_2_1;
|
||||||
|
import static precomp.Const9x8.OFF_2_3;
|
||||||
import static puzzle.Export.Clue.DOWN;
|
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.SwedishGenerator.Dict;
|
|
||||||
import static puzzle.SwedishGenerator.Lemma;
|
import static puzzle.SwedishGenerator.Lemma;
|
||||||
import static puzzle.SwedishGenerator.Slotinfo;
|
import static puzzle.SwedishGenerator.Slotinfo;
|
||||||
import static puzzle.SwedishGenerator.fillMask;
|
import static puzzle.SwedishGenerator.fillMask;
|
||||||
@@ -36,13 +42,6 @@ import static puzzle.SwedishGeneratorTest.Idx.IDX_1_1;
|
|||||||
import static puzzle.SwedishGeneratorTest.Idx.IDX_2_1;
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_2_1;
|
||||||
import static puzzle.SwedishGeneratorTest.LETTER_A;
|
import static puzzle.SwedishGeneratorTest.LETTER_A;
|
||||||
import static puzzle.SwedishGeneratorTest.LETTER_Z;
|
import static puzzle.SwedishGeneratorTest.LETTER_Z;
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_0;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_1;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_2;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_1_1;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_1_2;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_2_1;
|
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_2_3;
|
|
||||||
|
|
||||||
public class MainTest {
|
public class MainTest {
|
||||||
|
|
||||||
@@ -57,7 +56,6 @@ public class MainTest {
|
|||||||
this.tries = 1;
|
this.tries = 1;
|
||||||
this.verbose = false;
|
this.verbose = false;
|
||||||
}};
|
}};
|
||||||
static final Dict dict = DictData.DICT;//loadDict(opts.wordsPath);
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractSlots() {
|
void testExtractSlots() {
|
||||||
|
|
||||||
@@ -68,7 +66,7 @@ public class MainTest {
|
|||||||
val g = grid.grid().g;
|
val g = grid.grid().g;
|
||||||
GridBuilder.placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
|
GridBuilder.placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
|
||||||
|
|
||||||
var slots = Masker.extractSlots(clues, dict.index());
|
var slots = Masker.extractSlots(clues, DictData.DICT.index());
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
var s = slots[0];
|
var s = slots[0];
|
||||||
assertEquals(8, Masker.Slot.length(s.lo(), s.hi()));
|
assertEquals(8, Masker.Slot.length(s.lo(), s.hi()));
|
||||||
@@ -184,7 +182,7 @@ public class MainTest {
|
|||||||
Assertions.assertEquals(20, mask.clueCount());
|
Assertions.assertEquals(20, mask.clueCount());
|
||||||
val map = mask.stream().collect(Collectors.toMap(ClueAt::index, ClueAt::clue));
|
val map = mask.stream().collect(Collectors.toMap(ClueAt::index, ClueAt::clue));
|
||||||
Assertions.assertEquals(20, map.size());
|
Assertions.assertEquals(20, map.size());
|
||||||
var slots = Masker.slots(mask.c(), dict.index());
|
var slots = Masker.slots(mask.c(), DictData.DICT.index());
|
||||||
// var filled = fillMask(rng, slotInfo, grid, false);
|
// var filled = fillMask(rng, slotInfo, grid, false);
|
||||||
// val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0));
|
// val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0));
|
||||||
}
|
}
|
||||||
@@ -200,7 +198,7 @@ public class MainTest {
|
|||||||
" 1 \n" +
|
" 1 \n" +
|
||||||
" 1 2\n" +
|
" 1 2\n" +
|
||||||
"21 22 3");
|
"21 22 3");
|
||||||
var slotInfo = Masker.slots(mask.c(), dict.index());
|
var slotInfo = Masker.slots(mask.c(), DictData.DICT.index());
|
||||||
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)");
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import module java.base;
|
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import puzzle.Export.Clued;
|
import puzzle.Export.Clued;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static precomp.Const9x8.OFF_0_0;
|
||||||
|
import static precomp.Const9x8.OFF_0_2;
|
||||||
|
import static precomp.Const9x8.OFF_0_3;
|
||||||
|
import static precomp.Const9x8.OFF_0_7;
|
||||||
|
import static precomp.Const9x8.OFF_0_8;
|
||||||
|
import static precomp.Const9x8.OFF_1_1;
|
||||||
|
import static precomp.Const9x8.OFF_2_2;
|
||||||
import static puzzle.Masker.Clues;
|
import static puzzle.Masker.Clues;
|
||||||
import static puzzle.SwedishGenerator.*;
|
|
||||||
import static puzzle.Masker.Slot;
|
import static puzzle.Masker.Slot;
|
||||||
|
import static puzzle.SwedishGenerator.Rng;
|
||||||
|
import static puzzle.SwedishGenerator.STACK_SIZE;
|
||||||
|
|
||||||
public class MaskerCluesTest {
|
public class MaskerCluesTest {
|
||||||
|
|
||||||
@@ -18,7 +27,7 @@ public class MaskerCluesTest {
|
|||||||
for (int i = 0; i < 200; i++) {
|
for (int i = 0; i < 200; i++) {
|
||||||
for (int j = 19; j < 24; j++) {
|
for (int j = 19; j < 24; j++) {
|
||||||
var clues = masker.randomMask(j);
|
var clues = masker.randomMask(j);
|
||||||
assertTrue(masker.isValid(clues, MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
|
assertTrue(masker.isValid(clues), "Mask should be valid for length \n" + new Clued(clues).gridToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +45,7 @@ public class MaskerCluesTest {
|
|||||||
simCount++;
|
simCount++;
|
||||||
masker.mutate(clues);
|
masker.mutate(clues);
|
||||||
sim += orig.similarity(clues);
|
sim += orig.similarity(clues);
|
||||||
assertTrue(masker.isValid(clues, MIN_LEN), "Mask should be valid for length \n" + new Clued(clues).gridToString());
|
assertTrue(masker.isValid(clues), "Mask should be valid for length \n" + new Clued(clues).gridToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Average similarity: " + sim / simCount);
|
System.out.println("Average similarity: " + sim / simCount);
|
||||||
@@ -55,7 +64,7 @@ public class MaskerCluesTest {
|
|||||||
simCount++;
|
simCount++;
|
||||||
var cross = masker.crossover(clues, clues2);
|
var cross = masker.crossover(clues, clues2);
|
||||||
sim += Math.max(cross.similarity(clues), cross.similarity(clues2));
|
sim += Math.max(cross.similarity(clues), cross.similarity(clues2));
|
||||||
assertTrue(masker.isValid(cross, MIN_LEN), "Mask should be valid for length \n" + new Clued(cross).gridToString());
|
assertTrue(masker.isValid(cross), "Mask should be valid for length \n" + new Clued(cross).gridToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Average similarity: " + sim / simCount);
|
System.out.println("Average similarity: " + sim / simCount);
|
||||||
@@ -90,16 +99,16 @@ public class MaskerCluesTest {
|
|||||||
void testIsValid() {
|
void testIsValid() {
|
||||||
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
|
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
|
||||||
Clues g = Clues.createEmpty();
|
Clues g = Clues.createEmpty();
|
||||||
assertTrue(masker.isValid(g, MIN_LEN));
|
assertTrue(masker.isValid(g));
|
||||||
|
|
||||||
// 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 << OFF_0_0, (byte) 1);
|
||||||
assertTrue(masker.isValid(g, MIN_LEN));
|
assertTrue(masker.isValid(g));
|
||||||
|
|
||||||
// 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 << OFF_0_7, (byte) 1);
|
||||||
assertFalse(masker.isValid(g2, MIN_LEN));
|
assertFalse(masker.isValid(g2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -107,22 +116,22 @@ public class MaskerCluesTest {
|
|||||||
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(OFF_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(OFF_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 << OFF_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(OFF_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 << OFF_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(OFF_0_0, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -130,17 +139,17 @@ public class MaskerCluesTest {
|
|||||||
Clues g = Clues.createEmpty();
|
Clues g = Clues.createEmpty();
|
||||||
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
|
Masker masker = new Masker(new Rng(42), new int[STACK_SIZE], 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 << OFF_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 << OFF_2_2, (byte) 2);
|
||||||
assertTrue(masker.isValid(g, MIN_LEN));
|
assertTrue(masker.isValid(g));
|
||||||
|
|
||||||
// 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 << OFF_1_1, (byte) 1);
|
||||||
assertTrue(masker.isValid(g, MIN_LEN));
|
assertTrue(masker.isValid(g));
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -149,9 +158,9 @@ public class MaskerCluesTest {
|
|||||||
// 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 << OFF_0_0, (byte) 4); // Corner Down
|
||||||
g3.setClueLo(1L << Masker.offset(0, 2), (byte) 5); // Corner Down Left
|
g3.setClueLo(1L << OFF_0_2, (byte) 5); // Corner Down Left
|
||||||
assertFalse(masker.isValid(g3, MIN_LEN));
|
assertFalse(masker.isValid(g3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -160,11 +169,11 @@ public class MaskerCluesTest {
|
|||||||
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(masker.isValid(g,MIN_LEN));
|
assertFalse(masker.isValid(g));
|
||||||
|
|
||||||
// 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(masker.isValid(g2,MIN_LEN));
|
assertFalse(masker.isValid(g2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package puzzle;
|
|||||||
import module java.base;
|
import module java.base;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import puzzle.Export.Clue;
|
||||||
import puzzle.Export.Clued;
|
import puzzle.Export.Clued;
|
||||||
import puzzle.Export.Gridded;
|
import puzzle.Export.Gridded;
|
||||||
import puzzle.Masker.Clues;
|
import puzzle.Masker.Clues;
|
||||||
@@ -12,12 +13,14 @@ import puzzle.SwedishGenerator.Slotinfo;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static puzzle.SwedishGenerator.fillMask;
|
import static puzzle.SwedishGenerator.fillMask;
|
||||||
|
import static puzzle.dict800.DictData.DICT800;
|
||||||
|
import static puzzle.dict900.DictData.DICT900;
|
||||||
|
|
||||||
public class PerformanceTest {
|
public class PerformanceTest {
|
||||||
|
|
||||||
final DictEntry[] EN = DictData.DICT.index();
|
final DictEntry[] EN = DICT800.index();
|
||||||
void main() {
|
void main() {
|
||||||
testPerformance();
|
testIncrementalComplexity();
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
void testPerformance() {
|
void testPerformance() {
|
||||||
@@ -80,7 +83,7 @@ public class PerformanceTest {
|
|||||||
" 1 \n" +
|
" 1 \n" +
|
||||||
"221 22\n";
|
"221 22\n";
|
||||||
val mask = Clued.parse(maskStr);
|
val mask = Clued.parse(maskStr);
|
||||||
val allSlots = Masker.slots(mask.c(), EN);
|
val allSlots = Masker.slots(mask.c(), DICT900.index());
|
||||||
//mask.toGrid()
|
//mask.toGrid()
|
||||||
System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
|
System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
|
||||||
System.out.println("[DEBUG_LOG] Full Slot Layout:");
|
System.out.println("[DEBUG_LOG] Full Slot Layout:");
|
||||||
@@ -123,7 +126,9 @@ public class PerformanceTest {
|
|||||||
for (Slotinfo s : slots) s.assign().w = 0;
|
for (Slotinfo s : slots) s.assign().w = 0;
|
||||||
|
|
||||||
val result = fillMask(rng, slots, Slotinfo.grid(slots));
|
val result = fillMask(rng, slots, Slotinfo.grid(slots));
|
||||||
if (result.ok()) successCount++;
|
if (result.ok()) {
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
totalNodes += result.nodes();
|
totalNodes += result.nodes();
|
||||||
totalBacktracks += result.backtracks();
|
totalBacktracks += result.backtracks();
|
||||||
}
|
}
|
||||||
@@ -132,6 +137,7 @@ public class PerformanceTest {
|
|||||||
|
|
||||||
System.out.printf(Locale.ROOT, "[DEBUG_LOG] %s: %d/%d SUCCESS | avg nodes=%d | avg backtracks=%d | total time=%.3fs%n",
|
System.out.printf(Locale.ROOT, "[DEBUG_LOG] %s: %d/%d SUCCESS | avg nodes=%d | avg backtracks=%d | total time=%.3fs%n",
|
||||||
label, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
|
label, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
|
||||||
|
visualizeSlots(slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visualizeSlots(Slotinfo[] slots) {
|
private void visualizeSlots(Slotinfo[] slots) {
|
||||||
@@ -142,7 +148,7 @@ public class PerformanceTest {
|
|||||||
|
|
||||||
for (Slotinfo slot : slots) {
|
for (Slotinfo slot : slots) {
|
||||||
int key = slot.key();
|
int key = slot.key();
|
||||||
int dir = Masker.Slot.dir(key);
|
Clue dir = Clue.from(Masker.Slot.dir(key));
|
||||||
int clueIdx = Masker.Slot.clueIndex(key);
|
int clueIdx = Masker.Slot.clueIndex(key);
|
||||||
|
|
||||||
int cr = Masker.IT[clueIdx].r();
|
int cr = Masker.IT[clueIdx].r();
|
||||||
@@ -150,30 +156,8 @@ public class PerformanceTest {
|
|||||||
|
|
||||||
// User requested: aAAAA for a four letter to RIGHT clue slot.
|
// User requested: aAAAA for a four letter to RIGHT clue slot.
|
||||||
// SwedishGenerator: 1=RIGHT, 0=DOWN, 2=UP, 3=LEFT
|
// SwedishGenerator: 1=RIGHT, 0=DOWN, 2=UP, 3=LEFT
|
||||||
char clueChar;
|
char clueChar = dir.clueChar;
|
||||||
char slotChar;
|
char slotChar = dir.slotChar;
|
||||||
switch (dir) {
|
|
||||||
case 1:
|
|
||||||
clueChar = 'a';
|
|
||||||
slotChar = 'A';
|
|
||||||
break; // RIGHT
|
|
||||||
case 0:
|
|
||||||
clueChar = 'b';
|
|
||||||
slotChar = 'B';
|
|
||||||
break; // DOWN
|
|
||||||
case 2:
|
|
||||||
clueChar = 'c';
|
|
||||||
slotChar = 'C';
|
|
||||||
break; // UP
|
|
||||||
case 3:
|
|
||||||
clueChar = 'd';
|
|
||||||
slotChar = 'D';
|
|
||||||
break; // LEFT
|
|
||||||
default:
|
|
||||||
clueChar = '?';
|
|
||||||
slotChar = '?';
|
|
||||||
}
|
|
||||||
|
|
||||||
display[cr][cc] = clueChar;
|
display[cr][cc] = clueChar;
|
||||||
|
|
||||||
Gridded.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).forEach(idx -> {
|
Gridded.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).forEach(idx -> {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import puzzle.Masker.Clues;
|
|||||||
import puzzle.Masker.Slot;
|
import puzzle.Masker.Slot;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static precomp.Const9x8.*;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
import static puzzle.SwedishGeneratorTest.Idx.IDX_0_0;
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_0_0;
|
||||||
|
|
||||||
@@ -78,31 +79,6 @@ public class SwedishGeneratorTest {
|
|||||||
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 int OFF_1_0 = Masker.offset(1, 0);
|
|
||||||
static final int OFF_1_1 = Masker.offset(1, 1);
|
|
||||||
static final int OFF_1_2 = Masker.offset(1, 2);
|
|
||||||
static final int OFF_1_3 = Masker.offset(1, 3);
|
|
||||||
static final int OFF_1_4 = Masker.offset(1, 4);
|
|
||||||
static final int OFF_1_5 = Masker.offset(1, 5);
|
|
||||||
static final int OFF_2_1 = Masker.offset(2, 1);
|
|
||||||
static final int OFF_2_3 = Masker.offset(2, 3);
|
|
||||||
static final int OFF_2_2 = Masker.offset(2, 2);
|
|
||||||
static final int OFF_2_4 = Masker.offset(2, 4);
|
|
||||||
static final int OFF_0_0 = Masker.offset(0, 0);
|
|
||||||
static final int OFF_0_4 = Masker.offset(0, 4);
|
|
||||||
static final int OFF_0_5 = Masker.offset(0, 5);
|
|
||||||
static final int OFF_0_1 = Masker.offset(0, 1);
|
|
||||||
static final int OFF_0_2 = Masker.offset(0, 2);
|
|
||||||
static final int OFF_0_3 = Masker.offset(0, 3);
|
|
||||||
static final int OFF_2_0 = Masker.offset(2, 0);
|
|
||||||
static final int OFF_2_5 = Masker.offset(2, 5);
|
|
||||||
static final int OFF_3_5 = Masker.offset(3, 5);
|
|
||||||
static final int OFF_4_5 = Masker.offset(4, 5);
|
|
||||||
static final int OFF_3_0 = Masker.offset(3, 0);
|
|
||||||
static final int OFF_3_1 = Masker.offset(3, 1);
|
|
||||||
static final int OFF_3_2 = Masker.offset(3, 2);
|
|
||||||
static final int OFF_3_3 = Masker.offset(3, 3);
|
|
||||||
static final int OFF_3_4 = Masker.offset(3, 4);
|
|
||||||
static final byte D_BYTE_2 = CLUE_RIGHT;
|
static final byte D_BYTE_2 = CLUE_RIGHT;
|
||||||
|
|
||||||
enum Idx {
|
enum Idx {
|
||||||
|
|||||||
Reference in New Issue
Block a user