introduce bitloops
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import static puzzle.SwedishGenerator.*;
|
||||||
public final class DictData {
|
public final class DictData {
|
||||||
private DictData() {}
|
private DictData() {}
|
||||||
|
|
||||||
public static final SwedishGenerator.Dict DICT = build();
|
public static final Dict DICT = build();
|
||||||
|
|
||||||
private static SwedishGenerator.Dict build() {
|
private static Dict build() {
|
||||||
SwedishGenerator.DictEntry[] idx = new SwedishGenerator.DictEntry[SwedishGenerator.MAX_WORD_LENGTH_PLUS_ONE];
|
DictEntry[] idx = new DictEntry[MAX_WORD_LENGTH_PLUS_ONE];
|
||||||
idx[2] = DictDataL2.entry();
|
idx[2] = DictDataL2.entry();
|
||||||
idx[3] = DictDataL3.entry();
|
idx[3] = DictDataL3.entry();
|
||||||
idx[4] = DictDataL4.entry();
|
idx[4] = DictDataL4.entry();
|
||||||
@@ -14,6 +15,6 @@ public final class DictData {
|
|||||||
idx[6] = DictDataL6.entry();
|
idx[6] = DictDataL6.entry();
|
||||||
idx[7] = DictDataL7.entry();
|
idx[7] = DictDataL7.entry();
|
||||||
idx[8] = DictDataL8.entry();
|
idx[8] = DictDataL8.entry();
|
||||||
return new SwedishGenerator.Dict(idx, 40670);
|
return new Dict(idx, 40670);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import static puzzle.SwedishGenerator.*;
|
||||||
public final class DictDataL3 {
|
public final class DictDataL3 {
|
||||||
private DictDataL3() {}
|
private DictDataL3() {}
|
||||||
|
|
||||||
@@ -17,11 +18,11 @@ public final class DictDataL3 {
|
|||||||
return DictDataL3P0.get();
|
return DictDataL3P0.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SwedishGenerator.DictEntry entry() {
|
public static DictEntry entry() {
|
||||||
long[] wds = words();
|
long[] wds = words();
|
||||||
long[] flat = posFlat();
|
long[] flat = posFlat();
|
||||||
long[][] pos = reshape(flat, ROWS, COLS);
|
long[][] pos = reshape(flat, ROWS, COLS);
|
||||||
return new SwedishGenerator.DictEntry(wds, pos, wds.length, (wds.length + 63) >>> 6);
|
return new DictEntry(wds, pos, wds.length, (wds.length + 63) >>> 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int copy(long[] dst, int at, long[] src) {
|
private static int copy(long[] dst, int at, long[] src) {
|
||||||
|
|||||||
@@ -10,4 +10,7 @@ public final class Config {
|
|||||||
public static final int MAX_LEN = ${MAX_LEN};
|
public static final int MAX_LEN = ${MAX_LEN};
|
||||||
public static final int PUZZLE_ROWS = ${PUZZLE_ROWS};
|
public static final int PUZZLE_ROWS = ${PUZZLE_ROWS};
|
||||||
public static final int PUZZLE_COLS = ${PUZZLE_COLS};
|
public static final int PUZZLE_COLS = ${PUZZLE_COLS};
|
||||||
|
public static final int PUZZLE_SIZE = PUZZLE_ROWS*PUZZLE_COLS;
|
||||||
|
public static final int MAX_WORD_LENGTH = PUZZLE_ROWS;
|
||||||
|
public static final int MAX_WORD_LENGTH_MIN_1 = PUZZLE_ROWS-1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
@@ -13,13 +14,14 @@ import puzzle.SwedishGenerator.DictEntry;
|
|||||||
import puzzle.SwedishGenerator.FillResult;
|
import puzzle.SwedishGenerator.FillResult;
|
||||||
import puzzle.SwedishGenerator.Grid;
|
import puzzle.SwedishGenerator.Grid;
|
||||||
import puzzle.SwedishGenerator.Slotinfo;
|
import puzzle.SwedishGenerator.Slotinfo;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.IntSupplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import static puzzle.Export.Clue.DOWN;
|
||||||
|
import static puzzle.Export.Clue.RIGHT;
|
||||||
|
import static puzzle.Masker.Clues.createEmpty;
|
||||||
import static puzzle.SwedishGenerator.R;
|
import static puzzle.SwedishGenerator.R;
|
||||||
import static puzzle.SwedishGenerator.Lemma;
|
import static puzzle.SwedishGenerator.Lemma;
|
||||||
import static puzzle.Masker.Slot;
|
import static puzzle.Masker.Slot;
|
||||||
@@ -37,42 +39,78 @@ import static puzzle.SwedishGenerator.X;
|
|||||||
*/
|
*/
|
||||||
public record Export() {
|
public record Export() {
|
||||||
|
|
||||||
record Strings() {
|
public static final ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[SwedishGenerator.MAX_WORD_LENGTH]);
|
||||||
|
static final byte CLUE_DOWN = 0;
|
||||||
static String padRight(String s, int n) {
|
static final byte CLUE_RIGHT = 1;
|
||||||
if (s.length() >= n) return s;
|
static final byte CLUE_UP = 2;
|
||||||
return s + " ".repeat(n - s.length());
|
static final byte CLUE_LEFT = 3;
|
||||||
}
|
static final byte CLUE_LEFT_TOP = 4;
|
||||||
|
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(int r, int cols, int c) { return r * cols + c; }
|
||||||
|
@AllArgsConstructor
|
||||||
|
enum Clue {
|
||||||
|
DOWN(CLUE_DOWN),
|
||||||
|
RIGHT(CLUE_RIGHT),
|
||||||
|
UP(CLUE_UP),
|
||||||
|
LEFT(CLUE_LEFT);
|
||||||
|
final byte dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String INIT = IntStream.range(0, R).mapToObj(l_ -> " ").collect(Collectors.joining("\n"));
|
record Strings() {
|
||||||
|
|
||||||
|
static String padRight(String s, int n) { return s.length() >= n ? s : s + " ".repeat(n - s.length()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String INIT = IntStream.range(0, Config.PUZZLE_ROWS).mapToObj(l_ -> " ").collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
public record ClueAt(int index, int clue) { }
|
public record ClueAt(int index, int clue) { }
|
||||||
|
|
||||||
public record Clued(@Delegate Clues c) {
|
public record Clued(@Delegate Clues c) {
|
||||||
|
|
||||||
public Clued deepCopyGrid() { return new Clued(new Clues(c.lo, c.hi, c.vlo, c.vhi, c.rlo, c.rhi, c.xlo, c.xhi)); }
|
public Clued deepCopyGrid() { return new Clued(new Clues(c.lo, c.hi, c.vlo, c.vhi, c.rlo, c.rhi, c.xlo, c.xhi)); }
|
||||||
|
public static Clued parse(String s) {
|
||||||
|
var c = createEmpty();
|
||||||
|
var lines = s.split("\n");
|
||||||
|
for (int r = 0; r < Math.min(lines.length, R); r++) {
|
||||||
|
var line = lines[r];
|
||||||
|
for (int col = 0; col < Math.min(line.length(), C); col++) {
|
||||||
|
char ch = line.charAt(col);
|
||||||
|
if (ch >= '0' && ch <= '4') {
|
||||||
|
int idx = Masker.offset(r, col);
|
||||||
|
byte dir = (byte) (ch - '0');
|
||||||
|
if ((idx & 64) == 0) c.setClueLo(1L << idx, dir);
|
||||||
|
else c.setClueHi(1L << (idx & 63), dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Clued(c);
|
||||||
|
}
|
||||||
String gridToString() {
|
String gridToString() {
|
||||||
var sb = new StringBuilder(INIT);
|
var sb = new StringBuilder(INIT);
|
||||||
forEachSlot((s, _, _) -> {
|
forEachSlot((s, _, _) -> {
|
||||||
val idx = Slot.clueIndex(s);
|
val idx = Slot.clueIndex(s);
|
||||||
val r = idx & 7;
|
|
||||||
val c = idx >>> 3;
|
|
||||||
val dir = Slot.dir(s);
|
val dir = Slot.dir(s);
|
||||||
sb.setCharAt(r * (C + 1) + c, (char) (dir | 48));
|
sb.setCharAt(INDEX(INDEX_ROW(idx), C + 1, INDEX_COL(idx)), CLUE_CHAR(dir));
|
||||||
});
|
});
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
public Stream<ClueAt> stream() {
|
public Stream<ClueAt> stream() {
|
||||||
val stream = Stream.<ClueAt>builder();
|
val stream = Stream.<ClueAt>builder();
|
||||||
for (var l = c.lo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), 1));
|
for (var l = c.lo & ~c.xlo & ~c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), RIGHT.dir));
|
||||||
for (var l = c.lo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), 0));
|
for (var l = c.lo & ~c.xlo & ~c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), DOWN.dir));
|
||||||
for (var l = c.lo & c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), 2));
|
for (var l = c.lo & ~c.xlo & c.rlo & ~c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_UP));
|
||||||
for (var l = c.lo & c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), 3));
|
for (var l = c.lo & ~c.xlo & c.rlo & c.vlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT));
|
||||||
for (var h = c.hi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(64 | Long.numberOfTrailingZeros(h), 1));
|
for (var l = c.lo & c.xlo; l != X; l &= l - 1) stream.accept(new ClueAt(Long.numberOfTrailingZeros(l), CLUE_LEFT_TOP));
|
||||||
for (var h = c.hi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(64 | Long.numberOfTrailingZeros(h), 0));
|
|
||||||
for (var h = c.hi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt((64 | Long.numberOfTrailingZeros(h)), 2));
|
for (var h = c.hi & ~c.xhi & ~c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_RIGHT));
|
||||||
for (var h = c.hi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt((64 | Long.numberOfTrailingZeros(h)), 3));
|
for (var h = c.hi & ~c.xhi & ~c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_DOWN));
|
||||||
|
for (var h = c.hi & ~c.xhi & c.rhi & ~c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_UP));
|
||||||
|
for (var h = c.hi & ~c.xhi & c.rhi & c.vhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT));
|
||||||
|
for (var h = c.hi & c.xhi; h != X; h &= h - 1) stream.accept(new ClueAt(HI(Long.numberOfTrailingZeros(h)), CLUE_LEFT_TOP));
|
||||||
|
|
||||||
return stream.build();
|
return stream.build();
|
||||||
}
|
}
|
||||||
@@ -83,29 +121,51 @@ public record Export() {
|
|||||||
|
|
||||||
record LetterAt(int index, byte letter) {
|
record LetterAt(int index, byte letter) {
|
||||||
|
|
||||||
public char human() {
|
public int row() { return INDEX_ROW(index); }
|
||||||
return (char) (letter | 64);
|
public int col() { return INDEX_COL(index); }
|
||||||
}
|
public char human() { return LETTER(letter); }
|
||||||
static LetterAt from(int index, byte[] bytes) { return new LetterAt(index, bytes[index]); }
|
static LetterAt from(int index, byte[] bytes) { return new LetterAt(index, bytes[index]); }
|
||||||
|
public int index(int cols) { return (row() * cols) + col(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(int index, byte letter);
|
void visit(int index, byte letter);
|
||||||
default void visit(int index, byte[] letters) { visit(index, letters[index]); }
|
default void visit(int index, byte[] letters) { visit(index, letters[index]); }
|
||||||
}
|
}
|
||||||
|
|
||||||
record Gridded(@Delegate Grid grid) {
|
record Gridded(@Delegate Grid grid, Clues cl) {
|
||||||
|
|
||||||
|
public Gridded(Clues clues) { this(clues.toGrid(), clues); }
|
||||||
public Stream<LetterAt> stream(Clues clues) {
|
public Stream<LetterAt> stream(Clues clues) {
|
||||||
val stream = Stream.<LetterAt>builder();
|
val stream = Stream.<LetterAt>builder();
|
||||||
for (var l = grid.lo & ~clues.lo; l != X; l &= l - 1) stream.accept(LetterAt.from(Long.numberOfTrailingZeros(l), grid.g));
|
for (var l = grid.lo & ~clues.lo; l != X; l &= l - 1) stream.accept(LetterAt.from(Long.numberOfTrailingZeros(l), grid.g));
|
||||||
for (var h = grid.hi & ~clues.hi & 0xFF; h != X; h &= h - 1) stream.accept(LetterAt.from(64 | Long.numberOfTrailingZeros(h), grid.g));
|
for (var h = grid.hi & ~clues.hi & 0xFF; h != X; h &= h - 1) stream.accept(LetterAt.from(64 | Long.numberOfTrailingZeros(h), grid.g));
|
||||||
return stream.build();
|
return stream.build();
|
||||||
}
|
}
|
||||||
public void forEachLetter(Clues clues, LetterVisit visitor) {
|
String gridToString(Clues clues) {
|
||||||
for (var l = grid.lo & ~clues.lo; l != X; l &= l - 1) visitor.visit(Long.numberOfTrailingZeros(l), grid.g);
|
var sb = new StringBuilder(INIT);
|
||||||
for (var h = grid.hi & ~clues.hi & 0xFF; h != X; h &= h - 1) visitor.visit(64 | Long.numberOfTrailingZeros(h), grid.g);
|
clues.forEachSlot((s, _, _) -> {
|
||||||
|
val idx = Slot.clueIndex(s);
|
||||||
|
val r = idx & 7;
|
||||||
|
val c = idx >>> 3;
|
||||||
|
val dir = Slot.dir(s);
|
||||||
|
sb.setCharAt(r * (C + 1) + c, (char) (dir | 48));
|
||||||
|
});
|
||||||
|
stream(clues).forEach((l) -> sb.setCharAt(l.index(C + 1), l.human()));
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
public static IntStream walk(byte base, long lo, long hi) {
|
public String[] exportGrid(Clues clues, Replacar clueChar, char emptyFallback) {
|
||||||
|
var sb = new StringBuilder(INIT);
|
||||||
|
clues.forEachSlot((s, l, a) -> {
|
||||||
|
val idx = Slot.clueIndex(s);
|
||||||
|
val r = idx & 7;
|
||||||
|
val c = idx >>> 3;
|
||||||
|
val dir = Slot.dir(s);
|
||||||
|
sb.setCharAt(r * (C + 1) + c, clueChar.replace(new Cell(grid, clues, idx, (byte) (dir | 48))));
|
||||||
|
});
|
||||||
|
stream(clues).forEach((l) -> sb.setCharAt(l.index(C + 1), l.human()));
|
||||||
|
return sb.toString().replaceAll(" ", "" + emptyFallback).split("\n");
|
||||||
|
}
|
||||||
|
public static IntStream cellWalk(byte base, long lo, long hi) {
|
||||||
if (Slotinfo.increasing(base)) {
|
if (Slotinfo.increasing(base)) {
|
||||||
return IntStream.concat(
|
return IntStream.concat(
|
||||||
IntStream.generate(new IntSupplier() {
|
IntStream.generate(new IntSupplier() {
|
||||||
@@ -136,9 +196,8 @@ public record Export() {
|
|||||||
@Override
|
@Override
|
||||||
public int getAsInt() {
|
public int getAsInt() {
|
||||||
int msb = 63 - Long.numberOfLeadingZeros(temp);
|
int msb = 63 - Long.numberOfLeadingZeros(temp);
|
||||||
int res = 64 | msb;
|
|
||||||
temp &= ~(1L << msb);
|
temp &= ~(1L << msb);
|
||||||
return res;
|
return 64 | msb;
|
||||||
}
|
}
|
||||||
}).limit(Long.bitCount(hi)),
|
}).limit(Long.bitCount(hi)),
|
||||||
IntStream.generate(new IntSupplier() {
|
IntStream.generate(new IntSupplier() {
|
||||||
@@ -147,65 +206,33 @@ public record Export() {
|
|||||||
@Override
|
@Override
|
||||||
public int getAsInt() {
|
public int getAsInt() {
|
||||||
int msb = 63 - Long.numberOfLeadingZeros(temp);
|
int msb = 63 - Long.numberOfLeadingZeros(temp);
|
||||||
int res = msb;
|
|
||||||
temp &= ~(1L << msb);
|
temp &= ~(1L << msb);
|
||||||
return res;
|
return msb;
|
||||||
}
|
}
|
||||||
}).limit(Long.bitCount(lo)));
|
}).limit(Long.bitCount(lo)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String gridToString(Clues clues) {
|
|
||||||
var sb = new StringBuilder(INIT);
|
public String renderHuman(Clues clues) { return String.join("\n", exportGrid(clues, _ -> ' ', '#')); }
|
||||||
clues.forEachSlot((s, _, _) -> {
|
|
||||||
val idx = Slot.clueIndex(s);
|
|
||||||
val r = idx & 7;
|
|
||||||
val c = idx >>> 3;
|
|
||||||
val dir = Slot.dir(s);
|
|
||||||
sb.setCharAt(r * (C + 1) + c, (char) (dir | 48));
|
|
||||||
});
|
|
||||||
forEachLetter(clues, (idx, letter) -> {
|
|
||||||
val r = idx & 7;
|
|
||||||
val c = idx >>> 3;
|
|
||||||
sb.setCharAt(r * (C + 1) + c, (char) (letter | 64));
|
|
||||||
});
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
public String renderHuman(Clues clues) {
|
|
||||||
return String.join("\n", exportGrid(clues, _ -> ' ', '#'));
|
|
||||||
}
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface Replacar {
|
interface Replacar {
|
||||||
|
|
||||||
record Cell(Grid grid, Clues clues, int index, byte data) { }
|
record Cell(Grid grid, Clues clues, int index, byte data) { }
|
||||||
char replace(Cell c);
|
char replace(Cell c);
|
||||||
}
|
}
|
||||||
public String[] exportGrid(Clues clues, Replacar clueChar, char emptyFallback) {
|
|
||||||
var sb = new StringBuilder(INIT);
|
|
||||||
clues.forEachSlot((s, l, a) -> {
|
|
||||||
val idx = Slot.clueIndex(s);
|
|
||||||
val r = idx & 7;
|
|
||||||
val c = idx >>> 3;
|
|
||||||
val dir = Slot.dir(s);
|
|
||||||
sb.setCharAt(r * (C + 1) + c, clueChar.replace(new Cell(grid, clues, idx, (byte) (dir | 48))));
|
|
||||||
});
|
|
||||||
forEachLetter(clues, (idx, letter) -> {
|
|
||||||
val r = idx & 7;
|
|
||||||
val c = idx >>> 3;
|
|
||||||
sb.setCharAt(r * (C + 1) + c, (char) (letter | 64));
|
|
||||||
});
|
|
||||||
return sb.toString().replaceAll(" ", "" + emptyFallback).split("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record Placed(long lemma, int slotKey, int[] cells) {
|
record Placed(long lemma, int slotKey, int[] cells) {
|
||||||
|
|
||||||
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL };
|
|
||||||
public static final char HORIZONTAL = 'h';
|
public static final char HORIZONTAL = 'h';
|
||||||
static final char VERTICAL = 'v';
|
static final char VERTICAL = 'v';
|
||||||
public int arrowCol() { return SwedishGenerator.IT[Slot.clueIndex(slotKey)].c(); }
|
static final char[] DIRECTION = { Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL, Placed.HORIZONTAL, Placed.VERTICAL };
|
||||||
public int arrowRow() { return SwedishGenerator.IT[Slot.clueIndex(slotKey)].r(); }
|
|
||||||
public int startRow() { return SwedishGenerator.IT[cells[0]].r(); }
|
public int arrowCol() { return Masker.IT[Slot.clueIndex(slotKey)].c(); }
|
||||||
public int startCol() { return SwedishGenerator.IT[cells[0]].c(); }
|
public int arrowRow() { return Masker.IT[Slot.clueIndex(slotKey)].r(); }
|
||||||
|
public int startRow() { return Masker.IT[cells[0]].r(); }
|
||||||
|
public int startCol() { return Masker.IT[cells[0]].c(); }
|
||||||
public boolean isReversed() { return !Slotinfo.increasing(slotKey); }
|
public boolean isReversed() { return !Slotinfo.increasing(slotKey); }
|
||||||
public char direction() { return DIRECTION[Slot.dir(slotKey)]; }
|
public char direction() { return DIRECTION[Slot.dir(slotKey)]; }
|
||||||
}
|
}
|
||||||
@@ -214,9 +241,9 @@ public record Export() {
|
|||||||
|
|
||||||
public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) {
|
public record WordOut(String word, int[] cell, int startRow, int startCol, char direction, int arrowRow, int arrowCol, boolean isReversed, int complex, String[] clue) {
|
||||||
|
|
||||||
public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed) {
|
public WordOut(long l, int startRow, int startCol, char d, int arrowRow, int arrowCol, boolean isReversed, byte[] bytes) {
|
||||||
val meta = Meta.readRecord(Meta.shardKey(l), Lemma.unpackShardIndex(l));
|
val meta = Meta.readRecord(Meta.shardKey(l), Lemma.unpackShardIndex(l));
|
||||||
this(Lemma.asWord(l), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed,
|
this(Lemma.asWord(l, bytes), new int[]{ arrowRow, arrowCol, startRow, startCol }, startRow, startCol, d, arrowRow, arrowCol, isReversed,
|
||||||
meta.simpel(), meta.clues());
|
meta.simpel(), meta.clues());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,7 +270,7 @@ public record Export() {
|
|||||||
return new ExportedPuzzle(grid.exportGrid(clues.c, _ -> '#', '#'), new WordOut[0], difficulty, rewards);
|
return new ExportedPuzzle(grid.exportGrid(clues.c, _ -> '#', '#'), new WordOut[0], difficulty, rewards);
|
||||||
}
|
}
|
||||||
|
|
||||||
var placed = Arrays.stream(slots).map(slot -> new Placed(slot.assign().w, slot.key(), Gridded.walk((byte) slot.key(), slot.lo(), slot.hi()).toArray())).toArray(
|
var placed = Arrays.stream(slots).map(slot -> new Placed(slot.assign().w, slot.key(), Gridded.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).toArray())).toArray(
|
||||||
Placed[]::new);
|
Placed[]::new);
|
||||||
|
|
||||||
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
|
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
|
||||||
@@ -252,7 +279,7 @@ public record Export() {
|
|||||||
|
|
||||||
for (var rc : placed) {
|
for (var rc : placed) {
|
||||||
for (var c : rc.cells) {
|
for (var c : rc.cells) {
|
||||||
val it = SwedishGenerator.IT[c];
|
val it = Masker.IT[c];
|
||||||
minR = Math.min(minR, it.r());
|
minR = Math.min(minR, it.r());
|
||||||
minC = Math.min(minC, it.c());
|
minC = Math.min(minC, it.c());
|
||||||
maxR = Math.max(maxR, it.r());
|
maxR = Math.max(maxR, it.r());
|
||||||
@@ -270,12 +297,13 @@ public record Export() {
|
|||||||
var gridv2 = new String[Math.max(0, maxR - minR + 1)];
|
var gridv2 = new String[Math.max(0, maxR - minR + 1)];
|
||||||
for (int r = minR, i = 0; r <= maxR; r++, i++) {
|
for (int r = minR, i = 0; r <= maxR; r++, i++) {
|
||||||
var row = new StringBuilder(Math.max(0, maxC - minC + 1));
|
var row = new StringBuilder(Math.max(0, maxC - minC + 1));
|
||||||
for (var c = minC; c <= maxC; c++) row.append(map.getOrDefault(Grid.offset(r, c), '#'));
|
for (var c = minC; c <= maxC; c++) row.append(map.getOrDefault(Masker.offset(r, c), '#'));
|
||||||
gridv2[i] = row.toString();
|
gridv2[i] = row.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5) words output with cropped coordinates
|
// 5) words output with cropped coordinates
|
||||||
int MIN_R = minR, MIN_C = minC;
|
int MIN_R = minR, MIN_C = minC;
|
||||||
|
val bytes = BYTES.get();
|
||||||
var wordsOut = Arrays.stream(placed).map(p -> new WordOut(
|
var wordsOut = Arrays.stream(placed).map(p -> new WordOut(
|
||||||
p.lemma,
|
p.lemma,
|
||||||
p.startRow() - MIN_R,
|
p.startRow() - MIN_R,
|
||||||
@@ -283,7 +311,7 @@ public record Export() {
|
|||||||
p.direction(),
|
p.direction(),
|
||||||
p.arrowRow() - MIN_R,
|
p.arrowRow() - MIN_R,
|
||||||
p.arrowCol() - MIN_C,
|
p.arrowCol() - MIN_C,
|
||||||
p.isReversed()
|
p.isReversed(), bytes
|
||||||
)).toArray(WordOut[]::new);
|
)).toArray(WordOut[]::new);
|
||||||
return new ExportedPuzzle(gridv2, wordsOut, difficulty, rewards);
|
return new ExportedPuzzle(gridv2, wordsOut, difficulty, rewards);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,34 +373,6 @@ public class Main {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static Clued generateClues() {
|
|
||||||
String simple = "000 3000\n" +
|
|
||||||
" 3 \n" +
|
|
||||||
" 31 \n" +
|
|
||||||
" 3\n" +
|
|
||||||
"1 \n" +
|
|
||||||
"1 \n" +
|
|
||||||
"1 2\n" +
|
|
||||||
"1 222 3";
|
|
||||||
String sampleComplex = "1 0000\n" +
|
|
||||||
"1 \n" +
|
|
||||||
"00 01 \n" +
|
|
||||||
" 1 \n" +
|
|
||||||
" 1 \n" +
|
|
||||||
" 2 1 \n" +
|
|
||||||
" 1 \n" +
|
|
||||||
"221 22\n";
|
|
||||||
String def = " 30000\n" +
|
|
||||||
"0 001 \n" +
|
|
||||||
" 1 \n" +
|
|
||||||
" 3 \n" +
|
|
||||||
" 3 \n" +
|
|
||||||
" 32 \n" +
|
|
||||||
" 32 2\n" +
|
|
||||||
"2222 3";
|
|
||||||
return Clues.parse(sampleComplex
|
|
||||||
);
|
|
||||||
}
|
|
||||||
static Clues generateNewClues(Rng rng, Opts opts) {
|
static Clues generateNewClues(Rng rng, Opts opts) {
|
||||||
var masker = new Masker(rng, new int[STACK_SIZE], Masker.Clues.createEmpty());
|
var masker = new Masker(rng, new int[STACK_SIZE], Masker.Clues.createEmpty());
|
||||||
return masker.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
|
return masker.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
|
||||||
@@ -415,7 +387,7 @@ public class Main {
|
|||||||
|
|
||||||
val slotInfo = Masker.slots(mask, dict.index());
|
val slotInfo = Masker.slots(mask, dict.index());
|
||||||
var grid = Slotinfo.grid(slotInfo);// mask.toGrid();
|
var grid = Slotinfo.grid(slotInfo);// mask.toGrid();
|
||||||
var filled = fillMask(rng, slotInfo, grid, (!Main.VERBOSE || multiThreaded));
|
var filled = fillMask(rng, slotInfo, grid);
|
||||||
|
|
||||||
if (!multiThreaded) {
|
if (!multiThreaded) {
|
||||||
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
||||||
@@ -451,7 +423,7 @@ public class Main {
|
|||||||
System.out.println(Arrays.stream(new Clued(mask).gridToString().split("\n")).map(s -> "\"" + s + "\\n\" +").collect(Collectors.joining("\n")));
|
System.out.println(Arrays.stream(new Clued(mask).gridToString().split("\n")).map(s -> "\"" + s + "\\n\" +").collect(Collectors.joining("\n")));
|
||||||
}
|
}
|
||||||
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {
|
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {
|
||||||
return new PuzzleResult(new Clued(mask), new Gridded(grid), slotInfo, filled);
|
return new PuzzleResult(new Clued(mask), new Gridded(grid, mask), slotInfo, filled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.verbose && filled.ok()) {
|
if (opts.verbose && filled.ok()) {
|
||||||
|
|||||||
@@ -2,24 +2,22 @@ package puzzle;
|
|||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import puzzle.Export.Clued;
|
import precomp.Neighbors9x8;
|
||||||
import puzzle.Export.Gridded;
|
import precomp.Neighbors9x8.rci;
|
||||||
|
|
||||||
import java.sql.Array;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
import static java.lang.Long.*;
|
import static java.lang.Long.*;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
|
|
||||||
public final class Masker {
|
public final class Masker {
|
||||||
|
|
||||||
private final Rng rng;
|
public static final rci[] IT = Neighbors9x8.IT;
|
||||||
private final int[] stack;
|
private final Rng rng;
|
||||||
private final Clues cache;
|
private final int[] stack;
|
||||||
|
private final Clues cache;
|
||||||
private final int[] activeCIdx = new int[SwedishGenerator.SIZE];
|
private final int[] activeCIdx = new int[SwedishGenerator.SIZE];
|
||||||
private final long[] activeSLo = new long[SwedishGenerator.SIZE];
|
private final long[] activeSLo = new long[SwedishGenerator.SIZE];
|
||||||
private final long[] activeSHi = new long[SwedishGenerator.SIZE];
|
private final long[] activeSHi = new long[SwedishGenerator.SIZE];
|
||||||
@@ -42,8 +40,8 @@ public final class Masker {
|
|||||||
for (int dc1 = -2; dc1 <= 2; dc1++)
|
for (int dc1 = -2; dc1 <= 2; dc1++)
|
||||||
for (int dc2 = -2; dc2 <= 2; dc2++) {
|
for (int dc2 = -2; dc2 <= 2; dc2++) {
|
||||||
val ti = IT[i];
|
val ti = IT[i];
|
||||||
MUTATE_RI[i][k++] = Grid.offset(SwedishGenerator.clamp(ti.r() + dr1 + dr2, 0, R - 1),
|
MUTATE_RI[i][k++] = offset(clamp(ti.r() + dr1 + dr2, 0, R - 1),
|
||||||
SwedishGenerator.clamp(ti.c() + dc1 + dc2, 0, C - 1));
|
clamp(ti.c() + dc1 + dc2, 0, C - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +68,27 @@ public final class Masker {
|
|||||||
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
||||||
visitor.visit(key, rayLo, rayHi);
|
visitor.visit(key, rayLo, rayHi);
|
||||||
}
|
}
|
||||||
|
private static boolean validSlotRev(long lo, long hi, int min, int key) {
|
||||||
|
long rayLo = PATH_LO[key];
|
||||||
|
long rayHi = PATH_HI[key];
|
||||||
|
// only consider clue cells
|
||||||
|
long hitsLo = rayLo & lo;
|
||||||
|
long hitsHi = rayHi & hi;
|
||||||
|
|
||||||
|
if (hitsHi != X) return (Long.bitCount(rayHi & -(1L << 63 - numberOfLeadingZeros(hitsHi) << 1)) >= min);
|
||||||
|
else if (hitsLo != X) return (Long.bitCount(rayLo & -(1L << 63 - numberOfLeadingZeros(hitsLo) << 1)) + Long.bitCount(rayHi) >= min);
|
||||||
|
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
|
||||||
|
}
|
||||||
|
private static boolean validSlot(long lo, long hi, int min, int key) {
|
||||||
|
long rayLo = PATH_LO[key];
|
||||||
|
long rayHi = PATH_HI[key];
|
||||||
|
long hitsLo = rayLo & lo;
|
||||||
|
long hitsHi = rayHi & hi;
|
||||||
|
|
||||||
|
if (hitsLo != X) return (Long.bitCount(rayLo & ((1L << numberOfTrailingZeros(hitsLo)) - 1)) >= min);
|
||||||
|
else if (hitsHi != X) return (Long.bitCount(rayLo) + Long.bitCount(rayHi & ((1L << numberOfTrailingZeros(hitsHi)) - 1)) >= min);
|
||||||
|
else return (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= min);
|
||||||
|
}
|
||||||
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
|
private static void processSlot(Clues c, SlotVisitor visitor, int key) {
|
||||||
long rayLo = PATH_LO[key];
|
long rayLo = PATH_LO[key];
|
||||||
long rayHi = PATH_HI[key];
|
long rayHi = PATH_HI[key];
|
||||||
@@ -79,10 +98,9 @@ public final class Masker {
|
|||||||
if (hitsLo != X) {
|
if (hitsLo != X) {
|
||||||
long stop = 1L << numberOfTrailingZeros(hitsLo);
|
long stop = 1L << numberOfTrailingZeros(hitsLo);
|
||||||
rayLo &= (stop - 1);
|
rayLo &= (stop - 1);
|
||||||
rayHi = 0; // any hi is beyond the stop
|
rayHi = 0;
|
||||||
} else if (hitsHi != X) {
|
} else if (hitsHi != X) {
|
||||||
long stop = 1L << numberOfTrailingZeros(hitsHi);
|
long stop = 1L << numberOfTrailingZeros(hitsHi);
|
||||||
// keep all lo (lo indices are < any hi index), but cut hi below stop
|
|
||||||
rayHi &= (stop - 1);
|
rayHi &= (stop - 1);
|
||||||
}
|
}
|
||||||
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
if (Long.bitCount(rayLo) + Long.bitCount(rayHi) >= MIN_LEN)
|
||||||
@@ -116,6 +134,8 @@ public final class Masker {
|
|||||||
for (long b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1);
|
for (long b = hi; b != X; b &= b - 1) cross += (count[64 | numberOfTrailingZeros(b)] - 1);
|
||||||
return cross * 10 + Slot.length(lo, hi);
|
return cross * 10 + Slot.length(lo, hi);
|
||||||
}
|
}
|
||||||
|
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
||||||
|
public static int offset(int r, int c) { return r | (c << 3); }
|
||||||
|
|
||||||
public long maskFitness(final Clues grid, int clueSize) {
|
public long maskFitness(final Clues grid, int clueSize) {
|
||||||
|
|
||||||
@@ -216,7 +236,8 @@ public final class Masker {
|
|||||||
|
|
||||||
// Connectiviteitscheck
|
// Connectiviteitscheck
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (int i = 0; i < numClues; i++) {
|
||||||
adjLo[i] = 0; adjHi[i] = 0;
|
adjLo[i] = 0;
|
||||||
|
adjHi[i] = 0;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < numClues; i++) {
|
for (int i = 0; i < numClues; i++) {
|
||||||
for (int j = i + 1; j < numClues; j++) {
|
for (int j = i + 1; j < numClues; j++) {
|
||||||
@@ -240,22 +261,22 @@ public final class Masker {
|
|||||||
stack[0] = 0;
|
stack[0] = 0;
|
||||||
int sp = 1;
|
int sp = 1;
|
||||||
while (sp > 0) {
|
while (sp > 0) {
|
||||||
int cur = stack[--sp];
|
int cur = stack[--sp];
|
||||||
long nLo = adjLo[cur] & ~reachedLo;
|
long nLo = adjLo[cur] & ~reachedLo;
|
||||||
long nHi = adjHi[cur] & ~reachedHi;
|
long nHi = adjHi[cur] & ~reachedHi;
|
||||||
while (nLo != 0) {
|
while (nLo != 0) {
|
||||||
long lsb = nLo & -nLo;
|
long lsb = nLo & -nLo;
|
||||||
int idx = numberOfTrailingZeros(lsb);
|
int idx = numberOfTrailingZeros(lsb);
|
||||||
reachedLo |= lsb;
|
reachedLo |= lsb;
|
||||||
stack[sp++] = idx;
|
stack[sp++] = idx;
|
||||||
nLo &= ~lsb;
|
nLo &= ~lsb;
|
||||||
}
|
}
|
||||||
while (nHi != 0) {
|
while (nHi != 0) {
|
||||||
long lsb = nHi & -nHi;
|
long lsb = nHi & -nHi;
|
||||||
int idx = 64 | numberOfTrailingZeros(lsb);
|
int idx = 64 | numberOfTrailingZeros(lsb);
|
||||||
reachedHi |= lsb;
|
reachedHi |= lsb;
|
||||||
stack[sp++] = idx;
|
stack[sp++] = idx;
|
||||||
nHi &= ~lsb;
|
nHi &= ~lsb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int count = bitCount(reachedLo) + bitCount(reachedHi);
|
int count = bitCount(reachedLo) + bitCount(reachedHi);
|
||||||
@@ -501,29 +522,13 @@ public final class Masker {
|
|||||||
return best.grid;
|
return best.grid;
|
||||||
}//@formatter:off
|
}//@formatter:off
|
||||||
@FunctionalInterface public interface SlotVisitor { void visit(int key, long lo, long hi); }
|
@FunctionalInterface public interface SlotVisitor { void visit(int key, long lo, long hi); }
|
||||||
|
//@formatter:on
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class Clues {
|
public static class Clues {
|
||||||
|
|
||||||
long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
|
long lo, hi, vlo, vhi, rlo, rhi, xlo, xhi;
|
||||||
|
|
||||||
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0, 0, 0); }
|
public static Clues createEmpty() { return new Clues(0, 0, 0, 0, 0, 0, 0, 0); }
|
||||||
public static Clued parse(String s) {
|
|
||||||
var c = createEmpty();
|
|
||||||
var lines = s.split("\n");
|
|
||||||
for (int r = 0; r < Math.min(lines.length, R); r++) {
|
|
||||||
var line = lines[r];
|
|
||||||
for (int col = 0; col < Math.min(line.length(), C); col++) {
|
|
||||||
char ch = line.charAt(col);
|
|
||||||
if (ch >= '0' && ch <= '4') {
|
|
||||||
int idx = Grid.offset(r, col);
|
|
||||||
byte dir = (byte) (ch - '0');
|
|
||||||
if ((idx & 64) == 0) c.setClueLo(1L << idx, dir);
|
|
||||||
else c.setClueHi(1L << (idx & 63), dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Clued(c);
|
|
||||||
}
|
|
||||||
public boolean cluelessLo(int idx) {
|
public boolean cluelessLo(int idx) {
|
||||||
if (!isClueLo(idx)) return false;
|
if (!isClueLo(idx)) return false;
|
||||||
clearClueLo(~(1L << idx));
|
clearClueLo(~(1L << idx));
|
||||||
@@ -536,10 +541,8 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
public boolean hasRoomForClue(int key, long packed) {
|
public boolean hasRoomForClue(int key, long packed) {
|
||||||
if (packed == X || !notClue(packed & 0x7FL) || !notClue((packed >>> 7) & 0x7FL)) return false;
|
if (packed == X || !notClue(packed & 0x7FL) || !notClue((packed >>> 7) & 0x7FL)) return false;
|
||||||
final boolean[] res = {false};
|
if (Slotinfo.increasing(key)) if (!validSlot(lo, hi, MIN_LEN, key)) return false;
|
||||||
if (Slotinfo.increasing(key)) processSlot(this, (k, lo, hi) -> res[0] = true, key);
|
return validSlotRev(lo, hi, MIN_LEN, key);
|
||||||
else processSlotRev(this, (k, lo, hi) -> res[0] = true, key);
|
|
||||||
return res[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setClueLo(long mask, byte idx) {
|
public void setClueLo(long mask, byte idx) {
|
||||||
@@ -586,16 +589,18 @@ public final class Masker {
|
|||||||
}
|
}
|
||||||
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); }
|
public Grid toGrid() { return new Grid(new byte[SwedishGenerator.SIZE], lo, hi); }
|
||||||
public boolean isValid(int minLen) {
|
public boolean isValid(int minLen) {
|
||||||
class ValidationVisitor implements SlotVisitor {
|
for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 1))) return false;
|
||||||
boolean ok = true;
|
for (var l = lo & ~xlo & ~rlo & ~vlo; l != X; l &= l - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 0))) return false;
|
||||||
@Override
|
for (var l = lo & ~xlo & rlo & ~vlo; l != X; l &= l - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 2))) return false;
|
||||||
public void visit(int key, long lo, long hi) {
|
for (var l = lo & ~xlo & rlo & vlo; l != X; l &= l - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 3))) return false;
|
||||||
if (bitCount(lo) + bitCount(hi) < minLen) ok = false;
|
for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(numberOfTrailingZeros(l), 4))) return false;
|
||||||
}
|
|
||||||
}
|
for (var h = hi & ~xhi & ~rhi & vhi; h != X; h &= h - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1))) return false;
|
||||||
ValidationVisitor v = new ValidationVisitor();
|
for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0))) return false;
|
||||||
forEachSlot(v);
|
for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2))) return false;
|
||||||
return v.ok;
|
for (var h = hi & ~xhi & rhi & vhi; h != X; h &= h - 1) if (!validSlotRev(lo, hi, minLen, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 3))) return false;
|
||||||
|
for (var h = hi & xhi & ~rhi & ~vhi; h != X; h &= h - 1) if (!validSlot(lo, hi, minLen, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 4))) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
public void forEachSlot(SlotVisitor visitor) {
|
public void forEachSlot(SlotVisitor visitor) {
|
||||||
for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1));
|
for (var l = lo & ~xlo & ~rlo & vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 1));
|
||||||
@@ -603,7 +608,7 @@ public final class Masker {
|
|||||||
for (var l = lo & ~xlo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2));
|
for (var l = lo & ~xlo & rlo & ~vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 2));
|
||||||
for (var l = lo & ~xlo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3));
|
for (var l = lo & ~xlo & rlo & vlo; l != X; l &= l - 1) processSlotRev(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 3));
|
||||||
for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4));
|
for (var l = lo & xlo & ~rlo & ~vlo; l != X; l &= l - 1) processSlot(this, visitor, Slot.packSlotKey(numberOfTrailingZeros(l), 4));
|
||||||
|
|
||||||
for (var h = hi & ~xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1));
|
for (var h = hi & ~xhi & ~rhi & vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 1));
|
||||||
for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0));
|
for (var h = hi & ~xhi & ~rhi & ~vhi; h != X; h &= h - 1) processSlot(this, visitor, Slot.packSlotKey(64 | numberOfTrailingZeros(h), 0));
|
||||||
for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2));
|
for (var h = hi & ~xhi & rhi & ~vhi; h != X; h &= h - 1) processSlotRev(this, visitor, Slot.packSlotKey((64 | numberOfTrailingZeros(h)), 2));
|
||||||
@@ -640,11 +645,10 @@ public final class Masker {
|
|||||||
|
|
||||||
static final int BIT_FOR_DIR = 3;
|
static final int BIT_FOR_DIR = 3;
|
||||||
public static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
|
public static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
|
||||||
public static int length(long lo, long hi) { return bitCount(lo) + bitCount(hi); }
|
public static int length(long lo, long hi) { return bitCount(lo) + bitCount(hi); }
|
||||||
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
||||||
public static int dir(int key) { return key & 7; }
|
public static int dir(int key) { return key & 7; }
|
||||||
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
public static boolean horiz(int d) { return (d & 1) != 0 && (d & 4) == 0; }
|
||||||
public static boolean horiz(int d) { return (d & 1) != 0 && (d & 4) == 0; }
|
public static int packSlotKey(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
||||||
public static int packSlotKey(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
import lombok.experimental.Delegate;
|
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import precomp.Neighbors9x8;
|
import precomp.Neighbors9x8;
|
||||||
import precomp.Neighbors9x8.rci;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import static java.lang.Long.*;
|
import static java.lang.Long.*;
|
||||||
import static java.lang.Long.numberOfTrailingZeros;
|
import static java.lang.Long.numberOfTrailingZeros;
|
||||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
@@ -38,9 +32,7 @@ public record SwedishGenerator() {
|
|||||||
public static final int LOG_EVERY_MS = 200;
|
public static final int LOG_EVERY_MS = 200;
|
||||||
public static final int BAR_LEN = 22;
|
public static final int BAR_LEN = 22;
|
||||||
public static final int C = Config.PUZZLE_COLS;
|
public static final int C = Config.PUZZLE_COLS;
|
||||||
public static final double CROSS_R = (C - 1) / 2.0;
|
|
||||||
public static final int R = Config.PUZZLE_ROWS;
|
public static final int R = Config.PUZZLE_ROWS;
|
||||||
public static final double CROSS_C = (R - 1) / 2.0;
|
|
||||||
public static final int SIZE = C * R;// ~18
|
public static final int SIZE = C * R;// ~18
|
||||||
public static final int SIZE_MIN_1 = SIZE - 1;// ~18
|
public static final int SIZE_MIN_1 = SIZE - 1;// ~18
|
||||||
public static final double SIZED = (double) SIZE;// ~18
|
public static final double SIZED = (double) SIZE;// ~18
|
||||||
@@ -55,7 +47,7 @@ public record SwedishGenerator() {
|
|||||||
public static final byte DASH = (byte) C_DASH;
|
public static final byte DASH = (byte) C_DASH;
|
||||||
public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L;
|
public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L;
|
||||||
public static final long RANGE_0_624 = 624L - 0L + 1L;
|
public static final long RANGE_0_624 = 624L - 0L + 1L;
|
||||||
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
public static boolean isLo(int n) { return (n & 64) == 0; }
|
||||||
interface Bit1029 {
|
interface Bit1029 {
|
||||||
|
|
||||||
static long[] bit1029() { return new long[2048]; }
|
static long[] bit1029() { return new long[2048]; }
|
||||||
@@ -64,10 +56,7 @@ public record SwedishGenerator() {
|
|||||||
static void set(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; }
|
static void set(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] |= 1L << bitIndex; }
|
||||||
static void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); }
|
static void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); }
|
||||||
}
|
}
|
||||||
static String padRight(String s, int n) {
|
|
||||||
if (s.length() >= n) return s;
|
|
||||||
return s + " ".repeat(n - s.length());
|
|
||||||
}
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class Pick {
|
public static class Pick {
|
||||||
|
|
||||||
@@ -76,29 +65,36 @@ public record SwedishGenerator() {
|
|||||||
public int count;
|
public int count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final long[] OFFSETS_D_IDX=Neighbors9x8.OFFSETS_D_IDX;
|
//@formatter:off
|
||||||
public static final rci[] IT = Neighbors9x8.IT;
|
public static record Dict(DictEntry[] index, int length) { }
|
||||||
|
@AllArgsConstructor @NoArgsConstructor static class Assign { long w; }
|
||||||
|
public static final class FillStats { public double simplicity; }
|
||||||
|
//@formatter:on
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class Grid {
|
||||||
|
|
||||||
|
public final byte[] g;
|
||||||
|
public long lo, hi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { }
|
||||||
|
|
||||||
|
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
||||||
|
|
||||||
|
public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSETS_D_IDX;
|
||||||
public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
|
public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
|
||||||
public static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
|
public static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
|
||||||
public static final long[] PATH_LO= Neighbors9x8.PATH_LO;
|
|
||||||
public static final long[] PATH_HI= Neighbors9x8.PATH_HI;
|
|
||||||
|
|
||||||
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
|
public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
|
||||||
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
|
public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
|
||||||
public static final class FillStats {
|
|
||||||
|
|
||||||
public double simplicity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) {
|
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Rng {
|
public static final class Rng {
|
||||||
|
|
||||||
@Getter private int x;
|
private int x;
|
||||||
public Rng(int seed) {
|
public Rng(int seed) {
|
||||||
var s = seed;
|
var s = seed;
|
||||||
if (s == 0) s = 1;
|
if (s == 0) s = 1;
|
||||||
@@ -114,9 +110,9 @@ public record SwedishGenerator() {
|
|||||||
}
|
}
|
||||||
public byte randint2bitByte() {
|
public byte randint2bitByte() {
|
||||||
int r = nextU32() & 7;
|
int r = nextU32() & 7;
|
||||||
if (r < 4) return (byte) r;
|
|
||||||
if (r == 4) return (byte) 4;
|
if (r == 4) return (byte) 4;
|
||||||
return (byte) (r % 4);
|
//if (r < 4) return (byte) r;
|
||||||
|
return (byte) (r & 3);
|
||||||
}
|
}
|
||||||
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; }
|
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; }
|
||||||
public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); }
|
public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); }
|
||||||
@@ -127,20 +123,6 @@ public record SwedishGenerator() {
|
|||||||
public int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); }
|
public int biasedIndexPow3(int N) { return (int) (((Math.min(nextU32(), Math.min(nextU32(), nextU32())) & 0xFFFFFFFFL) * (long) N) >>> 32); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
public static class Grid {
|
|
||||||
|
|
||||||
public final byte[] g;
|
|
||||||
public long lo, hi;
|
|
||||||
public static int offset(int r, int c) { return r | (c << 3); }
|
|
||||||
|
|
||||||
public Grid copy() {
|
|
||||||
return new Grid(g.clone(), lo, hi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
|
||||||
|
|
||||||
public static interface Lemma {
|
public static interface Lemma {
|
||||||
|
|
||||||
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
|
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
|
||||||
@@ -148,28 +130,20 @@ public record SwedishGenerator() {
|
|||||||
|
|
||||||
static long from(byte[] word) { return packShiftIn(word) | ((long) (word.length - 1) << 40); }
|
static long from(byte[] word) { return packShiftIn(word) | ((long) (word.length - 1) << 40); }
|
||||||
static long pack(long w, int shardIndex) { return w | (((long) shardIndex) << 43) | ((long) length0(w)) << 40; }
|
static long pack(long w, int shardIndex) { return w | (((long) shardIndex) << 43) | ((long) length0(w)) << 40; }
|
||||||
/* static long pack(byte[] b) {
|
|
||||||
long w = 0;
|
|
||||||
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & 31) << (i * 5);
|
|
||||||
return w;
|
|
||||||
}*/
|
|
||||||
static long packShiftIn(byte[] b) {
|
static long packShiftIn(byte[] b) {
|
||||||
long w = 0;
|
long w = 0;
|
||||||
for (int i = b.length - 1; i >= 0; i--) w = (w << 5) | ((long) b[i] & 31);
|
for (int i = b.length - 1; i >= 0; i--) w = (w << 5) | ((long) b[i] & 31);
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
static public long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); }
|
static public long from(String word) { return packShiftIn(word.getBytes(US_ASCII)) | ((long) (word.length() - 1) << 40); }
|
||||||
static byte byteAt(long word, int idx) { return (byte) ((word >>> ((long) idx * 5L)) & 0b11111L); }
|
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111L); }
|
||||||
static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); }
|
static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); }
|
||||||
static ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[MAX_WORD_LENGTH]);
|
public static String asWord(long word, byte[] bytes) {
|
||||||
public static String asWord(long word) {
|
|
||||||
var b = BYTES.get();
|
|
||||||
int bi = 0;
|
int bi = 0;
|
||||||
|
|
||||||
for (long w = word & LETTER_MASK; w != 0; w >>>= 5) {
|
for (long w = word & LETTER_MASK; w != 0; w >>>= 5) {
|
||||||
b[bi++] = (byte) ((w & 31) | 64); // neem laagste 5 bits
|
bytes[bi++] = (byte) ((w & 31) | 64); // neem laagste 5 bits
|
||||||
}
|
}
|
||||||
return new String(b, 0, bi, US_ASCII);
|
return new String(bytes, 0, bi, US_ASCII);
|
||||||
}
|
}
|
||||||
static int unpackIndex(long w) { return (int) (w >>> 40); }
|
static int unpackIndex(long w) { return (int) (w >>> 40); }
|
||||||
static int unpackShardIndex(long w) { return (int) (w >>> 43); }
|
static int unpackShardIndex(long w) { return (int) (w >>> 43); }
|
||||||
@@ -177,15 +151,6 @@ public record SwedishGenerator() {
|
|||||||
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
|
static int unpackLetters(long w) { return (int) (w & LETTER_MASK); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static record Dict(DictEntry[] index, int length) { }
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
static class Assign {
|
|
||||||
|
|
||||||
long w;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static record Slotinfo(int key, long lo, long hi, int score, Assign assign, DictEntry entry) {
|
public static record Slotinfo(int key, long lo, long hi, int score, Assign assign, DictEntry entry) {
|
||||||
|
|
||||||
public static int wordCount(int k, Slotinfo[] arr) {
|
public static int wordCount(int k, Slotinfo[] arr) {
|
||||||
@@ -203,34 +168,29 @@ public record SwedishGenerator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLo(int n) { return (n & 64) == 0; }
|
|
||||||
public static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) {
|
public static long patternForSlot(final long glo, final long ghi, final byte[] g, final int key, final long lo, final long hi) {
|
||||||
if (((lo & glo) | (hi & ghi)) == X) return 0;
|
if (((lo & glo) | (hi & ghi)) == X) return X;
|
||||||
long p = 0;
|
long p = 0;
|
||||||
int n = 0;
|
int n = 0, offset, idx;
|
||||||
if (Slotinfo.increasing(key)) {
|
if (Slotinfo.increasing(key)) {
|
||||||
for (long b = lo & glo; b != X; b &= b - 1) {
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
||||||
int idx = numberOfTrailingZeros(b);
|
idx = numberOfTrailingZeros(b);
|
||||||
int i = bitCount(lo & ((1L << idx) - 1));
|
p |= ((long) (bitCount(lo & ((1L << idx) - 1)) * 26 + g[idx])) << (n++ << 3);
|
||||||
p |= ((long) (i * 26 + g[idx])) << (n++ << 3);
|
|
||||||
}
|
}
|
||||||
int offset = bitCount(lo);
|
offset = bitCount(lo);
|
||||||
for (long b = hi & ghi; b != X; b &= b - 1) {
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
||||||
int idx = numberOfTrailingZeros(b);
|
idx = numberOfTrailingZeros(b);
|
||||||
int i = offset + bitCount(hi & ((1L << idx) - 1));
|
p |= ((long) ((offset + bitCount(hi & ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
|
||||||
p |= ((long) (i * 26 + g[64 | idx])) << (n++ << 3);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int offset = bitCount(hi);
|
offset = bitCount(hi);
|
||||||
for (long b = hi & ghi; b != X; b &= b - 1) {
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
||||||
int idx = numberOfTrailingZeros(b);
|
idx = numberOfTrailingZeros(b);
|
||||||
int i = bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)));
|
p |= ((long) (bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))) * 26 + g[64 | idx])) << (n++ << 3);
|
||||||
p |= ((long) (i * 26 + g[64 | idx])) << (n++ << 3);
|
|
||||||
}
|
}
|
||||||
for (long b = lo & glo; b != X; b &= b - 1) {
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
||||||
int idx = numberOfTrailingZeros(b);
|
idx = numberOfTrailingZeros(b);
|
||||||
int i = offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)));
|
p |= ((long) ((offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)))) * 26 + g[idx])) << (n++ << 3);
|
||||||
p |= ((long) (i * 26 + g[idx])) << (n++ << 3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
@@ -268,8 +228,7 @@ public record SwedishGenerator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
|
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
|
||||||
final Grid grid,
|
final Grid grid) {
|
||||||
final boolean NO_LOG) {
|
|
||||||
val used = new long[2048];
|
val used = new long[2048];
|
||||||
val bitset = new long[2500];
|
val bitset = new long[2500];
|
||||||
val g = grid.g;
|
val g = grid.g;
|
||||||
@@ -283,27 +242,6 @@ public record SwedishGenerator() {
|
|||||||
int lastMRV;
|
int lastMRV;
|
||||||
long lastLog = t0, glo = grid.lo, ghi = grid.hi;
|
long lastLog = t0, glo = grid.lo, ghi = grid.hi;
|
||||||
Pick current = CARRIER;
|
Pick current = CARRIER;
|
||||||
void renderProgress() {
|
|
||||||
var now = System.currentTimeMillis();
|
|
||||||
if ((now - lastLog) < LOG_EVERY_MS) return;
|
|
||||||
lastLog = (now);
|
|
||||||
var done = 0;
|
|
||||||
for (var lemma : slots) {
|
|
||||||
if (lemma.assign.w != X) done++;
|
|
||||||
}
|
|
||||||
var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100);
|
|
||||||
var filled = Math.min(BAR_LEN, (int) Math.floor((pct / 100.0) * BAR_LEN));
|
|
||||||
var bar = "[" + "#".repeat(filled) + "-".repeat(BAR_LEN - filled) + "]";
|
|
||||||
var elapsed = String.format(Locale.ROOT, "%.1fs", (now - t0) / 1000.0);
|
|
||||||
|
|
||||||
var msg = String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"%s %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %s",
|
|
||||||
bar, done, TOTAL, nodes, backtracks, lastMRV, elapsed
|
|
||||||
);
|
|
||||||
System.out.print("\r" + padRight(msg, 120));
|
|
||||||
System.out.flush();
|
|
||||||
}
|
|
||||||
boolean placeWordDec(final long lo, final long hi, final long w) {
|
boolean placeWordDec(final long lo, final long hi, final long w) {
|
||||||
int idx;
|
int idx;
|
||||||
int bcHi = bitCount(hi);
|
int bcHi = bitCount(hi);
|
||||||
@@ -358,7 +296,6 @@ public record SwedishGenerator() {
|
|||||||
if (count <= 1) break;
|
if (count <= 1) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Re-calculate for the best slot to get actual indices
|
|
||||||
if (best == null) {
|
if (best == null) {
|
||||||
current = PICK_DONE;
|
current = PICK_DONE;
|
||||||
return;
|
return;
|
||||||
@@ -383,7 +320,6 @@ public record SwedishGenerator() {
|
|||||||
}
|
}
|
||||||
val info = pick.indices;
|
val info = pick.indices;
|
||||||
lastMRV = pick.count;
|
lastMRV = pick.count;
|
||||||
if (!NO_LOG) renderProgress();
|
|
||||||
|
|
||||||
val s = pick.slot;
|
val s = pick.slot;
|
||||||
val inc = Slotinfo.increasing(s.key);
|
val inc = Slotinfo.increasing(s.key);
|
||||||
@@ -397,11 +333,8 @@ public record SwedishGenerator() {
|
|||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
||||||
|
|
||||||
for (var t = 0; t < tries; t++) {
|
for (var t = 0; t < tries; t++) {
|
||||||
//var r = rng.nextFloat();
|
var w = entry.words[idxs[rng.biasedIndexPow3(L - 1)]];
|
||||||
//var idxInArray = (int) (r * r * r * (L - 1));
|
var lemIdx = Lemma.unpackIndex(w);
|
||||||
int idxInArray = rng.biasedIndexPow3(L - 1);
|
|
||||||
var w = entry.words[idxs[idxInArray/*(int) (r * r * r * (L - 1))*/]];
|
|
||||||
var lemIdx = Lemma.unpackIndex(w);
|
|
||||||
if (Bit1029.get(used, lemIdx)) continue;
|
if (Bit1029.get(used, lemIdx)) continue;
|
||||||
low = glo;
|
low = glo;
|
||||||
top = ghi;
|
top = ghi;
|
||||||
@@ -427,8 +360,7 @@ public record SwedishGenerator() {
|
|||||||
|
|
||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
|
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
|
||||||
for (var t = 0; t < tries; t++) {
|
for (var t = 0; t < tries; t++) {
|
||||||
// double r = rng.nextFloat();
|
var w = entry.words[rng.biasedIndexPow3(N - 1)];
|
||||||
var w = entry.words[rng.biasedIndexPow3(N - 1)/*(int) (r * r * r * (N - 1))*/];
|
|
||||||
var lemIdx = Lemma.unpackIndex(w);
|
var lemIdx = Lemma.unpackIndex(w);
|
||||||
if (Bit1029.get(used, lemIdx)) continue;
|
if (Bit1029.get(used, lemIdx)) continue;
|
||||||
low = glo;
|
low = glo;
|
||||||
@@ -454,11 +386,8 @@ public record SwedishGenerator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial render (same feel)
|
|
||||||
var solver = new Solver();
|
var solver = new Solver();
|
||||||
if (!NO_LOG) solver.renderProgress();
|
var ok = solver.backtrack(0);
|
||||||
var ok = solver.backtrack(0);
|
|
||||||
// final progress line
|
|
||||||
grid.lo = solver.glo;
|
grid.lo = solver.glo;
|
||||||
grid.hi = solver.ghi;
|
grid.hi = solver.ghi;
|
||||||
|
|
||||||
|
|||||||
@@ -18,17 +18,17 @@ public class ConnectivityTest {
|
|||||||
// Clue 1: (0,0) Right. Slot: (0,1), (0,2), (0,3)
|
// Clue 1: (0,0) Right. Slot: (0,1), (0,2), (0,3)
|
||||||
// Clue 2: (1,2) Up. Slot: (0,2)
|
// Clue 2: (1,2) Up. Slot: (0,2)
|
||||||
// Ze zijn NIET 8-naburig, maar wel verbonden via slot op (0,2)
|
// Ze zijn NIET 8-naburig, maar wel verbonden via slot op (0,2)
|
||||||
singleComp.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1);
|
singleComp.setClueLo(1L << Masker.offset(0, 0), (byte)1);
|
||||||
singleComp.setClueLo(1L << SwedishGenerator.Grid.offset(2,2), (byte)2); // Up van (2,2) naar (1,2), (0,2)
|
singleComp.setClueLo(1L << Masker.offset(2, 2), (byte)2); // Up van (2,2) naar (1,2), (0,2)
|
||||||
|
|
||||||
long fitnessSingle = masker.maskFitness(singleComp, 2);
|
long fitnessSingle = masker.maskFitness(singleComp, 2);
|
||||||
|
|
||||||
// 2. Maak een masker met twee eilandjes van clues
|
// 2. Maak een masker met twee eilandjes van clues
|
||||||
Clues twoIslands = Clues.createEmpty();
|
Clues twoIslands = Clues.createEmpty();
|
||||||
twoIslands.setClueLo(1L << SwedishGenerator.Grid.offset(0,0), (byte)1);
|
twoIslands.setClueLo(1L << Masker.offset(0, 0), (byte)1);
|
||||||
twoIslands.setClueLo( SwedishGenerator.Grid.offset(7,7) < 64 ? 1L << SwedishGenerator.Grid.offset(7,7) : 0, (byte)1);
|
twoIslands.setClueLo(Masker.offset(7, 7) < 64 ? 1L << Masker.offset(7, 7) : 0, (byte)1);
|
||||||
// Voor de zekerheid checken we of offset(7,7) in lo of hi zit
|
// Voor de zekerheid checken we of offset(7,7) in lo of hi zit
|
||||||
int off77 = SwedishGenerator.Grid.offset(7,7);
|
int off77 = Masker.offset(7, 7);
|
||||||
if (off77 < 64) twoIslands.setClueLo(1L << off77, (byte)1);
|
if (off77 < 64) twoIslands.setClueLo(1L << off77, (byte)1);
|
||||||
else twoIslands.setClueHi(1L << (off77 - 64), (byte)1);
|
else twoIslands.setClueHi(1L << (off77 - 64), (byte)1);
|
||||||
|
|
||||||
@@ -49,8 +49,8 @@ public class ConnectivityTest {
|
|||||||
// Twee clues naast elkaar, maar slots kruisen niet.
|
// Twee clues naast elkaar, maar slots kruisen niet.
|
||||||
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
// Clue 1: (1,1) Right. Slot (1,2), (1,3), (1,4)
|
||||||
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
// Clue 2: (2,1) Right. Slot (2,2), (2,3), (2,4)
|
||||||
clues.setClueLo(1L << SwedishGenerator.Grid.offset(1,1), (byte)1);
|
clues.setClueLo(1L << Masker.offset(1, 1), (byte)1);
|
||||||
clues.setClueLo(1L << SwedishGenerator.Grid.offset(2,1), (byte)1);
|
clues.setClueLo(1L << Masker.offset(2, 1), (byte)1);
|
||||||
|
|
||||||
long fitness = masker.maskFitness(clues, 2);
|
long fitness = masker.maskFitness(clues, 2);
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ public class ConnectivityTest {
|
|||||||
// Clue A: (2,0) Right. Slot: (2,1), (2,2), (2,3), ...
|
// Clue A: (2,0) Right. Slot: (2,1), (2,2), (2,3), ...
|
||||||
// Clue B: (1,2) Corner Down. Word starts at (1,3) en gaat omlaag: (1,3), (2,3), (3,3)...
|
// Clue B: (1,2) Corner Down. Word starts at (1,3) en gaat omlaag: (1,3), (2,3), (3,3)...
|
||||||
// Ze kruisen op (2,3).
|
// Ze kruisen op (2,3).
|
||||||
clues.setClueLo(1L << SwedishGenerator.Grid.offset(2,0), (byte)1); // Right
|
clues.setClueLo(1L << Masker.offset(2, 0), (byte)1); // Right
|
||||||
clues.setClueLo(1L << SwedishGenerator.Grid.offset(1,2), (byte)4); // Corner Down
|
clues.setClueLo(1L << Masker.offset(1, 2), (byte)4); // Corner Down
|
||||||
|
|
||||||
long fitness = masker.maskFitness(clues, 2);
|
long fitness = masker.maskFitness(clues, 2);
|
||||||
System.out.println("[DEBUG_LOG] Fitness corner clue connected: " + fitness);
|
System.out.println("[DEBUG_LOG] Fitness corner clue connected: " + fitness);
|
||||||
@@ -77,11 +77,11 @@ public class ConnectivityTest {
|
|||||||
// Als ze verbonden zijn, is de penalty voor eilandjes 0.
|
// Als ze verbonden zijn, is de penalty voor eilandjes 0.
|
||||||
// We vergelijken met een island scenario (2 clues die elkaar NIET raken)
|
// We vergelijken met een island scenario (2 clues die elkaar NIET raken)
|
||||||
Clues island = Clues.createEmpty();
|
Clues island = Clues.createEmpty();
|
||||||
int offA = SwedishGenerator.Grid.offset(2,0);
|
int offA = Masker.offset(2, 0);
|
||||||
if (offA < 64) island.setClueLo(1L << offA, (byte)1);
|
if (offA < 64) island.setClueLo(1L << offA, (byte)1);
|
||||||
else island.setClueHi(1L << (offA - 64), (byte)1);
|
else island.setClueHi(1L << (offA - 64), (byte)1);
|
||||||
|
|
||||||
int offB = SwedishGenerator.Grid.offset(7,7);
|
int offB = Masker.offset(7, 7);
|
||||||
if (offB < 64) island.setClueLo(1L << offB, (byte)1);
|
if (offB < 64) island.setClueLo(1L << offB, (byte)1);
|
||||||
else island.setClueHi(1L << (offB - 64), (byte)1);
|
else island.setClueHi(1L << (offB - 64), (byte)1);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class CornerClueTest {
|
|||||||
void testCornerDownSlot() {
|
void testCornerDownSlot() {
|
||||||
Clues clues = Clues.createEmpty();
|
Clues clues = Clues.createEmpty();
|
||||||
// Clue op (0,0), type 4 (Corner Down)
|
// Clue op (0,0), type 4 (Corner Down)
|
||||||
int idx = SwedishGenerator.Grid.offset(0,0);
|
int idx = Masker.offset(0, 0);
|
||||||
clues.setClueLo(1L << idx, (byte)4);
|
clues.setClueLo(1L << idx, (byte)4);
|
||||||
|
|
||||||
assertEquals(4, clues.getDir(idx));
|
assertEquals(4, clues.getDir(idx));
|
||||||
@@ -22,10 +22,10 @@ public class CornerClueTest {
|
|||||||
if (Masker.Slot.dir(key) == 4) {
|
if (Masker.Slot.dir(key) == 4) {
|
||||||
found[0] = true;
|
found[0] = true;
|
||||||
// Woord zou moeten starten op (0,1)
|
// Woord zou moeten starten op (0,1)
|
||||||
int startIdx = SwedishGenerator.Grid.offset(0, 1);
|
int startIdx = Masker.offset(0, 1);
|
||||||
assertTrue((lo & (1L << startIdx)) != 0, "Slot should start at (0,1)");
|
assertTrue((lo & (1L << startIdx)) != 0, "Slot should start at (0,1)");
|
||||||
// En omlaag gaan
|
// En omlaag gaan
|
||||||
int secondIdx = SwedishGenerator.Grid.offset(1, 1);
|
int secondIdx = Masker.offset(1, 1);
|
||||||
assertTrue((lo & (1L << secondIdx)) != 0, "Slot should continue to (1,1)");
|
assertTrue((lo & (1L << secondIdx)) != 0, "Slot should continue to (1,1)");
|
||||||
|
|
||||||
// Lengte van het slot zou 8 moeten zijn (van rij 0 t/m 7 in kolom 1)
|
// Lengte van het slot zou 8 moeten zijn (van rij 0 t/m 7 in kolom 1)
|
||||||
@@ -38,7 +38,7 @@ public class CornerClueTest {
|
|||||||
@Test
|
@Test
|
||||||
void testCornerDownExtraction() {
|
void testCornerDownExtraction() {
|
||||||
Clues clues = Clues.createEmpty();
|
Clues clues = Clues.createEmpty();
|
||||||
int idx = SwedishGenerator.Grid.offset(0,0);
|
int idx = Masker.offset(0, 0);
|
||||||
clues.setClueLo(1L << idx, (byte)4);
|
clues.setClueLo(1L << idx, (byte)4);
|
||||||
|
|
||||||
DictEntry[] dict = DictData.DICT.index();
|
DictEntry[] dict = DictData.DICT.index();
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public final class DictJavaGeneratorMulti {
|
|||||||
try (var lines = Files.lines(wordsPath, StandardCharsets.UTF_8)) {
|
try (var lines = Files.lines(wordsPath, StandardCharsets.UTF_8)) {
|
||||||
lines.forEach(line -> {
|
lines.forEach(line -> {
|
||||||
CsvIndexService.lineToLemma(line, w -> {
|
CsvIndexService.lineToLemma(line, w -> {
|
||||||
String word = Lemma.asWord(w);
|
String word = Lemma.asWord(w, Export.BYTES.get());
|
||||||
String[] clues = CsvIndexService.lineToClue(line);
|
String[] clues = CsvIndexService.lineToClue(line);
|
||||||
int simpel = CsvIndexService.lineToSimpel(line);
|
int simpel = CsvIndexService.lineToSimpel(line);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import puzzle.Export.Clued;
|
import puzzle.Export.Clued;
|
||||||
@@ -13,13 +12,11 @@ import puzzle.SwedishGenerator.FillResult;
|
|||||||
import puzzle.SwedishGenerator.Lemma;
|
import puzzle.SwedishGenerator.Lemma;
|
||||||
import puzzle.SwedishGenerator.Slotinfo;
|
import puzzle.SwedishGenerator.Slotinfo;
|
||||||
import puzzle.SwedishGeneratorTest.Idx;
|
import puzzle.SwedishGeneratorTest.Idx;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static puzzle.ExportFormatTest.Clue.RIGHT;
|
import static puzzle.Export.Clue.LEFT;
|
||||||
|
import static puzzle.Export.Clue.RIGHT;
|
||||||
import static puzzle.SwedishGenerator.C;
|
import static puzzle.SwedishGenerator.C;
|
||||||
import static puzzle.Masker.Clues;
|
import static puzzle.Masker.Clues;
|
||||||
import static puzzle.SwedishGenerator.FillStats;
|
import static puzzle.SwedishGenerator.FillStats;
|
||||||
@@ -34,25 +31,6 @@ import static puzzle.SwedishGeneratorTest.TEST;
|
|||||||
|
|
||||||
public class ExportFormatTest {
|
public class ExportFormatTest {
|
||||||
|
|
||||||
static final byte CLUE_DOWN = 0;
|
|
||||||
static final byte CLUE_RIGHT = 1;
|
|
||||||
static final byte CLUE_UP = 2;
|
|
||||||
static final byte CLUE_LEFT = 3;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
enum Clue {
|
|
||||||
DOWN(CLUE_DOWN),
|
|
||||||
RIGHT(CLUE_RIGHT),
|
|
||||||
UP(CLUE_UP),
|
|
||||||
LEFT(CLUE_LEFT);
|
|
||||||
Clue(byte dir) {
|
|
||||||
this.dir = dir;
|
|
||||||
this.clueDir = dir;
|
|
||||||
}
|
|
||||||
final byte dir;
|
|
||||||
final int clueDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExportFormatFromFilled() {
|
void testExportFormatFromFilled() {
|
||||||
val clues = Clues.createEmpty();
|
val clues = Clues.createEmpty();
|
||||||
@@ -60,11 +38,11 @@ public class ExportFormatTest {
|
|||||||
clues.setClueLo(Idx.IDX_0_0.lo, RIGHT.dir);
|
clues.setClueLo(Idx.IDX_0_0.lo, RIGHT.dir);
|
||||||
// This creates a slot starting at (0,1)
|
// This creates a slot starting at (0,1)
|
||||||
// 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
|
||||||
clues.setClueLo(Idx.IDX_0_5.lo, CLUE_LEFT);
|
clues.setClueLo(Idx.IDX_0_5.lo, LEFT.dir);
|
||||||
var grid = new Gridded(clues.toGrid());
|
var grid = new Gridded(clues);
|
||||||
|
|
||||||
// key = (cellIndex << 2) | (direction)
|
// key = (cellIndex << 2) | (direction)
|
||||||
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
var key = Slot.packSlotKey(0, RIGHT.dir);
|
||||||
var lo = (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3) | (1L << OFF_0_4);
|
var lo = (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3) | (1L << OFF_0_4);
|
||||||
|
|
||||||
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, 0L, TEST));
|
assertTrue(placeWord(grid.grid(), grid.grid().g, key, lo, 0L, TEST));
|
||||||
@@ -112,7 +90,7 @@ public class ExportFormatTest {
|
|||||||
var grid = SwedishGeneratorTest.createEmpty();
|
var grid = SwedishGeneratorTest.createEmpty();
|
||||||
val clues = Clues.createEmpty();
|
val clues = Clues.createEmpty();
|
||||||
var fillResult = new FillResult(true, 0, 0, 0, 0, new FillStats());
|
var fillResult = new FillResult(true, 0, 0, 0, 0, new FillStats());
|
||||||
var puzzleResult = new PuzzleResult(new Clued(clues), new Gridded(grid), new Slotinfo[0], fillResult);
|
var puzzleResult = new PuzzleResult(new Clued(clues), new Gridded(grid, clues), new Slotinfo[0], fillResult);
|
||||||
|
|
||||||
var exported = puzzleResult.exportFormatFromFilled(1, new Rewards(0, 0, 0));
|
var exported = puzzleResult.exportFormatFromFilled(1, new Rewards(0, 0, 0));
|
||||||
|
|
||||||
@@ -133,13 +111,13 @@ public class ExportFormatTest {
|
|||||||
val words = entry.words();
|
val words = entry.words();
|
||||||
for (int i = 0; i < Math.min(words.length, 5); i++) {
|
for (int i = 0; i < Math.min(words.length, 5); i++) {
|
||||||
val wordVal = words[i];
|
val wordVal = words[i];
|
||||||
val word = Lemma.asWord(wordVal);
|
val word = Lemma.asWord(wordVal, Export.BYTES.get());
|
||||||
val assigned = new Assign(wordVal);
|
val assigned = new Assign(wordVal);
|
||||||
val shard = Meta.shardKey(assigned.w);
|
val shard = Meta.shardKey(assigned.w);
|
||||||
val clueRec = Meta.readRecord(shard, i);
|
val clueRec = Meta.readRecord(shard, i);
|
||||||
|
|
||||||
assertNotNull(clueRec);
|
assertNotNull(clueRec);
|
||||||
assertEquals(word, Lemma.asWord(clueRec.w()));
|
assertEquals(word, Lemma.asWord(clueRec.w(), Export.BYTES.get()));
|
||||||
assertTrue(clueRec.simpel() >= 0);
|
assertTrue(clueRec.simpel() >= 0);
|
||||||
assertTrue(clueRec.clues().length > 0);
|
assertTrue(clueRec.clues().length > 0);
|
||||||
}
|
}
|
||||||
@@ -160,7 +138,7 @@ public class ExportFormatTest {
|
|||||||
int idx = -1;
|
int idx = -1;
|
||||||
long[] words = entry.words();
|
long[] words = entry.words();
|
||||||
for (int i = 0; i < words.length; i++) {
|
for (int i = 0; i < words.length; i++) {
|
||||||
if (Lemma.asWord(words[i]).equals(wStr)) {
|
if (Lemma.asWord(words[i], Export.BYTES.get()).equals(wStr)) {
|
||||||
idx = i;
|
idx = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -170,7 +148,7 @@ public class ExportFormatTest {
|
|||||||
val shard = Meta.shardKey(w);
|
val shard = Meta.shardKey(w);
|
||||||
val clueRec = Meta.readRecord(shard, idx);
|
val clueRec = Meta.readRecord(shard, idx);
|
||||||
assertNotNull(clueRec);
|
assertNotNull(clueRec);
|
||||||
assertEquals(wStr, Lemma.asWord(clueRec.w()));
|
assertEquals(wStr, Lemma.asWord(clueRec.w(), Export.BYTES.get()));
|
||||||
// Check some expected complexity values (from CSV head output, column 3)
|
// Check some expected complexity values (from CSV head output, column 3)
|
||||||
if (wStr.equals("EEN")) {
|
if (wStr.equals("EEN")) {
|
||||||
assertEquals(451, clueRec.simpel());
|
assertEquals(451, clueRec.simpel());
|
||||||
|
|||||||
@@ -10,20 +10,18 @@ import puzzle.Export.LetterVisit.LetterAt;
|
|||||||
import puzzle.Export.PuzzleResult;
|
import puzzle.Export.PuzzleResult;
|
||||||
import puzzle.Export.Rewards;
|
import puzzle.Export.Rewards;
|
||||||
import puzzle.Main.Opts;
|
import puzzle.Main.Opts;
|
||||||
import puzzle.Masker.Clues;
|
|
||||||
import puzzle.SwedishGenerator.Rng;
|
import puzzle.SwedishGenerator.Rng;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static puzzle.ExportFormatTest.Clue.DOWN;
|
import static puzzle.Export.Clue.DOWN;
|
||||||
import static puzzle.ExportFormatTest.Clue.LEFT;
|
import static puzzle.Export.Clue.LEFT;
|
||||||
import static puzzle.ExportFormatTest.Clue.RIGHT;
|
import static puzzle.Export.Clue.RIGHT;
|
||||||
import static puzzle.ExportFormatTest.Clue.UP;
|
import static puzzle.Export.Clue.UP;
|
||||||
import static puzzle.SwedishGenerator.Dict;
|
import static puzzle.SwedishGenerator.Dict;
|
||||||
import static puzzle.SwedishGenerator.Lemma;
|
import static puzzle.SwedishGenerator.Lemma;
|
||||||
import static puzzle.SwedishGenerator.STACK_SIZE;
|
|
||||||
import static puzzle.SwedishGenerator.Slotinfo;
|
import static puzzle.SwedishGenerator.Slotinfo;
|
||||||
import static puzzle.SwedishGenerator.fillMask;
|
import static puzzle.SwedishGenerator.fillMask;
|
||||||
import static puzzle.SwedishGeneratorTest.AB;
|
import static puzzle.SwedishGeneratorTest.AB;
|
||||||
@@ -66,7 +64,7 @@ public class MainTest {
|
|||||||
var clues = Masker.Clues.createEmpty();
|
var clues = Masker.Clues.createEmpty();
|
||||||
val key = Masker.Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
val key = Masker.Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
||||||
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
||||||
var grid = new Gridded(clues.toGrid());
|
var grid = new Gridded(clues);
|
||||||
val g = grid.grid().g;
|
val g = grid.grid().g;
|
||||||
GridBuilder.placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
|
GridBuilder.placeWord(grid.grid(), g, key, (1L << OFF_0_1) | (1L << OFF_0_2), 0, AB);
|
||||||
|
|
||||||
@@ -74,11 +72,11 @@ public class MainTest {
|
|||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
var s = slots[0];
|
var s = slots[0];
|
||||||
assertEquals(8, Masker.Slot.length(s.lo(), s.hi()));
|
assertEquals(8, Masker.Slot.length(s.lo(), s.hi()));
|
||||||
var cells = s.walk().toArray();
|
var cells = Gridded.cellWalk((byte) s.key(), s.lo(), s.hi()).toArray();
|
||||||
assertEquals(0, SwedishGenerator.IT[cells[0]].r());
|
assertEquals(0, Masker.IT[cells[0]].r());
|
||||||
assertEquals(1, SwedishGenerator.IT[cells[0]].c());
|
assertEquals(1, Masker.IT[cells[0]].c());
|
||||||
assertEquals(0, SwedishGenerator.IT[cells[1]].r());
|
assertEquals(0, Masker.IT[cells[1]].r());
|
||||||
assertEquals(2, SwedishGenerator.IT[cells[1]].c());
|
assertEquals(2, Masker.IT[cells[1]].c());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -98,8 +96,8 @@ public class MainTest {
|
|||||||
clues.forEachSlot((key, lo, hi) -> {
|
clues.forEachSlot((key, lo, hi) -> {
|
||||||
count.incrementAndGet();
|
count.incrementAndGet();
|
||||||
assertEquals(8, Long.bitCount(lo) + Long.bitCount(hi));
|
assertEquals(8, Long.bitCount(lo) + Long.bitCount(hi));
|
||||||
assertEquals(0, SwedishGenerator.IT[Long.numberOfTrailingZeros(lo)].r());
|
assertEquals(0, Masker.IT[Long.numberOfTrailingZeros(lo)].r());
|
||||||
assertEquals(1, SwedishGenerator.IT[Long.numberOfTrailingZeros(lo)].c());
|
assertEquals(1, Masker.IT[Long.numberOfTrailingZeros(lo)].c());
|
||||||
});
|
});
|
||||||
assertEquals(1, count.get());
|
assertEquals(1, count.get());
|
||||||
}
|
}
|
||||||
@@ -115,7 +113,7 @@ public class MainTest {
|
|||||||
var clues = new Clued(Masker.Clues.createEmpty());
|
var clues = new Clued(Masker.Clues.createEmpty());
|
||||||
val key = Masker.Slot.packSlotKey(OFF_2_1, CLUE_UP);
|
val key = Masker.Slot.packSlotKey(OFF_2_1, CLUE_UP);
|
||||||
clues.setClueLo(IDX_2_1.lo, CLUE_UP);
|
clues.setClueLo(IDX_2_1.lo, CLUE_UP);
|
||||||
var grid = new Gridded(clues.toGrid());
|
var grid = new Gridded(clues.c());
|
||||||
|
|
||||||
// Test set/get
|
// Test set/get
|
||||||
GridBuilder.placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ);
|
GridBuilder.placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_1_1) | (1L << OFF_0_1), 0, AZ);
|
||||||
@@ -184,7 +182,7 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
void testFiller2() {
|
void testFiller2() {
|
||||||
val rng = new Rng(-343913721);
|
val rng = new Rng(-343913721);
|
||||||
val mask = Clues.parse(
|
val mask = Clued.parse(
|
||||||
"1 000000\n" +
|
"1 000000\n" +
|
||||||
"1 \n" +
|
"1 \n" +
|
||||||
"1 \n" +
|
"1 \n" +
|
||||||
@@ -203,7 +201,7 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
void testFiller() {
|
void testFiller() {
|
||||||
val rng = new Rng(-343913721);
|
val rng = new Rng(-343913721);
|
||||||
val mask = Clues.parse(
|
val mask = Clued.parse(
|
||||||
" 3 300\n" +
|
" 3 300\n" +
|
||||||
" 1 \n" +
|
" 1 \n" +
|
||||||
" 1 \n" +
|
" 1 \n" +
|
||||||
@@ -214,13 +212,13 @@ public class MainTest {
|
|||||||
"21 22 3");
|
"21 22 3");
|
||||||
var slotInfo = Masker.slots(mask.c(), dict.index());
|
var slotInfo = Masker.slots(mask.c(), dict.index());
|
||||||
var grid = Slotinfo.grid(slotInfo);
|
var grid = Slotinfo.grid(slotInfo);
|
||||||
var filled = fillMask(rng, slotInfo, grid, false);
|
var filled = fillMask(rng, slotInfo, grid);
|
||||||
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
||||||
Assertions.assertEquals(13, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
|
Assertions.assertEquals(13, Slotinfo.wordCount(0, slotInfo), "Number of assigned words changed");
|
||||||
Assertions.assertEquals("WAANZIN", Lemma.asWord(slotInfo[0].assign().w));
|
Assertions.assertEquals("WAANZIN", Lemma.asWord(slotInfo[0].assign().w, Export.BYTES.get()));
|
||||||
Assertions.assertEquals(-1L, grid.lo);
|
Assertions.assertEquals(-1L, grid.lo);
|
||||||
Assertions.assertEquals(-1L, grid.hi);
|
Assertions.assertEquals(-1L, grid.hi);
|
||||||
var g = new Gridded(grid);
|
var g = new Gridded(grid, mask.c());
|
||||||
g.gridToString(mask.c());
|
g.gridToString(mask.c());
|
||||||
var aa = new PuzzleResult(mask, g, slotInfo, filled).exportFormatFromFilled(1, new Rewards(1, 1, 1));
|
var aa = new PuzzleResult(mask, g, slotInfo, filled).exportFormatFromFilled(1, new Rewards(1, 1, 1));
|
||||||
System.out.println(String.join("\n", aa.grid()));
|
System.out.println(String.join("\n", aa.grid()));
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import puzzle.Export.Clued;
|
||||||
|
import puzzle.Export.Gridded;
|
||||||
import puzzle.SwedishGenerator.DictEntry;
|
import puzzle.SwedishGenerator.DictEntry;
|
||||||
import puzzle.SwedishGenerator.Rng;
|
import puzzle.SwedishGenerator.Rng;
|
||||||
import puzzle.SwedishGenerator.Slotinfo;
|
import puzzle.SwedishGenerator.Slotinfo;
|
||||||
import puzzle.SwedishGenerator.Grid;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static puzzle.SwedishGenerator.fillMask;
|
import static puzzle.SwedishGenerator.fillMask;
|
||||||
|
|
||||||
public class PerformanceTest {
|
public class PerformanceTest {
|
||||||
|
|
||||||
final DictEntry[] EN = DictData.DICT.index();
|
final DictEntry[] EN = DictData.DICT.index();
|
||||||
@Test
|
@Test
|
||||||
void testPerformance() {
|
void testPerformance() {
|
||||||
val rng = new Rng(42);
|
val rng = new Rng(42);
|
||||||
@@ -45,7 +43,6 @@ public class PerformanceTest {
|
|||||||
val mask = masker.generateMask(size, 100, 50, 20);
|
val mask = masker.generateMask(size, 100, 50, 20);
|
||||||
|
|
||||||
val slotInfo = Masker.slots(mask, EN);
|
val slotInfo = Masker.slots(mask, EN);
|
||||||
val grid = mask.toGrid();
|
|
||||||
|
|
||||||
long t0 = System.currentTimeMillis();
|
long t0 = System.currentTimeMillis();
|
||||||
// Try to fill multiple times to get a better average
|
// Try to fill multiple times to get a better average
|
||||||
@@ -54,7 +51,7 @@ public class PerformanceTest {
|
|||||||
long totalBacktracks = 0;
|
long totalBacktracks = 0;
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
for (int i = 0; i < iterations; i++) {
|
for (int i = 0; i < iterations; i++) {
|
||||||
val result = fillMask(rng, slotInfo, grid.copy(), false);
|
val result = fillMask(rng, slotInfo, Slotinfo.grid(slotInfo));
|
||||||
if (result.ok()) successCount++;
|
if (result.ok()) successCount++;
|
||||||
totalNodes += result.nodes();
|
totalNodes += result.nodes();
|
||||||
totalBacktracks += result.backtracks();
|
totalBacktracks += result.backtracks();
|
||||||
@@ -66,7 +63,7 @@ public class PerformanceTest {
|
|||||||
size, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
|
size, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void main() {
|
void main() {
|
||||||
testIncrementalComplexity();
|
testIncrementalComplexity();
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
@@ -81,7 +78,7 @@ public class PerformanceTest {
|
|||||||
" 2 1 \n" +
|
" 2 1 \n" +
|
||||||
" 1 \n" +
|
" 1 \n" +
|
||||||
"221 22\n";
|
"221 22\n";
|
||||||
val mask = Masker.Clues.parse(maskStr);
|
val mask = Clued.parse(maskStr);
|
||||||
val allSlots = Masker.slots(mask.c(), EN);
|
val allSlots = Masker.slots(mask.c(), EN);
|
||||||
//mask.toGrid()
|
//mask.toGrid()
|
||||||
System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
|
System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
|
||||||
@@ -90,11 +87,11 @@ public class PerformanceTest {
|
|||||||
|
|
||||||
for (int i = 10; i <= allSlots.length; i++) {
|
for (int i = 10; i <= allSlots.length; i++) {
|
||||||
val subset = Arrays.copyOf(allSlots, i);
|
val subset = Arrays.copyOf(allSlots, i);
|
||||||
// Arrays.sort(subset, Comparator.comparingInt(Slotinfo::score));
|
// Arrays.sort(subset, Comparator.comparingInt(Slotinfo::score));
|
||||||
System.out.printf("[DEBUG_LOG] Testing with first %d slots%n of %s", i, allSlots.length);
|
System.out.printf("[DEBUG_LOG] Testing with first %d slots%n of %s", i, allSlots.length);
|
||||||
var grid = Slotinfo.grid(subset);
|
var grid = Slotinfo.grid(subset);
|
||||||
visualizeSlots(subset);
|
visualizeSlots(subset);
|
||||||
measureFill(new Rng(123 + i), subset, grid, "Subset size " + i);
|
measureFill(new Rng(123 + i), subset, "Subset size " + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,18 +100,18 @@ public class PerformanceTest {
|
|||||||
val rng = new Rng(42);
|
val rng = new Rng(42);
|
||||||
|
|
||||||
// A single horizontal slot at (0,0)
|
// A single horizontal slot at (0,0)
|
||||||
val mask = Masker.Clues.parse("1 \n");
|
val mask = Clued.parse("1 \n");
|
||||||
val slots = Masker.slots(mask.c(), EN);
|
val slots = Masker.slots(mask.c(), EN);
|
||||||
|
|
||||||
System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---");
|
System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---");
|
||||||
if (slots.length > 0) {
|
if (slots.length > 0) {
|
||||||
measureFill(rng, slots, mask.toGrid(), "Single Slot");
|
measureFill(rng, slots, "Single Slot");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("[DEBUG_LOG] Error: No slots found in mask.");
|
System.out.println("[DEBUG_LOG] Error: No slots found in mask.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void measureFill(Rng rng, Slotinfo[] slots, Grid grid, String label) {
|
private void measureFill(Rng rng, Slotinfo[] slots, String label) {
|
||||||
long t0 = System.currentTimeMillis();
|
long t0 = System.currentTimeMillis();
|
||||||
int iterations = 1;
|
int iterations = 1;
|
||||||
long totalNodes = 0;
|
long totalNodes = 0;
|
||||||
@@ -125,7 +122,7 @@ public class PerformanceTest {
|
|||||||
// Reset assignments for each iteration
|
// Reset assignments for each iteration
|
||||||
for (Slotinfo s : slots) s.assign().w = 0;
|
for (Slotinfo s : slots) s.assign().w = 0;
|
||||||
|
|
||||||
val result = fillMask(rng, slots, grid.copy(), false);
|
val result = fillMask(rng, slots, Slotinfo.grid(slots));
|
||||||
if (result.ok()) successCount++;
|
if (result.ok()) successCount++;
|
||||||
totalNodes += result.nodes();
|
totalNodes += result.nodes();
|
||||||
totalBacktracks += result.backtracks();
|
totalBacktracks += result.backtracks();
|
||||||
@@ -148,8 +145,8 @@ public class PerformanceTest {
|
|||||||
int dir = Masker.Slot.dir(key);
|
int dir = Masker.Slot.dir(key);
|
||||||
int clueIdx = Masker.Slot.clueIndex(key);
|
int clueIdx = Masker.Slot.clueIndex(key);
|
||||||
|
|
||||||
int cr = SwedishGenerator.IT[clueIdx].r();
|
int cr = Masker.IT[clueIdx].r();
|
||||||
int cc = SwedishGenerator.IT[clueIdx].c();
|
int cc = Masker.IT[clueIdx].c();
|
||||||
|
|
||||||
// User requested: aAAAA for a four letter to RIGHT clue slot.
|
// User requested: aAAAA for a four letter to RIGHT clue slot.
|
||||||
// SwedishGenerator: 1=RIGHT, 0=DOWN, 2=UP, 3=LEFT
|
// SwedishGenerator: 1=RIGHT, 0=DOWN, 2=UP, 3=LEFT
|
||||||
@@ -179,9 +176,9 @@ public class PerformanceTest {
|
|||||||
|
|
||||||
display[cr][cc] = clueChar;
|
display[cr][cc] = clueChar;
|
||||||
|
|
||||||
Masker.Slot.from(slot.key(), slot.lo(), slot.hi(), null).walk().forEach(idx -> {
|
Gridded.cellWalk((byte) slot.key(), slot.lo(), slot.hi()).forEach(idx -> {
|
||||||
int r = SwedishGenerator.IT[idx].r();
|
int r = Masker.IT[idx].r();
|
||||||
int c = SwedishGenerator.IT[idx].c();
|
int c = Masker.IT[idx].c();
|
||||||
if (display[r][c] == ' ' || (display[r][c] >= 'A' && display[r][c] <= 'D')) {
|
if (display[r][c] == ' ' || (display[r][c] >= 'A' && display[r][c] <= 'D')) {
|
||||||
if (display[r][c] != ' ' && display[r][c] != slotChar) {
|
if (display[r][c] != ' ' && display[r][c] != slotChar) {
|
||||||
display[r][c] = '+'; // Intersection
|
display[r][c] = '+'; // Intersection
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import puzzle.Export.Dicts;
|
|||||||
import puzzle.Export.Gridded;
|
import puzzle.Export.Gridded;
|
||||||
import puzzle.Export.IntListDTO;
|
import puzzle.Export.IntListDTO;
|
||||||
import puzzle.Export.LetterVisit.LetterAt;
|
import puzzle.Export.LetterVisit.LetterAt;
|
||||||
|
import puzzle.Masker.Clues;
|
||||||
import puzzle.Masker.Slot;
|
import puzzle.Masker.Slot;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -27,74 +28,81 @@ public class SwedishGeneratorTest {
|
|||||||
public static Context get() { return CTX.get(); }
|
public static Context get() { return CTX.get(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static final long TEST = Lemma.from("TEST");
|
static final long TEST = Lemma.from("TEST");
|
||||||
|
static final long IN = Lemma.from("IN");
|
||||||
|
static final long INER = Lemma.from("INER");
|
||||||
|
static final long INEREN = Lemma.from("INEREN");
|
||||||
|
static final long INERENA = Lemma.from("INERENA");
|
||||||
|
static final long INERENAE = Lemma.from("INERENAE");
|
||||||
|
static final long APPLE = Lemma.from("APPLE");
|
||||||
|
static final long EXE = Lemma.from("AXE");
|
||||||
|
static final long ABC = Lemma.from("ABC");
|
||||||
|
static final long ABD = Lemma.from("ABD");
|
||||||
|
static final long AZ = Lemma.from("AZ");
|
||||||
|
static final long AB = Lemma.from("AB");
|
||||||
static final long[] WORDS = new long[]{
|
static final long[] WORDS = new long[]{
|
||||||
Lemma.from("AT"),
|
Lemma.from("AT"),
|
||||||
Lemma.from("CAT"),
|
Lemma.from("CAT"),
|
||||||
Lemma.from("DOGS"),
|
Lemma.from("DOGS"),
|
||||||
Lemma.from("APPLE"),
|
APPLE,
|
||||||
Lemma.from("APPLY"),
|
Lemma.from("APPLY"),
|
||||||
Lemma.from("BANAN"),
|
Lemma.from("BANAN"),
|
||||||
Lemma.from("BANANA"),
|
Lemma.from("BANANA"),
|
||||||
Lemma.from("BANANAS"),
|
Lemma.from("BANANAS"),
|
||||||
Lemma.from("BANANASS") // length 8
|
Lemma.from("BANANASS") // length 8
|
||||||
};
|
};
|
||||||
static final long l2a = Lemma.from("IN");
|
|
||||||
static final long l4a = Lemma.from("INER");
|
|
||||||
static final long l6a = Lemma.from("INEREN");
|
|
||||||
static final long l7a = Lemma.from("INERENA");
|
|
||||||
static final long l8a = Lemma.from("INERENAE");
|
|
||||||
static final long l1 = Lemma.from("APPLE");
|
|
||||||
static final long l2 = Lemma.from("AXE");
|
|
||||||
|
|
||||||
static final long[] WORDS2 = new long[]{ Lemma.from("IN"),
|
static final long[] WORDS2 = new long[]{ IN,
|
||||||
Lemma.from("APPLE"),
|
APPLE,
|
||||||
Lemma.from("APPLY"),
|
Lemma.from("APPLY"),
|
||||||
Lemma.from("BANAN"),
|
Lemma.from("BANAN"),
|
||||||
Lemma.from("INE"),
|
Lemma.from("INE"),
|
||||||
Lemma.from("INER"),
|
INER,
|
||||||
Lemma.from("INEREN"),
|
INEREN,
|
||||||
Lemma.from("INERENA"),
|
INERENA,
|
||||||
Lemma.from("INERENAE") };
|
INERENAE };
|
||||||
static final long ABC = Lemma.from("ABC");
|
|
||||||
static final long ABD = Lemma.from("ABD");
|
|
||||||
static final long AZ = Lemma.from("AZ");
|
|
||||||
static final long AB = Lemma.from("AB");
|
|
||||||
static final byte LETTER_A = ((byte) 'A') & 31;
|
|
||||||
static final byte LETTER_B = ((byte) 'B') & 31;
|
|
||||||
static final byte LETTER_C = ((byte) 'C') & 31;
|
|
||||||
static final byte LETTER_X = ((byte) 'X') & 31;
|
|
||||||
static final byte LETTER_Z = ((byte) 'Z') & 31;
|
|
||||||
static final byte CLUE_DOWN = 0;
|
|
||||||
static final byte CLUE_RIGHT = 1;
|
|
||||||
static final byte CLUE_UP = 2;
|
|
||||||
static final byte CLUE_LEFT = 3;
|
|
||||||
|
|
||||||
static final int OFF_1_0 = Grid.offset(1, 0);
|
static final byte LETTER_A = ((byte) 'A') & 31;
|
||||||
static final int OFF_1_1 = Grid.offset(1, 1);
|
static final byte LETTER_P = ((byte) 'P') & 31;
|
||||||
static final int OFF_1_2 = Grid.offset(1, 2);
|
static final byte LETTER_L = ((byte) 'L') & 31;
|
||||||
static final int OFF_1_3 = Grid.offset(1, 3);
|
static final byte LETTER_B = ((byte) 'B') & 31;
|
||||||
static final int OFF_1_4 = Grid.offset(1, 4);
|
static final byte LETTER_C = ((byte) 'C') & 31;
|
||||||
static final int OFF_1_5 = Grid.offset(1, 5);
|
static final byte LETTER_E = ((byte) 'E') & 31;
|
||||||
static final int OFF_2_1 = Grid.offset(2, 1);
|
static final byte LETTER_I = ((byte) 'I') & 31;
|
||||||
static final int OFF_2_3 = Grid.offset(2, 3);
|
static final byte LETTER_N = ((byte) 'N') & 31;
|
||||||
static final int OFF_2_2 = Grid.offset(2, 2);
|
static final byte LETTER_X = ((byte) 'X') & 31;
|
||||||
static final int OFF_2_4 = Grid.offset(2, 4);
|
static final byte LETTER_R = ((byte) 'R') & 31;
|
||||||
static final int OFF_0_0 = Grid.offset(0, 0);
|
static final byte LETTER_Z = ((byte) 'Z') & 31;
|
||||||
static final int OFF_0_4 = Grid.offset(0, 4);
|
static final byte CLUE_DOWN = 0;
|
||||||
static final int OFF_0_5 = Grid.offset(0, 5);
|
static final byte CLUE_RIGHT = 1;
|
||||||
static final int OFF_0_1 = Grid.offset(0, 1);
|
static final byte CLUE_UP = 2;
|
||||||
static final int OFF_0_2 = Grid.offset(0, 2);
|
static final byte CLUE_LEFT = 3;
|
||||||
static final int OFF_0_3 = Grid.offset(0, 3);
|
|
||||||
static final int OFF_2_0 = Grid.offset(2, 0);
|
static final int OFF_1_0 = Masker.offset(1, 0);
|
||||||
static final int OFF_2_5 = Grid.offset(2, 5);
|
static final int OFF_1_1 = Masker.offset(1, 1);
|
||||||
static final int OFF_3_5 = Grid.offset(3, 5);
|
static final int OFF_1_2 = Masker.offset(1, 2);
|
||||||
static final int OFF_4_5 = Grid.offset(4, 5);
|
static final int OFF_1_3 = Masker.offset(1, 3);
|
||||||
static final int OFF_3_0 = Grid.offset(3, 0);
|
static final int OFF_1_4 = Masker.offset(1, 4);
|
||||||
static final int OFF_3_1 = Grid.offset(3, 1);
|
static final int OFF_1_5 = Masker.offset(1, 5);
|
||||||
static final int OFF_3_2 = Grid.offset(3, 2);
|
static final int OFF_2_1 = Masker.offset(2, 1);
|
||||||
static final int OFF_3_3 = Grid.offset(3, 3);
|
static final int OFF_2_3 = Masker.offset(2, 3);
|
||||||
static final int OFF_3_4 = Grid.offset(3, 4);
|
static final int OFF_2_2 = Masker.offset(2, 2);
|
||||||
|
static final int OFF_2_4 = Masker.offset(2, 4);
|
||||||
|
static final int OFF_0_0 = Masker.offset(0, 0);
|
||||||
|
static final int OFF_0_4 = Masker.offset(0, 4);
|
||||||
|
static final int OFF_0_5 = Masker.offset(0, 5);
|
||||||
|
static final int OFF_0_1 = Masker.offset(0, 1);
|
||||||
|
static final int OFF_0_2 = Masker.offset(0, 2);
|
||||||
|
static final int OFF_0_3 = Masker.offset(0, 3);
|
||||||
|
static final int OFF_2_0 = Masker.offset(2, 0);
|
||||||
|
static final int OFF_2_5 = Masker.offset(2, 5);
|
||||||
|
static final int OFF_3_5 = Masker.offset(3, 5);
|
||||||
|
static final int OFF_4_5 = Masker.offset(4, 5);
|
||||||
|
static final int OFF_3_0 = Masker.offset(3, 0);
|
||||||
|
static final int OFF_3_1 = Masker.offset(3, 1);
|
||||||
|
static final int OFF_3_2 = Masker.offset(3, 2);
|
||||||
|
static final int OFF_3_3 = Masker.offset(3, 3);
|
||||||
|
static final int OFF_3_4 = Masker.offset(3, 4);
|
||||||
static final byte D_BYTE_2 = CLUE_RIGHT;
|
static final byte D_BYTE_2 = CLUE_RIGHT;
|
||||||
|
|
||||||
enum Idx {
|
enum Idx {
|
||||||
@@ -139,9 +147,9 @@ public class SwedishGeneratorTest {
|
|||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
void testPatternForSlotAllLetters() {
|
void testPatternForSlotAllLetters() {
|
||||||
var grid = new Gridded(createEmpty());
|
|
||||||
var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
var key = Slot.packSlotKey(OFF_0_0, CLUE_RIGHT);
|
||||||
val clues = Masker.Clues.createEmpty();
|
val clues = Masker.Clues.createEmpty();
|
||||||
|
var grid = new Gridded(clues);
|
||||||
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
clues.setClueLo(IDX_0_0.lo, CLUE_RIGHT);
|
||||||
GridBuilder.placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC);
|
GridBuilder.placeWord(grid.grid(), grid.grid().g, key, (1L << OFF_0_1) | (1L << OFF_0_2) | (1L << OFF_0_3), 0L, ABC);
|
||||||
val map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
val map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
@@ -192,13 +200,21 @@ public class SwedishGeneratorTest {
|
|||||||
var f = rng.nextFloat();
|
var f = rng.nextFloat();
|
||||||
assertTrue(f >= 0.0 && f <= 1.0);
|
assertTrue(f >= 0.0 && f <= 1.0);
|
||||||
}
|
}
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
|
assertTrue(rng.biasedIndexPow3(100) >= 0 && rng.biasedIndexPow3(100) < 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGrid() {
|
void testGrid() {
|
||||||
var grid = new Gridded(createEmpty());
|
var empty = Clues.createEmpty();
|
||||||
|
var grid = new Gridded(empty);
|
||||||
GridBuilder.placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from("A"));
|
GridBuilder.placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_0, 0, Lemma.from("A"));
|
||||||
val arr = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
val arr = grid.stream(empty).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
assertEquals(1, arr.size());
|
assertEquals(1, arr.size());
|
||||||
assertEquals(LETTER_A, arr.get(OFF_0_0));
|
assertEquals(LETTER_A, arr.get(OFF_0_0));
|
||||||
}
|
}
|
||||||
@@ -217,11 +233,18 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testLemmaAndDict() {
|
void testLemmaAndDict() {
|
||||||
Assertions.assertEquals(Lemma.packShiftIn("APPLE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(l1));
|
Assertions.assertEquals(Lemma.packShiftIn("APPLE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(APPLE));
|
||||||
assertEquals(4, Lemma.unpackSize(l1));
|
assertEquals(4, Lemma.unpackSize(APPLE));
|
||||||
assertEquals(LETTER_A, Lemma.byteAt(l1, 0));
|
assertEquals(LETTER_I, Lemma.byteAt(INERENAE, 0));
|
||||||
|
assertEquals(LETTER_N, Lemma.byteAt(INERENAE, 1));
|
||||||
|
assertEquals(LETTER_E, Lemma.byteAt(INERENAE, 2));
|
||||||
|
assertEquals(LETTER_R, Lemma.byteAt(INERENAE, 3));
|
||||||
|
assertEquals(LETTER_E, Lemma.byteAt(INERENAE, 4));
|
||||||
|
assertEquals(LETTER_N, Lemma.byteAt(INERENAE, 5));
|
||||||
|
assertEquals(LETTER_A, Lemma.byteAt(INERENAE, 6));
|
||||||
|
assertEquals(LETTER_E, Lemma.byteAt(INERENAE, 7));
|
||||||
|
|
||||||
var dict = Dicts.makeDict(new long[]{ l1, l2, l2a, l4a, l6a, l7a, l8a });
|
var dict = Dicts.makeDict(new long[]{ APPLE, EXE, IN, INER, INEREN, INERENA, INERENAE });
|
||||||
|
|
||||||
assertEquals(1, dict.index()[3].words().length);
|
assertEquals(1, dict.index()[3].words().length);
|
||||||
assertEquals(1, dict.index()[5].words().length);
|
assertEquals(1, dict.index()[5].words().length);
|
||||||
@@ -251,13 +274,13 @@ public class SwedishGeneratorTest {
|
|||||||
assertEquals(OFF_2_3, Slot.clueIndex(key));
|
assertEquals(OFF_2_3, Slot.clueIndex(key));
|
||||||
assertEquals(CLUE_DOWN, Slot.dir(key));
|
assertEquals(CLUE_DOWN, Slot.dir(key));
|
||||||
assertFalse(Slot.horiz(key));
|
assertFalse(Slot.horiz(key));
|
||||||
var cells = Gridded.walk((byte) key, lo, 0L).toArray();
|
var cells = Gridded.cellWalk((byte) key, lo, 0L).toArray();
|
||||||
assertEquals(2, SwedishGenerator.IT[cells[0]].r());
|
assertEquals(2, Masker.IT[cells[0]].r());
|
||||||
assertEquals(3, SwedishGenerator.IT[cells[1]].r());
|
assertEquals(3, Masker.IT[cells[1]].r());
|
||||||
assertEquals(4, SwedishGenerator.IT[cells[2]].r());
|
assertEquals(4, Masker.IT[cells[2]].r());
|
||||||
assertEquals(5, SwedishGenerator.IT[cells[0]].c());
|
assertEquals(5, Masker.IT[cells[0]].c());
|
||||||
assertEquals(5, SwedishGenerator.IT[cells[1]].c());
|
assertEquals(5, Masker.IT[cells[1]].c());
|
||||||
assertEquals(5, SwedishGenerator.IT[cells[2]].c());
|
assertEquals(5, Masker.IT[cells[2]].c());
|
||||||
|
|
||||||
assertTrue(Slot.horiz(CLUE_RIGHT)); // right
|
assertTrue(Slot.horiz(CLUE_RIGHT)); // right
|
||||||
assertFalse(Slot.horiz(CLUE_DOWN)); // down
|
assertFalse(Slot.horiz(CLUE_DOWN)); // down
|
||||||
@@ -335,7 +358,8 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPlaceWord() {
|
void testPlaceWord() {
|
||||||
var grid = new Gridded(createEmpty());
|
var empty = Clues.createEmpty();
|
||||||
|
var grid = new Gridded(empty);
|
||||||
// Slot at OFF_0_0 length 3, horizontal (right)
|
// Slot at OFF_0_0 length 3, horizontal (right)
|
||||||
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
||||||
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
|
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
|
||||||
@@ -344,7 +368,8 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
// 1. Successful placement in empty grid
|
// 1. Successful placement in empty grid
|
||||||
assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
|
assertTrue(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
|
||||||
var map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
|
||||||
|
var map = grid.stream(empty).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
assertEquals(3, map.size());
|
assertEquals(3, map.size());
|
||||||
assertEquals(LETTER_A, map.get(OFF_0_0));
|
assertEquals(LETTER_A, map.get(OFF_0_0));
|
||||||
assertEquals(LETTER_B, map.get(OFF_0_1));
|
assertEquals(LETTER_B, map.get(OFF_0_1));
|
||||||
@@ -362,7 +387,7 @@ public class SwedishGeneratorTest {
|
|||||||
assertEquals(LETTER_C, map.get(OFF_0_2));
|
assertEquals(LETTER_C, map.get(OFF_0_2));
|
||||||
|
|
||||||
// 4. Partial placement then conflict (rollback)
|
// 4. Partial placement then conflict (rollback)
|
||||||
grid = new Gridded(createEmpty());
|
grid = new Gridded(Clues.createEmpty());
|
||||||
GridBuilder.placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from("X")); // Conflict at the end
|
GridBuilder.placeWord(grid.grid(), grid.grid().g, Slot.packSlotKey(0, CLUE_RIGHT), 1L << OFF_0_2, 0, Lemma.from("X")); // Conflict at the end
|
||||||
assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
|
assertFalse(GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, hi, w1));
|
||||||
map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
@@ -372,7 +397,8 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBacktrackingHelpers() {
|
void testBacktrackingHelpers() {
|
||||||
var grid = new Gridded(createEmpty());
|
var clues = Clues.createEmpty();
|
||||||
|
var grid = new Gridded(clues);
|
||||||
// Slot at 0,1 length 2
|
// Slot at 0,1 length 2
|
||||||
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
var key = Slot.packSlotKey(0, CLUE_RIGHT);
|
||||||
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
|
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
|
||||||
@@ -381,7 +407,8 @@ public class SwedishGeneratorTest {
|
|||||||
val top = grid.grid().hi;
|
val top = grid.grid().hi;
|
||||||
var placed = GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, 0L, w);
|
var placed = GridBuilder.placeWord(grid.grid(), grid.grid().g, key, lo, 0L, w);
|
||||||
assertTrue(placed);
|
assertTrue(placed);
|
||||||
var map = grid.stream(Masker.Clues.createEmpty()).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
|
||||||
|
var map = grid.stream(clues).collect(Collectors.toMap(LetterAt::index, LetterAt::letter));
|
||||||
assertEquals(2, map.size());
|
assertEquals(2, map.size());
|
||||||
assertEquals(LETTER_A, map.get(OFF_0_1));
|
assertEquals(LETTER_A, map.get(OFF_0_1));
|
||||||
assertEquals(LETTER_Z, map.get(OFF_0_2));
|
assertEquals(LETTER_Z, map.get(OFF_0_2));
|
||||||
|
|||||||
Reference in New Issue
Block a user