introduce bitloops

This commit is contained in:
mike
2026-01-17 04:18:35 +01:00
parent 81ea708345
commit 47b33af09d
8 changed files with 680 additions and 676 deletions

View File

@@ -138,8 +138,8 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotAllLetters() {
var grid = new Gridded(createEmpty());
var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
val clues = Clues.createEmpty();
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);
val map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
@@ -151,9 +151,9 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotMixed() {
var grid = createEmpty();
placeWord(grid, grid.g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
placeWord(grid, grid.g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_2_0, 0, Lemma.from(0, "C"));
var key = Slot.packSlotKey(OFF_1_0, CLUE_RIGHT);
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"));
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);
}
@@ -161,7 +161,7 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotAllDashes() {
var grid = createEmpty();
var key = Slot.packSlotKey(1 << Slot.BIT_FOR_DIR, CLUE_RIGHT);
var key = Masker.Slot.packSlotKey(1 << Masker.Slot.BIT_FOR_DIR, CLUE_RIGHT);
var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L);
assertEquals(0L, pattern);
}
@@ -169,8 +169,8 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotSingleLetter() {
var grid = createEmpty();
placeWord(grid, grid.g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
var key = Slot.packSlotKey(1, CLUE_RIGHT);
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);
}
@@ -195,8 +195,8 @@ public class SwedishGeneratorTest {
@Test
void testGrid() {
var grid = new Gridded(createEmpty());
placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A"));
val arr = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
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));
}
@@ -231,11 +231,11 @@ public class SwedishGeneratorTest {
@Test
void testSlot() {
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Slot.BIT_FOR_DIR);
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Masker.Slot.BIT_FOR_DIR);
// key = (r << 8) | (c << 4) | d
var offset = OFF_2_3;
System.out.println("[DEBUG_LOG] Grid.offset(2, 3) = " + offset);
var key = Slot.packSlotKey(offset, CLUE_DOWN);
var key = Masker.Slot.packSlotKey(offset, CLUE_DOWN);
System.out.println("[DEBUG_LOG] key = " + key);
long lo = 0;
// pos 0: (2, 5)
@@ -245,10 +245,10 @@ public class SwedishGeneratorTest {
// pos 2: (4, 5)
lo |= 1L << OFF_4_5;
System.out.println("[DEBUG_LOG] s.dir() = " + Slot.dir(key));
assertEquals(OFF_2_3, Slot.clueIndex(key));
assertEquals(CLUE_DOWN, Slot.dir(key));
assertFalse(Slot.horiz(key));
System.out.println("[DEBUG_LOG] s.dir() = " + Masker.Slot.dir(key));
assertEquals(OFF_2_3, Masker.Slot.clueIndex(key));
assertEquals(CLUE_DOWN, Masker.Slot.dir(key));
assertFalse(Masker.Slot.horiz(key));
var cells = Gridded.walk((byte) key, lo, 0L).toArray();
assertEquals(2, SwedishGenerator.IT[cells[0]].r());
assertEquals(3, SwedishGenerator.IT[cells[1]].r());
@@ -257,8 +257,8 @@ public class SwedishGeneratorTest {
assertEquals(5, SwedishGenerator.IT[cells[1]].c());
assertEquals(5, SwedishGenerator.IT[cells[2]].c());
assertTrue(Slot.horiz(CLUE_RIGHT)); // right
assertFalse(Slot.horiz(CLUE_DOWN)); // down
assertTrue(Masker.Slot.horiz(CLUE_RIGHT)); // right
assertFalse(Masker.Slot.horiz(CLUE_DOWN)); // down
}
static long packPattern(String s) {
@@ -287,22 +287,22 @@ public class SwedishGeneratorTest {
@Test
void testForEachSlotAndExtractSlots() {
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
var clues = Clues.createEmpty();
var clues = Masker.Clues.createEmpty();
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
var dict = new Dict(WORDS2);
var slots = extractSlots(clues, dict.index());
var slots = Masker.extractSlots(clues, dict.index());
assertEquals(1, slots.length);
var s = slots[0];
assertTrue(Slot.length(s.lo(), s.hi()) >= 2);
assertEquals(OFF_0_0, Slot.clueIndex(s.key()));
assertEquals(CLUE_RIGHT, Slot.dir(s.key()));
assertTrue(Masker.Slot.length(s.lo(), s.hi()) >= 2);
assertEquals(OFF_0_0, Masker.Slot.clueIndex(s.key()));
assertEquals(CLUE_RIGHT, Masker.Slot.dir(s.key()));
}
@Test
void testMaskFitnessBasic() {
var gen = new SwedishGenerator(new Rng(0), new int[STACK_SIZE], Clues.createEmpty());
var grid = Clues.createEmpty();
var gen = new Masker(new Rng(0), new int[STACK_SIZE], Masker.Clues.createEmpty());
var grid = Masker.Clues.createEmpty();
// Empty grid should have high penalty (no slots)
var f1 = gen.maskFitness(grid, 18);
assertTrue(f1 >= 1_000_000_000L);
@@ -316,7 +316,7 @@ public class SwedishGeneratorTest {
@Test
void testGeneticAlgorithmComponents() {
var rng = new Rng(42);
var gen = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
var gen = new Masker(rng, new int[STACK_SIZE], Masker.Clues.createEmpty());
var c1 = new Clued(gen.randomMask(18));
assertNotNull(c1);
@@ -335,14 +335,14 @@ public class SwedishGeneratorTest {
void testPlaceWord() {
var grid = new Gridded(createEmpty());
// Slot at OFF_0_0 length 3, horizontal (right)
var key = Slot.packSlotKey(0, CLUE_RIGHT);
var key = Masker.Slot.packSlotKey(0, CLUE_RIGHT);
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
val hi = 0L;
var w1 = ABC;
// 1. Successful placement in empty grid
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
var map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
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));
assertEquals(LETTER_B, map.get(OFF_0_1));
@@ -353,7 +353,7 @@ public class SwedishGeneratorTest {
// 3. Conflict: place "ABD" where "ABC" is
assertFalse(placeWord(grid.grid(), grid.grid().g, key, lo, hi, ABD));
// Verify grid is unchanged (still "ABC")
map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
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));
assertEquals(LETTER_B, map.get(OFF_0_1));
@@ -361,9 +361,9 @@ public class SwedishGeneratorTest {
// 4. Partial placement then conflict (rollback)
grid = new Gridded(createEmpty());
placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from(0, "X")); // Conflict at the end
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));
map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
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));
}
@@ -372,21 +372,21 @@ public class SwedishGeneratorTest {
void testBacktrackingHelpers() {
var grid = new Gridded(createEmpty());
// Slot at 0,1 length 2
var key = Slot.packSlotKey(0, CLUE_RIGHT);
var key = Masker.Slot.packSlotKey(0, CLUE_RIGHT);
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
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);
assertTrue(placed);
var map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
var map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(2, map.size());
assertEquals(LETTER_A, map.get(OFF_0_1));
assertEquals(LETTER_Z, map.get(OFF_0_2));
grid.grid().hi = top;
grid.grid().lo = low;
map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(0, map.size());
assertEquals(DASH, map.getOrDefault(OFF_0_1, DASH));
assertEquals(DASH, map.getOrDefault(OFF_0_2, DASH));
@@ -395,13 +395,13 @@ public class SwedishGeneratorTest {
@Test
void testInnerWorkings() {
// 1. Test Slot.increasing
assertFalse(Slot.increasing(CLUE_LEFT)); // Left
assertTrue(Slot.increasing(CLUE_RIGHT)); // Right
assertTrue(Slot.increasing(CLUE_DOWN)); // Down
assertFalse(Slot.increasing(CLUE_UP)); // Up
assertFalse(Slotinfo.increasing(CLUE_LEFT)); // Left
assertTrue(Slotinfo.increasing(CLUE_RIGHT)); // Right
assertTrue(Slotinfo.increasing(CLUE_DOWN)); // Down
assertFalse(Slotinfo.increasing(CLUE_UP)); // Up
assertTrue(Slot.increasing(Slot.packSlotKey(0, CLUE_RIGHT)));
assertFalse(Slot.increasing(Slot.packSlotKey(0, CLUE_LEFT)));
assertTrue(Slotinfo.increasing(Masker.Slot.packSlotKey(0, CLUE_RIGHT)));
assertFalse(Slotinfo.increasing(Masker.Slot.packSlotKey(0, CLUE_LEFT)));
// 2. Test slotScore
val counts = new byte[SIZE];
@@ -411,7 +411,7 @@ public class SwedishGeneratorTest {
var entry5 = dict.index()[5];
// cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3
// score = 3 * 10 + len(2) = 32
assertEquals(32, slotScore(counts, (1L << 1) | (1L << 2), 0L));
assertEquals(32, Masker.slotScore(counts, (1L << 1) | (1L << 2), 0L));
// 3. Test candidateCountForPattern
var ctx = Context.get();
@@ -427,8 +427,8 @@ public class SwedishGeneratorTest {
@Test
void testMaskFitnessDetailed() {
var gen = new SwedishGenerator(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
var grid = Clues.createEmpty();
var gen = new Masker(new Rng(42), new int[STACK_SIZE], Masker.Clues.createEmpty());
var grid = Masker.Clues.createEmpty();
// Empty grid: huge penalty
var fitEmpty = gen.maskFitness(grid, 18);
assertTrue(fitEmpty >= 1_000_000_000L);