Gather data
This commit is contained in:
@@ -341,7 +341,7 @@ public class Main {
|
|||||||
TOTAL_ATTEMPTS.incrementAndGet();
|
TOTAL_ATTEMPTS.incrementAndGet();
|
||||||
var swe = new SwedishGenerator(rng);
|
var swe = new SwedishGenerator(rng);
|
||||||
var mask = swe.generateMask(opts.pop, opts.gens);
|
var mask = swe.generateMask(opts.pop, opts.gens);
|
||||||
var filled = new CSP(rng).fillMask(mask, dict.index(), opts.fillTimeout);
|
var filled = swe.fillMask(mask, dict.index(), opts.fillTimeout);
|
||||||
|
|
||||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||||
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
if (rawClue.startsWith("\"") && rawClue.endsWith("\"")) {
|
if (rawClue.startsWith("\"") && rawClue.endsWith("\"")) {
|
||||||
rawClue = rawClue.substring(1, rawClue.length() - 1).replace("\"\"", "\"");
|
rawClue = rawClue.substring(1, rawClue.length() - 1).replace("\"\"", "\"");
|
||||||
}
|
}
|
||||||
map.add(new Lemma(id,s, simpel, GSON.fromJson(rawClue, String[].class)));
|
map.add(new Lemma(id, s, simpel, GSON.fromJson(rawClue, String[].class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Dict(map.toArray(Lemma[]::new));
|
return new Dict(map.toArray(Lemma[]::new));
|
||||||
@@ -379,8 +379,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
Arrays.fill(covH, 0, SIZE, 0);
|
Arrays.fill(covH, 0, SIZE, 0);
|
||||||
Arrays.fill(covV, 0, SIZE, 0);
|
Arrays.fill(covV, 0, SIZE, 0);
|
||||||
|
|
||||||
boolean[] hasSlots = { false };
|
boolean hasSlots = false;
|
||||||
long lo_cl = grid.bo[0], hi_cl = grid.bo[1];
|
long lo_cl = grid.bo[0], hi_cl = grid.bo[1];
|
||||||
while (lo_cl != 0) {
|
while (lo_cl != 0) {
|
||||||
int clueIdx = Long.numberOfTrailingZeros(lo_cl);
|
int clueIdx = Long.numberOfTrailingZeros(lo_cl);
|
||||||
lo_cl &= (lo_cl - 1);
|
lo_cl &= (lo_cl - 1);
|
||||||
@@ -400,12 +400,10 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
cc += nbrs16.dc();
|
cc += nbrs16.dc();
|
||||||
}
|
}
|
||||||
if (n == 0) continue;
|
if (n == 0) continue;
|
||||||
hasSlots[0] = true;
|
hasSlots = true;
|
||||||
if (n < MIN_LEN) {
|
if (n < MIN_LEN) {
|
||||||
penalty[0] += 8000;
|
penalty[0] += 8000;
|
||||||
} /*else if (lenCounts[n] <= 0) {
|
}
|
||||||
penalty[0] += 12000;
|
|
||||||
}*/
|
|
||||||
var horiz = Slot.horiz(d) ? covH : covV;
|
var horiz = Slot.horiz(d) ? covH : covV;
|
||||||
for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1;
|
for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1;
|
||||||
}
|
}
|
||||||
@@ -428,17 +426,15 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
cc += nbrs16.dc();
|
cc += nbrs16.dc();
|
||||||
}
|
}
|
||||||
if (n == 0) continue;
|
if (n == 0) continue;
|
||||||
hasSlots[0] = true;
|
hasSlots = true;
|
||||||
if (n < MIN_LEN) {
|
if (n < MIN_LEN) {
|
||||||
penalty[0] += 8000;
|
penalty[0] += 8000;
|
||||||
} /*else if (lenCounts[n] <= 0) {
|
}
|
||||||
penalty[0] += 12000;
|
|
||||||
}*/
|
|
||||||
var horiz = Slot.horiz(d) ? covH : covV;
|
var horiz = Slot.horiz(d) ? covH : covV;
|
||||||
for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1;
|
for (var i = 0; i < n; i++) horiz[Slot.offset(packedPos, i)] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasSlots[0]) return 1_000_000_000L;
|
if (!hasSlots) return 1_000_000_000L;
|
||||||
|
|
||||||
int idx, h, v;
|
int idx, h, v;
|
||||||
for (idx = 0; idx < SIZE; idx++) {
|
for (idx = 0; idx < SIZE; idx++) {
|
||||||
@@ -480,8 +476,19 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
|
|
||||||
if (size >= 2) penalty[0] += (size - 1L) * 120L;
|
if (size >= 2) penalty[0] += (size - 1L) * 120L;
|
||||||
});
|
});
|
||||||
int walls;
|
|
||||||
// dead-end-ish letter cell (3+ walls)
|
// dead-end-ish letter cell (3+ walls)
|
||||||
|
int walls, wc, wr;
|
||||||
|
/*for (var rci : IT) {
|
||||||
|
if (grid.isDigitAt(rci.i())) continue;
|
||||||
|
walls = 0;
|
||||||
|
for (var d : nbrs4) {
|
||||||
|
wr = rci.r() + d.r();
|
||||||
|
wc = rci.c() + d.c();
|
||||||
|
if (wr < 0 || wr >= R || wc < 0 || wc >= C || grid.isDigitAt(wr, wc)) walls++;
|
||||||
|
}
|
||||||
|
if (walls >= 3) penalty[0] += 400;
|
||||||
|
}*/
|
||||||
|
|
||||||
for (var rci : IT) {
|
for (var rci : IT) {
|
||||||
if (grid.isDigitAt(rci.i())) continue;
|
if (grid.isDigitAt(rci.i())) continue;
|
||||||
walls = (4 - rci.nbrCount()) + Long.bitCount(rci.n1() & grid.bo[0]) + Long.bitCount(rci.n2() & grid.bo[1]);
|
walls = (4 - rci.nbrCount()) + Long.bitCount(rci.n1() & grid.bo[0]) + Long.bitCount(rci.n2() & grid.bo[1]);
|
||||||
@@ -691,33 +698,31 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
|
|
||||||
return new CandidateInfo(in, curLen);
|
return new CandidateInfo(in, curLen);
|
||||||
}
|
}
|
||||||
record CSP(Rng rng) {
|
public FillResult fillMask(Grid mask, DictEntry[] dictIndex,
|
||||||
|
int timeLimitMs) {
|
||||||
|
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
||||||
|
val grid = mask.deepCopyGrid();
|
||||||
|
val used = new Bit1029();
|
||||||
|
val assigned = new HashMap<Integer, Lemma>();
|
||||||
|
val ctx = CTX.get();
|
||||||
|
val count = ctx.cellCount;
|
||||||
|
Arrays.fill(count, 0, SIZE, 0);
|
||||||
|
|
||||||
public FillResult fillMask(Grid mask, DictEntry[] dictIndex,
|
val slots = extractSlots(grid);
|
||||||
int timeLimitMs) {
|
for (var s : slots) for (int i = 0, len = s.len(); i < len; i++) count[s.pos(i)]++;
|
||||||
boolean multiThreaded = Thread.currentThread().getName().contains("pool");
|
|
||||||
var grid = mask.deepCopyGrid();
|
|
||||||
var slots = extractSlots(grid);
|
|
||||||
|
|
||||||
var used = new Bit1029();
|
val t0 = System.currentTimeMillis();
|
||||||
var assigned = new HashMap<Integer, Lemma>();
|
val stats = new FillStats();
|
||||||
|
val TOTAL = slots.size();
|
||||||
|
|
||||||
var ctx = CTX.get();
|
class Solver {
|
||||||
var count = ctx.cellCount;
|
|
||||||
Arrays.fill(count, 0, SIZE, 0);
|
|
||||||
for (var s : slots) for (int i = 0, len = s.len(); i < len; i++) count[s.pos(i)]++;
|
|
||||||
|
|
||||||
var t0 = System.currentTimeMillis();
|
long lastLog = t0;
|
||||||
final var lastLog = new AtomicLong(t0);
|
void renderProgress() {
|
||||||
|
|
||||||
var stats = new FillStats();
|
|
||||||
final var TOTAL = slots.size();
|
|
||||||
|
|
||||||
Runnable renderProgress = () -> {
|
|
||||||
if (!Main.VERBOSE || multiThreaded) return;
|
if (!Main.VERBOSE || multiThreaded) return;
|
||||||
var now = System.currentTimeMillis();
|
var now = System.currentTimeMillis();
|
||||||
if ((now - lastLog.get()) < LOG_EVERY_MS) return;
|
if ((now - lastLog) < LOG_EVERY_MS) return;
|
||||||
lastLog.set(now);
|
lastLog = (now);
|
||||||
|
|
||||||
var done = assigned.size();
|
var done = assigned.size();
|
||||||
var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100);
|
var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100);
|
||||||
@@ -732,9 +737,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
);
|
);
|
||||||
System.out.print("\r" + Strings.padRight(msg, 120));
|
System.out.print("\r" + Strings.padRight(msg, 120));
|
||||||
System.out.flush();
|
System.out.flush();
|
||||||
};
|
}
|
||||||
|
Pick chooseMRV() {
|
||||||
Supplier<Pick> chooseMRV = () -> {
|
|
||||||
Slot best = null;
|
Slot best = null;
|
||||||
CandidateInfo bestInfo = null;
|
CandidateInfo bestInfo = null;
|
||||||
int bestSlot = -1;
|
int bestSlot = -1;
|
||||||
@@ -766,79 +770,39 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
} else {
|
} else {
|
||||||
return new Pick(best, bestInfo, false);
|
return new Pick(best, bestInfo, false);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
boolean backtrack(int depth) {
|
||||||
|
if (Thread.currentThread().isInterrupted()) return false;
|
||||||
|
stats.nodes++;
|
||||||
|
|
||||||
class Solver {
|
if (timeLimitMs > 0 && (System.currentTimeMillis() - t0) > timeLimitMs) return false;
|
||||||
|
|
||||||
boolean backtrack(int depth) {
|
var pick = chooseMRV();
|
||||||
if (Thread.currentThread().isInterrupted()) return false;
|
if (pick.done) return true;
|
||||||
stats.nodes++;
|
if (pick.slot == null) {
|
||||||
|
stats.backtracks++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
val info = pick.info;
|
||||||
|
stats.lastMRV = info.count;
|
||||||
|
renderProgress();
|
||||||
|
|
||||||
if (timeLimitMs > 0 && (System.currentTimeMillis() - t0) > timeLimitMs) return false;
|
var s = pick.slot;
|
||||||
|
var k = s.key();
|
||||||
|
int patLen = s.len();
|
||||||
|
var entry = dictIndex[patLen];
|
||||||
|
var pat = new byte[patLen];
|
||||||
|
patternForSlot(grid, s, pat);
|
||||||
|
if (info.indices != null && info.indices.length > 0) {
|
||||||
|
var idxs = info.indices;
|
||||||
|
var L = idxs.length;
|
||||||
|
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
||||||
|
|
||||||
var pick = chooseMRV.get();
|
|
||||||
if (pick.done) return true;
|
|
||||||
if (pick.slot == null) {
|
|
||||||
stats.backtracks++;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
val info = pick.info;
|
|
||||||
stats.lastMRV = info.count;
|
|
||||||
renderProgress.run();
|
|
||||||
|
|
||||||
var s = pick.slot;
|
|
||||||
var k = s.key();
|
|
||||||
int patLen = s.len();
|
|
||||||
var entry = dictIndex[patLen];
|
|
||||||
var pat = new byte[patLen];
|
|
||||||
patternForSlot(grid, s, pat);
|
|
||||||
if (info.indices != null && info.indices.length > 0) {
|
|
||||||
var idxs = info.indices;
|
|
||||||
var L = idxs.length;
|
|
||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
|
||||||
|
|
||||||
for (var t = 0; t < tries; t++) {
|
|
||||||
double r = rng.nextFloat();
|
|
||||||
int idxInArray = (int) (r * r * r * L);
|
|
||||||
var idx = idxs[idxInArray];
|
|
||||||
var w = entry.words.get(idx);
|
|
||||||
|
|
||||||
if (used.get(w.index())) continue;
|
|
||||||
|
|
||||||
boolean match = true;
|
|
||||||
for (var i = 0; i < patLen; i++) {
|
|
||||||
if (pat[i] != DASH && pat[i] != w.byteAt(i)) {
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match || !placeWord(grid, s, w, ctx.undo, depth)) continue;
|
|
||||||
|
|
||||||
used.set(w.index());
|
|
||||||
assigned.put(k, w);
|
|
||||||
|
|
||||||
if (backtrack(depth + 1)) return true;
|
|
||||||
|
|
||||||
assigned.remove(k);
|
|
||||||
used.clear(w.index);
|
|
||||||
s.undoPlace(grid, ctx.undo[depth]);
|
|
||||||
}
|
|
||||||
stats.backtracks++;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var N = entry.words.size();
|
|
||||||
if (N == 0) {
|
|
||||||
stats.backtracks++;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
double r = rng.nextFloat();
|
||||||
int idxInArray = (int) (r * r * r * N);
|
int idxInArray = (int) (r * r * r * L);
|
||||||
var w = entry.words.get(idxInArray);
|
var idx = idxs[idxInArray];
|
||||||
|
var w = entry.words.get(idx);
|
||||||
|
|
||||||
if (used.get(w.index())) continue;
|
if (used.get(w.index())) continue;
|
||||||
|
|
||||||
@@ -849,6 +813,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match || !placeWord(grid, s, w, ctx.undo, depth)) continue;
|
if (!match || !placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||||
|
|
||||||
used.set(w.index());
|
used.set(w.index());
|
||||||
@@ -860,36 +825,71 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
used.clear(w.index);
|
used.clear(w.index);
|
||||||
s.undoPlace(grid, ctx.undo[depth]);
|
s.undoPlace(grid, ctx.undo[depth]);
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.backtracks++;
|
stats.backtracks++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var N = entry.words.size();
|
||||||
|
if (N == 0) {
|
||||||
|
stats.backtracks++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
|
||||||
|
for (var t = 0; t < tries; t++) {
|
||||||
|
double r = rng.nextFloat();
|
||||||
|
int idxInArray = (int) (r * r * r * N);
|
||||||
|
var w = entry.words.get(idxInArray);
|
||||||
|
|
||||||
|
if (used.get(w.index())) continue;
|
||||||
|
|
||||||
|
boolean match = true;
|
||||||
|
for (var i = 0; i < patLen; i++) {
|
||||||
|
if (pat[i] != DASH && pat[i] != w.byteAt(i)) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match || !placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||||
|
|
||||||
|
used.set(w.index());
|
||||||
|
assigned.put(k, w);
|
||||||
|
|
||||||
|
if (backtrack(depth + 1)) return true;
|
||||||
|
|
||||||
|
assigned.remove(k);
|
||||||
|
used.clear(w.index);
|
||||||
|
s.undoPlace(grid, ctx.undo[depth]);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.backtracks++;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial render (same feel)
|
|
||||||
renderProgress.run();
|
|
||||||
var ok = new Solver().backtrack(0);
|
|
||||||
// final progress line
|
|
||||||
if (!multiThreaded) {
|
|
||||||
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
|
||||||
System.out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.seconds = (System.currentTimeMillis() - t0) / 1000.0;
|
|
||||||
var res = new FillResult(ok, new Gridded(grid), assigned, stats);
|
|
||||||
|
|
||||||
// print a final progress line
|
|
||||||
if (Main.VERBOSE && !multiThreaded) {
|
|
||||||
System.out.println(
|
|
||||||
String.format(Locale.ROOT,
|
|
||||||
"[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs",
|
|
||||||
assigned.size(), TOTAL, stats.nodes, stats.backtracks, stats.lastMRV, stats.seconds
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// initial render (same feel)
|
||||||
|
var solver = new Solver();
|
||||||
|
solver.renderProgress();
|
||||||
|
var ok = solver.backtrack(0);
|
||||||
|
// final progress line
|
||||||
|
if (!multiThreaded) {
|
||||||
|
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
||||||
|
System.out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.seconds = (System.currentTimeMillis() - t0) / 1000.0;
|
||||||
|
var res = new FillResult(ok, new Gridded(grid), assigned, stats);
|
||||||
|
|
||||||
|
// print a final progress line
|
||||||
|
if (Main.VERBOSE && !multiThreaded) {
|
||||||
|
System.out.println(
|
||||||
|
String.format(Locale.ROOT,
|
||||||
|
"[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs",
|
||||||
|
assigned.size(), TOTAL, stats.nodes, stats.backtracks, stats.lastMRV, stats.seconds
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user