From f83c07c460b50f2f507a20a322146971fa1219f5 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Fri, 4 Oct 2024 14:50:40 +0200 Subject: [PATCH] Initial commit --- .gitignore | 38 ++++++ .idea/.gitignore | 3 + .idea/encodings.xml | 7 ++ .idea/misc.xml | 14 +++ .idea/uiDesigner.xml | 124 ++++++++++++++++++++ .idea/vcs.xml | 6 + pom.xml | 26 ++++ src/main/java/eu/m724/Crossword.java | 25 ++++ src/main/java/eu/m724/CrosswordBuilder.java | 96 +++++++++++++++ src/main/java/eu/m724/Generator.java | 45 +++++++ src/main/java/eu/m724/Main.java | 20 ++++ src/main/java/eu/m724/PlacedWord.java | 20 ++++ src/main/java/eu/m724/Renderer.java | 29 +++++ src/main/java/eu/m724/Word.java | 17 +++ 14 files changed, 470 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 pom.xml create mode 100644 src/main/java/eu/m724/Crossword.java create mode 100644 src/main/java/eu/m724/CrosswordBuilder.java create mode 100644 src/main/java/eu/m724/Generator.java create mode 100644 src/main/java/eu/m724/Main.java create mode 100644 src/main/java/eu/m724/PlacedWord.java create mode 100644 src/main/java/eu/m724/Renderer.java create mode 100644 src/main/java/eu/m724/Word.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..abb4f2c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a723276 --- /dev/null +++ b/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + eu.m724 + crossword-generator + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + + + + + + org.jfree + org.jfree.svg + 5.0.6 + + + + \ No newline at end of file diff --git a/src/main/java/eu/m724/Crossword.java b/src/main/java/eu/m724/Crossword.java new file mode 100644 index 0000000..b37405c --- /dev/null +++ b/src/main/java/eu/m724/Crossword.java @@ -0,0 +1,25 @@ +package eu.m724; + +/** + * Represents the crossword
+ * A tile is a single letter + * + * @param width Width (x) in tiles + * @param height Height (y) in tiles + * @param solution The solution + * @param words Words of the crossword + */ +public record Crossword( + int width, int height, + String solution, + PlacedWord[] words +) { + /** + * Render as SVG + * + * @return SVG file content + */ + public String render() { + return Renderer.render(this); + } +} diff --git a/src/main/java/eu/m724/CrosswordBuilder.java b/src/main/java/eu/m724/CrosswordBuilder.java new file mode 100644 index 0000000..4a20007 --- /dev/null +++ b/src/main/java/eu/m724/CrosswordBuilder.java @@ -0,0 +1,96 @@ +package eu.m724; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Easily builds {@link Crossword}s
+ * TODO bad javadoc + */ +public class CrosswordBuilder { + private int width, height; + private String solution; + private Set words = new HashSet<>(); + private Set placedWords = new HashSet<>(); + + /** + * Creates a new {@link CrosswordBuilder} + * + * @param width The width of the crossword + * @param height The height of the crossword + * @param solution The solution + */ + public CrosswordBuilder(int width, int height, String solution) { + this.width = width; + this.height = height; + this.solution = solution.toLowerCase(); + System.out.printf("Initialized builder of %dx%d with solution \"%s\"\n", width, height, solution); + } + + /** + * Add a word. + * + * @param word The word + * @param hint the hint / clue + * @return This exact {@link CrosswordBuilder} + */ + public CrosswordBuilder addWord(String word, String hint) { + return addWord(new Word(word.toLowerCase(), hint)); + } + + /** + * Add a word. + * + * @param word The word + * @return This exact {@link CrosswordBuilder} + */ + public CrosswordBuilder addWord(Word word) { + this.words.add(word); + System.out.println("Word added: " + word.word()); + return this; + } + + /** + * Add multiple words. + * + * @param words The words + * @return This exact {@link CrosswordBuilder} + */ + public CrosswordBuilder addWords(Word... words) { + this.words.addAll(Set.of(words)); + System.out.println("Multiple words added: " + String.join(", ", Arrays.stream(words).map(Word::word).toList())); + return this; + } + + public CrosswordBuilder generate() { + System.out.println("Generator invoked"); + placedWords = Generator.generate(width, height, solution, words.toArray(Word[]::new)); + System.out.println("Generator done"); + return this; + } + + /** + * Builds a {@link Crossword} + * + * @throws SizeMismatchException When not all words have been placed. Or too many, for some reason. + * @return The {@link Crossword} + */ + public Crossword build() throws SizeMismatchException { // TODO maybe it should be unchecked + System.out.println("Building"); + // TODO + if (words.size() != placedWords.size()) { + throw new SizeMismatchException("Words: %d, placed: %d".formatted(words.size(), placedWords.size())); + } + return new Crossword(width, height, solution, placedWords.toArray(PlacedWord[]::new)); + } + + /** + * When not all words have been placed. Or too many, for some reason. + */ + public static class SizeMismatchException extends Exception { + public SizeMismatchException(String message) { + super(message); + } + } +} diff --git a/src/main/java/eu/m724/Generator.java b/src/main/java/eu/m724/Generator.java new file mode 100644 index 0000000..2dd78a6 --- /dev/null +++ b/src/main/java/eu/m724/Generator.java @@ -0,0 +1,45 @@ +package eu.m724; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +public class Generator { + public static Set generate(int width, int height, String solution, Word[] words) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + Set placedWords = new HashSet<>(words.length); + + System.out.printf("Generator running with %d words\n", words.length); + + boolean vertical = random.nextBoolean(); + for (int i=0; i placedWords) { + for (PlacedWord placedWord : placedWords) { + if (placedWord == null) continue; + + if (placedWord.vertical()) { + if (placedWord.x() == x) { + if (y >= placedWord.y() && y <= placedWord.y() + placedWord.wordLength() - 1) { + return placedWord; + } + } + } else { + if (placedWord.y() == y) { + if (x >= placedWord.x() && x <= placedWord.y() + placedWord.wordLength() - 1) { + return placedWord; + } + } + } + } + + return null; + } +} diff --git a/src/main/java/eu/m724/Main.java b/src/main/java/eu/m724/Main.java new file mode 100644 index 0000000..8a7790b --- /dev/null +++ b/src/main/java/eu/m724/Main.java @@ -0,0 +1,20 @@ +package eu.m724; + +public class Main { + public static void main(String[] args) throws CrosswordBuilder.SizeMismatchException { + CrosswordBuilder builder = new CrosswordBuilder(20, 20, "ardour") + .addWord("cat", "Furry feline pet") + .addWord("sun", "Bright star in our sky") + .addWord("book", "Bound pages with a story") + .addWord("tree", "Woody plant with branches") + .addWord("blue", "Color of a clear sky") + .addWord("door", "Entrance to a room") + .generate(); + + Crossword crossword = builder.build(); + + System.out.println(crossword.render()); + + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/src/main/java/eu/m724/PlacedWord.java b/src/main/java/eu/m724/PlacedWord.java new file mode 100644 index 0000000..ddcc186 --- /dev/null +++ b/src/main/java/eu/m724/PlacedWord.java @@ -0,0 +1,20 @@ +package eu.m724; + +/** + * Represents a placement of a word + * + * @param x x of the first letter + * @param y y of the first letter + * @param vertical is the word, from the first letter, downwards (true), or rightwards (false) + * @param word the word + */ +public record PlacedWord( + int x, + int y, + boolean vertical, + Word word +) { + public int wordLength() { + return word.word().length(); + } +} diff --git a/src/main/java/eu/m724/Renderer.java b/src/main/java/eu/m724/Renderer.java new file mode 100644 index 0000000..ba7e5d3 --- /dev/null +++ b/src/main/java/eu/m724/Renderer.java @@ -0,0 +1,29 @@ +package eu.m724; + +import org.jfree.svg.SVGGraphics2D; + +import java.awt.*; + +public class Renderer { + public static String render(Crossword crossword) { + SVGGraphics2D g2 = new SVGGraphics2D(crossword.width() * 10, crossword.height() * 10); + g2.setPaint(Color.BLACK); + + for (PlacedWord word : crossword.words()) { + int x = word.x(); + int y = word.y(); + + if (word.vertical()) { + for (int _y=y; _y + * The word should be lowercase. TODO make that not a requirement + * + * @param word the word + * @param hint the hint / clue + */ +public record Word( + String word, + String hint +) { + public int length() { + return word.length(); + } +}