introduce bitloops

This commit is contained in:
mike
2026-01-14 12:28:07 +01:00
parent 6afe675a9d
commit dfb4679da8
5 changed files with 49 additions and 44 deletions

View File

@@ -6,6 +6,7 @@ import lombok.experimental.Delegate;
import lombok.val; import lombok.val;
import puzzle.Export.Gridded.Replacar.Cell; import puzzle.Export.Gridded.Replacar.Cell;
import puzzle.SwedishGenerator.Clues; import puzzle.SwedishGenerator.Clues;
import puzzle.SwedishGenerator.DictEntry;
import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.FillResult;
import puzzle.SwedishGenerator.Grid; import puzzle.SwedishGenerator.Grid;
import java.util.ArrayList; import java.util.ArrayList;
@@ -197,10 +198,11 @@ public record Export() {
var g = filled().grid(); var g = filled().grid();
var placed = new ArrayList<Placed>(); var placed = new ArrayList<Placed>();
var clueMap = filled().clueMap(); var clueMap = filled().clueMap();
val entries = new DictEntry[10];
mask.forEachSlot((int key, long lo, long hi) -> { mask.forEachSlot((int key, long lo, long hi) -> {
var word = clueMap[key]; var word = clueMap[key];
if (word != 0L) { if (word != 0L) {
placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi), word)); placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi, entries[Slot.length(lo, hi)]), word));
} else { } else {
System.err.println("Could not find clue for slot: " + key); System.err.println("Could not find clue for slot: " + key);
} }

View File

@@ -361,7 +361,7 @@ public class Main {
var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5))); var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index()); var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
TOTAL_NODES.addAndGet(filled.stats().nodes); TOTAL_NODES.addAndGet(filled.stats().nodes);
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks); TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);

View File

