introduce bitloops
This commit is contained in:
@@ -6,7 +6,6 @@ import lombok.experimental.Delegate;
|
||||
import lombok.val;
|
||||
import puzzle.Export.Gridded.Replacar.Cell;
|
||||
import puzzle.SwedishGenerator.Clues;
|
||||
import puzzle.SwedishGenerator.Dict;
|
||||
import puzzle.SwedishGenerator.FillResult;
|
||||
import puzzle.SwedishGenerator.Grid;
|
||||
import java.util.ArrayList;
|
||||
@@ -47,7 +46,7 @@ public record Export() {
|
||||
for (var c = 0; c < C; c++) {
|
||||
val idx = Grid.offset(r, c);
|
||||
if (mask.isClue(idx))
|
||||
sb.append((char) mask.digitAt(idx));
|
||||
sb.append((char) (mask.digitAt(idx) | 48));
|
||||
else {
|
||||
sb.append(' ');
|
||||
}
|
||||
@@ -182,7 +181,7 @@ public record Export() {
|
||||
|
||||
public record ExportedPuzzle(String[] grid, WordOut[] words, int difficulty, Rewards rewards) { }
|
||||
|
||||
public record PuzzleResult(SwedishGenerator swe, Dict dict, Clued mask, FillResult filled) {
|
||||
public record PuzzleResult(Clued mask, FillResult filled) {
|
||||
|
||||
boolean inBounds(int idx) { return idx >= 0 && idx < SwedishGenerator.SIZE; }
|
||||
Placed extractPlacedFromSlot(Slot s, long lemma) { return new Placed(lemma, s.key(), s.walk().toArray()); }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package puzzle;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.val;
|
||||
import lombok.NoArgsConstructor;
|
||||
import puzzle.SwedishGenerator.Rng;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -38,17 +39,18 @@ public class Main {
|
||||
static final AtomicLong TOTAL_SIMPLICITY = new AtomicLong(0); // Scaled by 100 for precision
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Opts {
|
||||
|
||||
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
||||
public int pop = 18;
|
||||
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
|
||||
public int pop = 18;
|
||||
public int gens = 1200;
|
||||
public String wordsPath = "nl_score_hints_v3.csv";
|
||||
public double minSimplicity = 0; // 0 means no limit
|
||||
public int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
|
||||
public int tries = threads;
|
||||
public boolean reindex = false;
|
||||
public int fillTimeout = 20_000;
|
||||
public boolean verbose = false;
|
||||
|
||||
}
|
||||
@@ -356,9 +358,9 @@ public class Main {
|
||||
}
|
||||
static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) {
|
||||
TOTAL_ATTEMPTS.incrementAndGet();
|
||||
val stack = new int[STACK_SIZE];
|
||||
var swe = new SwedishGenerator(rng, stack, 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 filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index());
|
||||
|
||||
TOTAL_NODES.addAndGet(filled.stats().nodes);
|
||||
@@ -380,7 +382,7 @@ public class Main {
|
||||
);
|
||||
|
||||
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {
|
||||
return new PuzzleResult(swe, dict, new Clued(mask), filled);
|
||||
return new PuzzleResult(new Clued(mask), filled);
|
||||
}
|
||||
|
||||
if (opts.verbose && filled.ok()) {
|
||||
|
||||
@@ -148,14 +148,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
}
|
||||
}
|
||||
|
||||
static final record Context(long[] bitset) {
|
||||
|
||||
public Context() { this(new long[2500]); }
|
||||
private static final ThreadLocal<Context> CTX = ThreadLocal.withInitial(Context::new);
|
||||
|
||||
public static Context get() { return CTX.get(); }
|
||||
}
|
||||
|
||||
static final class Rng {
|
||||
|
||||
@Getter private int x;
|
||||
@@ -315,8 +307,6 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
|
||||
static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
|
||||
|
||||
static int LEMMA_COUNTER = 0;
|
||||
|
||||
public static interface Lemma {
|
||||
|
||||
static final long LETTER_MASK = (1L << 40) - 1; // low 40 bits
|
||||
@@ -329,12 +319,12 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
for (var i = 0; i < b.length; i++) w |= ((long) b[i] & 63) << (i * 5);
|
||||
return w;
|
||||
}
|
||||
static public long from(String word) { return pack(LEMMA_COUNTER++, word.getBytes(US_ASCII)); }
|
||||
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; }
|
||||
static int intAt(long word, int idx) { return (int) (((word >>> (idx * 5))) & 0b11111); }// word[]; }
|
||||
static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); }
|
||||
static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); }
|
||||
static int length(long word) { return ((63 - Long.numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; }
|
||||
static public long from(int index, String word) { return pack(index, word.getBytes(US_ASCII)); }
|
||||
static byte byteAt(long word, int idx) { return (byte) ((word >>> (idx * 5)) & 0b11111 | B64); }// word[]; }
|
||||
static int intAt(long word, int idx) { return (int) (((word >>> (idx * 5))) & 0b11111); }// word[]; }
|
||||
static String[] clue(long w) { return CsvIndexService.clues(unpackIndex(w)); }
|
||||
static int simpel(long w) { return CsvIndexService.simpel(unpackIndex(w)); }
|
||||
static int length(long word) { return ((63 - Long.numberOfLeadingZeros(word & LETTER_MASK)) / 5) + 1; }
|
||||
public static String asWord(long word) {
|
||||
var b = new byte[Lemma.length(word)];
|
||||
for (var i = 0; i < b.length; i++) b[i] = (byte) ((word >>> (i * 5)) & 0b11111 | B64);
|
||||
@@ -851,14 +841,13 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int[] candidateInfoForPattern(long[] res, long pattern, DictEntry entry, int lenb) {
|
||||
int numLongs = entry.numlong;
|
||||
static int[] candidateInfoForPattern(long[] res, long pattern, long[][] posBitsets, int numLongs) {
|
||||
boolean first = true;
|
||||
|
||||
for (long p = pattern; p != 0; ) {
|
||||
int combined = (int) (p & 0xFF);
|
||||
if (combined != 0) {
|
||||
long[] bs = entry.posBitsets[combined - 1];
|
||||
long[] bs = posBitsets[combined - 1];
|
||||
if (first) {
|
||||
System.arraycopy(bs, 0, res, 0, numLongs);
|
||||
first = false;
|
||||
@@ -888,8 +877,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
return indices;
|
||||
}
|
||||
|
||||
static int candidateCountForPattern(final long[] res, final long pattern, final DictEntry entry, final int lenb) {
|
||||
val numLongs = entry.numlong;
|
||||
static int candidateCountForPattern(final long[] res, final long pattern, final DictEntry entry, final int numLongs) {
|
||||
val posBitsets = entry.posBitsets;
|
||||
boolean first = true;
|
||||
|
||||
@@ -975,7 +963,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
if (assigned[s.key] != X) continue;
|
||||
var pattern = patternForSlot(grid, s);
|
||||
var index = dictIndex[s.length()];
|
||||
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index, s.length());
|
||||
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index, index.numlong);
|
||||
|
||||
if (count == 0) {
|
||||
current = PICK_NOT_DONE;
|
||||
@@ -1004,7 +992,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
|
||||
current.indices = null;
|
||||
return;
|
||||
}
|
||||
current.indices = candidateInfoForPattern(bitset, pattern, index, best.length());
|
||||
current.indices = candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
|
||||
}
|
||||
boolean backtrack(int depth) {
|
||||
if (Thread.currentThread().isInterrupted()) return false;
|
||||
|
||||
@@ -9,7 +9,6 @@ import puzzle.Export.Rewards;
|
||||
import puzzle.Export.PuzzleResult;
|
||||
import puzzle.SwedishGenerator.FillResult;
|
||||
import puzzle.SwedishGenerator.Grid;
|
||||
import puzzle.SwedishGenerator.Lemma;
|
||||
import puzzle.SwedishGenerator.Rng;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -24,7 +23,7 @@ public class ExportFormatTest {
|
||||
static final byte CLUE_RIGHT = 1;
|
||||
static final byte CLUE_UP = 2;
|
||||
static final byte CLUE_LEFT = 3;
|
||||
|
||||
|
||||
@Test
|
||||
void testExportFormatFromFilled() {
|
||||
var swe = new SwedishGenerator(new Rng(0), new int[STACK_SIZE], Clues.createEmpty());
|
||||
@@ -40,7 +39,7 @@ public class ExportFormatTest {
|
||||
var clueMap = new long[300];
|
||||
// key = (cellIndex << 2) | (direction)
|
||||
var key = (0) | (CLUE_RIGHT);
|
||||
clueMap[key] = Lemma.from("TEST");
|
||||
clueMap[key] = SwedishGeneratorTest.TEST;
|
||||
|
||||
// Manually fill the grid letters for "TEST" at (0,1), (0,2), (0,3), (0,4)
|
||||
grid.setLetter(Grid.offset(0, 1), (byte) 'T');
|
||||
@@ -49,7 +48,7 @@ public class ExportFormatTest {
|
||||
grid.setLetter(Grid.offset(0, 4), (byte) 'T');
|
||||
|
||||
var fillResult = new FillResult(true, new Gridded(grid), clueMap, new FillStats(0, 0, 0, 0));
|
||||
var puzzleResult = new PuzzleResult(swe, null, new Clued(clues), fillResult);
|
||||
var puzzleResult = new PuzzleResult(new Clued(clues), fillResult);
|
||||
|
||||
var rewards = new Rewards(10, 5, 1);
|
||||
var exported = puzzleResult.exportFormatFromFilled(2, rewards);
|
||||
@@ -90,7 +89,7 @@ public class ExportFormatTest {
|
||||
var grid = Grid.createEmpty();
|
||||
val clues = Clues.createEmpty();
|
||||
var fillResult = new FillResult(true, new Gridded(grid), new long[300], new FillStats(0, 0, 0, 0));
|
||||
var puzzleResult = new PuzzleResult(swe, null, new Clued(clues), fillResult);
|
||||
var puzzleResult = new PuzzleResult(new Clued(clues), fillResult);
|
||||
|
||||
var exported = puzzleResult.exportFormatFromFilled(1, new Rewards(0, 0, 0));
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ package puzzle;
|
||||
import lombok.val;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import puzzle.Export.Clued;
|
||||
import puzzle.Export.PuzzleResult;
|
||||
import puzzle.Export.Rewards;
|
||||
import puzzle.Main.Opts;
|
||||
import puzzle.SwedishGenerator.Rng;
|
||||
import puzzle.SwedishGenerator.Slot;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -24,19 +26,28 @@ public class MainTest {
|
||||
static final byte CLUE_UP = 2;
|
||||
static final byte CLUE_LEFT = 3;
|
||||
|
||||
static final int OFF_0_0 = Grid.offset(0, 0);
|
||||
static final int OFF_0_1 = Grid.offset(0, 1);
|
||||
static final int OFF_0_2 = Grid.offset(0, 2);
|
||||
static final int OFF_1_1 = Grid.offset(1, 1);
|
||||
static final int OFF_1_2 = Grid.offset(1, 2);
|
||||
static final int OFF_2_3 = Grid.offset(2, 3);
|
||||
|
||||
static final int OFF_0_0 = Grid.offset(0, 0);
|
||||
static final int OFF_0_1 = Grid.offset(0, 1);
|
||||
static final int OFF_0_2 = Grid.offset(0, 2);
|
||||
static final int OFF_1_1 = Grid.offset(1, 1);
|
||||
static final int OFF_1_2 = Grid.offset(1, 2);
|
||||
static final int OFF_2_3 = Grid.offset(2, 3);
|
||||
static final Opts opts = new Main.Opts() {{
|
||||
this.seed = 12348;
|
||||
this.pop = 4; // Tiny population
|
||||
this.gens = 20; // Very few generations
|
||||
this.minSimplicity = 0;
|
||||
this.threads = 1;
|
||||
this.tries = 1;
|
||||
this.verbose = false;
|
||||
}};
|
||||
static final Dict dict = Dict.loadDict(opts.wordsPath);
|
||||
@Test
|
||||
void testExtractSlots() {
|
||||
|
||||
var clues = Clues.createEmpty();
|
||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
||||
var grid = clues.toGrid();
|
||||
var grid = clues.toGrid();
|
||||
grid.setLetter(OFF_0_1, LETTER_A);
|
||||
grid.setLetter(OFF_0_2, LETTER_B);
|
||||
|
||||
@@ -84,7 +95,7 @@ public class MainTest {
|
||||
public void testGridBasics() {
|
||||
var clues = Clues.createEmpty();
|
||||
clues.setClue(OFF_1_2, CLUE_UP);
|
||||
var grid = clues.toGrid();
|
||||
var grid = clues.toGrid();
|
||||
|
||||
// Test set/get
|
||||
grid.setLetter(OFF_0_0, LETTER_A);
|
||||
@@ -130,28 +141,52 @@ public class MainTest {
|
||||
}
|
||||
@Test
|
||||
public void testMini() {
|
||||
val idx = OFF_1_1;
|
||||
val idx = OFF_1_1;
|
||||
var clues = Clues.createEmpty();
|
||||
clues.setClue(idx, CLUE_LEFT);
|
||||
var grid = clues.toGrid();
|
||||
var grid = clues.toGrid();
|
||||
Assertions.assertTrue(grid.isClue(idx));
|
||||
}
|
||||
@Test
|
||||
void testMaskerCreation() {
|
||||
var swe = new SwedishGenerator(new Rng(12348), 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)));
|
||||
val clued = new Clued(mask);
|
||||
val test = clued.gridToString();
|
||||
val RESULT = " 3 300\n" +
|
||||
" 1 \n" +
|
||||
" 1 \n" +
|
||||
" 3 0 \n" +
|
||||
" 31 \n" +
|
||||
" 1 \n" +
|
||||
" 1 2\n" +
|
||||
"21 22 3";
|
||||
|
||||
Assertions.assertEquals(18, clued.clueCount(), "Found seed changed");
|
||||
Assertions.assertEquals(RESULT, test, "Found seed changed");
|
||||
}
|
||||
@Test
|
||||
void testFiller() {
|
||||
val rng = new Rng(-343913721);
|
||||
val mask = new Clues(
|
||||
74732156493031040L,
|
||||
193L,
|
||||
281475397248512L,
|
||||
128L,
|
||||
422762372923520L,
|
||||
192L);
|
||||
var filled = fillMask(rng, extractSlots(mask), mask.toGrid(), dict.index());
|
||||
Assertions.assertTrue(filled.ok(), "Puzzle generation failed (not ok)");
|
||||
Assertions.assertEquals(18, filled.wordCount(), "Number of assigned words changed");
|
||||
Assertions.assertEquals("SLEDE", Lemma.asWord(filled.clueMap()[282]));
|
||||
Assertions.assertEquals(74732156493031040L, filled.grid().grid().lo());
|
||||
Assertions.assertEquals(193L, filled.grid().grid().hi());
|
||||
|
||||
var aa = new PuzzleResult(new Clued(mask), filled).exportFormatFromFilled(1, new Rewards(1, 1, 1));
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testAttempt() {
|
||||
// Arrange
|
||||
var opts = new Main.Opts();
|
||||
opts.seed = 12348;
|
||||
opts.pop = 4; // Tiny population
|
||||
opts.gens = 20; // Very few generations
|
||||
opts.minSimplicity = 0;
|
||||
opts.fillTimeout = 10_000;
|
||||
opts.threads = 1;
|
||||
opts.tries = 1;
|
||||
opts.verbose = false;
|
||||
|
||||
var dict = Dict.loadDict(opts.wordsPath);
|
||||
|
||||
// Act
|
||||
PuzzleResult res = null;
|
||||
int foundSeed = -1;
|
||||
for (int i = 0; i < 50; i++) {
|
||||
@@ -164,21 +199,13 @@ public class MainTest {
|
||||
System.out.println("[DEBUG_LOG] ClueMap Size: " + res.filled().wordCount());
|
||||
System.out.println("[DEBUG_LOG] Grid:");
|
||||
System.out.println(res.filled().grid().renderHuman());
|
||||
var aa = res.exportFormatFromFilled(1, new Rewards(1, 1, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assertions.assertNotNull(res, "Puzzle generation failed (null result)");
|
||||
Assertions.assertTrue(res.filled().ok(), "Puzzle generation failed (not ok)");
|
||||
|
||||
// Regression baseline for seed search starting at 12347, pop 4, gens 20
|
||||
Assertions.assertEquals(12348, foundSeed, "Found seed changed");
|
||||
Assertions.assertEquals(18, res.filled().wordCount(), "Number of assigned words changed");
|
||||
Assertions.assertEquals("SLEDE", Lemma.asWord(res.filled().clueMap()[282]));
|
||||
Assertions.assertEquals(74732156493031040L, res.filled().grid().grid().lo());
|
||||
Assertions.assertEquals(193L, res.filled().grid().grid().hi());
|
||||
}
|
||||
boolean isLetter(byte b) { return (b & 64) != 0; }
|
||||
@Test
|
||||
|
||||
@@ -12,15 +12,55 @@ import static puzzle.SwedishGenerator.*;
|
||||
|
||||
public class SwedishGeneratorTest {
|
||||
|
||||
static final byte LETTER_A = (byte) 'A';
|
||||
static final byte LETTER_B = (byte) 'B';
|
||||
static final byte LETTER_C = (byte) 'C';
|
||||
static final byte LETTER_X = (byte) 'X';
|
||||
static final byte LETTER_Z = (byte) 'Z';
|
||||
static final byte CLUE_DOWN = 0;
|
||||
static final byte CLUE_RIGHT = 1;
|
||||
static final byte CLUE_UP = 2;
|
||||
static final byte CLUE_LEFT = 3;
|
||||
record Context(long[] bitset) {
|
||||
|
||||
public Context() { this(new long[2500]); }
|
||||
private static final ThreadLocal<Context> CTX = ThreadLocal.withInitial(Context::new);
|
||||
|
||||
public static Context get() { return CTX.get(); }
|
||||
}
|
||||
|
||||
static final long TEST = Lemma.from(0, "TEST");
|
||||
static final long[] WORDS = new long[]{
|
||||
Lemma.from(1, "AT"),
|
||||
Lemma.from(2, "CAT"),
|
||||
Lemma.from(3, "DOGS"),
|
||||
Lemma.from(4, "APPLE"),
|
||||
Lemma.from(5, "APPLY"),
|
||||
Lemma.from(6, "BANAN"),
|
||||
Lemma.from(7, "BANANA"),
|
||||
Lemma.from(8, "BANANAS"),
|
||||
Lemma.from(9, "BANANASS") // length 8
|
||||
};
|
||||
static final long l2a = Lemma.from(10, "IN");
|
||||
static final long l4a = Lemma.from(11, "INER");
|
||||
static final long l6a = Lemma.from(12, "INEREN");
|
||||
static final long l7a = Lemma.from(13, "INERENA");
|
||||
static final long l8a = Lemma.from(14, "INERENAE");
|
||||
static final long l1 = Lemma.from(15, "APPLE");
|
||||
static final long l2 = Lemma.from(16, "AXE");
|
||||
|
||||
static final long[] WORDS2 = new long[]{ Lemma.from(17, "IN"),
|
||||
Lemma.from(18, "APPLE"),
|
||||
Lemma.from(19, "APPLY"),
|
||||
Lemma.from(20, "BANAN"),
|
||||
Lemma.from(21, "INE"),
|
||||
Lemma.from(22, "INER"),
|
||||
Lemma.from(23, "INEREN"),
|
||||
Lemma.from(24, "INERENA"),
|
||||
Lemma.from(25, "INERENAE") };
|
||||
static final long ABC = Lemma.from(26, "ABC");
|
||||
static final long ABD = Lemma.from(27, "ABD");
|
||||
static final long AZ = Lemma.from(28, "AZ");
|
||||
static final byte LETTER_A = (byte) 'A';
|
||||
static final byte LETTER_B = (byte) 'B';
|
||||
static final byte LETTER_C = (byte) 'C';
|
||||
static final byte LETTER_X = (byte) 'X';
|
||||
static final byte LETTER_Z = (byte) 'Z';
|
||||
static final byte CLUE_DOWN = 0;
|
||||
static final byte CLUE_RIGHT = 1;
|
||||
static final byte CLUE_UP = 2;
|
||||
static final byte CLUE_LEFT = 3;
|
||||
|
||||
static final int OFF_0_0 = Grid.offset(0, 0);
|
||||
static final int OFF_0_1 = Grid.offset(0, 1);
|
||||
@@ -108,19 +148,11 @@ public class SwedishGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testLemmaAndDict() {
|
||||
var l2a = Lemma.from("IN");
|
||||
var l4a = Lemma.from("INER");
|
||||
var l6a = Lemma.from("INEREN");
|
||||
var l7a = Lemma.from("INERENA");
|
||||
var l8a = Lemma.from("INERENAE");
|
||||
|
||||
var l1 = Lemma.from("APPLE");
|
||||
Assertions.assertEquals(Lemma.pack("APPLE".getBytes(StandardCharsets.US_ASCII)), Lemma.unpackLetters(l1));
|
||||
assertEquals(5, Lemma.length(l1));
|
||||
assertEquals((byte) 'A', Lemma.byteAt(l1, 0));
|
||||
assertEquals(1, Lemma.intAt(l1, 0));
|
||||
|
||||
var l2 = Lemma.from("AXE");
|
||||
var dict = new Dict(new long[]{ l1, l2, l2a, l4a, l6a, l7a, l8a });
|
||||
|
||||
assertEquals(1, dict.index()[3].words().length);
|
||||
@@ -232,19 +264,10 @@ public class SwedishGeneratorTest {
|
||||
|
||||
@Test
|
||||
void testCandidateInfoForPattern() {
|
||||
var l0 = Lemma.from("IN");
|
||||
var l3a = Lemma.from("INE");
|
||||
var l4a = Lemma.from("INER");
|
||||
var l6a = Lemma.from("INEREN");
|
||||
var l7a = Lemma.from("INERENA");
|
||||
var l8a = Lemma.from("INERENAE");
|
||||
var l1 = Lemma.from("APPLE");
|
||||
var l2 = Lemma.from("APPLY");
|
||||
var l3 = Lemma.from("BANAN");
|
||||
var dict = new Dict(new long[]{ l0, l1, l2, l3, l3a, l4a, l6a, l7a, l8a });
|
||||
var dict = new Dict(WORDS2);
|
||||
|
||||
// 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].posBitsets(), dict.index()[5].numlong());
|
||||
|
||||
assertEquals(2, info.length);
|
||||
assertNotNull(info);
|
||||
@@ -255,7 +278,6 @@ public class SwedishGeneratorTest {
|
||||
// This should detect a slot starting at 0,1 with length 2 (0,1 and 0,2)
|
||||
var clues = Clues.createEmpty();
|
||||
clues.setClue(OFF_0_0, CLUE_RIGHT);
|
||||
var grid = clues.toGrid();
|
||||
|
||||
var slots = extractSlots(clues);
|
||||
assertEquals(1, slots.length);
|
||||
@@ -305,7 +327,7 @@ public class SwedishGeneratorTest {
|
||||
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT);
|
||||
var lo = (1L << OFF_0_0) | (1L << OFF_0_1) | (1L << OFF_0_2);
|
||||
var s = Slot.from(key, lo, 0L);
|
||||
var w1 = Lemma.from("ABC");
|
||||
var w1 = ABC;
|
||||
var undoBuffer = new long[10];
|
||||
|
||||
// 1. Successful placement in empty grid
|
||||
@@ -320,7 +342,7 @@ public class SwedishGeneratorTest {
|
||||
assertEquals(0L, undoBuffer[2]); // 0 new characters placed
|
||||
|
||||
// 3. Conflict: place "ABD" where "ABC" is
|
||||
var w2 = Lemma.from("ABD");
|
||||
var w2 = ABD;
|
||||
assertFalse(placeWord(grid, s, w2, undoBuffer, 2));
|
||||
// Verify grid is unchanged (still "ABC")
|
||||
assertEquals('A', grid.letter32At(OFF_0_0));
|
||||
@@ -344,7 +366,7 @@ public class SwedishGeneratorTest {
|
||||
var key = (OFF_0_0 << Slot.BIT_FOR_DIR) | (CLUE_RIGHT);
|
||||
var lo = (1L << OFF_0_1) | (1L << OFF_0_2);
|
||||
var s = Slot.from(key, lo, 0L);
|
||||
var w = Lemma.from("AZ");
|
||||
var w = AZ;
|
||||
var undoBuffer = new long[10];
|
||||
|
||||
var placed = placeWord(grid, s, w, undoBuffer, 0);
|
||||
@@ -381,30 +403,19 @@ public class SwedishGeneratorTest {
|
||||
assertEquals(32, slotScore(counts, sScore));
|
||||
|
||||
// 3. Test candidateCountForPattern
|
||||
var words = new long[]{
|
||||
Lemma.from("AT"),
|
||||
Lemma.from("CAT"),
|
||||
Lemma.from("DOGS"),
|
||||
Lemma.from("APPLE"),
|
||||
Lemma.from("APPLY"),
|
||||
Lemma.from("BANAN"),
|
||||
Lemma.from("BANANA"),
|
||||
Lemma.from("BANANAS"),
|
||||
Lemma.from("BANANASS") // length 8
|
||||
};
|
||||
|
||||
var dict = new Dict(words);
|
||||
var dict = new Dict(WORDS);
|
||||
var entry5 = dict.index()[5];
|
||||
|
||||
var ctx = Context.get();
|
||||
var pattern = packPattern("APP");
|
||||
assertEquals(2, candidateCountForPattern(ctx.bitset(), pattern, entry5, 3));
|
||||
assertEquals(2, candidateCountForPattern(ctx.bitset(), pattern, entry5, entry5.numlong()));
|
||||
|
||||
pattern = packPattern("BAN");
|
||||
assertEquals(1, candidateCountForPattern(ctx.bitset(), pattern, entry5, 3));
|
||||
assertEquals(1, candidateCountForPattern(ctx.bitset(), pattern, entry5, entry5.numlong()));
|
||||
|
||||
pattern = packPattern("CAT");
|
||||
assertEquals(0, candidateCountForPattern(ctx.bitset(), pattern, entry5, 3));
|
||||
assertEquals(0, candidateCountForPattern(ctx.bitset(), pattern, entry5, entry5.numlong()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package puzzle;
|
||||
import puzzle.ThemePoolBuilderLength.Lexicon;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
public class TestSort {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Lexicon lex = new Lexicon(
|
||||
Arrays.asList("A", "B", "C"),
|
||||
new HashMap<>(),
|
||||
new int[]{10, 30, 20},
|
||||
new BitSet[9]
|
||||
);
|
||||
BitSet bs = new BitSet();
|
||||
bs.set(0); bs.set(1); bs.set(2);
|
||||
Path p = Paths.get("test_pool.txt");
|
||||
ThemePoolBuilderLength.writeWordList(p, lex, bs);
|
||||
List<String> lines = Files.readAllLines(p);
|
||||
System.out.println("Sorted words: " + lines);
|
||||
if (lines.get(0).equals("B") && lines.get(1).equals("C") && lines.get(2).equals("A")) {
|
||||
System.out.println("SUCCESS");
|
||||
} else {
|
||||
System.out.println("FAILURE");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user