Gather data

This commit is contained in:
mike
2026-01-09 08:57:46 +01:00
parent fa806a1078
commit 171ea60636
3 changed files with 45 additions and 73 deletions

View File

@@ -172,7 +172,7 @@ public record ExportFormat() {
var d = s.dir();
var cells = new int[s.len()];
for (var i = 0; i < s.len(); i++) cells[i] = s.pos(i);
for (int i = 0, len = s.len(); i < len; i++) cells[i] = s.pos(i);
// Canonicalize: always output right/down
String direction;

View File

@@ -1,7 +1,7 @@
package puzzle;
import lombok.Data;
import lombok.Getter;
import lombok.val;
import puzzle.ExportFormat.Bit;
import puzzle.ExportFormat.Bit1029;
import puzzle.ExportFormat.Gridded;
@@ -105,7 +105,7 @@ public record SwedishGenerator(int[] buff) {
Bit seen,
byte[] pattern,
IntList[] intListBuffer,
int[] undoBuffer) {
int[] undo) {
public Context() {
this(new int[SIZE], new int[SIZE], new int[SIZE], new int[SIZE], new Bit(), new byte[MAX_WORD_LENGTH], new IntList[MAX_WORD_LENGTH],
@@ -388,7 +388,7 @@ public record SwedishGenerator(int[] buff) {
public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); }
}
static void undoPlace(Grid grid, Slot s, int mask) {
for (var i = 0; i < s.len(); i++) {
for (int i = 0, len = s.len(); i < len; i++) {
if ((mask & (1L << i)) != 0) {
grid.clear(s.pos(i));
}
@@ -404,7 +404,7 @@ public record SwedishGenerator(int[] buff) {
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;
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;
@@ -419,26 +419,6 @@ public record SwedishGenerator(int[] buff) {
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];
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;
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((rci.r << 8) | (rci.c << 4) | d, packedPos, n);
}
}*/
}
ArrayList<Slot> extractSlots(Grid grid) {
@@ -470,7 +450,7 @@ public record SwedishGenerator(int[] buff) {
Arrays.fill(covH, 0, SIZE, 0);
Arrays.fill(covV, 0, SIZE, 0);
boolean[] hasSlots = {false};
boolean[] hasSlots = { false };
grid.forEachSetBit71(clueIdx -> {
var d = grid.digitAt(clueIdx);
var nbrs16 = OFFSETS[d];
@@ -704,6 +684,9 @@ public record SwedishGenerator(int[] buff) {
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,
@@ -721,21 +704,21 @@ public record SwedishGenerator(int[] buff) {
}
static void patternForSlot(Grid grid, Slot s, byte[] pat) {
for (var i = 0; i < s.len(); i++) {
for (int i = 0, len = s.len(); i < len; i++) {
var ch = grid.byteAt(s.pos(i));
pat[i] = isLetter(ch) ? ch : DASH;
}
}
static int slotScore(int[] cellCount, Slot s) {
var cross = 0;
for (var i = 0; i < s.len(); i++) cross += (cellCount[s.pos(i)] - 1);
return cross * 10 + s.len();
static int slotScore(int[] count, Slot s) {
int cross = 0, len = s.len();
for (int i = 0; i < len; i++) cross += (count[s.pos(i)] - 1);
return cross * 10 + len;
}
static int placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) {
int mask = 0, idx;
static boolean placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) {
int mask = 0;
byte cur, ch;
for (var i = 0; i < s.len(); i++) {
for (int i = 0, leng = s.len(), idx; i < leng; i++) {
idx = s.pos(i);
cur = grid.byteAt(idx);
ch = w.byteAt(i);
@@ -748,11 +731,11 @@ public record SwedishGenerator(int[] buff) {
grid.clear(s.pos(j));
}
}
return -1;
return false;
}
}
undoBuffer[offset] = mask;
return 1;
return true;
}
public FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex,
@@ -764,10 +747,10 @@ public record SwedishGenerator(int[] buff) {
var used = new Bit1029();
var assigned = new HashMap<Integer, Lemma>();
var ctx = CTX.get();
var cellCount = ctx.cellCount;
Arrays.fill(cellCount, 0, SIZE, 0);
for (var s : slots) for (var i = 0; i < s.len(); i++) cellCount[s.pos(i)]++;
var ctx = CTX.get();
var count = ctx.cellCount;
Arrays.fill(count, 0, SIZE, 0);
for (var s : slots) for (int i = 0, len = s.len(); i < len; i++) count[s.pos(i)]++;
var t0 = System.currentTimeMillis();
final var lastLog = new AtomicLong(t0);
@@ -799,34 +782,29 @@ public record SwedishGenerator(int[] buff) {
Supplier<Pick> chooseMRV = () -> {
Slot best = null;
CandidateInfo bestInfo = null;
int bestSlot = -1;
for (var s : slots) {
var k = s.key();
if (assigned.containsKey(k)) continue;
if (assigned.containsKey(s.key())) continue;
var entry = dictIndex[s.len()];
if (entry == null) {
return new Pick(null, null, false);
}
var patLen = s.len();
if (entry == null) return PICK_NOT_DONE;
patternForSlot(grid, s, ctx.pattern);
var info = candidateInfoForPattern(ctx, entry, patLen);
if (info.count == 0) {
return new Pick(null, null, false);
}
var info = candidateInfoForPattern(ctx, entry, s.len());
if (info.count == 0) return PICK_NOT_DONE;
var slotScore = -1;
if (best == null
|| info.count < bestInfo.count
|| (info.count == bestInfo.count && slotScore(cellCount, s) > slotScore(cellCount, best))) {
|| (info.count == bestInfo.count && (slotScore = slotScore(count, s)) > bestSlot)) {
best = s;
bestSlot = (slotScore != -1) ? slotScore : slotScore(count, s);
bestInfo = info;
if (info.count <= 1) break;
}
}
if (best == null) {
return new Pick(null, null, true);
return PICK_DONE;
} else {
return new Pick(best, bestInfo, false);
}
@@ -846,8 +824,8 @@ public record SwedishGenerator(int[] buff) {
stats.backtracks++;
return false;
}
stats.lastMRV = pick.info.count;
val info = pick.info;
stats.lastMRV = info.count;
renderProgress.run();
var s = pick.slot;
@@ -856,8 +834,8 @@ public record SwedishGenerator(int[] buff) {
var entry = dictIndex[patLen];
var pat = new byte[patLen];
patternForSlot(grid, s, pat);
if (pick.info.indices != null && pick.info.indices.length > 0) {
var idxs = pick.info.indices;
if (info.indices != null && info.indices.length > 0) {
var idxs = info.indices;
var L = idxs.length;
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
@@ -878,8 +856,7 @@ public record SwedishGenerator(int[] buff) {
}
if (!match) continue;
int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, depth);
if (nPlaced < 0) continue;
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
used.set(w.index());
assigned.put(k, w);
@@ -888,7 +865,7 @@ public record SwedishGenerator(int[] buff) {
assigned.remove(k);
used.clear(w.index);
undoPlace(grid, s, ctx.undoBuffer[depth]);
undoPlace(grid, s, ctx.undo[depth]);
}
stats.backtracks++;
return false;
@@ -917,8 +894,7 @@ public record SwedishGenerator(int[] buff) {
}
if (!match) continue;
int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, depth);
if (nPlaced < 0) continue;
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
used.set(w.index());
assigned.put(k, w);
@@ -927,7 +903,7 @@ public record SwedishGenerator(int[] buff) {
assigned.remove(k);
used.clear(w.index);
undoPlace(grid, s, ctx.undoBuffer[depth]);
undoPlace(grid, s, ctx.undo[depth]);
}
stats.backtracks++;

View File

@@ -264,22 +264,19 @@ public class SwedishGeneratorTest {
var undoBuffer = new int[10];
// 1. Successful placement in empty grid
int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0);
assertEquals(1, placed);
assertTrue(SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0));
assertEquals('A', grid.byteAt(0, 0));
assertEquals('B', grid.byteAt(0, 1));
assertEquals('C', grid.byteAt(0, 2));
assertEquals(0b111L, undoBuffer[0]);
// 2. Successful placement with partial overlap (same characters)
placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 1);
assertEquals(1, placed);
assertTrue(SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 1));
assertEquals(0L, undoBuffer[1]); // 0 new characters placed
// 3. Conflict: place "ABD" where "ABC" is
var w2 = new Lemma("ABD", 1, "conflict");
placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2);
assertEquals(-1, placed);
assertFalse(SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2));
// Verify grid is unchanged (still "ABC")
assertEquals('A', grid.byteAt(0, 0));
assertEquals('B', grid.byteAt(0, 1));
@@ -288,8 +285,7 @@ public class SwedishGeneratorTest {
// 4. Partial placement then conflict (rollback)
grid = SwedishGenerator.makeEmptyGrid();
grid.setCharAt(0, 2, 'X'); // Conflict at the end
placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 3);
assertEquals(-1, placed);
assertFalse(SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 3));
// Verify grid is still empty (except for 'X')
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 0));
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 1));
@@ -306,7 +302,7 @@ public class SwedishGeneratorTest {
var undoBuffer = new int[10];
var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0);
assertEquals(1, placed);
assertTrue(placed);
assertEquals('A', grid.byteAt(0, 1));
assertEquals('Z', grid.byteAt(0, 2));
assertEquals(0b11L, undoBuffer[0]);