introduce bitloops
This commit is contained in:
@@ -5,8 +5,9 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.experimental.Delegate;
|
||||
import lombok.val;
|
||||
import precomp.Const9x8.Cell;
|
||||
import puzzle.Export.Gridded.Replacar.Rell;
|
||||
import puzzle.Export.Puzzle.Replacar.Rell;
|
||||
import puzzle.Masker.Clues;
|
||||
import puzzle.Meta.ShardLem;
|
||||
import puzzle.SwedishGenerator.Dict;
|
||||
import puzzle.SwedishGenerator.FillResult;
|
||||
import puzzle.SwedishGenerator.Grid;
|
||||
@@ -19,7 +20,6 @@ import static puzzle.Masker.Clues.createEmpty;
|
||||
import static puzzle.Masker.Slot;
|
||||
import static puzzle.Masker.C;
|
||||
import static puzzle.SwedishGenerator.Lemma;
|
||||
import static puzzle.SwedishGenerator.SIZE;
|
||||
import static puzzle.SwedishGenerator.X;
|
||||
|
||||
/**
|
||||
@@ -61,21 +61,16 @@ public record Export() {
|
||||
public static Clue from(int dir) { return CLUES[dir]; }
|
||||
}
|
||||
|
||||
record Strings() {
|
||||
|
||||
static String padRight(String s, int n) { return s.length() >= n ? s : s + " ".repeat(n - s.length()); }
|
||||
}
|
||||
public record Vestigium(int index, int clue) { }
|
||||
|
||||
public record ClueAt(int index, int clue) { }
|
||||
|
||||
public record Clued(@Delegate Clues c) {
|
||||
public record Signa(@Delegate Clues c) {
|
||||
|
||||
public static Clued of(Cell... cells) {
|
||||
public static Signa of(Cell... cells) {
|
||||
var c = createEmpty();
|
||||
for (var cell : cells) c.setClue(cell);
|
||||
return new Clued(c);
|
||||
return new Signa(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 Signa deepCopyGrid() { return new Signa(new Clues(c.lo, c.hi, c.vlo, c.vhi, c.rlo, c.rhi, c.xlo, c.xhi)); }
|
||||
String gridToString() {
|
||||
var sb = new StringBuilder(INIT_GRID_OUTPUT);
|
||||
forEachSlot((s, _, _) -> {
|
||||
@@ -85,21 +80,21 @@ public record Export() {
|
||||
});
|
||||
return sb.toString();
|
||||
}
|
||||
public Stream<ClueAt> stream() {
|
||||
val stream = Stream.<ClueAt>builder();
|
||||
for (var l = c.lo & ~c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), RIGHT1.dir));
|
||||
for (var l = c.lo & ~c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), DOWN0.dir));
|
||||
for (var l = c.lo & ~c.xlo & c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_UP));
|
||||
for (var l = c.lo & ~c.xlo & c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT));
|
||||
for (var l = c.lo & c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP));
|
||||
for (var l = c.lo & c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_RIGHT_TOP));
|
||||
public Stream<Vestigium> stream() {
|
||||
val stream = Stream.<Vestigium>builder();
|
||||
for (var l = c.lo & ~c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), RIGHT1.dir));
|
||||
for (var l = c.lo & ~c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), DOWN0.dir));
|
||||
for (var l = c.lo & ~c.xlo & c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_UP));
|
||||
for (var l = c.lo & ~c.xlo & c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_LEFT));
|
||||
for (var l = c.lo & c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP));
|
||||
for (var l = c.lo & c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new Vestigium(Long.numberOfTrailingZeros(l), CLUE_RIGHT_TOP));
|
||||
|
||||
for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT));
|
||||
for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN));
|
||||
for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_UP));
|
||||
for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT));
|
||||
for (var h = c.hi & c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP));
|
||||
for (var h = c.hi & c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT_TOP));
|
||||
for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT));
|
||||
for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN));
|
||||
for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_UP));
|
||||
for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT));
|
||||
for (var h = c.hi & c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP));
|
||||
for (var h = c.hi & c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new Vestigium(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT_TOP));
|
||||
|
||||
return stream.build();
|
||||
}
|
||||
@@ -108,15 +103,15 @@ public record Export() {
|
||||
}
|
||||
}
|
||||
|
||||
record Gridded(@Delegate Grid grid, Clues cl)
|
||||
implements Stream<LetterAt> {
|
||||
record Puzzle(@Delegate Grid grid, Clues cl)
|
||||
implements Stream<Lettrix> {
|
||||
|
||||
public Gridded(Clues clues) { this(clues.toGrid(), clues); }
|
||||
public Gridded(Clued clues) { this(clues.c); }
|
||||
public @Delegate Stream<LetterAt> stream() {
|
||||
val stream = Stream.<LetterAt>builder();
|
||||
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));
|
||||
public Puzzle(Clues clues) { this(clues.toGrid(), clues); }
|
||||
public Puzzle(Signa clues) { this(clues.c); }
|
||||
public @Delegate Stream<Lettrix> stream() {
|
||||
val stream = Stream.<Lettrix>builder();
|
||||
for (var l = grid.lo & ~cl.lo; l != X; l &= l - 1) stream.accept(Lettrix.from(Long.numberOfTrailingZeros(l), grid.g));
|
||||
for (var h = grid.hi & ~cl.hi & 0xFF; h != X; h &= h - 1) stream.accept(Lettrix.from(64 | Long.numberOfTrailingZeros(h), grid.g));
|
||||
return stream.build();
|
||||
}
|
||||
String gridToString() {
|
||||
@@ -141,7 +136,7 @@ public record Export() {
|
||||
sb[r * (C + 1) + c] = (byte) clueChar.replace(new Rell(grid, cl, idx, (byte) (dir | 48)));
|
||||
});
|
||||
stream().forEach((l) -> sb[l.index(C + 1)] = (byte) l.human());
|
||||
return new String(sb).replaceAll(" ", "" + emptyFallback).split("\n");
|
||||
return new String(sb).replaceAll(" ", String.valueOf(emptyFallback)).split("\n");
|
||||
}
|
||||
public static IntStream cellWalk(byte base, long lo, long hi) {
|
||||
if (Slotinfo.increasing(base)) {
|
||||
@@ -219,8 +214,19 @@ 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) {
|
||||
|
||||
private static ShardLem lookup(long w, byte[] bytes) {
|
||||
|
||||
try {
|
||||
val rec = Meta.lookupSilent(w);
|
||||
System.out.println("\nQuery: w=" + w + " -> i=" + rec.mmap());
|
||||
System.out.println(" word=" + Lemma.asWord(w, bytes) + "\n" + " simpel=" + rec.simpel() + "\n" + " clues=" + Arrays.toString(rec.clues()));
|
||||
return rec;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed, byte[] bytes) {
|
||||
val meta = Meta.lookup(l);
|
||||
val meta = lookup(l, bytes);
|
||||
this(Lemma.asWord(l, bytes), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed,
|
||||
meta.simpel(), meta.clues());
|
||||
}
|
||||
@@ -228,7 +234,7 @@ public record Export() {
|
||||
|
||||
public record ExportedPuzzle(String[] grid, WordOut[] words, int difficulty, Rewards rewards) { }
|
||||
|
||||
public record PuzzleResult(Clued clues, Gridded grid, Slotinfo[] slots, FillResult filled) {
|
||||
public record PuzzleResult(Signa clues, Puzzle grid, Slotinfo[] slots, FillResult filled) {
|
||||
|
||||
public ExportedPuzzle exportFormatFromFilled(Rewards rewards) {
|
||||
// If nothing placed: return full grid mapped to letters/# only
|
||||
@@ -236,7 +242,7 @@ public record Export() {
|
||||
return new ExportedPuzzle(grid.exportGrid(_ -> '#', '#'), new WordOut[0], 1, rewards);
|
||||
}
|
||||
|
||||
var placed = Arrays.stream(slots).map(slot -> new Placed(slot.assign().w, slot.key(), Gridded.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).toArray())).toArray(
|
||||
var placed = Arrays.stream(slots).map(slot -> new Placed(slot.assign().w, slot.key(), Puzzle.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).toArray())).toArray(
|
||||
Placed[]::new);
|
||||
|
||||
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
|
||||
@@ -258,7 +264,7 @@ public record Export() {
|
||||
}
|
||||
|
||||
// 3) map of only used letter cells (everything else becomes '#')
|
||||
var map = grid.stream().collect(Collectors.toMap(LetterAt::index, LetterAt::human));
|
||||
var map = grid.stream().collect(Collectors.toMap(Lettrix::index, Lettrix::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++) {
|
||||
@@ -279,20 +285,17 @@ public record Export() {
|
||||
p.arrowCol() - MIN_C,
|
||||
p.isReversed(), bytes
|
||||
)).toArray(WordOut[]::new);
|
||||
var total = 0.0001d;
|
||||
for (var word : wordsOut) {
|
||||
total += word.complex();
|
||||
}
|
||||
var total = 0.0001d + Arrays.stream(wordsOut).mapToDouble(WordOut::complex).sum();
|
||||
return new ExportedPuzzle(gridv2, wordsOut, (int) (total / wordsOut.length), rewards);
|
||||
}
|
||||
}
|
||||
|
||||
record LetterAt(int index, byte letter) {
|
||||
record Lettrix(int index, byte letter) {
|
||||
|
||||
public int row() { return INDEX_ROW(index); }
|
||||
public int col() { return INDEX_COL(index); }
|
||||
public char human() { return LETTER(letter); }
|
||||
static LetterAt from(int index, byte[] bytes) { return new LetterAt(index, bytes[index]); }
|
||||
public int index(int cols) { return (row() * cols) + col(); }
|
||||
public int row() { return INDEX_ROW(index); }
|
||||
public int col() { return INDEX_COL(index); }
|
||||
public char human() { return LETTER(letter); }
|
||||
static Lettrix from(int index, byte[] bytes) { return new Lettrix(index, bytes[index]); }
|
||||
public int index(int cols) { return (row() * cols) + col(); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
package puzzle;
|
||||
|
||||
import module java.base;
|
||||
import module java.sql;
|
||||
|
||||
public final class HintScores {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:tools/hint/hint.sqlite")) {
|
||||
updateCrossScores(conn, HintScores::crossabilityScore, 1000);
|
||||
}
|
||||
}
|
||||
static final Map<Character, Integer> LETTER_WEIGHT = Map.ofEntries(
|
||||
Map.entry('E', 10), Map.entry('N', 9), Map.entry('A', 9), Map.entry('R', 8),
|
||||
Map.entry('I', 8), Map.entry('O', 7), Map.entry('S', 7), Map.entry('T', 7),
|
||||
Map.entry('D', 6), Map.entry('L', 6), Map.entry('K', 5), Map.entry('M', 5),
|
||||
Map.entry('U', 5), Map.entry('P', 4), Map.entry('G', 4), Map.entry('H', 4),
|
||||
Map.entry('V', 4), Map.entry('B', 3), Map.entry('W', 3),
|
||||
Map.entry('C', 2), Map.entry('F', 2), Map.entry('Z', 2),
|
||||
Map.entry('J', 1), Map.entry('Y', 1), Map.entry('Q', 0), Map.entry('X', 0)
|
||||
);
|
||||
|
||||
static boolean isVowel(char ch) {
|
||||
return ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U';
|
||||
}
|
||||
|
||||
static int crossabilityScore(String w) {
|
||||
var score = 0;
|
||||
var vowels = 0;
|
||||
for (var i = 0; i < w.length(); i++) {
|
||||
var ch = w.charAt(i);
|
||||
score += LETTER_WEIGHT.getOrDefault(ch, 2);
|
||||
if (isVowel(ch)) vowels++;
|
||||
}
|
||||
var ratio = vowels / (double) w.length();
|
||||
if (ratio >= 0.35 && ratio <= 0.65) score += 8;
|
||||
if (w.indexOf('Q') >= 0 || w.indexOf('X') >= 0) score -= 6;
|
||||
if (w.indexOf('Y') >= 0 || w.indexOf('J') >= 0) score -= 2;
|
||||
return score;
|
||||
}
|
||||
/**
|
||||
* Updates hints.cross_score by computing a score from hints.word.
|
||||
*
|
||||
* @param conn open JDBC connection (PostgreSQL)
|
||||
* @param scoreFn callback: scoreFn.applyAsInt(word)
|
||||
* @param batchSize e.g. 1000
|
||||
*/
|
||||
public static void updateCrossScores(
|
||||
Connection conn,
|
||||
ToIntFunction<String> scoreFn,
|
||||
int batchSize
|
||||
) throws SQLException {
|
||||
|
||||
// Use a transaction for speed + consistency
|
||||
final boolean prevAutoCommit = conn.getAutoCommit();
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
// Server-side cursor behavior in pgjdbc requires autoCommit=false + fetchSize>0
|
||||
final String selectSql =
|
||||
"SELECT id, puzzle_norm " +
|
||||
"FROM hints " +
|
||||
"WHERE puzzle_norm IS NOT NULL"; // optionally add: " AND cross_score IS NULL"
|
||||
|
||||
final String updateSql =
|
||||
"UPDATE hints SET cross_score = ? WHERE id = ?";
|
||||
|
||||
try (PreparedStatement psSel = conn.prepareStatement(selectSql);
|
||||
PreparedStatement psUpd = conn.prepareStatement(updateSql)) {
|
||||
|
||||
psSel.setFetchSize(batchSize);
|
||||
|
||||
int pending = 0;
|
||||
|
||||
try (ResultSet rs = psSel.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
long id = rs.getLong("id");
|
||||
String word = rs.getString("puzzle_norm");
|
||||
|
||||
int score;
|
||||
try {
|
||||
score = scoreFn.applyAsInt(word);
|
||||
} catch (RuntimeException ex) {
|
||||
ex.printStackTrace();
|
||||
// If scoring fails, decide your policy: skip or set 0.
|
||||
// Here: skip row.
|
||||
continue;
|
||||
}
|
||||
|
||||
psUpd.setInt(1, score);
|
||||
psUpd.setLong(2, id);
|
||||
psUpd.addBatch();
|
||||
pending++;
|
||||
|
||||
if (pending >= batchSize) {
|
||||
psUpd.executeBatch();
|
||||
conn.commit();
|
||||
pending = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pending > 0) {
|
||||
psUpd.executeBatch();
|
||||
conn.commit();
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
conn.setAutoCommit(prevAutoCommit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,25 @@
|
||||
package puzzle;
|
||||
|
||||
import module java.base;
|
||||
import anno.DictGen;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
import puzzle.Masker.Clues;
|
||||
import puzzle.SwedishGenerator.Rng;
|
||||
|
||||
import static puzzle.Export.*;
|
||||
import static puzzle.SwedishGenerator.*;
|
||||
|
||||
@DictGen(
|
||||
packageName = "puzzle.dict800",
|
||||
className = "DictData800",
|
||||
scv = "/home/mike/dev/puzzle-generator/nl_score_hints_v4.csv",
|
||||
simpleMax = 800,
|
||||
minLen = 2,
|
||||
maxLen = 8
|
||||
)
|
||||
public class Main {
|
||||
|
||||
final static String OUT_DIR = envOrDefault("OUT_DIR", "/data/puzzle");
|
||||
@@ -50,6 +59,7 @@ public class Main {
|
||||
void main(String[] args) {
|
||||
_main(args);
|
||||
}
|
||||
@SneakyThrows
|
||||
public void _main(String[] args) {
|
||||
var opts = parseArgs(args);
|
||||
|
||||
@@ -245,7 +255,7 @@ public class Main {
|
||||
PuzzleResult generatePuzzle(Opts opts) {
|
||||
|
||||
var tLoad0 = System.nanoTime();
|
||||
var dict = puzzle.dict800.DictData.DICT800;//loadDict(opts.wordsPath);
|
||||
Dict dict = puzzle.dict800.DictData800.DICT800;//loadDict(opts.wordsPath);
|
||||
var tLoad1 = System.nanoTime();
|
||||
|
||||
section("Load");
|
||||
@@ -365,7 +375,7 @@ public class Main {
|
||||
var filled = fillMask(rng, slotInfo, grid);
|
||||
|
||||
if (!multiThreaded) {
|
||||
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
||||
System.out.print("\r" + " ".repeat(120 - "".length()) + "\r");
|
||||
System.out.flush();
|
||||
}
|
||||
// print a final progress line
|
||||
@@ -395,7 +405,7 @@ public class Main {
|
||||
//System.out.println(Arrays.stream(new Clued(mask).gridToString().split("\n")).map(s -> "\"" + s + "\\n\" +").collect(Collectors.joining("\n")));
|
||||
}
|
||||
if (filled.ok()) {
|
||||
return new PuzzleResult(new Clued(mask), new Gridded(grid, mask), slotInfo, filled);
|
||||
return new PuzzleResult(new Signa(mask), new Puzzle(grid, mask), slotInfo, filled);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package puzzle;
|
||||
|
||||
import module java.base;
|
||||
import anno.GenerateNeighbor;
|
||||
import anno.GenerateNeighbors;
|
||||
import anno.Shaped;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
@@ -13,29 +16,28 @@ import static puzzle.SwedishGenerator.*;
|
||||
|
||||
public final class Masker {
|
||||
|
||||
public static final rci[] IT = Neighbors9x8.IT;
|
||||
public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
|
||||
public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
|
||||
public static final long MASK_LO = -1L;
|
||||
public static final long MASK_HI = Neighbors9x8.MASK_HI;//(1L << (SIZE - 64)) - 1;
|
||||
public static final int MIN_LEN = Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
|
||||
public static final int STACK_SIZE = 128;
|
||||
public static final int C = Neighbors9x8.C;
|
||||
public static final int R = Neighbors9x8.R;
|
||||
public static final double SIZED = Neighbors9x8.SIZED;// ~18
|
||||
private static final long[] NBR_LO = Neighbors9x8.NBR_LO;
|
||||
private static final long[] NBR_HI = Neighbors9x8.NBR_HI;
|
||||
private final Rng rng;
|
||||
private final int[] stack;
|
||||
private final Clues cache;
|
||||
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[Neighbors9x8.R];
|
||||
private final int[] cCount = new int[Neighbors9x8.C];
|
||||
|
||||
@Shaped public static final rci[] IT = Neighbors9x8.IT;
|
||||
@Shaped public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
|
||||
@Shaped public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
|
||||
@Shaped public static final long MASK_LO = -1L;
|
||||
@Shaped public static final long MASK_HI = Neighbors9x8.MASK_HI;//(1L << (SIZE - 64)) - 1;
|
||||
@Shaped public static final int MIN_LEN = Neighbors9x8.MIN_LEN;//Config.MIN_LEN;
|
||||
@Shaped public static final int C = Neighbors9x8.C;
|
||||
@Shaped public static final int R = Neighbors9x8.R;
|
||||
@Shaped public static final double SIZED = Neighbors9x8.SIZED;// ~18
|
||||
@Shaped private static final long[] NBR_LO = Neighbors9x8.NBR_LO;
|
||||
@Shaped private static final long[] NBR_HI = Neighbors9x8.NBR_HI;
|
||||
@Shaped private final int[] activeCIdx = new int[Neighbors9x8.SIZE];
|
||||
@Shaped private final long[] activeSLo = new long[Neighbors9x8.SIZE];
|
||||
@Shaped private final long[] activeSHi = new long[Neighbors9x8.SIZE];
|
||||
@Shaped private final long[] adjLo = new long[Neighbors9x8.SIZE];
|
||||
@Shaped private final long[] adjHi = new long[Neighbors9x8.SIZE];
|
||||
@Shaped private final int[] rCount = new int[Neighbors9x8.R];
|
||||
@Shaped private final int[] cCount = new int[Neighbors9x8.C];
|
||||
private final Rng rng;
|
||||
private final int[] stack;
|
||||
private final Clues cache;
|
||||
public static final int STACK_SIZE = 128;
|
||||
public Masker(Rng rng, int[] stack, Clues cache) {
|
||||
this.rng = rng;
|
||||
this.stack = stack;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package puzzle;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import puzzle.SwedishGenerator.Lemma;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -11,8 +12,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
@UtilityClass
|
||||
public class Meta {
|
||||
|
||||
static final Gson GSON = new Gson();
|
||||
@@ -27,14 +27,14 @@ public class Meta {
|
||||
static final Path shardData = dir.resolve("shard0.data");
|
||||
static final Path shardMap = dir.resolve("shard0.map");
|
||||
|
||||
private static Path detectShardDir() {
|
||||
private Path detectShardDir() {
|
||||
// 1) optioneel override
|
||||
String p = System.getProperty("puzzle.shards.dir");
|
||||
if (p != null && !p.isBlank()) return Path.of(p).toAbsolutePath().normalize();
|
||||
|
||||
// 2) default: naast classes output (CLASS_OUTPUT/shards)
|
||||
try {
|
||||
var url = Meta.class.getProtectionDomain().getCodeSource().getLocation(); // classes dir (niet jar)
|
||||
var url = Meta.class.getProtectionDomain().getCodeSource().getLocation(); // classes dir (niet jar)
|
||||
Path classes = Path.of(url.toURI());
|
||||
return classes.resolve("shards");
|
||||
} catch (Exception e) {
|
||||
@@ -43,7 +43,7 @@ public class Meta {
|
||||
}
|
||||
}
|
||||
// --- Lookup: w -> i using mmap ---
|
||||
static int findIndexInMapMmap(long target) throws IOException {
|
||||
private int findIndexInMapMmap(long target) throws IOException {
|
||||
try (var ch = FileChannel.open(Meta.shardMap, StandardOpenOption.READ)) {
|
||||
var mbb = (MappedByteBuffer) ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size()).order(ORDER);
|
||||
|
||||
@@ -67,7 +67,7 @@ public class Meta {
|
||||
}
|
||||
|
||||
// --- Read record i from shard.data (your format) ---
|
||||
static ShardLem readRecord(long w, int i) throws IOException {
|
||||
private ShardLem readRecord(long w, int i) throws IOException {
|
||||
try (var ch = FileChannel.open(Meta.shardData, StandardOpenOption.READ)) {
|
||||
var hdr = ByteBuffer.allocate(12).order(ORDER);
|
||||
ch.read(hdr);
|
||||
@@ -97,11 +97,11 @@ public class Meta {
|
||||
|
||||
var simpel = Integer.parseInt(parts[1]);
|
||||
var clues = GSON.fromJson(parts[2], String[].class);
|
||||
return new ShardLem(w, simpel, clues);
|
||||
return new ShardLem(w, simpel, i, clues);
|
||||
}
|
||||
}
|
||||
|
||||
static int readIntAt(FileChannel ch, long pos) throws IOException {
|
||||
private int readIntAt(FileChannel ch, long pos) throws IOException {
|
||||
var b = ByteBuffer.allocate(4).order(ORDER);
|
||||
ch.position(pos);
|
||||
ch.read(b);
|
||||
@@ -109,23 +109,18 @@ public class Meta {
|
||||
return b.getInt();
|
||||
}
|
||||
|
||||
// --- Demo main ---
|
||||
public static ShardLem lookup(long w) {
|
||||
public record ShardLem(long w, int simpel, int mmap, String[] clues) { }
|
||||
|
||||
public ShardLem lookupSilent(long w) {
|
||||
try {
|
||||
var i = findIndexInMapMmap(Lemma.pack43(w));
|
||||
System.out.println("\nQuery: w=" + w + " -> i=" + i);
|
||||
var i = findIndexInMapMmap(Lemma.packLetterAndLengthBits(w));
|
||||
if (i >= 0) {
|
||||
var rec = readRecord(w, i);
|
||||
System.out.println(" simpel=" + rec.simpel());
|
||||
System.out.println(" clues=" + Arrays.toString(rec.clues()));
|
||||
return rec;
|
||||
return readRecord(w, i);
|
||||
} else {
|
||||
System.out.println(" NOT FOUND");
|
||||
throw new RuntimeException("NOT FOUND");
|
||||
throw new RuntimeException("NOT FOUND{w=" + w + ", text=" + Lemma.asWord(w, new byte[8]) + "}");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public record ShardLem(long w, int simpel, String[] clues) { }
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package puzzle;
|
||||
|
||||
import gen.GenDict;
|
||||
import gen.GenerateConst;
|
||||
import gen.GenerateNeighbors;
|
||||
import anno.ConstGen;
|
||||
import anno.GenerateNeighbor;
|
||||
import anno.GenerateNeighbors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.val;
|
||||
import precomp.Neighbors9x8;
|
||||
import static java.lang.Long.*;
|
||||
import static java.lang.Long.bitCount;
|
||||
import static java.lang.Long.numberOfLeadingZeros;
|
||||
import static java.lang.Long.numberOfTrailingZeros;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
@@ -27,24 +28,21 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
* javac SwedishGenerator.java
|
||||
* java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt]
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
@GenerateNeighbors(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2)
|
||||
@GenerateConst(C = 9, R = 8, packageName = "precomp", className = "Const9x8")
|
||||
@GenDict(
|
||||
packageName = "puzzle.dict950",
|
||||
className = "DictData950",
|
||||
scv = "/home/mike/dev/puzzle-generator/nl_score_hints_v4.csv",
|
||||
simpleMax = 950,
|
||||
minLen = 2,
|
||||
maxLen = 8
|
||||
)
|
||||
@ConstGen(C = 9, R = 8, packageName = "precomp", className = "Const9x8")
|
||||
@GenerateNeighbors({
|
||||
@GenerateNeighbor(C = 9, R = 8, packageName = "precomp", className = "Neighbors9x8", MIN_LEN = 2),
|
||||
@GenerateNeighbor(C = 4, R = 3, packageName = "precomp", className = "Neighbors4x3", MIN_LEN = 2)
|
||||
})
|
||||
/*@GenerateShapedCopies(
|
||||
shapes = { "precomp.Neighbors9x8", "precomp.Neighbors4x3" }
|
||||
)*/
|
||||
public record SwedishGenerator() {
|
||||
|
||||
public static final long X = 0L;
|
||||
public static final int SIZE = Neighbors9x8.SIZE;
|
||||
public static final int MAX_TRIES_PER_SLOT = 500;// MAX_TRIES_PER_SLOT;
|
||||
public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;
|
||||
public static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624;
|
||||
public static final long X = 0L;
|
||||
public static final int SIZE = Neighbors9x8.SIZE;
|
||||
public static final int MAX_TRIES_PER_SLOT = 500;// MAX_TRIES_PER_SLOT;
|
||||
public static final long RANGE_0_SIZE = Neighbors9x8.RANGE_0_SIZE;
|
||||
public static final long RANGE_0_624 = Neighbors9x8.RANGE_0_624;
|
||||
|
||||
public static boolean isLo(int n) { return (n & 64) == 0; }
|
||||
interface Bit1029 {
|
||||
@@ -57,12 +55,12 @@ public record SwedishGenerator() {
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
public static record Dict(DictEntry[] index, int length) { }
|
||||
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
||||
public record Dict(DictEntry[] index, int length) { }
|
||||
public record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
||||
@AllArgsConstructor @NoArgsConstructor static final class Assign { long w; }
|
||||
public static final class FillStats { public double simplicity; }
|
||||
@AllArgsConstructor public static final class Grid { public final byte[] g; public long lo, hi; }
|
||||
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { }
|
||||
public record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { }
|
||||
//@formatter:on
|
||||
|
||||
public static final class Rng {
|
||||
@@ -91,10 +89,10 @@ public record SwedishGenerator() {
|
||||
public int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); }
|
||||
}
|
||||
|
||||
public static interface Lemma {
|
||||
public interface Lemma {
|
||||
|
||||
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
|
||||
static final long INDEX_MASK = (1L << 43) - 1; // 24 bits
|
||||
long LETTER_MASK = (1L << 40) - 1; // low 40 bits
|
||||
long LETTER_AND_LENGTH_MASK = (1L << 43) - 1;
|
||||
|
||||
static long from(byte[] word) { return packShiftIn(word) | ((long) (word.length - 1) << 40); }
|
||||
static long pack(long w, int shardIndex) { return w | (((long) shardIndex) << 43) | ((long) length0(w)) << 40; }
|
||||
@@ -103,23 +101,21 @@ public record SwedishGenerator() {
|
||||
for (int i = b.length - 1; i >= 0; i--) w = (w << 5) | ((long) b[i] & 31);
|
||||
return w;
|
||||
}
|
||||
static public long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); }
|
||||
static long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); }
|
||||
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111L); }
|
||||
static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); }
|
||||
public static String asWord(long word, byte[] bytes) {
|
||||
static String asWord(long word, byte[] bytes) {
|
||||
int bi = 0;
|
||||
for (long w = word & LETTER_MASK; w != 0; w >>>= 5) bytes[bi++] = (byte) ((w & 31) | 64);
|
||||
return new String(bytes, 0, bi, US_ASCII);
|
||||
}
|
||||
static int unpackIndex(long w) { return (int) (w >>> 40); }
|
||||
static int unpackSize(long w) { return (int) (w >>> 40) & 7; }
|
||||
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
|
||||
static long pack43(long w) {
|
||||
return w & INDEX_MASK;
|
||||
}
|
||||
static int unpackIndex(long w) { return (int) (w >>> 40); }
|
||||
static int unpackSize(long w) { return (int) (w >>> 40) & 7; }
|
||||
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
|
||||
static long packLetterAndLengthBits(long w) { return w & LETTER_AND_LENGTH_MASK; }
|
||||
}
|
||||
|
||||
public static record Slotinfo(int key, long lo, long hi, int score, Assign assign, DictEntry entry, int minL) {
|
||||
public record Slotinfo(int key, long lo, long hi, int score, Assign assign, DictEntry entry, int minL) {
|
||||
|
||||
public static int wordCount(int k, Slotinfo[] arr) {
|
||||
for (var n = 1; n < arr.length; n++) if (arr[n].assign.w != X) k++;
|
||||
@@ -289,12 +285,11 @@ public record SwedishGenerator() {
|
||||
val words = s.entry.words;
|
||||
long low, top;
|
||||
if (info != null && info.length > 0) {
|
||||
var idxs = info;
|
||||
var L = idxs.length;
|
||||
var L = info.length;
|
||||
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
||||
|
||||
for (var t = 0; t < tries; t++) {
|
||||
var w = words[idxs[rng.biasedIndexPow3(L - 1)]];
|
||||
var w = words[info[rng.biasedIndexPow3(L - 1)]];
|
||||
var lemIdx = Lemma.unpackIndex(w);
|
||||
if (Bit1029.get(used, lemIdx)) continue;
|
||||
low = glo;
|
||||
|
||||
Reference in New Issue
Block a user