From b3b1921414e12f109a108225cf918a4b7630e3e1 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 12 Jan 2026 20:03:31 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/Export.java | 56 +++++++++++++++-- src/main/java/puzzle/SwedishGenerator.java | 62 ++++++------------- src/test/java/puzzle/MainTest.java | 9 +-- .../java/puzzle/SwedishGeneratorTest.java | 13 ++-- 4 files changed, 82 insertions(+), 58 deletions(-) diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 179e411..9e482a0 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -3,14 +3,14 @@ package puzzle; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.Delegate; -import puzzle.Export.Gridded; import puzzle.SwedishGenerator.Dict; import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.Grid; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.StringJoiner; +import java.util.function.IntSupplier; +import java.util.stream.IntStream; import static puzzle.SwedishGenerator.R; import static puzzle.SwedishGenerator.Lemma; import static puzzle.SwedishGenerator.Slot; @@ -38,6 +38,55 @@ public record Export() { record Gridded(@Delegate Grid grid) { static boolean isLetter(byte b) { return (b & SwedishGenerator.B64) != SwedishGenerator.B0; } + public static IntStream walk(byte base, long lo, long hi) { + if (Slot.increasing(base)) { + return IntStream.concat( + IntStream.generate(new IntSupplier() { + + long temp = lo; + @Override + public int getAsInt() { + int res = Long.numberOfTrailingZeros(temp); + temp &= temp - 1; + return res; + } + }).limit(Long.bitCount(lo)), + IntStream.generate(new IntSupplier() { + + long temp = hi; + @Override + public int getAsInt() { + int res = 64 | Long.numberOfTrailingZeros(temp); + temp &= temp - 1; + return res; + } + }).limit(Long.bitCount(hi))); + } else { + return IntStream.concat( + IntStream.generate(new IntSupplier() { + + long temp = hi; + @Override + public int getAsInt() { + int msb = 63 - Long.numberOfLeadingZeros(temp); + int res = 64 | msb; + temp &= ~(1L << msb); + return res; + } + }).limit(Long.bitCount(hi)), + IntStream.generate(new IntSupplier() { + + long temp = lo; + @Override + public int getAsInt() { + int msb = 63 - Long.numberOfLeadingZeros(temp); + int res = msb; + temp &= ~(1L << msb); + return res; + } + }).limit(Long.bitCount(lo))); + } + } //public boolean isLetterSet(int idx) { return isLetter(g[idx]); } char NOT_CLUE_NOT_LETTER_TO(byte b, char fallback) { return isLetter(b) ? (char) b : fallback; } String gridToString() { @@ -123,8 +172,7 @@ public record Export() { Placed extractPlacedFromSlot(Slot s, long lemma) { var d = s.dir(); - var cells = new int[s.len()]; - for (int i = 0, len = s.len(); i < len; i++) cells[i] = s.pos(i); + var cells = s.walk().toArray(); char direction; var startRow = Grid.r(cells[0]); diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 74b1ca7..4ca927f 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -3,7 +3,6 @@ package puzzle; import lombok.Getter; import lombok.val; import precomp.Neighbors9x8; -import precomp.Neighbors9x8.nbrs_16; import precomp.Neighbors9x8.rci; import puzzle.Export.Bit; import puzzle.Export.Bit1029; @@ -19,6 +18,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.stream.IntStream; import static java.nio.charset.StandardCharsets.*; /** @@ -58,7 +58,7 @@ public record SwedishGenerator(Rng rng) { static final char C_DASH = '\0'; static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH; static final ThreadLocal CTX = ThreadLocal.withInitial(Context::new); - + 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) { } @@ -108,7 +108,7 @@ public record SwedishGenerator(Rng rng) { stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size(); } } - + } static final class Context { @@ -186,10 +186,10 @@ public record SwedishGenerator(Rng rng) { if ((idx & 64) == 0) lo &= ~(1L << idx); else hi &= ~(1L << (idx & 63)); } - boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; } - boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; } - boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } - boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } + boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; } + boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; } + boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } + boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } boolean clueless(int idx) { if ((idx & 64) == 0) { val test = (1L << idx); @@ -314,44 +314,18 @@ public record SwedishGenerator(Rng rng) { static record Slot(int key, long lo, long hi) { static final int BIT_FOR_DIR = 3; - static Slot from(int key, long lo, long hi) { return new Slot(key, lo, hi); } + static Slot from(int key, long lo, long hi) { return new Slot(key, lo, hi); } - public int len() { return Long.bitCount(lo) + Long.bitCount(hi); } - public int clueR() { return Grid.r((key >>> BIT_FOR_DIR)); } - public int clueIndex() { return key >>> BIT_FOR_DIR; } - public int clueC() { return Grid.c((key >>> BIT_FOR_DIR)); } - public int dir() { return key & 7; } - public boolean horiz() { return horiz(dir()); } - public boolean reversed() { return (key & 2) == 0; } - public boolean increasing() { return (key & 2) != 0; } - public static boolean increasing(int dir) { return (dir & 2) != 0; } - public int pos(int i) { - if (increasing()) { - int bcLo = Long.bitCount(lo); - if (i < bcLo) { - long temp = lo; - for (int k = 0; k < i; k++) temp &= temp - 1; - return Long.numberOfTrailingZeros(temp); - } else { - i -= bcLo; - long temp = hi; - for (int k = 0; k < i; k++) temp &= temp - 1; - return 64 | Long.numberOfTrailingZeros(temp); - } - } else { - int bcHi = Long.bitCount(hi); - if (i < bcHi) { - long temp = hi; - for (int k = 0; k < i; k++) temp &= ~(1L << (63 - Long.numberOfLeadingZeros(temp))); - return 64 | (63 - Long.numberOfLeadingZeros(temp)); - } else { - i -= bcHi; - long temp = lo; - for (int k = 0; k < i; k++) temp &= ~(1L << (63 - Long.numberOfLeadingZeros(temp))); - return 63 - Long.numberOfLeadingZeros(temp); - } - } - } + public int len() { return Long.bitCount(lo) + Long.bitCount(hi); } + public int clueR() { return Grid.r((key >>> BIT_FOR_DIR)); } + public int clueIndex() { return key >>> BIT_FOR_DIR; } + public int clueC() { return Grid.c((key >>> BIT_FOR_DIR)); } + public int dir() { return key & 7; } + public boolean horiz() { return horiz(key); } + public boolean reversed() { return (key & 2) == 0; } + public boolean increasing() { return (key & 2) != 0; } + public static boolean increasing(int dir) { return (dir & 2) != 0; } + public IntStream walk() { return Gridded.walk((byte) key, lo, hi); } public static boolean horiz(int d) { return (d & 1) == 0; } public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; } } diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 20b42e4..bda7d51 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -29,10 +29,11 @@ public class MainTest { assertEquals(1, slots.size()); var s = slots.getFirst(); assertEquals(8, s.len()); - assertEquals(0, Grid.r(s.pos(0))); - assertEquals(1, Grid.c(s.pos(0))); - assertEquals(0, Grid.r(s.pos(1))); - assertEquals(2, Grid.c(s.pos(1))); + var cells = s.walk().toArray(); + assertEquals(0, Grid.r(cells[0])); + assertEquals(1, Grid.c(cells[0])); + assertEquals(0, Grid.r(cells[1])); + assertEquals(2, Grid.c(cells[1])); } @Test diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 57e8e6d..123c689 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -152,12 +152,13 @@ public class SwedishGeneratorTest { assertEquals(3, s.clueC()); assertEquals(3, s.dir()); assertFalse(s.horiz()); - assertEquals(2, Grid.r(s.pos(0))); - assertEquals(3, Grid.r(s.pos(1))); - assertEquals(4, Grid.r(s.pos(2))); - assertEquals(5, Grid.c(s.pos(0))); - assertEquals(5, Grid.c(s.pos(1))); - assertEquals(5, Grid.c(s.pos(2))); + var cells = s.walk().toArray(); + assertEquals(2, Grid.r(cells[0])); + assertEquals(3, Grid.r(cells[1])); + assertEquals(4, Grid.r(cells[2])); + assertEquals(5, Grid.c(cells[0])); + assertEquals(5, Grid.c(cells[1])); + assertEquals(5, Grid.c(cells[2])); assertTrue(Slot.horiz(2)); // right assertFalse(Slot.horiz(3)); // down