introduce bitloops
This commit is contained in:
@@ -6,6 +6,7 @@ import lombok.experimental.Delegate;
|
|||||||
import lombok.val;
|
import lombok.val;
|
||||||
import puzzle.Export.Gridded.Replacar.Cell;
|
import puzzle.Export.Gridded.Replacar.Cell;
|
||||||
import puzzle.SwedishGenerator.Clues;
|
import puzzle.SwedishGenerator.Clues;
|
||||||
|
import puzzle.SwedishGenerator.DictEntry;
|
||||||
import puzzle.SwedishGenerator.FillResult;
|
import puzzle.SwedishGenerator.FillResult;
|
||||||
import puzzle.SwedishGenerator.Grid;
|
import puzzle.SwedishGenerator.Grid;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -197,10 +198,11 @@ public record Export() {
|
|||||||
var g = filled().grid();
|
var g = filled().grid();
|
||||||
var placed = new ArrayList<Placed>();
|
var placed = new ArrayList<Placed>();
|
||||||
var clueMap = filled().clueMap();
|
var clueMap = filled().clueMap();
|
||||||
|
val entries = new DictEntry[10];
|
||||||
mask.forEachSlot((int key, long lo, long hi) -> {
|
mask.forEachSlot((int key, long lo, long hi) -> {
|
||||||
var word = clueMap[key];
|
var word = clueMap[key];
|
||||||
if (word != 0L) {
|
if (word != 0L) {
|
||||||
placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi), word));
|
placed.add(extractPlacedFromSlot(Slot.from(key, lo, hi, entries[Slot.length(lo, hi)]), word));
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Could not find clue for slot: " + key);
|
System.err.println("Could not find clue for slot: " + key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ public class Main {
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
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 = 18;
|
public int pop = 18;
|
||||||
public int gens = 1200;
|
public int gens = 1200;
|
||||||
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
|
||||||
@@ -358,10 +358,10 @@ 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.pop, opts.gens, Math.max(opts.pop, (int) Math.floor(opts.pop * 1.5)));
|
||||||
|
|
||||||
var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index());
|
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
|
||||||
|
|
||||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||||
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
TOTAL_BACKTRACKS.addAndGet(filled.stats().backtracks);
|
||||||
|
|||||||
@@ -383,18 +383,17 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static record Slot(int key, long lo, long hi) {
|
static record Slot(int key, long lo, long hi, DictEntry entry) {
|
||||||
|
|
||||||
static final int BIT_FOR_DIR = 2;
|
static final int BIT_FOR_DIR = 2;
|
||||||
static Slot from(int key, long lo, long hi) { return new Slot(key, lo, hi); }
|
static Slot from(int key, long lo, long hi, DictEntry entry) { return new Slot(key, lo, hi, entry); }
|
||||||
|
public static int length(long lo, long hi) { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||||
public int length() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
||||||
public static int clueIndex(int key) { return key >>> BIT_FOR_DIR; }
|
public static int dir(int key) { return key & 3; }
|
||||||
public static int dir(int key) { return key & 3; }
|
public static boolean increasing(int dir) { return (dir & 2) == 0; }
|
||||||
public static boolean increasing(int dir) { return (dir & 2) == 0; }
|
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
||||||
public IntStream walk() { return Gridded.walk((byte) key, lo, hi); }
|
public static boolean horiz(int d) { return (d & 1) != 0; }
|
||||||
public static boolean horiz(int d) { return (d & 1) != 0; }
|
public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
||||||
public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void processSlot(Clues grid, SlotVisitor visitor, int idx) {
|
private static void processSlot(Clues grid, SlotVisitor visitor, int idx) {
|
||||||
@@ -438,10 +437,10 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
visitor.visit(key, rayLo, rayHi);
|
visitor.visit(key, rayLo, rayHi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Slot[] extractSlots(Clues grid) {
|
static Slot[] extractSlots(Clues grid, DictEntry[] index) {
|
||||||
var slots = new Slot[grid.clueCount()];
|
var slots = new Slot[grid.clueCount()];
|
||||||
int[] N = new int[]{ 0 };
|
int[] N = new int[]{ 0 };
|
||||||
grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi));
|
grid.forEachSlot((key, lo, hi) -> slots[N[0]++] = Slot.from(key, lo, hi, index[Slot.length(lo, hi)]));
|
||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,11 +763,11 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
static int slotScore(byte[] count, Slot s) {
|
static int slotScore(byte[] count, long lo, long hi) {
|
||||||
int cross = 0;
|
int cross = 0;
|
||||||
for (long b = s.lo; b != 0; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1);
|
for (long b = lo; b != 0; b &= b - 1) cross += (count[Long.numberOfTrailingZeros(b)] - 1);
|
||||||
for (long b = s.hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1);
|
for (long b = hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1);
|
||||||
return cross * 10 + s.length();
|
return cross * 10 + Slot.length(lo, hi);
|
||||||
}
|
}
|
||||||
static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) {
|
static boolean placeWord(Grid grid, final int key, final long lo, final long hi, final long w) {
|
||||||
final long glo = grid.lo, ghi = grid.hi;
|
final long glo = grid.lo, ghi = grid.hi;
|
||||||
@@ -890,10 +889,13 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
for (long b = s.lo; b != 0; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++;
|
for (long b = s.lo; b != 0; b &= b - 1) count[Long.numberOfTrailingZeros(b)]++;
|
||||||
for (long b = s.hi; b != 0; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++;
|
for (long b = s.hi; b != 0; b &= b - 1) count[64 | Long.numberOfTrailingZeros(b)]++;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]);
|
for (int i = 0; i < slots.length; i++) {
|
||||||
|
var slot = slots[i];
|
||||||
|
slotScores[i] = slotScore(count, slot.lo, slot.hi);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask, DictEntry[] dictIndex) {
|
public static FillResult fillMask(Rng rng, Slot[] slots, Grid mask) {
|
||||||
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
||||||
val NO_LOG = (!Main.VERBOSE || multiThreaded);
|
val NO_LOG = (!Main.VERBOSE || multiThreaded);
|
||||||
val grid = mask;
|
val grid = mask;
|
||||||
@@ -943,7 +945,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
var s = slots[i];
|
var s = slots[i];
|
||||||
if (assigned[s.key] != X) continue;
|
if (assigned[s.key] != X) continue;
|
||||||
var pattern = patternForSlot(grid, s.key, s.lo, s.hi);
|
var pattern = patternForSlot(grid, s.key, s.lo, s.hi);
|
||||||
var index = dictIndex[s.length()];
|
var index = s.entry;
|
||||||
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
|
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
|
||||||
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
@@ -965,7 +967,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pattern = patternForSlot(grid, best.key, best.lo, best.hi);
|
var pattern = patternForSlot(grid, best.key, best.lo, best.hi);
|
||||||
var index = dictIndex[best.length()];
|
var index = best.entry;
|
||||||
current = CARRIER;
|
current = CARRIER;
|
||||||
current.slot = best;
|
current.slot = best;
|
||||||
current.count = index.length;
|
current.count = index.length;
|
||||||
@@ -992,10 +994,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
lastMRV = pick.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 slo = s.lo;
|
||||||
|
val shi = s.hi;
|
||||||
|
val entry = s.entry;
|
||||||
|
long low, top;
|
||||||
if (info != null && info.length > 0) {
|
if (info != null && info.length > 0) {
|
||||||
var idxs = info;
|
var idxs = info;
|
||||||
var L = idxs.length;
|
var L = idxs.length;
|
||||||
@@ -1009,9 +1013,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
var w = entry.words[idx];
|
var w = entry.words[idx];
|
||||||
var lemIdx = Lemma.unpackIndex(w);
|
var lemIdx = Lemma.unpackIndex(w);
|
||||||
if (used.get(lemIdx)) continue;
|
if (used.get(lemIdx)) continue;
|
||||||
val low = grid.lo;
|
low = grid.lo;
|
||||||
val top = grid.hi;
|
top = grid.hi;
|
||||||
if (!placeWord(grid, k, s.lo, s.hi, w)) continue;
|
if (!placeWord(grid, k, slo, shi, w)) continue;
|
||||||
|
|
||||||
used.set(lemIdx);
|
used.set(lemIdx);
|
||||||
assigned[k] = w;
|
assigned[k] = w;
|
||||||
@@ -1036,9 +1040,9 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
|||||||
var w = entry.words[idxInArray];
|
var w = entry.words[idxInArray];
|
||||||
var lemIdx = Lemma.unpackIndex(w);
|
var lemIdx = Lemma.unpackIndex(w);
|
||||||
if (used.get(lemIdx)) continue;
|
if (used.get(lemIdx)) continue;
|
||||||
val low = grid.lo;
|
low = grid.lo;
|
||||||
val top = grid.hi;
|
top = grid.hi;
|
||||||
if (!placeWord(grid, k, s.lo, s.hi, w)) continue;
|
if (!placeWord(grid, k, slo, shi, w)) continue;
|
||||||
|
|
||||||
used.set(lemIdx);
|
used.set(lemIdx);
|
||||||
assigned[k] = w;
|
assigned[k] = w;
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ public class MainTest {
|
|||||||
grid.setLetterLo(OFF_0_1, LETTER_A);
|
grid.setLetterLo(OFF_0_1, LETTER_A);
|
||||||
grid.setLetterLo(OFF_0_2, LETTER_B);
|
grid.setLetterLo(OFF_0_2, LETTER_B);
|
||||||
|
|
||||||
var slots = extractSlots(clues);
|
var slots = extractSlots(clues, dict.index());
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
var s = slots[0];
|
var s = slots[0];
|
||||||
assertEquals(8, s.length());
|
assertEquals(8, Slot.length(s.lo(), s.hi()));
|
||||||
var cells = s.walk().toArray();
|
var cells = s.walk().toArray();
|
||||||
assertEquals(0, SwedishGenerator.IT[cells[0]].r());
|
assertEquals(0, SwedishGenerator.IT[cells[0]].r());
|
||||||
assertEquals(1, SwedishGenerator.IT[cells[0]].c());
|
assertEquals(1, SwedishGenerator.IT[cells[0]].c());
|
||||||
@@ -184,7 +184,7 @@ public class MainTest {
|
|||||||
128L,
|
128L,
|
||||||
422762372923520L,
|
422762372923520L,
|
||||||
192L);
|
192L);
|
||||||
var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index());
|
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
|
||||||
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
||||||
Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed");
|
Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed");
|
||||||
Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282]));
|
Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282]));
|
||||||
|
|||||||
@@ -280,12 +280,12 @@ public class SwedishGeneratorTest {
|
|||||||
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
||||||
var clues = Clues.createEmpty();
|
var clues = Clues.createEmpty();
|
||||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
||||||
|
var dict = new Dict(WORDS2);
|
||||||
var slots = extractSlots(clues);
|
var slots = extractSlots(clues, dict.index());
|
||||||
assertEquals(1, slots.length);
|
assertEquals(1, slots.length);
|
||||||
var s = slots[0];
|
var s = slots[0];
|
||||||
|
|
||||||
assertTrue(s.length() >= 2);
|
assertTrue(Slot.length(s.lo(), s.hi()) >= 2);
|
||||||
assertEquals(OFF_0_0, Slot.clueIndex(s.key()));
|
assertEquals(OFF_0_0, Slot.clueIndex(s.key()));
|
||||||
assertEquals(CLUE_RIGHT, Slot.dir(s.key()));
|
assertEquals(CLUE_RIGHT, Slot.dir(s.key()));
|
||||||
}
|
}
|
||||||
@@ -396,10 +396,9 @@ public class SwedishGeneratorTest {
|
|||||||
counts[2] = 3;
|
counts[2] = 3;
|
||||||
var dict = new Dict(WORDS);
|
var dict = new Dict(WORDS);
|
||||||
var entry5 = dict.index()[5];
|
var entry5 = dict.index()[5];
|
||||||
var sScore = Slot.from(0, (1L << 1) | (1L << 2), 0L);
|
|
||||||
// cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3
|
// cross = (counts[1]-1) + (counts[2]-1) = 1 + 2 = 3
|
||||||
// score = 3 * 10 + len(2) = 32
|
// score = 3 * 10 + len(2) = 32
|
||||||
assertEquals(32, slotScore(counts, sScore));
|
assertEquals(32, slotScore(counts, (1L << 1) | (1L << 2), 0L));
|
||||||
// 3. Test candidateCountForPattern
|
// 3. Test candidateCountForPattern
|
||||||
|
|
||||||
var ctx = Context.get();
|
var ctx = Context.get();
|
||||||
|
|||||||
Reference in New Issue
Block a user