Gather data
This commit is contained in:
@@ -30,7 +30,12 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
|
|
||||||
record nbrs_8(int r, int c) { }
|
record nbrs_8(int r, int c) { }
|
||||||
|
|
||||||
record nbrs_16(int r, int c, int dr, int dc) { }
|
record nbrs_16(int r, int c, int dr, int dc, int d, byte dbyte) {
|
||||||
|
|
||||||
|
public nbrs_16(int r, int c, int dr, int dc, int d) {
|
||||||
|
this(r, c, dr, dc, d, (byte) (48 + d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static final int C = Config.PUZZLE_COLS;
|
static final int C = Config.PUZZLE_COLS;
|
||||||
static final double CROSS_Y = (C - 1) / 2.0;
|
static final double CROSS_Y = (C - 1) / 2.0;
|
||||||
@@ -54,12 +59,12 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
// Directions for '1'..'6'
|
// Directions for '1'..'6'
|
||||||
static final nbrs_16[] OFFSETS = new nbrs_16[]{
|
static final nbrs_16[] OFFSETS = new nbrs_16[]{
|
||||||
null,
|
null,
|
||||||
new nbrs_16(-1, 0, -1, 0), // 1: up
|
new nbrs_16(-1, 0, -1, 0, 1), // 1: up
|
||||||
new nbrs_16(0, 1, 0, 1), // 2: right
|
new nbrs_16(0, 1, 0, 1, 2), // 2: right
|
||||||
new nbrs_16(1, 0, 1, 0),// 3: down
|
new nbrs_16(1, 0, 1, 0, 3),// 3: down
|
||||||
new nbrs_16(0, -1, 0, -1),// 4: left
|
new nbrs_16(0, -1, 0, -1, 4),// 4: left
|
||||||
new nbrs_16(0, -1, 1, 0),// 5: vertical down, clue is on the right of the first letter
|
new nbrs_16(0, -1, 1, 0, 5),// 5: vertical down, clue is on the right of the first letter
|
||||||
new nbrs_16(0, 1, 1, 0)// 6: vertical down, clue is on the left of the first letter
|
new nbrs_16(0, 1, 1, 0, 6)// 6: vertical down, clue is on the left of the first letter
|
||||||
};
|
};
|
||||||
final static nbrs_8[] nbrs8 = new nbrs_8[]{
|
final static nbrs_8[] nbrs8 = new nbrs_8[]{
|
||||||
new nbrs_8(-1, -1),
|
new nbrs_8(-1, -1),
|
||||||
@@ -110,6 +115,11 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
x = y;
|
x = y;
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
byte randbyte(int min, int max) {
|
||||||
|
var u = (nextU32() & 0xFFFFFFFFL);
|
||||||
|
var range = (long) max - (long) min + 1L;
|
||||||
|
return (byte) (min + (u % range));
|
||||||
|
}
|
||||||
int randint(int min, int max) {
|
int randint(int min, int max) {
|
||||||
var u = (nextU32() & 0xFFFFFFFFL);
|
var u = (nextU32() & 0xFFFFFFFFL);
|
||||||
var range = (long) max - (long) min + 1L;
|
var range = (long) max - (long) min + 1L;
|
||||||
@@ -118,18 +128,22 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
|
double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
record Grid(byte[] g) {
|
static final byte _48 = 48;
|
||||||
|
|
||||||
|
record Grid(byte[] g) {
|
||||||
|
int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
|
||||||
public static int r(int offset) { return offset & 7; }
|
public static int r(int offset) { return offset & 7; }
|
||||||
public static int c(int offset) { return offset >>> 3; }
|
public static int c(int offset) { return offset >>> 3; }
|
||||||
static int offset(int r, int c) { return r | (c << 3); }
|
static int offset(int r, int c) { return r | (c << 3); }
|
||||||
Grid deepCopyGrid() { return new Grid(g.clone()); }
|
Grid deepCopyGrid() { return new Grid(g.clone()); }
|
||||||
char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); }
|
char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); }
|
||||||
char getCharAt(int pos) { return (char) (g[pos]); }
|
char getCharAt(int pos) { return (char) (g[pos]); }
|
||||||
int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
|
|
||||||
byte byteAt(int r, int c) { return g[offset(r, c)]; }
|
byte byteAt(int r, int c) { return g[offset(r, c)]; }
|
||||||
|
byte byteAt(int pos) { return g[pos]; }
|
||||||
|
byte byteAtStatic(int r, int c) { return g[offset(r, c)]; }
|
||||||
void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; }
|
void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; }
|
||||||
void setByteAt(int r, int c, byte ch) { g[offset(r, c)] = ch; }
|
void setByteAt(int r, int c, byte ch) { g[offset(r, c)] = ch; }
|
||||||
|
void setDigitAt(int r, int c, int ch) { g[offset(r, c)] = (byte) (_48 + ch); }
|
||||||
void setCharAt(int idx, char ch) { g[idx] = (byte) ch; }
|
void setCharAt(int idx, char ch) { g[idx] = (byte) ch; }
|
||||||
void setByteAt(int idx, byte ch) { g[idx] = ch; }
|
void setByteAt(int idx, byte ch) { g[idx] = ch; }
|
||||||
void clear(int r, int c) { g[offset(r, c)] = 0; }
|
void clear(int r, int c) { g[offset(r, c)] = 0; }
|
||||||
@@ -391,7 +405,17 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
forEachSlot(grid, (key, packedPos, len) -> slots.add(Slot.from(key, packedPos, len)));
|
forEachSlot(grid, (key, packedPos, len) -> slots.add(Slot.from(key, packedPos, len)));
|
||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
|
boolean hasRoomForClue(Grid grid, int idx, nbrs_16 nbrs16) {
|
||||||
|
int rr = Grid.r(idx) + nbrs16.r, cc = Grid.c(idx) + nbrs16.c;
|
||||||
|
var run = 0;
|
||||||
|
while (rr >= 0 && rr < R && cc >= 0 && cc < C && (grid.isLettercell(rr, cc)) && run < MAX_WORD_LENGTH) {
|
||||||
|
run++;
|
||||||
|
rr += nbrs16.dr;
|
||||||
|
cc += nbrs16.dc;
|
||||||
|
if (run >= MIN_LEN) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
boolean hasRoomForClue(Grid grid, int r, int c, char d) {
|
boolean hasRoomForClue(Grid grid, int r, int c, char d) {
|
||||||
var nbrs16 = OFFSETS[d - '0'];
|
var nbrs16 = OFFSETS[d - '0'];
|
||||||
int rr = r + nbrs16.r, cc = c + nbrs16.c;
|
int rr = r + nbrs16.r, cc = c + nbrs16.c;
|
||||||
@@ -516,7 +540,25 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- Mask generation ----------------
|
// ---------------- Mask generation ----------------
|
||||||
|
Grid randomMask2(Rng rng) {
|
||||||
|
var g = makeEmptyGrid();
|
||||||
|
|
||||||
|
int placed = 0, guard = 0;
|
||||||
|
|
||||||
|
while (placed < TARGET_CLUES && guard++ < 4000) {
|
||||||
|
var idx = rng.randint(0, SIZE - 1);
|
||||||
|
if (g.isDigitAt(idx)) continue;
|
||||||
|
|
||||||
|
var d = OFFSETS[rng.randbyte(1, 4)];
|
||||||
|
g.setByteAt(idx, d.dbyte);
|
||||||
|
if (!hasRoomForClue(g, idx, d)) {
|
||||||
|
g.clear(idx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
placed++;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
Grid randomMask(Rng rng) {
|
Grid randomMask(Rng rng) {
|
||||||
var g = makeEmptyGrid();
|
var g = makeEmptyGrid();
|
||||||
var targetClues = (int) Math.round(SIZE * 0.25);
|
var targetClues = (int) Math.round(SIZE * 0.25);
|
||||||
@@ -537,7 +579,26 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
Grid mutate2(Rng rng, Grid grid) {
|
||||||
|
var g = grid.deepCopyGrid();
|
||||||
|
var cx = rng.randint(0, R - 1);
|
||||||
|
var cy = rng.randint(0, C - 1);
|
||||||
|
|
||||||
|
var steps = 4;
|
||||||
|
for (var k = 0; k < steps; k++) {
|
||||||
|
var rr = clamp(cx + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, R - 1);
|
||||||
|
var cc = clamp(cy + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, C - 1);
|
||||||
|
|
||||||
|
if (g.isDigitAt(rr, cc)) {
|
||||||
|
g.clear(rr, cc);
|
||||||
|
} else {
|
||||||
|
var d = OFFSETS[rng.randint(1, 4)];
|
||||||
|
g.setByteAt(rr, cc, d.dbyte);
|
||||||
|
if (!hasRoomForClue(g, Grid.offset(rr, cc), d)) g.clear(rr, cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
Grid mutate(Rng rng, Grid grid) {
|
Grid mutate(Rng rng, Grid grid) {
|
||||||
var g = grid.deepCopyGrid();
|
var g = grid.deepCopyGrid();
|
||||||
var cx = rng.randint(0, R - 1);
|
var cx = rng.randint(0, R - 1);
|
||||||
@@ -558,7 +619,6 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
Grid crossover(Rng rng, Grid a, Grid b) {
|
Grid crossover(Rng rng, Grid a, Grid b) {
|
||||||
var out = makeEmptyGrid();
|
var out = makeEmptyGrid();
|
||||||
var theta = rng.nextFloat() * Math.PI;
|
var theta = rng.nextFloat() * Math.PI;
|
||||||
|
|||||||
@@ -145,57 +145,47 @@ public class MainTest {
|
|||||||
Assertions.assertTrue(grid.isDigitAt(1, 1));
|
Assertions.assertTrue(grid.isDigitAt(1, 1));
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
|
||||||
public void testAttempt() {
|
public void testAttempt() {
|
||||||
// Arrange
|
// Arrange
|
||||||
var opts = new Main.Opts();
|
var opts = new Main.Opts();
|
||||||
//seed=1811328180
|
opts.seed = 12347;
|
||||||
opts.seed = -1645461655;// (int) (System.nanoTime() ^ System.currentTimeMillis());
|
opts.pop = 4; // Tiny population
|
||||||
opts.pop = 18; // Small for micro-scale
|
opts.gens = 20; // Very few generations
|
||||||
opts.gens = 200;
|
|
||||||
opts.minSimplicity = 0;
|
opts.minSimplicity = 0;
|
||||||
opts.fillTimeout = 20_000;
|
opts.fillTimeout = 10_000;
|
||||||
opts.threads = 1;
|
opts.threads = 1;
|
||||||
opts.tries = 1;
|
opts.tries = 1;
|
||||||
opts.verbose = true;
|
opts.verbose = false;
|
||||||
|
|
||||||
// We need a small dictionary for testing
|
var dict = SwedishGenerator.loadWords(opts.wordsPath);
|
||||||
// Instead of loading from file, we might want a way to create a mock Dict
|
|
||||||
// But SwedishGenerator.loadWords(path) is what we have.
|
|
||||||
// Let's try to load a real one or a small subset if possible.
|
|
||||||
var dict = new Dict(new Lemma[]{
|
|
||||||
new Lemma(0, "NU", 1, "NU"),
|
|
||||||
new Lemma(1, "ET", 2, "ET"),
|
|
||||||
new Lemma(2, "NUT", 3, "NUT"),
|
|
||||||
new Lemma(3, "ETE", 4, "ETE"),
|
|
||||||
new Lemma(4, "IK", 5, "IK"),
|
|
||||||
new Lemma(5, "IN", 6, "IN"),
|
|
||||||
new Lemma(6, "AU", 7, "AU"),
|
|
||||||
new Lemma(7, "JE", 8, "JE"),
|
|
||||||
new Lemma(8, "AI", 9, "AI"),
|
|
||||||
new Lemma(9, "NA", 10, "NA"),
|
|
||||||
new Lemma(10, "AF", 11, "AF"),
|
|
||||||
new Lemma(11, "AL", 14, "AL"),
|
|
||||||
new Lemma(12, "EA", 15, "EA"),
|
|
||||||
new Lemma(13, "AV", 18, "AV"),
|
|
||||||
new Lemma(14, "IL", 19, "IL"),
|
|
||||||
new Lemma(15, "EN", 22, "EN")
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
for (int i = 0; i < 200; i++) {
|
PuzzleResult res = null;
|
||||||
int seed = opts.seed + i;
|
int foundSeed = -1;
|
||||||
var rng = new Rng(seed);
|
for (int i = 0; i < 50; i++) {
|
||||||
PuzzleResult res = Main.attempt(rng, dict, opts);
|
int seed = opts.seed + i;
|
||||||
// Assert
|
var rng = new Rng(seed);
|
||||||
|
res = Main.attempt(rng, dict, opts);
|
||||||
if (res != null && res.filled().ok()) {
|
if (res != null && res.filled().ok()) {
|
||||||
System.out.println("Test Passed: Puzzle generated");
|
foundSeed = seed;
|
||||||
System.out.println("Seed: " + seed);
|
System.out.println("[DEBUG_LOG] Seed found: " + seed);
|
||||||
System.out.print(indentLines(res.swe().renderHuman(res.filled().grid()), " "));
|
System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().simplicity());
|
||||||
return;
|
System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().clueMap().size());
|
||||||
|
System.out.println("[DEBUG_LOG] Grid:");
|
||||||
|
System.out.println(res.swe().renderHuman(res.filled().grid()));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Test Note: Puzzle not generated in 1 attempt (this is possible depending on RNG)");
|
|
||||||
|
// Assert
|
||||||
|
Assertions.assertNotNull(res, "Puzzle generation failed (null result)");
|
||||||
|
Assertions.assertTrue(res.filled().ok(), "Puzzle generation failed (not ok)");
|
||||||
|
|
||||||
|
// Regression baseline for seed search starting at 12347, pop 4, gens 20
|
||||||
|
Assertions.assertEquals(12347, foundSeed, "Found seed changed");
|
||||||
|
Assertions.assertEquals(20, res.filled().clueMap().size(), "Number of assigned words changed");
|
||||||
|
Assertions.assertEquals(763.8, res.filled().simplicity(), 1e-9, "Simplicity value changed");
|
||||||
|
Assertions.assertArrayEquals(new char[]{ 'M', 'A', 'N', 'T', 'A' }, res.filled().clueMap().get(1377).word());
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testIsLetterA() {
|
public void testIsLetterA() {
|
||||||
|
|||||||
Reference in New Issue
Block a user