diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index 9e482a0..d0b57fc 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -3,6 +3,7 @@ package puzzle; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.Delegate; +import puzzle.Export.Gridded.Replacar.Cell; import puzzle.SwedishGenerator.Dict; import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.Grid; @@ -98,17 +99,26 @@ public record Export() { return sb.toString(); } public String renderHuman() { - return String.join("\n", exportGrid(' ', '#')); + return String.join("\n", exportGrid(_ -> ' ', '#')); + } - public String[] exportGrid(char clueChar, char emptyFallback) { + public boolean notClue(int c) { return grid.notClue(c); } + @FunctionalInterface + interface Replacar { + + record Cell(Grid grid, int index, byte data) { } + char replace(Cell c); + } + public String[] exportGrid(Replacar clueChar, char emptyFallback) { var out = new String[R]; for (var r = 0; r < R; r++) { var sb = new StringBuilder(C); for (var c = 0; c < C; c++) { - if (grid.isClue(Grid.offset(r, c))) { - sb.append(clueChar); + var offset = Grid.offset(r, c); + if (grid.isClue(offset)) { + sb.append(clueChar.replace(new Cell(grid, offset, grid.byteAt(offset)))); } else { - sb.append(NOT_CLUE_NOT_LETTER_TO(grid.byteAt(Grid.offset(r, c)), emptyFallback)); + sb.append(NOT_CLUE_NOT_LETTER_TO(grid.byteAt(offset), emptyFallback)); } } out[r] = sb.toString(); @@ -149,10 +159,15 @@ public record Export() { public void clear() { Arrays.fill(bits, 0L); } } - record Placed(long lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) { + record Placed(long lemma, char direction, int slotKey, int[] cells) { public static final char HORIZONTAL = 'h'; static final char VERTICAL = 'v'; + public int arrowCol() { return Grid.c(Slot.clueIndex(slotKey)); } + public int arrowRow() { return Grid.r(Slot.clueIndex(slotKey)); } + public int startRow() { return Grid.r(cells[0]); } + public int startCol() { return Grid.c(cells[0]); } + public boolean isReversed() { return !Slot.increasing(slotKey); } } public record Rewards(int coins, int stars, int hints) { } @@ -168,36 +183,19 @@ 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; } + boolean inBounds(int idx) { return idx >= 0 && idx < SwedishGenerator.SIZE; } Placed extractPlacedFromSlot(Slot s, long lemma) { var d = s.dir(); var cells = s.walk().toArray(); - char direction; - var startRow = Grid.r(cells[0]); - var startCol = Grid.c(cells[0]); - if (d == 2) { // right -> horizontal - direction = Placed.HORIZONTAL; - } else if (d == 3 || d == 5) { // down or down-bent -> vertical - direction = Placed.VERTICAL; - } else if (d == 4) { // left -> horizontal (REVERSED) - direction = Placed.HORIZONTAL; - } else if (d == 1) { // up -> vertical (REVERSED) - direction = Placed.VERTICAL; - } else { - return null; - } + char[] DIRECTION = { '\0', Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL }; return new Placed( lemma, - startRow, - startCol, - direction, - s.clueR(), - s.clueC(), - cells, - !s.increasing() + DIRECTION[d], + s.key(), + cells ); } public ExportedPuzzle exportFormatFromFilled(int difficulty, Rewards rewards) { @@ -207,14 +205,15 @@ public record Export() { g.grid().forEachSlot((int key, long lo, long hi) -> { var word = clueMap.get(key); if (word != null) { - var p = extractPlacedFromSlot(Slot.from(key, lo, hi), word); - if (p != null) placed.add(p); + placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi), word)); + } else { + System.err.println("Could not find clue for slot: " + key); } }); // If nothing placed: return full grid mapped to letters/# only if (placed.isEmpty()) { - return new ExportedPuzzle(g.exportGrid('#', '#'), new WordOut[0], difficulty, rewards); + return new ExportedPuzzle(g.exportGrid(_ -> '#', '#'), new WordOut[0], difficulty, rewards); } // 2) bounding box around all word cells + arrow cells, with 1-cell margin @@ -229,20 +228,18 @@ public record Export() { maxR = Math.max(maxR, Grid.r(c)); maxC = Math.max(maxC, Grid.c(c)); } - minR = Math.min(minR, rc.arrowRow); - minC = Math.min(minC, rc.arrowCol); - maxR = Math.max(maxR, rc.arrowRow); - maxC = Math.max(maxC, rc.arrowCol); + minR = Math.min(minR, rc.arrowRow()); + minC = Math.min(minC, rc.arrowCol()); + maxR = Math.max(maxR, rc.arrowRow()); + maxC = Math.max(maxC, rc.arrowCol()); } // 3) map of only used letter cells (everything else becomes '#') - var letterAt = new HashMap(); + var letterAt = new HashMap(); for (var p : placed) { for (var c : p.cells) { - int rr = Grid.r(c), cc = Grid.c(c); - int idx = Grid.offset(rr, cc); - if (inBounds(rr, cc) && g.isLetterSet(idx)) { - letterAt.put(Bit.pack(rr, cc), (char) g.byteAt(idx)); + if (inBounds(c) && g.notClue(c)) { + letterAt.put(c, (char) g.byteAt(c)); } } } @@ -251,7 +248,7 @@ public record Export() { var gridv2 = new String[Math.max(0, maxR - minR + 1)]; for (int r = minR, i = 0; r <= maxR; r++, i++) { var row = new StringBuilder(Math.max(0, maxC - minC + 1)); - for (var c = minC; c <= maxC; c++) row.append(letterAt.getOrDefault(Bit.pack(r, c), '#')); + for (var c = minC; c <= maxC; c++) row.append(letterAt.getOrDefault(Grid.offset(r, c), '#')); gridv2[i] = row.toString(); } @@ -259,12 +256,12 @@ public record Export() { int MIN_R = minR, MIN_C = minC; var wordsOut = placed.stream().map(p -> new WordOut( p.lemma, - p.startRow - MIN_R, - p.startCol - MIN_C, + p.startRow() - MIN_R, + p.startCol() - MIN_C, p.direction, - p.arrowRow - MIN_R, - p.arrowCol - MIN_C, - p.isReversed + p.arrowRow() - MIN_R, + p.arrowCol() - MIN_C, + p.isReversed() )).toArray(WordOut[]::new); return new ExportedPuzzle(gridv2, wordsOut, difficulty, rewards); } diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 4ca927f..0e6acb8 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -119,7 +119,7 @@ public record SwedishGenerator(Rng rng) { final int[] stack = new int[SIZE]; final Bit seen = new Bit(); long pattern; - final long[] undo = new long[4096]; + final long[] undo = new long[128]; final long[] bitset = new long[2500]; void setPattern(long p) { this.pattern = p; } @@ -207,9 +207,6 @@ public record SwedishGenerator(Rng rng) { return true; } - static boolean isLetter(byte b) { return (b & B64) != B0; } - public boolean isLetterSet(int idx) { return isLetter(g[idx]); } - public double similarity(Grid b) { var same = 0; for (int i = 0; i < SIZE; i++) if (g[i] == b.g[i]) same++; @@ -318,7 +315,8 @@ public record SwedishGenerator(Rng rng) { 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 clueIndex() { return clueIndex(key); } + public static int clueIndex(int key) { 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); } diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index bda7d51..e0e4e4f 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -4,6 +4,7 @@ import lombok.val; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import puzzle.Export.PuzzleResult; +import puzzle.Export.Rewards; import puzzle.SwedishGenerator.Rng; import puzzle.SwedishGenerator.Slot; import java.util.concurrent.atomic.AtomicInteger; @@ -81,10 +82,10 @@ public class MainTest { Assertions.assertEquals(DASH, grid.byteAt(Grid.offset(1, 1))); // Test isLetterAt - Assertions.assertTrue(grid.isLetterSet(Grid.offset(0, 0))); - Assertions.assertFalse(grid.isLetterSet(Grid.offset(1, 2))); - Assertions.assertTrue(grid.isLetterSet(Grid.offset(2, 3))); - Assertions.assertFalse(grid.isLetterSet(Grid.offset(1, 1))); + Assertions.assertTrue(grid.notClue(Grid.offset(0, 0))); + Assertions.assertFalse(grid.notClue(Grid.offset(1, 2))); + Assertions.assertTrue(grid.notClue(Grid.offset(2, 3))); + Assertions.assertFalse(grid.isClue(Grid.offset(1, 1))); // Test isDigitAt Assertions.assertFalse(grid.isClue(0)); @@ -150,6 +151,7 @@ public class MainTest { System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().clueMap().size()); System.out.println("[DEBUG_LOG] Grid:"); System.out.println(res.filled().grid().renderHuman()); + var aa = res.exportFormatFromFilled(1, new Rewards(1, 1, 1)); break; } }