diff --git a/src/main/java/puzzle/CsvIndexService.java b/src/main/java/puzzle/CsvIndexService.java index 2e99844..854f673 100644 --- a/src/main/java/puzzle/CsvIndexService.java +++ b/src/main/java/puzzle/CsvIndexService.java @@ -100,7 +100,7 @@ public final class CsvIndexService throw new RuntimeException("Index mismatch after rebuild. Requested=" + lineIndex + ", got line=" + preview(line)); } - private void ensureLoaded() throws IOException { + public void ensureLoaded() throws IOException { if (offsets != null && csvChannel != null && csvChannel.isOpen()) return; synchronized (lock) { diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index a0a2d79..548b875 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -131,9 +131,6 @@ public record Export() { }).limit(Long.bitCount(lo))); } } - char NOT_CLUE_NOT_LETTER_TO(byte b) { - return (char) (64 | b); - } String gridToString(Clues clues) { var sb = new StringBuilder(INIT); clues.forEachSlot((s, l, a) -> { @@ -174,34 +171,17 @@ public record Export() { sb.setCharAt(r * (C + 1) + c, (char) (letter | 64)); }); return sb.toString().replaceAll(" ", "" + emptyFallback).split("\n"); - /* - var out = new String[R]; - for (var r = 0; r < R; r++) { - var sb = new StringBuilder(C); - for (var c = 0; c < C; c++) { - var offset = Grid.offset(r, c); - if (clues.isClue(offset)) { - sb.append(clueChar.replace(new Cell(grid, clues, offset, clues.digitAt(offset)))); - } else if (lisLetterAt(offset)) { - sb.append(NOT_CLUE_NOT_LETTER_TO(grid.letter32At(offset))); - } else { - sb.append(emptyFallback); - } - } - out[r] = sb.toString(); - } - return out;*/ } } - record Bit1029(long[] bits) { + interface Bit1029 { - public Bit1029() { this(new long[2048]); } - static int wordIndex(int bitIndex) { return bitIndex >> 6; } - public boolean get(int bitIndex) { return (this.bits[wordIndex(bitIndex)] & 1L << bitIndex) != 0L; } - public void set(int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; } - public void clear(int bitIndex) { this.bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); } - public void clear() { Arrays.fill(bits, 0L); } + public static long[] bit1029() { return new long[2048]; } + static int wordIndex(int bitIndex) { return bitIndex >> 6; } + static public boolean get(long[] bits, int bitIndex) { return (bits[wordIndex(bitIndex)] & 1L << bitIndex) != 0L; } + static public void set(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; } + static public void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); } + static public void clear(long[] bits) { Arrays.fill(bits, 0L); } } record Placed(long lemma, int slotKey, int[] cells) { diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index f166eb9..094bb13 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -3,6 +3,7 @@ package puzzle; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.val; import puzzle.SwedishGenerator.Rng; import java.io.IOException; @@ -60,7 +61,14 @@ public class Main { public void main(String[] args) { var csv = Paths.get("nl_score_hints_v3.csv"); var idx = Paths.get("nl_score_hints_v3.idx"); - ScopedValue.where(SC, new CsvIndexService(csv, idx)).run(() -> _main(args)); + try { + val scv = new CsvIndexService(csv, idx); + scv.ensureLoaded(); + ScopedValue.where(SC, scv).run(() -> _main(args)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } public void _main(String[] args) { var opts = parseArgs(args); diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 0d93b45..7bb017f 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -13,6 +13,7 @@ import puzzle.Export.DictEntryDTO; import puzzle.Export.Gridded; import puzzle.Export.Strings; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -20,7 +21,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Locale; import java.util.stream.IntStream; -import static java.nio.charset.StandardCharsets.*; +import static java.nio.charset.StandardCharsets.US_ASCII; /** * NOTE: @@ -66,8 +67,10 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { static final int STACK_SIZE = 64; static final char C_DASH = '\0'; static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH; + static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L; + static final long RANGE_0_624 = 624L - 0L + 1L; //72 << 3; - static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1; + static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1; static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); } @AllArgsConstructor static class Pick { @@ -140,11 +143,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } public int wordCount() { int k = 0; - for (var n = 1; n < clueMap.length; n++) { - if (clueMap[n] != X) { - k++; - } - } + for (var n = 1; n < clueMap.length; n++) if (clueMap[n] != X) k++; return k; } } @@ -165,38 +164,26 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { x = y; return y; } - byte randbyte(int min, int max) { - var u = (nextU32() & 0xFFFFFFFFL); - var range = (long) max - (long) min + 1L; - return (byte) (min + (u % range)); - } - int randint2bit() { return nextU32() & 3; } - byte randint2bitByte() { return (byte) (nextU32() & 3); } - int randint(int min, int max) { - var u = (nextU32() & 0xFFFFFFFFL); - var range = (long) max - (long) min + 1L; - return (int) (min + (u % range)); - } - double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; } - int biasedIndexPow3(int N) { - int m = Math.min(nextU32(), Math.min(nextU32(), nextU32())); - return (int) (((m & 0xFFFFFFFFL) * (long) N) >>> 32); - } + int randint2bit() { return nextU32() & 3; } + byte randint2bitByte() { return (byte) (nextU32() & 3); } + int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max - 0L + 1L))); } + int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); } + int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); } + double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; } + int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); } } @AllArgsConstructor static class Clues { long lo, hi, vlo, vhi, rlo, rhi; - public static Clues createEmpty() { - return new Clues(0, 0, 0, 0, 0, 0); - } - public Clues deepCopyGrid() { return new Clues(lo, hi, vlo, vhi, rlo, rhi); } + public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); } + public Clues deepCopyGrid() { return new Clues(lo, hi, vlo, vhi, rlo, rhi); } boolean clueless(int idx) { if (!isClue(idx)) return false; if ((idx & 64) == 0) { clearClueLo(idx); - }else{ + } else { clearClueHi(idx); } return true; @@ -260,7 +247,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { return (Long.bitCount(matchLo & MASK_LO) + Long.bitCount(matchHi & MASK_HI)) / SIZED; } public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); } - public void forEachSlot(SlotVisitor visitor) { + public void forEachSlot(SwedishGenerator.SlotVisitor visitor) { for (var l = lo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 1)); for (var l = lo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 0)); for (var l = lo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(Long.numberOfTrailingZeros(l), 2)); @@ -308,10 +295,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); } static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); } static int length(long word) { return ((63 - Long.numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; } + static ThreadLocal BYTES = ThreadLocal.withInitial(() -> new byte[SwedishGenerator.MAX_WORD_LENGTH]); 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); + val len = Lemma.length(word); + var b = BYTES.get();//new byte[Lemma.length(word)]; + for (int i = 0, bi = 0; i < len * 5; bi++, i += 5) b[bi] = (byte) (((word >>> i) & 31) | 64); + return new String(b, 0, 0, len); } static int unpackIndex(long w) { return (int) (w >>> 40); } static int unpackLetters(long w) { return (int) (w & LETTER_MASK); } @@ -330,12 +319,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var entry = index[L]; var idx = entry.words().size(); entry.words().add(lemma); - - for (var i = 0; i < L; i++) { - var letter = Lemma.byteAt(lemma, i) - 1; - if (letter < 0 || letter >= 26) throw new RuntimeException("Illegal letter: " + letter + " in word " + lemma); - entry.pos()[i][letter].add(idx); - } + for (var i = 0; i < L; i++) entry.pos()[i][Lemma.byteAt(lemma, i) - 1].add(idx); } 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 -> { @@ -360,7 +344,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { static Dict loadDict(String wordsPath) { try { var map = new LongArrayList(100_000); - Files.lines(Path.of(wordsPath), UTF_8).forEach(line -> CsvIndexService.lineToLemma(line, map::add)); + Files.lines(Path.of(wordsPath), StandardCharsets.UTF_8).forEach(line -> CsvIndexService.lineToLemma(line, map::add)); return new Dict(map.toArray()); } catch (IOException e) { e.printStackTrace(); @@ -601,7 +585,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { Clues randomMask(final int clueSize) { var g = Clues.createEmpty(); for (int placed = 0, guard = 0, idx; placed < clueSize && guard < 4000; guard++) { - idx = rng.randint(0, SIZE_MIN_1); + + idx = rng.randint0_SIZE(); if (g.isClue(idx)) continue; var d_idx = rng.randint2bitByte(); if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, d_idx)])) { @@ -613,9 +598,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } Clues mutate(Clues c) { int ri; - var bytes = MUTATE_RI[rng.randint(0, SIZE_MIN_1)]; + var bytes = MUTATE_RI[rng.randint0_SIZE()]; for (var k = 0; k < 4; k++) { - ri = bytes[rng.randint(0, 624)]; + ri = bytes[rng.randint0_624()]; if (!c.clueless(ri)) { var d_idx = rng.randint2bitByte(); if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClue(ri, d_idx); @@ -697,8 +682,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (var k = 0; k < offspring; k++) { if (Thread.currentThread().isInterrupted()) break; - var p1 = pop.get(rng.randint(0, pop.size() - 1)); - var p2 = pop.get(rng.randint(0, pop.size() - 1)); + var p1 = pop.get(rng.randint(pop.size() - 1)); + var p2 = pop.get(rng.randint(pop.size() - 1)); var child = crossover(p1.grid, p2.grid); children.add(new GridAndFit(hillclimb(child, clueSize, 70))); } @@ -731,39 +716,34 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { return best.grid; } - static long patternForSlot(Grid grid, final int key, final long lo, final long hi) { - final long glo = grid.lo, ghi = grid.hi; - if ((lo & glo) == X && (hi & ghi) == X) { + static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) { + if (((lo & glo) | (hi & ghi)) == X) { return 0; } long p = 0; if (Slot.increasing(key)) { for (long b = lo & glo; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - byte val = grid.g[idx]; int i = Long.bitCount(lo & ((1L << idx) - 1)); - p |= ((long) (i * 26 + val)) << (i << 3); + p |= ((long) (i * 26 + g[idx])) << (i << 3); } int offset = Long.bitCount(lo); for (long b = hi & ghi; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - byte val = grid.g[64 | idx]; int i = offset + Long.bitCount(hi & ((1L << idx) - 1)); - p |= ((long) (i * 26 + val)) << (i << 3); + p |= ((long) (i * 26 + g[64 | idx])) << (i << 3); } } else { int offset = Long.bitCount(hi); for (long b = hi & ghi; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - byte val = grid.g[64 | idx]; int i = Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))); - p |= ((long) (i * 26 + val)) << (i << 3); + p |= ((long) (i * 26 + g[64 | idx])) << (i << 3); } for (long b = lo & glo; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - byte val = grid.g[idx]; int i = offset + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))); - p |= ((long) (i * 26 + val)) << (i << 3); + p |= ((long) (i * 26 + g[idx])) << (i << 3); } } return p; @@ -774,28 +754,28 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (long b = hi; b != X; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); return cross * 10 + Slot.length(lo, hi); } - static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) { + static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) { final long glo = grid.lo, ghi = grid.hi; if (Slot.increasing(key)) { for (long b = lo & glo; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[idx] != Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1)))) return false; + if (g[idx] != Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1)))) return false; } int bcLo = Long.bitCount(lo); for (long b = hi & ghi; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[64 | idx] != Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1)))) return false; + if (g[64 | idx] != Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1)))) return false; } long maskLo = lo & ~glo, maskHi = hi & ~ghi; if ((maskLo | maskHi) != X) { for (long b = maskLo; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[idx] = Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1))); + g[idx] = Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1))); } for (long b = maskHi; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[64 | idx] = Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1))); + g[64 | idx] = Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1))); } grid.lo |= maskLo; grid.hi |= maskHi; @@ -804,22 +784,22 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { int bcHi = Long.bitCount(hi); for (long b = hi & ghi; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[64 | idx] != Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; + if (g[64 | idx] != Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; } for (long b = lo & glo; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[idx] != Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; + if (g[idx] != Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; } long maskLo = lo & ~glo, maskHi = hi & ~ghi; if ((maskLo | maskHi) != X) { for (long b = maskHi; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[64 | idx] = Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); + g[64 | idx] = Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); } for (long b = maskLo; b != X; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[idx] = Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); + g[idx] = Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); } grid.lo |= maskLo; grid.hi |= maskHi; @@ -900,13 +880,14 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } } - public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask) { + public static SwedishGenerator.FillResult fillMask(Rng rng, Slot[] slots, Grid mask) { val multiThreaded = Thread.currentThread().getName().contains("pool"); val NO_LOG = (!Main.VERBOSE || multiThreaded); val grid = mask; - val used = new Bit1029(); + val used = new long[2048]; val assigned = new long[CLUE_INDEX_MAX_SIZE]; val bitset = new long[2500]; + val g = grid.g; val TOTAL = slots.length; val slotScores = new int[TOTAL]; @@ -917,7 +898,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { class Solver { - private final Pick CARRIER = new Pick(null, null, 0); + private final SwedishGenerator.Pick CARRIER = new Pick(null, null, 0); long nodes; long backtracks; int lastMRV; @@ -949,7 +930,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) { var s = slots[i]; if (assigned[s.key] != X) continue; - var pattern = patternForSlot(grid, s.key, s.lo, s.hi); + var pattern = patternForSlot(grid.lo, grid.hi, g, s.key, s.lo, s.hi); var index = s.entry; count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); @@ -971,7 +952,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { current = PICK_DONE; return; } - var pattern = patternForSlot(grid, best.key, best.lo, best.hi); + var pattern = patternForSlot(grid.lo, grid.hi, g, best.key, best.lo, best.hi); var index = best.entry; current = CARRIER; current.slot = best; @@ -1017,18 +998,18 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var idx = idxs[idxInArray]; var w = entry.words[idx]; var lemIdx = Lemma.unpackIndex(w); - if (used.get(lemIdx)) continue; + if (Bit1029.get(used, lemIdx)) continue; low = grid.lo; top = grid.hi; - if (!placeWord(grid, k, slo, shi, w)) continue; + if (!placeWord(grid, g, k, slo, shi, w)) continue; - used.set(lemIdx); + Bit1029.set(used, lemIdx); assigned[k] = w; if (backtrack(depth + 1)) return true; assigned[k] = X; - used.clear(lemIdx); + Bit1029.clear(used, lemIdx); grid.lo = low; grid.hi = top; } @@ -1044,18 +1025,18 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { int idxInArray = (int) (r * r * r * (N - 1)); var w = entry.words[idxInArray]; var lemIdx = Lemma.unpackIndex(w); - if (used.get(lemIdx)) continue; + if (Bit1029.get(used, lemIdx)) continue; low = grid.lo; top = grid.hi; - if (!placeWord(grid, k, slo, shi, w)) continue; + if (!placeWord(grid, g, k, slo, shi, w)) continue; - used.set(lemIdx); + Bit1029.set(used, lemIdx); assigned[k] = w; if (backtrack(depth + 1)) return true; assigned[k] = X; - used.clear(lemIdx); + Bit1029.clear(used, lemIdx); grid.lo = low; grid.hi = top; } @@ -1071,7 +1052,8 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var ok = solver.backtrack(0); // final progress line - var res = new FillResult(ok, new Gridded(grid), assigned, new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV)); + var res = new FillResult(ok, new Gridded(grid), assigned, + new SwedishGenerator.FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV)); if (!multiThreaded) { System.out.print("\r" + Strings.padRight("", 120) + "\r"); System.out.flush(); diff --git a/src/test/java/puzzle/ExportFormatTest.java b/src/test/java/puzzle/ExportFormatTest.java index 4ef2fc7..2123c27 100644 --- a/src/test/java/puzzle/ExportFormatTest.java +++ b/src/test/java/puzzle/ExportFormatTest.java @@ -52,7 +52,7 @@ public class ExportFormatTest { var key = Slot.packSlotKey(0, CLUE_RIGHT); var lo = (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3) | (1L << OFF_0_4); clueMap[key] = TEST; - assertTrue(placeWord(grid.grid(), key, lo, 0L, TEST)); + assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, 0L, TEST)); var fillResult = new FillResult(true, grid, clueMap, new FillStats(0, 0, 0, 0)); var puzzleResult = new PuzzleResult(new Clued(clues), fillResult); diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 9f53718..f9a86fb 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -24,8 +24,7 @@ import static puzzle.SwedishGeneratorTest.OFF_0_2; public class MainTest { - - static final Opts opts = new Main.Opts() {{ + static final Opts opts = new Main.Opts() {{ this.seed = 12348; this.clueSize = 4; this.pop = 4; // Tiny population @@ -36,7 +35,7 @@ public class MainTest { this.tries = 1; this.verbose = false; }}; - static final Dict dict = Dict.loadDict(opts.wordsPath); + static final Dict dict = Dict.loadDict(opts.wordsPath); @Test void testExtractSlots() { @@ -44,8 +43,8 @@ public class MainTest { val key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT); clues.setClue(OFF_0_0, CLUE_RIGHT); var grid = new Gridded(clues.toGrid()); - - placeWord(grid.grid(), key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB); + val g = grid.grid().g; + placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB); var slots = extractSlots(clues, dict.index()); assertEquals(1, slots.length); @@ -95,7 +94,7 @@ public class MainTest { var grid = new Gridded(clues.toGrid()); // Test set/get - placeWord(grid.grid(), key, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ); + placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ); val arr = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); Assertions.assertEquals(LETTER_A, arr.get(OFF_1_1)); Assertions.assertEquals(LETTER_Z, arr.get(OFF_0_1)); @@ -193,6 +192,8 @@ public class MainTest { System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().wordCount()); System.out.println("[DEBUG_LOG] Grid:"); System.out.println(res.filled().grid().renderHuman(res.clues().mask())); + System.out.println(res.filled().grid().gridToString(res.clues().mask())); + System.out.println(res.filled().grid().renderHuman(res.clues().mask())); break; } } diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index d721e13..60a5a3d 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -90,7 +90,7 @@ public class SwedishGeneratorTest { var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT); val clues = Clues.createEmpty(); clues.setClue(OFF_0_0, CLUE_RIGHT); - placeWord(grid.grid(), key, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC); + placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC); val map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); assertEquals(LETTER_A, map.get(OFF_0_1)); assertEquals(LETTER_B, map.get(OFF_0_2)); @@ -100,10 +100,10 @@ public class SwedishGeneratorTest { @Test void testPatternForSlotMixed() { var grid = createEmpty(); - placeWord(grid, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A")); - placeWord(grid, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_2_0, 0, Lemma.from(0, "C")); + placeWord(grid, grid.g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A")); + placeWord(grid, grid.g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_2_0, 0, Lemma.from(0, "C")); var key = Slot.packSlotKey(OFF_1_0, CLUE_RIGHT); - var pattern = patternForSlot(grid, key, 7L, 0L); + var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L); assertEquals(1L | (55L << 16), pattern); } @@ -111,16 +111,16 @@ public class SwedishGeneratorTest { void testPatternForSlotAllDashes() { var grid = createEmpty(); var key = Slot.packSlotKey(1 << Slot.BIT_FOR_DIR, CLUE_RIGHT); - var pattern = patternForSlot(grid, key, 7L, 0L); + var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L); assertEquals(0L, pattern); } @Test void testPatternForSlotSingleLetter() { var grid = createEmpty(); - placeWord(grid, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A")); + placeWord(grid, grid.g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A")); var key = Slot.packSlotKey(1, CLUE_RIGHT); - var pattern = patternForSlot(grid, key, 7L, 0L); + var pattern = patternForSlot(grid.lo, grid.hi, grid.g, key, 7L, 0L); assertEquals(1L, pattern); } @Test @@ -134,8 +134,8 @@ public class SwedishGeneratorTest { assertEquals(val1, rng2.nextU32()); for (var i = 0; i < 100; i++) { - var r = rng.randint(5, 10); - assertTrue(r >= 5 && r <= 10); + var r = rng.randint(5); + assertTrue(r >= 0 && r <= 5); var f = rng.nextFloat(); assertTrue(f >= 0.0 && f <= 1.0); } @@ -144,7 +144,7 @@ public class SwedishGeneratorTest { @Test void testGrid() { var grid = new Gridded(createEmpty()); - placeWord(grid.grid(), Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A")); + placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from(0, "A")); val arr = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); assertEquals(1, arr.size()); assertEquals(LETTER_A, arr.get(OFF_0_0)); @@ -290,7 +290,7 @@ public class SwedishGeneratorTest { var w1 = ABC; // 1. Successful placement in empty grid - assertTrue(placeWord(grid.grid(), key, lo, hi, w1)); + assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1)); var map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); assertEquals(3, map.size()); assertEquals(LETTER_A, map.get(OFF_0_0)); @@ -298,9 +298,9 @@ public class SwedishGeneratorTest { assertEquals(LETTER_C, map.get(OFF_0_2)); // 2. Successful placement with partial overlap (same characters) - assertTrue(placeWord(grid.grid(), key, lo, hi, w1)); + assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1)); // 3. Conflict: place "ABD" where "ABC" is - assertFalse(placeWord(grid.grid(), key, lo, hi, ABD)); + assertFalse(placeWord(grid.grid(), grid.grid().g, key, lo, hi, ABD)); // Verify grid is unchanged (still "ABC") map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); assertEquals(3, map.size()); @@ -310,8 +310,8 @@ public class SwedishGeneratorTest { // 4. Partial placement then conflict (rollback) grid = new Gridded(createEmpty()); - placeWord(grid.grid(), Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from(0, "X")); // Conflict at the end - assertFalse(placeWord(grid.grid(), key, lo, hi, w1)); + placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from(0, "X")); // Conflict at the end + assertFalse(placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1)); map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); assertEquals(1, map.size()); assertEquals(LETTER_X, map.get(OFF_0_2)); @@ -326,7 +326,7 @@ public class SwedishGeneratorTest { var w = AZ; val low = grid.grid().lo; val top = grid.grid().hi; - var placed = placeWord(grid.grid(), key, lo, 0L, w); + var placed = placeWord(grid.grid(), grid.grid().g, key, lo, 0L, w); assertTrue(placed); var map = grid.stream(Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter)); assertEquals(2, map.size());