From ed7cade1c75aea33a9192fad38fae40dbd209d84 Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 23 Jan 2026 03:37:54 +0100 Subject: [PATCH] redo --- src/main/java/puzzle/Clues.java | 82 +++++++++ src/main/java/puzzle/Export.java | 11 +- src/main/java/puzzle/Main.java | 4 +- src/main/java/puzzle/Masker.java | 163 +++++------------- src/test/java/puzzle/MainTest.java | 4 +- src/test/java/puzzle/MarkerTest.java | 23 ++- src/test/java/puzzle/PerformanceTest.java | 4 +- .../java/puzzle/SwedishGeneratorTest.java | 11 +- src/test/java/puzzle/TestDuplication.java | 3 +- 9 files changed, 150 insertions(+), 155 deletions(-) create mode 100644 src/main/java/puzzle/Clues.java diff --git a/src/main/java/puzzle/Clues.java b/src/main/java/puzzle/Clues.java new file mode 100644 index 0000000..3f3b527 --- /dev/null +++ b/src/main/java/puzzle/Clues.java @@ -0,0 +1,82 @@ +package puzzle; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.Accessors; +import static java.lang.Long.bitCount; +import static puzzle.SwedishGenerator.X; +//@formatter:on +@AllArgsConstructor +@Accessors(fluent = true) +public 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); } + + public Clues setClue(precomp.Const9x8.Cell cell) { + if ((cell.index & 64) == 0) setClueLo(cell.mask, cell.d); + else setClueHi(cell.mask, cell.d); + return this; + } + + public void setClueLo(long mask, byte idx) { + lo |= mask; + if ((idx & 1) != 0) vlo |= mask; + else vlo &= ~mask; + if ((idx & 2) != 0) rlo |= mask; + else rlo &= ~mask; + if ((idx & 4) != 0) xlo |= mask; + else xlo &= ~mask; + } + public void setClueHi(long mask, byte idx) { + hi |= mask; + if ((idx & 1) != 0) vhi |= mask; + else vhi &= ~mask; + if ((idx & 2) != 0) rhi |= mask; + else rhi &= ~mask; + if ((idx & 4) != 0) xhi |= mask; + else xhi &= ~mask; + } + public void clearClueLo(long mask) { + lo &= mask; + vlo &= mask; + rlo &= mask; + xlo &= mask; + } + public void clearClueHi(long mask) { + hi &= mask; + vhi &= mask; + rhi &= mask; + xhi &= mask; + } + public boolean isClueLo(int index) { return ((lo >>> index) & 1L) != X; } + public boolean isClueHi(int index) { return ((hi >>> (index & 63)) & 1L) != X; } + public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } + + public int clueCount() { return bitCount(lo) + bitCount(hi); } + + public Clues from(Clues best) { + lo = best.lo; + hi = best.hi; + vlo = best.vlo; + vhi = best.vhi; + rlo = best.rlo; + rhi = best.rhi; + xlo = best.xlo; + xhi = best.xhi; + return this; + } + public byte getDir(int index) { + if ((index & 64) == 0) { + var v = (vlo & (1L << index)) != 0 ? 1 : 0; + var r = (rlo & (1L << index)) != 0 ? 1 : 0; + var x = (xlo & (1L << index)) != 0 ? 1 : 0; + return (byte) ((x << 2) | (r << 1) | v); + } else { + var v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0; + var r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0; + var x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0; + return (byte) ((x << 2) | (r << 1) | v); + } + } +} diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 15bbc22..1170dbd 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -6,7 +6,6 @@ import lombok.experimental.Delegate; import lombok.val; import precomp.Const9x8.Cell; import puzzle.Export.Puzzle.Replacar.Rell; -import puzzle.Masker.Clues; import puzzle.Meta.ShardLem; import puzzle.SwedishGenerator.Dict; import puzzle.SwedishGenerator.FillResult; @@ -18,7 +17,7 @@ import static precomp.Const9x8.INIT_GRID_OUTPUT; import static precomp.Const9x8.INIT_GRID_OUTPUT_ARR; import static puzzle.Export.Clue.DOWN0; import static puzzle.Export.Clue.RIGHT1; -import static puzzle.Masker.Clues.createEmpty; +import static puzzle.Clues.createEmpty; import static puzzle.Masker.Slot; import static puzzle.Masker.C; import static puzzle.SwedishGenerator.Lemma; @@ -73,7 +72,7 @@ public record Export() { 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, _, _) -> { + Masker.forEachSlot(c, (s, _, _) -> { val idx = Slot.clueIndex(s); val dir = Slot.dir(s); sb.setCharAt(INDEX(INDEX_ROW(idx), C + 1, INDEX_COL(idx)), CLUE_CHAR(dir)); @@ -106,7 +105,7 @@ public record Export() { record Puzzle(@Delegate Grid grid, Clues cl) implements Stream { - public Puzzle(Clues clues) { this(clues.toGrid(), clues); } + public Puzzle(Clues clues) { this(Masker.toGrid(clues), clues); } public Puzzle(Signa clues) { this(clues.c); } public @Delegate Stream stream() { val stream = Stream.builder(); @@ -116,7 +115,7 @@ public record Export() { } String gridToString() { var sb = INIT_GRID_OUTPUT_ARR.clone(); - cl.forEachSlot((s, _, _) -> { + Masker.forEachSlot(cl, (s, _, _) -> { val idx = Slot.clueIndex(s); val r = idx & 7; val c = idx >>> 3; @@ -128,7 +127,7 @@ public record Export() { } public String[] exportGrid(Replacar clueChar, char emptyFallback) { var sb = INIT_GRID_OUTPUT_ARR.clone(); - cl.forEachSlot((s, l, a) -> { + Masker.forEachSlot(cl, (s, l, a) -> { val idx = Slot.clueIndex(s); val r = idx & 7; val c = idx >>> 3; diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index a86708e..505f987 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -5,9 +5,7 @@ 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.*; @@ -358,7 +356,7 @@ public class Main { } } static Clues generateNewClues(Rng rng, Opts opts) { - var masker = new Masker(rng, new int[Masker.STACK_SIZE], Masker.Clues.createEmpty()); + var masker = new Masker(rng, new int[Masker.STACK_SIZE], Clues.createEmpty()); return masker.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring); } static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) { diff --git a/src/main/java/puzzle/Masker.java b/src/main/java/puzzle/Masker.java index ab60979..1834557 100644 --- a/src/main/java/puzzle/Masker.java +++ b/src/main/java/puzzle/Masker.java @@ -3,9 +3,6 @@ package puzzle; import module java.base; import anno.GenerateShapedCopies; import anno.Shaped; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.experimental.Accessors; import lombok.val; import precomp.Neighbors9x8; import gen.rci; @@ -13,7 +10,7 @@ import gen.rci; import static java.lang.Long.*; import static puzzle.SwedishGenerator.*; @GenerateShapedCopies( - packageName = "gen", + packageName = "puzzle", className = "Masker", shapes = { "precomp.Neighbors9x8", "precomp.Neighbors4x3" } ) @@ -31,6 +28,7 @@ public final class Masker { @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; + public static final int[][] MUTATE_RI = new int[SIZE][625]; private static final boolean VERBOSE = false; private final int[] activeCIdx = new int[SIZE]; private final long[] activeSLo = new long[SIZE]; @@ -49,6 +47,12 @@ public final class Masker { this.cache = cache; } public static boolean isLo(int n) { return (n & 64) == 0; } + public static double similarity(Clues a, Clues b) { + var matchLo = (~(a.lo ^ b.lo)) & (~a.lo | (~(a.vlo ^ b.vlo) & ~(a.rlo ^ b.rlo) & ~(a.xlo ^ b.xlo))); + var matchHi = (~(a.hi ^ b.hi)) & (~a.hi | (~(a.vhi ^ b.vhi) & ~(a.rhi ^ b.rhi) & ~(a.xhi ^ b.xhi))); + + return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED; + } public boolean isValid(Clues c) { return findOffendingClue(c) == -1; @@ -91,7 +95,7 @@ public final class Masker { } return -1; } - + public static Grid toGrid(Clues c) { return new Grid(new byte[SIZE], c.lo, c.hi); } public void cleanup(Clues c) { var guard = 0; while (guard++ < 50) { @@ -102,10 +106,8 @@ public final class Masker { } } - public static final int[][] MUTATE_RI = new int[Neighbors9x8.SIZE][625]; - static { - for (var i = 0; i < Neighbors9x8.SIZE; i++) { + for (var i = 0; i < SIZE; i++) { var k = 0; for (var dr1 = -2; dr1 <= 2; dr1++) for (var dr2 = -2; dr2 <= 2; dr2++) @@ -120,7 +122,7 @@ public final class Masker { // slice ray to stop before first clue, depending on direction monotonicity // right/down => increasing indices; up/left => decreasing indices // first clue is highest index among hits (hi first, then lo) - private static void processSlotRev(Clues c, SlotVisitor visitor, int key) { + static void processSlotRev(Clues c, SlotVisitor visitor, int key) { var rayLo = PATH_LO[key]; var rayHi = PATH_HI[key]; // only consider clue cells @@ -140,7 +142,7 @@ public final class Masker { if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN) visitor.visit(key, rayLo, rayHi); } - private static boolean validSlotRev(long lo, long hi, int key) { + static boolean validSlotRev(long lo, long hi, int key) { var rayLo = PATH_LO[key]; var rayHi = PATH_HI[key]; // only consider clue cells @@ -151,7 +153,7 @@ public final class Masker { else if (hitsLo != X) return (Long.bitCount(rayLo & -(1L << 63 - numberOfLeadingZeros(hitsLo) << 1)) + Long.bitCount(rayHi) >= MIN_LEN); else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN); } - private static boolean validSlot(long lo, long hi, int key) { + static boolean validSlot(long lo, long hi, int key) { var rayLo = PATH_LO[key]; var rayHi = PATH_HI[key]; var hitsLo = rayLo & lo; @@ -161,7 +163,7 @@ public final class Masker { else if (hitsHi != X) return (Long.bitCount(rayLo) + Long.bitCount(rayHi & ((1L << numberOfTrailingZeros(hitsHi)) - 1)) >= MIN_LEN); else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN); } - private static void processSlot(Clues c, SlotVisitor visitor, int key) { + static void processSlot(Clues c, SlotVisitor visitor, int key) { var rayLo = PATH_LO[key]; var rayHi = PATH_HI[key]; var hitsLo = rayLo & c.lo; @@ -178,9 +180,25 @@ public final class Masker { if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN) visitor.visit(key, rayLo, rayHi); } + public static void forEachSlot(Clues c,SlotVisitor visitor) { + final long lo = c.lo, hi = c.hi, xlo = c.xlo, xhi = c.xhi, rlo = c.rlo, rhi = c.rhi, vlo = c.vlo, vhi = c.vhi; + for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(c, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1)); + for (var l = lo & ~xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(c, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 0)); + for (var l = lo & ~xlo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(c, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2)); + for (var l = lo & ~xlo & rlo & vlo; l != X; l &= l - 1) processSlotRev(c, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3)); + for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(c, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4)); + for (var l = lo & xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(c, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 5)); + + for (var h = hi & ~xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(c, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1)); + for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(c, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0)); + for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(c, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2)); + for (var h = hi & ~xhi & rhi & vhi; h != X; h &= h - 1) processSlotRev(c, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3)); + for (var h = hi & xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(c, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 4)); + for (var h = hi & xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(c, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 5)); + } public static Slot[] extractSlots(Clues c, DictEntry[] index, DictEntry[] rev) { var slots = new ArrayList(c.clueCount()); - c.forEachSlot((key, lo, hi) -> slots.add(Slot.from(key, lo, hi, Slotinfo.increasing(key) ? index[Slot.length(lo, hi)] : rev[Slot.length(lo, hi)]))); + forEachSlot(c,(key, lo, hi) -> slots.add(Slot.from(key, lo, hi, Slotinfo.increasing(key) ? index[Slot.length(lo, hi)] : rev[Slot.length(lo, hi)]))); return slots.toArray(Slot[]::new); } public static Slotinfo[] slots(Clues mask, Dict d) { return slots(mask, d.index(), d.reversed()); } @@ -437,7 +455,10 @@ public final class Masker { return penalty; } - + public static boolean hasRoomForClue(Clues c,int key) { + if (Slotinfo.increasing(key)) if (!validSlot(c.lo, c.hi, key)) return false; + return validSlotRev(c.lo, c.hi, key); + } public Clues randomMask(final int clueSize) { var g = Clues.createEmpty(); for (int placed = 0, guard = 0, ri; placed < clueSize && guard < 4000; guard++) { @@ -447,7 +468,7 @@ public final class Masker { if (g.isClueLo(ri)) continue; var d_idx = rng.randomClueDir(); var key = Slot.packSlotKey(ri, d_idx); - if (g.hasRoomForClue(key)) { + if (hasRoomForClue(g,key)) { g.setClueLo(1L << ri, d_idx); if (isValid(g)) placed++; else g.clearClueLo(~(1L << ri)); @@ -456,7 +477,7 @@ public final class Masker { if (g.isClueHi(ri)) continue; var d_idx = rng.randomClueDir(); var key = Slot.packSlotKey(ri, d_idx); - if (g.hasRoomForClue(key)) { + if (hasRoomForClue(g,key)) { g.setClueHi(1L << (ri & 63), d_idx); if (isValid(g)) placed++; else g.clearClueHi(~(1L << (ri & 63))); @@ -474,7 +495,7 @@ public final class Masker { if (c.notClue(ri)) { // ADD var d = rng.randomClueDir(); var key = Slot.packSlotKey(ri, d); - if (c.hasRoomForClue(key)) { + if (hasRoomForClue(c,key)) { if (isLo(ri)) { c.setClueLo(1L << ri, d); if (!isValid(c)) c.clearClueLo(~(1L << ri)); @@ -502,7 +523,7 @@ public final class Masker { if (op < 4) { // CHANGE DIRECTION var d = rng.randomClueDir(); var key = Slot.packSlotKey(ri, d); - if (c.hasRoomForClue(key)) { + if (hasRoomForClue(c,key)) { var oldD = c.getDir(ri); if (isLo(ri)) { c.setClueLo(1L << ri, d); @@ -519,7 +540,7 @@ public final class Masker { if (c.notClue(nri)) { var d = c.getDir(ri); var nkey = Slot.packSlotKey(nri, d); - if (c.hasRoomForClue(nkey)) { + if (hasRoomForClue(c,nkey)) { if (isLo(ri)) c.clearClueLo(~(1L << ri)); else c.clearClueHi(~(1L << (ri & 63))); if (isLo(nri)) c.setClueLo(1L << nri, d); @@ -626,7 +647,7 @@ public final class Masker { if (nextCount >= popSize) break; var unique = true; for (var i = 0; i < nextCount; i++) { - if (cand.grid.similarity(next[i].grid) > 0.92) { + if (similarity(cand.grid, next[i].grid) > 0.92) { unique = false; break; } @@ -661,108 +682,6 @@ public final class Masker { } //@formatter:off @FunctionalInterface public interface SlotVisitor { void visit(int key, long lo, long hi); } - //@formatter:on - @AllArgsConstructor - @Accessors(fluent = true) - 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); } - - public boolean hasRoomForClue(int key) { - if (Slotinfo.increasing(key)) if (!validSlot(lo, hi, key)) return false; - return validSlotRev(lo, hi, key); - } - - public Clues setClue(precomp.Const9x8.Cell cell) { - if ((cell.index & 64) == 0) setClueLo(cell.mask, cell.d); - else setClueHi(cell.mask, cell.d); - return this; - } - - public void setClueLo(long mask, byte idx) { - lo |= mask; - if ((idx & 1) != 0) vlo |= mask; - else vlo &= ~mask; - if ((idx & 2) != 0) rlo |= mask; - else rlo &= ~mask; - if ((idx & 4) != 0) xlo |= mask; - else xlo &= ~mask; - } - public void setClueHi(long mask, byte idx) { - hi |= mask; - if ((idx & 1) != 0) vhi |= mask; - else vhi &= ~mask; - if ((idx & 2) != 0) rhi |= mask; - else rhi &= ~mask; - if ((idx & 4) != 0) xhi |= mask; - else xhi &= ~mask; - } - public void clearClueLo(long mask) { - lo &= mask; - vlo &= mask; - rlo &= mask; - xlo &= mask; - } - public void clearClueHi(long mask) { - hi &= mask; - vhi &= mask; - rhi &= mask; - xhi &= mask; - } - public boolean isClueLo(int index) { return ((lo >>> index) & 1L) != X; } - public boolean isClueHi(int index) { return ((hi >>> (index & 63)) & 1L) != X; } - public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } - - public int clueCount() { return bitCount(lo) + bitCount(hi); } - public double similarity(Clues b) { - var matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo) & ~(xlo ^ b.xlo))); - var matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi) & ~(xhi ^ b.xhi))); - - return (bitCount(matchLo & MASK_LO) + bitCount(matchHi & MASK_HI)) / SIZED; - } - public Grid toGrid() { return new Grid(new byte[SIZE], lo, hi); } - - public void forEachSlot(SlotVisitor visitor) { - for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1)); - for (var l = lo & ~xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 0)); - for (var l = lo & ~xlo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2)); - for (var l = lo & ~xlo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3)); - for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4)); - for (var l = lo & xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 5)); - - for (var h = hi & ~xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1)); - for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0)); - for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2)); - for (var h = hi & ~xhi & rhi & vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3)); - for (var h = hi & xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 4)); - for (var h = hi & xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 5)); - } - public Clues from(Clues best) { - lo = best.lo; - hi = best.hi; - vlo = best.vlo; - vhi = best.vhi; - rlo = best.rlo; - rhi = best.rhi; - xlo = best.xlo; - xhi = best.xhi; - return this; - } - public byte getDir(int index) { - if ((index & 64) == 0) { - var v = (vlo & (1L << index)) != 0 ? 1 : 0; - var r = (rlo & (1L << index)) != 0 ? 1 : 0; - var x = (xlo & (1L << index)) != 0 ? 1 : 0; - return (byte) ((x << 2) | (r << 1) | v); - } else { - var v = (vhi & (1L << (index & 63))) != 0 ? 1 : 0; - var r = (rhi & (1L << (index & 63))) != 0 ? 1 : 0; - var x = (xhi & (1L << (index & 63))) != 0 ? 1 : 0; - return (byte) ((x << 2) | (r << 1) | v); - } - } - } public record Slot(int key, long lo, long hi, DictEntry entry) { diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index f6c8888..4d50c27 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -78,7 +78,7 @@ public class MainTest { @Test void testForEachSlot() { var count = new AtomicInteger(0); - Signa.of(r0c0d1).forEachSlot((key, lo, hi) -> { + Masker.forEachSlot(Signa.of(r0c0d1).c(), (key, lo, hi) -> { count.incrementAndGet(); assertEquals(8, Long.bitCount(lo) + Long.bitCount(hi)); assertEquals(0, Masker.IT[Long.numberOfTrailingZeros(lo)].r()); @@ -181,7 +181,7 @@ public class MainTest { ); var slotInfo = mask.slots(DictData950.DICT950); var grid = Slotinfo.grid(slotInfo); - var filled = fillMask(rng, slotInfo, grid.lo,grid.hi, grid.g); + var filled = fillMask(rng, slotInfo, grid.lo, grid.hi, grid.g); Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)"); Assertions.assertEquals(17, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed"); Assertions.assertEquals("BEADEMT", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get())); diff --git a/src/test/java/puzzle/MarkerTest.java b/src/test/java/puzzle/MarkerTest.java index 0928be6..0b59c68 100644 --- a/src/test/java/puzzle/MarkerTest.java +++ b/src/test/java/puzzle/MarkerTest.java @@ -7,7 +7,6 @@ import puzzle.Export.Puzzle; import puzzle.Export.Placed; import puzzle.Export.PuzzleResult; import puzzle.Export.Rewards; -import puzzle.Masker.Clues; import puzzle.SwedishGenerator.Assign; import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.Lemma; @@ -52,7 +51,7 @@ public class MarkerTest { val orig = cache.from(clues); simCount++; masker.mutate(clues); - sim += orig.similarity(clues); + sim += Masker.similarity(orig, clues); assertTrue(masker.isValid(clues), "Mask should be valid for length \n" + new Signa(clues).gridToString()); } } @@ -71,7 +70,7 @@ public class MarkerTest { var clues2 = masker.randomMask(j); simCount++; var cross = masker.crossover(clues, clues2); - sim += Math.max(cross.similarity(clues), cross.similarity(clues2)); + sim += Math.max(Masker.similarity(cross, clues), Masker.similarity(cross, clues2)); assertTrue(masker.isValid(cross), "Mask should be valid for length \n" + new Signa(cross).gridToString()); } } @@ -83,17 +82,17 @@ public class MarkerTest { var b = Signa.of(r0c0d1, r2c1d0).c(); // Identity - assertEquals(1.0, a.similarity(b), 0.001); + assertEquals(1.0, Masker.similarity(a, b), 0.001); // Different direction var c = Signa.of(r0c0d0, r2c1d0); - assertTrue(a.similarity(c.c()) < 1.0); + assertTrue(Masker.similarity(a, c.c()) < 1.0); // Completely different var d = Clues.createEmpty(); // Matching empty cells count towards similarity. // a has 2 clues, d has 0. They match on 70 empty cells. - assertEquals(70.0 / 72.0, a.similarity(d), 0.001); + assertEquals(70.0 / 72.0, Masker.similarity(a, d), 0.001); } @Test @@ -113,22 +112,22 @@ public class MarkerTest { var g = Clues.createEmpty(); // Room for Right clue at (0,0) (length 8) - assertTrue(g.hasRoomForClue(r0c0d1.slotKey)); + assertTrue(Masker.hasRoomForClue(g,r0c0d1.slotKey)); // No room for Right clue at (0,8) (length 0 < MIN_LEN) - assertFalse(g.hasRoomForClue(r0c8d1.slotKey)); + assertFalse(Masker.hasRoomForClue(g,r0c8d1.slotKey)); // Blocked room // Let's place a clue that leaves only 1 cell for another clue. g.setClue(r0c2d1); // Now Right at (0,0) only has (0,1) available -> length 1 < MIN_LEN (which is 2) - assertFalse(g.hasRoomForClue(r0c0d1.slotKey)); + assertFalse(Masker.hasRoomForClue(g,r0c0d1.slotKey)); // But enough room g.clearClueLo(0L); g.setClue(r0c3d1); // Now Right at (0,0) has (0,1), (0,2) -> length 2 == MIN_LEN - assertTrue(g.hasRoomForClue(r0c0d1.slotKey)); + assertTrue(Masker.hasRoomForClue(g,r0c0d1.slotKey)); } @Test @@ -239,7 +238,7 @@ public class MarkerTest { // Controleer of forEachSlot het slot vindt final var found = new boolean[]{ false }; - clues.forEachSlot((key, lo, hi) -> { + Masker.forEachSlot(clues.c(), (key, lo, hi) -> { if (key == r0c0d4.slotKey) { found[0] = true; // Woord zou moeten starten op (0,1) @@ -269,7 +268,7 @@ public class MarkerTest { // Controleer of forEachSlot het slot vindt final var found = new boolean[]{ false }; - clues.forEachSlot((key, lo, hi) -> { + Masker.forEachSlot(clues.c(), (key, lo, hi) -> { if (key == r0c1d5.slotKey) { found[0] = true; // Woord zou moeten starten op (0,0) diff --git a/src/test/java/puzzle/PerformanceTest.java b/src/test/java/puzzle/PerformanceTest.java index 6fbf44f..aba0b18 100644 --- a/src/test/java/puzzle/PerformanceTest.java +++ b/src/test/java/puzzle/PerformanceTest.java @@ -8,8 +8,6 @@ import org.junit.jupiter.api.Test; import puzzle.Export.Clue; import puzzle.Export.Signa; import puzzle.Export.Puzzle; -import puzzle.Masker.Clues; -import puzzle.SwedishGenerator.Grid; import puzzle.SwedishGenerator.Rng; import puzzle.SwedishGenerator.Slotinfo; @@ -50,7 +48,7 @@ public class PerformanceTest { var c = 0; for (var size : clueSizes) { var t0 = System.currentTimeMillis(); - val masker = new Masker(rng, new int[Masker.STACK_SIZE], Masker.Clues.createEmpty()); + val masker = new Masker(rng, new int[Masker.STACK_SIZE], Clues.createEmpty()); // Increased population and generations for stress arr[c++] = masker.generateMask(size, 200, 100, 50); var t1 = System.currentTimeMillis(); diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 01a977c..80b0962 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -13,7 +13,6 @@ import puzzle.DictJavaGeneratorMulti.DictEntryDTO.IntListDTO; import puzzle.Export.Signa; import puzzle.Export.Puzzle; import puzzle.Export.Lettrix; -import puzzle.Masker.Clues; import puzzle.Masker.Slot; import static org.junit.jupiter.api.Assertions.*; @@ -220,8 +219,8 @@ public class SwedishGeneratorTest { @Test void testMaskFitnessBasic() { - var gen = new Masker(new Rng(0), new int[Masker.STACK_SIZE], Masker.Clues.createEmpty()); - var grid = Masker.Clues.createEmpty(); + var gen = new Masker(new Rng(0), new int[Masker.STACK_SIZE], Clues.createEmpty()); + var grid = Clues.createEmpty(); // Empty grid should have high penalty (no slots) var f1 = gen.maskFitness(grid, 18); assertTrue(f1 >= 1_000_000_000L); @@ -234,7 +233,7 @@ public class SwedishGeneratorTest { @Test void testGeneticAlgorithmComponents() { - var gen = new Masker(new Rng(42), new int[Masker.STACK_SIZE], Masker.Clues.createEmpty()); + var gen = new Masker(new Rng(42), new int[Masker.STACK_SIZE], Clues.createEmpty()); var c1 = new Signa(gen.randomMask(18)); assertNotNull(c1); var g2 = new Signa(gen.mutate(c1.deepCopyGrid().c())); @@ -332,8 +331,8 @@ public class SwedishGeneratorTest { @Test void testMaskFitnessDetailed() { - var gen = new Masker(new Rng(42), new int[Masker.STACK_SIZE], Masker.Clues.createEmpty()); - var grid = Masker.Clues.createEmpty(); + var gen = new Masker(new Rng(42), new int[Masker.STACK_SIZE], Clues.createEmpty()); + var grid = Clues.createEmpty(); // Empty grid: huge penalty var fitEmpty = gen.maskFitness(grid, 18); assertTrue(fitEmpty >= 1_000_000_000L); diff --git a/src/test/java/puzzle/TestDuplication.java b/src/test/java/puzzle/TestDuplication.java index f4b9e5c..bea52e9 100644 --- a/src/test/java/puzzle/TestDuplication.java +++ b/src/test/java/puzzle/TestDuplication.java @@ -1,5 +1,6 @@ package puzzle; +import puzzle.Masker_Neighbors4x3; import gen.Test123X_Neighbors4x3; import gen.Test123X_Neighbors9x8; import lombok.val; @@ -32,7 +33,7 @@ public class TestDuplication { Assertions.assertEquals(4, mask.clueCount()); val map = mask.stream().collect(Collectors.toMap(Vestigium::index, Vestigium::clue)); Assertions.assertEquals(4, map.size()); - var slots = mask.slots(DictData800.DICT800); + var slots = Masker_Neighbors4x3.slots(mask.c(), DictData800.DICT800); // 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)); }