509 lines
22 KiB
Java
509 lines
22 KiB
Java
package puzzle;
|
|
|
|
import lombok.AllArgsConstructor;
|
|
import lombok.Getter;
|
|
import lombok.NoArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.experimental.Accessors;
|
|
import lombok.experimental.Delegate;
|
|
import lombok.val;
|
|
import precomp.Neighbors9x8;
|
|
import precomp.Neighbors9x8.rci;
|
|
import java.util.Locale;
|
|
import static java.lang.Long.*;
|
|
import static java.lang.Long.numberOfTrailingZeros;
|
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
|
|
|
/**
|
|
* NOTE:
|
|
* A) generate randoms for and idx and direction (clue together)
|
|
* B) convert IDX_PATH_0_BASE into bit-pattern (i think) Length should not be checked, just left empty if not good.
|
|
* C) store more information in the byte[] even consider the long[] or try sqeeze the 288 options (clue) in a byte.
|
|
* F) pre-determine random clue arrangements, so they do not involve impossible clue's such as bottom at last or the line before that and such to all sides
|
|
* G) Check 3wall, the current implementation may be faster, but the accuracy is lower, degrade performance on the filler
|
|
*/
|
|
|
|
/**
|
|
* SwedishGenerator.java
|
|
*
|
|
* Usage:
|
|
* javac SwedishGenerator.java
|
|
* java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt]
|
|
*/
|
|
@SuppressWarnings("ALL")
|
|
public class SwedishGenerator {
|
|
|
|
public static final long GT_1_OFFSET_53_BIT = 0x3E00000000000000L;
|
|
public static final long X = 0L;
|
|
public static final int LOG_EVERY_MS = 200;
|
|
public static final int BAR_LEN = 22;
|
|
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 double CROSS_C = (R - 1) / 2.0;
|
|
public static final int SIZE = C * R;// ~18
|
|
public static final int SIZE_MIN_1 = SIZE - 1;// ~18
|
|
public static final double SIZED = (double) SIZE;// ~18
|
|
public static final long MASK_LO = (SIZE >= 64) ? -1L : (1L << SIZE) - 1;
|
|
public static final long MASK_HI = (SIZE <= 64) ? 0L : (SIZE >= 128 ? -1L : (1L << (SIZE - 64)) - 1);
|
|
public static final int MAX_WORD_LENGTH = C <= R ? C : R;
|
|
public static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
|
|
public static final int MIN_LEN = Config.MIN_LEN;
|
|
public static final int MAX_TRIES_PER_SLOT = Config.MAX_TRIES_PER_SLOT;
|
|
public static final int STACK_SIZE = 64;
|
|
public static final char C_DASH = '\0';
|
|
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_624 = 624L - 0L + 1L;
|
|
public static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1;
|
|
public static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
|
interface Bit1029 {
|
|
|
|
static long[] bit1029() { return new long[2048]; }
|
|
private static int wordIndex(int bitIndex) { return bitIndex >> 6; }
|
|
static boolean get(long[] bits, int bitIndex) { return (bits[wordIndex(bitIndex)] & 1L << bitIndex) != 0L; }
|
|
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 String padRight(String s, int n) {
|
|
if (s.length() >= n) return s;
|
|
return s + " ".repeat(n - s.length());
|
|
}
|
|
@AllArgsConstructor
|
|
public static class Pick {
|
|
|
|
public Slotinfo slot;
|
|
public int[] indices;
|
|
public int count;
|
|
}
|
|
|
|
public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSET_D_IDX_0_BASE;
|
|
public static final rci[] IT = Neighbors9x8.IT;
|
|
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[] 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 Pick PICK_NOT_DONE = new Pick(null, null, 0);
|
|
|
|
@RequiredArgsConstructor
|
|
@Getter
|
|
@Accessors(fluent = true)
|
|
public static final class FillStats {
|
|
|
|
final public double seconds;
|
|
final public int lastMRV;
|
|
public double simplicity;
|
|
}
|
|
|
|
public static record FillResult(boolean ok, long nodes, long backtracks, @Delegate FillStats stats) {
|
|
|
|
static public long calcSimpel(Slotinfo[] slots) {
|
|
int k = 0;
|
|
long simpel = 0L;
|
|
for (var n = 1; n < slots.length; n++) {
|
|
if (slots[n].assign.w != X) {
|
|
k++;
|
|
simpel += Lemma.simpel(slots[n].assign.w);
|
|
}
|
|
}
|
|
simpel = k == 0 ? 0 : simpel / k;
|
|
return simpel;
|
|
}
|
|
}
|
|
|
|
public static final class Rng {
|
|
|
|
@Getter private int x;
|
|
public Rng(int seed) {
|
|
var s = seed;
|
|
if (s == 0) s = 1;
|
|
this.x = s;
|
|
}
|
|
public int nextU32() {
|
|
var y = x;
|
|
y ^= (y << 13);
|
|
y ^= (y >>> 17);
|
|
y ^= (y << 5);
|
|
x = y;
|
|
return y;
|
|
}
|
|
public int randint2bit() { return nextU32() & 3; }
|
|
public byte randint2bitByte() { return (byte) (nextU32() & 3); }
|
|
public int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max - 0L + 1L))); }
|
|
public int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); }
|
|
public int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); }
|
|
public double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
|
|
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 static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
|
|
|
public static interface Lemma {
|
|
|
|
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
|
|
static final long INDEX_MASK = (1L << 24) - 1; // 24 bits
|
|
|
|
static long pack(String word) { return pack(word.getBytes(US_ASCII)); }
|
|
static long pack(int index, byte[] b) { return pack(b) | ((long) index << 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 public long from(int index, String word) { return pack(index, word.getBytes(US_ASCII)); }
|
|
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111); }
|
|
static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); }
|
|
static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); }
|
|
static int length(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; }
|
|
static ThreadLocal<byte[]> BYTES = ThreadLocal.withInitial(() -> new byte[MAX_WORD_LENGTH]);
|
|
public static String asWord(long word) {
|
|
val len = Lemma.length(word);
|
|
var b = BYTES.get();//new byte[Lemma.length(word)];
|
|
for (int i = 0, bi = 0; i < len * 5; bi++, i += 5) b[bi] = (byte) (((word >>> i) & 31) | 64);
|
|
return new String(b, 0, 0, len);
|
|
}
|
|
static int unpackIndex(long w) { return (int) (w >>> 40); }
|
|
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;
|
|
}
|
|
|
|
static record Slotinfo(int key, long lo, long hi, int score, Assign assign, DictEntry entry) {
|
|
|
|
public static int wordCount(int k, Slotinfo[] arr) {
|
|
for (var n = 1; n < arr.length; n++) if (arr[n].assign.w != X) k++;
|
|
return k;
|
|
}
|
|
public static boolean increasing(int dir) { return (dir & 2) == 0; }
|
|
}
|
|
|
|
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) {
|
|
if (((lo & glo) | (hi & ghi)) == X) return 0;
|
|
long p = 0;
|
|
int n = 0;
|
|
if (Slotinfo.increasing(key)) {
|
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
int i = bitCount(lo & ((1L << idx) - 1));
|
|
p |= ((long) (i * 26 + g[idx])) << (n++ << 3);
|
|
}
|
|
int offset = bitCount(lo);
|
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
int i = offset + bitCount(hi & ((1L << idx) - 1));
|
|
p |= ((long) (i * 26 + g[64 | idx])) << (n++ << 3);
|
|
}
|
|
} else {
|
|
int offset = bitCount(hi);
|
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
int i = bitCount(hi & ~((1L << idx) | ((1L << idx) - 1)));
|
|
p |= ((long) (i * 26 + g[64 | idx])) << (n++ << 3);
|
|
}
|
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
int i = offset + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1)));
|
|
p |= ((long) (i * 26 + g[idx])) << (n++ << 3);
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
public static boolean placeWord(final Grid grid, final byte[] g, final int key, final long lo, final long hi, final long w) {
|
|
final long glo = grid.lo, ghi = grid.hi;
|
|
if (Slotinfo.increasing(key)) {
|
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
if (g[idx] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
|
|
}
|
|
int bcLo = bitCount(lo);
|
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
if (g[64 | idx] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false;
|
|
}
|
|
|
|
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
|
|
if ((maskLo | maskHi) != X) {
|
|
for (long b = maskLo; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
g[idx] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
|
|
}
|
|
for (long b = maskHi; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
g[64 | idx] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
|
|
}
|
|
grid.lo |= maskLo;
|
|
grid.hi |= maskHi;
|
|
}
|
|
} else {
|
|
int bcHi = bitCount(hi);
|
|
for (long b = hi & ghi; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
if (g[64 | idx] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false;
|
|
}
|
|
for (long b = lo & glo; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
if (g[idx] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false;
|
|
}
|
|
|
|
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
|
|
if ((maskLo | maskHi) != X) {
|
|
for (long b = maskHi; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
g[64 | idx] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))));
|
|
}
|
|
for (long b = maskLo; b != X; b &= b - 1) {
|
|
int idx = numberOfTrailingZeros(b);
|
|
g[idx] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))));
|
|
}
|
|
grid.lo |= maskLo;
|
|
grid.hi |= maskHi;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// pattern cannot be X
|
|
public static int[] candidateInfoForPattern(long[] res, long pattern, long[][] posBitsets, int numLongs) {
|
|
System.arraycopy(posBitsets[(int) (pattern & 0xFF) - 1], 0, res, 0, numLongs);
|
|
for (long p = pattern >>> 8; p != X; p >>>= 8) {
|
|
long[] bs = posBitsets[(int) (p & 0xFF) - 1];
|
|
for (int k = 0; k < numLongs; k++) res[k] &= bs[k];
|
|
}
|
|
|
|
int count = 0;
|
|
for (int k = 0; k < numLongs; k++) count += bitCount(res[k]);
|
|
|
|
int[] indices = new int[count];
|
|
for (int k = 0, ki = 0; k < numLongs; k++) {
|
|
for (long w = res[k]; w != X; w &= w - 1) indices[ki++] = (k << 6) | numberOfTrailingZeros(w);
|
|
}
|
|
|
|
return indices;
|
|
}
|
|
/// pattern cannot be X
|
|
public static int candidateCountForPattern(final long[] res, final long pattern, final long[][] posBitsets, final int numLongs) {
|
|
System.arraycopy(posBitsets[(int) (pattern & 0xFF) - 1], 0, res, 0, numLongs);
|
|
for (long p = pattern >>> 8; p != X; p >>>= 8) {
|
|
long[] bs = posBitsets[(int) (p & 0xFF) - 1];
|
|
for (int k = 0; k < numLongs; k++) res[k] &= bs[k];
|
|
}
|
|
|
|
int count = 0;
|
|
for (int k = 0; k < numLongs; k++) count += bitCount(res[k]);
|
|
return count;
|
|
}
|
|
|
|
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
|
|
final Grid grid,
|
|
final boolean multiThreaded) {
|
|
val NO_LOG = (!Main.VERBOSE || multiThreaded);
|
|
val used = new long[2048];
|
|
val bitset = new long[2500];
|
|
val g = grid.g;
|
|
val TOTAL = slots.length;
|
|
val t0 = System.currentTimeMillis();
|
|
val CARRIER = new Pick(null, null, 0);
|
|
class Solver {
|
|
|
|
long nodes;
|
|
long backtracks;
|
|
int lastMRV;
|
|
long lastLog = t0, glo = grid.lo, ghi = grid.hi;
|
|
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 placeWord(final int key, final long lo, final long hi, final long w) {
|
|
int idx;
|
|
if (Slotinfo.increasing(key)) {
|
|
for (long b = lo & glo; b != X; b &= b - 1) if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)))) return false;
|
|
int bcLo = bitCount(lo);
|
|
for (long b = hi & ghi; b != X; b &= b - 1)
|
|
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)))) return false;
|
|
|
|
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
|
|
if ((maskLo | maskHi) != X) {
|
|
for (long b = maskLo; b != X; b &= b - 1) g[idx = idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bitCount(lo & ((1L << idx) - 1)));
|
|
for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bcLo + bitCount(hi & ((1L << idx) - 1)));
|
|
glo |= maskLo;
|
|
ghi |= maskHi;
|
|
}
|
|
} else {
|
|
int bcHi = bitCount(hi);
|
|
for (long b = hi & ghi; b != X; b &= b - 1)
|
|
if (g[64 | (idx = numberOfTrailingZeros(b))] != Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))))) return false;
|
|
for (long b = lo & glo; b != X; b &= b - 1)
|
|
if (g[idx = numberOfTrailingZeros(b)] != Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))))) return false;
|
|
|
|
long maskLo = lo & ~glo, maskHi = hi & ~ghi;
|
|
if ((maskLo | maskHi) != X) {
|
|
for (long b = maskHi; b != X; b &= b - 1) g[64 | (idx = numberOfTrailingZeros(b))] = Lemma.byteAt(w, bitCount(hi & ~((1L << idx) | ((1L << idx) - 1))));
|
|
for (long b = maskLo; b != X; b &= b - 1) g[idx = numberOfTrailingZeros(b)] = Lemma.byteAt(w, bcHi + bitCount(lo & ~((1L << idx) | ((1L << idx) - 1))));
|
|
glo |= maskLo;
|
|
ghi |= maskHi;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
void chooseMRV() {
|
|
Slotinfo best = null;
|
|
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) {
|
|
var s = slots[i];
|
|
if (s.assign.w != X) continue;
|
|
var pattern = patternForSlot(glo, ghi, g, s.key, s.lo, s.hi);
|
|
var index = s.entry;
|
|
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
|
|
|
|
if (count == 0) {
|
|
current = PICK_NOT_DONE;
|
|
return;
|
|
}
|
|
if (best == null
|
|
|| count < count2
|
|
|| (count == count2 && s.score > bestScore)) {
|
|
best = s;
|
|
bestScore = s.score;
|
|
count2 = count;
|
|
if (count <= 1) break;
|
|
}
|
|
}
|
|
// Re-calculate for the best slot to get actual indices
|
|
if (best == null) {
|
|
current = PICK_DONE;
|
|
return;
|
|
}
|
|
var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi);
|
|
var index = best.entry;
|
|
current = CARRIER;
|
|
current.slot = best;
|
|
current.count = index.length;
|
|
if (pattern == X) {
|
|
current.indices = null;
|
|
return;
|
|
}
|
|
current.indices = candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
|
|
}
|
|
boolean backtrack(int depth) {
|
|
if (Thread.currentThread().isInterrupted()) return false;
|
|
nodes++;
|
|
|
|
if (20_000 > 0 && (System.currentTimeMillis() - t0) > 20_000) return false;
|
|
|
|
chooseMRV();
|
|
var pick = current;
|
|
if (pick == PICK_DONE) return true;
|
|
if (pick.slot == null) {
|
|
backtracks++;
|
|
return false;
|
|
}
|
|
val info = pick.indices;
|
|
lastMRV = pick.count;
|
|
if (!NO_LOG) renderProgress();
|
|
|
|
val s = pick.slot;
|
|
val k = s.key;
|
|
val slo = s.lo;
|
|
val shi = s.hi;
|
|
val entry = s.entry;
|
|
long low, top;
|
|
if (info != null && info.length > 0) {
|
|
var idxs = info;
|
|
var L = idxs.length;
|
|
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
|
|
|
for (var t = 0; t < tries; t++) {
|
|
var r = rng.nextFloat();
|
|
//int idxInArray = rng.biasedIndexPow3(L - 1);
|
|
var w = entry.words[idxs[(int) (r * r * r * (L - 1))]];
|
|
var lemIdx = Lemma.unpackIndex(w);
|
|
if (Bit1029.get(used, lemIdx)) continue;
|
|
low = glo;
|
|
top = ghi;
|
|
if (!placeWord(k, slo, shi, w)) continue;
|
|
|
|
Bit1029.set(used, lemIdx);
|
|
s.assign.w = w;
|
|
if (backtrack(depth + 1)) return true;
|
|
s.assign.w = X;
|
|
Bit1029.clear(used, lemIdx);
|
|
glo = low;
|
|
ghi = top;
|
|
}
|
|
backtracks++;
|
|
return false;
|
|
}
|
|
|
|
var N = entry.words.length;
|
|
|
|
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
|
|
for (var t = 0; t < tries; t++) {
|
|
double r = rng.nextFloat();
|
|
var w = entry.words[(int) (r * r * r * (N - 1))];
|
|
var lemIdx = Lemma.unpackIndex(w);
|
|
if (Bit1029.get(used, lemIdx)) continue;
|
|
low = glo;
|
|
top = ghi;
|
|
if (!placeWord(k, slo, shi, w)) continue;
|
|
|
|
Bit1029.set(used, lemIdx);
|
|
s.assign.w = w;
|
|
if (backtrack(depth + 1)) return true;
|
|
s.assign.w = X;
|
|
Bit1029.clear(used, lemIdx);
|
|
|
|
glo = low;
|
|
ghi = top;
|
|
}
|
|
|
|
backtracks++;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// initial render (same feel)
|
|
var solver = new Solver();
|
|
if (!NO_LOG) solver.renderProgress();
|
|
var ok = solver.backtrack(0);
|
|
// final progress line
|
|
grid.lo = solver.glo;
|
|
grid.hi = solver.ghi;
|
|
|
|
return new FillResult(ok, solver.nodes, solver.backtracks, new FillStats((System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
|
|
}
|
|
}
|