introduce bitloops

This commit is contained in:
mike
2026-01-11 23:11:16 +01:00
parent 85e6f32b3c
commit 986c2f85a9
5 changed files with 50 additions and 35 deletions

View File

@@ -93,7 +93,7 @@ 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(new String(l.word(), US_ASCII), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, l.simpel(), l.clue());
this(l.asWord(), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, l.simpel(), l.clue());
}
}

View File

@@ -347,7 +347,7 @@ public class Main {
static PuzzleResult attempt(Rng rng, Dict dict, Opts opts) {
TOTAL_ATTEMPTS.incrementAndGet();
var swe = new SwedishGenerator(rng);
var mask = swe.generateMask(opts.pop, opts.gens);
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
var filled = swe.fillMask(mask, dict.index(), opts.fillTimeout);
TOTAL_NODES.addAndGet(filled.stats().nodes);

View File

@@ -19,6 +19,8 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.stream.IntStream;
import static java.nio.charset.StandardCharsets.*;
/**
* SwedishGenerator.java
@@ -30,7 +32,10 @@ import java.util.Locale;
@SuppressWarnings("ALL")
public record SwedishGenerator(Rng rng) {
record CandidateInfo(int[] indices, int count) { }
record CandidateInfo(int[] indices, int count) {
public CandidateInfo(int n) { this(null, n); }
}
//@formatter:off
@FunctionalInterface interface SlotVisitor { void visit(int key, long packedPos, int len); }
@@ -283,15 +288,25 @@ public record SwedishGenerator(Rng rng) {
}
}
public static record Lemma(int index, byte[] word, int simpel) {
public static record Lemma(int index, long word, byte len, short simpel) {
static int LEMMA_COUNTER = 0;
public Lemma(int index, String word, int simpel) { this(index, word.getBytes(StandardCharsets.US_ASCII), simpel); }
static long pack(byte[] b) {
long w = 0;
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & ~64) << (i * 5);
return w;
}
public Lemma(int index, String word, int simpel) { this(index, pack(word.getBytes(US_ASCII)), (byte) word.length(), (short) simpel); }
public Lemma(String word, int simpel) { this(LEMMA_COUNTER++, word, simpel); }
byte byteAt(int idx) { return word[idx]; }
byte byteAt(int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | Grid.B64); }// word[]; }
@Override public int hashCode() { return index; }
@Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.index == index); }
String[] clue() { return CsvIndexService.clues(index); }
public String asWord() {
var b = new byte[len];
for (var i = 0; i < len; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | Grid.B64);
return new String(b, US_ASCII);
}
}
public static record Dict(
@@ -302,7 +317,7 @@ public record SwedishGenerator(Rng rng) {
var index = new DictEntry[MAX_WORD_LENGTH_PLUS_ONE];
Arrays.setAll(index, i -> new DictEntry(i));
for (var lemma : wordz) {
var L = lemma.word.length;
var L = lemma.len;
var entry = index[L];
var idx = entry.words.size();
@@ -320,7 +335,7 @@ public record SwedishGenerator(Rng rng) {
static Dict loadDict(String wordsPath) {
try {
var map = new ArrayList<Lemma>();
Files.lines(Path.of(wordsPath), StandardCharsets.UTF_8).forEach(line -> CsvIndexService.lineToLemma(line, map::add));
Files.lines(Path.of(wordsPath), UTF_8).forEach(line -> CsvIndexService.lineToLemma(line, map::add));
return new Dict(map.toArray(Lemma[]::new));
} catch (IOException e) {
e.printStackTrace();
@@ -549,7 +564,7 @@ public record SwedishGenerator(Rng rng) {
return best;
}
public Grid generateMask(int popSize, int gens) {
public Grid generateMask(int popSize, int gens, int pairs) {
class GridAndFit {
Grid grid;
@@ -569,7 +584,6 @@ public record SwedishGenerator(Rng rng) {
for (var gen = 0; gen < gens; gen++) {
if (Thread.currentThread().isInterrupted()) break;
var children = new ArrayList<GridAndFit>();
var pairs = Math.max(popSize, (int) Math.floor(popSize * 1.5));
for (var k = 0; k < pairs; k++) {
var p1 = pop.get(rng.randint(0, pop.size() - 1));
@@ -595,18 +609,16 @@ public record SwedishGenerator(Rng rng) {
}
pop = next;
if (Main.VERBOSE && gen % 10 == 0) {
var bestF = pop.get(0).fit();
System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + bestF);
}
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop.get(0).fit());
}
pop.sort(Comparator.comparingLong(GridAndFit::fit));
return pop.get(0).grid;
}
static void patternForSlot(Grid grid, Slot s, byte[] pat) {
byte ch;
for (int i = 0, len = s.len(); i < len; i++) {
var ch = grid.byteAt(s.pos(i));
ch = grid.byteAt(s.pos(i));
pat[i] = isLetter(ch) ? ch : DASH;
}
}
@@ -638,26 +650,29 @@ public record SwedishGenerator(Rng rng) {
undoBuffer[offset] = mask;
return true;
}
static final CandidateInfo[] CANDIDATES = IntStream.range(0, 10192 << 2).mapToObj(CandidateInfo::new).toArray(CandidateInfo[]::new);
static CandidateInfo candidateInfoForPattern(Context ctx, DictEntry entry, int len) {
var pattern = ctx.pattern;
var listBuffer = ctx.intListBuffer;
var listCount = 0;
var pattern = ctx.pattern;
var listBuffer = ctx.intListBuffer;
var listCount = 0;
IntList tmp;
byte ch;
for (var i = 0; i < len; i++) {
var ch = pattern[i];
ch = pattern[i];
if (isLetter(ch)) {
listBuffer[listCount++] = entry.pos[i][ch - 'A'];
}
}
if (listCount == 0) {
return new CandidateInfo(null, entry.words.size());
return CANDIDATES[entry.words.size()];
}
// Sort constraints by size to optimize intersection
for (int i = 0; i < listCount - 1; i++) {
for (int j = i + 1; j < listCount; j++) {
for (var i = 0; i < listCount - 1; i++) {
for (var j = i + 1; j < listCount; j++) {
if (listBuffer[j].size() < listBuffer[i].size()) {
var tmp = listBuffer[i];
tmp = listBuffer[i];
listBuffer[i] = listBuffer[j];
listBuffer[j] = tmp;
}
@@ -668,14 +683,14 @@ public record SwedishGenerator(Rng rng) {
var curLen = listBuffer[0].size();
if (listCount == 1) return new CandidateInfo(cur, curLen);
int[] b1 = ctx.inter1;
int[] b2 = ctx.inter2;
int[] in = cur;
int[] out = b1;
val b1 = ctx.inter1;
val b2 = ctx.inter2;
var in = cur;
var out = b1;
for (var k = 1; k < listCount; k++) {
var nxt = listBuffer[k];
curLen = intersectSorted(in, curLen, nxt.data(), nxt.size(), out);
tmp = listBuffer[k];
curLen = intersectSorted(in, curLen, tmp.data(), tmp.size(), out);
in = out;
out = (out == b1) ? b2 : b1;
if (curLen == 0) break;

View File

@@ -168,7 +168,7 @@ public class MainTest {
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
Assertions.assertEquals(22, res.filled().clueMap().size(), "Number of assigned words changed");
Assertions.assertEquals(747.5454545454545, res.filled().stats().simplicity, 1e-9, "Simplicity value changed");
Assertions.assertArrayEquals(new byte[]{ 'I', 'E', 'M', 'A', 'N', 'D', 'S' }, res.filled().clueMap().get(515).word());
Assertions.assertEquals(Lemma.pack(new byte[]{ 'I', 'E', 'M', 'A', 'N', 'D', 'S' }), res.filled().clueMap().get(515).word());
}
@Test
public void testIsLetterA() {

View File

@@ -79,7 +79,7 @@ public class SwedishGeneratorTest {
assertFalse(grid.isDigitAt(0));
assertTrue(grid.isDigitAt(Grid.offset(0, 1)));
assertFalse(grid.isLetterAt(Grid.offset(0, 1)));
assertTrue(grid.isLetterAt( 0));
assertTrue(grid.isLetterAt(0));
assertFalse(grid.isLetterAt(Grid.offset(0, 1)));
var copy = grid.deepCopyGrid();
@@ -110,8 +110,8 @@ public class SwedishGeneratorTest {
var l8a = new Lemma("INERENAE", 1);
var l1 = new Lemma("APPLE", 5);
Assertions.assertArrayEquals("APPLE".getBytes(StandardCharsets.US_ASCII), l1.word());
assertEquals(5, l1.word().length);
Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), l1.word());
assertEquals(5, l1.len());
assertEquals(5, l1.simpel());
assertEquals((byte) 'A', l1.byteAt(0));
@@ -123,7 +123,7 @@ public class SwedishGeneratorTest {
var entry3 = dict.index()[3];
assertEquals(1, entry3.words().size());
Assertions.assertArrayEquals("AXE".getBytes(StandardCharsets.US_ASCII), entry3.words().getFirst().word());
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), entry3.words().getFirst().word());
// Check pos indexing
// AXE: A at 0, X at 1, E at 2
@@ -279,7 +279,7 @@ public class SwedishGeneratorTest {
// 1. Successful placement in empty grid
assertTrue(placeWord(grid, s, w1, undoBuffer, 0));
assertEquals('A', grid.byteAt( 0));
assertEquals('A', grid.byteAt(0));
assertEquals('B', grid.byteAt(Grid.offset(0, 1)));
assertEquals('C', grid.byteAt(Grid.offset(0, 2)));
assertEquals(0b111L, undoBuffer[0]);