package puzzle; import lombok.val; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import puzzle.Export.PuzzleResult; import puzzle.Export.Rewards; import puzzle.SwedishGenerator.Rng; import puzzle.SwedishGenerator.Slot; import java.util.concurrent.atomic.AtomicInteger; 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 puzzle.SwedishGenerator.*; import static puzzle.SwedishGenerator.DASH; public class MainTest { @Test void testExtractSlots() { var grid = Grid.createEmpty(); // Set up digits on the grid to create slots. // '2' (right) at (0,0) -> slot at (0,1), (0,2) grid.setClue(0, (byte) '2'); grid.setLetter(Grid.offset(0, 1), (byte) 'A'); grid.setLetter(Grid.offset(0, 2), (byte) 'B'); var slots = extractSlots(grid); assertEquals(1, slots.size()); var s = slots.getFirst(); assertEquals(8, s.len()); var cells = s.walk().toArray(); assertEquals(0, Grid.r(cells[0])); assertEquals(1, Grid.c(cells[0])); assertEquals(0, Grid.r(cells[1])); assertEquals(2, Grid.c(cells[1])); } @Test void testStaticSlotMethods() { // Test static horiz // dir 2 (right) is horizontal assertTrue(Slot.horiz(2)); // dir 3 (down) is vertical assertFalse(Slot.horiz(3)); } @Test void testForEachSlot() { var grid = Grid.createEmpty(); grid.setClue(0, (byte) '2'); // right var count = new AtomicInteger(0); grid.forEachSlot((key, lo, hi) -> { count.incrementAndGet(); assertEquals(8, Long.bitCount(lo) + Long.bitCount(hi)); assertEquals(0, Grid.r(Long.numberOfTrailingZeros(lo))); assertEquals(1, Grid.c(Long.numberOfTrailingZeros(lo))); }); assertEquals(1, count.get()); } @Test public void testHoriz() { assertTrue(Slot.from(2, 0L, 0L).horiz()); assertTrue(Slot.from(4, 0L, 0L).horiz()); assertFalse(Slot.from(1, 0L, 0L).horiz()); assertFalse(Slot.from(3, 0L, 0L).horiz()); } @Test public void testGridBasics() { var grid = Grid.createEmpty(); // Test set/get grid.setLetter(Grid.offset(0, 0), (byte) 'A'); grid.setClue(Grid.offset(1, 2), (byte) '5'); grid.setLetter(Grid.offset(2, 3), (byte) 'Z'); Assertions.assertEquals((byte) 'A', grid.byteAt(Grid.offset(0, 0))); Assertions.assertEquals((byte) '5', grid.byteAt(Grid.offset(1, 2))); Assertions.assertEquals((byte) 'Z', grid.byteAt(Grid.offset(2, 3))); Assertions.assertEquals(DASH, grid.byteAt(Grid.offset(1, 1))); // Test isLetterAt Assertions.assertTrue(grid.notClue(Grid.offset(0, 0))); Assertions.assertFalse(grid.notClue(Grid.offset(1, 2))); Assertions.assertTrue(grid.notClue(Grid.offset(2, 3))); Assertions.assertFalse(grid.isClue(Grid.offset(1, 1))); // Test isDigitAt Assertions.assertFalse(grid.isClue(0)); Assertions.assertTrue(grid.isClue(Grid.offset(1, 2))); Assertions.assertEquals(5, grid.digitAt(Grid.offset(1, 2))); Assertions.assertFalse(grid.isClue(Grid.offset(2, 3))); Assertions.assertFalse(grid.isClue(Grid.offset(1, 1))); // Test isLettercell Assertions.assertTrue(grid.notClue(Grid.offset(0, 0))); // 'A' is letter Assertions.assertTrue(grid.isClue(Grid.offset(1, 2))); // '5' is digit Assertions.assertTrue(grid.notClue(Grid.offset(1, 1))); // '#' is lettercell } @Test public void testGridDeepCopy() { var grid = Grid.createEmpty(); grid.setLetter(Grid.offset(0, 0), (byte) 'A'); grid.setLetter(Grid.offset(0, 1), (byte) 'B'); grid.setLetter(Grid.offset(1, 0), (byte) 'C'); grid.setLetter(Grid.offset(1, 1), (byte) 'D'); var copy = grid.deepCopyGrid(); Assertions.assertEquals((byte) 'A', copy.byteAt(0)); copy.setLetter(0, (byte) 'X'); Assertions.assertEquals((byte) 'X', copy.byteAt(0)); Assertions.assertEquals((byte) 'A', grid.byteAt(0)); // Original should be unchanged } @Test public void testMini() { var grid = Grid.createEmpty(); val idx = Grid.offset(1, 1); grid.setClue(idx, (byte) '1'); Assertions.assertTrue(grid.isClue(idx)); } @Test public void testAttempt() { // Arrange var opts = new Main.Opts(); opts.seed = 12348; opts.pop = 4; // Tiny population opts.gens = 20; // Very few generations opts.minSimplicity = 0; opts.fillTimeout = 10_000; opts.threads = 1; opts.tries = 1; opts.verbose = false; var dict = Dict.loadDict(opts.wordsPath); // Act PuzzleResult res = null; int foundSeed = -1; for (int i = 0; i < 50; i++) { int seed = opts.seed + i; res = Main.attempt(new Rng(seed), dict, opts); if (res != null && res.filled().ok()) { foundSeed = seed; System.out.println("[DEBUG_LOG] Seed found: " + seed); System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().stats().simplicity); System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().clueMap().size()); System.out.println("[DEBUG_LOG] Grid:"); System.out.println(res.filled().grid().renderHuman()); var aa = res.exportFormatFromFilled(1, new Rewards(1, 1, 1)); break; } } // Assert Assertions.assertNotNull(res, "Puzzle generation failed (null result)"); Assertions.assertTrue(res.filled().ok(), "Puzzle generation failed (not ok)"); // Regression baseline for seed search starting at 12347, pop 4, gens 20 Assertions.assertEquals(12348, foundSeed, "Found seed 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(301794542151533187L, res.filled().grid().grid().lo); Assertions.assertEquals(193L, res.filled().grid().grid().hi); } boolean isLetter(byte b) { return (b & 64) != 0; } @Test public void testIsLetterA() { assertTrue(isLetter((byte) 'A')); } @Test public void testIsLetterZ() { assertTrue(isLetter((byte) 'Z')); } }