introduce bitloops
This commit is contained in:
@@ -16,9 +16,11 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
|
||||
@@ -37,7 +39,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
public CandidateInfo(int n) { this(null, n); }
|
||||
}
|
||||
|
||||
static final CandidateInfo[] CANDIDATES = IntStream.range(0, 10192 << 2).mapToObj(CandidateInfo::new).toArray(CandidateInfo[]::new);
|
||||
// static final CandidateInfo[] CANDIDATES = IntStream.range(0, 10192 << 2).mapToObj(CandidateInfo::new).toArray(CandidateInfo[]::new);
|
||||
|
||||
//@formatter:off
|
||||
@FunctionalInterface interface SlotVisitor { void visit(int key, long packedPos, int len); }
|
||||
@@ -108,7 +110,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
|
||||
public static record FillResult(boolean ok,
|
||||
Gridded grid,
|
||||
HashMap<Integer, Lemma> clueMap,
|
||||
Map<Integer, Lemma> clueMap,
|
||||
FillStats stats) {
|
||||
|
||||
public void calcSimpel() {
|
||||
@@ -191,7 +193,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
if (idx < 64) lo |= (1L << idx);
|
||||
else hi |= (1L << (idx & 63));
|
||||
}
|
||||
void clear(int idx) { g[idx] = DASH; }
|
||||
void clearletter(int idx) { g[idx] = DASH; }
|
||||
void clearClue(int idx) {
|
||||
g[idx] = DASH;
|
||||
if (idx < 64) lo &= ~(1L << idx);
|
||||
@@ -246,7 +248,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
boolean hasRoomForClue(long packed) { return (packed & GT_1_OFFSET_53_BIT) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
||||
void forEachSlot(SlotVisitor visitor) {
|
||||
for (var l = lo; l != X; l &= l - 1) processSlot(this, visitor, Long.numberOfTrailingZeros(l));
|
||||
for (var h = hi; h != X; h &= h - 1) processSlot(this, visitor, 64 | Long.numberOfTrailingZeros(h));
|
||||
for (var h = hi; h != X; h &= h - 1) processSlot(this, visitor, 64 + Long.numberOfTrailingZeros(h));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,17 +354,19 @@ public record SwedishGenerator(Rng rng) {
|
||||
|
||||
static record Slot(int key, long packedPos) {
|
||||
|
||||
static final int BIT_FOR_DIR = 3;
|
||||
static Slot from(int key, long packedPos, int len) { return new Slot(key, packedPos | ((long) len << 56)); }
|
||||
void undoPlace(Grid grid, int mask) { for (int i = 0, len = len(); i < len; i++) if ((mask & (1L << i)) != 0) grid.clear(pos(i)); }
|
||||
void undoPlace(Grid grid, int mask) { for (int i = 0, len = len(); i < len; i++) if ((mask & (1L << i)) != 0) grid.clearletter(pos(i)); }
|
||||
public int len() { return (int) (packedPos >>> 56); }
|
||||
public int clueR() { return Grid.r((key >>> 4)); }
|
||||
public int clueIndex() { return key >>> 4; }
|
||||
public int clueC() { return Grid.c((key >>> 4)); }
|
||||
public int dir() { return key & 15; }
|
||||
public int clueR() { return Grid.r((key >>> BIT_FOR_DIR)); }
|
||||
public int clueIndex() { return key >>> BIT_FOR_DIR; }
|
||||
public int clueC() { return Grid.c((key >>> BIT_FOR_DIR)); }
|
||||
public int dir() { return key & 7; }
|
||||
public boolean horiz() { return horiz(key); }
|
||||
public int pos(int i) { return offset(packedPos, i); }
|
||||
public static boolean horiz(int key) { return (key & 1) == 0/*((key & 15) & 1) == 0*/; }
|
||||
public static int offset(long packedPos, int i) { return (int) ((packedPos >> (i * 7)) & 127); }
|
||||
public static int packSlotDir(int idx, int d) { return (idx << BIT_FOR_DIR) | d; }
|
||||
}
|
||||
|
||||
private static void processSlot(Grid grid, SlotVisitor visitor, int idx) {
|
||||
@@ -376,7 +380,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
packedPos |= iidx << offset;
|
||||
}
|
||||
if (k > 0) {
|
||||
visitor.visit((idx << 4) | d, packedPos, k);
|
||||
visitor.visit(Slot.packSlotDir(idx, d), packedPos, k);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,38 +414,39 @@ public record SwedishGenerator(Rng rng) {
|
||||
var covV = ctx.covV;
|
||||
Arrays.fill(covH, 0, SIZE, 0);
|
||||
Arrays.fill(covV, 0, SIZE, 0);
|
||||
int clueIdx;
|
||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||
long penalty = 0L;
|
||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||
long penalty = (((long) Math.abs(grid.clueCount() - TARGET_CLUES)) << 3);
|
||||
boolean hasSlots = false;
|
||||
|
||||
for (int i = 0; i < 65; i += 64) {
|
||||
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
||||
clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var d = grid.digitAt(clueIdx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
long packed = nbrs16.path()[clueIdx];
|
||||
int n = (int) (packed >>> 56) * 7, k, idx;
|
||||
var horiz = Slot.horiz(d) ? covH : covV;
|
||||
int clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var d = grid.digitAt(clueIdx);
|
||||
var nbrs16 = OFFSETS[d];
|
||||
long packed = nbrs16.path()[clueIdx];
|
||||
int n = (int) (packed >>> 56) * 7, k, idx;
|
||||
var horiz = Slot.horiz(d) ? covH : covV;
|
||||
for (k = 0; k < n && k < MAX_WORD_LENGTH7; k += 7) {
|
||||
idx = (int) ((packed >>> (k)) & 0x7F);
|
||||
if (grid.isClue(idx)) break;
|
||||
horiz[idx] += 1;
|
||||
}
|
||||
if (k > 0) {
|
||||
hasSlots = true;
|
||||
if (k < MIN_LEN7) penalty += 8000;
|
||||
penalty |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((penalty & 1) == 0) return 1_000_000_000L;
|
||||
penalty = (penalty & ~3L) + (((long) Math.abs(grid.clueCount() - TARGET_CLUES)) << 3);
|
||||
if (!hasSlots) return 1_000_000_000L;
|
||||
|
||||
var seen = ctx.seen;
|
||||
var stack = ctx.stack;
|
||||
seen.clear();
|
||||
|
||||
for (int i = 0; i < 65; i += 64) {
|
||||
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
||||
clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
int clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
if (seen.get(clueIdx)) continue;
|
||||
int size = 0;
|
||||
stack[0] = clueIdx;
|
||||
@@ -462,8 +467,8 @@ public record SwedishGenerator(Rng rng) {
|
||||
for (int i = 0; i < 65; i += 64) {
|
||||
long bits = (i == 0 ? ~lo_cl : (~hi_cl & 0xFFL));
|
||||
for (; bits != X; bits &= bits - 1) {
|
||||
clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var rci = IT[clueIdx];
|
||||
int clueIdx = i + Long.numberOfTrailingZeros(bits);
|
||||
var rci = IT[clueIdx];
|
||||
if ((4 - rci.nbrCount()) + Long.bitCount(rci.n1() & lo_cl) + Long.bitCount(rci.n2() & hi_cl) >= 3) penalty += 400;
|
||||
var h = covH[clueIdx];
|
||||
var v = covV[clueIdx];
|
||||
@@ -601,9 +606,14 @@ public record SwedishGenerator(Rng rng) {
|
||||
|
||||
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop.get(0).fit());
|
||||
}
|
||||
|
||||
pop.sort(Comparator.comparingLong(GridAndFit::fit));
|
||||
return pop.get(0).grid;
|
||||
GridAndFit best = pop.get(0);
|
||||
for (int i = 1; i < pop.size(); i++) {
|
||||
var x = pop.get(i);
|
||||
if (x.fit() < best.fit()) best = x;
|
||||
}
|
||||
//pop.sort(Comparator.comparingLong(GridAndFit::fit));
|
||||
//return pop.get(0).grid;
|
||||
return best.grid;
|
||||
}
|
||||
static void patternForSlot(Grid grid, Slot s, byte[] pat) {
|
||||
byte ch;
|
||||
@@ -631,7 +641,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
} else if (cur != ch) {
|
||||
for (var j = 0; j < i; j++) {
|
||||
if ((mask & (1 << j)) != 0) {
|
||||
grid.clear(s.pos(j));
|
||||
grid.clearletter(s.pos(j));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -655,7 +665,8 @@ public record SwedishGenerator(Rng rng) {
|
||||
}
|
||||
|
||||
if (listCount == 0) {
|
||||
return CANDIDATES[entry.words.length];
|
||||
//return CANDIDATES[entry.words.length];
|
||||
return new CandidateInfo(null, entry.words.length);
|
||||
}
|
||||
|
||||
// Sort constraints by size to optimize intersection
|
||||
@@ -693,9 +704,10 @@ public record SwedishGenerator(Rng rng) {
|
||||
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
||||
val grid = mask.deepCopyGrid();
|
||||
val used = new Bit1029();
|
||||
val assigned = new HashMap<Integer, Lemma>();
|
||||
val ctx = CTX.get();
|
||||
val count = ctx.cellCount;
|
||||
// val assigned = new HashMap<Integer, Lemma>();
|
||||
Lemma[] assigned = new Lemma[1024];
|
||||
val ctx = CTX.get();
|
||||
val count = ctx.cellCount;
|
||||
Arrays.fill(count, 0, SIZE, 0);
|
||||
|
||||
val slots = extractSlots(grid);
|
||||
@@ -716,8 +728,10 @@ public record SwedishGenerator(Rng rng) {
|
||||
var now = System.currentTimeMillis();
|
||||
if ((now - lastLog) < LOG_EVERY_MS) return;
|
||||
lastLog = (now);
|
||||
|
||||
var done = assigned.size();
|
||||
var done = 0;
|
||||
for (var lemma : assigned) {
|
||||
if (lemma != null) done++;
|
||||
}
|
||||
var pct = (TOTAL == 0) ? 100 : (int) Math.floor((done / (double) TOTAL) * 100);
|
||||
var filled = Math.min(BAR_LEN, (int) Math.floor((pct / 100.0) * BAR_LEN));
|
||||
var bar = "[" + "#".repeat(filled) + "-".repeat(BAR_LEN - filled) + "]";
|
||||
@@ -737,7 +751,7 @@ public record SwedishGenerator(Rng rng) {
|
||||
int bestScore = -1;
|
||||
for (int i = 0, n = slots.size(); i < n; i++) {
|
||||
var s = slots.get(i);
|
||||
if (assigned.containsKey(s.key())) continue;
|
||||
if (assigned[s.key()] != null) continue;
|
||||
var entry = dictIndex[s.len()];
|
||||
if (entry == null) return PICK_NOT_DONE;
|
||||
patternForSlot(grid, s, ctx.pattern);
|
||||
@@ -798,11 +812,11 @@ public record SwedishGenerator(Rng rng) {
|
||||
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||
|
||||
used.set(w.index);
|
||||
assigned.put(k, w);
|
||||
assigned[k] = w;
|
||||
|
||||
if (backtrack(depth + 1)) return true;
|
||||
|
||||
assigned.remove(k);
|
||||
assigned[k] = null;
|
||||
used.clear(w.index);
|
||||
s.undoPlace(grid, ctx.undo[depth]);
|
||||
}
|
||||
@@ -827,11 +841,11 @@ public record SwedishGenerator(Rng rng) {
|
||||
if (!placeWord(grid, s, w, ctx.undo, depth)) continue;
|
||||
|
||||
used.set(w.index);
|
||||
assigned.put(k, w);
|
||||
assigned[k] = w;
|
||||
|
||||
if (backtrack(depth + 1)) return true;
|
||||
|
||||
assigned.remove(k);
|
||||
assigned[k] = null;
|
||||
used.clear(w.index);
|
||||
s.undoPlace(grid, ctx.undo[depth]);
|
||||
}
|
||||
@@ -852,14 +866,20 @@ public record SwedishGenerator(Rng rng) {
|
||||
}
|
||||
|
||||
stats.seconds = (System.currentTimeMillis() - t0) / 1000.0;
|
||||
var res = new FillResult(ok, new Gridded(grid), assigned, stats);
|
||||
Map<Integer, Lemma> lemmaMap = new HashMap<>();
|
||||
for (var i = 0; i < assigned.length; i++) {
|
||||
if (assigned[i] != null) {
|
||||
lemmaMap.put(i, assigned[i]);
|
||||
}
|
||||
}
|
||||
var res = new FillResult(ok, new Gridded(grid), lemmaMap, stats);
|
||||
|
||||
// print a final progress line
|
||||
if (Main.VERBOSE && !multiThreaded) {
|
||||
System.out.println(
|
||||
String.format(Locale.ROOT,
|
||||
"[######################] %d/%d slots | nodes=%d | backtracks=%d | mrv=%d | %.1fs",
|
||||
assigned.size(), TOTAL, stats.nodes, stats.backtracks, stats.lastMRV, stats.seconds
|
||||
lemmaMap.size(), TOTAL, stats.nodes, stats.backtracks, stats.lastMRV, stats.seconds
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public class MainTest {
|
||||
// Regression baseline for seed search starting at 12347, pop 4, gens 20
|
||||
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
||||
Assertions.assertEquals(22, res.filled().clueMap().size(), "Number of assigned words changed");
|
||||
Assertions.assertEquals(Lemma.pack(new byte[]{ 'I', 'E', 'M', 'A', 'N', 'D', 'S' }), res.filled().clueMap().get(515).word());
|
||||
Assertions.assertEquals(Lemma.pack(new byte[]{ 'I', 'E', 'M', 'A', 'N', 'D', 'S' }), res.filled().clueMap().get(259).word());
|
||||
Assertions.assertEquals(648985643903632391L, res.filled().grid().grid().lo);
|
||||
Assertions.assertEquals(140L, res.filled().grid().grid().hi);
|
||||
}
|
||||
|
||||
@@ -135,8 +135,12 @@ public class SwedishGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testSlot() {
|
||||
System.out.println("[DEBUG_LOG] Slot.BIT_FOR_DIR = " + Slot.BIT_FOR_DIR);
|
||||
// key = (r << 8) | (c << 4) | d
|
||||
var key = (Grid.offset(2, 3) << 4) | 5;
|
||||
var offset = Grid.offset(2, 3);
|
||||
System.out.println("[DEBUG_LOG] Grid.offset(2, 3) = " + offset);
|
||||
var key = (offset << Slot.BIT_FOR_DIR) | 5;
|
||||
System.out.println("[DEBUG_LOG] key = " + key);
|
||||
long packedPos = 0;
|
||||
// pos 0: (2, 5)
|
||||
packedPos |= Grid.offset(2, 5);
|
||||
@@ -146,6 +150,7 @@ public class SwedishGeneratorTest {
|
||||
packedPos |= (long) Grid.offset(4, 5) << 14;
|
||||
|
||||
var s = Slot.from(key, packedPos, 3);
|
||||
System.out.println("[DEBUG_LOG] s.dir() = " + s.dir());
|
||||
assertEquals(2, s.clueR());
|
||||
assertEquals(3, s.clueC());
|
||||
assertEquals(5, s.dir());
|
||||
|
||||
Reference in New Issue
Block a user