introduce bitloops
This commit is contained in:
@@ -44,7 +44,9 @@ public class Main {
|
|||||||
public static class Opts {
|
public static class Opts {
|
||||||
|
|
||||||
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
||||||
public int pop = 24;
|
public int clueSize = 24;
|
||||||
|
public int pop = 40;
|
||||||
|
public int offspring = 60;
|
||||||
public int gens = 700;
|
public int gens = 700;
|
||||||
public String wordsPath = "nl_score_hints_v3.csv";
|
public String wordsPath = "nl_score_hints_v3.csv";
|
||||||
public double minSimplicity = 0; // 0 means no limit
|
public double minSimplicity = 0; // 0 means no limit
|
||||||
@@ -153,7 +155,9 @@ public class Main {
|
|||||||
|
|
||||||
private static void printSettings(Opts o) {
|
private static void printSettings(Opts o) {
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "seed", o.seed);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "seed", o.seed);
|
||||||
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "clues", o.clueSize);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "population", o.pop);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "population", o.pop);
|
||||||
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "offspring", o.offspring);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %d%n", "generations", o.gens);
|
System.out.printf(Locale.ROOT, " %-14s: %d%n", "generations", o.gens);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %s%n", "wordsPath", o.wordsPath);
|
System.out.printf(Locale.ROOT, " %-14s: %s%n", "wordsPath", o.wordsPath);
|
||||||
System.out.printf(Locale.ROOT, " %-14s: %.2f%n", "minSimplicity", o.minSimplicity);
|
System.out.printf(Locale.ROOT, " %-14s: %.2f%n", "minSimplicity", o.minSimplicity);
|
||||||
@@ -196,11 +200,13 @@ public class Main {
|
|||||||
static void usage() {
|
static void usage() {
|
||||||
System.out.println("""
|
System.out.println("""
|
||||||
Usage:
|
Usage:
|
||||||
java puzzle.Main [--seed N] [--pop N] [--gens N] [--tries N] [--words FILE] [--min-simplicity N.N] [--threads N] [--reindex]
|
java puzzle.Main [--seed N] [--clues N] [--pop N] [--offspring N] [--gens N] [--tries N] [--words FILE] [--min-simplicity N.N] [--threads N] [--reindex]
|
||||||
|
|
||||||
Defaults:
|
Defaults:
|
||||||
--pop 18
|
--clues 18
|
||||||
--gens 500
|
--pop 40
|
||||||
|
--offspring 60
|
||||||
|
--gens 700
|
||||||
--words nl_score_hints.csv
|
--words nl_score_hints.csv
|
||||||
--min-simplicity 0 (no limit)
|
--min-simplicity 0 (no limit)
|
||||||
--threads %d
|
--threads %d
|
||||||
@@ -221,9 +227,15 @@ public class Main {
|
|||||||
if (a.equals("--seed")) {
|
if (a.equals("--seed")) {
|
||||||
out.seed = Integer.parseInt(v);
|
out.seed = Integer.parseInt(v);
|
||||||
i++;
|
i++;
|
||||||
|
} else if (a.equals("--clues")) {
|
||||||
|
out.clueSize = Integer.parseInt(v);
|
||||||
|
i++;
|
||||||
} else if (a.equals("--pop")) {
|
} else if (a.equals("--pop")) {
|
||||||
out.pop = Integer.parseInt(v);
|
out.pop = Integer.parseInt(v);
|
||||||
i++;
|
i++;
|
||||||
|
} else if (a.equals("--offspring")) {
|
||||||
|
out.offspring = Integer.parseInt(v);
|
||||||
|
i++;
|
||||||
} else if (a.equals("--gens")) {
|
} else if (a.equals("--gens")) {
|
||||||
out.gens = Integer.parseInt(v);
|
out.gens = Integer.parseInt(v);
|
||||||
i++;
|
i++;
|
||||||
@@ -359,7 +371,7 @@ public class Main {
|
|||||||
static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) {
|
static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) {
|
||||||
TOTAL_ATTEMPTS.incrementAndGet();
|
TOTAL_ATTEMPTS.incrementAndGet();
|
||||||
var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
|
var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
|
||||||
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
|
||||||
|
|
||||||
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
|
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
static final double SIZED = (double) SIZE;// ~18
|
static final double SIZED = (double) SIZE;// ~18
|
||||||
static final long MASK_LO = (SIZE >= 64) ? -1L : (1L << SIZE) - 1;
|
static final long MASK_LO = (SIZE >= 64) ? -1L : (1L << SIZE) - 1;
|
||||||
static final long MASK_HI = (SIZE <= 64) ? 0L : (SIZE >= 128 ? -1L : (1L << (SIZE - 64)) - 1);
|
static final long MASK_HI = (SIZE <= 64) ? 0L : (SIZE >= 128 ? -1L : (1L << (SIZE - 64)) - 1);
|
||||||
static final int TARGET_CLUES = 24;
|
|
||||||
static final int MAX_WORD_LENGTH = C <= R ? C : R;
|
static final int MAX_WORD_LENGTH = C <= R ? C : R;
|
||||||
static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
|
static final int MAX_WORD_LENGTH_PLUS_ONE = MAX_WORD_LENGTH + 1;
|
||||||
static final int MIN_LEN = Config.MIN_LEN;
|
static final int MIN_LEN = Config.MIN_LEN;
|
||||||
@@ -433,11 +432,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// does not modify the grid
|
/// does not modify the grid
|
||||||
long maskFitness(final Clues grid) {
|
long maskFitness(final Clues grid, int clueSize) {
|
||||||
|
|
||||||
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
|
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
|
||||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||||
long penalty = (((long) Math.abs(grid.clueCount() - TARGET_CLUES)) * 16000L);
|
long penalty = (((long) Math.abs(grid.clueCount() - clueSize)) * 16000L);
|
||||||
boolean hasSlots = false;
|
boolean hasSlots = false;
|
||||||
|
|
||||||
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
for (long bits = lo_cl; bits != X; bits &= bits - 1) {
|
||||||
@@ -600,9 +599,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
return penalty;
|
return penalty;
|
||||||
}
|
}
|
||||||
|
|
||||||
Clues randomMask() {
|
Clues randomMask(final int clueSize) {
|
||||||
var g = Clues.createEmpty();
|
var g = Clues.createEmpty();
|
||||||
for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) {
|
for (int placed = 0, guard = 0, idx; placed < clueSize && guard < 4000; guard++) {
|
||||||
idx = rng.randint(0, SIZE_MIN_1);
|
idx = rng.randint(0, SIZE_MIN_1);
|
||||||
if (g.isClue(idx)) continue;
|
if (g.isClue(idx)) continue;
|
||||||
var d_idx = rng.randint2bitByte();
|
var d_idx = rng.randint2bitByte();
|
||||||
@@ -653,15 +652,15 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
}
|
}
|
||||||
public static void clearClues(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, out.digitAt(idx))])) out.clearClue(idx); }
|
public static void clearClues(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotKey(idx, out.digitAt(idx))])) out.clearClue(idx); }
|
||||||
|
|
||||||
Clues hillclimb(Clues start, int limit) {
|
Clues hillclimb(Clues start, int clue_size, int limit) {
|
||||||
var best = start;
|
var best = start;
|
||||||
var bestF = maskFitness(best);
|
var bestF = maskFitness(best, clue_size);
|
||||||
var fails = 0;
|
var fails = 0;
|
||||||
|
|
||||||
while (fails < limit) {
|
while (fails < limit) {
|
||||||
cache.from(best);
|
cache.from(best);
|
||||||
var cand = mutate(best);
|
var cand = mutate(best);
|
||||||
var f = maskFitness(cand);
|
var f = maskFitness(cand, clue_size);
|
||||||
if (f < bestF) {
|
if (f < bestF) {
|
||||||
best = cand;
|
best = cand;
|
||||||
bestF = f;
|
bestF = f;
|
||||||
@@ -674,32 +673,32 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Clues generateMask(int popSize, int gens, int pairs) {
|
public Clues generateMask(int clueSize, int popSize, int gens, int offspring) {
|
||||||
class GridAndFit {
|
class GridAndFit {
|
||||||
|
|
||||||
Clues grid;
|
Clues grid;
|
||||||
long fite = -1;
|
long fite = -1;
|
||||||
GridAndFit(Clues grid) { this.grid = grid; }
|
GridAndFit(Clues grid) { this.grid = grid; }
|
||||||
long fit() {
|
long fit() {
|
||||||
if (fite == -1) this.fite = maskFitness(grid);
|
if (fite == -1) this.fite = maskFitness(grid, clueSize);
|
||||||
return this.fite;
|
return this.fite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize);
|
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize);
|
||||||
var pop = new ArrayList<GridAndFit>();
|
var pop = new ArrayList<GridAndFit>();
|
||||||
for (var i = 0; i < popSize; i++) {
|
for (var i = 0; i < popSize; i++) {
|
||||||
pop.add(new GridAndFit(hillclimb(randomMask(), 180)));
|
pop.add(new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var gen = 0; gen < gens; gen++) {
|
for (var gen = 0; gen < gens; gen++) {
|
||||||
if (Thread.currentThread().isInterrupted()) break;
|
if (Thread.currentThread().isInterrupted()) break;
|
||||||
var children = new ArrayList<GridAndFit>();
|
var children = new ArrayList<GridAndFit>();
|
||||||
|
|
||||||
for (var k = 0; k < pairs; k++) {
|
for (var k = 0; k < offspring; k++) {
|
||||||
var p1 = pop.get(rng.randint(0, pop.size() - 1));
|
var p1 = pop.get(rng.randint(0, pop.size() - 1));
|
||||||
var p2 = 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);
|
var child = crossover(p1.grid, p2.grid);
|
||||||
children.add(new GridAndFit(hillclimb(child, 70)));
|
children.add(new GridAndFit(hillclimb(child, clueSize, 70)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pop.addAll(children);
|
pop.addAll(children);
|
||||||
@@ -707,7 +706,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
|
|
||||||
var next = new ArrayList<GridAndFit>();
|
var next = new ArrayList<GridAndFit>();
|
||||||
for (var cand : pop) {
|
for (var cand : pop) {
|
||||||
if (next.size() >= popSize) break;
|
if (next.size() >= offspring) break;
|
||||||
var ok = true;
|
var ok = true;
|
||||||
for (var kept : next) {
|
for (var kept : next) {
|
||||||
if (cand.grid.similarity(kept.grid) > 0.92) {
|
if (cand.grid.similarity(kept.grid) > 0.92) {
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ public class MainTest {
|
|||||||
static final int OFF_2_3 = Grid.offset(2, 3);
|
static final int OFF_2_3 = Grid.offset(2, 3);
|
||||||
static final Opts opts = new Main.Opts() {{
|
static final Opts opts = new Main.Opts() {{
|
||||||
this.seed = 12348;
|
this.seed = 12348;
|
||||||
|
this.clueSize = 4;
|
||||||
this.pop = 4; // Tiny population
|
this.pop = 4; // Tiny population
|
||||||
|
this.offspring = 18;
|
||||||
this.gens = 20; // Very few generations
|
this.gens = 20; // Very few generations
|
||||||
this.minSimplicity = 0;
|
this.minSimplicity = 0;
|
||||||
this.threads = 1;
|
this.threads = 1;
|
||||||
@@ -159,19 +161,19 @@ public class MainTest {
|
|||||||
@Test
|
@Test
|
||||||
void testMaskerCreation() {
|
void testMaskerCreation() {
|
||||||
var swe = new SwedishGenerator(new Rng(12348), new int[STACK_SIZE], Clues.createEmpty());
|
var swe = new SwedishGenerator(new Rng(12348), new int[STACK_SIZE], Clues.createEmpty());
|
||||||
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
|
||||||
val clued = new Clued(mask);
|
val clued = new Clued(mask);
|
||||||
val test = clued.gridToString();
|
val test = clued.gridToString();
|
||||||
val RESULT = "001 \n" +
|
val RESULT = "1 \n" +
|
||||||
" 3 3\n" +
|
" \n" +
|
||||||
" 3\n" +
|
" 3\n" +
|
||||||
" 3\n" +
|
" \n" +
|
||||||
" 21 1 \n" +
|
" \n" +
|
||||||
" 3 \n" +
|
"1 \n" +
|
||||||
"221 1 \n" +
|
" \n" +
|
||||||
"1 22";
|
" 3";
|
||||||
|
|
||||||
Assertions.assertEquals(18, clued.clueCount(), "Found seed changed");
|
Assertions.assertEquals(4, clued.clueCount(), "Found seed changed");
|
||||||
Assertions.assertEquals(RESULT, test, "Found seed changed");
|
Assertions.assertEquals(RESULT, test, "Found seed changed");
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -241,12 +241,12 @@ public class SwedishGeneratorTest {
|
|||||||
var gen = new SwedishGenerator(new Rng(0), new int[STACK_SIZE], Clues.createEmpty());
|
var gen = new SwedishGenerator(new Rng(0), new int[STACK_SIZE], Clues.createEmpty());
|
||||||
var grid = Clues.createEmpty();
|
var grid = Clues.createEmpty();
|
||||||
// Empty grid should have high penalty (no slots)
|
// Empty grid should have high penalty (no slots)
|
||||||
var f1 = gen.maskFitness(grid);
|
var f1 = gen.maskFitness(grid, 18);
|
||||||
assertTrue(f1 >= 1_000_000_000L);
|
assertTrue(f1 >= 1_000_000_000L);
|
||||||
|
|
||||||
// Add a slot
|
// Add a slot
|
||||||
grid.setClue(OFF_0_0, D_BYTE_2);
|
grid.setClue(OFF_0_0, D_BYTE_2);
|
||||||
var f2 = gen.maskFitness(grid);
|
var f2 = gen.maskFitness(grid, 18);
|
||||||
assertTrue(f2 < f1);
|
assertTrue(f2 < f1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ public class SwedishGeneratorTest {
|
|||||||
var rng = new Rng(42);
|
var rng = new Rng(42);
|
||||||
var gen = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
|
var gen = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
|
||||||
|
|
||||||
var g1 = gen.randomMask();
|
var g1 = gen.randomMask(18);
|
||||||
assertNotNull(g1);
|
assertNotNull(g1);
|
||||||
|
|
||||||
var g2 = gen.mutate(g1.deepCopyGrid());
|
var g2 = gen.mutate(g1.deepCopyGrid());
|
||||||
@@ -264,7 +264,7 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
assertNotNull(gen.crossover(g1, g2));
|
assertNotNull(gen.crossover(g1, g2));
|
||||||
|
|
||||||
var g4 = gen.hillclimb(g1, 10);
|
var g4 = gen.hillclimb(g1, 18, 10);
|
||||||
assertNotNull(g4);
|
assertNotNull(g4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,10 +363,10 @@ public class SwedishGeneratorTest {
|
|||||||
var gen = new SwedishGenerator(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
|
var gen = new SwedishGenerator(new Rng(42), new int[STACK_SIZE], Clues.createEmpty());
|
||||||
var grid = Clues.createEmpty();
|
var grid = Clues.createEmpty();
|
||||||
// Empty grid: huge penalty
|
// Empty grid: huge penalty
|
||||||
var fitEmpty = gen.maskFitness(grid);
|
var fitEmpty = gen.maskFitness(grid, 18);
|
||||||
assertTrue(fitEmpty >= 1_000_000_000L);
|
assertTrue(fitEmpty >= 1_000_000_000L);
|
||||||
grid.setClue(0, D_BYTE_2); // Right from 0,0. Len 2 if 3x3.
|
grid.setClue(0, D_BYTE_2); // Right from 0,0. Len 2 if 3x3.
|
||||||
var fitOne = gen.maskFitness(grid);
|
var fitOne = gen.maskFitness(grid, 18);
|
||||||
assertTrue(fitOne < fitEmpty);
|
assertTrue(fitOne < fitEmpty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user