This commit is contained in:
mike
2026-01-23 11:58:57 +01:00
parent 2aa08fedb0
commit 7db439a9dc
8 changed files with 129 additions and 106 deletions

View File

@@ -1,10 +1,11 @@
package puzzle;
import module java.base;
import anno.Shaped;
import lombok.AllArgsConstructor;
import lombok.experimental.Delegate;
import lombok.val;
import precomp.Const9x8.Cell;
import precomp.Const9x8;
import puzzle.Export.Puzzle.Replacar.Rell;
import puzzle.Meta.ShardLem;
import puzzle.SwedishGenerator.Dict;
@@ -12,14 +13,16 @@ import puzzle.SwedishGenerator.FillResult;
import puzzle.SwedishGenerator.Grid;
import puzzle.SwedishGenerator.Slotinfo;
import static precomp.Const9x8.CLUE_DOWN0;
import static precomp.Const9x8.CLUE_LEFT3;
import static precomp.Const9x8.CLUE_LEFT_TOP4;
import static precomp.Const9x8.CLUE_NONE;
import static precomp.Const9x8.CLUE_RIGHT1;
import static precomp.Const9x8.INIT_GRID_OUTPUT;
import static precomp.Const9x8.INIT_GRID_OUTPUT_ARR;
import static precomp.Const9x8.CLUE_RIGHT_TOP5;
import static precomp.Const9x8.CLUE_UP2;
import static puzzle.Export.Clue.DOWN0;
import static puzzle.Export.Clue.RIGHT1;
import static puzzle.Clues.createEmpty;
import static puzzle.Masker.Slot;
import static puzzle.Masker.C;
import static puzzle.SwedishGenerator.Lemma;
import static puzzle.SwedishGenerator.X;
@@ -34,26 +37,30 @@ import static puzzle.SwedishGenerator.X;
*/
public record Export() {
public static final ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[8]);
static final byte CLUE_UP = 2;
static final byte CLUE_LEFT = 3;
static final byte CLUE_LEFT_TOP = 4;
static final byte CLUE_RIGHT_TOP = 5;
public static final ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[8]);
static final byte CLUE_UP = 2;
static final byte CLUE_LEFT = 3;
static final byte CLUE_LEFT_TOP = 4;
static final byte CLUE_RIGHT_TOP = 5;
@Shaped static final String INIT_GRID_OUTPUT = Const9x8.INIT_GRID_OUTPUT;
@Shaped static final int R = Const9x8.R;
@Shaped static final int C = Const9x8.C;
@Shaped static final byte[] INIT_GRID_OUTPUT_ARR = Const9x8.INIT_GRID_OUTPUT_ARR;
static int HI(int in) { return in | 64; }
static char LETTER(int in) { return (char) (in | 64); }
static char CLUE_CHAR(int s) { return (char) (s | 48); }
static int INDEX_ROW(int idx) { return idx & 7; }
static int INDEX_COL(int idx) { return idx >>> 3; }
static int INDEX_ROW(int idx) { return idx % R; }
static int INDEX_COL(int idx) { return idx / R; }
static int INDEX(int r, int cols, int c) { return r * cols + c; }
@AllArgsConstructor
enum Clue {
DOWN0(CLUE_DOWN0, 'B', 'b'),
RIGHT1(CLUE_RIGHT1, 'A', 'a'),
UP2(CLUE_UP, 'C', 'c'),
LEFT3(CLUE_LEFT, 'D', 'd'),
LEFT_TOP4(CLUE_LEFT_TOP, 'E', 'e'),
RIGHT_TOP5(CLUE_RIGHT_TOP, 'F', 'f'),
NONE(CLUE_LEFT, '?', '?');
UP2(CLUE_UP2, 'C', 'c'),
LEFT3(CLUE_LEFT3, 'D', 'd'),
LEFT_TOP4(CLUE_LEFT_TOP4, 'E', 'e'),
RIGHT_TOP5(CLUE_RIGHT_TOP5, 'F', 'f'),
NONE(CLUE_NONE, '?', '?');
final byte dir;
final char slotChar, clueChar;
private static final Clue[] CLUES = new Clue[]{ DOWN0, RIGHT1, UP2, LEFT3, LEFT_TOP4, RIGHT_TOP5, NONE, NONE, NONE };
@@ -113,15 +120,15 @@ public record Export() {
for (var h = grid.hi & ~cl.hi & 0xFF; h != X; h &= h - 1) stream.accept(Lettrix.from(64 | Long.numberOfTrailingZeros(h), grid.g));
return stream.build();
}
String gridToString() {
String gridToString(Slotinfo[] slots) {
var sb = INIT_GRID_OUTPUT_ARR.clone();
Masker.forEachSlot(cl, (s, _, _) -> {
val idx = Slot.clueIndex(s);
for (var slot : slots) {
val idx = Slot.clueIndex(slot.key());
val r = idx & 7;
val c = idx >>> 3;
val dir = Slot.dir(s);
val dir = Slot.dir(slot.key());
sb[r * (C + 1) + c] = (byte) (dir | 48);
});
}
stream().forEach((l) -> sb[l.index(C + 1)] = (byte) l.human());
return new String(sb);
}
@@ -137,9 +144,9 @@ public record Export() {
stream().forEach((l) -> sb[l.index(C + 1)] = (byte) l.human());
return new String(sb).replaceAll(" ", String.valueOf(emptyFallback)).split("\n");
}
public static IntStream cellWalk(byte base, long lo, long hi) {
public static IntStream cellWalk(int base, long lo, long hi) {
if (Slotinfo.increasing(base)) {
return IntStream.concat(
return IntStream.concat(IntStream.of(Slot.clueIndex(base)), IntStream.concat(
IntStream.generate(new IntSupplier() {
long temp = lo;
@@ -159,9 +166,9 @@ public record Export() {
temp &= temp - 1;
return res;
}
}).limit(Long.bitCount(hi)));
}).limit(Long.bitCount(hi))));
} else {
return IntStream.concat(
return IntStream.concat(IntStream.of(Slot.clueIndex(base)), IntStream.concat(
IntStream.generate(new IntSupplier() {
long temp = hi;
@@ -181,11 +188,10 @@ public record Export() {
temp &= ~(1L << msb);
return msb;
}
}).limit(Long.bitCount(lo)));
}).limit(Long.bitCount(lo))));
}
}
public String renderHuman() { return String.join("\n", exportGrid(_ -> ' ', '#')); }
@FunctionalInterface
interface Replacar {
@@ -195,16 +201,16 @@ public record Export() {
}
record Placed(rci[] rcis, long lemma, int slotKey, int[] cells) {
record Placed(long lemma, int slotKey, rci[] cells) {
public static final char HORIZONTAL = 'h';
static final char VERTICAL = 'v';
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.VERTICAL };
public int arrowCol() { return rcis[Slot.clueIndex(slotKey)].c(); }
public int arrowRow() { return rcis[Slot.clueIndex(slotKey)].r(); }
public int startRow() { return rcis[cells[0]].r(); }
public int startCol() { return rcis[cells[0]].c(); }
public int arrowCol() { return cells[0].c()/*rcis[Slot.clueIndex(slotKey)].c()*/; }
public int arrowRow() { return cells[0].r()/*rcis[Slot.clueIndex(slotKey)].r()*/; }
public int startRow() { return cells[1].r(); }
public int startCol() { return cells[1].c(); }
public boolean isReversed() { return !Slotinfo.increasing(slotKey); }
public char direction() { return DIRECTION[Slot.dir(slotKey)]; }
}
@@ -247,31 +253,33 @@ public record Export() {
public record PuzzleResult(Signa clues, Puzzle grid, Slotinfo[] slots, FillResult filled) {
public String gridRenderHuman() {
return String.join("\n", grid.exportGrid(_ -> ' ', '#'));
}
public String gridGridToString() {
return grid.gridToString(slots);
}
public ExportedPuzzle exportFormatFromFilled(Rewards rewards, rci[] rcis, int bits) {
// If nothing placed: return full grid mapped to letters/# only
if (slots.length == 0) {
return new ExportedPuzzle(grid.exportGrid(_ -> '#', '#'), new WordOut[0], 1, rewards);
}
var placed = Arrays.stream(slots).map(slot -> new Placed(rcis, slot.assign().w, slot.key(), Puzzle.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).toArray())).toArray(
Placed[]::new);
var placed = Arrays.stream(slots)
.map(slot -> new Placed(slot.assign().w, slot.key(), Puzzle.cellWalk(slot.key(), slot.lo(), slot.hi()).mapToObj(i -> rcis[i]).toArray(rci[]::new)))
.toArray(Placed[]::new);
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
int minR = Integer.MAX_VALUE, minC = Integer.MAX_VALUE;
int maxR = Integer.MIN_VALUE, maxC = Integer.MIN_VALUE;
for (var rc : placed) {
for (var c : rc.cells) {
val it = rcis[c];
for (var it : rc.cells) {
minR = Math.min(minR, it.r());
minC = Math.min(minC, it.c());
maxR = Math.max(maxR, it.r());
maxC = Math.max(maxC, it.c());
}
minR = Math.min(minR, rc.arrowRow());
minC = Math.min(minC, rc.arrowCol());
maxR = Math.max(maxR, rc.arrowRow());
maxC = Math.max(maxC, rc.arrowCol());
}
// 3) map of only used letter cells (everything else becomes '#')
@@ -299,6 +307,8 @@ public record Export() {
var total = 0.0001d + Arrays.stream(wordsOut).mapToDouble(WordOut::complex).sum();
return new ExportedPuzzle(gridv2, wordsOut, (int) (total / wordsOut.length), rewards);
}
}
record Lettrix(int index, byte letter) {