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 { static final byte LETTER_A = (byte) 'A'; static final byte LETTER_B = (byte) 'B'; static final byte LETTER_Z = (byte) 'Z'; static final byte CLUE_DOWN = 0; static final byte CLUE_RIGHT = 1; static final byte CLUE_UP = 2; static final byte CLUE_LEFT = 3; static final int OFF_0_0 = Grid.offset(0, 0); static final int OFF_0_1 = Grid.offset(0, 1); static final int OFF_0_2 = Grid.offset(0, 2); static final int OFF_1_1 = Grid.offset(1, 1); static final int OFF_1_2 = Grid.offset(1, 2); static final int OFF_2_3 = Grid.offset(2, 3); @Test void testExtractSlots() { var grid = Grid.createEmpty(); // Set up digits on the grid to create slots. // CLUE_RIGHT at OFF_0_0 -> slot at (0,1), (0,2) grid.setClue(OFF_0_0, CLUE_RIGHT); grid.setLetter(OFF_0_1, LETTER_A); grid.setLetter(OFF_0_2, LETTER_B); var slots = extractSlots(grid); assertEquals(1, slots.size()); var s = slots.getFirst(); assertEquals(8, s.length()); 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 1 (right) is horizontal assertTrue(Slot.horiz(1)); // dir 0 (down) is vertical assertFalse(Slot.horiz(0)); } @Test void testForEachSlot() { var grid = Grid.createEmpty(); grid.setClue(OFF_0_0, CLUE_RIGHT); // 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(1, 0L, 0L).horiz()); // Right assertTrue(Slot.from(3, 0L, 0L).horiz()); // Left assertFalse(Slot.from(0, 0L, 0L).horiz()); // Down assertFalse(Slot.from(2, 0L, 0L).horiz()); // Up } @Test public void testGridBasics() { var grid = Grid.createEmpty(); // Test set/get grid.setLetter(OFF_0_0, LETTER_A); grid.setClue(OFF_1_2, CLUE_UP); grid.setLetter(OFF_2_3, LETTER_Z); Assertions.assertEquals(LETTER_A, grid.byteAt(OFF_0_0)); Assertions.assertEquals(CLUE_UP, grid.byteAt(OFF_1_2)); Assertions.assertEquals(LETTER_Z, grid.byteAt(OFF_2_3)); Assertions.assertEquals(DASH, grid.byteAt(OFF_1_1)); // Test isLetterAt Assertions.assertTrue(grid.notClue(OFF_0_0)); Assertions.assertFalse(grid.notClue(OFF_1_2)); Assertions.assertTrue(grid.notClue(OFF_2_3)); Assertions.assertFalse(grid.isClue(OFF_1_1)); // Test isDigitAt Assertions.assertFalse(grid.isClue(0)); Assertions.assertTrue(grid.isClue(OFF_1_2)); Assertions.assertEquals(CLUE_UP, grid.digitAt(OFF_1_2)); Assertions.assertFalse(grid.isClue(OFF_2_3)); Assertions.assertFalse(grid.isClue(OFF_1_1)); // Test isLettercell Assertions.assertTrue(grid.notClue(OFF_0_0)); // 'A' is letter Assertions.assertTrue(grid.isClue(OFF_1_2)); // digit Assertions.assertTrue(grid.notClue(OFF_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 = OFF_1_1; grid.setClue(idx, CLUE_LEFT); 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().wordCount()); 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().wordCount(), "Number of assigned words changed"); Assertions.assertEquals("SLEDE", Lemma.asWord(res.filled().clueMap()[282])); Assertions.assertEquals(74732156493031040L, 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')); } }