199 lines
7.1 KiB
Java
199 lines
7.1 KiB
Java
package puzzle;
|
|
|
|
import module java.base;
|
|
import anno.DictGen;
|
|
import anno.Dictionaries;
|
|
import lombok.val;
|
|
import org.junit.jupiter.api.Test;
|
|
import puzzle.Export.Clue;
|
|
import puzzle.Export.Signa;
|
|
import puzzle.Export.Puzzle;
|
|
import puzzle.SwedishGenerator.Rng;
|
|
import puzzle.SwedishGenerator.Slotinfo;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
import static precomp.Const9x8.Cell.*;
|
|
import static puzzle.SwedishGenerator.fillMask;
|
|
import static puzzle.dict800.DictData800.DICT800;
|
|
import static puzzle.dict900.DictData900.DICT900;
|
|
|
|
@Dictionaries({ @DictGen(
|
|
packageName = "puzzle.dict900",
|
|
className = "DictData900",
|
|
scv = "/home/mike/dev/puzzle-generator/nl_score_hints_v4.csv",
|
|
simpleMax = 900,
|
|
minLen = 2,
|
|
maxLen = 8
|
|
), @DictGen(
|
|
packageName = "puzzle.dict800",
|
|
className = "DictData800",
|
|
scv = "/home/mike/dev/puzzle-generator/nl_score_hints_v4.csv",
|
|
simpleMax = 800,
|
|
minLen = 2,
|
|
maxLen = 8
|
|
) })
|
|
public class PerformanceTest {
|
|
|
|
void main() {
|
|
testIncrementalComplexity();
|
|
}
|
|
@Test
|
|
void testPerformance() {
|
|
val rng = new Rng(42);
|
|
|
|
// 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 c = 0;
|
|
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);
|
|
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);
|
|
// Basic sanity check: should not take forever
|
|
assertTrue(duration < 10.0, "Mask generation took too long for size " + size);
|
|
}
|
|
|
|
// 2. Stress test Word Filler
|
|
System.out.println("[DEBUG_LOG] \n--- Word Filler Performance ---");
|
|
c = 0;
|
|
for (var size : clueSizes) {
|
|
|
|
var t0 = System.currentTimeMillis();
|
|
// Try to fill multiple times to get a better average
|
|
var iterations = 10;
|
|
long totalNodes = 0;
|
|
long totalBacktracks = 0;
|
|
var successCount = 0;
|
|
|
|
for (var i = 0; i < iterations; i++) {
|
|
val slotInfo = Masker.slots(arr[c], DICT800);
|
|
var grid = Slotinfo.grid(slotInfo);
|
|
val result = fillMask(rng, slotInfo,grid.lo,grid.hi, grid.g);
|
|
if (result.ok()) successCount++;
|
|
totalNodes += result.nodes();
|
|
totalBacktracks += result.backtracks();
|
|
}
|
|
c++;
|
|
var t1 = System.currentTimeMillis();
|
|
var totalDuration = (t1 - t0) / 1000.0;
|
|
|
|
System.out.printf(Locale.ROOT, "[DEBUG_LOG] Size %d: %d/%d SUCCESS | avg nodes=%d | avg backtracks=%d | total time=%.3fs%n",
|
|
size, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
|
|
}
|
|
}
|
|
@Test
|
|
void testIncrementalComplexity() {
|
|
|
|
// Use the complex mask from Main.java
|
|
var mask = Signa.of(
|
|
r0c0d1, r0c5d0, r0c6d0, r0c7d0, r0c8d0,
|
|
r1c0d1,
|
|
r2c0d0, r2c1d0, r2c3d0, r2c4d1,
|
|
r3c4d1,
|
|
r4c4d1,
|
|
r5c2d2, r5c4d1,
|
|
r6c2d1,
|
|
r7c0d2, r7c1d2, r7c2d1, r7c7d2, r7c8d2
|
|
);
|
|
val allSlots = Masker.slots(mask.c(), DICT900);
|
|
//mask.toGrid()
|
|
System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
|
|
System.out.println("[DEBUG_LOG] Full Slot Layout:");
|
|
visualizeSlots(allSlots);
|
|
|
|
for (var i = 10; i <= allSlots.length; i++) {
|
|
val subset = Arrays.copyOf(allSlots, i);
|
|
// Arrays.sort(subset, Comparator.comparingInt(Slotinfo::score));
|
|
System.out.printf("[DEBUG_LOG] Testing with first %d slots%n of %s", i, allSlots.length);
|
|
visualizeSlots(subset);
|
|
measureFill(new Rng(123 + i), subset, "Subset size " + i);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void testSingleSlotResolution() {
|
|
val rng = new Rng(42);
|
|
|
|
// A single horizontal slot at (0,0)
|
|
val mask = Signa.of(r0c0d1);
|
|
val slots = Masker.slots(mask.c(), DICT800);
|
|
|
|
System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---");
|
|
if (slots.length > 0) {
|
|
measureFill(rng, slots, "Single Slot");
|
|
} else {
|
|
System.out.println("[DEBUG_LOG] Error: No slots found in mask.");
|
|
}
|
|
}
|
|
|
|
private void measureFill(Rng rng, Slotinfo[] slots, String label) {
|
|
var t0 = System.currentTimeMillis();
|
|
var iterations = 1;
|
|
long totalNodes = 0;
|
|
long totalBacktracks = 0;
|
|
var successCount = 0;
|
|
|
|
for (var i = 0; i < iterations; i++) {
|
|
// Reset assignments for each iteration
|
|
for (var s : slots) s.assign().w = 0;
|
|
|
|
var grid = Slotinfo.grid(slots);
|
|
val result = fillMask(rng, slots, grid.lo,grid.hi,grid.g);
|
|
if (result.ok()) {
|
|
successCount++;
|
|
}
|
|
totalNodes += result.nodes();
|
|
totalBacktracks += result.backtracks();
|
|
}
|
|
var t1 = System.currentTimeMillis();
|
|
var totalDuration = (t1 - t0) / 1000.0;
|
|
|
|
System.out.printf(Locale.ROOT, "[DEBUG_LOG] %s: %d/%d SUCCESS | avg nodes=%d | avg backtracks=%d | total time=%.3fs%n",
|
|
label, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
|
|
visualizeSlots(slots);
|
|
}
|
|
|
|
private void visualizeSlots(Slotinfo[] slots) {
|
|
var R = Masker.R;
|
|
var C = Masker.C;
|
|
var display = new char[R][C];
|
|
for (var r = 0; r < R; r++) Arrays.fill(display[r], ' ');
|
|
|
|
for (var slot : slots) {
|
|
var key = slot.key();
|
|
var dir = Clue.from(Masker.Slot.dir(key));
|
|
var clueIdx = Masker.Slot.clueIndex(key);
|
|
|
|
var cr = Masker.IT[clueIdx].r();
|
|
var cc = Masker.IT[clueIdx].c();
|
|
|
|
// User requested: aAAAA for a four letter to RIGHT clue slot.
|
|
// SwedishGenerator: 1=RIGHT, 0=DOWN, 2=UP, 3=LEFT
|
|
var clueChar = dir.clueChar;
|
|
var slotChar = dir.slotChar;
|
|
display[cr][cc] = clueChar;
|
|
|
|
Puzzle.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).forEach(idx -> {
|
|
var r = Masker.IT[idx].r();
|
|
var c = Masker.IT[idx].c();
|
|
if (display[r][c] == ' ' || (display[r][c] >= 'A' && display[r][c] <= 'D')) {
|
|
if (display[r][c] != ' ' && display[r][c] != slotChar) {
|
|
display[r][c] = '+'; // Intersection
|
|
} else {
|
|
display[r][c] = slotChar;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
for (var r = 0; r < R; r++) {
|
|
System.out.println("[DEBUG_LOG] " + new String(display[r]));
|
|
}
|
|
}
|
|
}
|