introduce bitloops

This commit is contained in:
mike
2026-01-21 00:56:18 +01:00
parent 78f72a024e
commit 92a736aa0a
7 changed files with 108 additions and 110 deletions

View File

@@ -70,15 +70,10 @@ public record Export() {
public record Clued(@Delegate Clues c) {
public static Clues create(Cell... cell) {
var empty = createEmpty();
for (var cell1 : cell) empty.setClue(cell1);
return empty;
}
public static Clues of(Cell... cells) {
public static Clued of(Cell... cells) {
var c = createEmpty();
for (var cell : cells) c.setClue(cell);
return c;
return new Clued(c);
}
public Clued deepCopyGrid() { return new Clued(new Clues(c.lo, c.hi, c.vlo, c.vhi, c.rlo, c.rhi, c.xlo, c.xhi)); }
public static Clued parse(String s) {
@@ -125,6 +120,9 @@ public record Export() {
return stream.build();
}
public Slotinfo[] slots() {
return Masker.slots(c, DictData.DICT.index());
}
}
@FunctionalInterface
@@ -143,13 +141,15 @@ public record Export() {
default void visit(int index, byte[] letters) { visit(index, letters[index]); }
}
record Gridded(@Delegate Grid grid, Clues cl) {
record Gridded(@Delegate Grid grid, Clues cl)
implements Stream<LetterAt> {
public Gridded(Clues clues) { this(clues.toGrid(), clues); }
public Stream<LetterAt> stream(Clues clues) {
public Gridded(Clued clues) { this(clues.toGrid(), clues.c); }
public @Delegate Stream<LetterAt> stream() {
val stream = Stream.<LetterAt>builder();
for (var l = grid.lo & ~clues.lo; l != X; l &= l - 1) stream.accept(LetterAt.from(Long.numberOfTrailingZeros(l), grid.g));
for (var h = grid.hi & ~clues.hi & 0xFF; h != X; h &= h - 1) stream.accept(LetterAt.from(64 | Long.numberOfTrailingZeros(h), grid.g));
for (var l = grid.lo & ~cl.lo; l != X; l &= l - 1) stream.accept(LetterAt.from(Long.numberOfTrailingZeros(l), grid.g));
for (var h = grid.hi & ~cl.hi & 0xFF; h != X; h &= h - 1) stream.accept(LetterAt.from(64 | Long.numberOfTrailingZeros(h), grid.g));
return stream.build();
}
String gridToString(Clues clues) {
@@ -161,7 +161,7 @@ public record Export() {
val dir = Slot.dir(s);
sb.setCharAt(r * (C + 1) + c, (char) (dir | 48));
});
stream(clues).forEach((l) -> sb.setCharAt(l.index(C + 1), l.human()));
stream().forEach((l) -> sb.setCharAt(l.index(C + 1), l.human()));
return sb.toString();
}
public String[] exportGrid(Clues clues, Replacar clueChar, char emptyFallback) {
@@ -173,7 +173,7 @@ public record Export() {
val dir = Slot.dir(s);
sb.setCharAt(r * (C + 1) + c, clueChar.replace(new Rell(grid, clues, idx, (byte) (dir | 48))));
});
stream(clues).forEach((l) -> sb.setCharAt(l.index(C + 1), l.human()));
stream().forEach((l) -> sb.setCharAt(l.index(C + 1), l.human()));
return sb.toString().replaceAll(" ", "" + emptyFallback).split("\n");
}
public static IntStream cellWalk(byte base, long lo, long hi) {
@@ -291,7 +291,7 @@ public record Export() {
}
// 3) map of only used letter cells (everything else becomes '#')
var map = grid.stream(clues.c()).collect(Collectors.toMap(LetterAt::index, LetterAt::human));
var map = grid.stream().collect(Collectors.toMap(LetterAt::index, LetterAt::human));
// 4) render gridv2 over cropped bounds (out-of-bounds become '#')
var gridv2 = new String[Math.max(0, maxR - minR + 1)];
for (int r = minR, i = 0; r <= maxR; r++, i++) {

View File

@@ -96,7 +96,7 @@ public class Main {
section("Words");
printWordsTable(exported.words());
section("Gridv2");
section("Grid");
for (var row : exported.grid()) System.out.println(" " + row);
var theme = "algemeen";

View File

@@ -2,6 +2,8 @@ package puzzle;
import module java.base;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.val;
import precomp.Neighbors9x8;
import precomp.Neighbors9x8.rci;
@@ -15,11 +17,11 @@ public final class Masker {
private final Rng rng;
private final int[] stack;
private final Clues cache;
private final int[] activeCIdx = new int[SwedishGenerator.SIZE];
private final long[] activeSLo = new long[SwedishGenerator.SIZE];
private final long[] activeSHi = new long[SwedishGenerator.SIZE];
private final long[] adjLo = new long[SwedishGenerator.SIZE];
private final long[] adjHi = new long[SwedishGenerator.SIZE];
private final int[] activeCIdx = new int[Neighbors9x8.SIZE];
private final long[] activeSLo = new long[Neighbors9x8.SIZE];
private final long[] activeSHi = new long[Neighbors9x8.SIZE];
private final long[] adjLo = new long[Neighbors9x8.SIZE];
private final long[] adjHi = new long[Neighbors9x8.SIZE];
private final int[] rCount = new int[8];
private final int[] cCount = new int[9];
private static final long[] NBR_LO = Neighbors9x8.NBR_LO;
@@ -133,20 +135,20 @@ public final class Masker {
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= SwedishGenerator.MIN_LEN);
}
private static boolean validSlot(long lo, long hi, int key) {
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
var hitsLo = rayLo & lo;
var hitsHi = rayHi & hi;
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
var hitsLo = rayLo & lo;
var hitsHi = rayHi & hi;
if (hitsLo != X) return (Long.bitCount(rayLo & ((1L << numberOfTrailingZeros(hitsLo)) - 1)) >= SwedishGenerator.MIN_LEN);
else if (hitsHi != X) return (Long.bitCount(rayLo) + Long.bitCount(rayHi & ((1L << numberOfTrailingZeros(hitsHi)) - 1)) >= SwedishGenerator.MIN_LEN);
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= SwedishGenerator.MIN_LEN);
}
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
var hitsLo = rayLo & c.lo;
var hitsHi = rayHi & c.hi;
var rayLo = PATH_LO[key];
var rayHi = PATH_HI[key];
var hitsLo = rayLo & c.lo;
var hitsHi = rayHi & c.hi;
if (hitsLo != X) {
var stop = 1L << numberOfTrailingZeros(hitsLo);
@@ -159,9 +161,9 @@ public final class Masker {
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
visitor.visit(key, rayLo, rayHi);
}
public static Slot[] extractSlots(Clues grid, DictEntry[] index) {
var slots = new ArrayList<Slot>(grid.clueCount());
grid.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, index[Slot.length(lo, hi)])));
public static Slot[] extractSlots(Clues c, DictEntry[] index) {
var slots = new ArrayList<Slot>(c.clueCount());
c.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, index[Slot.length(lo, hi)])));
return slots.toArray(Slot[]::new);
}
public static Slotinfo[] slots(Clues mask, DictEntry[] index) {
@@ -193,19 +195,19 @@ public final class Masker {
public long maskFitness(final Clues grid, int clueSize) {
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
long cHLo2 = 0L, cHHi2 = 0L, cVLo2 = 0L, cVHi2 = 0L;
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
long cHLo2 = 0L, cHHi2 = 0L, cVLo2 = 0L, cVHi2 = 0L;
long lo_cl = grid.lo, hi_cl = grid.hi;
var penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
var hasSlots = false;
var numClues = 0;
for (var bits = lo_cl; bits != X; bits &= bits - 1) {
var lsb = bits & -bits;
var clueIdx = numberOfTrailingZeros(lsb);
var lsb = bits & -bits;
var clueIdx = numberOfTrailingZeros(lsb);
int dir = grid.getDir(clueIdx);
var key = Slot.packSlotKey(clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
var key = Slot.packSlotKey(clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slotinfo.increasing(key)) {
if (hLo != X) {
@@ -245,11 +247,11 @@ public final class Masker {
} else penalty += 25000;
}
for (var bits = hi_cl; bits != X; bits &= bits - 1) {
var lsb = bits & -bits;
var clueIdx = numberOfTrailingZeros(lsb);
var lsb = bits & -bits;
var clueIdx = numberOfTrailingZeros(lsb);
int dir = grid.getDir(64 | clueIdx);
var key = Slot.packSlotKey(64 | clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
var key = Slot.packSlotKey(64 | clueIdx, dir);
long rLo = PATH_LO[key], rHi = PATH_HI[key];
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
if (Slotinfo.increasing(key)) {
if (hLo != X) {
@@ -588,9 +590,9 @@ public final class Masker {
var childCount = 0;
for (var k = 0; k < offspring; k++) {
if (Thread.currentThread().isInterrupted()) break;
var p1 = rng.rand(pop);
var p2 = rng.rand(pop);
var child = crossover(p1.grid, p2.grid);
var p1 = rng.rand(pop);
var p2 = rng.rand(pop);
var child = crossover(p1.grid, p2.grid);
children[k] = new GridAndFit(hillclimb(child, clueSize, 70));
childCount++;
}
@@ -641,11 +643,14 @@ public final class Masker {
}
//@formatter:off
@FunctionalInterface public interface SlotVisitor { void visit(int key, long lo, long hi); }
sealed interface BitPop permits Clues { long hi(); long lo(); }
//@formatter:on
@AllArgsConstructor
public static class Clues {
@Accessors(fluent = true)
public static final class Clues
implements BitPop {
long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
@Getter long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0, 0, 0); }
public boolean hasRoomForClue(int key) {

View File

@@ -39,7 +39,7 @@ public record SwedishGenerator() {
public static final long MASK_HI = Neighbors9x8.MASK_HI;//(1L << (SIZE - 64)) - 1;
public static final int MAX_WORD_LENGTH = Config.PUZZLE_ROWS;
public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
public static final int MIN_LEN = 2;//Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
public static final int MIN_LEN = Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
public static final int MAX_TRIES_PER_SLOT = 500;//Config.MAX_TRIES_PER_SLOT;
public static final int STACK_SIZE = 128;
public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;// (long) SIZE_MIN_1 - 0L + 1L
@@ -117,7 +117,7 @@ public record SwedishGenerator() {
static int unpackIndex(long w) { return (int) (w >>> 40); }
static int unpackShardIndex(long w) { return (int) (w >>> 43); }
static int unpackSize(long w) { return (int) (w >>> 40) & 7; }
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
static long pack43(long w) {
return w & INDEX_MASK;
}