introduce bitloops
This commit is contained in:
@@ -14,12 +14,20 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import static java.nio.charset.StandardCharsets.*;
|
import static java.nio.charset.StandardCharsets.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE:
|
||||||
|
* A) generate randoms for and idx and direction (clue together)
|
||||||
|
* B) convert IDX_PATH_0_BASE into bit-pattern (i think) Length should not be checked, just left empty if not good.
|
||||||
|
* C) store more information in the byte[] even consider the long[] or try sqeeze the 288 options (clue) in a byte.
|
||||||
|
* 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
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SwedishGenerator.java
|
* SwedishGenerator.java
|
||||||
*
|
*
|
||||||
@@ -53,7 +61,6 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
static final int MAX_TRIES_PER_SLOT = Config.MAX_TRIES_PER_SLOT;
|
static final int MAX_TRIES_PER_SLOT = Config.MAX_TRIES_PER_SLOT;
|
||||||
static final char C_DASH = '\0';
|
static final char C_DASH = '\0';
|
||||||
static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH;
|
static final byte _1 = 49, _9 = 57, A = 65, Z = 90, DASH = (byte) C_DASH;
|
||||||
static final ThreadLocal<Context> CTX = ThreadLocal.withInitial(Context::new);
|
|
||||||
|
|
||||||
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, CandidateInfo info, boolean done) { }
|
||||||
@@ -124,14 +131,12 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Context {
|
static final record Context(int[] stack, long[] undo, long[] bitset) {
|
||||||
|
|
||||||
final int[] stack = new int[SIZE];
|
public Context() { this(new int[SIZE], new long[128], new long[2500]); }
|
||||||
long pattern;
|
private static final ThreadLocal<Context> CTX = ThreadLocal.withInitial(Context::new);
|
||||||
final long[] undo = new long[128];
|
|
||||||
final long[] bitset = new long[2500];
|
|
||||||
|
|
||||||
void setPattern(long p) { this.pattern = p; }
|
public static Context get() { return CTX.get(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Rng {
|
static final class Rng {
|
||||||
@@ -163,6 +168,10 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return (int) (min + (u % range));
|
return (int) (min + (u % range));
|
||||||
}
|
}
|
||||||
double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
|
double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
|
||||||
|
int biasedIndexPow3(int N) {
|
||||||
|
int m = Math.min(nextU32(), Math.min(nextU32(), nextU32()));
|
||||||
|
return (int) (((m & 0xFFFFFFFFL) * (long) N) >>> 32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Grid {
|
static class Grid {
|
||||||
@@ -223,7 +232,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return same / SIZED;
|
return same / SIZED;
|
||||||
}
|
}
|
||||||
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
int clueCount() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||||
boolean hasRoomForClue(long packed) { return (packed & GT_1_OFFSET_53_BIT) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
boolean hasRoomForClue(long packed) { return (packed) != X && notClue(packed & 0x7FL) && notClue((packed >>> 7) & 0x7FL); }
|
||||||
void forEachSlot(SlotVisitor visitor) {
|
void forEachSlot(SlotVisitor visitor) {
|
||||||
for (var l = lo; l != X; l &= l - 1) processSlot(this, visitor, Long.numberOfTrailingZeros(l));
|
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));
|
||||||
@@ -234,7 +243,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static record DictEntry(long[] words, long[][] posBitsets, int numlong) { }
|
static record DictEntry(long[] words, long[][] posBitsets, CandidateInfo empty, int length, int numlong) { }
|
||||||
|
|
||||||
static int LEMMA_COUNTER = 0;
|
static int LEMMA_COUNTER = 0;
|
||||||
|
|
||||||
@@ -301,7 +310,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DictEntry(words, bitsets, (words.length + 63) >>> 6);
|
return new DictEntry(words, bitsets, new CandidateInfo(null, words.length), 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());
|
||||||
}
|
}
|
||||||
@@ -322,7 +331,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
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) { return new Slot(key, lo, hi); }
|
||||||
|
|
||||||
public int len() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
public int length() { return Long.bitCount(lo) + Long.bitCount(hi); }
|
||||||
public int clueIndex() { return clueIndex(key); }
|
public int clueIndex() { return clueIndex(key); }
|
||||||
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; }
|
||||||
@@ -411,7 +420,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
rLo &= ~((1L << msb << 1) - 1);
|
rLo &= ~((1L << msb << 1) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((rLo | rHi) != 0) {
|
if ((rLo | rHi) != 0L) {
|
||||||
hasSlots = true;
|
hasSlots = true;
|
||||||
if (Slot.horiz(key)) {
|
if (Slot.horiz(key)) {
|
||||||
cHLo |= rLo;
|
cHLo |= rLo;
|
||||||
@@ -428,7 +437,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!hasSlots) return 1_000_000_000L;
|
if (!hasSlots) return 1_000_000_000L;
|
||||||
var ctx = CTX.get();
|
var ctx = Context.get();
|
||||||
var stack = ctx.stack;
|
var stack = ctx.stack;
|
||||||
long seenLo = 0L, seenHi = 0L;
|
long seenLo = 0L, seenHi = 0L;
|
||||||
|
|
||||||
@@ -535,8 +544,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
ri = bytes[rng.randint(0, 624)];
|
ri = bytes[rng.randint(0, 624)];
|
||||||
if (!g.clueless(ri)) {
|
if (!g.clueless(ri)) {
|
||||||
var d_idx = rng.randint2bitByte();
|
var d_idx = rng.randint2bitByte();
|
||||||
val packed = OFFSETS_D_IDX[Slot.packSlotDir(ri, d_idx)];
|
if (g.hasRoomForClue(OFFSETS_D_IDX[Slot.packSlotDir(ri, d_idx)])) g.setClue(ri, d_idx);
|
||||||
if (g.hasRoomForClue(packed)) g.setClue(ri, d_idx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
@@ -692,7 +700,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
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 = s.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 = s.hi; b != 0; b &= b - 1) cross += (count[64 | Long.numberOfTrailingZeros(b)] - 1);
|
||||||
return cross * 10 + s.len();
|
return cross * 10 + s.length();
|
||||||
}
|
}
|
||||||
static boolean placeWord(Grid grid, Slot s, long w, long[] undoBuffer, int offset) {
|
static boolean placeWord(Grid grid, Slot s, long w, long[] undoBuffer, int offset) {
|
||||||
long maskLo = 0, maskHi = 0;
|
long maskLo = 0, maskHi = 0;
|
||||||
@@ -753,12 +761,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CandidateInfo candidateInfoForPattern(Context ctx, DictEntry entry, int lenb) {
|
static CandidateInfo candidateInfoForPattern(Context ctx, long pattern, DictEntry entry, int lenb) {
|
||||||
var pattern = ctx.pattern;
|
|
||||||
if (pattern == X) {
|
|
||||||
return new CandidateInfo(null, entry.words.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
int numLongs = entry.numlong;
|
int numLongs = entry.numlong;
|
||||||
long[] res = ctx.bitset;
|
long[] res = ctx.bitset;
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
@@ -782,8 +785,6 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]);
|
for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]);
|
||||||
|
|
||||||
if (count == 0) return new CandidateInfo(null, 0);
|
|
||||||
|
|
||||||
int[] indices = new int[count];
|
int[] indices = new int[count];
|
||||||
int ki = 0;
|
int ki = 0;
|
||||||
for (int k = 0; k < numLongs; k++) {
|
for (int k = 0; k < numLongs; k++) {
|
||||||
@@ -798,10 +799,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
return new CandidateInfo(indices, count);
|
return new CandidateInfo(indices, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int candidateCountForPattern(Context ctx, DictEntry entry, int lenb) {
|
static int candidateCountForPattern(Context ctx, long pattern, DictEntry entry, int lenb) {
|
||||||
long pattern = ctx.pattern;
|
|
||||||
if (pattern == X) return entry.words.length;
|
|
||||||
|
|
||||||
int numLongs = entry.numlong;
|
int numLongs = entry.numlong;
|
||||||
long[] res = ctx.bitset;
|
long[] res = ctx.bitset;
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
@@ -822,8 +820,6 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first) return entry.words.length; // should not happen if pattern != X
|
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]);
|
for (int k = 0; k < numLongs; k++) count += Long.bitCount(res[k]);
|
||||||
return count;
|
return count;
|
||||||
@@ -837,7 +833,7 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
val used = new Bit1029();
|
val used = new Bit1029();
|
||||||
|
|
||||||
long[] assigned = new long[BIGG];
|
long[] assigned = new long[BIGG];
|
||||||
val ctx = CTX.get();
|
val ctx = Context.get();
|
||||||
val count = new int[SIZE];
|
val count = new int[SIZE];
|
||||||
|
|
||||||
val slots = extractSlots(grid);
|
val slots = extractSlots(grid);
|
||||||
@@ -880,34 +876,29 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
Pick chooseMRV() {
|
Pick chooseMRV() {
|
||||||
Slot best = null;
|
Slot best = null;
|
||||||
CandidateInfo bestInfo = null;
|
for (int i = 0, count, count2 = -1, bestScore = -1, n = slots.size(); i < n; i++) {
|
||||||
int bestScore = -1;
|
|
||||||
for (int i = 0, n = slots.size(); i < n; i++) {
|
|
||||||
var s = slots.get(i);
|
var s = slots.get(i);
|
||||||
if (assigned[s.key()] != X) continue;
|
if (assigned[s.key] != X) continue;
|
||||||
var entry = dictIndex[s.len()];
|
var pattern = patternForSlot(grid, s);
|
||||||
if (entry == null) return PICK_NOT_DONE;
|
var index = dictIndex[s.length()];
|
||||||
ctx.pattern = patternForSlot(grid, s);
|
count = pattern == X ? index.length : candidateCountForPattern(ctx, pattern, index, s.length());
|
||||||
int count = candidateCountForPattern(ctx, entry, s.len());
|
|
||||||
|
|
||||||
if (count == 0) return PICK_NOT_DONE;
|
if (count == 0) return PICK_NOT_DONE;
|
||||||
if (best == null
|
if (best == null
|
||||||
|| count < bestInfo.count
|
|| count < count2
|
||||||
|| (count == bestInfo.count && slotScores[i] > bestScore)) {
|
|| (count == count2 && slotScores[i] > bestScore)) {
|
||||||
best = s;
|
best = s;
|
||||||
bestScore = slotScores[i];
|
bestScore = slotScores[i];
|
||||||
bestInfo = new CandidateInfo(null, count);
|
count2 = count;
|
||||||
if (count <= 1) break;
|
if (count <= 1) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best == null) return PICK_DONE;
|
|
||||||
|
|
||||||
// Re-calculate for the best slot to get actual indices
|
// Re-calculate for the best slot to get actual indices
|
||||||
ctx.pattern = patternForSlot(grid, best);
|
if (best == null) return PICK_DONE;
|
||||||
bestInfo = candidateInfoForPattern(ctx, dictIndex[best.len()], best.len());
|
var pattern = patternForSlot(grid, best);
|
||||||
|
var index = dictIndex[best.length()];
|
||||||
return new Pick(best, bestInfo, false);
|
if (pattern == X) return new Pick(best, index.empty, false);
|
||||||
|
return new Pick(best, candidateInfoForPattern(ctx, pattern, index, best.length()), false);
|
||||||
}
|
}
|
||||||
boolean backtrack(int depth) {
|
boolean backtrack(int depth) {
|
||||||
if (Thread.currentThread().isInterrupted()) return false;
|
if (Thread.currentThread().isInterrupted()) return false;
|
||||||
@@ -926,8 +917,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
renderProgress();
|
renderProgress();
|
||||||
|
|
||||||
var s = pick.slot;
|
var s = pick.slot;
|
||||||
var k = s.key();
|
var k = s.key;
|
||||||
int patLen = s.len();
|
int patLen = s.length();
|
||||||
var entry = dictIndex[patLen];
|
var entry = dictIndex[patLen];
|
||||||
|
|
||||||
if (info.indices != null && info.indices.length > 0) {
|
if (info.indices != null && info.indices.length > 0) {
|
||||||
@@ -936,7 +927,8 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
var tries = Math.min(MAX_TRIES_PER_SLOT, L);
|
||||||
|
|
||||||
for (var t = 0; t < tries; t++) {
|
for (var t = 0; t < tries; t++) {
|
||||||
double r = rng.nextFloat();
|
var r = rng.nextFloat();
|
||||||
|
//int idxInArray = rng.biasedIndexPow3(L - 1);
|
||||||
int idxInArray = (int) (r * r * r * (L - 1));
|
int idxInArray = (int) (r * r * r * (L - 1));
|
||||||
var idx = idxs[idxInArray];
|
var idx = idxs[idxInArray];
|
||||||
var w = entry.words[idx];
|
var w = entry.words[idx];
|
||||||
@@ -959,10 +951,6 @@ public record SwedishGenerator(Rng rng) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var N = entry.words.length;
|
var N = entry.words.length;
|
||||||
if (N == 0) {
|
|
||||||
stats.backtracks++;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
|
var tries = Math.min(MAX_TRIES_PER_SLOT, N);
|
||||||
for (var t = 0; t < tries; t++) {
|
for (var t = 0; t < tries; t++) {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class MainTest {
|
|||||||
var slots = extractSlots(grid);
|
var slots = extractSlots(grid);
|
||||||
assertEquals(1, slots.size());
|
assertEquals(1, slots.size());
|
||||||
var s = slots.getFirst();
|
var s = slots.getFirst();
|
||||||
assertEquals(8, s.len());
|
assertEquals(8, s.length());
|
||||||
var cells = s.walk().toArray();
|
var cells = s.walk().toArray();
|
||||||
assertEquals(0, Grid.r(cells[0]));
|
assertEquals(0, Grid.r(cells[0]));
|
||||||
assertEquals(1, Grid.c(cells[0]));
|
assertEquals(1, Grid.c(cells[0]));
|
||||||
|
|||||||
@@ -258,9 +258,7 @@ public class SwedishGeneratorTest {
|
|||||||
var dict = new Dict(new long[]{ l0, l1, l2, l3, l3a, l4a, l6a, l7a, l8a });
|
var dict = new Dict(new long[]{ l0, l1, l2, l3, l3a, l4a, l6a, l7a, l8a });
|
||||||
|
|
||||||
// Pattern "APP--" for length 5
|
// Pattern "APP--" for length 5
|
||||||
var context = new Context();
|
var info = candidateInfoForPattern(Context.get(), packPattern("APP"), dict.index()[5], 5);
|
||||||
context.setPattern(packPattern("APP"));
|
|
||||||
var info = candidateInfoForPattern(context, dict.index()[5], 5);
|
|
||||||
|
|
||||||
assertEquals(2, info.count());
|
assertEquals(2, info.count());
|
||||||
assertNotNull(info.indices());
|
assertNotNull(info.indices());
|
||||||
@@ -279,7 +277,7 @@ public class SwedishGeneratorTest {
|
|||||||
assertEquals(1, slots.size());
|
assertEquals(1, slots.size());
|
||||||
var s = slots.getFirst();
|
var s = slots.getFirst();
|
||||||
|
|
||||||
assertTrue(s.len() >= 2);
|
assertTrue(s.length() >= 2);
|
||||||
assertEquals(OFF_0_0, s.clueIndex());
|
assertEquals(OFF_0_0, s.clueIndex());
|
||||||
assertEquals(CLUE_RIGHT, Slot.dir(s.key()));
|
assertEquals(CLUE_RIGHT, Slot.dir(s.key()));
|
||||||
}
|
}
|
||||||
@@ -421,15 +419,15 @@ public class SwedishGeneratorTest {
|
|||||||
var dict = new Dict(words);
|
var dict = new Dict(words);
|
||||||
var entry5 = dict.index()[5];
|
var entry5 = dict.index()[5];
|
||||||
|
|
||||||
var ctx = new Context();
|
var ctx = Context.get();
|
||||||
ctx.setPattern(packPattern("APP"));
|
long pattern = packPattern("APP");
|
||||||
assertEquals(2, candidateCountForPattern(ctx, entry5, 3));
|
assertEquals(2, candidateCountForPattern(ctx, pattern, entry5, 3));
|
||||||
|
|
||||||
ctx.setPattern(packPattern("BAN"));
|
pattern = packPattern("BAN");
|
||||||
assertEquals(1, candidateCountForPattern(ctx, entry5, 3));
|
assertEquals(1, candidateCountForPattern(ctx, pattern, entry5, 3));
|
||||||
|
|
||||||
ctx.setPattern(packPattern("CAT"));
|
pattern = packPattern("CAT");
|
||||||
assertEquals(0, candidateCountForPattern(ctx, entry5, 3));
|
assertEquals(0, candidateCountForPattern(ctx, pattern, entry5, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user