diff --git a/src/main/java/puzzle/Export.java b/src/main/java/puzzle/Export.java index ee34fa6..179e411 100644 --- a/src/main/java/puzzle/Export.java +++ b/src/main/java/puzzle/Export.java @@ -3,12 +3,14 @@ package puzzle; import lombok.Getter; import lombok.experimental.Accessors; import lombok.experimental.Delegate; +import puzzle.Export.Gridded; import puzzle.SwedishGenerator.Dict; import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.Grid; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.StringJoiner; import static puzzle.SwedishGenerator.R; import static puzzle.SwedishGenerator.Lemma; import static puzzle.SwedishGenerator.Slot; @@ -35,6 +37,9 @@ public record Export() { record Gridded(@Delegate Grid grid) { + static boolean isLetter(byte b) { return (b & SwedishGenerator.B64) != SwedishGenerator.B0; } + //public boolean isLetterSet(int idx) { return isLetter(g[idx]); } + char NOT_CLUE_NOT_LETTER_TO(byte b, char fallback) { return isLetter(b) ? (char) b : fallback; } String gridToString() { var sb = new StringBuilder(); for (var r = 0; r < R; r++) { @@ -44,14 +49,22 @@ public record Export() { return sb.toString(); } public String renderHuman() { - var sb = new StringBuilder(); + return String.join("\n", exportGrid(' ', '#')); + } + public String[] exportGrid(char clueChar, char emptyFallback) { + var out = new String[R]; for (var r = 0; r < R; r++) { - if (r > 0) sb.append('\n'); + var sb = new StringBuilder(C); for (var c = 0; c < C; c++) { - sb.append(grid.isLetterSet(Grid.offset(r, c)) ? (char) grid.byteAt(Grid.offset(r, c)) : ' '); + if (grid.isClue(Grid.offset(r, c))) { + sb.append(clueChar); + } else { + sb.append(NOT_CLUE_NOT_LETTER_TO(grid.byteAt(Grid.offset(r, c)), emptyFallback)); + } } + out[r] = sb.toString(); } - return sb.toString(); + return out; } } @@ -114,16 +127,16 @@ public record Export() { for (int i = 0, len = s.len(); i < len; i++) cells[i] = s.pos(i); char direction; - var startRow = Grid.r(cells[0]); - var startCol = Grid.c(cells[0]); + 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; + direction = Placed.HORIZONTAL; } else if (d == 1) { // up -> vertical (REVERSED) - direction = Placed.VERTICAL; + direction = Placed.VERTICAL; } else { return null; } @@ -153,16 +166,7 @@ public record Export() { // If nothing placed: return full grid mapped to letters/# only if (placed.isEmpty()) { - var gridv2 = new String[R]; - for (var r = 0; r < R; r++) { - var sb = new StringBuilder(C); - for (var c = 0; c < C; c++) { - int idx = Grid.offset(r, c); - sb.append(g.isLetterSet(idx) ? (char) g.byteAt(idx) : '#'); - } - gridv2[r] = sb.toString(); - } - return new ExportedPuzzle(gridv2, 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 diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 799d841..74b1ca7 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -58,7 +58,7 @@ public record SwedishGenerator(Rng rng) { static final char C_DASH = '\0'; static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH; static final ThreadLocal CTX = ThreadLocal.withInitial(Context::new); - 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)); } record Pick(Slot slot, CandidateInfo info, boolean done) { } @@ -108,6 +108,7 @@ public record SwedishGenerator(Rng rng) { stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size(); } } + } static final class Context { @@ -185,7 +186,6 @@ public record SwedishGenerator(Rng rng) { if ((idx & 64) == 0) lo &= ~(1L << idx); else hi &= ~(1L << (idx & 63)); } - static boolean isDigit(byte b) { return (b & B48) == B48; } boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; } boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; } boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; } @@ -537,7 +537,7 @@ public record SwedishGenerator(Rng rng) { for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) { idx = rng.randint(0, SIZE_MIN_1); if (g.isClue(idx)) continue; - int d_idx = rng.randint2bit(); + int d_idx = rng.randint2bit(); if (g.hasRoomForClue(OFFSETS_D_IDX[d_idx | idx << 2])) { g.setClue(idx, (byte) (1 + (d_idx | 48))); placed++; @@ -572,7 +572,7 @@ public record SwedishGenerator(Rng rng) { byte ch = b.g[i]; if (out.g[i] != ch) { out.g[i] = ch; - if (Grid.isDigit(ch)) { + if (b.isClue(i)) { if ((i & 64) == 0) bo0 |= (1L << i); else bo1 |= (1L << (i & 63)); } else { diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 7b20618..20b42e4 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -164,6 +164,7 @@ public class MainTest { Assertions.assertEquals(301794542151533187L, res.filled().grid().grid().lo); Assertions.assertEquals(193L, res.filled().grid().grid().hi); } + boolean isLetter(byte b) { return (b & 64) != 0; } @Test public void testIsLetterA() { assertTrue(isLetter((byte) 'A'));