Gather data

This commit is contained in:
mike
2026-01-09 03:41:51 +01:00
parent 5bfea6f116
commit 30678b06d9
6 changed files with 157 additions and 159 deletions

View File

@@ -1,5 +1,6 @@
package puzzle; package puzzle;
import lombok.experimental.Delegate;
import puzzle.Main.PuzzleResult; import puzzle.Main.PuzzleResult;
import puzzle.SwedishGenerator.Grid; import puzzle.SwedishGenerator.Grid;
import java.util.ArrayList; import java.util.ArrayList;
@@ -24,6 +25,28 @@ import static puzzle.SwedishGenerator.C;
*/ */
public record ExportFormat() { 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) { record Bit(long[] bits) {
public Bit() { this(new long[(SIZE >> 6) + 1]); } public Bit() { this(new long[(SIZE >> 6) + 1]); }
@@ -52,7 +75,7 @@ public record ExportFormat() {
var g = puz.filled().grid(); var g = puz.filled().grid();
var placed = new ArrayList<Placed>(); var placed = new ArrayList<Placed>();
var clueMap = puz.filled().clueMap(); 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); var word = clueMap.get(key);
if (word != null) { if (word != null) {
var p = extractPlacedFromSlot(Slot.from(key, packedPos, len), word); var p = extractPlacedFromSlot(Slot.from(key, packedPos, len), word);

View File

@@ -21,14 +21,14 @@ import static puzzle.SwedishGenerator.loadWords;
public class Main { public class Main {
// ---------------- Top-level generatePuzzle ---------------- // ---------------- 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 String OUT_DIR = envOrDefault("OUT_DIR", "/data/puzzle");
final static Path PUZZLE_DIR = Paths.get(OUT_DIR, "puzzles"); final static Path PUZZLE_DIR = Paths.get(OUT_DIR, "puzzles");
static final Path INDEX_FILE = PUZZLE_DIR.resolve("index.json"); static final Path INDEX_FILE = PUZZLE_DIR.resolve("index.json");
static final OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC); 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 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 String FILE_NAME = FILE_ID + ".json";
static final Path OUTPUT_PATH = PUZZLE_DIR.resolve(FILE_NAME); static final Path OUTPUT_PATH = PUZZLE_DIR.resolve(FILE_NAME);
static final String DATE_STRING = now.toLocalDate().toString(); 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())); info(String.format(Locale.ROOT, "simplicity : %.2f", res.filled().simplicity()));
section("Mask"); section("Mask");
System.out.print(indentLines(res.swe().gridToString(res.mask()), " ")); System.out.print(indentLines(res.mask().gridToString(), " "));
section("Grid (raw)"); section("Grid (raw)");
System.out.print(indentLines(res.swe().gridToString(res.filled().grid()), " ")); System.out.print(indentLines(res.filled().grid().gridToString(), " "));
section("Grid (human)"); 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)); 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) { private static String envOrDefault(String key, String def) {
var v = System.getenv(key); 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) { private static void printSettings(Opts o) {
@@ -203,7 +203,7 @@ public class Main {
var out = new Opts(); var out = new Opts();
for (var i = 0; i < argv.length; i++) { for (var i = 0; i < argv.length; i++) {
var a = argv[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")) { if (a.equals("--help") || a.equals("-h")) {
usage(); usage();
@@ -332,9 +332,9 @@ public class Main {
section("Material"); section("Material");
info(String.format(Locale.ROOT, "attempts : %,d", TOTAL_ATTEMPTS.get())); 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) { 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)); 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)) { 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()) { if (opts.verbose && filled.ok()) {

View File

@@ -1,8 +1,10 @@
package puzzle; package puzzle;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import puzzle.ExportFormat.Bit; import puzzle.ExportFormat.Bit;
import puzzle.ExportFormat.Bit1029; import puzzle.ExportFormat.Bit1029;
import puzzle.ExportFormat.Gridded;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@@ -50,7 +52,7 @@ public record SwedishGenerator(int[] buff) {
static final char C_DASH = '\0'; static final char C_DASH = '\0';
static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH; static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH;
static final ThreadLocal<Context> CTX = ThreadLocal.withInitial(Context::new); static final ThreadLocal<Context> 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)); } static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
public SwedishGenerator() { this(new int[8124]); } public SwedishGenerator() { this(new int[8124]); }
@@ -136,18 +138,14 @@ public record SwedishGenerator(int[] buff) {
public static int c(int offset) { return offset >>> 3; } public static int c(int offset) { return offset >>> 3; }
static int offset(int r, int c) { return r | (c << 3); } static int offset(int r, int c) { return r | (c << 3); }
Grid deepCopyGrid() { return new Grid(g.clone()); } Grid deepCopyGrid() { return new Grid(g.clone()); }
char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); } public byte byteAt(int r, int c) { return g[offset(r, c)]; }
byte byteAt(int r, int c) { return g[offset(r, c)]; } public byte byteAt(int pos) { return g[pos]; }
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 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 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 setByteAt(int idx, byte ch) { g[idx] = ch; }
void clear(int r, int c) { g[offset(r, c)] = 0; } void clear(int r, int c) { g[offset(r, c)] = DASH; }
void clear(int idx) { g[idx] = 0; } void clear(int idx) { g[idx] = DASH; }
boolean isLetterAt(int r, int c) { return ((g[offset(r, c)] & 64) != 0); } 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 r, int c) { return (g[offset(r, c)] & 48) == 48; }
boolean isDigitAt(int index) { return (g[index] & 48) == 48; } boolean isDigitAt(int index) { return (g[index] & 48) == 48; }
static boolean isDigit(byte b) { return (b & 48) == 48; } static boolean isDigit(byte b) { return (b & 48) == 48; }
@@ -161,26 +159,6 @@ public record SwedishGenerator(int[] buff) {
} }
static Grid makeEmptyGrid() { return new Grid(new byte[SIZE]); } 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 { static final class IntList {
int[] a = new int[8]; int[] a = new int[8];
@@ -717,12 +695,12 @@ public record SwedishGenerator(int[] buff) {
record Pick(SwedishGenerator.Slot slot, SwedishGenerator.CandidateInfo info, boolean done) { } record Pick(SwedishGenerator.Slot slot, SwedishGenerator.CandidateInfo info, boolean done) { }
public static record FillResult(boolean ok, public static record FillResult(boolean ok,
Grid grid, Gridded grid,
HashMap<Integer, SwedishGenerator.Lemma> clueMap, HashMap<Integer, SwedishGenerator.Lemma> clueMap,
FillStats stats, FillStats stats,
double simplicity) { double simplicity) {
public FillResult(boolean ok, Grid grid, HashMap<Integer, Lemma> assigned, FillStats stats) { public FillResult(boolean ok, Gridded grid, HashMap<Integer, Lemma> assigned, FillStats stats) {
double totalSimplicity = 0; double totalSimplicity = 0;
if (ok) { if (ok) {
for (var w : assigned.values()) totalSimplicity += w.simpel; for (var w : assigned.values()) totalSimplicity += w.simpel;
@@ -957,7 +935,7 @@ public record SwedishGenerator(int[] buff) {
} }
stats.seconds = (System.currentTimeMillis() - t0) / 1000.0; 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 // print a final progress line
if (verbose && !multiThreaded) { if (verbose && !multiThreaded) {

View File

@@ -2,6 +2,7 @@ package puzzle;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import puzzle.ExportFormat.ExportedPuzzle; import puzzle.ExportFormat.ExportedPuzzle;
import puzzle.ExportFormat.Gridded;
import puzzle.ExportFormat.Rewards; import puzzle.ExportFormat.Rewards;
import puzzle.Main.PuzzleResult; import puzzle.Main.PuzzleResult;
import puzzle.SwedishGenerator.FillResult; import puzzle.SwedishGenerator.FillResult;
@@ -20,7 +21,7 @@ public class ExportFormatTest {
var grid = SwedishGenerator.makeEmptyGrid(); var grid = SwedishGenerator.makeEmptyGrid();
// Place a '2' (right) at (0,0) // Place a '2' (right) at (0,0)
grid.setCharAt(0, 0, '2'); grid.setByteAt(0, 0, (byte) '2');
// This creates a slot starting at (0,1) // This creates a slot starting at (0,1)
var clueMap = new HashMap<Integer, Lemma>(); var clueMap = new HashMap<Integer, Lemma>();
@@ -30,14 +31,14 @@ public class ExportFormatTest {
clueMap.put(key, lemma); clueMap.put(key, lemma);
// Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4) // Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4)
grid.setCharAt(0, 1, 'T'); grid.setByteAt(0, 1, (byte) 'T');
grid.setCharAt(0, 2, 'E'); grid.setByteAt(0, 2, (byte) 'E');
grid.setCharAt(0, 3, 'S'); grid.setByteAt(0, 3, (byte) 'S');
grid.setCharAt(0, 4, 'T'); grid.setByteAt(0, 4, (byte) 'T');
// Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH // Terminate the slot at (0,5) with another digit to avoid it extending to MAX_WORD_LENGTH
grid.setCharAt(0, 5, '1'); grid.setByteAt(0, 5, (byte) '1');
var fillResult = new FillResult(true, grid, clueMap, null); var fillResult = new FillResult(true, new Gridded(grid), clueMap, null);
var puzzleResult = new PuzzleResult(swe, null, null, fillResult); var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
Rewards rewards = new Rewards(10, 5, 1); Rewards rewards = new Rewards(10, 5, 1);
@@ -77,7 +78,7 @@ public class ExportFormatTest {
void testExportFormatEmpty() { void testExportFormatEmpty() {
var swe = new SwedishGenerator(); var swe = new SwedishGenerator();
var grid = SwedishGenerator.makeEmptyGrid(); var grid = SwedishGenerator.makeEmptyGrid();
var fillResult = new FillResult(true, grid, new HashMap<>(), null); var fillResult = new FillResult(true, new Gridded(grid), new HashMap<>(), null);
var puzzleResult = new PuzzleResult(swe, null, null, fillResult); var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 1, new Rewards(0, 0, 0)); ExportedPuzzle exported = ExportFormat.exportFormatFromFilled(puzzleResult, 1, new Rewards(0, 0, 0));

View File

@@ -94,9 +94,9 @@ public class MainTest {
grid.setCharAt(1, 2, '5'); grid.setCharAt(1, 2, '5');
grid.setCharAt(2, 3, 'Z'); grid.setCharAt(2, 3, 'Z');
Assertions.assertEquals('A', grid.getCharAt(0, 0)); Assertions.assertEquals((byte) 'A', grid.byteAt(0, 0));
Assertions.assertEquals('5', grid.getCharAt(1, 2)); Assertions.assertEquals((byte) '5', grid.byteAt(1, 2));
Assertions.assertEquals('Z', grid.getCharAt(2, 3)); Assertions.assertEquals((byte) 'Z', grid.byteAt(2, 3));
Assertions.assertEquals(DASH, grid.byteAt(1, 1)); Assertions.assertEquals(DASH, grid.byteAt(1, 1));
// Test isLetterAt // Test isLetterAt
@@ -127,11 +127,11 @@ public class MainTest {
grid.setCharAt(1, 1, 'D'); grid.setCharAt(1, 1, 'D');
var copy = grid.deepCopyGrid(); var copy = grid.deepCopyGrid();
Assertions.assertEquals('A', copy.getCharAt(0, 0)); Assertions.assertEquals((byte) 'A', copy.byteAt(0, 0));
copy.setCharAt(0, 0, 'X'); copy.setCharAt(0, 0, 'X');
Assertions.assertEquals('X', copy.getCharAt(0, 0)); Assertions.assertEquals((byte) 'X', copy.byteAt(0, 0));
Assertions.assertEquals('A', grid.getCharAt(0, 0)); // Original should be unchanged Assertions.assertEquals((byte) 'A', grid.byteAt(0, 0)); // Original should be unchanged
} }
@Test @Test
@@ -168,7 +168,7 @@ public class MainTest {
System.out.println("[DEBUG_LOG] Simplicity: " + res.filled().simplicity()); 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] ClueMap Size: " + res.filled().clueMap().size());
System.out.println("[DEBUG_LOG] Grid:"); System.out.println("[DEBUG_LOG] Grid:");
System.out.println(res.swe().renderHuman(res.filled().grid())); System.out.println(res.filled().grid().renderHuman());
break; break;
} }
} }
@@ -193,8 +193,4 @@ public class MainTest {
assertTrue(SwedishGenerator.isLetter((byte) 'Z')); assertTrue(SwedishGenerator.isLetter((byte) 'Z'));
} }
@Test
public void testIsNotLetterLowercaseA() {
assertFalse(SwedishGenerator.isLetter((byte) 'a'));
}
} }

View File

@@ -72,7 +72,7 @@ public class SwedishGeneratorTest {
grid.setCharAt(0, 0, 'A'); grid.setCharAt(0, 0, 'A');
grid.setCharAt(0, 1, '1'); grid.setCharAt(0, 1, '1');
assertEquals('A', grid.getCharAt(0, 0)); assertEquals('A', grid.byteAt(0, 0));
assertEquals(1, grid.digitAt(0, 1)); assertEquals(1, grid.digitAt(0, 1));
assertTrue(grid.isLetterAt(0, 0)); assertTrue(grid.isLetterAt(0, 0));
assertFalse(grid.isDigitAt(0, 0)); assertFalse(grid.isDigitAt(0, 0));
@@ -82,10 +82,10 @@ public class SwedishGeneratorTest {
assertFalse(grid.isLettercell(0, 1)); assertFalse(grid.isLettercell(0, 1));
var copy = grid.deepCopyGrid(); var copy = grid.deepCopyGrid();
assertEquals('A', copy.getCharAt(0, 0)); assertEquals('A', copy.byteAt(0, 0));
copy.setCharAt(0, 0, 'B'); copy.setCharAt(0, 0, 'B');
assertEquals('B', copy.getCharAt(0, 0)); assertEquals('B', copy.byteAt(0, 0));
assertEquals('A', grid.getCharAt(0, 0)); assertEquals('A', grid.byteAt(0, 0));
} }
@Test @Test
@@ -266,9 +266,9 @@ public class SwedishGeneratorTest {
// 1. Successful placement in empty grid // 1. Successful placement in empty grid
int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0); int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0);
assertEquals(1, placed); assertEquals(1, placed);
assertEquals('A', grid.getCharAt(0, 0)); assertEquals('A', grid.byteAt(0, 0));
assertEquals('B', grid.getCharAt(0, 1)); assertEquals('B', grid.byteAt(0, 1));
assertEquals('C', grid.getCharAt(0, 2)); assertEquals('C', grid.byteAt(0, 2));
assertEquals(0b111L, undoBuffer[0]); assertEquals(0b111L, undoBuffer[0]);
// 2. Successful placement with partial overlap (same characters) // 2. Successful placement with partial overlap (same characters)
@@ -281,9 +281,9 @@ public class SwedishGeneratorTest {
placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2); placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 2);
assertEquals(-1, placed); assertEquals(-1, placed);
// Verify grid is unchanged (still "ABC") // Verify grid is unchanged (still "ABC")
assertEquals('A', grid.getCharAt(0, 0)); assertEquals('A', grid.byteAt(0, 0));
assertEquals('B', grid.getCharAt(0, 1)); assertEquals('B', grid.byteAt(0, 1));
assertEquals('C', grid.getCharAt(0, 2)); assertEquals('C', grid.byteAt(0, 2));
// 4. Partial placement then conflict (rollback) // 4. Partial placement then conflict (rollback)
grid = SwedishGenerator.makeEmptyGrid(); grid = SwedishGenerator.makeEmptyGrid();
@@ -293,7 +293,7 @@ public class SwedishGeneratorTest {
// Verify grid is still empty (except for 'X') // Verify grid is still empty (except for 'X')
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 0)); assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 0));
assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 1)); assertEquals(SwedishGenerator.DASH, grid.byteAt(0, 1));
assertEquals('X', grid.getCharAt(0, 2)); assertEquals('X', grid.byteAt(0, 2));
} }
@Test @Test
@@ -307,8 +307,8 @@ public class SwedishGeneratorTest {
var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0); var placed = SwedishGenerator.placeWord(grid, s, w, undoBuffer, 0);
assertEquals(1, placed); assertEquals(1, placed);
assertEquals('A', grid.getCharAt(0, 1)); assertEquals('A', grid.byteAt(0, 1));
assertEquals('Z', grid.getCharAt(0, 2)); assertEquals('Z', grid.byteAt(0, 2));
assertEquals(0b11L, undoBuffer[0]); assertEquals(0b11L, undoBuffer[0]);
SwedishGenerator.undoPlace(grid, s, undoBuffer[0]); SwedishGenerator.undoPlace(grid, s, undoBuffer[0]);