introduce bitloops
This commit is contained in:
@@ -8,19 +8,18 @@ import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class CsvIndexService
|
||||
implements Closeable {
|
||||
|
||||
static final ScopedValue<CsvIndexService> SC = ScopedValue.newInstance();
|
||||
static final Gson GSON = new Gson();
|
||||
private static final int MAGIC = 0x4C494458; // "LIDX"
|
||||
private static final int VERSION = 1;
|
||||
|
||||
private final Path csvPath;
|
||||
private final Path idxPath;
|
||||
static final ScopedValue<CsvIndexService> SC = ScopedValue.newInstance();
|
||||
static final Gson GSON = new Gson();
|
||||
private static final int MAGIC = 0x4C494458; // "LIDX"
|
||||
private static final int VERSION = 1;
|
||||
static int SIMPEL_IDX = 3;
|
||||
private final Path csvPath;
|
||||
private final Path idxPath;
|
||||
|
||||
private volatile long[] offsets; // lazy
|
||||
private volatile FileChannel csvChannel; // open once
|
||||
@@ -30,7 +29,10 @@ public final class CsvIndexService
|
||||
this.csvPath = csvPath;
|
||||
this.idxPath = idxPath;
|
||||
}
|
||||
|
||||
public static int lineToSimpel(String line) {
|
||||
var parts = line.split(",", 5);
|
||||
return Integer.parseInt(parts[SIMPEL_IDX].trim());
|
||||
}
|
||||
public static String[] lineToClue(String line) {
|
||||
if (line.isBlank()) throw new RuntimeException("Empty line");
|
||||
var parts = line.split(",", 5);
|
||||
@@ -51,16 +53,23 @@ public final class CsvIndexService
|
||||
throw new RuntimeException("Invalid word:" + line);
|
||||
}
|
||||
|
||||
// CSV has level 1-10. llmScores use 10-level.
|
||||
int score = Integer.parseInt(parts[2].trim());
|
||||
if (score < 1) {
|
||||
if (Main.VERBOSE) System.err.println("Word too complex: " + line);
|
||||
return;
|
||||
}
|
||||
int simpel = Integer.parseInt(parts[3].trim());
|
||||
ok.accept(new Lemma(id, word, simpel));
|
||||
ok.accept(new Lemma(id, word));
|
||||
}
|
||||
|
||||
public static int simpel(int index) {
|
||||
try {
|
||||
if (SC.isBound())
|
||||
return lineToSimpel(SC.get().getLine(index));
|
||||
return -1;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to get clues for index " + index, e);
|
||||
}
|
||||
}
|
||||
public static String[] clues(int index) {
|
||||
try {
|
||||
if (SC.isBound())
|
||||
|
||||
@@ -7,7 +7,6 @@ import puzzle.SwedishGenerator.Grid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
import static puzzle.SwedishGenerator.R;
|
||||
import static puzzle.SwedishGenerator.Lemma;
|
||||
import static puzzle.SwedishGenerator.Slot;
|
||||
@@ -82,10 +81,10 @@ public record Export() {
|
||||
public void clear() { Arrays.fill(bits, 0L); }
|
||||
}
|
||||
|
||||
record Placed(Lemma lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) {
|
||||
record Placed(Lemma lemma, int startRow, int startCol, char direction, int arrowRow, int arrowCol, int[] cells, boolean isReversed) {
|
||||
|
||||
public static final char HORIZONTAL = 'h';
|
||||
static final char VERTICAL = 'v';
|
||||
static final char VERTICAL = 'v';
|
||||
}
|
||||
|
||||
public record Rewards(int coins, int stars, int hints) { }
|
||||
@@ -109,9 +108,9 @@ public record Export() {
|
||||
for (int i = 0, len = s.len(); i < len; i++) cells[i] = s.pos(i);
|
||||
|
||||
char direction;
|
||||
var isReversed = false;
|
||||
var startRow = Grid.r(cells[0]);
|
||||
var startCol = Grid.c(cells[0]);
|
||||
var isReversed = false;
|
||||
var startRow = Grid.r(cells[0]);
|
||||
var startCol = Grid.c(cells[0]);
|
||||
if (d == 2) { // right -> horizontal
|
||||
direction = Placed.HORIZONTAL;
|
||||
} else if (d == 3 || d == 5) { // down or down-bent -> vertical
|
||||
@@ -215,4 +214,25 @@ public record Export() {
|
||||
return new ExportedPuzzle(gridv2, wordsOut, difficulty, rewards);
|
||||
}
|
||||
}
|
||||
|
||||
static record DictEntryDTO(ArrayList<Lemma> words, IntListDTO[][] pos) {
|
||||
|
||||
public DictEntryDTO(int L) {
|
||||
this(new ArrayList<>(), new IntListDTO[L][26]);
|
||||
for (var i = 0; i < L; i++) for (var j = 0; j < 26; j++) pos[i][j] = new IntListDTO();
|
||||
}
|
||||
}
|
||||
|
||||
static final class IntListDTO {
|
||||
|
||||
int[] a = new int[8];
|
||||
int n = 0;
|
||||
void add(int v) {
|
||||
if (n >= a.length) a = Arrays.copyOf(a, a.length * 2);
|
||||
a[n++] = v;
|
||||
}
|
||||
int size() { return n; }
|
||||
int[] data() { return a; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,8 +74,6 @@ public class Main {
|
||||
|
||||
section("Settings");
|
||||
printSettings(opts);
|
||||
var csv = Paths.get("nl_score_hints_v3.csv");
|
||||
var idx = Paths.get("nl_score_hints_v3.idx");
|
||||
|
||||
var res = generatePuzzle(opts);
|
||||
if (res == null) {
|
||||
@@ -86,6 +84,7 @@ public class Main {
|
||||
}
|
||||
|
||||
section("Result");
|
||||
res.filled().calcSimpel();
|
||||
info(String.format(Locale.ROOT, "simplicity : %.2f", res.filled().stats().simplicity));
|
||||
|
||||
section("Mask");
|
||||
@@ -353,6 +352,7 @@ public class Main {
|
||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
||||
if (filled.ok()) {
|
||||
filled.calcSimpel();
|
||||
TOTAL_SUCCESS.incrementAndGet();
|
||||
TOTAL_SIMPLICITY.addAndGet((long) (filled.stats().simplicity * 100));
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import precomp.Neighbors9x8.nbrs_8;
|
||||
import precomp.Neighbors9x8.rci;
|
||||
import puzzle.Export.Bit;
|
||||
import puzzle.Export.Bit1029;
|
||||
import puzzle.Export.DictEntryDTO;
|
||||
import puzzle.Export.Gridded;
|
||||
import puzzle.Export.Strings;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -37,6 +37,8 @@ public record SwedishGenerator(Rng rng) {
|
||||
public CandidateInfo(int n) { this(null, n); }
|
||||
}
|
||||
|
||||
static final CandidateInfo[] CANDIDATES = IntStream.range(0, 10192 << 2).mapToObj(CandidateInfo::new).toArray(CandidateInfo[]::new);
|
||||
|
||||
//@formatter:off
|
||||
@FunctionalInterface interface SlotVisitor { void visit(int key, long packedPos, int len); }
|
||||
//@formatter:on
|
||||
@@ -67,6 +69,9 @@ public record SwedishGenerator(Rng rng) {
|
||||
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
||||
record Pick(Slot slot, CandidateInfo info, boolean done) { }
|
||||
|
||||
static final byte B0 = (byte) 0;
|
||||
static final byte B64 = (byte) 64;
|
||||
static final byte B48 = (byte) 48;
|
||||
// Directions for '1'..'6'
|
||||
static final nbrs_16[] OFFSETS = Neighbors9x8.OFFSETS;
|
||||
static final nbrs_16[] OFFSETS_FOUR = Neighbors9x8.OFFSETS_FOUR;
|
||||
@@ -75,6 +80,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
static final rci[] IT = Neighbors9x8.IT;
|
||||
static final long[] INBR8_PACKEDT = Neighbors9x8.NBR8_PACKED;
|
||||
static final int[][] MUTATE_RI = new int[SIZE][625];
|
||||
static final long[] NBR8_PACKED = Neighbors9x8.NBR8_PACKED;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
@@ -87,28 +93,6 @@ public record SwedishGenerator(Rng rng) {
|
||||
clamp(Grid.c(i) + dc1 + dc2, 0, C - 1));
|
||||
}
|
||||
}
|
||||
/*static {
|
||||
Rng trng = new Rng(1);
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
int[] neighborhood = new int[625];
|
||||
int k = 0;
|
||||
for (int dr1 = -2; dr1 <= 2; dr1++)
|
||||
for (int dr2 = -2; dr2 <= 2; dr2++)
|
||||
for (int dc1 = -2; dc1 <= 2; dc1++)
|
||||
for (int dc2 = -2; dc2 <= 2; dc2++)
|
||||
neighborhood[k++] = Grid.offset(clamp(Grid.r(i) + dr1 + dr2, 0, R - 1),
|
||||
clamp(Grid.c(i) + dc1 + dc2, 0, C - 1));
|
||||
for (k = 0; k < 625; k++) {
|
||||
long packed = 0;
|
||||
for (int s = 0; s < 4; s++) {
|
||||
int ri = neighborhood[trng.randint(0, 624)];
|
||||
int d = trng.randint(1, 4);
|
||||
packed |= ((long) ri | ((long) d << 8)) << (s << 4);
|
||||
}
|
||||
MUTATE_RI[i][k] = packed;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
static final Pick PICK_DONE = new Pick(null, null, true);
|
||||
static final Pick PICK_NOT_DONE = new Pick(null, null, false);
|
||||
@@ -127,9 +111,9 @@ public record SwedishGenerator(Rng rng) {
|
||||
HashMap<Integer, Lemma> clueMap,
|
||||
FillStats stats) {
|
||||
|
||||
public FillResult {
|
||||
public void calcSimpel() {
|
||||
if (ok) {
|
||||
clueMap.forEach((k, v) -> stats.simplicity += v.simpel);
|
||||
clueMap.forEach((k, v) -> stats.simplicity += v.simpel());
|
||||
stats.simplicity = clueMap.isEmpty() ? 0 : stats.simplicity / clueMap.size();
|
||||
}
|
||||
}
|
||||
@@ -247,9 +231,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static final byte B0 = (byte) 0;
|
||||
static final byte B64 = (byte) 64;
|
||||
static final byte B48 = (byte) 48;
|
||||
|
||||
static boolean isLetter(byte b) { return (b & B64) != B0; }
|
||||
public boolean isLetterSet(int idx) { return isLetter(g[idx]); }
|
||||
static boolean notDigit(byte b) { return (b & B48) != B48; }
|
||||
@@ -268,27 +250,11 @@ public record SwedishGenerator(Rng rng) {
|
||||
}
|
||||
}
|
||||
|
||||
static final class IntList {
|
||||
|
||||
int[] a = new int[8];
|
||||
int n = 0;
|
||||
void add(int v) {
|
||||
if (n >= a.length) a = Arrays.copyOf(a, a.length * 2);
|
||||
a[n++] = v;
|
||||
}
|
||||
int size() { return n; }
|
||||
int[] data() { return a; }
|
||||
}
|
||||
static final record IntList(int[] data, int size) { }
|
||||
|
||||
static record DictEntry(ArrayList<Lemma> words, IntList[][] pos) {
|
||||
|
||||
public DictEntry(int L) {
|
||||
this(new ArrayList<>(), new IntList[L][26]);
|
||||
for (var i = 0; i < L; i++) for (var j = 0; j < 26; j++) pos[i][j] = new IntList();
|
||||
}
|
||||
}
|
||||
static record DictEntry(Lemma[] words, IntList[][] pos) { }
|
||||
|
||||
public static record Lemma(int index, long word, byte len, short simpel) {
|
||||
public static record Lemma(int index, long word, byte len) {
|
||||
|
||||
static int LEMMA_COUNTER = 0;
|
||||
static long pack(byte[] b) {
|
||||
@@ -296,15 +262,17 @@ public record SwedishGenerator(Rng rng) {
|
||||
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & ~64) << (i * 5);
|
||||
return w;
|
||||
}
|
||||
public Lemma(int index, String word, int simpel) { this(index, pack(word.getBytes(US_ASCII)), (byte) word.length(), (short) simpel); }
|
||||
public Lemma(String word, int simpel) { this(LEMMA_COUNTER++, word, simpel); }
|
||||
byte byteAt(int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | Grid.B64); }// word[]; }
|
||||
@Override public int hashCode() { return index; }
|
||||
@Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.index == index); }
|
||||
String[] clue() { return CsvIndexService.clues(index); }
|
||||
public Lemma(int index, String word) { this(index, pack(word.getBytes(US_ASCII)), (byte) word.length()); }
|
||||
public Lemma(String word) { this(LEMMA_COUNTER++, word); }
|
||||
byte byteAt(int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; }
|
||||
int intAt(int idx) { return (int) (((word >>> (idx * 5))) & 0b11111); }// word[]; }
|
||||
@Override public int hashCode() { return index; }
|
||||
@Override public boolean equals(Object o) { return (o == this) || (o instanceof Lemma l && l.index == index); }
|
||||
String[] clue() { return CsvIndexService.clues(index); }
|
||||
int simpel() { return CsvIndexService.simpel(index); }
|
||||
public String asWord() {
|
||||
var b = new byte[len];
|
||||
for (var i = 0; i < len; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | Grid.B64);
|
||||
for (var i = 0; i < len; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64);
|
||||
return new String(b, US_ASCII);
|
||||
}
|
||||
}
|
||||
@@ -314,23 +282,29 @@ public record SwedishGenerator(Rng rng) {
|
||||
int length) {
|
||||
|
||||
public Dict(Lemma[] wordz) {
|
||||
var index = new DictEntry[MAX_WORD_LENGTH_PLUS_ONE];
|
||||
Arrays.setAll(index, i -> new DictEntry(i));
|
||||
var index = new DictEntryDTO[MAX_WORD_LENGTH_PLUS_ONE];
|
||||
Arrays.setAll(index, i -> new DictEntryDTO(i));
|
||||
for (var lemma : wordz) {
|
||||
var L = lemma.len;
|
||||
|
||||
var entry = index[L];
|
||||
var idx = entry.words.size();
|
||||
entry.words.add(lemma);
|
||||
var idx = entry.words().size();
|
||||
entry.words().add(lemma);
|
||||
|
||||
for (var i = 0; i < L; i++) {
|
||||
var letter = lemma.byteAt(i) - 'A';
|
||||
var letter = lemma.intAt(i) - 1;
|
||||
if (letter < 0 || letter >= 26) throw new RuntimeException("Illegal letter: " + letter + " in word " + lemma);
|
||||
entry.pos[i][letter].add(idx);
|
||||
entry.pos()[i][letter].add(idx);
|
||||
}
|
||||
}
|
||||
for (int i = MIN_LEN; i < index.length; i++) if (index[i].words.size() <= 0) throw new RuntimeException("No words for length " + i);
|
||||
this(index, Arrays.stream(index).mapToInt(i -> i.words.size()).sum());
|
||||
for (int i = MIN_LEN; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i);
|
||||
this(Arrays.stream(index).map(i -> new DictEntry(i.words().toArray(Lemma[]::new),
|
||||
Arrays.stream(i.pos())
|
||||
.map(ii -> Arrays.stream(ii).map(dto -> new IntList(dto.data(), dto.size()))
|
||||
.toArray(IntList[]::new))
|
||||
.toArray(IntList[][]::new)))
|
||||
.toArray(DictEntry[]::new),
|
||||
Arrays.stream(index).mapToInt(i -> i.words().size()).sum());
|
||||
}
|
||||
static Dict loadDict(String wordsPath) {
|
||||
try {
|
||||
@@ -394,12 +368,12 @@ public record SwedishGenerator(Rng rng) {
|
||||
private static void processSlot(Grid grid, SlotVisitor visitor, int idx) {
|
||||
var d = grid.digitAt(idx);
|
||||
var packed = OFFSETS[d].path()[idx];
|
||||
long packedPos = 0;
|
||||
long packedPos = 0L;
|
||||
int k = 0;
|
||||
for (int n = (int) (packed >>> 56), iidx; k < n && k < MAX_WORD_LENGTH; k++) {
|
||||
iidx = (int) ((packed >>> (k * 7)) & 0x7F);
|
||||
for (long n = (packed >>> 56), offset = 0L, iidx; k < n; k++, offset += 7L) {
|
||||
iidx = ((packed >>> offset) & 0x7FL);
|
||||
if (grid.isClue(iidx)) break;
|
||||
packedPos |= (long) iidx << (k * 7);
|
||||
packedPos |= iidx << offset;
|
||||
}
|
||||
if (k > 0) {
|
||||
visitor.visit((idx << 4) | d, packedPos, k);
|
||||
@@ -412,52 +386,68 @@ public record SwedishGenerator(Rng rng) {
|
||||
return slots;
|
||||
}
|
||||
|
||||
static long fitting(Grid grid, int clueIdx, int[] covH, int[] covV) {
|
||||
var d = grid.digitAt(clueIdx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
long packed = nbrs16.path()[clueIdx];
|
||||
int n = (int) (packed >>> 56) * 7, k, idx;
|
||||
var horiz = Slot.horiz(d) ? covH : covV;
|
||||
for (k = 0; k < n && k < MAX_WORD_LENGTH7; k += 7) {
|
||||
idx = (int) ((packed >>> (k)) & 0x7F);
|
||||
if (grid.isClue(idx)) break;
|
||||
horiz[idx] += 1;
|
||||
}
|
||||
if (k > 0) {
|
||||
if (k < MIN_LEN7) return 8001L;
|
||||
return 1L;
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
long maskFitness(Grid grid) {
|
||||
var ctx = CTX.get();
|
||||
var covH = ctx.covH;
|
||||
var covV = ctx.covV;
|
||||
Arrays.fill(covH, 0, SIZE, 0);
|
||||
Arrays.fill(covV, 0, SIZE, 0);
|
||||
|
||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||
long penalty = (((long) Math.abs(grid.clueCount() - TARGET_CLUES)) << 3);
|
||||
boolean hasSlots = false;
|
||||
|
||||
int clueIdx;
|
||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||
long penalty = 0L;
|
||||
for (int i = 0; i < 65; i += 64) {
|
||||
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
||||
int clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var d = grid.digitAt(clueIdx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
long packed = nbrs16.path()[clueIdx];
|
||||
int n = (int) (packed >>> 56) * 7, k, idx;
|
||||
var horiz = Slot.horiz(d) ? covH : covV;
|
||||
clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var d = grid.digitAt(clueIdx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
long packed = nbrs16.path()[clueIdx];
|
||||
int n = (int) (packed >>> 56) * 7, k, idx;
|
||||
var horiz = Slot.horiz(d) ? covH : covV;
|
||||
for (k = 0; k < n && k < MAX_WORD_LENGTH7; k += 7) {
|
||||
idx = (int) ((packed >>> (k)) & 0x7F);
|
||||
if (grid.isClue(idx)) break;
|
||||
horiz[idx] += 1;
|
||||
}
|
||||
if (k > 0) {
|
||||
hasSlots = true;
|
||||
if (k < MIN_LEN7) penalty += 8000;
|
||||
penalty |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasSlots) return 1_000_000_000L;
|
||||
|
||||
if ((penalty & 1) == 0) return 1_000_000_000L;
|
||||
penalty = (penalty & ~3L) + (((long) Math.abs(grid.clueCount() - TARGET_CLUES)) << 3);
|
||||
var seen = ctx.seen;
|
||||
var stack = ctx.stack;
|
||||
seen.clear();
|
||||
|
||||
for (int i = 0; i < 65; i += 64) {
|
||||
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
||||
int clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
if (seen.get(clueIdx)) continue;
|
||||
int size = 0;
|
||||
stack[0] = clueIdx;
|
||||
seen.set(clueIdx);
|
||||
for (int sp = 1; sp > 0; size++) {
|
||||
long packed = Neighbors9x8.NBR8_PACKED[stack[--sp]];
|
||||
long packed = NBR8_PACKED[stack[--sp]];
|
||||
for (int k = 0, n = (int) (packed >>> 56) * 7; k < n; k += 7) {
|
||||
int nidx = (int) ((packed >>> k) & 0x7F);
|
||||
if (seen.get(nidx) || grid.notClue(nidx)) continue;
|
||||
@@ -472,11 +462,11 @@ public record SwedishGenerator(Rng rng) {
|
||||
for (int i = 0; i < 65; i += 64) {
|
||||
long bits = (i == 0 ? ~lo_cl : (~hi_cl & 0xFFL));
|
||||
for (; bits != X; bits &= bits - 1) {
|
||||
int idx = i + Long.numberOfTrailingZeros(bits);
|
||||
var rci = IT[idx];
|
||||
clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var rci = IT[clueIdx];
|
||||
if ((4 - rci.nbrCount()) + Long.bitCount(rci.n1() & lo_cl) + Long.bitCount(rci.n2() & hi_cl) >= 3) penalty += 400;
|
||||
var h = covH[idx];
|
||||
var v = covV[idx];
|
||||
var h = covH[clueIdx];
|
||||
var v = covV[clueIdx];
|
||||
if (h == 0 && v == 0) penalty += 1500;
|
||||
else if (h > 0 && v > 0) { /* ok */ } else if (h + v == 1) penalty += 200;
|
||||
else penalty += 600;
|
||||
@@ -650,7 +640,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
undoBuffer[offset] = mask;
|
||||
return true;
|
||||
}
|
||||
static final CandidateInfo[] CANDIDATES = IntStream.range(0, 10192 << 2).mapToObj(CandidateInfo::new).toArray(CandidateInfo[]::new);
|
||||
|
||||
static CandidateInfo candidateInfoForPattern(Context ctx, DictEntry entry, int len) {
|
||||
var pattern = ctx.pattern;
|
||||
var listBuffer = ctx.intListBuffer;
|
||||
@@ -665,7 +655,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
}
|
||||
|
||||
if (listCount == 0) {
|
||||
return CANDIDATES[entry.words.size()];
|
||||
return CANDIDATES[entry.words.length];
|
||||
}
|
||||
|
||||
// Sort constraints by size to optimize intersection
|
||||
@@ -801,26 +791,26 @@ public record SwedishGenerator(Rng rng) {
|
||||
double r = rng.nextFloat();
|
||||
int idxInArray = (int) (r * r * r * L);
|
||||
var idx = idxs[idxInArray];
|
||||
var w = entry.words.get(idx);
|
||||
var w = entry.words[idx];
|
||||
|
||||
if (used.get(w.index())) continue;
|
||||
if (used.get(w.index)) continue;
|
||||
|
||||
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||
|
||||
used.set(w.index());
|
||||
used.set(w.index);
|
||||
assigned.put(k, w);
|
||||
|
||||
if (backtrack(depth + 1)) return true;
|
||||
|
||||
assigned.remove(k);
|
||||
used.clear(w.index());
|
||||
used.clear(w.index);
|
||||
s.undoPlace(grid, ctx.undo[depth]);
|
||||
}
|
||||
stats.backtracks++;
|
||||
return false;
|
||||
}
|
||||
|
||||
var N = entry.words.size();
|
||||
var N = entry.words.length;
|
||||
if (N == 0) {
|
||||
stats.backtracks++;
|
||||
return false;
|
||||
@@ -830,13 +820,13 @@ public record SwedishGenerator(Rng rng) {
|
||||
for (var t = 0; t < tries; t++) {
|
||||
double r = rng.nextFloat();
|
||||
int idxInArray = (int) (r * r * r * N);
|
||||
var w = entry.words.get(idxInArray);
|
||||
var w = entry.words[idxInArray];
|
||||
|
||||
if (used.get(w.index())) continue;
|
||||
if (used.get(w.index)) continue;
|
||||
|
||||
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||
|
||||
used.set(w.index());
|
||||
used.set(w.index);
|
||||
assigned.put(k, w);
|
||||
|
||||
if (backtrack(depth + 1)) return true;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class ExportFormatTest {
|
||||
var clueMap = new HashMap<Integer, Lemma>();
|
||||
// key = (cellIndex << 4) | direction
|
||||
var key = (0 << 4) | 2;
|
||||
var lemma = new Lemma("TEST", 1);
|
||||
var lemma = new Lemma("TEST");
|
||||
clueMap.put(key, lemma);
|
||||
|
||||
// Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4)
|
||||
|
||||
@@ -167,8 +167,9 @@ public class MainTest {
|
||||
// Regression baseline for seed search starting at 12347, pop 4, gens 20
|
||||
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
||||
Assertions.assertEquals(22, res.filled().clueMap().size(), "Number of assigned words changed");
|
||||
Assertions.assertEquals(747.5454545454545, res.filled().stats().simplicity, 1e-9, "Simplicity value changed");
|
||||
Assertions.assertEquals(Lemma.pack(new byte[]{ 'I', 'E', 'M', 'A', 'N', 'D', 'S' }), res.filled().clueMap().get(515).word());
|
||||
Assertions.assertEquals(648985643903632391L, res.filled().grid().grid().lo);
|
||||
Assertions.assertEquals(140L, res.filled().grid().grid().hi);
|
||||
}
|
||||
@Test
|
||||
public void testIsLetterA() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package puzzle;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import puzzle.Export.IntListDTO;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -91,7 +92,7 @@ public class SwedishGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testIntList() {
|
||||
var list = new IntList();
|
||||
var list = new IntListDTO();
|
||||
assertEquals(0, list.size());
|
||||
for (var i = 0; i < 10; i++) {
|
||||
list.add(i);
|
||||
@@ -103,27 +104,27 @@ public class SwedishGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testLemmaAndDict() {
|
||||
var l2a = new Lemma("IN", 1);
|
||||
var l4a = new Lemma("INER", 1);
|
||||
var l6a = new Lemma("INEREN", 1);
|
||||
var l7a = new Lemma("INERENA", 1);
|
||||
var l8a = new Lemma("INERENAE", 1);
|
||||
var l2a = new Lemma("IN");
|
||||
var l4a = new Lemma("INER");
|
||||
var l6a = new Lemma("INEREN");
|
||||
var l7a = new Lemma("INERENA");
|
||||
var l8a = new Lemma("INERENAE");
|
||||
|
||||
var l1 = new Lemma("APPLE", 5);
|
||||
var l1 = new Lemma("APPLE");
|
||||
Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), l1.word());
|
||||
assertEquals(5, l1.len());
|
||||
assertEquals(5, l1.simpel());
|
||||
assertEquals((byte) 'A', l1.byteAt(0));
|
||||
assertEquals(1, l1.intAt(0));
|
||||
|
||||
var l2 = new Lemma("AXE", 2);
|
||||
var l2 = new Lemma("AXE");
|
||||
var dict = new Dict(new Lemma[]{ l1, l2, l2a, l4a, l6a, l7a, l8a });
|
||||
|
||||
assertEquals(1, dict.index()[3].words().size());
|
||||
assertEquals(1, dict.index()[5].words().size());
|
||||
assertEquals(1, dict.index()[3].words().length);
|
||||
assertEquals(1, dict.index()[5].words().length);
|
||||
|
||||
var entry3 = dict.index()[3];
|
||||
assertEquals(1, entry3.words().size());
|
||||
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), entry3.words().getFirst().word());
|
||||
assertEquals(1, entry3.words().length);
|
||||
assertEquals(Lemma.pack("AXE".getBytes(StandardCharsets.US_ASCII)), entry3.words()[0].word());
|
||||
|
||||
// Check pos indexing
|
||||
// AXE: A at 0, X at 1, E at 2
|
||||
@@ -179,15 +180,15 @@ public class SwedishGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testCandidateInfoForPattern() {
|
||||
var l0 = new Lemma("IN", 1);
|
||||
var l3a = new Lemma("INE", 1);
|
||||
var l4a = new Lemma("INER", 1);
|
||||
var l6a = new Lemma("INEREN", 1);
|
||||
var l7a = new Lemma("INERENA", 1);
|
||||
var l8a = new Lemma("INERENAE", 1);
|
||||
var l1 = new Lemma("APPLE", 1);
|
||||
var l2 = new Lemma("APPLY", 1);
|
||||
var l3 = new Lemma("BANAN", 1);
|
||||
var l0 = new Lemma("IN");
|
||||
var l3a = new Lemma("INE");
|
||||
var l4a = new Lemma("INER");
|
||||
var l6a = new Lemma("INEREN");
|
||||
var l7a = new Lemma("INERENA");
|
||||
var l8a = new Lemma("INERENAE");
|
||||
var l1 = new Lemma("APPLE");
|
||||
var l2 = new Lemma("APPLY");
|
||||
var l3 = new Lemma("BANAN");
|
||||
var dict = new Dict(new Lemma[]{ l0, l1, l2, l3, l3a, l4a, l6a, l7a, l8a });
|
||||
|
||||
// Pattern "APP--" for length 5
|
||||
@@ -274,7 +275,7 @@ public class SwedishGeneratorTest {
|
||||
// r(i) and c(i) are used by placeWord.
|
||||
var packedPos = ((long) Grid.offset(0, 0)) | (((long) Grid.offset(0, 1)) << 7) | (((long) Grid.offset(0, 2)) << 14);
|
||||
var s = Slot.from(0, packedPos, 3);
|
||||
var w1 = new Lemma("ABC", 1);
|
||||
var w1 = new Lemma("ABC");
|
||||
var undoBuffer = new int[10];
|
||||
|
||||
// 1. Successful placement in empty grid
|
||||
@@ -289,7 +290,7 @@ public class SwedishGeneratorTest {
|
||||
assertEquals(0L, undoBuffer[1]); // 0 new characters placed
|
||||
|
||||
// 3. Conflict: place "ABD" where "ABC" is
|
||||
var w2 = new Lemma("ABD", 1);
|
||||
var w2 = new Lemma("ABD");
|
||||
assertFalse(placeWord(grid, s, w2, undoBuffer, 2));
|
||||
// Verify grid is unchanged (still "ABC")
|
||||
assertEquals('A', grid.byteAt(Grid.offset(0, 0)));
|
||||
@@ -312,7 +313,7 @@ public class SwedishGeneratorTest {
|
||||
// Slot at 0,1 length 2
|
||||
var packedPos = ((long) Grid.offset(0, 1)) | (((long) Grid.offset(0, 2)) << 7);
|
||||
var s = Slot.from((0 << 8) | (1 << 4) | 2, packedPos, 2);
|
||||
var w = new Lemma("AZ", 1);
|
||||
var w = new Lemma("AZ");
|
||||
var undoBuffer = new int[10];
|
||||
|
||||
var placed = placeWord(grid, s, w, undoBuffer, 0);
|
||||
|
||||
Reference in New Issue
Block a user