introduce bitloops
This commit is contained in:
@@ -3,7 +3,9 @@ package puzzle;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import lombok.experimental.Delegate;
|
import lombok.experimental.Delegate;
|
||||||
|
import lombok.val;
|
||||||
import puzzle.Export.Gridded.Replacar.Cell;
|
import puzzle.Export.Gridded.Replacar.Cell;
|
||||||
|
import puzzle.SwedishGenerator.Clues;
|
||||||
import puzzle.SwedishGenerator.Dict;
|
import puzzle.SwedishGenerator.Dict;
|
||||||
import puzzle.SwedishGenerator.FillResult;
|
import puzzle.SwedishGenerator.FillResult;
|
||||||
import puzzle.SwedishGenerator.Grid;
|
import puzzle.SwedishGenerator.Grid;
|
||||||
@@ -36,6 +38,25 @@ public record Export() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record Clued(Clues mask) {
|
||||||
|
|
||||||
|
String gridToString() {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (var r = 0; r < R; r++) {
|
||||||
|
if (r > 0) sb.append('\n');
|
||||||
|
for (var c = 0; c < C; c++) {
|
||||||
|
val idx = Grid.offset(r, c);
|
||||||
|
if (mask.isClue(idx))
|
||||||
|
sb.append((char) mask.digitAt(idx));
|
||||||
|
else {
|
||||||
|
sb.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
record Gridded(@Delegate Grid grid) {
|
record Gridded(@Delegate Grid grid) {
|
||||||
|
|
||||||
static boolean isLetter(byte b) { return (b & SwedishGenerator.B64) != SwedishGenerator.B0; }
|
static boolean isLetter(byte b) { return (b & SwedishGenerator.B64) != SwedishGenerator.B0; }
|
||||||
@@ -161,7 +182,7 @@ public record Export() {
|
|||||||
|
|
||||||
public record ExportedPuzzle(String[] grid, WordOut[] words, int difficulty, Rewards rewards) { }
|
public record ExportedPuzzle(String[] grid, WordOut[] words, int difficulty, Rewards rewards) { }
|
||||||
|
|
||||||
public record PuzzleResult(SwedishGenerator swe, Dict dict, Gridded mask, FillResult filled) {
|
public record PuzzleResult(SwedishGenerator swe, Dict dict, Clued mask, FillResult filled) {
|
||||||
|
|
||||||
boolean inBounds(int idx) { return idx >= 0 && idx < SwedishGenerator.SIZE; }
|
boolean inBounds(int idx) { return idx >= 0 && idx < SwedishGenerator.SIZE; }
|
||||||
Placed extractPlacedFromSlot(Slot s, long lemma) { return new Placed(lemma, s.key(), s.walk().toArray()); }
|
Placed extractPlacedFromSlot(Slot s, long lemma) { return new Placed(lemma, s.key(), s.walk().toArray()); }
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ public class Main {
|
|||||||
val stack = new int[STACK_SIZE];
|
val stack = new int[STACK_SIZE];
|
||||||
var swe = new SwedishGenerator(rng, stack);
|
var swe = new SwedishGenerator(rng, stack);
|
||||||
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
||||||
var filled = fillMask(rng, mask, dict.index());
|
var filled = fillMask(rng, mask.toGrid(), dict.index());
|
||||||
|
|
||||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||||
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
||||||
@@ -380,7 +380,7 @@ public class Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {
|
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {
|
||||||
return new PuzzleResult(swe, dict, new Gridded(mask), filled);
|
return new PuzzleResult(swe, dict, new Clued(mask), filled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.verbose && filled.ok()) {
|
if (opts.verbose && filled.ok()) {
|
||||||
|
|||||||
@@ -203,13 +203,17 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
if ((ri & 64) == 0) {
|
if ((ri & 64) == 0) {
|
||||||
long mask = 1L << ri;
|
long mask = 1L << ri;
|
||||||
lo |= mask;
|
lo |= mask;
|
||||||
if ((idx & 1) != 0) vlo |= mask; else vlo &= ~mask;
|
if ((idx & 1) != 0) vlo |= mask;
|
||||||
if ((idx & 2) != 0) rlo |= mask; else rlo &= ~mask;
|
else vlo &= ~mask;
|
||||||
|
if ((idx & 2) != 0) rlo |= mask;
|
||||||
|
else rlo &= ~mask;
|
||||||
} else {
|
} else {
|
||||||
long mask = 1L << (ri & 63);
|
long mask = 1L << (ri & 63);
|
||||||
hi |= mask;
|
hi |= mask;
|
||||||
if ((idx & 1) != 0) vhi |= mask; else vhi &= ~mask;
|
if ((idx & 1) != 0) vhi |= mask;
|
||||||
if ((idx & 2) != 0) rhi |= mask; else rhi &= ~mask;
|
else vhi &= ~mask;
|
||||||
|
if ((idx & 2) != 0) rhi |= mask;
|
||||||
|
else rhi &= ~mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public byte digitAt(int idx) {
|
public byte digitAt(int idx) {
|
||||||
@@ -270,42 +274,21 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Grid {
|
static record Grid(byte[] g, long lo, long hi) {
|
||||||
|
|
||||||
final byte[] g;
|
|
||||||
long lo, hi;
|
|
||||||
|
|
||||||
public Grid(byte[] g) { this(g, 0, 0); }
|
public Grid(byte[] g) { this(g, 0, 0); }
|
||||||
public Grid(byte[] g, long lo, long hi) {
|
|
||||||
this.g = g;
|
|
||||||
this.lo = lo;
|
|
||||||
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]; }
|
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); }
|
||||||
Grid deepCopyGrid() { return new Grid(g.clone(), lo, hi); }
|
|
||||||
public byte byteAt(int pos) { return g[pos]; }
|
public byte byteAt(int pos) { return g[pos]; }
|
||||||
|
|
||||||
void setClue(int idx, byte ch) {
|
|
||||||
g[idx] = ch;
|
|
||||||
if ((idx & 64) == 0) lo |= (1L << idx);
|
|
||||||
else hi |= (1L << (idx & 63));
|
|
||||||
}
|
|
||||||
void setLetter(int idx, byte ch) { g[idx] = ch; }
|
void setLetter(int idx, byte ch) { g[idx] = ch; }
|
||||||
void clearletter(int idx) { g[idx] = DASH; }
|
void clearletter(int idx) { g[idx] = DASH; }
|
||||||
void clearClue(int idx) {
|
|
||||||
g[idx] = DASH;
|
|
||||||
if ((idx & 64) == 0) lo &= ~(1L << idx);
|
|
||||||
else hi &= ~(1L << (idx & 63));
|
|
||||||
}
|
|
||||||
boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||||
boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||||
boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||||
boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||||
|
|
||||||
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||||
|
|
||||||
void forEachSlot(SlotVisitor visitor) {
|
void forEachSlot(SlotVisitor visitor) {
|
||||||
@@ -606,7 +589,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
return penalty;
|
return penalty;
|
||||||
}
|
}
|
||||||
|
|
||||||
SwedishGenerator.Clues randomMask() {
|
Clues randomMask() {
|
||||||
var g = Clues.createEmpty();
|
var g = Clues.createEmpty();
|
||||||
for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) {
|
for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) {
|
||||||
idx = rng.randint(0, SIZE_MIN_1);
|
idx = rng.randint(0, SIZE_MIN_1);
|
||||||
@@ -619,18 +602,18 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
Clues mutate(Clues grid) {
|
Clues mutate(Clues clues) {
|
||||||
var g = grid.deepCopyGrid();
|
var c = clues.deepCopyGrid();
|
||||||
int ri;
|
int ri;
|
||||||
var bytes = MUTATE_RI[rng.randint(0, SIZE_MIN_1)];
|
var bytes = MUTATE_RI[rng.randint(0, SIZE_MIN_1)];
|
||||||
for (var k = 0; k < 4; k++) {
|
for (var k = 0; k < 4; k++) {
|
||||||
ri = bytes[rng.randint(0, 624)];
|
ri = bytes[rng.randint(0, 624)];
|
||||||
if (!g.clueless(ri)) {
|
if (!c.clueless(ri)) {
|
||||||
var d_idx = rng.randint2bitByte();
|
var d_idx = rng.randint2bitByte();
|
||||||
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(ri, d_idx)])) g.setClue(ri, d_idx);
|
if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(ri, d_idx)])) c.setClue(ri, d_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return g;
|
return c;
|
||||||
}
|
}
|
||||||
Clues crossover(Clues a, Clues other) {
|
Clues crossover(Clues a, Clues other) {
|
||||||
var out = a.deepCopyGrid();
|
var out = a.deepCopyGrid();
|
||||||
@@ -693,7 +676,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwedishGenerator.Grid generateMask(int popSize, int gens, int pairs) {
|
public Clues generateMask(int popSize, int gens, int pairs) {
|
||||||
class GridAndFit {
|
class GridAndFit {
|
||||||
|
|
||||||
Clues grid;
|
Clues grid;
|
||||||
@@ -745,10 +728,10 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
var x = pop.get(i);
|
var x = pop.get(i);
|
||||||
if (x.fit() < best.fit()) best = x;
|
if (x.fit() < best.fit()) best = x;
|
||||||
}
|
}
|
||||||
return best.grid.toGrid();
|
return best.grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long patternForSlot(Grid grid, SwedishGenerator.Slot s) {
|
static long patternForSlot(Grid grid, Slot s) {
|
||||||
if ((s.lo & ~grid.lo) == 0 && (s.hi & ~grid.hi) == 0) return 0;
|
if ((s.lo & ~grid.lo) == 0 && (s.hi & ~grid.hi) == 0) return 0;
|
||||||
long p = 0;
|
long p = 0;
|
||||||
if (s.increasing()) {
|
if (s.increasing()) {
|
||||||
@@ -855,7 +838,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int[] candidateInfoForPattern(long[] res, long pattern, SwedishGenerator.DictEntry entry, int lenb) {
|
static int[] candidateInfoForPattern(long[] res, long pattern, DictEntry entry, int lenb) {
|
||||||
int numLongs = entry.numlong;
|
int numLongs = entry.numlong;
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
|
|
||||||
@@ -927,10 +910,10 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]);
|
for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]);
|
||||||
|
|
||||||
}
|
}
|
||||||
public static SwedishGenerator.FillResult fillMask(SwedishGenerator.Rng rng, Grid mask, DictEntry[] dictIndex) {
|
public static FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex) {
|
||||||
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
||||||
val NO_LOG = (!Main.VERBOSE || multiThreaded);
|
val NO_LOG = (!Main.VERBOSE || multiThreaded);
|
||||||
val grid = mask.deepCopyGrid();
|
val grid = mask;
|
||||||
val used = new Bit1029();
|
val used = new Bit1029();
|
||||||
val assigned = new long[CLUE_INDEX_MAX_SIZE];
|
val assigned = new long[CLUE_INDEX_MAX_SIZE];
|
||||||
val bitset = new long[2500];
|
val bitset = new long[2500];
|
||||||
@@ -971,7 +954,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
System.out.print("\r" + Strings.padRight(msg, 120));
|
System.out.print("\r" + Strings.padRight(msg, 120));
|
||||||
System.out.flush();
|
System.out.flush();
|
||||||
}
|
}
|
||||||
SwedishGenerator.Pick chooseMRV() {
|
Pick chooseMRV() {
|
||||||
Slot best = null;
|
Slot best = null;
|
||||||
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) {
|
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) {
|
||||||
var s = slots[i];
|
var s = slots[i];
|
||||||
@@ -1079,7 +1062,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
|||||||
var ok = solver.backtrack(0);
|
var ok = solver.backtrack(0);
|
||||||
// final progress line
|
// final progress line
|
||||||
|
|
||||||
var res = new FillResult(ok, new Gridded(grid), assigned, new SwedishGenerator.FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
|
var res = new FillResult(ok, new Gridded(grid), assigned, new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
|
||||||
if (!multiThreaded) {
|
if (!multiThreaded) {
|
||||||
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
||||||
System.out.flush();
|
System.out.flush();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import puzzle.Export.Gridded;
|
import puzzle.Export.Gridded;
|
||||||
import puzzle.Export.Placed;
|
import puzzle.Export.Placed;
|
||||||
@@ -27,11 +28,15 @@ public class ExportFormatTest {
|
|||||||
@Test
|
@Test
|
||||||
void testExportFormatFromFilled() {
|
void testExportFormatFromFilled() {
|
||||||
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]);
|
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]);
|
||||||
var grid = Grid.createEmpty();
|
|
||||||
|
|
||||||
|
val clues = Clues.createEmpty();
|
||||||
// Place a RIGHT clue at (0,0)
|
// Place a RIGHT clue at (0,0)
|
||||||
grid.setClue(0, CLUE_RIGHT);
|
clues.setClue(0, CLUE_RIGHT);
|
||||||
// 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
|
||||||
|
clues.setClue(Grid.offset(0, 5), CLUE_LEFT);
|
||||||
|
var grid = clues.toGrid();
|
||||||
|
|
||||||
|
|
||||||
var clueMap = new long[300];
|
var clueMap = new long[300];
|
||||||
// key = (cellIndex << 2) | (direction)
|
// key = (cellIndex << 2) | (direction)
|
||||||
@@ -43,8 +48,7 @@ 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 the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH
|
|
||||||
grid.setClue(Grid.offset(0, 5), CLUE_LEFT);
|
|
||||||
|
|
||||||
var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats(0, 0, 0, 0));
|
var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats(0, 0, 0, 0));
|
||||||
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
||||||
|
|||||||
@@ -33,11 +33,10 @@ public class MainTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExtractSlots() {
|
void testExtractSlots() {
|
||||||
var grid = Grid.createEmpty();
|
|
||||||
|
|
||||||
// Set up digits on the grid to create slots.
|
var clues = Clues.createEmpty();
|
||||||
// CLUE_RIGHT at OFF_0_0 -> slot at (0,1), (0,2)
|
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
||||||
grid.setClue(OFF_0_0, CLUE_RIGHT);
|
var grid = clues.toGrid();
|
||||||
grid.setLetter(OFF_0_1, LETTER_A);
|
grid.setLetter(OFF_0_1, LETTER_A);
|
||||||
grid.setLetter(OFF_0_2, LETTER_B);
|
grid.setLetter(OFF_0_2, LETTER_B);
|
||||||
|
|
||||||
@@ -63,8 +62,9 @@ public class MainTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testForEachSlot() {
|
void testForEachSlot() {
|
||||||
var grid = Grid.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
grid.setClue(OFF_0_0, CLUE_RIGHT); // right
|
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
||||||
|
var grid = clues.toGrid();
|
||||||
|
|
||||||
var count = new AtomicInteger(0);
|
var count = new AtomicInteger(0);
|
||||||
grid.forEachSlot((key, lo, hi) -> {
|
grid.forEachSlot((key, lo, hi) -> {
|
||||||
@@ -84,11 +84,12 @@ public class MainTest {
|
|||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testGridBasics() {
|
public void testGridBasics() {
|
||||||
var grid = Grid.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
|
clues.setClue(OFF_1_2, CLUE_UP);
|
||||||
|
var grid = clues.toGrid();
|
||||||
|
|
||||||
// Test set/get
|
// Test set/get
|
||||||
grid.setLetter(OFF_0_0, LETTER_A);
|
grid.setLetter(OFF_0_0, LETTER_A);
|
||||||
grid.setClue(OFF_1_2, CLUE_UP);
|
|
||||||
grid.setLetter(OFF_2_3, LETTER_Z);
|
grid.setLetter(OFF_2_3, LETTER_Z);
|
||||||
|
|
||||||
Assertions.assertEquals(LETTER_A, grid.byteAt(OFF_0_0));
|
Assertions.assertEquals(LETTER_A, grid.byteAt(OFF_0_0));
|
||||||
@@ -114,28 +115,27 @@ public class MainTest {
|
|||||||
Assertions.assertTrue(grid.isClue(OFF_1_2)); // digit
|
Assertions.assertTrue(grid.isClue(OFF_1_2)); // digit
|
||||||
Assertions.assertTrue(grid.notClue(OFF_1_1)); // '#' is lettercell
|
Assertions.assertTrue(grid.notClue(OFF_1_1)); // '#' is lettercell
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGridDeepCopy() {
|
public void testCluesDeepCopy() {
|
||||||
var grid = Grid.createEmpty();
|
var grid = Clues.createEmpty();
|
||||||
grid.setLetter(Grid.offset(0, 0), (byte) 'A');
|
grid.setClue(Grid.offset(0, 0), (byte) 1);
|
||||||
grid.setLetter(Grid.offset(0, 1), (byte) 'B');
|
grid.setClue(Grid.offset(0, 1), (byte) 2);
|
||||||
grid.setLetter(Grid.offset(1, 0), (byte) 'C');
|
grid.setClue(Grid.offset(1, 0), (byte) 3);
|
||||||
grid.setLetter(Grid.offset(1, 1), (byte) 'D');
|
grid.setClue(Grid.offset(1, 1), (byte) 0);
|
||||||
|
|
||||||
var copy = grid.deepCopyGrid();
|
var copy = grid.deepCopyGrid();
|
||||||
Assertions.assertEquals((byte) 'A', copy.byteAt(0));
|
Assertions.assertEquals((byte) 1, copy.digitAt(0));
|
||||||
|
|
||||||
copy.setLetter(0, (byte) 'X');
|
copy.setClue(0, (byte) 3);
|
||||||
Assertions.assertEquals((byte) 'X', copy.byteAt(0));
|
Assertions.assertEquals((byte) 3, copy.digitAt(0));
|
||||||
Assertions.assertEquals((byte) 'A', grid.byteAt(0)); // Original should be unchanged
|
Assertions.assertEquals((byte) 1, grid.digitAt(0)); // Original should be unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMini() {
|
public void testMini() {
|
||||||
var grid = Grid.createEmpty();
|
|
||||||
val idx = OFF_1_1;
|
val idx = OFF_1_1;
|
||||||
grid.setClue(idx, CLUE_LEFT);
|
var clues = Clues.createEmpty();
|
||||||
|
clues.setClue(idx, CLUE_LEFT);
|
||||||
|
var grid = clues.toGrid();
|
||||||
Assertions.assertTrue(grid.isClue(idx));
|
Assertions.assertTrue(grid.isClue(idx));
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
@@ -179,8 +179,8 @@ public class MainTest {
|
|||||||
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
||||||
Assertions.assertEquals(18, res.filled().wordCount(), "Number of assigned words changed");
|
Assertions.assertEquals(18, res.filled().wordCount(), "Number of assigned words changed");
|
||||||
Assertions.assertEquals("SLEDE", Lemma.asWord(res.filled().clueMap()[282]));
|
Assertions.assertEquals("SLEDE", Lemma.asWord(res.filled().clueMap()[282]));
|
||||||
Assertions.assertEquals(74732156493031040L, res.filled().grid().grid().lo);
|
Assertions.assertEquals(74732156493031040L, res.filled().grid().grid().lo());
|
||||||
Assertions.assertEquals(193L, res.filled().grid().grid().hi);
|
Assertions.assertEquals(193L, res.filled().grid().grid().hi());
|
||||||
}
|
}
|
||||||
boolean isLetter(byte b) { return (b & 64) != 0; }
|
boolean isLetter(byte b) { return (b & 64) != 0; }
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -91,21 +91,6 @@ 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);
|
|
||||||
|
|
||||||
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(OFF_0_0));
|
|
||||||
copy.setLetter(OFF_0_0, LETTER_B);
|
|
||||||
assertEquals('B', copy.byteAt(OFF_0_0));
|
|
||||||
assertEquals('A', grid.byteAt(OFF_0_0));
|
assertEquals('A', grid.byteAt(OFF_0_0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,11 +252,11 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testForEachSlotAndExtractSlots() {
|
void testForEachSlotAndExtractSlots() {
|
||||||
var grid = Grid.createEmpty();
|
|
||||||
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
|
|
||||||
// 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)
|
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
||||||
|
var clues = Clues.createEmpty();
|
||||||
|
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
||||||
|
var grid = clues.toGrid();
|
||||||
|
|
||||||
|
|
||||||
var slots = extractSlots(grid);
|
var slots = extractSlots(grid);
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
@@ -430,20 +415,8 @@ public class SwedishGeneratorTest {
|
|||||||
// Empty grid: huge penalty
|
// Empty grid: huge penalty
|
||||||
var fitEmpty = gen.maskFitness(grid);
|
var 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.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.
|
||||||
var fitOne = gen.maskFitness(grid);
|
var fitOne = gen.maskFitness(grid);
|
||||||
assertTrue(fitOne < fitEmpty);
|
assertTrue(fitOne < fitEmpty);
|
||||||
|
|
||||||
// Test penalty for TARGET_CLUES
|
|
||||||
// TARGET_CLUES = SIZE >>> 2. For 3x3 it is 9 >>> 2 = 2.
|
|
||||||
// If we have 1 clue, |1 - 2| * 16000 = 16000 penalty.
|
|
||||||
// If we add another clue at a distant position.
|
|
||||||
var grid2 = Grid.createEmpty();
|
|
||||||
grid2.setClue(0, D_BYTE_2);
|
|
||||||
grid2.setClue(Grid.offset(2, 0), D_BYTE_2);
|
|
||||||
// Now clueCount = 2, penalty should be 0 from target clues.
|
|
||||||
// But they might have other penalties (short slots, etc.)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user