diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index ac34f82..da0ac60 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -107,9 +107,7 @@ public record Export() { }).limit(Long.bitCount(lo))); } } - //public boolean isLetterSet(int idx) { return isLetter(g[idx]); } - char NOT_CLUE_NOT_LETTER_TO(byte b, char fallback) { - if (b == SwedishGenerator.DASH) throw new RuntimeException(); + char NOT_CLUE_NOT_LETTER_TO(byte b) { return (char) (64 | b); } String gridToString(Clues clues) { @@ -146,7 +144,7 @@ public record Export() { if (clues.isClue(offset)) { sb.append(clueChar.replace(new Cell(grid, clues, offset, clues.digitAt(offset)))); } else if (grid.lisLetterAt(offset)) { - sb.append(NOT_CLUE_NOT_LETTER_TO(grid.letter32At(offset), emptyFallback)); + sb.append(NOT_CLUE_NOT_LETTER_TO(grid.letter32At(offset))); } else { sb.append(emptyFallback); } diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 870c88e..bfa5356 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -441,7 +441,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { } visitor.visit(key, rayLo, rayHi); - //if ((rayLo | rayHi) == 0L) throw new RuntimeException() } static Slot[] extractSlots(Clues grid) { @@ -733,37 +732,38 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { return best.grid; } - static long patternForSlot(Grid grid, Slot s) { - if ((s.lo & grid.lo) == 0 && (s.hi & grid.hi) == 0) { + static long patternForSlot(Grid grid, final int key, final long lo, final long hi) { + final long glo = grid.lo, ghi = grid.hi; + if ((lo & glo) == 0 && (hi & ghi) == 0) { return 0; } long p = 0; - if (s.increasing()) { - for (long b = s.lo & grid.lo; b != 0; b &= b - 1) { + if (Slot.increasing(key)) { + for (long b = lo & glo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); byte val = grid.g[idx]; - int i = Long.bitCount(s.lo & ((1L << idx) - 1)); + int i = Long.bitCount(lo & ((1L << idx) - 1)); p |= ((long) (i * 26 + val)) << (i << 3); } - int offset = Long.bitCount(s.lo); - for (long b = s.hi & grid.hi; b != 0; b &= b - 1) { + int offset = Long.bitCount(lo); + for (long b = hi & ghi; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); byte val = grid.g[64 | idx]; - int i = offset + Long.bitCount(s.hi & ((1L << idx) - 1)); + int i = offset + Long.bitCount(hi & ((1L << idx) - 1)); p |= ((long) (i * 26 + val)) << (i << 3); } } else { - int offset = Long.bitCount(s.hi); - for (long b = s.hi & grid.hi; b != 0; b &= b - 1) { + int offset = Long.bitCount(hi); + for (long b = hi & ghi; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); byte val = grid.g[64 | idx]; - int i = Long.bitCount(s.hi & ~((1L << idx) | ((1L << idx) - 1))); + int i = Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))); p |= ((long) (i * 26 + val)) << (i << 3); } - for (long b = s.lo & grid.lo; b != 0; b &= b - 1) { + for (long b = lo & glo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); byte val = grid.g[idx]; - int i = offset + Long.bitCount(s.lo & ~((1L << idx) | ((1L << idx) - 1))); + int i = offset + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))); p |= ((long) (i * 26 + val)) << (i << 3); } } @@ -775,51 +775,52 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (long b = s.hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1); return cross * 10 + s.length(); } - static boolean placeWord(Grid grid, Slot s, long w) { - if (s.increasing()) { - for (long b = s.lo & grid.lo; b != 0; b &= b - 1) { + static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) { + final long glo = grid.lo, ghi = grid.hi; + if (Slot.increasing(key)) { + for (long b = lo & glo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[idx] != Lemma.byteAt(w, Long.bitCount(s.lo & ((1L << idx) - 1)))) return false; + if (grid.g[idx] != Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1)))) return false; } - int bcLo = Long.bitCount(s.lo); - for (long b = s.hi & grid.hi; b != 0; b &= b - 1) { + int bcLo = Long.bitCount(lo); + for (long b = hi & ghi; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[64 | idx] != Lemma.byteAt(w, bcLo + Long.bitCount(s.hi & ((1L << idx) - 1)))) return false; + if (grid.g[64 | idx] != Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1)))) return false; } - long maskLo = s.lo & ~grid.lo, maskHi = s.hi & ~grid.hi; + long maskLo = lo & ~glo, maskHi = hi & ~ghi; if ((maskLo | maskHi) != 0) { for (long b = maskLo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[idx] = Lemma.byteAt(w, Long.bitCount(s.lo & ((1L << idx) - 1))); + grid.g[idx] = Lemma.byteAt(w, Long.bitCount(lo & ((1L << idx) - 1))); } for (long b = maskHi; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[64 | idx] = Lemma.byteAt(w, bcLo + Long.bitCount(s.hi & ((1L << idx) - 1))); + grid.g[64 | idx] = Lemma.byteAt(w, bcLo + Long.bitCount(hi & ((1L << idx) - 1))); } grid.lo |= maskLo; grid.hi |= maskHi; } } else { - int bcHi = Long.bitCount(s.hi); - for (long b = s.hi & grid.hi; b != 0; b &= b - 1) { + int bcHi = Long.bitCount(hi); + for (long b = hi & ghi; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[64 | idx] != Lemma.byteAt(w, Long.bitCount(s.hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; + if (grid.g[64 | idx] != Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false; } - for (long b = s.lo & grid.lo; b != 0; b &= b - 1) { + for (long b = lo & glo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - if (grid.g[idx] != Lemma.byteAt(w, bcHi + Long.bitCount(s.lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; + if (grid.g[idx] != Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false; } - long maskLo = s.lo & ~grid.lo, maskHi = s.hi & ~grid.hi; + long maskLo = lo & ~glo, maskHi = hi & ~ghi; if ((maskLo | maskHi) != 0) { for (long b = maskHi; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[64 | idx] = Lemma.byteAt(w, Long.bitCount(s.hi & ~((1L << idx) | ((1L << idx) - 1)))); + grid.g[64 | idx] = Lemma.byteAt(w, Long.bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)))); } for (long b = maskLo; b != 0; b &= b - 1) { int idx = Long.numberOfTrailingZeros(b); - grid.g[idx] = Lemma.byteAt(w, bcHi + Long.bitCount(s.lo & ~((1L << idx) | ((1L << idx) - 1)))); + grid.g[idx] = Lemma.byteAt(w, bcHi + Long.bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))); } grid.lo |= maskLo; grid.hi |= maskHi; @@ -946,7 +947,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) { var s = slots[i]; if (assigned[s.key] != X) continue; - var pattern = patternForSlot(grid, s); + var pattern = patternForSlot(grid, s.key, s.lo, s.hi); var index = dictIndex[s.length()]; count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); @@ -968,7 +969,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { current = PICK_DONE; return; } - var pattern = patternForSlot(grid, best); + var pattern = patternForSlot(grid, best.key, best.lo, best.hi); var index = dictIndex[best.length()]; current = CARRIER; current.slot = best; @@ -1015,7 +1016,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { if (used.get(lemIdx)) continue; val low = grid.lo; val top = grid.hi; - if (!placeWord(grid, s, w)) continue; + if (!placeWord(grid, k, s.lo, s.hi, w)) continue; used.set(lemIdx); assigned[k] = w; @@ -1024,7 +1025,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { assigned[k] = X; used.clear(lemIdx); - //grid.undoPlace(undo[depth << 1], undo[(depth << 1) | 1]); grid.lo = low; grid.hi = top; } @@ -1043,7 +1043,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { if (used.get(lemIdx)) continue; val low = grid.lo; val top = grid.hi; - if (!placeWord(grid, s, w)) continue; + if (!placeWord(grid, k, s.lo, s.hi, w)) continue; used.set(lemIdx); assigned[k] = w; @@ -1054,7 +1054,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { used.clear(lemIdx); grid.lo = low; grid.hi = top; - //grid.undoPlace(undo[depth << 1], undo[(depth << 1) | 1]); } backtracks++; diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 61da43e..a0436f4 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -82,7 +82,7 @@ public class SwedishGeneratorTest { grid.setLetterLo(1, LETTER_B); grid.setLetterLo(2, LETTER_C); var slot = Slot.from(18 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); - var pattern = patternForSlot(grid, slot); + var pattern = patternForSlot(grid, slot.key(), slot.lo(), slot.hi()); assertEquals(1L | (28L << 8) | (55L << 16), pattern); } @@ -93,7 +93,7 @@ public class SwedishGeneratorTest { grid.setLetterLo(OFF_0_0, LETTER_A); grid.setLetterLo(2, LETTER_C); var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); - var pattern = patternForSlot(grid, slot); + var pattern = patternForSlot(grid, slot.key(), slot.lo(), slot.hi()); assertEquals(1L | (0L << 8) | (55L << 16), pattern); } @@ -102,7 +102,7 @@ public class SwedishGeneratorTest { void testPatternForSlotAllDashes() { var grid = createEmpty(); var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); - var pattern = patternForSlot(grid, slot); + var pattern = patternForSlot(grid, slot.key(), slot.lo(), slot.hi()); assertEquals(0L, pattern); } @@ -112,7 +112,7 @@ public class SwedishGeneratorTest { var grid = createEmpty(); grid.setLetterLo(OFF_0_0, LETTER_A); var slot = Slot.from(1 << Slot.BIT_FOR_DIR | (CLUE_RIGHT), 7L, 0L); - var pattern = patternForSlot(grid, slot); + var pattern = patternForSlot(grid, slot.key(), slot.lo(), slot.hi()); assertEquals(1L, pattern); } @@ -330,23 +330,23 @@ public class SwedishGeneratorTest { void testPlaceWord() { var grid = createEmpty(); // Slot at OFF_0_0 length 3, horizontal (right) - var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT); - var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2); - var s = Slot.from(key, lo, 0L); - var w1 = ABC; + var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT); + var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2); + var s = Slot.from(key, lo, 0L); + var w1 = ABC; // 1. Successful placement in empty grid - assertTrue(placeWord(grid, s, w1)); + assertTrue(placeWord(grid, s.key(), s.lo(), s.hi(), w1)); assertEquals(LETTER_A, grid.letter32At(OFF_0_0)); assertEquals(LETTER_B, grid.letter32At(OFF_0_1)); assertEquals(LETTER_C, grid.letter32At(OFF_0_2)); // 2. Successful placement with partial overlap (same characters) - assertTrue(placeWord(grid, s, w1)); + assertTrue(placeWord(grid, s.key(), s.lo(), s.hi(), w1)); // 3. Conflict: place "ABD" where "ABC" is var w2 = ABD; - assertFalse(placeWord(grid, s, w2)); + assertFalse(placeWord(grid, s.key(), s.lo(), s.hi(), w2)); // Verify grid is unchanged (still "ABC") assertEquals(LETTER_A, grid.letter32At(OFF_0_0)); assertEquals(LETTER_B, grid.letter32At(OFF_0_1)); @@ -355,7 +355,7 @@ public class SwedishGeneratorTest { // 4. Partial placement then conflict (rollback) grid = createEmpty(); grid.setLetterLo(OFF_0_2, LETTER_X); // Conflict at the end - assertFalse(placeWord(grid, s, w1)); + assertFalse(placeWord(grid, s.key(), s.lo(), s.hi(), w1)); // Verify grid is still empty (except for 'X') assertFalse(grid.lisLetterAtLo(OFF_0_0)); assertFalse(grid.lisLetterAtLo(OFF_0_1)); @@ -366,13 +366,13 @@ public class SwedishGeneratorTest { void testBacktrackingHelpers() { var grid = createEmpty(); // Slot at 0,1 length 2 - var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT); - var lo = (1L << OFF_0_1) | (1L << OFF_0_2); - var s = Slot.from(key, lo, 0L); - var w = AZ; - val low = grid.lo; - val top = grid.hi; - var placed = placeWord(grid, s, w); + var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT); + var lo = (1L << OFF_0_1) | (1L << OFF_0_2); + var s = Slot.from(key, lo, 0L); + var w = AZ; + val low = grid.lo; + val top = grid.hi; + var placed = placeWord(grid, s.key(), s.lo(), s.hi(), w); assertTrue(placed); assertEquals(LETTER_A, grid.letter32At(OFF_0_1)); assertEquals(LETTER_Z, grid.letter32At(OFF_0_2)); @@ -392,10 +392,10 @@ public class SwedishGeneratorTest { assertTrue(Slot.increasing(CLUE_DOWN)); // Down assertFalse(Slot.increasing(CLUE_UP)); // Up - var sInc = Slot.from((0 << Slot.BIT_FOR_DIR) | CLUE_RIGHT, 1L, 0L); - assertTrue(sInc.increasing()); - var sDec = Slot.from((0 << Slot.BIT_FOR_DIR) | CLUE_LEFT, 1L, 0L); - assertFalse(sDec.increasing()); + var sInc = Slot.from((0) | CLUE_RIGHT, 1L, 0L); + assertTrue(Slot.increasing(sInc.key())); + var sDec = Slot.from((0) | CLUE_LEFT, 1L, 0L); + assertFalse(Slot.increasing(sDec.key())); // 2. Test slotScore val counts = new byte[SIZE];