introduce bitloops

This commit is contained in:
mike
2026-01-12 06:42:22 +01:00
parent b7b66b5cd6
commit f655f70ab8
5 changed files with 38 additions and 36 deletions

View File

@@ -81,7 +81,7 @@ public record Export() {
public void clear() { Arrays.fill(bits, 0L); }
}
record Placed(Lemma lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) {
record Placed(long lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) {
public static final char HORIZONTAL = 'h';
static final char VERTICAL = 'v';
@@ -91,8 +91,8 @@ public record Export() {
public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) {
public WordOut(Lemma l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) {
this(l.asWord(), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, l.simpel(), l.clue());
public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) {
this(Lemma.asWord(l), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, Lemma.simpel(l), Lemma.clue(l));
}
}
@@ -101,7 +101,7 @@ public record Export() {
public record PuzzleResult(SwedishGenerator swe, Dict dict, Gridded mask, FillResult filled) {
boolean inBounds(int r, int c) { return r >= 0 && r < SwedishGenerator.R && c >= 0 && c < SwedishGenerator.C; }
Placed extractPlacedFromSlot(Slot s, Lemma lemma) {
Placed extractPlacedFromSlot(Slot s, long lemma) {
var d = s.dir();
var cells = new int[s.len()];

View File

@@ -108,12 +108,12 @@ public record SwedishGenerator(Rng rng) {
public static record FillResult(boolean ok,
Gridded grid,
Map<Integer, Lemma> clueMap,
Map<Integer, Long> clueMap,
FillStats stats) {
public void calcSimpel() {
if (ok) {
clueMap.forEach((k, v) -> stats.simplicity += v.simpel());
clueMap.forEach((k, v) -> stats.simplicity += Lemma.simpel(v));
stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size();
}
}
@@ -243,7 +243,7 @@ public record SwedishGenerator(Rng rng) {
}
}
static record DictEntry(Lemma[] words, long[][] posBitsets) { }
static record DictEntry(long[] words, long[][] posBitsets) { }
public static record Lemma(long word) {
@@ -261,13 +261,15 @@ public record SwedishGenerator(Rng rng) {
}
public Lemma(int index, String word) { this(pack(index, word.getBytes(US_ASCII))); }
public Lemma(String word) { this(LEMMA_COUNTER++, word); }
byte byteAt(int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; }
byte byteAt(int idx) { return Lemma.byteAt(word, idx); }// word[]; }
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; }
int intAt(int idx) { return (int) (((word >>> (idx * 5))) & 0b11111); }// word[]; }
@Override public int hashCode() { return unpackIndex(word); }
@Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.word == word); }
String[] clue() { return CsvIndexService.clues(unpackIndex(word)); }
int simpel() { return CsvIndexService.simpel(unpackIndex(word)); }
int length() {
static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); }
static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); }
int length() { return Lemma.length(word); }
static int length(long word) {
if (word == 0) return 0;
int highestBit = 63 - Long.numberOfLeadingZeros(word & LETTER_MASK);
return (highestBit / 5) + 1;
@@ -277,9 +279,9 @@ public record SwedishGenerator(Rng rng) {
int highestBit = 63 - Long.numberOfLeadingZeros(p & LETTER_MASK); // 0-based
return (highestBit / 5) + 1;
}
public String asWord() {
var b = new byte[length()];
for (var i = 0; i < length(); i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64);
public static String asWord(long word) {
var b = new byte[Lemma.length(word)];
for (var i = 0; i < b.length; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64);
return new String(b, US_ASCII);
}
static int unpackIndex(long w) {
@@ -312,7 +314,7 @@ public record SwedishGenerator(Rng rng) {
}
for (int i = MIN_LEN; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i);
this(Arrays.stream(index).map(i -> {
var words = i.words().toArray(Lemma[]::new);
var words = i.words().stream().mapToLong(ww -> ww.word).toArray();
int numWords = words.length;
int numLongs = (numWords + 63) >>> 6;
var bitsets = new long[i.pos().length * 26][numLongs];
@@ -677,13 +679,13 @@ public record SwedishGenerator(Rng rng) {
for (int i = 0; i < len; i++) cross += (count[s.pos(i)] - 1);
return cross * 10 + len;
}
static boolean placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) {
static boolean placeWord(Grid grid, Slot s, long w, int[] undoBuffer, int offset) {
int mask = 0;
byte cur, ch;
for (int i = 0, leng = s.len(), idx; i < leng; i++) {
idx = s.pos(i);
cur = grid.byteAt(idx);
ch = w.byteAt(i);
ch = Lemma.byteAt(w, i);
if (cur == DASH) {
mask |= (1 << i);
grid.setByteAt(idx, ch);
@@ -778,9 +780,9 @@ public record SwedishGenerator(Rng rng) {
val used = new Bit1029();
// val assigned = new HashMap<Integer, Lemma>();
Lemma[] assigned = new Lemma[BIGG];
val ctx = CTX.get();
val count = ctx.cellCount;
long[] assigned = new long[BIGG];
val ctx = CTX.get();
val count = ctx.cellCount;
Arrays.fill(count, 0, SIZE, 0);
val slots = extractSlots(grid);
@@ -803,7 +805,7 @@ public record SwedishGenerator(Rng rng) {
lastLog = (now);
var done = 0;
for (var lemma : assigned) {
if (lemma != null) done++;
if (lemma != X) done++;
}
var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100);
var filled = Math.min(BAR_LEN, (int) Math.floor((pct / 100.0) * BAR_LEN));
@@ -824,7 +826,7 @@ public record SwedishGenerator(Rng rng) {
int bestScore = -1;
for (int i = 0, n = slots.size(); i < n; i++) {
var s = slots.get(i);
if (assigned[s.key()] != null) continue;
if (assigned[s.key()] != X) continue;
var entry = dictIndex[s.len()];
if (entry == null) return PICK_NOT_DONE;
ctx.pattern = patternForSlot(grid, s);
@@ -880,7 +882,7 @@ public record SwedishGenerator(Rng rng) {
int idxInArray = (int) (r * r * r * L);
var idx = idxs[idxInArray];
var w = entry.words[idx];
var lemIdx = Lemma.unpackIndex(w.word);
var lemIdx = Lemma.unpackIndex(w);
if (used.get(lemIdx)) continue;
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
@@ -890,7 +892,7 @@ public record SwedishGenerator(Rng rng) {
if (backtrack(depth + 1)) return true;
assigned[k] = null;
assigned[k] = X;
used.clear(lemIdx);
s.undoPlace(grid, ctx.undo[depth]);
}
@@ -909,7 +911,7 @@ public record SwedishGenerator(Rng rng) {
double r = rng.nextFloat();
int idxInArray = (int) (r * r * r * N);
var w = entry.words[idxInArray];
var lemIdx = Lemma.unpackIndex(w.word);
var lemIdx = Lemma.unpackIndex(w);
if (used.get(lemIdx)) continue;
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
@@ -919,7 +921,7 @@ public record SwedishGenerator(Rng rng) {
if (backtrack(depth + 1)) return true;
assigned[k] = null;
assigned[k] = X;
used.clear(lemIdx);
s.undoPlace(grid, ctx.undo[depth]);
}
@@ -940,9 +942,9 @@ public record SwedishGenerator(Rng rng) {
}
stats.seconds = (System.currentTimeMillis() - t0) / 1000.0;
Map<Integer, Lemma> lemmaMap = new HashMap<>();
Map<Integer, Long> lemmaMap = new HashMap<>();
for (var i = 0; i < assigned.length; i++) {
if (assigned[i] != null) {
if (assigned[i] != X) {
lemmaMap.put(i, assigned[i]);
}
}

View File

@@ -28,11 +28,11 @@ public class ExportFormatTest {
grid.setClue(0, (byte) '2');
// This creates a slot starting at (0,1)
var clueMap = new HashMap<Integer, Lemma>();
var clueMap = new HashMap<Integer, Long>();
// key = (cellIndex << 4) | direction
var key = (0 << 4) | 2;
var lemma = new Lemma("TEST");
clueMap.put(key, lemma);
clueMap.put(key, lemma.word());
// Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4)
grid.setByteAt(Grid.offset(0, 1), (byte) 'T');

View File

@@ -167,7 +167,7 @@ public class MainTest {
// Regression baseline for seed search starting at 12347, pop 4, gens 20
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
Assertions.assertEquals(18, res.filled().clueMap().size(), "Number of assigned words changed");
Assertions.assertEquals("RIJTUIG", res.filled().clueMap().get(74).asWord());
Assertions.assertEquals("RIJTUIG", Lemma.asWord( res.filled().clueMap().get(74)));
Assertions.assertEquals(301794542151533187L, res.filled().grid().grid().lo);
Assertions.assertEquals(193L, res.filled().grid().grid().hi);
}

View File

@@ -108,7 +108,7 @@ public class SwedishGeneratorTest {
var l1 = new Lemma("APPLE");
Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(l1.word()));
assertEquals(5, l1.length());
assertEquals(5, Lemma.length(l1.word()));
assertEquals((byte) 'A', l1.byteAt(0));
assertEquals(1, l1.intAt(0));
@@ -120,7 +120,7 @@ public class SwedishGeneratorTest {
var entry3 = dict.index()[3];
assertEquals(1, entry3.words().length);
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(entry3.words()[0].word()));
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(entry3.words()[0]));
// Check pos indexing
// AXE: A at 0, X at 1, E at 2
@@ -276,7 +276,7 @@ public class SwedishGeneratorTest {
// r(i) and c(i) are used by placeWord.
var packedPos = ((long) Grid.offset(0, 0)) | (((long) Grid.offset(0, 1)) << 7) | (((long) Grid.offset(0, 2)) << 14);
var s = Slot.from(0, packedPos, 3);
var w1 = new Lemma("ABC");
var w1 = new Lemma("ABC").word();
var undoBuffer = new int[10];
// 1. Successful placement in empty grid
@@ -291,7 +291,7 @@ public class SwedishGeneratorTest {
assertEquals(0L, undoBuffer[1]); // 0 new characters placed
// 3. Conflict: place "ABD" where "ABC" is
var w2 = new Lemma("ABD");
var w2 = new Lemma("ABD").word();
assertFalse(placeWord(grid, s, w2, undoBuffer, 2));
// Verify grid is unchanged (still "ABC")
assertEquals('A', grid.byteAt(Grid.offset(0, 0)));
@@ -314,7 +314,7 @@ public class SwedishGeneratorTest {
// Slot at 0,1 length 2
var packedPos = ((long) Grid.offset(0, 1)) | (((long) Grid.offset(0, 2)) << 7);
var s = Slot.from((0 << 8) | (1 << 4) | 2, packedPos, 2);
var w = new Lemma("AZ");
var w = new Lemma("AZ").word();
var undoBuffer = new int[10];
var placed = placeWord(grid, s, w, undoBuffer, 0);