introduce bitloops

This commit is contained in:
mike
2026-01-17 21:43:17 +01:00
parent 938d2ac66b
commit 19812d81e5
5 changed files with 34 additions and 36 deletions

View File

@@ -83,6 +83,9 @@ public record Export() {
record LetterAt(int index, byte letter) { record LetterAt(int index, byte letter) {
public char human() {
return (char) (letter | 64);
}
static LetterAt from(int index, byte[] bytes) { return new LetterAt(index, bytes[index]); } static LetterAt from(int index, byte[] bytes) { return new LetterAt(index, bytes[index]); }
} }
@@ -235,18 +238,14 @@ public record Export() {
return simpel; return simpel;
} }
public ExportedPuzzle exportFormatFromFilled(int difficulty, Rewards rewards) { public ExportedPuzzle exportFormatFromFilled(int difficulty, Rewards rewards) {
var placed = new ArrayList<Placed>();
for (var slot : slots) {
placed.add(new Placed(slot.assign().w, slot.key(), Gridded.walk((byte) slot.key(), slot.lo(), slot.hi()).toArray()));
}
// If nothing placed: return full grid mapped to letters/# only // If nothing placed: return full grid mapped to letters/# only
if (placed.isEmpty()) { if (slots.length == 0) {
return new ExportedPuzzle(grid.exportGrid(clues.c, _ -> '#', '#'), new WordOut[0], difficulty, rewards); return new ExportedPuzzle(grid.exportGrid(clues.c, _ -> '#', '#'), new WordOut[0], difficulty, rewards);
} }
// 2) bounding box around all word cells + arrow cells, with 1-cell margin var placed = Arrays.stream(slots).map(slot -> new Placed(slot.assign().w, slot.key(), Gridded.walk((byte) slot.key(), slot.lo(), slot.hi()).toArray())).toArray(Placed[]::new);
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
int minR = Integer.MAX_VALUE, minC = Integer.MAX_VALUE; int minR = Integer.MAX_VALUE, minC = Integer.MAX_VALUE;
int maxR = Integer.MIN_VALUE, maxC = Integer.MIN_VALUE; int maxR = Integer.MIN_VALUE, maxC = Integer.MIN_VALUE;
@@ -265,23 +264,18 @@ public record Export() {
} }
// 3) map of only used letter cells (everything else becomes '#') // 3) map of only used letter cells (everything else becomes '#')
var letterAt = new HashMap<Integer, Character>(); var map = grid.stream(clues.c()).collect(Collectors.toMap(LetterAt::index, LetterAt::human));
grid.forEachLetter(clues.c(), (idx, letter) -> {
if (letter == 0) return;
letterAt.put(idx, (char) (64 | letter));
});
// 4) render gridv2 over cropped bounds (out-of-bounds become '#') // 4) render gridv2 over cropped bounds (out-of-bounds become '#')
var gridv2 = new String[Math.max(0, maxR - minR + 1)]; var gridv2 = new String[Math.max(0, maxR - minR + 1)];
for (int r = minR, i = 0; r <= maxR; r++, i++) { for (int r = minR, i = 0; r <= maxR; r++, i++) {
var row = new StringBuilder(Math.max(0, maxC - minC + 1)); var row = new StringBuilder(Math.max(0, maxC - minC + 1));
for (var c = minC; c <= maxC; c++) row.append(letterAt.getOrDefault(Grid.offset(r, c), '#')); for (var c = minC; c <= maxC; c++) row.append(map.getOrDefault(Grid.offset(r, c), '#'));
gridv2[i] = row.toString(); gridv2[i] = row.toString();
} }
// 5) words output with cropped coordinates // 5) words output with cropped coordinates
int MIN_R = minR, MIN_C = minC; int MIN_R = minR, MIN_C = minC;
var wordsOut = placed.stream().map(p -> new WordOut( var wordsOut = Arrays.stream(placed).map(p -> new WordOut(
p.lemma, p.lemma,
p.startRow() - MIN_R, p.startRow() - MIN_R,
p.startCol() - MIN_C, p.startCol() - MIN_C,

View File

@@ -402,20 +402,19 @@ public class Main {
} }
static Clues generateNewClues(Rng rng, Opts opts) { static Clues generateNewClues(Rng rng, Opts opts) {
var masker = new Masker(rng, new int[STACK_SIZE], Masker.Clues.createEmpty()); var masker = new Masker(rng, new int[STACK_SIZE], Masker.Clues.createEmpty());
var mask = masker.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring); return masker.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
return mask;
} }
static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) { static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) {
long t0 = System.currentTimeMillis(); val multiThreaded = Thread.currentThread().getName().contains("pool");
long t0 = System.currentTimeMillis();
TOTAL_ATTEMPTS.incrementAndGet(); TOTAL_ATTEMPTS.incrementAndGet();
val mask = generateNewClues(rng, opts); val mask = generateNewClues(rng, opts);
//val mask = generateClues(); //val mask = generateClues();
if (mask == null) return null; if (mask == null) return null;
val multiThreaded = Thread.currentThread().getName().contains("pool");
var slots = Masker.extractSlots(mask, dict.index()); val slotInfo = Masker.slots(mask, dict.index());
val slotInfo = Masker.scoreSlots(slots); var grid = mask.toGrid();
var grid = mask.toGrid(); var filled = fillMask(rng, slotInfo, grid, (!Main.VERBOSE || multiThreaded));
var filled = fillMask(rng, slotInfo, grid, multiThreaded);
if (!multiThreaded) { if (!multiThreaded) {
System.out.print("\r" + Strings.padRight("", 120) + "\r"); System.out.print("\r" + Strings.padRight("", 120) + "\r");

View File

@@ -75,6 +75,10 @@ public record Masker(Rng rng, int[] stack, Clues cache) {
grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)])); grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)]));
return slots; return slots;
} }
public static Slotinfo[] slots(Clues mask, DictEntry[] index) {
var slots = Masker.extractSlots(mask, index);
return Masker.scoreSlots(slots);
}
public static Slotinfo[] scoreSlots(Slot[] slots) { public static Slotinfo[] scoreSlots(Slot[] slots) {
val count = new byte[SwedishGenerator.SIZE]; val count = new byte[SwedishGenerator.SIZE];
Slotinfo[] slotInfo = new Slotinfo[slots.length]; Slotinfo[] slotInfo = new Slotinfo[slots.length];

View File

@@ -134,6 +134,10 @@ public record SwedishGenerator() {
public final byte[] g; public final byte[] g;
public long lo, hi; public long lo, hi;
public static int offset(int r, int c) { return r | (c << 3); } public static int offset(int r, int c) { return r | (c << 3); }
public Grid copy() {
return new Grid(g.clone(), lo, hi);
}
} }
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { } public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
@@ -258,8 +262,7 @@ public record SwedishGenerator() {
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots, public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
final Grid grid, final Grid grid,
final boolean multiThreaded) { final boolean NO_LOG) {
val NO_LOG = (!Main.VERBOSE || multiThreaded);
val used = new long[2048]; val used = new long[2048];
val bitset = new long[2500]; val bitset = new long[2500];
val g = grid.g; val g = grid.g;
@@ -388,12 +391,11 @@ public record SwedishGenerator() {
var tries = Math.min(MAX_TRIES_PER_SLOT, L); var tries = Math.min(MAX_TRIES_PER_SLOT, L);
for (var t = 0; t < tries; t++) { for (var t = 0; t < tries; t++) {
var r = rng.nextFloat(); //var r = rng.nextFloat();
//int idxInArray = rng.biasedIndexPow3(L - 1); //var idxInArray = (int) (r * r * r * (L - 1));
var arrIndex = (int) (r * r * r * (L - 1)); int idxInArray = rng.biasedIndexPow3(L - 1);
var shardIdx = idxs[arrIndex]; var w = entry.words[idxs[idxInArray/*(int) (r * r * r * (L - 1))*/]];
var w = entry.words[shardIdx]; var lemIdx = Lemma.unpackIndex(w);
var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
top = ghi; top = ghi;
@@ -419,10 +421,9 @@ public record SwedishGenerator() {
var tries = Math.min(MAX_TRIES_PER_SLOT, N); var tries = Math.min(MAX_TRIES_PER_SLOT, N);
for (var t = 0; t < tries; t++) { for (var t = 0; t < tries; t++) {
double r = rng.nextFloat(); // double r = rng.nextFloat();
var shardIdx = (int) (r * r * r * (N - 1)); var w = entry.words[rng.biasedIndexPow3(N - 1)/*(int) (r * r * r * (N - 1))*/];
var w = entry.words[shardIdx]; var lemIdx = Lemma.unpackIndex(w);
var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
top = ghi; top = ghi;

View File

@@ -212,7 +212,7 @@ public class MainTest {
var filled = fillMask(rng, slotInfo, grid, false); var filled = fillMask(rng, slotInfo, grid, false);
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed"); Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
Assertions.assertEquals("POENIGE", Lemma.asWord(slotInfo[0].assign().w)); Assertions.assertEquals("VREEMDS", Lemma.asWord(slotInfo[0].assign().w));
Assertions.assertEquals(-1L, grid.lo); Assertions.assertEquals(-1L, grid.lo);
Assertions.assertEquals(255L, grid.hi); Assertions.assertEquals(255L, grid.hi);
var g = new Gridded(grid); var g = new Gridded(grid);