diff --git a/src/main/java/puzzle/DictMark.java b/src/main/java/puzzle/DictMark.java new file mode 100644 index 0000000..85ca257 --- /dev/null +++ b/src/main/java/puzzle/DictMark.java @@ -0,0 +1,13 @@ +package puzzle; + +import gen.GenDict; + +@GenDict( + packageName = "puzzle.dict950", + className = "DictData950", + scv = "/home/mike/dev/puzzle-generator/nl_score_hints_v4.csv", + simpleMax = 950, + minLen = 2, + maxLen = 8 +) +public class DictMark { } \ No newline at end of file diff --git a/src/main/java/puzzle/DictMarker.java b/src/main/java/puzzle/DictMarker.java index 88cc15e..2fa80a8 100644 --- a/src/main/java/puzzle/DictMarker.java +++ b/src/main/java/puzzle/DictMarker.java @@ -4,6 +4,8 @@ import gen.GenerateDict; @GenerateDict( packageName = "puzzle", + packageNameDict = "puzzle", + classNameDict = "DictData", className = "LemmaData", scv = "nl_score_hints_v4.csv", words = { @@ -11,6 +13,7 @@ import gen.GenerateDict; "APPLE", "AXE", "ABC", "ABD", "AZ", "AB", "AT", "CAT", "DOGS", "APPLY", "BANAN", "BANANA", "BANANAS", "BANANASS" }, + simpleMax=900, minLen = 2, maxLen = 8 ) diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 6c0e565..52088c1 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -8,16 +8,17 @@ import precomp.Const9x8.Cell; import puzzle.Export.Gridded.Replacar.Rell; import puzzle.Export.LetterVisit.LetterAt; import puzzle.Masker.Clues; +import puzzle.SwedishGenerator.Dict; import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.Grid; import puzzle.SwedishGenerator.Slotinfo; +import static precomp.Const9x8.INIT_GRID_OUTPUT; import static puzzle.Export.Clue.DOWN0; import static puzzle.Export.Clue.RIGHT1; import static puzzle.Masker.Clues.createEmpty; -import static puzzle.SwedishGenerator.R; -import static puzzle.SwedishGenerator.Lemma; import static puzzle.Masker.Slot; import static puzzle.SwedishGenerator.C; +import static puzzle.SwedishGenerator.Lemma; import static puzzle.SwedishGenerator.X; /** @@ -64,8 +65,6 @@ public record Export() { static String padRight(String s, int n) { return s.length() >= n ? s : s + " ".repeat(n - s.length()); } } - static final String INIT = IntStream.range(0, Config.PUZZLE_ROWS).mapToObj(l_ -> " ").collect(Collectors.joining("\n")); - public record ClueAt(int index, int clue) { } public record Clued(@Delegate Clues c) { @@ -76,25 +75,8 @@ public record Export() { 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) { - var c = createEmpty(); - var lines = s.split("\n"); - for (int r = 0; r < Math.min(lines.length, R); r++) { - var line = lines[r]; - for (int col = 0; col < Math.min(line.length(), C); col++) { - char ch = line.charAt(col); - if (ch >= '0' && ch <= '4') { - int idx = Masker.offset(r, col); - byte dir = (byte) (ch - '0'); - if ((idx & 64) == 0) c.setClueLo(1L << idx, dir); - else c.setClueHi(1L << (idx & 63), dir); - } - } - } - return new Clued(c); - } String gridToString() { - var sb = new StringBuilder(INIT); + var sb = new StringBuilder(INIT_GRID_OUTPUT); forEachSlot((s, _, _) -> { val idx = Slot.clueIndex(s); val dir = Slot.dir(s); @@ -121,11 +103,13 @@ public record Export() { return stream.build(); } public Slotinfo[] slots() { - return Masker.slots(c, DictData.DICT.index()); + return slots(DictData.DICT); + } + public Slotinfo[] slots(Dict D) { + return Masker.slots(c, D.index()); } } - @FunctionalInterface interface LetterVisit { record LetterAt(int index, byte letter) { @@ -137,24 +121,22 @@ public record Export() { public int index(int cols) { return (row() * cols) + col(); } } - void visit(int index, byte letter); - default void visit(int index, byte[] letters) { visit(index, letters[index]); } } record Gridded(@Delegate Grid grid, Clues cl) implements Stream { public Gridded(Clues clues) { this(clues.toGrid(), clues); } - public Gridded(Clued clues) { this(clues.toGrid(), clues.c); } + public Gridded(Clued clues) { this(clues.c); } public @Delegate Stream stream() { val stream = Stream.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)); return stream.build(); } - String gridToString(Clues clues) { - var sb = new StringBuilder(INIT); - clues.forEachSlot((s, _, _) -> { + String gridToString() { + var sb = new StringBuilder(INIT_GRID_OUTPUT); + cl.forEachSlot((s, _, _) -> { val idx = Slot.clueIndex(s); val r = idx & 7; val c = idx >>> 3; @@ -164,14 +146,14 @@ public record Export() { stream().forEach((l) -> sb.setCharAt(l.index(C + 1), l.human())); return sb.toString(); } - public String[] exportGrid(Clues clues, Replacar clueChar, char emptyFallback) { - var sb = new StringBuilder(INIT); - clues.forEachSlot((s, l, a) -> { + public String[] exportGrid(Replacar clueChar, char emptyFallback) { + var sb = new StringBuilder(INIT_GRID_OUTPUT); + cl.forEachSlot((s, l, a) -> { val idx = Slot.clueIndex(s); val r = idx & 7; val c = idx >>> 3; val dir = Slot.dir(s); - sb.setCharAt(r * (C + 1) + c, clueChar.replace(new Rell(grid, clues, idx, (byte) (dir | 48)))); + sb.setCharAt(r * (C + 1) + c, clueChar.replace(new Rell(grid, cl, idx, (byte) (dir | 48)))); }); stream().forEach((l) -> sb.setCharAt(l.index(C + 1), l.human())); return sb.toString().replaceAll(" ", "" + emptyFallback).split("\n"); @@ -224,7 +206,7 @@ public record Export() { } } - public String renderHuman(Clues clues) { return String.join("\n", exportGrid(clues, _ -> ' ', '#')); } + public String renderHuman() { return String.join("\n", exportGrid(_ -> ' ', '#')); } @FunctionalInterface interface Replacar { @@ -266,7 +248,7 @@ public record Export() { public ExportedPuzzle exportFormatFromFilled(Rewards rewards) { // If nothing placed: return full grid mapped to letters/# only if (slots.length == 0) { - return new ExportedPuzzle(grid.exportGrid(clues.c, _ -> '#', '#'), new WordOut[0], 1, rewards); + 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( diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index 7eb20d0..8231589 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -7,7 +7,6 @@ import lombok.NoArgsConstructor; import lombok.val; import puzzle.Masker.Clues; import puzzle.SwedishGenerator.Rng; -import puzzle.dict800.DictData; import static puzzle.Export.*; import static puzzle.SwedishGenerator.*; @@ -80,10 +79,10 @@ public class Main { System.out.print(indentLines(res.clues().gridToString(), " ")); section("Grid (raw)"); - System.out.print(indentLines(res.grid().gridToString(res.clues().c()), " ")); + System.out.print(indentLines(res.grid().gridToString(), " ")); section("Grid (human)"); - System.out.print(indentLines(res.grid().renderHuman(res.clues().c()), " ")); + System.out.print(indentLines(res.grid().renderHuman(), " ")); var exported = res.exportFormatFromFilled(new Rewards(50, 2, 1)); @@ -247,7 +246,7 @@ public class Main { PuzzleResult generatePuzzle(Opts opts) { var tLoad0 = System.nanoTime(); - var dict = DictData.DICT800;//loadDict(opts.wordsPath); + var dict = puzzle.dict800.DictData.DICT800;//loadDict(opts.wordsPath); var tLoad1 = System.nanoTime(); section("Load"); diff --git a/src/main/java/puzzle/Masker.java b/src/main/java/puzzle/Masker.java index d1df52f..5804d63 100644 --- a/src/main/java/puzzle/Masker.java +++ b/src/main/java/puzzle/Masker.java @@ -643,12 +643,10 @@ 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 @Accessors(fluent = true) - public static final class Clues - implements BitPop { + public static final class Clues { @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); } diff --git a/src/main/java/puzzle/Meta.java b/src/main/java/puzzle/Meta.java index 09a5aa0..964ad72 100644 --- a/src/main/java/puzzle/Meta.java +++ b/src/main/java/puzzle/Meta.java @@ -1,7 +1,6 @@ package puzzle; import com.google.gson.Gson; -import lombok.val; import puzzle.SwedishGenerator.Lemma; import java.io.IOException; import java.nio.ByteBuffer; @@ -30,14 +29,10 @@ public class Meta { static final Path dir = projectRoot.resolve("src/main/resources/shards"); static final Path shardData = dir.resolve("shard0.data"); static final Path shardMap = dir.resolve("shard0.map"); - static String normWord(String s) { - // belangrijk: zelfde normalisatie bij build en query - return s.toUpperCase(Locale.ROOT); - } // --- Lookup: w -> i using mmap --- - static int findIndexInMapMmap(Path mapFile, long target) throws IOException { - try (var ch = FileChannel.open(mapFile, StandardOpenOption.READ)) { + static 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); var magic = mbb.getInt(0); @@ -60,8 +55,8 @@ public class Meta { } // --- Read record i from shard.data (your format) --- - static ShardLem readRecord(Path shardFile, long w, int i) throws IOException { - try (var ch = FileChannel.open(shardFile, StandardOpenOption.READ)) { + static 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); hdr.flip(); @@ -105,10 +100,10 @@ public class Meta { // --- Demo main --- public static ShardLem lookup(long w) { try { - var i = findIndexInMapMmap(shardMap, Lemma.pack43(w)); + var i = findIndexInMapMmap(Lemma.pack43(w)); System.out.println("\nQuery: w=" + w + " -> i=" + i); if (i >= 0) { - var rec = readRecord(shardData, w, i); + var rec = readRecord(w, i); System.out.println(" simpel=" + rec.simpel()); System.out.println(" clues=" + Arrays.toString(rec.clues())); return rec; @@ -117,7 +112,6 @@ public class Meta { throw new RuntimeException("NOT FOUND"); } } catch (Exception e) { - e.printStackTrace(); throw new RuntimeException(e); } } diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index cf6d1db..0a53d47 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -30,14 +30,14 @@ public record SwedishGenerator() { public static final long X = 0L; public static final int LOG_EVERY_MS = 200; public static final int BAR_LEN = 22; - public static final int C = Config.PUZZLE_COLS; - public static final int R = Config.PUZZLE_ROWS; + public static final int C = Neighbors9x8.C; + public static final int R = Neighbors9x8.R; public static final int SIZE = Neighbors9x8.SIZE;// ~18 - public static final int SIZE_MIN_1 = SIZE - 1;// ~18 - public static final double SIZED = (double) SIZE;// ~18 + public static final int SIZE_MIN_1 = Neighbors9x8.SIZE_MIN_1;// ~18 + public static final double SIZED = Neighbors9x8.SIZED;// ~18 public static final long MASK_LO = -1L; 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 = Neighbors9x8.R; public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1; 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; @@ -57,12 +57,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) { } @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 Dict(DictEntry[] index, int length) { } public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { } - public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { } //@formatter:on public static final long[] PATH_LO = Neighbors9x8.PATH_LO; diff --git a/src/test/java/puzzle/BuildClueAndSimpelIndex.java b/src/test/java/puzzle/BuildClueAndSimpelIndex.java index 643dde4..69e69e3 100644 --- a/src/test/java/puzzle/BuildClueAndSimpelIndex.java +++ b/src/test/java/puzzle/BuildClueAndSimpelIndex.java @@ -23,14 +23,13 @@ public class BuildClueAndSimpelIndex { buildShard(records); - for (var qRaw : List.of("FIETS", "huis", "kiwi")) { - var q = Meta.normWord(qRaw); - var w = Lemma.from(q); - var i = Meta.findIndexInMapMmap(Meta.shardMap, w); + for (var qRaw : List.of("FIETS", "HUIS", "KIWI")) { + var w = Lemma.from(qRaw); + var i = Meta.findIndexInMapMmap(w); - System.out.println("\nQuery: " + qRaw + " (norm=" + q + ") w=" + w + " -> i=" + i); + System.out.println("\nQuery: " + qRaw + " (norm=" + qRaw + ") w=" + w + " -> i=" + i); if (i >= 0) { - var rec = Meta.readRecord(Meta.shardData, w, i); + var rec = Meta.readRecord(w, i); System.out.println(" simpel=" + rec.simpel()); System.out.println(" clues=" + Arrays.toString(rec.clues())); } else { diff --git a/src/test/java/puzzle/DictJavaGeneratorMulti.java b/src/test/java/puzzle/DictJavaGeneratorMulti.java index 9b4ed4f..2726091 100644 --- a/src/test/java/puzzle/DictJavaGeneratorMulti.java +++ b/src/test/java/puzzle/DictJavaGeneratorMulti.java @@ -110,11 +110,7 @@ public final class DictJavaGeneratorMulti { static final class ShardBuilder { int c; - int addRecord() { - val currSize = c; - c++; - return currSize; - } + int addRecord() { return c++; } } private static void writeAggregator(Path outDir, String pkg, String cls, int totalLen, int thress) throws IOException { @@ -262,9 +258,7 @@ public final class DictJavaGeneratorMulti { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); } - private static String toLongLiteral(long v) { - return "0x" + Long.toUnsignedString(v, 16) + "L"; - } + private static String toLongLiteral(long v) { return "0x" + Long.toUnsignedString(v, 16) + "L"; } public static final class CsvIndexService { static int SIMPEL_IDX = 2; diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 7820a24..ab5fa2c 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -13,6 +13,7 @@ import puzzle.Export.PuzzleResult; import puzzle.Export.Rewards; import puzzle.Main.Opts; import puzzle.SwedishGenerator.Rng; +import puzzle.dict950.DictData950; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -140,15 +141,17 @@ public class MainTest { } @Test void testFiller2() { - val mask = Clued.parse( - "1 000000\n" + - "1 \n" + - "1 \n" + - "3 3 \n" + - "3 0 3 \n" + - "3 \n" + - "3 \n" + - "222 3"); + var mask = Clued.of( + r0c0d1, + r0c3d0, r0c4d0, r0c5d0, r0c6d0, r0c7d0, r0c8d0, + r1c0d1, + r2c0d1, + r3c0d3, r3c3d3, + r4c0d3, r4c3d0, r4c6d3, + r5c0d3, + r6c0d3, + r7c0d2, r7c1d2, r7c2d2, r7c8d3 + ); Assertions.assertEquals(20, mask.clueCount()); val map = mask.stream().collect(Collectors.toMap(ClueAt::index, ClueAt::clue)); Assertions.assertEquals(20, map.size()); @@ -158,17 +161,19 @@ public class MainTest { } @Test void testFiller() { + System.out.println(DictData950.DICT950.index().length); val rng = new Rng(-343913721); - val mask = Clued.parse( - " 3 300\n" + - " 1 \n" + - " 1 \n" + - " 3 0 \n" + - " 31 \n" + - " 1 \n" + - " 1 2\n" + - "21 22 3"); - var slotInfo = mask.slots(); + var mask = Clued.of( + r0c3d3, r0c6d3, r0c7d0, r0c8d0, + r1c1d1, + r2c1d1, + r3c3d3, r3c6d0, + r4c2d3, r4c3d1, + r5c1d1, + r6c1d1, r6c8d2, + r7c0d2, r7c1d1, r7c4d2, r7c5d2, r7c8d3 + ); + var slotInfo = mask.slots(/*DictData950.DICT950*/); var grid = Slotinfo.grid(slotInfo); var filled = fillMask(rng, slotInfo, grid); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); @@ -177,7 +182,7 @@ public class MainTest { Assertions.assertEquals(-1L, grid.lo); Assertions.assertEquals(-1L, grid.hi); var g = new Gridded(grid, mask.c()); - g.gridToString(mask.c()); + g.gridToString(); var aa = new PuzzleResult(mask, g, slotInfo, filled).exportFormatFromFilled(new Rewards(1, 1, 1)); System.out.println(String.join("\n", aa.grid())); @@ -194,8 +199,8 @@ public class MainTest { System.out.println("[DEBUG_LOG] Seed found: " + seed); System.out.println("[DEBUG_LOG] ClueMap Size: " + Slotinfo.wordCount(0, res.slots())); System.out.println("[DEBUG_LOG] Grid:"); - System.out.println(res.grid().renderHuman(res.clues().c())); - System.out.println(res.grid().gridToString(res.clues().c())); + System.out.println(res.grid().renderHuman()); + System.out.println(res.grid().gridToString()); break; } } diff --git a/src/test/java/puzzle/PerformanceTest.java b/src/test/java/puzzle/PerformanceTest.java index 01aba0a..cb49866 100644 --- a/src/test/java/puzzle/PerformanceTest.java +++ b/src/test/java/puzzle/PerformanceTest.java @@ -12,6 +12,7 @@ import puzzle.SwedishGenerator.Rng; import puzzle.SwedishGenerator.Slotinfo; import static org.junit.jupiter.api.Assertions.assertTrue; +import static precomp.Const9x8.Cell.*; import static puzzle.SwedishGenerator.fillMask; import static puzzle.dict800.DictData.DICT800; import static puzzle.dict900.DictData.DICT900; @@ -74,15 +75,16 @@ public class PerformanceTest { void testIncrementalComplexity() { // Use the complex mask from Main.java - var maskStr = "1 0000\n" + - "1 \n" + - "00 01 \n" + - " 1 \n" + - " 1 \n" + - " 2 1 \n" + - " 1 \n" + - "221 22\n"; - val mask = Clued.parse(maskStr); + var mask = Clued.of( + r0c0d1, r0c5d0, r0c6d0, r0c7d0, r0c8d0, + r1c0d1, + r2c0d0, r2c1d0, r2c3d0, r2c4d1, + r3c4d1, + r4c4d1, + r5c2d2, r5c4d1, + r6c2d1, + r7c0d2, r7c1d2, r7c2d1, r7c7d2, r7c8d2 + ); val allSlots = Masker.slots(mask.c(), DICT900.index()); //mask.toGrid() System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---"); @@ -103,7 +105,7 @@ public class PerformanceTest { val rng = new Rng(42); // A single horizontal slot at (0,0) - val mask = Clued.parse("1 \n"); + val mask = Clued.of(r0c0d1); val slots = Masker.slots(mask.c(), EN); System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---");