diff --git a/src/main/java/puzzle/ExportFormat.java b/src/main/java/puzzle/ExportFormat.java index 057c123..186617e 100644 --- a/src/main/java/puzzle/ExportFormat.java +++ b/src/main/java/puzzle/ExportFormat.java @@ -51,10 +51,10 @@ public record ExportFormat() { var g = puz.filled().grid(); var placed = new ArrayList(); var clueMap = puz.filled().clueMap(); - puz.swe().forEachSlot(g, (int key, long rs, long cs, int len) -> { + puz.swe().forEachSlot(g, (int key, long packedPos, int len) -> { var word = clueMap.get(key); if (word != null) { - var p = extractPlacedFromSlot(Slot.from(key, rs, cs, len), word); + var p = extractPlacedFromSlot(Slot.from(key, packedPos, len), word); if (p != null) placed.add(p); } }); diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 8f3c5dc..60a2d0f 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -327,25 +327,21 @@ public record SwedishGenerator(int[] buff) { return new CandidateInfo(cur, curLen); } - static record Slot(int key, long rs, long cs, int len) { - //perhaps just put len into key and use hash-code and derrive index from key. or just put both ints into tail of the two longs. - static Slot from(int key, long rs, long cs, int len) { -/* if ((Long.highestOneBit(rs | cs) >> 2) != (len - 1)) throw new RuntimeException(); - if ((Long.highestOneBit(cs) >> 2) != (len - 1)) throw new RuntimeException(); - if ((Long.highestOneBit(rs) >> 2) != (len - 1)) throw new RuntimeException();*/ - return new Slot(key, rs, cs, len); + static record Slot(int key, long packedPos) { + + static Slot from(int key, long packedPos, int len) { + return new Slot(key, packedPos | ((long) len << 56)); } - //public int len() { return (int) (Long.highestOneBit(rs | cs) >> 2); } - public int clueR() { return (key >> 8) & 15; } - public int clueC() { return (key >> 4) & 15; } - public int dir() { return key & 15; } - public boolean horiz() { return horiz(key); } - public int r(int i) { return r(rs, i); } - public int c(int i) { return c(cs, i); } - public static boolean horiz(int key) { return ((key & 15) & 1) == 0; } - public static int r(long rs, int i) { return (int) ((rs >> (i << 2)) & 15); } - public static int c(long cs, int i) { return (int) ((cs >> (i << 2)) & 15); } + public int len() { return (int) (packedPos >>> 56); } + public int clueR() { return (key >> 8) & 15; } + public int clueC() { return (key >> 4) & 15; } + public int dir() { return key & 15; } + public boolean horiz() { return horiz(key); } + public int r(int i) { return Grid.r(offset(packedPos, i)); } + public int c(int i) { return Grid.c(offset(packedPos, i)); } + public static boolean horiz(int key) { return ((key & 15) & 1) == 0; } + public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); } } static void undoPlace(Grid grid, long[] undoBuffer, int offset, int n) { for (var i = 0; i < n; i++) { @@ -356,7 +352,7 @@ public record SwedishGenerator(int[] buff) { @FunctionalInterface interface SlotVisitor { - void visit(int key, long rs, long cs, int len); + void visit(int key, long packedPos, int len); } void forEachSlot(Grid grid, SlotVisitor visitor) { @@ -368,19 +364,17 @@ public record SwedishGenerator(int[] buff) { int rr = r + nbrs16.r, cc = c + nbrs16.c; if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue; - long packedRs = 0; - long packedCs = 0; - var n = 0; + long packedPos = 0; + var n = 0; while (rr >= 0 && rr < R && cc >= 0 && cc < C && grid.isLettercell(rr, cc) && n < MAX_WORD_LENGTH) { - packedRs |= (long) rr << (n << 2); - packedCs |= (long) cc << (n << 2); + packedPos |= (long) Grid.offset(rr, cc) << (n * 7); n++; rr += nbrs16.dr; cc += nbrs16.dc; } if (n > 0) { - visitor.visit((r << 8) | (c << 4) | d, packedRs, packedCs, n); + visitor.visit((r << 8) | (c << 4) | d, packedPos, n); } } } @@ -388,7 +382,7 @@ public record SwedishGenerator(int[] buff) { ArrayList extractSlots(Grid grid) { var slots = new ArrayList(64); - forEachSlot(grid, (key, rs, cs, len) -> slots.add(Slot.from(key, rs, cs, len))); + forEachSlot(grid, (key, packedPos, len) -> slots.add(Slot.from(key, packedPos, len))); return slots; } @@ -428,14 +422,12 @@ public record SwedishGenerator(int[] buff) { int rr = r + nbrs16.r, cc = c + nbrs16.c; if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue; - long packedRs = 0; - long packedCs = 0; - var n = 0; + 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; - packedRs |= (long) rr << (n << 2); - packedCs |= (long) cc << (n << 2); + packedPos |= (long) Grid.offset(rr, cc) << (n * 7); n++; rr += nbrs16.dr; cc += nbrs16.dc; @@ -450,7 +442,7 @@ public record SwedishGenerator(int[] buff) { } var horiz = Slot.horiz(d) ? covH : covV; - for (var i = 0; i < n; i++) horiz[Grid.offset(Slot.r(packedRs, i), Slot.c(packedCs, i))] += 1; + for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1; } } @@ -730,12 +722,11 @@ public record SwedishGenerator(int[] buff) { } } - static int patternForSlot(Grid grid, Slot s, char[] pat) { + static void patternForSlot(Grid grid, Slot s, char[] pat) { for (var i = 0; i < s.len(); i++) { var ch = grid.getCharAt(s.r(i), s.c(i)); pat[i] = isLetter(ch) ? ch : C_DASH; } - return s.len(); } static int slotScore(int[] cellCount, Slot s, Grid grid) { @@ -821,9 +812,9 @@ public record SwedishGenerator(int[] buff) { if (entry == null) { return new Pick(null, null, false); } - - var patLen = patternForSlot(grid, s, ctx.pattern); - var info = candidateInfoForPattern(ctx, entry, patLen); + var patLen = s.len(); + patternForSlot(grid, s, ctx.pattern); + var info = candidateInfoForPattern(ctx, entry, patLen); if (info.count == 0) { return new Pick(null, null, false); @@ -863,11 +854,12 @@ public record SwedishGenerator(int[] buff) { stats.lastMRV = pick.info.count; renderProgress.run(); - var s = pick.slot; - var k = s.key(); - var entry = dictIndex[s.len()]; - var pat = new char[s.len()]; - int patLen = patternForSlot(grid, s, pat); + var s = pick.slot; + var k = s.key(); + int patLen = s.len(); + var entry = dictIndex[patLen]; + var pat = new char[patLen]; + patternForSlot(grid, s, pat); int undoOffset = depth * SIZE; if (pick.info.indices != null && pick.info.indices.length > 0) { var idxs = pick.info.indices; diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 681ec9f..1777b94 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -47,11 +47,11 @@ public class MainTest { @Test void testStaticSlotMethods() { - // Test static r and c extraction - // packedRs: 1 at index 0, 2 at index 1 - var packedRs = (1L << 0) | (2L << 4); - assertEquals(1, Slot.r(packedRs, 0)); - assertEquals(2, Slot.r(packedRs, 1)); + // Test static offset extraction + // packedPos: offset(1, 1) at index 0, offset(2, 2) at index 1 + var packedPos = ((long) SwedishGenerator.Grid.offset(1, 1)) | (((long) SwedishGenerator.Grid.offset(2, 2)) << 7); + assertEquals(SwedishGenerator.Grid.offset(1, 1), Slot.offset(packedPos, 0)); + assertEquals(SwedishGenerator.Grid.offset(2, 2), Slot.offset(packedPos, 1)); // Test static horiz // dir 2 (right) is horizontal @@ -67,21 +67,21 @@ public class MainTest { grid.setCharAt(0, 0, '2'); // right var count = new AtomicInteger(0); - generator.forEachSlot(grid, (key, rs, cs, len) -> { + generator.forEachSlot(grid, (key, packedPos, len) -> { count.incrementAndGet(); assertEquals(8, len); - assertEquals(0, Slot.r(rs, 0)); - assertEquals(1, Slot.c(cs, 0)); + assertEquals(0, SwedishGenerator.Grid.r(Slot.offset(packedPos, 0))); + assertEquals(1, SwedishGenerator.Grid.c(Slot.offset(packedPos, 0))); }); assertEquals(1, count.get()); } @Test public void testHoriz() { - assertTrue(new Slot(2, 0L, 0L, 1).horiz()); - assertTrue(new Slot(4, 0L, 0L, 1).horiz()); - assertFalse(new Slot(1, 0L, 3L, 1).horiz()); - assertFalse(new Slot(3, 0L, 3L, 1).horiz()); - assertFalse(new Slot(5, 0L, 3L, 1).horiz()); + assertTrue(Slot.from(2, 0L, 1).horiz()); + assertTrue(Slot.from(4, 0L, 1).horiz()); + assertFalse(Slot.from(1, 0L, 1).horiz()); + assertFalse(Slot.from(3, 0L, 1).horiz()); + assertFalse(Slot.from(5, 0L, 1).horiz()); } @Test public void testGridBasics() { diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index ab74d55..adf93d3 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -12,6 +12,45 @@ import static org.junit.jupiter.api.Assertions.*; public class SwedishGeneratorTest { + @Test + void testPatternForSlotAllLetters() { + var grid = new Grid(new byte[]{ 65, 66, 67 }); // A B C + var slot = Slot.from(0 << 8 | 1 << 4 | 2, ((long) 0) | ((long) 1 << 7) | ((long) 2 << 14), 3); + var pattern = new char[3]; + SwedishGenerator.patternForSlot(grid, slot, pattern); + + assertArrayEquals(new char[]{ 'A', 'B', 'C' }, pattern); + } + + @Test + void testPatternForSlotMixed() { + var grid = new Grid(new byte[]{ 65, (byte) ('0' + 1), 67 }); // A - C + var slot = Slot.from(0 << 8 | 1 << 4 | 2, ((long) 0) | ((long) 1 << 7) | ((long) 2 << 14), 3); + var pattern = new char[3]; + SwedishGenerator.patternForSlot(grid, slot, pattern); + + assertArrayEquals(new char[]{ 'A', SwedishGenerator.C_DASH, 'C' }, pattern); + } + + @Test + void testPatternForSlotAllDashes() { + var grid = new Grid(new byte[]{ (byte) ('0' + 1), (byte) ('0' + 1), (byte) ('0' + 1) }); // - - - + var slot = Slot.from(0 << 8 | 1 << 4 | 2, ((long) 0) | ((long) 1 << 7) | ((long) 2 << 14), 3); + var pattern = new char[3]; + SwedishGenerator.patternForSlot(grid, slot, pattern); + + assertArrayEquals(new char[]{ SwedishGenerator.C_DASH, SwedishGenerator.C_DASH, SwedishGenerator.C_DASH }, pattern); + } + + @Test + void testPatternForSlotSingleLetter() { + var grid = new Grid(new byte[]{ 65, (byte) ('0' + 1), (byte) ('0' + 1) }); // A - - + var slot = Slot.from(0 << 8 | 1 << 4 | 2, ((long) 0) | ((long) 1 << 7) | ((long) 2 << 14), 3); + var pattern = new char[3]; + SwedishGenerator.patternForSlot(grid, slot, pattern); + + assertArrayEquals(new char[]{ 'A', SwedishGenerator.C_DASH, SwedishGenerator.C_DASH }, pattern); + } @Test void testRng() { var rng = new Rng(123); @@ -92,19 +131,16 @@ public class SwedishGeneratorTest { @Test void testSlot() { // key = (r << 8) | (c << 4) | d - var key = (2 << 8) | (3 << 4) | 5; - long rs = 0; - long cs = 0; - // rs: [2, 3, 4] -> packed 4-bit: 2 | (3<<4) | (4<<8) - rs |= 2L; - rs |= 3L << 4; - rs |= 4L << 8; - // cs: [5, 5, 5] - cs |= 5L; - cs |= 5L << 4; - cs |= 5L << 8; + var key = (2 << 8) | (3 << 4) | 5; + long packedPos = 0; + // pos 0: (2, 5) + packedPos |= (long) Grid.offset(2, 5) << 0; + // pos 1: (3, 5) + packedPos |= (long) Grid.offset(3, 5) << 7; + // pos 2: (4, 5) + packedPos |= (long) Grid.offset(4, 5) << 14; - var s = new Slot(key, rs, cs, 3); + var s = Slot.from(key, packedPos, 3); assertEquals(2, s.clueR()); assertEquals(3, s.clueC()); assertEquals(5, s.dir()); @@ -223,7 +259,8 @@ public class SwedishGeneratorTest { void testBacktrackingHelpers() { var grid = SwedishGenerator.makeEmptyGrid(); // Slot at 0,1 length 2 - var s = new Slot((0 << 8) | (1 << 4) | 2, 0L, (1L | (2L << 4)), 2); + var packedPos = ((long) Grid.offset(0, 1)) | (((long) Grid.offset(0, 2)) << 7); + var s = Slot.from((0 << 8) | (1 << 4) | 2, packedPos, 2); var w = new Lemma("AZ", 1, "A to Z"); var undoBuffer = new long[10];