From dc331fc96bd763ec2ffc139ab0f1faefd99233c8 Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 9 Jan 2026 20:02:22 +0100 Subject: [PATCH] Gather data --- src/main/java/puzzle/SwedishGenerator.java | 211 ++++++++++-------- .../java/puzzle/SwedishGeneratorTest.java | 10 +- 2 files changed, 125 insertions(+), 96 deletions(-) diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index fb04020..8aa9ad4 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -26,7 +26,7 @@ import java.util.function.Supplier; * java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt] */ @SuppressWarnings("ALL") -public record SwedishGenerator(int[] buff) { +public record SwedishGenerator() { record CandidateInfo(int[] indices, int count) { } @@ -39,20 +39,7 @@ public record SwedishGenerator(int[] buff) { } } - record rci(int r, int c, int i, long n1, long n2) { - void forEachSetBit71(java.util.function.IntConsumer consumer) { - long lo = n1, hi = n2; - while (lo != 0L) { - int bit = Long.numberOfTrailingZeros(lo); - consumer.accept(bit); - lo &= (lo - 1); - } - while (hi != 0L) { - int bit = Long.numberOfTrailingZeros(hi); - consumer.accept(64 + bit); - hi &= (hi - 1L); - } - } + record rci(int r, int c, int i, long n1, long n2, int nbrCount, long n8_1, long n8_2) { } static final int BAR_LEN = 22; @@ -74,7 +61,8 @@ public record SwedishGenerator(int[] buff) { static boolean isLetter(byte b) { return (b & 64) != 0; } static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); } - public SwedishGenerator() { this(new int[8124]); } + public SwedishGenerator() { } + public SwedishGenerator(int[] buff) { this(); } // Directions for '1'..'6' static final nbrs_16[] OFFSETS = new nbrs_16[]{ @@ -103,11 +91,11 @@ public record SwedishGenerator(int[] buff) { new nbrs_8(0, 1) }; - static final rci[] IT = new rci[SIZE]; + static final rci[] IT = new rci[SIZE]; static { for (int i = 0; i < SIZE; i++) { - int r = i % R; - int c = i / R; + int r = i % R; + int c = i / R; long n1 = 0, n2 = 0; for (var d : nbrs4) { int nr = r + d.r; @@ -118,10 +106,20 @@ public record SwedishGenerator(int[] buff) { else n2 |= (1L << (nidx - 64)); } } - IT[i] = new rci(r, c, i, n1, n2); + long n8_1 = 0, n8_2 = 0; + for (var d : nbrs8) { + int nr = r + d.r; + int nc = c + d.c; + if (nr >= 0 && nr < R && nc >= 0 && nc < C) { + int nidx = nc * R + nr; + if (nidx < 64) n8_1 |= (1L << nidx); + else n8_2 |= (1L << (nidx - 64)); + } + } + IT[i] = new rci(r, c, i, n1, n2, (int) (Long.bitCount(n1) + Long.bitCount(n2)), n8_1, n8_2); } } - + static record Context(int[] covH, int[] covV, int[] cellCount, @@ -129,11 +127,13 @@ public record SwedishGenerator(int[] buff) { Bit seen, byte[] pattern, IntList[] intListBuffer, - int[] undo) { + int[] undo, + int[] inter1, + int[] inter2) { public Context() { this(new int[SIZE], new int[SIZE], new int[SIZE], new int[SIZE], new Bit(), new byte[MAX_WORD_LENGTH], new IntList[MAX_WORD_LENGTH], - new int[2048]); + new int[2048], new int[160000], new int[160000]); } void setPatter(byte[] chars) { System.arraycopy(chars, 0, this.pattern, 0, chars.length); } } @@ -334,22 +334,21 @@ public record SwedishGenerator(int[] buff) { } } - return new Dict(map .toArray(Lemma[]::new)); + return new Dict(map.toArray(Lemma[]::new)); } - static int[] intersectSorted(int[] buff, int[] a, int aLen, int[] b, int bLen) { - //var out = new int[Math.min(aLen, bLen)]; - int i = 0, j = 0, k = 0, x = 0, y = 0; + static int intersectSorted(int[] a, int aLen, int[] b, int bLen, int[] out) { + int i = 0, j = 0, k = 0; while (i < aLen && j < bLen) { - x = a[i]; - y = b[j]; + int x = a[i]; + int y = b[j]; if (x == y) { - buff[k++] = x; + out[k++] = x; i++; j++; } else if (x < y) i++; else j++; } - return Arrays.copyOf(buff, k); + return k; } CandidateInfo candidateInfoForPattern(Context ctx, DictEntry entry, int len) { @@ -378,18 +377,24 @@ public record SwedishGenerator(int[] buff) { } } - var first = listBuffer[0]; - var cur = first.data(); - var curLen = first.size(); + var cur = listBuffer[0].data(); + var curLen = listBuffer[0].size(); + if (listCount == 1) return new CandidateInfo(cur, curLen); + + int[] b1 = ctx.inter1; + int[] b2 = ctx.inter2; + int[] in = cur; + int[] out = b1; for (var k = 1; k < listCount; k++) { var nxt = listBuffer[k]; - cur = intersectSorted(buff, cur, curLen, nxt.data(), nxt.size()); - curLen = cur.length; + curLen = intersectSorted(in, curLen, nxt.data(), nxt.size(), out); + in = out; + out = (out == b1) ? b2 : b1; if (curLen == 0) break; } - return new CandidateInfo(cur, curLen); + return new CandidateInfo(in, curLen); } static record Slot(int key, long packedPos) { @@ -421,24 +426,37 @@ public record SwedishGenerator(int[] buff) { } void forEachSlot(Grid grid, SlotVisitor visitor) { - grid.forEachSetBit71(idx -> { - var d = grid.digitAt(idx); - var nbrs16 = OFFSETS[d]; - int r = Grid.r(idx), c = Grid.c(idx), rr = r + nbrs16.r, cc = c + nbrs16.c; - if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) return; - long packedPos = 0; - var n = 0; - - while (rr >= 0 && rr < R && cc >= 0 && cc < C && grid.isLettercell(rr, cc) && n < MAX_WORD_LENGTH) { - packedPos |= (long) Grid.offset(rr, cc) << (n * 7); - n++; - rr += nbrs16.dr; - cc += nbrs16.dc; - } - if (n > 0) { - visitor.visit((idx << 4) | d, packedPos, n); - } - }); + long lo = grid.bo[0], hi = grid.bo[1]; + int idx; + while (lo != 0) { + idx = Long.numberOfTrailingZeros(lo); + lo &= (lo - 1); + processSlot(grid, visitor, idx); + } + while (hi != 0) { + idx = 64 + Long.numberOfTrailingZeros(hi); + hi &= (hi - 1); + processSlot(grid, visitor, idx); + } + } + + private void processSlot(Grid grid, SlotVisitor visitor, int idx) { + var d = grid.digitAt(idx); + var nbrs16 = OFFSETS[d]; + int r = Grid.r(idx), c = Grid.c(idx), rr = r + nbrs16.r, cc = c + nbrs16.c; + if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) return; + long packedPos = 0; + var n = 0; + + while (rr >= 0 && rr < R && cc >= 0 && cc < C && grid.isLettercell(rr, cc) && n < MAX_WORD_LENGTH) { + packedPos |= (long) Grid.offset(rr, cc) << (n * 7); + n++; + rr += nbrs16.dr; + cc += nbrs16.dc; + } + if (n > 0) { + visitor.visit((idx << 4) | d, packedPos, n); + } } ArrayList extractSlots(Grid grid) { @@ -471,15 +489,18 @@ public record SwedishGenerator(int[] buff) { Arrays.fill(covV, 0, SIZE, 0); boolean[] hasSlots = { false }; - grid.forEachSetBit71(clueIdx -> { + long lo_cl = grid.bo[0], hi_cl = grid.bo[1]; + while (lo_cl != 0) { + int clueIdx = Long.numberOfTrailingZeros(lo_cl); + lo_cl &= (lo_cl - 1); + var d = grid.digitAt(clueIdx); var nbrs16 = OFFSETS[d]; int rr = Grid.r(clueIdx) + nbrs16.r, cc = Grid.c(clueIdx) + nbrs16.c; - if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) return; + if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue; long packedPos = 0; var n = 0; - while (rr >= 0 && rr < R && cc >= 0 && cc < C && n < MAX_WORD_LENGTH) { if (grid.isDigitAt(rr, cc)) break; packedPos |= (long) Grid.offset(rr, cc) << (n * 7); @@ -487,28 +508,47 @@ public record SwedishGenerator(int[] buff) { rr += nbrs16.dr; cc += nbrs16.dc; } - if (n == 0) return; + if (n == 0) continue; hasSlots[0] = true; - if (n < MIN_LEN) { penalty[0] += 8000; - } else { - if (lenCounts[n] <= 0) penalty[0] += 12000; + } else if (lenCounts[n] <= 0) { + penalty[0] += 12000; } - var horiz = Slot.horiz(d) ? covH : covV; for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1; - }); + } + while (hi_cl != 0) { + int clueIdx = 64 + Long.numberOfTrailingZeros(hi_cl); + hi_cl &= (hi_cl - 1); + + var d = grid.digitAt(clueIdx); + var nbrs16 = OFFSETS[d]; + int rr = Grid.r(clueIdx) + nbrs16.r, cc = Grid.c(clueIdx) + nbrs16.c; + if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue; + + long packedPos = 0; + var n = 0; + while (rr >= 0 && rr < R && cc >= 0 && cc < C && n < MAX_WORD_LENGTH) { + if (grid.isDigitAt(rr, cc)) break; + packedPos |= (long) Grid.offset(rr, cc) << (n * 7); + n++; + rr += nbrs16.dr; + cc += nbrs16.dc; + } + if (n == 0) continue; + hasSlots[0] = true; + if (n < MIN_LEN) { + penalty[0] += 8000; + } else if (lenCounts[n] <= 0) { + penalty[0] += 12000; + } + var horiz = Slot.horiz(d) ? covH : covV; + for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1; + } if (!hasSlots[0]) return 1_000_000_000L; - /* grid.forEachSetBit71(clueIdx -> { - var h = covH[clueIdx]; - var v = covV[clueIdx]; - if (h == 0 && v == 0) penalty[0] += 1500; - else if (h > 0 && v > 0) { *//* ok *//* } else if (h + v == 1) penalty[0] += 200; - else penalty[0] += 600; - });*/ int idx, h, v; for (idx = 0; idx < SIZE; idx++) { if (grid.isDigitAt(idx)) continue; @@ -549,26 +589,13 @@ public record SwedishGenerator(int[] buff) { if (size >= 2) penalty[0] += (size - 1L) * 120L; }); - int sp, nr, nc; - long size; - int walls; + int walls; // dead-end-ish letter cell (3+ walls) for (var rci : IT) { if (grid.isDigitAt(rci.i)) continue; - walls = 4 - Long.bitCount(rci.n1) - Long.bitCount(rci.n2); - walls += Long.bitCount(rci.n1 & grid.bo[0]) + Long.bitCount(rci.n2 & grid.bo[1]); + walls = (4 - rci.nbrCount) + Long.bitCount(rci.n1 & grid.bo[0]) + Long.bitCount(rci.n2 & grid.bo[1]); if (walls >= 3) penalty[0] += 400; } - /*for (var rci : IT) { - if (grid.isDigitAt(rci.i)) continue; - walls = 0; - for (var d : nbrs4) { - wr = rci.r + d.r; - wc = rci.c + d.c; - if (wr < 0 || wr >= R || wc < 0 || wc >= C || grid.isDigitAt(wr, wc)) walls++; - } - if (walls >= 3) penalty[0] += 400; - }*/ return penalty[0]; } @@ -789,8 +816,7 @@ public record SwedishGenerator(int[] buff) { if ((now - lastLog.get()) < logEveryMs) return; lastLog.set(now); - var done = assigned.size(); - // if (done!=grid.clueCount())throw new RuntimeException(); + var done = assigned.size(); var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100); var filled = Math.min(BAR_LEN, (int) Math.floor((pct / 100.0) * BAR_LEN)); var bar = "[" + "#".repeat(filled) + "-".repeat(BAR_LEN - filled) + "]"; @@ -811,9 +837,6 @@ public record SwedishGenerator(int[] buff) { int bestSlot = -1; for (var s : slots) { if (assigned.containsKey(s.key())) continue; - /* if (assigned.size()!= grid.clueCount()) - throw new RuntimeException();*/ - var entry = dictIndex[s.len()]; if (entry == null) return PICK_NOT_DONE; patternForSlot(grid, s, ctx.pattern); @@ -826,7 +849,11 @@ public record SwedishGenerator(int[] buff) { || (info.count == bestInfo.count && (slotScore = slotScore(count, s)) > bestSlot)) { best = s; bestSlot = (slotScore != -1) ? slotScore : slotScore(count, s); - bestInfo = info; + if (info.indices != null && (info.indices == ctx.inter1 || info.indices == ctx.inter2)) { + bestInfo = new CandidateInfo(Arrays.copyOf(info.indices, info.count), info.count); + } else { + bestInfo = info; + } if (info.count <= 1) break; } } diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 478db4f..662229c 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -159,13 +159,15 @@ public class SwedishGeneratorTest { var a = new int[]{ 1, 3, 5, 7, 9 }; var b = new int[]{ 2, 3, 6, 7, 10 }; - var res = SwedishGenerator.intersectSorted(buff, a, a.length, b, b.length); - assertArrayEquals(new int[]{ 3, 7 }, res); + var count = SwedishGenerator.intersectSorted(a, a.length, b, b.length, buff); + assertEquals(2, count); + assertEquals(3, buff[0]); + assertEquals(7, buff[1]); var c = new int[]{ 1, 2, 3 }; var d = new int[]{ 4, 5, 6 }; - res = SwedishGenerator.intersectSorted(buff, c, c.length, d, d.length); - assertEquals(0, res.length); + count = SwedishGenerator.intersectSorted(c, c.length, d, d.length, buff); + assertEquals(0, count); } @Test