introduce bitloops
This commit is contained in:
@@ -179,63 +179,50 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
long lo, hi, vlo, vhi, rlo, rhi;
|
long lo, hi, vlo, vhi, rlo, rhi;
|
||||||
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); }
|
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); }
|
||||||
public Clues deepCopyGrid() { return new Clues(lo, hi, vlo, vhi, rlo, rhi); }
|
public Clues deepCopyGrid() { return new Clues(lo, hi, vlo, vhi, rlo, rhi); }
|
||||||
boolean clueless(int idx) {
|
boolean cluelessLo(int idx) {
|
||||||
if (!isClue(idx)) return false;
|
if (!isClueLo(idx)) return false;
|
||||||
if ((idx & 64) == 0) {
|
clearClueLo(~(1L << idx));
|
||||||
clearClueLo(idx);
|
return true;
|
||||||
} else {
|
}
|
||||||
clearClueHi(idx);
|
boolean cluelessHi(int idx) {
|
||||||
}
|
if (!isClueHi(idx)) return false;
|
||||||
|
clearClueHi(~(1L << (idx & 63)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
public boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
||||||
|
|
||||||
public void setClue(int ri, byte idx) {
|
public void setClueLo(long mask, byte idx) {
|
||||||
if ((ri & 64) == 0) {
|
lo |= mask;
|
||||||
long mask = 1L << ri;
|
if ((idx & 1) != 0) vlo |= mask;
|
||||||
lo |= mask;
|
else vlo &= ~mask;
|
||||||
if ((idx & 1) != 0) vlo |= mask;
|
if ((idx & 2) != 0) rlo |= mask;
|
||||||
else vlo &= ~mask;
|
else rlo &= ~mask;
|
||||||
if ((idx & 2) != 0) rlo |= mask;
|
}
|
||||||
else rlo &= ~mask;
|
public void setClueHi(long mask, byte idx) {
|
||||||
} else {
|
hi |= mask;
|
||||||
long mask = 1L << (ri & 63);
|
if ((idx & 1) != 0) vhi |= mask;
|
||||||
hi |= mask;
|
else vhi &= ~mask;
|
||||||
if ((idx & 1) != 0) vhi |= mask;
|
if ((idx & 2) != 0) rhi |= mask;
|
||||||
else vhi &= ~mask;
|
else rhi &= ~mask;
|
||||||
if ((idx & 2) != 0) rhi |= mask;
|
|
||||||
else rhi &= ~mask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public byte digitAtRevLo(int idx) { return (byte) (2 | ((vlo >>> idx) & 1L)); }
|
public byte digitAtRevLo(int idx) { return (byte) (2 | ((vlo >>> idx) & 1L)); }
|
||||||
public byte digitAtRevHi(int idx) { return (byte) (2 | ((vhi >>> (idx & 63)) & 1L)); }
|
public byte digitAtRevHi(int idx) { return (byte) (2 | ((vhi >>> (idx & 63)) & 1L)); }
|
||||||
public byte digitAtDevLo(int idx) { return (byte) ((vlo >>> idx) & 1L); }
|
public byte digitAtDevLo(int idx) { return (byte) ((vlo >>> idx) & 1L); }
|
||||||
public byte digitAtDevHi(int idx) { return (byte) ((vhi >>> (idx & 63)) & 1L); }
|
public byte digitAtDevHi(int idx) { return (byte) ((vhi >>> (idx & 63)) & 1L); }
|
||||||
public byte digitAt(int idx) {
|
public int digitAtHi(int idx) { return (int) ((((rhi >>> idx) & 1L) << 1) | ((vhi >>> idx) & 1L)); }
|
||||||
if ((idx & 64) == 0) {
|
public int digitAtLo(int idx) { return (int) ((((rlo >>> idx) & 1L) << 1) | ((vlo >>> idx) & 1L)); }
|
||||||
int v = (int) ((vlo >>> idx) & 1L);
|
public void clearClueLo(long mask) {
|
||||||
int r = (int) ((rlo >>> idx) & 1L);
|
|
||||||
return (byte) ((r << 1) | v);
|
|
||||||
} else {
|
|
||||||
int v = (int) ((vhi >>> (idx & 63)) & 1L);
|
|
||||||
int r = (int) ((rhi >>> (idx & 63)) & 1L);
|
|
||||||
return (byte) ((r << 1) | v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void clearClueLo(int idx) {
|
|
||||||
long mask = ~(1L << idx);
|
|
||||||
lo &= mask;
|
lo &= mask;
|
||||||
vlo &= mask;
|
vlo &= mask;
|
||||||
rlo &= mask;
|
rlo &= mask;
|
||||||
}
|
}
|
||||||
public void clearClueHi(int idx) {
|
public void clearClueHi(long mask) {
|
||||||
long mask = ~(1L << (idx & 63));
|
|
||||||
hi &= mask;
|
hi &= mask;
|
||||||
vhi &= mask;
|
vhi &= mask;
|
||||||
rhi &= mask;
|
rhi &= mask;
|
||||||
}
|
}
|
||||||
public boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
public boolean isClueLo(int index) { return ((lo >>> index) & 1L) != X; }
|
||||||
public boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
public boolean isClueHi(int index) { return ((hi >>> (index & 63)) & 1L) != X; }
|
||||||
public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||||
public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||||
|
|
||||||
@@ -501,18 +488,18 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
long seenLo = X, seenHi = X;
|
long seenLo = X, seenHi = X;
|
||||||
|
|
||||||
// loop over beide helften
|
// loop over beide helften
|
||||||
for (int base = 0; base <= 64; base += 64) {
|
for (int base = 0, size, sp, cur; base <= 64; base += 64) {
|
||||||
long clueMask = (base == 0) ? lo_cl : hi_cl;
|
long clueMask = (base == 0) ? lo_cl : hi_cl;
|
||||||
long seenMask = (base == 0) ? seenLo : seenHi;
|
long seenMask = (base == 0) ? seenLo : seenHi;
|
||||||
|
|
||||||
// "unseen clues" in deze helft
|
// "unseen clues" in deze helft
|
||||||
for (long bits = clueMask & ~seenMask; bits != X; bits &= bits - 1) {
|
for (long bits = clueMask & ~seenMask, nLo, nHi; bits != X; bits &= bits - 1) {
|
||||||
int clueIdx = base | Long.numberOfTrailingZeros(bits);
|
int clueIdx = base | Long.numberOfTrailingZeros(bits);
|
||||||
|
|
||||||
// start nieuwe component
|
// start nieuwe component
|
||||||
int size = 0;
|
size = 0;
|
||||||
int sp = 0;
|
stack[0] = clueIdx;
|
||||||
stack[sp++] = clueIdx;
|
sp = 1;
|
||||||
|
|
||||||
// mark seen
|
// mark seen
|
||||||
if ((clueIdx & 64) == 0) seenLo |= 1L << clueIdx;
|
if ((clueIdx & 64) == 0) seenLo |= 1L << clueIdx;
|
||||||
@@ -520,12 +507,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
|
|
||||||
// flood fill / bfs
|
// flood fill / bfs
|
||||||
while (sp > 0) {
|
while (sp > 0) {
|
||||||
int cur = stack[--sp];
|
cur = stack[--sp];
|
||||||
size++;
|
size++;
|
||||||
|
|
||||||
// neighbors als 2x long masks
|
// neighbors als 2x long masks
|
||||||
long nLo = NBR8_PACKED_LO[cur];
|
nLo = NBR8_PACKED_LO[cur];
|
||||||
long nHi = NBR8_PACKED_HI[cur];
|
nHi = NBR8_PACKED_HI[cur];
|
||||||
|
|
||||||
// filter: alleen clues, en nog niet seen
|
// filter: alleen clues, en nog niet seen
|
||||||
nLo &= lo_cl & ~seenLo;
|
nLo &= lo_cl & ~seenLo;
|
||||||
@@ -584,32 +571,51 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
|
|
||||||
Clues randomMask(final int clueSize) {
|
Clues randomMask(final int clueSize) {
|
||||||
var g = Clues.createEmpty();
|
var g = Clues.createEmpty();
|
||||||
for (int placed = 0, guard = 0, idx; placed < clueSize && guard < 4000; guard++) {
|
for (int placed = 0, guard = 0, ri; placed < clueSize && guard < 4000; guard++) {
|
||||||
|
|
||||||
idx = rng.randint0_SIZE();
|
ri = rng.randint0_SIZE();
|
||||||
if (g.isClue(idx)) continue;
|
if (isLo(ri)) {
|
||||||
var d_idx = rng.randint2bitByte();
|
if (g.isClueLo(ri)) continue;
|
||||||
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, d_idx)])) {
|
var d_idx = rng.randint2bitByte();
|
||||||
g.setClue(idx, d_idx);
|
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) {
|
||||||
placed++;
|
g.setClueLo(1L << ri, d_idx);
|
||||||
|
placed++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (g.isClueHi(ri)) continue;
|
||||||
|
var d_idx = rng.randint2bitByte();
|
||||||
|
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) {
|
||||||
|
g.setClueHi(1L << (ri & 63), d_idx);
|
||||||
|
placed++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
Clues mutate(Clues c) {
|
static boolean isLo(int n) { return (n & 64) == 0; }
|
||||||
|
Clues mutate(SwedishGenerator.Clues c) {
|
||||||
int ri;
|
int ri;
|
||||||
var bytes = MUTATE_RI[rng.randint0_SIZE()];
|
var bytes = MUTATE_RI[rng.randint0_SIZE()];
|
||||||
for (var k = 0; k < 4; k++) {
|
for (var k = 0; k < 4; k++) {
|
||||||
ri = bytes[rng.randint0_624()];
|
ri = bytes[rng.randint0_624()];
|
||||||
if (!c.clueless(ri)) {
|
if (isLo(ri)) {
|
||||||
var d_idx = rng.randint2bitByte();
|
if (!c.cluelessLo(ri)) {
|
||||||
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClue(ri, d_idx);
|
var d_idx = rng.randint2bitByte();
|
||||||
|
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueLo(1L << ri, d_idx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!c.cluelessHi(ri)) {
|
||||||
|
var d_idx = rng.randint2bitByte();
|
||||||
|
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueHi(1L << (ri & 63), d_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
Clues crossover(Clues a, Clues other) {
|
Clues crossover(Clues a, Clues other) {
|
||||||
var out = a.deepCopyGrid();
|
|
||||||
var theta = rng.nextFloat() * Math.PI;
|
var theta = rng.nextFloat() * Math.PI;
|
||||||
var nc = Math.cos(theta);
|
var nc = Math.cos(theta);
|
||||||
var nr = Math.sin(theta);
|
var nr = Math.sin(theta);
|
||||||
@@ -622,20 +628,20 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
else maskHi |= (1L << (i - 64));
|
else maskHi |= (1L << (i - 64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var out = new Clues(
|
||||||
out.lo = (out.lo & ~maskLo) | (other.lo & maskLo);
|
(a.lo & ~maskLo) | (other.lo & maskLo),
|
||||||
out.hi = (out.hi & ~maskHi) | (other.hi & maskHi);
|
(a.hi & ~maskHi) | (other.hi & maskHi),
|
||||||
out.vlo = (out.vlo & ~maskLo) | (other.vlo & maskLo);
|
(a.vlo & ~maskLo) | (other.vlo & maskLo),
|
||||||
out.vhi = (out.vhi & ~maskHi) | (other.vhi & maskHi);
|
(a.vhi & ~maskHi) | (other.vhi & maskHi),
|
||||||
out.rlo = (out.rlo & ~maskLo) | (other.rlo & maskLo);
|
(a.rlo & ~maskLo) | (other.rlo & maskLo),
|
||||||
out.rhi = (out.rhi & ~maskHi) | (other.rhi & maskHi);
|
(a.rhi & ~maskHi) | (other.rhi & maskHi));
|
||||||
|
|
||||||
for (var lo = out.lo; lo != X; lo &= lo - 1L) clearCluesLo(out, Long.numberOfTrailingZeros(lo));
|
for (var lo = out.lo; lo != X; lo &= lo - 1L) clearCluesLo(out, Long.numberOfTrailingZeros(lo));
|
||||||
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearCluesHi(out, 64 | Long.numberOfTrailingZeros(hi));
|
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearCluesHi(out, Long.numberOfTrailingZeros(hi));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
public static void clearCluesLo(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, out.digitAt(idx))])) out.clearClueLo(idx); }
|
public static void clearCluesLo(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, out.digitAtLo(idx))])) out.clearClueLo(~(1L << idx)); }
|
||||||
public static void clearCluesHi(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, out.digitAt(idx))])) out.clearClueHi(idx); }
|
public static void clearCluesHi(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(64 | idx, out.digitAtHi(idx))])) out.clearClueHi(~(1L << idx)); }
|
||||||
|
|
||||||
Clues hillclimb(Clues start, int clue_size, int limit) {
|
Clues hillclimb(Clues start, int clue_size, int limit) {
|
||||||
var best = start;
|
var best = start;
|
||||||
@@ -723,26 +729,26 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
long p = 0;
|
long p = 0;
|
||||||
if (Slot.increasing(key)) {
|
if (Slot.increasing(key)) {
|
||||||
for (long b = lo & glo; b != X; b &= b - 1) {
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
||||||
int idx = Long.numberOfTrailingZeros(b);
|
int idx = Long.numberOfTrailingZeros(b);
|
||||||
int i = Long.bitCount(lo & ((1L << idx) - 1));
|
int i = Long.bitCount(lo & ((1L << idx) - 1));
|
||||||
p |= ((long) (i * 26 + g[idx])) << (i << 3);
|
p |= ((long) (i * 26 + g[idx])) << (i << 3);
|
||||||
}
|
}
|
||||||
int offset = Long.bitCount(lo);
|
int offset = Long.bitCount(lo);
|
||||||
for (long b = hi & ghi; b != X; b &= b - 1) {
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
||||||
int idx = Long.numberOfTrailingZeros(b);
|
int idx = Long.numberOfTrailingZeros(b);
|
||||||
int i = offset + Long.bitCount(hi & ((1L << idx) - 1));
|
int i = offset + Long.bitCount(hi & ((1L << idx) - 1));
|
||||||
p |= ((long) (i * 26 + g[64 | idx])) << (i << 3);
|
p |= ((long) (i * 26 + g[64 | idx])) << (i << 3);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int offset = Long.bitCount(hi);
|
int offset = Long.bitCount(hi);
|
||||||
for (long b = hi & ghi; b != X; b &= b - 1) {
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
||||||
int idx = Long.numberOfTrailingZeros(b);
|
int idx = Long.numberOfTrailingZeros(b);
|
||||||
int i = Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)));
|
int i = Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)));
|
||||||
p |= ((long) (i * 26 + g[64 | idx])) << (i << 3);
|
p |= ((long) (i * 26 + g[64 | idx])) << (i << 3);
|
||||||
}
|
}
|
||||||
for (long b = lo & glo; b != X; b &= b - 1) {
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
||||||
int idx = Long.numberOfTrailingZeros(b);
|
int idx = Long.numberOfTrailingZeros(b);
|
||||||
int i = offset + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)));
|
int i = offset + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)));
|
||||||
p |= ((long) (i * 26 + g[idx])) << (i << 3);
|
p |= ((long) (i * 26 + g[idx])) << (i << 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import puzzle.Export.Clued;
|
import puzzle.Export.Clued;
|
||||||
@@ -9,11 +10,13 @@ import puzzle.Export.PuzzleResult;
|
|||||||
import puzzle.Export.Rewards;
|
import puzzle.Export.Rewards;
|
||||||
import puzzle.SwedishGenerator.FillResult;
|
import puzzle.SwedishGenerator.FillResult;
|
||||||
import puzzle.SwedishGenerator.Rng;
|
import puzzle.SwedishGenerator.Rng;
|
||||||
|
import puzzle.SwedishGeneratorTest.Idx;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static puzzle.ExportFormatTest.Clue.RIGHT;
|
||||||
import static puzzle.SwedishGenerator.C;
|
import static puzzle.SwedishGenerator.C;
|
||||||
import static puzzle.SwedishGenerator.Clues;
|
import static puzzle.SwedishGenerator.Clues;
|
||||||
import static puzzle.SwedishGenerator.FillStats;
|
import static puzzle.SwedishGenerator.FillStats;
|
||||||
@@ -35,19 +38,33 @@ public class ExportFormatTest {
|
|||||||
static final byte CLUE_UP = 2;
|
static final byte CLUE_UP = 2;
|
||||||
static final byte CLUE_LEFT = 3;
|
static final byte CLUE_LEFT = 3;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
enum Clue {
|
||||||
|
DOWN(CLUE_DOWN),
|
||||||
|
RIGHT(CLUE_RIGHT),
|
||||||
|
UP(CLUE_UP),
|
||||||
|
LEFT(CLUE_LEFT);
|
||||||
|
Clue(byte dir) {
|
||||||
|
this.dir = dir;
|
||||||
|
this.clueDir = dir;
|
||||||
|
}
|
||||||
|
final byte dir;
|
||||||
|
final int clueDir;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExportFormatFromFilled() {
|
void testExportFormatFromFilled() {
|
||||||
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE], Clues.createEmpty());
|
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE], Clues.createEmpty());
|
||||||
|
|
||||||
val clues = Clues.createEmpty();
|
val clues = Clues.createEmpty();
|
||||||
// Place a RIGHT clue at (0,0)
|
// Place a RIGHT clue at (0,0)
|
||||||
clues.setClue(0, CLUE_RIGHT);
|
clues.setClueLo(Idx.IDX_0_0.lo, RIGHT.dir);
|
||||||
// This creates a slot starting at (0,1)
|
// This creates a slot starting at (0,1)
|
||||||
// Terminate the 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
|
||||||
clues.setClue(OFF_0_5, CLUE_LEFT);
|
clues.setClueLo(Idx.IDX_0_5.lo, CLUE_LEFT);
|
||||||
var grid = new Gridded(clues.toGrid());
|
var grid = new Gridded(clues.toGrid());
|
||||||
|
|
||||||
var clueMap = new long[300];
|
var clueMap = new long[SwedishGenerator.CLUE_INDEX_MAX_SIZE];
|
||||||
// key = (cellIndex << 2) | (direction)
|
// key = (cellIndex << 2) | (direction)
|
||||||
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
||||||
var lo = (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3) | (1L << OFF_0_4);
|
var lo = (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3) | (1L << OFF_0_4);
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
import static puzzle.SwedishGeneratorTest.*;
|
import static puzzle.SwedishGeneratorTest.*;
|
||||||
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_0_0;
|
||||||
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_0_1;
|
||||||
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_1_0;
|
||||||
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_1_1;
|
||||||
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_2_1;
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_0;
|
import static puzzle.SwedishGeneratorTest.OFF_0_0;
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_1;
|
import static puzzle.SwedishGeneratorTest.OFF_0_1;
|
||||||
import static puzzle.SwedishGeneratorTest.OFF_0_2;
|
import static puzzle.SwedishGeneratorTest.OFF_0_2;
|
||||||
@@ -41,7 +46,7 @@ public class MainTest {
|
|||||||
|
|
||||||
var clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
val key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
val key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
||||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
||||||
var grid = new Gridded(clues.toGrid());
|
var grid = new Gridded(clues.toGrid());
|
||||||
val g = grid.grid().g;
|
val g = grid.grid().g;
|
||||||
placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
|
placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
|
||||||
@@ -69,7 +74,7 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
void testForEachSlot() {
|
void testForEachSlot() {
|
||||||
var clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
||||||
var count = new AtomicInteger(0);
|
var count = new AtomicInteger(0);
|
||||||
clues.forEachSlot((key, lo, hi) -> {
|
clues.forEachSlot((key, lo, hi) -> {
|
||||||
count.incrementAndGet();
|
count.incrementAndGet();
|
||||||
@@ -90,7 +95,7 @@ public class MainTest {
|
|||||||
public void testGridBasics() {
|
public void testGridBasics() {
|
||||||
var clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
val key = Slot.packSlotKey(OFF_2_1, CLUE_UP);
|
val key = Slot.packSlotKey(OFF_2_1, CLUE_UP);
|
||||||
clues.setClue(OFF_2_1, CLUE_UP);
|
clues.setClueLo(IDX_2_1.lo, CLUE_UP);
|
||||||
var grid = new Gridded(clues.toGrid());
|
var grid = new Gridded(clues.toGrid());
|
||||||
|
|
||||||
// Test set/get
|
// Test set/get
|
||||||
@@ -98,47 +103,47 @@ public class MainTest {
|
|||||||
val arr = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
val arr = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
Assertions.assertEquals(LETTER_A, arr.get(OFF_1_1));
|
Assertions.assertEquals(LETTER_A, arr.get(OFF_1_1));
|
||||||
Assertions.assertEquals(LETTER_Z, arr.get(OFF_0_1));
|
Assertions.assertEquals(LETTER_Z, arr.get(OFF_0_1));
|
||||||
Assertions.assertEquals(CLUE_UP, clues.digitAt(OFF_2_1));
|
Assertions.assertEquals(CLUE_UP, (byte) clues.digitAtLo(OFF_2_1));
|
||||||
|
|
||||||
// Test isLetterAt
|
// Test isLetterAt
|
||||||
Assertions.assertTrue(clues.notClue(OFF_0_0));
|
Assertions.assertTrue(clues.notClue(OFF_0_0));
|
||||||
Assertions.assertTrue(clues.notClue(OFF_1_2));
|
Assertions.assertTrue(clues.notClue(OFF_1_2));
|
||||||
Assertions.assertTrue(clues.notClue(OFF_2_3));
|
Assertions.assertTrue(clues.notClue(OFF_2_3));
|
||||||
Assertions.assertFalse(clues.isClue(OFF_1_1));
|
Assertions.assertFalse(clues.isClueLo(OFF_1_1));
|
||||||
|
|
||||||
// Test isDigitAt
|
// Test isDigitAt
|
||||||
Assertions.assertFalse(clues.isClue(0));
|
Assertions.assertFalse(clues.isClueLo(OFF_0_0));
|
||||||
Assertions.assertTrue(clues.isClue(OFF_2_1));
|
Assertions.assertTrue(clues.isClueLo(OFF_2_1));
|
||||||
Assertions.assertEquals(CLUE_UP, clues.digitAt(OFF_2_1));
|
Assertions.assertEquals(CLUE_UP, (byte) clues.digitAtLo(OFF_2_1));
|
||||||
Assertions.assertFalse(clues.isClue(OFF_2_3));
|
Assertions.assertFalse(clues.isClueLo(OFF_2_3));
|
||||||
Assertions.assertFalse(clues.isClue(OFF_1_1));
|
Assertions.assertFalse(clues.isClueLo(OFF_1_1));
|
||||||
|
|
||||||
// Test isLettercell
|
// Test isLettercell
|
||||||
Assertions.assertTrue(clues.notClue(OFF_0_0)); // 'A' is letter
|
Assertions.assertTrue(clues.notClue(OFF_0_0)); // 'A' is letter
|
||||||
Assertions.assertTrue(clues.isClue(OFF_2_1)); // digit
|
Assertions.assertTrue(clues.isClueLo(OFF_2_1)); // digit
|
||||||
Assertions.assertTrue(clues.notClue(OFF_1_1)); // '#' is lettercell
|
Assertions.assertTrue(clues.notClue(OFF_1_1)); // '#' is lettercell
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testCluesDeepCopy() {
|
public void testCluesDeepCopy() {
|
||||||
var grid = Clues.createEmpty();
|
var grid = Clues.createEmpty();
|
||||||
grid.setClue(OFF_0_0, (byte) 1);
|
grid.setClueLo(IDX_0_0.lo, (byte) 1);
|
||||||
grid.setClue(OFF_0_1, (byte) 2);
|
grid.setClueLo(IDX_0_1.lo, (byte) 2);
|
||||||
grid.setClue(OFF_1_0, (byte) 3);
|
grid.setClueLo(IDX_1_0.lo, (byte) 3);
|
||||||
grid.setClue(OFF_1_1, (byte) 0);
|
grid.setClueLo(IDX_1_1.lo, (byte) 0);
|
||||||
|
|
||||||
var copy = grid.deepCopyGrid();
|
var copy = grid.deepCopyGrid();
|
||||||
Assertions.assertEquals((byte) 1, copy.digitAt(0));
|
Assertions.assertEquals(1, copy.digitAtLo(0));
|
||||||
|
|
||||||
copy.setClue(0, (byte) 3);
|
copy.setClueLo(IDX_0_0.lo, (byte) 3);
|
||||||
Assertions.assertEquals((byte) 3, copy.digitAt(0));
|
Assertions.assertEquals(3, copy.digitAtLo(0));
|
||||||
Assertions.assertEquals((byte) 1, grid.digitAt(0)); // Original should be unchanged
|
Assertions.assertEquals(1, grid.digitAtLo(0)); // Original should be unchanged
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testMini() {
|
public void testMini() {
|
||||||
val idx = OFF_1_1;
|
val idx = IDX_1_1;
|
||||||
var clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
clues.setClue(idx, CLUE_LEFT);
|
clues.setClueLo(idx.lo, CLUE_LEFT);
|
||||||
Assertions.assertTrue(clues.isClue(idx));
|
Assertions.assertTrue(clues.isClueLo(idx.index));
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
void testMaskerCreation() {
|
void testMaskerCreation() {
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import puzzle.Export.Gridded;
|
|||||||
import puzzle.Export.IntListDTO;
|
import puzzle.Export.IntListDTO;
|
||||||
import puzzle.Export.LetterVisit.LetterAt;
|
import puzzle.Export.LetterVisit.LetterAt;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
|
import static puzzle.SwedishGeneratorTest.Idx.IDX_0_0;
|
||||||
|
|
||||||
public class SwedishGeneratorTest {
|
public class SwedishGeneratorTest {
|
||||||
|
|
||||||
@@ -67,29 +67,79 @@ public class SwedishGeneratorTest {
|
|||||||
static final byte CLUE_UP = 2;
|
static final byte CLUE_UP = 2;
|
||||||
static final byte CLUE_LEFT = 3;
|
static final byte CLUE_LEFT = 3;
|
||||||
|
|
||||||
static final int OFF_1_0 = Grid.offset(1, 0);
|
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_1 = Grid.offset(1, 1);
|
||||||
static final int OFF_1_2 = Grid.offset(1, 2);
|
static final int OFF_1_2 = Grid.offset(1, 2);
|
||||||
static final int OFF_2_1 = Grid.offset(2, 1);
|
static final int OFF_1_3 = Grid.offset(1, 3);
|
||||||
static final int OFF_2_3 = Grid.offset(2, 3);
|
static final int OFF_1_4 = Grid.offset(1, 4);
|
||||||
static final int OFF_0_0 = Grid.offset(0, 0);
|
static final int OFF_1_5 = Grid.offset(1, 5);
|
||||||
static final int OFF_0_4 = Grid.offset(0, 4);
|
static final int OFF_2_1 = Grid.offset(2, 1);
|
||||||
static final int OFF_0_5 = Grid.offset(0, 5);
|
static final int OFF_2_3 = Grid.offset(2, 3);
|
||||||
static final int OFF_0_1 = Grid.offset(0, 1);
|
static final int OFF_2_2 = Grid.offset(2, 2);
|
||||||
static final int OFF_0_2 = Grid.offset(0, 2);
|
static final int OFF_2_4 = Grid.offset(2, 4);
|
||||||
static final int OFF_0_3 = Grid.offset(0, 3);
|
static final int OFF_0_0 = Grid.offset(0, 0);
|
||||||
static final int OFF_2_0 = Grid.offset(2, 0);
|
static final int OFF_0_4 = Grid.offset(0, 4);
|
||||||
static final int OFF_2_5 = Grid.offset(2, 5);
|
static final int OFF_0_5 = Grid.offset(0, 5);
|
||||||
static final int OFF_3_5 = Grid.offset(3, 5);
|
static final int OFF_0_1 = Grid.offset(0, 1);
|
||||||
static final int OFF_4_5 = Grid.offset(4, 5);
|
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);
|
||||||
|
static final int OFF_3_0 = Grid.offset(3, 0);
|
||||||
|
static final int OFF_3_1 = Grid.offset(3, 1);
|
||||||
|
static final int OFF_3_2 = Grid.offset(3, 2);
|
||||||
|
static final int OFF_3_3 = Grid.offset(3, 3);
|
||||||
|
static final int OFF_3_4 = Grid.offset(3, 4);
|
||||||
static final byte D_BYTE_2 = CLUE_RIGHT;
|
static final byte D_BYTE_2 = CLUE_RIGHT;
|
||||||
|
|
||||||
|
enum Idx {
|
||||||
|
IDX_0_0(OFF_0_0, 0, 0),
|
||||||
|
IDX_0_1(OFF_0_1, 0, 1),
|
||||||
|
IDX_0_2(OFF_0_2, 0, 2),
|
||||||
|
IDX_0_3(OFF_0_3, 0, 3),
|
||||||
|
IDX_0_4(OFF_0_4, 0, 4),
|
||||||
|
IDX_0_5(OFF_0_5, 0, 5),
|
||||||
|
IDX_1_0(OFF_1_0, 1, 0),
|
||||||
|
IDX_1_1(OFF_1_1, 1, 1),
|
||||||
|
IDX_1_2(OFF_1_2, 1, 2),
|
||||||
|
IDX_1_3(OFF_1_3, 1, 3),
|
||||||
|
IDX_1_4(OFF_1_4, 1, 4),
|
||||||
|
IDX_1_5(OFF_1_5, 1, 5),
|
||||||
|
IDX_2_0(OFF_2_0, 2, 0),
|
||||||
|
IDX_2_1(OFF_2_1, 2, 1),
|
||||||
|
IDX_2_2(OFF_2_2, 2, 2),
|
||||||
|
IDX_2_3(OFF_2_3, 2, 3),
|
||||||
|
IDX_2_4(OFF_2_4, 2, 4),
|
||||||
|
IDX_2_5(OFF_2_5, 2, 5),
|
||||||
|
IDX_3_0(OFF_3_0, 3, 0),
|
||||||
|
IDX_3_1(OFF_3_1, 3, 1),
|
||||||
|
IDX_3_2(OFF_3_2, 3, 2),
|
||||||
|
IDX_3_3(OFF_3_3, 3, 3),
|
||||||
|
IDX_3_4(OFF_3_4, 3, 4),
|
||||||
|
IDX_3_5(OFF_3_5, 3, 5);
|
||||||
|
Idx(int idx, int r, int c) {
|
||||||
|
this.index = idx;
|
||||||
|
this.r = r;
|
||||||
|
this.c = c;
|
||||||
|
if (isLo(idx)) {
|
||||||
|
this.lo = 1L << idx;
|
||||||
|
this.hi = 0L;
|
||||||
|
} else {
|
||||||
|
this.lo = 0L;
|
||||||
|
this.hi = 1L << (idx & 63);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int index, r, c;
|
||||||
|
final long lo, hi;
|
||||||
|
}
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotAllLetters() {
|
void testPatternForSlotAllLetters() {
|
||||||
var grid = new Gridded(createEmpty());
|
var grid = new Gridded(createEmpty());
|
||||||
var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
||||||
val clues = Clues.createEmpty();
|
val clues = Clues.createEmpty();
|
||||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
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);
|
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));
|
val map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
assertEquals(LETTER_A, map.get(OFF_0_1));
|
assertEquals(LETTER_A, map.get(OFF_0_1));
|
||||||
@@ -237,7 +287,7 @@ public class SwedishGeneratorTest {
|
|||||||
void testForEachSlotAndExtractSlots() {
|
void testForEachSlotAndExtractSlots() {
|
||||||
// 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 clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
||||||
var dict = new Dict(WORDS2);
|
var dict = new Dict(WORDS2);
|
||||||
var slots = extractSlots(clues, dict.index());
|
var slots = extractSlots(clues, dict.index());
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
@@ -257,7 +307,7 @@ public class SwedishGeneratorTest {
|
|||||||
assertTrue(f1 >= 1_000_000_000L);
|
assertTrue(f1 >= 1_000_000_000L);
|
||||||
|
|
||||||
// Add a slot
|
// Add a slot
|
||||||
grid.setClue(OFF_0_0, D_BYTE_2);
|
grid.setClueLo(IDX_0_0.lo, D_BYTE_2);
|
||||||
var f2 = gen.maskFitness(grid, 18);
|
var f2 = gen.maskFitness(grid, 18);
|
||||||
assertTrue(f2 < f1);
|
assertTrue(f2 < f1);
|
||||||
}
|
}
|
||||||
@@ -381,7 +431,7 @@ public class SwedishGeneratorTest {
|
|||||||
// Empty grid: huge penalty
|
// Empty grid: huge penalty
|
||||||
var fitEmpty = gen.maskFitness(grid, 18);
|
var fitEmpty = gen.maskFitness(grid, 18);
|
||||||
assertTrue(fitEmpty >= 1_000_000_000L);
|
assertTrue(fitEmpty >= 1_000_000_000L);
|
||||||
grid.setClue(0, D_BYTE_2); // Right from 0,0. Len 2 if 3x3.
|
grid.setClueLo(IDX_0_0.lo, D_BYTE_2); // Right from 0,0. Len 2 if 3x3.
|
||||||
var fitOne = gen.maskFitness(grid, 18);
|
var fitOne = gen.maskFitness(grid, 18);
|
||||||
assertTrue(fitOne < fitEmpty);
|
assertTrue(fitOne < fitEmpty);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user