introduce bitloops
This commit is contained in:
@@ -61,16 +61,21 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
|
|
||||||
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
||||||
record Pick(Slot slot, CandidateInfo info, boolean done) { }
|
record Pick(Slot slot, CandidateInfo info, boolean done) { }
|
||||||
|
// 0b11
|
||||||
|
//0b00
|
||||||
|
// 0b01
|
||||||
|
// 0b10
|
||||||
|
|
||||||
static final byte B0 = (byte) 0;
|
static final byte B0 = (byte) 0;
|
||||||
static final byte B64 = (byte) 64;
|
static final byte B64 = (byte) 64;
|
||||||
static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX;
|
static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX;
|
||||||
static final rci[] IT = Neighbors9x8.IT;
|
static final long[] OFFSET_D_IDX_0_BASE = Neighbors9x8.OFFSET_D_IDX_0_BASE;
|
||||||
static final int[][] MUTATE_RI = new int[SIZE][625];
|
static final rci[] IT = Neighbors9x8.IT;
|
||||||
static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
|
static final int[][] MUTATE_RI = new int[SIZE][625];
|
||||||
static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
|
static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
|
||||||
static final long[] PATH_LO = Neighbors9x8.PATH_LO;
|
static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
|
||||||
static final long[] PATH_HI = Neighbors9x8.PATH_HI;
|
static final long[] PATH_LO = Neighbors9x8.PATH_LO;
|
||||||
|
static final long[] PATH_HI = Neighbors9x8.PATH_HI;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (int i = 0; i < SIZE; i++) {
|
for (int i = 0; i < SIZE; i++) {
|
||||||
@@ -317,11 +322,11 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
||||||
public static int dir(int key) { return key & 7; }
|
public static int dir(int key) { return key & 7; }
|
||||||
public boolean horiz() { return horiz(key); }
|
public boolean horiz() { return horiz(key); }
|
||||||
public boolean reversed() { return (key & 2) == 0; }
|
|
||||||
public boolean increasing() { return (key & 2) != 0; }
|
public boolean increasing() { return (key & 2) != 0; }
|
||||||
public static boolean increasing(int dir) { return (dir & 2) != 0; }
|
public static boolean increasing(int dir) { return (dir & 2) != 0; }
|
||||||
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
||||||
public static boolean horiz(int d) { return (d & 1) == 0; }
|
public static boolean horiz(int d) { return (d & 1) == 0; }
|
||||||
|
public static boolean horizv2(int d) { return (d & 1) == 1; }
|
||||||
public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +415,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
if ((rLo | rHi) != 0) {
|
if ((rLo | rHi) != 0) {
|
||||||
hasSlots = true;
|
hasSlots = true;
|
||||||
if (Slot.horiz(d + 1)) covH.or(rLo, rHi);
|
if (Slot.horizv2(d)) 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 {
|
||||||
@@ -555,7 +560,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) ) | (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;
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ public class MainTest {
|
|||||||
void testStaticSlotMethods() {
|
void testStaticSlotMethods() {
|
||||||
// Test static horiz
|
// Test static horiz
|
||||||
// dir 2 (right) is horizontal
|
// dir 2 (right) is horizontal
|
||||||
assertTrue(Slot.horiz(2));
|
assertTrue(Slot.horizv2(1));
|
||||||
// dir 3 (down) is vertical
|
// dir 3 (down) is vertical
|
||||||
assertFalse(Slot.horiz(3));
|
assertFalse(Slot.horizv2(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -78,7 +78,7 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testHoriz() {
|
public void testHoriz() {
|
||||||
assertTrue(Slot.from(2, 0L, 0L).horiz());
|
assertTrue(Slot.from(2, 0L, 0L).horiz());
|
||||||
assertTrue(Slot.from(4, 0L, 0L).horiz());
|
assertTrue(Slot.from(0, 0L, 0L).horiz());
|
||||||
assertFalse(Slot.from(1, 0L, 0L).horiz());
|
assertFalse(Slot.from(1, 0L, 0L).horiz());
|
||||||
assertFalse(Slot.from(3, 0L, 0L).horiz());
|
assertFalse(Slot.from(3, 0L, 0L).horiz());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ import static puzzle.SwedishGenerator.*;
|
|||||||
|
|
||||||
public class SwedishGeneratorTest {
|
public class SwedishGeneratorTest {
|
||||||
|
|
||||||
static final byte LETTER_A = (byte) 'A';
|
static final byte LETTER_A = (byte) 'A';
|
||||||
static final byte LETTER_B = (byte) 'B';
|
static final byte LETTER_B = (byte) 'B';
|
||||||
static final byte LETTER_C = (byte) 'C';
|
static final byte LETTER_C = (byte) 'C';
|
||||||
static final byte LETTER_X = (byte) 'X';
|
static final byte LETTER_X = (byte) 'X';
|
||||||
static final byte LETTER_Z = (byte) 'Z';
|
static final byte LETTER_Z = (byte) 'Z';
|
||||||
static final byte CLUE_UP = 0;
|
static final byte CLUE_UP = 0;
|
||||||
static final byte CLUE_RIGHT = 1;
|
static final byte CLUE_RIGHT = 1;
|
||||||
static final byte CLUE_DOWN = 2;
|
static final byte CLUE_DOWN = 2;
|
||||||
static final byte CLUE_LEFT = 3;
|
static final byte CLUE_LEFT = 3;
|
||||||
|
|
||||||
static final int OFF_0_0 = Grid.offset(0, 0);
|
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_1 = Grid.offset(0, 1);
|
||||||
static final int OFF_0_2 = Grid.offset(0, 2);
|
static final int OFF_0_2 = Grid.offset(0, 2);
|
||||||
@@ -31,11 +31,11 @@ public class SwedishGeneratorTest {
|
|||||||
static final int OFF_2_5 = Grid.offset(2, 5);
|
static final int OFF_2_5 = Grid.offset(2, 5);
|
||||||
static final int OFF_3_5 = Grid.offset(3, 5);
|
static final int OFF_3_5 = Grid.offset(3, 5);
|
||||||
static final int OFF_4_5 = Grid.offset(4, 5);
|
static final int OFF_4_5 = Grid.offset(4, 5);
|
||||||
|
|
||||||
static final byte D_BYTE_2 = CLUE_RIGHT;
|
static final byte D_BYTE_2 = CLUE_RIGHT;
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotAllLetters() {
|
void testPatternForSlotAllLetters() {
|
||||||
var grid = new Grid(new byte[]{ LETTER_A, LETTER_B, LETTER_C });
|
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);
|
var slot = Slot.from(18 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
|
||||||
long pattern = patternForSlot(grid, slot);
|
long pattern = patternForSlot(grid, slot);
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotMixed() {
|
void testPatternForSlotMixed() {
|
||||||
var grid = new Grid(new byte[]{ LETTER_A, DASH, LETTER_C });
|
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);
|
var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 7L, 0L);
|
||||||
long pattern = patternForSlot(grid, slot);
|
long pattern = patternForSlot(grid, slot);
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotSingleLetter() {
|
void testPatternForSlotSingleLetter() {
|
||||||
var grid = new Grid(new byte[]{ LETTER_A, DASH, DASH });
|
var grid = new Grid(new byte[]{ LETTER_A, DASH, DASH });
|
||||||
var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT + 1), 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);
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ public class SwedishGeneratorTest {
|
|||||||
void testGrid() {
|
void testGrid() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
grid.setLetter(OFF_0_0, LETTER_A);
|
grid.setLetter(OFF_0_0, LETTER_A);
|
||||||
grid.setClue(OFF_0_1, CLUE_LEFT);
|
grid.setClue(OFF_0_1, CLUE_LEFT);
|
||||||
|
|
||||||
assertEquals('A', grid.byteAt(OFF_0_0));
|
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||||
assertEquals(CLUE_LEFT, grid.digitAt(OFF_0_1));
|
assertEquals(CLUE_LEFT, grid.digitAt(OFF_0_1));
|
||||||
@@ -180,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(CLUE_RIGHT + 1)); // right
|
assertTrue(Slot.horizv2(CLUE_RIGHT)); // right
|
||||||
assertFalse(Slot.horiz(CLUE_DOWN + 1)); // down
|
assertFalse(Slot.horizv2(CLUE_DOWN)); // 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;
|
||||||
@@ -315,8 +315,8 @@ public class SwedishGeneratorTest {
|
|||||||
void testPlaceWord() {
|
void testPlaceWord() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
// Slot at OFF_0_0 length 3, horizontal (right)
|
// Slot at OFF_0_0 length 3, horizontal (right)
|
||||||
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
|
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 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];
|
||||||
@@ -354,8 +354,8 @@ public class SwedishGeneratorTest {
|
|||||||
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 = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
|
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT + 1);
|
||||||
var lo = (1L << OFF_0_1) | (1L << OFF_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];
|
||||||
@@ -366,11 +366,11 @@ public class SwedishGeneratorTest {
|
|||||||
assertEquals('Z', grid.byteAt(OFF_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(OFF_0_1));
|
assertEquals(DASH, grid.byteAt(OFF_0_1));
|
||||||
assertEquals(DASH, grid.byteAt(OFF_0_2));
|
assertEquals(DASH, grid.byteAt(OFF_0_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInnerWorkings() {
|
void testInnerWorkings() {
|
||||||
// 1. Test Slot.increasing
|
// 1. Test Slot.increasing
|
||||||
@@ -378,12 +378,12 @@ public class SwedishGeneratorTest {
|
|||||||
assertTrue(Slot.increasing(CLUE_RIGHT + 1)); // Right
|
assertTrue(Slot.increasing(CLUE_RIGHT + 1)); // Right
|
||||||
assertTrue(Slot.increasing(CLUE_DOWN + 1)); // Down
|
assertTrue(Slot.increasing(CLUE_DOWN + 1)); // Down
|
||||||
assertFalse(Slot.increasing(CLUE_UP + 1)); // Up
|
assertFalse(Slot.increasing(CLUE_UP + 1)); // Up
|
||||||
|
|
||||||
var sInc = Slot.from((0 << Slot.BIT_FOR_DIR) | CLUE_RIGHT + 1, 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) | CLUE_LEFT + 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
|
||||||
int[] counts = new int[SIZE];
|
int[] counts = new int[SIZE];
|
||||||
counts[1] = 2;
|
counts[1] = 2;
|
||||||
@@ -392,48 +392,48 @@ public class SwedishGeneratorTest {
|
|||||||
// cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3
|
// cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3
|
||||||
// score = 3 * 10 + len(2) = 32
|
// score = 3 * 10 + len(2) = 32
|
||||||
assertEquals(32, slotScore(counts, sScore));
|
assertEquals(32, slotScore(counts, sScore));
|
||||||
|
|
||||||
// 3. Test candidateCountForPattern
|
// 3. Test candidateCountForPattern
|
||||||
var words = new long[] {
|
var words = new long[]{
|
||||||
Lemma.from("AT"),
|
Lemma.from("AT"),
|
||||||
Lemma.from("CAT"),
|
Lemma.from("CAT"),
|
||||||
Lemma.from("DOGS"),
|
Lemma.from("DOGS"),
|
||||||
Lemma.from("APPLE"),
|
Lemma.from("APPLE"),
|
||||||
Lemma.from("APPLY"),
|
Lemma.from("APPLY"),
|
||||||
Lemma.from("BANAN"),
|
Lemma.from("BANAN"),
|
||||||
Lemma.from("BANANA"),
|
Lemma.from("BANANA"),
|
||||||
Lemma.from("BANANAS"),
|
Lemma.from("BANANAS"),
|
||||||
Lemma.from("BANANASS") // length 8
|
Lemma.from("BANANASS") // length 8
|
||||||
};
|
};
|
||||||
|
|
||||||
var dict = new Dict(words);
|
var dict = new Dict(words);
|
||||||
var entry5 = dict.index()[5];
|
var entry5 = dict.index()[5];
|
||||||
|
|
||||||
var ctx = new Context();
|
var ctx = new Context();
|
||||||
ctx.setPattern(Lemma.pack("APP".getBytes(StandardCharsets.US_ASCII)));
|
ctx.setPattern(Lemma.pack("APP".getBytes(StandardCharsets.US_ASCII)));
|
||||||
assertEquals(2, candidateCountForPattern(ctx, entry5));
|
assertEquals(2, candidateCountForPattern(ctx, entry5));
|
||||||
|
|
||||||
ctx.setPattern(Lemma.pack("BAN".getBytes(StandardCharsets.US_ASCII)));
|
ctx.setPattern(Lemma.pack("BAN".getBytes(StandardCharsets.US_ASCII)));
|
||||||
assertEquals(1, candidateCountForPattern(ctx, entry5));
|
assertEquals(1, candidateCountForPattern(ctx, entry5));
|
||||||
|
|
||||||
ctx.setPattern(Lemma.pack("CAT".getBytes(StandardCharsets.US_ASCII)));
|
ctx.setPattern(Lemma.pack("CAT".getBytes(StandardCharsets.US_ASCII)));
|
||||||
assertEquals(0, candidateCountForPattern(ctx, entry5));
|
assertEquals(0, candidateCountForPattern(ctx, entry5));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMaskFitnessDetailed() {
|
void testMaskFitnessDetailed() {
|
||||||
var gen = new SwedishGenerator(new Rng(42));
|
var gen = new SwedishGenerator(new Rng(42));
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
|
|
||||||
// Empty grid: huge penalty
|
// Empty grid: huge penalty
|
||||||
long fitEmpty = gen.maskFitness(grid);
|
long fitEmpty = gen.maskFitness(grid);
|
||||||
assertTrue(fitEmpty >= 1_000_000_000L);
|
assertTrue(fitEmpty >= 1_000_000_000L);
|
||||||
|
|
||||||
// Grid with one short slot: still high penalty but less than empty
|
// Grid with one short slot: still high penalty but less than empty
|
||||||
grid.setClue(0, D_BYTE_2); // Right from 0,0. Len 2 if 3x3.
|
grid.setClue(0, D_BYTE_2); // Right from 0,0. Len 2 if 3x3.
|
||||||
long fitOne = gen.maskFitness(grid);
|
long fitOne = gen.maskFitness(grid);
|
||||||
assertTrue(fitOne < fitEmpty);
|
assertTrue(fitOne < fitEmpty);
|
||||||
|
|
||||||
// Test penalty for TARGET_CLUES
|
// Test penalty for TARGET_CLUES
|
||||||
// TARGET_CLUES = SIZE >>> 2. For 3x3 it is 9 >>> 2 = 2.
|
// TARGET_CLUES = SIZE >>> 2. For 3x3 it is 9 >>> 2 = 2.
|
||||||
// If we have 1 clue, |1 - 2| * 16000 = 16000 penalty.
|
// If we have 1 clue, |1 - 2| * 16000 = 16000 penalty.
|
||||||
|
|||||||
Reference in New Issue
Block a user