Gather data
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import puzzle.Main.PuzzleResult;
|
import puzzle.Main.PuzzleResult;
|
||||||
|
import puzzle.SwedishGenerator.Grid;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -78,11 +79,11 @@ public record ExportFormat() {
|
|||||||
int maxR = Integer.MIN_VALUE, maxC = Integer.MIN_VALUE;
|
int maxR = Integer.MIN_VALUE, maxC = Integer.MIN_VALUE;
|
||||||
|
|
||||||
for (var rc : placed) {
|
for (var rc : placed) {
|
||||||
for (var r : rc.cells) {
|
for (var c : rc.cells) {
|
||||||
minR = Math.min(minR, r[0]);
|
minR = Math.min(minR, Grid.r(c));
|
||||||
minC = Math.min(minC, r[1]);
|
minC = Math.min(minC, Grid.c(c));
|
||||||
maxR = Math.max(maxR, r[0]);
|
maxR = Math.max(maxR, Grid.r(c));
|
||||||
maxC = Math.max(maxC, r[1]);
|
maxC = Math.max(maxC, Grid.c(c));
|
||||||
}
|
}
|
||||||
minR = Math.min(minR, rc.arrowRow);
|
minR = Math.min(minR, rc.arrowRow);
|
||||||
minC = Math.min(minC, rc.arrowCol);
|
minC = Math.min(minC, rc.arrowCol);
|
||||||
@@ -93,8 +94,8 @@ public record ExportFormat() {
|
|||||||
// 3) map of only used letter cells (everything else becomes '#')
|
// 3) map of only used letter cells (everything else becomes '#')
|
||||||
var letterAt = new HashMap<Long, Character>();
|
var letterAt = new HashMap<Long, Character>();
|
||||||
for (var p : placed) {
|
for (var p : placed) {
|
||||||
for (var rc : p.cells) {
|
for (var c : p.cells) {
|
||||||
int rr = rc[0], cc = rc[1];
|
int rr = Grid.r(c), cc = Grid.c(c);
|
||||||
if (inBounds(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));
|
||||||
}
|
}
|
||||||
@@ -134,40 +135,34 @@ public record ExportFormat() {
|
|||||||
int c = s.clueC();
|
int c = s.clueC();
|
||||||
int d = s.dir();
|
int d = s.dir();
|
||||||
|
|
||||||
int[][] cells = new int[s.len()][];
|
int[] cells = new int[s.len()];
|
||||||
for (int i = 0; i < s.len(); i++) {
|
for (int i = 0; i < s.len(); i++) {
|
||||||
cells[i] = new int[]{ s.r(i), s.c(i) };
|
cells[i] = s.pos(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalize: always output right/down
|
// Canonicalize: always output right/down
|
||||||
int startRow, startCol, arrowRow, arrowCol;
|
int startRow, startCol, arrowRow, arrowCol;
|
||||||
String direction;
|
String direction;
|
||||||
boolean isReversed = false;
|
boolean isReversed = false;
|
||||||
|
startRow = Grid.r(cells[0]);
|
||||||
|
startCol = Grid.c(cells[0]);
|
||||||
if (d == 2) { // right -> horizontal
|
if (d == 2) { // right -> horizontal
|
||||||
direction = HORIZONTAL;
|
direction = HORIZONTAL;
|
||||||
startRow = cells[0][0];
|
|
||||||
startCol = cells[0][1];
|
arrowRow = r;
|
||||||
arrowRow = r;
|
arrowCol = c;
|
||||||
arrowCol = c;
|
|
||||||
} else if (d == 3 || d == 5) { // down or down-bent -> vertical
|
} else if (d == 3 || d == 5) { // down or down-bent -> vertical
|
||||||
direction = VERTICAL;
|
direction = VERTICAL;
|
||||||
startRow = cells[0][0];
|
|
||||||
startCol = cells[0][1];
|
|
||||||
arrowRow = r;
|
arrowRow = r;
|
||||||
arrowCol = c;
|
arrowCol = c;
|
||||||
} else if (d == 4) { // left -> horizontal (REVERSED)
|
} else if (d == 4) { // left -> horizontal (REVERSED)
|
||||||
direction = HORIZONTAL;
|
direction = HORIZONTAL;
|
||||||
isReversed = true;
|
isReversed = true;
|
||||||
startRow = cells[0][0];
|
|
||||||
startCol = cells[0][1];
|
|
||||||
arrowRow = r;
|
arrowRow = r;
|
||||||
arrowCol = c;
|
arrowCol = c;
|
||||||
} else if (d == 1) { // up -> vertical (REVERSED)
|
} else if (d == 1) { // up -> vertical (REVERSED)
|
||||||
direction = VERTICAL;
|
direction = VERTICAL;
|
||||||
isReversed = true;
|
isReversed = true;
|
||||||
startRow = cells[0][0];
|
|
||||||
startCol = cells[0][1];
|
|
||||||
arrowRow = r;
|
arrowRow = r;
|
||||||
arrowCol = c;
|
arrowCol = c;
|
||||||
} else {
|
} else {
|
||||||
@@ -187,7 +182,7 @@ public record ExportFormat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static long pack(int r, int c) { return (((long) r) << 32) ^ (c & 0xFFFFFFFFL); }
|
private static long pack(int r, int c) { return (((long) r) << 32) ^ (c & 0xFFFFFFFFL); }
|
||||||
private record Placed(Lemma lemma, int startRow, int startCol, String direction, int arrowRow, int arrowCol, int[][] cells, boolean isReversed) { }
|
private record Placed(Lemma lemma, int startRow, int startCol, String direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) { }
|
||||||
|
|
||||||
public record Rewards(int coins, int stars, int hints) { }
|
public record Rewards(int coins, int stars, int hints) { }
|
||||||
|
|
||||||
|
|||||||
@@ -125,9 +125,13 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
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)]); }
|
char getCharAt(int r, int c) { return (char) (g[offset(r, c)]); }
|
||||||
|
char getCharAt(int pos) { return (char) (g[pos]); }
|
||||||
int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
|
int digitAt(int r, int c) { return g[offset(r, c)] - 48; }
|
||||||
byte byteAt(int r, int c) { return g[offset(r, c)]; }
|
byte byteAt(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 setCharAt(int idx, char ch) { g[idx] = (byte) 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 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; }
|
||||||
@@ -339,6 +343,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
public int dir() { return key & 15; }
|
public int dir() { return key & 15; }
|
||||||
public boolean horiz() { return horiz(key); }
|
public boolean horiz() { return horiz(key); }
|
||||||
public int r(int i) { return Grid.r(offset(packedPos, i)); }
|
public int r(int i) { return Grid.r(offset(packedPos, i)); }
|
||||||
|
public int pos(int i) { return offset(packedPos, i); }
|
||||||
public int c(int i) { return Grid.c(offset(packedPos, i)); }
|
public int c(int i) { return Grid.c(offset(packedPos, i)); }
|
||||||
public static boolean horiz(int key) { return ((key & 15) & 1) == 0; }
|
public static boolean horiz(int key) { return ((key & 15) & 1) == 0; }
|
||||||
public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); }
|
public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); }
|
||||||
@@ -346,7 +351,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
static void undoPlace(Grid grid, long[] undoBuffer, int offset, int n) {
|
static void undoPlace(Grid grid, long[] undoBuffer, int offset, int n) {
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
long v = undoBuffer[offset + i];
|
long v = undoBuffer[offset + i];
|
||||||
grid.setCharAt((int) (v >> 16) & 0xFF, (int) (v >> 8) & 0xFF, (char) (v & 0xFF));
|
grid.clear((int)v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
@@ -724,32 +729,53 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
|
|
||||||
static void patternForSlot(Grid grid, Slot s, char[] pat) {
|
static void patternForSlot(Grid grid, Slot s, char[] pat) {
|
||||||
for (var i = 0; i < s.len(); i++) {
|
for (var i = 0; i < s.len(); i++) {
|
||||||
var ch = grid.getCharAt(s.r(i), s.c(i));
|
var ch = grid.getCharAt(s.pos(i));
|
||||||
pat[i] = isLetter(ch) ? ch : C_DASH;
|
pat[i] = isLetter(ch) ? ch : C_DASH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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[s.pos(i)] - 1);
|
||||||
return cross * 10 + s.len();
|
return cross * 10 + s.len();
|
||||||
}
|
}
|
||||||
|
static int placeWord1(Grid grid, Slot s, Lemma w, long[] undoBuffer, int offset) {
|
||||||
static int placeWord(Grid grid, Slot s, Lemma w, long[] undoBuffer, int offset) {
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (var i = 0; i < s.len(); i++) {
|
for (var i = 0; i < s.len(); i++) {
|
||||||
int r = s.r(i), c = s.c(i);
|
int r = s.r(i), c = s.c(i);
|
||||||
char cur = grid.getCharAt(r, c);
|
char cur = grid.getCharAt(r, c);
|
||||||
var ch = w.charAt(i);
|
var ch = w.charAt(i);
|
||||||
if (cur == C_DASH) {
|
if (cur == C_DASH) {
|
||||||
undoBuffer[offset + n] = ((long) r << 16) | ((long) c << 8) | (long) C_DASH;
|
undoBuffer[offset + n] = ((long) r << 16) | ((long) c << 8);
|
||||||
n++;
|
n++;
|
||||||
grid.setCharAt(r, c, ch);
|
grid.setCharAt(r, c, ch);
|
||||||
} else {
|
} else {
|
||||||
if (cur != ch) {
|
if (cur != ch) {
|
||||||
for (var j = 0; j < n; j++) {
|
for (var j = 0; j < n; j++) {
|
||||||
long v = undoBuffer[offset + j];
|
long v = undoBuffer[offset + j];
|
||||||
grid.setCharAt((int) (v >> 16) & 0xFF, (int) (v >> 8) & 0xFF, (char) (v & 0xFF));
|
grid.clear((int) (v >> 16) & 0xFF, (int) (v >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
static int placeWord(Grid grid, Slot s, Lemma w, long[] undoBuffer, int offset) {
|
||||||
|
int n = 0;
|
||||||
|
for (var i = 0; i < s.len(); i++) {
|
||||||
|
int idx = s.pos(i);
|
||||||
|
char cur = grid.getCharAt(idx);
|
||||||
|
var ch = w.charAt(i);
|
||||||
|
if (cur == C_DASH) {
|
||||||
|
undoBuffer[offset + n] = idx;
|
||||||
|
n++;
|
||||||
|
grid.setCharAt(idx, ch);
|
||||||
|
} else {
|
||||||
|
if (cur != ch) {
|
||||||
|
for (var j = 0; j < n; j++) {
|
||||||
|
long v = undoBuffer[offset + j];
|
||||||
|
grid.clear((int) v);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -770,7 +796,7 @@ public record SwedishGenerator(int[] buff) {
|
|||||||
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[s.pos(i)]++;
|
||||||
|
|
||||||
var t0 = System.currentTimeMillis();
|
var t0 = System.currentTimeMillis();
|
||||||
final var lastLog = new AtomicLong(t0);
|
final var lastLog = new AtomicLong(t0);
|
||||||
|
|||||||
@@ -255,6 +255,49 @@ public class SwedishGeneratorTest {
|
|||||||
assertNotNull(g4);
|
assertNotNull(g4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPlaceWord() {
|
||||||
|
var grid = SwedishGenerator.makeEmptyGrid();
|
||||||
|
// Slot at (0,0) length 3, horizontal (right)
|
||||||
|
// key = (r << 8) | (c << 4) | d. Here we just need a valid slot for placeWord.
|
||||||
|
// r(i) and c(i) are used by placeWord.
|
||||||
|
var packedPos = ((long) Grid.offset(0, 0)) | (((long) Grid.offset(0, 1)) << 7) | (((long) Grid.offset(0, 2)) << 14);
|
||||||
|
var s = Slot.from(0, packedPos, 3);
|
||||||
|
var w1 = new Lemma("ABC", 1, "test");
|
||||||
|
var undoBuffer = new long[10];
|
||||||
|
|
||||||
|
// 1. Successful placement in empty grid
|
||||||
|
int placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0);
|
||||||
|
assertEquals(3, placed);
|
||||||
|
assertEquals('A', grid.getCharAt(0, 0));
|
||||||
|
assertEquals('B', grid.getCharAt(0, 1));
|
||||||
|
assertEquals('C', grid.getCharAt(0, 2));
|
||||||
|
|
||||||
|
// 2. Successful placement with partial overlap (same characters)
|
||||||
|
// Clear grid first or use another slot. Let's just verify it works if we place it again.
|
||||||
|
placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0);
|
||||||
|
assertEquals(0, placed); // 0 new characters placed
|
||||||
|
|
||||||
|
// 3. Conflict: place "ABD" where "ABC" is
|
||||||
|
var w2 = new Lemma("ABD", 1, "conflict");
|
||||||
|
placed = SwedishGenerator.placeWord(grid, s, w2, undoBuffer, 0);
|
||||||
|
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));
|
||||||
|
|
||||||
|
// 4. Partial placement then conflict (rollback)
|
||||||
|
grid = SwedishGenerator.makeEmptyGrid();
|
||||||
|
grid.setCharAt(0, 2, 'X'); // Conflict at the end
|
||||||
|
placed = SwedishGenerator.placeWord(grid, s, w1, undoBuffer, 0);
|
||||||
|
assertEquals(-1, placed);
|
||||||
|
// Verify grid is still empty (except for 'X')
|
||||||
|
assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 0));
|
||||||
|
assertEquals(SwedishGenerator.C_DASH, grid.getCharAt(0, 1));
|
||||||
|
assertEquals('X', grid.getCharAt(0, 2));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBacktrackingHelpers() {
|
void testBacktrackingHelpers() {
|
||||||
var grid = SwedishGenerator.makeEmptyGrid();
|
var grid = SwedishGenerator.makeEmptyGrid();
|
||||||
|
|||||||
Reference in New Issue
Block a user