Files
puzzle-generator/src/test/java/puzzle/MainTest.java
2026-01-23 12:51:13 +01:00

223 lines
8.8 KiB
Java

package puzzle;
import module java.base;
import anno.DictGen;
import lombok.val;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import puzzle.Export.Vestigium;
import puzzle.Export.Signa;
import puzzle.Export.Puzzle;
import puzzle.Export.Lettrix;
import puzzle.Export.PuzzleResult;
import puzzle.Export.Rewards;
import puzzle.Main.Opts;
import puzzle.SwedishGenerator.Rng;
import puzzle.dict950.DictData950;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static precomp.Const9x8.*;
import static precomp.Const9x8.Cell.*;
import static puzzle.Export.Clue.*;
import static puzzle.LemmaData.AB;
import static puzzle.LemmaData.AZ;
import static puzzle.SwedishGenerator.Lemma;
import static puzzle.SwedishGenerator.Slotinfo;
import static puzzle.SwedishGenerator.fillMask;
@DictGen(
packageName = "puzzle.dict950",
className = "DictData950",
scv = "/home/mike/dev/puzzle-generator/nl_score_hints_v4.csv",
simpleMax = 950,
minLen = 2,
maxLen = 8
)
public class MainTest {
static final Opts opts = new Main.Opts() {{
this.seed = 12348;
this.clueSize = 4;
this.pop = 4; // Tiny population
this.offspring = 18;
this.gens = 20; // Very few generations
this.minSimplicity = 0;
this.threads = 1;
this.tries = 1;
this.verbose = false;
}};
@Test
void testExtractSlots() {
var clues = Signa.of(r0c0d1);
var grid = new Puzzle(clues);
val g = grid.grid().g;
GridBuilder.placeWord(grid.grid(), g, r0c0d1.slotKey, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
var slots = Masker.slots(clues.c(), DictData950.DICT950.index(), DictData950.DICT950.reversed());
assertEquals(1, slots.length);
var s = slots[0];
assertEquals(8, Masker.Slot.length(s.lo(), s.hi()));
var cells = Puzzle.cellWalk(s.key(), s.lo(), s.hi()).mapToObj(c -> Masker.IT[c]).toArray(rci[]::new);
assertEquals(0, cells[1].r());
assertEquals(1, cells[1].c());
assertEquals(0, cells[2].r());
assertEquals(2, cells[2].c());
}
@Test
void testStaticSlotMethods() {
// Test static horiz
// dir 1 (right) is horizontal
assertTrue(Masker.Slot.horiz(1));
// dir 0 (down) is vertical
assertFalse(Masker.Slot.horiz(0));
}
@Test
void testForEachSlot() {
var count = new AtomicInteger(0);
Masker.forEachSlot(Signa.of(r0c0d1).c(), (key, lo, hi) -> {
count.incrementAndGet();
assertEquals(8, Long.bitCount(lo) + Long.bitCount(hi));
assertEquals(0, Masker.IT[Long.numberOfTrailingZeros(lo)].r());
assertEquals(1, Masker.IT[Long.numberOfTrailingZeros(lo)].c());
});
assertEquals(1, count.get());
}
@Test
public void testHoriz() {
assertTrue(Masker.Slot.horiz(1)); // Right
assertTrue(Masker.Slot.horiz(3)); // Left
assertFalse(Masker.Slot.horiz(0)); // Down
assertFalse(Masker.Slot.horiz(2)); // Up
assertFalse(Masker.Slot.horiz(4)); //
assertFalse(Masker.Slot.horiz(5)); //
}
@Test
public void testGridBasics() {
var clues = Signa.of(r2c1d2);
var grid = new Puzzle(clues);
r1c1.or(r0c1).lo();
// Test set/get
GridBuilder.placeWord(grid.grid(), grid.grid().g, r2c1d2.slotKey, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ);
val map = grid.collect(Collectors.toMap(Lettrix::index, Lettrix::letter));
Assertions.assertEquals(LETTER_A, map.get(OFF_1_1));
Assertions.assertEquals(LETTER_Z, map.get(OFF_0_1));
var clueMap = clues.stream().collect(Collectors.toMap(Vestigium::index, Vestigium::clue));
Assertions.assertEquals(1, clueMap.size());
Assertions.assertEquals(UP2.dir, clueMap.get(OFF_2_1));
// Test isLetterAt
Assertions.assertFalse(clueMap.containsKey(OFF_0_0));
Assertions.assertFalse(clueMap.containsKey(OFF_1_2));
Assertions.assertFalse(clueMap.containsKey(OFF_2_3));
Assertions.assertFalse(clueMap.containsKey(OFF_1_1));
// Test isDigitAt
Assertions.assertFalse(clues.isClueLo(OFF_0_0));
Assertions.assertTrue(clues.isClueLo(OFF_2_1));
clueMap = clues.stream().collect(Collectors.toMap(Vestigium::index, Vestigium::clue));
Assertions.assertEquals(UP2.dir, clueMap.get(OFF_2_1));
Assertions.assertFalse(clues.isClueLo(OFF_2_3));
Assertions.assertFalse(clues.isClueLo(OFF_1_1));
// Test isLettercell
Assertions.assertTrue(clues.notClue(OFF_0_0)); // 'A' is letter
Assertions.assertTrue(clues.isClueLo(OFF_2_1)); // digit
Assertions.assertTrue(clues.notClue(OFF_1_1)); // '#' is lettercell
}
@Test
public void testCluesDeepCopy() {
var clues = Signa.of(r0c0d1, r0c1d2, r1c0d3, r1c1d0);
var copy = clues.deepCopyGrid();
var clueMap = clues.stream().collect(Collectors.toMap(Vestigium::index, Vestigium::clue));
Assertions.assertEquals(RIGHT1.dir, clueMap.get(OFF_0_0));
copy.setClue(r0c0d0);
var copied = copy.stream().collect(Collectors.toMap(Vestigium::index, Vestigium::clue));
Assertions.assertEquals(DOWN0.dir, copied.get(OFF_0_0));
Assertions.assertEquals(RIGHT1.dir, clueMap.get(OFF_0_0));
}
@Test
public void testMini() {
Assertions.assertTrue(Signa.of(r1c1d3).isClueLo(OFF_1_1));
}
@Test
void testFiller2() {
var mask = Signa.of(
r0c0d1,
r0c3d0, r0c4d0, r0c5d0, r0c6d0, r0c7d0, r0c8d0,
r1c0d1,
r2c0d1,
r3c0d3, r3c3d3,
r4c0d3, r4c3d0, r4c6d3,
r5c0d3,
r6c0d3,
r7c0d2, r7c1d2, r7c2d2, r7c8d3
);
Assertions.assertEquals(20, mask.clueCount());
val map = mask.stream().collect(Collectors.toMap(Vestigium::index, Vestigium::clue));
Assertions.assertEquals(20, map.size());
var slots = Masker.slots(mask.c(), DictData950.DICT950.index(), DictData950.DICT950.reversed());
// var filled = fillMask(rng, slotInfo, grid, false);
// val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0));
}
@Test
void testFiller() {
System.out.println(DictData950.DICT950.index().length);
val rng = new Rng(-343913721);
var mask = Signa.of(
r0c3d3, r0c6d3, r0c7d0, r0c8d0,
r1c1d1,
r2c1d1,
r3c3d3, r3c6d0,
r4c2d3, r4c3d1,
r5c1d1,
r6c1d1, r6c8d2,
r7c0d2, r7c1d1, r7c4d2, r7c5d2, r7c8d3
);
var slotInfo = Masker.slots(mask.c(), DictData950.DICT950.index(), DictData950.DICT950.reversed());
var grid = Masker.grid(slotInfo);
var filled = fillMask(rng, slotInfo, grid.lo, grid.hi, grid.g);
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
Assertions.assertEquals("BEADEMT", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get()));
Assertions.assertEquals(74732156493031040L, grid.lo);
Assertions.assertEquals(193L, grid.hi);
var g = new Puzzle(grid, mask.c());
var result = new PuzzleResult(mask, g, slotInfo, filled);
var aa = result.exportFormatFromFilled(new Rewards(1, 1, 1), Masker.IT);
result.gridGridToString();
System.out.println(String.join("\n", aa.grid()));
}
@Test
public void testAttempt() {
PuzzleResult res = null;
int foundSeed = -1;
for (int i = 0; i < 50; i++) {
int seed = opts.seed + i;
res = Main.attempt(new Rng(seed), DictData950.DICT950, opts);
if (res != null && res.filled().ok()) {
foundSeed = seed;
System.out.println("[DEBUG_LOG] Seed found: " + seed);
System.out.println("[DEBUG_LOG] ClueMap Size: " + Slotinfo.wordCount(0, res.slots()));
System.out.println("[DEBUG_LOG] Grid:");
System.out.println(res.gridRenderHuman());
System.out.println(res.gridGridToString());
break;
}
}
Assertions.assertNotNull(res, "Puzzle generation failed (null result)");
Assertions.assertTrue(res.filled().ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
}
boolean isLetter(byte b) { return (b & 64) != 0; }
@Test public void testIsLetterA() { assertTrue(isLetter((byte) 'A')); }
@Test public void testIsLetterZ() { assertTrue(isLetter((byte) 'Z')); }
}