introduce bitloops

This commit is contained in:
mike
2026-01-14 23:15:52 +01:00
parent 5849f543c5
commit 04e3844732
6 changed files with 254 additions and 207 deletions

View File

@@ -5,8 +5,10 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import puzzle.Export.Gridded;
import puzzle.Export.IntListDTO;
import puzzle.Export.LetterVisit.LetterAt;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
import static puzzle.SwedishGenerator.*;
@@ -54,6 +56,7 @@ public class SwedishGeneratorTest {
static final long ABC = Lemma.from(26, "ABC");
static final long ABD = Lemma.from(27, "ABD");
static final long AZ = Lemma.from(28, "AZ");
static final long AB = Lemma.from(29, "AB");
static final byte LETTER_A = ((byte) 'A') & 31;
static final byte LETTER_B = ((byte) 'B') & 31;
static final byte LETTER_C = ((byte) 'C') & 31;
@@ -64,13 +67,18 @@ public class SwedishGeneratorTest {
static final byte CLUE_UP = 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_0 = Grid.offset(1, 0);
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_1 = Grid.offset(2, 1);
static final int OFF_2_3 = Grid.offset(2, 3);
static final int OFF_0_0 = Grid.offset(0, 0);
static final int OFF_0_4 = Grid.offset(0, 4);
static final int OFF_0_5 = Grid.offset(0, 5);
static final int OFF_0_1 = Grid.offset(0, 1);
static final int OFF_0_2 = Grid.offset(0, 2);
static final int OFF_0_3 = Grid.offset(0, 3);
static final int OFF_2_0 = Grid.offset(2, 0);
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);
@@ -78,21 +86,23 @@ public class SwedishGeneratorTest {
static final byte D_BYTE_2 = CLUE_RIGHT;
@Test
void testPatternForSlotAllLetters() {
var grid = createEmpty();
grid.setLetterLo(0, LETTER_A);
grid.setLetterLo(1, LETTER_B);
grid.setLetterLo(2, LETTER_C);
var key = Slot.packSlotKey(18, CLUE_RIGHT);
var pattern = patternForSlot(grid, key, 7L, 0L);
assertEquals(1L | (28L << 8) | (55L << 16), pattern);
var grid = new Gridded(createEmpty());
var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
val clues = Clues.createEmpty();
clues.setClue(OFF_0_0, CLUE_RIGHT);
placeWord(grid.grid(), 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));
assertEquals(LETTER_C, map.get(OFF_0_3));
}
@Test
void testPatternForSlotMixed() {
var grid = createEmpty();
grid.setLetterLo(OFF_0_0, LETTER_A);
grid.setLetterLo(2, LETTER_C);
var key = Slot.packSlotKey(1, CLUE_RIGHT);
grid.setLetterLo(OFF_2_0, LETTER_C);
var key = Slot.packSlotKey(OFF_1_0, CLUE_RIGHT);
var pattern = patternForSlot(grid, key, 7L, 0L);
assertEquals(1L | (0L) | (55L << 16), pattern);
}
@@ -133,9 +143,11 @@ public class SwedishGeneratorTest {
@Test
void testGrid() {
var grid = createEmpty();
grid.setLetterLo(OFF_0_0, LETTER_A);
assertEquals(LETTER_A, grid.letter32At(OFF_0_0));
var grid = new Gridded(createEmpty());
grid.grid().setLetterLo(OFF_0_0, LETTER_A);
val arr = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(1, arr.size());
assertEquals(LETTER_A, arr.get(OFF_0_0));
}
@Test
@@ -270,7 +282,7 @@ public class SwedishGeneratorTest {
@Test
void testPlaceWord() {
var grid = createEmpty();
var grid = new Gridded(createEmpty());
// Slot at OFF_0_0 length 3, horizontal (right)
var key = Slot.packSlotKey(0, CLUE_RIGHT);
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
@@ -278,51 +290,55 @@ public class SwedishGeneratorTest {
var w1 = ABC;
// 1. Successful placement in empty grid
assertTrue(placeWord(grid, key, lo, hi, w1));
assertEquals(LETTER_A, grid.letter32At(OFF_0_0));
assertEquals(LETTER_B, grid.letter32At(OFF_0_1));
assertEquals(LETTER_C, grid.letter32At(OFF_0_2));
assertTrue(placeWord(grid.grid(), key, lo, hi, w1));
var map = grid.stream(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));
assertEquals(LETTER_C, map.get(OFF_0_2));
// 2. Successful placement with partial overlap (same characters)
assertTrue(placeWord(grid, key, lo, hi, w1));
assertTrue(placeWord(grid.grid(), key, lo, hi, w1));
// 3. Conflict: place "ABD" where "ABC" is
var w2 = ABD;
assertFalse(placeWord(grid, key, lo, hi, w2));
assertFalse(placeWord(grid.grid(), key, lo, hi, ABD));
// Verify grid is unchanged (still "ABC")
assertEquals(LETTER_A, grid.letter32At(OFF_0_0));
assertEquals(LETTER_B, grid.letter32At(OFF_0_1));
assertEquals(LETTER_C, grid.letter32At(OFF_0_2));
map = grid.stream(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));
assertEquals(LETTER_C, map.get(OFF_0_2));
// 4. Partial placement then conflict (rollback)
grid = createEmpty();
grid.setLetterLo(OFF_0_2, LETTER_X); // Conflict at the end
assertFalse(placeWord(grid, key, lo, hi, w1));
// Verify grid is still empty (except for 'X')
assertFalse(grid.lisLetterAtLo(OFF_0_0));
assertFalse(grid.lisLetterAtLo(OFF_0_1));
assertEquals(LETTER_X, grid.letter32At(OFF_0_2));
grid = new Gridded(createEmpty());
grid.grid().setLetterLo(OFF_0_2, LETTER_X); // Conflict at the end
assertFalse(placeWord(grid.grid(), key, lo, hi, w1));
map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
assertEquals(1, map.size());
assertEquals(LETTER_X, map.get(OFF_0_2));
}
@Test
void testBacktrackingHelpers() {
var grid = createEmpty();
var grid = new Gridded(createEmpty());
// Slot at 0,1 length 2
var key = Slot.packSlotKey(0, CLUE_RIGHT);
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
var w = AZ;
val low = grid.lo;
val top = grid.hi;
var placed = placeWord(grid, key, lo, 0L, w);
val low = grid.grid().lo;
val top = grid.grid().hi;
var placed = placeWord(grid.grid(), key, lo, 0L, w);
assertTrue(placed);
assertEquals(LETTER_A, grid.letter32At(OFF_0_1));
assertEquals(LETTER_Z, grid.letter32At(OFF_0_2));
var map = grid.stream(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.hi = top;
grid.lo = low;
//grid.undoPlace(undoBuffer[0], undoBuffer[1]);
assertFalse(grid.lisLetterAtLo(OFF_0_1));
assertFalse(grid.lisLetterAtLo(OFF_0_2));
grid.grid().hi = top;
grid.grid().lo = low;
map = grid.stream(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));
}
@Test