Gather data

This commit is contained in:
mike
2026-01-10 01:23:56 +01:00
parent cc0191f494
commit b698b9e8be
4 changed files with 45 additions and 103 deletions

View File

@@ -79,7 +79,7 @@ public class Main {
}
section("Result");
info(String.format(Locale.ROOT, "simplicity : %.2f", res.filled().simplicity()));
info(String.format(Locale.ROOT, "simplicity : %.2f", res.filled().stats().simplicity));
section("Mask");
System.out.print(indentLines(res.mask().gridToString(), " "));
@@ -347,12 +347,12 @@ public class Main {
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
if (filled.ok()) {
TOTAL_SUCCESS.incrementAndGet();
TOTAL_SIMPLICITY.addAndGet((long) (filled.simplicity() * 100));
TOTAL_SIMPLICITY.addAndGet((long) (filled.stats().simplicity * 100));
}
var name = Thread.currentThread().getName();
var status = filled.ok() ? "SUCCESS" : "FAILED";
var simplicity = String.format(Locale.ROOT, "%.2f", filled.simplicity());
var simplicity = String.format(Locale.ROOT, "%.2f", filled.stats().simplicity);
var nps = (int) (filled.stats().nodes / Math.max(0.001, filled.stats().seconds));
System.out.printf(Locale.ROOT,
@@ -360,14 +360,14 @@ public class Main {
name, status, filled.stats().nodes, filled.stats().backtracks, nps, simplicity, filled.stats().seconds
);
if (filled.ok() && (opts.minSimplicity <= 0 || filled.simplicity() >= opts.minSimplicity)) {
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {
return new PuzzleResult(swe, dict, new Gridded(mask), filled);
}
if (opts.verbose && filled.ok()) {
System.err.printf(Locale.ROOT,
"simplicity : %.2f (below min %.2f)%n",
filled.simplicity(), opts.minSimplicity
filled.stats().simplicity, opts.minSimplicity
);
}
return null;

View File

@@ -3,6 +3,10 @@ package puzzle;
import com.google.gson.Gson;
import lombok.Getter;
import lombok.val;
import precomp.Neighbors9x8;
import precomp.Neighbors9x8.nbrs_16;
import precomp.Neighbors9x8.nbrs_8;
import precomp.Neighbors9x8.rci;
import puzzle.Export.Bit;
import puzzle.Export.Bit1029;
import puzzle.Export.Gridded;
@@ -55,71 +59,16 @@ public record SwedishGenerator(Rng rng) {
static final ThreadLocal<Context> CTX = ThreadLocal.withInitial(Context::new);
static boolean isLetter(byte b) { return (b & 64) != 0; }
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
record nbrs_8(int r, int c) { }
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)); }
}
record Pick(Slot slot, CandidateInfo info, boolean done) { }
// Directions for '1'..'6'
static final nbrs_16[] OFFSETS = new nbrs_16[]{
null,
new nbrs_16(-1, 0, -1, 0, 1), // 1: up
new nbrs_16(0, 1, 0, 1, 2), // 2: right
new nbrs_16(1, 0, 1, 0, 3),// 3: down
new nbrs_16(0, -1, 0, -1, 4),// 4: left
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)// 6: vertical down, clue is on the left of the first letter
};
final static nbrs_8[] nbrs8 = new nbrs_8[]{
new nbrs_8(-1, -1),
new nbrs_8(-1, 0),
new nbrs_8(-1, 1),
new nbrs_8(0, -1),
new nbrs_8(0, 1),
new nbrs_8(1, -1),
new nbrs_8(1, 0),
new nbrs_8(1, 1)
};
static final nbrs_8[] nbrs4 = new nbrs_8[]{
new nbrs_8(-1, 0),
new nbrs_8(1, 0),
new nbrs_8(0, -1),
new nbrs_8(0, 1)
};
static final nbrs_16[] OFFSETS = Neighbors9x8.OFFSETS;
static final nbrs_8[] nbrs8 = Neighbors9x8.nbrs8;
static final nbrs_8[] nbrs4 = Neighbors9x8.nbrs4;
static final rci[] IT = Neighbors9x8.IT;
record rci(int r, int c, int i, long n1, long n2, int nbrCount, long n8_1, long n8_2) { }
static final rci[] IT = new rci[SIZE];
static {
for (int i = 0; i < SIZE; i++) {
int r = i % R;
int c = i / R;
long n1 = 0, n2 = 0;
for (var d : nbrs4) {
int nr = r + d.r;
int nc = c + d.c;
if (nr >= 0 && nr < R && nc >= 0 && nc < C) {
int nidx = nc * R + nr;
if (nidx < 64) n1 |= (1L << nidx);
else n2 |= (1L << (nidx - 64));
}
}
long n8_1 = 0, n8_2 = 0;
for (var d : nbrs8) {
int nr = r + d.r;
int nc = c + d.c;
if (nr >= 0 && nr < R && nc >= 0 && nc < C) {
int nidx = nc * R + nr;
if (nidx < 64) n8_1 |= (1L << nidx);
else n8_2 |= (1L << (nidx - 64));
}
}
IT[i] = new rci(r, c, i, n1, n2, (int) (Long.bitCount(n1) + Long.bitCount(n2)), n8_1, n8_2);
}
}
static final Pick PICK_DONE = new Pick(null, null, true);
static final Pick PICK_NOT_DONE = new Pick(null, null, false);
public static final class FillStats {
@@ -127,26 +76,19 @@ public record SwedishGenerator(Rng rng) {
public long backtracks;
public double seconds;
public int lastMRV;
public double simplicity;
}
record Pick(Slot slot, CandidateInfo info, boolean done) { }
static final Pick PICK_DONE = new Pick(null, null, true);
static final Pick PICK_NOT_DONE = new Pick(null, null, false);
public static record FillResult(boolean ok,
Gridded grid,
HashMap<Integer, Lemma> clueMap,
FillStats stats,
double simplicity) {
FillStats stats) {
public FillResult(boolean ok, Gridded grid, HashMap<Integer, Lemma> assigned, FillStats stats) {
double totalSimplicity = 0;
public FillResult {
if (ok) {
for (var w : assigned.values()) totalSimplicity += w.simpel;
totalSimplicity = assigned.isEmpty() ? 0 : totalSimplicity / assigned.size();
clueMap.forEach((k, v) -> stats.simplicity += v.simpel);
stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size();
}
this(ok, grid, assigned, stats, totalSimplicity);
}
}
@@ -392,7 +334,7 @@ public record SwedishGenerator(Rng rng) {
private static void processSlot(Grid grid, SlotVisitor visitor, int 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;
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;
@@ -400,8 +342,8 @@ public record SwedishGenerator(Rng rng) {
while (rr >= 0 && rr < R && cc >= 0 && cc < C && grid.isLetterAt(rr, cc) && n < MAX_WORD_LENGTH) {
packedPos |= (long) Grid.offset(rr, cc) << (n * 7);
n++;
rr += nbrs16.dr;
cc += nbrs16.dc;
rr += nbrs16.dr();
cc += nbrs16.dc();
}
if (n > 0) {
visitor.visit((idx << 4) | d, packedPos, n);
@@ -414,12 +356,12 @@ public record SwedishGenerator(Rng rng) {
return slots;
}
boolean hasRoomForClue(Grid grid, int idx, nbrs_16 nbrs16) {
int rr = Grid.r(idx) + nbrs16.r, cc = Grid.c(idx) + nbrs16.c;
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.isLetterAt(rr, cc)) && run < MAX_WORD_LENGTH) {
run++;
rr += nbrs16.dr;
cc += nbrs16.dc;
rr += nbrs16.dr();
cc += nbrs16.dc();
if (run >= MIN_LEN) return true;
}
return false;
@@ -445,7 +387,7 @@ public record SwedishGenerator(Rng rng) {
var d = grid.digitAt(clueIdx);
var nbrs16 = OFFSETS[d];
int rr = Grid.r(clueIdx) + nbrs16.r, cc = Grid.c(clueIdx) + nbrs16.c;
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)) continue;
long packedPos = 0;
@@ -454,8 +396,8 @@ public record SwedishGenerator(Rng rng) {
if (grid.isDigitAt(rr, cc)) break;
packedPos |= (long) Grid.offset(rr, cc) << (n * 7);
n++;
rr += nbrs16.dr;
cc += nbrs16.dc;
rr += nbrs16.dr();
cc += nbrs16.dc();
}
if (n == 0) continue;
hasSlots[0] = true;
@@ -473,7 +415,7 @@ public record SwedishGenerator(Rng rng) {
var d = grid.digitAt(clueIdx);
var nbrs16 = OFFSETS[d];
int rr = Grid.r(clueIdx) + nbrs16.r, cc = Grid.c(clueIdx) + nbrs16.c;
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)) continue;
long packedPos = 0;
@@ -482,8 +424,8 @@ public record SwedishGenerator(Rng rng) {
if (grid.isDigitAt(rr, cc)) break;
packedPos |= (long) Grid.offset(rr, cc) << (n * 7);
n++;
rr += nbrs16.dr;
cc += nbrs16.dc;
rr += nbrs16.dr();
cc += nbrs16.dc();
}
if (n == 0) continue;
hasSlots[0] = true;
@@ -526,8 +468,8 @@ public record SwedishGenerator(Rng rng) {
size++;
for (var d : nbrs8) {
var nr = rr + d.r;
var nc = cc + d.c;
var nr = rr + d.r();
var nc = cc + d.c();
if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
var nidx = Grid.offset(nr, nc);
if (seen.get(nidx) || grid.isLetterAt(nidx)) continue;
@@ -541,8 +483,8 @@ public record SwedishGenerator(Rng rng) {
int walls;
// dead-end-ish letter cell (3+ walls)
for (var rci : IT) {
if (grid.isDigitAt(rci.i)) continue;
walls = (4 - rci.nbrCount) + Long.bitCount(rci.n1 & grid.bo[0]) + Long.bitCount(rci.n2 & grid.bo[1]);
if (grid.isDigitAt(rci.i())) continue;
walls = (4 - rci.nbrCount()) + Long.bitCount(rci.n1() & grid.bo[0]) + Long.bitCount(rci.n2() & grid.bo[1]);
if (walls >= 3) penalty[0] += 400;
}
return penalty[0];
@@ -559,7 +501,7 @@ public record SwedishGenerator(Rng rng) {
var d = OFFSETS[rng.randbyte(1, 4)];
if (hasRoomForClue(g, idx, d)) {
g.setClue(idx, d.dbyte);
g.setClue(idx, d.dbyte());
placed++;
}
}
@@ -580,7 +522,7 @@ public record SwedishGenerator(Rng rng) {
g.clearClue(ri);
} else {
var d = OFFSETS[rng.randint(1, 4)];
if (hasRoomForClue(g, ri, d)) g.setClue(ri, d.dbyte);
if (hasRoomForClue(g, ri, d)) g.setClue(ri, d.dbyte());
}
}
return g;
@@ -591,8 +533,8 @@ public record SwedishGenerator(Rng rng) {
var nc = Math.cos(theta);
var nr = Math.sin(theta);
for (var rci : IT) out.setAt(rci.i, ((rci.r - CROSS_C) * nc + (rci.c - CROSS_R) * nr >= 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);
for (var rci : IT) out.setAt(rci.i(), ((rci.r() - CROSS_C) * nc + (rci.c() - CROSS_R) * nr >= 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;
}

View File

@@ -40,7 +40,7 @@ public class ExportFormatTest {
// Terminate thGrid.offset(e slot at) (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH
grid.setClue(Grid.offset(0, 5), (byte) '1');
var fillResult = new FillResult(true, new Gridded(grid), clueMap, null);
var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats());
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
var rewards = new Rewards(10, 5, 1);
@@ -80,7 +80,7 @@ public class ExportFormatTest {
void testExportFormatEmpty() {
var swe = new SwedishGenerator(new Rng(0));
var grid = Grid.createEmpty();
var fillResult = new FillResult(true, new Gridded(grid), new HashMap<>(), null);
var fillResult = new FillResult(true, new Gridded(grid), new HashMap<>(), new FillStats());
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
var exported = puzzleResult.exportFormatFromFilled(1, new Rewards(0, 0, 0));

View File

@@ -152,7 +152,7 @@ public class MainTest {
if (res != null && res.filled().ok()) {
foundSeed = seed;
System.out.println("[DEBUG_LOG] Seed found: " + seed);
System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().simplicity());
System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().stats().simplicity);
System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().clueMap().size());
System.out.println("[DEBUG_LOG] Grid:");
System.out.println(res.filled().grid().renderHuman());
@@ -167,7 +167,7 @@ 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(758.55, res.filled().simplicity(), 1e-9, "Simplicity value changed");
Assertions.assertEquals(758.55, res.filled().stats().simplicity, 1e-9, "Simplicity value changed");
Assertions.assertArrayEquals(new byte[]{ 'M', 'A', 'N', 'T', 'A' }, res.filled().clueMap().get(849).word());
}
@Test