From b6351a6fb2e76b6f16fe823ea5a15792dc948f6c Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 12 Jan 2026 11:46:38 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/SwedishGenerator.java | 84 +++++++++++++++++----- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 35192f2..7e1827c 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -62,14 +62,16 @@ public record SwedishGenerator(Rng rng) { static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); } record Pick(Slot slot, CandidateInfo info, boolean done) { } - static final byte B0 = (byte) 0; - static final byte B64 = (byte) 64; - static final byte B48 = (byte) 48; - static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX; - static final rci[] IT = Neighbors9x8.IT; - static final int[][] MUTATE_RI = new int[SIZE][625]; - static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO; - static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI; + static final byte B0 = (byte) 0; + static final byte B64 = (byte) 64; + static final byte B48 = (byte) 48; + static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX; + static final rci[] IT = Neighbors9x8.IT; + static final int[][] MUTATE_RI = new int[SIZE][625]; + static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO; + static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI; + static final long[] PATH_LO = Neighbors9x8.PATH_LO; + static final long[] PATH_HI = Neighbors9x8.PATH_HI; static { for (int i = 0; i < SIZE; i++) { @@ -305,10 +307,10 @@ public record SwedishGenerator(Rng rng) { } } - static record Slot(int key, long packedPos) { + static record Slot(int key, long packedPos, long packedPoslo, long packedPoshi) { static final int BIT_FOR_DIR = 3; - static Slot from(int key, long packedPos, int len) { return new Slot(key, packedPos | ((long) len << 56)); } + static Slot from(int key, long packedPos, int len) { return new Slot(key, packedPos | ((long) len << 56), 0L, 0L); } void undoPlace(Grid grid, int mask) { for (int i = 0, len = len(); i < len; i++) if ((mask & (1L << i)) != 0) grid.clearletter(pos(i)); } public int len() { return (int) (packedPos >>> 56); } public int clueR() { return Grid.r((key >>> BIT_FOR_DIR)); } @@ -322,6 +324,56 @@ public record SwedishGenerator(Rng rng) { public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; } } + interface SlotVisitor2 { + + void visitMasks(int slotDir, long loMask, long hiMask); + } + private static void processSlot(Grid grid, SlotVisitor2 visitor, int idx) { + int d = grid.digitAt(idx); // 1..4 + int di = d - 1; // 0..3 + int key = (idx << 2) | di; + + long rayLo = PATH_LO[key]; + long rayHi = PATH_HI[key]; + + // only consider clue cells + long hitsLo = rayLo & grid.lo; + long hitsHi = rayHi & grid.hi; + + // slice ray to stop before first clue, depending on direction monotonicity + // right/down => increasing indices; up/left => decreasing indices + boolean increasing = (d == 2 || d == 3); + + if (increasing) { + // first clue is lowest index among hits (lo first, then hi) + if (hitsLo != 0) { + long stop = 1L << Long.numberOfTrailingZeros(hitsLo); + rayLo &= (stop - 1); + rayHi = 0; // any hi is beyond the stop + } else if (hitsHi != 0) { + long stop = 1L << Long.numberOfTrailingZeros(hitsHi); + // keep all lo (lo indices are < any hi index), but cut hi below stop + rayHi &= (stop - 1); + } + } else { + // first clue is highest index among hits (hi first, then lo) + if (hitsHi != 0) { + int msb = 63 - Long.numberOfLeadingZeros(hitsHi); + long stop = 1L << msb; + rayHi &= ~((stop << 1) - 1); // keep bits > stop + rayLo = 0; // lo indices are below stop + } else if (hitsLo != 0) { + int msb = 63 - Long.numberOfLeadingZeros(hitsLo); + long stop = 1L << msb; + rayLo &= ~((stop << 1) - 1); + } + } + + if ((rayLo | rayHi) != 0) { + visitor.visitMasks(Slot.packSlotDir(idx, d), rayLo, rayHi); + } + } + private static void processSlot(Grid grid, SlotVisitor visitor, int idx) { var d = grid.digitAt(idx); var packed = OFFSETS_D_IDX[(d - 1) | (idx << 2)]; @@ -462,7 +514,7 @@ public record SwedishGenerator(Rng rng) { for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) { idx = rng.randint(0, SIZE_MIN_1); if (g.isClue(idx)) continue; - int d_idx = rng.randint2bit(); + int d_idx = rng.randint2bit(); val packed = OFFSETS_D_IDX[d_idx | idx << 2]; if (g.hasRoomForClue(packed)) { g.setClue(idx, (byte) (1 + (d_idx | 48))); @@ -472,13 +524,13 @@ public record SwedishGenerator(Rng rng) { return g; } Grid mutate(Grid grid) { - var g = grid.deepCopyGrid(); - int ri; - var bytes = MUTATE_RI[rng.randint(0, SIZE_MIN_1)]; + var g = grid.deepCopyGrid(); + int ri; + var bytes = MUTATE_RI[rng.randint(0, SIZE_MIN_1)]; for (var k = 0; k < 4; k++) { ri = bytes[rng.randint(0, 624)]; if (!g.clueless(ri)) { - int d_idx = rng.randint2bit(); + int d_idx = rng.randint2bit(); val packed = OFFSETS_D_IDX[d_idx | ri << 2]; if (g.hasRoomForClue(packed)) g.setClue(ri, (byte) (1 + (d_idx | 48))); } @@ -587,8 +639,6 @@ public record SwedishGenerator(Rng rng) { var x = pop.get(i); if (x.fit() < best.fit()) best = x; } - //pop.sort(Comparator.comparingLong(GridAndFit::fit)); - //return pop.get(0).grid; return best.grid; }