This commit is contained in:
mike
2026-01-24 04:03:52 +01:00
parent f61d04bb61
commit 26cd6fb284
5 changed files with 94 additions and 43 deletions

14
pom.xml
View File

@@ -69,6 +69,12 @@
<version>1.7-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>
<groupId>mike.plugin</groupId>
<artifactId>plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>-->
</dependencies>
<build>
@@ -94,6 +100,13 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<!-- <compilerArgs>
<arg>&#45;&#45;add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>&#45;&#45;add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>&#45;&#45;add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>&#45;&#45;add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-Xplugin:autogetter</arg>
</compilerArgs>-->
<annotationProcessorPaths>
<!-- Lombok processor -->
<path>
@@ -111,6 +124,7 @@
</annotationProcessorPaths>
<source>25</source>
<target>25</target>
<release>25</release>
</configuration>
</plugin>
<plugin>

View File

@@ -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();

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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());
// 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);