introduce bitloops
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package puzzle;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
@@ -183,6 +184,92 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
static class Clues {
|
||||
|
||||
long lo, hi, vlo, vhi, rlo, rhi;
|
||||
public static Clues createEmpty() {
|
||||
return new Clues(0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
public Clues deepCopyGrid() { return new Clues(lo, hi, vlo, vhi, rlo, rhi); }
|
||||
boolean clueless(int idx) {
|
||||
if (!isClue(idx)) return false;
|
||||
clearClue(idx);
|
||||
return true;
|
||||
}
|
||||
public boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
||||
|
||||
public void setClue(int ri, byte idx) {
|
||||
if ((ri & 64) == 0) {
|
||||
long mask = 1L << ri;
|
||||
lo |= mask;
|
||||
if ((idx & 1) != 0) vlo |= mask; else vlo &= ~mask;
|
||||
if ((idx & 2) != 0) rlo |= mask; else rlo &= ~mask;
|
||||
} else {
|
||||
long mask = 1L << (ri & 63);
|
||||
hi |= mask;
|
||||
if ((idx & 1) != 0) vhi |= mask; else vhi &= ~mask;
|
||||
if ((idx & 2) != 0) rhi |= mask; else rhi &= ~mask;
|
||||
}
|
||||
}
|
||||
public byte digitAt(int idx) {
|
||||
if ((idx & 64) == 0) {
|
||||
int v = (int) ((vlo >>> idx) & 1L);
|
||||
int r = (int) ((rlo >>> idx) & 1L);
|
||||
return (byte) ((r << 1) | v);
|
||||
} else {
|
||||
int v = (int) ((vhi >>> (idx & 63)) & 1L);
|
||||
int r = (int) ((rhi >>> (idx & 63)) & 1L);
|
||||
return (byte) ((r << 1) | v);
|
||||
}
|
||||
}
|
||||
public void clearClue(int idx) {
|
||||
if ((idx & 64) == 0) {
|
||||
long mask = ~(1L << idx);
|
||||
lo &= mask;
|
||||
vlo &= mask;
|
||||
rlo &= mask;
|
||||
} else {
|
||||
long mask = ~(1L << (idx & 63));
|
||||
hi &= mask;
|
||||
vhi &= mask;
|
||||
rhi &= mask;
|
||||
}
|
||||
}
|
||||
public boolean isClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||
public boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||
public boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||
public boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||
|
||||
public int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||
public double similarity(Clues b) {
|
||||
long matchLo = (~(lo ^ b.lo)) & (~lo | (~(vlo ^ b.vlo) & ~(rlo ^ b.rlo)));
|
||||
long matchHi = (~(hi ^ b.hi)) & (~hi | (~(vhi ^ b.vhi) & ~(rhi ^ b.rhi)));
|
||||
|
||||
long maskLo = (SIZE >= 64) ? -1L : (1L << SIZE) - 1;
|
||||
long maskHi = (SIZE <= 64) ? 0L : (1L << (SIZE - 64)) - 1;
|
||||
|
||||
return (Long.bitCount(matchLo & maskLo) + Long.bitCount(matchHi & maskHi)) / SIZED;
|
||||
|
||||
|
||||
/* var same = 0;
|
||||
for (int i = 0; i < SIZE; i++) if (digitAt(i) == b.digitAt(i)) same++;
|
||||
return same / SIZED;*/
|
||||
}
|
||||
public Grid toGrid() {
|
||||
var grid = new Grid(new byte[SIZE], lo, hi);
|
||||
for (var l = lo; l != X; l &= l - 1) {
|
||||
int idx = Long.numberOfTrailingZeros(l);
|
||||
grid.g[idx] = digitAt(idx);
|
||||
}
|
||||
for (var h = hi; h != X; h &= h - 1) {
|
||||
int idx = 64 | Long.numberOfTrailingZeros(h);
|
||||
grid.g[idx] = digitAt(idx);
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
|
||||
static class Grid {
|
||||
|
||||
final byte[] g;
|
||||
@@ -218,30 +305,9 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
boolean isClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) != X : ((hi >>> (index & 63)) & 1L) != X; }
|
||||
boolean notClue(long index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||
boolean notClue(int index) { return ((index & 64) == 0) ? ((lo >>> index) & 1L) == X : ((hi >>> (index & 63)) & 1L) == X; }
|
||||
boolean clueless(int idx) {
|
||||
if ((idx & 64) == 0) {
|
||||
val test = (1L << idx);
|
||||
if ((test & lo) == X) return false;
|
||||
g[idx] = DASH;
|
||||
|
||||
lo &= ~test;
|
||||
} else {
|
||||
val test = (1L << (idx & 63));
|
||||
if ((test & hi) == X) return false;
|
||||
g[idx] = DASH;
|
||||
|
||||
hi &= ~test;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public double similarity(Grid b) {
|
||||
var same = 0;
|
||||
for (int i = 0; i < SIZE; i++) if (g[i] == b.g[i]) same++;
|
||||
return same / SIZED;
|
||||
}
|
||||
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||
boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
||||
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||
|
||||
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));
|
||||
@@ -402,7 +468,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
}
|
||||
|
||||
/// does not modify the grid
|
||||
long maskFitness(final Grid grid) {
|
||||
long maskFitness(final Clues grid) {
|
||||
|
||||
long cHLo = 0L, cHHi = 0L, cVLo = 0L, cVHi = 0L;
|
||||
long lo_cl = grid.lo, hi_cl = grid.hi;
|
||||
@@ -413,6 +479,15 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
for (long bits = (i == 0 ? lo_cl : hi_cl); bits != X; bits &= bits - 1) {
|
||||
int clueIdx = i | Long.numberOfTrailingZeros(bits);
|
||||
int key = Slot.packSlotDir(clueIdx, grid.digitAt(clueIdx));
|
||||
/// long clueBits = (i == 0 ? lo_cl : hi_cl);
|
||||
/// long vBits = (i == 0 ? grid.vlo : grid.vhi);
|
||||
/// long rBits = (i == 0 ? grid.rlo : grid.rhi);
|
||||
/// for (long bits = clueBits; bits != X; bits &= bits - 1) {
|
||||
/// long lsb = bits & -bits;
|
||||
/// int clueIdx = i | Long.numberOfTrailingZeros(lsb);
|
||||
/// int v = (vBits & lsb) != 0 ? 1 : 0;
|
||||
/// int r = (rBits & lsb) != 0 ? 1 : 0;
|
||||
/// int key = Slot.packSlotDir(clueIdx, (r << 1) | v);
|
||||
long rLo = PATH_LO[key], rHi = PATH_HI[key];
|
||||
long hLo = rLo & lo_cl, hHi = rHi & hi_cl;
|
||||
if (Slot.increasing(key)) {
|
||||
@@ -531,8 +606,8 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
return penalty;
|
||||
}
|
||||
|
||||
Grid randomMask() {
|
||||
var g = Grid.createEmpty();
|
||||
SwedishGenerator.Clues randomMask() {
|
||||
var g = Clues.createEmpty();
|
||||
for (int placed = 0, guard = 0, idx; placed < TARGET_CLUES && guard < 4000; guard++) {
|
||||
idx = rng.randint(0, SIZE_MIN_1);
|
||||
if (g.isClue(idx)) continue;
|
||||
@@ -544,7 +619,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
}
|
||||
return g;
|
||||
}
|
||||
Grid mutate(Grid grid) {
|
||||
Clues mutate(Clues grid) {
|
||||
var g = grid.deepCopyGrid();
|
||||
int ri;
|
||||
var bytes = MUTATE_RI[rng.randint(0, SIZE_MIN_1)];
|
||||
@@ -557,38 +632,49 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
}
|
||||
return g;
|
||||
}
|
||||
Grid crossover(Grid a, Grid other) {
|
||||
Clues crossover(Clues a, Clues other) {
|
||||
var out = a.deepCopyGrid();
|
||||
var theta = rng.nextFloat() * Math.PI;
|
||||
var nc = Math.cos(theta);
|
||||
var nr = Math.sin(theta);
|
||||
|
||||
long bo0 = out.lo, bo1 = out.hi;
|
||||
for (var rci : IT) {
|
||||
int i = rci.i();
|
||||
if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
|
||||
byte ch = other.g[i];
|
||||
if (out.g[i] != ch) {
|
||||
out.g[i] = ch;
|
||||
var ch = other.digitAt(i);
|
||||
if (out.digitAt(i) != ch) {
|
||||
if (other.isClue(i)) {
|
||||
if ((i & 64) == 0) bo0 |= (1L << i);
|
||||
else bo1 |= (1L << (i & 63));
|
||||
out.setClue(i, ch);
|
||||
} else {
|
||||
if ((i & 64) == 0) bo0 &= ~(1L << i);
|
||||
else bo1 &= ~(1L << (i & 63));
|
||||
out.clearClue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out.lo = bo0;
|
||||
out.hi = bo1;
|
||||
|
||||
// long maskLo = 0, maskHi = 0;
|
||||
// for (var rci : IT) {
|
||||
// if ((rci.cross_r()) * nc + (rci.cross_c()) * nr < 0) {
|
||||
// int i = rci.i();
|
||||
// if (i < 64) maskLo |= (1L << i);
|
||||
// else maskHi |= (1L << (i - 64));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// out.lo = (out.lo & ~maskLo) | (other.lo & maskLo);
|
||||
// out.hi = (out.hi & ~maskHi) | (other.hi & maskHi);
|
||||
// out.vlo = (out.vlo & ~maskLo) | (other.vlo & maskLo);
|
||||
// out.vhi = (out.vhi & ~maskHi) | (other.vhi & maskHi);
|
||||
// out.rlo = (out.rlo & ~maskLo) | (other.rlo & maskLo);
|
||||
// out.rhi = (out.rhi & ~maskHi) | (other.rhi & maskHi);
|
||||
|
||||
for (var lo = out.lo; lo != X; lo &= lo - 1L) clearClues(out, Long.numberOfTrailingZeros(lo));
|
||||
for (var hi = out.hi; hi != X; hi &= hi - 1L) clearClues(out, 64 | Long.numberOfTrailingZeros(hi));
|
||||
return out;
|
||||
}
|
||||
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(Clues out, int idx) { if (!out.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(idx, out.digitAt(idx))])) out.clearClue(idx); }
|
||||
|
||||
Grid hillclimb(Grid start, int limit) {
|
||||
Clues hillclimb(Clues start, int limit) {
|
||||
var best = start;
|
||||
var bestF = maskFitness(best);
|
||||
var fails = 0;
|
||||
@@ -607,12 +693,12 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
return best;
|
||||
}
|
||||
|
||||
public Grid generateMask(int popSize, int gens, int pairs) {
|
||||
public SwedishGenerator.Grid generateMask(int popSize, int gens, int pairs) {
|
||||
class GridAndFit {
|
||||
|
||||
Grid grid;
|
||||
long fite = -1;
|
||||
GridAndFit(Grid grid) { this.grid = grid; }
|
||||
Clues grid;
|
||||
long fite = -1;
|
||||
GridAndFit(Clues grid) { this.grid = grid; }
|
||||
long fit() {
|
||||
if (fite == -1) this.fite = maskFitness(grid);
|
||||
return this.fite;
|
||||
@@ -659,10 +745,10 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
var x = pop.get(i);
|
||||
if (x.fit() < best.fit()) best = x;
|
||||
}
|
||||
return best.grid;
|
||||
return best.grid.toGrid();
|
||||
}
|
||||
|
||||
static long patternForSlot(Grid grid, Slot s) {
|
||||
static long patternForSlot(Grid grid, SwedishGenerator.Slot s) {
|
||||
if ((s.lo & ~grid.lo) == 0 && (s.hi & ~grid.hi) == 0) return 0;
|
||||
long p = 0;
|
||||
if (s.increasing()) {
|
||||
@@ -769,7 +855,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int[] candidateInfoForPattern(long[] res, long pattern, DictEntry entry, int lenb) {
|
||||
static int[] candidateInfoForPattern(long[] res, long pattern, SwedishGenerator.DictEntry entry, int lenb) {
|
||||
int numLongs = entry.numlong;
|
||||
boolean first = true;
|
||||
|
||||
@@ -841,7 +927,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
for (int i = 0; i < slots.length; i++) slotScores[i] = slotScore(count, slots[i]);
|
||||
|
||||
}
|
||||
public static FillResult fillMask(Rng rng, Grid mask, DictEntry[] dictIndex) {
|
||||
public static SwedishGenerator.FillResult fillMask(SwedishGenerator.Rng rng, Grid mask, DictEntry[] dictIndex) {
|
||||
val multiThreaded = Thread.currentThread().getName().contains("pool");
|
||||
val NO_LOG = (!Main.VERBOSE || multiThreaded);
|
||||
val grid = mask.deepCopyGrid();
|
||||
@@ -885,7 +971,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
System.out.print("\r" + Strings.padRight(msg, 120));
|
||||
System.out.flush();
|
||||
}
|
||||
Pick chooseMRV() {
|
||||
SwedishGenerator.Pick chooseMRV() {
|
||||
Slot best = null;
|
||||
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) {
|
||||
var s = slots[i];
|
||||
@@ -993,7 +1079,7 @@ public record SwedishGenerator(Rng rng, int[] stack) {
|
||||
var ok = solver.backtrack(0);
|
||||
// final progress line
|
||||
|
||||
var res = new FillResult(ok, new Gridded(grid), assigned, new FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
|
||||
var res = new FillResult(ok, new Gridded(grid), assigned, new SwedishGenerator.FillStats(solver.nodes, solver.backtracks, (System.currentTimeMillis() - t0) / 1000.0, solver.lastMRV));
|
||||
if (!multiThreaded) {
|
||||
System.out.print("\r" + Strings.padRight("", 120) + "\r");
|
||||
System.out.flush();
|
||||
|
||||
@@ -285,7 +285,7 @@ public class SwedishGeneratorTest {
|
||||
@Test
|
||||
void testMaskFitnessBasic() {
|
||||
var gen = new SwedishGenerator(new Rng(0), new int[STACK_SIZE]);
|
||||
var grid = Grid.createEmpty();
|
||||
var grid = Clues.createEmpty();
|
||||
// Empty grid should have high penalty (no slots)
|
||||
var f1 = gen.maskFitness(grid);
|
||||
assertTrue(f1 >= 1_000_000_000L);
|
||||
@@ -426,7 +426,7 @@ public class SwedishGeneratorTest {
|
||||
@Test
|
||||
void testMaskFitnessDetailed() {
|
||||
var gen = new SwedishGenerator(new Rng(42), new int[STACK_SIZE]);
|
||||
var grid = Grid.createEmpty();
|
||||
var grid = Clues.createEmpty();
|
||||
// Empty grid: huge penalty
|
||||
var fitEmpty = gen.maskFitness(grid);
|
||||
assertTrue(fitEmpty >= 1_000_000_000L);
|
||||
|
||||
Reference in New Issue
Block a user