From 5849f543c5d7ca92a23cee1532995c24c002c854 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 14 Jan 2026 20:04:14 +0100 Subject: [PATCH] introduce bitloops --- src/main/java/puzzle/CsvIndexService.java | 22 +++++++--------- src/main/java/puzzle/Main.java | 29 ++++++++++++++-------- src/main/java/puzzle/SwedishGenerator.java | 3 +++ 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/main/java/puzzle/CsvIndexService.java b/src/main/java/puzzle/CsvIndexService.java index ccbcca1..2e99844 100644 --- a/src/main/java/puzzle/CsvIndexService.java +++ b/src/main/java/puzzle/CsvIndexService.java @@ -50,9 +50,9 @@ public final class CsvIndexService var parts = line.split(",", 5); var id = Integer.parseInt(parts[0].trim()); var word = parts[1].trim(); - if (!word.matches("^[A-Z]{2,8}$")) { + /* if (!word.matches("^[A-Z]{2,8}$")) { throw new RuntimeException("Invalid word:" + line); - } + }*/ int score = Integer.parseInt(parts[2].trim()); if (score < 1) { @@ -133,8 +133,7 @@ public final class CsvIndexService throw new IndexOutOfBoundsException("lineIndex=" + lineIndex + ", max=" + (local.length - 1)); } - var start = local[lineIndex]; - csvChannel.position(start); + long currentPos = local[lineIndex]; // lees in blokjes (sneller dan 1 byte) tot newline var buf = new byte[8192]; @@ -143,17 +142,15 @@ public final class CsvIndexService while (true) { var bb = ByteBuffer.wrap(buf); - var n = csvChannel.read(bb); + var n = csvChannel.read(bb, currentPos); if (n < 0) break; // EOF + currentPos += n; var end = n; for (var i = 0; i < end; i++) { var b = buf[i]; if (b == (byte) '\n') { - // reposition kanaal op byte na newline - long back = (end - i - 1); - csvChannel.position(csvChannel.position() - back); return new String(out, 0, total, StandardCharsets.UTF_8); } if (b == (byte) '\r') continue; @@ -194,10 +191,10 @@ public final class CsvIndexService try (var ch = FileChannel.open(path, StandardOpenOption.READ)) { var offs = new long[131072]; // start-capacity, groeit indien nodig var c = 0; - offs[c++] = 0L; + offs[c++] = 0; - var buf = ByteBuffer.allocateDirect(1 << 20); - long pos = 0; + var buf = ByteBuffer.allocateDirect(1 << 20); + int pos = 0; while (true) { buf.clear(); @@ -230,8 +227,7 @@ public final class CsvIndexService public static long[] readIndex(Path in) throws IOException { try (var dis = new DataInputStream(new BufferedInputStream(Files.newInputStream(in)))) { - var magic = dis.readInt(); - if (magic != MAGIC) throw new IOException("Not a LIDX file"); + if (dis.readInt() != MAGIC) throw new IOException("Not a LIDX file"); var version = dis.readInt(); if (version != VERSION) throw new IOException("Unsupported version: " + version); diff --git a/src/main/java/puzzle/Main.java b/src/main/java/puzzle/Main.java index afdf8e9..2aaa5bd 100644 --- a/src/main/java/puzzle/Main.java +++ b/src/main/java/puzzle/Main.java @@ -44,16 +44,16 @@ public class Main { public static class Opts { public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis()); - public int clueSize = 24; + public int clueSize = 20; public int pop = 40; public int offspring = 60; - public int gens = 700; + public int gens = 500; public String wordsPath = "nl_score_hints_v3.csv"; public double minSimplicity = 0; // 0 means no limit public int threads = Math.max(1, Runtime.getRuntime().availableProcessors()); public int tries = threads; public boolean reindex = false; - public boolean verbose = false; + public boolean verbose = true; } @@ -206,7 +206,7 @@ public class Main { --clues 18 --pop 40 --offspring 60 - --gens 700 + --gens 500 --words nl_score_hints.csv --min-simplicity 0 (no limit) --threads %d @@ -286,9 +286,10 @@ public class Main { try { // Keep at least some tasks in flight + final var service = CsvIndexService.SC.get(); for (int i = 0; i < opts.threads; i++) { - final int attempt = ++submitted; - completionService.submit(() -> attempt(new Rng(opts.seed + attempt), dict, opts)); + final int attemptIdx = ++submitted; + completionService.submit(() -> ScopedValue.where(CsvIndexService.SC, service).call(() -> attempt(new Rng(opts.seed + attemptIdx), dict, opts))); } while (System.currentTimeMillis() < deadline) { @@ -304,8 +305,8 @@ public class Main { // Submit another task if we still have time if (System.currentTimeMillis() < deadline) { - final int attempt = ++submitted; - completionService.submit(() -> attempt(new Rng(opts.seed + attempt), dict, opts)); + final int attemptIdx = ++submitted; + completionService.submit(() -> ScopedValue.where(CsvIndexService.SC, service).call(() -> attempt(new Rng(opts.seed + attemptIdx), dict, opts))); } } if (resFinal == null) warn("status : UNSOLVED (timeout)"); @@ -316,6 +317,11 @@ public class Main { warn("status : ERROR (" + e.getMessage() + ")"); } finally { executor.shutdownNow(); + try { + executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } } else { @@ -369,9 +375,11 @@ public class Main { } } static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) { + long t0 = System.currentTimeMillis(); TOTAL_ATTEMPTS.incrementAndGet(); var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); - var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, Math.max(opts.offspring, (int) Math.floor(1.5 * opts.pop))); + var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring); + if (mask == null) return null; var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); @@ -387,10 +395,11 @@ public class Main { var status = filled.ok() ? "SUCCESS" : "FAILED"; var simplicity = String.format(Locale.ROOT, "%.2f", filled.stats().simplicity); var nps = (int) (filled.stats().nodes / Math.max(0.001, filled.stats().seconds)); + var totalTime = (System.currentTimeMillis() - t0) / 1000.0; 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 + name, status, filled.stats().nodes, filled.stats().backtracks, nps, simplicity, totalTime ); if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) { diff --git a/src/main/java/puzzle/SwedishGenerator.java b/src/main/java/puzzle/SwedishGenerator.java index 32b01be..8cc6ed2 100644 --- a/src/main/java/puzzle/SwedishGenerator.java +++ b/src/main/java/puzzle/SwedishGenerator.java @@ -687,6 +687,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize); var pop = new ArrayList(); for (var i = 0; i < popSize; i++) { + if (Thread.currentThread().isInterrupted()) return null; pop.add(new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180))); } @@ -695,6 +696,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { var children = new ArrayList(); for (var k = 0; k < offspring; k++) { + if (Thread.currentThread().isInterrupted()) break; var p1 = pop.get(rng.randint(0, pop.size() - 1)); var p2 = pop.get(rng.randint(0, pop.size() - 1)); var child = crossover(p1.grid, p2.grid); @@ -720,6 +722,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) { if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop.get(0).fit()); } + if (pop.isEmpty()) return null; GridAndFit best = pop.get(0); for (int i = 1; i < pop.size(); i++) { var x = pop.get(i);