Gather data
This commit is contained in:
@@ -172,7 +172,7 @@ public record ExportFormat() {
|
|||||||
var d = s.dir();
|
var d = s.dir();
|
||||||
|
|
||||||
var cells = new int[s.len()];
|
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
|
// Canonicalize: always output right/down
|
||||||
String direction;
|
String direction;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.val;
|
||||||
import puzzle.ExportFormat.Bit;
|
import puzzle.ExportFormat.Bit;
|
||||||
import puzzle.ExportFormat.Bit1029;
|
import puzzle.ExportFormat.Bit1029;
|
||||||
import puzzle.ExportFormat.Gridded;
|
import puzzle.ExportFormat.Gridded;
|
||||||
@@ -105,7 +105,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
Bit seen,
|
Bit seen,
|
||||||
byte[] pattern,
|
byte[] pattern,
|
||||||
IntList[] intListBuffer,
|
IntList[] intListBuffer,
|
||||||
int[] undoBuffer) {
|
int[] undo) {
|
||||||
|
|
||||||
public Context() {
|
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],
|
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); }
|
public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); }
|
||||||
}
|
}
|
||||||
static void undoPlace(Grid grid, Slot s, int mask) {
|
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) {
|
if ((mask & (1L << i)) != 0) {
|
||||||
grid.clear(s.pos(i));
|
grid.clear(s.pos(i));
|
||||||
}
|
}
|
||||||
@@ -404,7 +404,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
grid.forEachSetBit71(idx -> {
|
grid.forEachSetBit71(idx -> {
|
||||||
var d = grid.digitAt(idx);
|
var d = grid.digitAt(idx);
|
||||||
var nbrs16 = OFFSETS[d];
|
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;
|
if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) return;
|
||||||
long packedPos = 0;
|
long packedPos = 0;
|
||||||
var n = 0;
|
var n = 0;
|
||||||
@@ -419,26 +419,6 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
visitor.visit((r << 8) | (c << 4) | d, packedPos, n);
|
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) {
|
ArrayList<Slot> extractSlots(Grid grid) {
|
||||||
@@ -470,7 +450,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
Arrays.fill(covH, 0, SIZE, 0);
|
Arrays.fill(covH, 0, SIZE, 0);
|
||||||
Arrays.fill(covV, 0, SIZE, 0);
|
Arrays.fill(covV, 0, SIZE, 0);
|
||||||
|
|
||||||
boolean[] hasSlots = {false};
|
boolean[] hasSlots = { false };
|
||||||
grid.forEachSetBit71(clueIdx -> {
|
grid.forEachSetBit71(clueIdx -> {
|
||||||
var d = grid.digitAt(clueIdx);
|
var d = grid.digitAt(clueIdx);
|
||||||
var nbrs16 = OFFSETS[d];
|
var nbrs16 = OFFSETS[d];
|
||||||
@@ -704,6 +684,9 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
|
|
||||||
record Pick(Slot slot, CandidateInfo info, boolean done) { }
|
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,
|
public static record FillResult(boolean ok,
|
||||||
Gridded grid,
|
Gridded grid,
|
||||||
HashMap<Integer, Lemma> clueMap,
|
HashMap<Integer, Lemma> clueMap,
|
||||||
@@ -721,21 +704,21 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void patternForSlot(Grid grid, Slot s, byte[] pat) {
|
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));
|
var ch = grid.byteAt(s.pos(i));
|
||||||
pat[i] = isLetter(ch) ? ch : DASH;
|
pat[i] = isLetter(ch) ? ch : DASH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int slotScore(int[] cellCount, Slot s) {
|
static int slotScore(int[] count, Slot s) {
|
||||||
var cross = 0;
|
int cross = 0, len = s.len();
|
||||||
for (var i = 0; i < s.len(); i++) cross += (cellCount[s.pos(i)] - 1);
|
for (int i = 0; i < len; i++) cross += (count[s.pos(i)] - 1);
|
||||||
return cross * 10 + s.len();
|
return cross * 10 + len;
|
||||||
}
|
}
|
||||||
static int placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) {
|
static boolean placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) {
|
||||||
int mask = 0, idx;
|
int mask = 0;
|
||||||
byte cur, ch;
|
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);
|
idx = s.pos(i);
|
||||||
cur = grid.byteAt(idx);
|
cur = grid.byteAt(idx);
|
||||||
ch = w.byteAt(i);
|
ch = w.byteAt(i);
|
||||||
@@ -748,11 +731,11 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
grid.clear(s.pos(j));
|
grid.clear(s.pos(j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
undoBuffer[offset] = mask;
|
undoBuffer[offset] = mask;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex,
|
public FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex,
|
||||||
@@ -764,10 +747,10 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
var used = new Bit1029();
|
var used = new Bit1029();
|
||||||
var assigned = new HashMap<Integer, Lemma>();
|
var assigned = new HashMap<Integer, Lemma>();
|
||||||
|
|
||||||
var ctx = CTX.get();
|
var ctx = CTX.get();
|
||||||
var cellCount = ctx.cellCount;
|
var count = ctx.cellCount;
|
||||||
Arrays.fill(cellCount, 0, SIZE, 0);
|
Arrays.fill(count, 0, SIZE, 0);
|
||||||
for (var s : slots) for (var i = 0; i < s.len(); i++) cellCount[s.pos(i)]++;
|
for (var s : slots) for (int i = 0, len = s.len(); i < len; i++) count[s.pos(i)]++;
|
||||||
|
|
||||||
var t0 = System.currentTimeMillis();
|
var t0 = System.currentTimeMillis();
|
||||||
final var lastLog = new AtomicLong(t0);
|
final var lastLog = new AtomicLong(t0);
|
||||||
@@ -799,34 +782,29 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
Supplier<Pick> chooseMRV = () -> {
|
Supplier<Pick> chooseMRV = () -> {
|
||||||
Slot best = null;
|
Slot best = null;
|
||||||
CandidateInfo bestInfo = null;
|
CandidateInfo bestInfo = null;
|
||||||
|
int bestSlot = -1;
|
||||||
for (var s : slots) {
|
for (var s : slots) {
|
||||||
var k = s.key();
|
if (assigned.containsKey(s.key())) continue;
|
||||||
if (assigned.containsKey(k)) continue;
|
|
||||||
|
|
||||||
var entry = dictIndex[s.len()];
|
var entry = dictIndex[s.len()];
|
||||||
if (entry == null) {
|
if (entry == null) return PICK_NOT_DONE;
|
||||||
return new Pick(null, null, false);
|
|
||||||
}
|
|
||||||
var patLen = s.len();
|
|
||||||
patternForSlot(grid, s, ctx.pattern);
|
patternForSlot(grid, s, ctx.pattern);
|
||||||
var info = candidateInfoForPattern(ctx, entry, patLen);
|
var info = candidateInfoForPattern(ctx, entry, s.len());
|
||||||
|
|
||||||
if (info.count == 0) {
|
|
||||||
return new Pick(null, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (info.count == 0) return PICK_NOT_DONE;
|
||||||
|
var slotScore = -1;
|
||||||
if (best == null
|
if (best == null
|
||||||
|| info.count < bestInfo.count
|
|| 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;
|
best = s;
|
||||||
|
bestSlot = (slotScore != -1) ? slotScore : slotScore(count, s);
|
||||||
bestInfo = info;
|
bestInfo = info;
|
||||||
if (info.count <= 1) break;
|
if (info.count <= 1) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best == null) {
|
if (best == null) {
|
||||||
return new Pick(null, null, true);
|
return PICK_DONE;
|
||||||
} else {
|
} else {
|
||||||
return new Pick(best, bestInfo, false);
|
return new Pick(best, bestInfo, false);
|
||||||
}
|
}
|
||||||
@@ -846,8 +824,8 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
stats.backtracks++;
|
stats.backtracks++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
val info = pick.info;
|
||||||
stats.lastMRV = pick.info.count;
|
stats.lastMRV = info.count;
|
||||||
renderProgress.run();
|
renderProgress.run();
|
||||||
|
|
||||||
var s = pick.slot;
|
var s = pick.slot;
|
||||||
@@ -856,8 +834,8 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
var entry = dictIndex[patLen];
|
var entry = dictIndex[patLen];
|
||||||
var pat = new byte[patLen];
|
var pat = new byte[patLen];
|
||||||
patternForSlot(grid, s, pat);
|
patternForSlot(grid, s, pat);
|
||||||
if (pick.info.indices != null && pick.info.indices.length > 0) {
|
if (info.indices != null && info.indices.length > 0) {
|
||||||
var idxs = pick.info.indices;
|
var idxs = info.indices;
|
||||||
var L = idxs.length;
|
var L = idxs.length;
|
||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
||||||
|
|
||||||
@@ -878,8 +856,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
}
|
}
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
|
|
||||||
int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, depth);
|
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||||
if (nPlaced < 0) continue;
|
|
||||||
|
|
||||||
used.set(w.index());
|
used.set(w.index());
|
||||||
assigned.put(k, w);
|
assigned.put(k, w);
|
||||||
@@ -888,7 +865,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
|
|
||||||
assigned.remove(k);
|
assigned.remove(k);
|
||||||
used.clear(w.index);
|
used.clear(w.index);
|
||||||
undoPlace(grid, s, ctx.undoBuffer[depth]);
|
undoPlace(grid, s, ctx.undo[depth]);
|
||||||
}
|
}
|
||||||
stats.backtracks++;
|
stats.backtracks++;
|
||||||
return false;
|
return false;
|
||||||
@@ -917,8 +894,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
}
|
}
|
||||||
if (!match) continue;
|
if (!match) continue;
|
||||||
|
|
||||||
int nPlaced = placeWord(grid, s, w, ctx.undoBuffer, depth);
|
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||||
if (nPlaced < 0) continue;
|
|
||||||
|
|
||||||
used.set(w.index());
|
used.set(w.index());
|
||||||
assigned.put(k, w);
|
assigned.put(k, w);
|
||||||
@@ -927,7 +903,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
|
|
||||||
assigned.remove(k);
|
assigned.remove(k);
|
||||||
used.clear(w.index);
|
used.clear(w.index);
|
||||||
undoPlace(grid, s, ctx.undoBuffer[depth]);
|
undoPlace(grid, s, ctx.undo[depth]);
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.backtracks++;
|
stats.backtracks++;
|
||||||
|
|||||||
@@ -264,22 +264,19 @@ public class SwedishGeneratorTest {
|
|||||||
var undoBuffer = new int[10];
|
var undoBuffer = new int[10];
|
||||||
|
|
||||||
// 1. Successful placement in empty grid
|
// 1. Successful placement in empty grid
|
||||||
int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0);
|
assertTrue(SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0));
|
||||||
assertEquals(1, placed);
|
|
||||||
assertEquals('A', grid.byteAt(0, 0));
|
assertEquals('A', grid.byteAt(0, 0));
|
||||||
assertEquals('B', grid.byteAt(0, 1));
|
assertEquals('B', grid.byteAt(0, 1));
|
||||||
assertEquals('C', grid.byteAt(0, 2));
|
assertEquals('C', grid.byteAt(0, 2));
|
||||||
assertEquals(0b111L, undoBuffer[0]);
|
assertEquals(0b111L, undoBuffer[0]);
|
||||||
|
|
||||||
// 2. Successful placement with partial overlap (same characters)
|
// 2. Successful placement with partial overlap (same characters)
|
||||||
placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 1);
|
assertTrue(SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 1));
|
||||||
assertEquals(1, placed);
|
|
||||||
assertEquals(0L, undoBuffer[1]); // 0 new characters placed
|
assertEquals(0L, undoBuffer[1]); // 0 new characters placed
|
||||||
|
|
||||||
// 3. Conflict: place "ABD" where "ABC" is
|
// 3. Conflict: place "ABD" where "ABC" is
|
||||||
var w2 = new Lemma("ABD", 1, "conflict");
|
var w2 = new Lemma("ABD", 1, "conflict");
|
||||||
placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2);
|
assertFalse(SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2));
|
||||||
assertEquals(-1, placed);
|
|
||||||
// Verify grid is unchanged (still "ABC")
|
// Verify grid is unchanged (still "ABC")
|
||||||
assertEquals('A', grid.byteAt(0, 0));
|
assertEquals('A', grid.byteAt(0, 0));
|
||||||
assertEquals('B', grid.byteAt(0, 1));
|
assertEquals('B', grid.byteAt(0, 1));
|
||||||
@@ -288,8 +285,7 @@ public class SwedishGeneratorTest {
|
|||||||
// 4. Partial placement then conflict (rollback)
|
// 4. Partial placement then conflict (rollback)
|
||||||
grid = SwedishGenerator.makeEmptyGrid();
|
grid = SwedishGenerator.makeEmptyGrid();
|
||||||
grid.setCharAt(0, 2, 'X'); // Conflict at the end
|
grid.setCharAt(0, 2, 'X'); // Conflict at the end
|
||||||
placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 3);
|
assertFalse(SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 3));
|
||||||
assertEquals(-1, placed);
|
|
||||||
// Verify grid is still empty (except for 'X')
|
// Verify grid is still empty (except for 'X')
|
||||||
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 0));
|
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 0));
|
||||||
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 1));
|
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 1));
|
||||||
@@ -306,7 +302,7 @@ public class SwedishGeneratorTest {
|
|||||||
var undoBuffer = new int[10];
|
var undoBuffer = new int[10];
|
||||||
|
|
||||||
var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0);
|
var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0);
|
||||||
assertEquals(1, placed);
|
assertTrue(placed);
|
||||||
assertEquals('A', grid.byteAt(0, 1));
|
assertEquals('A', grid.byteAt(0, 1));
|
||||||
assertEquals('Z', grid.byteAt(0, 2));
|
assertEquals('Z', grid.byteAt(0, 2));
|
||||||
assertEquals(0b11L, undoBuffer[0]);
|
assertEquals(0b11L, undoBuffer[0]);
|
||||||
|
|||||||
Reference in New Issue
Block a user