introduce bitloops
This commit is contained in:
@@ -356,9 +356,9 @@ 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);
|
|
||||||
val stack = new int[STACK_SIZE];
|
val stack = new int[STACK_SIZE];
|
||||||
var mask = swe.generateMask(stack, opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
var swe = new SwedishGenerator(rng, stack);
|
||||||
|
var mask = swe.generateMask(opts.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
||||||
var filled = fillMask(rng, mask, dict.index());
|
var filled = fillMask(rng, mask, dict.index());
|
||||||
|
|
||||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import static java.nio.charset.StandardCharsets.*;
|
|||||||
* D) separate clue into three long pairs of hi and lo, (v,h,r) so we do not store the clue anymore in the grid array at all
|
* D) separate clue into three long pairs of hi and lo, (v,h,r) so we do not store the clue anymore in the grid array at all
|
||||||
* E) store letter-set also in a long pair so we can use it in path determinations
|
* E) store letter-set also in a long pair so we can use it in path determinations
|
||||||
* 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
|
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +40,7 @@ import static java.nio.charset.StandardCharsets.*;
|
|||||||
* java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt]
|
* java SwedishGenerator [--seed N] [--pop N] [--gens N] [--tries N] [--words word-list.txt]
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
public record SwedishGenerator(Rng rng) {
|
public record SwedishGenerator(Rng rng, int[] stack) {
|
||||||
|
|
||||||
record CandidateInfo(int[] indices, int count) { }
|
record CandidateInfo(int[] indices, int count) { }
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
//72 << 3;
|
//72 << 3;
|
||||||
static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1;
|
static final int CLUE_INDEX_MAX_SIZE = (288 | 3) + 1;
|
||||||
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
static int clamp(int x, int a, int b) { return Math.max(a, Math.min(b, x)); }
|
||||||
record Pick(Slot slot, CandidateInfo info, boolean done) { }
|
record Pick(Slot slot, int[] indices, int count) { }
|
||||||
// 0b11
|
// 0b11
|
||||||
//0b00
|
//0b00
|
||||||
// 0b01
|
// 0b01
|
||||||
@@ -96,8 +97,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Pick PICK_DONE = new Pick(null, null, true);
|
static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
|
||||||
static final Pick PICK_NOT_DONE = new Pick(null, null, false);
|
static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@@ -251,7 +252,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static record DictEntry(long[] words, long[][] posBitsets, CandidateInfo empty, int length, int numlong) { }
|
static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
||||||
|
|
||||||
static int LEMMA_COUNTER = 0;
|
static int LEMMA_COUNTER = 0;
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DictEntry(words, bitsets, new CandidateInfo(null, words.length), words.length, (words.length + 63) >>> 6);
|
return new DictEntry(words, bitsets, words.length, (words.length + 63) >>> 6);
|
||||||
}).toArray(DictEntry[]::new),
|
}).toArray(DictEntry[]::new),
|
||||||
Arrays.stream(index).mapToInt(i -> i.words().size()).sum());
|
Arrays.stream(index).mapToInt(i -> i.words().size()).sum());
|
||||||
}
|
}
|
||||||
@@ -400,7 +401,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
|
|
||||||
long maskFitness(Grid grid, int[] stack) {
|
/// does not modify the grid
|
||||||
|
long maskFitness(final Grid grid) {
|
||||||
|
|
||||||
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;
|
||||||
@@ -555,7 +557,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
Grid crossover(Grid a, Grid b) {
|
Grid crossover(Grid a, Grid other) {
|
||||||
var out = a.deepCopyGrid();
|
var out = a.deepCopyGrid();
|
||||||
var theta = rng.nextFloat() * Math.PI;
|
var theta = rng.nextFloat() * Math.PI;
|
||||||
var nc = Math.cos(theta);
|
var nc = Math.cos(theta);
|
||||||
@@ -565,10 +567,10 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
for (var rci : IT) {
|
for (var rci : IT) {
|
||||||
int i = rci.i();
|
int i = rci.i();
|
||||||
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
|
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
|
||||||
byte ch = b.g[i];
|
byte ch = other.g[i];
|
||||||
if (out.g[i] != ch) {
|
if (out.g[i] != ch) {
|
||||||
out.g[i] = ch;
|
out.g[i] = ch;
|
||||||
if (b.isClue(i)) {
|
if (other.isClue(i)) {
|
||||||
if ((i & 64) == 0) bo0 |= (1L << i);
|
if ((i & 64) == 0) bo0 |= (1L << i);
|
||||||
else bo1 |= (1L << (i & 63));
|
else bo1 |= (1L << (i & 63));
|
||||||
} else {
|
} else {
|
||||||
@@ -586,14 +588,14 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
public static void clearClues(Grid out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(idx, out.digitAt(idx))])) out.clearClue(idx); }
|
public static void clearClues(Grid out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(idx, out.digitAt(idx))])) out.clearClue(idx); }
|
||||||
|
|
||||||
Grid hillclimb(int[] stack, Grid start, int limit) {
|
Grid hillclimb(Grid start, int limit) {
|
||||||
var best = start;
|
var best = start;
|
||||||
var bestF = maskFitness(best, stack);
|
var bestF = maskFitness(best);
|
||||||
var fails = 0;
|
var fails = 0;
|
||||||
|
|
||||||
while (fails < limit) {
|
while (fails < limit) {
|
||||||
var cand = mutate(best);
|
var cand = mutate(best);
|
||||||
var f = maskFitness(cand, stack);
|
var f = maskFitness(cand);
|
||||||
if (f < bestF) {
|
if (f < bestF) {
|
||||||
best = cand;
|
best = cand;
|
||||||
bestF = f;
|
bestF = f;
|
||||||
@@ -605,21 +607,21 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Grid generateMask(int[] stack, int popSize, int gens, int pairs) {
|
public Grid generateMask(int popSize, int gens, int pairs) {
|
||||||
class GridAndFit {
|
class GridAndFit {
|
||||||
|
|
||||||
Grid grid;
|
Grid grid;
|
||||||
long fite = -1;
|
long fite = -1;
|
||||||
GridAndFit(Grid grid) { this.grid = grid; }
|
GridAndFit(Grid grid) { this.grid = grid; }
|
||||||
long fit() {
|
long fit() {
|
||||||
if (fite == -1) this.fite = maskFitness(grid, stack);
|
if (fite == -1) this.fite = maskFitness(grid);
|
||||||
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);
|
||||||
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(stack, randomMask(), 180)));
|
pop.add(new GridAndFit(hillclimb(randomMask(), 180)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var gen = 0; gen < gens; gen++) {
|
for (var gen = 0; gen < gens; gen++) {
|
||||||
@@ -630,7 +632,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
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(stack, child, 70)));
|
children.add(new GridAndFit(hillclimb(child, 70)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pop.addAll(children);
|
pop.addAll(children);
|
||||||
@@ -767,7 +769,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CandidateInfo candidateInfoForPattern(long[] res, long pattern, DictEntry entry, int lenb) {
|
static int[] candidateInfoForPattern(long[] res, long pattern, DictEntry entry, int lenb) {
|
||||||
int numLongs = entry.numlong;
|
int numLongs = entry.numlong;
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
|
|
||||||
@@ -801,7 +803,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CandidateInfo(indices, count);
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int candidateCountForPattern(final long[] res, final long pattern, final DictEntry entry, final int lenb) {
|
static int candidateCountForPattern(final long[] res, final long pattern, final DictEntry entry, final int lenb) {
|
||||||
@@ -906,8 +908,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
if (best == null) return PICK_DONE;
|
if (best == null) return PICK_DONE;
|
||||||
var pattern = patternForSlot(grid, best);
|
var pattern = patternForSlot(grid, best);
|
||||||
var index = dictIndex[best.length()];
|
var index = dictIndex[best.length()];
|
||||||
if (pattern == X) return new Pick(best, index.empty, false);
|
if (pattern == X) return new Pick(best, null, index.length);
|
||||||
return new Pick(best, candidateInfoForPattern(bitset, pattern, index, best.length()), false);
|
return new Pick(best, candidateInfoForPattern(bitset, pattern, index, best.length()), index.length);
|
||||||
}
|
}
|
||||||
boolean backtrack(int depth) {
|
boolean backtrack(int depth) {
|
||||||
if (Thread.currentThread().isInterrupted()) return false;
|
if (Thread.currentThread().isInterrupted()) return false;
|
||||||
@@ -916,21 +918,21 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
if (20_000 > 0 && (System.currentTimeMillis() - t0) > 20_000) return false;
|
if (20_000 > 0 && (System.currentTimeMillis() - t0) > 20_000) return false;
|
||||||
|
|
||||||
var pick = chooseMRV();
|
var pick = chooseMRV();
|
||||||
if (pick.done) return true;
|
if (pick == PICK_DONE) return true;
|
||||||
if (pick.slot == null) {
|
if (pick.slot == null) {
|
||||||
backtracks++;
|
backtracks++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
val info = pick.info;
|
val info = pick.indices;
|
||||||
lastMRV = info.count;
|
lastMRV = pick.count;
|
||||||
if (!NO_LOG) renderProgress();
|
if (!NO_LOG) renderProgress();
|
||||||
|
|
||||||
val s = pick.slot;
|
val s = pick.slot;
|
||||||
val k = s.key;
|
val k = s.key;
|
||||||
val entry = dictIndex[s.length()];
|
val entry = dictIndex[s.length()];
|
||||||
|
|
||||||
if (info.indices != null && info.indices.length > 0) {
|
if (info != null && info.length > 0) {
|
||||||
var idxs = info.indices;
|
var idxs = info;
|
||||||
var L = idxs.length;
|
var L = idxs.length;
|
||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class ExportFormatTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExportFormatFromFilled() {
|
void testExportFormatFromFilled() {
|
||||||
var swe = new SwedishGenerator(new Rng(0));
|
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]);
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
|
|
||||||
// Place a RIGHT clue at (0,0)
|
// Place a RIGHT clue at (0,0)
|
||||||
@@ -84,7 +84,7 @@ public class ExportFormatTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testExportFormatEmpty() {
|
void testExportFormatEmpty() {
|
||||||
var swe = new SwedishGenerator(new Rng(0));
|
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]);
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
var fillResult = new FillResult(true, new Gridded(grid), new long[300], new FillStats(0, 0, 0, 0));
|
var fillResult = new FillResult(true, new Gridded(grid), new long[300], new FillStats(0, 0, 0, 0));
|
||||||
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
var puzzleResult = new PuzzleResult(swe, null, null, fillResult);
|
||||||
|
|||||||
@@ -261,13 +261,12 @@ public class SwedishGeneratorTest {
|
|||||||
// Pattern "APP--" for length 5
|
// Pattern "APP--" for length 5
|
||||||
var info = candidateInfoForPattern(Context.get().bitset(), packPattern("APP"), dict.index()[5], 5);
|
var info = candidateInfoForPattern(Context.get().bitset(), packPattern("APP"), dict.index()[5], 5);
|
||||||
|
|
||||||
assertEquals(2, info.count());
|
assertEquals(2, info.length);
|
||||||
assertNotNull(info.indices());
|
assertNotNull(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testForEachSlotAndExtractSlots() {
|
void testForEachSlotAndExtractSlots() {
|
||||||
var gen = new SwedishGenerator(new Rng(0));
|
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
|
// 3x3 grid (Config.PUZZLE_ROWS/COLS are 3 in test env)
|
||||||
// Set CLUE_RIGHT at OFF_0_0
|
// Set CLUE_RIGHT at OFF_0_0
|
||||||
@@ -285,23 +284,22 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMaskFitnessBasic() {
|
void testMaskFitnessBasic() {
|
||||||
var gen = new SwedishGenerator(new Rng(0));
|
var gen = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]);
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
var stack = new int[STACK_SIZE];
|
|
||||||
// Empty grid should have high penalty (no slots)
|
// Empty grid should have high penalty (no slots)
|
||||||
var f1 = gen.maskFitness(grid, stack);
|
var f1 = gen.maskFitness(grid);
|
||||||
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, stack);
|
var f2 = gen.maskFitness(grid);
|
||||||
assertTrue(f2 < f1);
|
assertTrue(f2 < f1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGeneticAlgorithmComponents() {
|
void testGeneticAlgorithmComponents() {
|
||||||
var rng = new Rng(42);
|
var rng = new Rng(42);
|
||||||
var gen = new SwedishGenerator(rng);
|
var gen = new SwedishGenerator(rng, new int[STACK_SIZE]);
|
||||||
|
|
||||||
var g1 = gen.randomMask();
|
var g1 = gen.randomMask();
|
||||||
assertNotNull(g1);
|
assertNotNull(g1);
|
||||||
@@ -312,8 +310,7 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
assertNotNull(gen.crossover(g1, g2));
|
assertNotNull(gen.crossover(g1, g2));
|
||||||
|
|
||||||
val stack = new int[STACK_SIZE];
|
var g4 = gen.hillclimb(g1, 10);
|
||||||
var g4 = gen.hillclimb(stack, g1, 10);
|
|
||||||
assertNotNull(g4);
|
assertNotNull(g4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,16 +425,15 @@ public class SwedishGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMaskFitnessDetailed() {
|
void testMaskFitnessDetailed() {
|
||||||
var gen = new SwedishGenerator(new Rng(42));
|
var gen = new SwedishGenerator(new Rng(42), new int[STACK_SIZE]);
|
||||||
var grid = Grid.createEmpty();
|
var grid = Grid.createEmpty();
|
||||||
val stack = new int[STACK_SIZE];
|
|
||||||
// Empty grid: huge penalty
|
// Empty grid: huge penalty
|
||||||
var fitEmpty = gen.maskFitness(grid, stack);
|
var fitEmpty = gen.maskFitness(grid);
|
||||||
assertTrue(fitEmpty >= 1_000_000_000L);
|
assertTrue(fitEmpty >= 1_000_000_000L);
|
||||||
|
|
||||||
// Grid with one short slot: still high penalty but less than empty
|
// Grid with one short slot: still high penalty but less than empty
|
||||||
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, stack);
|
var fitOne = gen.maskFitness(grid);
|
||||||
assertTrue(fitOne < fitEmpty);
|
assertTrue(fitOne < fitEmpty);
|
||||||
|
|
||||||
// Test penalty for TARGET_CLUES
|
// Test penalty for TARGET_CLUES
|
||||||
|
|||||||
Reference in New Issue
Block a user