From 50ba53ff44cc91f2a080707593373d5650bfce17 Mon Sep 17 00:00:00 2001 From: Antonio Orta <60408336+19690ao@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:43:19 -0400 Subject: [PATCH 01/77] Region Based Changes (#559) Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> --- .../puzzle/nurikabe/NurikabeUtilities.java | 63 +++++++++++++++++++ .../rules/NoNumberContradictionRule.java | 22 +++---- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java index 34278ff9f..024cf6bb2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java @@ -241,4 +241,67 @@ public static HashMap getWhiteRegionMap(NurikabeBoard boa } return whiteRegionMap; } + + /** + * Gets all the non-black cells connected to the given cell + * + * @param board nurikabe board + * @param center nurikabe cell + * @return a set of all white/numbered cells in the region + */ + public static Set getSurroundedRegionOf(NurikabeBoard board, NurikabeCell center) { + int width = board.getWidth(); + int height = board.getHeight(); + + // Mark all the vertices as not visited(By default + // set as false) + Set visited = new HashSet<>(); + + // Create a queue for BFS + LinkedList queue = new LinkedList<>(); + + // Mark the current node as visited and enqueue it + visited.add(center); + queue.add(center); + + // Set of cells in the current region + Set connected = new HashSet<>(); + + while (queue.size() != 0) { + // Dequeue a vertex from queue and print it + // s is the source node in the graph + NurikabeCell s = queue.poll(); + System.out.print(s + " "); + + // Make a set of all adjacent squares + Set adj = new HashSet<>(); + + Point loc = s.getLocation(); + // First check if the side is on the board + if (loc.x >= 1) { + adj.add(board.getCell(loc.x - 1, loc.y)); + } + if (loc.x < width - 1) { + adj.add(board.getCell(loc.x + 1, loc.y)); + } + if (loc.y >= 1) { + adj.add(board.getCell(loc.x, loc.y - 1)); + } + if (loc.y < height - 1) { + adj.add(board.getCell(loc.x, loc.y + 1)); + } + // Get all adjacent vertices of the dequeued vertex s + // If a adjacent has not been visited, then mark it + // visited and enqueue it + for (NurikabeCell n : adj) { + if (!visited.contains(n) && n.getType() != NurikabeType.BLACK) { + connected.add(n); + visited.add(n); + queue.add(n); + } + } + } + + return connected; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/NoNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/NoNumberContradictionRule.java index 06eb9d2eb..c2752da7a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/NoNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/NoNumberContradictionRule.java @@ -42,20 +42,18 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } -// If the transition creates a room of white cells with no number, a contradiction occurs. - DisjointSets anotherRegion = NurikabeUtilities.getPossibleWhiteRegions(nurikabeBoard); - List> allsets = anotherRegion.getAllSets(); - for (Set s : allsets) { - boolean numberExists = false; - for (NurikabeCell c : s) { - if (c.getType() == NurikabeType.NUMBER) { - numberExists = true; - } - } - if (!numberExists) { - return null; + Set region = NurikabeUtilities.getSurroundedRegionOf(nurikabeBoard, cell); + + boolean numberExists = false; + for (NurikabeCell c : region) { + if (c.getType() == NurikabeType.NUMBER) { + numberExists = true; + break; } } + if (!numberExists) { + return null; + } return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } From f3dbafb2791ed0c1280730649441fb1edbd83345 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:15:00 -0400 Subject: [PATCH 02/77] Short Truth Table Puzzle Editor (#451) * Created files for STT elements * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Created element classes and added placeholder tile images (#452) * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Set the current board on boardView * Fixed typo and turned on STT puzzle editor for testing * Added preliminary valid dimensions checker This will most definitely change in the future, hopefully can change to accept a number of statements * Fixed image file paths * Added ActionListener Allows us to determine what puzzle is selected by the user * Hide rows and columns input for Short Truth Table * Added text area for Short Truth Table * Added scrollbars to show up as needed * Reformatted code * More code reformatting * Even more reformatting * Separate the data from the TextArea into different lines * Did some researching/testing Tested certain variable values with a STT file with no true/false values * Made more progress Added new methods to handle creating Short Truth Table boards from an array of strings * Added a bunch of TODOs - Implemented a couple functions to be used later - Added a bunch of TODO comments for future work * Made some more progress * Implemented abstract methods from PuzzleImporter * Added abstract methods to Fillapix and added other exception reporting * CheckStyle formatting * Removed a TODO comment * Statements show up in puzzle editor Fixed a bug where the importer was not properly being initialized. Statements now show up in the puzzle editor. * Removed empty statements * Changed InvalidFormatException to IllegalArgumentException * Remove argument that has already been caught * Removed elements that will not be used * Added puzzle editor cell clicking functionality * Added ability to toggle certain logical elements * New icons and more functionality implemented * Fixed a bug where spacer rows could be modified * Added statement error checking * Fixed formatting * Only one logic symbol element needed * Changed InputMismatchException to UnsupportedOperationException * Renamed variables to not be STT specific * Finding initial issue and starting fix * Issue is statement copying and modifying * STT exporter now working. Overrode setCell for STTBoard. * Added code documentation * removed testing println() * Gradle fixes * Revert "Merge pull request #545 from MMosley502/puzzle_editor-short_truth_table-file_saving" This reverts commit 2e82547896a7fb3e52ec27634cd8938ef299732f, reversing changes made to beb60a2ab67c8317d404f54e52471739f698bf22. * Saving files now works * Fixed the blank element to be categorized as a placeable element * Fixed a bug where file wouldn't save due to batch grader updates * Reformatted code in STT * Reformatted code again * MORE REFORMATTING Pls like my code CheckStyle --------- Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> --- bin/main/edu/rpi/legup/legup/config | 2 +- puzzles files/shorttruthtable/empty_test.xml | 14 +++ .../edu/rpi/legup/app/GameBoardFacade.java | 84 ++++++++++++-- src/main/java/edu/rpi/legup/model/Puzzle.java | 14 +++ .../edu/rpi/legup/model/PuzzleExporter.java | 1 + .../edu/rpi/legup/model/PuzzleImporter.java | 19 +++- .../java/edu/rpi/legup/model/tree/Tree.java | 1 + .../puzzle/battleship/BattleshipImporter.java | 15 +++ .../puzzle/fillapix/FillapixImporter.java | 15 +++ .../puzzle/heyawake/HeyawakeImporter.java | 15 +++ .../legup/puzzle/lightup/LightUpImporter.java | 15 +++ .../rpi/legup/puzzle/masyu/MasyuImporter.java | 15 +++ .../puzzle/nurikabe/NurikabeImporter.java | 15 +++ .../shorttruthtable/ShortTruthTable.java | 29 ++++- .../shorttruthtable/ShortTruthTableCell.java | 103 +++++++++++++++++- .../ShortTruthTableCellType.java | 1 + .../ShortTruthTableController.java | 2 +- .../ShortTruthTableExporter.java | 9 +- .../ShortTruthTableImporter.java | 80 ++++++++++++-- .../elements/ArgumentElement.java | 9 ++ .../elements/GreenElement.java | 9 ++ .../elements/LogicSymbolElement.java | 9 ++ .../shorttruthtable/elements/RedElement.java | 9 ++ .../elements/UnknownElement.java | 9 ++ .../shorttruthtable_elements_reference_sheet | 6 + ...shorttruthtable_rules_reference_sheet.txt} | 0 .../skyscrapers/SkyscrapersImporter.java | 15 +++ .../legup/puzzle/sudoku/SudokuImporter.java | 15 +++ .../puzzle/treetent/TreeTentImporter.java | 15 +++ .../edu/rpi/legup/ui/CreatePuzzleDialog.java | 79 ++++++++++++-- src/main/java/edu/rpi/legup/ui/HomePanel.java | 27 ++++- .../edu/rpi/legup/ui/PuzzleEditorPanel.java | 14 +++ .../shorttruthtable/tiles/AndOrTile.png | Bin 0 -> 442 bytes .../tiles/ConditionalBiconditionalTile.png | Bin 0 -> 326 bytes .../shorttruthtable/tiles/GreenTile.png | Bin 0 -> 190 bytes .../shorttruthtable/tiles/LetterTile.png | Bin 0 -> 579 bytes .../images/shorttruthtable/tiles/RedTile.png | Bin 0 -> 152 bytes .../shorttruthtable/tiles/UnknownTile.png | Bin 0 -> 9733 bytes src/main/resources/edu/rpi/legup/legup/config | 2 +- 39 files changed, 629 insertions(+), 38 deletions(-) create mode 100644 puzzles files/shorttruthtable/empty_test.xml create mode 100644 src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet rename src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/{shorttruthtable_reference_sheet.txt => shorttruthtable_rules_reference_sheet.txt} (100%) create mode 100644 src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/AndOrTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/ConditionalBiconditionalTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/GreenTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/LetterTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/RedTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/UnknownTile.png diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index bb7da871a..24fdcf365 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -27,7 +27,7 @@ + fileCreationDisabled="false"/> diff --git a/puzzles files/shorttruthtable/empty_test.xml b/puzzles files/shorttruthtable/empty_test.xml new file mode 100644 index 000000000..2d8e4b6c8 --- /dev/null +++ b/puzzles files/shorttruthtable/empty_test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java index 2686086a8..55273ab4f 100644 --- a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java +++ b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java @@ -1,23 +1,22 @@ package edu.rpi.legup.app; +import edu.rpi.legup.history.History; import edu.rpi.legup.history.IHistoryListener; import edu.rpi.legup.history.IHistorySubject; +import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.save.InvalidFileFormatException; +import edu.rpi.legup.ui.LegupUI; import edu.rpi.legup.ui.ProofEditorPanel; import edu.rpi.legup.ui.PuzzleEditorPanel; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXException; -import edu.rpi.legup.save.InvalidFileFormatException; -import edu.rpi.legup.ui.LegupUI; -import edu.rpi.legup.history.History; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -144,6 +143,30 @@ public boolean validateDimensions(String game, int rows, int columns) throws Run } } + /** + * Validates the given text input for the given puzzle + * + * @param game the name of the puzzle + * @param statements an array of statements + * @return true if it is possible to create a board for the given game with the given statements, + * false otherwise + * @throws RuntimeException if any of the input is invalid + */ + public boolean validateTextInput(String game, String[] statements) throws RuntimeException { + String qualifiedClassName = config.getPuzzleClassForName(game); + try { + Class c = Class.forName(qualifiedClassName); + Constructor constructor = c.getConstructor(); + Puzzle puzzle = (Puzzle) constructor.newInstance(); + return puzzle.isValidTextInput(statements); + } + catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | + InstantiationException e) { + LOGGER.error(e); + throw new RuntimeException("Error validating puzzle text input"); + } + } + /** * Loads an empty puzzle * @@ -159,7 +182,6 @@ public void loadPuzzle(String game, int rows, int columns) throws RuntimeExcepti Class c = Class.forName(qualifiedClassName); Constructor cons = c.getConstructor(); Puzzle puzzle = (Puzzle) cons.newInstance(); - setWindowTitle(puzzle.getName(), "New " + puzzle.getName() + " Puzzle"); PuzzleImporter importer = puzzle.getImporter(); if (importer == null) { @@ -167,6 +189,13 @@ public void loadPuzzle(String game, int rows, int columns) throws RuntimeExcepti throw new RuntimeException("Puzzle importer null"); } + // Theoretically, this exception should never be thrown, since LEGUP should not be + // allowing the user to give row/column input for a puzzle that doesn't support it + if (!importer.acceptsRowsAndColumnsInput()) { + throw new IllegalArgumentException(puzzle.getName() + " does not accept rows and columns input"); + } + + setWindowTitle(puzzle.getName(), "New " + puzzle.getName() + " Puzzle"); importer.initializePuzzle(rows, columns); puzzle.initializeView(); @@ -183,6 +212,45 @@ public void loadPuzzle(String game, int rows, int columns) throws RuntimeExcepti } } + public void loadPuzzle(String game, String[] statements) { + String qualifiedClassName = config.getPuzzleClassForName(game); + LOGGER.debug("Loading " + qualifiedClassName); + + try { + Class c = Class.forName(qualifiedClassName); + Constructor cons = c.getConstructor(); + Puzzle puzzle = (Puzzle) cons.newInstance(); + + PuzzleImporter importer = puzzle.getImporter(); + if (importer == null) { + LOGGER.error("Puzzle importer is null"); + throw new RuntimeException("Puzzle importer null"); + } + + // Theoretically, this exception should never be thrown, since LEGUP should not be + // allowing the user to give text input for a puzzle that doesn't support it + if (!importer.acceptsTextInput()) { + throw new IllegalArgumentException(puzzle.getName() + " does not accept text input"); + } + + setWindowTitle(puzzle.getName(), "New " + puzzle.getName() + " Puzzle"); + importer.initializePuzzle(statements); + + puzzle.initializeView(); +// puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); + setPuzzleEditor(puzzle); + } + catch (IllegalArgumentException exception) { + throw new IllegalArgumentException(exception.getMessage()); + } + catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | + IllegalAccessException | InstantiationException e) { + LOGGER.error(e); + throw new RuntimeException("Puzzle creation error"); + } + + } + /** * Loads a puzzle file * diff --git a/src/main/java/edu/rpi/legup/model/Puzzle.java b/src/main/java/edu/rpi/legup/model/Puzzle.java index d25afa2cb..18614131b 100644 --- a/src/main/java/edu/rpi/legup/model/Puzzle.java +++ b/src/main/java/edu/rpi/legup/model/Puzzle.java @@ -204,12 +204,26 @@ public boolean isValidDimensions(int rows, int columns) { return rows > 0 && columns > 0; } + /** + * Checks if the given array of statements is valid text input for the given puzzle + * + * @param statements + * @return + */ + public boolean isValidTextInput(String[] statements) { + return statements.length > 0; + } + /** * Determines if the edu.rpi.legup.puzzle was solves correctly * * @return true if the board was solved correctly, false otherwise */ public boolean isPuzzleComplete() { + if (tree == null) { + return false; + } + boolean isComplete = tree.isValid(); if (isComplete) { for (TreeElement leaf : tree.getLeafTreeElements()) { diff --git a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java index a2f662772..613d2ed1c 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java @@ -29,6 +29,7 @@ public abstract class PuzzleExporter { /** * PuzzleExporter Constructor exports the puzzle object to a file + * * @param puzzle puzzle that is to be exported */ public PuzzleExporter(Puzzle puzzle) { diff --git a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java index c2b5b37fc..327a92773 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -12,10 +12,7 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public abstract class PuzzleImporter { private static final Logger LOGGER = LogManager.getLogger(PuzzleImporter.class.getName()); @@ -24,12 +21,17 @@ public abstract class PuzzleImporter { /** * PuzzleImporter Constructor creates the puzzle object + * * @param puzzle puzzle that is imported */ public PuzzleImporter(Puzzle puzzle) { this.puzzle = puzzle; } + public abstract boolean acceptsRowsAndColumnsInput(); + + public abstract boolean acceptsTextInput(); + /** * Initializes an empty puzzle * @@ -46,6 +48,13 @@ public void initializePuzzle(int rows, int columns) throws RuntimeException { } } + public void initializePuzzle(String[] statements) throws InputMismatchException, IllegalArgumentException { + // Note: Error checking for the statements will be left up to the puzzles that support + // text input. For example, some puzzles may be okay with "blank" statements (Strings with + // length = 0) while others may not. + initializeBoard(statements); + } + /** * Initializes the puzzle attributes * @@ -116,6 +125,8 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { */ public abstract void initializeBoard(Node node) throws InvalidFileFormatException; + public abstract void initializeBoard(String[] statements) throws UnsupportedOperationException, IllegalArgumentException; + /** * Creates the proof for building * diff --git a/src/main/java/edu/rpi/legup/model/tree/Tree.java b/src/main/java/edu/rpi/legup/model/tree/Tree.java index 31ef92359..79c0bcece 100644 --- a/src/main/java/edu/rpi/legup/model/tree/Tree.java +++ b/src/main/java/edu/rpi/legup/model/tree/Tree.java @@ -101,6 +101,7 @@ public Set getLeafTreeElements() { /** * Gets a Set of TreeNodes that are leaf nodes from the sub tree rooted at the specified node + * * @param node node that is input * @return Set of TreeNodes that are leaf nodes from the sub tree */ diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java index aa7209f71..749ceaaa9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java @@ -13,6 +13,16 @@ public BattleshipImporter(Battleship battleShip) { super(battleShip); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -177,4 +187,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { "unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Battleship cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java index 45ad786e8..6c30b2272 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java @@ -13,6 +13,16 @@ public FillapixImporter(Fillapix fillapix) { super(fillapix); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -88,4 +98,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Fillapix Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Fillapix cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java index d09a15389..7527c717f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java @@ -14,6 +14,16 @@ public HeyawakeImporter(Heyawake heyawake) { super(heyawake); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -91,4 +101,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Heyawake Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Hey Awake cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java index fd9fd49e9..7ef24ca69 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java @@ -13,6 +13,16 @@ public LightUpImporter(LightUp lightUp) { super(lightUp); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -102,4 +112,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("lightup Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Light Up cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java index 50bf0c0c7..3e0d328c4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java @@ -13,6 +13,16 @@ public MasyuImporter(Masyu masyu) { super(masyu); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -90,4 +100,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Masyu Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Masyu cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java index 7665a4865..2cbcc9ad0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java @@ -13,6 +13,16 @@ public NurikabeImporter(Nurikabe nurikabe) { super(nurikabe); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -100,4 +110,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("nurikabe Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Nurikabe cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java index 3ce185b6c..e8f9ffc0d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java @@ -25,6 +25,7 @@ public ShortTruthTable() { public void initializeView() { ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) currentBoard; boardView = new ShortTruthTableView(sttBoard); + boardView.setBoard(currentBoard); addBoardListener(boardView); } @@ -48,8 +49,32 @@ public Board generatePuzzle(int difficulty) { * @return true if the given dimensions are valid for Short Truth Table, false otherwise */ public boolean isValidDimensions(int rows, int columns) { - // This is a placeholder, this method needs to be implemented - throw new UnsupportedOperationException(); + // Number of rows must be odd to allow for proper spacing between the statements + if (rows % 2 != 1) { + return false; + } + + return true; + } + + /** + * Determines if the given statements are valid for Short Truth Table + * + * @param statements + * @return true if the statements are valid for Short Truth Table, false otherwise + */ + public boolean isValidTextInput(String[] statements) { + if (statements.length == 0) { + return false; + } + + ShortTruthTableImporter importer = (ShortTruthTableImporter) this.getImporter(); + for (String s : statements) { + if (!importer.validGrammar(s)) { + return false; + } + } + return true; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java index 768b4ed2a..59b5f4272 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java @@ -1,15 +1,17 @@ package edu.rpi.legup.puzzle.shorttruthtable; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; import java.awt.Point; +import java.awt.event.MouseEvent; public class ShortTruthTableCell extends GridCell { //The symbol on the cell - private final char symbol; + private char symbol; //This is a reference to the statement that contains this cell private ShortTruthTableStatement statement; @@ -127,5 +129,104 @@ public ShortTruthTableCell copy() { return copy; } + /** + * Sets the type of this ShortTruthTableCell + * + * @param e element to set the type of this Short Truth Table cell to + */ + @Override + public void setType(Element e, MouseEvent m) { + // Do not allow odd rows to be modified since they are spacer rows + if (this.getLocation().getY() % 2 == 1) { + return; + } + // Red Element + if (e.getElementID().equals("STTT-PLAC-0002")) { + this.data = ShortTruthTableCellType.FALSE; + } + // Green Element + else { + if (e.getElementID().equals("STTT-PLAC-0001")) { + this.data = ShortTruthTableCellType.TRUE; + } + // Unknown Element + else { + if (e.getElementID().equals("STTT-PLAC-0003")) { + this.data = ShortTruthTableCellType.UNKNOWN; + } + // Argument Element + else { + if (e.getElementID().equals("STTT-UNPL-0001")) { + // Prevents non-argument symbols from being changed + if (!(this.symbol >= 'A' && this.symbol <= 'Z')) { + return; + } + + if (m.getButton() == MouseEvent.BUTTON1) { + this.symbol += 1; + if (this.symbol > 'Z') { + this.symbol = 'A'; + } + } + else { + if (m.getButton() == MouseEvent.BUTTON3) { + this.symbol -= 1; + if (this.symbol < 'A') { + this.symbol = 'Z'; + } + } + } + } + // And/Or Element + else { + if (e.getElementID().equals("STTT-UNPL-0002")) { + if (m.getButton() == MouseEvent.BUTTON1) { + if (this.symbol == '^') { + this.symbol = '|'; + } + else { + if (this.symbol == '|') { + this.symbol = '>'; + } + else { + if (this.symbol == '>') { + this.symbol = '-'; + } + else { + if (this.symbol == '-') { + this.symbol = '^'; + } + } + } + } + } + else { + if (m.getButton() == MouseEvent.BUTTON3) { + if (this.symbol == '^') { + this.symbol = '-'; + } + else { + if (this.symbol == '|') { + this.symbol = '^'; + } + else { + if (this.symbol == '>') { + this.symbol = '|'; + } + else { + if (this.symbol == '-') { + this.symbol = '>'; + } + } + } + } + } + } + } + } + } + } + } + } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellType.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellType.java index c01fe74d8..c997faf5f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellType.java @@ -26,6 +26,7 @@ public static ShortTruthTableCellType valueOf(int cellType) { /** * Gets the char value of a cell, Used for debugging + * * @param type cell type input * @return true if value is 1, false if value is 0, ? if value is -1, or blank otherwise */ diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableController.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableController.java index ccf4e9274..bddde44a5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableController.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableController.java @@ -12,7 +12,7 @@ public void changeCell(MouseEvent e, PuzzleElement data) { System.out.println("STTController: Cell change"); - //cast the data to a short truth table cell + // cast the data to a short truth table cell ShortTruthTableCell cell = (ShortTruthTableCell) data; if (e.getButton() == MouseEvent.BUTTON1) { diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableExporter.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableExporter.java index bcb744789..9d6553c7c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; import org.w3c.dom.Document; public class ShortTruthTableExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public ShortTruthTableExporter(ShortTruthTable stt) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - ShortTruthTableBoard board = (ShortTruthTableBoard) puzzle.getTree().getRootNode().getBoard(); + ShortTruthTableBoard board; + if (puzzle.getTree() != null) { + board = (ShortTruthTableBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (ShortTruthTableBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java index cccdbca19..84d04fb45 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java @@ -3,16 +3,15 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.w3c.dom.NamedNodeMap; +import javax.swing.*; import java.awt.*; - -import java.util.List; import java.util.ArrayList; - -import javax.swing.*; +import java.util.LinkedList; +import java.util.List; class ShortTruthTableImporter extends PuzzleImporter { @@ -55,8 +54,8 @@ private List getCells(String statement, int y) { * @return the length, in chars, of the longest statement */ private int parseAllStatementsAndCells(final NodeList statementData, - List> allCells, - List statements) throws InvalidFileFormatException { + List> allCells, + List statements) throws InvalidFileFormatException { int maxStatementLength = 0; @@ -65,7 +64,10 @@ private int parseAllStatementsAndCells(final NodeList statementData, //Get the atributes from the statement i in the file NamedNodeMap attributeList = statementData.item(i).getAttributes(); + String statementRep = attributeList.getNamedItem("representation").getNodeValue(); + System.out.println("STATEMENT REP: " + statementRep); + System.out.println("ROW INDEX: " + attributeList.getNamedItem("row_index").getNodeValue()); //parser time (on statementRep) //if (!validGrammar(statementRep)) throw some error if (!validGrammar(statementRep)) { @@ -85,10 +87,32 @@ private int parseAllStatementsAndCells(final NodeList statementData, } return maxStatementLength; + } + + private int parseAllStatementsAndCells(String[] statementData, + List> allCells, + List statements) throws IllegalArgumentException { + int maxStatementLength = 0; + + for (int i = 0; i < statementData.length; i++) { + if (!validGrammar(statementData[i])) { + JOptionPane.showMessageDialog(null, "ERROR: Invalid file syntax"); + throw new IllegalArgumentException("shorttruthtable importer: invalid sentence syntax"); + } + + //get the cells for the statement + List rowOfCells = getCells(statementData[i], i * 2); + allCells.add(rowOfCells); + statements.add(new ShortTruthTableStatement(statementData[i], rowOfCells)); + + //keep track of the length of the longest statement + maxStatementLength = Math.max(maxStatementLength, statementData[i].length()); + } + return maxStatementLength; } - private boolean validGrammar(String sentence) { + protected boolean validGrammar(String sentence) { int open = 0; int close = 0; char[] valid_characters = new char[]{'^', 'v', '!', '>', '-', '&', '|', '~', '$', '%'}; @@ -217,6 +241,16 @@ private void setGivenCells(ShortTruthTableBoard sttBoard, } + @Override + public boolean acceptsRowsAndColumnsInput() { + return false; + } + + @Override + public boolean acceptsTextInput() { + return true; + } + /** * Creates an empty board for building * @@ -278,9 +312,37 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { catch (NumberFormatException e) { throw new InvalidFileFormatException("short truth table Importer: unknown value where integer expected"); } + } + /** + * Creates the board for building using statements + * + * @param statementInput + * @throws UnsupportedOperationException + * @throws IllegalArgumentException + */ + public void initializeBoard(String[] statementInput) throws UnsupportedOperationException, IllegalArgumentException { + List statementsList = new LinkedList<>(); + for (String s : statementInput) { + if (s.strip().length() > 0) { + statementsList.add(s); + } + } + String[] statementData = statementsList.toArray(new String[statementsList.size()]); - } + if (statementData.length == 0) { + throw new IllegalArgumentException("short truth table Importer: no statements found for board"); + } + // Store all cells and statements + List> allCells = new ArrayList<>(); + List statements = new ArrayList<>(); + // Parse the data + int maxStatementLength = parseAllStatementsAndCells(statementData, allCells, statements); + + // Generate and set the board - don't set given cell values since none are given + ShortTruthTableBoard sttBoard = generateBoard(allCells, statements, maxStatementLength); + puzzle.setCurrentBoard(sttBoard); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java new file mode 100644 index 000000000..9f238a9bf --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.shorttruthtable.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class ArgumentElement extends NonPlaceableElement { + public ArgumentElement() { + super("STTT-UNPL-0001", "Argument Element", "Argument of logic statement element", "edu/rpi/legup/images/shorttruthtable/tiles/LetterTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java new file mode 100644 index 000000000..605f6a207 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.shorttruthtable.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class GreenElement extends PlaceableElement { + public GreenElement() { + super("STTT-PLAC-0001", "Green Element", "A green tile to set certain tiles to true", "edu/rpi/legup/images/shorttruthtable/tiles/GreenTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java new file mode 100644 index 000000000..b2adfddef --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.shorttruthtable.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class LogicSymbolElement extends NonPlaceableElement { + public LogicSymbolElement() { + super("STTT-UNPL-0002", "Logic Symbol Element", "Logic symbol element", "edu/rpi/legup/images/shorttruthtable/tiles/ConditionalBiconditionalTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java new file mode 100644 index 000000000..ecc7d5a02 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.shorttruthtable.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class RedElement extends PlaceableElement { + public RedElement() { + super("STTT-PLAC-0002", "Red Element", "A red tile to set certain tiles to false", "edu/rpi/legup/images/shorttruthtable/tiles/RedTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java new file mode 100644 index 000000000..9a9ab8b84 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.shorttruthtable.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class UnknownElement extends PlaceableElement { + public UnknownElement() { + super("STTT-PLAC-0003", "Unknown Element", "A blank tile", "edu/rpi/legup/images/shorttruthtable/tiles/UnknownTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet new file mode 100644 index 000000000..471631553 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet @@ -0,0 +1,6 @@ +STTT-UNPL-0001 : ArgumentElement +STTT-UNPL-0002 : ConditionalBiconditionalElement + +STTT-PLAC-0001 : GreenElement +STTT-PLAC-0002 : RedElement +STTT-PLAC-0003 : UnknownElement \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/shorttruthtable_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/shorttruthtable_rules_reference_sheet.txt similarity index 100% rename from src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/shorttruthtable_reference_sheet.txt rename to src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/shorttruthtable_rules_reference_sheet.txt diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java index 22af18a4c..6dded9764 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java @@ -13,6 +13,16 @@ public SkyscrapersImporter(Skyscrapers skyscrapers) { super(skyscrapers); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -150,4 +160,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Skyscraper Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Skyscrapers cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java index f77a68d7a..dccec52d4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java @@ -13,6 +13,16 @@ public SudokuImporter(Sudoku sudoku) { super(sudoku); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -112,4 +122,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Sudoku Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Sudoku cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java index e48122a7a..2b4861c9f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java @@ -13,6 +13,16 @@ public TreeTentImporter(TreeTent treeTent) { super(treeTent); } + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + /** * Creates an empty board for building * @@ -174,4 +184,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("TreeTent Importer: unknown value where integer expected"); } } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Tree Tent cannot accept text input"); + } } diff --git a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java index 8e0858b72..fa049ab38 100644 --- a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java +++ b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java @@ -9,35 +9,71 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Arrays; +import java.util.Objects; public class CreatePuzzleDialog extends JDialog { private HomePanel homePanel; private String[] games; private JComboBox gameBox; + private ActionListener gameBoxListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JComboBox comboBox = (JComboBox) e.getSource(); + String puzzleName = (String) comboBox.getSelectedItem(); + if (puzzleName.equals("ShortTruthTable")) { + textInputScrollPane.setVisible(true); + rowsLabel.setVisible(false); + rows.setVisible(false); + columnsLabel.setVisible(false); + columns.setVisible(false); + } + else { + textInputScrollPane.setVisible(false); + rowsLabel.setVisible(true); + rows.setVisible(true); + columnsLabel.setVisible(true); + columns.setVisible(true); + } + } + }; - private JLabel puzzleLabel = new JLabel("Puzzle:"); + private JLabel puzzleLabel; + private JLabel rowsLabel; private JTextField rows; + private JLabel columnsLabel; private JTextField columns; + private JTextArea textArea; + private JScrollPane textInputScrollPane; + private JButton ok = new JButton("Ok"); private ActionListener okButtonListener = new ActionListener() { /** * Attempts to open the puzzle editor interface for the given game with the given dimensions - * @param e the event to be processed + * @param ae the event to be processed */ @Override public void actionPerformed(ActionEvent ae) { String game = Config.convertDisplayNameToClassName((String) gameBox.getSelectedItem()); // Check if all 3 TextFields are filled - if (game.equals("") || rows.getText().equals("") || columns.getText().equals("")) { + if (game.equals("ShortTruthTable") && textArea.getText().equals("")) { + System.out.println("Unfilled fields"); + return; + } + if (!game.equals("ShortTruthTable") && (game.equals("") || rows.getText().equals("") || columns.getText().equals(""))) { System.out.println("Unfilled fields"); return; } try { - homePanel.openEditorWithNewPuzzle(game, Integer.valueOf(rows.getText()), Integer.valueOf(columns.getText())); + if (game.equals("ShortTruthTable")) { + homePanel.openEditorWithNewPuzzle("ShortTruthTable", textArea.getText().split("\n")); + } + else { + homePanel.openEditorWithNewPuzzle(game, Integer.valueOf(rows.getText()), Integer.valueOf(columns.getText())); + } setVisible(false); } catch (IllegalArgumentException e) { @@ -75,6 +111,7 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { Container c = getContentPane(); c.setLayout(null); + puzzleLabel = new JLabel("Puzzle:"); puzzleLabel.setBounds(10, 30, 70, 25); gameBox.setBounds(80, 30, 190, 25); @@ -87,8 +124,8 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { rows = new JTextField(); columns = new JTextField(); - JLabel rowsLabel = new JLabel("Rows:"); - JLabel columnsLabel = new JLabel("Columns:"); + rowsLabel = new JLabel("Rows:"); + columnsLabel = new JLabel("Columns:"); rowsLabel.setBounds(30, 70, 60, 25); columnsLabel.setBounds(30, 95, 60, 25); @@ -102,9 +139,31 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { c.add(rows); c.add(columns); + textArea = new JTextArea(); + textInputScrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + textInputScrollPane.setBounds(10, 70, this.getWidth() - 30, 50); + c.add(textInputScrollPane); + c.add(ok); c.add(cancel); + if (Objects.equals(this.gameBox.getSelectedItem(), "ShortTruthTable")) { + textInputScrollPane.setVisible(true); + rowsLabel.setVisible(false); + rows.setVisible(false); + columnsLabel.setVisible(false); + columns.setVisible(false); + } + else { + textInputScrollPane.setVisible(false); + rowsLabel.setVisible(true); + rows.setVisible(true); + columnsLabel.setVisible(true); + columns.setVisible(true); + } + + ActionListener cursorSelectedGame = CursorController.createListener(this, gameBoxListener); + gameBox.addActionListener(cursorSelectedGame); ActionListener cursorPressedOk = CursorController.createListener(this, okButtonListener); ok.addActionListener(cursorPressedOk); ActionListener cursorPressedCancel = CursorController.createListener(this, cancelButtonListener); @@ -123,7 +182,13 @@ public void actionPerformed(ActionEvent e) { String game = Config.convertDisplayNameToClassName((String) gameBox.getSelectedItem()); try { - this.homePanel.openEditorWithNewPuzzle(game, Integer.valueOf(this.rows.getText()), Integer.valueOf(this.columns.getText())); + if (game.equals("ShortTruthTable")) { + this.homePanel.openEditorWithNewPuzzle("ShortTruthTable", this.textArea.getText().split("\n")); + } + else { + this.homePanel.openEditorWithNewPuzzle(game, Integer.valueOf(this.rows.getText()), Integer.valueOf(this.columns.getText())); + + } this.setVisible(false); } catch (IllegalArgumentException exception) { diff --git a/src/main/java/edu/rpi/legup/ui/HomePanel.java b/src/main/java/edu/rpi/legup/ui/HomePanel.java index f72694cc0..48f0d03d7 100644 --- a/src/main/java/edu/rpi/legup/ui/HomePanel.java +++ b/src/main/java/edu/rpi/legup/ui/HomePanel.java @@ -1,11 +1,9 @@ package edu.rpi.legup.ui; -import edu.rpi.legup.Legup; import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.controller.CursorController; import edu.rpi.legup.save.InvalidFileFormatException; -import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.save.ExportFileException; @@ -29,7 +27,6 @@ import java.io.FileWriter; import java.net.URI; import java.net.URL; -import java.util.Objects; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -640,4 +637,28 @@ public void openEditorWithNewPuzzle(String game, int rows, int columns) throws I this.legupUI.displayPanel(2); this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, rows, columns); } + + /** + * Opens the puzzle editor for the specified game with the given statements + * + * @param game a String containing the name of the game + * @param statements an array of statements + */ + public void openEditorWithNewPuzzle(String game, String[] statements) { + // Validate the text input + GameBoardFacade facade = GameBoardFacade.getInstance(); + boolean isValidTextInput = facade.validateTextInput(game, statements); + if (!isValidTextInput) { + JOptionPane.showMessageDialog(null, + "The input you entered is invalid. Please double check \n" + + "your statements and try again.", + "ERROR: Invalid Text Input", + JOptionPane.ERROR_MESSAGE); + throw new IllegalArgumentException("ERROR: Invalid dimensions given"); + } + + // Set game type on the puzzle editor + this.legupUI.displayPanel(2); + this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, statements); + } } diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java index cce4feec5..90f0c3cdf 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -327,6 +327,20 @@ public void loadPuzzleFromHome(String game, int rows, int columns) throws Illega } } + public void loadPuzzleFromHome(String game, String[] statements) { + GameBoardFacade facade = GameBoardFacade.getInstance(); + try { + facade.loadPuzzle(game, statements); + } + catch (IllegalArgumentException exception) { + throw new IllegalArgumentException(exception.getMessage()); + } + catch (RuntimeException e) { + e.printStackTrace(); + LOGGER.error(e.getMessage()); + } + } + // File opener public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); diff --git a/src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/AndOrTile.png b/src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/AndOrTile.png new file mode 100644 index 0000000000000000000000000000000000000000..9ea93250297e9bc46769cc2b1df83fa1fdfdf6a6 GIT binary patch literal 442 zcmV;r0Y(0aP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf5&!@T5&_cPe*6Fc0Z&OpK~z{r?UFyL zgFq0*CD_J*h2#J^frXyIM#L_cNMRQc3ojrZKu8@@`L9U8KUH$8d9Gxa>^dfS%@ZCQ z_`%Hg&CYyal;H;pqrl_wSOghB29N<{fd3hwC<;`kbX~{luLAhK57+Az^)Pf@hcFCL z&ooU4f`Ij31$Yjj-W+rDqW;`HzdT72ux%Sws}<@~qA23}&1M7V^O@5QhXZ(?$7vq} zP%qDO*lxEIpJEsW*SBrU-cqnTL>s^ZkfWNWv3_ojKn{wUs;Y3m-^V)3GT7~QlbN>x zL_LmUuq=zy2*=}bq7llng!Oto*6(hh0dBV&`Uum=KU>r&Wd3rwpzo+ZfFB~CK-3+_ zf&G5ZX-(5`{VS)_Y24NKJ?g&=AZnCXFHKX!lvI6;>1s;*b3=G^tAk28_ZrvZC;B8MA$B+p3w|yHqTMa~9buXnba%+p9;P6XK zpRK1Hv2=REV-~#!Oie3T?r8m&Hu%%-sLFNdIV<0jd(*}JzSurXRobj`T6+Nl7ZClK z?ht$Z-oK9HENzJ&zNOo!2^`HpV$-uM2lEXSG8IlI&#iZ{%*^CduID~M-Ia|#lP#+3s{dyoCpnV z+%rk#zopr0F1hR+W-In literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/GreenTile.png b/src/main/resources/edu/rpi/legup/images/shorttruthtable/tiles/GreenTile.png new file mode 100644 index 0000000000000000000000000000000000000000..a438e30375f0598657b2a7e3873d3d03655a5b31 GIT binary patch literal 190 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 QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index bb7da871a..24fdcf365 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -27,7 +27,7 @@ + fileCreationDisabled="false"/> From 44ea007be20126cb47e289fd74155d2f5b88c71e Mon Sep 17 00:00:00 2001 From: Rorymar <36866664+Rorymar@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:16:00 -0400 Subject: [PATCH 03/77] Improvement of TouchingTents Tests Improved the Diagonal and Adjacency Tests to verify different orientations all work. --- .../LightInHorizontalPath | 18 +++--- .../LightInVerticalPath | 18 +++--- .../TouchingTentsContradictionRuleTest.java | 58 +++++++++++++++++-- .../TouchingTentsAdjacentAlt | 19 ++++++ .../TouchingTentsDiagonalAlt | 19 ++++++ .../TouchingTentsFull2By2 | 21 +++++++ 6 files changed, 129 insertions(+), 24 deletions(-) create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2 diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath index 633ccc80b..1b4926106 100644 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath +++ b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath index 70419c40c..48aa7010c 100644 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath +++ b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java index 79fc70118..1f66b863b 100644 --- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java @@ -10,13 +10,9 @@ import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.TouchingTentsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; -import java.awt.*; - public class TouchingTentsContradictionRuleTest { private static final TouchingTentsContradictionRule RULE = new TouchingTentsContradictionRule(); @@ -28,8 +24,13 @@ public static void setUp() { treetent = new TreeTent(); } + //DIAGONAL TESTS + /** + * Tests a tent diagonal of orientation T + * T + **/ @Test - public void TouchingTentsContradictionRule_Diagonal() throws InvalidFileFormatException { + public void TouchingTentsContradictionRule_DiagonalUpLeftToDownRight() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonal", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -44,8 +45,33 @@ public void TouchingTentsContradictionRule_Diagonal() throws InvalidFileFormatEx Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); } + /** + * Tests a tent diagonal of orientation T + * T + **/ @Test - public void TouchingTentsContradictionRule_Adjacent() throws InvalidFileFormatException { + public void TouchingTentsContradictionRule_DiagonalDownLeftToUpRight() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition,board.getCell(1,0))); + Assert.assertNull(RULE.checkRuleAt(transition,board.getCell(0,1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + + //ADJACENT TESTS + /** + * Tests a tent adjacent of orientation T + * T + **/ + @Test + public void TouchingTentsContradictionRule_AdjacentVertical() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -59,6 +85,26 @@ public void TouchingTentsContradictionRule_Adjacent() throws InvalidFileFormatEx Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } + + /** + * Tests a tent adjacent of orientation TT + * + **/ + @Test + public void TouchingTentsContradictionRule_AdjacentHorizontal() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } } diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt new file mode 100644 index 000000000..902fb3ee7 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt new file mode 100644 index 000000000..9b35fb998 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2 b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2 new file mode 100644 index 000000000..59805ca35 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2 @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From bcb8629d97adcd839413702045aefae473de0fed Mon Sep 17 00:00:00 2001 From: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:34:47 -0400 Subject: [PATCH 04/77] Have null changes be valid and fix IsolatedBlackContradicitonRule error message (#561) * Get Tests to be called Revert "Create first cypress test template" This reverts commit 3e50909b93b5aa9634cf0d296e9aeff756b0a909. First commit Finish Lightup tests * Add more tests Update TestRunner.java * Somehow ended up in the wrong spot Fix Import * Please let this be the fix Update TreeTransition.java Update TreeTransition.java Update DirectRule.java Check to see which is not correct Update ElementController.java Revert "maybe the null is making it think that it's not valid" This reverts commit 7bf1de0d66ced6749ee37fbb9c252636b2fcdc79. Just trying to change color Revert "Just trying to change color" This reverts commit ec44695ee578d664055d135a668927a0fd900f5d. Revert "maybe the null is making it think that it's not valid" This reverts commit 3f162fbdc32e6fbd23da321a14a6af96f0ff520d. Check to see which is not correct Revert "Check to see which is not correct" This reverts commit 136b0a41b9d103e6f3e9a7f8cd5d970bf76b050b. Update TreeTransition.java Update TreeTransition.java Revert "Update TreeTransition.java" This reverts commit cde45bb9001cfbfa4f6e2a49b4e9990d2fa7ad33. * Fix error with isolated Black Fix error message with isolated black * Removed excess whitespace and imports. Added short JavaDoc for `TestRunner.java` --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet --- .../history/ValidateContradictionRuleCommand.java | 12 ++++++++++-- .../java/edu/rpi/legup/model/rules/DirectRule.java | 14 ++++---------- src/test/java/legup/TestRunner.java | 7 ++++--- src/test/java/legup/TestUtilities.java | 9 --------- 4 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java index c5f8f0831..a0f58a571 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java @@ -86,9 +86,17 @@ public void executeCommand() { } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; - finalTreeElement = transitionView.getChildView().getTreeElement(); + if (transitionView.getChildView() != null) { + finalTreeElement = transitionView.getChildView().getTreeElement(); + } + else { + finalTreeElement = null; + } + } + + if (finalTreeElement != null) { + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); } - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java index 940bbe32f..4acc7573e 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -29,18 +29,12 @@ public DirectRule(String ruleID, String ruleName, String description, String ima */ public String checkRule(TreeTransition transition) { Board finalBoard = transition.getBoard(); - - if (!finalBoard.isModified()) { - return "State must be modified"; + if (transition.getParents().size() != 1 || + transition.getParents().get(0).getChildren().size() != 1) { + return "State must have only 1 parent and 1 child"; } else { - if (transition.getParents().size() != 1 || - transition.getParents().get(0).getChildren().size() != 1) { - return "State must have only 1 parent and 1 child"; - } - else { - return checkRuleRaw(transition); - } + return checkRuleRaw(transition); } } diff --git a/src/test/java/legup/TestRunner.java b/src/test/java/legup/TestRunner.java index 7e2dbe737..9d79c590e 100644 --- a/src/test/java/legup/TestRunner.java +++ b/src/test/java/legup/TestRunner.java @@ -9,6 +9,9 @@ import puzzles.nurikabe.rules.*; import puzzles.treetent.rules.*; +/** + * This class runs all of the tests for the project without needing to run build scripts. + */ public class TestRunner { public static void main(String[] args) { // Battleship Tests @@ -103,6 +106,4 @@ private static void printTestResults(Result result) { System.out.println("All tests passed: " + result.wasSuccessful()); System.out.println(); } - - -} \ No newline at end of file +} diff --git a/src/test/java/legup/TestUtilities.java b/src/test/java/legup/TestUtilities.java index d49529921..9f203b223 100644 --- a/src/test/java/legup/TestUtilities.java +++ b/src/test/java/legup/TestUtilities.java @@ -6,11 +6,6 @@ import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.save.InvalidFileFormatException; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.junit.runner.notification.Failure; -import puzzles.battleship.rules.AdjacentShipsContradictionRuleTest; -import puzzles.battleship.rules.FinishWithShipsDirectRuleTests; public final class TestUtilities { public static void importTestBoard(String fileName, Puzzle puzzle) throws InvalidFileFormatException { @@ -21,8 +16,4 @@ public static void importTestBoard(String fileName, Puzzle puzzle) throws Invali TreeTransition transition = new TreeTransition(rootNode, board); rootNode.getChildren().add(transition); } - - } - - From 8503f38ea86563bb9095bcb58d9c9ed6166619c4 Mon Sep 17 00:00:00 2001 From: Rorymar <36866664+Rorymar@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:37:52 -0400 Subject: [PATCH 05/77] Added Mixed Tests Added tests for a combination of diagonals and adjacents. --- .../TouchingTentsContradictionRuleTest.java | 102 ++++++++++++++++++ .../TouchingTentsMixedDownLeft | 20 ++++ .../TouchingTentsMixedDownRight | 20 ++++ .../TouchingTentsMixedUpLeft | 20 ++++ .../TouchingTentsMixedUpRight | 20 ++++ 5 files changed, 182 insertions(+) create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java index 1f66b863b..aece8eaba 100644 --- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java @@ -105,6 +105,108 @@ public void TouchingTentsContradictionRule_AdjacentHorizontal() throws InvalidFi Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } + //MIXED TESTS + /** + * Tests a tent of orientation TT + * TT + * + **/ + @Test + public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + /** + * Tests a tent of orientation TT + * T + * + **/ + @Test + public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + /** + * Tests a tent of orientation TT + * T + * + **/ + @Test + public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + } + /** + * Tests a tent of orientation T + * TT + * + **/ + @Test + public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + } + /** + * Tests a tent of orientation T + * TT + * + **/ + @Test + public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + } + } diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft new file mode 100644 index 000000000..af4ca0831 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight new file mode 100644 index 000000000..05b7a7667 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft new file mode 100644 index 000000000..b8b81c7b4 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight new file mode 100644 index 000000000..af7fb5df2 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 8b69f1b3fedfab8453be59a6a1278f4f37ab629a Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Fri, 29 Sep 2023 22:04:48 -0400 Subject: [PATCH 06/77] New Nurikabe unit test Wrote a new unit test for the Nurikabe black square contradiction rule. --- .idea/codeStyles/codeStyleConfig.xml | 2 +- .../CannotFillMiddle | 0 .../LightInHorizontalPath | 18 +++++----- .../LightInVerticalPath | 18 +++++----- .../CannotFillCorners | 13 ------- .../CannotFillMiddle | 11 ------ .../BlockEnclosed | 13 ------- .../CompleteBoardBlockEnclosed | 15 -------- .../CornerBlockEnclosed | 11 ------ .../ManyBlocksEnclosed | 17 ---------- .../BlockEnclosed | 13 ------- .../CompleteBoardBlockEnclosed | 15 -------- .../CornerBlockEnclosed | 11 ------ .../ManyBlocksEnclosed | 17 ---------- .../BlackSquareContradictionRuleTest.java | 34 +++++++++++++++++++ .../CornerBlackSquare | 12 +++++++ 16 files changed, 65 insertions(+), 155 deletions(-) delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/CannotFillMiddle delete mode 100644 build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners delete mode 100644 build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed create mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackSquareContradictionRule/CornerBlackSquare diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a179..f23d52492 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/CannotFillMiddle b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/CannotFillMiddle deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath index 633ccc80b..1b4926106 100644 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath +++ b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath index 70419c40c..48aa7010c 100644 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath +++ b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath @@ -1,10 +1,10 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners b/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners deleted file mode 100644 index 38b52f04d..000000000 --- a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle b/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle deleted file mode 100644 index 44086f145..000000000 --- a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed deleted file mode 100644 index a57a2473e..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed deleted file mode 100644 index f48d240f0..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed deleted file mode 100644 index 1a9cd60d9..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed deleted file mode 100644 index 32200d831..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed deleted file mode 100644 index c5760aede..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed deleted file mode 100644 index 88fb0a8f1..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed deleted file mode 100644 index a9a8dc5a0..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed deleted file mode 100644 index e743862eb..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java index 7c0a00dd5..5e1cb659c 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java @@ -25,6 +25,7 @@ public static void setUp() { nurikabe = new Nurikabe(); } + @Test public void BlackSquareContradictionRule_TwoSurroundBlackTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackSquareContradictionRule/SimpleBlackSquare", nurikabe); @@ -53,4 +54,37 @@ public void BlackSquareContradictionRule_TwoSurroundBlackTest() throws InvalidFi } } } + + @Test + public void BlackSquareContradictionRule_CornerSquareTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackSquareContradictionRule/CornerBlackSquare",nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + System.out.println(transition.getRule().getRuleType()); + System.out.println(transition.getRule().getRuleID()); + + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeCell cell1 = board.getCell(0,0); + NurikabeCell cell2 = board.getCell(0,1); + NurikabeCell cell3 = board.getCell(1,0); + NurikabeCell cell4 = board.getCell(1,1); + + Assert.assertNull(RULE.checkContradiction((NurikabeBoard) transition.getBoard())); + for(int i=0; i + + + + + + + + + + + \ No newline at end of file From f1c798576c3b4248ae35971d914d101697a5542b Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 3 Oct 2023 16:33:29 -0400 Subject: [PATCH 07/77] Fixed a bug --- .../rpi/legup/puzzle/battleship/BattleshipExporter.java | 9 ++++++++- .../edu/rpi/legup/puzzle/fillapix/FillapixExporter.java | 9 ++++++++- .../edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.java | 9 ++++++++- .../edu/rpi/legup/puzzle/lightup/LightUpExporter.java | 9 ++++++++- .../java/edu/rpi/legup/puzzle/masyu/MasyuExporter.java | 9 ++++++++- .../legup/puzzle/skyscrapers/SkyscrapersExporter.java | 9 ++++++++- .../java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java | 9 ++++++++- 7 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java index b954a1065..4205d0125 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class BattleshipExporter extends PuzzleExporter { @@ -18,7 +19,13 @@ public BattleshipExporter(Battleship battleShip) { */ @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - BattleshipBoard board = (BattleshipBoard) puzzle.getTree().getRootNode().getBoard(); + BattleshipBoard board; + if (puzzle.getTree() != null) { + board = (BattleshipBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (BattleshipBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java index f7dc9d375..757d14cd8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class FillapixExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public FillapixExporter(Fillapix fillapix) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - FillapixBoard board = (FillapixBoard) puzzle.getTree().getRootNode().getBoard(); + FillapixBoard board; + if (puzzle.getTree() != null) { + board = (FillapixBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (FillapixBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.java index a23273ab4..344a0be92 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class HeyawakeExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public HeyawakeExporter(Heyawake heyawake) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - HeyawakeBoard board = (HeyawakeBoard) puzzle.getTree().getRootNode().getBoard(); + HeyawakeBoard board; + if (puzzle.getTree() != null) { + board = (HeyawakeBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (HeyawakeBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpExporter.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpExporter.java index 78270e112..89024ad6c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class LightUpExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public LightUpExporter(LightUp lightUp) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - LightUpBoard board = (LightUpBoard) puzzle.getTree().getRootNode().getBoard(); + LightUpBoard board; + if (puzzle.getTree() != null) { + board = (LightUpBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (LightUpBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuExporter.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuExporter.java index 471be22f1..e5fb071b4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class MasyuExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public MasyuExporter(Masyu masyu) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - MasyuBoard board = (MasyuBoard) puzzle.getTree().getRootNode().getBoard(); + MasyuBoard board; + if (puzzle.getTree() != null) { + board = (MasyuBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (MasyuBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java index dac09bd16..d5d5a9911 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class SkyscrapersExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public SkyscrapersExporter(Skyscrapers skyscrapers) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - SkyscrapersBoard board = (SkyscrapersBoard) puzzle.getTree().getRootNode().getBoard(); + SkyscrapersBoard board; + if (puzzle.getTree() != null) { + board = (SkyscrapersBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (SkyscrapersBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java index 5950fa7f4..45e320293 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import org.w3c.dom.Document; public class SudokuExporter extends PuzzleExporter { @@ -12,7 +13,13 @@ public SudokuExporter(Sudoku sudoku) { @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - SudokuBoard board = (SudokuBoard) puzzle.getTree().getRootNode().getBoard(); + SudokuBoard board; + if (puzzle.getTree() != null) { + board = (SudokuBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (SudokuBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("size", String.valueOf(board.getSize())); From 473300c6a7b721acca5fce92171eaa5290246ea2 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 3 Oct 2023 16:57:53 -0400 Subject: [PATCH 08/77] Update BlackTile.java Black tile should not be placeable --- .../edu/rpi/legup/puzzle/lightup/elements/BlackTile.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java index b72ae1fab..d3e8cf506 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java @@ -1,9 +1,9 @@ package edu.rpi.legup.puzzle.lightup.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class BlackTile extends PlaceableElement { +public class BlackTile extends NonPlaceableElement { public BlackTile() { - super("LTUP-PLAC-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); + super("LTUP-UNPL-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); } } From 0832621a55f4c96f439dafe159a03bccbc4b6e69 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 3 Oct 2023 17:04:55 -0400 Subject: [PATCH 09/77] Added unknown tile stuff --- .../puzzle/lightup/elements/UnknownTile.java | 9 +++++++++ .../edu/rpi/legup/images/lightup/UnknownTile.png | Bin 0 -> 9733 bytes 2 files changed, 9 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/lightup/UnknownTile.png diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java new file mode 100644 index 000000000..b2b93667a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.lightup.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class UnknownTile extends NonPlaceableElement { + public UnknownTile() { + super("NURI-UNPL-0003", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/lightup/UnknownTile.png"); + } +} diff --git a/src/main/resources/edu/rpi/legup/images/lightup/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/lightup/UnknownTile.png new file mode 100644 index 0000000000000000000000000000000000000000..850fbf127f04020fe83c6a4f6db75078967916ea GIT binary patch literal 9733 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 QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 From 23d6558aa567c50906318f2fc6518a94dd653593 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 3 Oct 2023 17:07:12 -0400 Subject: [PATCH 10/77] ID error --- .../rpi/legup/puzzle/lightup/LightUpCell.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java index 3189eff7a..1cffa7f82 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java @@ -1,8 +1,10 @@ package edu.rpi.legup.puzzle.lightup; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import java.awt.*; +import java.awt.event.MouseEvent; public class LightUpCell extends GridCell { private boolean isLite; @@ -12,6 +14,21 @@ public LightUpCell(int valueInt, Point location) { this.isLite = false; } + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()) { + case "LTUP-PLAC-0001": + this.data = -4; + break; + case "LTUP-UNPL-0002": + this.data = -1; + break; + case "LTUP-UNPL-0003": + this.data = -2; + break; + } + } + public LightUpCellType getType() { switch (data) { case -4: From 145e316dc7f00329b1d287297e562bb341743050 Mon Sep 17 00:00:00 2001 From: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:07:57 -0400 Subject: [PATCH 11/77] Some Fixes to Recently Discussed UX Bugs (#563) * frame and panels default sizes, default pos on screen * hardcoded version number * homepanel default size * set panels' own sizes * some changes * Removed unused import --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Corppet --- src/main/java/edu/rpi/legup/ui/HomePanel.java | 2 +- src/main/java/edu/rpi/legup/ui/LegupUI.java | 7 +++---- src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java | 3 ++- src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java | 3 ++- .../rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/rpi/legup/ui/HomePanel.java b/src/main/java/edu/rpi/legup/ui/HomePanel.java index 48f0d03d7..2270c92b8 100644 --- a/src/main/java/edu/rpi/legup/ui/HomePanel.java +++ b/src/main/java/edu/rpi/legup/ui/HomePanel.java @@ -481,7 +481,7 @@ private void initText() { credits.setFont(new Font("Roboto", Font.PLAIN, 12)); credits.setAlignmentX(Component.CENTER_ALIGNMENT); - JLabel version = new JLabel("Version 3.0.0"); // This should be autochanged in the future + JLabel version = new JLabel("Version 5.1.0"); // This should be autochanged in the future version.setFont(new Font("Roboto", Font.ITALIC, 10)); version.setAlignmentX(Component.CENTER_ALIGNMENT); diff --git a/src/main/java/edu/rpi/legup/ui/LegupUI.java b/src/main/java/edu/rpi/legup/ui/LegupUI.java index 82ef7dc44..452bbb487 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupUI.java +++ b/src/main/java/edu/rpi/legup/ui/LegupUI.java @@ -72,8 +72,6 @@ public LegupUI() { setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH); } - setVisible(true); - this.addWindowListener(this); addKeyListener(new KeyAdapter() { /** @@ -88,8 +86,8 @@ public void keyTyped(KeyEvent e) { super.keyTyped(e); } }); - setLocationRelativeTo(null); setMinimumSize(getPreferredSize()); + setVisible(true); } private void initPanels() { @@ -112,6 +110,7 @@ protected void displayPanel(int option) { panels[option].makeVisible(); this.window.add(panels[option]); pack(); + setLocationRelativeTo(null); revalidate(); repaint(); } @@ -208,4 +207,4 @@ public DynamicView getEditorDynamicBoardView() { public TreePanel getTreePanel() { return getProofEditor().getTreePanel(); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java index 12cd0ca3b..e83f660e7 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -97,6 +97,7 @@ public ProofEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.frame = frame; this.legupUI = legupUI; setLayout(new BorderLayout()); + setPreferredSize(new Dimension(800, 700)); } @Override @@ -343,7 +344,7 @@ public void actionPerformed(ActionEvent e) { about.add(aboutLegup); aboutLegup.addActionListener(l -> { - JOptionPane.showMessageDialog(null, "Version: 2.0.0"); + JOptionPane.showMessageDialog(null, "Version: 5.1.0"); }); about.add(helpLegup); diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java index 90f0c3cdf..2c4f37c85 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -64,6 +64,7 @@ public PuzzleEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.frame = frame; this.legupUI = legupUI; setLayout(new BorderLayout()); + setPreferredSize(new Dimension(800, 700)); } protected void setupContent() { @@ -202,7 +203,7 @@ public void actionPerformed(ActionEvent e) { }); menus[2].add(aboutLegup); aboutLegup.addActionListener(l -> { - JOptionPane.showMessageDialog(null, "Version: 2.0.0"); + JOptionPane.showMessageDialog(null, "Version: 5.1.0"); }); // add menus to menubar for (JMenu menu : menus) { diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java index a2d37e800..0fe03d476 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java @@ -70,7 +70,7 @@ protected boolean shouldRotateTabRuns(int i) { setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); - setPreferredSize(new Dimension(330, 256)); + setPreferredSize(new Dimension(355, 256)); add(tabbedPane); add(status, BorderLayout.SOUTH); From 1357dbd01b0485b11dd2895f2620bf004c1c59cc Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 3 Oct 2023 17:08:05 -0400 Subject: [PATCH 12/77] Oops pushed the wrong file Yeah some tiles work now but this is the ID error --- .../java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java index b2b93667a..6839e70de 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java @@ -4,6 +4,6 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("NURI-UNPL-0003", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/lightup/UnknownTile.png"); + super("LTUP-UNPL-0003", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/lightup/UnknownTile.png"); } } From 964cf7b165d15bfab55764beb75143140cf75c6c Mon Sep 17 00:00:00 2001 From: Rorymar <36866664+Rorymar@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:14:14 -0400 Subject: [PATCH 13/77] Added Tree-Including Tests Added tests to the TouchingTents tests that made sure trees do not trigger this contradiction. --- .../TouchingTentsContradictionRuleTest.java | 32 +++++++++++++++++++ .../TouchingTentsTreeAdjacent | 19 +++++++++++ .../TouchingTentsTreeDiagonal | 19 +++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent create mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java index aece8eaba..764021caf 100644 --- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java @@ -206,6 +206,38 @@ public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatE Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); } + /** + * Tests if tree adjacent triggers a null + */ + @Test + public void TouchingTentsContradictionRule_TreeAdjacent() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNotNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + } + /** + * Tests if tree diagonal triggers a null + */ + @Test + public void TouchingTentsContradictionRule_TreeDiagonal() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNotNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + } } diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent new file mode 100644 index 000000000..4c63558c2 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal new file mode 100644 index 000000000..b2f54aef3 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f3ba447f43ffbdf819596f6ba605091a7e9e6975 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 3 Oct 2023 17:20:04 -0400 Subject: [PATCH 14/77] Number Tile working --- .../rpi/legup/puzzle/lightup/LightUpCell.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java index 1cffa7f82..36e5a5088 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java @@ -26,6 +26,26 @@ public void setType(Element e, MouseEvent m) { case "LTUP-UNPL-0003": this.data = -2; break; + case "LTUP-UNPL-0001": + switch (m.getButton()){ + case MouseEvent.BUTTON1: + if (this.data < 0 || this.data > 3) { + this.data = 0; + } + else { + this.data = this.data + 1; + } + break; + case MouseEvent.BUTTON3: + if (this.data > 0) { + this.data = this.data - 1; + } + else { + this.data = 4; + } + break; + } + break; } } From 18d1ab64cd223aacab4ec583c9d447f49656182c Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Thu, 5 Oct 2023 11:03:46 -0400 Subject: [PATCH 15/77] Added test cases for Black Bottleneck rule Added two new test cases for the Black Bottleneck direct rule. The first test case checks whether bottlenecks can be successfully detected in a corner The second test case intuitively looks like a bottleneck, but is not actually a bottleneck. This is to avoid false positives. --- .../rules/BlackBottleNeckDirectRuleTest.java | 51 +++++++++++++++++++ .../CornerBottleNeck | 11 ++++ .../BlackBottleNeckDirectRule/FalseBottleNeck | 11 ++++ 3 files changed, 73 insertions(+) create mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/CornerBottleNeck create mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/FalseBottleNeck diff --git a/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java index 91dcec802..8ce3db968 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java @@ -54,4 +54,55 @@ public void BlackBottleNeckDirectRule_TwoSurroundBlackTest() throws InvalidFileF } } } + @Test + public void BlackBottleNeckDirectRule_CornerBottleneck() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBottleNeckDirectRule/CornerBottleNeck", nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + NurikabeBoard board = (NurikabeBoard)transition.getBoard(); + NurikabeCell cell = board.getCell(0,1); + cell.setData(NurikabeType.BLACK.toValue()); + + board.addModifiedData(cell); + + Assert.assertNull(RULE.checkRule(transition)); + + for(int i = 0; i < board.getHeight(); i++){ + for(int k = 0; k < board.getWidth(); k++){ + Point point = new Point(k, i); + if(point.equals(cell.getLocation())){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void BlackBottleNeckDirectRule_FalseBottleneck() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBottleNeckDirectRule/FalseBottleNeck", nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + NurikabeBoard board = (NurikabeBoard)transition.getBoard(); + NurikabeCell cell = board.getCell(0,1); + cell.setData(NurikabeType.BLACK.toValue()); + + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + + for(int i = 0; i < board.getHeight(); i++){ + for(int k = 0; k < board.getWidth(); k++){ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } } + + diff --git a/src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/CornerBottleNeck b/src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/CornerBottleNeck new file mode 100644 index 000000000..a9065ea9d --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/CornerBottleNeck @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/FalseBottleNeck b/src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/FalseBottleNeck new file mode 100644 index 000000000..3e3d7610a --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/BlackBottleNeckDirectRule/FalseBottleNeck @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From f05479d3d4ee89e01fa35d2f840ae80789acb6d2 Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Thu, 5 Oct 2023 11:40:00 -0400 Subject: [PATCH 16/77] Added comments and fixed black square corner test case Added comments to the various test cases, and fixed the corner test for the black square contradiction rule. --- .../rules/BlackBottleNeckDirectRuleTest.java | 12 ++++++++++++ .../BlackSquareContradictionRuleTest.java | 19 ++++++++++--------- .../CornerBlackSquare | 8 ++++---- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java index 8ce3db968..3fe58d164 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackBottleNeckDirectRuleTest.java @@ -27,6 +27,9 @@ public static void setUp() { nurikabe = new Nurikabe(); } + /** + * Tests the Black BottleNeck direct rule for a bottleneck in the center of the board + */ @Test public void BlackBottleNeckDirectRule_TwoSurroundBlackTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBottleNeckDirectRule/SimpleBlackBottleNeck", nurikabe); @@ -54,6 +57,11 @@ public void BlackBottleNeckDirectRule_TwoSurroundBlackTest() throws InvalidFileF } } } + + + /** + * Tests the Black BottleNeck direct rule for a bottleneck in the corner of the board + */ @Test public void BlackBottleNeckDirectRule_CornerBottleneck() throws InvalidFileFormatException{ TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBottleNeckDirectRule/CornerBottleNeck", nurikabe); @@ -82,6 +90,10 @@ public void BlackBottleNeckDirectRule_CornerBottleneck() throws InvalidFileForma } } + + /** + * Tests the Black BottleNeck direct rule for a false bottleneck + */ @Test public void BlackBottleNeckDirectRule_FalseBottleneck() throws InvalidFileFormatException{ TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBottleNeckDirectRule/FalseBottleNeck", nurikabe); diff --git a/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java index 5e1cb659c..f688918e8 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackSquareContradictionRuleTest.java @@ -25,7 +25,9 @@ public static void setUp() { nurikabe = new Nurikabe(); } - + /** + * Tests the Black Square contradiction rule for a black square in the middle of the board + */ @Test public void BlackSquareContradictionRule_TwoSurroundBlackTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackSquareContradictionRule/SimpleBlackSquare", nurikabe); @@ -55,29 +57,28 @@ public void BlackSquareContradictionRule_TwoSurroundBlackTest() throws InvalidFi } } + /** + * Tests the Black Square contradiction rule for a square in the corner of the board + */ @Test public void BlackSquareContradictionRule_CornerSquareTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackSquareContradictionRule/CornerBlackSquare",nurikabe); TreeNode rootNode = nurikabe.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - System.out.println(transition.getRule().getRuleType()); - System.out.println(transition.getRule().getRuleID()); NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - NurikabeCell cell1 = board.getCell(0,0); - NurikabeCell cell2 = board.getCell(0,1); - NurikabeCell cell3 = board.getCell(1,0); - NurikabeCell cell4 = board.getCell(1,1); + NurikabeCell cell1 = board.getCell(2,2); + NurikabeCell cell2 = board.getCell(2,3); + NurikabeCell cell3 = board.getCell(3,2); + NurikabeCell cell4 = board.getCell(3,3); Assert.assertNull(RULE.checkContradiction((NurikabeBoard) transition.getBoard())); for(int i=0; i - - - - + + + + From 4e1c003159234872858945aa060f2989ce1bac26 Mon Sep 17 00:00:00 2001 From: Rorymar <36866664+Rorymar@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:41:05 -0400 Subject: [PATCH 17/77] 2x2 NoTreeForTent Basic Tests Added Files for NoTreeForTent Tests, and test cases for the basic 2x2 Tests. Also fixed some spacing in the TouchingTents Test File. --- .../NoTreeForTentContradictionRuleTest.java | 76 +++++++++++++++++-- .../TouchingTentsContradictionRuleTest.java | 17 +++-- .../NoTreeForTent3x3 | 0 .../NoTreeForTentDiagonals | 0 .../NoTreeForTentJustTent | 0 .../NoTreeForTentNE | 21 +++++ .../{NoTreeForTent => NoTreeForTentNW} | 0 .../NoTreeForTentSE | 21 +++++ .../NoTreeForTentSW | 21 +++++ .../NoTreeForTentYesTree | 0 10 files changed, 144 insertions(+), 12 deletions(-) create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE rename src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/{NoTreeForTent => NoTreeForTentNW} (100%) create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW create mode 100644 src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree diff --git a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java index c3003cef1..4e83c8778 100644 --- a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java @@ -10,13 +10,9 @@ import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.NoTreeForTentContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; -import java.awt.*; - public class NoTreeForTentContradictionRuleTest { private static final NoTreeForTentContradictionRule RULE = new NoTreeForTentContradictionRule(); @@ -28,9 +24,13 @@ public static void setUp() { treetent = new TreeTent(); } + /** + * @throws InvalidFileFormatException + * Tests if, in a 2x2 Grid, a Tent in the NW corner has no adjacent trees + */ @Test - public void NoTreeForTentContradictionRule_() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent", treetent); + public void NoTreeForTentContradictionRule_NW() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -43,5 +43,69 @@ public void NoTreeForTentContradictionRule_() throws InvalidFileFormatException Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } + + /** + * @throws InvalidFileFormatException + * Tests if, in a 2x2 Grid, a Tent in the NE corner has no adjacent trees + */ + @Test + public void NoTreeForTentContradictionRule_NE() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + + /** + * @throws InvalidFileFormatException + * Tests if, in a 2x2 Grid, a Tent in the NW corner has no adjacent trees + */ + @Test + public void NoTreeForTentContradictionRule_SW() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + + /** + * @throws InvalidFileFormatException + * Tests if, in a 2x2 Grid, a Tent in the SE corner has no adjacent trees + */ + @Test + public void NoTreeForTentContradictionRule_SE() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + //Center of 3x3 Board + //Test with a board with Trees all diagonal + //Test with a board with Trees all adjacent (should assertNotNull) + //Test with a board with Tents all adjacent } diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java index 764021caf..f48afe5d7 100644 --- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java @@ -26,6 +26,7 @@ public static void setUp() { //DIAGONAL TESTS /** + * @throws InvalidFileFormatException * Tests a tent diagonal of orientation T * T **/ @@ -46,6 +47,7 @@ public void TouchingTentsContradictionRule_DiagonalUpLeftToDownRight() throws In } /** + * @throws InvalidFileFormatException * Tests a tent diagonal of orientation T * T **/ @@ -67,6 +69,7 @@ public void TouchingTentsContradictionRule_DiagonalDownLeftToUpRight() throws In //ADJACENT TESTS /** + * @throws InvalidFileFormatException * Tests a tent adjacent of orientation T * T **/ @@ -87,8 +90,8 @@ public void TouchingTentsContradictionRule_AdjacentVertical() throws InvalidFile } /** + * @throws InvalidFileFormatException * Tests a tent adjacent of orientation TT - * **/ @Test public void TouchingTentsContradictionRule_AdjacentHorizontal() throws InvalidFileFormatException { @@ -107,9 +110,9 @@ public void TouchingTentsContradictionRule_AdjacentHorizontal() throws InvalidFi } //MIXED TESTS /** + * @throws InvalidFileFormatException * Tests a tent of orientation TT * TT - * **/ @Test public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormatException { @@ -127,9 +130,9 @@ public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormat Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } /** + * @throws InvalidFileFormatException * Tests a tent of orientation TT * T - * **/ @Test public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatException { @@ -147,9 +150,9 @@ public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatExce Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } /** + * @throws InvalidFileFormatException * Tests a tent of orientation TT * T - * **/ @Test public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatException { @@ -167,9 +170,9 @@ public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatExc Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); } /** + * @throws InvalidFileFormatException * Tests a tent of orientation T * TT - * **/ @Test public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatException { @@ -187,9 +190,9 @@ public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatEx Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); } /** + * @throws InvalidFileFormatException * Tests a tent of orientation T * TT - * **/ @Test public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatException { @@ -207,6 +210,7 @@ public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatE Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); } /** + * @throws InvalidFileFormatException * Tests if tree adjacent triggers a null */ @Test @@ -223,6 +227,7 @@ public void TouchingTentsContradictionRule_TreeAdjacent() throws InvalidFileForm Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); } /** + * @throws InvalidFileFormatException * Tests if tree diagonal triggers a null */ @Test diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE new file mode 100644 index 000000000..80ffb9525 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW similarity index 100% rename from src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent rename to src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE new file mode 100644 index 000000000..c2fc5206d --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW new file mode 100644 index 000000000..8e8249b27 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree new file mode 100644 index 000000000..e69de29bb From b603f1a837562fb35ed83dc7c059596d95c43c44 Mon Sep 17 00:00:00 2001 From: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:45:17 -0400 Subject: [PATCH 18/77] Update Exporter (#627) * Update Exporter * Delete Test_Save --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- .../skyscrapers/SkyscrapersCellFactory.java | 2 +- .../puzzle/skyscrapers/SkyscrapersExporter.java | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java index d276c8bff..da6899f75 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java @@ -35,7 +35,7 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat if (x >= width || y >= height) { throw new InvalidFileFormatException("TreeTent Factory: cell location out of bounds"); } - if (value < 0 || value > 3) { + if (value < 0 || value > width) { throw new InvalidFileFormatException("TreeTent Factory: cell unknown value"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java index d5d5a9911..155a1466c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java @@ -22,8 +22,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { } org.w3c.dom.Element boardElement = newDocument.createElement("board"); - boardElement.setAttribute("width", String.valueOf(board.getWidth())); - boardElement.setAttribute("height", String.valueOf(board.getHeight())); + boardElement.setAttribute("size", String.valueOf(board.getWidth())); org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); for (PuzzleElement puzzleElement : board.getPuzzleElements()) { @@ -37,20 +36,20 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element axisEast = newDocument.createElement("axis"); axisEast.setAttribute("side", "east"); - for (SkyscrapersClue clue : board.getEastClues()) { + for (int i=0; i Date: Fri, 6 Oct 2023 17:43:40 -0400 Subject: [PATCH 19/77] Improved NoTreeForTent Tests Added more complex 3x3 tests to the NoTreeForTents. Fixed an error with the 2x2 Tests --- .../NoTreeForTentContradictionRuleTest.java | 112 ++++++++++++++++-- .../NoTreeForTent3x3 | 28 +++++ .../NoTreeForTentDiagonals | 28 +++++ .../NoTreeForTentJustTent | 28 +++++ .../NoTreeForTentYesTree | 28 +++++ 5 files changed, 216 insertions(+), 8 deletions(-) diff --git a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java index 4e83c8778..2ad2ac90e 100644 --- a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java @@ -59,8 +59,8 @@ public void NoTreeForTentContradictionRule_NE() throws InvalidFileFormatExceptio Assert.assertNull(RULE.checkContradiction(board)); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } @@ -79,8 +79,8 @@ public void NoTreeForTentContradictionRule_SW() throws InvalidFileFormatExceptio Assert.assertNull(RULE.checkContradiction(board)); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } @@ -103,9 +103,105 @@ public void NoTreeForTentContradictionRule_SE() throws InvalidFileFormatExceptio Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } - //Center of 3x3 Board - //Test with a board with Trees all diagonal - //Test with a board with Trees all adjacent (should assertNotNull) - //Test with a board with Tents all adjacent + + /** + * @throws InvalidFileFormatException + * Tests if, in a 3x3 Grid with no trees, a Tent in the center cell has no adjacent trees + */ + @Test + public void NoTreeForTentContradictionRule_3x3() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2))); + } + + /** + * @throws InvalidFileFormatException + * Tests if, in a 3x3 Grid with diagonal trees, a Tent in the center cell has no adjacent trees + */ + @Test + public void NoTreeForTentContradictionRule_3x3WithDiagonalTrees() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2))); + } + + /** + * @throws InvalidFileFormatException + * Tests if, in a 3x3 Grid with an adjacent tree, test does not assert null. + */ + @Test + public void NoTreeForTentContradictionRule_YesTree() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNotNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2))); + } + + /** + * @throws InvalidFileFormatException + * Tests if, in a 3x3 Grid with touching tents, a Tent in the center cell has no adjacent trees + */ + @Test + public void NoTreeForTentContradictionRule_JustTent() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2))); + } } diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 index e69de29bb..9c5e81936 100644 --- a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals index e69de29bb..ad086bbc1 100644 --- a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent index e69de29bb..93aaf451f 100644 --- a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree index e69de29bb..f46e4b5a5 100644 --- a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree +++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From ef1be26288e796673f4106da7265b22758290d50 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:32:31 -0400 Subject: [PATCH 20/77] Create run-tests.yml --- .github/workflows/run-tests.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/run-tests.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 000000000..0cce6a9f1 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,26 @@ +name: Java CI with Gradle + +on: + push: + branches: + - master, dev + pull_request: + branches: + - master, dev + +jobs: + test: + runs-on: ubuntu-latest, windows-latest, macos-latest + + steps: + - name: Check Out Code + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v2 + with: + java-version: 11 + + - name: Run Gradle Build and Tests + run: | + ./gradlew clean build test From 20ff3dc954221919c23bc639759a89c0a7b0c89a Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:34:52 -0400 Subject: [PATCH 21/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 44 +++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 0cce6a9f1..068f3831e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -3,14 +3,16 @@ name: Java CI with Gradle on: push: branches: - - master, dev + - master + - dev pull_request: branches: - - master, dev + - master + - dev jobs: - test: - runs-on: ubuntu-latest, windows-latest, macos-latest + linux-build-and-test: + runs-on: ubuntu-latest steps: - name: Check Out Code @@ -21,6 +23,38 @@ jobs: with: java-version: 11 - - name: Run Gradle Build and Tests + - name: Run Gradle Build and Tests on Linux run: | ./gradlew clean build test + + windows-build-and-test: + runs-on: windows-latest + + steps: + - name: Check Out Code + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v2 + with: + java-version: 11 + + - name: Run Gradle Build and Tests on Windows + run: | + .\gradlew clean build test + + macos-build-and-test: + runs-on: macos-latest + + steps: + - name: Check Out Code + uses: actions/checkout@v2 + + - name: Set up JDK + uses: actions/setup-java@v2 + with: + java-version: 11 + + - name: Run Gradle Build and Tests on macOS + run: | + ./gradlew clean build test \ No newline at end of file From 4df83139bc8188309437b23437e16c184e4cf574 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:37:50 -0400 Subject: [PATCH 22/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 068f3831e..527302973 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,4 +1,4 @@ -name: Java CI with Gradle +name: Run JUnit Tests on: push: @@ -11,50 +11,50 @@ on: - dev jobs: - linux-build-and-test: + test: runs-on: ubuntu-latest steps: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK + - name: Set up JDK and Gradle on Linux uses: actions/setup-java@v2 with: java-version: 11 - - name: Run Gradle Build and Tests on Linux + - name: Run JUnit Tests on Linux run: | - ./gradlew clean build test + ./gradlew test - windows-build-and-test: - runs-on: windows-latest + test-macos: + runs-on: macos-latest steps: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK + - name: Set up JDK and Gradle on macOS uses: actions/setup-java@v2 with: java-version: 11 - - name: Run Gradle Build and Tests on Windows + - name: Run JUnit Tests on macOS run: | - .\gradlew clean build test + ./gradlew test - macos-build-and-test: - runs-on: macos-latest + test-windows: + runs-on: windows-latest steps: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK + - name: Set up JDK and Gradle on Windows uses: actions/setup-java@v2 with: java-version: 11 - - name: Run Gradle Build and Tests on macOS + - name: Run JUnit Tests on Windows run: | - ./gradlew clean build test \ No newline at end of file + .\gradlew.bat test \ No newline at end of file From 60c99db64326d9f9534d3575e04740c47dbee37c Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:41:27 -0400 Subject: [PATCH 23/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 527302973..3d18134c4 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,7 +11,7 @@ on: - dev jobs: - test: + ubuntu-test: runs-on: ubuntu-latest steps: @@ -22,6 +22,7 @@ jobs: uses: actions/setup-java@v2 with: java-version: 11 + distribution: 'adopt' - name: Run JUnit Tests on Linux run: | @@ -38,6 +39,7 @@ jobs: uses: actions/setup-java@v2 with: java-version: 11 + distribution: 'adopt' - name: Run JUnit Tests on macOS run: | @@ -54,6 +56,7 @@ jobs: uses: actions/setup-java@v2 with: java-version: 11 + distribution: 'adopt' - name: Run JUnit Tests on Windows run: | From 84517f8b99d4010689627bc57d0407f08801851c Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:44:15 -0400 Subject: [PATCH 24/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 36 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3d18134c4..45ecbd346 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -18,11 +18,16 @@ jobs: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK and Gradle on Linux - uses: actions/setup-java@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 with: java-version: 11 - distribution: 'adopt' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build -x test - name: Run JUnit Tests on Linux run: | @@ -35,11 +40,16 @@ jobs: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK and Gradle on macOS - uses: actions/setup-java@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 with: java-version: 11 - distribution: 'adopt' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build -x test - name: Run JUnit Tests on macOS run: | @@ -52,11 +62,19 @@ jobs: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK and Gradle on Windows - uses: actions/setup-java@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 with: java-version: 11 - distribution: 'adopt' + + - name: Grant execute permission for gradlew + run: | + gradlew.bat + echo "gradlew.bat granted execute permission" + + - name: Build with Gradle + run: | + gradlew.bat build -x test - name: Run JUnit Tests on Windows run: | From 57532c4ad9559b9bbe9dc669fcfe3b7ce8b65735 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:47:38 -0400 Subject: [PATCH 25/77] Windows things --- .github/workflows/run-tests.yml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 45ecbd346..1a22769f7 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -62,19 +62,11 @@ jobs: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - name: Set up JDK and Gradle on Windows + uses: actions/setup-java@v2 with: java-version: 11 - - - name: Grant execute permission for gradlew - run: | - gradlew.bat - echo "gradlew.bat granted execute permission" - - - name: Build with Gradle - run: | - gradlew.bat build -x test + distribution: 'adopt' # Specify the distribution, e.g., 'adopt', 'adopt@1.11', etc. - name: Run JUnit Tests on Windows run: | From aac1ed4b76fec617e808993bf21e49115bf32a7a Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 13:58:20 -0400 Subject: [PATCH 26/77] Added print messages --- .github/workflows/run-tests.yml | 40 ++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1a22769f7..d81bafc36 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -29,9 +29,19 @@ jobs: - name: Build with Gradle run: ./gradlew build -x test - - name: Run JUnit Tests on Linux + - name: Run JUnit Tests run: | - ./gradlew test + test_output=$(./gradlew test --quiet | grep -E "tests? passed|tests? failed") + echo "$test_output" + tests_run=$(echo "$test_output" | grep -Eo "[0-9]+" | head -n 1) + tests_passed=$(echo "$test_output" | grep -Eo "[0-9]+" | tail -n 1) + tests_failed=$((tests_run - tests_passed)) + echo "Tests Run: $tests_run" + echo "Tests Passed: $tests_passed" + echo "Tests Failed: $tests_failed" + if [[ $tests_failed -gt 0 ]]; then + exit 1 + fi test-macos: runs-on: macos-latest @@ -53,7 +63,17 @@ jobs: - name: Run JUnit Tests on macOS run: | - ./gradlew test + test_output=$(./gradlew test --quiet | grep -E "tests? passed|tests? failed") + echo "$test_output" + tests_run=$(echo "$test_output" | grep -Eo "[0-9]+" | head -n 1) + tests_passed=$(echo "$test_output" | grep -Eo "[0-9]+" | tail -n 1) + tests_failed=$((tests_run - tests_passed)) + echo "Tests Run: $tests_run" + echo "Tests Passed: $tests_passed" + echo "Tests Failed: $tests_failed" + if [[ $tests_failed -gt 0 ]]; then + exit 1 + fi test-windows: runs-on: windows-latest @@ -66,8 +86,18 @@ jobs: uses: actions/setup-java@v2 with: java-version: 11 - distribution: 'adopt' # Specify the distribution, e.g., 'adopt', 'adopt@1.11', etc. + distribution: 'adopt' - name: Run JUnit Tests on Windows run: | - .\gradlew.bat test \ No newline at end of file + test_output=$(./gradlew.bat test --quiet | grep -E "tests? passed|tests? failed") + echo "$test_output" + tests_run=$(echo "$test_output" | grep -Eo "[0-9]+" | head -n 1) + tests_passed=$(echo "$test_output" | grep -Eo "[0-9]+" | tail -n 1) + tests_failed=$((tests_run - tests_passed)) + echo "Tests Run: $tests_run" + echo "Tests Passed: $tests_passed" + echo "Tests Failed: $tests_failed" + if [[ $tests_failed -gt 0 ]]; then + exit 1 + fi \ No newline at end of file From 25d559af58e87f3a367ff57197e6c0a8d3555a9c Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:00:23 -0400 Subject: [PATCH 27/77] More Windows things --- .github/workflows/run-tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d81bafc36..897894c1c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -98,6 +98,7 @@ jobs: echo "Tests Run: $tests_run" echo "Tests Passed: $tests_passed" echo "Tests Failed: $tests_failed" - if [[ $tests_failed -gt 0 ]]; then + + if %tests_failed% gtr 0 ( exit 1 - fi \ No newline at end of file + ) \ No newline at end of file From 48479a6d06cd1f0e834f03b298ce5675f58be1b4 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:02:00 -0400 Subject: [PATCH 28/77] Debugging --- .github/workflows/run-tests.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 897894c1c..18ba0c425 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -29,6 +29,24 @@ jobs: - name: Build with Gradle run: ./gradlew build -x test + - name: Debug Info + run: | + echo "Java version:" + java -version + echo "Gradle version:" + ./gradlew --version + shell: bash + + - name: Run JUnit Tests on Linux + run: | + ./gradlew test + test_result=$? + if [ $test_result -ne 0 ]; then + echo "Tests failed with exit code $test_result" + exit $test_result + fi + shell: bash + - name: Run JUnit Tests run: | test_output=$(./gradlew test --quiet | grep -E "tests? passed|tests? failed") From 28c06bc3bb60b576ecf41119f9381e0b79fee424 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:07:39 -0400 Subject: [PATCH 29/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 58 +++++++-------------------------- 1 file changed, 12 insertions(+), 46 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 18ba0c425..21704c965 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -29,35 +29,12 @@ jobs: - name: Build with Gradle run: ./gradlew build -x test - - name: Debug Info - run: | - echo "Java version:" - java -version - echo "Gradle version:" - ./gradlew --version - shell: bash - - name: Run JUnit Tests on Linux run: | - ./gradlew test - test_result=$? - if [ $test_result -ne 0 ]; then + ./gradlew test --tests '*' | grep -E 'Test:|PASSED|FAILED' + test_result=${PIPESTATUS[0]} + if [[ $test_result -ne 0 ]]; then echo "Tests failed with exit code $test_result" - exit $test_result - fi - shell: bash - - - name: Run JUnit Tests - run: | - test_output=$(./gradlew test --quiet | grep -E "tests? passed|tests? failed") - echo "$test_output" - tests_run=$(echo "$test_output" | grep -Eo "[0-9]+" | head -n 1) - tests_passed=$(echo "$test_output" | grep -Eo "[0-9]+" | tail -n 1) - tests_failed=$((tests_run - tests_passed)) - echo "Tests Run: $tests_run" - echo "Tests Passed: $tests_passed" - echo "Tests Failed: $tests_failed" - if [[ $tests_failed -gt 0 ]]; then exit 1 fi @@ -81,15 +58,10 @@ jobs: - name: Run JUnit Tests on macOS run: | - test_output=$(./gradlew test --quiet | grep -E "tests? passed|tests? failed") - echo "$test_output" - tests_run=$(echo "$test_output" | grep -Eo "[0-9]+" | head -n 1) - tests_passed=$(echo "$test_output" | grep -Eo "[0-9]+" | tail -n 1) - tests_failed=$((tests_run - tests_passed)) - echo "Tests Run: $tests_run" - echo "Tests Passed: $tests_passed" - echo "Tests Failed: $tests_failed" - if [[ $tests_failed -gt 0 ]]; then + ./gradlew test --tests '*' | grep -E 'Test:|PASSED|FAILED' + test_result=${PIPESTATUS[0]} + if [[ $test_result -ne 0 ]]; then + echo "Tests failed with exit code $test_result" exit 1 fi @@ -108,15 +80,9 @@ jobs: - name: Run JUnit Tests on Windows run: | - test_output=$(./gradlew.bat test --quiet | grep -E "tests? passed|tests? failed") - echo "$test_output" - tests_run=$(echo "$test_output" | grep -Eo "[0-9]+" | head -n 1) - tests_passed=$(echo "$test_output" | grep -Eo "[0-9]+" | tail -n 1) - tests_failed=$((tests_run - tests_passed)) - echo "Tests Run: $tests_run" - echo "Tests Passed: $tests_passed" - echo "Tests Failed: $tests_failed" - - if %tests_failed% gtr 0 ( + ./gradlew.bat test --tests '*' | findstr /C:"Test:" /C:"PASSED" /C:"FAILED" + test_result=${PIPESTATUS[0]} + if [[ $test_result -ne 0 ]]; then + echo "Tests failed with exit code $test_result" exit 1 - ) \ No newline at end of file + fi From 7377a15ce91224eca96585f82f8683f667a9396b Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:13:11 -0400 Subject: [PATCH 30/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 21704c965..cdc519538 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -31,12 +31,7 @@ jobs: - name: Run JUnit Tests on Linux run: | - ./gradlew test --tests '*' | grep -E 'Test:|PASSED|FAILED' - test_result=${PIPESTATUS[0]} - if [[ $test_result -ne 0 ]]; then - echo "Tests failed with exit code $test_result" - exit 1 - fi + ./gradlew test --info test-macos: runs-on: macos-latest @@ -58,12 +53,7 @@ jobs: - name: Run JUnit Tests on macOS run: | - ./gradlew test --tests '*' | grep -E 'Test:|PASSED|FAILED' - test_result=${PIPESTATUS[0]} - if [[ $test_result -ne 0 ]]; then - echo "Tests failed with exit code $test_result" - exit 1 - fi + ./gradlew test --info test-windows: runs-on: windows-latest @@ -80,9 +70,4 @@ jobs: - name: Run JUnit Tests on Windows run: | - ./gradlew.bat test --tests '*' | findstr /C:"Test:" /C:"PASSED" /C:"FAILED" - test_result=${PIPESTATUS[0]} - if [[ $test_result -ne 0 ]]; then - echo "Tests failed with exit code $test_result" - exit 1 - fi + .\gradlew.bat test --info \ No newline at end of file From 49fb14f9aa83c148abaf742b4e5334c9e6b4fcb1 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:27:18 -0400 Subject: [PATCH 31/77] Maybe this will work now? --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index fafa54cac..f838f7ade 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,12 @@ mainClassName = 'edu.rpi.legup.Legup' sourceCompatibility = 11 +sourceSets { + test { + java.srcDirs = ['src/test/java'] + } +} + dependencies { implementation 'org.jetbrains:annotations:20.1.0' implementation 'org.jetbrains:annotations:20.1.0' From cfeee6aa5264c2ca8523d9854645edc3b603128b Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:31:17 -0400 Subject: [PATCH 32/77] Didn't work --- build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build.gradle b/build.gradle index f838f7ade..fafa54cac 100644 --- a/build.gradle +++ b/build.gradle @@ -13,12 +13,6 @@ mainClassName = 'edu.rpi.legup.Legup' sourceCompatibility = 11 -sourceSets { - test { - java.srcDirs = ['src/test/java'] - } -} - dependencies { implementation 'org.jetbrains:annotations:20.1.0' implementation 'org.jetbrains:annotations:20.1.0' From 7b49c11b00566cfe86b645fa6ede9d1856bc4164 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:33:58 -0400 Subject: [PATCH 33/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index cdc519538..d4ac97181 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -31,7 +31,7 @@ jobs: - name: Run JUnit Tests on Linux run: | - ./gradlew test --info + ./gradlew test --info --tests "*" test-macos: runs-on: macos-latest @@ -53,7 +53,7 @@ jobs: - name: Run JUnit Tests on macOS run: | - ./gradlew test --info + ./gradlew test --info --tests "*" test-windows: runs-on: windows-latest @@ -70,4 +70,4 @@ jobs: - name: Run JUnit Tests on Windows run: | - .\gradlew.bat test --info \ No newline at end of file + .\gradlew.bat test --info --tests "*" \ No newline at end of file From 4029b7c798f0967ba7047c8bbd1313ff85f011e9 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:39:13 -0400 Subject: [PATCH 34/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d4ac97181..9c3804aa2 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -31,7 +31,7 @@ jobs: - name: Run JUnit Tests on Linux run: | - ./gradlew test --info --tests "*" + ./gradlew test --tests "*" test-macos: runs-on: macos-latest From 19f0c26a52e1a674d3d9235345c932e2606ae16e Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:42:49 -0400 Subject: [PATCH 35/77] Create DummyTest.java For debugging purposes --- src/test/java/DummyTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/test/java/DummyTest.java diff --git a/src/test/java/DummyTest.java b/src/test/java/DummyTest.java new file mode 100644 index 000000000..e7b9f7eb9 --- /dev/null +++ b/src/test/java/DummyTest.java @@ -0,0 +1,10 @@ +import org.junit.Test; +import static org.junit.Assert.fail; + +public class DummyTest { + + @Test + public void testFailure() { + fail("This test intentionally fails. Your JUnit configuration is working if you see this message."); + } +} From f2b1cfaa461895f7c59bee6e5224921d42b05246 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:44:55 -0400 Subject: [PATCH 36/77] Added another dummy test --- .../puzzles/shorttruthtable/rules/DummyTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/DummyTest.java diff --git a/src/test/java/puzzles/shorttruthtable/rules/DummyTest.java b/src/test/java/puzzles/shorttruthtable/rules/DummyTest.java new file mode 100644 index 000000000..4d8106f86 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/DummyTest.java @@ -0,0 +1,13 @@ +package puzzles.shorttruthtable.rules; + +import org.junit.Test; + +import static org.junit.Assert.fail; + +public class DummyTest { + + @Test + public void testFailure() { + fail("This test intentionally fails. Your JUnit configuration is working if you see this message."); + } +} From 7fd577b89422da5e280bfc713d2b78711ef03b26 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:46:43 -0400 Subject: [PATCH 37/77] Update run-tests.yml --- .github/workflows/run-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9c3804aa2..3282dfddc 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -31,7 +31,7 @@ jobs: - name: Run JUnit Tests on Linux run: | - ./gradlew test --tests "*" + ./gradlew test test-macos: runs-on: macos-latest @@ -53,7 +53,7 @@ jobs: - name: Run JUnit Tests on macOS run: | - ./gradlew test --info --tests "*" + ./gradlew test --info test-windows: runs-on: windows-latest @@ -70,4 +70,4 @@ jobs: - name: Run JUnit Tests on Windows run: | - .\gradlew.bat test --info --tests "*" \ No newline at end of file + .\gradlew.bat test --info \ No newline at end of file From 8675598712dd9d3826303ec7d7d0753e53212915 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:48:55 -0400 Subject: [PATCH 38/77] Update run-tests.yml Get rid of all this info --- .github/workflows/run-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3282dfddc..bed9d7ad2 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -53,7 +53,7 @@ jobs: - name: Run JUnit Tests on macOS run: | - ./gradlew test --info + ./gradlew test test-windows: runs-on: windows-latest @@ -70,4 +70,4 @@ jobs: - name: Run JUnit Tests on Windows run: | - .\gradlew.bat test --info \ No newline at end of file + .\gradlew.bat test \ No newline at end of file From 4bd17879d144fb513073e1f888a0e6a62bc4c6e8 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Tue, 10 Oct 2023 14:52:56 -0400 Subject: [PATCH 39/77] Deleted the dummy tests --- src/test/java/DummyTest.java | 10 ---------- .../puzzles/shorttruthtable/rules/DummyTest.java | 13 ------------- 2 files changed, 23 deletions(-) delete mode 100644 src/test/java/DummyTest.java delete mode 100644 src/test/java/puzzles/shorttruthtable/rules/DummyTest.java diff --git a/src/test/java/DummyTest.java b/src/test/java/DummyTest.java deleted file mode 100644 index e7b9f7eb9..000000000 --- a/src/test/java/DummyTest.java +++ /dev/null @@ -1,10 +0,0 @@ -import org.junit.Test; -import static org.junit.Assert.fail; - -public class DummyTest { - - @Test - public void testFailure() { - fail("This test intentionally fails. Your JUnit configuration is working if you see this message."); - } -} diff --git a/src/test/java/puzzles/shorttruthtable/rules/DummyTest.java b/src/test/java/puzzles/shorttruthtable/rules/DummyTest.java deleted file mode 100644 index 4d8106f86..000000000 --- a/src/test/java/puzzles/shorttruthtable/rules/DummyTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package puzzles.shorttruthtable.rules; - -import org.junit.Test; - -import static org.junit.Assert.fail; - -public class DummyTest { - - @Test - public void testFailure() { - fail("This test intentionally fails. Your JUnit configuration is working if you see this message."); - } -} From b718165e1d13711e9239aab4da6fed1139c510b1 Mon Sep 17 00:00:00 2001 From: Jacob Long <123578010+jac-oblong@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:49:14 -0400 Subject: [PATCH 40/77] Fillapix (#569) * rework fill-a-pix UI pictures to make clearer * allow right click to cycle backwards through cell states * Rename file * create isComplete utility, begin implementation of CompleteClue case rule * find number of completion cases for a rule * create icons for more fill-a-pix rules * implement max possible number of cases * fillapix utility for finding all combinations * implement CompleteClue case rule, still some bugs * potentially shorten time taken for calculating combinations, catch some null reference bugs * make sure cell has empty adjacent cells before adding it to CompleteClue caseBoard * combine similar rules into one * begin work on verifying cases * implement CompleteClueCaseRule * fix typo * initial version of MirrorDirectRule * prevent trying to access out of board bounds * further changing of contradictions to stop errors * more stopping of accessing non-existent data * remove duplicate methods * complete mirror direct rule * add another utility * move method of FillapixUtilities * add TouchingSides direct rule * Rename a file, complete touching corners rule * touch up some basic rule pictures * add puzzle, rename puzzle * implement nontouching shared direct rule * implementing fillapix puzzle editor * comply with style guide * make fillapix boards editable through puzzle editor * better handling of whether or not a cell is numbered * give cells in blank board default number * remove debug print * register the FillapixView that is created * fix setting elements in fillapix puzzle editor * finally fix fillapix puzzle editor * remove data when setting fillapix cell to empty in puzzle editor * remove debug print * add ability to save puzzles created in fillapix puzzle editor * add more fillapix puzzles * comply with checkstyle * Changed max cases to 10 Talked to Bram and decided to up the limit to 10. --------- Co-authored-by: Jacob Long Co-authored-by: Corppet Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> --- bin/main/edu/rpi/legup/legup/config | 4 +- .../FillapixAdvanced10x10_1 | 48 ++++ .../FillapixBasic10x10_1} | 0 .../FillapixAdvanced15x15_1 | 91 +++++++ .../15x15 Fillapix Basic/FillapixBasic15x15_1 | 111 +++++++++ .../FillapixAdvanced20x20_1 | 141 +++++++++++ .../20x20 Fillapix Basic/FillapixBasic20x20_1 | 159 ++++++++++++ .../20x20 Fillapix Basic/FillapixBasic20x20_2 | 161 ++++++++++++ .../legup/history/AutoCaseRuleCommand.java | 6 + .../rpi/legup/puzzle/fillapix/Fillapix.java | 5 +- .../legup/puzzle/fillapix/FillapixBoard.java | 3 + .../legup/puzzle/fillapix/FillapixCell.java | 56 ++++- .../fillapix/FillapixCellController.java | 23 +- .../puzzle/fillapix/FillapixElementView.java | 2 +- .../puzzle/fillapix/FillapixExporter.java | 3 +- .../puzzle/fillapix/FillapixImporter.java | 11 + .../puzzle/fillapix/FillapixUtilities.java | 191 +++++++++++++- .../puzzle/fillapix/elements/BlackTile.java | 9 + .../puzzle/fillapix/elements/NumberTile.java | 27 ++ .../puzzle/fillapix/elements/UnknownTile.java | 9 + .../puzzle/fillapix/elements/WhiteTile.java | 9 + .../fillapix_elements_reference_sheet.txt | 5 + .../fillapix/rules/BlackOrWhiteCaseRule.java | 4 +- .../rules/FinishWithBlackDirectRule.java | 15 +- .../rules/FinishWithWhiteDirectRule.java | 17 +- .../fillapix/rules/MirrorDirectRule.java | 103 ++++++++ .../rules/NonTouchingSharedDirectRule.java | 99 ++++++++ .../fillapix/rules/SatisfyClueCaseRule.java | 234 ++++++++++++++++++ .../TooFewBlackCellsContradictionRule.java | 33 +-- .../TooManyBlackCellsContradictionRule.java | 29 +-- .../rules/TouchingCornersDirectRule.java | 108 ++++++++ .../rules/TouchingSidesDirectRule.java | 115 +++++++++ .../rules/fillapix_reference_sheet.txt | 6 +- .../images/fillapix/cases/BlackOrWhite.png | Bin 717 -> 722 bytes .../images/fillapix/cases/SatisfyClue.png | Bin 0 -> 1789 bytes .../contradictions/TooFewBlackCells.png | Bin 1054 -> 13581 bytes .../contradictions/TooManyBlackCells.png | Bin 1076 -> 1238 bytes .../images/fillapix/rules/FinishWithBlack.png | Bin 1825 -> 14647 bytes .../images/fillapix/rules/FinishWithWhite.png | Bin 1801 -> 13654 bytes .../legup/images/fillapix/rules/Mirror.png | Bin 0 -> 1233 bytes .../fillapix/rules/NontouchingShared.png | Bin 0 -> 5094 bytes .../images/fillapix/rules/TouchingCorners.png | Bin 0 -> 5335 bytes .../images/fillapix/rules/TouchingSides.png | Bin 0 -> 5255 bytes .../legup/images/fillapix/tiles/BlackTile.png | Bin 0 -> 9543 bytes .../images/fillapix/tiles/NumberTile.png | Bin 0 -> 10067 bytes .../images/fillapix/tiles/UnknownTile.png | Bin 0 -> 9733 bytes .../legup/images/fillapix/tiles/WhiteTile.png | Bin 0 -> 9700 bytes src/main/resources/edu/rpi/legup/legup/config | 4 +- 48 files changed, 1768 insertions(+), 73 deletions(-) create mode 100644 puzzles files/fillapix/10x10 Fillapix Advanced/FillapixAdvanced10x10_1 rename puzzles files/fillapix/{10x10 Fillapix Very Easy/03010000074 => 10x10 Fillapix Basic/FillapixBasic10x10_1} (100%) create mode 100644 puzzles files/fillapix/15x15 Fillapix Advanced/FillapixAdvanced15x15_1 create mode 100644 puzzles files/fillapix/15x15 Fillapix Basic/FillapixBasic15x15_1 create mode 100644 puzzles files/fillapix/20x20 Fillaix Advanced/FillapixAdvanced20x20_1 create mode 100644 puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_1 create mode 100644 puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_2 create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/cases/SatisfyClue.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/rules/Mirror.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/rules/NontouchingShared.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingCorners.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingSides.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/tiles/BlackTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/tiles/NumberTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/tiles/UnknownTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/fillapix/tiles/WhiteTile.png diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index 24fdcf365..19e63a2a3 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -7,7 +7,7 @@ + fileCreationDisabled="false"/> - \ No newline at end of file + diff --git a/puzzles files/fillapix/10x10 Fillapix Advanced/FillapixAdvanced10x10_1 b/puzzles files/fillapix/10x10 Fillapix Advanced/FillapixAdvanced10x10_1 new file mode 100644 index 000000000..3706caf3b --- /dev/null +++ b/puzzles files/fillapix/10x10 Fillapix Advanced/FillapixAdvanced10x10_1 @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/fillapix/10x10 Fillapix Very Easy/03010000074 b/puzzles files/fillapix/10x10 Fillapix Basic/FillapixBasic10x10_1 similarity index 100% rename from puzzles files/fillapix/10x10 Fillapix Very Easy/03010000074 rename to puzzles files/fillapix/10x10 Fillapix Basic/FillapixBasic10x10_1 diff --git a/puzzles files/fillapix/15x15 Fillapix Advanced/FillapixAdvanced15x15_1 b/puzzles files/fillapix/15x15 Fillapix Advanced/FillapixAdvanced15x15_1 new file mode 100644 index 000000000..954d6d409 --- /dev/null +++ b/puzzles files/fillapix/15x15 Fillapix Advanced/FillapixAdvanced15x15_1 @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/fillapix/15x15 Fillapix Basic/FillapixBasic15x15_1 b/puzzles files/fillapix/15x15 Fillapix Basic/FillapixBasic15x15_1 new file mode 100644 index 000000000..d17e230f0 --- /dev/null +++ b/puzzles files/fillapix/15x15 Fillapix Basic/FillapixBasic15x15_1 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/fillapix/20x20 Fillaix Advanced/FillapixAdvanced20x20_1 b/puzzles files/fillapix/20x20 Fillaix Advanced/FillapixAdvanced20x20_1 new file mode 100644 index 000000000..3582ba947 --- /dev/null +++ b/puzzles files/fillapix/20x20 Fillaix Advanced/FillapixAdvanced20x20_1 @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_1 b/puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_1 new file mode 100644 index 000000000..59d8a2114 --- /dev/null +++ b/puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_1 @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_2 b/puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_2 new file mode 100644 index 000000000..af3f936f8 --- /dev/null +++ b/puzzles files/fillapix/20x20 Fillapix Basic/FillapixBasic20x20_2 @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index b42f73c9a..331a3dddf 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -24,6 +24,8 @@ public class AutoCaseRuleCommand extends PuzzleCommand { private List caseTrans; + private static final int MAX_CASES = 10; + /** * AutoCaseRuleCommand Constructor creates a command for validating a case rule * @@ -112,6 +114,10 @@ public String getErrorString() { return "The selection must produce at least one case"; } + if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() > MAX_CASES) { + return "The selection can produce a max of " + MAX_CASES + " cases"; + } + return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java index ca10ab266..ef78f66aa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java @@ -26,6 +26,8 @@ public Fillapix() { @Override public void initializeView() { boardView = new FillapixView((FillapixBoard) currentBoard); + boardView.setBoard(currentBoard); + addBoardListener(boardView); } @Override @@ -42,8 +44,7 @@ public Board generatePuzzle(int difficulty) { * @return true if the given dimensions are valid for Fillapix, false otherwise */ public boolean isValidDimensions(int rows, int columns) { - // This is a placeholder, this method needs to be implemented - throw new UnsupportedOperationException(); + return super.isValidDimensions(rows, columns); } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixBoard.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixBoard.java index 24615db5c..67987a6fd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixBoard.java @@ -53,6 +53,9 @@ public int getNumCells(FillapixCell cell, FillapixCellType type) { int numCells = 0; for (int i = -1; i < 2; i++) { for (int j = -1; j < 2; j++) { + if (loc.x + i > dimension.width || loc.y + j > dimension.height) { + continue; + } FillapixCell c = getCell(loc.x + i, loc.y + j); if (c != null && c.getType() == type) { numCells++; diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java index 00f66708d..5e6d4b9ed 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java @@ -1,10 +1,13 @@ package edu.rpi.legup.puzzle.fillapix; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import java.awt.*; +import java.awt.event.MouseEvent; +import java.util.Objects; -public class FillapixCell extends GridCell { +public class FillapixCell extends GridCell implements Comparable { public static final int DEFAULT_VALUE = 10; @@ -33,10 +36,44 @@ public FillapixCellType getType() { } } - public void setType(FillapixCellType type) { + public void setCellType(FillapixCellType type) { data = type.value * 100 + (data % 100); } + @Override + public void setType(Element e, MouseEvent m) { + switch(e.getElementID()) { + case "FPIX-PLAC-0001": + this.setCellType(FillapixCellType.BLACK); + break; + case "FPIX-PLAC-0002": + this.setCellType(FillapixCellType.WHITE); + break; + case "FPIX-UNPL-0001": + int n = this.getNumber(); + switch (m.getButton()) { + case MouseEvent.BUTTON1: + n++; + break; + case MouseEvent.BUTTON3: + n--; + break; + } + if (n > 9) { + n = 0; + } + if (n < 0) { + n = 9; + } + this.setNumber(n); + break; + default: + this.setCellType(FillapixCellType.UNKNOWN); + this.data = -1; + break; + } + } + /** * Performs a deep copy on the FillapixCell * @@ -49,4 +86,19 @@ public FillapixCell copy() { cell.setModifiable(isModifiable); return cell; } + + public boolean equals(FillapixCell otherCell) { +// return this.location.equals(otherCell.location) && this.index == otherCell.index && this.data == otherCell.data; + //return this.index == otherCell.index && this.data == otherCell.data; + //return this.index == otherCell.index; + return this.location.x == otherCell.location.x && this.location.y == otherCell.location.y; + } + + public int compareTo(FillapixCell otherCell) { + return this.index - otherCell.index; + } + + public int hashCode() { + return Objects.hash(this.index); + } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellController.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellController.java index 62d14630d..df3bba403 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellController.java @@ -15,15 +15,32 @@ public void changeCell(MouseEvent e, PuzzleElement puzzleElement) { } else { if (cell.getType() == FillapixCellType.UNKNOWN) { - cell.setType(FillapixCellType.BLACK); + cell.setCellType(FillapixCellType.BLACK); } else { if (cell.getType() == FillapixCellType.BLACK) { - cell.setType(FillapixCellType.WHITE); + cell.setCellType(FillapixCellType.WHITE); } else { if (cell.getType() == FillapixCellType.WHITE) { - cell.setType(FillapixCellType.UNKNOWN); + cell.setCellType(FillapixCellType.UNKNOWN); + } + } + } + } + } + else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getType() == FillapixCellType.UNKNOWN) { + cell.setCellType(FillapixCellType.WHITE); + } + else { + if (cell.getType() == FillapixCellType.BLACK) { + cell.setCellType(FillapixCellType.UNKNOWN); + } + else { + if (cell.getType() == FillapixCellType.WHITE) { + cell.setCellType(FillapixCellType.BLACK); } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixElementView.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixElementView.java index 00d0a6807..93fa0d451 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixElementView.java @@ -47,7 +47,7 @@ public void drawElement(Graphics2D graphics2D) { break; } graphics2D.fillRect(location.x, location.y, size.width, size.height); - if (cell.getNumber() != -1) { + if (cell.getNumber() >= 0 && cell.getNumber() < 10) { graphics2D.setColor(type == FillapixCellType.WHITE ? BLACK_COLOR : WHITE_COLOR); graphics2D.setFont(FONT); FontMetrics metrics = graphics2D.getFontMetrics(FONT); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java index 757d14cd8..2fa8fb64c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixExporter.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; import org.w3c.dom.Document; public class FillapixExporter extends PuzzleExporter { @@ -16,7 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { FillapixBoard board; if (puzzle.getTree() != null) { board = (FillapixBoard) puzzle.getTree().getRootNode().getBoard(); - } + } else { board = (FillapixBoard) puzzle.getBoardView().getBoard(); } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java index 6c30b2272..5213e7139 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java @@ -32,7 +32,18 @@ public boolean acceptsTextInput() { */ @Override public void initializeBoard(int rows, int columns) { + FillapixBoard fillapixBoard = new FillapixBoard(columns, rows); + for (int y = 0; y < rows; y++) { + for (int x = 0; x < columns; x++) { + FillapixCell cell = new FillapixCell(FillapixCellType.UNKNOWN.value, new Point(x, y)); + cell.setIndex(y * columns + x); + cell.setNumber(FillapixCell.DEFAULT_VALUE); + cell.setModifiable(true); + fillapixBoard.setCell(x, y, cell); + } + } + puzzle.setCurrentBoard(fillapixBoard); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java index 2098676b5..0df99b820 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java @@ -1,23 +1,206 @@ package edu.rpi.legup.puzzle.fillapix; +import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.puzzle.fillapix.rules.TooFewBlackCellsContradictionRule; import edu.rpi.legup.puzzle.fillapix.rules.TooManyBlackCellsContradictionRule; +import java.awt.*; +import java.util.ArrayList; + public class FillapixUtilities { public static boolean isForcedBlack(FillapixBoard board, FillapixCell cell) { TooFewBlackCellsContradictionRule tooManyBlackCells = new TooFewBlackCellsContradictionRule(); FillapixBoard whiteCaseBoard = board.copy(); FillapixCell whiteCell = (FillapixCell) whiteCaseBoard.getPuzzleElement(cell); - whiteCell.setType(FillapixCellType.WHITE); - return tooManyBlackCells.checkContradictionAt(whiteCaseBoard, cell) != null; + whiteCell.setCellType(FillapixCellType.WHITE); + ArrayList adjCells = getAdjacentCells(whiteCaseBoard, whiteCell); + for (FillapixCell adjCell : adjCells) { + if (tooManyBlackCells.checkContradictionAt(whiteCaseBoard, adjCell) == null) { + return true; + } + } + return false; } public static boolean isForcedWhite(FillapixBoard board, FillapixCell cell) { TooManyBlackCellsContradictionRule tooManyBlackCells = new TooManyBlackCellsContradictionRule(); FillapixBoard blackCaseBoard = board.copy(); FillapixCell blackCell = (FillapixCell) blackCaseBoard.getPuzzleElement(cell); - blackCell.setType(FillapixCellType.BLACK); - return tooManyBlackCells.checkContradictionAt(blackCaseBoard, cell) != null; + blackCell.setCellType(FillapixCellType.BLACK); + ArrayList adjCells = getAdjacentCells(blackCaseBoard, blackCell); + for (FillapixCell adjCell : adjCells) { + if (tooManyBlackCells.checkContradictionAt(blackCaseBoard, adjCell) == null) { + return true; + } + } + return false; + } + + public static boolean isComplete(FillapixBoard board, FillapixCell cell) { + int cellNum = cell.getNumber(); + int cellTouchBlack = 0; + ArrayList adjCells = getAdjacentCells(board, cell); + for (FillapixCell adjCell : adjCells) { + if (adjCell.getType() == FillapixCellType.BLACK) { + cellTouchBlack++; + } + } + return cellNum == cellTouchBlack; + } + + public static boolean hasEmptyAdjacent(FillapixBoard board, FillapixCell cell) { + ArrayList adjCells = getAdjacentCells(board, cell); + for (FillapixCell adjCell : adjCells) { + if (adjCell.getType() == FillapixCellType.UNKNOWN) { + return true; + } + } + return false; + } + + /** + * Gets all cells adjacent to a specific cell. The cell itself will be included. + */ + public static ArrayList getAdjacentCells(FillapixBoard board, FillapixCell cell) { + ArrayList adjCells = new ArrayList(); + Point cellLoc = cell.getLocation(); + for (int i=-1; i <= 1; i++) { + for (int j=-1; j <= 1; j++) { + if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) { + continue; + } + FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j); + if (adjCell == null) { + continue; + } + adjCells.add(adjCell); + } + } + return adjCells; + } + + /** + * Gets all cells that are contained in the square defined as having 'distance' + * cells between the center and the outer wall. For example, distance = 1:

+ * |X|X|X|X|X|

+ * |X| | | |X|

+ * |X| |O| |X|

+ * |X| | | |X|

+ * |X|X|X|X|X|

+ * O is 'cell', and all 'X' will be returned in the ArrayList + */ + public static ArrayList getCellsAtDistance(FillapixBoard board, FillapixCell cell, int distance) { + ArrayList adjCells = new ArrayList(); + Point cellLoc = cell.getLocation(); + int i = 0, j = 0; + // top line + for (i = cellLoc.x - (distance), j = cellLoc.y - (distance+1); i <= cellLoc.x + (distance+1); i++) { + if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) { + continue; + } + FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j); + if (adjCell == null) { + continue; + } + adjCells.add(adjCell); + } + // right line + for (i = cellLoc.x + (distance+1), j = cellLoc.y - (distance); j <= cellLoc.y + (distance+1); j++) { + if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) { + continue; + } + FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j); + if (adjCell == null) { + continue; + } + adjCells.add(adjCell); + } + // bottom line + for (i = cellLoc.x + (distance), j = cellLoc.y + (distance+1); i <= cellLoc.x - (distance+1); i--) { + if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) { + continue; + } + FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j); + if (adjCell == null) { + continue; + } + adjCells.add(adjCell); + } + // left line + for (i = cellLoc.x - (distance+1), j = cellLoc.y + (distance); j <= cellLoc.y - (distance+1); j--) { + if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) { + continue; + } + FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j); + if (adjCell == null) { + continue; + } + adjCells.add(adjCell); + } + + return adjCells; + } + + /** + * Finds all possible combinations of chosenNumObj items can be + * chosen from totalNumObj total items. + * For example, if 1 item is chosen from 2 possible items, the combinations + * are: + *

[ [true,false], [false,true] ]
+ * + * @param totalNumItems the total number of items that can possibly be chosen + * @param chosenNumItems the number of items to be chosen + * + * @return an ArrayList of Boolean arrays. Each index in the ArrayList represents + * a distinct combination. Each Boolean array will be totalNumItems + * long and each index will be true<\code> if the corresponding item is + * included in that combination, and false otherwise. + */ + public static ArrayList getCombinations(int chosenNumItems, int totalNumItems) { + ArrayList combinations = new ArrayList(); + + // calculate all combinations + boolean[] array = new boolean[totalNumItems]; + recurseCombinations(combinations, 0, chosenNumItems, 0, totalNumItems, array); + + return combinations; + } + + private static void recurseCombinations(ArrayList result, int curIndex, int maxBlack, int numBlack, int len, boolean[] workingArray) { + if (curIndex == len) { + // complete, but not valid solution + if (numBlack != maxBlack) { + return; + } + // complete and valid solution + result.add(workingArray.clone()); + return; + } + // there is no chance of completing the required number of solutions, so quit + if (len - curIndex < maxBlack - numBlack) { + return; + } + + if (numBlack < maxBlack) { + workingArray[curIndex] = true; + recurseCombinations(result, curIndex+1, maxBlack, numBlack+1, len, workingArray); + } + workingArray[curIndex] = false; + recurseCombinations(result, curIndex+1, maxBlack, numBlack, len, workingArray); + } + + public static boolean checkBoardForContradiction(FillapixBoard board) { + ContradictionRule tooManyBlack = new TooManyBlackCellsContradictionRule(); + ContradictionRule tooManyWhite = new TooFewBlackCellsContradictionRule(); + for (int i= 0; i < board.getWidth(); i++) { + for (int j=0; j < board.getHeight(); j++) { + if (tooManyBlack.checkContradictionAt(board, board.getCell(i, j)) == null || + tooManyWhite.checkContradictionAt(board, board.getCell(i, j)) == null) { + return true; + } + } + } + return false; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java new file mode 100644 index 000000000..7e43fc6b4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.fillapix.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class BlackTile extends PlaceableElement { + public BlackTile() { + super("FPIX-PLAC-0001", "Black Tile", "The black tile", "edu/rpi/legup/images/fillapix/tiles/BlackTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java new file mode 100644 index 000000000..beee70e21 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java @@ -0,0 +1,27 @@ +package edu.rpi.legup.puzzle.fillapix.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class NumberTile extends NonPlaceableElement { + private int object_num; + + public NumberTile() { + super("FPIX-UNPL-0001", "Number Tile", "A numbered tile", "edu/rpi/legup/images/fillapix/tiles/NumberTile.png"); + object_num = 0; + } + + /** + * @return this object's tile number... + */ + public int getTileNumber() { + return object_num; + } + + /** + * @param num Amount to set tile object to. + */ + public void setTileNumber(int num) { + object_num = num; + } + +} diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java new file mode 100644 index 000000000..ef754782f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.fillapix.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class UnknownTile extends NonPlaceableElement { + public UnknownTile() { + super("FPIX-UNPL-0002", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/fillapix/tiles/UnknownTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java new file mode 100644 index 000000000..dd27d2834 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.fillapix.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class WhiteTile extends PlaceableElement { + public WhiteTile() { + super("FPIX-PLAC-0002", "White Tile", "The white tile", "edu/rpi/legup/images/fillapix/tiles/WhiteTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt new file mode 100644 index 000000000..0409fa800 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt @@ -0,0 +1,5 @@ +FPIX-PLAC-0001 : BlackTile +FPIX-PLAC-0002 : WhiteTile + +FPIX-UNPL-0001 : NumberTile +FPIX-UNPL-0002 : UnknownTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java index fd6b7c022..1e5151c48 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java @@ -40,13 +40,13 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { Board case1 = board.copy(); FillapixCell cell1 = (FillapixCell) case1.getPuzzleElement(puzzleElement); - cell1.setType(FillapixCellType.BLACK); + cell1.setCellType(FillapixCellType.BLACK); case1.addModifiedData(cell1); cases.add(case1); Board case2 = board.copy(); FillapixCell cell2 = (FillapixCell) case2.getPuzzleElement(puzzleElement); - cell2.setType(FillapixCellType.WHITE); + cell2.setCellType(FillapixCellType.WHITE); case2.addModifiedData(cell2); cases.add(case2); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java index afd226693..06a8045ed 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java @@ -8,6 +8,7 @@ import edu.rpi.legup.puzzle.fillapix.FillapixBoard; import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; public class FinishWithBlackDirectRule extends DirectRule { public FinishWithBlackDirectRule() { @@ -28,7 +29,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem return super.getInvalidUseOfRuleMessage() + ": This cell must be black to be applicable with this rule."; } - if (isForcedBlack(parentBoard, cell)) { + if (FillapixUtilities.isForcedBlack(parentBoard, cell)) { return null; } else { @@ -36,14 +37,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } } - private boolean isForcedBlack(FillapixBoard board, FillapixCell cell) { - TooFewBlackCellsContradictionRule tooManyBlackCells = new TooFewBlackCellsContradictionRule(); - FillapixBoard whiteCaseBoard = board.copy(); - FillapixCell whiteCell = (FillapixCell) whiteCaseBoard.getPuzzleElement(cell); - whiteCell.setType(FillapixCellType.WHITE); - return tooManyBlackCells.checkContradictionAt(whiteCaseBoard, cell) == null; - } - /** * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. * @@ -55,8 +48,8 @@ public Board getDefaultBoard(TreeNode node) { FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy(); for (PuzzleElement element : fillapixBoard.getPuzzleElements()) { FillapixCell cell = (FillapixCell) element; - if (cell.getType() == FillapixCellType.UNKNOWN && isForcedBlack((FillapixBoard) node.getBoard(), cell)) { - cell.setType(FillapixCellType.BLACK); + if (cell.getType() == FillapixCellType.UNKNOWN && FillapixUtilities.isForcedBlack((FillapixBoard) node.getBoard(), cell)) { + cell.setCellType(FillapixCellType.BLACK); fillapixBoard.addModifiedData(cell); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java index ec482d5f7..7e213a59c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java @@ -8,10 +8,11 @@ import edu.rpi.legup.puzzle.fillapix.FillapixBoard; import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; public class FinishWithWhiteDirectRule extends DirectRule { public FinishWithWhiteDirectRule() { - super("FFIX-BASC-0002", + super("FPIX-BASC-0002", "Finish with White", "The remaining unknowns around and on a cell must be white to satisfy the number", "edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png"); @@ -28,7 +29,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem return super.getInvalidUseOfRuleMessage() + ": This cell must be white to be applicable with this rule"; } - if (isForcedWhite(parentBoard, cell)) { + if (FillapixUtilities.isForcedWhite(parentBoard, cell)) { return null; } else { @@ -36,14 +37,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } } - private boolean isForcedWhite(FillapixBoard board, FillapixCell cell) { - TooManyBlackCellsContradictionRule tooManyBlackCells = new TooManyBlackCellsContradictionRule(); - FillapixBoard blackCaseBoard = board.copy(); - FillapixCell blackCell = (FillapixCell) blackCaseBoard.getPuzzleElement(cell); - blackCell.setType(FillapixCellType.BLACK); - return tooManyBlackCells.checkContradictionAt(blackCaseBoard, cell) == null; - } - /** * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. * @@ -55,8 +48,8 @@ public Board getDefaultBoard(TreeNode node) { FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy(); for (PuzzleElement element : fillapixBoard.getPuzzleElements()) { FillapixCell cell = (FillapixCell) element; - if (cell.getType() == FillapixCellType.UNKNOWN && isForcedWhite((FillapixBoard) node.getBoard(), cell)) { - cell.setType(FillapixCellType.WHITE); + if (cell.getType() == FillapixCellType.UNKNOWN && FillapixUtilities.isForcedWhite((FillapixBoard) node.getBoard(), cell)) { + cell.setCellType(FillapixCellType.WHITE); fillapixBoard.addModifiedData(cell); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java new file mode 100644 index 000000000..656cedb3f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java @@ -0,0 +1,103 @@ +package edu.rpi.legup.puzzle.fillapix.rules; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.fillapix.FillapixBoard; +import edu.rpi.legup.puzzle.fillapix.FillapixCell; +import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; + +public class MirrorDirectRule extends DirectRule { + public MirrorDirectRule() { + super("FPIX-BASC-0003", + "Mirror", + "Two adjacent clues with the same value must have the same number of black squares in their unshared regions", + "edu/rpi/legup/images/fillapix/rules/Mirror.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + FillapixBoard board = (FillapixBoard) transition.getBoard(); + FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy(); + FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); + FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); + + // cell has to have been empty before + if (parentCell.getType() != FillapixCellType.UNKNOWN) { + return super.getInvalidUseOfRuleMessage(); + } + + // parentBoard cannot have any contradictions + if (FillapixUtilities.checkBoardForContradiction(parentBoard)) { + return super.getInvalidUseOfRuleMessage(); + } + + // find all cells adjacent to cell that are numbered + ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell); + ArrayList adjNums = new ArrayList(); + for (int i=0; i < adjCells.size(); i++) { + if ((adjCells.get(i)).getNumber() >= 0 && adjCells.get(i).getNumber() < 10) { + adjNums.add(adjCells.get(i)); + } + } + // the numbered cells must be next to another numbered cell of the same value + Iterator itr = adjNums.iterator(); + while (itr.hasNext()) { + FillapixCell adjNum = itr.next(); + adjCells = FillapixUtilities.getAdjacentCells(parentBoard, adjNum); + boolean found = false; + for (FillapixCell adjCell : adjCells) { + if (adjCell.getNumber() == adjNum.getNumber() && adjCell.getIndex() != adjNum.getIndex()) { + found = true; + } + } + if (!found) { + itr.remove(); + } + } + + // change the color of the parentCell, and check if there exists a valid board + if (cell.getType() == FillapixCellType.BLACK) { + parentCell.setCellType(FillapixCellType.WHITE); + } + else { + parentCell.setCellType(FillapixCellType.BLACK); + } + parentBoard.addModifiedData(parentCell); + CaseRule completeClue = new SatisfyClueCaseRule(); + List caseBoards; + for (FillapixCell adjNum : adjNums) { + caseBoards = completeClue.getCases(parentBoard, adjNum); + boolean found = true; + for (Board b : caseBoards) { + if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) { + found = false; + } + } + if (found) { + return null; + } + } + + return super.getInvalidUseOfRuleMessage(); + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java new file mode 100644 index 000000000..fe94dbcb4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java @@ -0,0 +1,99 @@ +package edu.rpi.legup.puzzle.fillapix.rules; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.fillapix.FillapixBoard; +import edu.rpi.legup.puzzle.fillapix.FillapixCell; +import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; + +public class NonTouchingSharedDirectRule extends DirectRule { + public NonTouchingSharedDirectRule() { + super("FPIX-BASC-0005", + "NonTouching Shared", + "Clues with shared cells have the same difference in black cells in their unshared regions as the difference in their numbers", + "edu/rpi/legup/images/fillapix/rules/TouchingSides.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + FillapixBoard board = (FillapixBoard) transition.getBoard(); + FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy(); + FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); + FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); + + // cell has to have been empty before + if (parentCell.getType() != FillapixCellType.UNKNOWN) { + return super.getInvalidUseOfRuleMessage(); + } + + // parentBoard cannot have any contradictions + if (FillapixUtilities.checkBoardForContradiction(parentBoard)) { + return super.getInvalidUseOfRuleMessage(); + } + + // get all adjCells that have a number + ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell); + adjCells.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10); + /* remove any number cell that does not have another number cell not + * touching, but sharing cells */ + Iterator itr = adjCells.iterator(); + while (itr.hasNext()) { + ArrayList sharingCells = FillapixUtilities.getCellsAtDistance(parentBoard, parentCell, 1); + boolean found = false; + for (FillapixCell sharingCell : sharingCells) { + if (sharingCell.getNumber() >= 0 && sharingCell.getNumber() < 10) { + found = true; + } + } + if (!found) { + itr.remove(); + } + } + + // change the cell to the opposite color + if (cell.getType() == FillapixCellType.BLACK) { + parentCell.setCellType(FillapixCellType.WHITE); + } + else { + parentCell.setCellType(FillapixCellType.BLACK); + } + // check for some contradiction in all cases + parentBoard.addModifiedData(parentCell); + CaseRule completeClue = new SatisfyClueCaseRule(); + List caseBoards; + for (FillapixCell adjCell : adjCells) { + caseBoards = completeClue.getCases(parentBoard, adjCell); + boolean found = true; + for (Board b : caseBoards) { + if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) { + found = false; + } + } + if (found) { + return null; + } + } + + return super.getInvalidUseOfRuleMessage(); + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java new file mode 100644 index 000000000..4520add31 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java @@ -0,0 +1,234 @@ +package edu.rpi.legup.puzzle.fillapix.rules; + +import java.awt.Point; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.fillapix.FillapixBoard; +import edu.rpi.legup.puzzle.fillapix.FillapixCell; +import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; + +public class SatisfyClueCaseRule extends CaseRule { + public SatisfyClueCaseRule() { + super("FPIX-CASE-0002", + "Satisfy Clue", + "Each clue must touch that number of squares.", + "edu/rpi/legup/images/fillapix/cases/SatisfyClue.png"); + } + + @Override + public CaseBoard getCaseBoard(Board board) { + FillapixBoard fillapixBoard = (FillapixBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(fillapixBoard, this); + fillapixBoard.setModifiable(false); + for (PuzzleElement data : fillapixBoard.getPuzzleElements()) { + FillapixCell cell = (FillapixCell) data; + if (cell.getNumber() >= 0 && cell.getNumber() <= 9 && FillapixUtilities.hasEmptyAdjacent(fillapixBoard, cell)) { + caseBoard.addPickableElement(data); + } + } + return caseBoard; + } + + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList(); + + // get value of cell + FillapixBoard fillapixBoard = (FillapixBoard) board.copy(); + FillapixCell cell = (FillapixCell) fillapixBoard.getPuzzleElement(puzzleElement); + int cellMaxBlack = cell.getNumber(); + if (cellMaxBlack < 0 || cellMaxBlack > 9) { // cell is not valid cell + return null; + } + + // find number of black & empty squares + int cellNumBlack = 0; + int cellNumEmpty = 0; + ArrayList emptyCells = new ArrayList(); + ArrayList adjCells = FillapixUtilities.getAdjacentCells(fillapixBoard, cell); + for (FillapixCell adjCell : adjCells) { + if (adjCell.getType() == FillapixCellType.BLACK) { + cellNumBlack++; + } + if (adjCell.getType() == FillapixCellType.UNKNOWN) { + cellNumEmpty++; + emptyCells.add(adjCell); + } + } + // no cases if no empty or if too many black already + if (cellNumBlack > cellMaxBlack || cellNumEmpty == 0) { + return cases; + } + + // generate all cases as boolean expressions + ArrayList combinations; + combinations = FillapixUtilities.getCombinations(cellMaxBlack - cellNumBlack, cellNumEmpty); + + for (int i=0; i < combinations.size(); i++) { + Board case_ = board.copy(); + for (int j=0; j < combinations.get(i).length; j++) { + cell = (FillapixCell) case_.getPuzzleElement(emptyCells.get(j)); + if (combinations.get(i)[j]) { + cell.setCellType(FillapixCellType.BLACK); + } + else { + cell.setCellType(FillapixCellType.WHITE); + } + case_.addModifiedData(cell); + } + cases.add(case_); + } + + return cases; + } + + @Override + public String checkRuleRaw(TreeTransition transition) { + TreeNode parent = transition.getParents().get(0); + List childTransitions = parent.getChildren(); + + /* + * In order for the transition to be valid, it can only be applied to + * one cell, thus: + * * there must be modified cells + * * all modified cells must share at least one common adjacent + * cell + * * all modified cells must fit within a 3X3 square + * * the center of one of the possible squaress must be a cell + * with a number + * * that cells possible combinations must match the transitions + * If all the above is verified, then the transition is valid + */ + + + /* ensure there are modified cells */ + Set modCells = transition.getBoard().getModifiedData(); + if (modCells.size() <= 0) { + return super.getInvalidUseOfRuleMessage(); + } + + + /* ensure modified cells occur within a 3X3 square */ + int minVertLoc = Integer.MAX_VALUE, maxVertLoc = Integer.MIN_VALUE; + int minHorzLoc = Integer.MAX_VALUE, maxHorzLoc = Integer.MIN_VALUE; + for (PuzzleElement modCell : modCells) { + Point loc = ((FillapixCell) modCell).getLocation(); + if (loc.x < minHorzLoc) { + minHorzLoc = loc.x; + } + if (loc.x > maxHorzLoc) { + maxHorzLoc = loc.x; + } + if (loc.y < minVertLoc) { + minVertLoc = loc.y; + } + if (loc.y > maxVertLoc) { + maxVertLoc = loc.y; + } + } + if (maxVertLoc - minVertLoc > 3 || maxHorzLoc - minHorzLoc > 3) { + return super.getInvalidUseOfRuleMessage(); + } + + + /* get the center of all possible 3X3 squares, + * and collect all that have numbers */ + FillapixBoard board = (FillapixBoard) transition.getParents().get(0).getBoard(); + Set possibleCenters = new TreeSet(); + possibleCenters.addAll(FillapixUtilities.getAdjacentCells(board, (FillapixCell) modCells.iterator().next())); + for (PuzzleElement modCell : modCells) { + possibleCenters.retainAll((FillapixUtilities.getAdjacentCells(board, (FillapixCell) modCell))); + } + // removing all elements without a valid number + possibleCenters.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10); + if (possibleCenters.isEmpty()) { + return super.getInvalidUseOfRuleMessage(); + } + + + /* Now go through the remaining centers, and check if their combinations + * match the transitions */ + for (FillapixCell possibleCenter : possibleCenters) { + int numBlack = 0; + int numEmpty = 0; + int maxBlack = possibleCenter.getNumber(); + for (FillapixCell adjCell : FillapixUtilities.getAdjacentCells(board, possibleCenter)) { + if (adjCell.getType() == FillapixCellType.BLACK) { + numBlack++; + } + if (adjCell.getType() == FillapixCellType.UNKNOWN) { + numEmpty++; + } + } + if (numEmpty <= 0 || numBlack > maxBlack) { + // this cell has no cases (no empty) or is already broken (too many black) + continue; + } + + ArrayList combinations = FillapixUtilities.getCombinations(maxBlack - numBlack, numEmpty); + if (combinations.size() != childTransitions.size()) { + // not this center because combinations do not match transitions + continue; + } + boolean quitEarly = false; + for (TreeTransition trans : childTransitions) { + /* convert the transition board into boolean format, so that it + * can be compared to the combinations */ + FillapixBoard transBoard = (FillapixBoard) trans.getBoard(); + ArrayList transModCells = new ArrayList(); + for (PuzzleElement modCell : modCells) { + transModCells.add((FillapixCell) transBoard.getPuzzleElement(modCell)); + } + + boolean[] translatedModCells = new boolean[transModCells.size()]; + for (int i=0; i < transModCells.size(); i++) { + if (transModCells.get(i).getType() == FillapixCellType.BLACK) { + translatedModCells[i] = true; + } + else { + translatedModCells[i] = false; + } + } + + // try to find the above state in the combinations, remove if found + boolean removed = false; + for (boolean[] combination : combinations) { + if (Arrays.equals(combination, translatedModCells)) { + combinations.remove(combination); + removed = true; + break; + } + } + // if combination not found, no need to check further, just quit + if (!removed) { + quitEarly = true; + break; + } + } + + /* we found a center that is valid */ + if (combinations.isEmpty() && !quitEarly) { + return null; + } + } + + return super.getInvalidUseOfRuleMessage(); + } + + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java index e8dd32860..c37050978 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java @@ -6,8 +6,9 @@ import edu.rpi.legup.puzzle.fillapix.FillapixBoard; import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; -import java.awt.*; +import java.util.ArrayList; public class TooFewBlackCellsContradictionRule extends ContradictionRule { @@ -31,22 +32,24 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { FillapixBoard fillapixBoard = (FillapixBoard) board; FillapixCell cell = (FillapixCell) fillapixBoard.getPuzzleElement(puzzleElement); - Point loc = cell.getLocation(); - for (int i = -1; i < 2; i++) { - for (int j = -1; j < 2; j++) { - FillapixCell adjCell = fillapixBoard.getCell(loc.x + i, loc.y + j); - if (adjCell != null) { - int cellNum = adjCell.getNumber(); - if (cellNum >= 0) { - int numBlackCells = fillapixBoard.getNumCells(adjCell, FillapixCellType.BLACK); - int numUnknownCells = fillapixBoard.getNumCells(adjCell, FillapixCellType.UNKNOWN); - if (numBlackCells + numUnknownCells < cellNum) { - return null; - } - } - } + int cellNum = cell.getNumber(); + if (cellNum < 0 || cellNum >= 10) { + return super.getNoContradictionMessage(); + } + int numBlack = 0, numEmpty = 0; + ArrayList adjCells = FillapixUtilities.getAdjacentCells(fillapixBoard, cell); + for (FillapixCell adjCell : adjCells) { + if (adjCell.getType() == FillapixCellType.BLACK) { + numBlack++; + } + if (adjCell.getType() == FillapixCellType.UNKNOWN) { + numEmpty++; } } + if (numBlack + numEmpty < cellNum) { + return null; + } + return super.getNoContradictionMessage(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java index 5a1192cc9..68395ce7f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java @@ -6,8 +6,9 @@ import edu.rpi.legup.puzzle.fillapix.FillapixBoard; import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; -import java.awt.*; +import java.util.ArrayList; public class TooManyBlackCellsContradictionRule extends ContradictionRule { @@ -31,21 +32,21 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { FillapixBoard fillapixBoard = (FillapixBoard) board; FillapixCell cell = (FillapixCell) fillapixBoard.getPuzzleElement(puzzleElement); - Point loc = cell.getLocation(); - for (int i = -1; i < 2; i++) { - for (int j = -1; j < 2; j++) { - FillapixCell adjCell = fillapixBoard.getCell(loc.x + i, loc.y + j); - if (adjCell != null) { - int cellNum = adjCell.getNumber(); - if (cellNum >= 0) { - int numBlackCells = fillapixBoard.getNumCells(adjCell, FillapixCellType.BLACK); - if (numBlackCells > cellNum) { - return null; - } - } - } + int cellNum = cell.getNumber(); + if (cellNum < 0 || cellNum >= 10) { + return super.getNoContradictionMessage(); + } + int numBlack = 0; + ArrayList adjCells = FillapixUtilities.getAdjacentCells(fillapixBoard, cell); + for (FillapixCell adjCell : adjCells) { + if (adjCell.getType() == FillapixCellType.BLACK) { + numBlack++; } } + if (numBlack > cellNum) { + return null; + } + return super.getNoContradictionMessage(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java new file mode 100644 index 000000000..6f4be7842 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java @@ -0,0 +1,108 @@ +package edu.rpi.legup.puzzle.fillapix.rules; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.fillapix.FillapixBoard; +import edu.rpi.legup.puzzle.fillapix.FillapixCell; +import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; + +public class TouchingCornersDirectRule extends DirectRule { + public TouchingCornersDirectRule() { + super("FPIX-BASC-0005", + "Touching Corners", + "Clues with touching corners have the same difference in black cells in their unshared regions as the difference in their numbers", + "edu/rpi/legup/images/fillapix/rules/TouchingCorners.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + FillapixBoard board = (FillapixBoard) transition.getBoard(); + FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy(); + FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); + FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); + + // cell has to have been empty before + if (parentCell.getType() != FillapixCellType.UNKNOWN) { + return super.getInvalidUseOfRuleMessage(); + } + + // parentBoard cannot have any contradictions + if (FillapixUtilities.checkBoardForContradiction(parentBoard)) { + return super.getInvalidUseOfRuleMessage(); + } + + // get all adjCells that have a number + ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell); + adjCells.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10); + /* remove any number cell that does not have another number cell diagonally + * adjacent to it on the opposite side of the modified cell */ + Iterator itr = adjCells.iterator(); + while (itr.hasNext()) { + FillapixCell adjCell = itr.next(); + + boolean found = false; + ArrayList adjAdjCells = FillapixUtilities.getAdjacentCells(parentBoard, adjCell); + for (FillapixCell adjAdjCell : adjAdjCells) { + if (adjAdjCell.getLocation().x != adjCell.getLocation().x && + adjAdjCell.getLocation().y != adjCell.getLocation().y && + adjAdjCell.getNumber() >= 0 && adjAdjCell.getNumber() < 10 && + adjAdjCell.getIndex() != parentCell.getIndex()) { + // adjAdjCell is diagonally adjacent to adjCell && it has a + // number && it is not parentCell + found = true; + } + } + + // does not qualify for this rule + if (!found) { + itr.remove(); + } + } + + // change the cell to the opposite color + if (cell.getType() == FillapixCellType.BLACK) { + parentCell.setCellType(FillapixCellType.WHITE); + } + else { + parentCell.setCellType(FillapixCellType.BLACK); + } + // check for some contradiction in all cases + parentBoard.addModifiedData(parentCell); + CaseRule completeClue = new SatisfyClueCaseRule(); + List caseBoards; + for (FillapixCell adjCell : adjCells) { + caseBoards = completeClue.getCases(parentBoard, adjCell); + boolean found = true; + for (Board b : caseBoards) { + if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) { + found = false; + } + } + if (found) { + return null; + } + } + + return super.getInvalidUseOfRuleMessage(); + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java new file mode 100644 index 000000000..bd6dd0169 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java @@ -0,0 +1,115 @@ +package edu.rpi.legup.puzzle.fillapix.rules; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.fillapix.FillapixBoard; +import edu.rpi.legup.puzzle.fillapix.FillapixCell; +import edu.rpi.legup.puzzle.fillapix.FillapixCellType; +import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; + +public class TouchingSidesDirectRule extends DirectRule { + public TouchingSidesDirectRule() { + super("FPIX-BASC-0004", + "Touching Sides", + "Clues with touching sides have the same difference in black cells in their unshared regions as the difference in their numbers", + "edu/rpi/legup/images/fillapix/rules/TouchingSides.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + FillapixBoard board = (FillapixBoard) transition.getBoard(); + FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy(); + FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); + FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); + + // cell has to have been empty before + if (parentCell.getType() != FillapixCellType.UNKNOWN) { + return super.getInvalidUseOfRuleMessage(); + } + + // parentBoard cannot have any contradictions + if (FillapixUtilities.checkBoardForContradiction(parentBoard)) { + return super.getInvalidUseOfRuleMessage(); + } + + // get all adjCells that have a number + ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell); + adjCells.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10); + /* remove any number cell that does not have another number cell adjacent + * to it on the opposite side of the modified cell */ + Iterator itr = adjCells.iterator(); + while (itr.hasNext()) { + // calculate x and y offset of adjCell from cell + FillapixCell adjCell = itr.next(); + int xOffset = adjCell.getLocation().x - cell.getLocation().x; + int yOffset = adjCell.getLocation().y - cell.getLocation().y; + + boolean found = false; + // check vertically for numbered cell in opposite direction of cell + if (adjCell.getLocation().x + xOffset >= 0 && adjCell.getLocation().x < parentBoard.getWidth()) { + int adjNum = parentBoard.getCell(adjCell.getLocation().x + xOffset, adjCell.getLocation().y).getNumber(); + if (adjNum >= 0 && adjNum < 10) { + found = true; + } + } + // check horizontally for numbered cell in opposite direction of cell + if (adjCell.getLocation().y + yOffset >= 0 && adjCell.getLocation().y < parentBoard.getHeight()) { + int adjNum = parentBoard.getCell(adjCell.getLocation().x, adjCell.getLocation().y + yOffset).getNumber(); + if (adjNum >= 0 && adjNum < 10) { + found = true; + } + } + + // if no horizontally or vertically adjacent cell on opposite side of 'cell' has number, + // then adjCell is not valid, so should be removed + if (!found) { + itr.remove(); + } + } + + // change the cell to the opposite color + if (cell.getType() == FillapixCellType.BLACK) { + parentCell.setCellType(FillapixCellType.WHITE); + } + else { + parentCell.setCellType(FillapixCellType.BLACK); + } + // check for some contradiction in all cases + parentBoard.addModifiedData(parentCell); + CaseRule completeClue = new SatisfyClueCaseRule(); + List caseBoards; + for (FillapixCell adjCell : adjCells) { + caseBoards = completeClue.getCases(parentBoard, adjCell); + boolean found = true; + for (Board b : caseBoards) { + if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) { + found = false; + } + } + if (found) { + return null; + } + } + + return super.getInvalidUseOfRuleMessage(); + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt index b6172e7fb..2e3c96a86 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt @@ -1,7 +1,11 @@ FPIX-BASC-0001 : FinishWithBlackDirectRule FPIX-BASC-0002 : FinishWithWhiteDirectRule +FPIX-BASC-0003 : MirrorDirectRule +FPIX-BASC-0004 : TouchingSidesDirectRule +FPIX-BASC-0005 : TouchingCornersDirectRule FPIX-CONT-0001 : TooFewBlackCellsContradictionRule FPIX-CONT-0002 : TooManyBlackCellsContradictionRule -FPIX-CASE-0001 : BlackOrWhiteCaseRule \ No newline at end of file +FPIX-CASE-0001 : BlackOrWhiteCaseRule +FPIX-CASE-0002 : CompleteClueCaseRule \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png b/src/main/resources/edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png index ff234c17f654e7ba4f743a6c2c7e3f73978e5a39..17df92f1a7e3d8b3090c3508fe715aeb9a021f22 100644 GIT binary patch literal 722 zcmeAS@N?(olHy`uVBq!ia0vp^DImg@a~r*@->Wx zJFc!?`TmVJ&%0pjU+4B6d7yT@tncL8BM+4 z3oiZ?J2!ueP_y3Z-|Od<3p9((&;55ZT3m>E|J=Fd_wK5*rTxDBuKr zr%K$2+5K>9ytOBfjm&P#eLv?(Nc`CLsrF}hQi17ryLa!ylQul8cs$QHEx7Nbn&FLx z(`U;Hew1E&y7*{<^WJysw{Jh>a=gUw#)n<+xcz4o{<$hH(9CNdm=i^q=9;7rXv-_bpcg(<+0ftDnm{r-UW|oc=P6 literal 717 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-u*eSZQ`DQHoo4sKb~>^k(an7d1U67=Mv&8_x7)EI&{U~UR0&=fDnfc zm^iY0&B+uYz3JY!KKFld6lOG)%YHiH*o5zZCw@2%4%S%%@{Ln!5I}BYJq1AS?H$c%Ic}A}Su? z5@Eg?78BMPu89f=Pe%S48Yg?~S){`JnTmLG97?{+v{%IgTe~DWM4f1rr^N diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/cases/SatisfyClue.png b/src/main/resources/edu/rpi/legup/images/fillapix/cases/SatisfyClue.png new file mode 100644 index 0000000000000000000000000000000000000000..8d84efed99f2fc4b6a180b20cbfd06c70777675c GIT binary patch literal 1789 zcmY*Zc|6fgd3C}iUBh(hllxo^)8k7=iR4t*_XDOZf=(Dd) z$6AbvZEB4SrBzx+RINoZOj{2l(@wlMf4ukk+;h+8d(OSzbMLw5+;nG08yUC?8~^|r ziY<{U()1&NNr-0VVxAiSh$Z_I2+kA&L7f>I%q#n-!RjSGuTj{3AuL2*dVIas@`3I@s*QfZpEH!J0c6Ee)~_iR$~}i|-e!)-WgI z@=;y`Q9~he+p*gtZ&>W7G)B#@NO8B(v($8M&Myt{-tWW9OW&O)Yck%4RXj*q-S0@f zyUlVbS-G^gy5=5wN<0PasTP_FpgBOYg7K))`nGSdzGuv!A=sJQ7?aC3|>_ zF>T9vm)<{sFV9-RF-U}yC75jO8uG#C%n|si{@9K7;ex$o1L>8Y@~$^bsE{EU7ZPD& zXKS>}rKMbEzNMb8S~LG`K`m5>X{U$g_3)e6a5vj*HKAPG-s@*hj$BH)$%_LYfm)>% zQ^{{K@bGZ@$#E^K`D5IdUZ3q$9~@|QzkZ3av3iSRhP-*XY%w1ysT}k2Bq6j4(2uv2 z>na^LzMhb9vCMaL>*7Z=N6xAsIMUA z-bKR4-i8QR07M|8UWzLeS-xi4dWHdjl){mS0R=_MBBNwD#epQbDj|;o&xF_RUIYN> z0flJo7B#a~=x|;mLYY_U?^$;FLK*DzY3?A@15!QGt3pb6z(w|@TOhzd%)$#qS?Y=v zOo_Xdpnxuu?E6ZonW&>oQcyV4lqz$|{s4h{gvvnvpl{Ub*H)iuw{q>O@%TDw^`Q9S zM1R-1!So!q!Bg?p=Wa7~wJb)T*V8{6U`u1B_S^ zSOg~FwUeA-$q0Bk0{{O2^}n7YfdB8Z4`^i@Tf?ydWX)W+76r_>JeOAw?6a9_$833UEpcwUW_q@^UaK9i} zSsgacF}xhxBKiEcpZ!QI7HfzOy<;tkR##W2t}G1!K1Ew}baX6FfV>Tf7^Wa?aWTK# zu3O%$G`FE|sI)^2xP3d$do-4EzalI&)MrRRLBTH|Kpe=td$+Q+RT0Q3EYwp2;mK^c z>)fvfN%1AF4Kt*SoY+yO6FS(%mJX@D?KsbScT#i z)5O`!3vP|@QANt8j?d1biy7sJhaAp|9(}!2HO8nV*_Oz*tF{spPnA(GA?w%X=4i@U zWFs67&zUH%tXv}IXJ%zxWip*h_CUG(*#|K@;uxgZloa)9OF=Hn-5`2FHdVZ{v(tC1 zv9a+QgF!AaPH`$t`TZB66TtrdgZuvDSDBx}Th~Z?ySrDz!%+tZ2e8!s3p85FkisRb zo}S(rGc&2$SGwPU2|cAILJd>?sG>wwm1a8a@wi{oDC3S-Jy{#ox3gn7JTl@!r6!69 zrk}JueX3?!>`22;zNdc*FC3q^newB#rQlggiUgfbG?o?!1Z$g{P+&jCa&l^_e=9qv zv!$hFd~ObF0NPbUJSFs-rM>(nbwYGZ&Yk->5E2m)u^D#r=2m(qtD1UiJxN&mEpOFq z;7`fg>EEHwu6Y@(n#>BNuE&qrB_$^`wX`(!^<_ka(=#&=0HvsCUHgh>$_NT_q^UM< z#U*a59WH#a$ZUTeFFN`+-SdNkr1K6AAUd07&zb0@pB0V4qar!VY(#`RO_j>jR~>A; z;iFpN##{A|v$vPR;czder_=iS@P0-rL<~}Nk-(s!zM!`Lptg~1o-@auBq%Pthq5#@ zWG5tu3E$4Ex&5a@`0JV}oNho>OT8p&&r&0Mdkjwfd(CB>Bi^ zUFlKwxx0%yh~(0X@7x#B!^#nG5QV2PAAQWiN5aIGVCil2;%p7{7qnBwGi2g-@LUJTF;IAb91)&BdC;$Ke literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png b/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png index b017078a22135c904475dc7dfdb075b31b3fcf1a..b93d81e67780e8ec936f2155408292dbb881c12b 100644 GIT binary patch literal 13581 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 literal 1054 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$#l+WL3ZZOU59@k+1B z-cfgpptm*m!DE-IuDS_q4`p%OAQCM-F*jEy_YZ5?KgPt1UyIMqojmj6&Z51kpVap7 zw-lZelm6s0N$}GqS*4^&ZVWSK%n;B{sjG_<6BE0%DtXa|;U#OR1`|TD>c^ zzCPa0-hOG=YR~fW^03n5r%!jw$jP00@?^YHBvUAuO@xq5u%=lLaNzbfrM8wTXaB$kzhJ$v@- zQdjc2_3NL0`xZ8B`t;PSEUneMp8oi8LvGu>8#g|Pxm#FTPV`zjiOci!5%a5ArlN}y zXI{N}b<3+6n>QN+y|`)bUR`5j{6PCy5%}zCGcD_qG%^B-8!FY*=g1VTdBj-;`>70*POZi)Jqucnd{X;r zzi5_X&D*a>_OtDa;I7o?^z!&v-&gbV)a;-?HX%)O6n`H0pJOO+RZfUmgsDyR((T*X z`85-s8~*zJ`|VZnT~B^;>3#k3C8b{Vug9!FMZ3O-+UL)o|Mcyf8Z$HVy`Ye@X_xm& z@bL0h-t7LedngwuE zJ9o~kdo`nKuO2%)dr@VjW_NctFpw87UTn6ZxW4}V(}Pz+lTuP#jvYV#*48KCxH!+7 z3GoXB3^Z7HxEPzA4lJOId3avk{hK#GR{i?>;)Tb#^XF4DGc{N5a`m74`sMfCyRG#t zXNIj#{k`BU*Twn2H2=Nn^SFVdQ&MBb@00nu?`~Uy| diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png b/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png index a0b27424d54b2865cceb5fb466be4fd5a287c363..393a8c5447522bd5f2af0287f6b60b159925c7fd 100644 GIT binary patch delta 1219 zcmV;!1U&n+2-XRZBYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmKp2MKwrWKxf_4y5 z$WWc^q9VH0DionYs1;guFuC*(nlvOSE{=k0!NH%!s)LKOt`4q(Aov5~>f)s6A|-y8 z6k5c1$8itueecWNcYwcMW~$i{160j2Qi-^b$*u~)S9GDD=6@_kBxdS!qL_r|__~LW z?{`t2t?f%Ws?u4*Pj# z*hr`5iNnM~p^fD>W<^6Ko+6GYsz&+#jLQn=EzWAW%9{7&FAU|hm1VBe8b$((Sb_)v zGO8${3=2`(HGfh}r06{E;U9ARBDrL8mB7fcfC^MdjvxFFe)rbQPfoZ=p*Ya_V%s01 zKyVjm)NK3v*tQ!dK;Rj;(pvsX9hmtfz1GqqM?l{;aB4ydX>BZ4i15_0%fmzynnm9y>I{4H2e1h+WKq=0Ob!5rTwBTm%C_kfvUOjX5}EtN0>( z1L0mTz<;f!sS+DhM0C<2BN`lp7Qqye=rr7m^oP4#=$w;&KT{rr1Afo*oac}at~DAB zQ-7sWDK#1m*@1Ka@k?+Nf;h~YB$JQ9Iex#NzgBr3H*Fjd+(Vo^Lih;2Ua?j%xv8_t z0zn7@f)E4*AqWTpf)E4*AqWUU5DFDT)Wv^8diG(gMFBJ#`J{&g;Lxy1}6bfm7e_x?cNT;W#jF9ivZnssfRwYT3 z$$vz>UQex7OZ|RdudlC?B#+0VY&Of3WtGFjLzPM;B@zk6VlgF?NkyYkRVtOmd)#g} zQZf;7Fn$4zixg0^PmP{t)^Z6F9 z$K$ce<+4srPFVIjfzRhtE|=58!^4kV`hWiDAb!7J+uPepr_(BxN_u*Fk|gcy?5J2Q z62uOJ!9W)m7b+AAIzK;O9N*pD)xp65LF_b}%|5)2L?Q(FZrN;BXJ==M$Kzk_AL(>@ z@p?X=>-P4RRn~V4hr>EPK2|1^(O@vpXf%?`OH&^uBj1UK6JA%`uEPfqo@{5A#=B-29_9^^@8j z{+7b?BGyl4`lKnYUnw;4RJbw&4-Zd=ppl)OUaOPhN~>itdZ%yRoO$Nlxl<=jbgbQ# zT3j4#XJ?m^nd$lJ)vKJVvi|=5oA&P2ZEkMvoY33Lt0~rfGV=JGIde{2yEe^o+xG3Z zS4A&dwybBu>(|+td3oDH%kSK~=jHC+{^k33@2z6@?%zMlDWBo4k)NOc>_xZN(oeOz z>n{FVzv|LP#ZG}H=GF#B#e@J32@xhXN|+LgTeog~*36%km38Lg@l~JihlKy$X@A)+ z;LNM<8#fxBeE!+^wX+N zlZ-x|y>KBw$>p$7adENdRYP`mb~Wiun>HyeUc7kQt7$)fRtme`xPO28v14x0rN{5y zovWjx^G9XHwQJW>l9H4xU(a~_IC-+1m6g?`nKLD$OTD9`XP?Z7iH-I2_U>N0>+!Q^ zZo78vTC#q9e|}^Js=pn%F2-N|pS4hU%7mQd+3T!t2RTg_dctv%%cIlPE5NC)zfI!d z5OD25zYJNLu=7+^qDVf}J6ivXms6`UOV5IqBcIg1+Aoq)taEC84O%IQQH+zv$BLcN-2L;QJ}e#n|k0V1a;v1`7{`%ri-^uU-vZzxsZ8S=o~h z9|F|W)pM`v?s^pyzCS)DrsCvD8ylM$*+1g`u8aPgvi)vh=Q^wF=PtbezV+#ZXTc(8 vm;d1Ee`PwO#zSv<_wRUR7MJ||^)*aMGIrYA{&$N3b2@{ktDnm{r-UW|MF-ts diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png index 41e540ad1db08d9b0498115037f08c98299dbf49..d098bce41515db6d7509cb8c4200031ac7e74e97 100644 GIT binary patch literal 14647 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 literal 1825 zcmcJQdsNcd7RM3I(M(J-Fv^*1)G~o|GNsJxAQ+-OW-MPINmDK6`!FIcuH0_WqvF zx%SOTg5_q!W)KKuN%Y4D0eKdPSTj>#Ep#2P0+LZi5WyG3@7<*Wgh@R11QrCk|D%QB z>?S~)r}>9xfIyJ0zrv{01+o(aG8GZ=*fZIY8W}OchHUdf%YJI>3OUzVxGw>=zcu*y zu6%ibiRYdaIU3z%WP{xk-BG&7Y!4=B+&LeEbiYxUZ$Y=l;vMen!+ed$I=--%C?8!^ zf2%7xaDXCPlu9DbKSz-UQH=b2A3H9v7#hZu(JaA7QwZ#*_I z@TIY_F#{_7=XRIa6`g89uOa)G^V-{^5D3K7{QGCZtdEgCmWgAcN~>&18{huPyYS#DiLzV?b4}&f`U28CfH?56PW9j4lrwLSjSed zg{?2u2xo?*nlU$Ru@_vn|DQKoJET&n816PWIM_-^-dpruuNhkpKT2X18v4Hyw54h2 zJscE!dV0FkL2%nMRs3DEUa#LrXOmbpjZ~7TIp#NrkEI%D7rzDFr=iF9R+JQ4z*mTv zmA9j=7+Sx>BgR_ptsrD$PQ=5M$}z&&=%~RRnVj90t_ZLf6k133WKc`2(1M+wDYstV z_JlhzX(jhFyFOSSJr>RnNltkyd|pZGtMq~^%+{5XFI7tAOFETICdYjkyvu+VGxX1k z;Bn)v=Y+sDKp{P42l!suk~@Av>QgYFC!XXj7?%I^rCRO6>`ol!quREOD0<2iN=fy? zf+8?SKU>aaRY!fM`?~7sP$1v-d|kw^7jpQ*K^SF^uHUSlE9R zVs~?Gb#avzMt+!5db;l9xiVYZ6AeOb^VIJ@{+A)4wyh1|a>F|>{a}XssB_|-G4ZID zU!D-tdr*mH$qJjR?mZ$W!488tHaNU^X(_c^G<3ts#R5#eBNveBykPNf7Xq%fM3^O+ zuQv;7tw$H?2ZJq`uXo3Wv3u?Y@bN2$U!d)z>8XL>#4B?q_ORdUpr8E%>~&1l^WNu# zu)un}KR9>HdIYLc2w00*J%~=yj_%Sx?f&gdR0%5xM{a|{aJS`WH-vYPf>p)?eq0X2si3se4sk z;m!6bcMPm~_k|1iHWu2e14D_fMWL|&Si8031=2Du2YwisI48$w~d_*ihBH8|I zxG5?wE$vNZT(sTOmDN>Q3=UV2x8(#6{i(yR>fo_SaNhhM*tZYjPo0H^#mCq23Wifg zb{za}iH6Ay;T}rl@N8z)>eU%(p1;-SN7KApav%-s5k>F=dUl%bL5qKQ6GNvC{NY_6 zz@(a*8o$iBac7N2!$5GRr#cHQY>u2(9zC+QyguzaTi4YSCY1gk1uWf+AI=Hi?{IV>IkN$ zrUr+Gk+`r?8IN918WrU&rT_#45S?yV()vp)XHlr(MjP}B9T>$mc) nX^NR{_TDv~;d}aSKerMtCVi44iq;x{Zw*BBJBjD}QuF@};qYhX diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png index 330f705f15419e60988fc7e443c7dca294d83625..758ca7591de8d8301f8650430858a63217bcd861 100644 GIT binary patch literal 13654 zcmeHO2|Scr|9>Wxtu7Uc)C?)Y7-q4BA!I38Qd-R&V=!4`?8{RmQPCb+w5!~bic*-g zZ<>@YS}a*BvS%>!JpVHzMfcv`_rCXi|NsBz{oL_6^PG9kIlujP&i9<>k?ge8X0nu$ z6a+z&?QE@Fz|~V&BqoCIpm+ORAxL!wbETb^ot-8m0q%jGnDG$iOFzu0`Z$ajBnIwp z0c9$v3;PI*5@v$vx*gPKeyQ&Tb&W55gk$tDhh!Xz>FKFX)7H2rOw3(8H~t|R-tu}tohry9v4I9^gpVfqY3rMdIeHRfxQDf$M6 zM#h#_){AUx?d+GiE??oca@A^vC)3N@hqXS09U2xM5gC=RIdMzU*5s7kd-m?ze;_?0 z^Z1FAr%q>|IeYHXRJbd*0Ma9csE32ws)xT?KY-(<4egC1W zyQjDBb3bojP^gzs&ymltX5Z;G1?V+y{CKhP;zGSJW!MQ`F%3ukfqhH0wA zO%ieocOA6G^fE|*eJBiGOE5~?QB?4MIC{$JAUt76~udIe1q!vN=rO@XLT zZ%RX)Jp7RIoZ5aBNsXe`FOSedHgX0i1}HS$9j@a}^vUFI@zV)lH<-S;TdJkKF>fky zBV=~dO@}ajlD1s)!5alh3JYdAPH=ENbI{?nV^(Hc^gj7!PUqQP&Y(gr)d+>|rSV(` zX##1ttV1Z&u$K0k%xQUpbnw&LPv%3Mo6qL5V9ViY>IzNaUP?}I{#j;B!QS$^aI z7a?;BSTMIxP}^yTLMt^;ha_S=`{PBGV=On82vQA5n6>o7r80f-Y5c(~WE|&7 z?>Vh9c#Pr3c&|N^*7FGt=ga21(mwxm6@|)u-5naJ zAlwYL1b*_r(lFu8TnMkd`7BX$r-fPLilijI*wGJB`Uh2>ABjHOeqvYr;`!QlG3uC- z>=gL5oN4CS$M|&xZk*a?x6cT+TC?cV<(Ps?#cf$Isp?EFWvubwF){NsAk|Rl7dg14 z0);x|5${v~zyq)!h`X$iAx>S=V5tof>xe=aT65I$9|gb7=Q6@TKlKJ80sm!Aelg2Y zXtysg=xeM%d0(~L79JOb+m8B!LkIm0OT#72r5CGcE=f5l>sTn?itRjCB#%PoG=Anm z6xw#-%{-kMXN*cI-u{j@VyW3XPJdYDpw^!IxU7GHW)E#M3U!NhqL3g4h1$b%?QXq;FG8W*TK=xy$#Tv#_t+|DYdPpk z!SwXsO&h@Q5|9%3^Yu!LP7cDX9qe`#Ok0aWf6zixS=g zO#`^^&D4ADdsCh;LgcAkxfO;9JTm7#_FL4p9Tt2w$ z#3rH8M=-(p=CiRsYK=*xfn1`&Pk8pgg*^xd?zR!S253kBW9`<7-$J2*1MtU(RFS7f zYd)qeCXtXQB>#YCjP33aQdhqS6zFTInIPwEK2osMn3Y1Aw^BtSpO0D|nLFAUr`&e! zfu*u}wXG2*RK60Y;Xq@2QDr@qzmZP`)Tu-3a-Qdu#Z5z@2Qs%&sILcw@?ULl;kkmf zgyZy1rVb>YGCVI<%v#RvLUie9z0zWwP$**?Qj~>4R+ZGwEZ{Qiu*+UP`$1c`d|a&{ z7KKRlS}63GJ19V*hghL+`dZ#5{y;T0RvLwx*8vV-@(?;}jN;$5cFUFA7c!Ys*9t%8 z2zfj>avSJSnH4dTGs0_(;JZj`=8Sd-2*pJXe9f!|_*flC(a!ZKluFwz^?juMHfc#& z^~=B#DX`p1O<2s|z|RmSlzU;vh}(qM*`{5Vh!OHtr14f``$+F=`#&pzdE$>(b6>$Ar}Bv@M__VZ0KdL(fkM)j z$m!Sx2y%OrOd%k!{s_U@X`)mol)Bt?61-_VKkrj}bamw6 zs!1hVNRx-oJ`66qaYH>?rwoM-bF(@1K5zv$8?LPq;^1%5mYeQ#f!lVS(}Tb;e;Q{< z>8fBnw*zjDHU0g#j=61(tqz4-_y~wi6~T@*NW6DHr^oEb7^9} zS&)Wm2eKUDThCDFY}Qf~;#@2r#f+v~Zu5B^4PYE92f#eW3KTk;A?V<9y}J6hlHWSs zx}Ki4{L;1S8M_|cH&$RY&o_hw4pEN$xiv}v>Td&Z{$>@B?cBh^lKnua<#oA(;{b4~ zsO7HR7+we&N}m4WrOlZOTa}*)!PxgeOw4`k z@>oat9Uyi*MXS=Z?s$p@G*WUeX*EO*sc!ry*2(UXh0;;{e{rRZyn6RwuH5R@11>(% z4T&DxNH)~{k7|6xXZ#$+=*iXV?6}2M_}FEqzV-CWE%!9jD;NRk_g!kv(-!v~-)Lsv zRhbxL(b-j>`FzOr>Xmd&#mTJ?L#{PQShObOM}uwFjJLc4=2ub?=I&N&?bNv7H}>XD z$=va7vxXL}?=m|~Ke-tum8G_95Swu)XM0O-{pwl_J47bQuWwyla(i=HKvW$6^Zg;S zE10&uzeOl@l#a1rS?qTaipKHckQynC;~T%0A=MIt+)EnJKgP|ubG`uQ&WQg5 z4!reZgwpf3JFe_mtes+L{`d+2qwHh6lv;z`M+qkZGN#`tSO?tT~wB-~L=; z$p{(j4+qpU(@k?TK$u-|g6UVU7bYq0V&P+oP8%{5^K07h-*P z6yBWmW>Bi8JSMarg=A?nP4yAQ7RjPtLKfYbwCRx6w2Bm{we@%kG%)n;KET&txfkMV z-+zP9SPLV_=qhIYlh(V*LAuB zG)8ycffY}~ulDP`D2_TW+J}C(2i;i78N2(5nEX4iQ{#9_A2$MgLnB42ErnN8^5E!C z28cp#Jg1ccHusm|F8LN@QrD5>6A@mwUbQ&uuDi8%XP&M7o;kX>O`j%5arvrd!et{| zxNLw^AYNuqAjE?mD)?Hb70EeT+symNu7=Cfdiw*|(9yzXDkKB5TYzIrvtU@SKlX#LotUTB-GJzDxH!@;?I-7<6(TKW+H%lBM>sYef`*88#GUSdrg*bF7Iv7O)m<-#0ei`!Ir zObBfM$Xm`xSszPXk?(u1xQ)-Ldqb^Om}++qR=R$@x%qN83MGX6OYTY=_SS#5vwC1f z9@pyE7!vI7eaTT1#b^_ub%s$<>VACe0X51fy#4zm@eZd%Yt+6u2)?*nPpUcKX1M(KlplbTTV|Mzun^hwHh=U@M=ue>$kVvTd@7 zKV|6&mU^sNoMU0yk5*Ig2yFUi3SnRXY&J11a857W(gu!h<{~zkKU(&pmdn8m7x4IE z&k(?R;)5I;Bo3^T<7kcG8618G0ok&K`v~p?``~YQ!0nW?%?K;#-@qxNW2;H5@R81` z886+zBb}2w7T*PrbR@*)#wBEOJH5B}I?TMSuTW{69RF!*V`9oe*9^r?<7I+;e~noO z4glT3+5OKS(*z1HcXZ|;b{BS`kUyt4`YG2)% z#!rr#E=T}p8NI731Ext%(pJX3zEP5WV$;nRrOVjw3tog*HZ|VBYBC*yppA{KieQJ=y)v_g;^P9VK zJX+;dCcW6MiX%Nv-*b{2bHCDbTnuMhk2ZRpdVk~Ui!)?plczn=^?x_{U53N!x*|tq zI&;-rGxa07@8j>Jt#vbrYgik+sxD&+>0pL>s_eE?IxDWH?{nA@k!g5(y>XMYQQGpS zYYyeFKb+t3kTyf7RqOHEYJ5j zFZZyyS*IVg;cfX|)4E{s8IeTKWnrgq_+u-_*Kk`8cHex4f5vIq_=Yhb6|0Kz|*&XP^gDC&E8S z^p1lGH6n=4@MW@b9!zgmfT?Q74UQ^~bk`D;^Vg+Ci zLIgdCpr@lt@b@Q-9udN}4g*BK2=u!nLR=$*m;@JQNML9%gJ~Vc3}DY0O~RA$b$n20 zu%AdBPX>YM$MgqXLjYI3F)W2|E`1##G=VqEKS(qR7<)`lHp}aqvBtlqqS^t^QSNCsYpqe$5Wgs|xW45pA2Ag;p#bQp94LlT9li>K)7d*I0w3KQ?4 zOQPWQjVK0050WR{z{p?}3CCa-h)TNOXsLvxJOL?vJ$)vHM55!pNJK+CnQ3H*H}upc z;fWNYhXL7utk3XZh)8)dj28t4`_q9tS^jiyCLt)mTht&lxUq$kovA8ONB7$uCqFvd z3k)z-UBU_ojri8!%JOF}W7CD!)YCW8)uT{Iz#>EgqOQ@mPAizfAs`ZkG)3>AiCTni zF$T&2!_tLu3J8eq16_6$1E2if#>O)H3FV;a|f){aB(@!r??o zVN7QXs}MpDV|t3@1hj^m7(Vm>Zzh;MzJ%+@eb(=Uf`=}VVW6i+#DiD8^zdXaPeZ(i zhrS`6q_4+ddU|>=^vTSz)I$Qj*x~eGriC}KBd`?+PtluxI1Q1b=8x?i?!y#%0T>Lg zD~wTBJ$++6vN1{bi@^y0c@>T5Ai$Xn5}v8APsWq=$)0#4FR~F{pH3%w=@JbMDD>eF z{c6B}T}1;tku(w$`eRizAqc0%5vQ6Ee&hDx4x{E#V4C53V15SEJ>lzo|HWoN-v8w1 zOSt_fS>SMgGx<~Y{o7oBo9js2jT@?aB($jz{YU}5>fi@hfM9M1cbRUj(&b62GH+!TpF0`)MuT$9tXJmGyqnw&U`Y zRaln$n+VyKQ(R|BbfR`!}_ zfgrY^prEzAeZtWdIU8bP{A7|FrM)k^YRU%$1f*wWd9&Gj3knW|QA|vpG&ZV1+1c4o zo;=Y|lL{_Wj*gDLa`h^wu%@=Q)-Wz2B51$-+zuwRmPRv3wOO=CZmK%ifBtgeg8tL& z)2HR8PESiqbInksZn@@G+}8CB3EAV3Cvmg8n|R7){=RLc71ENocec0tD@>n`S-ED-2`7!6 zWnOj*&fZ99Y4HvV51%h51x@jj`Up@TQ>%$%fY{Qm$kUS|6M literal 1801 zcmcJQdobJS8poptajn*>suHI*y}8rWtqogbh%V`INFszpX+vB}v^Kg?TC_#0Z6Xd+ zQni(7DJf}+x}d_Qvub-cFfd=^2zb*wnM#6-Y z&%;-PDi7^lo$xH#F%l@e+K-)*L`&p>Q?|zUx2+Dk5X8^I(zZ9LJI7851cFU?&)lxp z>9UY;UShb5?!wqha@zcR%MXolg+-R9hEd%zrLyz}*hrvKPR7l@uTZ3t9HZLq{hJ;9 zuKXlunQAOueo~By&_V$K^h0Pu%(l*wDD&bd;(2oSAMHnnD zE-p*EZ$dePgM*D1g_Sd9d|t|=V%Lg#m8|mBea_>dZES7v1aF#phE0^b&OprxSu7c! z$K?hD1>Irgx>1*7WuHF%R@^0DSYQ+s=y_b4%3c_K(T(aJ;}CCHz%xS&R&yX{Vt#&P zGLg?zxb+FjEEi{sl-r>p&o*HrfqIBn*> z`u1dLS6(CO=f|XAamA@(%+m7H$XV+TkM^S>0 zEtD;&h2)j1!QF#y!S#_X#!pPnk|CHntkkRe^YHT(5r->X`n9(ZNuckI=}#lvH+10C zVj7q)$svOvFa{dtCR<+QgQZm%CVwwzhhfxu-s6 z9*+gkDCO~ZP|oWNB~z_d18a4N zZ2srX-IfnZ9is&+^TI8d#g^wIu%4k}UdY;l)kVBYeg|}S!g3UN(mEtFOW#pL+G!iZ zsIE+vqQaIj(Rr;CV6h+7osgKwn0{MKDKRq(tZQ8Ik(F8fy&EVd2j3Wbdpx^M5Jb?m z$?sMx8I--)t^z3sg6_@07U@uRYF(_bXiO2b1xrksXp{YRqCKNhy+8RWYp~8~9~K`D9@jH;eY-O&HF1w~&h8Y<=oL1#L{TAp${L*WNWQj`ldhT>VCBe@TU^jVxDlhp-jhU z1)C_F2Xy=y(EZ`2i^4S#2LjKWF^Av@{Q`mCE3=NQEgL{{$tAZhYn~+^v9+=Jm_))| zehPNbt8g~iKiYm(CEz8EKR^-ybEw}P^_s~3w(M87^wTr1q%j!g_~^t0WcI;M_e~=_ zDv9}z+MNcuJi%MnU-cXz$Xu(Aj*e;bR!(-7j*a0yv{9R2Fc?5)IUQ(a8}q3xWwPTB zvq_c&RyE!Cxd^wvd-y^X#ozztw-ufz6RMgZ$ATZazX^-s8~-0X;J>ZqmTh-JLPEh< zRL1&m#Q3D7%Xz-e0}nk!FGu1JVT!JB!A6h(JRV=u&%xnvKh)Ipp}Gpg^TmK|${Fb?eX$Oa=og)U0`)YHn)3a7XO>Y>R7y)^CGAxxaYy=TvynGPy%a zHj34X`zgHX!WSe~eL#Ex0Vjo`(du|CAc)l7X@qx{jWiWz*}$^3l1@W aE*q_fahNR`v-g1S3xq@j`_=mpuKx~mV_n(+ diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/Mirror.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/Mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..991408d9c0b4a870ea9ea6ccc67742cbdb358828 GIT binary patch literal 1233 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingCorners.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingCorners.png new file mode 100644 index 0000000000000000000000000000000000000000..f3eba46b64bf3194c53ed17f205c2b4011c01030 GIT binary patch literal 5335 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 HzrXdrpSG&$ literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingSides.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingSides.png new file mode 100644 index 0000000000000000000000000000000000000000..6d3b12dfd6db7e4fa693fab8043466b117c73a54 GIT binary patch literal 5255 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 PU5B)x;p#e7%F6!$21{|w literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/BlackTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/BlackTile.png new file mode 100644 index 0000000000000000000000000000000000000000..93e169df8f834511f9ac63d5504a41c1b9a4e917 GIT binary patch literal 9543 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/NumberTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/NumberTile.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8540d02b509f12c7f059648bbadf021efcedda GIT binary patch literal 10067 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/UnknownTile.png new file mode 100644 index 0000000000000000000000000000000000000000..850fbf127f04020fe83c6a4f6db75078967916ea GIT binary patch literal 9733 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 QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/WhiteTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/WhiteTile.png new file mode 100644 index 0000000000000000000000000000000000000000..fc2c683eb7860f47d4322fe0e73b0508099a89de GIT binary patch literal 9700 zcmeHLc{G&m`yXUY*@a4DN~m|3jbSpD>_yqiTFlB|W|$df2ni+9B3ZI!iP9olg(zhy zDN6P%@ghW)tVww zXp@<#p*8RiUi}I10as=~zzhh)6C7Y`&$7m`!QKoync_tPv;4hDU=oKy27x%86-TLs ze<}-qYgCyNh~HqkEkeZRK05JFdf0YdrPz)DpXpj#VF86Cn*La;%ZlH;h$t0N87TfDHCCNg0#+}qI&uY?JDbd)8rSCUgNfSlsq3f<6qSd-f&(ymiE zC+hBE%9nX6bNo~jHRU1K^ip2yT~+jpxmk&BKhBr!L2L~U??Gtg!9+U+he>h)RY6ua zKH720cx&)3d8!z*DD!-~j^#q{B4^h(_f!Ekn@1$PsCRt2(nfo<>T?5+WSYdjj(s=u zbfky&8J^9mU(^zNf1*fdKKdAO$5VV4+V_2(mzJiq=uW1iq189;PRzmm1&V4?p~cy% zyxG=}GvcY*>Mxrq*+Xw`+Mas*gD+lFT-6?iYvxGy}p4*1MY=Eo(Uu~1)AB1&L&2FiC&X##1>WZOW?(83e#Wu~Hx7dBy5t(U>l6g~@{S?632e#7 zl5XS`vJ~h7U9K<<*Qg~>%&kn`CGgPwx=bt~rq%ukWl;8} z3MP>{XJn2wYpOoDlX*PM_=vhilEo{he0L9W9ix9&$^8B(v)Jq_jM^mG802m@rfmE( zqbRkE>dcx0drBcex9(-s-fHo{ma5~hZ_;#&LZr_avkNjJo`Q=9XjW1=$N)irRIxBD~Wca&3knwLc zD9z}&EOP?_(>s%4k(D7MIkQ)5b1IDa?(@OY1YainyA?XvNDF=agh8nR`r&Badx@6L z1^R89f^o?U$P$4=J8JFfom+eQ-fdce#GjC@y?dhKqmD$*7B<%WsnO=Dt{t=&+QxGg z&%(y>>3=`PG_JCdyEFhwqHJcv&-Qb+p?6sOE-&gG16D6<;1pB z%hEx!G8g?>9TSsurIBFX7B@`{cMzzBU+e3&+kH~DJBEgNZU%>Bp{JNj12SI2BvlM1DmH;D(78>9xI|f3 zWuDDQRDnV!x0sRP1}Xhe2w(IWNC713bcpLy`AgvRYF9}sD}Q9+QP$vKMM6UF4!0oB zYZdcYw~3(EGSH>+<q{_v!uAETh2CZh-?piT}O;8v8>MR2o){}GOli3kc3w$ z!@n5uOBR$TN^I$vENX=8J9jm;mT=BKa7frs&gm5v(vze(a8_n&i;tCbLM*i33DRmV zmE&Xd?dZ1TS{#`>1QI#Uy?L)$bq-|rRMq}2!*WT`HkGNp7gnNpQ?{L2u%?a3mIl)B zmqYUEo3fHSIuXM?DPy)5^jsyYiP^A4uQI0qN50sEjg|eWx(AJ&oyu;$XeB>rZ8aXM zCA&zCKB0ZLh&=Jg^j@g$ym01hP{DGXO8^!W?O;Xq3oNLa;7unWLDTb3+EF6hV3kp) zrAoW`jL46QX5bh3NO)aCgT@fUzP3N~d&bJhBzmT0KDuV)yRt}kYHX!sj?i#_SwWM{ zyVHRIWWLy%jIGQ5XIFeaxLm7(g~wL*F*%uMz29E(@sG^(`_k^rg-$j3IQ`v5Vg=WJ ztyr@SVkM`;<#7joNiLmxpiL(vQ0Df<+KyX%~pn6Kv=53-hqtD5odk&ZVTsuenJzr2Ye@go_r&@46v6?4v7RIoB7_4LAja z;VDV*5z~!`kXH(P0|Nef-z^0sd293|MMcE-FTUA*bHkp9TN#*}f?wq^yvIWF_J_T} zx=SX721oQ%37CiG<|FQGO0E=c=ba0y`Vi+0DVJ*q+t@emE#)k-BkX1LX>`6z?zCdu=`No=rzO9qT7P!7? z2B(27-Y95_J`<=5KOeql|HeM?K7~H17*kuNPVulv=YBrC)Rg9wkk6Hc3%d-OluBdp zru&=xns812P1vTrh^u!j{C1nf@(zp-?4CB7CQLgCMuiuIU)+#pm4eY7Yt~*c{dzt6*J8O;#RRyh*(~QZL%eAqtvMy?pq##H1Mo=TxBUw#$*-E5* zL?=QX;dOQYRRhEpQmwLpQCPn*&Pu~dPB}sOPg~(55u*kcIq{ZHvgT~uh$JGER7GME zJ&BDkF1!&<(s&&ae_(G`YI=7@MS@K{Gv?11)L74$t(TKyT_6*GG{&gK)8n2%SB{^` z`;c)dGv6k|pf3h;z{&+~4|kdh=~t{7QEXP6J7|9}Dj~(5;Uajn$xg`1F^grIdzf<6 z!KmY)mVy7hX`?}kR8myYfx-i)UpmC89ErM>wa4!M{@IjKWZ56xr_&DH%bb7X)_kS8 zyd~Vj*JIh^%P3~FzGY*xQ_BvDC`v|7P)VR)=xc6Rm1QZ>zTPv-ESgh@6N=|c+T(( zyYhJQEy2xU0~Z-{akWmNTpB8N=JmsWJm9P;O=Die)ho)YHx?bd%>|o?aO33@f!YQb1);f z!MdU8Og3Tj=w|Z>OoW(886o9Q{pVsz>Pk5_74Ib6Hn}}uW_<}imv*qQ9-gozFDEas zO`}ZH~9iQ5{c>3#+CG1q&{Kq9WZzb1bu1DPRs_z_Yom#~NxP!RaygEWnf{xo~ z2(aCn$|buOC}$l^gH7qmiYSd4_u*t%TYh0af4y(Q#sqf6!86ap(We`YN=&YpL>nd< z=AAE>&z7k~l1^V#NBl(VCAr%IZz+Ud5~yvu2^I*GVTR#)JKowV7{ zEyzIKV8ZSzrHCRMJ*j2$SwY{^$)~d%E;y(=(2J^y4iA}roMe1u%rYMi9vbi;3VOtM z|IGcIeJ;?Ggzv+S3HN;7!_X6+06Aup8jP-je<|xJ~?l|C(-S!y1lrx%q zdR*}RQvIF!&102gAI3|H-cdfrFh-`!rwfj8J?h*$EL9_QkNy1fV@HaE!=s3NlM=V< zFQ43E-W%%KenQt)=}2#$&s|FQjbi%y$EC|-%Le=@BE`*n+TxoZ>%PEp9yZ+0t2qAX z@wDiWWoyQbO?{gxdJ&JQ9=2Y8P`+^z*_;8>7cptOlXG&i4w%#@^^tL3nhO#M=*P^J zyrP<$*?DoTj*M;Fw#c8H3+kF@A6iHq)~VC%m=0?dS?W}#){I-8pe>o3SU)#)v;Fu% zrPrqW*xWIyuG|V|p}2D-30dg&vO&B-I_;Qd0-H7$Si3l~F>*_78R5|m=jd|IZKaYM zE0=q-_a<%9zIDaak1yxiu-nuugbo*N8*Skya`Mvls^!A=% zIcE&V?*(P}nZCjF zQL8~h_VZcYPs?2xJeEOj4l1578;<2C+$s3@g!p|LS3aq-NEr+tHGFq{abQHHF?Va@ zQSGfK)20H0%ZBYnT;?m@1Wi@Z8=lO*Hf)G~GVsKCp<*WP+3l)(P#1P1-)o)V*g0}= z-oo8y?{`kfOa%0rlwV71{2sDwIcqgRXj6J}SY%6+wDfmgp2RQTG`5K}0p~m!3UHpY zw=l;L=u}l4k&Y*+a;VU;qFriG>4ms9rQChJ%Hy;bMUMRk0cbyk^4k#6s*X zEWrkJ1__K(MXAD|MjVPC0-_@X)@BgN7;8i0pAf(k7UIrgd1KVn*le~c8>vcXxT(Rl zw6xS<2sH!(3Rpmy{xlYj1En!{u0s64FeEVv42n04LZ^XOF>!dhFAEES0DACW@lm}k zEPlb$m_Jzn_)z2Eyw%{UFf}Sw?RO6*%g7G^`5DlE^kCWo$6_^W5|i%BAdrmwNHo^Y z-yw*EU;f^{46n6xhy*o~7l{g(GJ#Ry{}|H5%);`Q$0`MG6sq@{7eMwuELjxt-(>wG zwpGnqI==@3xc|cahxK2%uNebY78V#oI>C1}JTpTqWOaNDkxrlxF>AL-4GlaTMkGOj zixw0`hT)-j5{?MfK#-Ae6at4LlgPh8nbDXm9F0I)g#y4;DFBWZ4vy3yZh{FsDP?b2Z-=kWEA_7owG)^6XBH^KAEd&q^NfQCZ zqj3l*PMwU>LZY=akTBv}G(-Z%n9iW$fa#=Aac(3vZ<^bhVwG@=zNHx!f>4G1EwS{% zvB-b}um&hJBAw0rTV+e3l5AMGRX*Vw>YD0sbvPUe*VNL`ME-4bl*C{HwYZ83hp8eo z*EFjWg8|Y3sKu>TDgdx12eQE!Fi1ESoncF-dto7~L4j92*9si0{bO28DNMk^f3@a+ zR=o|$=f~5JMc_qQQ-Q&2g^R%vehk9I`H_CG0Qmh-5!`V!HxjVIf0oo=a>{?0E<8$; zK+w`8LrEwc9*Tk^$WSdJ2?j;s2{?df5|T*xLEJBNCY{V;;}|4;Hy~3W8=!>NvH>gq zBvWNA?O*n6chV|TFkpSd&`<=z7LLLoH8DseMD1s?YO8DduWYr|{udwGYX-k<0)XES z8L+tkdzIR+&FUv#z}WxI&(C@IZw>(luMhIC_+6)Kovweyz`rtHuda2v{uKlN%6PrH z{@>^l`un&;q5*F}Y~V0+!$cwZ|LC-vMy_|-*E{X&o%Z!k`+BE+z0sK-pp6o%KQ2&GiVPs+XPH?T@elD! zi|2uE&vc9GowR>dHY>M{|6I<%h><` literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index 24fdcf365..19e63a2a3 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -7,7 +7,7 @@ + fileCreationDisabled="false"/> - \ No newline at end of file + From cf72a8194e5aea711ac89f580425fa0ec31c25cf Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:55:21 -0400 Subject: [PATCH 41/77] EmptyFieldTest added test to EmptyFieldTest added comments to tests in EmptyFieldTest --- .../rules/EmptyFieldDirectRuleTest.java | 72 ++++++++++++++++--- .../rules/EmptyFieldDirectRule/EmptyFieldFail | 23 ++++++ 2 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java index 50e2bb970..c40bf5903 100644 --- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java @@ -27,6 +27,9 @@ public static void setUp() { treetent = new TreeTent(); } + // creates a 3x3 puzzle with no trees + // make the (1,1) tile GRASS + // checks if tiles logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyField", treetent); @@ -34,28 +37,38 @@ public void EmptyFieldTest() throws InvalidFileFormatException { TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); + // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + // change the board's cells considering the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); - board.addModifiedData(cell1); + // confirm there is a logical following of the EmptyField rule Assert.assertNull(RULE.checkRule(transition)); + // only the cell above should change following the rule + TreeTentCell c; for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - Point point = new Point(k, i); - if (point.equals(cell1.getLocation())) { - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } } } + // creates a 3x3 puzzle with 4 trees + // trees are at (0,0), (2,0), (0,2), and (2,2) + // make the (1,1) tile GRASS. + // checks if tiles logically follow the EmptyFieldDirectRule @Test public void DiagonalTreeTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/DiagonalTree", treetent); @@ -63,26 +76,65 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); + // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + //change the board's cells considering the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); - board.addModifiedData(cell1); + // confirm there is a logical following of the EmptyField rule Assert.assertNull(RULE.checkRule(transition)); + // only the cell above should change following the rule + TreeTentCell c; for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - Point point = new Point(k, i); - if (point.equals(cell1.getLocation())) { - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } } } + + // creates a 3x3 puzzle with 4 trees + // trees are at (0,1), (1,0), (1,2), and (2,1) + // make the center tile GRASS. + // checks if tiles don't logically follow the EmptyFieldDirectRule + @Test + public void EmptyFieldTestFail() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + //change the board's cells considering breaking the EmptyField rule + TreeTentCell cell1 = board.getCell(1, 1); + cell1.setData(TreeTentType.GRASS); + board.addModifiedData(cell1); + + // confirm there is not a logical following of the EmptyField rule + Assert.assertNotNull(RULE.checkRule(transition)); + + // the cells should not follow the rule + TreeTentCell c; + for (int i = 0; i < board.getWidth(); i++) { + for (int j = 0; j < board.getHeight(); j++) { + c = board.getCell(j, i); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } } diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail new file mode 100644 index 000000000..f826667e3 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From ef8d09e02ff48ded15523f5d23f803accea4cafe Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:11:06 -0400 Subject: [PATCH 42/77] editted comments --- .../java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java index c40bf5903..fa9706cd6 100644 --- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java @@ -106,7 +106,7 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { // creates a 3x3 puzzle with 4 trees // trees are at (0,1), (1,0), (1,2), and (2,1) - // make the center tile GRASS. + // make the (1,1) tile GRASS. // checks if tiles don't logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTestFail() throws InvalidFileFormatException { From f9cb9cf041bd3ab21f36bc8fa48f8bb2e9d809a3 Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:28:16 -0400 Subject: [PATCH 43/77] added more tests --- .../rules/EmptyFieldDirectRuleTest.java | 132 ++++++++++++++++++ .../EmptyFieldDirectRule/EmptyFieldFailBottom | 20 +++ .../EmptyFieldDirectRule/EmptyFieldFailLeft | 20 +++ .../EmptyFieldDirectRule/EmptyFieldFailRight | 20 +++ .../EmptyFieldDirectRule/EmptyFieldFailTop | 20 +++ 5 files changed, 212 insertions(+) create mode 100644 src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom create mode 100644 src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft create mode 100644 src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight create mode 100644 src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java index fa9706cd6..b7ec8eb02 100644 --- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java @@ -136,5 +136,137 @@ public void EmptyFieldTestFail() throws InvalidFileFormatException { } } } + + // creates a 3x3 puzzle with 1 tree + // tree is at (1,0) + // make the (1,1) tile GRASS. + // checks if tiles don't logically follow the EmptyFieldDirectRule + @Test + public void EmptyFieldTestFailTop() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + //change the board's cells considering breaking the EmptyField rule + TreeTentCell cell1 = board.getCell(1, 1); + cell1.setData(TreeTentType.GRASS); + board.addModifiedData(cell1); + + // confirm there is not a logical following of the EmptyField rule + Assert.assertNotNull(RULE.checkRule(transition)); + + // the cells should not follow the rule + TreeTentCell c; + for (int i = 0; i < board.getWidth(); i++) { + for (int j = 0; j < board.getHeight(); j++) { + c = board.getCell(j, i); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + + // creates a 3x3 puzzle with 1 tree + // tree is at (1,2) + // make the (1,1) tile GRASS. + // checks if tiles don't logically follow the EmptyFieldDirectRule + @Test + public void EmptyFieldTestFailTopBottom() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + //change the board's cells considering breaking the EmptyField rule + TreeTentCell cell1 = board.getCell(1, 1); + cell1.setData(TreeTentType.GRASS); + board.addModifiedData(cell1); + + // confirm there is not a logical following of the EmptyField rule + Assert.assertNotNull(RULE.checkRule(transition)); + + // the cells should not follow the rule + TreeTentCell c; + for (int i = 0; i < board.getWidth(); i++) { + for (int j = 0; j < board.getHeight(); j++) { + c = board.getCell(j, i); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + + // creates a 3x3 puzzle with 1 tree + // tree is at (0,1) + // make the (1,1) tile GRASS. + // checks if tiles don't logically follow the EmptyFieldDirectRule + @Test + public void EmptyFieldTestFailLeft() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + //change the board's cells considering breaking the EmptyField rule + TreeTentCell cell1 = board.getCell(1, 1); + cell1.setData(TreeTentType.GRASS); + board.addModifiedData(cell1); + + // confirm there is not a logical following of the EmptyField rule + Assert.assertNotNull(RULE.checkRule(transition)); + + // the cells should not follow the rule + TreeTentCell c; + for (int i = 0; i < board.getWidth(); i++) { + for (int j = 0; j < board.getHeight(); j++) { + c = board.getCell(j, i); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + + // creates a 3x3 puzzle with 1 tree + // tree is at (2,1) + // make the (1,1) tile GRASS. + // checks if tiles don't logically follow the EmptyFieldDirectRule + @Test + public void EmptyFieldTestFailRight() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + //change the board's cells considering breaking the EmptyField rule + TreeTentCell cell1 = board.getCell(1, 1); + cell1.setData(TreeTentType.GRASS); + board.addModifiedData(cell1); + + // confirm there is not a logical following of the EmptyField rule + Assert.assertNotNull(RULE.checkRule(transition)); + + // the cells should not follow the rule + TreeTentCell c; + for (int i = 0; i < board.getWidth(); i++) { + for (int j = 0; j < board.getHeight(); j++) { + c = board.getCell(j, i); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } } diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom new file mode 100644 index 000000000..80deadaea --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft new file mode 100644 index 000000000..d19e01daf --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight new file mode 100644 index 000000000..bf3954964 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop new file mode 100644 index 000000000..8eaa974ea --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c8bd17e9871405ff986063856393c10be5993766 Mon Sep 17 00:00:00 2001 From: Rorymar <36866664+Rorymar@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:33:50 -0400 Subject: [PATCH 44/77] Added NoTentForTree Tests Added NoTentForTree Tests involving more complex instances for the contradiction to consider. --- .../NoTentForTreeContradictionRuleTest.java | 66 ++++++++++++++++++- .../NoTentForTreeDiagonal | 21 ++++++ .../NoTentForTreeTwoTrees | 21 ++++++ .../NoTentForTreeTwoTreesDiagonal | 21 ++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal create mode 100644 src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees create mode 100644 src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal diff --git a/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java index b2d47e222..52aea28c0 100644 --- a/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java @@ -28,8 +28,12 @@ public static void setUp() { treetent = new TreeTent(); } + /** + * @throws InvalidFileFormatException + * Tests if a tree is next to only grass in a 2x2 grid triggers the contradiction + */ @Test - public void NoTentForTreeContradictionRule_() throws InvalidFileFormatException { + public void NoTentForTreeContradictionRule_Basic() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -43,5 +47,65 @@ public void NoTentForTreeContradictionRule_() throws InvalidFileFormatException Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } + + /** + * @throws InvalidFileFormatException + * Tests similarly to above, but now with a tent diagonally next to the tree, which should still contradict + */ + @Test + public void NoTentForTreeContradictionRule_Diagonal() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + + /** + * @throws InvalidFileFormatException + * Tests that adjacent trees do not allow a pass + */ + @Test + public void NoTentForTreeContradictionRule_TwoTrees() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } + + /** + * @throws InvalidFileFormatException + * Tests similarly to above, but now with a tent diagonally next to two trees, which should still contradict on one. + */ + @Test + public void NoTentForTreeContradictionRule_TwoTreesDiagonal() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); + } } diff --git a/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal new file mode 100644 index 000000000..af0b49e31 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees new file mode 100644 index 000000000..5111e0585 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal new file mode 100644 index 000000000..10391b85c --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From e8fce28332c5cc3da49a9c67d75dc3f4b7c793fe Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Fri, 13 Oct 2023 16:37:57 -0400 Subject: [PATCH 45/77] remainder of the tests for the nurikabe contradiction rules This commit contains the remainder of the unit tests for the Nurikabe contradiction rules, with the corresponding board files and appropriate comments --- .../IsolateBlackContradictionRuleTest.java | 75 +++++++++++++----- .../MultipleNumbersContradictionRuleTest.java | 24 ++++++ .../rules/NoNumbersContradictionRuleTest.java | 7 +- .../TooFewSpacesContradictionRuleTest.java | 34 ++++++++ .../TooManySpacesContradictionRuleTest.java | 24 ++++++ ...achableWhiteCellContradictionRuleTest.java | 79 +++++++++++++++++++ .../DiagonalIsolateBlack | 15 ++++ .../FalseContradiction | 16 ++++ .../InsufficientSpace | 17 ++++ .../ExtraDiagonalSpace | 14 ++++ .../AllCellsReachable | 13 +++ .../SimpleUnreachableTest | 10 +++ 12 files changed, 309 insertions(+), 19 deletions(-) create mode 100644 src/test/java/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRuleTest.java create mode 100644 src/test/resources/puzzles/nurikabe/rules/IsolateBlackContradictionRule/DiagonalIsolateBlack create mode 100644 src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/FalseContradiction create mode 100644 src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/InsufficientSpace create mode 100644 src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/ExtraDiagonalSpace create mode 100644 src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/AllCellsReachable create mode 100644 src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/SimpleUnreachableTest diff --git a/src/test/java/puzzles/nurikabe/rules/IsolateBlackContradictionRuleTest.java b/src/test/java/puzzles/nurikabe/rules/IsolateBlackContradictionRuleTest.java index 11a5e8f7d..ce578d853 100644 --- a/src/test/java/puzzles/nurikabe/rules/IsolateBlackContradictionRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/IsolateBlackContradictionRuleTest.java @@ -1,6 +1,7 @@ package puzzles.nurikabe.rules; import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; import legup.MockGameBoardFacade; import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; @@ -25,25 +26,63 @@ public static void setUp() { nurikabe = new Nurikabe(); } + /** + * Tests the Isolate Black contradiction rule for a black squares in the corner, separated by a diagonal of white squares + */ @Test public void IsolateBlackContradictionRule_SimpleIsolateBlackTest() throws InvalidFileFormatException { -// TestUtilities.importTestBoard("puzzles/nurikabe/rules/IsolateBlackContradictionRule/SimpleIsolateBlack", nurikabe); -// TreeNode rootNode = nurikabe.getTree().getRootNode(); -// TreeTransition transition = rootNode.getChildren().get(0); -// transition.setRule(RULE); -// -// Assert.assertNull(RULE.checkContradiction((NurikabeBoard)transition.getBoard())); -// -// NurikabeBoard board = (NurikabeBoard)transition.getBoard(); -// for(int i = 0; i < board.getHeight(); i++) { -// for(int k = 0; k < board.getWidth(); k++) { -// Point point = new Point(k, i); -// if(point.equals(new Point(0, 0)) || point.equals(new Point(3, 3))) { -// Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); -// } else { -// Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); -// } -// } -// } + TestUtilities.importTestBoard("puzzles/nurikabe/rules/IsolateBlackContradictionRule/SimpleIsolateBlack", nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeCell cell1 = board.getCell(0,0); + NurikabeCell cell2 = board.getCell(2,2); + + Assert.assertNull(RULE.checkContradiction((NurikabeBoard)transition.getBoard())); + + for(int i=0; i + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/FalseContradiction b/src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/FalseContradiction new file mode 100644 index 000000000..0f14d6916 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/FalseContradiction @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/InsufficientSpace b/src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/InsufficientSpace new file mode 100644 index 000000000..22163947a --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/InsufficientSpace @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/ExtraDiagonalSpace b/src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/ExtraDiagonalSpace new file mode 100644 index 000000000..5d9ad70e1 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/ExtraDiagonalSpace @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/AllCellsReachable b/src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/AllCellsReachable new file mode 100644 index 000000000..dbc5fadb2 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/AllCellsReachable @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/SimpleUnreachableTest b/src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/SimpleUnreachableTest new file mode 100644 index 000000000..3d456bdb0 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/UnreachableWhiteCellContradictionRule/SimpleUnreachableTest @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file From 7eddb36d60b65cff9ed0ff93bb6392060c9c4c5f Mon Sep 17 00:00:00 2001 From: Rorymar <36866664+Rorymar@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:09:13 -0400 Subject: [PATCH 46/77] Added SurroundTent Tests Added tests for the SurroundTestWithGrass direct rule --- .../SurroundTentWithGrassDirectRuleTest.java | 88 +++++++++++++++++++ .../SurroundTentWithGrassBad | 20 +++++ .../SurroundTentWithGrassTrees | 28 ++++++ 3 files changed, 136 insertions(+) create mode 100644 src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad create mode 100644 src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees diff --git a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java index 14afb4dc0..999405747 100644 --- a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java @@ -27,6 +27,10 @@ public static void setUp() { treetent = new TreeTent(); } + /** + * @throws InvalidFileFormatException + * Test to check if all adjacent and diagonals not filled with a tree are filled with grass + */ @Test public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrass", treetent); @@ -58,6 +62,90 @@ public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatExcepti } } } + + /** + * @throws InvalidFileFormatException + * + * Test with a 3x3 board with an absolutely empty area aside from a tent in the middle + * While such a situation is an illegal treetent setup, this direct rule doesn't consider that aspect, so its ok in this context + */ + @Test + public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0, 0); + cell1.setData(TreeTentType.GRASS); + TreeTentCell cell2 = board.getCell(1, 0); + cell2.setData(TreeTentType.GRASS); + TreeTentCell cell3 = board.getCell(2, 0); + cell3.setData(TreeTentType.GRASS); + TreeTentCell cell4 = board.getCell(0, 1); + cell4.setData(TreeTentType.GRASS); + //Skip (1,1) due to being the Tent + TreeTentCell cell5 = board.getCell(2, 1); + cell5.setData(TreeTentType.GRASS); + TreeTentCell cell6 = board.getCell(0, 2); + cell6.setData(TreeTentType.GRASS); + TreeTentCell cell7 = board.getCell(1, 2); + cell7.setData(TreeTentType.GRASS); + TreeTentCell cell8 = board.getCell(2, 2); + cell8.setData(TreeTentType.GRASS); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + //board.addModifiedData(cell4); + board.addModifiedData(cell5); + board.addModifiedData(cell6); + board.addModifiedData(cell7); + board.addModifiedData(cell8); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || + point.equals(cell3.getLocation()) || //point.equals(cell4.getLocation()) || + point.equals(cell5.getLocation()) || point.equals(cell6.getLocation()) || + point.equals(cell7.getLocation()) || point.equals(cell8.getLocation()) + ) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * + * Test to see if the rule passes even if no grass was able to be placed due to the presence of trees. + */ + @Test + public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++){ + for (int k = 0; k < board.getWidth(); k++){ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } } diff --git a/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad new file mode 100644 index 000000000..6065d42cd --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees new file mode 100644 index 000000000..e8fef64dc --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From e05d8e5753c4fd2f334f47482f8b1884940f80b6 Mon Sep 17 00:00:00 2001 From: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:11:06 -0400 Subject: [PATCH 47/77] Issue 624 nonmodifiable contradiction (#649) * Update TreeView.java Made board unmodifiable when a selected tree node is part of a contradiction branch * Added contradiction check to mouse event Removed previous changes from TreeView Added board modifiability update to TreeController for each mouse press on the proof tree: modifiability is determined by whether or not the current selected node is part of a contradiction branch * Added comments Added comments to TreeController * Updated modifiability on rule application Set board modifiability of the current transition to false when a contradiction is applied in ValidateContradictionRuleCommand; Removed previous change to TreeController. --- .../rpi/legup/controller/TreeController.java | 3 +- .../ValidateContradictionRuleCommand.java | 2 + .../ui/proofeditorui/treeview/TreeView.java | 1538 ++++++++--------- 3 files changed, 759 insertions(+), 784 deletions(-) diff --git a/src/main/java/edu/rpi/legup/controller/TreeController.java b/src/main/java/edu/rpi/legup/controller/TreeController.java index b954edd79..6eae4ac3b 100644 --- a/src/main/java/edu/rpi/legup/controller/TreeController.java +++ b/src/main/java/edu/rpi/legup/controller/TreeController.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.proofeditorui.treeview.*; @@ -42,7 +43,7 @@ public void mousePressed(MouseEvent e) { /** * Mouse Released event sets the cursor back to the default cursor and reset info for panning - * + * Set board modifiability * @param e MouseEvent object */ @Override diff --git a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java index a0f58a571..23f8dce21 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java @@ -66,9 +66,11 @@ public void executeCommand() { if (transition == null) { transition = tree.addNewTransition(treeNode); transition.setRule(newRule); + transition.getBoard().setModifiable(false); tree.addTreeElement(transition); } else { + transition.getBoard().setModifiable(false); tree.addTreeElement(treeNode, transition); } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java index fa08972ed..ecf59146d 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java @@ -1,784 +1,756 @@ -package edu.rpi.legup.ui.proofeditorui.treeview; - -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.controller.TreeController; -import edu.rpi.legup.model.observer.ITreeListener; -import edu.rpi.legup.model.tree.Tree; -import edu.rpi.legup.model.tree.TreeElement; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.ui.ScrollView; -import edu.rpi.legup.utility.DisjointSets; - -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.*; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import static edu.rpi.legup.model.tree.TreeElementType.NODE; -import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; - -public class TreeView extends ScrollView implements ITreeListener { - private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); - - private static final int TRANS_GAP = 5; - - private static final int NODE_GAP_WIDTH = 70; - private static final int NODE_GAP_HEIGHT = 15; - - private static final int BORDER_GAP_HEIGHT = 20; - private static final int BORDER_GAP_WIDTH = 20; - - private static final int BORDER_SPACING = 100; - - private TreeNodeView nodeHover; - - private ArrayList currentStateBoxes; - private Rectangle bounds = new Rectangle(0, 0, 0, 0); - - private Tree tree; - private TreeNodeView rootNodeView; - private Map viewMap; - private Dimension dimension; - - private TreeViewSelection selection; - - public TreeView(TreeController treeController) { - super(treeController); - currentStateBoxes = new ArrayList<>(); - setSize(dimension = new Dimension(100, 200)); - setPreferredSize(new Dimension(640, 160)); - - viewMap = new HashMap<>(); - - selection = new TreeViewSelection(); - } - - public TreeViewSelection getSelection() { - return selection; - } - - /** - * Gets the tree node puzzleElement that the mouse is hovering over - * - * @return tree node puzzleElement that the mouse is hovering over - */ - public TreeNodeView getNodeHover() { - return nodeHover; - } - - /** - * Sets the tree node puzzleElement that the mouse is hovering over - * - * @param nodeHover tree node puzzleElement the mouse is hovering over - */ - public void setNodeHover(TreeNodeView nodeHover) { - this.nodeHover = nodeHover; - } - - /** - * Gets the TreeElementView by the specified point or null if no view exists at the specified point - * - * @param point location to query for a view - * @return TreeElementView at the point specified, otherwise null - */ - public TreeElementView getTreeElementView(Point point) { - return getTreeElementView(point, rootNodeView); - } - - /** - * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or - * the view specified is null - * - * @param point location to query for a view - * @param elementView view to determine if the point is contained within it - * @return TreeElementView at the point specified, otherwise null - */ - private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { - if (elementView == null) { - return null; - } - else { - if (elementView.contains(point) && elementView.isVisible()) { - if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { - return null; - } - return elementView; - } - else { - if (elementView.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) elementView; - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - TreeElementView view = getTreeElementView(point, transitionView); - if (view != null) { - return view; - } - } - } - else { - TreeTransitionView transitionView = (TreeTransitionView) elementView; - return getTreeElementView(point, transitionView.getChildView()); - } - } - } - return null; - } - - public void updateTreeView(Tree tree) { - this.tree = tree; - if (selection.getSelectedViews().size() == 0) { - selection.newSelection(new TreeNodeView(tree.getRootNode())); - } - repaint(); - } - - /** - * Sets the tree associated with this TreeView - * - * @param tree tree - */ - public void setTree(Tree tree) { - this.tree = tree; - } - - public void updateTreeSize() { - if (GameBoardFacade.getInstance().getTree() == null) { - return; - } - setSize(bounds.getSize()); - } - - public void reset() { - if (bounds.x != 0 || bounds.y != 0) { - updateTreeSize(); - } - } - - public void zoomFit() { - double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); - double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); - zoomTo(Math.min(fitWidth, fitHeight)); - viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); - } - - /** - * Creates a customized viewport for the scroll pane - * - * @return viewport for the scroll pane - */ - @Override - protected JViewport createViewport() { - return new JViewport() { - @Override - protected LayoutManager createLayoutManager() { - return new ViewportLayout() { - @Override - public void layoutContainer(Container parent) { - Point point = viewport.getViewPosition(); - // determine the maximum x and y view positions - int mx = getCanvas().getWidth() - viewport.getWidth(); - int my = getCanvas().getHeight() - viewport.getHeight(); - // obey edge boundaries - if (point.x < 0) { - point.x = 0; - } - if (point.x > mx) { - point.x = mx; - } - if (point.y < 0) { - point.y = 0; - } - if (point.y > my) { - point.y = my; - } - // center margins - if (mx < 0) { - point.x = 0; - } - if (my < 0) { - point.y = my / 2; - } - viewport.setViewPosition(point); - } - }; - } - }; - } - - public void draw(Graphics2D graphics2D) { - currentStateBoxes.clear(); - Tree tree = GameBoardFacade.getInstance().getTree(); - if (tree != null) { - //setSize(bounds.getDimension()); - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - drawTree(graphics2D); - - dimension.width += BORDER_SPACING; - setSize(dimension); -// graphics2D.drawRect(0,0, dimension.width, dimension.height); - - if (selection.getHover() != null) { - drawMouseOver(graphics2D); - } - } - } - - public void zoomReset() { - zoomTo(1.0); - viewport.setViewPosition(new Point(0, 0)); - } - - private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { - if (nodeView != null) { - nodeView.draw(graphics2D); - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - transitionView.draw(graphics2D); - redrawTree(graphics2D, transitionView.getChildView()); - } - } - } - - public void removeTreeElement(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - nodeView.getParentView().setChildView(null); - } - else { - TreeTransitionView transitionView = (TreeTransitionView) view; - transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); - } - } - - /** - * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image - * - * @param g the graphics to use to draw - */ - public void drawMouseOver(Graphics2D g) { - if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { - TreeTransition transition = (TreeTransition) selection.getHover().treeElement; - int imgWidth = 100; - int imgHeight = 100; - - BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); - image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); - Point mousePoint = selection.getMousePoint(); - g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); - } - } - - public void resetView() { - this.tree = null; - this.rootNodeView = null; - this.selection.clearSelection(); - this.selection.clearHover(); - } - - /** - * Called when a tree puzzleElement is added to the tree - * - * @param treeElement TreeElement that was added to the tree - */ - @Override - public void onTreeElementAdded(TreeElement treeElement) { - if (treeElement.getType() == NODE) { - addTreeNode((TreeNode) treeElement); - } - else { - addTreeTransition((TreeTransition) treeElement); - } - repaint(); - } - - /** - * Called when a tree puzzleElement is removed from the tree - * - * @param element TreeElement that was removed to the tree - */ - @Override - public void onTreeElementRemoved(TreeElement element) { - if (element.getType() == NODE) { - TreeNode node = (TreeNode) element; - TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); - - nodeView.getParentView().setChildView(null); - removeTreeNode(node); - } - else { - TreeTransition trans = (TreeTransition) element; - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - - transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); - removeTreeTransition(trans); - } - repaint(); - } - - /** - * Called when the tree selection was changed - * - * @param selection tree selection that was changed - */ - @Override - public void onTreeSelectionChanged(TreeViewSelection selection) { - this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); - selection.getSelectedViews().forEach(v -> v.setSelected(true)); - - this.selection = selection; - -// List selectedViews = selection.getSelectedViews(); -// Point avg = new Point(); -// for(TreeElementView elementView : selectedViews) { -// if(elementView.getType() == NODE) { -// TreeNodeView nodeView = (TreeNodeView)elementView; -// avg.x += nodeView.getX(); -// avg.y += nodeView.getY(); -// } else { -// TreeTransitionView transitionView = (TreeTransitionView) elementView; -// avg.x += transitionView.getEndX(); -// avg.y += transitionView.getEndY(); -// } -// } -// avg.x /= selectedViews.size(); -// avg.y /= selectedViews.size(); -// -// Point pos = viewport.getViewPosition(); -// if(pos.x < avg.x) { -// pos.x = avg.x - pos.x; -// } -// if(pos.y < avg.y) { -// pos.y = avg.y - pos.y; -// } -// -// viewport.setViewPosition(pos); - - repaint(); - } - - /** - * Called when the model has finished updating the tree. - */ - @Override - public void onUpdateTree() { - repaint(); - } - - /** - * Gets the TreeElementView by the corresponding TreeElement associated with it - * - * @param element TreeElement of the view - * @return TreeElementView of the TreeElement associated with it - */ - public TreeElementView getElementView(TreeElement element) { - return viewMap.get(element); - } - - private void removeTreeNode(TreeNode node) { - viewMap.remove(node); - node.getChildren().forEach(t -> removeTreeTransition(t)); - } - - private void removeTreeTransition(TreeTransition trans) { - viewMap.remove(trans); - if (trans.getChildNode() != null) { - removeTreeNode(trans.getChildNode()); - } - } - - private void addTreeNode(TreeNode node) { - TreeTransition parent = node.getParent(); - - TreeNodeView nodeView = new TreeNodeView(node); - TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); - - nodeView.setParentView(parentView); - parentView.setChildView(nodeView); - - viewMap.put(node, nodeView); - - if (!node.getChildren().isEmpty()) { - node.getChildren().forEach(t -> addTreeTransition(t)); - } - } - - private void addTreeTransition(TreeTransition trans) { - List parents = trans.getParents(); - - TreeTransitionView transView = new TreeTransitionView(trans); - for (TreeNode parent : parents) { - TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); - transView.addParentView(parentNodeView); - parentNodeView.addChildrenView(transView); - } - - viewMap.put(trans, transView); - - if (trans.getChildNode() != null) { - addTreeNode(trans.getChildNode()); - } - } - - ///New Draw Methods - - public void drawTree(Graphics2D graphics2D) { - if (tree == null) { - LOGGER.error("Unable to draw tree."); - } - else { - if (rootNodeView == null) { - rootNodeView = new TreeNodeView(tree.getRootNode()); - - LOGGER.debug("Creating new views for tree view."); - createViews(rootNodeView); - - selection.newSelection(rootNodeView); - } - - dimension = new Dimension(0, 0); - calcSpan(rootNodeView); - rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); - - calculateViewLocations(rootNodeView, 0); - dimension.height = (int) rootNodeView.getSpan(); - - redrawTree(graphics2D, rootNodeView); - LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); - } - } - - public void createViews(TreeNodeView nodeView) { - if (nodeView != null) { - viewMap.put(nodeView.getTreeElement(), nodeView); - - TreeNode node = nodeView.getTreeElement(); - for (TreeTransition trans : node.getChildren()) { - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - if (transView != null) { - nodeView.addChildrenView(transView); - transView.addParentView(nodeView); - break; - } - transView = new TreeTransitionView(trans); - - viewMap.put(transView.getTreeElement(), transView); - - transView.addParentView(nodeView); - nodeView.addChildrenView(transView); - - TreeNode childNode = trans.getChildNode(); - if (childNode != null) { - TreeNodeView childNodeView = new TreeNodeView(childNode); - viewMap.put(childNodeView.getTreeElement(), childNodeView); - - childNodeView.setParentView(transView); - transView.setChildView(childNodeView); - - createViews(childNodeView); - } - } - } - } - - public void calculateViewLocations(TreeNodeView nodeView, int depth) { - nodeView.setDepth(depth); - int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; - nodeView.setX(xLoc); - dimension.width = Math.max(dimension.width, xLoc); - - TreeTransitionView parentTransView = nodeView.getParentView(); - int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); - nodeView.setY(yLoc); - - ArrayList children = nodeView.getChildrenViews(); - switch (children.size()) { - case 0: - break; - case 1: { - TreeTransitionView childView = children.get(0); - - List parentsViews = childView.getParentViews(); - if (parentsViews.size() == 1) { - childView.setEndY(yLoc); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - else { - if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { - int yAvg = 0; - for (int i = 0; i < parentsViews.size(); i++) { - TreeNodeView parentNodeView = parentsViews.get(i); - depth = Math.max(depth, parentNodeView.getDepth()); - yAvg += parentNodeView.getY(); - - Point lineStartPoint = childView.getLineStartPoint(i); - lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = parentNodeView.getY(); - } - yAvg /= parentsViews.size(); - childView.setEndY(yAvg); - - childView.setDepth(depth); - - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - } - break; - } - default: { - int span = 0; - for (TreeTransitionView childView : children) { - span += childView.getSpan(); - } - - span = (int) ((nodeView.getSpan() - span) / 2); - for (int i = 0; i < children.size(); i++) { - TreeTransitionView childView = children.get(i); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); - - span += childView.getSpan(); - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - break; - } - } - } - - public void calcSpan(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - calcSpan(childView); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - ArrayList newChildrenViews = new ArrayList<>(); - for (TreeTransition trans : node.getChildren()) { - newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); - } - nodeView.setChildrenViews(newChildrenViews); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - calcSpan(mergePointView); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - calcSpan(transView); - span += transView.getSpan(); - } - } - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view - * specified as stop. Stop tree puzzleElement is NOT included in the span calculation - * - * @param view - * @param stop - */ - private void subCalcSpan(TreeElementView view, TreeElementView stop) { - //safe-guard for infinite loop - if (view == stop) { - return; - } - - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - if (childView == stop) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - subCalcSpan(childView, stop); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - subCalcSpan(mergePointView, stop); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - subCalcSpan(transView, stop); - span += transView.getSpan(); - } - } - - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null || nodeView == stop) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in - * relative order in the list of child transitions of the specified node - * - * @param node root node of the branches - * @param branches DisjointSets of the child branches of the specified node which determine which branches merge - */ - private void reorderBranches(TreeNode node, DisjointSets branches) { - List children = node.getChildren(); - List> mergingSets = branches.getAllSets(); - - List> newOrder = new ArrayList<>(); - for (Set set : mergingSets) { - List mergeBranch = new ArrayList<>(); - newOrder.add(mergeBranch); - children.forEach(t -> { - if (set.contains(t)) { - mergeBranch.add(t); - } - }); - mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> - children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); - } - - newOrder.sort((List b1, List b2) -> { - int low1 = -1; - int low2 = -1; - for (TreeTransition t1 : b1) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - for (TreeTransition t1 : b2) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - return low1 < low2 ? -1 : 1; - }); - - List newChildren = new ArrayList<>(); - newOrder.forEach(l -> newChildren.addAll(l)); - node.setChildren(newChildren); - } +package edu.rpi.legup.ui.proofeditorui.treeview; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.controller.TreeController; +import edu.rpi.legup.model.observer.ITreeListener; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.ui.ScrollView; +import edu.rpi.legup.utility.DisjointSets; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.*; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import static edu.rpi.legup.model.tree.TreeElementType.NODE; +import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; + +public class TreeView extends ScrollView implements ITreeListener { + private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); + + private static final int TRANS_GAP = 5; + + private static final int NODE_GAP_WIDTH = 70; + private static final int NODE_GAP_HEIGHT = 15; + + private static final int BORDER_GAP_HEIGHT = 20; + private static final int BORDER_GAP_WIDTH = 20; + + private static final int BORDER_SPACING = 100; + + private TreeNodeView nodeHover; + + private ArrayList currentStateBoxes; + private Rectangle bounds = new Rectangle(0, 0, 0, 0); + + private Tree tree; + private TreeNodeView rootNodeView; + private Map viewMap; + private Dimension dimension; + + private TreeViewSelection selection; + + public TreeView(TreeController treeController) { + super(treeController); + currentStateBoxes = new ArrayList<>(); + setSize(dimension = new Dimension(100, 200)); + setPreferredSize(new Dimension(640, 160)); + + viewMap = new HashMap<>(); + + selection = new TreeViewSelection(); + } + + public TreeViewSelection getSelection() { + return selection; + } + + /** + * Gets the tree node puzzleElement that the mouse is hovering over + * + * @return tree node puzzleElement that the mouse is hovering over + */ + public TreeNodeView getNodeHover() { + return nodeHover; + } + + /** + * Sets the tree node puzzleElement that the mouse is hovering over + * + * @param nodeHover tree node puzzleElement the mouse is hovering over + */ + public void setNodeHover(TreeNodeView nodeHover) { + this.nodeHover = nodeHover; + } + + /** + * Gets the TreeElementView by the specified point or null if no view exists at the specified point + * + * @param point location to query for a view + * @return TreeElementView at the point specified, otherwise null + */ + public TreeElementView getTreeElementView(Point point) { + return getTreeElementView(point, rootNodeView); + } + + /** + * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or + * the view specified is null + * + * @param point location to query for a view + * @param elementView view to determine if the point is contained within it + * @return TreeElementView at the point specified, otherwise null + */ + private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { + if (elementView == null) { + return null; + } + else { + if (elementView.contains(point) && elementView.isVisible()) { + if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { + return null; + } + return elementView; + } + else { + if (elementView.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) elementView; + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + TreeElementView view = getTreeElementView(point, transitionView); + if (view != null) { + return view; + } + } + } + else { + TreeTransitionView transitionView = (TreeTransitionView) elementView; + return getTreeElementView(point, transitionView.getChildView()); + } + } + } + return null; + } + + public void updateTreeView(Tree tree) { + this.tree = tree; + if (selection.getSelectedViews().size() == 0) { + selection.newSelection(new TreeNodeView(tree.getRootNode())); + } + repaint(); + } + + /** + * Sets the tree associated with this TreeView + * + * @param tree tree + */ + public void setTree(Tree tree) { + this.tree = tree; + } + + public void updateTreeSize() { + if (GameBoardFacade.getInstance().getTree() == null) { + return; + } + setSize(bounds.getSize()); + } + + public void reset() { + if (bounds.x != 0 || bounds.y != 0) { + updateTreeSize(); + } + } + + public void zoomFit() { + double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); + double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); + zoomTo(Math.min(fitWidth, fitHeight)); + viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); + } + + /** + * Creates a customized viewport for the scroll pane + * + * @return viewport for the scroll pane + */ + @Override + protected JViewport createViewport() { + return new JViewport() { + @Override + protected LayoutManager createLayoutManager() { + return new ViewportLayout() { + @Override + public void layoutContainer(Container parent) { + Point point = viewport.getViewPosition(); + // determine the maximum x and y view positions + int mx = getCanvas().getWidth() - viewport.getWidth(); + int my = getCanvas().getHeight() - viewport.getHeight(); + // obey edge boundaries + if (point.x < 0) { + point.x = 0; + } + if (point.x > mx) { + point.x = mx; + } + if (point.y < 0) { + point.y = 0; + } + if (point.y > my) { + point.y = my; + } + // center margins + if (mx < 0) { + point.x = 0; + } + if (my < 0) { + point.y = my / 2; + } + viewport.setViewPosition(point); + } + }; + } + }; + } + + public void draw(Graphics2D graphics2D) { + currentStateBoxes.clear(); + Tree tree = GameBoardFacade.getInstance().getTree(); + if (tree != null) { + //setSize(bounds.getDimension()); + graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + drawTree(graphics2D); + + dimension.width += BORDER_SPACING; + setSize(dimension); +// graphics2D.drawRect(0,0, dimension.width, dimension.height); + + if (selection.getHover() != null) { + drawMouseOver(graphics2D); + } + } + } + + public void zoomReset() { + zoomTo(1.0); + viewport.setViewPosition(new Point(0, 0)); + } + + private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { + if (nodeView != null) { + nodeView.draw(graphics2D); + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + transitionView.draw(graphics2D); + redrawTree(graphics2D, transitionView.getChildView()); + } + } + } + + public void removeTreeElement(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + nodeView.getParentView().setChildView(null); + } + else { + TreeTransitionView transitionView = (TreeTransitionView) view; + transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); + } + } + + /** + * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image + * + * @param g the graphics to use to draw + */ + public void drawMouseOver(Graphics2D g) { + if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { + TreeTransition transition = (TreeTransition) selection.getHover().treeElement; + int imgWidth = 100; + int imgHeight = 100; + + BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); + image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); + Point mousePoint = selection.getMousePoint(); + g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); + } + } + + public void resetView() { + this.tree = null; + this.rootNodeView = null; + this.selection.clearSelection(); + this.selection.clearHover(); + } + + /** + * Called when a tree puzzleElement is added to the tree + * + * @param treeElement TreeElement that was added to the tree + */ + @Override + public void onTreeElementAdded(TreeElement treeElement) { + if (treeElement.getType() == NODE) { + addTreeNode((TreeNode) treeElement); + } + else { + addTreeTransition((TreeTransition) treeElement); + } + repaint(); + } + + /** + * Called when a tree puzzleElement is removed from the tree + * + * @param element TreeElement that was removed to the tree + */ + @Override + public void onTreeElementRemoved(TreeElement element) { + if (element.getType() == NODE) { + TreeNode node = (TreeNode) element; + TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); + + nodeView.getParentView().setChildView(null); + removeTreeNode(node); + } + else { + TreeTransition trans = (TreeTransition) element; + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + + transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); + removeTreeTransition(trans); + } + repaint(); + } + + /** + * Called when the tree selection was changed + * + * @param selection tree selection that was changed + */ + @Override + public void onTreeSelectionChanged(TreeViewSelection selection) { + this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); + selection.getSelectedViews().forEach(v -> v.setSelected(true)); + this.selection = selection; + repaint(); + } + + /** + * Called when the model has finished updating the tree. + */ + @Override + public void onUpdateTree() { + repaint(); + } + + /** + * Gets the TreeElementView by the corresponding TreeElement associated with it + * + * @param element TreeElement of the view + * @return TreeElementView of the TreeElement associated with it + */ + public TreeElementView getElementView(TreeElement element) { + return viewMap.get(element); + } + + private void removeTreeNode(TreeNode node) { + viewMap.remove(node); + node.getChildren().forEach(t -> removeTreeTransition(t)); + } + + private void removeTreeTransition(TreeTransition trans) { + viewMap.remove(trans); + if (trans.getChildNode() != null) { + removeTreeNode(trans.getChildNode()); + } + } + + private void addTreeNode(TreeNode node) { + TreeTransition parent = node.getParent(); + + TreeNodeView nodeView = new TreeNodeView(node); + TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); + + nodeView.setParentView(parentView); + parentView.setChildView(nodeView); + + viewMap.put(node, nodeView); + + if (!node.getChildren().isEmpty()) { + node.getChildren().forEach(t -> addTreeTransition(t)); + } + } + + private void addTreeTransition(TreeTransition trans) { + List parents = trans.getParents(); + + TreeTransitionView transView = new TreeTransitionView(trans); + for (TreeNode parent : parents) { + TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); + transView.addParentView(parentNodeView); + parentNodeView.addChildrenView(transView); + } + + viewMap.put(trans, transView); + + if (trans.getChildNode() != null) { + addTreeNode(trans.getChildNode()); + } + } + + ///New Draw Methods + + public void drawTree(Graphics2D graphics2D) { + if (tree == null) { + LOGGER.error("Unable to draw tree."); + } + else { + if (rootNodeView == null) { + rootNodeView = new TreeNodeView(tree.getRootNode()); + + LOGGER.debug("Creating new views for tree view."); + createViews(rootNodeView); + + selection.newSelection(rootNodeView); + } + + dimension = new Dimension(0, 0); + calcSpan(rootNodeView); + rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); + + calculateViewLocations(rootNodeView, 0); + dimension.height = (int) rootNodeView.getSpan(); + + redrawTree(graphics2D, rootNodeView); + LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); + } + } + + public void createViews(TreeNodeView nodeView) { + if (nodeView != null) { + viewMap.put(nodeView.getTreeElement(), nodeView); + + TreeNode node = nodeView.getTreeElement(); + for (TreeTransition trans : node.getChildren()) { + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + if (transView != null) { + nodeView.addChildrenView(transView); + transView.addParentView(nodeView); + break; + } + transView = new TreeTransitionView(trans); + + viewMap.put(transView.getTreeElement(), transView); + + transView.addParentView(nodeView); + nodeView.addChildrenView(transView); + + TreeNode childNode = trans.getChildNode(); + if (childNode != null) { + TreeNodeView childNodeView = new TreeNodeView(childNode); + viewMap.put(childNodeView.getTreeElement(), childNodeView); + + childNodeView.setParentView(transView); + transView.setChildView(childNodeView); + + createViews(childNodeView); + } + } + } + } + + public void calculateViewLocations(TreeNodeView nodeView, int depth) { + nodeView.setDepth(depth); + int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; + nodeView.setX(xLoc); + dimension.width = Math.max(dimension.width, xLoc); + + TreeTransitionView parentTransView = nodeView.getParentView(); + int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); + nodeView.setY(yLoc); + + ArrayList children = nodeView.getChildrenViews(); + switch (children.size()) { + case 0: + break; + case 1: { + TreeTransitionView childView = children.get(0); + + List parentsViews = childView.getParentViews(); + if (parentsViews.size() == 1) { + childView.setEndY(yLoc); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + else { + if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { + int yAvg = 0; + for (int i = 0; i < parentsViews.size(); i++) { + TreeNodeView parentNodeView = parentsViews.get(i); + depth = Math.max(depth, parentNodeView.getDepth()); + yAvg += parentNodeView.getY(); + + Point lineStartPoint = childView.getLineStartPoint(i); + lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = parentNodeView.getY(); + } + yAvg /= parentsViews.size(); + childView.setEndY(yAvg); + + childView.setDepth(depth); + + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + } + break; + } + default: { + int span = 0; + for (TreeTransitionView childView : children) { + span += childView.getSpan(); + } + + span = (int) ((nodeView.getSpan() - span) / 2); + for (int i = 0; i < children.size(); i++) { + TreeTransitionView childView = children.get(i); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); + + span += childView.getSpan(); + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + break; + } + } + } + + public void calcSpan(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + calcSpan(childView); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + ArrayList newChildrenViews = new ArrayList<>(); + for (TreeTransition trans : node.getChildren()) { + newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); + } + nodeView.setChildrenViews(newChildrenViews); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + calcSpan(mergePointView); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + calcSpan(transView); + span += transView.getSpan(); + } + } + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view + * specified as stop. Stop tree puzzleElement is NOT included in the span calculation + * + * @param view + * @param stop + */ + private void subCalcSpan(TreeElementView view, TreeElementView stop) { + //safe-guard for infinite loop + if (view == stop) { + return; + } + + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + if (childView == stop) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + subCalcSpan(childView, stop); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + subCalcSpan(mergePointView, stop); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + subCalcSpan(transView, stop); + span += transView.getSpan(); + } + } + + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null || nodeView == stop) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in + * relative order in the list of child transitions of the specified node + * + * @param node root node of the branches + * @param branches DisjointSets of the child branches of the specified node which determine which branches merge + */ + private void reorderBranches(TreeNode node, DisjointSets branches) { + List children = node.getChildren(); + List> mergingSets = branches.getAllSets(); + + List> newOrder = new ArrayList<>(); + for (Set set : mergingSets) { + List mergeBranch = new ArrayList<>(); + newOrder.add(mergeBranch); + children.forEach(t -> { + if (set.contains(t)) { + mergeBranch.add(t); + } + }); + mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> + children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); + } + + newOrder.sort((List b1, List b2) -> { + int low1 = -1; + int low2 = -1; + for (TreeTransition t1 : b1) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + for (TreeTransition t1 : b2) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + return low1 < low2 ? -1 : 1; + }); + + List newChildren = new ArrayList<>(); + newOrder.forEach(l -> newChildren.addAll(l)); + node.setChildren(newChildren); + } } \ No newline at end of file From 6986a3d8379055f04a118cc688ae63de68882ab8 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:35:32 -0400 Subject: [PATCH 48/77] Atomic Direct Rule Test (#651) * Initial setup * Working initial test * Added another test * Added more tests * Added another test * Added comments, removed useless imports, added 1 more test * Reformatting * Removed useless import --------- Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> --- .../rules/AtomicDirectRuleTest.java | 188 ++++++++++++++++++ .../rules/DirectRuleAtomicTest.java | 6 - .../rules/AtomicDirectRule/Empty | 13 ++ .../rules/AtomicDirectRule/FalseA | 14 ++ .../rules/AtomicDirectRule/TrueB | 14 ++ 5 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java delete mode 100644 src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB diff --git a/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java new file mode 100644 index 000000000..51aa213c6 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java @@ -0,0 +1,188 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.DirectRuleAtomic; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AtomicDirectRuleTest { + private static final DirectRuleAtomic RULE = new DirectRuleAtomic(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given two statements: + * A + * A + * where the first A is set to false. + * + * This test sets the second A to false and then asserts that this + * is a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void MatchingFalseTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell cell = board.getCell(0, 2); + cell.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(cell); + + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given two statements: + * A + * A + * where the first A is set to false. + * + * This test sets the second A to true and then asserts that this + * is not a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void MismatchingFalseTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell cell = board.getCell(0, 2); + cell.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given two statements: + * B + * B + * where the first B is set to true. + * + * This test sets the second B to true and then asserts that this + * is a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void MatchingTrueTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell cell = board.getCell(0, 2); + cell.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(cell); + + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given two statements: + * B + * B + * where the first B is set to true. + * + * This test sets the second B to false and then asserts that this + * is not a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void MismatchingTrueTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell cell = board.getCell(0, 2); + cell.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given two statements: + * C + * C + * where neither statement is set to anything. + * + * This test sets the second C to false and then asserts that this + * is not a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void NothingPreviouslyMarkedTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/Empty", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell cell = board.getCell(0, 2); + cell.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given two statements: + * C + * C + * where neither statement is set to anything. + * + * This test sets the second C to true and then asserts that this + * is not a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void NothingPreviouslyMarkedTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/Empty", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell cell = board.getCell(0, 2); + cell.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java b/src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java deleted file mode 100644 index 81991fa46..000000000 --- a/src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package puzzles.shorttruthtable.rules; - -class DirectRuleAtomicTest { - - -} \ No newline at end of file diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty new file mode 100644 index 000000000..6a6effadf --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA new file mode 100644 index 000000000..8c5ddc7b9 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB new file mode 100644 index 000000000..46a1beb2d --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + From 535a825f6968464c98d1a9b8d1dce73cd919db5a Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:49:30 -0400 Subject: [PATCH 49/77] Gradle fixes + removed build files (#648) * Removed build files These shouldn't be here * Removed legup-update and legup Gradle projects --------- Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> --- build.gradle | 1 - .../BlockInVerticalPath | 11 ----------- .../CannotFillMiddle | 0 .../LightInHorizontalPath | 10 ---------- .../LightInVerticalPath | 10 ---------- .../CannotFillCorners | 13 ------------- .../CannotFillMiddle | 11 ----------- .../TooFewBulbsContradictionRule/BlockEnclosed | 13 ------------- .../CompleteBoardBlockEnclosed | 15 --------------- .../CornerBlockEnclosed | 11 ----------- .../ManyBlocksEnclosed | 17 ----------------- .../TooManyBulbsContradictionRule/BlockEnclosed | 13 ------------- .../CompleteBoardBlockEnclosed | 15 --------------- .../CornerBlockEnclosed | 11 ----------- .../ManyBlocksEnclosed | 17 ----------------- settings.gradle | 2 -- 16 files changed, 170 deletions(-) delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/CannotFillMiddle delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath delete mode 100644 build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners delete mode 100644 build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed delete mode 100644 build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed diff --git a/build.gradle b/build.gradle index fafa54cac..1ba7ea006 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,6 @@ dependencies { implementation 'org.jetbrains:annotations:20.1.0' implementation 'org.jetbrains:annotations:20.1.0' implementation 'com.formdev:flatlaf:3.0' - implementation project(':legup-update') implementation 'com.google.firebase:firebase-admin:6.3.0' implementation 'org.apache.httpcomponents:httpclient:4.5.1' implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath deleted file mode 100644 index 5f27b3ec8..000000000 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/CannotFillMiddle b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/CannotFillMiddle deleted file mode 100644 index e69de29bb..000000000 diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath deleted file mode 100644 index 633ccc80b..000000000 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath deleted file mode 100644 index 70419c40c..000000000 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners b/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners deleted file mode 100644 index 38b52f04d..000000000 --- a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillCorners +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle b/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle deleted file mode 100644 index 44086f145..000000000 --- a/build/resources/test/puzzles/lightup/rules/CannotLightACellContradictionRule/CannotFillMiddle +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed deleted file mode 100644 index a57a2473e..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/BlockEnclosed +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed deleted file mode 100644 index f48d240f0..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CompleteBoardBlockEnclosed +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed deleted file mode 100644 index 1a9cd60d9..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/CornerBlockEnclosed +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed b/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed deleted file mode 100644 index 32200d831..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooFewBulbsContradictionRule/ManyBlocksEnclosed +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed deleted file mode 100644 index c5760aede..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/BlockEnclosed +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed deleted file mode 100644 index 88fb0a8f1..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CompleteBoardBlockEnclosed +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed deleted file mode 100644 index a9a8dc5a0..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/CornerBlockEnclosed +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed b/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed deleted file mode 100644 index e743862eb..000000000 --- a/build/resources/test/puzzles/lightup/rules/TooManyBulbsContradictionRule/ManyBlocksEnclosed +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 936e82b6f..f9f593d2b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,2 @@ rootProject.name = 'Legup' -include ':legup' -include ':legup-update' From f69fbe328216fda52db8da1eed2d748c0cda67f4 Mon Sep 17 00:00:00 2001 From: Jacob Long Date: Tue, 17 Oct 2023 19:14:16 -0400 Subject: [PATCH 50/77] fix typo that was causing javadoc to fail --- .../java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java index 0df99b820..a7feac91d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java @@ -154,7 +154,7 @@ public static ArrayList getCellsAtDistance(FillapixBoard board, Fi * * @return an ArrayList of Boolean arrays. Each index in the ArrayList represents * a distinct combination. Each Boolean array will be totalNumItems - * long and each index will be true<\code> if the corresponding item is + * long and each index will be true if the corresponding item is * included in that combination, and false otherwise. */ public static ArrayList getCombinations(int chosenNumItems, int totalNumItems) { From ee2feaad4f29a48ddf6fb9b9ae62f68afa4829e4 Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Fri, 20 Oct 2023 17:10:49 -0400 Subject: [PATCH 51/77] Finished Nurikabe test cases This commit includes all the finished Nurikabe test cases, including direct rules and the case rule. Comments have been added where appropriate. No significant change was made in some classes. --- .../BlackBetweenRegionsDirectRuleTest.java | 12 +++ .../rules/BlackOrWhiteCaseRuleTest.java | 5 +- .../rules/CannotReachCellDirectRuleTest.java | 94 +++++++++++++++++++ .../rules/CornerBlackDirectRuleTest.java | 3 + .../rules/FillinBlackDirectRuleTest.java | 63 +++++++++++++ .../rules/FillinWhiteDirectRuleTest.java | 61 ++++++++++++ .../PreventBlackSquareDirectRuleTest.java | 12 +++ .../rules/SurroundRegionDirectRuleTest.java | 6 ++ .../rules/WhiteBottleNeckDirectRuleTest.java | 6 ++ .../BlackOrWhiteCaseRule/SimpleBlackOrWhite | 13 +++ .../AllCellsReachable | 11 +++ .../SimpleUnreachableCell | 9 ++ .../rules/FillinBlackDirectRule/CornerBlack1 | 10 ++ .../rules/FillinBlackDirectRule/CornerBlack2 | 10 ++ .../rules/FillinWhiteDirectRule/CornerWhite1 | 11 +++ .../rules/FillinWhiteDirectRule/CornerWhite2 | 11 +++ 16 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 src/test/java/puzzles/nurikabe/rules/CannotReachCellDirectRuleTest.java create mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackOrWhiteCaseRule/SimpleBlackOrWhite create mode 100644 src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/AllCellsReachable create mode 100644 src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/SimpleUnreachableCell create mode 100644 src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack1 create mode 100644 src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack2 create mode 100644 src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite1 create mode 100644 src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite2 diff --git a/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java index b32ce23ce..a6652ea60 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java @@ -30,6 +30,9 @@ public static void setUp() { nurikabe = new Nurikabe(); } + /** + * Tests the Black Between Regions direct rule for regions that are diagonal to each other (diagonal going from top left to bottom right) + */ @Test public void BlackBetweenRegionsDirectRule_DiagonalBlackBetweenRegions1Test() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/DiagonalBlackBetweenRegions1", nurikabe); @@ -62,6 +65,9 @@ public void BlackBetweenRegionsDirectRule_DiagonalBlackBetweenRegions1Test() thr } } + /** + * Tests the Black Between Regions direct rule for regions that are diagonal to each other (diagonal going from bottom left to top right) + */ @Test public void BlackBetweenRegionsDirectRule_DiagonalBlackBetweenRegions2Test() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/DiagonalBlackBetweenRegions2", nurikabe); @@ -94,6 +100,9 @@ public void BlackBetweenRegionsDirectRule_DiagonalBlackBetweenRegions2Test() thr } } + /** + * Tests the Black Between Regions direct rule for regions that are horizontally opposite each other + */ @Test public void BlackBetweenRegionsDirectRule_HorizontalBlackBetweenRegionsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/HorizontalBlackBetweenRegions", nurikabe); @@ -123,6 +132,9 @@ public void BlackBetweenRegionsDirectRule_HorizontalBlackBetweenRegionsTest() th } } + /** + * Tests the Black Between Regions direct rule for regions that are vertically opposite each other + */ @Test public void BlackBetweenRegionsDirectRule_VerticalBlackBetweenRegionsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/VerticalBlackBetweenRegions", nurikabe); diff --git a/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java index d270204ac..0b851be5c 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java @@ -25,9 +25,12 @@ public static void setUp() { nurikabe = new Nurikabe(); } + /** + * Tests the Black Or White case rule + */ @Test public void TooFewSpacesContradictionRule_TwoSurroundBlackTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/nurikabe/rules/TooFewSpacesContradictionRule/TwoSurroundBlack", nurikabe); + TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackOrWhiteCaseRule/SimpleBlackOrWhite", nurikabe); TreeNode rootNode = nurikabe.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); diff --git a/src/test/java/puzzles/nurikabe/rules/CannotReachCellDirectRuleTest.java b/src/test/java/puzzles/nurikabe/rules/CannotReachCellDirectRuleTest.java new file mode 100644 index 000000000..267f57f6d --- /dev/null +++ b/src/test/java/puzzles/nurikabe/rules/CannotReachCellDirectRuleTest.java @@ -0,0 +1,94 @@ +package puzzles.nurikabe.rules; + +import legup.MockGameBoardFacade; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.Assert; +import edu.rpi.legup.puzzle.nurikabe.Nurikabe; +import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.puzzle.nurikabe.rules.CannotReachCellDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import java.awt.*; + +public class CannotReachCellDirectRuleTest { + + private static final CannotReachCellDirectRule RULE = new CannotReachCellDirectRule(); + private static Nurikabe nurikabe; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + nurikabe = new Nurikabe(); + } + + /** + * Tests the Cannot Reach Cell direct rule for a simple unreachable cell + */ + @Test + public void CannotReachCellDirectRule_SimpleUnreachableCell() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/nurikabe/rules/CannotReachCellDirectRule/SimpleUnreachableCell", nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + + NurikabeBoard board = (NurikabeBoard)transition.getBoard(); + NurikabeCell cell = board.getCell(0,0); + cell.setData(NurikabeType.BLACK.toValue()); + board.addModifiedData(cell); + + Assert.assertNull(RULE.checkRule(transition)); + + for(int i=0; i + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/AllCellsReachable b/src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/AllCellsReachable new file mode 100644 index 000000000..63e38d25f --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/AllCellsReachable @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/SimpleUnreachableCell b/src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/SimpleUnreachableCell new file mode 100644 index 000000000..e11487564 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/CannotReachCellDirectRule/SimpleUnreachableCell @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack1 b/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack1 new file mode 100644 index 000000000..ae078229e --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack1 @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack2 b/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack2 new file mode 100644 index 000000000..f274fc774 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/CornerBlack2 @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite1 b/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite1 new file mode 100644 index 000000000..de0ca4f71 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite1 @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite2 b/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite2 new file mode 100644 index 000000000..9fc228483 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/CornerWhite2 @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 7e2b0c47a4f1c6ef6c647efccf4fed5df7f06799 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 24 Oct 2023 16:53:56 -0400 Subject: [PATCH 52/77] Merge pull request #672 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial setup * Working initial test * Added another test * Added more tests * Added another test * Added comments, removed useless imports, added 1 more test * Reformatting * Removed useless import * Added initial and elimination test * Merge branch 'dev' into stt-test-suite * Merge branch 'stt-test-suite' of https://github.com/charlestian23/LEG… * Comments and spacing * Another test * Updated comments and wrote new test * Created two new test files * Renamed test to be more descriptive * Added another test * Rewrote test to be more comprehensive * More tests :)))) * Fixed test name and file * Fixed test * Fixed broken tests * Shouldn't have touched these files * Merge branch 'dev' into stt-test-suite * Merge branch 'stt-test-suite' of https://github.com/charlestian23/LEG… * CHECKSTYLE * Merge branch 'dev' into stt-test-suite * Trying to make CheckStyle happy --- .../rules/AndEliminationDirectRuleTest.java | 204 ++++++++++++++++++ .../rules/AndEliminationDirectRule/FalseAnd | 13 ++ .../FalseAndWithKnownFalse | 14 ++ .../FalseAndWithKnownTrue | 14 ++ .../rules/AndEliminationDirectRule/TrueAnd | 13 ++ 5 files changed, 258 insertions(+) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java new file mode 100644 index 000000000..0d94eb672 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java @@ -0,0 +1,204 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination.DirectRuleAndElimination; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AndEliminationDirectRuleTest { + private static final DirectRuleAndElimination RULE = new DirectRuleAndElimination(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given one statement: B^C where ^ is true + * + * Checks all possible combinations of true, false, and unknown for B and C + * except for where both B and C are true and asserts that each one of them + * is not a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void trueAndTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + if (cellType1 == cellType2 && cellType1 == ShortTruthTableCellType.TRUE) { + continue; + } + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell bonnie = board.getCell(0, 0); + ShortTruthTableCell clyde = board.getCell(2, 0); + + if (cellType1 != ShortTruthTableCellType.UNKNOWN) { + bonnie.setData(cellType1); + board.addModifiedData(bonnie); + } + + if (cellType2 != ShortTruthTableCellType.UNKNOWN) { + clyde.setData(cellType2); + board.addModifiedData(clyde); + } + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: B^C where ^ is true + * + * Checks all possible combinations of true and unknown for B and C + * except for where both B and C are unknown and asserts that each one + * of them is a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void trueAndTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + if (cellType1 == cellType2 && cellType1 == ShortTruthTableCellType.UNKNOWN) { + continue; + } + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell bonnie = board.getCell(0, 0); + ShortTruthTableCell clyde = board.getCell(2, 0); + + if (cellType1 != ShortTruthTableCellType.UNKNOWN) { + bonnie.setData(cellType1); + board.addModifiedData(bonnie); + } + + if (cellType2 != ShortTruthTableCellType.UNKNOWN) { + clyde.setData(cellType2); + board.addModifiedData(clyde); + } + + Assert.assertNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: B^C where ^ is false + * + * Checks all possible combinations of true, false, and unknown for B and C + * and asserts that each one of them is not a valid application of the rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void falseAndWithUnknownsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell bonnie = board.getCell(0, 0); + ShortTruthTableCell clyde = board.getCell(2, 0); + + if (cellType1 != ShortTruthTableCellType.UNKNOWN) { + bonnie.setData(cellType1); + board.addModifiedData(bonnie); + } + + if (cellType2 != ShortTruthTableCellType.UNKNOWN) { + clyde.setData(cellType2); + board.addModifiedData(clyde); + } + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: B^C where both B and ^ are false + * + * Asserts that this is not a valid application of the rule if C is set to + * either true or false. + * + * @throws InvalidFileFormatException + */ + @Test + public void falseAndWithKnownFalseTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell clyde = board.getCell(2, 0); + clyde.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(clyde); + Assert.assertNotNull(RULE.checkRule(transition)); + + clyde.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(clyde); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: B^C where B is true and ^ is false + * + * Asserts that this is a valid application of the rule if and only if C is + * set to false. + * + * @throws InvalidFileFormatException + */ + @Test + public void falseAndWithKnownTrueTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + + ShortTruthTableCell clyde = board.getCell(2, 0); + clyde.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(clyde); + Assert.assertNotNull(RULE.checkRule(transition)); + + clyde.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(clyde); + Assert.assertNull(RULE.checkRule(transition)); + } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd new file mode 100644 index 000000000..f6f60abc3 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse new file mode 100644 index 000000000..d8edf4a76 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue new file mode 100644 index 000000000..364d8faf6 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd new file mode 100644 index 000000000..307f6d14a --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From ab59072dc95864617f3422460dab965055f5b4f5 Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Tue, 24 Oct 2023 17:04:47 -0400 Subject: [PATCH 53/77] added FinishWithGrassTest (#650) * October Merge (#636) * Region Based Changes (#559) Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> * Short Truth Table Puzzle Editor (#451) * Created files for STT elements * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Created element classes and added placeholder tile images (#452) * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Set the current board on boardView * Fixed typo and turned on STT puzzle editor for testing * Added preliminary valid dimensions checker This will most definitely change in the future, hopefully can change to accept a number of statements * Fixed image file paths * Added ActionListener Allows us to determine what puzzle is selected by the user * Hide rows and columns input for Short Truth Table * Added text area for Short Truth Table * Added scrollbars to show up as needed * Reformatted code * More code reformatting * Even more reformatting * Separate the data from the TextArea into different lines * Did some researching/testing Tested certain variable values with a STT file with no true/false values * Made more progress Added new methods to handle creating Short Truth Table boards from an array of strings * Added a bunch of TODOs - Implemented a couple functions to be used later - Added a bunch of TODO comments for future work * Made some more progress * Implemented abstract methods from PuzzleImporter * Added abstract methods to Fillapix and added other exception reporting * CheckStyle formatting * Removed a TODO comment * Statements show up in puzzle editor Fixed a bug where the importer was not properly being initialized. Statements now show up in the puzzle editor. * Removed empty statements * Changed InvalidFormatException to IllegalArgumentException * Remove argument that has already been caught * Removed elements that will not be used * Added puzzle editor cell clicking functionality * Added ability to toggle certain logical elements * New icons and more functionality implemented * Fixed a bug where spacer rows could be modified * Added statement error checking * Fixed formatting * Only one logic symbol element needed * Changed InputMismatchException to UnsupportedOperationException * Renamed variables to not be STT specific * Finding initial issue and starting fix * Issue is statement copying and modifying * STT exporter now working. Overrode setCell for STTBoard. * Added code documentation * removed testing println() * Gradle fixes * Revert "Merge pull request #545 from MMosley502/puzzle_editor-short_truth_table-file_saving" This reverts commit 2e82547896a7fb3e52ec27634cd8938ef299732f, reversing changes made to beb60a2ab67c8317d404f54e52471739f698bf22. * Saving files now works * Fixed the blank element to be categorized as a placeable element * Fixed a bug where file wouldn't save due to batch grader updates * Reformatted code in STT * Reformatted code again * MORE REFORMATTING Pls like my code CheckStyle --------- Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> * Have null changes be valid and fix IsolatedBlackContradicitonRule error message (#561) * Get Tests to be called Revert "Create first cypress test template" This reverts commit 3e50909b93b5aa9634cf0d296e9aeff756b0a909. First commit Finish Lightup tests * Add more tests Update TestRunner.java * Somehow ended up in the wrong spot Fix Import * Please let this be the fix Update TreeTransition.java Update TreeTransition.java Update DirectRule.java Check to see which is not correct Update ElementController.java Revert "maybe the null is making it think that it's not valid" This reverts commit 7bf1de0d66ced6749ee37fbb9c252636b2fcdc79. Just trying to change color Revert "Just trying to change color" This reverts commit ec44695ee578d664055d135a668927a0fd900f5d. Revert "maybe the null is making it think that it's not valid" This reverts commit 3f162fbdc32e6fbd23da321a14a6af96f0ff520d. Check to see which is not correct Revert "Check to see which is not correct" This reverts commit 136b0a41b9d103e6f3e9a7f8cd5d970bf76b050b. Update TreeTransition.java Update TreeTransition.java Revert "Update TreeTransition.java" This reverts commit cde45bb9001cfbfa4f6e2a49b4e9990d2fa7ad33. * Fix error with isolated Black Fix error message with isolated black * Removed excess whitespace and imports. Added short JavaDoc for `TestRunner.java` --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet * Fixed a bug * Update BlackTile.java Black tile should not be placeable * Added unknown tile stuff * ID error * Some Fixes to Recently Discussed UX Bugs (#563) * frame and panels default sizes, default pos on screen * hardcoded version number * homepanel default size * set panels' own sizes * some changes * Removed unused import --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Corppet * Oops pushed the wrong file Yeah some tiles work now but this is the ID error * Number Tile working * Update Exporter (#627) * Update Exporter * Delete Test_Save --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> * Create run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Windows things * Added print messages * More Windows things * Debugging * Update run-tests.yml * Update run-tests.yml * Maybe this will work now? * Didn't work * Update run-tests.yml * Update run-tests.yml * Create DummyTest.java For debugging purposes * Added another dummy test * Update run-tests.yml * Update run-tests.yml Get rid of all this info * Deleted the dummy tests --------- Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> * fixed error on puzzle * added some tests * added test on empty puzzle * fixed error on puzzle * added test with tent in the middle * added test where rule should not work * added space behind bracket * updated to be inline with dev * added SpacedOutTentTest added a test with the tents spread out * added some comments to tests * added comments to tests * removed isolationg code * reverted TestRunner.java --------- Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> --- .../rules/FinishWithGrassDirectRuleTest.java | 320 +++++++++++++++++- .../{FinishWithGrass => CornerTent} | 2 +- .../rules/FinishWithGrassDirectRule/FailTent | 19 ++ .../FinishWithGrassDirectRule/MiddleTent | 20 ++ .../rules/FinishWithGrassDirectRule/NoTent | 19 ++ .../FinishWithGrassDirectRule/SpacedOutTent | 30 ++ 6 files changed, 402 insertions(+), 8 deletions(-) rename src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/{FinishWithGrass => CornerTent} (92%) create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent diff --git a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java index 8dbec657a..2517df563 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java @@ -15,6 +15,8 @@ import org.junit.Test; import java.awt.*; +import java.util.List; +import java.util.ArrayList; public class FinishWithGrassDirectRuleTest { @@ -27,15 +29,24 @@ public static void setUp() { treetent = new TreeTent(); } + /** + * 3x3 TreeTent puzzle with a tent at (0,0) + * Tests FinishWithGrassDirectRule on GRASS tiles horizontal of the tent + * at (1,0) and (2,0) + * + * @throws InvalidFileFormatException + */ @Test - public void EmptyFieldTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass", treetent); + public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); + // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + // change the board's cells considering the FinishWithGrass rule TreeTentCell cell1 = board.getCell(1, 0); cell1.setData(TreeTentType.GRASS); TreeTentCell cell2 = board.getCell(2, 0); @@ -44,21 +55,316 @@ public void EmptyFieldTest() throws InvalidFileFormatException { board.addModifiedData(cell1); board.addModifiedData(cell2); + // confirm there is a logical following of the EmptyField rule Assert.assertNull(RULE.checkRule(transition)); + // only the cell above should change following the rule + TreeTentCell c; for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - Point point = new Point(k, i); - if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation()) || c.getLocation().equals(cell2.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } } } -} + /** + * 3x3 TreeTent puzzle with a tent at (0,0) + * Tests FinishWithGrassDirectRule on GRASS tiles vertical of the tent + * at (0,1) and (0,2) + * + * @throws InvalidFileFormatException + */ + @Test + public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + // change the board's cells considering the FinishWithGrass rule + TreeTentCell cell1 = board.getCell(0, 1); + cell1.setData(TreeTentType.GRASS); + TreeTentCell cell2 = board.getCell(0, 2); + cell2.setData(TreeTentType.GRASS); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + // confirm there is a logical following of the EmptyField rule + Assert.assertNull(RULE.checkRule(transition)); + + // only the cell above should change following the rule + TreeTentCell c; + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation()) || c.getLocation().equals(cell2.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } + + /** + * 3x3 TreeTent puzzle with a tent at (0,0) + * Tests FinishWithGrassDirectRule on GRASS tiles + * at (1,0), (2,0), (0,1), and (0,2) + * + * @throws InvalidFileFormatException + */ + @Test + public void FinishWithGrassTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + // change the board's cells considering the FinishWithGrass rule + TreeTentCell cell1 = board.getCell(1, 0); + cell1.setData(TreeTentType.GRASS); + TreeTentCell cell2 = board.getCell(2, 0); + cell2.setData(TreeTentType.GRASS); + TreeTentCell cell3 = board.getCell(0, 1); + cell3.setData(TreeTentType.GRASS); + TreeTentCell cell4 = board.getCell(0, 2); + cell4.setData(TreeTentType.GRASS); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + + // confirm there is a logical following of the EmptyField rule + Assert.assertNull(RULE.checkRule(transition)); + + // only the cell above should change following the rule + TreeTentCell c; + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation()) || + c.getLocation().equals(cell2.getLocation()) || + c.getLocation().equals(cell3.getLocation()) || + c.getLocation().equals(cell4.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } + + /** + * 3x3 TreeTent puzzle with no tents + * Tests FinishWithGrassDirectRule on GRASS tiles + * GRASS tiles fill entire board + * + * @throws InvalidFileFormatException + */ + @Test + public void NoTentTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + // change the board's cells considering the FinishWithGrass rule + List cells = new ArrayList(); + for (int i = 0; i < board.getWidth(); i++) { + for (int k = 0; k < board.getHeight(); k++) { + TreeTentCell c = board.getCell(i, k); + c.setData(TreeTentType.GRASS); + cells.add(c); + } + } + + for (TreeTentCell c : cells) { + board.addModifiedData(c); + } + + // confirm there is a logical following of the EmptyField rule + Assert.assertNull(RULE.checkRule(transition)); + + // all cells should change following the rule + for (TreeTentCell c : cells) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + } + /** + * 3x3 TreeTent puzzle with a tent at (1,1) + * Tests FinishWithGrassDirectRule on GRASS tiles surrounding the tent + * at (1,0), (0,1), (2,1), and (1,2) + * + * @throws InvalidFileFormatException + */ + @Test + public void MiddleTentTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + // change the board's cells considering the FinishWithGrass rule + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(0, 1); + TreeTentCell cell3 = board.getCell(2, 1); + TreeTentCell cell4 = board.getCell(1, 2); + + cell1.setData(TreeTentType.GRASS); + cell2.setData(TreeTentType.GRASS); + cell3.setData(TreeTentType.GRASS); + cell4.setData(TreeTentType.GRASS); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + + // confirm there is a logical following of the EmptyField rule + Assert.assertNull(RULE.checkRule(transition)); + + // only the cell above should change following the rule + TreeTentCell c; + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation()) || + c.getLocation().equals(cell2.getLocation()) || + c.getLocation().equals(cell3.getLocation()) || + c.getLocation().equals(cell4.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } + + /** + * 3x3 TreeTent puzzle with missing tents + * Tests FinishWithGrassDirectRule on GRASS tiles filling the puzzle + * all GRASS tiles should fail the FinishWithGrassDirectRule + * + * @throws InvalidFileFormatException + */ + @Test + public void FailTentTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + // change the board's cells not following the FinishWithGrass rule + List cells = new ArrayList(); + for (int i = 0; i < board.getWidth(); i++) { + for (int k = 0; k < board.getHeight(); k++) { + TreeTentCell c = board.getCell(i, k); + c.setData(TreeTentType.GRASS); + cells.add(c); + } + } + + for (TreeTentCell c : cells) { + board.addModifiedData(c); + } + + // confirm there is a logical following of the EmptyField rule + Assert.assertNotNull(RULE.checkRule(transition)); + + // all cells should fail the rule test + for (TreeTentCell c : cells) { + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + /** + * 7x7 TreeTent puzzle with multiple tents spaced out + * Tests FinishWithGrassDirectRule on GRASS tiles between the tents + * at (0,3), (2,3), (4,3), and (6,3) + * + * @throws InvalidFileFormatException + */ + @Test + public void SpacedOutTentTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + // get board state + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + // change the board's cells considering the FinishWithGrass rule + TreeTentCell cell1 = board.getCell(0, 3); + TreeTentCell cell2 = board.getCell(2, 3); + TreeTentCell cell3 = board.getCell(4, 3); + TreeTentCell cell4 = board.getCell(6, 3); + + cell1.setData(TreeTentType.GRASS); + cell2.setData(TreeTentType.GRASS); + cell3.setData(TreeTentType.GRASS); + cell4.setData(TreeTentType.GRASS); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + + // confirm there is a logical following of the EmptyField rule + Assert.assertNull(RULE.checkRule(transition)); + + // only the cell above should change following the rule + TreeTentCell c; + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + c = board.getCell(k, i); + if (c.getLocation().equals(cell1.getLocation()) || + c.getLocation().equals(cell2.getLocation()) || + c.getLocation().equals(cell3.getLocation()) || + c.getLocation().equals(cell4.getLocation())) { + // logically follows + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + // does not use the rule to logically follow + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent similarity index 92% rename from src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass rename to src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent index 3e293d22f..daba3648e 100644 --- a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent @@ -10,7 +10,7 @@ - + diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent new file mode 100644 index 000000000..9fa14ebe4 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent new file mode 100644 index 000000000..8f71a57f9 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent new file mode 100644 index 000000000..a13c7cc55 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent new file mode 100644 index 000000000..f7b523b0a --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From a6932d5ce81fb3c15044f7d8b79c8b4ff0f6bcf9 Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Fri, 27 Oct 2023 17:56:54 -0400 Subject: [PATCH 54/77] Complete rewrite of case rule test and some minor changes This commit includes a complete rewite of the case rule to test that it creates the number of children required in a correct manner. It also includes minor changes to some other tests. --- bin/main/edu/rpi/legup/log4j2.properties | 28 ++++++------- .../rules/BlackOrWhiteCaseRuleTest.java | 40 +++++++++++-------- .../MultipleNumbersContradictionRuleTest.java | 33 +++++++++++++++ .../TooFewSpacesContradictionRuleTest.java | 2 +- .../CornerBottleNeck | 2 +- .../BlackOrWhiteCaseRule/SimpleBlackOrWhite | 5 --- .../ComplexRegion | 19 +++++++++ 7 files changed, 92 insertions(+), 37 deletions(-) create mode 100644 src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/ComplexRegion diff --git a/bin/main/edu/rpi/legup/log4j2.properties b/bin/main/edu/rpi/legup/log4j2.properties index de1fa02ed..4f2556c2d 100644 --- a/bin/main/edu/rpi/legup/log4j2.properties +++ b/bin/main/edu/rpi/legup/log4j2.properties @@ -1,15 +1,15 @@ -# Logging level -# Root logger option -log4j.rootLogger=DEBUG, stdout, file -# Redirect log messages to console -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n -# Redirect log messages to a log file, support file rolling. -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=Legup.log -log4j.appender.file.MaxFileSize=5MB -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout +# Logging level +# Root logger option +log4j.rootLogger=DEBUG, stdout, file +# Redirect log messages to console +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n +# Redirect log messages to a log file, support file rolling. +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.File=Legup.log +log4j.appender.file.MaxFileSize=5MB +log4j.appender.file.MaxBackupIndex=10 +log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file diff --git a/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java index 0b851be5c..7c802ff78 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java @@ -1,6 +1,10 @@ package puzzles.nurikabe.rules; +import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.puzzle.nurikabe.rules.BlackOrWhiteCaseRule; import legup.MockGameBoardFacade; import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; @@ -12,11 +16,13 @@ import edu.rpi.legup.puzzle.nurikabe.rules.TooFewSpacesContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; + import java.awt.*; public class BlackOrWhiteCaseRuleTest { - private static final TooFewSpacesContradictionRule RULE = new TooFewSpacesContradictionRule(); + private static final BlackOrWhiteCaseRule RULE = new BlackOrWhiteCaseRule(); private static Nurikabe nurikabe; @BeforeClass @@ -26,7 +32,7 @@ public static void setUp() { } /** - * Tests the Black Or White case rule + * Tests the Black Or White case rule by ensuring that it results in two children, that contain a modified cell that is either black or white */ @Test public void TooFewSpacesContradictionRule_TwoSurroundBlackTest() throws InvalidFileFormatException { @@ -35,20 +41,22 @@ public void TooFewSpacesContradictionRule_TwoSurroundBlackTest() throws InvalidF TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - Assert.assertNull(RULE.checkContradiction((NurikabeBoard) transition.getBoard())); - NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - Point location = new Point(1, 1); - for (int i = 0; i < board.getHeight(); i++) { - for (int k = 0; k < board.getWidth(); k++) { - Point point = new Point(k, i); - if (point.equals(location)) { - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - } - } + NurikabeCell cell = board.getCell(0,0); + ArrayList cases = RULE.getCases(board,cell); + + Assert.assertEquals(2,cases.size()); + + NurikabeBoard caseBoard = (NurikabeBoard) cases.get(0); + NurikabeBoard caseBoard2 = (NurikabeBoard) cases.get(1); + + Integer v1 = new Integer(0); + Integer v2 = new Integer(-1); + + Assert.assertTrue((caseBoard.getCell(0,0).getData().equals(v1) || caseBoard.getCell(0,0).equals(v2)) && + (caseBoard2.getCell(0,0).getData().equals(v1) || caseBoard2.getCell(0,0).getData().equals(v2))); + Assert.assertFalse(caseBoard.getCell(0,0).getData().equals(caseBoard2.getCell(0,0).getData())); + + } } diff --git a/src/test/java/puzzles/nurikabe/rules/MultipleNumbersContradictionRuleTest.java b/src/test/java/puzzles/nurikabe/rules/MultipleNumbersContradictionRuleTest.java index 56ada91d0..67995bffc 100644 --- a/src/test/java/puzzles/nurikabe/rules/MultipleNumbersContradictionRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/MultipleNumbersContradictionRuleTest.java @@ -52,6 +52,38 @@ public void MultipleNumbersContradictionRule_TwoSurroundBlackTest() throws Inval } } + /** + * Tests the Multiple Numbers contradiction rule for a more complex regions with multiple numbers + */ + @Test + public void MultipleNumbersContradictionRule_ComplexRegion() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/nurikabe/rules/MultipleNumbersContradictionRule/ComplexRegion",nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeCell cell1 = board.getCell(0,0); + NurikabeCell cell2 = board.getCell(2,0); + NurikabeCell cell3 = board.getCell(4,0); + NurikabeCell cell4 = board.getCell(2,3); + NurikabeCell cell5 = board.getCell(4,4); + + Assert.assertNull(RULE.checkContradiction((NurikabeBoard)transition.getBoard())); + for(int i=0; i - + diff --git a/src/test/resources/puzzles/nurikabe/rules/BlackOrWhiteCaseRule/SimpleBlackOrWhite b/src/test/resources/puzzles/nurikabe/rules/BlackOrWhiteCaseRule/SimpleBlackOrWhite index b30f64b75..fb5e45218 100644 --- a/src/test/resources/puzzles/nurikabe/rules/BlackOrWhiteCaseRule/SimpleBlackOrWhite +++ b/src/test/resources/puzzles/nurikabe/rules/BlackOrWhiteCaseRule/SimpleBlackOrWhite @@ -2,11 +2,6 @@ - - - - - diff --git a/src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/ComplexRegion b/src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/ComplexRegion new file mode 100644 index 000000000..5f8e607fe --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/MultipleNumbersContradictionRule/ComplexRegion @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 851ffaf5ea70e46b401075a64a95a62a32b27669 Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Tue, 31 Oct 2023 16:26:44 -0400 Subject: [PATCH 55/77] Minor addition to case rule Made a minor addition to the case rule to make it more comprehensive - testing the equality of the non-modified cells, and the relative sizes of the all the boards. --- .../nurikabe/rules/BlackOrWhiteCaseRuleTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java index 7c802ff78..55001e6d2 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackOrWhiteCaseRuleTest.java @@ -57,6 +57,19 @@ public void TooFewSpacesContradictionRule_TwoSurroundBlackTest() throws InvalidF (caseBoard2.getCell(0,0).getData().equals(v1) || caseBoard2.getCell(0,0).getData().equals(v2))); Assert.assertFalse(caseBoard.getCell(0,0).getData().equals(caseBoard2.getCell(0,0).getData())); + Assert.assertEquals(caseBoard.getHeight(),caseBoard2.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard.getWidth(),caseBoard2.getWidth(), board.getWidth()); + + for(int i=0; i Date: Fri, 3 Nov 2023 17:21:59 -0400 Subject: [PATCH 56/77] Surround completed region newest (#673) * Update SurroundRegionDirectRule.java * Update SurroundRegionDirectRule.java * Update SurroundRegionDirectRule.java * Revised `directions` to initialize in one line. --------- Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> Co-authored-by: Corppet --- .../rules/SurroundRegionDirectRule.java | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java index b77f8a79f..d992fd22c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java @@ -3,12 +3,20 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.DirectRule; -import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; +import edu.rpi.legup.utility.DisjointSets; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.HashSet; +import java.util.Set; +import java.awt.*; public class SurroundRegionDirectRule extends DirectRule { @@ -29,7 +37,6 @@ public SurroundRegionDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - ContradictionRule contraRule = new TooManySpacesContradictionRule(); NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); @@ -44,12 +51,35 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); modCell.setData(NurikabeType.WHITE.toValue()); - if (contraRule.checkContradiction(modified) == null) { - return null; - } - else { - return "Does not follow from this rule at this index"; + if(cell.getType() == NurikabeType.BLACK) { + DisjointSets regions = NurikabeUtilities.getNurikabeRegions(destBoardState); + Set adj = new HashSet<>(); //set to hold adjacent cells + Point loc = cell.getLocation(); //position of placed cell + List directions = Arrays.asList(new Point(-1, 0), new Point(1, 0), new Point(0, -1), new Point(0, 1)); + for(Point direction : directions) { + NurikabeCell curr = destBoardState.getCell(loc.x + direction.x, loc.y + direction.y); + if(curr != null) { + if(curr.getType() == NurikabeType.WHITE || curr.getType() == NurikabeType.NUMBER) { + adj.add(curr); //adds cells to adj only if they are white or number blocks + } + } + } + List numberedCells = new ArrayList<>(); //number value of number cells + for (NurikabeCell c : adj) { //loops through adjacent cells + Set disRow = regions.getSet(c); //set of white spaces + for (NurikabeCell d : disRow) { //loops through white spaces + if (d.getType() == NurikabeType.NUMBER) { //if the white space is a number + numberedCells.add(d); //add that number to numberedCells + } + } + } + for (NurikabeCell number : numberedCells) { //loops through numberedCells + if (regions.getSet(number).size() == number.getData()) { //if that cells white area is the exact + return null; //size of the number of one of the number cells within that set + } + } } + return "Does not follow from this rule at this index"; } /** From 85603e6b3ec691f128537e38313f80d949323848 Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Fri, 3 Nov 2023 17:28:11 -0400 Subject: [PATCH 57/77] deleted build folder --- .../BulbsInPathContradictionRule/BlockInVerticalPath | 11 ----------- .../LightInHorizontalPath | 10 ---------- .../BulbsInPathContradictionRule/LightInVerticalPath | 10 ---------- 3 files changed, 31 deletions(-) delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath delete mode 100644 build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath deleted file mode 100644 index 5f27b3ec8..000000000 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath deleted file mode 100644 index 1b4926106..000000000 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath b/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath deleted file mode 100644 index 48aa7010c..000000000 --- a/build/resources/test/puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file From fffa63ba7de3b82ffbbda44c4fee8bcc4eccdec8 Mon Sep 17 00:00:00 2001 From: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:25:25 -0500 Subject: [PATCH 58/77] Issue 413 operator transition (#680) * Began updating case rule evaluation algorithm Added new logic to checkRuleRaw in CaseRule_Generic Updated null definition in CaseRule_GenericStatement Updated STT case rule constants * Fixed immutable cell bug Fixed bug in CaseRule_GenericStatement where cells that had type UNKNOWN in a generated case-rule board were immutable; Continued debugging for CaseRule_Generic; Updated specs and method descriptors. * Finalized rule verification Added final rule verification to CaseRule_Generic; Removed debugging. --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- .../ShortTruthTableOperation.java | 118 ++++---- .../rules/caserule/CaseRuleAnd.java | 46 +-- .../rules/caserule/CaseRuleAtomic.java | 1 - .../rules/caserule/CaseRuleConditional.java | 46 +-- .../rules/caserule/CaseRuleOr.java | 46 +-- .../rules/caserule/CaseRule_Generic.java | 144 +++++----- .../caserule/CaseRule_GenericStatement.java | 261 ++++++++---------- 7 files changed, 316 insertions(+), 346 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableOperation.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableOperation.java index 380afb4cd..bc713d407 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableOperation.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableOperation.java @@ -1,61 +1,57 @@ -package edu.rpi.legup.puzzle.shorttruthtable; - -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -public class ShortTruthTableOperation { - - public static final char AND = '^'; - public static final char OR = '|'; - public static final char NOT = '~'; - public static final char CONDITIONAL = '>'; - public static final char BICONDITIONAL = '-'; - - private ShortTruthTableOperation() { - } - - - public static String getLogicSymbol(char c) { - switch (c) { - case AND: - return "\u2227"; - case OR: - return "\u2228"; - case NOT: - return "\u00AC"; - case CONDITIONAL: - return "\u2192"; - case BICONDITIONAL: - return "\u2194"; - } - return "" + c; - } - - public static String getRuleName(char operation) { - switch (operation) { - case AND: - return "And"; - case OR: - return "Or"; - case NOT: - return "Not"; - case CONDITIONAL: - return "Conditional"; - case BICONDITIONAL: - return "Biconditional"; - } - return null; - } - - - public static boolean isOperation(char c) { - return c == AND || - c == OR || - c == NOT || - c == CONDITIONAL || - c == BICONDITIONAL; - } - - -} +package edu.rpi.legup.puzzle.shorttruthtable; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +public class ShortTruthTableOperation { + + public static final char AND = '^'; + public static final char OR = '|'; + public static final char NOT = '~'; + public static final char CONDITIONAL = '>'; + public static final char BICONDITIONAL = '-'; + + private ShortTruthTableOperation() { + } + + public static String getLogicSymbol(char c) { + switch (c) { + case AND: + return "\u2227"; + case OR: + return "\u2228"; + case NOT: + return "\u00AC"; + case CONDITIONAL: + return "\u2192"; + case BICONDITIONAL: + return "\u2194"; + } + return "" + c; + } + + public static String getRuleName(char operation) { + switch (operation) { + case AND: + return "And"; + case OR: + return "Or"; + case NOT: + return "Not"; + case CONDITIONAL: + return "Conditional"; + case BICONDITIONAL: + return "Biconditional"; + } + return null; + } + + public static boolean isOperation(char c) { + return c == AND || + c == OR || + c == NOT || + c == CONDITIONAL || + c == BICONDITIONAL; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAnd.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAnd.java index ddc84df16..12fac9c4c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAnd.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAnd.java @@ -1,23 +1,23 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; - -public class CaseRuleAnd extends CaseRule_GenericStatement { - - public CaseRuleAnd() { - super("STTT-CASE-0001", ShortTruthTableOperation.AND, - "And", - trueCases, - falseCases); - } - - private static final ShortTruthTableCellType[][] trueCases = { - {T, T} - }; - private static final ShortTruthTableCellType[][] falseCases = { - {F, N}, - {N, F} - }; - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; + +public class CaseRuleAnd extends CaseRule_GenericStatement { + + public CaseRuleAnd() { + super("STTT-CASE-0001", ShortTruthTableOperation.AND, + "And", + trueCases, + falseCases); + } + + private static final ShortTruthTableCellType[][] trueCases = { + {T, T} + }; + private static final ShortTruthTableCellType[][] falseCases = { + {F, U}, + {U, F} + }; + +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java index 9b2ef7fea..f168499cc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java @@ -20,7 +20,6 @@ public CaseRuleAtomic() { super("STTT-CASE-0002", "Atomic", "True or False", "Each unknown cell must either be true or false"); - System.out.println("Case Rule T/F constructor"); } // Adds all elements that can be selected for this case rule diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleConditional.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleConditional.java index 867708729..a9fee2b4e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleConditional.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleConditional.java @@ -1,23 +1,23 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; - -public class CaseRuleConditional extends CaseRule_GenericStatement { - - public CaseRuleConditional() { - super("STTT-CASE-0004", ShortTruthTableOperation.CONDITIONAL, - "Conditional", - trueCases, - falseCases); - } - - private static final ShortTruthTableCellType[][] trueCases = { - {N, T}, - {F, N} - }; - private static final ShortTruthTableCellType[][] falseCases = { - {T, F}, - }; - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; + +public class CaseRuleConditional extends CaseRule_GenericStatement { + + public CaseRuleConditional() { + super("STTT-CASE-0004", ShortTruthTableOperation.CONDITIONAL, + "Conditional", + trueCases, + falseCases); + } + + private static final ShortTruthTableCellType[][] trueCases = { + {U, T}, + {F, U} + }; + private static final ShortTruthTableCellType[][] falseCases = { + {T, F}, + }; + +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleOr.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleOr.java index e0bc9774c..82f814cc8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleOr.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleOr.java @@ -1,23 +1,23 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; - -public class CaseRuleOr extends CaseRule_GenericStatement { - - public CaseRuleOr() { - super("STTT-CASE-0005", ShortTruthTableOperation.OR, - "Or", - trueCases, - falseCases); - } - - private static final ShortTruthTableCellType[][] trueCases = { - {T, N}, - {N, T} - }; - private static final ShortTruthTableCellType[][] falseCases = { - {F, F}, - }; - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; + +public class CaseRuleOr extends CaseRule_GenericStatement { + + public CaseRuleOr() { + super("STTT-CASE-0005", ShortTruthTableOperation.OR, + "Or", + trueCases, + falseCases); + } + + private static final ShortTruthTableCellType[][] trueCases = { + {T, U}, + {U, T} + }; + private static final ShortTruthTableCellType[][] falseCases = { + {F, F}, + }; + +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java index 047ea5d33..9f3a7073f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java @@ -1,73 +1,71 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.CaseRule; -import edu.rpi.legup.model.tree.TreeTransition; - -import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; -import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; - - -import java.util.ArrayList; -import java.util.List; - -public abstract class CaseRule_Generic extends CaseRule { - - public CaseRule_Generic(String ruleID, String ruleName, String title, String description) { - super(ruleID, title, description, "edu/rpi/legup/images/shorttruthtable/ruleimages/case/" + ruleName + ".png"); - } - - - /** - * Checks whether the transition logically follows from the parent node using this rule - * - * @param transition transition to check - * @return null if the child node logically follow from the parent node, otherwise error message - */ - @Override - public String checkRuleRaw(TreeTransition transition) { - List childTransitions = transition.getParents().get(0).getChildren(); - if (childTransitions.size() != 2) { - return "This case rule must have 2 children."; - } - - TreeTransition case1 = childTransitions.get(0); - TreeTransition case2 = childTransitions.get(1); - if (case1.getBoard().getModifiedData().size() != 1 || case2.getBoard().getModifiedData().size() != 1) { - return "This case rule must have 1 modified cell for each case."; - } - - ShortTruthTableCell mod1 = (ShortTruthTableCell) case1.getBoard().getModifiedData().iterator().next(); - ShortTruthTableCell mod2 = (ShortTruthTableCell) case2.getBoard().getModifiedData().iterator().next(); - if (!mod1.getLocation().equals(mod2.getLocation())) { - return "This case rule must modify the same cell for each case."; - } - - boolean firstPossibility = mod1.getType() == ShortTruthTableCellType.TRUE && mod2.getType() == ShortTruthTableCellType.FALSE; - boolean secondPossibility = mod1.getType() == ShortTruthTableCellType.FALSE && mod2.getType() == ShortTruthTableCellType.TRUE; - if (!firstPossibility && !secondPossibility) { - return "This case rule must an empty true or false cell."; - } - - return null; - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return checkRuleRaw(transition); - } -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.tree.TreeTransition; + +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.JOptionPane; + +import com.google.firebase.database.core.utilities.Tree; + +public abstract class CaseRule_Generic extends CaseRule { + + public CaseRule_Generic(String ruleID, String ruleName, String title, String description) { + super(ruleID, title, description, "edu/rpi/legup/images/shorttruthtable/ruleimages/case/" + ruleName + ".png"); + } + + + /** + * Checks whether the transition logically follows from the parent node using this rule + * + * @param transition transition to check + * @return null if the child node logically follow from the parent node, otherwise error message + */ + @Override + public String checkRuleRaw(TreeTransition transition) { + // Validate that two children are generated + List childTransitions = transition.getParents().get(0).getChildren(); + if (childTransitions.size() != 2) { + return "ERROR: This case rule must have 2 children."; + } + + // Validate that the modified cells are of type UNKNOWN, TRUE, or FALSE + List cases = Arrays.asList(childTransitions.get(0), childTransitions.get(1)); + for (TreeTransition c : cases) { + ShortTruthTableCell mod1 = (ShortTruthTableCell)c.getBoard().getModifiedData().iterator().next(); + ShortTruthTableCell mod2 = (ShortTruthTableCell)c.getBoard().getModifiedData().iterator().next(); + if (!(mod1.getType() == ShortTruthTableCellType.TRUE || mod1.getType() == ShortTruthTableCellType.FALSE || mod1.getType() == ShortTruthTableCellType.UNKNOWN) && + (mod2.getType() == ShortTruthTableCellType.TRUE || mod2.getType() == ShortTruthTableCellType.FALSE || mod2.getType() == ShortTruthTableCellType.UNKNOWN)) { + return "ERROR: This case rule must be an unknown, true, or false cell."; + } + } + return null; + } + + /** + * Checks whether the child node logically follows from the parent node + * at the specific puzzleElement index using this rule + * + * @param transition transition to check + * @param puzzleElement equivalent puzzleElement + * @return null if the child node logically follow from the parent node at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return checkRuleRaw(transition); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java index 375a60292..2a40bf45d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java @@ -1,142 +1,119 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; - - -import java.util.ArrayList; - -public abstract class CaseRule_GenericStatement extends CaseRule_Generic { - - public CaseRule_GenericStatement(String ruleID, char operation, String title, - ShortTruthTableCellType[][] trueCases, - ShortTruthTableCellType[][] falseCases) { - super(ruleID, ShortTruthTableOperation.getRuleName(operation), - title + " case", - "A known " + title.toUpperCase() + " statement can have multiple forms"); - - this.operation = operation; - - this.trueCases = trueCases; - this.falseCases = falseCases; - } - - private final char operation; - - private final ShortTruthTableCellType[][] trueCases; - private final ShortTruthTableCellType[][] falseCases; - - protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; - protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; - protected static final ShortTruthTableCellType N = null; - - //Adds all elements that can be selected for this caserule - @Override - public CaseBoard getCaseBoard(Board board) { - //copy the board and add all elements that can be selected - ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); - sttBoard.setModifiable(false); - CaseBoard caseBoard = new CaseBoard(sttBoard, this); - - //add all elements that can be selected for the case rule statement - for (PuzzleElement element : sttBoard.getPuzzleElements()) { - - System.out.println("GetCaseBoard Testing: " + element); - - //get the cell object - ShortTruthTableCell cell = sttBoard.getCellFromElement(element); - //the cell must match the symbol - if (cell.getSymbol() != this.operation) continue; - - System.out.println(" Selectable... checking logic"); - - //the statement must be assigned with unassigned sub-statements - if (!cell.getType().isTrueOrFalse()) continue; - System.out.println(" Operation is known"); - if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; - System.out.println(" right side is unknown"); - if (this.operation != ShortTruthTableOperation.NOT && - cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { - continue; - } - System.out.println(" left side is unknown"); - - System.out.println(" Valid choice"); - //if the element has passed all the checks, it can be selected - caseBoard.addPickableElement(element); - - } - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ - @SuppressWarnings("unchecked") - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - - ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); - - ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); - - //if the statement is set to true - if (cell.getType() == ShortTruthTableCellType.TRUE) { - return getCasesFromCell(sttBoard, puzzleElement, trueCases); - } - - //if the statement is set to false - return getCasesFromCell(sttBoard, puzzleElement, falseCases); - - } - - private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { - - //store all possible boards - ArrayList cases = new ArrayList<>(); - - //go through all the possibilities - for (int i = 0; i < possibilities.length; i++) { - //create a new board - ShortTruthTableBoard b = board.copy(); - - //get the statement of the square that was selected - ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); - ShortTruthTableStatement statement = cell.getStatementReference(); - - //modify its children - //avoid error if it is a NOT statement - if (possibilities[i][0] != null) { - ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); - leftCell.setData(possibilities[i][0]); - b.addModifiedData(leftCell); - } - - //always modify the right side of the statement - if (possibilities[i][1] != null) { - ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); - rightCell.setData(possibilities[i][1]); - b.addModifiedData(rightCell); - } - - //add the board possibility to the list - cases.add(b); - } - - //return all possibilities - return cases; - - } - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + + +import java.util.ArrayList; + +public abstract class CaseRule_GenericStatement extends CaseRule_Generic { + + public CaseRule_GenericStatement(String ruleID, char operation, String title, + ShortTruthTableCellType[][] trueCases, + ShortTruthTableCellType[][] falseCases) { + super(ruleID, ShortTruthTableOperation.getRuleName(operation), + title + " case", + "A known " + title.toUpperCase() + " statement can have multiple forms"); + + this.operation = operation; + + this.trueCases = trueCases; + this.falseCases = falseCases; + } + + private final char operation; + + private final ShortTruthTableCellType[][] trueCases; + private final ShortTruthTableCellType[][] falseCases; + + protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; + protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; + protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; + + // Adds all elements that can be selected for this caserule + @Override + public CaseBoard getCaseBoard(Board board) { + //copy the board and add all elements that can be selected + ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); + sttBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(sttBoard, this); + + //add all elements that can be selected for the case rule statement + for (PuzzleElement element : sttBoard.getPuzzleElements()) { + //get the cell object + ShortTruthTableCell cell = sttBoard.getCellFromElement(element); + //the cell must match the symbol + if (cell.getSymbol() != this.operation) continue; + //the statement must be assigned with unassigned sub-statements + if (!cell.getType().isTrueOrFalse()) continue; + if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; + if (this.operation != ShortTruthTableOperation.NOT && + cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { + continue; + } + //if the element has passed all the checks, it can be selected + caseBoard.addPickableElement(element); + } + return caseBoard; + } + + /** + * Gets the possible cases at a specific location based on this case rule + * + * @param board the current board state + * @param puzzleElement equivalent puzzleElement + * @return a list of elements the specified could be + */ + @SuppressWarnings("unchecked") + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); + ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); + + // If the statement is set to true, collect true cases. Otherwise, collect the false cases + if (cell.getType() == ShortTruthTableCellType.TRUE) { + return getCasesFromCell(sttBoard, puzzleElement, trueCases); + } + return getCasesFromCell(sttBoard, puzzleElement, falseCases); + } + + /** + * Collects a list of boards for each possible outcome of case-rule application + * @param board current board state + * @param puzzleElement case rule operator + * @param possibilities list of possibilities for operator state + * @return ArrayList of Boards + */ + private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { + // Create branch case for each possibility + ArrayList cases = new ArrayList<>(); + for (int i = 0; i < possibilities.length; i++) { + // Create a new board to modify and get statement of selected square + ShortTruthTableBoard b = board.copy(); + ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); + ShortTruthTableStatement statement = cell.getStatementReference(); + + // Modify neighboring cells of case-rule application by the provided logical cases + if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); + leftCell.setData(possibilities[i][0]); + b.addModifiedData(leftCell); + } + if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); + rightCell.setData(possibilities[i][1]); + b.addModifiedData(rightCell); + } + + cases.add(b); + } + return cases; + } +} From 3ea1346ab167dc641e9d1ea9c60ab147ba1a3551 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:26:11 -0500 Subject: [PATCH 59/77] Added more Short Truth Table tests (#675) * Initial setup * Working initial test * Added another test * Added more tests * Added another test * Added comments, removed useless imports, added 1 more test * Reformatting * Removed useless import * Added initial and elimination test * Comments and spacing * Another test * Updated comments and wrote new test * Created two new test files * Renamed test to be more descriptive * Added another test * Rewrote test to be more comprehensive * More tests :)))) * Fixed test name and file * Fixed test * Fixed broken tests * Shouldn't have touched these files * CHECKSTYLE * Trying to make CheckStyle happy * Initial commit for more tests * Finished a test * Another test done * More tests * Added cannot set both at once test * Conditional files * Some tests done * More tests * Even more tests * Fixed comments to use biconditional symbol * Added tests for false conditional Now tests only setting the A value and only setting the B value * Fixed some broken biconditional files and added new ones * Added more tests for B and fixed some descriptions * Checkstyle fix * Added tests for Not Elimination One of the tests failed, but I tested that and that seems to be the case. It should be failing. * Temporarily commenting out broken test --------- Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> --- .../rules/BiconditionalEliminationTest.java | 373 ++++++++++++++++++ .../rules/ConditionalEliminationTest.java | 233 +++++++++++ .../rules/NotEliminationTest.java | 124 ++++++ .../FalseBiconditional | 13 + .../FalseBiconditionalWithFalseA | 14 + .../FalseBiconditionalWithFalseB | 14 + .../FalseBiconditionalWithTrueA | 14 + .../FalseBiconditionalWithTrueB | 14 + .../TrueBiconditional | 13 + .../TrueBiconditionalWithFalseA | 14 + .../TrueBiconditionalWithFalseB | 14 + .../TrueBiconditionalWithTrueA | 14 + .../TrueBiconditionalWithTrueB | 14 + .../FalseConditional | 13 + .../TrueConditional | 13 + .../TrueConditionalWithFalseB | 14 + .../TrueConditionalWithTrueA | 14 + .../TrueConditionalWithTrueB | 14 + .../rules/NotEliminationDirectRule/BlankNot | 12 + .../rules/NotEliminationDirectRule/FalseNot | 13 + .../rules/NotEliminationDirectRule/TrueNot | 13 + 21 files changed, 974 insertions(+) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/ConditionalEliminationTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/NotEliminationTest.java create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditional create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseB create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueB create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditional create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseB create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueB create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditional create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithFalseB create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueB create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/BlankNot create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/FalseNot create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/TrueNot diff --git a/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java new file mode 100644 index 000000000..05faf87bb --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java @@ -0,0 +1,373 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination.DirectRuleBiconditionalElimination; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class BiconditionalEliminationTest { + private static final DirectRuleBiconditionalElimination RULE = new DirectRuleBiconditionalElimination(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given one statement: A <-> B where both A and <-> are true + * + * Asserts that this is a valid application of the rule if and only if B is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueBiconditionalWithTrueATest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell morty = board.getCell(2, 0); + + // Asserts that this is not a valid application of the rule when B is unknown + morty.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when B is true + morty.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(morty); + Assert.assertNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when B is false + morty.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where both B and <-> are true + * + * Asserts that this is a valid application of the rule if and only if A is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueBiconditionalWithTrueBTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell rick = board.getCell(0, 0); + + // Asserts that this is a valid application of the rule when A is true + rick.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(rick); + Assert.assertNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when A is unknown + rick.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when A is false + rick.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + + /** + * Given one statement: A <-> B where A is false and <-> is true + * + * Asserts that this is a valid application of the rule if and only if B is false. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueBiconditionalWithFalseATest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell morty = board.getCell(2, 0); + + // Asserts that this is not a valid application of the rule when B is unknown + morty.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when B is true + morty.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when B is false + morty.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(morty); + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where B is false and <-> is true + * + * Asserts that this is a valid application of the rule if and only if A is false. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueBiconditionalWithFalseBTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell rick = board.getCell(0, 0); + + // Asserts that this is not a valid application of the rule when A is unknown + rick.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when A is true + rick.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when A is false + rick.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(rick); + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where A is true and <-> is false + * + * Asserts that this is a valid application of the rule if and only if B is false. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseBiconditionalWithTrueATest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell morty = board.getCell(2, 0); + + // Asserts that this is not a valid application of the rule when B is unknown + morty.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when B is true + morty.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when B is false + morty.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(morty); + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where B is true and <-> is false + * + * Asserts that this is a valid application of the rule if and only if A is false. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseBiconditionalWithTrueBTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell rick = board.getCell(0, 0); + + // Asserts that this is not a valid application of the rule when A is unknown + rick.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when A is true + rick.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when A is false + rick.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(rick); + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where A and <-> are false + * + * Asserts that this is a valid application of the rule if and only if B is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseBiconditionalWithFalseATest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell morty = board.getCell(2, 0); + + // Asserts that this is not a valid application of the rule when B is unknown + morty.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when B is true + morty.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(morty); + Assert.assertNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when B is false + morty.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(morty); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where B and <-> are false + * + * Asserts that this is a valid application of the rule if and only if A is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseBiconditionalWithFalseBTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell rick = board.getCell(0, 0); + + // Asserts that this is not a valid application of the rule when A is unknown + rick.setData(ShortTruthTableCellType.UNKNOWN); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is not a valid application of the rule when A is false + rick.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(rick); + Assert.assertNotNull(RULE.checkRule(transition)); + + // Asserts that this is a valid application of the rule when A is true + rick.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(rick); + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A <-> B where <-> is true + * + * Asserts that setting any combination of A and B at the same time is not a valid + * application of this rule + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueBiconditionalSetBothAtOnceTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditional", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell rick = board.getCell(0, 0); + ShortTruthTableCell morty = board.getCell(2, 0); + + rick.setData(cellType1); + morty.setData(cellType2); + + board.addModifiedData(rick); + board.addModifiedData(morty); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Asserts that setting any combination of A and B at the same time is not a valid + * application of this rule. This is tested on multiple files. + * + * @throws InvalidFileFormatException + */ + @Test + public void CannotSetBothAandBAtOnceTest() throws InvalidFileFormatException { + String directory = "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/"; + setAandBBothAtOnceTest(directory + "FalseBiconditional"); + setAandBBothAtOnceTest(directory + "TrueBiconditional"); + setAandBBothAtOnceTest(directory + "FalseBiconditionalWithFalseA"); + setAandBBothAtOnceTest(directory + "TrueBiconditionalWithFalseA"); + setAandBBothAtOnceTest(directory + "FalseBiconditionalWithTrueA"); + setAandBBothAtOnceTest(directory + "TrueBiconditionalWithTrueA"); + } + + /** + * Helper function to test biconditional elimination rule with given file path. + * + * @param filePath The file path for test board setup. + * @throws InvalidFileFormatException + */ + private void setAandBBothAtOnceTest(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell rick = board.getCell(0, 0); + ShortTruthTableCell morty = board.getCell(2, 0); + + rick.setData(cellType1); + morty.setData(cellType2); + + board.addModifiedData(rick); + board.addModifiedData(morty); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/ConditionalEliminationTest.java b/src/test/java/puzzles/shorttruthtable/rules/ConditionalEliminationTest.java new file mode 100644 index 000000000..8d0bb4e1a --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/ConditionalEliminationTest.java @@ -0,0 +1,233 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination.DirectRuleConditionalElimination; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ConditionalEliminationTest { + private static final DirectRuleConditionalElimination RULE = new DirectRuleConditionalElimination(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given one statement: A -> B where -> is false + * + * Asserts that the only valid combination of A and B that is a valid application + * of this rule is when A is true and B is false + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseConditionalTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell aubergine = board.getCell(0, 0); + ShortTruthTableCell boniato = board.getCell(2, 0); + + aubergine.setData(cellType1); + boniato.setData(cellType2); + + board.addModifiedData(aubergine); + board.addModifiedData(boniato); + + if (cellType1 == ShortTruthTableCellType.TRUE && cellType2 == ShortTruthTableCellType.FALSE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + } + + /** + * Given one statement: A -> B where -> is false + * + * Asserts that this is a valid application of the rule if and only if A + * is set to true. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseConditionalTrueATest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell aubergine = board.getCell(0, 0); + + aubergine.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(aubergine); + Assert.assertNull(RULE.checkRule(transition)); + + aubergine.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(aubergine); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A -> B where -> is false + * + * Asserts that this is a valid application of the rule if and only if B is + * set to false. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseConditionalFalseBTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell boniato = board.getCell(2, 0); + + boniato.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(boniato); + Assert.assertNull(RULE.checkRule(transition)); + + boniato.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(boniato); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A -> B where -> is true + * + * Asserts that you cannot set any combination of both A and B at the same time. + * + * @throws InvalidFileFormatException + */ + @Test + public void CannotSetBothAandBTrueConditionalTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditional", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell aubergine = board.getCell(0, 0); + ShortTruthTableCell boniato = board.getCell(2, 0); + + aubergine.setData(cellType1); + boniato.setData(cellType2); + + board.addModifiedData(aubergine); + board.addModifiedData(boniato); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: A -> B where A and -> are true + * + * Asserts that this is a valid application of this rule if and only if B + * is set to true. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueAMeansTrueBTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell boniato = board.getCell(2, 0); + + boniato.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(boniato); + Assert.assertNull(RULE.checkRule(transition)); + + boniato.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(boniato); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A -> B where B is false and -> is true + * + * Asserts that this is a valid application of this rule if and only if A + * is set to false. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseBMeansFalseATest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithFalseB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell aubergine = board.getCell(0, 0); + + aubergine.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(aubergine); + Assert.assertNull(RULE.checkRule(transition)); + + aubergine.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(aubergine); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given one statement: A -> B where B and -> are true + * + * Asserts that this is not a valid application of this rule no matter what + * A is set to. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueBCannotDetermineA() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueB", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell boniato = board.getCell(2, 0); + + boniato.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(boniato); + Assert.assertNotNull(RULE.checkRule(transition)); + + boniato.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(boniato); + Assert.assertNotNull(RULE.checkRule(transition)); + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/NotEliminationTest.java b/src/test/java/puzzles/shorttruthtable/rules/NotEliminationTest.java new file mode 100644 index 000000000..6dbbf141c --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/NotEliminationTest.java @@ -0,0 +1,124 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination.DirectRuleNotElimination; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class NotEliminationTest { + private static final DirectRuleNotElimination RULE = new DirectRuleNotElimination(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given one statement: ¬A where ¬ is false + * + * Asserts that this is a valid application of this rule if and only if A is true + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseNot() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/NotEliminationDirectRule/FalseNot", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell a = board.getCell(1, 0); + a.setData(cellType); + board.addModifiedData(a); + + if (cellType == ShortTruthTableCellType.TRUE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: ¬A where ¬ is true + * + * Asserts that this is a valid application of this rule if and only if A is false + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueNot() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/NotEliminationDirectRule/TrueNot", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell a = board.getCell(1, 0); + a.setData(cellType); + board.addModifiedData(a); + + if (cellType == ShortTruthTableCellType.FALSE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + +// /** +// * Given one statement: ¬A +// * +// * Asserts that setting both ¬ and A to any values would not be a valid +// * application of this rule +// * +// * @throws InvalidFileFormatException +// */ +// @Test +// public void CannotSetBothAtOnceTest() throws InvalidFileFormatException { +// TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/NotEliminationDirectRule/BlankNot", stt); +// TreeNode rootNode = stt.getTree().getRootNode(); +// TreeTransition transition = rootNode.getChildren().get(0); +// transition.setRule(RULE); +// +// ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; +// +// for (ShortTruthTableCellType cellType1 : cellTypes) { +// for (ShortTruthTableCellType cellType2 : cellTypes) { +// ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); +// ShortTruthTableCell not = board.getCell(0, 0); +// ShortTruthTableCell a = board.getCell(1, 0); +// +// not.setData(cellType1); +// a.setData(cellType2); +// +// board.addModifiedData(not); +// board.addModifiedData(a); +// +// System.out.println("TYPE1:" + cellType1); +// System.out.println("TYPE2:" + cellType2); +// Assert.assertNotNull(RULE.checkRule(transition)); +// } +// } +// } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditional b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditional new file mode 100644 index 000000000..5ea1c8a63 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditional @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseA b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseA new file mode 100644 index 000000000..1f956a743 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseA @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseB b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseB new file mode 100644 index 000000000..49fd4f49f --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueA b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueA new file mode 100644 index 000000000..000a07d91 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueA @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueB b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueB new file mode 100644 index 000000000..18cbd4156 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditional b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditional new file mode 100644 index 000000000..cbf64468f --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditional @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseA b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseA new file mode 100644 index 000000000..9655bc7ae --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseA @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseB b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseB new file mode 100644 index 000000000..032fc23df --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueA b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueA new file mode 100644 index 000000000..44381c235 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueA @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueB b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueB new file mode 100644 index 000000000..8e42c0032 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional new file mode 100644 index 000000000..2da458f51 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditional b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditional new file mode 100644 index 000000000..829861f75 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditional @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithFalseB b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithFalseB new file mode 100644 index 000000000..85e42664a --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithFalseB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueA b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueA new file mode 100644 index 000000000..d77a27502 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueA @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueB b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueB new file mode 100644 index 000000000..86b10a7cc --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueB @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/BlankNot b/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/BlankNot new file mode 100644 index 000000000..e7bf96e4a --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/BlankNot @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/FalseNot b/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/FalseNot new file mode 100644 index 000000000..2e9ac998d --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/FalseNot @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/TrueNot b/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/TrueNot new file mode 100644 index 000000000..6f19fc871 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/NotEliminationDirectRule/TrueNot @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From 75556802f1862253b8f8df692dfac9f62e88d613 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:30:17 -0500 Subject: [PATCH 60/77] Dynamically update FlatLaf version (#681) * Update build.gradle * Update build.gradle Made FlatLaf versioning dynamic * Getting rid of stray line --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1ba7ea006..062a2cbff 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ sourceCompatibility = 11 dependencies { implementation 'org.jetbrains:annotations:20.1.0' implementation 'org.jetbrains:annotations:20.1.0' - implementation 'com.formdev:flatlaf:3.0' + implementation 'com.formdev:flatlaf:3.+' implementation 'com.google.firebase:firebase-admin:6.3.0' implementation 'org.apache.httpcomponents:httpclient:4.5.1' implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' From 3401742ab2fc5a647d440bb68f7183f1b93c3973 Mon Sep 17 00:00:00 2001 From: Ivan Ho <41582274+Corppet@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:30:37 -0500 Subject: [PATCH 61/77] Adjusted root project name to reflect case-sensitive root directory. (#686) Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index f9f593d2b..dcbd58cf9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -rootProject.name = 'Legup' +rootProject.name = 'LEGUP' From 0e2d70b94c1628e1bd640efd8a59b00483498ff9 Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Thu, 9 Nov 2023 11:15:21 -0500 Subject: [PATCH 62/77] added FinishWithTentsTest (#676) * added some tests * added another test * adhere to style guide * added comments to tests --------- Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> --- .../rules/FinishWithTentsDirectRuleTest.java | 309 +++++++++++++++++- .../FinishWithTentsDirectRule/AdditionalTents | 20 ++ .../FinishWithTentsDirectRule/AmbiguousTents | 20 ++ .../FinishWithHorizontalTents | 20 ++ .../FinishWithTentsDirectRule/FinishWithTents | 8 +- .../FinishWithTentsFail | 19 ++ .../FinishWithVerticalTents | 20 ++ .../FinishWithTentsDirectRule/TooManyTents | 20 ++ 8 files changed, 426 insertions(+), 10 deletions(-) create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents diff --git a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java index 6c1468c50..dff292977 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java @@ -15,6 +15,7 @@ import org.junit.Test; import java.awt.*; +import java.util.*; public class FinishWithTentsDirectRuleTest { @@ -26,9 +27,94 @@ public static void setUp() { MockGameBoardFacade.getInstance(); treetent = new TreeTent(); } + + /** + * 3x3 TreeTent puzzle with a GRASS tile at (0,0) + * Tests FinishWithTentsDirectRule on TENT tiles horizontal of the GRASS tile + * at (1,0) and (2,0) + * + * @throws InvalidFileFormatException + */ + @Test + public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + cell1.setData(TreeTentType.TENT); + TreeTentCell cell2 = board.getCell(2, 0); + cell2.setData(TreeTentType.TENT); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + TreeTentCell c = board.getCell(k, i); + if ((c.getLocation()).equals(cell1.getLocation()) || (c.getLocation()).equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } + + /** + * 3x3 TreeTent puzzle with a GRASS tile at (0,0) + * Tests FinishWithTentsDirectRule on TENT tiles vertical of the GRASS tile + * at (0,1) and (0,2) + * + * @throws InvalidFileFormatException + */ + @Test + public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0, 1); + cell1.setData(TreeTentType.TENT); + TreeTentCell cell2 = board.getCell(0, 2); + cell2.setData(TreeTentType.TENT); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + Assert.assertNull(RULE.checkRule(transition)); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + TreeTentCell c = board.getCell(k, i); + if ((c.getLocation()).equals(cell1.getLocation()) || (c.getLocation()).equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } + + /** + * 3x3 TreeTent puzzle with a GRASS tile at (1,1) + * Tests FinishWithTentsDirectRule on TENT tiles around the GRASS tile + * at (1,0), (1,2), (0,1), and (2,1) + * + * @throws InvalidFileFormatException + */ @Test - public void EmptyFieldTest() throws InvalidFileFormatException { + public void FinishWithTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -37,27 +123,238 @@ public void EmptyFieldTest() throws InvalidFileFormatException { TreeTentBoard board = (TreeTentBoard) transition.getBoard(); TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 2); + TreeTentCell cell3 = board.getCell(0, 1); + TreeTentCell cell4 = board.getCell(2, 1); + cell1.setData(TreeTentType.TENT); - TreeTentCell cell2 = board.getCell(2, 0); cell2.setData(TreeTentType.TENT); + cell3.setData(TreeTentType.TENT); + cell4.setData(TreeTentType.TENT); board.addModifiedData(cell1); board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); Assert.assertNull(RULE.checkRule(transition)); for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - Point point = new Point(k, i); - if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + TreeTentCell c = board.getCell(k, i); + if ((c.getLocation()).equals(cell1.getLocation()) || + (c.getLocation()).equals(cell2.getLocation()) || + (c.getLocation()).equals(cell3.getLocation()) || + (c.getLocation()).equals(cell4.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, c)); } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); + } + } + } + } + + /** + * 3x3 TreeTent puzzle with a TENT tile at (1,1) + * Tests FinishWithTentsDirectRule on TENT tiles around the TENT tile + * at (1,0), (1,2), (0,1), and (2,1) + * + * @throws InvalidFileFormatException + */ + @Test + public void AdditionalTentsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 2); + TreeTentCell cell3 = board.getCell(0, 1); + TreeTentCell cell4 = board.getCell(2, 1); + + cell1.setData(TreeTentType.TENT); + cell2.setData(TreeTentType.TENT); + cell3.setData(TreeTentType.TENT); + cell4.setData(TreeTentType.TENT); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + TreeTentCell c = board.getCell(k, i); + if ((c.getLocation()).equals(cell1.getLocation()) || + (c.getLocation()).equals(cell2.getLocation()) || + (c.getLocation()).equals(cell3.getLocation()) || + (c.getLocation()).equals(cell4.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, c)); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } } } + + /** + * Empty 3x3 TreeTent puzzle + * Tests FinishWithTentsDirectRule on TENT tiles of entire puzzle + * all TENT tiles should fail FinishWithTentsDirectRule + * as no TENT tiles should be there + * + * @throws InvalidFileFormatException + */ + @Test + public void FinishWithTentsFailTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + ArrayList cells = new ArrayList(); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + TreeTentCell c = board.getCell(k, i); + c.setData(TreeTentType.TENT); + board.addModifiedData(c); + cells.add(c); + } + } + + Assert.assertNotNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + + /** + * 3x3 TreeTent puzzle with a TENT tile at (1,1) + * Tests FinishWithTentsDirectRule on TENT tiles around the TENT tile + * at (1,0), (1,2), (0,1), and (2,1) + * all TENT tiles should fail FinishWithTentsDirectRule + * as there were already sufficient number of TENT tiles + * + * @throws InvalidFileFormatException + */ + @Test + public void TooManyTentsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + ArrayList cells = new ArrayList(); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if((k == 1)&&(i == 1)) { + continue; + } + TreeTentCell c = board.getCell(k, i); + c.setData(TreeTentType.TENT); + board.addModifiedData(c); + cells.add(c); + } + } + + Assert.assertNotNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + + /** + * 3x3 TreeTent puzzle with a TENT tile at (1,1) + * Tests FinishWithTentsDirectRule on TENT tiles around the TENT tile + * at (1,0), (1,2), (0,1), and (2,1) + * all TENT tiles should fail FinishWithTentsDirectRule + * as there are multiple configurations of the placement + * of the TENT tiles + * + * @throws InvalidFileFormatException + */ + @Test + public void AmbiguousTentsTest () throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell = board.getCell(0, 1); + cell.setData(TreeTentType.TENT); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + + cell.setData(TreeTentType.UNKNOWN); + board.addModifiedData(cell); + cell = board.getCell(1, 0); + cell.setData(TreeTentType.TENT); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + + cell.setData(TreeTentType.UNKNOWN); + board.addModifiedData(cell); + cell = board.getCell(2, 1); + cell.setData(TreeTentType.TENT); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + + cell.setData(TreeTentType.UNKNOWN); + board.addModifiedData(cell); + cell = board.getCell(1, 2); + cell.setData(TreeTentType.TENT); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } } diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents new file mode 100644 index 000000000..6fea9f102 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents new file mode 100644 index 000000000..96f2ed729 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents new file mode 100644 index 000000000..a10c04124 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents index a10c04124..a292cc972 100644 --- a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents @@ -2,16 +2,16 @@ - + - - + + - + diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail new file mode 100644 index 000000000..a13c7cc55 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents new file mode 100644 index 000000000..0a340cd17 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents new file mode 100644 index 000000000..8f71a57f9 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From d749c84a8eab27a5b02f8e804ba71acaf67202f2 Mon Sep 17 00:00:00 2001 From: pitbull51067 <103721450+pitbull51067@users.noreply.github.com> Date: Thu, 9 Nov 2023 11:21:03 -0500 Subject: [PATCH 63/77] Test suite branch (#685) * BulbsInPathContradictionRuleTest looks good. I added one more function to check for the BlockInHorizontalPath test case. * I think I might have run into a problem on the testing function. I will confirm this problem next time and will write another test case. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. --------- Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- .../BulbsInPathContradictionRuleTest.java | 16 ++++++++++ ...CannotLightACellContradictionRuleTest.java | 30 +++++++++++++++---- .../BlockInHorizontalPath | 11 +++++++ .../CanLightTest | 18 +++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInHorizontalPath create mode 100644 src/test/resources/puzzles/lightup/rules/CannotLightACellContradictionRule/CanLightTest diff --git a/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java index b595fec15..0e7930751 100644 --- a/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java @@ -34,6 +34,7 @@ public void BulbsInPathContradictionRule_LightInHorizontalPath() throws InvalidF Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); } @@ -67,4 +68,19 @@ public void BulbsInPathContradictionRule_BlockInVerticalPath() throws InvalidFil Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); } + + @Test + public void BulbsInPathContradictionRule_BlockInHorizontalPath() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInHorizontalPath", lightUp); + TreeNode rootNode = lightUp.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + LightUpBoard board = (LightUpBoard) transition.getBoard(); + Assert.assertNotNull(RULE.checkContradiction(board)); + Assert.assertNotNull(RULE.checkContradictionAt(board,board.getCell(0,0))); + Assert.assertNotNull(RULE.checkContradictionAt(board,board.getCell(2,0))); + Assert.assertNotNull(RULE.checkContradictionAt(board,board.getCell(1,1))); + + } } diff --git a/src/test/java/puzzles/lightup/rules/CannotLightACellContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/CannotLightACellContradictionRuleTest.java index 447476dbb..7b3ffd2b9 100644 --- a/src/test/java/puzzles/lightup/rules/CannotLightACellContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/CannotLightACellContradictionRuleTest.java @@ -21,13 +21,13 @@ public static void setUp() { lightUp = new LightUp(); } - @Test + @Test //extensive full testing of null and non-null in a 5x5 board public void FullLightTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/lightup/rules/CannotLightACellContradictionRule/FullLightTest", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); //confirm there is a contradiction somewhere on the board @@ -36,7 +36,7 @@ public void FullLightTest() throws InvalidFileFormatException { //confirm it is impossible to light up these squares Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 3))); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(3, 3))); - + //confirm these are not required to be lit because they are already lit or unable to be Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); @@ -44,13 +44,13 @@ public void FullLightTest() throws InvalidFileFormatException { Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(3, 2))); } - @Test + @Test //simple contradiction testing for null and non-null in a 3x3 board public void CannotLightMiddleTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/lightup/rules/CannotLightACellContradictionRule/CannotLight", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); //confirm there is a contradiction somewhere on the board @@ -68,4 +68,24 @@ public void CannotLightMiddleTest() throws InvalidFileFormatException { Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); } + + @Test + public void CanLightTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/lightup/rules/CannotLightACellContradictionRule/CanLightTest", lightUp); + TreeNode rootNode = lightUp.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + LightUpBoard board = (LightUpBoard) transition.getBoard(); + //confirm there is not a contradiction somewhere on the board + Assert.assertNotNull(RULE.checkContradiction(board)); + + //confirm that these cells can be lit, are already lit, or that they are just black blocks + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 3))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(3, 3))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(3, 2))); + } } diff --git a/src/test/resources/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInHorizontalPath b/src/test/resources/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInHorizontalPath new file mode 100644 index 000000000..3c8786e54 --- /dev/null +++ b/src/test/resources/puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInHorizontalPath @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/lightup/rules/CannotLightACellContradictionRule/CanLightTest b/src/test/resources/puzzles/lightup/rules/CannotLightACellContradictionRule/CanLightTest new file mode 100644 index 000000000..4169bf382 --- /dev/null +++ b/src/test/resources/puzzles/lightup/rules/CannotLightACellContradictionRule/CanLightTest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file From 9ac7253f2b35a65af08597e09f0ad91e9dda1b9e Mon Sep 17 00:00:00 2001 From: Relurk1 Date: Tue, 14 Nov 2023 16:55:34 -0500 Subject: [PATCH 64/77] Changes and additions to test suite This commit includes: - A change for readability made in the case rule, replacing Integer objects with NurikabeType - Adding false test cases where necessary - Updating test cases where necessary --- .../BlackBetweenRegionsDirectRuleTest.java | 24 +++++++++++++++ .../rules/BlackOrWhiteCaseRuleTest.java | 10 +++---- .../BlackSquareContradictionRuleTest.java | 22 +++++++++++++- .../rules/CornerBlackDirectRuleTest.java | 30 +++++++++++++++++-- .../rules/FillinBlackDirectRuleTest.java | 23 ++++++++++++++ .../rules/FillinWhiteDirectRuleTest.java | 26 ++++++++++++++++ .../IsolateBlackContradictionRuleTest.java | 22 +++++++++++++- .../rules/NoNumbersContradictionRuleTest.java | 26 +++++++++++++--- .../PreventBlackSquareDirectRuleTest.java | 20 +++++++++++++ .../rules/SurroundRegionDirectRuleTest.java | 22 ++++++++++++++ .../TooFewSpacesContradictionRuleTest.java | 23 ++++++++++++++ .../TooManySpacesContradictionRuleTest.java | 25 ++++++++++++++++ .../rules/WhiteBottleNeckDirectRuleTest.java | 26 ++++++++++++++++ .../FalseBlackBetweenRegions | 13 ++++++++ .../FalseBlackSquare | 11 +++++++ .../CornerBlackDirectRule/FalseCornerBlack | 12 ++++++++ .../FillinBlackDirectRule/FalseFillinBlack | 12 ++++++++ .../FillinWhiteDirectRule/FalseFillinWhite | 12 ++++++++ .../FalseIsolateBlack | 10 +++++++ .../{NoNumberReachable => FalseNoNumber} | 0 .../NoNumberContradictionRule/FalseNoNumber2 | 18 +++++++++++ .../{NoNumberSurroundBlack => SimpleNoNumber} | 0 .../FalseBlackSquare | 10 +++++++ .../FalseSurroundRegion | 10 +++++++ .../FalseTooFewSpaces | 17 +++++++++++ .../MultipleNumberRegion | 11 +++++++ .../WhiteBottleNeckDirectRule/FalseBottleNeck | 11 +++++++ 27 files changed, 432 insertions(+), 14 deletions(-) create mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/FalseBlackBetweenRegions create mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackSquareContradictionRule/FalseBlackSquare create mode 100644 src/test/resources/puzzles/nurikabe/rules/CornerBlackDirectRule/FalseCornerBlack create mode 100644 src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/FalseFillinBlack create mode 100644 src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/FalseFillinWhite create mode 100644 src/test/resources/puzzles/nurikabe/rules/IsolateBlackContradictionRule/FalseIsolateBlack rename src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/{NoNumberReachable => FalseNoNumber} (100%) create mode 100644 src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/FalseNoNumber2 rename src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/{NoNumberSurroundBlack => SimpleNoNumber} (100%) create mode 100644 src/test/resources/puzzles/nurikabe/rules/PreventBlackSquareDirectRule/FalseBlackSquare create mode 100644 src/test/resources/puzzles/nurikabe/rules/SurroundRegionDirectRule/FalseSurroundRegion create mode 100644 src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/FalseTooFewSpaces create mode 100644 src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/MultipleNumberRegion create mode 100644 src/test/resources/puzzles/nurikabe/rules/WhiteBottleNeckDirectRule/FalseBottleNeck diff --git a/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java index a6652ea60..7e8b5eb83 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java @@ -163,4 +163,28 @@ public void BlackBetweenRegionsDirectRule_VerticalBlackBetweenRegionsTest() thro } } } + + /** + * Tests the Black Between Regions direct rule for a false application of the rule, where a black tile is enclosed by one region + */ + @Test + public void BlackBetweenRegionsDirectRule_FalseBlackBetweenRegionsTest() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/FalseBlackBetweenRegions",nurikabe); + TreeNode rootNode = nurikabe.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeCell cell = board.getCell(1,1); + cell.setData(NurikabeType.BLACK.toValue()); + board.addModifiedData(cell); + + Assert.assertNotNull(RULE.checkRule(transition)); + + for(int i=0; i + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/BlackSquareContradictionRule/FalseBlackSquare b/src/test/resources/puzzles/nurikabe/rules/BlackSquareContradictionRule/FalseBlackSquare new file mode 100644 index 000000000..813114443 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/BlackSquareContradictionRule/FalseBlackSquare @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/CornerBlackDirectRule/FalseCornerBlack b/src/test/resources/puzzles/nurikabe/rules/CornerBlackDirectRule/FalseCornerBlack new file mode 100644 index 000000000..0599fdfa4 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/CornerBlackDirectRule/FalseCornerBlack @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/FalseFillinBlack b/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/FalseFillinBlack new file mode 100644 index 000000000..731e2dc74 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/FillinBlackDirectRule/FalseFillinBlack @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/FalseFillinWhite b/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/FalseFillinWhite new file mode 100644 index 000000000..f2599c71f --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/FillinWhiteDirectRule/FalseFillinWhite @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/IsolateBlackContradictionRule/FalseIsolateBlack b/src/test/resources/puzzles/nurikabe/rules/IsolateBlackContradictionRule/FalseIsolateBlack new file mode 100644 index 000000000..b4eb09302 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/IsolateBlackContradictionRule/FalseIsolateBlack @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/NoNumberReachable b/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/FalseNoNumber similarity index 100% rename from src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/NoNumberReachable rename to src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/FalseNoNumber diff --git a/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/FalseNoNumber2 b/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/FalseNoNumber2 new file mode 100644 index 000000000..be1fc64f7 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/FalseNoNumber2 @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/NoNumberSurroundBlack b/src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/SimpleNoNumber similarity index 100% rename from src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/NoNumberSurroundBlack rename to src/test/resources/puzzles/nurikabe/rules/NoNumberContradictionRule/SimpleNoNumber diff --git a/src/test/resources/puzzles/nurikabe/rules/PreventBlackSquareDirectRule/FalseBlackSquare b/src/test/resources/puzzles/nurikabe/rules/PreventBlackSquareDirectRule/FalseBlackSquare new file mode 100644 index 000000000..2fd8e7d35 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/PreventBlackSquareDirectRule/FalseBlackSquare @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/SurroundRegionDirectRule/FalseSurroundRegion b/src/test/resources/puzzles/nurikabe/rules/SurroundRegionDirectRule/FalseSurroundRegion new file mode 100644 index 000000000..5e0b04f68 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/SurroundRegionDirectRule/FalseSurroundRegion @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/FalseTooFewSpaces b/src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/FalseTooFewSpaces new file mode 100644 index 000000000..ca3e9be84 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/TooFewSpacesContradictionRule/FalseTooFewSpaces @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/MultipleNumberRegion b/src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/MultipleNumberRegion new file mode 100644 index 000000000..3ec126199 --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/TooManySpacesContradictionRule/MultipleNumberRegion @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/WhiteBottleNeckDirectRule/FalseBottleNeck b/src/test/resources/puzzles/nurikabe/rules/WhiteBottleNeckDirectRule/FalseBottleNeck new file mode 100644 index 000000000..521576aeb --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/WhiteBottleNeckDirectRule/FalseBottleNeck @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 9a617456bca7e0e7de3b3fd3f932dd2a3553967a Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:17:44 -0500 Subject: [PATCH 65/77] Or Elimination, Or Introduction, Not Introduction, and And Introduction Test Cases (#689) * Initial setup * Working initial test * Added another test * Added more tests * Added another test * Added comments, removed useless imports, added 1 more test * Reformatting * Removed useless import * Added initial and elimination test * Comments and spacing * Another test * Updated comments and wrote new test * Created two new test files * Renamed test to be more descriptive * Added another test * Rewrote test to be more comprehensive * More tests :)))) * Fixed test name and file * Fixed test * Fixed broken tests * Shouldn't have touched these files * CHECKSTYLE * Trying to make CheckStyle happy * Initial commit for more tests * Finished a test * Another test done * More tests * Added cannot set both at once test * Conditional files * Some tests done * More tests * Even more tests * Fixed comments to use biconditional symbol * Added tests for false conditional Now tests only setting the A value and only setting the B value * Fixed some broken biconditional files and added new ones * Added more tests for B and fixed some descriptions * Checkstyle fix * Added tests for Not Elimination One of the tests failed, but I tested that and that seems to be the case. It should be failing. * Temporarily commenting out broken test * Added or elimination puzzle files * Renamed directory These should be for Or Introduction, not Or Elimination * Added Or Elimination files * Added FTU and UTF tests * More tests * Add Or Intro tests * Checkstyle fix * Spacing fix * Added not introduction test * Added And Introduction tests and modified Or Introduction tests --------- Co-authored-by: Ivan Ho <41582274+Corppet@users.noreply.github.com> --- .../rules/AndIntroductionDirectRuleTest.java | 105 ++++++++++++ .../rules/NotIntroductionTest.java | 122 ++++++++++++++ .../rules/OrEliminationTest.java | 150 ++++++++++++++++++ .../rules/OrIntroductionTest.java | 105 ++++++++++++ .../rules/AndIntroductionDirectRule/FUF | 14 ++ .../rules/AndIntroductionDirectRule/FUT | 14 ++ .../rules/AndIntroductionDirectRule/FUU | 13 ++ .../rules/AndIntroductionDirectRule/TUF | 14 ++ .../rules/AndIntroductionDirectRule/TUT | 14 ++ .../rules/AndIntroductionDirectRule/TUU | 13 ++ .../rules/AndIntroductionDirectRule/UUF | 13 ++ .../rules/AndIntroductionDirectRule/UUT | 13 ++ .../rules/AndIntroductionDirectRule/UUU | 12 ++ .../rules/NotIntroductionDirectRule/BlankA | 12 ++ .../rules/NotIntroductionDirectRule/FalseA | 13 ++ .../rules/NotIntroductionDirectRule/TrueA | 13 ++ .../rules/OrEliminationDirectRule/FTU | 14 ++ .../rules/OrEliminationDirectRule/UFU | 13 ++ .../rules/OrEliminationDirectRule/UTF | 14 ++ .../rules/OrEliminationDirectRule/UTU | 13 ++ .../rules/OrIntroductionDirectRule/FUF | 14 ++ .../rules/OrIntroductionDirectRule/FUT | 14 ++ .../rules/OrIntroductionDirectRule/FUU | 13 ++ .../rules/OrIntroductionDirectRule/TUF | 14 ++ .../rules/OrIntroductionDirectRule/TUT | 14 ++ .../rules/OrIntroductionDirectRule/TUU | 13 ++ .../rules/OrIntroductionDirectRule/UUF | 13 ++ .../rules/OrIntroductionDirectRule/UUT | 13 ++ .../rules/OrIntroductionDirectRule/UUU | 12 ++ 29 files changed, 814 insertions(+) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/OrEliminationTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/OrIntroductionTest.java create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/BlankA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/FalseA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/TrueA create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/FTU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UFU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUU diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java new file mode 100644 index 000000000..4d4e009b2 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java @@ -0,0 +1,105 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction.DirectRuleAndIntroduction; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AndIntroductionDirectRuleTest { + private static final DirectRuleAndIntroduction RULE = new DirectRuleAndIntroduction(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given a statement: A ^ B + * + * Asserts that if at least 1 of A or B is false, then this is a valid application + * of the rule if and only if ^ is false. + * + * @param filePath The file path for test board setup. + * @throws InvalidFileFormatException + */ + @Test + public void FalseAndTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/AndIntroductionDirectRule/"; + falseAndTestHelper(path + "FUF"); + falseAndTestHelper(path + "FUU"); + falseAndTestHelper(path + "UUF"); + falseAndTestHelper(path + "FUT"); + falseAndTestHelper(path + "TUF"); + } + + private void falseAndTestHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell and = board.getCell(1, 0); + + and.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(and); + Assert.assertNotNull(RULE.checkRule(transition)); + + and.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(and); + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * Given a statement: A ^ B + * + * Asserts that setting ^ to true is a valid application of the rule if + * and only if both A and B are true. + * + * @param filePath The file path for test board setup. + * @throws InvalidFileFormatException + */ + @Test + public void FalseOrTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/AndIntroductionDirectRule/"; + String[] letters = {"T", "F", "U"}; + for (String first : letters) { + for (String second : letters) { + trueAndTestHelper(path + first + "U" + second); + } + } + } + + private void trueAndTestHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + ShortTruthTableCell and = board.getCell(1, 0); + + and.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(and); + + if (a.getType() == ShortTruthTableCellType.TRUE && b.getType() == ShortTruthTableCellType.TRUE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java b/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java new file mode 100644 index 000000000..a0a062ab3 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java @@ -0,0 +1,122 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction.DirectRuleNotIntroduction; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class NotIntroductionTest { + private static final DirectRuleNotIntroduction RULE = new DirectRuleNotIntroduction(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given one statement: ¬A where A is false + * + * Asserts that this is a valid application of this rule if and only if ¬ is true + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseNot() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/NotIntroductionDirectRule/FalseA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell not = board.getCell(0, 0); + not.setData(cellType); + board.addModifiedData(not); + + if (cellType == ShortTruthTableCellType.TRUE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: ¬A where A is true + * + * Asserts that this is a valid application of this rule if and only if ¬ is false + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueNot() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/NotIntroductionDirectRule/TrueA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell not = board.getCell(0, 0); + not.setData(cellType); + board.addModifiedData(not); + + if (cellType == ShortTruthTableCellType.FALSE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + + /** + * Given one statement: ¬A + * + * Asserts that setting both ¬ and A to any values would not be a valid + * application of this rule + * + * @throws InvalidFileFormatException + */ + @Test + public void CannotSetBothAtOnceTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/NotIntroductionDirectRule/BlankA", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell not = board.getCell(0, 0); + ShortTruthTableCell a = board.getCell(1, 0); + + not.setData(cellType1); + a.setData(cellType2); + + board.addModifiedData(not); + board.addModifiedData(a); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/OrEliminationTest.java b/src/test/java/puzzles/shorttruthtable/rules/OrEliminationTest.java new file mode 100644 index 000000000..6cdd0d639 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/OrEliminationTest.java @@ -0,0 +1,150 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination.DirectRuleOrElimination; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class OrEliminationTest { + private static final DirectRuleOrElimination RULE = new DirectRuleOrElimination(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given a statement: A V B, where A is false and V is true + * + * Asserts that this is a valid application of the rule if and only if B is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void FTUTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/OrEliminationDirectRule/FTU", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(2, 0); + + cell.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(cell); + Assert.assertNull(RULE.checkRule(transition)); + + cell.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(cell); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given a statement: A V B, where B is false and V is true + * + * Asserts that this is a valid application of the rule if and only if B is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void UTFTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTF", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(0, 0); + + cell.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(cell); + Assert.assertNull(RULE.checkRule(transition)); + + cell.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(cell); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given a statement: A V B, where V is false + * + * Asserts that this is a valid application of the rule if and only if both A + * and B are false. + * + * @throws InvalidFileFormatException + */ + @Test + public void UFUTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/OrEliminationDirectRule/UFU", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + + a.setData(cellType1); + b.setData(cellType2); + + board.addModifiedData(a); + board.addModifiedData(b); + + if (cellType1 == ShortTruthTableCellType.FALSE && cellType2 == ShortTruthTableCellType.FALSE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } + } + + /** + * Given a statement: A V B, where V is true + * + * Asserts that setting both A and B is not a valid application of this rule. + * + * @throws InvalidFileFormatException + */ + @Test + public void UTUTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTU", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + + for (ShortTruthTableCellType cellType1 : cellTypes) { + for (ShortTruthTableCellType cellType2 : cellTypes) { + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + + a.setData(cellType1); + b.setData(cellType2); + + board.addModifiedData(a); + board.addModifiedData(b); + + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/OrIntroductionTest.java b/src/test/java/puzzles/shorttruthtable/rules/OrIntroductionTest.java new file mode 100644 index 000000000..13cb10d55 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/OrIntroductionTest.java @@ -0,0 +1,105 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction.DirectRuleOrIntroduction; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class OrIntroductionTest { + private static final DirectRuleOrIntroduction RULE = new DirectRuleOrIntroduction(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given a statement: A V B + * + * Asserts that if at least 1 of A or B is true, then this is a valid application + * of the rule if and only if V is true. + * + * @param filePath The file path for test board setup. + * @throws InvalidFileFormatException + */ + @Test + public void TrueOrTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/OrIntroductionDirectRule/"; + trueOrTestHelper(path + "TUT"); + trueOrTestHelper(path + "TUU"); + trueOrTestHelper(path + "UUT"); + trueOrTestHelper(path + "TUF"); + trueOrTestHelper(path + "FUT"); + } + + private void trueOrTestHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell or = board.getCell(1, 0); + + or.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(or); + Assert.assertNull(RULE.checkRule(transition)); + + or.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(or); + Assert.assertNotNull(RULE.checkRule(transition)); + } + + /** + * Given a statement: A V B + * + * Asserts that setting V to false is a valid application of the rule if + * and only if both A and B are false. + * + * @param filePath The file path for test board setup. + * @throws InvalidFileFormatException + */ + @Test + public void FalseOrTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/OrIntroductionDirectRule/"; + String[] letters = {"T", "F", "U"}; + for (String first : letters) { + for (String second : letters) { + falseOrTestHelper(path + first + "U" + second); + } + } + } + + private void falseOrTestHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + ShortTruthTableCell or = board.getCell(1, 0); + + or.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(or); + + if (a.getType() == ShortTruthTableCellType.FALSE && b.getType() == ShortTruthTableCellType.FALSE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUF b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUF new file mode 100644 index 000000000..003792ec0 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUT b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUT new file mode 100644 index 000000000..05803b88d --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUU b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUU new file mode 100644 index 000000000..b97eafdfe --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/FUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUF b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUF new file mode 100644 index 000000000..fc10f77bb --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUT b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUT new file mode 100644 index 000000000..dc99e3cdc --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUU b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUU new file mode 100644 index 000000000..2c592ef86 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/TUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUF b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUF new file mode 100644 index 000000000..39b5fb079 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUF @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUT b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUT new file mode 100644 index 000000000..261c035cd --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUT @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUU b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUU new file mode 100644 index 000000000..d9f415439 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndIntroductionDirectRule/UUU @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/BlankA b/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/BlankA new file mode 100644 index 000000000..e7bf96e4a --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/BlankA @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/FalseA b/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/FalseA new file mode 100644 index 000000000..d4c178464 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/FalseA @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/TrueA b/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/TrueA new file mode 100644 index 000000000..7c4f71984 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/NotIntroductionDirectRule/TrueA @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/FTU b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/FTU new file mode 100644 index 000000000..bda22c0e6 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/FTU @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UFU b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UFU new file mode 100644 index 000000000..b7c23215e --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UFU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTF b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTF new file mode 100644 index 000000000..60eaca2b8 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTU b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTU new file mode 100644 index 000000000..c28ba1fde --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUF b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUF new file mode 100644 index 000000000..2326f8df4 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUT b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUT new file mode 100644 index 000000000..447c91981 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUU b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUU new file mode 100644 index 000000000..083c30039 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/FUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUF b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUF new file mode 100644 index 000000000..ef36a432d --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUT b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUT new file mode 100644 index 000000000..05f65ad45 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUU b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUU new file mode 100644 index 000000000..d6e54b326 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/TUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUF b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUF new file mode 100644 index 000000000..5f69fb73f --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUF @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUT b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUT new file mode 100644 index 000000000..d7ef8cd88 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUT @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUU b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUU new file mode 100644 index 000000000..005a4f462 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrIntroductionDirectRule/UUU @@ -0,0 +1,12 @@ + + + + + + + + + + + + From e858844dcf83d8508ba698f23447fbe11941e1ad Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:18:11 -0500 Subject: [PATCH 66/77] Added Conditional Introduction and Biconditional Introduction tests (#693) * Added Conditional Intro files * Create ConditionalIntroductionTest.java * Added Biconditional Introduction tests --- .../rules/BiconditionalIntroductionTest.java | 118 ++++++++++++++++++ .../rules/ConditionalIntroductionTest.java | 110 ++++++++++++++++ .../BiconditionalIntroductionDirectRule/FUF | 14 +++ .../BiconditionalIntroductionDirectRule/FUT | 14 +++ .../BiconditionalIntroductionDirectRule/FUU | 13 ++ .../BiconditionalIntroductionDirectRule/TUF | 14 +++ .../BiconditionalIntroductionDirectRule/TUT | 14 +++ .../BiconditionalIntroductionDirectRule/TUU | 13 ++ .../BiconditionalIntroductionDirectRule/UUF | 13 ++ .../BiconditionalIntroductionDirectRule/UUT | 13 ++ .../BiconditionalIntroductionDirectRule/UUU | 12 ++ .../ConditionalIntroductionDirectRule/FUF | 14 +++ .../ConditionalIntroductionDirectRule/FUT | 14 +++ .../ConditionalIntroductionDirectRule/FUU | 13 ++ .../ConditionalIntroductionDirectRule/TUF | 14 +++ .../ConditionalIntroductionDirectRule/TUT | 14 +++ .../ConditionalIntroductionDirectRule/TUU | 13 ++ .../ConditionalIntroductionDirectRule/UUF | 13 ++ .../ConditionalIntroductionDirectRule/UUT | 13 ++ .../ConditionalIntroductionDirectRule/UUU | 12 ++ 20 files changed, 468 insertions(+) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/ConditionalIntroductionTest.java create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUU create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUF create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUT create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUU diff --git a/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java new file mode 100644 index 000000000..fe2574b5e --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java @@ -0,0 +1,118 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction.DirectRuleBiconditionalIntroduction; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class BiconditionalIntroductionTest { + private static final DirectRuleBiconditionalIntroduction RULE = new DirectRuleBiconditionalIntroduction(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given a statement: A <-> B + * + * Asserts that if setting <-> to false is a valid application of this rule if and + * only if A and B do not match. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseConditionalTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/"; + + String[] letters = {"T", "F", "U"}; + for (String a : letters) { + for (String b : letters) { + System.out.println(a + b); + falseConditionalHelper(path + a + "U" + b); + } + } + } + + private void falseConditionalHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell conditional = board.getCell(1, 0); + + conditional.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(conditional); + + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + if (a.getType() != b.getType()) { + // Not valid if they don't match but at least one of the values of A or B is unknown + if (a.getType() == ShortTruthTableCellType.UNKNOWN || b.getType() == ShortTruthTableCellType.UNKNOWN) { + Assert.assertNotNull(RULE.checkRule(transition)); + } + else { + Assert.assertNull(RULE.checkRule(transition)); + } + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + + /** + * Given a statement: A <-> B + * + * Asserts that if setting <-> to true is a valid application of this rule if and + * only if A and B match. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueConditionalTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/"; + + String[] letters = {"T", "F", "U"}; + for (String a : letters) { + for (String b : letters) { + System.out.println(a + b); + trueConditionalHelper(path + a + "U" + b); + } + } + } + + private void trueConditionalHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell conditional = board.getCell(1, 0); + + conditional.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(conditional); + + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + if (a.getType() == b.getType() && a.getType() != ShortTruthTableCellType.UNKNOWN) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/ConditionalIntroductionTest.java b/src/test/java/puzzles/shorttruthtable/rules/ConditionalIntroductionTest.java new file mode 100644 index 000000000..c1507eab1 --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/ConditionalIntroductionTest.java @@ -0,0 +1,110 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction.DirectRuleConditionalIntroduction; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ConditionalIntroductionTest { + private static final DirectRuleConditionalIntroduction RULE = new DirectRuleConditionalIntroduction(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setup() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Given a statement: A -> B + * + * Asserts that if setting -> to false is a valid application of this rule if and + * only if A is true and B is false. + * + * @throws InvalidFileFormatException + */ + @Test + public void FalseConditionalTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/"; + + String[] letters = {"T", "F", "U"}; + for (String a : letters) { + for (String b : letters) { + falseConditionalHelper(path + a + "U" + b); + } + } + } + + private void falseConditionalHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell conditional = board.getCell(1, 0); + + conditional.setData(ShortTruthTableCellType.FALSE); + board.addModifiedData(conditional); + + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + if (a.getType() == ShortTruthTableCellType.TRUE && b.getType() == ShortTruthTableCellType.FALSE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } + + /** + * Given a statement: A -> B + * + * Asserts that if setting -> to true is a valid application of this rule if and + * only if A is false or B is true. + * + * @throws InvalidFileFormatException + */ + @Test + public void TrueConditionalTest() throws InvalidFileFormatException { + String path = "puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/"; + + String[] letters = {"T", "F", "U"}; + for (String a : letters) { + for (String b : letters) { + trueConditionalTestHelper(path + a + "U" + b); + } + } + } + + private void trueConditionalTestHelper(String filePath) throws InvalidFileFormatException { + TestUtilities.importTestBoard(filePath, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell conditional = board.getCell(1, 0); + + conditional.setData(ShortTruthTableCellType.TRUE); + board.addModifiedData(conditional); + + ShortTruthTableCell a = board.getCell(0, 0); + ShortTruthTableCell b = board.getCell(2, 0); + if (a.getType() == ShortTruthTableCellType.FALSE || b.getType() == ShortTruthTableCellType.TRUE) { + Assert.assertNull(RULE.checkRule(transition)); + } + else { + Assert.assertNotNull(RULE.checkRule(transition)); + } + } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUF b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUF new file mode 100644 index 000000000..817025f78 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUT b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUT new file mode 100644 index 000000000..4bb3cb635 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUU b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUU new file mode 100644 index 000000000..7ddeeb5c8 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/FUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUF b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUF new file mode 100644 index 000000000..fb40ff4d4 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUT b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUT new file mode 100644 index 000000000..6ce5ab64c --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUU b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUU new file mode 100644 index 000000000..9307de3ad --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/TUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUF b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUF new file mode 100644 index 000000000..c61474b96 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUF @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUT b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUT new file mode 100644 index 000000000..2dead7328 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUT @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUU b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUU new file mode 100644 index 000000000..04596d449 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/BiconditionalIntroductionDirectRule/UUU @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUF b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUF new file mode 100644 index 000000000..a2adf2297 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUT b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUT new file mode 100644 index 000000000..5b849c7ac --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUU b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUU new file mode 100644 index 000000000..48d5fd7c6 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/FUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUF b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUF new file mode 100644 index 000000000..480a409ef --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUF @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUT b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUT new file mode 100644 index 000000000..862ee1628 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUT @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUU b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUU new file mode 100644 index 000000000..79b97ba46 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/TUU @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUF b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUF new file mode 100644 index 000000000..e5c9c4c0d --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUF @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUT b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUT new file mode 100644 index 000000000..efe86b390 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUT @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUU b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUU new file mode 100644 index 000000000..b0aa8ba00 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/ConditionalIntroductionDirectRule/UUU @@ -0,0 +1,12 @@ + + + + + + + + + + + + From ca9fef9d88f83e6885bf56b45221715f6b824b6d Mon Sep 17 00:00:00 2001 From: Jacob Long <123578010+jac-oblong@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:59:54 -0500 Subject: [PATCH 67/77] Fix treetent puzzle editor (#692) * fix treetent puzzle editor (add breaks for switch) (exporter exports same data that importer expects) * allow for treetent clue to go up/down * comply with checkstyles --------- Co-authored-by: Jacob Long Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- .../rpi/legup/model/gameboard/GridBoard.java | 37 ++++++++++++++++--- .../legup/puzzle/treetent/TreeTentCell.java | 16 ++++++++ .../puzzle/treetent/TreeTentCellFactory.java | 2 +- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java b/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java index 3e94c57ad..d31e8185a 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java @@ -71,15 +71,40 @@ public void setCell(int x, int y, Element e, MouseEvent m) { if (this instanceof TreeTentBoard && ((y == dimension.height && 0 <= x && x < dimension.width) || (x == dimension.width && 0 <= y && y < dimension.height))) { TreeTentBoard treeTentBoard = ((TreeTentBoard) this); TreeTentClue clue = treeTentBoard.getClue(x, y); - if (y == dimension.height && clue.getData() < dimension.width) { - clue.setData(clue.getData() + 1); + if (y == dimension.height) { + if (m.getButton() == MouseEvent.BUTTON1) { + if (clue.getData() < dimension.height) { + clue.setData(clue.getData() + 1); + } + else { + clue.setData(0); + } + } + else { + if (clue.getData() > 0) { + clue.setData(clue.getData() - 1); + } + else { + clue.setData(dimension.height); + } + } } - else { - if (x == dimension.width && clue.getData() < dimension.height) { - clue.setData(clue.getData() + 1); + else { //x == dimension.width + if (m.getButton() == MouseEvent.BUTTON1) { + if (clue.getData() < dimension.width) { + clue.setData(clue.getData() + 1); + } + else { + clue.setData(0); + } } else { - clue.setData(0); + if (clue.getData() > 0) { + clue.setData(clue.getData() - 1); + } + else { + clue.setData(dimension.width); + } } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCell.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCell.java index 290e0858d..b6411a5cf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCell.java @@ -16,15 +16,31 @@ public TreeTentType getType() { return data; } + public int getValue() { + switch (data) { + case TREE: + return 1; + case GRASS: + return 2; + case TENT: + return 3; + default: + return 0; + } + } + @Override public void setType(Element e, MouseEvent m) { switch (e.getElementName()) { case "Unknown Tile": this.data = TreeTentType.UNKNOWN; + break; case "Tree Tile": this.data = TreeTentType.TREE; + break; case "Grass Tile": this.data = TreeTentType.GRASS; + break; case "Tent Tile": this.data = TreeTentType.TENT; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java index 969ffdf0f..20b8066a4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java @@ -83,7 +83,7 @@ public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleEle TreeTentCell cell = (TreeTentCell) puzzleElement; Point loc = cell.getLocation(); - cellElement.setAttribute("value", String.valueOf(cell.getData())); + cellElement.setAttribute("value", String.valueOf(cell.getValue())); cellElement.setAttribute("x", String.valueOf(loc.x)); cellElement.setAttribute("y", String.valueOf(loc.y)); From 4df66c8966fdacd5846f00c1220bd47127c8bf75 Mon Sep 17 00:00:00 2001 From: pitbull51067 <103721450+pitbull51067@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:10:07 -0500 Subject: [PATCH 68/77] EmptyCellinLight tested (#696) * BulbsInPathContradictionRuleTest looks good. I added one more function to check for the BlockInHorizontalPath test case. * I think I might have run into a problem on the testing function. I will confirm this problem next time and will write another test case. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. * Test suite branch -- BulbsInPathContradictionRuleTest (#674) * Region Based Changes (#559) Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> * Short Truth Table Puzzle Editor (#451) * Created files for STT elements * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Created element classes and added placeholder tile images (#452) * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Set the current board on boardView * Fixed typo and turned on STT puzzle editor for testing * Added preliminary valid dimensions checker This will most definitely change in the future, hopefully can change to accept a number of statements * Fixed image file paths * Added ActionListener Allows us to determine what puzzle is selected by the user * Hide rows and columns input for Short Truth Table * Added text area for Short Truth Table * Added scrollbars to show up as needed * Reformatted code * More code reformatting * Even more reformatting * Separate the data from the TextArea into different lines * Did some researching/testing Tested certain variable values with a STT file with no true/false values * Made more progress Added new methods to handle creating Short Truth Table boards from an array of strings * Added a bunch of TODOs - Implemented a couple functions to be used later - Added a bunch of TODO comments for future work * Made some more progress * Implemented abstract methods from PuzzleImporter * Added abstract methods to Fillapix and added other exception reporting * CheckStyle formatting * Removed a TODO comment * Statements show up in puzzle editor Fixed a bug where the importer was not properly being initialized. Statements now show up in the puzzle editor. * Removed empty statements * Changed InvalidFormatException to IllegalArgumentException * Remove argument that has already been caught * Removed elements that will not be used * Added puzzle editor cell clicking functionality * Added ability to toggle certain logical elements * New icons and more functionality implemented * Fixed a bug where spacer rows could be modified * Added statement error checking * Fixed formatting * Only one logic symbol element needed * Changed InputMismatchException to UnsupportedOperationException * Renamed variables to not be STT specific * Finding initial issue and starting fix * Issue is statement copying and modifying * STT exporter now working. Overrode setCell for STTBoard. * Added code documentation * removed testing println() * Gradle fixes * Revert "Merge pull request #545 from MMosley502/puzzle_editor-short_truth_table-file_saving" This reverts commit 2e82547896a7fb3e52ec27634cd8938ef299732f, reversing changes made to beb60a2ab67c8317d404f54e52471739f698bf22. * Saving files now works * Fixed the blank element to be categorized as a placeable element * Fixed a bug where file wouldn't save due to batch grader updates * Reformatted code in STT * Reformatted code again * MORE REFORMATTING Pls like my code CheckStyle --------- Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> * Have null changes be valid and fix IsolatedBlackContradicitonRule error message (#561) * Get Tests to be called Revert "Create first cypress test template" This reverts commit 3e50909b93b5aa9634cf0d296e9aeff756b0a909. First commit Finish Lightup tests * Add more tests Update TestRunner.java * Somehow ended up in the wrong spot Fix Import * Please let this be the fix Update TreeTransition.java Update TreeTransition.java Update DirectRule.java Check to see which is not correct Update ElementController.java Revert "maybe the null is making it think that it's not valid" This reverts commit 7bf1de0d66ced6749ee37fbb9c252636b2fcdc79. Just trying to change color Revert "Just trying to change color" This reverts commit ec44695ee578d664055d135a668927a0fd900f5d. Revert "maybe the null is making it think that it's not valid" This reverts commit 3f162fbdc32e6fbd23da321a14a6af96f0ff520d. Check to see which is not correct Revert "Check to see which is not correct" This reverts commit 136b0a41b9d103e6f3e9a7f8cd5d970bf76b050b. Update TreeTransition.java Update TreeTransition.java Revert "Update TreeTransition.java" This reverts commit cde45bb9001cfbfa4f6e2a49b4e9990d2fa7ad33. * Fix error with isolated Black Fix error message with isolated black * Removed excess whitespace and imports. Added short JavaDoc for `TestRunner.java` --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet * Fixed a bug * Update BlackTile.java Black tile should not be placeable * Added unknown tile stuff * ID error * Some Fixes to Recently Discussed UX Bugs (#563) * frame and panels default sizes, default pos on screen * hardcoded version number * homepanel default size * set panels' own sizes * some changes * Removed unused import --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Corppet * Oops pushed the wrong file Yeah some tiles work now but this is the ID error * Number Tile working * Update Exporter (#627) * Update Exporter * Delete Test_Save --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> * Create run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Windows things * Added print messages * More Windows things * Debugging * Update run-tests.yml * Update run-tests.yml * Maybe this will work now? * Didn't work * Update run-tests.yml * Update run-tests.yml * Create DummyTest.java For debugging purposes * Added another dummy test * Update run-tests.yml * Update run-tests.yml Get rid of all this info * Deleted the dummy tests * BulbsInPathContradictionRuleTest looks good. I added one more function to check for the BlockInHorizontalPath test case. * I think I might have run into a problem on the testing function. I will confirm this problem next time and will write another test case. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. --------- Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> * Revert "Test suite branch -- BulbsInPathContradictionRuleTest (#674)" (#687) This reverts commit 6afa15c57fca05a9b8da4b4f27b88dd55d69bfb8. * Finished testing EmptyCellinLightDirectRule. I editet the test puzzle to include testing if a cell is unknown and changed the test code accordingly. * Forgot to remove this print statement * Forgot to remove this print statement as well * Finished testing EmptyCornersDirectRuleTest. Testing in case the rules was not being used correctly was not done so I remade the puzzle for the test and changed the testing code accordingly. * Reverted changes to make another branch to make pull request. --------- Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> --- .../rules/EmptyCellinLightDirectRuleTest.java | 23 ++++++++++++++++--- .../EmptyCellinLightDirectRule/EmptyCells | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java b/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java index 637804892..b0a2d10ed 100644 --- a/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java @@ -59,16 +59,33 @@ public void EmptyCellinLightDirectRule() throws InvalidFileFormatException{ cell7.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell7); + LightUpCell cell8 = board.getCell(3,0); + cell8.setData(LightUpCellType.EMPTY.value); + board.addModifiedData(cell8); + + LightUpCell cell9 = board.getCell(3,2); + cell9.setData(LightUpCellType.EMPTY.value); + board.addModifiedData(cell9); + + LightUpCell cell10 = board.getCell(2,3); + cell10.setData(LightUpCellType.EMPTY.value); + board.addModifiedData(cell10); + + LightUpCell cell11 = board.getCell(0,3); + cell11.setData(LightUpCellType.EMPTY.value); + board.addModifiedData(cell11); + //confirm there is a logical following of the EmptyCellinLight rule Assert.assertNull(RULE.checkRule(transition)); - //cells (0,0) and (2,2) are not empty because they have lightbulbs, and (1,1) - //because it is a black tile. Confirm the rest are empty + //cells (0,0) and (2,2) are not empty because they have lightbulbs, (1,1) + //because it is a black tile, and (1,3),(3,1),(3,3) because they are not lit. Confirm the rest are empty LightUpCell c; for (int i = 0; i < board.getHeight(); i++) { for (int j = 0; j < board.getWidth(); j++) { c = board.getCell(j, i); - if ((i == 0 && j == 0) || (i == 2 && j == 2) || (i == 1 && j == 1)){ + if ((i == 0 && j == 0) || (i == 2 && j == 2) || (i == 1 && j == 1) || (i == 1 && j == 3) || (i == 3 && j == 1) + || (i==3 && j==3)){ Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } else { diff --git a/src/test/resources/puzzles/lightup/rules/EmptyCellinLightDirectRule/EmptyCells b/src/test/resources/puzzles/lightup/rules/EmptyCellinLightDirectRule/EmptyCells index feca5b861..d6fba5369 100644 --- a/src/test/resources/puzzles/lightup/rules/EmptyCellinLightDirectRule/EmptyCells +++ b/src/test/resources/puzzles/lightup/rules/EmptyCellinLightDirectRule/EmptyCells @@ -1,6 +1,6 @@ - + From a16ae5a1c7aacd9756ad5aef9b7686ddb6eab0e0 Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:26:36 -0500 Subject: [PATCH 69/77] Added back TreeTent line creation (#695) * Test suite branch -- BulbsInPathContradictionRuleTest (#674) * Region Based Changes (#559) Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> * Short Truth Table Puzzle Editor (#451) * Created files for STT elements * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Created element classes and added placeholder tile images (#452) * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Set the current board on boardView * Fixed typo and turned on STT puzzle editor for testing * Added preliminary valid dimensions checker This will most definitely change in the future, hopefully can change to accept a number of statements * Fixed image file paths * Added ActionListener Allows us to determine what puzzle is selected by the user * Hide rows and columns input for Short Truth Table * Added text area for Short Truth Table * Added scrollbars to show up as needed * Reformatted code * More code reformatting * Even more reformatting * Separate the data from the TextArea into different lines * Did some researching/testing Tested certain variable values with a STT file with no true/false values * Made more progress Added new methods to handle creating Short Truth Table boards from an array of strings * Added a bunch of TODOs - Implemented a couple functions to be used later - Added a bunch of TODO comments for future work * Made some more progress * Implemented abstract methods from PuzzleImporter * Added abstract methods to Fillapix and added other exception reporting * CheckStyle formatting * Removed a TODO comment * Statements show up in puzzle editor Fixed a bug where the importer was not properly being initialized. Statements now show up in the puzzle editor. * Removed empty statements * Changed InvalidFormatException to IllegalArgumentException * Remove argument that has already been caught * Removed elements that will not be used * Added puzzle editor cell clicking functionality * Added ability to toggle certain logical elements * New icons and more functionality implemented * Fixed a bug where spacer rows could be modified * Added statement error checking * Fixed formatting * Only one logic symbol element needed * Changed InputMismatchException to UnsupportedOperationException * Renamed variables to not be STT specific * Finding initial issue and starting fix * Issue is statement copying and modifying * STT exporter now working. Overrode setCell for STTBoard. * Added code documentation * removed testing println() * Gradle fixes * Revert "Merge pull request #545 from MMosley502/puzzle_editor-short_truth_table-file_saving" This reverts commit 2e82547896a7fb3e52ec27634cd8938ef299732f, reversing changes made to beb60a2ab67c8317d404f54e52471739f698bf22. * Saving files now works * Fixed the blank element to be categorized as a placeable element * Fixed a bug where file wouldn't save due to batch grader updates * Reformatted code in STT * Reformatted code again * MORE REFORMATTING Pls like my code CheckStyle --------- Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> * Have null changes be valid and fix IsolatedBlackContradicitonRule error message (#561) * Get Tests to be called Revert "Create first cypress test template" This reverts commit 3e50909b93b5aa9634cf0d296e9aeff756b0a909. First commit Finish Lightup tests * Add more tests Update TestRunner.java * Somehow ended up in the wrong spot Fix Import * Please let this be the fix Update TreeTransition.java Update TreeTransition.java Update DirectRule.java Check to see which is not correct Update ElementController.java Revert "maybe the null is making it think that it's not valid" This reverts commit 7bf1de0d66ced6749ee37fbb9c252636b2fcdc79. Just trying to change color Revert "Just trying to change color" This reverts commit ec44695ee578d664055d135a668927a0fd900f5d. Revert "maybe the null is making it think that it's not valid" This reverts commit 3f162fbdc32e6fbd23da321a14a6af96f0ff520d. Check to see which is not correct Revert "Check to see which is not correct" This reverts commit 136b0a41b9d103e6f3e9a7f8cd5d970bf76b050b. Update TreeTransition.java Update TreeTransition.java Revert "Update TreeTransition.java" This reverts commit cde45bb9001cfbfa4f6e2a49b4e9990d2fa7ad33. * Fix error with isolated Black Fix error message with isolated black * Removed excess whitespace and imports. Added short JavaDoc for `TestRunner.java` --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet * Fixed a bug * Update BlackTile.java Black tile should not be placeable * Added unknown tile stuff * ID error * Some Fixes to Recently Discussed UX Bugs (#563) * frame and panels default sizes, default pos on screen * hardcoded version number * homepanel default size * set panels' own sizes * some changes * Removed unused import --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Corppet * Oops pushed the wrong file Yeah some tiles work now but this is the ID error * Number Tile working * Update Exporter (#627) * Update Exporter * Delete Test_Save --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> * Create run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Windows things * Added print messages * More Windows things * Debugging * Update run-tests.yml * Update run-tests.yml * Maybe this will work now? * Didn't work * Update run-tests.yml * Update run-tests.yml * Create DummyTest.java For debugging purposes * Added another dummy test * Update run-tests.yml * Update run-tests.yml Get rid of all this info * Deleted the dummy tests * BulbsInPathContradictionRuleTest looks good. I added one more function to check for the BlockInHorizontalPath test case. * I think I might have run into a problem on the testing function. I will confirm this problem next time and will write another test case. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. --------- Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> * Revert "Test suite branch -- BulbsInPathContradictionRuleTest (#674)" (#687) This reverts commit 6afa15c57fca05a9b8da4b4f27b88dd55d69bfb8. * added back link functionality --------- Co-authored-by: pitbull51067 <103721450+pitbull51067@users.noreply.github.com> Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> --- .../puzzle/treetent/TreeTentController.java | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java index 2b969b4bb..1f431594c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java @@ -29,83 +29,83 @@ public TreeTentController() { this.lastCellPressed = null; } -// @Override -// public void mousePressed(MouseEvent e) { -// if (e.getButton() != MouseEvent.BUTTON2) { -// BoardView boardView = getInstance().getLegupUI().getBoardView(); -// dragStart = boardView.getElement(e.getPoint()); -// lastCellPressed = boardView.getElement(e.getPoint()); -// } -// } + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() != MouseEvent.BUTTON2) { + BoardView boardView = getInstance().getLegupUI().getBoardView(); + dragStart = boardView.getElement(e.getPoint()); + lastCellPressed = boardView.getElement(e.getPoint()); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (GameBoardFacade.getInstance().getLegupUI().getTreePanel() != null && e.getButton() != MouseEvent.BUTTON2) { + TreePanel treePanel = GameBoardFacade.getInstance().getLegupUI().getTreePanel(); + TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); + BoardView boardView = getInstance().getLegupUI().getBoardView(); + lastCellPressed = boardView.getElement(e.getPoint()); + Board board = boardView.getBoard(); + TreeViewSelection selection = treeView.getSelection(); -// @Override -// public void mouseReleased(MouseEvent e) { -// if (GameBoardFacade.getInstance().getLegupUI().getTreePanel() != null && e.getButton() != MouseEvent.BUTTON2) { -// TreePanel treePanel = GameBoardFacade.getInstance().getLegupUI().getTreePanel(); -// TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); -// BoardView boardView = getInstance().getLegupUI().getBoardView(); -// lastCellPressed = boardView.getElement(e.getPoint()); -// Board board = boardView.getBoard(); -// TreeViewSelection selection = treeView.getSelection(); -// -// if (dragStart != null) { -// if (board instanceof CaseBoard) { -// CaseBoard caseBoard = (CaseBoard) board; -// AutoCaseRuleCommand autoCaseRuleCommand = new AutoCaseRuleCommand(dragStart, selection, caseBoard.getCaseRule(), caseBoard, e); -// if (autoCaseRuleCommand.canExecute()) { -// autoCaseRuleCommand.execute(); -// getInstance().getHistory().pushChange(autoCaseRuleCommand); -// treePanel.updateError(""); -// } -// else { -// treePanel.updateError(autoCaseRuleCommand.getError()); -// } -// } -// else { -// if (dragStart == lastCellPressed) { -// if (dragStart.getPuzzleElement().getIndex() >= 0) { -// ICommand edit = new EditDataCommand(lastCellPressed, selection, e); -// if (edit.canExecute()) { -// edit.execute(); -// getInstance().getHistory().pushChange(edit); -// treePanel.updateError(""); -// } -// else { -// treePanel.updateError(edit.getError()); -// } -// } -// else { -// ClueCommand edit = new ClueCommand(selection, (TreeTentClueView) dragStart); -// if (edit.canExecute()) { -// edit.execute(); -// getInstance().getHistory().pushChange(edit); -// treePanel.updateError(""); -// } -// else { -// treePanel.updateError(edit.getError()); -// } -// } -// } -// else { -// if (lastCellPressed != null) { -// if (dragStart instanceof TreeTentElementView) { -// ICommand editLine = new EditLineCommand(selection, (TreeTentElementView) dragStart, lastCellPressed); -// if (editLine.canExecute()) { -// editLine.execute(); -// getInstance().getHistory().pushChange(editLine); -// } -// else { -// treePanel.updateError(editLine.getError()); -// } -// } -// } -// } -// } -// } -// dragStart = null; -// lastCellPressed = null; -// } -// } + if (dragStart != null) { + if (board instanceof CaseBoard) { + CaseBoard caseBoard = (CaseBoard) board; + AutoCaseRuleCommand autoCaseRuleCommand = new AutoCaseRuleCommand(dragStart, selection, caseBoard.getCaseRule(), caseBoard, e); + if (autoCaseRuleCommand.canExecute()) { + autoCaseRuleCommand.execute(); + getInstance().getHistory().pushChange(autoCaseRuleCommand); + treePanel.updateError(""); + } + else { + treePanel.updateError(autoCaseRuleCommand.getError()); + } + } + else { + if (dragStart == lastCellPressed) { + if (dragStart.getPuzzleElement().getIndex() >= 0) { + ICommand edit = new EditDataCommand(lastCellPressed, selection, e); + if (edit.canExecute()) { + edit.execute(); + getInstance().getHistory().pushChange(edit); + treePanel.updateError(""); + } + else { + treePanel.updateError(edit.getError()); + } + } + else { + ClueCommand edit = new ClueCommand(selection, (TreeTentClueView) dragStart); + if (edit.canExecute()) { + edit.execute(); + getInstance().getHistory().pushChange(edit); + treePanel.updateError(""); + } + else { + treePanel.updateError(edit.getError()); + } + } + } + else { + if (lastCellPressed != null) { + if (dragStart instanceof TreeTentElementView) { + ICommand editLine = new EditLineCommand(selection, (TreeTentElementView) dragStart, lastCellPressed); + if (editLine.canExecute()) { + editLine.execute(); + getInstance().getHistory().pushChange(editLine); + } + else { + treePanel.updateError(editLine.getError()); + } + } + } + } + } + } + dragStart = null; + lastCellPressed = null; + } + } @Override public void changeCell(MouseEvent e, PuzzleElement element) { From 80328d533185de64294129c11dff19aea0dedbe8 Mon Sep 17 00:00:00 2001 From: Rory Martin <36866664+Rorymar@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:51:38 -0500 Subject: [PATCH 70/77] Tree Tent Test Suite- All Remaining Non-Case Type Tests (#683) * TooManyTents Test Overhaul Overhauled the TooManyTents Tests to better account for possible situations, and improved commenting. * TooFewTents Tests Added more expansive tests for the TooFewTents rule * Fill With Tests Completed the Fill With Test Overhaul * Last Camping Spot Test Updated the Last Camping Spot Test to be commented and more comprehensive. * Test suite branch -- BulbsInPathContradictionRuleTest (#674) * Region Based Changes (#559) Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> * Short Truth Table Puzzle Editor (#451) * Created files for STT elements * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Created element classes and added placeholder tile images (#452) * Renamed Tiles classes to Elements to match package name Also added an elements reference sheet and renamed rules reference sheet accordingly * More progress made This won't compile, just saving progress made * More progress being made * Fixed file name typo and added placeholder tiles * Added image paths * Set the current board on boardView * Fixed typo and turned on STT puzzle editor for testing * Added preliminary valid dimensions checker This will most definitely change in the future, hopefully can change to accept a number of statements * Fixed image file paths * Added ActionListener Allows us to determine what puzzle is selected by the user * Hide rows and columns input for Short Truth Table * Added text area for Short Truth Table * Added scrollbars to show up as needed * Reformatted code * More code reformatting * Even more reformatting * Separate the data from the TextArea into different lines * Did some researching/testing Tested certain variable values with a STT file with no true/false values * Made more progress Added new methods to handle creating Short Truth Table boards from an array of strings * Added a bunch of TODOs - Implemented a couple functions to be used later - Added a bunch of TODO comments for future work * Made some more progress * Implemented abstract methods from PuzzleImporter * Added abstract methods to Fillapix and added other exception reporting * CheckStyle formatting * Removed a TODO comment * Statements show up in puzzle editor Fixed a bug where the importer was not properly being initialized. Statements now show up in the puzzle editor. * Removed empty statements * Changed InvalidFormatException to IllegalArgumentException * Remove argument that has already been caught * Removed elements that will not be used * Added puzzle editor cell clicking functionality * Added ability to toggle certain logical elements * New icons and more functionality implemented * Fixed a bug where spacer rows could be modified * Added statement error checking * Fixed formatting * Only one logic symbol element needed * Changed InputMismatchException to UnsupportedOperationException * Renamed variables to not be STT specific * Finding initial issue and starting fix * Issue is statement copying and modifying * STT exporter now working. Overrode setCell for STTBoard. * Added code documentation * removed testing println() * Gradle fixes * Revert "Merge pull request #545 from MMosley502/puzzle_editor-short_truth_table-file_saving" This reverts commit 2e82547896a7fb3e52ec27634cd8938ef299732f, reversing changes made to beb60a2ab67c8317d404f54e52471739f698bf22. * Saving files now works * Fixed the blank element to be categorized as a placeable element * Fixed a bug where file wouldn't save due to batch grader updates * Reformatted code in STT * Reformatted code again * MORE REFORMATTING Pls like my code CheckStyle --------- Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> * Have null changes be valid and fix IsolatedBlackContradicitonRule error message (#561) * Get Tests to be called Revert "Create first cypress test template" This reverts commit 3e50909b93b5aa9634cf0d296e9aeff756b0a909. First commit Finish Lightup tests * Add more tests Update TestRunner.java * Somehow ended up in the wrong spot Fix Import * Please let this be the fix Update TreeTransition.java Update TreeTransition.java Update DirectRule.java Check to see which is not correct Update ElementController.java Revert "maybe the null is making it think that it's not valid" This reverts commit 7bf1de0d66ced6749ee37fbb9c252636b2fcdc79. Just trying to change color Revert "Just trying to change color" This reverts commit ec44695ee578d664055d135a668927a0fd900f5d. Revert "maybe the null is making it think that it's not valid" This reverts commit 3f162fbdc32e6fbd23da321a14a6af96f0ff520d. Check to see which is not correct Revert "Check to see which is not correct" This reverts commit 136b0a41b9d103e6f3e9a7f8cd5d970bf76b050b. Update TreeTransition.java Update TreeTransition.java Revert "Update TreeTransition.java" This reverts commit cde45bb9001cfbfa4f6e2a49b4e9990d2fa7ad33. * Fix error with isolated Black Fix error message with isolated black * Removed excess whitespace and imports. Added short JavaDoc for `TestRunner.java` --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet * Fixed a bug * Update BlackTile.java Black tile should not be placeable * Added unknown tile stuff * ID error * Some Fixes to Recently Discussed UX Bugs (#563) * frame and panels default sizes, default pos on screen * hardcoded version number * homepanel default size * set panels' own sizes * some changes * Removed unused import --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Corppet * Oops pushed the wrong file Yeah some tiles work now but this is the ID error * Number Tile working * Update Exporter (#627) * Update Exporter * Delete Test_Save --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> * Create run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Update run-tests.yml * Windows things * Added print messages * More Windows things * Debugging * Update run-tests.yml * Update run-tests.yml * Maybe this will work now? * Didn't work * Update run-tests.yml * Update run-tests.yml * Create DummyTest.java For debugging purposes * Added another dummy test * Update run-tests.yml * Update run-tests.yml Get rid of all this info * Deleted the dummy tests * BulbsInPathContradictionRuleTest looks good. I added one more function to check for the BlockInHorizontalPath test case. * I think I might have run into a problem on the testing function. I will confirm this problem next time and will write another test case. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. * I made another puzzle and test case to check what would happen if the cells could actually be lit, meaning there would be no contradiction. The test case passed and so I think CannotLightACellContradictionRule works correctly for all cases. --------- Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> * Revert "Test suite branch -- BulbsInPathContradictionRuleTest (#674)" (#687) This reverts commit 6afa15c57fca05a9b8da4b4f27b88dd55d69bfb8. * TooFewTents Overhaul --------- Co-authored-by: pitbull51067 <103721450+pitbull51067@users.noreply.github.com> Co-authored-by: Antonio Orta <60408336+19690ao@users.noreply.github.com> Co-authored-by: Hanson Gu <123511202+hansongu123@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Matthew Mosley Co-authored-by: MMosley502 <74743867+MMosley502@users.noreply.github.com> Co-authored-by: Viane Matsibekker <117249183+04vmatsibekker@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Corppet Co-authored-by: charlestian23 Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> --- .../rules/FinishWithTentsDirectRuleTest.java | 2 +- .../rules/LastCampingSpotDirectRuleTest.java | 111 ++++++++- .../rules/TentOrGrassCaseRuleTest.java | 54 +++++ .../TooFewTentsContradictionRuleTest.java | 216 ++++++++++++++++- .../TooManyTentsContradictionRuleTest.java | 228 +++++++++++++++++- .../FinishWithGrassJustRow | 20 ++ .../FinishWithGrassJustTent | 16 ++ .../FinishWithTentsBothWays | 20 ++ .../LastCampingSpotDown | 23 ++ .../LastCampingSpotLeft | 23 ++ .../LastCampingSpotRight | 23 ++ .../{LastCampingSpot => LastCampingSpotUp} | 0 .../rules/TentOrGrassCaseRule/TentOrGrassTest | 20 ++ .../TooFewTents2x2Column | 19 ++ .../TooFewTents2x2Row | 19 ++ .../TooFewTents3x3Column | 22 ++ .../TooFewTents3x3DoubleColumn | 27 +++ .../TooFewTentsDoubleBad | 16 ++ .../{TooFewTents => TooFewTentsJustY} | 0 .../TooFewTentsNoContradiction | 19 ++ .../TooFewTentsWithTent | 16 ++ .../TooManyTentsBottomAccount | 22 ++ ...nRuleColumn_Row => TooManyTentsBottomDown} | 2 +- .../TooManyTentsBottomMissDown | 22 ++ .../TooManyTentsBottomMissRight | 22 ++ .../TooManyTentsBottomRight | 22 ++ .../TooManyTentsTopAccount | 22 ++ .../TooManyTentsTopDown | 22 ++ .../TooManyTentsTopRight | 22 ++ .../TooManyTentsTotalFail | 22 ++ 30 files changed, 1057 insertions(+), 15 deletions(-) create mode 100644 src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustRow create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustTent create mode 100644 src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsBothWays create mode 100644 src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown create mode 100644 src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft create mode 100644 src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight rename src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/{LastCampingSpot => LastCampingSpotUp} (100%) create mode 100644 src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TentOrGrassTest create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Column create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Row create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3Column create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3DoubleColumn create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad rename src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/{TooFewTents => TooFewTentsJustY} (100%) create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsNoContradiction create mode 100644 src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsWithTent create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomAccount rename src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/{TooManyTentsContradictionRuleColumn_Row => TooManyTentsBottomDown} (93%) create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissDown create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissRight create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomRight create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopAccount create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopDown create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopRight create mode 100644 src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTotalFail diff --git a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java index dff292977..725c3c9de 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java @@ -126,7 +126,7 @@ public void FinishWithTentsTest() throws InvalidFileFormatException { TreeTentCell cell2 = board.getCell(1, 2); TreeTentCell cell3 = board.getCell(0, 1); TreeTentCell cell4 = board.getCell(2, 1); - + cell1.setData(TreeTentType.TENT); cell2.setData(TreeTentType.TENT); cell3.setData(TreeTentType.TENT); diff --git a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java index 415b4f4b9..3e1b390fb 100644 --- a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java @@ -27,9 +27,14 @@ public static void setUp() { treetent = new TreeTent(); } + /** + * @throws InvalidFileFormatException + * + * Checks if a test works for an empty square above a tree which is surrounded on all other sides. + */ @Test - public void EmptyFieldTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpot", treetent); + public void EmptyFieldTest_Up() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -55,6 +60,108 @@ public void EmptyFieldTest() throws InvalidFileFormatException { } } } + + /** + * @throws InvalidFileFormatException + * + * Checks if a test works for an empty square below a tree which is surrounded on all other sides. + */ + @Test + public void EmptyFieldTest_Down() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 2); + cell1.setData(TreeTentType.TENT); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * + * Checks if a test works for an empty square to the left of a tree which is surrounded on all other sides. + */ + @Test + public void EmptyFieldTest_Left() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0, 1); + cell1.setData(TreeTentType.TENT); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * + * Checks if a test works for an empty square to the right of a tree which is surrounded on all other sides. + */ + @Test + public void EmptyFieldTest_Right() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(2, 1); + cell1.setData(TreeTentType.TENT); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } } diff --git a/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java new file mode 100644 index 000000000..c7159ccad --- /dev/null +++ b/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java @@ -0,0 +1,54 @@ +package puzzles.treetent.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTent; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import edu.rpi.legup.puzzle.treetent.rules.FillinRowCaseRule; +import edu.rpi.legup.puzzle.treetent.rules.TentOrGrassCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; +import java.util.ArrayList; + +public class TentOrGrassCaseRuleTest { + private static final TentOrGrassCaseRule RULE = new TentOrGrassCaseRule(); + private static TreeTent treetent; + + @BeforeClass + public static void setUp(){ + MockGameBoardFacade.getInstance(); + treetent = new TreeTent(); + } + + /** + * @throws InvalidFileFormatException + * A temporary test + */ + @Test + public void TentOrGrassCaseRule_Test() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TentOrGrassCaseRule/TentOrGrassTest", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell testing_cell = board.getCell(1, 0); + ArrayList cases = RULE.getCases(board, testing_cell); + // assert correct number of cases created + Assert.assertEquals(2, cases.size()); + //Store the 0,1 cells from each case + //Assert that the Array of their states match to a an array of the expected. + + } + +} diff --git a/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java index bfcb80a23..a5999dc6e 100644 --- a/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java @@ -1,5 +1,6 @@ package puzzles.treetent.rules; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; import legup.MockGameBoardFacade; import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; @@ -10,8 +11,6 @@ import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.TooFewTentsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; @@ -28,9 +27,46 @@ public static void setUp() { treetent = new TreeTent(); } + /** + * @throws InvalidFileFormatException + * Using a 1x1 Puzzle Grid, which is just grass, checks if the fact it expects a tent on the y-axis is caught. + */ + @Test + public void TooFewTentsContradictionRule_JustY() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsJustY", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + } + + /** + * @throws InvalidFileFormatException + * Using a 1x1 Puzzle Grid, which is just a tent, checks if the fact it expects 2 tents on the y-axis is caught. + * (This is an impossible situation given the constraints, but for the purposes of the test it is fine) + */ + @Test + public void TooFewTentsContradictionRule_WithTent() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsWithTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + Assert.assertNull(RULE.checkContradiction(board)); + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); + } + + /** + * @throws InvalidFileFormatException + * Using a 1x1 Puzzle Grid, which is just grass, checks if the fact it expects a tent on both x and y is caught. + */ @Test - public void TooFewTentsContradictionRule_() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents", treetent); + public void TooFewTentsContradictionRule_DoubleBad() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -39,5 +75,177 @@ public void TooFewTentsContradictionRule_() throws InvalidFileFormatException { Assert.assertNull(RULE.checkContradiction(board)); Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); } + + /** + * @throws InvalidFileFormatException + * Looks at a 2x2 Board in the format: + * [] Tr + * [] Gr + * Column 2 is checked to have 1 Tent (which is not present, thus producing a contradiction) + */ + @Test + public void TooFewTentsContradictionRule_2x2ColumnOnly() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Column",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + + TreeTentCell cell1 = board.getCell(1,0); + TreeTentCell cell2 = board.getCell(1,1); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Looks at a 2x2 Board in the format: + * Tr Gr + * [] [] + * Row 1 is checked to have 1 Tent (which is not present, thus producing a contradiction) + */ + @Test + public void TooFewTentsContradictionRule_2x2RowOnly() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Row",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + + TreeTentCell cell1 = board.getCell(0,0); + TreeTentCell cell2 = board.getCell(1,0); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Looks at a 3x3 Board in the format: + * [] Tr [] + * [] Gr [] + * [] Gr [] + * Column 2 is checked to have 1 Tent (which is not present, thus producing a contradiction) + */ + @Test + public void TooFewTentsContradictionRule_3x3OneColumn() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3Column",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + + TreeTentCell cell1 = board.getCell(1,0); + TreeTentCell cell2 = board.getCell(1,1); + TreeTentCell cell3 = board.getCell(1,2); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || point.equals(cell3.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Looks at a 3x3 Board in the format: + * Gr Tr Gr + * Gr [] Gr + * Gr Tr Gr + * Column 1 and 3 are checked to have 1 Tent (which is not present, thus producing a contradiction) + */ + @Test + public void TooFewTentsContradictionRule_3x3TwoColumn() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3DoubleColumn",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNull(RULE.checkContradiction(board)); + + TreeTentCell cell1 = board.getCell(0,0); + TreeTentCell cell2 = board.getCell(0,1); + TreeTentCell cell3 = board.getCell(0,2); + TreeTentCell cell4 = board.getCell(2,0); + TreeTentCell cell5 = board.getCell(2,1); + TreeTentCell cell6 = board.getCell(2,2); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || point.equals(cell3.getLocation()) || + point.equals(cell4.getLocation()) || point.equals(cell5.getLocation()) || point.equals(cell6.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Looks at a 2x2 Board in the format: + * Tn [] + * Tr [] + * This should fail the contradiction as it is a legal board. + */ + @Test + public void TooFewTentsContradictionRule_NoContradiction() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsNoContradiction",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + Assert.assertNotNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + + + } diff --git a/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java index 2845c7b25..2a351556b 100644 --- a/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java @@ -28,17 +28,162 @@ public static void setUp() { treetent = new TreeTent(); } + /* + TESTING BASIS: + All test in this Rule use a 3x3 table. + There is a Tree at (1,1) + There are tents at (0,1) and (2,2) + All Tent Counts are listed left to right or top to bottom + */ + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTents if: + * Row Tent Counts: 0,0,0 + * Column Tent Counts: 0,0,0 + */ + @Test + public void TooManyTentsContradictionRule_TotalFail() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTotalFail",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0,1); + + Assert.assertNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTents if: + * Row Tent Counts: 1,0,0 + * Column Tent Counts: 0,0,0 + */ + @Test + public void TooManyTentsContradictionRule_TopRight() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopRight",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0,0); + TreeTentCell cell2 = board.getCell(0,1); + + Assert.assertNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTests if: + * Row Tent Counts: 0,0,1 + * Column Tent Counts: 0,0,0 + */ + @Test + public void TooManyTentsContradictionRule_BottomRight() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomRight",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0,1); + TreeTentCell cell2 = board.getCell(0,2); + + Assert.assertNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTents if: + * Row Tent Counts: 0,0,0 + * Column Tent Counts: 0,1,0 + */ + @Test + public void TooManyTentsContradictionRule_TopDown() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopDown",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0,1); + TreeTentCell cell2 = board.getCell(1,1); + + Assert.assertNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTents if: + * Row Tent Counts: 0,0,0 + * Column Tent Counts: 0,0,1 + */ @Test - public void TooManyTentsContradictionRule_Column_Row() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsContradictionRuleColumn_Row", treetent); + public void TooManyTentsContradictionRule_BottomDown() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomDown",treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - TreeTentCell cell1 = board.getCell(0, 1); - TreeTentCell cell2 = board.getCell(2, 1); + TreeTentCell cell1 = board.getCell(0,1); + TreeTentCell cell2 = board.getCell(2,1); Assert.assertNull(RULE.checkContradiction(board)); @@ -49,9 +194,78 @@ public void TooManyTentsContradictionRule_Column_Row() throws InvalidFileFormatE Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } else { - // The TooManyTentsContradictionRule checks the col and row the cell is in - // Therefore, even if a cell(0, 0) is empty, it follows the contradiction rule because - // the row it is in follows the contradiciton rule. (And because cell(1, 0) has tent row tent total is 0) + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTents if the Top Tent is completely accounted for, but not the bottom + * Row Tent Counts: 1,0,0 + * Column Tent Counts: 0,1,0 + */ + @Test + public void TooManyTentsContradictionRule_TopAccount() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopAccount",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0,0); + TreeTentCell cell2 = board.getCell(1,0); + TreeTentCell cell3 = board.getCell(0,1); + TreeTentCell cell4 = board.getCell(1,1); + + Assert.assertNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || + point.equals(cell3.getLocation()) || point.equals(cell4.getLocation())) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + /** + * @throws InvalidFileFormatException + * Tests for TooManyTents if the Bottom Tent is completely accounted for, but not the Top + * Row Tent Counts: 0,0,1 + * Column Tent Counts: 0,0,1 + */ + @Test + public void TooManyTentsContradictionRule_BottomAccount() throws InvalidFileFormatException{ + TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomAccount",treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(0,1); + TreeTentCell cell2 = board.getCell(2,1); + TreeTentCell cell3 = board.getCell(0,2); + TreeTentCell cell4 = board.getCell(2,2); + + Assert.assertNull(RULE.checkContradiction(board)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || + point.equals(cell3.getLocation()) || point.equals(cell4.getLocation())) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustRow b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustRow new file mode 100644 index 000000000..3e293d22f --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustRow @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustTent new file mode 100644 index 000000000..cbd3662e6 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrassJustTent @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsBothWays b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsBothWays new file mode 100644 index 000000000..c4d58ef60 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsBothWays @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown new file mode 100644 index 000000000..cb19ab33f --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft new file mode 100644 index 000000000..e70dc4be5 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight new file mode 100644 index 000000000..64ddf29db --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpot b/src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp similarity index 100% rename from src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpot rename to src/test/resources/puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp diff --git a/src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TentOrGrassTest b/src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TentOrGrassTest new file mode 100644 index 000000000..9ae8455e1 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TentOrGrassTest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Column b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Column new file mode 100644 index 000000000..0f78702c4 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Column @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Row b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Row new file mode 100644 index 000000000..c423b179b --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Row @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3Column b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3Column new file mode 100644 index 000000000..d1e79a76e --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3Column @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3DoubleColumn b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3DoubleColumn new file mode 100644 index 000000000..e71832f08 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3DoubleColumn @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad new file mode 100644 index 000000000..ecc8988c6 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsJustY similarity index 100% rename from src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents rename to src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsJustY diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsNoContradiction b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsNoContradiction new file mode 100644 index 000000000..2f609e161 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsNoContradiction @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsWithTent b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsWithTent new file mode 100644 index 000000000..8ae51f0a3 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsWithTent @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomAccount b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomAccount new file mode 100644 index 000000000..8474e916a --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomAccount @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsContradictionRuleColumn_Row b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomDown similarity index 93% rename from src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsContradictionRuleColumn_Row rename to src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomDown index 78e956c35..8051a5501 100644 --- a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsContradictionRuleColumn_Row +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomDown @@ -8,7 +8,7 @@ - + diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissDown b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissDown new file mode 100644 index 000000000..3cbb1cdb4 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissDown @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissRight b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissRight new file mode 100644 index 000000000..d22778c4e --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomMissRight @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomRight b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomRight new file mode 100644 index 000000000..aeb4cd148 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomRight @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopAccount b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopAccount new file mode 100644 index 000000000..258e32d47 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopAccount @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopDown b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopDown new file mode 100644 index 000000000..58d4bbddf --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopDown @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopRight b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopRight new file mode 100644 index 000000000..dd5b7b935 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTopRight @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTotalFail b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTotalFail new file mode 100644 index 000000000..9fbb8b82f --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsTotalFail @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 14b0750264664ca0c97b1b4dd32e29e91152dd85 Mon Sep 17 00:00:00 2001 From: Kevin-771 <70790256+Kevin-771@users.noreply.github.com> Date: Thu, 14 Dec 2023 19:33:56 -0500 Subject: [PATCH 71/77] added TentOrGrassCaseRuleTests (#688) * added a test * added more to test * test * should work now * should work now --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- src/test/java/legup/TestRunner.java | 5 +- .../rules/TentOrGrassCaseRuleTest.java | 51 +++++++++++++++---- .../rules/TentOrGrassCaseRule/TestPuzzle | 19 +++++++ 3 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle diff --git a/src/test/java/legup/TestRunner.java b/src/test/java/legup/TestRunner.java index 9d79c590e..3a74c4c61 100644 --- a/src/test/java/legup/TestRunner.java +++ b/src/test/java/legup/TestRunner.java @@ -42,8 +42,6 @@ public static void main(String[] args) { Result result12 = JUnitCore.runClasses(TooManyBulbsContradictionRuleTest.class); printTestResults(result12); - - //nurikabe tests Result result13 = JUnitCore.runClasses(BlackBetweenRegionsDirectRuleTest.class); printTestResults(result13); @@ -76,7 +74,6 @@ public static void main(String[] args) { Result result27 = JUnitCore.runClasses(WhiteBottleNeckDirectRuleTest.class); printTestResults(result27); - // Treetent Result result28 = JUnitCore.runClasses(EmptyFieldDirectRuleTest.class); printTestResults(result28); @@ -94,6 +91,8 @@ public static void main(String[] args) { printTestResults(result34); Result result35 = JUnitCore.runClasses(TreeForTentDirectRuleTest.class); printTestResults(result35); + Result result36 = JUnitCore.runClasses(TentOrGrassCaseRuleTest.class); + printTestResults(result36); } private static void printTestResults(Result result) { diff --git a/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java index c7159ccad..4ec8b4e36 100644 --- a/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java @@ -1,14 +1,12 @@ package puzzles.treetent.rules; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.tree.Tree; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; import edu.rpi.legup.puzzle.treetent.TreeTentType; -import edu.rpi.legup.puzzle.treetent.rules.FillinRowCaseRule; import edu.rpi.legup.puzzle.treetent.rules.TentOrGrassCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; import legup.MockGameBoardFacade; @@ -23,32 +21,63 @@ public class TentOrGrassCaseRuleTest { private static final TentOrGrassCaseRule RULE = new TentOrGrassCaseRule(); private static TreeTent treetent; - + @BeforeClass - public static void setUp(){ + public static void setUp() { MockGameBoardFacade.getInstance(); treetent = new TreeTent(); } /** + * empty 3x3 TreeTent puzzle + * Tests TentOrGrassCaseRule on UNKOWN tile + * at (0,0) + * + * checks 2 cases are created + * checks first case is TENT tile + * checks second case is GRASS tile + * checks other cells have not been modified + * * @throws InvalidFileFormatException - * A temporary test */ @Test - public void TentOrGrassCaseRule_Test() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TentOrGrassCaseRule/TentOrGrassTest", treetent); + public void TentOrTreeTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - TreeTentCell testing_cell = board.getCell(1, 0); + TreeTentCell testing_cell = board.getCell(0, 0); ArrayList cases = RULE.getCases(board, testing_cell); + // assert correct number of cases created Assert.assertEquals(2, cases.size()); - //Store the 0,1 cells from each case - //Assert that the Array of their states match to a an array of the expected. - } + // TENT case + TreeTentBoard tentCase = (TreeTentBoard) cases.get(0); + Assert.assertEquals(tentCase.getCell(0, 0).getType(), TreeTentType.TENT); + + // GRASS case + TreeTentBoard grassCase = (TreeTentBoard) cases.get(1); + Assert.assertEquals(grassCase.getCell(0, 0).getType(), TreeTentType.GRASS); + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w =0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (w == 0 && h ==0) { + continue; + } + original_cell = board.getCell(w, h); + case_cell = tentCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + + case_cell = grassCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } } diff --git a/src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle b/src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle new file mode 100644 index 000000000..a13c7cc55 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 69410ab17430156d4dc2bfb4b8aa5863c81b14ca Mon Sep 17 00:00:00 2001 From: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Date: Sat, 16 Dec 2023 13:01:40 -0500 Subject: [PATCH 72/77] Issue 679 atomic true on empty (#697) * Fixed true on empty bug Added IF check that returns false when a Direct Rule receives 0 modified cells * Updated syntax for checkstyle Added braces to IF statement * Bug fix progress Added condition to all DirectRule.Java that uses raw evaluation when no modified data is applied to the board; Added null check for STT checkRuleRaw; Updated STT case rule child count assertion from 2 to at least 1; Added null check for TreeTent SurrountTentWithGrass Direct Rule. --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- src/main/java/edu/rpi/legup/model/rules/DirectRule.java | 5 +++++ .../shorttruthtable/rules/basic/DirectRule_Generic.java | 2 ++ .../shorttruthtable/rules/caserule/CaseRule_Generic.java | 4 ++-- .../treetent/rules/SurroundTentWithGrassDirectRule.java | 3 +++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java index 4acc7573e..0465ca88c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -48,6 +48,11 @@ public String checkRule(TreeTransition transition) { public String checkRuleRaw(TreeTransition transition) { Board finalBoard = transition.getBoard(); String checkStr = null; + + // Go directly to specific direct rule's judgement if no cell's are edited + if (finalBoard.getModifiedData().size() == 0) { + checkStr = checkRuleRawAt(transition, null); + } for (PuzzleElement puzzleElement : finalBoard.getModifiedData()) { String tempStr = checkRuleAt(transition, puzzleElement); if (tempStr != null) { diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRule_Generic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRule_Generic.java index a9b9ab651..e1ac78b8c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRule_Generic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRule_Generic.java @@ -22,6 +22,8 @@ public DirectRule_Generic(String ruleID, String ruleName, String description, St } public String checkRuleRawAt(TreeTransition transition, PuzzleElement element) { + // Rule must have cell to evaluate on + if (element == null) return super.getInvalidUseOfRuleMessage() + ": Must have painted cell"; // Check that the puzzle element is not unknown ShortTruthTableBoard parentBoard = (ShortTruthTableBoard) transition.getParents().get(0).getBoard(); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java index 9f3a7073f..1409f4baa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java @@ -38,8 +38,8 @@ public CaseRule_Generic(String ruleID, String ruleName, String title, String des public String checkRuleRaw(TreeTransition transition) { // Validate that two children are generated List childTransitions = transition.getParents().get(0).getChildren(); - if (childTransitions.size() != 2) { - return "ERROR: This case rule must have 2 children."; + if (childTransitions.size() >= 1) { + return "ERROR: This case rule must spawn at least 1 child."; } // Validate that the modified cells are of type UNKNOWN, TRUE, or FALSE diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/SurroundTentWithGrassDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/SurroundTentWithGrassDirectRule.java index 829c1c1e9..44e9dfcc4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/SurroundTentWithGrassDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/SurroundTentWithGrassDirectRule.java @@ -31,6 +31,9 @@ public SurroundTentWithGrassDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + if (puzzleElement == null) { + return null; + } if (puzzleElement instanceof TreeTentLine) { return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; } From 8cb121166f7ef9de0925508df876883c5a4426b5 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Sat, 16 Dec 2023 13:25:47 -0500 Subject: [PATCH 73/77] Update publish-javadoc.yml (#702) Now uses the most recent version of version 2.x of the publisher --- .github/workflows/publish-javadoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-javadoc.yml b/.github/workflows/publish-javadoc.yml index 37eac9482..0e80d9850 100644 --- a/.github/workflows/publish-javadoc.yml +++ b/.github/workflows/publish-javadoc.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Publish JavaDoc - uses: MathieuSoysal/Javadoc-publisher.yml@v2.3.0 + uses: MathieuSoysal/Javadoc-publisher.yml@v2 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} javadoc-branch: javadoc From 59bb7eeaf99dd1dcdfb40a4a8bbba2e3af398f70 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Sat, 16 Dec 2023 13:31:24 -0500 Subject: [PATCH 74/77] Update javadocs action (#703) * Update publish-javadoc.yml Now uses the most recent version of version 2.x of the publisher * Update publish-javadoc.yml Another attempt at getting latest version --- .github/workflows/publish-javadoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-javadoc.yml b/.github/workflows/publish-javadoc.yml index 0e80d9850..5ea9b04a9 100644 --- a/.github/workflows/publish-javadoc.yml +++ b/.github/workflows/publish-javadoc.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Publish JavaDoc - uses: MathieuSoysal/Javadoc-publisher.yml@v2 + uses: MathieuSoysal/Javadoc-publisher.yml@master with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} javadoc-branch: javadoc From 9283197444c4cedfad45db8f06dbd3748256c183 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Sat, 16 Dec 2023 13:35:58 -0500 Subject: [PATCH 75/77] Update javadocs action (#704) * Update publish-javadoc.yml Now uses the most recent version of version 2.x of the publisher * Update publish-javadoc.yml Another attempt at getting latest version * Changing to version 2.4.0 --- .github/workflows/publish-javadoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-javadoc.yml b/.github/workflows/publish-javadoc.yml index 5ea9b04a9..4e1e64608 100644 --- a/.github/workflows/publish-javadoc.yml +++ b/.github/workflows/publish-javadoc.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Publish JavaDoc - uses: MathieuSoysal/Javadoc-publisher.yml@master + uses: MathieuSoysal/Javadoc-publisher.yml@v2.4.0 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} javadoc-branch: javadoc From f37f221fba33c45d518c42339e0bc37dbb2dda29 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Wed, 24 Jan 2024 00:40:02 -0500 Subject: [PATCH 76/77] Short Truth Table And, Or, and Atomic Case Rules (#694) * Added True or False case rule test * Added And case rule test files * Added Simple Test False * Made test generic * Added complex false statement test * Fixed bugs Fixed an issue with False test not being generic and issue with comparing two cells * Added true and tests * Added checkRule check to the case rules * Added Or Case Rule tests --- .../rules/AndCaseRuleTest.java | 160 +++++++++++++++++ .../shorttruthtable/rules/OrCaseRuleTest.java | 161 ++++++++++++++++++ .../rules/TrueOrFalseCaseRuleTest.java | 78 +++++++++ .../rules/AndCaseRule/ComplexStatement1_False | 13 ++ .../rules/AndCaseRule/ComplexStatement1_True | 13 ++ .../rules/AndCaseRule/SimpleStatement1_False | 13 ++ .../rules/AndCaseRule/SimpleStatement1_True | 13 ++ .../rules/OrCaseRule/ComplexStatement1_False | 13 ++ .../rules/OrCaseRule/ComplexStatement1_True | 13 ++ .../rules/OrCaseRule/SimpleStatement1_False | 13 ++ .../rules/OrCaseRule/SimpleStatement1_True | 13 ++ .../rules/TrueOrFalseCaseRule/Statement | 14 ++ 12 files changed, 517 insertions(+) create mode 100644 src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java create mode 100644 src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_False create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_True create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_False create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_True create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_False create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_True create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_False create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_True create mode 100644 src/test/resources/puzzles/shorttruthtable/rules/TrueOrFalseCaseRule/Statement diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java new file mode 100644 index 000000000..5f0a1243a --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java @@ -0,0 +1,160 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.caserule.CaseRuleAnd; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; + +public class AndCaseRuleTest { + + private static final CaseRuleAnd RULE = new CaseRuleAnd(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + private void falseAndTest(String fileName, + int andX, int andY, + int aX, int aY, + int bX, int bY) throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndCaseRule/" + fileName, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(andX,andY); + ArrayList cases = RULE.getCases(board, cell); + + // Make sure that the rule checks out + Assert.assertNotNull(RULE.checkRule(transition)); + + // Make sure there are two branches + Assert.assertEquals(2, cases.size()); + + ShortTruthTableBoard caseBoard1 = (ShortTruthTableBoard) cases.get(0); + ShortTruthTableCellType board1A = caseBoard1.getCell(aX, aY).getType(); + ShortTruthTableCellType board1B = caseBoard1.getCell(bX, bY).getType(); + + ShortTruthTableBoard caseBoard2 = (ShortTruthTableBoard) cases.get(1); + ShortTruthTableCellType board2A = caseBoard2.getCell(aX, aY).getType(); + ShortTruthTableCellType board2B = caseBoard2.getCell(bX, bY).getType(); + + // Assert that the corresponding cells for the different case rules do not + // match with each other + Assert.assertNotEquals(board1A, board2A); + Assert.assertNotEquals(board1B, board2B); + + // First assert the two cells are not equal, then verify that they are either + // unknown or false. + Assert.assertNotEquals(board1A, board1B); + Assert.assertTrue(board1A.equals(ShortTruthTableCellType.UNKNOWN) || board1A.equals(ShortTruthTableCellType.FALSE)); + Assert.assertTrue(board1B.equals(ShortTruthTableCellType.UNKNOWN) || board1B.equals(ShortTruthTableCellType.FALSE)); + + Assert.assertNotEquals(board2A, board2B); + Assert.assertTrue(board2A.equals(ShortTruthTableCellType.UNKNOWN) || board1A.equals(ShortTruthTableCellType.FALSE)); + Assert.assertTrue(board2B.equals(ShortTruthTableCellType.UNKNOWN) || board2B.equals(ShortTruthTableCellType.FALSE)); + + // Verify the board dimensions are unchanged + Assert.assertEquals(caseBoard1.getHeight(), caseBoard2.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard1.getWidth(), caseBoard2.getWidth(), board.getWidth()); + + // Verify that everywhere else on the board is unchanged + for (int i = 0; i< caseBoard1.getWidth(); i++) { + for (int j = 0; j < caseBoard1.getHeight(); j++) { + // Make sure not to check the two cells that should be different + if (!((i == aX && j == aY) || (i == bX && j == bY))) { + Assert.assertEquals(caseBoard1.getCell(i, j).getType(), caseBoard2.getCell(i, j).getType()); + } + } + } + } + + /** + * Given a statement A ^ B where ^ is false, tests this case rule by ensuring that + * two branches are created: one where A is false and one where B is false. + */ + @Test + public void SimpleStatement1FalseTest() throws InvalidFileFormatException { + falseAndTest("SimpleStatement1_False", 1, 0, 0, 0, + 2, 0); + } + + /** + * Given a statement ~(A|B)^(C^D) where the first ^ is false, tests this case rule + * by ensuring that two branches are created: one where ~ is false and one where + * the second ^ is false. + */ + @Test + public void ComplexStatement1FalseTest() throws InvalidFileFormatException { + falseAndTest("ComplexStatement1_False", 6, 0, 0, 0, + 9, 0); + } + + private void trueAndTest(String fileName, + int andX, int andY, + int aX, int aY, + int bX, int bY) throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndCaseRule/" + fileName, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(andX,andY); + ArrayList cases = RULE.getCases(board, cell); + + // Make sure that the rule checks out + Assert.assertNotNull(RULE.checkRule(transition)); + + // There should only be 1 branch + Assert.assertEquals(1, cases.size()); + + ShortTruthTableBoard caseBoard = (ShortTruthTableBoard) cases.get(0); + ShortTruthTableCellType caseBoardAType = caseBoard.getCell(aX, aY).getType(); + ShortTruthTableCellType caseBoardBType = caseBoard.getCell(bX, bY).getType(); + + // Both cells should be true + Assert.assertEquals(caseBoardAType, ShortTruthTableCellType.TRUE); + Assert.assertEquals(caseBoardBType, ShortTruthTableCellType.TRUE); + Assert.assertEquals(caseBoardAType, caseBoardBType); + + // Verify the board dimensions are unchanged + Assert.assertEquals(caseBoard.getHeight(), caseBoard.getHeight(), board.getHeight()); + } + + /** + * Given a statement A ^ B where ^ is false, tests this case rule by ensuring that + * one branch is created where A and B are both true. + */ + @Test + public void SimpleStatement1AndTest() throws InvalidFileFormatException { + trueAndTest("SimpleStatement1_True", 1, 0, 0, 0, + 2, 0); + } + + /** + * Given a statement ~(A|B)^(C^D) where the first ^ is true, tests this case rule + * by ensuring that one branch is created where both ~ and the second ^ are true. + */ + @Test + public void ComplexStatement1TrueTest() throws InvalidFileFormatException { + trueAndTest("ComplexStatement1_True", 6, 0, 0, 0, + 9, 0); + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java new file mode 100644 index 000000000..276e822cf --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java @@ -0,0 +1,161 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.caserule.CaseRuleAnd; +import edu.rpi.legup.puzzle.shorttruthtable.rules.caserule.CaseRuleOr; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; + +public class OrCaseRuleTest { + + private static final CaseRuleOr RULE = new CaseRuleOr(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + private void trueOrTest(String fileName, + int andX, int andY, + int aX, int aY, + int bX, int bY) throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/OrCaseRule/" + fileName, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(andX,andY); + ArrayList cases = RULE.getCases(board, cell); + + // Make sure that the rule checks out + Assert.assertNotNull(RULE.checkRule(transition)); + + // Make sure there are two branches + Assert.assertEquals(2, cases.size()); + + ShortTruthTableBoard caseBoard1 = (ShortTruthTableBoard) cases.get(0); + ShortTruthTableCellType board1A = caseBoard1.getCell(aX, aY).getType(); + ShortTruthTableCellType board1B = caseBoard1.getCell(bX, bY).getType(); + + ShortTruthTableBoard caseBoard2 = (ShortTruthTableBoard) cases.get(1); + ShortTruthTableCellType board2A = caseBoard2.getCell(aX, aY).getType(); + ShortTruthTableCellType board2B = caseBoard2.getCell(bX, bY).getType(); + + // Assert that the corresponding cells for the different case rules do not + // match with each other + Assert.assertNotEquals(board1A, board2A); + Assert.assertNotEquals(board1B, board2B); + + // First assert the two cells are not equal, then verify that they are either + // unknown or false. + Assert.assertNotEquals(board1A, board1B); + Assert.assertTrue(board1A.equals(ShortTruthTableCellType.UNKNOWN) || board1A.equals(ShortTruthTableCellType.TRUE)); + Assert.assertTrue(board1B.equals(ShortTruthTableCellType.UNKNOWN) || board1B.equals(ShortTruthTableCellType.TRUE)); + + Assert.assertNotEquals(board2A, board2B); + Assert.assertTrue(board2A.equals(ShortTruthTableCellType.UNKNOWN) || board1A.equals(ShortTruthTableCellType.TRUE)); + Assert.assertTrue(board2B.equals(ShortTruthTableCellType.UNKNOWN) || board2B.equals(ShortTruthTableCellType.TRUE)); + + // Verify the board dimensions are unchanged + Assert.assertEquals(caseBoard1.getHeight(), caseBoard2.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard1.getWidth(), caseBoard2.getWidth(), board.getWidth()); + + // Verify that everywhere else on the board is unchanged + for (int i = 0; i< caseBoard1.getWidth(); i++) { + for (int j = 0; j < caseBoard1.getHeight(); j++) { + // Make sure not to check the two cells that should be different + if (!((i == aX && j == aY) || (i == bX && j == bY))) { + Assert.assertEquals(caseBoard1.getCell(i, j).getType(), caseBoard2.getCell(i, j).getType()); + } + } + } + } + + /** + * Given a statement A ^ B where ^ is false, tests this case rule by ensuring that + * two branches are created: one where A is false and one where B is false. + */ + @Test + public void SimpleStatement1TrueTest() throws InvalidFileFormatException { + trueOrTest("SimpleStatement1_True", 1, 0, 0, 0, + 2, 0); + } + + /** + * Given a statement ~(A|B)^(C^D) where the first ^ is false, tests this case rule + * by ensuring that two branches are created: one where ~ is false and one where + * the second ^ is false. + */ + @Test + public void ComplexStatement1TrueTest() throws InvalidFileFormatException { + trueOrTest("ComplexStatement1_True", 6, 0, 0, 0, + 9, 0); + } + + private void falseOrTest(String fileName, + int andX, int andY, + int aX, int aY, + int bX, int bY) throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/OrCaseRule/" + fileName, stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(andX,andY); + ArrayList cases = RULE.getCases(board, cell); + + // Make sure that the rule checks out + Assert.assertNotNull(RULE.checkRule(transition)); + + // There should only be 1 branch + Assert.assertEquals(1, cases.size()); + + ShortTruthTableBoard caseBoard = (ShortTruthTableBoard) cases.get(0); + ShortTruthTableCellType caseBoardAType = caseBoard.getCell(aX, aY).getType(); + ShortTruthTableCellType caseBoardBType = caseBoard.getCell(bX, bY).getType(); + + // Both cells should be true + Assert.assertEquals(caseBoardAType, ShortTruthTableCellType.FALSE); + Assert.assertEquals(caseBoardBType, ShortTruthTableCellType.FALSE); + Assert.assertEquals(caseBoardAType, caseBoardBType); + + // Verify the board dimensions are unchanged + Assert.assertEquals(caseBoard.getHeight(), caseBoard.getHeight(), board.getHeight()); + } + + /** + * Given a statement A ^ B where ^ is false, tests this case rule by ensuring that + * one branch is created where A and B are both true. + */ + @Test + public void SimpleStatement1FalseTest() throws InvalidFileFormatException { + falseOrTest("SimpleStatement1_False", 1, 0, 0, 0, + 2, 0); + } + + /** + * Given a statement ~(A|B)^(C^D) where the first ^ is true, tests this case rule + * by ensuring that one branch is created where both ~ and the second ^ are true. + */ + @Test + public void ComplexStatement1FalseTest() throws InvalidFileFormatException { + falseOrTest("ComplexStatement1_False", 6, 0, 0, 0, + 9, 0); + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java new file mode 100644 index 000000000..849e9f15c --- /dev/null +++ b/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java @@ -0,0 +1,78 @@ +package puzzles.shorttruthtable.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.rules.caserule.CaseRuleAtomic; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; + +public class TrueOrFalseCaseRuleTest { + + private static final CaseRuleAtomic RULE = new CaseRuleAtomic(); + private static ShortTruthTable stt; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + stt = new ShortTruthTable(); + } + + /** + * Tests the True or False case rule by ensuring that it results in two children, + * one that contains Statement as true and one that contains Statement as false. + */ + @Test + public void TwoBranchesTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/TrueOrFalseCaseRule/Statement", stt); + TreeNode rootNode = stt.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); + ShortTruthTableCell cell = board.getCell(0,0); + ArrayList cases = RULE.getCases(board, cell); + + // Make sure that the rule checks out + Assert.assertNotNull(RULE.checkRule(transition)); + + // Make sure there are two branches + Assert.assertEquals(2, cases.size()); + + ShortTruthTableBoard caseBoard1 = (ShortTruthTableBoard) cases.get(0); + ShortTruthTableBoard caseBoard2 = (ShortTruthTableBoard) cases.get(1); + + ShortTruthTableCellType cellType1 = caseBoard1.getCell(0,0).getType(); + ShortTruthTableCellType cellType2 = caseBoard2.getCell(0,0).getType(); + + // First assert the two cells are not equal, then verify that they are true + // or false. + Assert.assertNotEquals(cellType1, cellType2); + Assert.assertTrue(cellType1.equals(ShortTruthTableCellType.TRUE) || cellType1.equals(ShortTruthTableCellType.FALSE)); + Assert.assertTrue(cellType2.equals(ShortTruthTableCellType.TRUE) || cellType2.equals(ShortTruthTableCellType.FALSE)); + + // Verify the board dimensions are unchanged + Assert.assertEquals(caseBoard1.getHeight(), caseBoard2.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard1.getWidth(), caseBoard2.getWidth(), board.getWidth()); + + // Verify that everywhere else on the board is unchanged + for (int i = 0; i< caseBoard1.getWidth(); i++) { + for (int j = 0; j < caseBoard1.getHeight(); j++) { + // Make sure not to check the one cell that should be different + if (i != 0 && j != 0) { + Assert.assertEquals(caseBoard1.getCell(i, j).getType(), caseBoard2.getCell(i, j).getType()); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_False b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_False new file mode 100644 index 000000000..f7e5824ff --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_False @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_True b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_True new file mode 100644 index 000000000..46dd5e603 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/ComplexStatement1_True @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_False b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_False new file mode 100644 index 000000000..aa3ed2b55 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_False @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_True b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_True new file mode 100644 index 000000000..881a1063e --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/AndCaseRule/SimpleStatement1_True @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_False b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_False new file mode 100644 index 000000000..1da467289 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_False @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_True b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_True new file mode 100644 index 000000000..e8067987e --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/ComplexStatement1_True @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_False b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_False new file mode 100644 index 000000000..4daaabc88 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_False @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_True b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_True new file mode 100644 index 000000000..765aad7d8 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/OrCaseRule/SimpleStatement1_True @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/shorttruthtable/rules/TrueOrFalseCaseRule/Statement b/src/test/resources/puzzles/shorttruthtable/rules/TrueOrFalseCaseRule/Statement new file mode 100644 index 000000000..51a9d8b39 --- /dev/null +++ b/src/test/resources/puzzles/shorttruthtable/rules/TrueOrFalseCaseRule/Statement @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + From 5048ee69d958fdde9b1bb35854c56c9920068346 Mon Sep 17 00:00:00 2001 From: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:50:08 -0500 Subject: [PATCH 77/77] Bugfix 549 (#682) * Simplify rule names * Contradiction Test Suite * checkstyle * revert changes to dev * null transitions and propagation bugfixes * 2 char/hour * cleanup * Case rule dependency locking * cleanup * chellstyle * cleanup * move selection to transition * modified data check * cleanup * Skyscrapers gneralization * cleanup * cleanup * review changes --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- puzzles files/skyscrapers/1646651 | 31 - puzzles files/skyscrapers/easy1.xml | 47 - .../legup/history/AutoCaseRuleCommand.java | 1 + .../legup/model/gameboard/PuzzleElement.java | 20 + .../edu/rpi/legup/model/rules/CaseRule.java | 29 +- .../edu/rpi/legup/model/rules/DirectRule.java | 4 + .../rpi/legup/model/tree/TreeTransition.java | 64 +- .../lightup/rules/SatisfyNumberCaseRule.java | 63 + .../caserule/CaseRule_GenericStatement.java | 256 +-- .../puzzle/skyscrapers/SkyscrapersBoard.java | 11 - .../puzzle/skyscrapers/SkyscrapersClue.java | 5 +- .../rules/CellForNumberCaseRule.java | 53 +- .../rules/NumberForCellCaseRule.java | 35 + .../legup/puzzle/treetent/TreeTentBoard.java | 20 +- .../treetent/rules/FillinRowCaseRule.java | 25 + .../treetent/rules/LinkTentCaseRule.java | 15 +- .../treetent/rules/LinkTreeCaseRule.java | 29 + .../ui/proofeditorui/treeview/TreeView.java | 1640 +++++++++-------- 18 files changed, 1354 insertions(+), 994 deletions(-) delete mode 100644 puzzles files/skyscrapers/1646651 delete mode 100644 puzzles files/skyscrapers/easy1.xml diff --git a/puzzles files/skyscrapers/1646651 b/puzzles files/skyscrapers/1646651 deleted file mode 100644 index 847d8639c..000000000 --- a/puzzles files/skyscrapers/1646651 +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/easy1.xml b/puzzles files/skyscrapers/easy1.xml deleted file mode 100644 index 9d3135bff..000000000 --- a/puzzles files/skyscrapers/easy1.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index 331a3dddf..a4c157c77 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -62,6 +62,7 @@ public void executeCommand() { board.setModifiable(false); transition.setBoard(board); transition.setRule(caseRule); + transition.setSelection(elementView.getPuzzleElement().copy()); caseTrans.add(transition); TreeNode childNode = (TreeNode) tree.addTreeElement(transition); diff --git a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java index 97c9205cf..3d84287e3 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java @@ -11,6 +11,7 @@ public abstract class PuzzleElement { protected boolean isModified; protected boolean isGiven; protected boolean isValid; + protected int casesDepended; /** * PuzzleElement Constructor creates a new puzzle element. @@ -22,6 +23,7 @@ public PuzzleElement() { this.isModified = false; this.isGiven = false; this.isValid = true; + this.casesDepended = 0; } /** @@ -148,6 +150,24 @@ public void setValid(boolean isValid) { this.isValid = isValid; } + /** + * Get the number of case rules that depend upon the state of this element + * + * @return number of cases + */ + public int getCasesDepended() { + return this.casesDepended; + } + + /** + * Sets the number of case rules that depend upon the state of this element + * + * @param cases number of cases + */ + public void setCasesDepended(int cases) { + this.casesDepended = cases; + } + /** * Tests whether two puzzle elements objects have the same puzzle element * diff --git a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java index d9c7e73e5..efb96e21a 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -7,9 +7,8 @@ import edu.rpi.legup.model.tree.TreeTransition; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Set; import static edu.rpi.legup.model.rules.RuleType.CASE; @@ -79,6 +78,7 @@ public String checkRule(TreeTransition transition) { String check = checkRuleRaw(transition); + // Mark transition and new data as valid or not boolean isCorrect = (check == null); for (TreeTransition childTrans : parentNodes.get(0).getChildren()) { childTrans.setCorrect(isCorrect); @@ -125,6 +125,31 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement */ @Override public abstract String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement); + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + List cases = getCases(board,puzzleElement); + for (Board caseBoard : cases) { + Set data = caseBoard.getModifiedData(); + for (PuzzleElement element : data) { + if(!elements.contains(board.getPuzzleElement(element))){ + elements.add(board.getPuzzleElement(element)); + } + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java index 0465ca88c..847764b7b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -33,6 +33,10 @@ public String checkRule(TreeTransition transition) { transition.getParents().get(0).getChildren().size() != 1) { return "State must have only 1 parent and 1 child"; } + else if (finalBoard.getModifiedData().isEmpty()) { + // null transition + return null; + } else { return checkRuleRaw(transition); } diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java index e1d042626..72572ac72 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.rules.RuleType; @@ -12,6 +13,8 @@ public class TreeTransition extends TreeElement { private ArrayList parents; private TreeNode childNode; private Rule rule; + + private PuzzleElement selection; private boolean isCorrect; private boolean isVerified; @@ -26,6 +29,7 @@ public TreeTransition(Board board) { this.childNode = null; this.board = board; this.rule = null; + this.selection = null; this.isCorrect = false; this.isVerified = false; } @@ -87,13 +91,42 @@ public void propagateChange(PuzzleElement element) { } } else { + // Overwrite previous modifications to this element + board.removeModifiedData(board.getPuzzleElement(element)); + + // apply changes to tranistion + board.notifyChange(element); + + // mark first transition as modified + if (!board.getPuzzleElement(element).equalsData(parents.get(0).getBoard().getPuzzleElement(element))) { + board.addModifiedData(element); + } + + // propagate to children if (childNode != null) { - board.notifyChange(element); - childNode.getBoard().notifyChange(element.copy()); - for (TreeTransition child : childNode.getChildren()) { - PuzzleElement copy = element.copy(); + + // find starting board + TreeNode head = childNode; + while (head.getParent() != null) { + head = head.getParent().getParents().get(0); + } + Board headBoard = head.getBoard(); + + PuzzleElement copy = element.copy(); + // Set as modifiable if reverted to starting value (and started modifiable) + if (headBoard.getPuzzleElement(element).equalsData(element)) { + copy.setModifiable(headBoard.getPuzzleElement(element).isModifiable()); + } + else{ copy.setModifiable(false); - child.propagateChange(copy); + } + + // apply changes to result node + childNode.getBoard().notifyChange(copy); + + // apply to all child transitions + for (TreeTransition child : childNode.getChildren()) { + child.propagateChange(copy.copy()); } } } @@ -327,6 +360,27 @@ public void setRule(Rule rule) { isVerified = false; } + /** + * Gets he selected element associated with this transition + * + * @return If this is a case rule, the selected element for that rule, null otherwise + */ + public PuzzleElement getSelection() { + if (this.rule instanceof CaseRule) { + return selection; + } + return null; + } + + /** + * Sets the selected element associated with this transition + * + * @param selection selected element for this transition + */ + public void setSelection(PuzzleElement selection) { + this.selection = selection; + } + /** * Gets whether this transition is correctly justified * diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java index 1f166685b..cf7b70ccd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java @@ -287,4 +287,67 @@ private List getAdjacentCells(LightUpBoard board, LightUpCell cell) } return cells; } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + LightUpBoard puzzleBoard = (LightUpBoard) board; + LightUpCell point = (LightUpCell)puzzleBoard.getPuzzleElement(puzzleElement); + + List cells = getAdjacentCells(puzzleBoard,point); + + for (LightUpCell cell : cells) { + //add cells that can light adjacents from any direction + Point location = cell.getLocation(); + for (int i = location.x; i < puzzleBoard.getWidth(); i++) { + System.out.println(i); + LightUpCell c = puzzleBoard.getCell(i, location.y); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + for (int i = location.x; i >= 0; i--) { + LightUpCell c = puzzleBoard.getCell(i, location.y); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + for (int i = location.y; i < puzzleBoard.getHeight(); i++) { + LightUpCell c = puzzleBoard.getCell(location.x, i); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + for (int i = location.y; i >= 0; i--) { + LightUpCell c = puzzleBoard.getCell(location.x, i); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java index 2a40bf45d..0e25586a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java @@ -1,119 +1,137 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; - - -import java.util.ArrayList; - -public abstract class CaseRule_GenericStatement extends CaseRule_Generic { - - public CaseRule_GenericStatement(String ruleID, char operation, String title, - ShortTruthTableCellType[][] trueCases, - ShortTruthTableCellType[][] falseCases) { - super(ruleID, ShortTruthTableOperation.getRuleName(operation), - title + " case", - "A known " + title.toUpperCase() + " statement can have multiple forms"); - - this.operation = operation; - - this.trueCases = trueCases; - this.falseCases = falseCases; - } - - private final char operation; - - private final ShortTruthTableCellType[][] trueCases; - private final ShortTruthTableCellType[][] falseCases; - - protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; - protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; - protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; - - // Adds all elements that can be selected for this caserule - @Override - public CaseBoard getCaseBoard(Board board) { - //copy the board and add all elements that can be selected - ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); - sttBoard.setModifiable(false); - CaseBoard caseBoard = new CaseBoard(sttBoard, this); - - //add all elements that can be selected for the case rule statement - for (PuzzleElement element : sttBoard.getPuzzleElements()) { - //get the cell object - ShortTruthTableCell cell = sttBoard.getCellFromElement(element); - //the cell must match the symbol - if (cell.getSymbol() != this.operation) continue; - //the statement must be assigned with unassigned sub-statements - if (!cell.getType().isTrueOrFalse()) continue; - if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; - if (this.operation != ShortTruthTableOperation.NOT && - cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { - continue; - } - //if the element has passed all the checks, it can be selected - caseBoard.addPickableElement(element); - } - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ - @SuppressWarnings("unchecked") - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); - ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); - - // If the statement is set to true, collect true cases. Otherwise, collect the false cases - if (cell.getType() == ShortTruthTableCellType.TRUE) { - return getCasesFromCell(sttBoard, puzzleElement, trueCases); - } - return getCasesFromCell(sttBoard, puzzleElement, falseCases); - } - - /** - * Collects a list of boards for each possible outcome of case-rule application - * @param board current board state - * @param puzzleElement case rule operator - * @param possibilities list of possibilities for operator state - * @return ArrayList of Boards - */ - private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { - // Create branch case for each possibility - ArrayList cases = new ArrayList<>(); - for (int i = 0; i < possibilities.length; i++) { - // Create a new board to modify and get statement of selected square - ShortTruthTableBoard b = board.copy(); - ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); - ShortTruthTableStatement statement = cell.getStatementReference(); - - // Modify neighboring cells of case-rule application by the provided logical cases - if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { - ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); - leftCell.setData(possibilities[i][0]); - b.addModifiedData(leftCell); - } - if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { - ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); - rightCell.setData(possibilities[i][1]); - b.addModifiedData(rightCell); - } - - cases.add(b); - } - return cases; - } -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + +import java.util.List; +import java.util.ArrayList; + +public abstract class CaseRule_GenericStatement extends CaseRule_Generic { + + public CaseRule_GenericStatement(String ruleID, char operation, String title, + ShortTruthTableCellType[][] trueCases, + ShortTruthTableCellType[][] falseCases) { + super(ruleID, ShortTruthTableOperation.getRuleName(operation), + title + " case", + "A known " + title.toUpperCase() + " statement can have multiple forms"); + + this.operation = operation; + + this.trueCases = trueCases; + this.falseCases = falseCases; + } + + private final char operation; + + private final ShortTruthTableCellType[][] trueCases; + private final ShortTruthTableCellType[][] falseCases; + + protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; + protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; + protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; + + // Adds all elements that can be selected for this caserule + @Override + public CaseBoard getCaseBoard(Board board) { + //copy the board and add all elements that can be selected + ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); + sttBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(sttBoard, this); + + //add all elements that can be selected for the case rule statement + for (PuzzleElement element : sttBoard.getPuzzleElements()) { + //get the cell object + ShortTruthTableCell cell = sttBoard.getCellFromElement(element); + //the cell must match the symbol + if (cell.getSymbol() != this.operation) continue; + //the statement must be assigned with unassigned sub-statements + if (!cell.getType().isTrueOrFalse()) continue; + if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; + if (this.operation != ShortTruthTableOperation.NOT && + cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { + continue; + } + //if the element has passed all the checks, it can be selected + caseBoard.addPickableElement(element); + } + return caseBoard; + } + + /** + * Gets the possible cases at a specific location based on this case rule + * + * @param board the current board state + * @param puzzleElement equivalent puzzleElement + * @return a list of elements the specified could be + */ + @SuppressWarnings("unchecked") + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); + ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); + + // If the statement is set to true, collect true cases. Otherwise, collect the false cases + if (cell.getType() == ShortTruthTableCellType.TRUE) { + return getCasesFromCell(sttBoard, puzzleElement, trueCases); + } + return getCasesFromCell(sttBoard, puzzleElement, falseCases); + } + + /** + * Collects a list of boards for each possible outcome of case-rule application + * @param board current board state + * @param puzzleElement case rule operator + * @param possibilities list of possibilities for operator state + * @return ArrayList of Boards + */ + private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { + // Create branch case for each possibility + ArrayList cases = new ArrayList<>(); + for (int i = 0; i < possibilities.length; i++) { + // Create a new board to modify and get statement of selected square + ShortTruthTableBoard b = board.copy(); + ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); + ShortTruthTableStatement statement = cell.getStatementReference(); + + // Modify neighboring cells of case-rule application by the provided logical cases + if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); + leftCell.setData(possibilities[i][0]); + b.addModifiedData(leftCell); + } + if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); + rightCell.setData(possibilities[i][1]); + b.addModifiedData(rightCell); + } + + cases.add(b); + } + return cases; + } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = super.dependentElements(board,puzzleElement); + + elements.add(board.getPuzzleElement(puzzleElement)); + + return elements; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java index ca6bcbe02..52e8a6400 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -25,9 +25,6 @@ public class SkyscrapersBoard extends GridBoard { private boolean viewFlag = false; private boolean dupeFlag = false; - private SkyscrapersClue modClue = null; - //helper variable for case rule verification, tracks recently modified row/col - public SkyscrapersBoard(int size) { super(size, size); @@ -94,14 +91,6 @@ public void setViewFlag(boolean newFlag) { viewFlag = newFlag; } - public SkyscrapersClue getmodClue() { - return modClue; - } - - public void setModClue(SkyscrapersClue newClue) { - modClue = newClue; - } - @Override public SkyscrapersCell getCell(int x, int y) { return (SkyscrapersCell) super.getCell(x, y); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java index dc68f45c7..1e7b1b45e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java @@ -47,6 +47,9 @@ public void setType(SkyscrapersType type) { } public SkyscrapersClue copy() { - return null; + SkyscrapersClue copy = new SkyscrapersClue(data, clueIndex, type); + copy.setIndex(index); + copy.setModifiable(isModifiable); + return copy; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java index 683b742bf..01527294a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java @@ -69,7 +69,6 @@ public ArrayList getCasesFor(Board board, PuzzleElement puzzleElement, In PuzzleElement newCell = newCase.getPuzzleElement(cell); newCell.setData(number); newCase.addModifiedData(newCell); - newCase.setModClue((SkyscrapersClue) newCase.getPuzzleElement(clue)); //if flags boolean passed = true; @@ -103,12 +102,7 @@ public String checkRuleRaw(TreeTransition transition) { return "This case rule must have at least one child."; } - //find changed row/col - SkyscrapersClue modClue = ((SkyscrapersBoard) childTransitions.get(0).getBoard()).getmodClue(); - - //System.out.println(modClue.getType()); - //System.out.println(modClue.getClueIndex()); - if (childTransitions.size() != getCasesFor(oldBoard, modClue, (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) { + if (childTransitions.size() != getCasesFor(oldBoard, oldBoard.getPuzzleElement(transition.getSelection()), (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) { //System.out.println("Wrong number of cases."); return "Wrong number of cases."; } @@ -132,4 +126,49 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board; + SkyscrapersClue clue = (SkyscrapersClue)puzzleBoard.getPuzzleElement(puzzleElement); + + // check each point in modified row/col + List data = puzzleBoard.getRowCol(clue.getClueIndex(),SkyscrapersType.ANY,clue.getType() == SkyscrapersType.CLUE_WEST); + for (SkyscrapersCell point : data) { + List cells = new ArrayList<>(List.of(point)); + + // if dependent on row/col + if ((puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) && point.getType() == SkyscrapersType.UNKNOWN) { + // get perpendicular row/col intersecting this point + int index; + if (clue.getType() == SkyscrapersType.CLUE_WEST) { + index = point.getLocation().x; + } + else { + index = point.getLocation().y; + } + cells.addAll(puzzleBoard.getRowCol(index,SkyscrapersType.ANY,clue.getType() != SkyscrapersType.CLUE_WEST)); + } + + // add all to result + for (SkyscrapersCell cell : cells) { + if (!elements.contains(board.getPuzzleElement(cell))) { + elements.add(board.getPuzzleElement(cell)); + } + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java index a061c62a3..3bf0de70a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java @@ -7,6 +7,7 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersClue; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; import java.awt.*; @@ -134,4 +135,38 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board; + SkyscrapersCell point = (SkyscrapersCell)puzzleBoard.getPuzzleElement(puzzleElement); + + List cells = new ArrayList<>(List.of(point)); + + // if dependent on row/col + if (puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) { + // add all cells in row/col intersecting given point + cells.addAll(puzzleBoard.getRowCol(point.getLocation().x,SkyscrapersType.ANY,false)); + cells.addAll(puzzleBoard.getRowCol(point.getLocation().y,SkyscrapersType.ANY,true)); + } + + for (SkyscrapersCell cell : cells) { + if (!elements.contains(board.getPuzzleElement(cell))) { + elements.add(board.getPuzzleElement(cell)); + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java index 2542ea335..dc809f34d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -124,21 +124,11 @@ public void notifyDeletion(PuzzleElement puzzleElement) { public List getAdjacent(TreeTentCell cell, TreeTentType type) { List adj = new ArrayList<>(); Point loc = cell.getLocation(); - TreeTentCell up = getCell(loc.x, loc.y - 1); - TreeTentCell right = getCell(loc.x + 1, loc.y); - TreeTentCell down = getCell(loc.x, loc.y + 1); - TreeTentCell left = getCell(loc.x - 1, loc.y); - if (up != null && up.getType() == type) { - adj.add(up); - } - if (right != null && right.getType() == type) { - adj.add(right); - } - if (down != null && down.getType() == type) { - adj.add(down); - } - if (left != null && left.getType() == type) { - adj.add(left); + for (int i = -2; i < 2; i++) { + TreeTentCell adjCell = getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); + if (adjCell != null && adjCell.getType() == type) { + adj.add(adjCell); + } } return adj; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index 538772b74..698b3aa5e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -160,4 +160,29 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return null; } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + TreeTentBoard treeTentBoard = (TreeTentBoard) board; + TreeTentClue clue = (TreeTentClue) puzzleElement; + + // add all elements of filled row + for (int i = 0; i < treeTentBoard.getWidth(); i++) { + TreeTentCell cell = treeTentBoard.getCell(i, clue.getClueIndex()-1); + elements.add(board.getPuzzleElement((cell))); + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java index 36f466f87..39b1d0251 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java @@ -5,7 +5,6 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.TreeTentCell; @@ -151,4 +150,18 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + return List.of(board.getPuzzleElement(puzzleElement)); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java index 249547301..72ffd62eb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java @@ -10,6 +10,7 @@ import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.awt.Point; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -152,4 +153,32 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(List.of(board.getPuzzleElement(puzzleElement))); + + TreeTentBoard treeTentBoard = (TreeTentBoard) board; + TreeTentCell point = (TreeTentCell) puzzleElement; + + // get all adjacent cells + Point loc = point.getLocation(); + for (int i = -2; i < 2; i++) { + TreeTentCell cell = treeTentBoard.getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); + if (cell != null) { + elements.add(board.getPuzzleElement(cell)); + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java index ecf59146d..9bfffe60a 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java @@ -1,756 +1,886 @@ -package edu.rpi.legup.ui.proofeditorui.treeview; - -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.controller.TreeController; -import edu.rpi.legup.model.observer.ITreeListener; -import edu.rpi.legup.model.tree.Tree; -import edu.rpi.legup.model.tree.TreeElement; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.ui.ScrollView; -import edu.rpi.legup.utility.DisjointSets; - -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.*; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import static edu.rpi.legup.model.tree.TreeElementType.NODE; -import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; - -public class TreeView extends ScrollView implements ITreeListener { - private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); - - private static final int TRANS_GAP = 5; - - private static final int NODE_GAP_WIDTH = 70; - private static final int NODE_GAP_HEIGHT = 15; - - private static final int BORDER_GAP_HEIGHT = 20; - private static final int BORDER_GAP_WIDTH = 20; - - private static final int BORDER_SPACING = 100; - - private TreeNodeView nodeHover; - - private ArrayList currentStateBoxes; - private Rectangle bounds = new Rectangle(0, 0, 0, 0); - - private Tree tree; - private TreeNodeView rootNodeView; - private Map viewMap; - private Dimension dimension; - - private TreeViewSelection selection; - - public TreeView(TreeController treeController) { - super(treeController); - currentStateBoxes = new ArrayList<>(); - setSize(dimension = new Dimension(100, 200)); - setPreferredSize(new Dimension(640, 160)); - - viewMap = new HashMap<>(); - - selection = new TreeViewSelection(); - } - - public TreeViewSelection getSelection() { - return selection; - } - - /** - * Gets the tree node puzzleElement that the mouse is hovering over - * - * @return tree node puzzleElement that the mouse is hovering over - */ - public TreeNodeView getNodeHover() { - return nodeHover; - } - - /** - * Sets the tree node puzzleElement that the mouse is hovering over - * - * @param nodeHover tree node puzzleElement the mouse is hovering over - */ - public void setNodeHover(TreeNodeView nodeHover) { - this.nodeHover = nodeHover; - } - - /** - * Gets the TreeElementView by the specified point or null if no view exists at the specified point - * - * @param point location to query for a view - * @return TreeElementView at the point specified, otherwise null - */ - public TreeElementView getTreeElementView(Point point) { - return getTreeElementView(point, rootNodeView); - } - - /** - * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or - * the view specified is null - * - * @param point location to query for a view - * @param elementView view to determine if the point is contained within it - * @return TreeElementView at the point specified, otherwise null - */ - private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { - if (elementView == null) { - return null; - } - else { - if (elementView.contains(point) && elementView.isVisible()) { - if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { - return null; - } - return elementView; - } - else { - if (elementView.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) elementView; - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - TreeElementView view = getTreeElementView(point, transitionView); - if (view != null) { - return view; - } - } - } - else { - TreeTransitionView transitionView = (TreeTransitionView) elementView; - return getTreeElementView(point, transitionView.getChildView()); - } - } - } - return null; - } - - public void updateTreeView(Tree tree) { - this.tree = tree; - if (selection.getSelectedViews().size() == 0) { - selection.newSelection(new TreeNodeView(tree.getRootNode())); - } - repaint(); - } - - /** - * Sets the tree associated with this TreeView - * - * @param tree tree - */ - public void setTree(Tree tree) { - this.tree = tree; - } - - public void updateTreeSize() { - if (GameBoardFacade.getInstance().getTree() == null) { - return; - } - setSize(bounds.getSize()); - } - - public void reset() { - if (bounds.x != 0 || bounds.y != 0) { - updateTreeSize(); - } - } - - public void zoomFit() { - double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); - double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); - zoomTo(Math.min(fitWidth, fitHeight)); - viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); - } - - /** - * Creates a customized viewport for the scroll pane - * - * @return viewport for the scroll pane - */ - @Override - protected JViewport createViewport() { - return new JViewport() { - @Override - protected LayoutManager createLayoutManager() { - return new ViewportLayout() { - @Override - public void layoutContainer(Container parent) { - Point point = viewport.getViewPosition(); - // determine the maximum x and y view positions - int mx = getCanvas().getWidth() - viewport.getWidth(); - int my = getCanvas().getHeight() - viewport.getHeight(); - // obey edge boundaries - if (point.x < 0) { - point.x = 0; - } - if (point.x > mx) { - point.x = mx; - } - if (point.y < 0) { - point.y = 0; - } - if (point.y > my) { - point.y = my; - } - // center margins - if (mx < 0) { - point.x = 0; - } - if (my < 0) { - point.y = my / 2; - } - viewport.setViewPosition(point); - } - }; - } - }; - } - - public void draw(Graphics2D graphics2D) { - currentStateBoxes.clear(); - Tree tree = GameBoardFacade.getInstance().getTree(); - if (tree != null) { - //setSize(bounds.getDimension()); - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - drawTree(graphics2D); - - dimension.width += BORDER_SPACING; - setSize(dimension); -// graphics2D.drawRect(0,0, dimension.width, dimension.height); - - if (selection.getHover() != null) { - drawMouseOver(graphics2D); - } - } - } - - public void zoomReset() { - zoomTo(1.0); - viewport.setViewPosition(new Point(0, 0)); - } - - private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { - if (nodeView != null) { - nodeView.draw(graphics2D); - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - transitionView.draw(graphics2D); - redrawTree(graphics2D, transitionView.getChildView()); - } - } - } - - public void removeTreeElement(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - nodeView.getParentView().setChildView(null); - } - else { - TreeTransitionView transitionView = (TreeTransitionView) view; - transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); - } - } - - /** - * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image - * - * @param g the graphics to use to draw - */ - public void drawMouseOver(Graphics2D g) { - if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { - TreeTransition transition = (TreeTransition) selection.getHover().treeElement; - int imgWidth = 100; - int imgHeight = 100; - - BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); - image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); - Point mousePoint = selection.getMousePoint(); - g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); - } - } - - public void resetView() { - this.tree = null; - this.rootNodeView = null; - this.selection.clearSelection(); - this.selection.clearHover(); - } - - /** - * Called when a tree puzzleElement is added to the tree - * - * @param treeElement TreeElement that was added to the tree - */ - @Override - public void onTreeElementAdded(TreeElement treeElement) { - if (treeElement.getType() == NODE) { - addTreeNode((TreeNode) treeElement); - } - else { - addTreeTransition((TreeTransition) treeElement); - } - repaint(); - } - - /** - * Called when a tree puzzleElement is removed from the tree - * - * @param element TreeElement that was removed to the tree - */ - @Override - public void onTreeElementRemoved(TreeElement element) { - if (element.getType() == NODE) { - TreeNode node = (TreeNode) element; - TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); - - nodeView.getParentView().setChildView(null); - removeTreeNode(node); - } - else { - TreeTransition trans = (TreeTransition) element; - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - - transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); - removeTreeTransition(trans); - } - repaint(); - } - - /** - * Called when the tree selection was changed - * - * @param selection tree selection that was changed - */ - @Override - public void onTreeSelectionChanged(TreeViewSelection selection) { - this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); - selection.getSelectedViews().forEach(v -> v.setSelected(true)); - this.selection = selection; - repaint(); - } - - /** - * Called when the model has finished updating the tree. - */ - @Override - public void onUpdateTree() { - repaint(); - } - - /** - * Gets the TreeElementView by the corresponding TreeElement associated with it - * - * @param element TreeElement of the view - * @return TreeElementView of the TreeElement associated with it - */ - public TreeElementView getElementView(TreeElement element) { - return viewMap.get(element); - } - - private void removeTreeNode(TreeNode node) { - viewMap.remove(node); - node.getChildren().forEach(t -> removeTreeTransition(t)); - } - - private void removeTreeTransition(TreeTransition trans) { - viewMap.remove(trans); - if (trans.getChildNode() != null) { - removeTreeNode(trans.getChildNode()); - } - } - - private void addTreeNode(TreeNode node) { - TreeTransition parent = node.getParent(); - - TreeNodeView nodeView = new TreeNodeView(node); - TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); - - nodeView.setParentView(parentView); - parentView.setChildView(nodeView); - - viewMap.put(node, nodeView); - - if (!node.getChildren().isEmpty()) { - node.getChildren().forEach(t -> addTreeTransition(t)); - } - } - - private void addTreeTransition(TreeTransition trans) { - List parents = trans.getParents(); - - TreeTransitionView transView = new TreeTransitionView(trans); - for (TreeNode parent : parents) { - TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); - transView.addParentView(parentNodeView); - parentNodeView.addChildrenView(transView); - } - - viewMap.put(trans, transView); - - if (trans.getChildNode() != null) { - addTreeNode(trans.getChildNode()); - } - } - - ///New Draw Methods - - public void drawTree(Graphics2D graphics2D) { - if (tree == null) { - LOGGER.error("Unable to draw tree."); - } - else { - if (rootNodeView == null) { - rootNodeView = new TreeNodeView(tree.getRootNode()); - - LOGGER.debug("Creating new views for tree view."); - createViews(rootNodeView); - - selection.newSelection(rootNodeView); - } - - dimension = new Dimension(0, 0); - calcSpan(rootNodeView); - rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); - - calculateViewLocations(rootNodeView, 0); - dimension.height = (int) rootNodeView.getSpan(); - - redrawTree(graphics2D, rootNodeView); - LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); - } - } - - public void createViews(TreeNodeView nodeView) { - if (nodeView != null) { - viewMap.put(nodeView.getTreeElement(), nodeView); - - TreeNode node = nodeView.getTreeElement(); - for (TreeTransition trans : node.getChildren()) { - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - if (transView != null) { - nodeView.addChildrenView(transView); - transView.addParentView(nodeView); - break; - } - transView = new TreeTransitionView(trans); - - viewMap.put(transView.getTreeElement(), transView); - - transView.addParentView(nodeView); - nodeView.addChildrenView(transView); - - TreeNode childNode = trans.getChildNode(); - if (childNode != null) { - TreeNodeView childNodeView = new TreeNodeView(childNode); - viewMap.put(childNodeView.getTreeElement(), childNodeView); - - childNodeView.setParentView(transView); - transView.setChildView(childNodeView); - - createViews(childNodeView); - } - } - } - } - - public void calculateViewLocations(TreeNodeView nodeView, int depth) { - nodeView.setDepth(depth); - int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; - nodeView.setX(xLoc); - dimension.width = Math.max(dimension.width, xLoc); - - TreeTransitionView parentTransView = nodeView.getParentView(); - int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); - nodeView.setY(yLoc); - - ArrayList children = nodeView.getChildrenViews(); - switch (children.size()) { - case 0: - break; - case 1: { - TreeTransitionView childView = children.get(0); - - List parentsViews = childView.getParentViews(); - if (parentsViews.size() == 1) { - childView.setEndY(yLoc); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - else { - if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { - int yAvg = 0; - for (int i = 0; i < parentsViews.size(); i++) { - TreeNodeView parentNodeView = parentsViews.get(i); - depth = Math.max(depth, parentNodeView.getDepth()); - yAvg += parentNodeView.getY(); - - Point lineStartPoint = childView.getLineStartPoint(i); - lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = parentNodeView.getY(); - } - yAvg /= parentsViews.size(); - childView.setEndY(yAvg); - - childView.setDepth(depth); - - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - } - break; - } - default: { - int span = 0; - for (TreeTransitionView childView : children) { - span += childView.getSpan(); - } - - span = (int) ((nodeView.getSpan() - span) / 2); - for (int i = 0; i < children.size(); i++) { - TreeTransitionView childView = children.get(i); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); - - span += childView.getSpan(); - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - break; - } - } - } - - public void calcSpan(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - calcSpan(childView); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - ArrayList newChildrenViews = new ArrayList<>(); - for (TreeTransition trans : node.getChildren()) { - newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); - } - nodeView.setChildrenViews(newChildrenViews); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - calcSpan(mergePointView); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - calcSpan(transView); - span += transView.getSpan(); - } - } - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view - * specified as stop. Stop tree puzzleElement is NOT included in the span calculation - * - * @param view - * @param stop - */ - private void subCalcSpan(TreeElementView view, TreeElementView stop) { - //safe-guard for infinite loop - if (view == stop) { - return; - } - - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - if (childView == stop) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - subCalcSpan(childView, stop); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - subCalcSpan(mergePointView, stop); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - subCalcSpan(transView, stop); - span += transView.getSpan(); - } - } - - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null || nodeView == stop) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in - * relative order in the list of child transitions of the specified node - * - * @param node root node of the branches - * @param branches DisjointSets of the child branches of the specified node which determine which branches merge - */ - private void reorderBranches(TreeNode node, DisjointSets branches) { - List children = node.getChildren(); - List> mergingSets = branches.getAllSets(); - - List> newOrder = new ArrayList<>(); - for (Set set : mergingSets) { - List mergeBranch = new ArrayList<>(); - newOrder.add(mergeBranch); - children.forEach(t -> { - if (set.contains(t)) { - mergeBranch.add(t); - } - }); - mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> - children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); - } - - newOrder.sort((List b1, List b2) -> { - int low1 = -1; - int low2 = -1; - for (TreeTransition t1 : b1) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - for (TreeTransition t1 : b2) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - return low1 < low2 ? -1 : 1; - }); - - List newChildren = new ArrayList<>(); - newOrder.forEach(l -> newChildren.addAll(l)); - node.setChildren(newChildren); - } +package edu.rpi.legup.ui.proofeditorui.treeview; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.controller.TreeController; +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.GridCell; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.observer.ITreeListener; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.rules.Rule; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.ui.ScrollView; +import edu.rpi.legup.utility.DisjointSets; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.*; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import static edu.rpi.legup.model.tree.TreeElementType.NODE; +import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; + +public class TreeView extends ScrollView implements ITreeListener { + private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); + + private static final int TRANS_GAP = 5; + + private static final int NODE_GAP_WIDTH = 70; + private static final int NODE_GAP_HEIGHT = 15; + + private static final int BORDER_GAP_HEIGHT = 20; + private static final int BORDER_GAP_WIDTH = 20; + + private static final int BORDER_SPACING = 100; + + private TreeNodeView nodeHover; + + private ArrayList currentStateBoxes; + private Rectangle bounds = new Rectangle(0, 0, 0, 0); + + private Tree tree; + private TreeNodeView rootNodeView; + private Map viewMap; + private Dimension dimension; + + private TreeViewSelection selection; + + public TreeView(TreeController treeController) { + super(treeController); + currentStateBoxes = new ArrayList<>(); + setSize(dimension = new Dimension(100, 200)); + setPreferredSize(new Dimension(640, 160)); + + viewMap = new HashMap<>(); + + selection = new TreeViewSelection(); + } + + public TreeViewSelection getSelection() { + return selection; + } + + /** + * Gets the tree node puzzleElement that the mouse is hovering over + * + * @return tree node puzzleElement that the mouse is hovering over + */ + public TreeNodeView getNodeHover() { + return nodeHover; + } + + /** + * Sets the tree node puzzleElement that the mouse is hovering over + * + * @param nodeHover tree node puzzleElement the mouse is hovering over + */ + public void setNodeHover(TreeNodeView nodeHover) { + this.nodeHover = nodeHover; + } + + /** + * Gets the TreeElementView by the specified point or null if no view exists at the specified point + * + * @param point location to query for a view + * @return TreeElementView at the point specified, otherwise null + */ + public TreeElementView getTreeElementView(Point point) { + return getTreeElementView(point, rootNodeView); + } + + /** + * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or + * the view specified is null + * + * @param point location to query for a view + * @param elementView view to determine if the point is contained within it + * @return TreeElementView at the point specified, otherwise null + */ + private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { + if (elementView == null) { + return null; + } + else { + if (elementView.contains(point) && elementView.isVisible()) { + if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { + return null; + } + return elementView; + } + else { + if (elementView.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) elementView; + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + TreeElementView view = getTreeElementView(point, transitionView); + if (view != null) { + return view; + } + } + } + else { + TreeTransitionView transitionView = (TreeTransitionView) elementView; + return getTreeElementView(point, transitionView.getChildView()); + } + } + } + return null; + } + + public void updateTreeView(Tree tree) { + this.tree = tree; + if (selection.getSelectedViews().size() == 0) { + selection.newSelection(new TreeNodeView(tree.getRootNode())); + } + repaint(); + } + + /** + * Sets the tree associated with this TreeView + * + * @param tree tree + */ + public void setTree(Tree tree) { + this.tree = tree; + } + + public void updateTreeSize() { + if (GameBoardFacade.getInstance().getTree() == null) { + return; + } + setSize(bounds.getSize()); + } + + public void reset() { + if (bounds.x != 0 || bounds.y != 0) { + updateTreeSize(); + } + } + + public void zoomFit() { + double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); + double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); + zoomTo(Math.min(fitWidth, fitHeight)); + viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); + } + + /** + * Creates a customized viewport for the scroll pane + * + * @return viewport for the scroll pane + */ + @Override + protected JViewport createViewport() { + return new JViewport() { + @Override + protected LayoutManager createLayoutManager() { + return new ViewportLayout() { + @Override + public void layoutContainer(Container parent) { + Point point = viewport.getViewPosition(); + // determine the maximum x and y view positions + int mx = getCanvas().getWidth() - viewport.getWidth(); + int my = getCanvas().getHeight() - viewport.getHeight(); + // obey edge boundaries + if (point.x < 0) { + point.x = 0; + } + if (point.x > mx) { + point.x = mx; + } + if (point.y < 0) { + point.y = 0; + } + if (point.y > my) { + point.y = my; + } + // center margins + if (mx < 0) { + point.x = 0; + } + if (my < 0) { + point.y = my / 2; + } + viewport.setViewPosition(point); + } + }; + } + }; + } + + public void draw(Graphics2D graphics2D) { + currentStateBoxes.clear(); + Tree tree = GameBoardFacade.getInstance().getTree(); + if (tree != null) { + //setSize(bounds.getDimension()); + graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + drawTree(graphics2D); + + dimension.width += BORDER_SPACING; + setSize(dimension); +// graphics2D.drawRect(0,0, dimension.width, dimension.height); + + if (selection.getHover() != null) { + drawMouseOver(graphics2D); + } + } + } + + public void zoomReset() { + zoomTo(1.0); + viewport.setViewPosition(new Point(0, 0)); + } + + private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { + if (nodeView != null) { + nodeView.draw(graphics2D); + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + transitionView.draw(graphics2D); + redrawTree(graphics2D, transitionView.getChildView()); + } + } + } + + public void removeTreeElement(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + nodeView.getParentView().setChildView(null); + } + else { + TreeTransitionView transitionView = (TreeTransitionView) view; + transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); + } + } + + /** + * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image + * + * @param g the graphics to use to draw + */ + public void drawMouseOver(Graphics2D g) { + if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { + TreeTransition transition = (TreeTransition) selection.getHover().treeElement; + int imgWidth = 100; + int imgHeight = 100; + + BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); + image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); + Point mousePoint = selection.getMousePoint(); + g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); + } + } + + public void resetView() { + this.tree = null; + this.rootNodeView = null; + this.selection.clearSelection(); + this.selection.clearHover(); + } + + /** + * Called when a tree puzzleElement is added to the tree + * + * @param treeElement TreeElement that was added to the tree + */ + @Override + public void onTreeElementAdded(TreeElement treeElement) { + if (treeElement.getType() == NODE) { + addTreeNode((TreeNode) treeElement); + } + else { + addTreeTransition((TreeTransition) treeElement); + } + repaint(); + } + + /** + * Called when a tree puzzleElement is removed from the tree + * + * @param element TreeElement that was removed to the tree + */ + @Override + public void onTreeElementRemoved(TreeElement element) { + if (element.getType() == NODE) { + TreeNode node = (TreeNode) element; + TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); + + nodeView.getParentView().setChildView(null); + removeTreeNode(node); + } + else { + TreeTransition trans = (TreeTransition) element; + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + + // unlock ancestor elements if case rule deleted + Rule rule = trans.getRule(); + for (TreeNode node : trans.getParents()) { + + // only if the last case of a case rule will be deleted + if (!(rule instanceof CaseRule && node.getChildren().isEmpty())) { + continue; + } + + CaseRule caseRule = (CaseRule)rule; + // set dependent elements to be modifiable by ancestors (if not dependent on others) + List ancestors = node.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + + for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), trans.getSelection())) { + // decrement, unlock if 0 cases depended + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); + oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); + if (oldElement.getCasesDepended() != 0) { + continue; + } + + // set modifiable if started modifiable + boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); + + // unmodifiable if already modified + TreeNode modNode = ancestor.getParent().getParents().get(0); + while (modNode.getParent()!=null) { + Board modBoard = modNode.getParent().getBoard(); + if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { + modifiable = false; + break; + } + modNode = modNode.getParent().getParents().get(0); + } + oldElement.setModifiable(modifiable); + } + } + } + + transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); + removeTreeTransition(trans); + } + repaint(); + } + + /** + * Called when the tree selection was changed + * + * @param selection tree selection that was changed + */ + @Override + public void onTreeSelectionChanged(TreeViewSelection selection) { + this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); + selection.getSelectedViews().forEach(v -> v.setSelected(true)); + this.selection = selection; + repaint(); + } + + /** + * Called when the model has finished updating the tree. + */ + @Override + public void onUpdateTree() { + repaint(); + } + + /** + * Gets the TreeElementView by the corresponding TreeElement associated with it + * + * @param element TreeElement of the view + * @return TreeElementView of the TreeElement associated with it + */ + public TreeElementView getElementView(TreeElement element) { + return viewMap.get(element); + } + + private void removeTreeNode(TreeNode node) { + viewMap.remove(node); + List children = node.getChildren(); + + // if child is a case rule, unlock ancestor elements + if (!children.isEmpty()) { + Rule rule = children.get(0).getRule(); + if (rule instanceof CaseRule) { + CaseRule caseRule = (CaseRule)rule; + // set dependent elements to be modifiable by ancestors (if not dependent on others) + List ancestors = node.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), children.get(0).getSelection())) { + // decrement, unlock if 0 cases depended + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); + oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); + if (oldElement.getCasesDepended() == 0) { + continue; + } + + // set modifiable if started modifiable + boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); + + // unmodifiable if already modified + TreeNode modNode = ancestor.getParent().getParents().get(0); + while (modNode.getParent() != null) { + Board modBoard = modNode.getParent().getBoard(); + if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { + modifiable = false; + break; + } + modNode = modNode.getParent().getParents().get(0); + } + oldElement.setModifiable(modifiable); + } + } + } + } + node.getChildren().forEach(t -> removeTreeTransition(t)); + } + + private void removeTreeTransition(TreeTransition trans) { + viewMap.remove(trans); + if (trans.getChildNode() != null) { + removeTreeNode(trans.getChildNode()); + } + } + + private void addTreeNode(TreeNode node) { + TreeTransition parent = node.getParent(); + + TreeNodeView nodeView = new TreeNodeView(node); + TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); + + nodeView.setParentView(parentView); + parentView.setChildView(nodeView); + + viewMap.put(node, nodeView); + + if (!node.getChildren().isEmpty()) { + + // if adding a case rule, lock dependent ancestor elements + Rule rule = node.getChildren().get(0).getRule(); + if (rule instanceof CaseRule) { + CaseRule caseRule = (CaseRule)rule; + + List ancestors = node.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + for (PuzzleElement element : caseRule.dependentElements(node.getBoard(), node.getChildren().get(0).getSelection())) { + // increment and lock + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended()+1); + oldElement.setModifiable(false); + } + } + } + + node.getChildren().forEach(t -> addTreeTransition(t)); + } + } + + private void addTreeTransition(TreeTransition trans) { + List parents = trans.getParents(); + + TreeTransitionView transView = new TreeTransitionView(trans); + for (TreeNode parent : parents) { + TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); + transView.addParentView(parentNodeView); + parentNodeView.addChildrenView(transView); + + // if transition is a new case rule, lock dependent ancestor elements + Rule rule = trans.getRule(); + if (rule instanceof CaseRule && parent.getChildren().size()==1) { + CaseRule caseRule = (CaseRule)rule; + + List ancestors = parent.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + for (PuzzleElement element : caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { + // increment and lock + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended()+1); + oldElement.setModifiable(false); + } + } + } + } + + viewMap.put(trans, transView); + + if (trans.getChildNode() != null) { + addTreeNode(trans.getChildNode()); + } + } + + ///New Draw Methods + + public void drawTree(Graphics2D graphics2D) { + if (tree == null) { + LOGGER.error("Unable to draw tree."); + } + else { + if (rootNodeView == null) { + rootNodeView = new TreeNodeView(tree.getRootNode()); + + LOGGER.debug("Creating new views for tree view."); + createViews(rootNodeView); + + selection.newSelection(rootNodeView); + } + + dimension = new Dimension(0, 0); + calcSpan(rootNodeView); + rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); + + calculateViewLocations(rootNodeView, 0); + dimension.height = (int) rootNodeView.getSpan(); + + redrawTree(graphics2D, rootNodeView); + LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); + } + } + + public void createViews(TreeNodeView nodeView) { + if (nodeView != null) { + viewMap.put(nodeView.getTreeElement(), nodeView); + + TreeNode node = nodeView.getTreeElement(); + for (TreeTransition trans : node.getChildren()) { + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + if (transView != null) { + nodeView.addChildrenView(transView); + transView.addParentView(nodeView); + break; + } + transView = new TreeTransitionView(trans); + + viewMap.put(transView.getTreeElement(), transView); + + transView.addParentView(nodeView); + nodeView.addChildrenView(transView); + + TreeNode childNode = trans.getChildNode(); + if (childNode != null) { + TreeNodeView childNodeView = new TreeNodeView(childNode); + viewMap.put(childNodeView.getTreeElement(), childNodeView); + + childNodeView.setParentView(transView); + transView.setChildView(childNodeView); + + createViews(childNodeView); + } + } + } + } + + public void calculateViewLocations(TreeNodeView nodeView, int depth) { + nodeView.setDepth(depth); + int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; + nodeView.setX(xLoc); + dimension.width = Math.max(dimension.width, xLoc); + + TreeTransitionView parentTransView = nodeView.getParentView(); + int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); + nodeView.setY(yLoc); + + ArrayList children = nodeView.getChildrenViews(); + switch (children.size()) { + case 0: + break; + case 1: { + TreeTransitionView childView = children.get(0); + + List parentsViews = childView.getParentViews(); + if (parentsViews.size() == 1) { + childView.setEndY(yLoc); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + else { + if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { + int yAvg = 0; + for (int i = 0; i < parentsViews.size(); i++) { + TreeNodeView parentNodeView = parentsViews.get(i); + depth = Math.max(depth, parentNodeView.getDepth()); + yAvg += parentNodeView.getY(); + + Point lineStartPoint = childView.getLineStartPoint(i); + lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = parentNodeView.getY(); + } + yAvg /= parentsViews.size(); + childView.setEndY(yAvg); + + childView.setDepth(depth); + + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + } + break; + } + default: { + int span = 0; + for (TreeTransitionView childView : children) { + span += childView.getSpan(); + } + + span = (int) ((nodeView.getSpan() - span) / 2); + for (int i = 0; i < children.size(); i++) { + TreeTransitionView childView = children.get(i); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); + + span += childView.getSpan(); + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + break; + } + } + } + + public void calcSpan(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + calcSpan(childView); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + ArrayList newChildrenViews = new ArrayList<>(); + for (TreeTransition trans : node.getChildren()) { + newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); + } + nodeView.setChildrenViews(newChildrenViews); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + calcSpan(mergePointView); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + calcSpan(transView); + span += transView.getSpan(); + } + } + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view + * specified as stop. Stop tree puzzleElement is NOT included in the span calculation + * + * @param view + * @param stop + */ + private void subCalcSpan(TreeElementView view, TreeElementView stop) { + //safe-guard for infinite loop + if (view == stop) { + return; + } + + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + if (childView == stop) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + subCalcSpan(childView, stop); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + subCalcSpan(mergePointView, stop); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + subCalcSpan(transView, stop); + span += transView.getSpan(); + } + } + + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null || nodeView == stop) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in + * relative order in the list of child transitions of the specified node + * + * @param node root node of the branches + * @param branches DisjointSets of the child branches of the specified node which determine which branches merge + */ + private void reorderBranches(TreeNode node, DisjointSets branches) { + List children = node.getChildren(); + List> mergingSets = branches.getAllSets(); + + List> newOrder = new ArrayList<>(); + for (Set set : mergingSets) { + List mergeBranch = new ArrayList<>(); + newOrder.add(mergeBranch); + children.forEach(t -> { + if (set.contains(t)) { + mergeBranch.add(t); + } + }); + mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> + children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); + } + + newOrder.sort((List b1, List b2) -> { + int low1 = -1; + int low2 = -1; + for (TreeTransition t1 : b1) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + for (TreeTransition t1 : b2) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + return low1 < low2 ? -1 : 1; + }); + + List newChildren = new ArrayList<>(); + newOrder.forEach(l -> newChildren.addAll(l)); + node.setChildren(newChildren); + } } \ No newline at end of file