@@ -383,12 +383,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
} }
static record Slot(int key, long lo, long hi) { static record Slot(int key, long lo, long hi, DictEntry entry) {
static final int BIT_FOR_DIR = 2; static final int BIT_FOR_DIR = 2;
static Slot from(int key, long lo, long hi) { return new Slot(key, lo, hi); } static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
public static int length(long lo, long hi) { return Long.bitCount(lo) + Long.bitCount(hi); }
public int length() { return Long.bitCount(lo) + Long.bitCount(hi); }
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; } public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
public static int dir(int key) { return key & 3; } public static int dir(int key) { return key & 3; }
public static boolean increasing(int dir) { return (dir & 2) == 0; } public static boolean increasing(int dir) { return (dir & 2) == 0; }
@@ -438,10 +437,10 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
visitor.visit(key, rayLo, rayHi); visitor.visit(key, rayLo, rayHi);
} }
static Slot[] extractSlots(Clues grid) { static Slot[] extractSlots(Clues grid, DictEntry[] index) {
var slots = new Slot[grid.clueCount()]; var slots = new Slot[grid.clueCount()];
int[] N = new int[]{ 0 }; int[] N = new int[]{ 0 };
grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi)); grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)]));
return slots; return slots;
} }
@@ -764,11 +763,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
} }
return p; return p;
} }
static int slotScore(byte[] count, Slot s) { static int slotScore(byte[] count, long lo, long hi) {
int cross = 0; int cross = 0;
for (long b = s.lo; b != 0; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1); for (long b = lo; b != 0; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1);
for (long b = s.hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); for (long b = hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1);
return cross * 10 + s.length(); return cross * 10 + Slot.length(lo, hi);
} }
static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) { static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) {
final long glo = grid.lo, ghi = grid.hi; final long glo = grid.lo, ghi = grid.hi;
@@ -890,10 +889,13 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
for (long b = s.lo; b != 0; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++; for (long b = s.lo; b != 0; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++;
for (long b = s.hi; b != 0; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++; for (long b = s.hi; b != 0; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++;
} }
for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]); for (int i = 0; i < slots.length; i++) {
var slot = slots[i];
slotScores[i] = slotScore(count, slot.lo, slot.hi);
}
} }
public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask, DictEntry[] dictIndex) { public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask) {
val multiThreaded = Thread.currentThread().getName().contains("pool"); val multiThreaded = Thread.currentThread().getName().contains("pool");
val NO_LOG = (!Main.VERBOSE || multiThreaded); val NO_LOG = (!Main.VERBOSE || multiThreaded);
val grid = mask; val grid = mask;
@@ -943,7 +945,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
var s = slots[i]; var s = slots[i];
if (assigned[s.key] != X) continue; if (assigned[s.key] != X) continue;
var pattern = patternForSlot(grid, s.key, s.lo, s.hi); var pattern = patternForSlot(grid, s.key, s.lo, s.hi);
var index = dictIndex[s.length()]; var index = s.entry;
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
if (count == 0) { if (count == 0) {
@@ -965,7 +967,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
return; return;
} }
var pattern = patternForSlot(grid, best.key, best.lo, best.hi); var pattern = patternForSlot(grid, best.key, best.lo, best.hi);
var index = dictIndex[best.length()]; var index = best.entry;
current = CARRIER; current = CARRIER;
current.slot = best; current.slot = best;
current.count = index.length; current.count = index.length;
@@ -994,8 +996,10 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
val s = pick.slot; val s = pick.slot;
val k = s.key; val k = s.key;
val entry = dictIndex[s.length()]; val slo = s.lo;
val shi = s.hi;
val entry = s.entry;
long low, top;
if (info != null && info.length > 0) { if (info != null && info.length > 0) {
var idxs = info; var idxs = info;
var L = idxs.length; var L = idxs.length;
@@ -1009,9 +1013,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
var w = entry.words[idx]; var w = entry.words[idx];
var lemIdx = Lemma.unpackIndex(w); var lemIdx = Lemma.unpackIndex(w);
if (used.get(lemIdx)) continue; if (used.get(lemIdx)) continue;
val low = grid.lo; low = grid.lo;
val top = grid.hi; top = grid.hi;
if (!placeWord(grid, k, s.lo, s.hi, w)) continue; if (!placeWord(grid, k, slo, shi, w)) continue;
used.set(lemIdx); used.set(lemIdx);
assigned[k] = w; assigned[k] = w;
@@ -1036,9 +1040,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
var w = entry.words[idxInArray]; var w = entry.words[idxInArray];
var lemIdx = Lemma.unpackIndex(w); var lemIdx = Lemma.unpackIndex(w);
if (used.get(lemIdx)) continue; if (used.get(lemIdx)) continue;
val low = grid.lo; low = grid.lo;
val top = grid.hi; top = grid.hi;
if (!placeWord(grid, k, s.lo, s.hi, w)) continue; if (!placeWord(grid, k, slo, shi, w)) continue;
used.set(lemIdx); used.set(lemIdx);
assigned[k] = w; assigned[k] = w;

View File

@@ -55,10 +55,10 @@ public class MainTest {
grid.setLetterLo(OFF_0_1, LETTER_A); grid.setLetterLo(OFF_0_1, LETTER_A);
grid.setLetterLo(OFF_0_2, LETTER_B); grid.setLetterLo(OFF_0_2, LETTER_B);
var slots = extractSlots(clues); var slots = extractSlots(clues, dict.index());
assertEquals(1, slots.length); assertEquals(1, slots.length);
var s = slots[0]; var s = slots[0];
assertEquals(8, s.length()); assertEquals(8, Slot.length(s.lo(), s.hi()));
var cells = s.walk().toArray(); var cells = s.walk().toArray();
assertEquals(0, SwedishGenerator.IT[cells[0]].r()); assertEquals(0, SwedishGenerator.IT[cells[0]].r());
assertEquals(1, SwedishGenerator.IT[cells[0]].c()); assertEquals(1, SwedishGenerator.IT[cells[0]].c());
@@ -184,7 +184,7 @@ public class MainTest {
128L, 128L,
422762372923520L, 422762372923520L,
192L); 192L);
var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index()); var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed"); Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed");
Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282])); Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282]));

View File

@@ -280,12 +280,12 @@ public class SwedishGeneratorTest {
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2) // This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
var clues = Clues.createEmpty(); var clues = Clues.createEmpty();
clues.setClue(OFF_0_0, CLUE_RIGHT); clues.setClue(OFF_0_0, CLUE_RIGHT);
var dict = new Dict(WORDS2);
var slots = extractSlots(clues); var slots = extractSlots(clues, dict.index());
assertEquals(1, slots.length); assertEquals(1, slots.length);
var s = slots[0]; var s = slots[0];
assertTrue(s.length() >= 2); assertTrue(Slot.length(s.lo(), s.hi()) >= 2);
assertEquals(OFF_0_0, Slot.clueIndex(s.key())); assertEquals(OFF_0_0, Slot.clueIndex(s.key()));
assertEquals(CLUE_RIGHT, Slot.dir(s.key())); assertEquals(CLUE_RIGHT, Slot.dir(s.key()));
} }
@@ -396,10 +396,9 @@ public class SwedishGeneratorTest {
counts[2] = 3; counts[2] = 3;
var dict = new Dict(WORDS); var dict = new Dict(WORDS);
var entry5 = dict.index()[5]; var entry5 = dict.index()[5];
var sScore = Slot.from(0, (1L << 1) | (1L << 2), 0L);
// cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3 // cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3
// score = 3 * 10 + len(2) = 32 // score = 3 * 10 + len(2) = 32
assertEquals(32, slotScore(counts, sScore)); assertEquals(32, slotScore(counts, (1L << 1) | (1L << 2), 0L));
// 3. Test candidateCountForPattern // 3. Test candidateCountForPattern
var ctx = Context.get(); var ctx = Context.get();