Gather data
This commit is contained in:
@@ -56,6 +56,7 @@ public record SwedishGenerator(int[] buff) {
|
||||
static final int R = Config.PUZZLE_ROWS;
|
||||
static final double CROSS_X = (R - 1) / 2.0;
|
||||
static final int SIZE = C * R;// ~18
|
||||
static final double SIZED = (double) SIZE;// ~18
|
||||
static final int TARGET_CLUES = SIZE >> 2;
|
||||
static final int MAX_WORD_LENGTH = Math.min(C, R);
|
||||
static final int MIN_LEN = Config.MIN_LEN;
|
||||
@@ -144,19 +145,34 @@ public record SwedishGenerator(int[] buff) {
|
||||
|
||||
static final byte _48 = 48;
|
||||
|
||||
record Grid(byte[] g) {
|
||||
record Grid(byte[] g, long[] bo) {
|
||||
|
||||
int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
|
||||
int digitAt(int index) { return g[index] - 48; }
|
||||
public static int r(int offset) { return offset & 7; }
|
||||
public static int c(int offset) { return offset >>> 3; }
|
||||
static int offset(int r, int c) { return r | (c << 3); }
|
||||
Grid deepCopyGrid() { return new Grid(g.clone()); }
|
||||
public byte byteAt(int r, int c) { return g[offset(r, c)]; }
|
||||
public byte byteAt(int pos) { return g[pos]; }
|
||||
void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; }
|
||||
void setByteAt(int idx, byte ch) { g[idx] = ch; }
|
||||
void clear(int idx) { g[idx] = DASH; }
|
||||
public Grid(byte[] g) { this(g, new long[2]); }
|
||||
int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
|
||||
int digitAt(int index) { return g[index] - 48; }
|
||||
public static int r(int offset) { return offset & 7; }
|
||||
public static int c(int offset) { return offset >>> 3; }
|
||||
static int offset(int r, int c) { return r | (c << 3); }
|
||||
Grid deepCopyGrid() { return new Grid(g.clone(), bo.clone()); }
|
||||
public byte byteAt(int r, int c) { return g[offset(r, c)]; }
|
||||
public byte byteAt(int pos) { return g[pos]; }
|
||||
void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; }
|
||||
void setByteAt(int idx, byte ch) { g[idx] = ch; }
|
||||
void setAt(int idx, byte ch) {
|
||||
if (isDigit(ch)) setClue(idx, ch);
|
||||
else g[idx] = ch;
|
||||
}
|
||||
void setClue(int idx, byte ch) {
|
||||
g[idx] = ch;
|
||||
if (idx < 64) bo[0] |= (1L << idx);
|
||||
else bo[1] |= (1L << (idx & 63));
|
||||
}
|
||||
void clear(int idx) { g[idx] = DASH; }
|
||||
void clearClue(int idx) {
|
||||
g[idx] = DASH;
|
||||
if (idx < 64) bo[0] &= ~(1L << idx);
|
||||
else bo[1] &= ~(1L << (idx & 63));
|
||||
}
|
||||
public boolean isLetterAt(int r, int c) { return ((g[offset(r, c)] & 64) != 0); }
|
||||
boolean isDigitAt(int r, int c) { return (g[offset(r, c)] & 48) == 48; }
|
||||
boolean isDigitAt(int index) { return (g[index] & 48) == 48; }
|
||||
@@ -166,10 +182,30 @@ public record SwedishGenerator(int[] buff) {
|
||||
public double similarity(Grid b) {
|
||||
var same = 0;
|
||||
for (int i = 0; i < SIZE; i++) if (g[i] == b.g[i]) same++;
|
||||
return same / (double) (SIZE);
|
||||
return same / SIZED;
|
||||
}
|
||||
int clueCount() {
|
||||
return Long.bitCount(bo[0]) + Long.bitCount(bo[1]);
|
||||
}
|
||||
void forEachSetBit71(java.util.function.IntConsumer consumer) {
|
||||
long lo = bo[0], hi = bo[1];
|
||||
// low 64 bits
|
||||
while (lo != 0L) {
|
||||
int bit = Long.numberOfTrailingZeros(lo);
|
||||
consumer.accept(bit); // 0..63
|
||||
lo &= (lo - 1); // clear lowest set bit
|
||||
}
|
||||
|
||||
// high 7 bits (positions 64..70)
|
||||
// hi &= 0x7FL;
|
||||
while (hi != 0L) {
|
||||
int bit = Long.numberOfTrailingZeros(hi);
|
||||
consumer.accept(64 + bit); // 64..70
|
||||
hi &= (hi - 1L);
|
||||
}
|
||||
}
|
||||
}
|
||||
static Grid makeEmptyGrid() { return new Grid(new byte[SIZE]); }
|
||||
static Grid makeEmptyGrid() { return new Grid(new byte[SIZE], new long[2]); }
|
||||
|
||||
static final class IntList {
|
||||
|
||||
@@ -365,7 +401,25 @@ public record SwedishGenerator(int[] buff) {
|
||||
}
|
||||
|
||||
void forEachSlot(Grid grid, SlotVisitor visitor) {
|
||||
for (var rci : IT) {
|
||||
grid.forEachSetBit71(idx -> {
|
||||
var d = grid.digitAt(idx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
int r = Grid.r(idx), c = Grid.c(idx), rr = r + nbrs16.r, cc = c + nbrs16.c;
|
||||
if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) return;
|
||||
long packedPos = 0;
|
||||
var n = 0;
|
||||
|
||||
while (rr >= 0 && rr < R && cc >= 0 && cc < C && grid.isLettercell(rr, cc) && n < MAX_WORD_LENGTH) {
|
||||
packedPos |= (long) Grid.offset(rr, cc) << (n * 7);
|
||||
n++;
|
||||
rr += nbrs16.dr;
|
||||
cc += nbrs16.dc;
|
||||
}
|
||||
if (n > 0) {
|
||||
visitor.visit((r << 8) | (c << 4) | d, packedPos, n);
|
||||
}
|
||||
});
|
||||
/* for (var rci : IT) {
|
||||
if (!grid.isDigitAt(rci.i)) continue;
|
||||
var d = grid.digitAt(rci.i);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
@@ -384,7 +438,7 @@ public record SwedishGenerator(int[] buff) {
|
||||
if (n > 0) {
|
||||
visitor.visit((rci.r << 8) | (rci.c << 4) | d, packedPos, n);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
ArrayList<Slot> extractSlots(Grid grid) {
|
||||
@@ -405,12 +459,10 @@ public record SwedishGenerator(int[] buff) {
|
||||
}
|
||||
|
||||
long maskFitness(Grid grid, int[] lenCounts) {
|
||||
long penalty = 0;
|
||||
final long[] penalty = { 0 };
|
||||
|
||||
var clueCount = 0;
|
||||
for (var v : grid.g) if (Grid.isDigit(v)) clueCount++;
|
||||
|
||||
penalty += 8L * Math.abs(clueCount - TARGET_CLUES);
|
||||
var clueCount = grid.clueCount();
|
||||
penalty[0] += 8L * Math.abs(clueCount - TARGET_CLUES);
|
||||
|
||||
var ctx = CTX.get();
|
||||
var covH = ctx.covH;
|
||||
@@ -418,13 +470,12 @@ public record SwedishGenerator(int[] buff) {
|
||||
Arrays.fill(covH, 0, SIZE, 0);
|
||||
Arrays.fill(covV, 0, SIZE, 0);
|
||||
|
||||
boolean hasSlots = false;
|
||||
for (var rci : IT) {
|
||||
if (!grid.isDigitAt(rci.i)) continue;
|
||||
var d = grid.digitAt(rci.i);
|
||||
boolean[] hasSlots = {false};
|
||||
grid.forEachSetBit71(clueIdx -> {
|
||||
var d = grid.digitAt(clueIdx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
int rr = rci.r + nbrs16.r, cc = rci.c + nbrs16.c;
|
||||
if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue;
|
||||
int rr = Grid.r(clueIdx) + nbrs16.r, cc = Grid.c(clueIdx) + nbrs16.c;
|
||||
if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) return;
|
||||
|
||||
long packedPos = 0;
|
||||
var n = 0;
|
||||
@@ -436,43 +487,49 @@ public record SwedishGenerator(int[] buff) {
|
||||
rr += nbrs16.dr;
|
||||
cc += nbrs16.dc;
|
||||
}
|
||||
if (n == 0) continue;
|
||||
hasSlots = true;
|
||||
if (n == 0) return;
|
||||
hasSlots[0] = true;
|
||||
|
||||
if (n < MIN_LEN) {
|
||||
penalty += 8000;
|
||||
penalty[0] += 8000;
|
||||
} else {
|
||||
if (lenCounts[n] <= 0) penalty += 12000;
|
||||
if (lenCounts[n] <= 0) penalty[0] += 12000;
|
||||
}
|
||||
|
||||
var horiz = Slot.horiz(d) ? covH : covV;
|
||||
for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasSlots) return 1_000_000_000L;
|
||||
if (!hasSlots[0]) return 1_000_000_000L;
|
||||
|
||||
/* grid.forEachSetBit71(clueIdx -> {
|
||||
var h = covH[clueIdx];
|
||||
var v = covV[clueIdx];
|
||||
if (h == 0 && v == 0) penalty[0] += 1500;
|
||||
else if (h > 0 && v > 0) { *//* ok *//* } else if (h + v == 1) penalty[0] += 200;
|
||||
else penalty[0] += 600;
|
||||
});*/
|
||||
int idx, h, v;
|
||||
for (idx = 0; idx < SIZE; idx++) {
|
||||
if (grid.isDigitAt(idx)) continue;
|
||||
h = covH[idx];
|
||||
v = covV[idx];
|
||||
if (h == 0 && v == 0) penalty += 1500;
|
||||
else if (h > 0 && v > 0) { /* ok */ } else if (h + v == 1) penalty += 200;
|
||||
else penalty += 600;
|
||||
if (h == 0 && v == 0) penalty[0] += 1500;
|
||||
else if (h > 0 && v > 0) { /* ok */ } else if (h + v == 1) penalty[0] += 200;
|
||||
else penalty[0] += 600;
|
||||
}
|
||||
|
||||
// clue clustering (8-connected)
|
||||
var seen = ctx.seen;
|
||||
seen.clear();
|
||||
var stack = ctx.stack;
|
||||
int sp, nr, nc;
|
||||
long size;
|
||||
for (var rci : IT) {
|
||||
idx = rci.i;
|
||||
if (seen.get(idx) || grid.isLetterAt(idx)) continue;
|
||||
sp = 0;
|
||||
stack[sp++] = idx;
|
||||
seen.set(idx);
|
||||
size = 0;
|
||||
var stack = ctx.stack;
|
||||
|
||||
grid.forEachSetBit71(clueIdx -> {
|
||||
if (seen.get(clueIdx)) return;
|
||||
var sp = 0;
|
||||
stack[sp++] = clueIdx;
|
||||
seen.set(clueIdx);
|
||||
var size = 0;
|
||||
|
||||
while (sp > 0) {
|
||||
var p = stack[--sp];
|
||||
@@ -480,19 +537,21 @@ public record SwedishGenerator(int[] buff) {
|
||||
size++;
|
||||
|
||||
for (var d : nbrs8) {
|
||||
nr = rr + d.r;
|
||||
nc = cc + d.c;
|
||||
var nr = rr + d.r;
|
||||
var nc = cc + d.c;
|
||||
if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
|
||||
idx = Grid.offset(nr, nc);
|
||||
if (seen.get(idx) || grid.isLetterAt(idx)) continue;
|
||||
seen.set(idx);
|
||||
stack[sp++] = idx;
|
||||
var nidx = Grid.offset(nr, nc);
|
||||
if (seen.get(nidx) || grid.isLetterAt(nidx)) continue;
|
||||
seen.set(nidx);
|
||||
stack[sp++] = nidx;
|
||||
}
|
||||
}
|
||||
|
||||
if (size >= 2) penalty += (size - 1L) * 120L;
|
||||
}
|
||||
int walls, wr, wc;
|
||||
if (size >= 2) penalty[0] += (size - 1L) * 120L;
|
||||
});
|
||||
int sp, nr, nc;
|
||||
long size;
|
||||
int walls, wr, wc;
|
||||
// dead-end-ish letter cell (3+ walls)
|
||||
for (var rci : IT) {
|
||||
if (grid.isDigitAt(rci.i)) continue;
|
||||
@@ -502,10 +561,10 @@ public record SwedishGenerator(int[] buff) {
|
||||
wc = rci.c + d.c;
|
||||
if (wr < 0 || wr >= R || wc < 0 || wc >= C || grid.isDigitAt(wr, wc)) walls++;
|
||||
}
|
||||
if (walls >= 3) penalty += 400;
|
||||
if (walls >= 3) penalty[0] += 400;
|
||||
}
|
||||
|
||||
return penalty;
|
||||
return penalty[0];
|
||||
}
|
||||
|
||||
Grid randomMask(Rng rng) {
|
||||
@@ -518,12 +577,10 @@ public record SwedishGenerator(int[] buff) {
|
||||
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;
|
||||
if (hasRoomForClue(g, idx, d)) {
|
||||
g.setClue(idx, d.dbyte);
|
||||
placed++;
|
||||
}
|
||||
placed++;
|
||||
}
|
||||
return g;
|
||||
}
|
||||
@@ -539,11 +596,10 @@ public record SwedishGenerator(int[] buff) {
|
||||
clamp(cy + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, C - 1));
|
||||
|
||||
if (g.isDigitAt(ri)) {
|
||||
g.clear(ri);
|
||||
g.clearClue(ri);
|
||||
} else {
|
||||
var d = OFFSETS[rng.randint(1, 4)];
|
||||
g.setByteAt(ri, d.dbyte);
|
||||
if (!hasRoomForClue(g, ri, d)) g.clear(ri);
|
||||
if (hasRoomForClue(g, ri, d)) g.setClue(ri, d.dbyte);
|
||||
}
|
||||
}
|
||||
return g;
|
||||
@@ -554,13 +610,15 @@ public record SwedishGenerator(int[] buff) {
|
||||
var nx = Math.cos(theta);
|
||||
var ny = Math.sin(theta);
|
||||
|
||||
for (var rci : IT) out.setByteAt(rci.i, ((rci.r - CROSS_X) * nx + (rci.c - CROSS_Y) * ny >= 0) ? a.byteAt(rci.i) : b.byteAt(rci.i));
|
||||
for (var rci : IT) if (out.isDigitAt(rci.i) && !hasRoomForClue(out, rci.i, OFFSETS[out.digitAt(rci.i)])) out.clear(rci.i);
|
||||
for (var rci : IT) {
|
||||
out.setAt(rci.i, ((rci.r - CROSS_X) * nx + (rci.c - CROSS_Y) * ny >= 0) ? a.byteAt(rci.i) : b.byteAt(rci.i));
|
||||
}
|
||||
for (var rci : IT) if (out.isDigitAt(rci.i) && !hasRoomForClue(out, rci.i, OFFSETS[out.digitAt(rci.i)])) out.clearClue(rci.i);
|
||||
return out;
|
||||
}
|
||||
|
||||
Grid hillclimb(Rng rng, Grid start, int[] lenCounts, int limit) {
|
||||
var best = start;//.deepCopyGrid();
|
||||
var best = start;//.deepCopyGrid();
|
||||
var bestF = maskFitness(best, lenCounts);
|
||||
var fails = 0;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class ExportFormatTest {
|
||||
var grid = SwedishGenerator.makeEmptyGrid();
|
||||
|
||||
// Place a '2' (right) at (0,0)
|
||||
grid.setByteAt(0, (byte) '2');
|
||||
grid.setClue(0, (byte) '2');
|
||||
// This creates a slot starting at (0,1)
|
||||
|
||||
var clueMap = new HashMap<Integer, Lemma>();
|
||||
@@ -36,7 +36,7 @@ public class ExportFormatTest {
|
||||
grid.setByteAt(Grid.offset(0, 3), (byte) 'S');
|
||||
grid.setByteAt(Grid.offset(0, 4), (byte) 'T');
|
||||
// Terminate thGrid.offset(e slot at) (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH
|
||||
grid.setByteAt(Grid.offset(0, 5), (byte) '1');
|
||||
grid.setClue(Grid.offset(0, 5), (byte) '1');
|
||||
|
||||
var fillResult = new FillResult(true, new Gridded(grid), clueMap, null);
|
||||
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
||||
|
||||
@@ -28,7 +28,7 @@ public class MainTest {
|
||||
|
||||
// Set up digits on the grid to create slots.
|
||||
// '2' (right) at (0,0) -> slot at (0,1), (0,2)
|
||||
grid.setCharAt(0, 0, '2');
|
||||
grid.setClue(0, (byte) '2');
|
||||
grid.setCharAt(0, 1, 'A');
|
||||
grid.setCharAt(0, 2, 'B');
|
||||
|
||||
@@ -61,7 +61,7 @@ public class MainTest {
|
||||
void testForEachSlot() {
|
||||
var generator = new SwedishGenerator();
|
||||
var grid = makeEmptyGrid();
|
||||
grid.setCharAt(0, 0, '2'); // right
|
||||
grid.setClue(0, (byte) '2'); // right
|
||||
|
||||
var count = new AtomicInteger(0);
|
||||
generator.forEachSlot(grid, (key, packedPos, len) -> {
|
||||
@@ -175,8 +175,8 @@ public class MainTest {
|
||||
// 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 byte[]{ 'M', 'A', 'N', 'T', 'A' }, res.filled().clueMap().get(1377).word());
|
||||
Assertions.assertEquals(775.45, res.filled().simplicity(), 1e-9, "Simplicity value changed");
|
||||
Assertions.assertArrayEquals(new byte[]{ 'I', 'N', 'E', 'R', 'T' }, res.filled().clueMap().get(1377).word());
|
||||
}
|
||||
@Test
|
||||
public void testIsLetterA() {
|
||||
|
||||
@@ -191,7 +191,7 @@ public class SwedishGeneratorTest {
|
||||
var grid = SwedishGenerator.makeEmptyGrid();
|
||||
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
|
||||
// Set '2' (right) at 0,0
|
||||
grid.setCharAt(0, 0, '2');
|
||||
grid.setClue(0, (byte) '2');
|
||||
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
||||
|
||||
var slots = gen.extractSlots(grid);
|
||||
@@ -226,7 +226,7 @@ public class SwedishGeneratorTest {
|
||||
assertTrue(f1 >= 1_000_000_000L);
|
||||
|
||||
// Add a slot
|
||||
grid.setCharAt(0, 0, '2');
|
||||
grid.setClue(0, SwedishGenerator.OFFSETS[2].dbyte());
|
||||
var f2 = gen.maskFitness(grid, lenCounts);
|
||||
assertTrue(f2 < f1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user