Gather data
This commit is contained in:
19
pom.xml
19
pom.xml
@@ -47,10 +47,29 @@
|
|||||||
<version>RELEASE</version>
|
<version>RELEASE</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.42</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.42</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
|
|
||||||
@@ -50,7 +53,7 @@ public final class ExportFormat {
|
|||||||
}
|
}
|
||||||
gridv2.add(sb.toString());
|
gridv2.add(sb.toString());
|
||||||
}
|
}
|
||||||
return new ExportedPuzzle(gridv2, List.of(), difficulty, rewards);
|
return new ExportedPuzzle(gridv2, new WordOut[0], difficulty, rewards);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
|
// 2) bounding box around all word cells + arrow cells, with 1-cell margin
|
||||||
@@ -72,7 +75,7 @@ public final class ExportFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3) map of only used letter cells (everything else becomes '#')
|
// 3) map of only used letter cells (everything else becomes '#')
|
||||||
Map<Long, Character> letterAt = new HashMap<>();
|
var letterAt = new HashMap<Long, Character>();
|
||||||
for (var p : placed) {
|
for (var p : placed) {
|
||||||
for (var rc : p.cells) {
|
for (var rc : p.cells) {
|
||||||
int rr = rc[0], cc = rc[1];
|
int rr = rc[0], cc = rc[1];
|
||||||
@@ -83,7 +86,7 @@ public final class ExportFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4) render gridv2 over cropped bounds (out-of-bounds become '#')
|
// 4) render gridv2 over cropped bounds (out-of-bounds become '#')
|
||||||
List<String> gridv2 = new ArrayList<>(Math.max(0, maxR - minR + 1));
|
var gridv2 = new ArrayList<String>(Math.max(0, maxR - minR + 1));
|
||||||
for (var r = minR; r <= maxR; r++) {
|
for (var r = minR; r <= maxR; r++) {
|
||||||
var row = new StringBuilder(Math.max(0, maxC - minC + 1));
|
var row = new StringBuilder(Math.max(0, maxC - minC + 1));
|
||||||
for (var c = minC; c <= maxC; c++) {
|
for (var c = minC; c <= maxC; c++) {
|
||||||
@@ -93,23 +96,17 @@ public final class ExportFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5) words output with cropped coordinates
|
// 5) words output with cropped coordinates
|
||||||
var wordsOut = new ArrayList<WordOut>(placed.size());
|
int MIN_R = minR, MIN_C = minC;
|
||||||
for (var p : placed) {
|
var wordsOut = placed.stream().map(p -> new WordOut(
|
||||||
wordsOut.add(new WordOut(
|
p.lemma,
|
||||||
p.lemma,
|
p.startRow - MIN_R,
|
||||||
p.word,
|
p.startCol - MIN_C,
|
||||||
p.clue, // placeholder = word (same as JS)
|
p.direction,
|
||||||
p.startRow - minR,
|
p.arrowRow - MIN_R,
|
||||||
p.startCol - minC,
|
p.arrowCol - MIN_C,
|
||||||
p.direction,
|
p.isReversed,
|
||||||
p.word, // answer
|
p.lemma.simpel()
|
||||||
p.arrowRow - minR,
|
)).toArray(WordOut[]::new);
|
||||||
p.arrowCol - minC,
|
|
||||||
p.isReversed,
|
|
||||||
p.lemma.simpel()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ExportedPuzzle(gridv2, wordsOut, difficulty, rewards);
|
return new ExportedPuzzle(gridv2, wordsOut, difficulty, rewards);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +114,8 @@ public final class ExportFormat {
|
|||||||
* Convert a generator Slot + assigned word into a Placed object for export.
|
* Convert a generator Slot + assigned word into a Placed object for export.
|
||||||
*/
|
*/
|
||||||
private static Placed extractPlacedFromSlot(Slot s, Lemma lemma) {
|
private static Placed extractPlacedFromSlot(Slot s, Lemma lemma) {
|
||||||
int r = s.clueR();
|
int r = s.clueR();
|
||||||
int c = s.clueC();
|
int c = s.clueC();
|
||||||
int d = s.dir();
|
int d = s.dir();
|
||||||
|
|
||||||
List<int[]> cells = new ArrayList<>();
|
List<int[]> cells = new ArrayList<>();
|
||||||
@@ -131,7 +128,7 @@ public final class ExportFormat {
|
|||||||
String direction;
|
String direction;
|
||||||
boolean isReversed = false;
|
boolean isReversed = false;
|
||||||
|
|
||||||
if (d ==2) { // right -> horizontal
|
if (d == 2) { // right -> horizontal
|
||||||
direction = HORIZONTAL;
|
direction = HORIZONTAL;
|
||||||
startRow = cells.get(0)[0];
|
startRow = cells.get(0)[0];
|
||||||
startCol = cells.get(0)[1];
|
startCol = cells.get(0)[1];
|
||||||
@@ -163,12 +160,9 @@ public final class ExportFormat {
|
|||||||
|
|
||||||
return new Placed(
|
return new Placed(
|
||||||
lemma,
|
lemma,
|
||||||
lemma.word(),
|
|
||||||
lemma.clue().toArray(String[]::new),
|
|
||||||
startRow,
|
startRow,
|
||||||
startCol,
|
startCol,
|
||||||
direction,
|
direction,
|
||||||
lemma.word(), // answer
|
|
||||||
arrowRow,
|
arrowRow,
|
||||||
arrowCol,
|
arrowCol,
|
||||||
cells,
|
cells,
|
||||||
@@ -177,21 +171,41 @@ public final class ExportFormat {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pack (r,c) into one long key (handles negatives too)
|
|
||||||
private static long pack(int r, int c) { return (((long) r) << 32) ^ (c & 0xFFFFFFFFL); }
|
private static long pack(int r, int c) { return (((long) r) << 32) ^ (c & 0xFFFFFFFFL); }
|
||||||
/**
|
@Value @Accessors(fluent = true) @AllArgsConstructor
|
||||||
* @param direction "h" | "v"
|
private static class Placed {
|
||||||
* @param cells word cells
|
Lemma lemma;
|
||||||
* @param arrow [arrowRow, arrowCol] */
|
int startRow, startCol;
|
||||||
private record Placed(Lemma lemma, String word, String[] clue, int startRow, int startCol, String direction, String answer, int arrowRow, int arrowCol, List<int[]> cells,
|
String direction;
|
||||||
int[] arrow,
|
int arrowRow, arrowCol;
|
||||||
boolean isReversed) { }
|
List<int[]> cells;
|
||||||
|
int[] arrow;
|
||||||
|
boolean isReversed;
|
||||||
|
}
|
||||||
|
|
||||||
public record Rewards(int coins, int stars, int hints) { }
|
@Value @Accessors(fluent = true) @AllArgsConstructor
|
||||||
|
public static class Rewards {
|
||||||
|
int coins, stars, hints;
|
||||||
|
}
|
||||||
|
|
||||||
/// @param direction "h" | "v"
|
@Value @Accessors(fluent = true) @AllArgsConstructor
|
||||||
public record WordOut(Lemma lemma, String word, String[] clue, int startRow, int startCol, String direction, String answer, int arrowRow, int arrowCol, boolean isReversed,
|
public static class WordOut {
|
||||||
int complex) { }
|
Lemma lemma;
|
||||||
|
int startRow, startCol;
|
||||||
|
String direction;
|
||||||
|
int arrowRow, arrowCol;
|
||||||
|
boolean isReversed;
|
||||||
|
int complex;
|
||||||
|
|
||||||
public record ExportedPuzzle(List<String> gridv2, List<WordOut> words, int difficulty, Rewards rewards) { }
|
public String word() { return lemma().word(); }
|
||||||
|
public ArrayList<String> clue() { return lemma.clue(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value @Accessors(fluent = true) @AllArgsConstructor
|
||||||
|
public static class ExportedPuzzle {
|
||||||
|
List<String> gridv2;
|
||||||
|
WordOut[] words;
|
||||||
|
int difficulty;
|
||||||
|
Rewards rewards;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
import puzzle.SwedishGenerator.PuzzleResult;
|
import puzzle.SwedishGenerator.PuzzleResult;
|
||||||
import puzzle.SwedishGenerator.Rng;
|
import puzzle.SwedishGenerator.Rng;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ import java.util.*;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static puzzle.ExportFormat.*;
|
||||||
import static puzzle.SwedishGenerator.*;
|
import static puzzle.SwedishGenerator.*;
|
||||||
import static puzzle.SwedishGenerator.loadWords;
|
import static puzzle.SwedishGenerator.loadWords;
|
||||||
|
|
||||||
@@ -28,8 +30,8 @@ public class Main {
|
|||||||
static final Path OUTPUT_PATH = PUZZLE_DIR.resolve(FILE_NAME);
|
static final Path OUTPUT_PATH = PUZZLE_DIR.resolve(FILE_NAME);
|
||||||
static final String DATE_STRING = now.toLocalDate().toString();
|
static final String DATE_STRING = now.toLocalDate().toString();
|
||||||
|
|
||||||
|
@Data
|
||||||
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 = 500;
|
public int gens = 500;
|
||||||
@@ -82,11 +84,11 @@ public class Main {
|
|||||||
section("Grid (human)");
|
section("Grid (human)");
|
||||||
System.out.print(indentLines(res.swe().renderHuman(res.filled().grid()), " "));
|
System.out.print(indentLines(res.swe().renderHuman(res.filled().grid()), " "));
|
||||||
|
|
||||||
var exported = ExportFormat.exportFormatFromFilled(res, 1, new ExportFormat.Rewards(50, 2, 1));
|
var exported = exportFormatFromFilled(res, 1, new Rewards(50, 2, 1));
|
||||||
|
|
||||||
section("Clues");
|
section("Clues");
|
||||||
info("status : generating...");
|
info("status : generating...");
|
||||||
info("generatedFor : " + exported.words().size());
|
info("generatedFor : " + exported.words().length);
|
||||||
info("status : done");
|
info("status : done");
|
||||||
|
|
||||||
section("Words");
|
section("Words");
|
||||||
@@ -144,21 +146,20 @@ public class Main {
|
|||||||
|
|
||||||
private static String fmtPoint(int r, int c) { return String.format(Locale.ROOT, "(%d,%d)", r, c); }
|
private static String fmtPoint(int r, int c) { return String.format(Locale.ROOT, "(%d,%d)", r, c); }
|
||||||
|
|
||||||
private static void printWordsTable(List<ExportFormat.WordOut> words) {
|
private static void printWordsTable(WordOut[] words) {
|
||||||
System.out.println(" # WORD CX DIR START ARROW CLUE");
|
System.out.println(" # WORD CX DIR START ARROW CLUE");
|
||||||
var i = 1;
|
for (int j = 0; j < words.length; j++) {
|
||||||
for (var w : words) {
|
var w = words[j];
|
||||||
System.out.printf(
|
System.out.printf(
|
||||||
Locale.ROOT,
|
Locale.ROOT,
|
||||||
" %-2d %-12s %-4s %-3s %-9s %-9s %s%n",
|
" %-2d %-12s %-4s %-3s %-9s %-9s %s%n",
|
||||||
i++,
|
j,
|
||||||
safe(w.word(), 12),
|
safe(w.word(), 12),
|
||||||
safe("" + w.complex(), 4),
|
safe("" + w.complex(), 4),
|
||||||
safe(w.direction(), 3),
|
safe(w.direction(), 3),
|
||||||
fmtPoint(w.startRow(), w.startCol()),
|
fmtPoint(w.startRow(), w.startCol()),
|
||||||
fmtPoint(w.arrowRow(), w.arrowCol()),
|
fmtPoint(w.arrowRow(), w.arrowCol()),
|
||||||
w.clue() == null ? "" : Arrays.toString(w.clue())
|
Arrays.toString(w.lemma().clue().toArray(String[]::new)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +169,7 @@ public class Main {
|
|||||||
return s.substring(0, Math.max(0, max - 1)) + "…";
|
return s.substring(0, Math.max(0, max - 1)) + "…";
|
||||||
}
|
}
|
||||||
|
|
||||||
static String indentLines(String s, String indent) {
|
static String indentLines(String s, String indent) {
|
||||||
if (s == null || s.isEmpty()) return "";
|
if (s == null || s.isEmpty()) return "";
|
||||||
var lines = s.split("\\R", -1);
|
var lines = s.split("\\R", -1);
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
@@ -330,7 +331,7 @@ public class Main {
|
|||||||
|
|
||||||
// ---------------- Export (unchanged logic) ----------------
|
// ---------------- Export (unchanged logic) ----------------
|
||||||
|
|
||||||
private static String toJson(ExportFormat.ExportedPuzzle puzzle, String date, String theme) {
|
private static String toJson(ExportedPuzzle puzzle, String date, String theme) {
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.append("{\n");
|
sb.append("{\n");
|
||||||
sb.append(" \"date\": \"").append(escapeJson(date)).append("\",\n");
|
sb.append(" \"date\": \"").append(escapeJson(date)).append("\",\n");
|
||||||
@@ -349,21 +350,21 @@ public class Main {
|
|||||||
}
|
}
|
||||||
sb.append(" ],\n");
|
sb.append(" ],\n");
|
||||||
sb.append(" \"words\": [\n");
|
sb.append(" \"words\": [\n");
|
||||||
for (var i = 0; i < puzzle.words().size(); i++) {
|
for (var i = 0; i < puzzle.words().length; i++) {
|
||||||
var w = puzzle.words().get(i);
|
var w = puzzle.words()[i];
|
||||||
Arrays.sort(w.clue(), Comparator.comparingInt(String::length));
|
var clues = w. clue().toArray(String[]::new);
|
||||||
|
Arrays.sort(clues, Comparator.comparingInt(String::length));
|
||||||
sb.append(" {\n");
|
sb.append(" {\n");
|
||||||
sb.append(" \"word\": \"").append(escapeJson(w.word())).append("\",\n");
|
sb.append(" \"word\": \"").append(escapeJson(w.word())).append("\",\n");
|
||||||
sb.append(" \"clue\": [").append(Arrays.stream(w.clue()).map(ss -> "\"" + escapeJson(ss) + "\"").collect(Collectors.joining(","))).append("],\n");
|
sb.append(" \"clue\": [").append(Arrays.stream(clues).map(ss -> "\"" + escapeJson(ss) + "\"").collect(Collectors.joining(","))).append("],\n");
|
||||||
sb.append(" \"startRow\": ").append(w.startRow()).append(",\n");
|
sb.append(" \"startRow\": ").append(w.startRow()).append(",\n");
|
||||||
sb.append(" \"startCol\": ").append(w.startCol()).append(",\n");
|
sb.append(" \"startCol\": ").append(w.startCol()).append(",\n");
|
||||||
sb.append(" \"direction\": \"").append(escapeJson(w.direction())).append("\",\n");
|
sb.append(" \"direction\": \"").append(escapeJson(w.direction())).append("\",\n");
|
||||||
sb.append(" \"answer\": \"").append(escapeJson(w.answer())).append("\",\n");
|
|
||||||
sb.append(" \"arrowRow\": ").append(w.arrowRow()).append(",\n");
|
sb.append(" \"arrowRow\": ").append(w.arrowRow()).append(",\n");
|
||||||
sb.append(" \"arrowCol\": ").append(w.arrowCol()).append(",\n");
|
sb.append(" \"arrowCol\": ").append(w.arrowCol()).append(",\n");
|
||||||
sb.append(" \"isReversed\": ").append(w.isReversed()).append("\n");
|
sb.append(" \"isReversed\": ").append(w.isReversed()).append("\n");
|
||||||
sb.append(" }");
|
sb.append(" }");
|
||||||
if (i < puzzle.words().size() - 1) sb.append(",");
|
if (i < puzzle.words().length - 1) sb.append(",");
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
sb.append(" ]\n");
|
sb.append(" ]\n");
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package puzzle;
|
package puzzle;
|
||||||
|
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.*;
|
||||||
import javax.naming.Context;
|
import javax.naming.Context;
|
||||||
import javax.xml.crypto.Data;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -25,7 +26,7 @@ public record SwedishGenerator(int W, int H, int SIZE, int MAX_LEN, int[] buff)
|
|||||||
public static final char C_DASH = '\0';
|
public 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;
|
||||||
|
|
||||||
record nbrs_8(int x, int y) { }
|
static record nbrs_8(int x, int y) { }
|
||||||
|
|
||||||
public SwedishGenerator(int W, int H) { this(W, H, W * H, Math.min(W, H), new int[10000]); }
|
public SwedishGenerator(int W, int H) { this(W, H, W * H, Math.min(W, H), new int[10000]); }
|
||||||
public SwedishGenerator() { this(9, 8); }
|
public SwedishGenerator() { this(9, 8); }
|
||||||
@@ -94,7 +95,7 @@ public record SwedishGenerator(int W, int H, int SIZE, int MAX_LEN, int[] buff)
|
|||||||
|
|
||||||
static final class Rng {
|
static final class Rng {
|
||||||
|
|
||||||
private int x;
|
@Getter private int x;
|
||||||
Rng(int seed) {
|
Rng(int seed) {
|
||||||
var s = seed;
|
var s = seed;
|
||||||
if (s == 0) s = 1;
|
if (s == 0) s = 1;
|
||||||
@@ -117,7 +118,7 @@ public record SwedishGenerator(int W, int H, int SIZE, int MAX_LEN, int[] buff)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)); }
|
||||||
static final record CandidateInfo(int[] indices, int count) { }
|
static record CandidateInfo(int[] indices, int count) { }
|
||||||
|
|
||||||
record Grid(byte[] g, int W) {
|
record Grid(byte[] g, int W) {
|
||||||
|
|
||||||
@@ -506,7 +507,6 @@ public record SwedishGenerator(int W, int H, int SIZE, int MAX_LEN, int[] buff)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dead-end-ish letter cell (3+ walls)
|
// dead-end-ish letter cell (3+ walls)
|
||||||
|
|
||||||
for (var r = 0; r < H; r++)
|
for (var r = 0; r < H; r++)
|
||||||
for (var c = 0; c < W; c++) {
|
for (var c = 0; c < W; c++) {
|
||||||
if (grid.isDigitAt(r, c)) continue;
|
if (grid.isDigitAt(r, c)) continue;
|
||||||
@@ -663,13 +663,10 @@ public record SwedishGenerator(int W, int H, int SIZE, int MAX_LEN, int[] buff)
|
|||||||
pop.sort(Comparator.comparingLong(g -> maskFitness(g, lenCounts)));
|
pop.sort(Comparator.comparingLong(g -> maskFitness(g, lenCounts)));
|
||||||
return pop.get(0);
|
return pop.get(0);
|
||||||
}
|
}
|
||||||
record UndoPlace(int ur, int uc, char up) { }
|
|
||||||
|
|
||||||
// ---------------- Fill (CSP) ----------------
|
// ---------------- Fill (CSP) ----------------
|
||||||
record Undo(UndoPlace[] ur, int n) { }
|
|
||||||
|
|
||||||
|
@Data
|
||||||
public static final class FillStats {
|
public static final class FillStats {
|
||||||
|
|
||||||
public long nodes;
|
public long nodes;
|
||||||
public long backtracks;
|
public long backtracks;
|
||||||
public double seconds;
|
public double seconds;
|
||||||
@@ -680,7 +677,7 @@ public record SwedishGenerator(int W, int H, int SIZE, int MAX_LEN, int[] buff)
|
|||||||
CandidateInfo info,
|
CandidateInfo info,
|
||||||
boolean done) { }
|
boolean done) { }
|
||||||
|
|
||||||
public static final record FillResult(boolean ok,
|
public static record FillResult(boolean ok,
|
||||||
Grid grid,
|
Grid grid,
|
||||||
HashMap<Integer, Lemma> clueMap,
|
HashMap<Integer, Lemma> clueMap,
|
||||||
FillStats stats,
|
FillStats stats,
|
||||||
|
|||||||
Reference in New Issue
Block a user