introduce bitloops
This commit is contained in:
@@ -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()];
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user