introduce bitloops
This commit is contained in:
@@ -166,7 +166,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
this.hi = hi;
|
this.hi = hi;
|
||||||
}
|
}
|
||||||
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] & 7; }
|
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); }
|
||||||
@@ -326,9 +326,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void processSlot(Grid grid, SlotVisitor visitor, int idx) {
|
private static void processSlot(Grid grid, SlotVisitor visitor, int idx) {
|
||||||
int d = grid.digitAt(idx); // 1..4
|
int d = grid.digitAt(idx); // 0..3
|
||||||
int di = d - 1; // 0..3
|
int key = (idx << 2) | d;
|
||||||
int key = (idx << 2) | di;
|
|
||||||
|
|
||||||
long rayLo = PATH_LO[key];
|
long rayLo = PATH_LO[key];
|
||||||
long rayHi = PATH_HI[key];
|
long rayHi = PATH_HI[key];
|
||||||
@@ -339,7 +338,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
|
|
||||||
// slice ray to stop before first clue, depending on direction monotonicity
|
// slice ray to stop before first clue, depending on direction monotonicity
|
||||||
// right/down => increasing indices; up/left => decreasing indices
|
// right/down => increasing indices; up/left => decreasing indices
|
||||||
boolean increasing = Slot.increasing(d);
|
boolean increasing = Slot.increasing(d + 1);
|
||||||
|
|
||||||
if (increasing) {
|
if (increasing) {
|
||||||
// first clue is lowest index among hits (lo first, then hi)
|
// first clue is lowest index among hits (lo first, then hi)
|
||||||
@@ -367,7 +366,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((rayLo | rayHi) != 0) {
|
if ((rayLo | rayHi) != 0) {
|
||||||
visitor.visit(Slot.packSlotDir(idx, d), rayLo, rayHi);
|
visitor.visit(Slot.packSlotDir(idx, d + 1), rayLo, rayHi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,11 +390,10 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
||||||
int clueIdx = i | Long.numberOfTrailingZeros(bits);
|
int clueIdx = i | Long.numberOfTrailingZeros(bits);
|
||||||
int d = grid.digitAt(clueIdx);
|
int d = grid.digitAt(clueIdx);
|
||||||
int di = d - 1;
|
int key = (clueIdx << 2) | d;
|
||||||
int key = (clueIdx << 2) | di;
|
|
||||||
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
||||||
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
||||||
if (Slot.increasing(d)) {
|
if (Slot.increasing(d + 1)) {
|
||||||
if (hLo != 0) {
|
if (hLo != 0) {
|
||||||
rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1);
|
rLo &= ((1L << Long.numberOfTrailingZeros(hLo)) - 1);
|
||||||
rHi = 0;
|
rHi = 0;
|
||||||
@@ -412,7 +410,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
if ((rLo | rHi) != 0) {
|
if ((rLo | rHi) != 0) {
|
||||||
hasSlots = true;
|
hasSlots = true;
|
||||||
if (Slot.horiz(d)) covH.or(rLo, rHi);
|
if (Slot.horiz(d + 1)) covH.or(rLo, rHi);
|
||||||
else covV.or(rLo, rHi);
|
else covV.or(rLo, rHi);
|
||||||
if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000;
|
if ((Long.bitCount(rLo) + Long.bitCount(rHi)) < MIN_LEN) penalty += 8000;
|
||||||
} else {
|
} else {
|
||||||
@@ -508,7 +506,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
if (g.isClue(idx)) continue;
|
if (g.isClue(idx)) continue;
|
||||||
int d_idx = rng.randint2bit();
|
int d_idx = rng.randint2bit();
|
||||||
if (g.hasRoomForClue(OFFSETS_D_IDX[d_idx | idx << 2])) {
|
if (g.hasRoomForClue(OFFSETS_D_IDX[d_idx | idx << 2])) {
|
||||||
g.setClue(idx, (byte) (1 + (d_idx | 48)));
|
g.setClue(idx, (byte) d_idx);
|
||||||
placed++;
|
placed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,7 +521,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
if (!g.clueless(ri)) {
|
if (!g.clueless(ri)) {
|
||||||
int d_idx = rng.randint2bit();
|
int d_idx = rng.randint2bit();
|
||||||
val packed = OFFSETS_D_IDX[d_idx | ri << 2];
|
val packed = OFFSETS_D_IDX[d_idx | ri << 2];
|
||||||
if (g.hasRoomForClue(packed)) g.setClue(ri, (byte) (1 + (d_idx | 48)));
|
if (g.hasRoomForClue(packed)) g.setClue(ri, (byte) d_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
@@ -557,7 +555,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearClues(out, 64 + Long.numberOfTrailingZeros(hi));
|
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearClues(out, 64 + Long.numberOfTrailingZeros(hi));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
public static void clearClues(Grid out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[(out.digitAt(idx) - 1) | (idx << 2)])) out.clearClue(idx); }
|
public static void clearClues(Grid out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[(out.digitAt(idx) ) | (idx << 2)])) out.clearClue(idx); }
|
||||||
|
|
||||||
Grid hillclimb(Grid start, int limit) {
|
Grid hillclimb(Grid start, int limit) {
|
||||||
var best = start;
|
var best = start;
|
||||||
|
|||||||
@@ -19,18 +19,23 @@ import static puzzle.SwedishGenerator.*;
|
|||||||
|
|
||||||
public class ExportFormatTest {
|
public class ExportFormatTest {
|
||||||
|
|
||||||
|
static final byte CLUE_UP = 0;
|
||||||
|
static final byte CLUE_RIGHT = 1;
|
||||||
|
static final byte CLUE_DOWN = 2;
|
||||||
|
static final byte CLUE_LEFT = 3;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExportFormatFromFilled() {
|
void testExportFormatFromFilled() {
|
||||||
var swe = new SwedishGenerator(new Rng(0));
|
var swe = new SwedishGenerator(new Rng(0));
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
|
|
||||||
// Place a '2' (right) at (0,0)
|
// Place a RIGHT clue at (0,0)
|
||||||
grid.setClue(0, (byte) '2');
|
grid.setClue(0, CLUE_RIGHT);
|
||||||
// This creates a slot starting at (0,1)
|
// This creates a slot starting at (0,1)
|
||||||
|
|
||||||
var clueMap = new HashMap<Integer, Long>();
|
var clueMap = new HashMap<Integer, Long>();
|
||||||
// key = (cellIndex << 4) | direction
|
// key = (cellIndex << 3) | (direction + 1)
|
||||||
var key = 2;
|
var key = (0 << 3) | (CLUE_RIGHT + 1);
|
||||||
clueMap.put(key, Lemma.from("TEST"));
|
clueMap.put(key, Lemma.from("TEST"));
|
||||||
|
|
||||||
// Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4)
|
// Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4)
|
||||||
@@ -38,8 +43,8 @@ public class ExportFormatTest {
|
|||||||
grid.setLetter(Grid.offset(0, 2), (byte) 'E');
|
grid.setLetter(Grid.offset(0, 2), (byte) 'E');
|
||||||
grid.setLetter(Grid.offset(0, 3), (byte) 'S');
|
grid.setLetter(Grid.offset(0, 3), (byte) 'S');
|
||||||
grid.setLetter(Grid.offset(0, 4), (byte) 'T');
|
grid.setLetter(Grid.offset(0, 4), (byte) 'T');
|
||||||
// Terminate thGrid.offset(e slot at) (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH
|
// Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH
|
||||||
grid.setClue(Grid.offset(0, 5), (byte) '1');
|
grid.setClue(Grid.offset(0, 5), CLUE_UP);
|
||||||
|
|
||||||
var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats());
|
var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats());
|
||||||
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
||||||
|
|||||||
@@ -16,15 +16,30 @@ import static puzzle.SwedishGenerator.DASH;
|
|||||||
|
|
||||||
public class MainTest {
|
public class MainTest {
|
||||||
|
|
||||||
|
static final byte LETTER_A = (byte) 'A';
|
||||||
|
static final byte LETTER_B = (byte) 'B';
|
||||||
|
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_3 = Grid.offset(2, 3);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractSlots() {
|
void testExtractSlots() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
|
|
||||||
// Set up digits on the grid to create slots.
|
// Set up digits on the grid to create slots.
|
||||||
// '2' (right) at (0,0) -> slot at (0,1), (0,2)
|
// CLUE_RIGHT at OFF_0_0 -> slot at (0,1), (0,2)
|
||||||
grid.setClue(0, (byte) '2');
|
grid.setClue(OFF_0_0, CLUE_RIGHT);
|
||||||
grid.setLetter(Grid.offset(0, 1), (byte) 'A');
|
grid.setLetter(OFF_0_1, LETTER_A);
|
||||||
grid.setLetter(Grid.offset(0, 2), (byte) 'B');
|
grid.setLetter(OFF_0_2, LETTER_B);
|
||||||
|
|
||||||
var slots = extractSlots(grid);
|
var slots = extractSlots(grid);
|
||||||
assertEquals(1, slots.size());
|
assertEquals(1, slots.size());
|
||||||
@@ -49,7 +64,7 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
void testForEachSlot() {
|
void testForEachSlot() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
grid.setClue(0, (byte) '2'); // right
|
grid.setClue(OFF_0_0, CLUE_RIGHT); // right
|
||||||
|
|
||||||
var count = new AtomicInteger(0);
|
var count = new AtomicInteger(0);
|
||||||
grid.forEachSlot((key, lo, hi) -> {
|
grid.forEachSlot((key, lo, hi) -> {
|
||||||
@@ -72,32 +87,32 @@ public class MainTest {
|
|||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
|
|
||||||
// Test set/get
|
// Test set/get
|
||||||
grid.setLetter(Grid.offset(0, 0), (byte) 'A');
|
grid.setLetter(OFF_0_0, LETTER_A);
|
||||||
grid.setClue(Grid.offset(1, 2), (byte) '3');
|
grid.setClue(OFF_1_2, CLUE_UP);
|
||||||
grid.setLetter(Grid.offset(2, 3), (byte) 'Z');
|
grid.setLetter(OFF_2_3, LETTER_Z);
|
||||||
|
|
||||||
Assertions.assertEquals((byte) 'A', grid.byteAt(Grid.offset(0, 0)));
|
Assertions.assertEquals(LETTER_A, grid.byteAt(OFF_0_0));
|
||||||
Assertions.assertEquals((byte) '3', grid.byteAt(Grid.offset(1, 2)));
|
Assertions.assertEquals(CLUE_UP, grid.byteAt(OFF_1_2));
|
||||||
Assertions.assertEquals((byte) 'Z', grid.byteAt(Grid.offset(2, 3)));
|
Assertions.assertEquals(LETTER_Z, grid.byteAt(OFF_2_3));
|
||||||
Assertions.assertEquals(DASH, grid.byteAt(Grid.offset(1, 1)));
|
Assertions.assertEquals(DASH, grid.byteAt(OFF_1_1));
|
||||||
|
|
||||||
// Test isLetterAt
|
// Test isLetterAt
|
||||||
Assertions.assertTrue(grid.notClue(Grid.offset(0, 0)));
|
Assertions.assertTrue(grid.notClue(OFF_0_0));
|
||||||
Assertions.assertFalse(grid.notClue(Grid.offset(1, 2)));
|
Assertions.assertFalse(grid.notClue(OFF_1_2));
|
||||||
Assertions.assertTrue(grid.notClue(Grid.offset(2, 3)));
|
Assertions.assertTrue(grid.notClue(OFF_2_3));
|
||||||
Assertions.assertFalse(grid.isClue(Grid.offset(1, 1)));
|
Assertions.assertFalse(grid.isClue(OFF_1_1));
|
||||||
|
|
||||||
// Test isDigitAt
|
// Test isDigitAt
|
||||||
Assertions.assertFalse(grid.isClue(0));
|
Assertions.assertFalse(grid.isClue(0));
|
||||||
Assertions.assertTrue(grid.isClue(Grid.offset(1, 2)));
|
Assertions.assertTrue(grid.isClue(OFF_1_2));
|
||||||
Assertions.assertEquals(3, grid.digitAt(Grid.offset(1, 2)));
|
Assertions.assertEquals(CLUE_UP, grid.digitAt(OFF_1_2));
|
||||||
Assertions.assertFalse(grid.isClue(Grid.offset(2, 3)));
|
Assertions.assertFalse(grid.isClue(OFF_2_3));
|
||||||
Assertions.assertFalse(grid.isClue(Grid.offset(1, 1)));
|
Assertions.assertFalse(grid.isClue(OFF_1_1));
|
||||||
|
|
||||||
// Test isLettercell
|
// Test isLettercell
|
||||||
Assertions.assertTrue(grid.notClue(Grid.offset(0, 0))); // 'A' is letter
|
Assertions.assertTrue(grid.notClue(OFF_0_0)); // 'A' is letter
|
||||||
Assertions.assertTrue(grid.isClue(Grid.offset(1, 2))); // '5' is digit
|
Assertions.assertTrue(grid.isClue(OFF_1_2)); // digit
|
||||||
Assertions.assertTrue(grid.notClue(Grid.offset(1, 1))); // '#' is lettercell
|
Assertions.assertTrue(grid.notClue(OFF_1_1)); // '#' is lettercell
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -119,8 +134,8 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMini() {
|
public void testMini() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
val idx = Grid.offset(1, 1);
|
val idx = OFF_1_1;
|
||||||
grid.setClue(idx, (byte) '1');
|
grid.setClue(idx, CLUE_LEFT);
|
||||||
Assertions.assertTrue(grid.isClue(idx));
|
Assertions.assertTrue(grid.isClue(idx));
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
@@ -163,9 +178,9 @@ public class MainTest {
|
|||||||
// Regression baseline for seed search starting at 12347, pop 4, gens 20
|
// Regression baseline for seed search starting at 12347, pop 4, gens 20
|
||||||
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
||||||
Assertions.assertEquals(18, res.filled().clueMap().size(), "Number of assigned words changed");
|
Assertions.assertEquals(18, res.filled().clueMap().size(), "Number of assigned words changed");
|
||||||
Assertions.assertEquals("TEN", Lemma.asWord(res.filled().clueMap().get(2)));
|
Assertions.assertEquals("DISCO", Lemma.asWord(res.filled().clueMap().get(476)));
|
||||||
Assertions.assertEquals(301794542151533187L, res.filled().grid().grid().lo);
|
Assertions.assertEquals(3332734179516361088L, res.filled().grid().grid().lo);
|
||||||
Assertions.assertEquals(193L, res.filled().grid().grid().hi);
|
Assertions.assertEquals(128L, res.filled().grid().grid().hi);
|
||||||
}
|
}
|
||||||
boolean isLetter(byte b) { return (b & 64) != 0; }
|
boolean isLetter(byte b) { return (b & 64) != 0; }
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -11,11 +11,32 @@ import static puzzle.SwedishGenerator.*;
|
|||||||
|
|
||||||
public class SwedishGeneratorTest {
|
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
|
@Test
|
||||||
void testPatternForSlotAllLetters() {
|
void testPatternForSlotAllLetters() {
|
||||||
var grid = new Grid(new byte[]{ 65, 66, 67 }); // A B C
|
var grid = new Grid(new byte[]{ LETTER_A, LETTER_B, LETTER_C });
|
||||||
var slot = Slot.from(18, 7L, 0L);
|
var slot = Slot.from(18 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
|
||||||
long pattern = patternForSlot(grid, slot);
|
long pattern = patternForSlot(grid, slot);
|
||||||
|
|
||||||
assertEquals(1 | (2 << 5) | (3 << 10), pattern);
|
assertEquals(1 | (2 << 5) | (3 << 10), pattern);
|
||||||
@@ -23,8 +44,8 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotMixed() {
|
void testPatternForSlotMixed() {
|
||||||
var grid = new Grid(new byte[]{ 65, DASH, 67 }); // A - C
|
var grid = new Grid(new byte[]{ LETTER_A, DASH, LETTER_C });
|
||||||
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);
|
long pattern = patternForSlot(grid, slot);
|
||||||
|
|
||||||
assertEquals(1 | (0 << 5) | (3 << 10), pattern);
|
assertEquals(1 | (0 << 5) | (3 << 10), pattern);
|
||||||
@@ -33,7 +54,7 @@ public class SwedishGeneratorTest {
|
|||||||
@Test
|
@Test
|
||||||
void testPatternForSlotAllDashes() {
|
void testPatternForSlotAllDashes() {
|
||||||
var grid = new Grid(new byte[]{ DASH, DASH, DASH }); // - - -
|
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);
|
long pattern = patternForSlot(grid, slot);
|
||||||
|
|
||||||
assertEquals(0, pattern);
|
assertEquals(0, pattern);
|
||||||
@@ -41,8 +62,8 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotSingleLetter() {
|
void testPatternForSlotSingleLetter() {
|
||||||
var grid = new Grid(new byte[]{ 65, DASH, DASH }); // A - -
|
var grid = new Grid(new byte[]{ LETTER_A, 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);
|
long pattern = patternForSlot(grid, slot);
|
||||||
|
|
||||||
assertEquals(1, pattern);
|
assertEquals(1, pattern);
|
||||||
@@ -68,23 +89,23 @@ public class SwedishGeneratorTest {
|
|||||||
@Test
|
@Test
|
||||||
void testGrid() {
|
void testGrid() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
grid.setLetter(0, (byte) 'A');
|
grid.setLetter(OFF_0_0, LETTER_A);
|
||||||
grid.setClue(Grid.offset(0, 1), (byte) '1');
|
grid.setClue(OFF_0_1, CLUE_LEFT);
|
||||||
|
|
||||||
assertEquals('A', grid.byteAt(0));
|
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||||
assertEquals(1, grid.digitAt(Grid.offset(0, 1)));
|
assertEquals(CLUE_LEFT, grid.digitAt(OFF_0_1));
|
||||||
assertTrue(grid.notClue(0));
|
assertTrue(grid.notClue(OFF_0_0));
|
||||||
assertFalse(grid.isClue(0));
|
assertFalse(grid.isClue(OFF_0_0));
|
||||||
assertTrue(grid.isClue(Grid.offset(0, 1)));
|
assertTrue(grid.isClue(OFF_0_1));
|
||||||
assertFalse(grid.notClue(Grid.offset(0, 1)));
|
assertFalse(grid.notClue(OFF_0_1));
|
||||||
assertTrue(grid.notClue(0));
|
assertTrue(grid.notClue(OFF_0_0));
|
||||||
assertFalse(grid.notClue(Grid.offset(0, 1)));
|
assertFalse(grid.notClue(OFF_0_1));
|
||||||
|
|
||||||
var copy = grid.deepCopyGrid();
|
var copy = grid.deepCopyGrid();
|
||||||
assertEquals('A', copy.byteAt(0));
|
assertEquals('A', copy.byteAt(OFF_0_0));
|
||||||
copy.setLetter(0, (byte) 'B');
|
copy.setLetter(OFF_0_0, LETTER_B);
|
||||||
assertEquals('B', copy.byteAt(0));
|
assertEquals('B', copy.byteAt(OFF_0_0));
|
||||||
assertEquals('A', grid.byteAt(0));
|
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -134,22 +155,22 @@ public class SwedishGeneratorTest {
|
|||||||
void testSlot() {
|
void testSlot() {
|
||||||
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Slot.BIT_FOR_DIR);
|
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Slot.BIT_FOR_DIR);
|
||||||
// key = (r << 8) | (c << 4) | d
|
// 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);
|
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);
|
System.out.println("[DEBUG_LOG] key = " + key);
|
||||||
long lo = 0;
|
long lo = 0;
|
||||||
// pos 0: (2, 5)
|
// pos 0: (2, 5)
|
||||||
lo |= 1L << Grid.offset(2, 5);
|
lo |= 1L << OFF_2_5;
|
||||||
// pos 1: (3, 5)
|
// pos 1: (3, 5)
|
||||||
lo |= 1L << Grid.offset(3, 5);
|
lo |= 1L << OFF_3_5;
|
||||||
// pos 2: (4, 5)
|
// pos 2: (4, 5)
|
||||||
lo |= 1L << Grid.offset(4, 5);
|
lo |= 1L << OFF_4_5;
|
||||||
|
|
||||||
var s = Slot.from(key, lo, 0L);
|
var s = Slot.from(key, lo, 0L);
|
||||||
System.out.println("[DEBUG_LOG] s.dir() = " + Slot.dir(s.key()));
|
System.out.println("[DEBUG_LOG] s.dir() = " + Slot.dir(s.key()));
|
||||||
assertEquals(Grid.offset(2,3), s.clueIndex());
|
assertEquals(OFF_2_3, s.clueIndex());
|
||||||
assertEquals(3, Slot.dir(s.key()));
|
assertEquals(CLUE_DOWN + 1, Slot.dir(s.key()));
|
||||||
assertFalse(s.horiz());
|
assertFalse(s.horiz());
|
||||||
var cells = s.walk().toArray();
|
var cells = s.walk().toArray();
|
||||||
assertEquals(2, Grid.r(cells[0]));
|
assertEquals(2, Grid.r(cells[0]));
|
||||||
@@ -159,8 +180,8 @@ public class SwedishGeneratorTest {
|
|||||||
assertEquals(5, Grid.c(cells[1]));
|
assertEquals(5, Grid.c(cells[1]));
|
||||||
assertEquals(5, Grid.c(cells[2]));
|
assertEquals(5, Grid.c(cells[2]));
|
||||||
|
|
||||||
assertTrue(Slot.horiz(2)); // right
|
assertTrue(Slot.horiz(CLUE_RIGHT + 1)); // right
|
||||||
assertFalse(Slot.horiz(3)); // down
|
assertFalse(Slot.horiz(CLUE_DOWN + 1)); // down
|
||||||
}
|
}
|
||||||
static int intersectSorted(int[] a, int aLen, int[] b, int bLen, int[] out) {
|
static int intersectSorted(int[] a, int aLen, int[] b, int bLen, int[] out) {
|
||||||
if (aLen == 0 || bLen == 0) return 0;
|
if (aLen == 0 || bLen == 0) return 0;
|
||||||
@@ -238,26 +259,17 @@ public class SwedishGeneratorTest {
|
|||||||
var gen = new SwedishGenerator(new Rng(0));
|
var gen = new SwedishGenerator(new Rng(0));
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
|
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
|
||||||
// Set '2' (right) at 0,0
|
// Set CLUE_RIGHT at OFF_0_0
|
||||||
grid.setClue(0, (byte) '2');
|
grid.setClue(OFF_0_0, CLUE_RIGHT);
|
||||||
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
||||||
|
|
||||||
var slots = extractSlots(grid);
|
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());
|
assertEquals(1, slots.size());
|
||||||
var s = slots.getFirst();
|
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);
|
assertTrue(s.len() >= 2);
|
||||||
assertEquals(0, s.clueIndex());
|
assertEquals(OFF_0_0, s.clueIndex());
|
||||||
assertEquals(2, Slot.dir(s.key()));
|
assertEquals(CLUE_RIGHT + 1, Slot.dir(s.key()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -273,8 +285,7 @@ public class SwedishGeneratorTest {
|
|||||||
assertTrue(f1 >= 1_000_000_000L);
|
assertTrue(f1 >= 1_000_000_000L);
|
||||||
|
|
||||||
// Add a slot
|
// Add a slot
|
||||||
//var dbyte = OFFSETS[2].dbyte();
|
grid.setClue(OFF_0_0, D_BYTE_2);
|
||||||
grid.setClue(0, D_BYTE_2);
|
|
||||||
var f2 = gen.maskFitness(grid);
|
var f2 = gen.maskFitness(grid);
|
||||||
assertTrue(f2 < f1);
|
assertTrue(f2 < f1);
|
||||||
}
|
}
|
||||||
@@ -303,18 +314,18 @@ public class SwedishGeneratorTest {
|
|||||||
@Test
|
@Test
|
||||||
void testPlaceWord() {
|
void testPlaceWord() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
// Slot at (0,0) length 3, horizontal (right)
|
// Slot at OFF_0_0 length 3, horizontal (right)
|
||||||
var key = (Grid.offset(0,0) << Slot.BIT_FOR_DIR) | 2;
|
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
|
||||||
var lo = (1L << Grid.offset(0, 0)) | (1L << Grid.offset(0, 1)) | (1L << Grid.offset(0, 2));
|
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
|
||||||
var s = Slot.from(key, lo, 0L);
|
var s = Slot.from(key, lo, 0L);
|
||||||
var w1 = Lemma.from("ABC");
|
var w1 = Lemma.from("ABC");
|
||||||
var undoBuffer = new long[10];
|
var undoBuffer = new long[10];
|
||||||
|
|
||||||
// 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(0));
|
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||||
assertEquals('B', grid.byteAt(Grid.offset(0, 1)));
|
assertEquals('B', grid.byteAt(OFF_0_1));
|
||||||
assertEquals('C', grid.byteAt(Grid.offset(0, 2)));
|
assertEquals('C', grid.byteAt(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)
|
||||||
@@ -325,52 +336,52 @@ 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(Grid.offset(0, 0)));
|
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||||
assertEquals('B', grid.byteAt(Grid.offset(0, 1)));
|
assertEquals('B', grid.byteAt(OFF_0_1));
|
||||||
assertEquals('C', grid.byteAt(Grid.offset(0, 2)));
|
assertEquals('C', grid.byteAt(OFF_0_2));
|
||||||
|
|
||||||
// 4. Partial placement then conflict (rollback)
|
// 4. Partial placement then conflict (rollback)
|
||||||
grid = Grid.createEmpty();
|
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));
|
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(Grid.offset(0, 0)));
|
assertEquals(DASH, grid.byteAt(OFF_0_0));
|
||||||
assertEquals(DASH, grid.byteAt(Grid.offset(0, 1)));
|
assertEquals(DASH, grid.byteAt(OFF_0_1));
|
||||||
assertEquals('X', grid.byteAt(Grid.offset(0, 2)));
|
assertEquals('X', grid.byteAt(OFF_0_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBacktrackingHelpers() {
|
void testBacktrackingHelpers() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
// Slot at 0,1 length 2
|
// Slot at 0,1 length 2
|
||||||
var key = (Grid.offset(0,0) << Slot.BIT_FOR_DIR) | 2;
|
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
|
||||||
var lo = (1L << Grid.offset(0, 1)) | (1L << Grid.offset(0, 2));
|
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
|
||||||
var s = Slot.from(key, lo, 0L);
|
var s = Slot.from(key, lo, 0L);
|
||||||
var w = Lemma.from("AZ");
|
var w = Lemma.from("AZ");
|
||||||
var undoBuffer = new long[10];
|
var undoBuffer = new long[10];
|
||||||
|
|
||||||
var placed = placeWord(grid, s, w, undoBuffer, 0);
|
var placed = placeWord(grid, s, w, undoBuffer, 0);
|
||||||
assertTrue(placed);
|
assertTrue(placed);
|
||||||
assertEquals('A', grid.byteAt(Grid.offset(0, 1)));
|
assertEquals('A', grid.byteAt(OFF_0_1));
|
||||||
assertEquals('Z', grid.byteAt(Grid.offset(0, 2)));
|
assertEquals('Z', grid.byteAt(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(Grid.offset(0, 1)));
|
assertEquals(DASH, grid.byteAt(OFF_0_1));
|
||||||
assertEquals(DASH, grid.byteAt(Grid.offset(0, 2)));
|
assertEquals(DASH, grid.byteAt(OFF_0_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInnerWorkings() {
|
void testInnerWorkings() {
|
||||||
// 1. Test Slot.increasing
|
// 1. Test Slot.increasing
|
||||||
assertFalse(Slot.increasing(1)); // Left
|
assertFalse(Slot.increasing(CLUE_LEFT + 1)); // Left
|
||||||
assertTrue(Slot.increasing(2)); // Right
|
assertTrue(Slot.increasing(CLUE_RIGHT + 1)); // Right
|
||||||
assertTrue(Slot.increasing(3)); // Down
|
assertTrue(Slot.increasing(CLUE_DOWN + 1)); // Down
|
||||||
assertFalse(Slot.increasing(4)); // Up
|
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());
|
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());
|
assertFalse(sDec.increasing());
|
||||||
|
|
||||||
// 2. Test slotScore
|
// 2. Test slotScore
|
||||||
|
|||||||
Reference in New Issue
Block a user