Gather data
This commit is contained in:
@@ -11,6 +11,7 @@ import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static puzzle.ExportFormat.*;
|
||||
@@ -18,8 +19,10 @@ import static puzzle.SwedishGenerator.*;
|
||||
import static puzzle.SwedishGenerator.loadWords;
|
||||
|
||||
public class Main {
|
||||
|
||||
// ---------------- Top-level generatePuzzle ----------------
|
||||
public record PuzzleResult(SwedishGenerator swe, Dict dict, Grid mask, FillResult filled) { }
|
||||
|
||||
final static String OUT_DIR = envOrDefault("OUT_DIR", "/data/puzzle");
|
||||
final static Path PUZZLE_DIR = Paths.get(OUT_DIR, "puzzles");
|
||||
static final Path INDEX_FILE = PUZZLE_DIR.resolve("index.json");
|
||||
@@ -30,12 +33,18 @@ public class Main {
|
||||
static final Path OUTPUT_PATH = PUZZLE_DIR.resolve(FILE_NAME);
|
||||
static final String DATE_STRING = now.toLocalDate().toString();
|
||||
|
||||
static final AtomicLong TOTAL_NODES = new AtomicLong(0);
|
||||
static final AtomicLong TOTAL_BACKTRACKS = new AtomicLong(0);
|
||||
static final AtomicLong TOTAL_ATTEMPTS = new AtomicLong(0);
|
||||
static final AtomicLong TOTAL_SUCCESS = new AtomicLong(0);
|
||||
static final AtomicLong TOTAL_SIMPLICITY = new AtomicLong(0); // Scaled by 100 for precision
|
||||
|
||||
@Data
|
||||
public static class Opts {
|
||||
|
||||
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
||||
public int pop = 18;
|
||||
public int gens = 500;
|
||||
public int gens = 2000;
|
||||
public String wordsPath = "nl_score_hints.csv";
|
||||
public double minSimplicity = 0; // 0 means no limit
|
||||
public int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
|
||||
@@ -224,6 +233,8 @@ public class Main {
|
||||
i++;
|
||||
} else if (a.equals("--reindex")) {
|
||||
out.reindex = true;
|
||||
} else if (a.equals("--verbose")) {
|
||||
out.verbose = true;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown arg: " + a);
|
||||
}
|
||||
@@ -245,9 +256,10 @@ public class Main {
|
||||
info(String.format(Locale.ROOT, "loadTime : %.3f s", (tLoad1 - tLoad0) / 1e9));
|
||||
|
||||
section("Search");
|
||||
|
||||
var tSearch0 = System.currentTimeMillis();
|
||||
var deadline = System.currentTimeMillis() + 40_000;
|
||||
|
||||
PuzzleResult resFinal = null;
|
||||
if (opts.threads > 1) {
|
||||
info("mode : multi-threaded (" + opts.threads + ")");
|
||||
var executor = Executors.newFixedThreadPool(opts.threads);
|
||||
@@ -268,7 +280,8 @@ public class Main {
|
||||
var result = future.get();
|
||||
if (result != null) {
|
||||
info("status : SOLVED");
|
||||
return result;
|
||||
resFinal = result;
|
||||
break;
|
||||
}
|
||||
|
||||
// Submit another task if we still have time
|
||||
@@ -277,7 +290,7 @@ public class Main {
|
||||
completionService.submit(() -> attempt(new Rng(opts.seed + attempt), dict, opts));
|
||||
}
|
||||
}
|
||||
warn("status : UNSOLVED (timeout)");
|
||||
if (resFinal == null) warn("status : UNSOLVED (timeout)");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
warn("status : INTERRUPTED");
|
||||
@@ -286,7 +299,6 @@ public class Main {
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
return null;
|
||||
|
||||
} else {
|
||||
info("mode : single-threaded");
|
||||
@@ -301,20 +313,57 @@ public class Main {
|
||||
if (result != null) {
|
||||
info("status : SOLVED");
|
||||
info("foundAtTry : " + attempt);
|
||||
return result;
|
||||
resFinal = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info("status : UNSOLVED (timeout)");
|
||||
return null;
|
||||
if (resFinal == null) info("status : UNSOLVED (timeout)");
|
||||
}
|
||||
|
||||
var tSearch1 = System.currentTimeMillis();
|
||||
var searchTime = (tSearch1 - tSearch0) / 1000.0;
|
||||
|
||||
section("Performance");
|
||||
info(String.format(Locale.ROOT, "totalNodes : %,d", TOTAL_NODES.get()));
|
||||
info(String.format(Locale.ROOT, "totalBacks : %,d", TOTAL_BACKTRACKS.get()));
|
||||
info(String.format(Locale.ROOT, "searchTime : %.2f s", searchTime));
|
||||
info(String.format(Locale.ROOT, "nodes/sec : %,d", (int) (TOTAL_NODES.get() / Math.max(0.001, searchTime))));
|
||||
|
||||
section("Material");
|
||||
info(String.format(Locale.ROOT, "attempts : %,d", TOTAL_ATTEMPTS.get()));
|
||||
info(String.format(Locale.ROOT, "successRate : %.1f%%", (TOTAL_ATTEMPTS.get() == 0) ? 0 : (TOTAL_SUCCESS.get() * 100.0 / TOTAL_ATTEMPTS.get())));
|
||||
if (TOTAL_SUCCESS.get() > 0) {
|
||||
info(String.format(Locale.ROOT, "avgSimplic : %.2f", (TOTAL_SIMPLICITY.get() / 100.0) / TOTAL_SUCCESS.get()));
|
||||
}
|
||||
info(String.format(Locale.ROOT, "dictWords : %,d", dict.wordz().length));
|
||||
|
||||
return resFinal;
|
||||
}
|
||||
|
||||
static PuzzleResult attempt(Rng rng, Dict dict, Opts opts) {
|
||||
TOTAL_ATTEMPTS.incrementAndGet();
|
||||
var swe = new SwedishGenerator();
|
||||
var mask = swe.generateMask(rng, dict.lenCounts(), opts.pop, opts.gens, opts.verbose);
|
||||
var filled = swe.fillMask(rng, mask, dict.index(), 200, opts.fillTimeout, opts.verbose);
|
||||
|
||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
||||
if (filled.ok()) {
|
||||
TOTAL_SUCCESS.incrementAndGet();
|
||||
TOTAL_SIMPLICITY.addAndGet((long) (filled.simplicity() * 100));
|
||||
}
|
||||
|
||||
var name = Thread.currentThread().getName();
|
||||
var status = filled.ok() ? "SUCCESS" : "FAILED";
|
||||
var simplicity = String.format(Locale.ROOT, "%.2f", filled.simplicity());
|
||||
var nps = (int) (filled.stats().nodes / Math.max(0.001, filled.stats().seconds));
|
||||
|
||||
System.out.printf(Locale.ROOT,
|
||||
"[ATTEMPT] thread=%s | status=%s | nodes=%d | backtracks=%d | nps=%d | simplicity=%s | time=%.1fs%n",
|
||||
name, status, filled.stats().nodes, filled.stats().backtracks, nps, simplicity, filled.stats().seconds
|
||||
);
|
||||
|
||||
if (filled.ok() && (opts.minSimplicity <= 0 || filled.simplicity() >= opts.minSimplicity)) {
|
||||
return new PuzzleResult(swe, dict, mask, filled);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user