diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 257d096..30f5283 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -81,7 +81,7 @@ public record Export() { public void clear() { Arrays.fill(bits, 0L); } } - record Placed(Lemma lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) { + record Placed(long lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) { public static final char HORIZONTAL = 'h'; static final char VERTICAL = 'v'; @@ -91,8 +91,8 @@ public record Export() { public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) { - public WordOut(Lemma l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) { - this(l.asWord(), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, l.simpel(), l.clue()); + public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) { + this(Lemma.asWord(l), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed, Lemma.simpel(l), Lemma.clue(l)); } } @@ -101,7 +101,7 @@ public record Export() { public record PuzzleResult(SwedishGenerator swe, Dict dict, Gridded mask, FillResult filled) { boolean inBounds(int r, int c) { return r >= 0 && r < SwedishGenerator.R && c >= 0 && c < SwedishGenerator.C; } - Placed extractPlacedFromSlot(Slot s, Lemma lemma) { + Placed extractPlacedFromSlot(Slot s, long lemma) { var d = s.dir(); var cells = new int[s.len()]; diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 9591b27..cfb9927 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -108,12 +108,12 @@ public record SwedishGenerator(Rng rng) { public static record FillResult(boolean ok, Gridded grid, - Map clueMap, + Map clueMap, FillStats stats) { public void calcSimpel() { if (ok) { - clueMap.forEach((k, v) -> stats.simplicity += v.simpel()); + clueMap.forEach((k, v) -> stats.simplicity += Lemma.simpel(v)); stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size(); } } @@ -243,7 +243,7 @@ public record SwedishGenerator(Rng rng) { } } - static record DictEntry(Lemma[] words, long[][] posBitsets) { } + static record DictEntry(long[] words, long[][] posBitsets) { } public static record Lemma(long word) { @@ -261,13 +261,15 @@ public record SwedishGenerator(Rng rng) { } public Lemma(int index, String word) { this(pack(index, word.getBytes(US_ASCII))); } public Lemma(String word) { this(LEMMA_COUNTER++, word); } - byte byteAt(int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; } + byte byteAt(int idx) { return Lemma.byteAt(word, idx); }// word[]; } + static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; } int intAt(int idx) { return (int) (((word >>> (idx * 5))) & 0b11111); }// word[]; } @Override public int hashCode() { return unpackIndex(word); } @Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.word == word); } - String[] clue() { return CsvIndexService.clues(unpackIndex(word)); } - int simpel() { return CsvIndexService.simpel(unpackIndex(word)); } - int length() { + static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); } + static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); } + int length() { return Lemma.length(word); } + static int length(long word) { if (word == 0) return 0; int highestBit = 63 - Long.numberOfLeadingZeros(word & LETTER_MASK); return (highestBit / 5) + 1; @@ -277,9 +279,9 @@ public record SwedishGenerator(Rng rng) { int highestBit = 63 - Long.numberOfLeadingZeros(p & LETTER_MASK); // 0-based return (highestBit / 5) + 1; } - public String asWord() { - var b = new byte[length()]; - for (var i = 0; i < length(); i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64); + public static String asWord(long word) { + var b = new byte[Lemma.length(word)]; + for (var i = 0; i < b.length; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64); return new String(b, US_ASCII); } static int unpackIndex(long w) { @@ -312,7 +314,7 @@ public record SwedishGenerator(Rng rng) { } for (int i = MIN_LEN; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i); this(Arrays.stream(index).map(i -> { - var words = i.words().toArray(Lemma[]::new); + var words = i.words().stream().mapToLong(ww -> ww.word).toArray(); int numWords = words.length; int numLongs = (numWords + 63) >>> 6; var bitsets = new long[i.pos().length * 26][numLongs]; @@ -677,13 +679,13 @@ public record SwedishGenerator(Rng rng) { for (int i = 0; i < len; i++) cross += (count[s.pos(i)] - 1); return cross * 10 + len; } - static boolean placeWord(Grid grid, Slot s, Lemma w, int[] undoBuffer, int offset) { + static boolean placeWord(Grid grid, Slot s, long w, int[] undoBuffer, int offset) { int mask = 0; byte cur, ch; for (int i = 0, leng = s.len(), idx; i < leng; i++) { idx = s.pos(i); cur = grid.byteAt(idx); - ch = w.byteAt(i); + ch = Lemma.byteAt(w, i); if (cur == DASH) { mask |= (1 << i); grid.setByteAt(idx, ch); @@ -778,9 +780,9 @@ public record SwedishGenerator(Rng rng) { val used = new Bit1029(); // val assigned = new HashMap(); - Lemma[] assigned = new Lemma[BIGG]; - val ctx = CTX.get(); - val count = ctx.cellCount; + long[] assigned = new long[BIGG]; + val ctx = CTX.get(); + val count = ctx.cellCount; Arrays.fill(count, 0, SIZE, 0); val slots = extractSlots(grid); @@ -803,7 +805,7 @@ public record SwedishGenerator(Rng rng) { lastLog = (now); var done = 0; for (var lemma : assigned) { - if (lemma != null) done++; + if (lemma != X) done++; } 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)); @@ -824,7 +826,7 @@ public record SwedishGenerator(Rng rng) { int bestScore = -1; for (int i = 0, n = slots.size(); i < n; i++) { var s = slots.get(i); - if (assigned[s.key()] != null) continue; + if (assigned[s.key()] != X) continue; var entry = dictIndex[s.len()]; if (entry == null) return PICK_NOT_DONE; ctx.pattern = patternForSlot(grid, s); @@ -880,7 +882,7 @@ public record SwedishGenerator(Rng rng) { int idxInArray = (int) (r * r * r * L); var idx = idxs[idxInArray]; var w = entry.words[idx]; - var lemIdx = Lemma.unpackIndex(w.word); + var lemIdx = Lemma.unpackIndex(w); if (used.get(lemIdx)) continue; if (!placeWord(grid, s, w, ctx.undo, depth)) continue; @@ -890,7 +892,7 @@ public record SwedishGenerator(Rng rng) { if (backtrack(depth + 1)) return true; - assigned[k] = null; + assigned[k] = X; used.clear(lemIdx); s.undoPlace(grid, ctx.undo[depth]); } @@ -909,7 +911,7 @@ public record SwedishGenerator(Rng rng) { double r = rng.nextFloat(); int idxInArray = (int) (r * r * r * N); var w = entry.words[idxInArray]; - var lemIdx = Lemma.unpackIndex(w.word); + var lemIdx = Lemma.unpackIndex(w); if (used.get(lemIdx)) continue; if (!placeWord(grid, s, w, ctx.undo, depth)) continue; @@ -919,7 +921,7 @@ public record SwedishGenerator(Rng rng) { if (backtrack(depth + 1)) return true; - assigned[k] = null; + assigned[k] = X; used.clear(lemIdx); s.undoPlace(grid, ctx.undo[depth]); } @@ -940,9 +942,9 @@ public record SwedishGenerator(Rng rng) { } stats.seconds = (System.currentTimeMillis() - t0) / 1000.0; - Map lemmaMap = new HashMap<>(); + Map lemmaMap = new HashMap<>(); for (var i = 0; i < assigned.length; i++) { - if (assigned[i] != null) { + if (assigned[i] != X) { lemmaMap.put(i, assigned[i]); } } diff --git a/src/test/java/puzzle/ExportFormatTest.java b/src/test/java/puzzle/ExportFormatTest.java index 66b76db..88bbf37 100644 --- a/src/test/java/puzzle/ExportFormatTest.java +++ b/src/test/java/puzzle/ExportFormatTest.java @@ -28,11 +28,11 @@ public class ExportFormatTest { grid.setClue(0, (byte) '2'); // This creates a slot starting at (0,1) - var clueMap = new HashMap(); + var clueMap = new HashMap(); // key = (cellIndex << 4) | direction var key = (0 << 4) | 2; var lemma = new Lemma("TEST"); - clueMap.put(key, lemma); + clueMap.put(key, lemma.word()); // Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4) grid.setByteAt(Grid.offset(0, 1), (byte) 'T'); diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 6ab9229..6050fc2 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -167,7 +167,7 @@ public class MainTest { // Regression baseline for seed search starting at 12347, pop 4, gens 20 Assertions.assertEquals(12348, foundSeed, "Found seed changed"); Assertions.assertEquals(18, res.filled().clueMap().size(), "Number of assigned words changed"); - Assertions.assertEquals("RIJTUIG", res.filled().clueMap().get(74).asWord()); + Assertions.assertEquals("RIJTUIG", Lemma.asWord( res.filled().clueMap().get(74))); Assertions.assertEquals(301794542151533187L, res.filled().grid().grid().lo); Assertions.assertEquals(193L, res.filled().grid().grid().hi); } diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index c38b84a..134d9f5 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -108,7 +108,7 @@ public class SwedishGeneratorTest { var l1 = new Lemma("APPLE"); Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(l1.word())); - assertEquals(5, l1.length()); + assertEquals(5, Lemma.length(l1.word())); assertEquals((byte) 'A', l1.byteAt(0)); assertEquals(1, l1.intAt(0)); @@ -120,7 +120,7 @@ public class SwedishGeneratorTest { var entry3 = dict.index()[3]; assertEquals(1, entry3.words().length); - assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(entry3.words()[0].word())); + assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(entry3.words()[0])); // Check pos indexing // AXE: A at 0, X at 1, E at 2 @@ -276,7 +276,7 @@ public class SwedishGeneratorTest { // r(i) and c(i) are used by placeWord. var packedPos = ((long) Grid.offset(0, 0)) | (((long) Grid.offset(0, 1)) << 7) | (((long) Grid.offset(0, 2)) << 14); var s = Slot.from(0, packedPos, 3); - var w1 = new Lemma("ABC"); + var w1 = new Lemma("ABC").word(); var undoBuffer = new int[10]; // 1. Successful placement in empty grid @@ -291,7 +291,7 @@ public class SwedishGeneratorTest { assertEquals(0L, undoBuffer[1]); // 0 new characters placed // 3. Conflict: place "ABD" where "ABC" is - var w2 = new Lemma("ABD"); + var w2 = new Lemma("ABD").word(); assertFalse(placeWord(grid, s, w2, undoBuffer, 2)); // Verify grid is unchanged (still "ABC") assertEquals('A', grid.byteAt(Grid.offset(0, 0))); @@ -314,7 +314,7 @@ public class SwedishGeneratorTest { // Slot at 0,1 length 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"); + var w = new Lemma("AZ").word(); var undoBuffer = new int[10]; var placed = placeWord(grid, s, w, undoBuffer, 0);