diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index c88e858..62f55e9 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -212,7 +212,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(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) { - val meta = Meta.readRecord(Meta.shardKey(l), Lemma.unpackIndex(l)); + val meta = Meta.readRecord(Meta.shardKey(l), Lemma.unpackShardIndex(l)); this(Lemma.asWord(l), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, meta.simpel(), meta.clues()); } @@ -228,7 +228,7 @@ public record Export() { for (var n = 1; n < slots.length; n++) { if (slots[n].assign().w != X) { k++; - simpel += Meta.readRecord(Meta.shardKey(slots[n].assign().w), Lemma.unpackIndex(slots[n].assign().w)).simpel();//.simpel(Lemma.unpackIndex(slots[n].assign().w)); + simpel += Meta.readRecord(Meta.shardKey(slots[n].assign().w), Lemma.unpackShardIndex(slots[n].assign().w)).simpel(); } } simpel = k == 0 ? 0 : simpel / k; diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index 4d1c0bf..c71ea49 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -413,7 +413,7 @@ public class Main { if (mask == null) return null; val multiThreaded = Thread.currentThread().getName().contains("pool"); var slots = Masker.extractSlots(mask, dict.index()); - val slotInfo = Masker.scoreSlots(new int[slots.length], slots); + val slotInfo = Masker.scoreSlots(slots); var grid = mask.toGrid(); var filled = fillMask(rng, slotInfo, grid, multiThreaded); diff --git a/src/main/java/puzzle/Masker.java b/src/main/java/puzzle/Masker.java index f90841b..2a10589 100644 --- a/src/main/java/puzzle/Masker.java +++ b/src/main/java/puzzle/Masker.java @@ -75,7 +75,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) { grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)])); return slots; } - public static Slotinfo[] scoreSlots(int[] slotScores, Slot[] slots) { + public static Slotinfo[] scoreSlots(Slot[] slots) { val count = new byte[SwedishGenerator.SIZE]; Slotinfo[] slotInfo = new Slotinfo[slots.length]; for (var s : slots) { @@ -84,8 +84,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) { } for (int i = 0; i < slots.length; i++) { var slot = slots[i]; - slotScores[i] = slotScore(count, slot.lo, slot.hi); - slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScores[i], new Assign(), slot.entry); + slotInfo[i] = new Slotinfo(slot.key, slot.lo, slot.hi, slotScore(count, slot.lo, slot.hi), new Assign(), slot.entry); } return slotInfo; } @@ -115,16 +114,14 @@ public record Masker(Rng rng, int[] stack, Clues cache) { if (hLo != X) { rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1); rHi = 0; - } else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); } - } else { - if (hHi != X) { - int msb = 63 - numberOfLeadingZeros(hHi); - rHi &= -(1L << msb << 1); - rLo = 0; - } else if (hLo != X) { - int msb = 63 - numberOfLeadingZeros(hLo); - rLo &= -(1L << msb << 1); - } + } else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); + } else if (hHi != X) { + int msb = 63 - numberOfLeadingZeros(hHi); + rHi &= -(1L << msb << 1); + rLo = 0; + } else if (hLo != X) { + int msb = 63 - numberOfLeadingZeros(hLo); + rLo &= -(1L << msb << 1); } if ((rLo | rHi) != X) { hasSlots = true; @@ -136,9 +133,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) { cVHi |= rHi; } if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; - } else { - penalty += 25000; - } + } else penalty += 25000; } for (long bits = hi_cl; bits != X; bits &= bits - 1) { long lsb = bits & -bits; @@ -152,16 +147,14 @@ public record Masker(Rng rng, int[] stack, Clues cache) { if (hLo != X) { rLo &= ((1L << numberOfTrailingZeros(hLo)) - 1); rHi = 0; - } else if (hHi != X) { rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); } - } else { - if (hHi != X) { - int msb = 63 - numberOfLeadingZeros(hHi); - rHi &= -(1L << msb << 1); - rLo = 0; - } else if (hLo != X) { - int msb = 63 - numberOfLeadingZeros(hLo); - rLo &= -(1L << msb << 1); - } + } else if (hHi != X) rHi &= ((1L << numberOfTrailingZeros(hHi)) - 1); + } else if (hHi != X) { + int msb = 63 - numberOfLeadingZeros(hHi); + rHi &= -(1L << msb << 1); + rLo = 0; + } else if (hLo != X) { + int msb = 63 - numberOfLeadingZeros(hLo); + rLo &= -(1L << msb << 1); } if ((rLo | rHi) != X) { hasSlots = true; @@ -173,9 +166,7 @@ public record Masker(Rng rng, int[] stack, Clues cache) { cVHi |= rHi; } if ((bitCount(rLo) + bitCount(rHi)) < MIN_LEN) penalty += 8000; - } else { - penalty += 25000; - } + } else penalty += 25000; } if (!hasSlots) return 1_000_000_000L; @@ -297,11 +288,9 @@ public record Masker(Rng rng, int[] stack, Clues cache) { var d_idx = rng.randint2bitByte(); if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueLo(1L << ri, d_idx); } - } else { - if (!c.cluelessHi(ri)) { - var d_idx = rng.randint2bitByte(); - if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueHi(1L << (ri & 63), d_idx); - } + } else if (!c.cluelessHi(ri)) { + var d_idx = rng.randint2bitByte(); + if (c.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(ri, d_idx)])) c.setClueHi(1L << (ri & 63), d_idx); } } @@ -315,13 +304,12 @@ public record Masker(Rng rng, int[] stack, Clues cache) { var nr = Math.sin(theta); long maskLo = 0, maskHi = 0; - for (var rci : IT) { + for (var rci : IT) if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) { int i = rci.i(); if ((i & 64) == 0) maskLo |= (1L << i); else maskHi |= (1L << (i - 64)); } - } var c = new Clues( (a.lo & ~maskLo) | (other.lo & maskLo), (a.hi & ~maskHi) | (other.hi & maskHi), @@ -403,12 +391,11 @@ public record Masker(Rng rng, int[] stack, Clues cache) { for (var cand : pop) { if (next.size() >= offspring) break; var ok = true; - for (var kept : next) { + for (var kept : next) if (cand.grid.similarity(kept.grid) > 0.92) { ok = false; break; } - } if (ok) next.add(cand); } pop = next; @@ -428,12 +415,6 @@ public record Masker(Rng rng, int[] stack, Clues cache) { public static class Clues { long lo, hi, vlo, vhi, rlo, rhi; - public long lo() { return lo; } - public long hi() { return hi; } - public long vlo() { return vlo; } - public long vhi() { return vhi; } - public long rlo() { return rlo; } - public long rhi() { return rhi; } public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0); } public static Clues parse(String s) { diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index f1935ec..89d1c42 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -31,7 +31,7 @@ import static java.nio.charset.StandardCharsets.US_ASCII; * java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt] */ @SuppressWarnings("ALL") -public class SwedishGenerator { +public record SwedishGenerator() { public static final long GT_1_OFFSET_53_BIT = 0x3E00000000000000L; public static final long X = 0L; @@ -160,8 +160,9 @@ public class SwedishGenerator { 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); } + static int unpackIndex(long w) { return (int) (w >>> 40); } + static int unpackShardIndex(long w) { return (int) (w >>> 43); } + static int unpackLetters(long w) { return (int) (w & LETTER_MASK); } } public static record Dict(DictEntry[] index, int length) { } @@ -284,35 +285,36 @@ public class SwedishGenerator { System.out.print("\r" + padRight(msg, 120)); System.out.flush(); } - boolean placeWord(final int key, final long lo, final long hi, final long w) { + boolean placeWordDec(final long lo, final long hi, final long w) { int idx; - if (Slotinfo.increasing(key)) { - for (long b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false; - int bcLo = bitCount(lo); - for (long b = hi & ghi; b != X; b &= b - 1) - if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + 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) g[idx = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1))); - for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1))); - glo |= maskLo; - ghi |= maskHi; - } - } else { - int bcHi = bitCount(hi); - for (long b = hi & ghi; b != X; b &= b - 1) - if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; - for (long b = lo & glo; b != X; b &= b - 1) - if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + 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) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); - for (long b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); - glo |= maskLo; - ghi |= maskHi; - } + int bcHi = bitCount(hi); + for (long b = hi & ghi; b != X; b &= b - 1) + if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; + for (long b = lo & glo; b != X; b &= b - 1) + if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + 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) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); + for (long b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); + glo |= maskLo; + ghi |= maskHi; + } + return true; + } + boolean placeWordInc(final long lo, final long hi, final long w) { + int idx; + for (long b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false; + int bcLo = bitCount(lo); + for (long b = hi & ghi; b != X; b &= b - 1) + if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + 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) g[idx = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1))); + for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1))); + glo |= maskLo; + ghi |= maskHi; } return true; } @@ -345,21 +347,15 @@ public class SwedishGenerator { } var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi); var index = best.entry; - current = CARRIER; - current.slot = best; - current.count = index.length; - if (pattern == X) { - current.indices = null; - return; - } - current.indices = candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong); + current = CARRIER; + current.slot = best; + current.count = index.length; + current.indices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong); } boolean backtrack(int depth) { - if (Thread.currentThread().isInterrupted()) return false; + if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false; nodes++; - if (20_000 > 0 && (System.currentTimeMillis() - t0) > 20_000) return false; - chooseMRV(); var pick = current; if (pick == PICK_DONE) return true; @@ -372,7 +368,7 @@ public class SwedishGenerator { if (!NO_LOG) renderProgress(); val s = pick.slot; - val k = s.key; + val inc = Slotinfo.increasing(s.key); val slo = s.lo; val shi = s.hi; val entry = s.entry; @@ -392,10 +388,14 @@ public class SwedishGenerator { if (Bit1029.get(used, lemIdx)) continue; low = glo; top = ghi; - if (!placeWord(k, slo, shi, w)) continue; + if (inc) { + if (!placeWordInc(slo, shi, w)) continue; + } else { + if (!placeWordDec(slo, shi, w)) continue; + } Bit1029.set(used, lemIdx); - s.assign.w = w; + s.assign.w = w; if (backtrack(depth + 1)) return true; s.assign.w = X; Bit1029.clear(used, lemIdx); @@ -417,10 +417,14 @@ public class SwedishGenerator { if (Bit1029.get(used, lemIdx)) continue; low = glo; top = ghi; - if (!placeWord(k, slo, shi, w)) continue; + if (inc) { + if (!placeWordInc(slo, shi, w)) continue; + } else { + if (!placeWordDec(slo, shi, w)) continue; + } Bit1029.set(used, lemIdx); - s.assign.w = w; + s.assign.w = w; if (backtrack(depth + 1)) return true; s.assign.w = X; Bit1029.clear(used, lemIdx); diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index e9aace3..0a08ac3 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -198,7 +198,7 @@ public class MainTest { "222 3"); Assertions.assertEquals(20, mask.clueCount()); var slots = Masker.extractSlots(mask, dict.index()); - val slotInfo = Masker.scoreSlots(new int[slots.length], slots); + val slotInfo = Masker.scoreSlots(slots); var grid = mask.toGrid(); // var filled = fillMask(rng, slotInfo, grid, false); // val res = new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled).exportFormatFromFilled(0, new Rewards(0, 0, 0)); @@ -216,7 +216,7 @@ public class MainTest { " 1 2\n" + "21 22 3"); var slots = Masker.extractSlots(mask, dict.index()); - val slotInfo = Masker.scoreSlots(new int[slots.length], slots); + val slotInfo = Masker.scoreSlots(slots); var grid = mask.toGrid(); var filled = fillMask(rng, slotInfo, grid, false); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");