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