Gather data

This commit is contained in:
mike
2026-01-08 20:47:42 +01:00
parent dbdd8ecfdb
commit e9978e0f4c
2 changed files with 110 additions and 93 deletions

View File

@@ -2,13 +2,15 @@ package puzzle;
import puzzle.Main.PuzzleResult; import puzzle.Main.PuzzleResult;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import static puzzle.SwedishGenerator.H; import static puzzle.SwedishGenerator.R;
import static puzzle.SwedishGenerator.Lemma; import static puzzle.SwedishGenerator.Lemma;
import static puzzle.SwedishGenerator.SIZE;
import static puzzle.SwedishGenerator.Slot; import static puzzle.SwedishGenerator.Slot;
import static puzzle.SwedishGenerator.W; import static puzzle.SwedishGenerator.C;
/** /**
* ExportFormat.java * ExportFormat.java
@@ -19,19 +21,34 @@ import static puzzle.SwedishGenerator.W;
* - crops to bounding box (words + arrow cells) with 1-cell margin * - crops to bounding box (words + arrow cells) with 1-cell margin
* - outputs gridv2 + words[] (+ difficulty, rewards) * - outputs gridv2 + words[] (+ difficulty, rewards)
*/ */
public final class ExportFormat { public record ExportFormat() {
record Bit(long[] bits) {
public Bit() { this(new long[(SIZE >> 6) + 1]); }
static int wordIndex(int bitIndex) { return bitIndex >> 6; }
public boolean get(int bitIndex) { return (this.bits[wordIndex(bitIndex)] & 1L << bitIndex) != 0L; }
public void set(int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; }
public void clear(int bitIndex) { this.bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); }
public void clear() { Arrays.fill(bits, 0L); }
}
record Bit1029(long[] bits) {
public Bit1029() { this(new long[1029]); }
static int wordIndex(int bitIndex) { return bitIndex >> 6; }
public boolean get(int bitIndex) { return (this.bits[wordIndex(bitIndex)] & 1L << bitIndex) != 0L; }
public void set(int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; }
public void clear(int bitIndex) { this.bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); }
public void clear() { Arrays.fill(bits, 0L); }
}
private ExportFormat() { }
static final String HORIZONTAL = "h", VERTICAL = "v"; static final String HORIZONTAL = "h", VERTICAL = "v";
private static boolean inBounds(int H, int W, int r, int c) { return r >= 0 && r < H && c >= 0 && c < W; } private static boolean inBounds(int r, int c) { return r >= 0 && r < SwedishGenerator.R && c >= 0 && c < SwedishGenerator.C; }
// ---------- Public API ----------
public static ExportedPuzzle exportFormatFromFilled(PuzzleResult puz, int difficulty, Rewards rewards) { public static ExportedPuzzle exportFormatFromFilled(PuzzleResult puz, int difficulty, Rewards rewards) {
Objects.requireNonNull(puz, "puz"); Objects.requireNonNull(puz, "puz");
var g = puz.filled().grid(); var g = puz.filled().grid();
// 1) extract "placed" list from all clue digits in the 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 rs, long cs, int len) -> { puz.swe().forEachSlot(g, (int key, long rs, long cs, int len) -> {
@@ -44,10 +61,10 @@ public final class ExportFormat {
// If nothing placed: return full grid mapped to letters/# only // If nothing placed: return full grid mapped to letters/# only
if (placed.isEmpty()) { if (placed.isEmpty()) {
var gridv2 = new ArrayList<String>(H); var gridv2 = new ArrayList<String>(R);
for (var r = 0; r < H; r++) { for (var r = 0; r < R; r++) {
var sb = new StringBuilder(W); var sb = new StringBuilder(C);
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
sb.append(g.isLetterAt(r, c) ? g.getCharAt(r, c) : '#'); sb.append(g.isLetterAt(r, c) ? g.getCharAt(r, c) : '#');
} }
gridv2.add(sb.toString()); gridv2.add(sb.toString());
@@ -78,7 +95,7 @@ public final class ExportFormat {
for (var p : placed) { for (var p : placed) {
for (var rc : p.cells) { for (var rc : p.cells) {
int rr = rc[0], cc = rc[1]; int rr = rc[0], cc = rc[1];
if (inBounds(H, W, rr, cc) && g.isLetterAt(rr, cc)) { if (inBounds(rr, cc) && g.isLetterAt(rr, cc)) {
letterAt.put(pack(rr, cc), g.getCharAt(rr, cc)); letterAt.put(pack(rr, cc), g.getCharAt(rr, cc));
} }
} }
@@ -181,4 +198,5 @@ public final class ExportFormat {
} }
public record ExportedPuzzle(List<String> gridv2, WordOut[] words, int difficulty, Rewards rewards) { } public record ExportedPuzzle(List<String> gridv2, WordOut[] words, int difficulty, Rewards rewards) { }
} }

View File

@@ -2,13 +2,14 @@ package puzzle;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import puzzle.ExportFormat.Bit;
import puzzle.ExportFormat.Bit1029;
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;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@@ -27,16 +28,17 @@ public record SwedishGenerator(int[] buff) {
record CandidateInfo(int[] indices, int count) { } record CandidateInfo(int[] indices, int count) { }
record nbrs_8(int x, int y) { } record nbrs_8(int r, int c) { }
record nbrs_16(int x, int y, int dx, int dy) { } record nbrs_16(int r, int c, int dr, int dc) { }
static final int W = Config.PUZZLE_COLS; static final int C = Config.PUZZLE_COLS;
static final double CROSS_Y = (W - 1) / 2.0; static final double CROSS_Y = (C - 1) / 2.0;
static final int H = Config.PUZZLE_ROWS; static final int R = Config.PUZZLE_ROWS;
static final double CROSS_X = (H - 1) / 2.0; static final double CROSS_X = (R - 1) / 2.0;
static final int SIZE = W * H; static final int SIZE = C * R;// ~18
static final int MAX_WORD_LENGTH = Math.min(W, H); static final int TARGET_CLUES = SIZE >> 2;
static final int MAX_WORD_LENGTH = Math.min(C, R);
static final int MIN_LEN = Config.MIN_LEN; static final int MIN_LEN = Config.MIN_LEN;
static final int CLUE_SIZE = Config.CLUE_SIZE; static final int CLUE_SIZE = Config.CLUE_SIZE;
static final int SIMPLICITY_DEFAULT_SCORE = 2; static final int SIMPLICITY_DEFAULT_SCORE = 2;
@@ -52,18 +54,12 @@ public record SwedishGenerator(int[] buff) {
// Directions for '1'..'6' // Directions for '1'..'6'
static final nbrs_16[] OFFSETS = new nbrs_16[]{ static final nbrs_16[] OFFSETS = new nbrs_16[]{
null, null,
// 1: up new nbrs_16(-1, 0, -1, 0), // 1: up
new nbrs_16(-1, 0, -1, 0), new nbrs_16(0, 1, 0, 1), // 2: right
// 2: right new nbrs_16(1, 0, 1, 0),// 3: down
new nbrs_16(0, 1, 0, 1), new nbrs_16(0, -1, 0, -1),// 4: left
// 3: down new nbrs_16(0, -1, 1, 0),// 5: vertical down, clue is on the right of the first letter
new nbrs_16(1, 0, 1, 0), new nbrs_16(0, 1, 1, 0)// 6: vertical down, clue is on the left of the first letter
// 4: left
new nbrs_16(0, -1, 0, -1),
// 5: vertical down, clue is on the right of the first letter
new nbrs_16(0, -1, 1, 0),
// 6: vertical down, clue is on the left of the first letter
new nbrs_16(0, 1, 1, 0)
}; };
final static nbrs_8[] nbrs8 = new nbrs_8[]{ final static nbrs_8[] nbrs8 = new nbrs_8[]{
new nbrs_8(-1, -1), new nbrs_8(-1, -1),
@@ -86,13 +82,13 @@ public record SwedishGenerator(int[] buff) {
int[] covV, int[] covV,
int[] cellCount, int[] cellCount,
int[] stack, int[] stack,
BitSet seen, Bit seen,
char[] pattern, char[] pattern,
IntList[] intListBuffer, IntList[] intListBuffer,
long[] undoBuffer) { long[] undoBuffer) {
public Context() { public Context() {
this(new int[SIZE], new int[SIZE], new int[SIZE], new int[SIZE], new BitSet(128), new char[MAX_WORD_LENGTH], new IntList[MAX_WORD_LENGTH], this(new int[SIZE], new int[SIZE], new int[SIZE], new int[SIZE], new Bit(), new char[MAX_WORD_LENGTH], new IntList[MAX_WORD_LENGTH],
new long[2048]); new long[2048]);
} }
void setPatter(char[] chars) { System.arraycopy(chars, 0, this.pattern, 0, chars.length); } void setPatter(char[] chars) { System.arraycopy(chars, 0, this.pattern, 0, chars.length); }
@@ -114,7 +110,7 @@ public record SwedishGenerator(int[] buff) {
x = y; x = y;
return y; return y;
} }
int randint(int min, int max) { // inclusive int randint(int min, int max) {
var u = (nextU32() & 0xFFFFFFFFL); var u = (nextU32() & 0xFFFFFFFFL);
var range = (long) max - (long) min + 1L; var range = (long) max - (long) min + 1L;
return (int) (min + (u % range)); return (int) (min + (u % range));
@@ -124,8 +120,10 @@ public record SwedishGenerator(int[] buff) {
record Grid(byte[] g) { record Grid(byte[] g) {
public static int r(int offset) { return offset & 7; }
public static int c(int offset) { return offset >> 3; }
Grid deepCopyGrid() { return new Grid(g.clone()); } Grid deepCopyGrid() { return new Grid(g.clone()); }
private int offset(int r, int c) { return r | (c << 3); } static int offset(int r, int c) { return r | (c << 3); }
boolean isLettercell(int r, int c) { return (g[offset(r, c)] & 48) != 48; } boolean isLettercell(int r, int c) { return (g[offset(r, c)] & 48) != 48; }
char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); } char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); }
int digitAt(int r, int c) { return g[offset(r, c)] - 48; } int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
@@ -133,6 +131,7 @@ public record SwedishGenerator(int[] buff) {
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; }
boolean isLetterAt(int r, int c) { return ((g[offset(r, c)] & 64) != 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 r, int c) { return (g[offset(r, c)] & 48) == 48; }
boolean isDigitAt(int index) { return (g[index] & 48) == 48; }
public double similarity(Grid b) { public double similarity(Grid b) {
var same = 0; var same = 0;
for (int i = 0; i < SIZE; i++) if (g[i] == b.g[i]) same++; for (int i = 0; i < SIZE; i++) if (g[i] == b.g[i]) same++;
@@ -143,18 +142,18 @@ public record SwedishGenerator(int[] buff) {
String gridToString(Grid g) { String gridToString(Grid g) {
var sb = new StringBuilder(); var sb = new StringBuilder();
for (var r = 0; r < H; r++) { for (var r = 0; r < R; r++) {
if (r > 0) sb.append('\n'); if (r > 0) sb.append('\n');
for (var c = 0; c < W; c++) sb.append(g.getCharAt(r, c)); for (var c = 0; c < C; c++) sb.append(g.getCharAt(r, c));
} }
return sb.toString(); return sb.toString();
} }
public String renderHuman(Grid g) { public String renderHuman(Grid g) {
var sb = new StringBuilder(); var sb = new StringBuilder();
for (var r = 0; r < H; r++) { for (var r = 0; r < R; r++) {
if (r > 0) sb.append('\n'); if (r > 0) sb.append('\n');
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
sb.append(g.isDigitAt(r, c) ? ' ' : g.getCharAt(r, c)); sb.append(g.isDigitAt(r, c) ? ' ' : g.getCharAt(r, c));
} }
} }
@@ -352,24 +351,24 @@ public record SwedishGenerator(int[] buff) {
} }
void forEachSlot(Grid grid, SlotVisitor visitor) { void forEachSlot(Grid grid, SlotVisitor visitor) {
for (var r = 0; r < H; r++) { for (var r = 0; r < R; r++) {
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
if (!grid.isDigitAt(r, c)) continue; if (!grid.isDigitAt(r, c)) continue;
var d = grid.digitAt(r, c); var d = grid.digitAt(r, c);
var nbrs16 = OFFSETS[d]; var nbrs16 = OFFSETS[d];
int rr = r + nbrs16.x, cc = c + nbrs16.y; int rr = r + nbrs16.r, cc = c + nbrs16.c;
if (rr < 0 || rr >= H || cc < 0 || cc >= W || grid.isDigitAt(rr, cc)) continue; if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue;
long packedRs = 0; long packedRs = 0;
long packedCs = 0; long packedCs = 0;
var n = 0; var n = 0;
while (rr >= 0 && rr < H && cc >= 0 && cc < W && grid.isLettercell(rr, cc) && n < MAX_WORD_LENGTH) { while (rr >= 0 && rr < R && cc >= 0 && cc < C && grid.isLettercell(rr, cc) && n < MAX_WORD_LENGTH) {
packedRs |= (long) rr << (n << 2); packedRs |= (long) rr << (n << 2);
packedCs |= (long) cc << (n << 2); packedCs |= (long) cc << (n << 2);
n++; n++;
rr += nbrs16.dx; rr += nbrs16.dr;
cc += nbrs16.dy; cc += nbrs16.dc;
} }
if (n > 0) { if (n > 0) {
visitor.visit((r << 8) | (c << 4) | d, packedRs, packedCs, n); visitor.visit((r << 8) | (c << 4) | d, packedRs, packedCs, n);
@@ -386,12 +385,12 @@ public record SwedishGenerator(int[] buff) {
boolean hasRoomForClue(Grid grid, int r, int c, char d) { boolean hasRoomForClue(Grid grid, int r, int c, char d) {
var nbrs16 = OFFSETS[d - '0']; var nbrs16 = OFFSETS[d - '0'];
int rr = r + nbrs16.x, cc = c + nbrs16.y; int rr = r + nbrs16.r, cc = c + nbrs16.c;
var run = 0; var run = 0;
while (rr >= 0 && rr < H && cc >= 0 && cc < W && (grid.isLettercell(rr, cc)) && run < MAX_WORD_LENGTH) { while (rr >= 0 && rr < R && cc >= 0 && cc < C && (grid.isLettercell(rr, cc)) && run < MAX_WORD_LENGTH) {
run++; run++;
rr += nbrs16.dx; rr += nbrs16.dr;
cc += nbrs16.dy; cc += nbrs16.dc;
if (run >= MIN_LEN) return true; if (run >= MIN_LEN) return true;
} }
return false; return false;
@@ -401,10 +400,9 @@ public record SwedishGenerator(int[] buff) {
long penalty = 0; long penalty = 0;
var clueCount = 0; var clueCount = 0;
for (var r = 0; r < H; r++) for (var c = 0; c < W; c++) if (grid.isDigitAt(r, c)) clueCount++; for (var r = 0; r < R; r++) for (var c = 0; c < C; c++) if (grid.isDigitAt(r, c)) clueCount++;
var targetClues = (int) Math.round(SIZE * 0.25); // ~18 penalty += 8L * Math.abs(clueCount - TARGET_CLUES);
penalty += 8L * Math.abs(clueCount - targetClues);
var ctx = CTX.get(); var ctx = CTX.get();
var covH = ctx.covH; var covH = ctx.covH;
@@ -413,25 +411,25 @@ public record SwedishGenerator(int[] buff) {
Arrays.fill(covV, 0, SIZE, 0); Arrays.fill(covV, 0, SIZE, 0);
boolean hasSlots = false; boolean hasSlots = false;
for (var r = 0; r < H; r++) { for (var r = 0; r < R; r++) {
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
if (!grid.isDigitAt(r, c)) continue; if (!grid.isDigitAt(r, c)) continue;
var d = grid.digitAt(r, c); var d = grid.digitAt(r, c);
var nbrs16 = OFFSETS[d]; var nbrs16 = OFFSETS[d];
int rr = r + nbrs16.x, cc = c + nbrs16.y; int rr = r + nbrs16.r, cc = c + nbrs16.c;
if (rr < 0 || rr >= H || cc < 0 || cc >= W || grid.isDigitAt(rr, cc)) continue; if (rr < 0 || rr >= R || cc < 0 || cc >= C || grid.isDigitAt(rr, cc)) continue;
long packedRs = 0; long packedRs = 0;
long packedCs = 0; long packedCs = 0;
var n = 0; var n = 0;
while (rr >= 0 && rr < H && cc >= 0 && cc < W && n < MAX_WORD_LENGTH) { while (rr >= 0 && rr < R && cc >= 0 && cc < C && n < MAX_WORD_LENGTH) {
if (grid.isDigitAt(rr, cc)) break; if (grid.isDigitAt(rr, cc)) break;
packedRs |= (long) rr << (n << 2); packedRs |= (long) rr << (n << 2);
packedCs |= (long) cc << (n << 2); packedCs |= (long) cc << (n << 2);
n++; n++;
rr += nbrs16.dx; rr += nbrs16.dr;
cc += nbrs16.dy; cc += nbrs16.dc;
} }
if (n == 0) continue; if (n == 0) continue;
hasSlots = true; hasSlots = true;
@@ -443,16 +441,16 @@ public record SwedishGenerator(int[] buff) {
} }
var horiz = Slot.horiz(d) ? covH : covV; var horiz = Slot.horiz(d) ? covH : covV;
for (var i = 0; i < n; i++) horiz[grid.offset(Slot.r(packedRs, i), Slot.c(packedCs, i))] += 1; for (var i = 0; i < n; i++) horiz[Grid.offset(Slot.r(packedRs, i), Slot.c(packedCs, i))] += 1;
} }
} }
if (!hasSlots) return 1_000_000_000L; if (!hasSlots) return 1_000_000_000L;
for (var r = 0; r < H; r++) for (var r = 0; r < R; r++)
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
if (grid.isDigitAt(r, c)) continue; if (grid.isDigitAt(r, c)) continue;
int idx = grid.offset(r, c); int idx = Grid.offset(r, c);
int h = covH[idx], v = covV[idx]; int h = covH[idx], v = covV[idx];
if (h == 0 && v == 0) penalty += 1500; if (h == 0 && v == 0) penalty += 1500;
else if (h > 0 && v > 0) { /* ok */ } else if (h + v == 1) penalty += 200; else if (h > 0 && v > 0) { /* ok */ } else if (h + v == 1) penalty += 200;
@@ -465,8 +463,8 @@ public record SwedishGenerator(int[] buff) {
var stack = ctx.stack; var stack = ctx.stack;
int sp, idx; int sp, idx;
for (var r = 0; r < H; r++) for (var r = 0; r < R; r++)
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
idx = grid.offset(r, c); idx = grid.offset(r, c);
if (!grid.isDigitAt(r, c) || seen.get(idx)) continue; if (!grid.isDigitAt(r, c) || seen.get(idx)) continue;
sp = 0; sp = 0;
@@ -476,12 +474,12 @@ public record SwedishGenerator(int[] buff) {
while (sp > 0) { while (sp > 0) {
var p = stack[--sp]; var p = stack[--sp];
int x = p / W, y = p % W; int x = p / C, y = p % C;
size++; size++;
for (var d : nbrs8) { for (var d : nbrs8) {
int nx = x + d.x, ny = y + d.y; int nx = x + d.r, ny = y + d.c;
if (nx < 0 || nx >= H || ny < 0 || ny >= W) continue; if (nx < 0 || nx >= R || ny < 0 || ny >= C) continue;
int nidx = grid.offset(nx, ny); int nidx = grid.offset(nx, ny);
if (seen.get(nidx)) continue; if (seen.get(nidx)) continue;
if (!grid.isDigitAt(nx, ny)) continue; if (!grid.isDigitAt(nx, ny)) continue;
@@ -494,13 +492,13 @@ public record SwedishGenerator(int[] buff) {
} }
// dead-end-ish letter cell (3+ walls) // dead-end-ish letter cell (3+ walls)
for (var r = 0; r < H; r++) for (var r = 0; r < R; r++)
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
if (grid.isDigitAt(r, c)) continue; if (grid.isDigitAt(r, c)) continue;
var walls = 0; var walls = 0;
for (var d : nbrs4) { for (var d : nbrs4) {
int rr = r + d.x, cc = c + d.y; int rr = r + d.r, cc = c + d.c;
if (rr < 0 || rr >= H || cc < 0 || cc >= W) { if (rr < 0 || rr >= R || cc < 0 || cc >= C) {
walls++; walls++;
continue; continue;
} }
@@ -520,8 +518,8 @@ public record SwedishGenerator(int[] buff) {
int placed = 0, guard = 0; int placed = 0, guard = 0;
while (placed < targetClues && guard++ < 4000) { while (placed < targetClues && guard++ < 4000) {
var r = rng.randint(0, H - 1); var r = rng.randint(0, R - 1);
var c = rng.randint(0, W - 1); var c = rng.randint(0, C - 1);
if (g.isDigitAt(r, c)) continue; if (g.isDigitAt(r, c)) continue;
var d = (char) ('0' + rng.randint(1, c == 0 ? CLUE_SIZE : 4)); var d = (char) ('0' + rng.randint(1, c == 0 ? CLUE_SIZE : 4));
@@ -537,13 +535,13 @@ public record SwedishGenerator(int[] buff) {
Grid mutate(Rng rng, Grid grid) { Grid mutate(Rng rng, Grid grid) {
var g = grid.deepCopyGrid(); var g = grid.deepCopyGrid();
var cx = rng.randint(0, H - 1); var cx = rng.randint(0, R - 1);
var cy = rng.randint(0, W - 1); var cy = rng.randint(0, C - 1);
var steps = 4; var steps = 4;
for (var k = 0; k < steps; k++) { for (var k = 0; k < steps; k++) {
var rr = clamp(cx + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, H - 1); var rr = clamp(cx + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, R - 1);
var cc = clamp(cy + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, W - 1); var cc = clamp(cy + (rng.randint(-2, 2) + rng.randint(-2, 2)), 0, C - 1);
if (g.isDigitAt(rr, cc)) { if (g.isDigitAt(rr, cc)) {
g.setCharAt(rr, cc, C_DASH); g.setCharAt(rr, cc, C_DASH);
@@ -562,13 +560,13 @@ public record SwedishGenerator(int[] buff) {
var nx = Math.cos(theta); var nx = Math.cos(theta);
var ny = Math.sin(theta); var ny = Math.sin(theta);
for (var r = 0; r < H; r++) for (var r = 0; r < R; r++)
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
out.setCharAt(r, c, ((r - CROSS_X) * nx + (c - CROSS_Y) * ny >= 0) ? a.getCharAt(r, c) : b.getCharAt(r, c)); out.setCharAt(r, c, ((r - CROSS_X) * nx + (c - CROSS_Y) * ny >= 0) ? a.getCharAt(r, c) : b.getCharAt(r, c));
} }
for (var r = 0; r < H; r++) for (var r = 0; r < R; r++)
for (var c = 0; c < W; c++) { for (var c = 0; c < C; c++) {
if (out.isDigitAt(r, c) && !hasRoomForClue(out, r, c, out.getCharAt(r, c))) out.setCharAt(r, c, C_DASH); if (out.isDigitAt(r, c) && !hasRoomForClue(out, r, c, out.getCharAt(r, c))) out.setCharAt(r, c, C_DASH);
} }
return out; return out;
@@ -648,6 +646,7 @@ public record SwedishGenerator(int[] buff) {
pop.sort(Comparator.comparingLong(GridAndFit::fit)); pop.sort(Comparator.comparingLong(GridAndFit::fit));
return pop.get(0).grid; return pop.get(0).grid;
} }
public Grid generateMask2(Rng rng, int[] lenCounts, int popSize, int gens, boolean verbose) { public Grid generateMask2(Rng rng, int[] lenCounts, int popSize, int gens, boolean verbose) {
if (verbose) System.out.println("generateMask init pop: " + popSize); if (verbose) System.out.println("generateMask init pop: " + popSize);
var pop = new ArrayList<Grid>(); var pop = new ArrayList<Grid>();
@@ -734,7 +733,7 @@ public record SwedishGenerator(int[] buff) {
static int slotScore(int[] cellCount, Slot s, Grid grid) { static int slotScore(int[] cellCount, Slot s, Grid grid) {
var cross = 0; var cross = 0;
for (var i = 0; i < s.len(); i++) cross += (cellCount[grid.offset(s.r(i), s.c(i))] - 1); for (var i = 0; i < s.len(); i++) cross += (cellCount[Grid.offset(s.r(i), s.c(i))] - 1);
return cross * 10 + s.len(); return cross * 10 + s.len();
} }
@@ -767,13 +766,13 @@ public record SwedishGenerator(int[] buff) {
var grid = mask.deepCopyGrid(); var grid = mask.deepCopyGrid();
var slots = extractSlots(grid); var slots = extractSlots(grid);
var used = new BitSet(SIZE << 1); var used = new Bit1029();
var assigned = new HashMap<Integer, Lemma>(); var assigned = new HashMap<Integer, Lemma>();
var ctx = CTX.get(); var ctx = CTX.get();
var cellCount = ctx.cellCount; var cellCount = ctx.cellCount;
Arrays.fill(cellCount, 0, SIZE, 0); Arrays.fill(cellCount, 0, SIZE, 0);
for (var s : slots) for (var i = 0; i < s.len(); i++) cellCount[grid.offset(s.r(i), s.c(i))]++; for (var s : slots) for (var i = 0; i < s.len(); i++) cellCount[Grid.offset(s.r(i), s.c(i))]++;
var t0 = System.currentTimeMillis(); var t0 = System.currentTimeMillis();
final var lastLog = new AtomicLong(t0); final var lastLog = new AtomicLong(t0);
@@ -894,7 +893,7 @@ public record SwedishGenerator(int[] buff) {
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
assigned.remove(k); assigned.remove(k);
used.set(w.index, false); used.clear(w.index);
undoPlace(grid, ctx.undoBuffer, undoOffset, nPlaced); undoPlace(grid, ctx.undoBuffer, undoOffset, nPlaced);
} }
stats.backtracks++; stats.backtracks++;
@@ -933,7 +932,7 @@ public record SwedishGenerator(int[] buff) {
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
assigned.remove(k); assigned.remove(k);
used.set(w.index, false); used.clear(w.index);
undoPlace(grid, ctx.undoBuffer, undoOffset, nPlaced); undoPlace(grid, ctx.undoBuffer, undoOffset, nPlaced);
} }