introduce bitloops

This commit is contained in:
mike
2026-01-18 03:32:34 +01:00
parent 6daab5ef4e
commit b026ebfbd2
6 changed files with 194 additions and 74 deletions

View File

@@ -0,0 +1,90 @@
package puzzle;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import puzzle.Masker.Clues;
import puzzle.SwedishGenerator.Rng;
import static puzzle.SwedishGenerator.STACK_SIZE;
public class ConnectivityTest {
@Test
void testConnectivityPenalty() {
Rng rng = new Rng(42);
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
// 1. Maak een masker met één component van clues (bijv. 3 clues naast elkaar)
Clues singleComp = Clues.createEmpty();
// Gebruik offsets die dicht bij elkaar liggen
int off1 = SwedishGenerator.Grid.offset(1, 1);
int off2 = SwedishGenerator.Grid.offset(1, 2);
int off3 = SwedishGenerator.Grid.offset(2, 1);
singleComp.setClueLo(1L << off1, (byte)1); // Right
singleComp.setClueLo(1L << off2, (byte)1); // Right
singleComp.setClueLo(1L << off3, (byte)0); // Down
long fitnessSingle = masker.maskFitness(singleComp, 3);
// 2. Maak een masker met twee eilandjes van clues
Clues twoIslands = Clues.createEmpty();
int offA1 = SwedishGenerator.Grid.offset(1, 1);
int offB1 = SwedishGenerator.Grid.offset(6, 6); // Ver weg
// We moeten zorgen dat ze elk minstens 1 slot vormen om door isValid(2) te komen
twoIslands.setClueLo(1L << offA1, (byte)1);
twoIslands.setClueLo(1L << offB1, (byte)1);
long fitnessIslands = masker.maskFitness(twoIslands, 2);
System.out.println("[DEBUG_LOG] Fitness single component: " + fitnessSingle);
System.out.println("[DEBUG_LOG] Fitness two islands: " + fitnessIslands);
// De eilandjes moeten een hogere penalty hebben (als clueCount gelijk is)
Clues twoIslands3 = Clues.createEmpty();
twoIslands3.setClueLo(1L << offA1, (byte)1);
twoIslands3.setClueLo(1L << offB1, (byte)1);
int offB2 = SwedishGenerator.Grid.offset(6, 7);
twoIslands3.setClueLo(1L << offB2, (byte)1);
long fitnessIslands3 = masker.maskFitness(twoIslands3, 3);
System.out.println("[DEBUG_LOG] Fitness three clues in two islands: " + fitnessIslands3);
assertTrue(fitnessIslands3 > fitnessSingle, "Islands should have higher penalty than single component");
}
@Test
void testIntersectionConnectivity() {
Rng rng = new Rng(42);
Masker masker = new Masker(rng, new int[STACK_SIZE], Clues.createEmpty());
// Test of slots die elkaar kruisen als verbonden worden beschouwd,
// zelfs als de clues niet 8-naburig zijn.
Clues crossing = Clues.createEmpty();
// Clue 1: (0,0) naar rechts. Slot op (0,1), (0,2), (0,3)
// Clue 2: (1,2) omhoog. Slot op (0,2)
// Ze kruisen op (0,2)
crossing.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1); // Right
crossing.setClueLo(1L << SwedishGenerator.Grid.offset(1,2), (byte)2); // Up
// Deze twee clues zijn niet 8-naburig (0,0 en 1,2)
// Maar hun slots kruisen op (0,2)
long fitness = masker.maskFitness(crossing, 2);
System.out.println("[DEBUG_LOG] Fitness crossing: " + fitness);
// Als ze als verbonden worden gezien, is er 1 component.
// Penalty voor connectiviteit zou 0 moeten zijn (bovenop andere penalties).
// Als we een derde clue ver weg toevoegen, moet de penalty significant stijgen.
Clues crossingPlusIsland = Clues.createEmpty();
crossingPlusIsland.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1);
crossingPlusIsland.setClueLo(1L << SwedishGenerator.Grid.offset(1,2), (byte)2);
crossingPlusIsland.setClueLo(1L << SwedishGenerator.Grid.offset(7,7), (byte)1);
long fitnessIsland = masker.maskFitness(crossingPlusIsland, 3);
System.out.println("[DEBUG_LOG] Fitness crossing plus island: " + fitnessIsland);
assertTrue(fitnessIsland > fitness + 10000, "Island should add significant penalty");
}
}

View File

@@ -196,9 +196,7 @@ public class MainTest {
Assertions.assertEquals(20, mask.clueCount());
val map = mask.stream().collect(Collectors.toMap(ClueAt::index, ClueAt::clue));
Assertions.assertEquals(20, map.size());
var slots = Masker.extractSlots(mask.c(), dict.index());
val slotInfo = Masker.scoreSlots(slots);
var grid = mask.toGrid();
var slots = Masker.slots(mask.c(), dict.index());
// var filled = fillMask(rng, slotInfo, grid, false);
// val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0));
}
@@ -214,15 +212,14 @@ public class MainTest {
" 1 \n" +
" 1 2\n" +
"21 22 3");
var slots = Masker.extractSlots(mask.c(), dict.index());
val slotInfo = Masker.scoreSlots(slots);
var grid = mask.toGrid();
var slotInfo = Masker.slots(mask.c(), dict.index());
var grid = Slotinfo.grid(slotInfo);
var filled = fillMask(rng, slotInfo, grid, false);
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(13, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
Assertions.assertEquals("WAANZIN", Lemma.asWord(slotInfo[0].assign().w));
Assertions.assertEquals(-2155876353L, grid.lo);
Assertions.assertEquals(255L, grid.hi);
Assertions.assertEquals(-1L, grid.lo);
Assertions.assertEquals(-1L, grid.hi);
var g = new Gridded(grid);
g.gridToString(mask.c());
var aa = new PuzzleResult(mask, g, slotInfo, filled).exportFormatFromFilled(1, new Rewards(1, 1, 1));