Gather data
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user