introduce bitloops
This commit is contained in:
@@ -115,7 +115,7 @@ public record Export() {
|
||||
var sb = new StringBuilder();
|
||||
for (var r = 0; r < R; r++) {
|
||||
if (r > 0) sb.append('\n');
|
||||
for (var c = 0; c < C; c++) sb.append((char) grid.byteAt(Grid.offset(r, c)));
|
||||
for (var c = 0; c < C; c++) sb.append((char) grid.letter32At(Grid.offset(r, c)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -137,9 +137,9 @@ public record Export() {
|
||||
for (var c = 0; c < C; c++) {
|
||||
var offset = Grid.offset(r, c);
|
||||
if (grid.isClue(offset)) {
|
||||
sb.append(clueChar.replace(new Cell(grid, offset, grid.byteAt(offset))));
|
||||
sb.append(clueChar.replace(new Cell(grid, offset, grid.letter32At(offset))));
|
||||
} else {
|
||||
sb.append(NOT_CLUE_NOT_LETTER_TO(grid.byteAt(offset), emptyFallback));
|
||||
sb.append(NOT_CLUE_NOT_LETTER_TO(grid.letter32At(offset), emptyFallback));
|
||||
}
|
||||
}
|
||||
out[r] = sb.toString();
|
||||
@@ -227,7 +227,7 @@ public record Export() {
|
||||
for (var p : placed) {
|
||||
for (var c : p.cells) {
|
||||
if (inBounds(c) && g.notClue(c)) {
|
||||
letterAt.put(c, (char) g.byteAt(c));
|
||||
letterAt.put(c, (char) g.letter32At(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,14 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
//72 << 3;
|
||||
static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1;
|
||||
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
||||
record Pick(Slot slot, int[] indices, int count) { }
|
||||
@AllArgsConstructor
|
||||
static class Pick {
|
||||
|
||||
Slot slot;
|
||||
int[] indices;
|
||||
int count;
|
||||
}
|
||||
|
||||
// 0b11
|
||||
//0b00
|
||||
// 0b01
|
||||
@@ -282,18 +289,14 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
|
||||
public Grid(byte[] g) { this(g, 0, 0); }
|
||||
static Grid createEmpty() { return new Grid(new byte[SIZE], X, X); }
|
||||
int digitAt(int index) { return g[index]; }
|
||||
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); }
|
||||
public byte byteAt(int pos) { return g[pos]; }
|
||||
public byte letter32At(int pos) { return g[pos]; }
|
||||
void setLetter(int idx, byte ch) { g[idx] = ch; }
|
||||
void clearletter(int idx) { g[idx] = DASH; }
|
||||
boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||
boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||
boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||
boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||
|
||||
void undoPlace(long maskLo, long maskHi) {
|
||||
for (long b = maskLo; b != 0; b &= b - 1) clearletter(Long.numberOfTrailingZeros(b));
|
||||
@@ -785,7 +788,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
if (s.increasing()) {
|
||||
for (long b = s.lo; b != 0; b &= b - 1, i++) {
|
||||
int idx = Long.numberOfTrailingZeros(b);
|
||||
byte cur = grid.byteAt(idx), ch = Lemma.byteAt(w, i);
|
||||
byte cur = grid.letter32At(idx), ch = Lemma.byteAt(w, i);
|
||||
if (cur == DASH) {
|
||||
maskLo |= (1L << idx);
|
||||
grid.setLetter(idx, ch);
|
||||
@@ -796,7 +799,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
}
|
||||
for (long b = s.hi; b != 0; b &= b - 1, i++) {
|
||||
int idx = 64 | Long.numberOfTrailingZeros(b);
|
||||
byte cur = grid.byteAt(idx), ch = Lemma.byteAt(w, i);
|
||||
byte cur = grid.letter32At(idx), ch = Lemma.byteAt(w, i);
|
||||
if (cur == DASH) {
|
||||
maskHi |= (1L << (idx & 63));
|
||||
grid.setLetter(idx, ch);
|
||||
@@ -809,7 +812,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
for (long b = s.hi; b != 0; i++) {
|
||||
int msb = 63 - Long.numberOfLeadingZeros(b);
|
||||
int idx = 64 | msb;
|
||||
byte cur = grid.byteAt(idx), ch = Lemma.byteAt(w, i);
|
||||
byte cur = grid.letter32At(idx), ch = Lemma.byteAt(w, i);
|
||||
if (cur == DASH) {
|
||||
maskHi |= (1L << msb);
|
||||
grid.setLetter(idx, ch);
|
||||
@@ -822,7 +825,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
for (long b = s.lo; b != 0; i++) {
|
||||
int msb = 63 - Long.numberOfLeadingZeros(b);
|
||||
int idx = msb;
|
||||
byte cur = grid.byteAt(idx), ch = Lemma.byteAt(w, i);
|
||||
byte cur = grid.letter32At(idx), ch = Lemma.byteAt(w, i);
|
||||
if (cur == DASH) {
|
||||
maskLo |= (1L << msb);
|
||||
grid.setLetter(idx, ch);
|
||||
@@ -928,10 +931,12 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
|
||||
class Solver {
|
||||
|
||||
private final Pick CARRIER = new Pick(null, null, 0);
|
||||
long nodes;
|
||||
long backtracks;
|
||||
int lastMRV;
|
||||
long lastLog = t0;
|
||||
Pick current = CARRIER;
|
||||
void renderProgress() {
|
||||
var now = System.currentTimeMillis();
|
||||
if ((now - lastLog) < LOG_EVERY_MS) return;
|
||||
@@ -953,7 +958,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
System.out.print("\r" + Strings.padRight(msg, 120));
|
||||
System.out.flush();
|
||||
}
|
||||
Pick chooseMRV() {
|
||||
void chooseMRV() {
|
||||
Slot best = null;
|
||||
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) {
|
||||
var s = slots[i];
|
||||
@@ -962,7 +967,10 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
var index = dictIndex[s.length()];
|
||||
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index, s.length());
|
||||
|
||||
if (count == 0) return PICK_NOT_DONE;
|
||||
if (count == 0) {
|
||||
current = PICK_NOT_DONE;
|
||||
return;
|
||||
}
|
||||
if (best == null
|
||||
|| count < count2
|
||||
|| (count == count2 && slotScores[i] > bestScore)) {
|
||||
@@ -973,11 +981,20 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
}
|
||||
}
|
||||
// Re-calculate for the best slot to get actual indices
|
||||
if (best == null) return PICK_DONE;
|
||||
if (best == null) {
|
||||
current = PICK_DONE;
|
||||
return;
|
||||
}
|
||||
var pattern = patternForSlot(grid, best);
|
||||
var index = dictIndex[best.length()];
|
||||
if (pattern == X) return new Pick(best, null, index.length);
|
||||
return new Pick(best, candidateInfoForPattern(bitset, pattern, index, best.length()), index.length);
|
||||
current = CARRIER;
|
||||
current.slot = best;
|
||||
current.count = index.length;
|
||||
if (pattern == X) {
|
||||
current.indices = null;
|
||||
return;
|
||||
}
|
||||
current.indices = candidateInfoForPattern(bitset, pattern, index, best.length());
|
||||
}
|
||||
boolean backtrack(int depth) {
|
||||
if (Thread.currentThread().isInterrupted()) return false;
|
||||
@@ -985,7 +1002,8 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
|
||||
if (20_000 > 0 && (System.currentTimeMillis() - t0) > 20_000) return false;
|
||||
|
||||
var pick = chooseMRV();
|
||||
chooseMRV();
|
||||
var pick = current;
|
||||
if (pick == PICK_DONE) return true;
|
||||
if (pick.slot == null) {
|
||||
backtracks++;
|
||||
|
||||
@@ -90,10 +90,10 @@ public class MainTest {
|
||||
grid.setLetter(OFF_0_0, LETTER_A);
|
||||
grid.setLetter(OFF_2_3, LETTER_Z);
|
||||
|
||||
Assertions.assertEquals(LETTER_A, grid.byteAt(OFF_0_0));
|
||||
Assertions.assertEquals(CLUE_UP, grid.byteAt(OFF_1_2));
|
||||
Assertions.assertEquals(LETTER_Z, grid.byteAt(OFF_2_3));
|
||||
Assertions.assertEquals(DASH, grid.byteAt(OFF_1_1));
|
||||
Assertions.assertEquals(LETTER_A, grid.letter32At(OFF_0_0));
|
||||
Assertions.assertEquals(CLUE_UP, grid.letter32At(OFF_1_2));
|
||||
Assertions.assertEquals(LETTER_Z, grid.letter32At(OFF_2_3));
|
||||
Assertions.assertEquals(DASH, grid.letter32At(OFF_1_1));
|
||||
|
||||
// Test isLetterAt
|
||||
Assertions.assertTrue(grid.notClue(OFF_0_0));
|
||||
@@ -104,7 +104,7 @@ public class MainTest {
|
||||
// Test isDigitAt
|
||||
Assertions.assertFalse(grid.isClue(0));
|
||||
Assertions.assertTrue(grid.isClue(OFF_1_2));
|
||||
Assertions.assertEquals(CLUE_UP, grid.digitAt(OFF_1_2));
|
||||
Assertions.assertEquals(CLUE_UP, clues.digitAt(OFF_1_2));
|
||||
Assertions.assertFalse(grid.isClue(OFF_2_3));
|
||||
Assertions.assertFalse(grid.isClue(OFF_1_1));
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public class SwedishGeneratorTest {
|
||||
void testGrid() {
|
||||
var grid = Grid.createEmpty();
|
||||
grid.setLetter(OFF_0_0, LETTER_A);
|
||||
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||
assertEquals('A', grid.letter32At(OFF_0_0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -311,9 +311,9 @@ public class SwedishGeneratorTest {
|
||||
|
||||
// 1. Successful placement in empty grid
|
||||
assertTrue(placeWord(grid, s, w1, undoBuffer, 0));
|
||||
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||
assertEquals('B', grid.byteAt(OFF_0_1));
|
||||
assertEquals('C', grid.byteAt(OFF_0_2));
|
||||
assertEquals('A', grid.letter32At(OFF_0_0));
|
||||
assertEquals('B', grid.letter32At(OFF_0_1));
|
||||
assertEquals('C', grid.letter32At(OFF_0_2));
|
||||
assertEquals(lo, undoBuffer[0]);
|
||||
|
||||
// 2. Successful placement with partial overlap (same characters)
|
||||
@@ -324,18 +324,18 @@ public class SwedishGeneratorTest {
|
||||
var w2 = Lemma.from("ABD");
|
||||
assertFalse(placeWord(grid, s, w2, undoBuffer, 2));
|
||||
// Verify grid is unchanged (still "ABC")
|
||||
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||
assertEquals('B', grid.byteAt(OFF_0_1));
|
||||
assertEquals('C', grid.byteAt(OFF_0_2));
|
||||
assertEquals('A', grid.letter32At(OFF_0_0));
|
||||
assertEquals('B', grid.letter32At(OFF_0_1));
|
||||
assertEquals('C', grid.letter32At(OFF_0_2));
|
||||
|
||||
// 4. Partial placement then conflict (rollback)
|
||||
grid = Grid.createEmpty();
|
||||
grid.setLetter(OFF_0_2, LETTER_X); // Conflict at the end
|
||||
assertFalse(placeWord(grid, s, w1, undoBuffer, 3));
|
||||
// Verify grid is still empty (except for 'X')
|
||||
assertEquals(DASH, grid.byteAt(OFF_0_0));
|
||||
assertEquals(DASH, grid.byteAt(OFF_0_1));
|
||||
assertEquals('X', grid.byteAt(OFF_0_2));
|
||||
assertEquals(DASH, grid.letter32At(OFF_0_0));
|
||||
assertEquals(DASH, grid.letter32At(OFF_0_1));
|
||||
assertEquals('X', grid.letter32At(OFF_0_2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -350,13 +350,13 @@ public class SwedishGeneratorTest {
|
||||
|
||||
var placed = placeWord(grid, s, w, undoBuffer, 0);
|
||||
assertTrue(placed);
|
||||
assertEquals('A', grid.byteAt(OFF_0_1));
|
||||
assertEquals('Z', grid.byteAt(OFF_0_2));
|
||||
assertEquals('A', grid.letter32At(OFF_0_1));
|
||||
assertEquals('Z', grid.letter32At(OFF_0_2));
|
||||
assertEquals(lo, undoBuffer[0]);
|
||||
|
||||
grid.undoPlace(undoBuffer[0], undoBuffer[1]);
|
||||
assertEquals(DASH, grid.byteAt(OFF_0_1));
|
||||
assertEquals(DASH, grid.byteAt(OFF_0_2));
|
||||
assertEquals(DASH, grid.letter32At(OFF_0_1));
|
||||
assertEquals(DASH, grid.letter32At(OFF_0_2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user