package puzzle; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import puzzle.Main.PuzzleResult; 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 { public static void main(String[] args) { MainTest t = new MainTest(); t.testGridBasics(); t.testGridDeepCopy(); t.testMini(); t.testAttempt(); } @Test void testExtractSlots() { var generator = new SwedishGenerator(); var grid = makeEmptyGrid(); // 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.setCharAt(0, 1, 'A'); grid.setCharAt(0, 2, 'B'); var slots = generator.extractSlots(grid); assertEquals(1, slots.size()); var s = slots.get(0); assertEquals(8, s.len()); assertEquals(0, Grid.r(s.pos(0))); assertEquals(1, Grid.c(s.pos(0))); assertEquals(0, Grid.r(s.pos(1))); assertEquals(2, Grid.c(s.pos(1))); } @Test void testStaticSlotMethods() { // Test static offset extraction // packedPos: offset(1, 1) at index 0, offset(2, 2) at index 1 var packedPos = ((long) Grid.offset(1, 1)) | (((long) Grid.offset(2, 2)) << 7); assertEquals(Grid.offset(1, 1), Slot.offset(packedPos, 0)); assertEquals(Grid.offset(2, 2), Slot.offset(packedPos, 1)); // 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 generator = new SwedishGenerator(); var grid = makeEmptyGrid(); grid.setClue(0, (byte) '2'); // right var count = new AtomicInteger(0); generator.forEachSlot(grid, (key, packedPos, len) -> { count.incrementAndGet(); assertEquals(8, len); assertEquals(0, Grid.r(Slot.offset(packedPos, 0))); assertEquals(1, Grid.c(Slot.offset(packedPos, 0))); }); assertEquals(1, count.get()); } @Test public void testHoriz() { assertTrue(Slot.from(2, 0L, 1).horiz()); assertTrue(Slot.from(4, 0L, 1).horiz()); assertFalse(Slot.from(1, 0L, 1).horiz()); assertFalse(Slot.from(3, 0L, 1).horiz()); assertFalse(Slot.from(5, 0L, 1).horiz()); } @Test public void testGridBasics() { var grid = makeEmptyGrid(); // Test set/get grid.setCharAt(0, 0, 'A'); grid.setCharAt(1, 2, '5'); grid.setCharAt(2, 3, 'Z'); Assertions.assertEquals((byte) 'A', grid.byteAt(0, 0)); Assertions.assertEquals((byte) '5', grid.byteAt(1, 2)); Assertions.assertEquals((byte) 'Z', grid.byteAt(2, 3)); Assertions.assertEquals(DASH, grid.byteAt(1, 1)); // Test isLetterAt Assertions.assertTrue(grid.isLetterAt(0, 0)); Assertions.assertFalse(grid.isLetterAt(1, 2)); Assertions.assertTrue(grid.isLetterAt(2, 3)); Assertions.assertFalse(grid.isLetterAt(1, 1)); // Test isDigitAt Assertions.assertFalse(grid.isDigitAt(0, 0)); Assertions.assertTrue(grid.isDigitAt(1, 2)); Assertions.assertEquals(5, grid.digitAt(1, 2)); Assertions.assertFalse(grid.isDigitAt(2, 3)); Assertions.assertFalse(grid.isDigitAt(1, 1)); // Test isLettercell Assertions.assertTrue(grid.isLettercell(0, 0)); // 'A' is letter Assertions.assertFalse(grid.isLettercell(1, 2)); // '5' is digit Assertions.assertTrue(grid.isLettercell(1, 1)); // '#' is lettercell } @Test public void testGridDeepCopy() { var grid = makeEmptyGrid(); grid.setCharAt(0, 0, 'A'); grid.setCharAt(0, 1, 'B'); grid.setCharAt(1, 0, 'C'); grid.setCharAt(1, 1, 'D'); var copy = grid.deepCopyGrid(); Assertions.assertEquals((byte) 'A', copy.byteAt(0, 0)); copy.setCharAt(0, 0, 'X'); Assertions.assertEquals((byte) 'X', copy.byteAt(0, 0)); Assertions.assertEquals((byte) 'A', grid.byteAt(0, 0)); // Original should be unchanged } @Test public void testMini() { var grid = makeEmptyGrid(); grid.setCharAt(1, 1, '1'); Assertions.assertTrue(grid.isDigitAt(1, 1)); } @Test public void testAttempt() { // Arrange var opts = new Main.Opts(); opts.seed = 12347; 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 = loadWords(opts.wordsPath); // Act PuzzleResult res = null; int foundSeed = -1; for (int i = 0; i < 50; i++) { int seed = opts.seed + i; var rng = new Rng(seed); res = Main.attempt(rng, 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().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()); 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(12347, foundSeed, "Found seed changed"); Assertions.assertEquals(20, res.filled().clueMap().size(), "Number of assigned words changed"); Assertions.assertEquals(775.45, res.filled().simplicity(), 1e-9, "Simplicity value changed"); Assertions.assertArrayEquals(new byte[]{ 'I', 'N', 'E', 'R', 'T' }, res.filled().clueMap().get(1377).word()); } @Test public void testIsLetterA() { assertTrue(isLetter((byte) 'A')); } @Test public void testIsLetterZ() { assertTrue(isLetter((byte) 'Z')); } }