introduce bitloops

This commit is contained in:
mike
2026-01-12 22:22:19 +01:00
parent 88a61e6f4d
commit a9b4dfb422
4 changed files with 148 additions and 119 deletions

View File

@@ -11,11 +11,32 @@ import static puzzle.SwedishGenerator.*;
public class SwedishGeneratorTest {
static final byte D_BYTE_2 = 50;
static final byte LETTER_A = (byte) 'A';
static final byte LETTER_B = (byte) 'B';
static final byte LETTER_C = (byte) 'C';
static final byte LETTER_X = (byte) 'X';
static final byte LETTER_Z = (byte) 'Z';
static final byte CLUE_UP = 0;
static final byte CLUE_RIGHT = 1;
static final byte CLUE_DOWN = 2;
static final byte CLUE_LEFT = 3;
static final int OFF_0_0 = Grid.offset(0, 0);
static final int OFF_0_1 = Grid.offset(0, 1);
static final int OFF_0_2 = Grid.offset(0, 2);
static final int OFF_1_1 = Grid.offset(1, 1);
static final int OFF_1_2 = Grid.offset(1, 2);
static final int OFF_2_0 = Grid.offset(2, 0);
static final int OFF_2_3 = Grid.offset(2, 3);
static final int OFF_2_5 = Grid.offset(2, 5);
static final int OFF_3_5 = Grid.offset(3, 5);
static final int OFF_4_5 = Grid.offset(4, 5);
static final byte D_BYTE_2 = CLUE_RIGHT;
@Test
void testPatternForSlotAllLetters() {
var grid = new Grid(new byte[]{ 65, 66, 67 }); // A B C
var slot = Slot.from(18, 7L, 0L);
var grid = new Grid(new byte[]{ LETTER_A, LETTER_B, LETTER_C });
var slot = Slot.from(18 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
long pattern = patternForSlot(grid, slot);
assertEquals(1 | (2 << 5) | (3 << 10), pattern);
@@ -23,8 +44,8 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotMixed() {
var grid = new Grid(new byte[]{ 65, DASH, 67 }); // A - C
var slot = Slot.from(1 << 3 | 2, 7L, 0L);
var grid = new Grid(new byte[]{ LETTER_A, DASH, LETTER_C });
var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
long pattern = patternForSlot(grid, slot);
assertEquals(1 | (0 << 5) | (3 << 10), pattern);
@@ -33,7 +54,7 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotAllDashes() {
var grid = new Grid(new byte[]{ DASH, DASH, DASH }); // - - -
var slot = Slot.from(1 << 3 | 2, 7L, 0L);
var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
long pattern = patternForSlot(grid, slot);
assertEquals(0, pattern);
@@ -41,8 +62,8 @@ public class SwedishGeneratorTest {
@Test
void testPatternForSlotSingleLetter() {
var grid = new Grid(new byte[]{ 65, DASH, DASH }); // A - -
var slot = Slot.from(1 << 3 | 2, 7L, 0L);
var grid = new Grid(new byte[]{ LETTER_A, DASH, DASH });
var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
long pattern = patternForSlot(grid, slot);
assertEquals(1, pattern);
@@ -68,23 +89,23 @@ public class SwedishGeneratorTest {
@Test
void testGrid() {
var grid = Grid.createEmpty();
grid.setLetter(0, (byte) 'A');
grid.setClue(Grid.offset(0, 1), (byte) '1');
grid.setLetter(OFF_0_0, LETTER_A);
grid.setClue(OFF_0_1, CLUE_LEFT);
assertEquals('A', grid.byteAt(0));
assertEquals(1, grid.digitAt(Grid.offset(0, 1)));
assertTrue(grid.notClue(0));
assertFalse(grid.isClue(0));
assertTrue(grid.isClue(Grid.offset(0, 1)));
assertFalse(grid.notClue(Grid.offset(0, 1)));
assertTrue(grid.notClue(0));
assertFalse(grid.notClue(Grid.offset(0, 1)));
assertEquals('A', grid.byteAt(OFF_0_0));
assertEquals(CLUE_LEFT, grid.digitAt(OFF_0_1));
assertTrue(grid.notClue(OFF_0_0));
assertFalse(grid.isClue(OFF_0_0));
assertTrue(grid.isClue(OFF_0_1));
assertFalse(grid.notClue(OFF_0_1));
assertTrue(grid.notClue(OFF_0_0));
assertFalse(grid.notClue(OFF_0_1));
var copy = grid.deepCopyGrid();
assertEquals('A', copy.byteAt(0));
copy.setLetter(0, (byte) 'B');
assertEquals('B', copy.byteAt(0));
assertEquals('A', grid.byteAt(0));
assertEquals('A', copy.byteAt(OFF_0_0));
copy.setLetter(OFF_0_0, LETTER_B);
assertEquals('B', copy.byteAt(OFF_0_0));
assertEquals('A', grid.byteAt(OFF_0_0));
}
@Test
@@ -134,22 +155,22 @@ public class SwedishGeneratorTest {
void testSlot() {
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Slot.BIT_FOR_DIR);
// key = (r << 8) | (c << 4) | d
var offset = Grid.offset(2, 3);
var offset = OFF_2_3;
System.out.println("[DEBUG_LOG] Grid.offset(2, 3) = " + offset);
var key = (offset << Slot.BIT_FOR_DIR) | 3;
var key = (offset << Slot.BIT_FOR_DIR) | (CLUE_DOWN + 1);
System.out.println("[DEBUG_LOG] key = " + key);
long lo = 0;
// pos 0: (2, 5)
lo |= 1L << Grid.offset(2, 5);
lo |= 1L << OFF_2_5;
// pos 1: (3, 5)
lo |= 1L << Grid.offset(3, 5);
lo |= 1L << OFF_3_5;
// pos 2: (4, 5)
lo |= 1L << Grid.offset(4, 5);
lo |= 1L << OFF_4_5;
var s = Slot.from(key, lo, 0L);
System.out.println("[DEBUG_LOG] s.dir() = " + Slot.dir(s.key()));
assertEquals(Grid.offset(2,3), s.clueIndex());
assertEquals(3, Slot.dir(s.key()));
assertEquals(OFF_2_3, s.clueIndex());
assertEquals(CLUE_DOWN + 1, Slot.dir(s.key()));
assertFalse(s.horiz());
var cells = s.walk().toArray();
assertEquals(2, Grid.r(cells[0]));
@@ -159,8 +180,8 @@ public class SwedishGeneratorTest {
assertEquals(5, Grid.c(cells[1]));
assertEquals(5, Grid.c(cells[2]));
assertTrue(Slot.horiz(2)); // right
assertFalse(Slot.horiz(3)); // down
assertTrue(Slot.horiz(CLUE_RIGHT + 1)); // right
assertFalse(Slot.horiz(CLUE_DOWN + 1)); // down
}
static int intersectSorted(int[] a, int aLen, int[] b, int bLen, int[] out) {
if (aLen == 0 || bLen == 0) return 0;
@@ -238,26 +259,17 @@ public class SwedishGeneratorTest {
var gen = new SwedishGenerator(new Rng(0));
var grid = Grid.createEmpty();
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
// Set '2' (right) at 0,0
grid.setClue(0, (byte) '2');
// Set CLUE_RIGHT at OFF_0_0
grid.setClue(OFF_0_0, CLUE_RIGHT);
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
var slots = extractSlots(grid);
// Depending on MAX_WORD_LENGTH and grid size.
// In 3x3, if we have '2' at 0,0, rr=0, cc=1.
// while loop:
// 1. rr=0, cc=1, n=0 -> packedRs |= 0, packedCs |= 1, n=1, rr=0, cc=2
// 2. rr=0, cc=2, n=1 -> packedRs |= 0, packedCs |= 2<<4, n=2, rr=0, cc=3 (out)
// result: Slot with len 2.
assertEquals(1, slots.size());
var s = slots.getFirst();
// MAX_WORD_LENGTH = Math.min(W, H). In tests with -DPUZZLE_ROWS=3 -DPUZZLE_COLS=3, it should be 3.
// However, the test run might be using default Config values if not properly overridden in the test environment.
// If Actual was 8, it means MAX_WORD_LENGTH was at least 8.
assertTrue(s.len() >= 2);
assertEquals(0, s.clueIndex());
assertEquals(2, Slot.dir(s.key()));
assertEquals(OFF_0_0, s.clueIndex());
assertEquals(CLUE_RIGHT + 1, Slot.dir(s.key()));
}
@Test
@@ -273,8 +285,7 @@ public class SwedishGeneratorTest {
assertTrue(f1 >= 1_000_000_000L);
// Add a slot
//var dbyte = OFFSETS[2].dbyte();
grid.setClue(0, D_BYTE_2);
grid.setClue(OFF_0_0, D_BYTE_2);
var f2 = gen.maskFitness(grid);
assertTrue(f2 < f1);
}
@@ -303,18 +314,18 @@ public class SwedishGeneratorTest {
@Test
void testPlaceWord() {
var grid = Grid.createEmpty();
// Slot at (0,0) length 3, horizontal (right)
var key = (Grid.offset(0,0) << Slot.BIT_FOR_DIR) | 2;
var lo = (1L << Grid.offset(0, 0)) | (1L << Grid.offset(0, 1)) | (1L << Grid.offset(0, 2));
// Slot at OFF_0_0 length 3, horizontal (right)
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
var s = Slot.from(key, lo, 0L);
var w1 = Lemma.from("ABC");
var undoBuffer = new long[10];
// 1. Successful placement in empty grid
assertTrue(placeWord(grid, s, w1, undoBuffer, 0));
assertEquals('A', grid.byteAt(0));
assertEquals('B', grid.byteAt(Grid.offset(0, 1)));
assertEquals('C', grid.byteAt(Grid.offset(0, 2)));
assertEquals('A', grid.byteAt(OFF_0_0));
assertEquals('B', grid.byteAt(OFF_0_1));
assertEquals('C', grid.byteAt(OFF_0_2));
assertEquals(lo, undoBuffer[0]);
// 2. Successful placement with partial overlap (same characters)
@@ -325,52 +336,52 @@ 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(Grid.offset(0, 0)));
assertEquals('B', grid.byteAt(Grid.offset(0, 1)));
assertEquals('C', grid.byteAt(Grid.offset(0, 2)));
assertEquals('A', grid.byteAt(OFF_0_0));
assertEquals('B', grid.byteAt(OFF_0_1));
assertEquals('C', grid.byteAt(OFF_0_2));
// 4. Partial placement then conflict (rollback)
grid = Grid.createEmpty();
grid.setLetter(Grid.offset(0, 2), (byte) 'X'); // Conflict at the end
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(Grid.offset(0, 0)));
assertEquals(DASH, grid.byteAt(Grid.offset(0, 1)));
assertEquals('X', grid.byteAt(Grid.offset(0, 2)));
assertEquals(DASH, grid.byteAt(OFF_0_0));
assertEquals(DASH, grid.byteAt(OFF_0_1));
assertEquals('X', grid.byteAt(OFF_0_2));
}
@Test
void testBacktrackingHelpers() {
var grid = Grid.createEmpty();
// Slot at 0,1 length 2
var key = (Grid.offset(0,0) << Slot.BIT_FOR_DIR) | 2;
var lo = (1L << Grid.offset(0, 1)) | (1L << Grid.offset(0, 2));
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
var s = Slot.from(key, lo, 0L);
var w = Lemma.from("AZ");
var undoBuffer = new long[10];
var placed = placeWord(grid, s, w, undoBuffer, 0);
assertTrue(placed);
assertEquals('A', grid.byteAt(Grid.offset(0, 1)));
assertEquals('Z', grid.byteAt(Grid.offset(0, 2)));
assertEquals('A', grid.byteAt(OFF_0_1));
assertEquals('Z', grid.byteAt(OFF_0_2));
assertEquals(lo, undoBuffer[0]);
grid.undoPlace( undoBuffer[0], undoBuffer[1]);
assertEquals(DASH, grid.byteAt(Grid.offset(0, 1)));
assertEquals(DASH, grid.byteAt(Grid.offset(0, 2)));
assertEquals(DASH, grid.byteAt(OFF_0_1));
assertEquals(DASH, grid.byteAt(OFF_0_2));
}
@Test
void testInnerWorkings() {
// 1. Test Slot.increasing
assertFalse(Slot.increasing(1)); // Left
assertTrue(Slot.increasing(2)); // Right
assertTrue(Slot.increasing(3)); // Down
assertFalse(Slot.increasing(4)); // Up
assertFalse(Slot.increasing(CLUE_LEFT + 1)); // Left
assertTrue(Slot.increasing(CLUE_RIGHT + 1)); // Right
assertTrue(Slot.increasing(CLUE_DOWN + 1)); // Down
assertFalse(Slot.increasing(CLUE_UP + 1)); // Up
var sInc = Slot.from((0 << Slot.BIT_FOR_DIR) | 2, 1L, 0L);
var sInc = Slot.from((0 << Slot.BIT_FOR_DIR) | CLUE_RIGHT + 1, 1L, 0L);
assertTrue(sInc.increasing());
var sDec = Slot.from((0 << Slot.BIT_FOR_DIR) | 1, 1L, 0L);
var sDec = Slot.from((0 << Slot.BIT_FOR_DIR) | CLUE_LEFT + 1, 1L, 0L);
assertFalse(sDec.increasing());
// 2. Test slotScore