diff --git a/src/main/java/puzzle/ExportFormat.java b/src/main/java/puzzle/ExportFormat.java index e0f46bc..233adba 100644 --- a/src/main/java/puzzle/ExportFormat.java +++ b/src/main/java/puzzle/ExportFormat.java @@ -1,5 +1,6 @@ package puzzle; +import lombok.experimental.Delegate; import puzzle.Main.PuzzleResult; import puzzle.SwedishGenerator.Grid; import java.util.ArrayList; @@ -24,6 +25,28 @@ import static puzzle.SwedishGenerator.C; */ public record ExportFormat() { + record Gridded(@Delegate Grid grid) { + + String gridToString() { + var sb = new StringBuilder(); + for (var r = 0; r < R; r++) { + if (r > 0) sb.append('\n'); + for (var c = 0; c < C; c++) sb.append((char) grid.byteAt(r, c)); + } + return sb.toString(); + } + public String renderHuman() { + var sb = new StringBuilder(); + for (var r = 0; r < R; r++) { + if (r > 0) sb.append('\n'); + for (var c = 0; c < C; c++) { + sb.append(grid.isDigitAt(r, c) ? ' ' : (char) grid.byteAt(r, c)); + } + } + return sb.toString(); + } + } + record Bit(long[] bits) { public Bit() { this(new long[(SIZE >> 6) + 1]); } @@ -52,7 +75,7 @@ public record ExportFormat() { var g = puz.filled().grid(); var placed = new ArrayList(); var clueMap = puz.filled().clueMap(); - puz.swe().forEachSlot(g, (int key, long packedPos, int len) -> { + puz.swe().forEachSlot(g.grid(), (int key, long packedPos, int len) -> { var word = clueMap.get(key); if (word != null) { var p = extractPlacedFromSlot(Slot.from(key, packedPos, len), word); diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index c856888..5e56c8b 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -21,14 +21,14 @@ import static puzzle.SwedishGenerator.loadWords; public class Main { // ---------------- Top-level generatePuzzle ---------------- - public record PuzzleResult(SwedishGenerator swe, Dict dict, Grid mask, FillResult filled) { } + public record PuzzleResult(SwedishGenerator swe, Dict dict, Gridded mask, FillResult filled) { } final static String OUT_DIR = envOrDefault("OUT_DIR", "/data/puzzle"); final static Path PUZZLE_DIR = Paths.get(OUT_DIR, "puzzles"); static final Path INDEX_FILE = PUZZLE_DIR.resolve("index.json"); static final OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC); static final String CREATED_AT = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")); - static final String FILE_ID = CREATED_AT.replace(":", "-") + "_" + (System.currentTimeMillis() / 1000); + static final String FILE_ID = CREATED_AT.replace(":", "-") + "_" + System.currentTimeMillis() / 1000; static final String FILE_NAME = FILE_ID + ".json"; static final Path OUTPUT_PATH = PUZZLE_DIR.resolve(FILE_NAME); static final String DATE_STRING = now.toLocalDate().toString(); @@ -84,13 +84,13 @@ public class Main { info(String.format(Locale.ROOT, "simplicity : %.2f", res.filled().simplicity())); section("Mask"); - System.out.print(indentLines(res.swe().gridToString(res.mask()), " ")); + System.out.print(indentLines(res.mask().gridToString(), " ")); section("Grid (raw)"); - System.out.print(indentLines(res.swe().gridToString(res.filled().grid()), " ")); + System.out.print(indentLines(res.filled().grid().gridToString(), " ")); section("Grid (human)"); - System.out.print(indentLines(res.swe().renderHuman(res.filled().grid()), " ")); + System.out.print(indentLines(res.filled().grid().renderHuman(), " ")); var exported = exportFormatFromFilled(res, 1, new Rewards(50, 2, 1)); @@ -140,7 +140,7 @@ public class Main { private static String envOrDefault(String key, String def) { var v = System.getenv(key); - return (v == null || v.isBlank()) ? def : v; + return v == null || v.isBlank() ? def : v; } private static void printSettings(Opts o) { @@ -203,7 +203,7 @@ public class Main { var out = new Opts(); for (var i = 0; i < argv.length; i++) { var a = argv[i]; - var v = (i + 1 < argv.length) ? argv[i + 1] : null; + var v = i + 1 < argv.length ? argv[i + 1] : null; if (a.equals("--help") || a.equals("-h")) { usage(); @@ -332,9 +332,9 @@ public class Main { section("Material"); info(String.format(Locale.ROOT, "attempts : %,d", TOTAL_ATTEMPTS.get())); - info(String.format(Locale.ROOT, "successRate : %.1f%%", (TOTAL_ATTEMPTS.get() == 0) ? 0 : (TOTAL_SUCCESS.get() * 100.0 / TOTAL_ATTEMPTS.get()))); + info(String.format(Locale.ROOT, "successRate : %.1f%%", TOTAL_ATTEMPTS.get() == 0 ? 0 : TOTAL_SUCCESS.get() * 100.0 / TOTAL_ATTEMPTS.get())); if (TOTAL_SUCCESS.get() > 0) { - info(String.format(Locale.ROOT, "avgSimplic : %.2f", (TOTAL_SIMPLICITY.get() / 100.0) / TOTAL_SUCCESS.get())); + info(String.format(Locale.ROOT, "avgSimplic : %.2f", TOTAL_SIMPLICITY.get() / 100.0 / TOTAL_SUCCESS.get())); } info(String.format(Locale.ROOT, "dictWords : %,d", dict.wordz().length)); @@ -365,7 +365,7 @@ public class Main { ); if (filled.ok() && (opts.minSimplicity <= 0 || filled.simplicity() >= opts.minSimplicity)) { - return new PuzzleResult(swe, dict, mask, filled); + return new PuzzleResult(swe, dict, new Gridded(mask), filled); } if (opts.verbose && filled.ok()) { diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index c0cfca8..d72c0c9 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -1,8 +1,10 @@ package puzzle; + import lombok.Data; import lombok.Getter; import puzzle.ExportFormat.Bit; import puzzle.ExportFormat.Bit1029; +import puzzle.ExportFormat.Gridded; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -50,7 +52,7 @@ public record SwedishGenerator(int[] buff) { 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 >= 'A' && b <= 'Z'; } + 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)); } public SwedishGenerator() { this(new int[8124]); } @@ -131,28 +133,24 @@ public record SwedishGenerator(int[] buff) { record Grid(byte[] g) { - int digitAt(int r, int c) { return g[offset(r, c)] - 48; } - public static int r(int offset) { return offset & 7; } - public static int c(int offset) { return offset >>> 3; } - static int offset(int r, int c) { return r | (c << 3); } - Grid deepCopyGrid() { return new Grid(g.clone()); } - char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); } - byte byteAt(int r, int c) { return g[offset(r, c)]; } - byte byteAt(int pos) { return g[pos]; } - byte byteAtStatic(int r, int c) { return g[offset(r, c)]; } - void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; } - void setByteAt(int r, int c, byte ch) { g[offset(r, c)] = ch; } - void setDigitAt(int r, int c, int ch) { g[offset(r, c)] = (byte) (_48 + ch); } - void setCharAt(int idx, char ch) { g[idx] = (byte) ch; } - void setByteAt(int idx, byte ch) { g[idx] = ch; } - void clear(int r, int c) { g[offset(r, c)] = 0; } - void clear(int idx) { g[idx] = 0; } - boolean isLetterAt(int r, int c) { return ((g[offset(r, c)] & 64) != 0); } - boolean isDigitAt(int r, int c) { return (g[offset(r, c)] & 48) == 48; } - boolean isDigitAt(int index) { return (g[index] & 48) == 48; } - static boolean isDigit(byte b) { return (b & 48) == 48; } - boolean isLettercell(int r, int c) { return (g[offset(r, c)] & 48) != 48; } - boolean isLetterAt(int index) { return (g[index] & 48) != 48; } + int digitAt(int r, int c) { return g[offset(r, c)] - 48; } + public static int r(int offset) { return offset & 7; } + public static int c(int offset) { return offset >>> 3; } + static int offset(int r, int c) { return r | (c << 3); } + Grid deepCopyGrid() { return new Grid(g.clone()); } + public byte byteAt(int r, int c) { return g[offset(r, c)]; } + public byte byteAt(int pos) { return g[pos]; } + void setCharAt(int r, int c, char ch) { g[offset(r, c)] = (byte) ch; } + void setByteAt(int r, int c, byte ch) { g[offset(r, c)] = ch; } + void setByteAt(int idx, byte ch) { g[idx] = ch; } + void clear(int r, int c) { g[offset(r, c)] = DASH; } + void clear(int idx) { g[idx] = DASH; } + public boolean isLetterAt(int r, int c) { return ((g[offset(r, c)] & 64) != 0); } + boolean isDigitAt(int r, int c) { return (g[offset(r, c)] & 48) == 48; } + boolean isDigitAt(int index) { return (g[index] & 48) == 48; } + static boolean isDigit(byte b) { return (b & 48) == 48; } + boolean isLettercell(int r, int c) { return (g[offset(r, c)] & 48) != 48; } + boolean isLetterAt(int index) { return (g[index] & 48) != 48; } public double similarity(Grid b) { var same = 0; for (int i = 0; i < SIZE; i++) if (g[i] == b.g[i]) same++; @@ -161,26 +159,6 @@ public record SwedishGenerator(int[] buff) { } static Grid makeEmptyGrid() { return new Grid(new byte[SIZE]); } - String gridToString(Grid g) { - var sb = new StringBuilder(); - for (var r = 0; r < R; r++) { - if (r > 0) sb.append('\n'); - for (var c = 0; c < C; c++) sb.append((char) g.byteAt(r, c)); - } - return sb.toString(); - } - - public String renderHuman(Grid g) { - var sb = new StringBuilder(); - for (var r = 0; r < R; r++) { - if (r > 0) sb.append('\n'); - for (var c = 0; c < C; c++) { - sb.append(g.isDigitAt(r, c) ? ' ' : (char) g.byteAt(r, c)); - } - } - return sb.toString(); - } - static final class IntList { int[] a = new int[8]; @@ -717,12 +695,12 @@ public record SwedishGenerator(int[] buff) { record Pick(SwedishGenerator.Slot slot, SwedishGenerator.CandidateInfo info, boolean done) { } public static record FillResult(boolean ok, - Grid grid, + Gridded grid, HashMap clueMap, FillStats stats, double simplicity) { - public FillResult(boolean ok, Grid grid, HashMap assigned, FillStats stats) { + public FillResult(boolean ok, Gridded grid, HashMap assigned, FillStats stats) { double totalSimplicity = 0; if (ok) { for (var w : assigned.values()) totalSimplicity += w.simpel; @@ -957,7 +935,7 @@ public record SwedishGenerator(int[] buff) { } stats.seconds = (System.currentTimeMillis() - t0) / 1000.0; - var res = new FillResult(ok, grid, assigned, stats); + var res = new FillResult(ok, new Gridded(grid), assigned, stats); // print a final progress line if (verbose && !multiThreaded) { diff --git a/src/test/java/puzzle/ExportFormatTest.java b/src/test/java/puzzle/ExportFormatTest.java index ea3055b..fd83078 100644 --- a/src/test/java/puzzle/ExportFormatTest.java +++ b/src/test/java/puzzle/ExportFormatTest.java @@ -2,6 +2,7 @@ package puzzle; import org.junit.jupiter.api.Test; import puzzle.ExportFormat.ExportedPuzzle; +import puzzle.ExportFormat.Gridded; import puzzle.ExportFormat.Rewards; import puzzle.Main.PuzzleResult; import puzzle.SwedishGenerator.FillResult; @@ -13,82 +14,82 @@ import java.util.HashMap; import static org.junit.jupiter.api.Assertions.*; public class ExportFormatTest { - - @Test - void testExportFormatFromFilled() { - var swe = new SwedishGenerator(); - var grid = SwedishGenerator.makeEmptyGrid(); - - // Place a '2' (right) at (0,0) - grid.setCharAt(0, 0, '2'); - // This creates a slot starting at (0,1) - - var clueMap = new HashMap(); - // key = (r << 8) | (c << 4) | d - int key = (0 << 8) | (0 << 4) | 2; - Lemma lemma = new Lemma("TEST", 1, "A test word"); - clueMap.put(key, lemma); - - // Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4) - grid.setCharAt(0, 1, 'T'); - grid.setCharAt(0, 2, 'E'); - grid.setCharAt(0, 3, 'S'); - grid.setCharAt(0, 4, 'T'); - // Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH - grid.setCharAt(0, 5, '1'); - - var fillResult = new FillResult(true, grid, clueMap, null); - var puzzleResult = new PuzzleResult(swe, null, null, fillResult); - - Rewards rewards = new Rewards(10, 5, 1); - ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 2, rewards); - - assertNotNull(exported); - assertEquals(2, exported.difficulty()); - assertEquals(rewards, exported.rewards()); - - // Check words - assertEquals(1, exported.words().length); - var w = exported.words()[0]; - assertEquals("TEST", w.word()); - assertEquals("h", w.direction()); - - // The bounding box should include (0,0) for the arrow and (0,1)-(0,4) for the word. - // minR=0, maxR=0, minC=0, maxC=4 - // startRow = 0 - minR = 0 - // startCol = 1 - minC = 1 - assertEquals(0, w.startRow()); - assertEquals(1, w.startCol()); - assertEquals(0, w.arrowRow()); - assertEquals(0, w.arrowCol()); - - // Check gridv2 - // It should be 1 row, containing "2TEST" -> but letters are mapped, digits are not explicitly in letterAt. - // Wait, look at exportFormatFromFilled logic: - // row.append(letterAt.getOrDefault(pack(r, c), '#')); - // letterAt only contains letters from placed words. - // arrow cells are NOT in letterAt unless they are also part of a word (unlikely). - // So (0,0) should be '#' - assertEquals(1, exported.gridv2().size()); - assertEquals("#TEST", exported.gridv2().get(0)); - } - - @Test - void testExportFormatEmpty() { - var swe = new SwedishGenerator(); - var grid = SwedishGenerator.makeEmptyGrid(); - var fillResult = new FillResult(true, grid, new HashMap<>(), null); - var puzzleResult = new PuzzleResult(swe, null, null, fillResult); - - ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 1, new Rewards(0,0,0)); - - assertNotNull(exported); - assertEquals(0, exported.words().length); - // Should return full grid with '#' - assertEquals(SwedishGenerator.R, exported.gridv2().size()); - for (String row : exported.gridv2()) { - assertEquals(SwedishGenerator.C, row.length()); - assertTrue(row.matches("#+")); - } - } + + @Test + void testExportFormatFromFilled() { + var swe = new SwedishGenerator(); + var grid = SwedishGenerator.makeEmptyGrid(); + + // Place a '2' (right) at (0,0) + grid.setByteAt(0, 0, (byte) '2'); + // This creates a slot starting at (0,1) + + var clueMap = new HashMap(); + // key = (r << 8) | (c << 4) | d + int key = (0 << 8) | (0 << 4) | 2; + Lemma lemma = new Lemma("TEST", 1, "A test word"); + clueMap.put(key, lemma); + + // Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4) + grid.setByteAt(0, 1, (byte) 'T'); + grid.setByteAt(0, 2, (byte) 'E'); + grid.setByteAt(0, 3, (byte) 'S'); + grid.setByteAt(0, 4, (byte) 'T'); + // Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH + grid.setByteAt(0, 5, (byte) '1'); + + var fillResult = new FillResult(true, new Gridded(grid), clueMap, null); + var puzzleResult = new PuzzleResult(swe, null, null, fillResult); + + Rewards rewards = new Rewards(10, 5, 1); + ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 2, rewards); + + assertNotNull(exported); + assertEquals(2, exported.difficulty()); + assertEquals(rewards, exported.rewards()); + + // Check words + assertEquals(1, exported.words().length); + var w = exported.words()[0]; + assertEquals("TEST", w.word()); + assertEquals("h", w.direction()); + + // The bounding box should include (0,0) for the arrow and (0,1)-(0,4) for the word. + // minR=0, maxR=0, minC=0, maxC=4 + // startRow = 0 - minR = 0 + // startCol = 1 - minC = 1 + assertEquals(0, w.startRow()); + assertEquals(1, w.startCol()); + assertEquals(0, w.arrowRow()); + assertEquals(0, w.arrowCol()); + + // Check gridv2 + // It should be 1 row, containing "2TEST" -> but letters are mapped, digits are not explicitly in letterAt. + // Wait, look at exportFormatFromFilled logic: + // row.append(letterAt.getOrDefault(pack(r, c), '#')); + // letterAt only contains letters from placed words. + // arrow cells are NOT in letterAt unless they are also part of a word (unlikely). + // So (0,0) should be '#' + assertEquals(1, exported.gridv2().size()); + assertEquals("#TEST", exported.gridv2().get(0)); + } + + @Test + void testExportFormatEmpty() { + var swe = new SwedishGenerator(); + var grid = SwedishGenerator.makeEmptyGrid(); + var fillResult = new FillResult(true, new Gridded(grid), new HashMap<>(), null); + var puzzleResult = new PuzzleResult(swe, null, null, fillResult); + + ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 1, new Rewards(0, 0, 0)); + + assertNotNull(exported); + assertEquals(0, exported.words().length); + // Should return full grid with '#' + assertEquals(SwedishGenerator.R, exported.gridv2().size()); + for (String row : exported.gridv2()) { + assertEquals(SwedishGenerator.C, row.length()); + assertTrue(row.matches("#+")); + } + } } diff --git a/src/test/java/puzzle/MainTest.java b/src/test/java/puzzle/MainTest.java index 6531ca5..24f3e71 100644 --- a/src/test/java/puzzle/MainTest.java +++ b/src/test/java/puzzle/MainTest.java @@ -94,9 +94,9 @@ public class MainTest { grid.setCharAt(1, 2, '5'); grid.setCharAt(2, 3, 'Z'); - Assertions.assertEquals('A', grid.getCharAt(0, 0)); - Assertions.assertEquals('5', grid.getCharAt(1, 2)); - Assertions.assertEquals('Z', grid.getCharAt(2, 3)); + Assertions.assertEquals((byte) 'A', grid.byteAt(0, 0)); + Assertions.assertEquals((byte) '5', grid.byteAt(1, 2)); + Assertions.assertEquals((byte) 'Z', grid.byteAt(2, 3)); Assertions.assertEquals(DASH, grid.byteAt(1, 1)); // Test isLetterAt @@ -127,11 +127,11 @@ public class MainTest { grid.setCharAt(1, 1, 'D'); var copy = grid.deepCopyGrid(); - Assertions.assertEquals('A', copy.getCharAt(0, 0)); + Assertions.assertEquals((byte) 'A', copy.byteAt(0, 0)); copy.setCharAt(0, 0, 'X'); - Assertions.assertEquals('X', copy.getCharAt(0, 0)); - Assertions.assertEquals('A', grid.getCharAt(0, 0)); // Original should be unchanged + Assertions.assertEquals((byte) 'X', copy.byteAt(0, 0)); + Assertions.assertEquals((byte) 'A', grid.byteAt(0, 0)); // Original should be unchanged } @Test @@ -168,7 +168,7 @@ public class MainTest { System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().simplicity()); System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().clueMap().size()); System.out.println("[DEBUG_LOG] Grid:"); - System.out.println(res.swe().renderHuman(res.filled().grid())); + System.out.println(res.filled().grid().renderHuman()); break; } } @@ -193,8 +193,4 @@ public class MainTest { assertTrue(SwedishGenerator.isLetter((byte) 'Z')); } - @Test - public void testIsNotLetterLowercaseA() { - assertFalse(SwedishGenerator.isLetter((byte) 'a')); - } } diff --git a/src/test/java/puzzle/SwedishGeneratorTest.java b/src/test/java/puzzle/SwedishGeneratorTest.java index 57b2394..4fd3d70 100644 --- a/src/test/java/puzzle/SwedishGeneratorTest.java +++ b/src/test/java/puzzle/SwedishGeneratorTest.java @@ -72,7 +72,7 @@ public class SwedishGeneratorTest { grid.setCharAt(0, 0, 'A'); grid.setCharAt(0, 1, '1'); - assertEquals('A', grid.getCharAt(0, 0)); + assertEquals('A', grid.byteAt(0, 0)); assertEquals(1, grid.digitAt(0, 1)); assertTrue(grid.isLetterAt(0, 0)); assertFalse(grid.isDigitAt(0, 0)); @@ -82,10 +82,10 @@ public class SwedishGeneratorTest { assertFalse(grid.isLettercell(0, 1)); var copy = grid.deepCopyGrid(); - assertEquals('A', copy.getCharAt(0, 0)); + assertEquals('A', copy.byteAt(0, 0)); copy.setCharAt(0, 0, 'B'); - assertEquals('B', copy.getCharAt(0, 0)); - assertEquals('A', grid.getCharAt(0, 0)); + assertEquals('B', copy.byteAt(0, 0)); + assertEquals('A', grid.byteAt(0, 0)); } @Test @@ -266,9 +266,9 @@ public class SwedishGeneratorTest { // 1. Successful placement in empty grid int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0); assertEquals(1, placed); - assertEquals('A', grid.getCharAt(0, 0)); - assertEquals('B', grid.getCharAt(0, 1)); - assertEquals('C', grid.getCharAt(0, 2)); + assertEquals('A', grid.byteAt(0, 0)); + assertEquals('B', grid.byteAt(0, 1)); + assertEquals('C', grid.byteAt(0, 2)); assertEquals(0b111L, undoBuffer[0]); // 2. Successful placement with partial overlap (same characters) @@ -281,9 +281,9 @@ public class SwedishGeneratorTest { placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2); assertEquals(-1, placed); // Verify grid is unchanged (still "ABC") - assertEquals('A', grid.getCharAt(0, 0)); - assertEquals('B', grid.getCharAt(0, 1)); - assertEquals('C', grid.getCharAt(0, 2)); + assertEquals('A', grid.byteAt(0, 0)); + assertEquals('B', grid.byteAt(0, 1)); + assertEquals('C', grid.byteAt(0, 2)); // 4. Partial placement then conflict (rollback) grid = SwedishGenerator.makeEmptyGrid(); @@ -293,7 +293,7 @@ public class SwedishGeneratorTest { // Verify grid is still empty (except for 'X') assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 0)); assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 1)); - assertEquals('X', grid.getCharAt(0, 2)); + assertEquals('X', grid.byteAt(0, 2)); } @Test @@ -307,8 +307,8 @@ public class SwedishGeneratorTest { var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0); assertEquals(1, placed); - assertEquals('A', grid.getCharAt(0, 1)); - assertEquals('Z', grid.getCharAt(0, 2)); + assertEquals('A', grid.byteAt(0, 1)); + assertEquals('Z', grid.byteAt(0, 2)); assertEquals(0b11L, undoBuffer[0]); SwedishGenerator.undoPlace(grid, s, undoBuffer[0]);