diff --git a/pom.xml b/pom.xml
index 37afb23..4061d85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,12 @@
1.7-SNAPSHOTprovided
+
@@ -94,6 +100,13 @@
maven-compiler-plugin3.13.0
+
@@ -111,6 +124,7 @@
2525
+ 25
diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java
index d530aea..a9a0e6f 100644
--- a/src/main/java/puzzle/Main.java
+++ b/src/main/java/puzzle/Main.java
@@ -386,6 +386,10 @@ public class Main {
var grid = Masker_Neighbors9x8.grid(slotInfo);// mask.toGrid();
var filled = fillMask(rng, slotInfo, grid.lo, grid.hi, grid.g);
+ if (Thread.currentThread().isInterrupted() && !filled.ok()) {
+ return null;
+ }
+
if (!multiThreaded) {
System.out.print("\r" + " ".repeat(120 - "".length()) + "\r");
System.out.flush();
diff --git a/src/main/java/puzzle/Person.java b/src/main/java/puzzle/Person.java
deleted file mode 100644
index ff93d03..0000000
--- a/src/main/java/puzzle/Person.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package puzzle;
-
-import anno.GenAccessors;
-
-@GenAccessors
-public class Person {
- @GenAccessors.Getter final String name;
- @GenAccessors.Getter final int age;
-
- public Person(String name, int age) { this.name = name; this.age = age; }
-
- public static void main(String[] args) {
- var p = new Person("Mike", 42);
-
- System.out.println(Person__Accessors.getName(p));
- System.out.println(Person__Accessors.getAge(p));
- }
-}
\ No newline at end of file
diff --git a/src/test/java/puzzle/MarkerTest.java b/src/test/java/puzzle/MarkerTest.java
index 18fc180..0d46e4e 100644
--- a/src/test/java/puzzle/MarkerTest.java
+++ b/src/test/java/puzzle/MarkerTest.java
@@ -159,9 +159,9 @@ public class MarkerTest {
// Intersection is exactly 1 cell (0,2). Valid.
assertTrue(masker.isValid(Riddle.Signa.of(r0c0d1, r2c2d2).c()));
- // Clue 3: (1,1) Right. Slot cells: (1,2), (1,3), ...
+ // Clue 3: (1,3) Right. Slot cells: (1,4), (1,5), ...
// No intersection with Clue 1 or 2. Valid.
- assertTrue(masker.isValid(Riddle.Signa.of(r0c0d1, r2c2d2, r1c1d1).c()));
+ assertTrue(masker.isValid(Riddle.Signa.of(r0c0d1, r2c2d2, precomp.Const9x8.Cell.r1c3d1).c()));
// Now create a violation: two slots sharing 2 cells.
// We can do this with Corner Down and another clue.
@@ -205,26 +205,76 @@ public class MarkerTest {
}
@Test
- void testPhysicalAdjacency() {
- var rng = new Rng(42);
- var masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
+ void testDeadCellPenalty() {
+ var masker = emptyMasker();
- // Twee clues naast elkaar, maar slots kruisen niet.
- // Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
- // Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
- var clues = Clues.createEmpty().setClue(r1c1d1).setClue(r2c1d1);
+ // A cell surrounded by 4 clues is a "dead cell"
+ // Let's take cell (4,4)
+ // Clues at (4,3), (4,5), (3,4), (5,4)
+ var deadClues = Clues.createEmpty();
+ deadClues.setClue(precomp.Const9x8.CELLS[precomp.Const9x8.OFF_4_3 * 33 + 1]); // (4,3) Down
+ deadClues.setClue(precomp.Const9x8.CELLS[precomp.Const9x8.OFF_4_5 * 33 + 1]); // (4,5) Down
+ deadClues.setClue(precomp.Const9x8.CELLS[precomp.Const9x8.OFF_3_4 * 33]); // (3,4) Right
+ deadClues.setClue(precomp.Const9x8.CELLS[precomp.Const9x8.OFF_5_4 * 33]); // (5,4) Right
- var fitness = masker.maskFitness(clues, 2);
- // Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
- System.out.println("[DEBUG_LOG] Fitness physically adjacent: " + fitness);
- assertTrue(fitness > 20000, "Should have island penalty even if physically adjacent");
+ var fitness = masker.maskFitness(deadClues, 4);
+ System.out.println("[DEBUG_LOG] Fitness dead cell: " + fitness);
- // Twee clues naast elkaar, maar slots kruisen niet.
- var clues2 = Clues.createEmpty().setClue(r1c1d1).setClue(r3c1d1);
- var fitness2 = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty()).maskFitness(clues2, 2);
- // Als 8-naburigheid NIET MEER meetelt, moet de penalty hoog zijn.
- System.out.println("[DEBUG_LOG] Fitness physically adjacent: " + fitness2);
- assertTrue(fitness2 > 20000, "Should have island penalty even if physically adjacent");
+ // The cell (4,4) should have 4 clue neighbors -> penalty 2000.
+ // It is also not covered by any word -> penalty 4000.
+ // Total penalty should be high.
+ assertTrue(fitness > 5000, "Dead cell should have high penalty");
+ }
+
+ @Test
+ void testDeadCellIsValid() {
+ var masker = emptyMasker();
+ var deadClues = Clues.createEmpty();
+ deadClues.setClue(precomp.Const9x8.Cell.r0c1d1);
+ deadClues.setClue(precomp.Const9x8.Cell.r2c1d1);
+ deadClues.setClue(precomp.Const9x8.Cell.r1c0d0);
+ deadClues.setClue(precomp.Const9x8.Cell.r1c2d0);
+
+ int offending = masker.findOffendingClue(deadClues);
+ if (offending != -1) {
+ System.out.println("[DEBUG_LOG] Offending clue index: " + offending);
+ }
+
+ assertFalse(masker.isValid(deadClues), "Mask with dead cell should be invalid");
+ }
+
+ @Test
+ void testCleanupDeadCell() {
+ var masker = emptyMasker();
+ var deadClues = Clues.createEmpty();
+ deadClues.setClue(precomp.Const9x8.Cell.r0c1d1);
+ deadClues.setClue(precomp.Const9x8.Cell.r2c1d1);
+ deadClues.setClue(precomp.Const9x8.Cell.r1c0d0);
+ deadClues.setClue(precomp.Const9x8.Cell.r1c2d0);
+
+ assertFalse(masker.isValid(deadClues));
+
+ masker.cleanup(deadClues);
+
+ assertTrue(masker.isValid(deadClues), "After cleanup, mask should be valid");
+ assertTrue(deadClues.clueCount() < 4, "Cleanup should have removed at least one clue");
+ }
+
+ @Test
+ void testOnlyOneWordCoveragePenalty() {
+ var masker = emptyMasker();
+
+ // A white cell covered by only one word.
+ // Clue (4,0) Right. Cell (4,1) is covered only by this horizontal word.
+ var oneWord = Clues.createEmpty().setClue(precomp.Const9x8.CELLS[precomp.Const9x8.OFF_4_0 * 33]);
+
+ var fitness = masker.maskFitness(oneWord, 1);
+ System.out.println("[DEBUG_LOG] Fitness only one word: " + fitness);
+
+ // Each white cell of (4,0) Right (length 8) will have 1 word coverage.
+ // Penalty per cell is 1500. 8 cells * 1500 = 12000.
+ // Plus some 1-clue-per-row/col penalties etc.
+ assertTrue(fitness > 11000, "Should have penalty for single word coverage");
}
@Test
diff --git a/src/test/java/puzzle/PerformanceTest.java b/src/test/java/puzzle/PerformanceTest.java
index abc1c7c..54e258d 100644
--- a/src/test/java/puzzle/PerformanceTest.java
+++ b/src/test/java/puzzle/PerformanceTest.java
@@ -57,7 +57,7 @@ import static puzzle.dict900.DictData900.DICT900;
public class PerformanceTest {
void main() {
- testIncrementalComplexity();
+ testPerformance();
}
@Test
void testPerformance() {
@@ -65,14 +65,15 @@ public class PerformanceTest {
// 1. Stress test Clue Generation (Mask Generation)
System.out.println("[DEBUG_LOG] --- Mask Generation Performance ---");
- var clueSizes = new int[]{ 20, 25, 30 };
- var arr = new Clues[3];
+ var clueSizes = new int[]{ 20,22,24, 25, 30 };
+ var arr = new Clues[clueSizes.length];
var c = 0;
+ val masker = new Masker(rng, new int[Masker.STACK_SIZE], Clues.createEmpty());
for (var size : clueSizes) {
- var t0 = System.currentTimeMillis();
- val masker = new Masker(rng, new int[Masker.STACK_SIZE], Clues.createEmpty());
+ var t0 = System.currentTimeMillis();
+
// Increased population and generations for stress
- arr[c++] = masker.generateMask(size, 200, 100, 50);
+ arr[c++] = masker.generateMask(size, 200, 700, 50);
var t1 = System.currentTimeMillis();
var duration = (t1 - t0) / 1000.0;
System.out.printf(Locale.ROOT, "[DEBUG_LOG] Size %d (pop=200, gen=100): %.3fs%n", size, duration);