introduce bitloops

This commit is contained in:
mike
2026-01-17 04:57:42 +01:00
parent 3bd7a0f958
commit 0c56fafeaa
7 changed files with 128 additions and 110 deletions

View File

@@ -23,7 +23,7 @@ import static puzzle.Masker.Clues;
import static puzzle.SwedishGenerator.FillStats;
import static puzzle.SwedishGenerator.R;
import static puzzle.Masker.Slot;
import static puzzle.SwedishGenerator.placeWord;
import static puzzle.GridBuilder.placeWord;
import static puzzle.SwedishGeneratorTest.OFF_0_1;
import static puzzle.SwedishGeneratorTest.OFF_0_2;
import static puzzle.SwedishGeneratorTest.OFF_0_3;
@@ -67,7 +67,7 @@ public class ExportFormatTest {
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, 0L, TEST));
var fillResult = new FillResult(true, 0, 0, new FillStats(0, 0));
var fillResult = new FillResult(true, 0, 0, 0, 0, new FillStats());
var puzzleResult = new PuzzleResult(new Clued(clues), grid, new Slotinfo[]{
new Slotinfo(key, lo, 0L, 0, new Assign(TEST), null)
}, fillResult);
@@ -109,7 +109,7 @@ public class ExportFormatTest {
void testExportFormatEmpty() {
var grid = SwedishGeneratorTest.createEmpty();
val clues = Clues.createEmpty();
var fillResult = new FillResult(true, 0, 0, new FillStats(0, 0));
var fillResult = new FillResult(true, 0, 0, 0, 0, new FillStats());
var puzzleResult = new PuzzleResult(new Clued(clues), new Gridded(grid), new Slotinfo[0], fillResult);
var exported = puzzleResult.exportFormatFromFilled(1, new Rewards(0, 0, 0));

View File

@@ -0,0 +1,63 @@
package puzzle;
import puzzle.SwedishGenerator.Grid;
import puzzle.SwedishGenerator.Lemma;
import puzzle.SwedishGenerator.Slotinfo;
import static java.lang.Long.bitCount;
import static java.lang.Long.numberOfTrailingZeros;
public class GridBuilder {
public static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) {
final long glo = grid.lo, ghi = grid.hi;
if (Slotinfo.increasing(key)) {
for (long b = lo & glo; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
if (g[idx] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
}
int bcLo = bitCount(lo);
for (long b = hi & ghi; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
if (g[64 | idx] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false;
}
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != SwedishGenerator.X) {
for (long b = maskLo; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
g[idx] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
}
for (long b = maskHi; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
g[64 | idx] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
}
grid.lo |= maskLo;
grid.hi |= maskHi;
}
} else {
int bcHi = bitCount(hi);
for (long b = hi & ghi; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
if (g[64 | idx] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false;
}
for (long b = lo & glo; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
if (g[idx] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false;
}
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
if ((maskLo | maskHi) != SwedishGenerator.X) {
for (long b = maskHi; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
g[64 | idx] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))));
}
for (long b = maskLo; b != SwedishGenerator.X; b &= b - 1) {
int idx = numberOfTrailingZeros(b);
g[idx] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))));
}
grid.lo |= maskLo;
grid.hi |= maskHi;
}
}
return true;
}
}

View File

@@ -52,7 +52,7 @@ public class MainTest {
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
var grid = new Gridded(clues.toGrid());
val g = grid.grid().g;
placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
GridBuilder.placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
var slots = Masker.extractSlots(clues, dict.index());
assertEquals(1, slots.length);
@@ -102,7 +102,7 @@ public class MainTest {
var grid = new Gridded(clues.toGrid());
// Test set/get
placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ);
GridBuilder.placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ);
val map = grid.stream(clues.c()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
Assertions.assertEquals(LETTER_A, map.get(OFF_1_1));
Assertions.assertEquals(LETTER_Z, map.get(OFF_0_1));
@@ -172,6 +172,17 @@ public class MainTest {
Assertions.assertEquals(RESULT, test, "Found seed changed");
}
@Test
void testFiller2() {
val mask = "1 000000\n" +
"1 \n" +
"1 \n" +
"3 3 \n" +
"3 0 3 \n" +
"3 \n" +
"3 \n" +
"222 3";
}
@Test
void testFiller() {
val rng = new Rng(-343913721);
val mask = new Clues(

View File

@@ -142,7 +142,7 @@ public class SwedishGeneratorTest {
var key = Masker.Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
val clues = Masker.Clues.createEmpty();
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC);
GridBuilder.placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC);
val map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(LETTER_A, map.get(OFF_0_1));
assertEquals(LETTER_B, map.get(OFF_0_2));
@@ -152,8 +152,8 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotMixed() {
var grid = createEmpty();
placeWord(grid, grid.g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
placeWord(grid, grid.g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_2_0, 0, Lemma.from(0, "C"));
GridBuilder.placeWord(grid, grid.g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
GridBuilder.placeWord(grid, grid.g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_2_0, 0, Lemma.from(0, "C"));
var key = Masker.Slot.packSlotKey(OFF_1_0, CLUE_RIGHT);
var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L);
assertEquals(14081L, pattern);
@@ -170,7 +170,7 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotSingleLetter() {
var grid = createEmpty();
placeWord(grid, grid.g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
GridBuilder.placeWord(grid, grid.g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
var key = Masker.Slot.packSlotKey(1, CLUE_RIGHT);
var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L);
assertEquals(1L, pattern);
@@ -196,7 +196,7 @@ public class SwedishGeneratorTest {
@Test
void testGrid() {
var grid = new Gridded(createEmpty());
placeWord(grid.grid(), grid.grid().g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
GridBuilder.placeWord(grid.grid(), grid.grid().g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
val arr = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(1, arr.size());
assertEquals(LETTER_A, arr.get(OFF_0_0));
@@ -342,7 +342,7 @@ public class SwedishGeneratorTest {
var w1 = ABC;
// 1. Successful placement in empty grid
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
var map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(3, map.size());
assertEquals(LETTER_A, map.get(OFF_0_0));
@@ -350,9 +350,9 @@ public class SwedishGeneratorTest {
assertEquals(LETTER_C, map.get(OFF_0_2));
// 2. Successful placement with partial overlap (same characters)
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
// 3. Conflict: place "ABD" where "ABC" is
assertFalse(placeWord(grid.grid(), grid.grid().g, key, lo, hi, ABD));
assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, ABD));
// Verify grid is unchanged (still "ABC")
map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(3, map.size());
@@ -362,8 +362,8 @@ public class SwedishGeneratorTest {
// 4. Partial placement then conflict (rollback)
grid = new Gridded(createEmpty());
placeWord(grid.grid(), grid.grid().g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from(0, "X")); // Conflict at the end
assertFalse(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
GridBuilder.placeWord(grid.grid(), grid.grid().g, Masker.Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from(0, "X")); // Conflict at the end
assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(1, map.size());
assertEquals(LETTER_X, map.get(OFF_0_2));
@@ -378,7 +378,7 @@ public class SwedishGeneratorTest {
var w = AZ;
val low = grid.grid().lo;
val top = grid.grid().hi;
var placed = placeWord(grid.grid(), grid.grid().g, key, lo, 0L, w);
var placed = GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, 0L, w);
assertTrue(placed);
var map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(2, map.size());