From 46082224940be16500ed80b27422d553ded84587 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 30 Jan 2024 18:31:47 -0500 Subject: [PATCH 01/89] Skyscrapers Test Suite (#708) * Simplify rule names * Contradiction Test Suite * checkstyle * Update Exporter * Revert "Update Exporter" This reverts commit bae1a1f531e407e3b9dd1d28cc79330aa181f410. * Case Rule Test Suite * Update SkyscrapersExporter.java * allow null transitions * Update TreeTransition.java * Direct Rule Test Suite * added tests pass * Update DirectRule.java Commenting out print statement --------- Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> --- .../edu/rpi/legup/model/rules/DirectRule.java | 1 + .../skyscrapers/SkyscrapersExporter.java | 5 + .../DuplicateNumberContradictionRule.java | 9 +- .../ExceedingVisibilityContradictionRule.java | 6 +- ...sufficientVisibilityContradictionRule.java | 8 +- .../rules/LastSingularCellDirectRule.java | 9 +- .../rules/LastSingularNumberDirectRule.java | 5 +- .../rules/LastVisibleCellDirectRule.java | 9 +- .../rules/LastVisibleNumberDirectRule.java | 3 +- .../skyscrapers/rules/NEdgeDirectRule.java | 3 +- .../rules/NumberForCellCaseRule.java | 4 - src/test/java/legup/TestRunner.java | 1 + .../rules/CellForNumberCaseRuleTest.java | 254 +++++++++++++++++ .../DuplicateNumberContradictionTest.java | 150 ++++++++++ .../ExceedingVisibilityContradictionTest.java | 138 ++++++++++ ...sufficientVisibilityContradictionTest.java | 138 ++++++++++ .../rules/LastSingularCellDirectTest.java | 232 ++++++++++++++++ .../rules/LastSingularNumberDirectTest.java | 145 ++++++++++ .../rules/LastVisibleCellDirectTest.java | 257 +++++++++++++++++ .../rules/LastVisibleNumberDirectTest.java | 258 ++++++++++++++++++ .../skyscrapers/rules/NEdgeDirectTest.java | 145 ++++++++++ .../rules/NumberForCellCaseRuleTest.java | 228 ++++++++++++++++ ...PreemptiveVisibilityContradictionTest.java | 155 +++++++++++ .../UnresolvedCellContradictionTest.java | 155 +++++++++++ .../UnresolvedNumberContradictionTest.java | 165 +++++++++++ .../AllContradiction | 39 +++ .../ColContradiction | 25 ++ .../RowContradiction | 25 ++ .../LastSingularCellDirectRule/0-3Opening | 26 ++ .../LastSingularCellDirectRule/1-2ColOpening | 26 ++ .../LastSingularCellDirectRule/1-2RowOpening | 26 ++ .../LastSingularCellDirectRule/2-1ColOpening | 26 ++ .../LastSingularCellDirectRule/2-1RowOpening | 26 ++ .../2-1ColOpening | 26 ++ .../2-1RowOpening | 26 ++ .../LastVisibleDirectRules/1-2ColOpening | 24 ++ .../LastVisibleDirectRules/1-2RowOpening | 24 ++ .../LastVisibleDirectRules/2-1ColOpening | 25 ++ .../LastVisibleDirectRules/2-1RowOpening | 25 ++ .../rules/NEdgeDirectRule/DownColEmpty | 26 ++ .../rules/NEdgeDirectRule/LeftRowPartial | 23 ++ .../rules/NEdgeDirectRule/UpColPartial | 25 ++ .../1-3NumberContradiction | 27 ++ .../2-2CellContradiction | 27 ++ .../2-2NumberContradiction | 28 ++ .../3-1ColContradiction | 27 ++ .../3-1RowContradiction | 27 ++ .../AllContradiction | 42 +++ .../FullColContradiction | 32 +++ .../FullRowContradiction | 32 +++ .../ImpliedAllContradiction | 31 +++ .../ImpliedColContradiction | 24 ++ .../ImpliedRowContradiction | 24 ++ .../skyscrapers/rules/common/3-0ColOpening | 26 ++ .../skyscrapers/rules/common/3-0RowOpening | 26 ++ .../puzzles/skyscrapers/rules/common/Solved | 42 +++ .../puzzles/skyscrapers/rules/common/empty | 26 ++ 57 files changed, 3334 insertions(+), 33 deletions(-) create mode 100644 src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java create mode 100644 src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java create mode 100644 src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty create mode 100644 src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial create mode 100644 src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial create mode 100644 src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction create mode 100644 src/test/resources/puzzles/skyscrapers/rules/common/3-0ColOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/common/3-0RowOpening create mode 100644 src/test/resources/puzzles/skyscrapers/rules/common/Solved create mode 100644 src/test/resources/puzzles/skyscrapers/rules/common/empty 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 847764b7b..304a2ca90 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -29,6 +29,7 @@ public DirectRule(String ruleID, String ruleName, String description, String ima */ public String checkRule(TreeTransition transition) { Board finalBoard = transition.getBoard(); + // System.out.println(finalBoard.getModifiedData().size()); if (transition.getParents().size() != 1 || transition.getParents().get(0).getChildren().size() != 1) { return "State must have only 1 parent and 1 child"; 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 155a1466c..765540770 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java @@ -54,6 +54,11 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { } boardElement.appendChild(axisSouth); + org.w3c.dom.Element flagsElement = newDocument.createElement("flags"); + flagsElement.setAttribute("dupe",String.valueOf(board.getDupeFlag())); + flagsElement.setAttribute("view",String.valueOf(board.getViewFlag())); + boardElement.appendChild(flagsElement); + if (!board.getLines().isEmpty()) { org.w3c.dom.Element linesElement = newDocument.createElement("lines"); for (PuzzleElement data : board.getLines()) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java index db5357fbe..620a87f68 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java @@ -29,7 +29,6 @@ public DuplicateNumberContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - //TODO:? Refactor to count each row/col once rather than per cell (override checkContradiction) SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; Point loc = cell.getLocation(); @@ -40,8 +39,8 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { for (int i = 0; i < skyscrapersboard.getWidth(); i++) { SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); if (i != loc.x && cell.getType() == SkyscrapersType.Number && c.getType() == SkyscrapersType.Number && c.getData() == cell.getData()) { - System.out.print(c.getData()); - System.out.println(cell.getData()); + //System.out.print(c.getData()); + //System.out.println(cell.getData()); return null; } } @@ -50,8 +49,8 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { for (int i = 0; i < skyscrapersboard.getHeight(); i++) { SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); if (i != loc.y && cell.getType() == SkyscrapersType.Number && c.getType() == SkyscrapersType.Number && c.getData() == cell.getData()) { - System.out.print(c.getData()); - System.out.println(cell.getData()); + //System.out.print(c.getData()); + //System.out.println(cell.getData()); return null; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java index efa905ea2..e38018745 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java @@ -65,7 +65,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Collections.reverse(row); for (SkyscrapersCell c : row) { if (c.getData() > max) { - System.out.print(c.getData()); + //System.out.print(c.getData()); //System.out.println(cell.getData()); max = c.getData(); count++; @@ -83,7 +83,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { max = 0; count = 0; for (SkyscrapersCell c : col) { - System.out.println(c.getData()); + //System.out.println(c.getData()); if (c.getData() > max) { //System.out.println(cell.getData()); @@ -100,7 +100,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { count = 0; Collections.reverse(col); for (SkyscrapersCell c : col) { - System.out.println(c.getData()); + //System.out.println(c.getData()); if (c.getData() > max) { //System.out.println(cell.getData()); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java index afc1b1341..7d405f122 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java @@ -49,7 +49,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { //from west border for (SkyscrapersCell c : row) { if (c.getData() > max) { - System.out.print(c.getData()); + //System.out.print(c.getData()); //System.out.println(cell.getData()); max = c.getData(); count++; @@ -65,7 +65,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Collections.reverse(row); for (SkyscrapersCell c : row) { if (c.getData() > max) { - System.out.print(c.getData()); + //System.out.print(c.getData()); //System.out.println(cell.getData()); max = c.getData(); count++; @@ -83,7 +83,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { max = 0; count = 0; for (SkyscrapersCell c : col) { - System.out.println(c.getData()); + //System.out.println(c.getData()); if (c.getData() > max) { //System.out.println(cell.getData()); @@ -100,7 +100,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { count = 0; Collections.reverse(col); for (SkyscrapersCell c : col) { - System.out.println(c.getData()); + //System.out.println(c.getData()); if (c.getData() > max) { //System.out.println(cell.getData()); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java index 541075167..2cede4117 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java @@ -14,7 +14,7 @@ public class LastSingularCellDirectRule extends DirectRule { public LastSingularCellDirectRule() { - super("SKYS-BASC-0002", "Last Non-Duplicate Cell", + super("SKYS-BASC-0002", "Last Cell for Number", "There is only one cell on this row/col for this number that does not create a duplicate contradiction", "edu/rpi/legup/images/skyscrapers/rules/LastCell.png"); } @@ -49,8 +49,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem initialBoard.setDupeFlag(dupeTemp); initialBoard.setViewFlag(viewTemp); - System.out.println(XCandidates.size()); - System.out.println(YCandidates.size()); + //System.out.println(XCandidates.size()); + //System.out.println(YCandidates.size()); //return null if either pass, both messages otherwise String xCheck = candidateCheck(XCandidates, puzzleElement, finalCell); @@ -96,9 +96,8 @@ private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { public Board getDefaultBoard(TreeNode node) { SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - System.out.println(lightUpBoard.getPuzzleElements().size()); + //System.out.println(lightUpBoard.getPuzzleElements().size()); for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - System.out.println("123"); SkyscrapersCell cell = (SkyscrapersCell) element; if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { //cell.setData(SkyscrapersType.BULB.value); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java index 934496827..280325190 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java @@ -14,7 +14,7 @@ public class LastSingularNumberDirectRule extends DirectRule { public LastSingularNumberDirectRule() { - super("SKYS-BASC-0003", "Last Non-Duplicate Number", + super("SKYS-BASC-0003", "Last Number for Cell", "There is only one number for this cell that does not create a duplicate contradiction", "edu/rpi/legup/images/skyscrapers/rules/LastNumber.png"); } @@ -79,9 +79,8 @@ private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { public Board getDefaultBoard(TreeNode node) { SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - System.out.println(lightUpBoard.getPuzzleElements().size()); + //System.out.println(lightUpBoard.getPuzzleElements().size()); for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - System.out.println("123"); SkyscrapersCell cell = (SkyscrapersCell) element; if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { //cell.setData(SkyscrapersType.BULB.value); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java index 49dc33677..3aa28bab8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java @@ -50,8 +50,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem initialBoard.setDupeFlag(dupeTemp); initialBoard.setViewFlag(viewTemp); - System.out.println(XCandidates.size()); - System.out.println(YCandidates.size()); + //System.out.println(XCandidates.size()); + //System.out.println(YCandidates.size()); //return null if either pass, both messages otherwise String xCheck = candidateCheck(XCandidates, puzzleElement, finalCell); @@ -97,16 +97,15 @@ private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { public Board getDefaultBoard(TreeNode node) { SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); SkyscrapersBoard modBoard = (SkyscrapersBoard) node.getBoard().copy(); - System.out.println(modBoard.getPuzzleElements().size()); + //System.out.println(modBoard.getPuzzleElements().size()); for (PuzzleElement element : modBoard.getPuzzleElements()) { - System.out.println("123"); SkyscrapersCell cell = (SkyscrapersCell) element; if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { //cell.setData(SkyscrapersType.BULB.value); modBoard.addModifiedData(cell); } } - System.out.println(modBoard.getModifiedData().isEmpty()); + //System.out.println(modBoard.getModifiedData().isEmpty()); if (modBoard.getModifiedData().isEmpty()) { return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java index d3d4ff2ab..e5524418d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java @@ -80,9 +80,8 @@ private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { public Board getDefaultBoard(TreeNode node) { SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - System.out.println(lightUpBoard.getPuzzleElements().size()); + //System.out.println(lightUpBoard.getPuzzleElements().size()); for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - System.out.println("123"); SkyscrapersCell cell = (SkyscrapersCell) element; if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { //cell.setData(SkyscrapersType.BULB.value); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java index 9ba726ae4..78533a819 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java @@ -81,9 +81,8 @@ private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { public Board getDefaultBoard(TreeNode node) { SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - System.out.println(lightUpBoard.getPuzzleElements().size()); + //System.out.println(lightUpBoard.getPuzzleElements().size()); for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - System.out.println("123"); SkyscrapersCell cell = (SkyscrapersCell) element; if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { //cell.setData(SkyscrapersType.BULB.value); 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 3bf0de70a..02000cd6e 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 @@ -88,7 +88,6 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { public String checkRuleRaw(TreeTransition transition) { List childTransitions = transition.getParents().get(0).getChildren(); if (childTransitions.size() == 0) { - //System.out.println("0"); return "This case rule must have at least one child."; } else { @@ -105,16 +104,13 @@ public String checkRuleRaw(TreeTransition transition) { for (int i = 0; i < childTransitions.size(); i++) { TreeTransition case2 = childTransitions.get(i); if (case2.getBoard().getModifiedData().size() != 1) { - //System.out.println("1"); return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; } SkyscrapersCell mod2 = (SkyscrapersCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - //System.out.println("2"); return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case."; } if (!(mod2.getType() == SkyscrapersType.Number)) { - //System.out.println("3"); return super.getInvalidUseOfRuleMessage() + ": This case rule must assign a number."; } } diff --git a/src/test/java/legup/TestRunner.java b/src/test/java/legup/TestRunner.java index 3a74c4c61..5e95a16bf 100644 --- a/src/test/java/legup/TestRunner.java +++ b/src/test/java/legup/TestRunner.java @@ -7,6 +7,7 @@ import puzzles.battleship.rules.*; import puzzles.lightup.rules.*; import puzzles.nurikabe.rules.*; +import puzzles.skyscrapers.rules.*; import puzzles.treetent.rules.*; /** diff --git a/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java b/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java new file mode 100644 index 000000000..eb2b692c8 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java @@ -0,0 +1,254 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.CellForNumberCaseRule; +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 CellForNumberCaseRuleTest { + + private static final CellForNumberCaseRule RULE = new CellForNumberCaseRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //basic, max cases + @Test + public void CellForNumberCaseRule_BasicEmpty() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(false); + + ArrayList cases = RULE.getCasesFor(board,board.getNorthClues().get(0), 1); + + Assert.assertEquals(board.getWidth(), cases.size()); + + for(int i=0;i cases = RULE.getCasesFor(board,board.getNorthClues().get(0), 1); + + Assert.assertEquals(board.getWidth(), cases.size()); + + for(int i=0;i cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + + Assert.assertEquals(1, cases.size()); + + SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); + PuzzleElement changedCell = expected.getCell(2,3); + changedCell.setData(1); + expected.addModifiedData(changedCell); + + Assert.assertTrue(expected.equalsBoard(cases.get(0))); + } + + //dupe, no cases + @Test + public void CellForNumberCaseRule_DupeNone() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(true); + board.setViewFlag(false); + + ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + + Assert.assertEquals(0, cases.size()); + } + + //visibility, max cases + @Test + public void CellForNumberCaseRule_ViewEmpty() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(true); + + ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(1), 1); + + Assert.assertEquals(board.getWidth(), cases.size()); + + for(int i=0;i cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + + Assert.assertEquals(1, cases.size()); + + SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); + PuzzleElement changedCell = expected.getCell(2,3); + changedCell.setData(1); + expected.addModifiedData(changedCell); + + Assert.assertTrue(expected.equalsBoard(cases.get(0))); + } + + //visibility, 1 Case, implied + @Test + public void CellForNumberCaseRule_ImpliedViewSingular() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(true); + + ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(0),5); + + Assert.assertEquals(1, cases.size()); + + SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); + PuzzleElement changedCell = expected.getCell(4,0); + changedCell.setData(5); + expected.addModifiedData(changedCell); + + Assert.assertTrue(expected.equalsBoard(cases.get(0))); + } + + //visibility, no cases + @Test + public void CellForNumberCaseRule_ViewNone() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(true); + + ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + + Assert.assertEquals(0, cases.size()); + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java new file mode 100644 index 000000000..00cce1b91 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java @@ -0,0 +1,150 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.DuplicateNumberContradictionRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class DuplicateNumberContradictionTest { + + private static final DuplicateNumberContradictionRule RULE = new DuplicateNumberContradictionRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //empty + @Test + public void DuplicateNumberContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + 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))); + } + } + } + + //correct board, no cont + @Test + public void DuplicateNumberContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + 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))); + } + } + } + + //invalid board, no cont + @Test + public void DuplicateNumberContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + 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))); + } + } + } + + //on row + @Test + public void DuplicateNumberContradictionRule_RowContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if((k==0 || k==1) && i==0){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + //on col + @Test + public void DuplicateNumberContradictionRule_ColContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if(k==0 && (i==0 || i==1)){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + //multitudes + @Test + public void DuplicateNumberContradictionRule_AllContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java new file mode 100644 index 000000000..c7f73c998 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java @@ -0,0 +1,138 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.ExceedingVisibilityContradictionRule; +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 ExceedingVisibilityContradictionTest { + + private static final ExceedingVisibilityContradictionRule RULE = new ExceedingVisibilityContradictionRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //empty + @Test + public void ExceedingVisibilityContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //correct board, no cont + @Test + public void ExceedingVisibilityContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //invalid board, no cont + @Test + public void ExceedingVisibilityContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //on row + @Test + public void ExceedingVisibilityContradictionRule_RowContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if(i==1 || i==3){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //on col + @Test + public void ExceedingVisibilityContradictionRule_ColContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if(i==2 || i==3){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //multitudes + @Test + public void ExceedingVisibilityContradictionRule_AllContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java new file mode 100644 index 000000000..4e90861fc --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java @@ -0,0 +1,138 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.InsufficientVisibilityContradictionRule; +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 InsufficientVisibilityContradictionTest { + + private static final InsufficientVisibilityContradictionRule RULE = new InsufficientVisibilityContradictionRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //empty + @Test + public void InsufficientVisibilityContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //correct board, no cont + @Test + public void InsufficientVisibilityContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //invalid board, no cont + @Test + public void InsufficientVisibilityContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //on row + @Test + public void InsufficientVisibilityContradictionRule_RowContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if(i==1 || i==3){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //on col + @Test + public void InsufficientVisibilityContradictionRule_ColContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if(i==2 || i==3){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //multitudes + @Test + public void InsufficientVisibilityContradictionRule_AllContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java new file mode 100644 index 000000000..855c358e8 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java @@ -0,0 +1,232 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.rules.LastSingularCellDirectRule; +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.*; + +public class LastSingularCellDirectTest { + + private static final LastSingularCellDirectRule RULE = new LastSingularCellDirectRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //full row + @Test + public void LastSingularCellDirectRule_FullRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,3); + cell.setData(1); + + 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))); + } + } + } + } + + //full col + @Test + public void LastSingularCellDirectRule_FullColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,1); + cell.setData(1); + + 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))); + } + } + } + } + + //empty row/col + @Test + public void LastSingularCellDirectRule_EmptyTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,1); + cell.setData(3); + + 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))); + } + } + } + } + + //2-1 row + @Test + public void LastSingularCellDirectRule_PartialRowTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,1); + cell.setData(1); + + 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))); + } + } + } + } + + //2-1 col + @Test + public void LastSingularCellDirectRule_PartialColTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,2); + cell.setData(3); + + 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))); + } + } + } + } + + //1-2 row + @Test + public void LastSingularCellDirectRule_PartialRowTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,1); + cell.setData(2); + + 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))); + } + } + } + } + + //1-2 col + @Test + public void LastSingularCellDirectRule_PartialColTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,0); + cell.setData(4); + + 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))); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java new file mode 100644 index 000000000..f08774a8a --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java @@ -0,0 +1,145 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.rules.LastSingularNumberDirectRule; +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.*; + +public class LastSingularNumberDirectTest { + + private static final LastSingularNumberDirectRule RULE = new LastSingularNumberDirectRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //full row / empty col + @Test + public void LastSingularNumberDirectRule_FullRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,3); + cell.setData(1); + + 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))); + } + } + } + } + + //full col / empty row + @Test + public void LastSingularNumberDirectRule_FullColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,1); + cell.setData(1); + + 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))); + } + } + } + } + + //2-1 row / 1-2 col + @Test + public void LastSingularNumberDirectRule_PartialRowColTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,1); + cell.setData(4); + + 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))); + } + } + } + } + + //2-1 col / 1-2 row + @Test + public void LastSingularNumberDirectRule_PartialRowColTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,2); + cell.setData(1); + + 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))); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java new file mode 100644 index 000000000..ca50bc873 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java @@ -0,0 +1,257 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.rules.LastVisibleCellDirectRule; +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.*; + +public class LastVisibleCellDirectTest { + + private static final LastVisibleCellDirectRule RULE = new LastVisibleCellDirectRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //full row + @Test + public void LastVisibleCellDirectRule_FullRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,3); + cell.setData(1); + + 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))); + } + } + } + } + + //full col + @Test + public void LastVisibleCellDirectRule_FullColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,1); + cell.setData(1); + + 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))); + } + } + } + } + + //empty row + @Test + public void LastVisibleCellDirectRule_EmptyRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,2); + cell.setData(5); + + 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))); + } + } + } + } + + //empty col + @Test + public void LastVisibleCellDirectRule_EmptyColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,4); + cell.setData(5); + + 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))); + } + } + } + } + + //1-2 row + public void LastVisibleCellDirectRule_PartialRowTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,2); + cell.setData(3); + + 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))); + } + } + } + } + + //1-2 col + public void LastVisibleCellDirectRule_PartialColTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,2); + cell.setData(2); + + 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))); + } + } + } + } + + //2-1 row + public void LastVisibleCellDirectRule_PartialRowTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,1); + cell.setData(2); + + 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))); + } + } + } + } + + //2-1 col + public void LastVisibleCellDirectRule_PartialColTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,2); + cell.setData(1); + + 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))); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java new file mode 100644 index 000000000..1816832b6 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java @@ -0,0 +1,258 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.rules.LastSingularCellDirectRule; +import edu.rpi.legup.puzzle.skyscrapers.rules.LastVisibleNumberDirectRule; +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.*; + +public class LastVisibleNumberDirectTest { + + private static final LastVisibleNumberDirectRule RULE = new LastVisibleNumberDirectRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //full row + @Test + public void LastVisibleNumberDirectRule_FullRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,3); + cell.setData(1); + + 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))); + } + } + } + } + + //full col + @Test + public void LastVisibleNumberDirectRule_FullColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,1); + cell.setData(1); + + 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))); + } + } + } + } + + //empty row + @Test + public void LastVisibleNumberDirectRule_EmptyRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,2); + cell.setData(5); + + 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))); + } + } + } + } + + //empty col + @Test + public void LastVisibleNumberDirectRule_EmptyColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(3,4); + cell.setData(5); + + 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))); + } + } + } + } + + //1-2 row + public void LastVisibleNumberDirectRule_PartialRowTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,2); + cell.setData(3); + + 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))); + } + } + } + } + + //1-2 col + public void LastVisibleNumberDirectRule_PartialColTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(2,2); + cell.setData(2); + + 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))); + } + } + } + } + + //2-1 row + public void LastVisibleNumberDirectRule_PartialRowTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,1); + cell.setData(2); + + 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))); + } + } + } + } + + //2-1 col + public void LastVisibleNumberDirectRule_PartialColTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(0,2); + cell.setData(1); + + 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))); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java new file mode 100644 index 000000000..cc39d8183 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java @@ -0,0 +1,145 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.rules.NEdgeDirectRule; +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.*; + +public class NEdgeDirectTest { + + private static final NEdgeDirectRule RULE = new NEdgeDirectRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //-> row, empty -> full + @Test + public void NEdgeDirectRule_RightRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + for(int i = 0; i < 5; i++){ + SkyscrapersCell cell = board.getCell(i,0); + cell.setData(i + 1); + board.addModifiedData(cell); + } + + Assert.assertNull(RULE.checkRule(transition)); + + for(int i = 0; i < board.getHeight(); i++) { + for(int k = 0; k < board.getWidth(); k++) { + if(i == 0) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + //<-row, partial -> partial + @Test + public void NEdgeDirectRule_LeftRowTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + SkyscrapersCell cell = board.getCell(1,3); + cell.setData(2); + + 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))); + } + } + } + } + + //up col, partial -> full + @Test + public void NEdgeDirectRule_UpColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + for(int i = 0; i < 2; i++){ + SkyscrapersCell cell = board.getCell(1,i); + cell.setData(i + 1); + board.addModifiedData(cell); + } + + Assert.assertNull(RULE.checkRule(transition)); + + for(int i = 0; i < board.getHeight(); i++) { + for(int k = 0; k < board.getWidth(); k++) { + if(k == 1 && i < 2) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + //down col, empty -> partial + @Test + public void NEdgeDirectRule_DownColTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty", skyscrapers); + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); + for(int i = 1; i < 5; i++){ + SkyscrapersCell cell = board.getCell(3,i); + cell.setData(5 - i); + board.addModifiedData(cell); + } + + Assert.assertNull(RULE.checkRule(transition)); + + for(int i = 0; i < board.getHeight(); i++) { + for(int k = 0; k < board.getWidth(); k++) { + if(k == 3 && i > 0) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java b/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java new file mode 100644 index 000000000..40c21e604 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java @@ -0,0 +1,228 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.NumberForCellCaseRule; +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 NumberForCellCaseRuleTest { + + private static final NumberForCellCaseRule RULE = new NumberForCellCaseRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //basic, max cases + @Test + public void NumberForCellCaseRule_BasicEmpty() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(false); + + ArrayList cases = RULE.getCases(board,board.getCell(0,0)); + + Assert.assertEquals(board.getWidth(), cases.size()); + + for(int i=0;i cases = RULE.getCases(board,board.getCell(0,0)); + + Assert.assertEquals(board.getWidth(), cases.size()); + + for(int i=0;i cases = RULE.getCases(board,board.getCell(2,3)); + + Assert.assertEquals(1, cases.size()); + + SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); + PuzzleElement changedCell = expected.getCell(2,3); + changedCell.setData(1); + expected.addModifiedData(changedCell); + + Assert.assertTrue(expected.equalsBoard(cases.get(0))); + } + + //dupe, no cases + @Test + public void NumberForCellCaseRule_DupeNone() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(true); + board.setViewFlag(false); + + ArrayList cases = RULE.getCases(board,board.getCell(2,3)); + + Assert.assertEquals(0, cases.size()); + } + + //visibility, max cases + @Test + public void NumberForCellCaseRule_ViewEmpty() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(true); + + ArrayList cases = RULE.getCases(board,board.getCell(1,4)); + + Assert.assertEquals(4, cases.size()); + + for(int i=0;i cases = RULE.getCases(board,board.getCell(2,3)); + + Assert.assertEquals(1, cases.size()); + + SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); + PuzzleElement changedCell = expected.getCell(2,3); + changedCell.setData(1); + expected.addModifiedData(changedCell); + + Assert.assertTrue(expected.equalsBoard(cases.get(0))); + } + + //visibility, no cases + @Test + public void NumberForCellCaseRule_ViewNone() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + + board.setDupeFlag(false); + board.setViewFlag(true); + + ArrayList cases = RULE.getCases(board,board.getCell(2,3)); + + Assert.assertEquals(0, cases.size()); + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java new file mode 100644 index 000000000..69f4e593a --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java @@ -0,0 +1,155 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.PreemptiveVisibilityContradictionRule; +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 PreemptiveVisibilityContradictionTest { + + private static final PreemptiveVisibilityContradictionRule RULE = new PreemptiveVisibilityContradictionRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //empty + @Test + public void PreemptiveVisibilityContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //correct board, no cont + @Test + public void PreemptiveVisibilityContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //invalid board, no cont + @Test + public void PreemptiveVisibilityContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //on row + @Test + public void PreemptiveVisibilityContradictionRule_RowContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if(i==1){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //on col + @Test + public void PreemptiveVisibilityContradictionRule_ColContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if(i==2){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //multitudes + @Test + public void PreemptiveVisibilityContradictionRule_AllContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //multitudes - preemptive + @Test + public void PreemptiveVisibilityContradictionRule_ImpliedAllContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java new file mode 100644 index 000000000..d5f21a8d7 --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java @@ -0,0 +1,155 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.UnresolvedCellContradictionRule; +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 UnresolvedCellContradictionTest { + + private static final UnresolvedCellContradictionRule RULE = new UnresolvedCellContradictionRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //empty + @Test + public void UnresolvedCellContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + 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))); + } + } + } + + //correct board, no cont + @Test + public void UnresolvedCellContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + 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))); + } + } + } + + //invalid board, no cont + @Test + public void UnresolvedCellContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + 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))); + } + } + } + + //3 in a row, 1 in col creates contradiction + @Test + public void UnresolvedCellContradictionRule_RowContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if(k==2 && i==3){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + //3 in a col, 1 in row creates contradiction + @Test + public void UnresolvedCellContradictionRule_ColContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if(k==1 && i==0){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + //2 in a col, 2 in row creates cell contradiction + @Test + public void UnresolvedCellContradictionRule_MixedContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if(k==2 && i==3){ + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else{ + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java new file mode 100644 index 000000000..fe4a4865a --- /dev/null +++ b/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java @@ -0,0 +1,165 @@ +package puzzles.skyscrapers.rules; + + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; +import edu.rpi.legup.puzzle.skyscrapers.rules.UnresolvedNumberContradictionRule; +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 UnresolvedNumberContradictionTest { + + private static final UnresolvedNumberContradictionRule RULE = new UnresolvedNumberContradictionRule(); + private static Skyscrapers skyscrapers; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + skyscrapers = new Skyscrapers(); + } + + //empty + @Test + public void UnresolvedNumberContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //correct board, no cont + @Test + public void UnresolvedNumberContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //invalid board, no cont + @Test + public void UnresolvedNumberContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNotNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + + //3 in a row, 1 in col creates contradiction + @Test + public void UnresolvedNumberContradictionRule_RowContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if (i == 3) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //3 in a col, 1 in row creates contradiction + @Test + public void UnresolvedNumberContradictionRule_ColContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if (i == 1) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //2 in a row/col, 2 in other row/cols creates number contradiction + @Test + public void UnresolvedNumberContradictionRule_TwoContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if (i == 1 || i == 3) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } + + //1 in a row/col, 3 in other row/cols creates number contradiction + @Test + public void UnresolvedNumberContradictionRule_ThreeContradictionTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction", skyscrapers); + + TreeNode rootNode = skyscrapers.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + Assert.assertNull(RULE.checkContradiction((SkyscrapersBoard) transition.getBoard())); + + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < board.getHeight(); i++) { + if (i == 1 || i == 2) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction b/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction new file mode 100644 index 000000000..a1e81a86e --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction b/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction new file mode 100644 index 000000000..2fd32645c --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction b/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction new file mode 100644 index 000000000..29da502d9 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening new file mode 100644 index 000000000..ed281abae --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening new file mode 100644 index 000000000..193fd2cff --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening new file mode 100644 index 000000000..10c3d6d4e --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening new file mode 100644 index 000000000..f96d920f5 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening new file mode 100644 index 000000000..1924e1336 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening new file mode 100644 index 000000000..1d1f9e1ba --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening b/src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening new file mode 100644 index 000000000..a3089ea98 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening new file mode 100644 index 000000000..978bafec4 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening new file mode 100644 index 000000000..6b35d20f7 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening new file mode 100644 index 000000000..766bf4151 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening new file mode 100644 index 000000000..2c9892dea --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty b/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty new file mode 100644 index 000000000..111d2bd87 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial b/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial new file mode 100644 index 000000000..b8ccb00e3 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial b/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial new file mode 100644 index 000000000..5b8fd6023 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction new file mode 100644 index 000000000..51aa44822 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction new file mode 100644 index 000000000..2d6dd8df0 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction new file mode 100644 index 000000000..7bd380e92 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction new file mode 100644 index 000000000..046d2ea8e --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction new file mode 100644 index 000000000..3063aeea1 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction new file mode 100644 index 000000000..1b2a81ca8 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction new file mode 100644 index 000000000..3c0e67993 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction new file mode 100644 index 000000000..0a731bc7d --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction new file mode 100644 index 000000000..29aa271bf --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction new file mode 100644 index 000000000..bdef438d5 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction new file mode 100644 index 000000000..3402712ad --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/common/3-0ColOpening b/src/test/resources/puzzles/skyscrapers/rules/common/3-0ColOpening new file mode 100644 index 000000000..8b63a4035 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/common/3-0ColOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/common/3-0RowOpening b/src/test/resources/puzzles/skyscrapers/rules/common/3-0RowOpening new file mode 100644 index 000000000..89bf1193b --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/common/3-0RowOpening @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/common/Solved b/src/test/resources/puzzles/skyscrapers/rules/common/Solved new file mode 100644 index 000000000..5dc1210e9 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/common/Solved @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/skyscrapers/rules/common/empty b/src/test/resources/puzzles/skyscrapers/rules/common/empty new file mode 100644 index 000000000..87f7db3d7 --- /dev/null +++ b/src/test/resources/puzzles/skyscrapers/rules/common/empty @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From bcc904a9d3a4077a0e419cb5913923deaa8f8dec Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:23:26 -0500 Subject: [PATCH 02/89] Added a text file File represents all files we will put and use to fully implement the StarBattle puzzle later. --- .../rpi/legup/puzzle/starbattle/allfiles.txt | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt new file mode 100644 index 000000000..7f006191a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -0,0 +1,230 @@ +//StarBattle.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class StarBattle extends Puzzle { + public StarBattle() { + super(); + this.name = "StarBattle"; + + this.importer = new StarBattleImporter(this); + this.exporter = new StarBattleExporter(this); + + this.factory = new StarBattleCellFactory(); + } + + @Override + public void initializeView() { + } + + @Override + public Board generatePuzzle(int difficulty) { + return null; + } + + @Override + public boolean isBoardComplete(Board board) { + return true; + } + + @Override + public void onBoardChange(Board board) { + } +} + +//StarBattleBoard.java + +package edu.rpi.legup.puzzle.lightup; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +public class StarBattleBoard extends GridBoard { + + private int size; + private vector group_sizes; + + /** + * StarBattleBoard Constructor - create a new Star Battle board + * + * @param size size of one side of the star battle board + */ + + public StarBattleBoard(int size) { + super(size, size); + group_sizes = vector(size); + } + + @Override + public StarBattleCell getCell(int x, int y) { + return (StarBattleCell) super.getCell(x, y); + } + + +} + +//StarBattleCell.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +public class StarBattleCell extends GridCell { + private int groupIndex; + private int max; + + /** + * SudokuCell Constructor - creates a new Sudoku cell to hold the puzzleElement + * + * @param valueInt value of the star battle cell denoting its state + * @param location location of the cell on the board + * @param size size of the sudoku cell + */ + public StarBattleCell(int value, Point location, int groupIndex, int size) { + super(value, location); + this.groupIndex = groupIndex; + this.max = size; + } + + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()) { + case "SBUP-PLAC-0001": + this.data = -4; + break; + case "SBUP-UNPL-0002": + this.data = -1; + break; + case "SBUP-UNPL-0003": + this.data = -2; + break; + case "SBUP-UNPL-0001"://Not sure how button events work + 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 = 3;//Unsure + } + break; + } + break; + } + } + + public LightUpCellType getType() { + switch (data) { + case -3: + return LightUpCellType.UNKNOWN; + case -2: + return LightUpCellType.STAR; + case -1: + return LightUpCellType.BLACK; + default: + if (data >= 0) { + return StarBattleCellType.WHITE; + } + } + return null; + } + + /** + * Gets the region index of the cell + * + * @return group index of the cell + */ + public int getGroupIndex() { + return groupIndex; + } + + /** + * Gets the size of the cell + * + * @return size of the cell + */ + + public int getMax() { + return max; + } + +} + +//StarBattleCellController.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.event.MouseEvent; + +public class StarBattleCellController extends ElementController { + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + StarBattleCell cell = (StarBattleCell) data; + if (e.getButton() == MouseEvent.BUTTON1) { + if (e.isControlDown()) { + this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); + } + else { + if (cell.getData() == 0) { + data.setData(-3); + } + else { + data.setData(cell.getData() + 1); + } + } + } + else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == -3) { + data.setData(0); + } + else { + data.setData(cell.getData() - 1); + } + } + } + } +} + +//StarBattleCellFactory.java + + + +//StarBattleCellType.java +package edu.rpi.legup.puzzle.starbattle; + +public enum StarBattleType { + UNKNOWN(-3), STAR(-2), BLACK(-1), WHITE(0); + + public int value; + + StarBattleCell(int value) { + this.value = value; + } +} + +//StarBattleExporter.java +//StarBattleImporter.java +//StarBattleView.java \ No newline at end of file From 80cb273dbaf046401ded2ea016784269329dfa79 Mon Sep 17 00:00:00 2001 From: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:59:03 -0500 Subject: [PATCH 03/89] Skyscrapers puzzle editor (#720) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Implementing Puzzle Editor Removing Extraneous TreeTent Code Allowing setting tile number * Allow for editing clues on all axies * Remove Extraneous Code Removed functionality required for TreeTent but unnecessary for Skyscrapers * Remove Extraneous Code * Clue Tile in Editor Editor now requires selecting the clue tile to edit clues Added images for the tiles * Merge branch 'dev' into skyscrapersPuzzleEditor * Checkstyle Requirement * Necessary Code * Allow for test functionality --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> --- bin/main/edu/rpi/legup/legup/config | 2 +- .../legup/puzzle/skyscrapers/ClueCommand.java | 215 ------------------ .../legup/puzzle/skyscrapers/Skyscrapers.java | 5 +- .../puzzle/skyscrapers/SkyscrapersBoard.java | 128 +++++------ .../puzzle/skyscrapers/SkyscrapersCell.java | 31 +++ .../skyscrapers/SkyscrapersCellFactory.java | 83 +++---- .../skyscrapers/SkyscrapersClueView.java | 7 - .../skyscrapers/SkyscrapersController.java | 84 +------ .../skyscrapers/SkyscrapersExporter.java | 11 +- .../skyscrapers/SkyscrapersImporter.java | 47 ++-- .../puzzle/skyscrapers/SkyscrapersLine.java | 42 ---- .../skyscrapers/SkyscrapersLineView.java | 31 --- .../puzzle/skyscrapers/SkyscrapersType.java | 4 + .../puzzle/skyscrapers/SkyscrapersView.java | 15 -- .../puzzle/skyscrapers/elements/ClueTile.java | 10 + .../skyscrapers/elements/NumberTile.java | 9 + .../skyscrapers/elements/UnknownTile.java | 9 + .../skyscrapers_elements_reference_sheet.txt | 3 + .../images/skyscrapers/tiles/ClueTile.png | Bin 0 -> 353 bytes .../images/skyscrapers/tiles/UnknownTile.png | Bin 0 -> 9733 bytes src/main/resources/edu/rpi/legup/legup/config | 2 +- 21 files changed, 188 insertions(+), 550 deletions(-) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLine.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt create mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/skyscrapers/tiles/UnknownTile.png diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index 19e63a2a3..ccd4f5be3 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -38,6 +38,6 @@ + fileCreationDisabled="false"/> diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java deleted file mode 100644 index e80cd3d77..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/ClueCommand.java +++ /dev/null @@ -1,215 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers; - -import edu.rpi.legup.history.CommandError; -import edu.rpi.legup.history.PuzzleCommand; -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeTransitionView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ClueCommand extends PuzzleCommand { - private TreeViewSelection selection; - private SkyscrapersClueView clueView; - private Map addTran; - private List> emptyCells; - - public ClueCommand(TreeViewSelection selection, SkyscrapersClueView clueView) { - this.selection = selection; - this.clueView = clueView; - this.addTran = new HashMap<>(); - this.emptyCells = new ArrayList<>(); - } - - /** - * Executes a command - */ - @Override - public void executeCommand() { - /*Puzzle puzzle = getInstance().getPuzzleModule(); - Tree tree = puzzle.getTree(); - TreeView treeView = getInstance().getLegupUI().getTreePanel().getTreeView(); - - final TreeViewSelection newSelection = new TreeViewSelection(); - for (int i = 0; i < selection.getSelectedViews().size(); i++) { - TreeElementView selectedView = selection.getSelectedViews().get(i); - TreeElement treeElement = selectedView.getTreeElement(); - - final TreeTransition finalTran; - SkyscrapersBoard board = (SkyscrapersBoard) treeElement.getBoard(); - List tempList = emptyCells.get(i); - if (treeElement.getType() == TreeElementType.NODE) { - TreeNode treeNode = (TreeNode) treeElement; - - TreeTransition transition = addTran.get(treeNode); - if (transition == null) { - transition = tree.addNewTransition(treeNode); - addTran.put(treeNode, transition); - } else { - treeNode.addChild(transition); - } - - finalTran = transition; - puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalTran)); - - newSelection.addToSelection(treeView.getElementView(finalTran)); - board = (SkyscrapersBoard) finalTran.getBoard(); - } else { - finalTran = (TreeTransition) treeElement; - newSelection.addToSelection(treeView.getElementView(treeElement)); - } - - for (SkyscrapersCell cell : tempList) { - cell = (SkyscrapersCell) board.getPuzzleElement(cell); - cell.setData(SkyscrapersType.GRASS.value); - board.addModifiedData(cell); - finalTran.propagateChange(cell); - - final SkyscrapersCell finalCell = cell; - puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(finalCell)); - } - if (i == 0) { - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTran)); - } - } - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection));*/ - } - - /** - * Gets the reason why the command cannot be executed - * - * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed - */ - @Override - public String getErrorString() { - List selectedViews = selection.getSelectedViews(); - if (selectedViews.size() != 1) { - return CommandError.ONE_SELECTED_VIEW.toString(); - } - TreeElementView selectedView = selection.getFirstSelection(); - Board board = selectedView.getTreeElement().getBoard(); - SkyscrapersClue selectedPuzzleElement = clueView.getPuzzleElement(); - if (selectedView.getType() == TreeElementType.NODE) { - - TreeNodeView nodeView = (TreeNodeView) selectedView; - if (!nodeView.getChildrenViews().isEmpty()) { - return CommandError.UNMODIFIABLE_BOARD.toString(); - } - else { - if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { - return CommandError.UNMODIFIABLE_DATA.toString(); - } - } - } - else { - TreeTransitionView transitionView = (TreeTransitionView) selectedView; - if (!transitionView.getTreeElement().getBoard().isModifiable()) { - return CommandError.UNMODIFIABLE_BOARD.toString(); - } - else { - if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { - return CommandError.UNMODIFIABLE_DATA.toString(); - } - } - } - return null; - } - /*@Override - public String getErrorString() { - if (selection.getSelectedViews().isEmpty()) { - return CommandError.NO_SELECTED_VIEWS.toString(); - } - - emptyCells.clear(); - for (TreeElementView view : selection.getSelectedViews()) { - TreeElement treeElement = view.getTreeElement(); - SkyscrapersBoard board = (SkyscrapersBoard) treeElement.getBoard(); - if (treeElement.getType() == TreeElementType.NODE) { - TreeNode node = (TreeNode) treeElement; - if (!node.getChildren().isEmpty()) { - return CommandError.UNMODIFIABLE_BOARD.toString(); - } - } else { - if (!board.isModifiable()) { - return CommandError.UNMODIFIABLE_BOARD.toString(); - } - } - - List tempList = new ArrayList<>(); - SkyscrapersClue clue = clueView.getPuzzleElement(); - if (clue.getType() == SkyscrapersType.CLUE_NORTH || clue.getType() == SkyscrapersType.CLUE_SOUTH) { - int col = clue.getType() == SkyscrapersType.CLUE_NORTH ? clue.getClueIndex() : clue.getClueIndex() - 1; - for (int i = 0; i < board.getWidth(); i++) { - SkyscrapersCell cell = board.getCell(col, i); - if (cell.getType() == SkyscrapersType.UNKNOWN && cell.isModifiable()) { - tempList.add(cell); - } - } - } else { - int row = clue.getType() == SkyscrapersType.CLUE_WEST ? clue.getClueIndex() : clue.getClueIndex() - 1; - for (int i = 0; i < board.getWidth(); i++) { - SkyscrapersCell cell = board.getCell(i, row); - if (cell.getType() == SkyscrapersType.UNKNOWN && cell.isModifiable()) { - tempList.add(cell); - } - } - } - if (tempList.isEmpty()) { - return "There are no modifiable unknown cells in every selected tree element."; - } - emptyCells.add(tempList); - } - return null; - }*/ - - /** - * Undoes an command - */ - @Override - public void undoCommand() { - /*Puzzle puzzle = getInstance().getPuzzleModule(); - Tree tree = puzzle.getTree(); - - for (int i = 0; i < selection.getSelectedViews().size(); i++) { - TreeElementView selectedView = selection.getSelectedViews().get(i); - TreeElement treeElement = selectedView.getTreeElement(); - - final TreeTransition finalTran; - SkyscrapersBoard board = (SkyscrapersBoard) treeElement.getBoard(); - List tempList = emptyCells.get(i); - if (treeElement.getType() == TreeElementType.NODE) { - TreeNode treeNode = (TreeNode) treeElement; - - finalTran = treeNode.getChildren().get(0); - tree.removeTreeElement(finalTran); - puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(finalTran)); - - board = (SkyscrapersBoard) finalTran.getBoard(); - } else { - finalTran = (TreeTransition) treeElement; - } - - for (SkyscrapersCell cell : tempList) { - cell = (SkyscrapersCell) board.getPuzzleElement(cell); - cell.setData(SkyscrapersType.UNKNOWN.value); - board.removeModifiedData(cell); - - final SkyscrapersCell finalCell = cell; - puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(finalCell)); - } - - if (i == 0) { - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTran)); - } - } - final TreeViewSelection newSelection = selection; - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection));*/ - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java index 71e8f8306..f87462978 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java @@ -24,6 +24,8 @@ public Skyscrapers() { @Override public void initializeView() { boardView = new SkyscrapersView((SkyscrapersBoard) currentBoard); + boardView.setBoard(currentBoard); + addBoardListener(boardView); } /** @@ -46,8 +48,7 @@ public Board generatePuzzle(int difficulty) { * @return true if the given dimensions are valid for Skyscrapers, false otherwise */ public boolean isValidDimensions(int rows, int columns) { - // This is a placeholder, this method needs to be implemented - throw new UnsupportedOperationException(); + return rows >= 4 && rows == columns; } /** 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 52e8a6400..65f5eb561 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -1,17 +1,16 @@ package edu.rpi.legup.puzzle.skyscrapers; -import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; import java.awt.*; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; public class SkyscrapersBoard extends GridBoard { - private ArrayList lines; - private ArrayList eastClues; //EAST clues @@ -28,8 +27,6 @@ public class SkyscrapersBoard extends GridBoard { public SkyscrapersBoard(int size) { super(size, size); - this.lines = new ArrayList<>(); - this.eastClues = new ArrayList<>(); this.southClues = new ArrayList<>(); this.westClues = new ArrayList<>(); @@ -43,9 +40,6 @@ public SkyscrapersBoard(int size) { } } - public ArrayList getLines() { - return lines; - } /** * @return eastClues a list of the eastern clues ordered from loc.y = 0 to max @@ -102,52 +96,13 @@ public int getSize() { @Override public PuzzleElement getPuzzleElement(PuzzleElement element) { - switch (element.getIndex()) { - case -2: - return element; - case -1: - SkyscrapersLine line = (SkyscrapersLine) element; - SkyscrapersLine thisLine = null; - for (SkyscrapersLine l : lines) { - if (line.compare(l)) { - thisLine = l; - break; - } - } - return thisLine; - default: - return super.getPuzzleElement(element); + // If the element index is -2, it is a clue and should be returned separately + if (element.getIndex() == -2) { + return element; } + return super.getPuzzleElement(element); } - /** - * Called when a {@link PuzzleElement} has been added and passes in the equivalent puzzle element with the data. - * - * @param puzzleElement equivalent puzzle element with the data. - */ - @Override - public void notifyAddition(PuzzleElement puzzleElement) { - if (puzzleElement instanceof SkyscrapersLine) { - lines.add((SkyscrapersLine) puzzleElement); - } - } - - /** - * Called when a {@link PuzzleElement} has been deleted and passes in the equivalent puzzle element with the data. - * - * @param puzzleElement equivalent puzzle element with the data. - */ - @Override - public void notifyDeletion(PuzzleElement puzzleElement) { - if (puzzleElement instanceof SkyscrapersLine) { - for (SkyscrapersLine line : lines) { - if (line.compare((SkyscrapersLine) puzzleElement)) { - lines.remove(line); - break; - } - } - } - } /** * Gets the cells of a certain type directly adjacent to a given cell @@ -251,26 +206,66 @@ public void printBoard() { } /** - * Determines if this board contains the equivalent puzzle elements as the one specified * - * @param board board to check equivalence - * @return true if the boards are equivalent, false otherwise + * @param x position of cell + * @param y position of cell + * @param e Element to be placed (null if nothing selected) + * @param m MouseEvent + * Increases clue values if in editor mode. Currently allows for + * presetting tile values, though they will not be saved. */ @Override - public boolean equalsBoard(Board board) { - SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - for (SkyscrapersLine l1 : lines) { - boolean hasLine = false; - for (SkyscrapersLine l2 : skyscrapersBoard.lines) { - if (l1.compare(l2)) { - hasLine = true; + public void setCell(int x, int y, Element e, MouseEvent m) { + SkyscrapersClue clue = this.getClue(x, y); + if (e == null) return; + if (clue != null) { + if (!e.getElementID().equals("SKYS-UNPL-0003")) { + return; + } + + if (m.getButton() == MouseEvent.BUTTON1) { + if (clue.getData() < dimension.height) { + clue.setData(clue.getData() + 1); + } + else { + clue.setData(0); } } - if (!hasLine) { - return false; + else { + if (clue.getData() > 0) { + clue.setData(clue.getData() - 1); + } + else { + clue.setData(dimension.height); + } } } - return super.equalsBoard(skyscrapersBoard); + else { + super.setCell(x - 1, y - 1, e, m); + } + } + + /** + * + * @param x position of element on boardView + * @param y position of element on boardView + * @return The clue at the given position + */ + public SkyscrapersClue getClue(int x, int y) { + int viewIndex = getSize() + 1; + if (x == 0 && y > 0 && y < viewIndex) { + return westClues.get(y-1); + } + else if (x == viewIndex && y > 0 && y < viewIndex) { + return eastClues.get(y-1); + } + else if (y == 0 && x > 0 && x < viewIndex) { + return northClues.get(x-1); + } + else if (y == viewIndex && x > 0 && x < viewIndex) { + return southClues.get(x-1); + } + return null; } @Override @@ -281,11 +276,6 @@ public SkyscrapersBoard copy() { copy.setCell(x, y, getCell(x, y).copy()); } } - for (SkyscrapersLine line : lines) { - SkyscrapersLine lineCpy = line.copy(); - lineCpy.setModifiable(false); - copy.getLines().add(lineCpy); - } for (PuzzleElement e : modifiedData) { copy.getPuzzleElement(e).setModifiable(false); } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java index f58c277d5..409555b83 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java @@ -1,8 +1,10 @@ package edu.rpi.legup.puzzle.skyscrapers; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import java.awt.*; +import java.awt.event.MouseEvent; import static edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType.convertToSkyType; @@ -23,6 +25,35 @@ public SkyscrapersType getType() { } } + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()){ + case "SKYS-UNPL-0001": + this.data = 0; + break; + case "SKYS-UNPL-0002": + switch (m.getButton()){ + case MouseEvent.BUTTON1: + if (this.data <= 0 || this.data >= this.max) { + this.data = 1; + } + else { + this.data = this.data + 1; + } + break; + case MouseEvent.BUTTON3: + if (this.data > 1) { + this.data = this.data - 1; + } + else { + this.data = this.max; + } + break; + } + break; + } + } + public int getMax() { return max; 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 da6899f75..5f6de7369 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java @@ -22,51 +22,34 @@ public class SkyscrapersCellFactory extends ElementFactory { @Override public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { try { - //SkyscrapersBoard treeTentBoard = (SkyscrapersBoard) board; + if (!node.getNodeName().equalsIgnoreCase("cell")) { + throw new InvalidFileFormatException("Skyscrapers Factory: unknown puzzleElement puzzleElement"); + } + SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; - int width = skyscrapersBoard.getWidth(); - int height = skyscrapersBoard.getHeight(); + int size = skyscrapersBoard.getSize(); NamedNodeMap attributeList = node.getAttributes(); - if (node.getNodeName().equalsIgnoreCase("cell")) { - - int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); - int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); - int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); - if (x >= width || y >= height) { - throw new InvalidFileFormatException("TreeTent Factory: cell location out of bounds"); - } - if (value < 0 || value > width) { - throw new InvalidFileFormatException("TreeTent Factory: cell unknown value"); - } - SkyscrapersCell cell = new SkyscrapersCell(value, new Point(x, y), width); - cell.setIndex(y * height + x); - return cell; + int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); + int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); + int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); + if (x >= size || y >= size) { + throw new InvalidFileFormatException("Skyscrapers Factory: cell location out of bounds"); } - else { - if (node.getNodeName().equalsIgnoreCase("line")) { - int x1 = Integer.valueOf(attributeList.getNamedItem("x1").getNodeValue()); - int y1 = Integer.valueOf(attributeList.getNamedItem("y1").getNodeValue()); - int x2 = Integer.valueOf(attributeList.getNamedItem("x2").getNodeValue()); - int y2 = Integer.valueOf(attributeList.getNamedItem("y2").getNodeValue()); - if (x1 >= width || y1 >= height || x2 >= width || y2 >= height) { - throw new InvalidFileFormatException("TreeTent Factory: line location out of bounds"); - } - - SkyscrapersCell c1 = skyscrapersBoard.getCell(x1, y1); - SkyscrapersCell c2 = skyscrapersBoard.getCell(x2, y2); - return new SkyscrapersLine(c1, c2); - } - else { - throw new InvalidFileFormatException("TreeTent Factory: unknown puzzleElement puzzleElement"); - } + if (value < 0 || value > size) { + throw new InvalidFileFormatException("Skyscrapers Factory: cell unknown value"); } + + SkyscrapersCell cell = new SkyscrapersCell(value, new Point(x, y), size); + cell.setIndex(y * size + x); + + return cell; } catch (NumberFormatException e) { - throw new InvalidFileFormatException("TreeTent Factory: unknown value where integer expected"); + throw new InvalidFileFormatException("Skyscrapers Factory: unknown value where integer expected"); } catch (NullPointerException e) { - throw new InvalidFileFormatException("TreeTent Factory: could not find attribute(s)"); + throw new InvalidFileFormatException("Skyscrapers Factory: could not find attribute(s)"); } } @@ -78,29 +61,15 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat * @return xml PuzzleElement */ public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { - if (puzzleElement instanceof SkyscrapersCell) { - org.w3c.dom.Element cellElement = document.createElement("cell"); + org.w3c.dom.Element cellElement = document.createElement("cell"); - SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; - Point loc = cell.getLocation(); + SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; + Point loc = cell.getLocation(); - cellElement.setAttribute("value", String.valueOf(cell.getData())); - cellElement.setAttribute("x", String.valueOf(loc.x)); - cellElement.setAttribute("y", String.valueOf(loc.y)); + cellElement.setAttribute("value", String.valueOf(cell.getData())); + cellElement.setAttribute("x", String.valueOf(loc.x)); + cellElement.setAttribute("y", String.valueOf(loc.y)); - return cellElement; - } - else { - org.w3c.dom.Element lineElement = document.createElement("line"); - - SkyscrapersLine line = (SkyscrapersLine) puzzleElement; - - lineElement.setAttribute("x1", String.valueOf(line.getC1().getLocation().x)); - lineElement.setAttribute("y1", String.valueOf(line.getC1().getLocation().y)); - lineElement.setAttribute("x2", String.valueOf(line.getC2().getLocation().x)); - lineElement.setAttribute("y2", String.valueOf(line.getC2().getLocation().y)); - - return lineElement; - } + return cellElement; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java index e22e16855..649f85f4e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java @@ -44,17 +44,10 @@ public void drawElement(Graphics2D graphics2D) { SkyscrapersClue clue = getPuzzleElement(); switch (clue.getType()) { case CLUE_NORTH: - value = String.valueOf(clue.getData()); - break; case CLUE_EAST: - value = String.valueOf(clue.getData()); - break; case CLUE_SOUTH: - value = String.valueOf(clue.getData()); - break; case CLUE_WEST: value = String.valueOf(clue.getData()); - //value = SkyscrapersClue.colNumToString(clue.getData() + 1); break; default: value = ""; diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java index 58994dfe8..8e338a351 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java @@ -1,96 +1,14 @@ package edu.rpi.legup.puzzle.skyscrapers; -import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.ElementController; -import edu.rpi.legup.history.AutoCaseRuleCommand; -import edu.rpi.legup.history.EditDataCommand; -import edu.rpi.legup.history.ICommand; -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.ui.boardview.BoardView; -import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.awt.event.MouseEvent; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class SkyscrapersController extends ElementController { - private ElementView lastCellPressed; - private ElementView dragStart; - public SkyscrapersController() { super(); - this.dragStart = null; - 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 mouseReleased(MouseEvent e) { - if (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, (SkyscrapersClueView) dragStart); - if (edit.canExecute()) { - edit.execute(); - getInstance().getHistory().pushChange(edit); - treePanel.updateError(""); - } - else { - treePanel.updateError(edit.getError()); - } - } - } - } - } - dragStart = null; - lastCellPressed = null; - } } @Override @@ -102,7 +20,7 @@ public void changeCell(MouseEvent e, PuzzleElement element) { cell.setData(num); } else { - cell.setData(SkyscrapersType.UNKNOWN.value); + cell.setData(SkyscrapersType.UNKNOWN.toValue()); } } else { 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 765540770..f784fb2d9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.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 SkyscrapersExporter extends PuzzleExporter { @@ -58,15 +57,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { flagsElement.setAttribute("dupe",String.valueOf(board.getDupeFlag())); flagsElement.setAttribute("view",String.valueOf(board.getViewFlag())); boardElement.appendChild(flagsElement); - - if (!board.getLines().isEmpty()) { - org.w3c.dom.Element linesElement = newDocument.createElement("lines"); - for (PuzzleElement data : board.getLines()) { - org.w3c.dom.Element lineElement = puzzle.getFactory().exportCell(newDocument, data); - linesElement.appendChild(lineElement); - } - boardElement.appendChild(linesElement); - } + return boardElement; } } 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 6dded9764..2df8fe88a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersImporter.java @@ -32,7 +32,30 @@ public boolean acceptsTextInput() { */ @Override public void initializeBoard(int rows, int columns) { + //assert(rows == columns); + int size = rows; + SkyscrapersBoard skyscrapersBoard = new SkyscrapersBoard(size); + + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + SkyscrapersCell cell = new SkyscrapersCell(SkyscrapersType.UNKNOWN.toValue(), new Point(x, y), size); + cell.setIndex(y * size + x); + cell.setModifiable(true); + skyscrapersBoard.setCell(x, y, cell); + } + } + + for (int i = 0; i < size; i++) { + skyscrapersBoard.getWestClues().set(/*index - 1*/i, new SkyscrapersClue(0, i, SkyscrapersType.CLUE_WEST)); + skyscrapersBoard.getEastClues().set(/*index - 1*/i, new SkyscrapersClue(0, i, SkyscrapersType.CLUE_EAST)); + } + + for (int i = 0; i < size; i++) { + skyscrapersBoard.getNorthClues().set(/*index - 1*/i, new SkyscrapersClue(0, i, SkyscrapersType.CLUE_NORTH)); + skyscrapersBoard.getSouthClues().set(/*index - 1*/i, new SkyscrapersClue(0, i, SkyscrapersType.CLUE_SOUTH)); + } + puzzle.setCurrentBoard(skyscrapersBoard); } /** @@ -48,12 +71,6 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Skyscrapers Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; - if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("Skyscrapers Importer: no puzzleElement found for board"); - } - Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); - NodeList elementDataList = dataElement.getElementsByTagName("cell"); - SkyscrapersBoard skyscrapersBoard = null; @@ -66,8 +83,13 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { throw new InvalidFileFormatException("Skyscraper Importer: invalid board dimensions"); } - int size = skyscrapersBoard.getSize(); + if (boardElement.getElementsByTagName("cells").getLength() == 0) { + throw new InvalidFileFormatException("Skyscrapers Importer: no puzzleElement found for board"); + } + Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("cell"); + int size = skyscrapersBoard.getSize(); for (int i = 0; i < elementDataList.getLength(); i++) { SkyscrapersCell cell = (SkyscrapersCell) puzzle.getFactory().importCell(elementDataList.item(i), skyscrapersBoard); @@ -79,6 +101,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { skyscrapersBoard.setCell(loc.x, loc.y, cell); } + for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { if (skyscrapersBoard.getCell(x, y) == null) { @@ -117,7 +140,6 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int i = 0; i < eastClues.getLength(); i++) { Element clue = (Element) eastClues.item(i); int value = Integer.valueOf(clue.getAttribute("value")); - //int index = SkyscrapersClue.colStringToColNum(clue.getAttribute("index")); int index = Integer.valueOf(clue.getAttribute("index")); skyscrapersBoard.getWestClues().set(/*index - 1*/i, new SkyscrapersClue(index, i, SkyscrapersType.CLUE_WEST)); @@ -134,15 +156,6 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { skyscrapersBoard.getSouthClues().set(/*index - 1*/i, new SkyscrapersClue(value, i, SkyscrapersType.CLUE_SOUTH)); } - if (boardElement.getElementsByTagName("lines").getLength() == 1) { - Element linesElement = (Element) boardElement.getElementsByTagName("lines").item(0); - NodeList linesList = linesElement.getElementsByTagName("line"); - for (int i = 0; i < linesList.getLength(); i++) { - skyscrapersBoard.getLines().add((SkyscrapersLine) puzzle.getFactory().importCell(linesList.item(i), skyscrapersBoard)); - } - } - - //Initialize present flags NodeList flagList = boardElement.getElementsByTagName("flags"); if (flagList.getLength() == 1) { Element flags = (Element) flagList.item(0); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLine.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLine.java deleted file mode 100644 index 4ebae3de9..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLine.java +++ /dev/null @@ -1,42 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers; - -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.utility.Entry; - -public class SkyscrapersLine extends PuzzleElement> { - - public SkyscrapersLine(SkyscrapersCell c1, SkyscrapersCell c2) { - this.data = new Entry<>(c1, c2); - } - - public SkyscrapersCell getC1() { - return data.getKey(); - } - - public void setC1(SkyscrapersCell c1) { - this.data.setKey(c1); - } - - public SkyscrapersCell getC2() { - return data.getValue(); - } - - public void setC2(SkyscrapersCell c2) { - this.data.setValue(c2); - } - - public boolean compare(SkyscrapersLine line) { - return ((line.getC1().getLocation().equals(data.getKey().getLocation()) && line.getC2().getLocation().equals(data.getValue().getLocation())) || - (line.getC1().getLocation().equals(data.getValue().getLocation()) && line.getC2().getLocation().equals(data.getKey().getLocation()))); - } - - /** - * Copies this elements puzzleElement to a new PuzzleElement object - * - * @return copied PuzzleElement object - */ - @Override - public SkyscrapersLine copy() { - return new SkyscrapersLine(data.getKey().copy(), data.getValue().copy()); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java deleted file mode 100644 index 02daa7a11..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersLineView.java +++ /dev/null @@ -1,31 +0,0 @@ -package edu.rpi.legup.puzzle.skyscrapers; - -import edu.rpi.legup.ui.boardview.ElementView; - -import java.awt.*; - -public class SkyscrapersLineView extends ElementView { - private final Color LINE_COLOR = Color.GREEN; - - private final Stroke LINE_STROKE = new BasicStroke(2); - - public SkyscrapersLineView(SkyscrapersLine line) { - super(line); - } - - @Override - public void draw(Graphics2D graphics2D) { - - SkyscrapersLine line = (SkyscrapersLine) puzzleElement; - Point p1 = line.getC1().getLocation(); - Point p2 = line.getC2().getLocation(); - int x1 = (p1.x + 1) * size.width + size.width / 2; - int y1 = (p1.y + 1) * size.height + size.height / 2; - - int x2 = (p2.x + 1) * size.width + size.width / 2; - int y2 = (p2.y + 1) * size.height + size.height / 2; - graphics2D.setColor(line.isModified() ? Color.GREEN : Color.WHITE); - graphics2D.setStroke(LINE_STROKE); - graphics2D.drawLine(x1, y1, x2, y2); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java index 0b53a9472..f85d9a40e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersType.java @@ -9,6 +9,10 @@ public enum SkyscrapersType { this.value = value; } + public int toValue() { + return value; + } + public static SkyscrapersType convertToSkyType(int num) { switch (num) { case 0: diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java index b12867eda..a04b32889 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersView.java @@ -9,15 +9,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import javax.imageio.ImageIO; import java.awt.*; -import java.io.IOException; import java.util.ArrayList; public class SkyscrapersView extends GridBoardView { private final static Logger LOGGER = LogManager.getLogger(SkyscrapersView.class.getName()); - private ArrayList lineViews; private ArrayList northClues; private ArrayList eastClues; private ArrayList southClues; @@ -26,8 +23,6 @@ public class SkyscrapersView extends GridBoardView { public SkyscrapersView(SkyscrapersBoard board) { super(new BoardController(), new SkyscrapersController(), board.getDimension()); - this.lineViews = new ArrayList<>(); - this.northClues = new ArrayList<>(); this.eastClues = new ArrayList<>(); this.southClues = new ArrayList<>(); @@ -43,12 +38,6 @@ public SkyscrapersView(SkyscrapersBoard board) { elementViews.add(elementView); } - for (SkyscrapersLine line : board.getLines()) { - SkyscrapersLineView lineView = new SkyscrapersLineView(line); - lineView.setSize(elementSize); - lineViews.add(lineView); - } - for (int i = 0; i < gridSize.height; i++) { SkyscrapersClueView row = new SkyscrapersClueView(board.getWestClues().get(i)); row.setLocation(new Point(0, (i + 1) * elementSize.height)); @@ -189,10 +178,6 @@ protected void setCasePickable() { public void drawBoard(Graphics2D graphics2D) { super.drawBoard(graphics2D); - for (SkyscrapersLineView view : lineViews) { - view.draw(graphics2D); - } - for (SkyscrapersClueView clueView : northClues) { clueView.draw(graphics2D); } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java new file mode 100644 index 000000000..8505de523 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java @@ -0,0 +1,10 @@ +package edu.rpi.legup.puzzle.skyscrapers.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class ClueTile extends NonPlaceableElement { + + public ClueTile() { + super("SKYS-UNPL-0003", "Clue Tile", "Clue Updater", "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java new file mode 100644 index 000000000..8f78fb1cf --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.skyscrapers.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class NumberTile extends NonPlaceableElement { + public NumberTile() { + super("SKYS-UNPL-0002", "Number Tile", "A numbered tile", "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java new file mode 100644 index 000000000..3559dc332 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.skyscrapers.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class UnknownTile extends NonPlaceableElement { + public UnknownTile() { + super("SKYS-UNPL-0001", "Unknown", "A blank tile", "edu/rpi/legup/images/skyscrapers/tiles/UnknownTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt new file mode 100644 index 000000000..604e1824e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt @@ -0,0 +1,3 @@ +SKYS-UNPL-0001: Unknown Tile +SKYS-UNPL-0002: Number Tile +SKYS-UNPL-0003: Clue "Tile" \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png new file mode 100644 index 0000000000000000000000000000000000000000..7a86827f96c840aca4175288e997c49a205ee7cd GIT binary patch literal 353 zcmV-n0iOPeP)Px$8%ab#R9HvtmOrjVKop06EWn@A7DT1eDCqQhg~~3hfW#6+6k-D^6)I}2*Z{q_ zFC%%)%gak<++44iQ_r09&6jgdMgZ`_gCP)recvBJkYyPhfE(ZjxB+SdbzLLRa}2|P z<2Yd3Hj1KvBuUiEjA;@1zK^P^Ad2EuFp46SWr<~3pePCz1QWn-6-Xk>vd}cma{z)M zVB5Cm05na*JkOs8h~pS(nj%ROgkktufN7fGI1UWMK-YCt#mV7YO*oDtT-SxJ>(@xK z1T#S2_wYOqrfH&WTbA0_9VUQvT`%Q#Uj9wY1fZ%a&hx}OzH!t5q|3=F4uSw#mNBbm zHb98~3R)}F25vw3M*C;92(%$B$j*UfgJ*;N7<4klmjq@7`jNmS4uuQ?ae8jr zQ%Yu)MZVp#9TYgf8*hX(%{CU^9J!;+j0A#MNvKa$Jt+njdWBUWjaiz zWF)szlOj%Q_{4l}jLmFyB&I)FG~53;!hrZZEkL$Czq;uQtVmlB%SPO$w|oiQdrz=U z#_+W<87!wdPFDT+xnzIQ+X`==boP_4ix-N5?teH9&*aqxo4?%C6pgASKeUZ2+3BwTuCnh&U9y{~cr{+wr+PhiQw1i)A`kRWnMuo}kS3dKUx1Pvs z+jYeNqkUclcRHmgqe-o((th%VCnur6L~?n!<74B5xFnLBFpM}O_lY?g<*0aFgQ5n$ ztU+qL8mTCpI_mp?G0+mPo@09-bSYVx>!ztVlH?tRQzH-YC71JTw$u`85t7w9`&1}$ zLV%cYr}?eqF8h<$VR6%!zwsxBTwlC4f7m!$wgpLj$gQ#E6_0HAgXsu4+5Rftm(M^` zb9ZXQ+BZvr# zz6Mt1Dw9>;B2vV(;j$@!%fGoAce8kh*a27nlBfO(Cj>orwEy|>NlRdub1;7b73%CLB?4~x%*w1=ytE@?bobt?62fI7@wAl3qRUwq+r+~OEB+7 zK4d%^xSRxTJZE|L73rd3=Qid}(~BOl>QxcBO6TqJ(oWsT`6_q4)4}%rNjvYm4~Cty z^PzV;5`5mWUb`+}S(C+A!tcbty!TbOlS8mnwl&n;Rz zdq7z5XUQN&WuCCGN|L=r(pNUDEiY05)+W$_hPBm4(o?;Zru3A{CYptndp3`2D3^)9 zQ?ZyeojIC1Dm|uR!P4UZ7MbBe4Y<~nhXEF>sTq!YZkO=*PUW(rIg_cQ%k32>V?Mf- zs4EMN?MnSP+;$>H&vnoq_O|s>t=0EU`Sigy{HS_rRia5$jy@r+Z{z2s{^Q28%$FJ_Nc zjLh?FJG(CFsMbn$P|h=$q?+MW@Gcr_xr)uGkBi z4TM;S;9-#?a?Z_INN<|bV6^PCjK8&XN+NW?8PaJXmG5u#?a;RL20WQN6cRJZy-D4y zE+3*iUAy-Uu0j&DO>J8J^vWsTjBO_tZD?a#9t6`o&V?2>wO>kq)q@!A&6uz|ZQw3h zN6dqL@hx)>a^g!&5w01?)Zb_9;#_w1NhkSkXQ%OS1KCY#yqfmiGUjNx>8&vR1(Dpj zW5vtKZb4X3oTD}MNN{oe6mK>G37T1`?na4ngVn~Jmul=6a$-i5%)n2IknqNq7VTk% zL&HGW_neinPw3f~3vu;h-&IB5W+v81<_nDulohwzzK9GCBJ(BI=WJaLj9&47=XS9c z7Li!f&*bDr`?V$c2gc+c`P}Woh0Zh?nfY$Jc?I8n@wQGE#9B^|%ew%6RxX=+uuCsA zSBy)7X>fbPavS=1u#Q-Dg}}?0`Ufk2j`o#4A`K`drZ4KYKzGEoNX(Qn2B zdF%JYh>1$<{qkJ-D$mZS>p7UKf?wq^ysn{zd&8e&y(H7ZLZW(W1uVj@6(I^Xrq_sc z^UjCYzDxFlRLHf23-?d@Nx6tBgum_={uHpNy_;J<%2Pa4Ok6Jar0jN)x{ZRk+ytJ8 zGX;jH63j2lQ}&ddJv^w42(wU}vXYhLH8RyiiKF=5^&bJ?_9 zV{7Eq16~LxL2Bq-!=-G+wwO_0mUx-O+i04h+ulCoGne|@VX|_@{Dko71|0qz+y|DI zx}18bIj|W_7D2|=dq@@WIq>^}&&No)#}*weeq1zCWZ3OL;k-@!$|>aIz=2%<-7Z{( z5^PDfG&&-^=GptaPfna&!Qia;*7}tGt)E;MxxVRyWPvT8Z`TofB+wWUAF*?eK~=yge83oQ zt!*u*nxgvNPUK+JxS?hKd8_J6^R}Ku5)n$OB{7LU#MURLpNpkwKZ`nVu6`*q`)y8T zitTx3!uu!GM4yDM=h741AX9&~CTO0gCs#vP4xcD|mvc6^$Tr8YKLKKH?FM&%J5PrW zDAkWCbtuj6bJ%w(CBuQ?CU~gbUdY<%63g`30m>mqqgVTM4Fhk@7!6UR(oU6{mzYOB zbxc+}c>DnPq+}cj38bM^Z<{%LwlP?`7}L%snXXq=ZD_*+EMtd&FuV#x6h;IZq6po`poi+xNq>;Q1~z;e2?MnoSWNQi+cUK zqltqaNu&;6l(z+P9<2fU2n)gIQY5G)FD`ctCSFUXT&IMOZ9r~8x|MR5YLxapn|tOs ztTlY^&nMDZ(jTB0=wT;02gaYG4!w7B9e5q6#p8FsdQl0_?$L^~O4@G6l)myrHfJNk zWb@+G?|M1gzn;5$h1&PZ@wMQ@{_bTmop{mXct;51T8m9f`^h}QrtwV{QJ5%klQKfa z`#q1vRkT#{Z7W}F_T1=smwD-PL~Pc+k|ubHOksXuaF=$ME_&C!JG7Cv;%v#&E@n2A1niqQ(3+E=M6~9D&J-CFO z?phdGV)NE;RdJPb%WJ%FYH;op7vMg|&F0k;Y8Q0+W0n9@)=|B$yhw?5Gz~GOt16+i zXT3(#VO>QfMf^2c6yD!>oK*ln)dsRZ*zKJx& z--(*bm~1X|Kj6PjXDB0vCn{nlq7fHW=Ne;cw%;t-?1c-OrnNvSVKb;RF6Qn}`;#I& zqB}ZKE>12zYFQ$hkzI*}=du#@Qt@Z4li(#iw7HKjk-_>Q1m&a$h*Dbvsbz~f!GOs0 z$V-l=9km?krM0C8hD}F4F}^b9nD>VE4+ah&E9bj&@=pFPH|TM~_fe;mI{_cE1ExQe zb@>!%y69Iu^r=mVkBC>>dVD7gs;bGURP(U4&tdeRd{q4GaC&;|j#G<9waydwI%r1J zA=+dgxtKb2_dxx`fR9qXQnHeQxno{e6?`dwJUwz!@bywtLDQy*nu&Ll_e)<;MiLlf zGZiz%u3Y6k>Z4NiQn%QTKUO(W939J}icIc%UVd7Aoq21x_m89cb}9$^3jJ?V-d?#) ze_i!pd1Bd+KST6($IhHwr5cmsib*4O?~QT-n&av9b?QMfJAx{gd*I zlgj1{nm$R$Qclm$zhrLGl-5tif9@zwC7`RAD}|-?SMv&!JDnKYw#mpJpFj3yfxUk* zb5yTU=haMjr|43TDz$#n>L_i=!o=pWsi)n@JGDOBx32T9RDHP>&f@K!u{30f=hGI6 z7U?WkofI~0KDgn_m~f0tLm8o5!6mMOb3^6+m6daSdFm-xO}B}FF)X{j|LNF`BlZVe z2`(S*(~ebEwqCzAsdD3D&)1tvularXF!b);Q8^b3=g1Sv=p)O8^c4D+H)SD5Urj#T z)im|ykJDS^A2e0%3e=qIZCY|%7*X)$M29$hGaa`cBIG@u)Bmvi2IGNckUNguo-!Lv zdgRrq+6GYwMxiTaRZ=2Zxl6+K;&{R6aj8T}yAN zo_mICiK`y0c3G^PO@4Hv_7>EQ-OBe&FC=lE98$P=^U-U?DcPx@K9h=zsjc5bm#yZk zrwCmt)dxgn+NGtx^KMA}{7rkCXghEQl%)V?JqJq*41rG7z!T{nBn=ML4>${gK)dxh zet3cpi3Ro`c~WRt$W(bX1WX}fA@*99FiSr}k{87^h(WRq+HXe)@*(IFA^LhkyEzyD zfJ$QF!5peDjfvr4A#1o8;C@xC2?4LEuzau(2TLokA)P@2qcl(&FsKoSas&a<69VsM z5Xl%DobgWx;0X)yVzK-%nwo4jTZ4_%pffx*;kvrInlOYW0s#dypv*uT3(tYln2M_q zKQM44CV@fmV^Qcd@G2(WgC4-bLLk66_^Q^Z*8dWORf?V=4X$K_vWb?-#)ET?>au&?NbisDLUH z@CyIUrHPrP)!!DY6nIjoerr|$*}rMBDCB?0`fYBjBWvOOIuXG9Z`|Ls|B8K08PKw{ z#Ng{jKp|q*Zs9065rIaLpl~>_6etvo4D}%CAfRXhSyu-JC+O&s z(7!;L(U>eejX+w30>CvW01gqaO(1C@&``8C$^(k>KxskoS~^Im4iT;Gfh6ea=)!b= zfjGdR09lFm{dHEWP(%QV;6c!VBVhz6jD$i#Q3RMa)B^_Kp-~93mbR9*E(s4`gCY_z z#&iZ14=g8ziuWXG`q4bs2385j?6ES#LJ%6Te;mi-oLC3cPB$mf+ysKbFOm!UQw|S9AVn*4vW&e?0vt0$|4gaBt8YOuawub>;H`|p?{7$BpUDwhz%TOP=6Tg{XaVG zrZMZC_VrHtdZ&H8)4twmU+=W9ciPuG?dzTP^-lY5opvR*bO6v!=kwTSf&;CrzOyT@ zBmxpaKT}602qe6D_2L4hWyk_TewLY~5&tm1w1f(eU_#WU)keG-ZjYTq?PM2R90U`o zHORBK8rF|IC|nzhtEmnOH7OP@Do>u?vF)j#7}y{b#Kp}c@Z;u1)ri6_TVDOep)ugE 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 19e63a2a3..ccd4f5be3 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -38,6 +38,6 @@ + fileCreationDisabled="false"/> From 9fa0e0205a7f744b9597135202290e1363339593 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:45:28 -0500 Subject: [PATCH 04/89] Imported the black and star tiles ID probably needs to be changed in the future, and more assets on the way. --- .../puzzle/starbattle/elements/BlackTile.java | 9 +++++++++ .../puzzle/starbattle/elements/StarTile.java | 9 +++++++++ .../edu/rpi/legup/images/starbattle/black.gif | Bin 0 -> 856 bytes .../edu/rpi/legup/images/starbattle/star.gif | Bin 0 -> 545 bytes 4 files changed, 18 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/black.gif create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/star.gif diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java new file mode 100644 index 000000000..fd74774a7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class BlackTile extends NonPlaceableElement { + public BlackTile() { + super("STBL-PLAC-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java new file mode 100644 index 000000000..2510869eb --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class StarTile extends NonPlaceableElement { + public StarTile() { + super("LTUP-PLAC-0001", "Star Tile", "The star tile", "edu/rpi/legup/images/starbattle/star.gif"); + } +} diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/black.gif b/src/main/resources/edu/rpi/legup/images/starbattle/black.gif new file mode 100644 index 0000000000000000000000000000000000000000..13381a7179c2af7ad23ef064b743d3e479b56070 GIT binary patch literal 856 zcmZ?wbhEHbRA5kG_|Cv^;=~DtQ7{?;BQ*pRf3kqRt^*=Ld4hq%l;J<8jK_ur2b(#B zwPH?eSa`TyK-p`K$HqlRyCsaX?wr`T_;|m9bC-Px$+et)0R7gwB)k~;OQ4|O8-%S#Q6f(WuN(@LbxC6?>z|bQh@)#H)BSjf;3uWXn zP!c6GWTuoTBMJi~86e~tQXYG0-#TsIx!*bRl~udbx7OP0zy9m7zdk*dz8=T--vKPZ zNZe}5ye)&{8DLYd0Dk7cjT39^h^+_)Av2;~qv~I+oyO zH;_68qf-gSS5A~<6}DyFAzZ*gyu-&-wh#DP0#*UeVHPIj6985Df-zYUfr3d@-r_ah z<1to;v1D)nd-JVpIkQjs`y%Q0XIp&7Vq88Gu{7JF`N8ys&8EnBIreozzwXyvjSvlDruB;5x!$Xnyj1X!07h$Pn6jcYf6SBM5q!d&djqZoq_nZLnHO!*stxbz$k z^O6OVs+_<8W@9oUnLlJW2AgrJxN#Uiu)EO55S(udFjW7z7Y*E=w)sI0G84zJJpYbF zwzLI!nx2AqGGew**VH(+^=R_>j2PR-CF)OWzawq%uDa$G`gogyQMoI%|H<=lr(y6R jw|ThRX3<*4@9h2yt8`HryKo;Y00000NkvXXu0mjflb`cS literal 0 HcmV?d00001 From 17d26d73444b353a1ea31abde928c754020f754d Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:53:11 -0500 Subject: [PATCH 05/89] Added UnknownTile Also fixed the descriptions of the black and star tile, not sure if we need another asset. --- .../puzzle/starbattle/elements/BlackTile.java | 2 +- .../puzzle/starbattle/elements/StarTile.java | 2 +- .../puzzle/starbattle/elements/UnknownTile.java | 9 +++++++++ .../edu/rpi/legup/images/starbattle/empty.gif | Bin 0 -> 857 bytes 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/empty.gif diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java index fd74774a7..2601bd351 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java @@ -4,6 +4,6 @@ public class BlackTile extends NonPlaceableElement { public BlackTile() { - super("STBL-PLAC-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); + super("STBL-PLAC-0002", "Black Tile", "The black tile that shows where you cannot place a star", "edu/rpi/legup/images/lightup/black.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java index 2510869eb..19ba7baed 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -4,6 +4,6 @@ public class StarTile extends NonPlaceableElement { public StarTile() { - super("LTUP-PLAC-0001", "Star Tile", "The star tile", "edu/rpi/legup/images/starbattle/star.gif"); + super("LTUP-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java new file mode 100644 index 000000000..192a95357 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class UnknownTile extends NonPlaceableElement { + public UnknownTile() { + super("LTUP-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + } +} diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif b/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif new file mode 100644 index 0000000000000000000000000000000000000000..38b91d0a2b9c6599eb19d14466db047a86d2ea97 GIT binary patch literal 857 zcmZ?wbhEHbRA5kG_|Cxa|Nno6Q7{?;BQ*qcKpqF>1qKc~21X7Uj|~eBHggDT#hlo% z@Nm0;vez7sjf;+UOBiR}IkEBaF$M+Z0v^pz$tNbN+pdZ^xoPR?=?2NC=6GICbzrat E0QgHHq5uE@ literal 0 HcmV?d00001 From 8955343d8bbdac96fa80cb31480b1784a58c1db5 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 13 Feb 2024 16:32:48 -0500 Subject: [PATCH 06/89] Create StarBattleCellType.java Created StarBattleCellType.java file --- .../legup/puzzle/starbattle/StarBattleCellType.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java new file mode 100644 index 000000000..0548a6b38 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -0,0 +1,12 @@ +//StarBattleCellType.java +package edu.rpi.legup.puzzle.starbattle; + +public enum StarBattleType { + UNKNOWN(-3), STAR(-2), BLACK(-1); + + public int value; + + StarBattleCell(int value) { + this.value = value; + } +} \ No newline at end of file From 1ad2347247d53afe83646401416f5ee4e3ecd735 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 13 Feb 2024 16:37:59 -0500 Subject: [PATCH 07/89] Update StarBattleCellType.java Fixed StarBattleCellType typos --- .../edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java index 0548a6b38..8fb3338f7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -1,12 +1,12 @@ //StarBattleCellType.java package edu.rpi.legup.puzzle.starbattle; -public enum StarBattleType { +public enum StarBattleCellType { UNKNOWN(-3), STAR(-2), BLACK(-1); public int value; - StarBattleCell(int value) { + StarBattleCellType(int value) { this.value = value; } } \ No newline at end of file From 12b7a4358c042c863ea0a03792cd790207244d18 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:41:30 -0500 Subject: [PATCH 08/89] Added StarBattle.java Also changed the IDs for our assets --- .../legup/puzzle/starbattle/StarBattle.java | 33 +++++++++++++++++++ .../puzzle/starbattle/StarBattleCell.java | 0 .../puzzle/starbattle/elements/StarTile.java | 2 +- .../starbattle/elements/UnknownTile.java | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java new file mode 100644 index 000000000..e9e18b92a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class StarBattle extends Puzzle { + public StarBattle() { + super(); + //this.name = "StarBattle"; + + //this.importer = new StarBattleImporter(this); + //this.exporter = new StarBattleExporter(this); + + //this.factory = new StarBattleCellFactory(); + } + + @Override + public void initializeView() { + } + + @Override + public Board generatePuzzle(int difficulty) { + return null; + } + + @Override + public boolean isBoardComplete(Board board) { + return true; + } + + @Override + public void onBoardChange(Board board) { + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java index 19ba7baed..d42cc0010 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -4,6 +4,6 @@ public class StarTile extends NonPlaceableElement { public StarTile() { - super("LTUP-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); + super("STBL-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java index 192a95357..3e1cbca26 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -4,6 +4,6 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("LTUP-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + super("STBL-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); } } From 24450fd99b358f5dfb06d76933c21ad49e13d0bd Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:31:13 -0500 Subject: [PATCH 09/89] Added White Tile and finished starter code for Cell Only Board needs to be added to get the basic puzzle running. --- .../puzzle/starbattle/StarBattleCell.java | 85 ++++++++++++++++++ .../puzzle/starbattle/StarBattleCellType.java | 2 +- .../starbattle/elements/UnknownTile.java | 2 +- .../puzzle/starbattle/elements/WhiteTile.java | 10 +++ .../edu/rpi/legup/images/starbattle/white.gif | Bin 0 -> 9700 bytes 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/white.gif diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index e69de29bb..22d74dd7f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -0,0 +1,85 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.elements.Element; +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.*; +import java.awt.event.MouseEvent; + +public class StarBattleCell extends GridCell { + private int groupIndex; + private int max; + + /** + * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement + * + * @param valueInt value of the star battle cell denoting its state + * @param location location of the cell on the board + * @param size size of the star battle cell + */ + public StarBattleCell(int value, Point location, int groupIndex, int size) { + super(value, location); + this.groupIndex = groupIndex; + this.max = size; + } + + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()) { + case "STBL-PLAC-0001": + this.data = -3; + break; + case "STBL-PLAC-0002": + this.data = -2; + break; + case "STBL-PLAC-0003": + this.data = -1; + break; + + case "STBL-UNPL-0001"://Not sure how button events work + switch (m.getButton()){ + case MouseEvent.BUTTON1: + if (this.data > 0 || this.data < -3) { + this.data = -3; + } + else { + this.data = this.data + 1; + } + break; + case MouseEvent.BUTTON3: + if (this.data > -4) { + this.data = this.data - 1; + } + else { + this.data = -1;//Unsure + } + break; + } + break; + } + } + + public StarBattleCellType getType() { + switch (data) { + case -3: + return StarBattleCellType.UNKNOWN; + case -2: + return StarBattleCellType.STAR; + case -1: + return StarBattleCellType.BLACK; + default: + if (data >= 0) { + return StarBattleCellType.UNKNOWN; + } + } + return null; + } + + public StarBattleCell copy() { + StarBattleCell copy = new StarBattleCell(data, (Point) location.clone(), groupIndex, max); + copy.setIndex(index); + copy.setModifiable(isModifiable); + copy.setGiven(isGiven); + return copy; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java index 8fb3338f7..f3524034e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -2,7 +2,7 @@ package edu.rpi.legup.puzzle.starbattle; public enum StarBattleCellType { - UNKNOWN(-3), STAR(-2), BLACK(-1); + WHITE(-3), STAR(-2), BLACK(-1), UNKNOWN(0); public int value; diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java index 3e1cbca26..c2459f642 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -4,6 +4,6 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("STBL-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + super("STBL-UNPL-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java new file mode 100644 index 000000000..a064c1fad --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java @@ -0,0 +1,10 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class WhiteTile extends PlaceableElement { + public WhiteTile() { + super("STBL-PLAC-0001", "White Tile", "The white tile", "edu/rpi/legup/images/starbattle/white.gif"); + } +} + diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/white.gif b/src/main/resources/edu/rpi/legup/images/starbattle/white.gif new file mode 100644 index 0000000000000000000000000000000000000000..fc2c683eb7860f47d4322fe0e73b0508099a89de GIT binary patch literal 9700 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 literal 0 HcmV?d00001 From c64481dbd4f964927f53fa0956c67eb3c83057c6 Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 16 Feb 2024 13:36:31 -0500 Subject: [PATCH 10/89] create StarBattleBoard.java --- .../puzzle/starbattle/StarBattleBoard.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java new file mode 100644 index 000000000..300695111 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -0,0 +1,21 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +public class StarBattleBoard extends GridBoard { + public StarBattleBoard(int width, int height) { + super(width, height); + } + + public StarBattleBoard(int size) { + super(size, size); + } + + @Override + public StarBattleCell getCell(int x, int y) { + return (StarBattleCell) super.getCell(x,y); + } +} + + From f3b23833821404742f88b8dd073e01b5fe25ab0d Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:36:02 -0500 Subject: [PATCH 11/89] Added getRow, getCol, and potential group implementation for later. All StarBattle boards should be square, so I deleted the width height constructor. I also added size for returning a row, col, for future rules. --- .../puzzle/starbattle/StarBattleBoard.java | 34 +++++++++++++++++-- .../rpi/legup/puzzle/starbattle/allfiles.txt | 10 +++--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index 300695111..a32197fa7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -1,21 +1,49 @@ package edu.rpi.legup.puzzle.starbattle; +import java.util.*; + import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; public class StarBattleBoard extends GridBoard { - public StarBattleBoard(int width, int height) { - super(width, height); - } + + private int size; + //private ArrayList groupSizes; public StarBattleBoard(int size) { super(size, size); + this.size = size; } @Override public StarBattleCell getCell(int x, int y) { return (StarBattleCell) super.getCell(x,y); } + + /* + public StarBattleCell getCell(int groupIndex, int x, int y) { + return getCell(x + (groupIndex % groupSize) * groupSize, y + (groupIndex / groupSize) * groupSize); + }*/ + + public int getSize() { + return size; + } + + public Set getRow(int rowNum) { + Set row = new HashSet<>(); + for (int i = 0; i < size; i++) { + row.add(getCell(i, rowNum)); + } + return row; + } + + public Set getCol(int colNum) { + Set column = new HashSet<>(); + for (int i = 0; i < size; i++) { + column.add(getCell(colNum, i)); + } + return column; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt index 7f006191a..1b1824f21 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -101,14 +101,14 @@ public class StarBattleCell extends GridCell { public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { case "SBUP-PLAC-0001": - this.data = -4; + this.data = -3; break; - case "SBUP-UNPL-0002": - this.data = -1; - break; - case "SBUP-UNPL-0003": + case "SBUP-PLAC-0002": this.data = -2; break; + case "SBUP-PLAC-0003": + this.data = -1; + break; case "SBUP-UNPL-0001"://Not sure how button events work switch (m.getButton()){ case MouseEvent.BUTTON1: From 2190f7ea792182699139b3626db6264778111f6b Mon Sep 17 00:00:00 2001 From: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:39:06 -0500 Subject: [PATCH 12/89] Java Autoformatter (#728) * Create java-autoformat.yml * Setup Java * Run spotless on repository * Adding spotless dependency during build * Adding spotless dependency during build * Give permissions to run spotless * Syntax * Adding Debug Tag * Check for modified files Also removed debug tag * Automated Java code formatting changes * Test command syntax * Correctly Identify modified files * Test autoformatter * Test autoformatter * Test autoformatter * Debugging * Debugging * Change method for detecting changed files * Try building before calling spotless * Update java-autoformat.yml * Update java-autoformat.yml * Update java-autoformat.yml * Purposely bad formatting Purposely adding some bad formatting to see if the auto-formatter triggers * Update build.gradle Disabling Checkstyle in build so the auto-formatter can trigger * Update java-autoformat.yml Have auto-formatter trigger when more commits are added to a pull request * Debugging * Adding more awful formatting * Update java-autoformat.yml * Changing repo URL * Going back to checkout v1 * Trying URL change * Trying out using env * Trying this now...? * Introducing more horrible changes * Spotless formatting using google format v1.19.2 * Manual formatting fix * Automated Java code formatting changes * Different format type Trying to get it to pass checkstyle * Disable checkstyle temporarily * Automated Java code formatting changes * Default google formatting style * Automated Java code formatting changes * Comments and reordering styles * Adding extra newlines * Automated Java code formatting changes * Changing tabs from 2 to 4 spaces Supposedly the only difference that aosp makes is the 2 spaces? Hopefully it doesn't break anything else * Remove solo } requirement and reactivate checkstyle * Automated Java code formatting changes * Update checkstyle.xml Removed problematic LeftCurly and RightCurly requirements * Changing back to tabWidth * Add newline to test formatter + build * Automated Java code formatting changes * Trying some ChatGPT stuff * Getting rid of problematic experimentation --------- Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 --- .github/workflows/java-autoformat.yml | 38 ++ build.gradle | 30 + config/checkstyle/checkstyle.xml | 6 - gradlew | 0 src/main/java/edu/rpi/legup/Legup.java | 2 +- src/main/java/edu/rpi/legup/ai/Solver.java | 3 +- src/main/java/edu/rpi/legup/app/Config.java | 22 +- .../edu/rpi/legup/app/GameBoardFacade.java | 179 +++--- .../edu/rpi/legup/app/LegupPreferences.java | 60 +- .../rpi/legup/app/PuzzleKeyAccelerator.java | 58 +- .../rpi/legup/controller/BoardController.java | 29 +- .../edu/rpi/legup/controller/Controller.java | 33 +- .../legup/controller/CursorController.java | 57 +- .../controller/EditorElementController.java | 21 +- .../legup/controller/ElementController.java | 110 ++-- .../rpi/legup/controller/RuleController.java | 61 +- .../legup/controller/ToolbarController.java | 12 +- .../rpi/legup/controller/TreeController.java | 53 +- .../legup/history/AddTreeElementCommand.java | 29 +- .../ApplyDefaultDirectRuleCommand.java | 257 ++++---- .../legup/history/AutoCaseRuleCommand.java | 36 +- .../edu/rpi/legup/history/CommandError.java | 1 - .../edu/rpi/legup/history/CommandState.java | 5 +- .../history/DeleteTreeElementCommand.java | 31 +- .../rpi/legup/history/EditDataCommand.java | 43 +- .../java/edu/rpi/legup/history/History.java | 35 +- .../java/edu/rpi/legup/history/ICommand.java | 17 +- .../rpi/legup/history/IHistoryListener.java | 8 +- .../InvalidCommandStateTransition.java | 11 +- .../edu/rpi/legup/history/MergeCommand.java | 24 +- .../edu/rpi/legup/history/PuzzleCommand.java | 53 +- .../history/ValidateCaseRuleCommand.java | 26 +- .../ValidateContradictionRuleCommand.java | 52 +- .../history/ValidateDirectRuleCommand.java | 294 +++++---- src/main/java/edu/rpi/legup/model/Puzzle.java | 90 ++- .../edu/rpi/legup/model/PuzzleExporter.java | 25 +- .../edu/rpi/legup/model/PuzzleImporter.java | 130 ++-- .../edu/rpi/legup/model/RegisterPuzzle.java | 4 +- .../edu/rpi/legup/model/elements/Element.java | 9 +- .../rpi/legup/model/elements/ElementType.java | 3 +- .../model/elements/NonPlaceableElement.java | 3 +- .../model/elements/PlaceableElement.java | 3 +- .../legup/model/elements/RegisterElement.java | 4 +- .../edu/rpi/legup/model/gameboard/Board.java | 24 +- .../rpi/legup/model/gameboard/CaseBoard.java | 1 - .../legup/model/gameboard/ElementFactory.java | 14 +- .../rpi/legup/model/gameboard/GridBoard.java | 73 +-- .../rpi/legup/model/gameboard/GridCell.java | 7 +- .../legup/model/gameboard/PuzzleElement.java | 13 +- .../legup/model/observer/ITreeListener.java | 4 +- .../edu/rpi/legup/model/rules/CaseRule.java | 66 +- .../legup/model/rules/ContradictionRule.java | 47 +- .../edu/rpi/legup/model/rules/DirectRule.java | 210 +++---- .../edu/rpi/legup/model/rules/MergeRule.java | 42 +- .../rpi/legup/model/rules/RegisterRule.java | 4 +- .../java/edu/rpi/legup/model/rules/Rule.java | 62 +- .../edu/rpi/legup/model/rules/RuleType.java | 5 +- .../java/edu/rpi/legup/model/tree/Tree.java | 46 +- .../edu/rpi/legup/model/tree/TreeElement.java | 11 +- .../rpi/legup/model/tree/TreeElementType.java | 3 +- .../edu/rpi/legup/model/tree/TreeNode.java | 48 +- .../rpi/legup/model/tree/TreeTransition.java | 41 +- .../rpi/legup/puzzle/PuzzleElementTypes.java | 3 +- .../legup/puzzle/battleship/Battleship.java | 174 +++--- .../puzzle/battleship/BattleshipBoard.java | 15 +- .../puzzle/battleship/BattleshipCell.java | 3 +- .../battleship/BattleshipCellController.java | 26 +- .../battleship/BattleshipCellFactory.java | 31 +- .../puzzle/battleship/BattleshipClueView.java | 2 +- .../battleship/BattleshipElementView.java | 45 +- .../puzzle/battleship/BattleshipExporter.java | 7 +- .../puzzle/battleship/BattleshipImporter.java | 125 ++-- .../puzzle/battleship/BattleshipType.java | 25 +- .../puzzle/battleship/BattleshipView.java | 6 +- .../rules/AdjacentShipsContradictionRule.java | 38 +- .../rules/ContinueShipDirectRule.java | 88 +-- .../rules/FinishWithShipsDirectRule.java | 224 ++++--- .../rules/FinishWithWaterDirectRule.java | 88 +-- .../IncompleteShipContradictionRule.java | 10 +- .../battleship/rules/SegmentTypeCaseRule.java | 27 +- .../rules/SegmentTypeDirectRule.java | 88 +-- .../rules/ShipLocationCaseRule.java | 27 +- .../battleship/rules/ShipOrWaterCaseRule.java | 27 +- .../rules/SurroundShipDirectRule.java | 88 +-- .../rules/TooFewInFleetContradictionRule.java | 10 +- .../rules/TooFewRowColContradictionRule.java | 10 +- .../TooManyInFleetContradictionRule.java | 10 +- .../rules/TooManyRowColContradiction.java | 10 +- .../rpi/legup/puzzle/fillapix/Fillapix.java | 18 +- .../legup/puzzle/fillapix/FillapixBoard.java | 3 +- .../legup/puzzle/fillapix/FillapixCell.java | 13 +- .../fillapix/FillapixCellController.java | 28 +- .../puzzle/fillapix/FillapixCellFactory.java | 22 +- .../puzzle/fillapix/FillapixCellType.java | 4 +- .../puzzle/fillapix/FillapixElementView.java | 6 +- .../puzzle/fillapix/FillapixExporter.java | 7 +- .../puzzle/fillapix/FillapixImporter.java | 34 +- .../puzzle/fillapix/FillapixUtilities.java | 133 ++-- .../legup/puzzle/fillapix/FillapixView.java | 6 +- .../puzzle/fillapix/elements/BlackTile.java | 6 +- .../puzzle/fillapix/elements/NumberTile.java | 7 +- .../puzzle/fillapix/elements/UnknownTile.java | 6 +- .../puzzle/fillapix/elements/WhiteTile.java | 6 +- .../fillapix/rules/BlackOrWhiteCaseRule.java | 22 +- .../rules/FinishWithBlackDirectRule.java | 129 ++-- .../rules/FinishWithWhiteDirectRule.java | 129 ++-- .../fillapix/rules/MirrorDirectRule.java | 210 ++++--- .../rules/NonTouchingSharedDirectRule.java | 202 +++--- .../fillapix/rules/SatisfyClueCaseRule.java | 65 +- .../TooFewBlackCellsContradictionRule.java | 11 +- .../TooManyBlackCellsContradictionRule.java | 15 +- .../rules/TouchingCornersDirectRule.java | 221 +++---- .../rules/TouchingSidesDirectRule.java | 241 +++---- .../rpi/legup/puzzle/heyawake/Heyawake.java | 12 +- .../legup/puzzle/heyawake/HeyawakeBoard.java | 1 - .../legup/puzzle/heyawake/HeyawakeCell.java | 1 - .../puzzle/heyawake/HeyawakeController.java | 3 +- .../puzzle/heyawake/HeyawakeElementView.java | 4 +- .../puzzle/heyawake/HeyawakeExporter.java | 7 +- .../puzzle/heyawake/HeyawakeFactory.java | 22 +- .../puzzle/heyawake/HeyawakeImporter.java | 32 +- .../legup/puzzle/heyawake/HeyawakeView.java | 7 +- .../AdjacentBlacksContradictionRule.java | 11 +- .../heyawake/rules/BlackOrWhiteCaseRule.java | 27 +- .../heyawake/rules/BlackPathDirectRule.java | 14 +- .../heyawake/rules/BottleNeckDirectRule.java | 14 +- .../rules/FillRoomBlackDirectRule.java | 88 +-- .../rules/FillRoomWhiteDirectRule.java | 88 +-- .../heyawake/rules/OneRowDirectRule.java | 14 +- .../rules/PreventWhiteLineDirectRule.java | 14 +- .../rules/RoomTooEmptyContradictionRule.java | 10 +- .../rules/RoomTooFullContradictionRule.java | 10 +- .../rules/ThreeByThreeDirectRule.java | 14 +- .../heyawake/rules/TwoInCornerDirectRule.java | 14 +- .../rules/WhiteAreaContradictionRule.java | 10 +- .../rules/WhiteAroundBlackDirectRule.java | 88 +-- .../heyawake/rules/WhiteEscapeDirectRule.java | 14 +- .../rules/WhiteLineContradictionRule.java | 11 +- .../heyawake/rules/ZigZagWhiteDirectRule.java | 14 +- .../edu/rpi/legup/puzzle/lightup/LightUp.java | 16 +- .../legup/puzzle/lightup/LightUpBoard.java | 16 +- .../rpi/legup/puzzle/lightup/LightUpCell.java | 9 +- .../puzzle/lightup/LightUpCellController.java | 26 +- .../puzzle/lightup/LightUpCellFactory.java | 22 +- .../legup/puzzle/lightup/LightUpCellType.java | 6 +- .../puzzle/lightup/LightUpElementView.java | 31 +- .../legup/puzzle/lightup/LightUpExporter.java | 7 +- .../legup/puzzle/lightup/LightUpImporter.java | 28 +- .../rpi/legup/puzzle/lightup/LightUpView.java | 43 +- .../puzzle/lightup/elements/BlackTile.java | 6 +- .../puzzle/lightup/elements/BulbTile.java | 6 +- .../puzzle/lightup/elements/NumberTile.java | 12 +- .../puzzle/lightup/elements/UnknownTile.java | 6 +- .../rules/BulbsInPathContradictionRule.java | 24 +- .../CannotLightACellContradictionRule.java | 26 +- .../rules/EmptyCellinLightDirectRule.java | 132 ++-- .../lightup/rules/EmptyCornersDirectRule.java | 232 +++---- .../rules/FinishWithBulbsDirectRule.java | 217 +++---- .../rules/FinishWithEmptyDirectRule.java | 240 +++---- .../lightup/rules/LightOrEmptyCaseRule.java | 35 +- .../lightup/rules/MustLightDirectRule.java | 302 ++++----- .../lightup/rules/SatisfyNumberCaseRule.java | 82 +-- .../rules/TooFewBulbsContradictionRule.java | 12 +- .../rules/TooManyBulbsContradictionRule.java | 12 +- .../legup/puzzle/masyu/EditLineCommand.java | 49 +- .../edu/rpi/legup/puzzle/masyu/Masyu.java | 13 +- .../rpi/legup/puzzle/masyu/MasyuBoard.java | 4 +- .../edu/rpi/legup/puzzle/masyu/MasyuCell.java | 1 - .../legup/puzzle/masyu/MasyuCellFactory.java | 19 +- .../legup/puzzle/masyu/MasyuController.java | 26 +- .../legup/puzzle/masyu/MasyuElementView.java | 7 +- .../rpi/legup/puzzle/masyu/MasyuExporter.java | 7 +- .../rpi/legup/puzzle/masyu/MasyuImporter.java | 31 +- .../edu/rpi/legup/puzzle/masyu/MasyuLine.java | 6 +- .../rpi/legup/puzzle/masyu/MasyuLineView.java | 1 - .../edu/rpi/legup/puzzle/masyu/MasyuType.java | 8 +- .../edu/rpi/legup/puzzle/masyu/MasyuView.java | 7 +- .../rules/BadLoopingContradictionRule.java | 11 +- .../masyu/rules/BlackContradictionRule.java | 11 +- .../masyu/rules/BlackEdgeDirectRule.java | 83 ++- .../masyu/rules/BlackSplitCaseRule.java | 28 +- .../masyu/rules/BlockedBlackDirectRule.java | 87 +-- .../masyu/rules/ConnectedCellsDirectRule.java | 87 +-- .../masyu/rules/FinishPathDirectRule.java | 87 +-- .../masyu/rules/NearWhiteDirectRule.java | 83 ++- .../rules/NoOptionsContradictionRule.java | 11 +- .../masyu/rules/NormalSplitCaseRule.java | 28 +- .../masyu/rules/OnlyOneChoiceDirectRule.java | 87 +-- .../masyu/rules/OnlyTwoContradictionRule.java | 11 +- .../masyu/rules/WhiteContradictionRule.java | 11 +- .../masyu/rules/WhiteEdgeDirectRule.java | 81 ++- .../masyu/rules/WhiteSplitCaseRule.java | 28 +- .../rpi/legup/puzzle/nurikabe/Nurikabe.java | 12 +- .../legup/puzzle/nurikabe/NurikabeBoard.java | 8 +- .../legup/puzzle/nurikabe/NurikabeCell.java | 17 +- .../puzzle/nurikabe/NurikabeCellFactory.java | 22 +- .../puzzle/nurikabe/NurikabeController.java | 26 +- .../puzzle/nurikabe/NurikabeElementView.java | 13 +- .../puzzle/nurikabe/NurikabeExporter.java | 6 +- .../puzzle/nurikabe/NurikabeImporter.java | 34 +- .../legup/puzzle/nurikabe/NurikabeType.java | 5 +- .../puzzle/nurikabe/NurikabeUtilities.java | 67 +- .../legup/puzzle/nurikabe/NurikabeView.java | 4 +- .../puzzle/nurikabe/elements/BlackTile.java | 6 +- .../puzzle/nurikabe/elements/NumberTile.java | 7 +- .../puzzle/nurikabe/elements/UnknownTile.java | 6 +- .../puzzle/nurikabe/elements/WhiteTile.java | 6 +- .../rules/BlackBetweenRegionsDirectRule.java | 234 +++---- .../rules/BlackBottleNeckDirectRule.java | 134 ++-- .../nurikabe/rules/BlackOrWhiteCaseRule.java | 38 +- .../rules/BlackSquareContradictionRule.java | 27 +- .../rules/CannotReachCellDirectRule.java | 125 ++-- .../nurikabe/rules/CornerBlackDirectRule.java | 241 +++---- .../nurikabe/rules/FillinBlackDirectRule.java | 122 ++-- .../nurikabe/rules/FillinWhiteDirectRule.java | 122 ++-- .../rules/IsolateBlackContradictionRule.java | 24 +- .../MultipleNumbersContradictionRule.java | 14 +- .../rules/NoNumberContradictionRule.java | 16 +- .../rules/PreventBlackSquareDirectRule.java | 133 ++-- .../rules/SurroundRegionDirectRule.java | 198 +++--- .../rules/TooFewSpacesContradictionRule.java | 21 +- .../rules/TooManySpacesContradictionRule.java | 14 +- ...UnreachableWhiteCellContradictionRule.java | 22 +- .../rules/WhiteBottleNeckDirectRule.java | 141 +++-- .../shorttruthtable/ShortTruthTable.java | 15 +- .../shorttruthtable/ShortTruthTableBoard.java | 41 +- .../shorttruthtable/ShortTruthTableCell.java | 53 +- .../ShortTruthTableCellFactory.java | 33 +- .../ShortTruthTableCellType.java | 14 +- .../ShortTruthTableController.java | 14 +- .../ShortTruthTableElementView.java | 25 +- .../ShortTruthTableExporter.java | 8 +- .../ShortTruthTableImporter.java | 202 +++--- .../ShortTruthTableOperation.java | 105 ++-- .../ShortTruthTableStatement.java | 163 +++-- .../shorttruthtable/ShortTruthTableView.java | 6 +- .../elements/ArgumentElement.java | 6 +- .../elements/GreenElement.java | 6 +- .../elements/LogicSymbolElement.java | 6 +- .../shorttruthtable/elements/RedElement.java | 6 +- .../elements/UnknownElement.java | 6 +- .../rules/basic/DirectRuleAtomic.java | 10 +- .../rules/basic/DirectRule_Generic.java | 45 +- .../elimination/DirectRuleAndElimination.java | 21 +- .../DirectRuleBiconditionalElimination.java | 21 +- .../DirectRuleConditionalElimination.java | 21 +- .../elimination/DirectRuleNotElimination.java | 21 +- .../elimination/DirectRuleOrElimination.java | 21 +- .../DirectRule_GenericElimination.java | 37 +- .../DirectRuleAndIntroduction.java | 21 +- .../DirectRuleBiconditionalIntroduction.java | 21 +- .../DirectRuleConditionalIntroduction.java | 21 +- .../DirectRuleNotIntroduction.java | 21 +- .../DirectRuleOrIntroduction.java | 21 +- .../DirectRule_GenericIntroduction.java | 37 +- .../rules/caserule/CaseRuleAnd.java | 40 +- .../rules/caserule/CaseRuleAtomic.java | 11 +- .../rules/caserule/CaseRuleBiconditional.java | 15 +- .../rules/caserule/CaseRuleConditional.java | 47 +- .../rules/caserule/CaseRuleOr.java | 42 +- .../rules/caserule/CaseRule_Generic.java | 134 ++-- .../caserule/CaseRule_GenericStatement.java | 69 +- .../contradiction/ContradictionRuleAnd.java | 22 +- .../ContradictionRuleAtomic.java | 32 +- .../ContradictionRuleBiconditional.java | 22 +- .../ContradictionRuleConditional.java | 20 +- .../contradiction/ContradictionRuleNot.java | 18 +- .../contradiction/ContradictionRuleOr.java | 20 +- .../ContradictionRule_GenericStatement.java | 40 +- .../legup/puzzle/skyscrapers/Skyscrapers.java | 12 +- .../puzzle/skyscrapers/SkyscrapersBoard.java | 62 +- .../puzzle/skyscrapers/SkyscrapersCell.java | 18 +- .../skyscrapers/SkyscrapersCellFactory.java | 25 +- .../puzzle/skyscrapers/SkyscrapersClue.java | 2 +- .../skyscrapers/SkyscrapersClueView.java | 1 - .../skyscrapers/SkyscrapersController.java | 10 +- .../skyscrapers/SkyscrapersElementView.java | 4 +- .../skyscrapers/SkyscrapersExporter.java | 28 +- .../skyscrapers/SkyscrapersImporter.java | 107 +++- .../puzzle/skyscrapers/SkyscrapersType.java | 10 +- .../puzzle/skyscrapers/SkyscrapersView.java | 37 +- .../puzzle/skyscrapers/elements/ClueTile.java | 6 +- .../skyscrapers/elements/NumberTile.java | 6 +- .../skyscrapers/elements/UnknownTile.java | 6 +- .../rules/CellForNumberCaseRule.java | 98 +-- .../DuplicateNumberContradictionRule.java | 32 +- .../ExceedingVisibilityContradictionRule.java | 52 +- ...sufficientVisibilityContradictionRule.java | 54 +- .../rules/LastSingularCellDirectRule.java | 243 ++++---- .../rules/LastSingularNumberDirectRule.java | 200 +++--- .../rules/LastVisibleCellDirectRule.java | 248 ++++---- .../rules/LastVisibleNumberDirectRule.java | 202 +++--- .../skyscrapers/rules/NEdgeDirectRule.java | 205 +++--- .../rules/NumberForCellCaseRule.java | 77 ++- ...PreemptiveVisibilityContradictionRule.java | 97 +-- .../UnresolvedCellContradictionRule.java | 17 +- .../UnresolvedNumberContradictionRule.java | 48 +- .../rpi/legup/puzzle/sudoku/GroupType.java | 4 +- .../sudoku/PossibleNumberCaseBoard.java | 11 +- .../edu/rpi/legup/puzzle/sudoku/Sudoku.java | 19 +- .../rpi/legup/puzzle/sudoku/SudokuBoard.java | 26 +- .../rpi/legup/puzzle/sudoku/SudokuCell.java | 7 +- .../puzzle/sudoku/SudokuCellController.java | 31 +- .../puzzle/sudoku/SudokuCellFactory.java | 19 +- .../puzzle/sudoku/SudokuElementView.java | 17 +- .../legup/puzzle/sudoku/SudokuExporter.java | 7 +- .../legup/puzzle/sudoku/SudokuImporter.java | 47 +- .../rpi/legup/puzzle/sudoku/SudokuView.java | 40 +- .../puzzle/sudoku/elements/NumberTile.java | 2 - .../rules/AdvancedDeductionDirectRule.java | 195 +++--- .../rules/LastCellForNumberDirectRule.java | 188 +++--- .../rules/LastNumberForCellDirectRule.java | 158 ++--- .../rules/NoSolutionContradictionRule.java | 12 +- .../sudoku/rules/PossibleCellCaseRule.java | 17 +- .../sudoku/rules/PossibleNumberCaseRule.java | 32 +- .../RepeatedNumberContradictionRule.java | 12 +- .../legup/puzzle/treetent/ClueCommand.java | 43 +- .../puzzle/treetent/EditLineCommand.java | 63 +- .../rpi/legup/puzzle/treetent/TreeTent.java | 16 +- .../legup/puzzle/treetent/TreeTentBoard.java | 22 +- .../legup/puzzle/treetent/TreeTentCell.java | 1 - .../puzzle/treetent/TreeTentCellFactory.java | 34 +- .../puzzle/treetent/TreeTentClueView.java | 1 - .../puzzle/treetent/TreeTentController.java | 60 +- .../puzzle/treetent/TreeTentElementView.java | 45 +- .../puzzle/treetent/TreeTentExporter.java | 6 +- .../puzzle/treetent/TreeTentImporter.java | 79 ++- .../legup/puzzle/treetent/TreeTentLine.java | 6 +- .../puzzle/treetent/TreeTentLineView.java | 3 +- .../legup/puzzle/treetent/TreeTentType.java | 14 +- .../legup/puzzle/treetent/TreeTentView.java | 58 +- .../puzzle/treetent/elements/GrassTile.java | 9 +- .../puzzle/treetent/elements/TentTile.java | 9 +- .../puzzle/treetent/elements/TreeTile.java | 8 +- .../puzzle/treetent/elements/UnknownTile.java | 8 +- .../treetent/rules/EmptyFieldDirectRule.java | 176 +++--- .../treetent/rules/FillinRowCaseRule.java | 96 +-- .../rules/FinishWithGrassDirectRule.java | 173 ++--- .../rules/FinishWithTentsDirectRule.java | 178 +++--- .../rules/LastCampingSpotDirectRule.java | 206 +++--- .../treetent/rules/LinkTentCaseRule.java | 54 +- .../treetent/rules/LinkTreeCaseRule.java | 52 +- .../rules/NoTentForTreeContradictionRule.java | 25 +- .../rules/NoTreeForTentContradictionRule.java | 23 +- .../SurroundTentWithGrassDirectRule.java | 171 ++--- .../treetent/rules/TentForTreeDirectRule.java | 249 ++++---- .../treetent/rules/TentOrGrassCaseRule.java | 34 +- .../rules/TooFewTentsContradictionRule.java | 19 +- .../rules/TooManyTentsContradictionRule.java | 19 +- .../rules/TouchingTentsContradictionRule.java | 18 +- .../treetent/rules/TreeForTentDirectRule.java | 246 ++++---- .../rpi/legup/save/ExportFileException.java | 1 - .../save/InvalidFileFormatException.java | 1 - .../java/edu/rpi/legup/save/SavableBoard.java | 7 - .../java/edu/rpi/legup/save/SavableProof.java | 4 +- .../edu/rpi/legup/ui/CreatePuzzleDialog.java | 189 +++--- .../java/edu/rpi/legup/ui/DynamicView.java | 93 +-- src/main/java/edu/rpi/legup/ui/HomePanel.java | 425 +++++++------ .../java/edu/rpi/legup/ui/LegupPanel.java | 5 +- src/main/java/edu/rpi/legup/ui/LegupUI.java | 113 ++-- .../legup/ui/ManualPuzzleCreatorDialog.java | 8 +- .../java/edu/rpi/legup/ui/PickGameDialog.java | 35 +- .../edu/rpi/legup/ui/PreferencesDialog.java | 278 +++++---- .../edu/rpi/legup/ui/ProofEditorPanel.java | 590 ++++++++++-------- .../edu/rpi/legup/ui/PuzzleEditorPanel.java | 306 ++++----- .../java/edu/rpi/legup/ui/ScrollView.java | 58 +- .../java/edu/rpi/legup/ui/ToolbarName.java | 14 +- .../java/edu/rpi/legup/ui/WrapLayout.java | 63 +- .../java/edu/rpi/legup/ui/ZoomWidget.java | 23 +- .../java/edu/rpi/legup/ui/ZoomablePane.java | 4 +- .../edu/rpi/legup/ui/boardview/BoardView.java | 26 +- .../legup/ui/boardview/DataSelectionView.java | 3 +- .../legup/ui/boardview/ElementSelection.java | 3 +- .../rpi/legup/ui/boardview/ElementView.java | 74 ++- .../rpi/legup/ui/boardview/GridBoardView.java | 21 +- .../legup/ui/boardview/SelectionItemView.java | 1 - .../ui/lookandfeel/LegupLookAndFeel.java | 114 ++-- .../animation/MaterialUIMovement.java | 5 +- .../animation/MaterialUITimer.java | 17 +- .../components/MaterialButtonUI.java | 5 +- .../MaterialCheckBoxMenuItemUI.java | 33 +- .../components/MaterialCheckBoxUI.java | 5 +- .../components/MaterialComboBoxRenderer.java | 17 +- .../components/MaterialComboBoxUI.java | 6 +- .../components/MaterialEditorPaneUI.java | 1 - .../components/MaterialFileChooserUI.java | 3 +- .../components/MaterialLabelUI.java | 3 +- .../components/MaterialMenuBarUI.java | 3 +- .../components/MaterialMenuItemUI.java | 3 +- .../components/MaterialMenuUI.java | 3 +- .../components/MaterialPanelUI.java | 3 +- .../components/MaterialPasswordFieldUI.java | 141 +++-- .../components/MaterialPopupMenuUI.java | 3 +- .../components/MaterialProgressBarUI.java | 5 +- .../MaterialRadioButtonMenuItemUI.java | 35 +- .../components/MaterialRadioButtonUI.java | 7 +- .../components/MaterialScrollBarUI.java | 5 +- .../components/MaterialSeparatorUI.java | 3 +- .../components/MaterialSliderUI.java | 51 +- .../components/MaterialSpinnerUI.java | 11 +- .../components/MaterialSplitPaneDivider.java | 26 +- .../components/MaterialSplitPaneUI.java | 18 +- .../components/MaterialTabbedPaneUI.java | 50 +- .../components/MaterialTableCellEditor.java | 10 +- .../components/MaterialTableCellRenderer.java | 10 +- .../MaterialTableHeaderCellRenderer.java | 10 +- .../components/MaterialTableHeaderUI.java | 3 +- .../components/MaterialTableUI.java | 6 +- .../components/MaterialTextFieldUI.java | 152 +++-- .../components/MaterialTextPaneUI.java | 1 - .../components/MaterialToggleButtonUI.java | 3 +- .../components/MaterialToolBarUI.java | 3 +- .../components/MaterialToolTipUI.java | 3 +- .../components/MaterialTreeCellEditor.java | 29 +- .../components/MaterialTreeCellRenderer.java | 17 +- .../components/MaterialTreeUI.java | 5 +- .../materialdesign/DropShadowBorder.java | 236 ++++--- .../materialdesign/MaterialBorders.java | 19 +- .../materialdesign/MaterialColors.java | 3 +- .../materialdesign/MaterialDrawingUtils.java | 10 +- .../materialdesign/MaterialFonts.java | 26 +- .../materialdesign/MaterialImages.java | 32 +- .../rulesview/CaseRulePanel.java | 7 +- .../rulesview/CaseRuleSelectionView.java | 3 +- .../rulesview/ContradictionRulePanel.java | 5 +- .../rulesview/DirectRulePanel.java | 46 +- .../proofeditorui/rulesview/RuleButton.java | 5 +- .../ui/proofeditorui/rulesview/RuleFrame.java | 50 +- .../ui/proofeditorui/rulesview/RulePanel.java | 154 ++--- .../rulesview/SearchBarPanel.java | 8 +- .../treeview/TreeElementView.java | 7 +- .../proofeditorui/treeview/TreeNodeView.java | 65 +- .../ui/proofeditorui/treeview/TreePanel.java | 21 +- .../treeview/TreeToolBarButton.java | 2 +- .../treeview/TreeToolBarName.java | 5 +- .../treeview/TreeToolbarPanel.java | 34 +- .../treeview/TreeTransitionView.java | 48 +- .../ui/proofeditorui/treeview/TreeView.java | 399 ++++++------ .../treeview/TreeViewSelection.java | 21 +- .../elementsview/ElementButton.java | 4 +- .../elementsview/ElementFrame.java | 40 +- .../elementsview/ElementPanel.java | 11 +- .../NonPlaceableElementPanel.java | 5 +- .../elementsview/PlaceableElementPanel.java | 5 +- .../resizeview/ResizePanel.java | 3 +- .../java/edu/rpi/legup/user/Submission.java | 8 +- .../edu/rpi/legup/user/UsageStatistics.java | 32 +- src/main/java/edu/rpi/legup/user/User.java | 4 +- .../rpi/legup/utility/ConnectedRegions.java | 23 +- .../edu/rpi/legup/utility/DisjointSets.java | 44 +- .../java/edu/rpi/legup/utility/Entry.java | 2 +- .../edu/rpi/legup/utility/LegupUtils.java | 29 +- .../java/edu/rpi/legup/utility/Logger.java | 33 +- src/test/java/legup/MockGameBoardFacade.java | 13 +- src/test/java/legup/TestRunner.java | 7 +- src/test/java/legup/TestUtilities.java | 3 +- .../AdjacentShipsContradictionRuleTest.java | 46 +- .../rules/FinishWithShipsDirectRuleTests.java | 35 +- .../BulbsInPathContradictionRuleTest.java | 45 +- ...CannotLightACellContradictionRuleTest.java | 39 +- .../rules/EmptyCellinLightDirectRuleTest.java | 70 ++- .../rules/EmptyCornersDirectRuleTest.java | 45 +- .../rules/FinishWithBulbsDirectRuleTest.java | 72 +-- .../rules/LightOrEmptyCaseRuleTest.java | 63 +- .../rules/MustLightDirectRuleTest.java | 38 +- .../rules/SatisfyNumberCaseRuleTest.java | 140 +++-- .../TooFewBulbsContradictionRuleTest.java | 30 +- .../TooManyBulbsContradictionRuleTest.java | 35 +- .../BlackBetweenRegionsDirectRuleTest.java | 394 ++++++------ .../rules/BlackBottleNeckDirectRuleTest.java | 82 ++- .../rules/BlackOrWhiteCaseRuleTest.java | 56 +- .../BlackSquareContradictionRuleTest.java | 76 +-- .../rules/CannotReachCellDirectRuleTest.java | 76 ++- .../rules/CornerBlackDirectRuleTest.java | 165 +++-- .../rules/FillinBlackDirectRuleTest.java | 87 ++- .../rules/FillinWhiteDirectRuleTest.java | 287 +++++---- .../IsolateBlackContradictionRuleTest.java | 105 ++-- .../MultipleNumbersContradictionRuleTest.java | 83 +-- .../rules/NoNumbersContradictionRuleTest.java | 38 +- .../PreventBlackSquareDirectRuleTest.java | 347 +++++----- .../rules/SurroundRegionDirectRuleTest.java | 245 ++++---- .../TooFewSpacesContradictionRuleTest.java | 67 +- .../TooManySpacesContradictionRuleTest.java | 83 +-- ...achableWhiteCellContradictionRuleTest.java | 65 +- .../rules/WhiteBottleNeckDirectRuleTest.java | 212 +++---- .../ShortTruthTableImporterTest.java | 9 +- .../ShortTruthTableStatementTest.java | 9 +- .../rules/AndCaseRuleTest.java | 71 ++- .../rules/AndEliminationDirectRuleTest.java | 67 +- .../rules/AndIntroductionDirectRuleTest.java | 16 +- .../rules/AtomicDirectRuleTest.java | 68 +- .../rules/BiconditionalEliminationTest.java | 76 ++- .../rules/BiconditionalIntroductionTest.java | 25 +- .../rules/ConditionalEliminationTest.java | 72 ++- .../rules/ConditionalIntroductionTest.java | 25 +- .../rules/NotEliminationTest.java | 102 +-- .../rules/NotIntroductionTest.java | 43 +- .../shorttruthtable/rules/OrCaseRuleTest.java | 72 ++- .../rules/OrEliminationTest.java | 42 +- .../rules/OrIntroductionTest.java | 16 +- .../rules/TrueOrFalseCaseRuleTest.java | 31 +- .../rules/CellForNumberCaseRuleTest.java | 82 +-- .../DuplicateNumberContradictionTest.java | 66 +- .../ExceedingVisibilityContradictionTest.java | 62 +- ...sufficientVisibilityContradictionTest.java | 62 +- .../rules/LastSingularCellDirectTest.java | 146 +++-- .../rules/LastSingularNumberDirectTest.java | 94 +-- .../rules/LastVisibleCellDirectTest.java | 160 +++-- .../rules/LastVisibleNumberDirectTest.java | 161 +++-- .../skyscrapers/rules/NEdgeDirectTest.java | 83 ++- .../rules/NumberForCellCaseRuleTest.java | 82 +-- ...PreemptiveVisibilityContradictionTest.java | 71 ++- .../UnresolvedCellContradictionTest.java | 64 +- .../UnresolvedNumberContradictionTest.java | 73 ++- .../rules/EmptyFieldDirectRuleTest.java | 43 +- .../rules/FinishWithGrassDirectRuleTest.java | 126 ++-- .../rules/FinishWithTentsDirectRuleTest.java | 139 ++--- .../rules/LastCampingSpotDirectRuleTest.java | 46 +- .../NoTentForTreeContradictionRuleTest.java | 48 +- .../NoTreeForTentContradictionRuleTest.java | 80 +-- .../SurroundTentWithGrassDirectRuleTest.java | 64 +- .../rules/TentForTreeDirectRuleTest.java | 19 +- .../rules/TentOrGrassCaseRuleTest.java | 26 +- .../TooFewTentsContradictionRuleTest.java | 151 +++-- .../TooManyTentsContradictionRuleTest.java | 165 +++-- .../TouchingTentsContradictionRuleTest.java | 150 ++--- .../rules/TreeForTentDirectRuleTest.java | 19 +- 527 files changed, 14439 insertions(+), 13222 deletions(-) create mode 100644 .github/workflows/java-autoformat.yml mode change 100644 => 100755 gradlew diff --git a/.github/workflows/java-autoformat.yml b/.github/workflows/java-autoformat.yml new file mode 100644 index 000000000..5afb1f7a1 --- /dev/null +++ b/.github/workflows/java-autoformat.yml @@ -0,0 +1,38 @@ +name: Java Code Auto Format +on: pull_request + +jobs: + format: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v1 + with: + ref: ${{ github.head_ref }} + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build -x test + + - name: Run spotless + run: ./gradlew :spotlessApply + + - name: Check for modified files + id: git-check + run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT + + - name: Push changes + if: steps.git-check.outputs.modified == 'true' + run: | + git config --global user.name 'Bram van Heuveln' + git config --global user.email 'bram28@users.noreply.github.com' + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} + git add . + git diff --cached --exit-code || git commit -am "Automated Java code formatting changes" && git push \ No newline at end of file diff --git a/build.gradle b/build.gradle index 062a2cbff..49829e473 100644 --- a/build.gradle +++ b/build.gradle @@ -2,13 +2,43 @@ plugins { id 'java' id 'edu.sc.seis.launch4j' version '2.5.3' id 'kr.motd.sphinx' version '2.10.0' + id 'com.diffplug.spotless' version '6.25.0' } + version '2.0.0' apply plugin: 'java' apply plugin: 'application' + +spotless{ + enforceCheck false + + format 'misc', { + // define the files to apply `misc` to + target '*.gradle', '*.md', '.gitignore' + + // define the steps to apply to those files + trimTrailingWhitespace() + indentWithSpaces() // or spaces. Takes an integer argument if you don't like 4 + endWithNewline() + } + + java{ + // Use the default importOrder configuration + importOrder() + + // Cleanthat will refactor your code, but it may break your style: apply it before your formatter + cleanthat() + + googleJavaFormat('1.19.2').aosp() + + formatAnnotations() + } +} + apply plugin: 'checkstyle' + mainClassName = 'edu.rpi.legup.Legup' sourceCompatibility = 11 diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index a5dfea0af..6e17e3fc5 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -7,12 +7,6 @@ - - - - - diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/edu/rpi/legup/Legup.java b/src/main/java/edu/rpi/legup/Legup.java index 49887de4f..79471286b 100644 --- a/src/main/java/edu/rpi/legup/Legup.java +++ b/src/main/java/edu/rpi/legup/Legup.java @@ -15,4 +15,4 @@ public static void main(String[] args) { GameBoardFacade.getInstance(); GameBoardFacade.setupConfig(); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ai/Solver.java b/src/main/java/edu/rpi/legup/ai/Solver.java index 68b934bfb..5b39c2008 100644 --- a/src/main/java/edu/rpi/legup/ai/Solver.java +++ b/src/main/java/edu/rpi/legup/ai/Solver.java @@ -1,4 +1,3 @@ package edu.rpi.legup.ai; -public class Solver { -} +public class Solver {} diff --git a/src/main/java/edu/rpi/legup/app/Config.java b/src/main/java/edu/rpi/legup/app/Config.java index 1847e14e4..8a8e9665d 100644 --- a/src/main/java/edu/rpi/legup/app/Config.java +++ b/src/main/java/edu/rpi/legup/app/Config.java @@ -2,21 +2,18 @@ import java.io.*; import java.util.*; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; - 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.NodeList; import org.xml.sax.SAXException; public class Config { - private final static Logger Logger = LogManager.getLogger(Config.class.getName()); + private static final Logger Logger = LogManager.getLogger(Config.class.getName()); private Map puzzles; private Map fileCreationDisabledStatuses; @@ -43,8 +40,8 @@ public List getPuzzleClassNames() { } /** - * Returns a list of the names of the puzzles which can have puzzles created and edited - * within the proof editor. + * Returns a list of the names of the puzzles which can have puzzles created and edited within + * the proof editor. * * @return the aforementioned list of Strings */ @@ -59,8 +56,8 @@ public List getFileCreationEnabledPuzzles() { } /** - * Converts the class name of the puzzles to their display names. Some examples of the conversion: - * convertClassNameToDisplayName("TreeTent") will return "Tree Tent" + * Converts the class name of the puzzles to their display names. Some examples of the + * conversion: convertClassNameToDisplayName("TreeTent") will return "Tree Tent" * convertClassNameToDisplayName("Nurikabe") will return "Nurikabe" * * @param className the name of the class @@ -140,15 +137,16 @@ private void loadConfig(InputStream stream) throws InvalidConfigException { Element puzzle = (Element) puzzleNodes.item(i); String name = puzzle.getAttribute("name"); String className = puzzle.getAttribute("qualifiedClassName"); - boolean status = Boolean.parseBoolean(puzzle.getAttribute("fileCreationDisabled").toLowerCase()); + boolean status = + Boolean.parseBoolean( + puzzle.getAttribute("fileCreationDisabled").toLowerCase()); Logger.debug("Class Name: " + className); this.puzzles.put(name, className); this.fileCreationDisabledStatuses.put(name, Boolean.valueOf(status)); } - } - catch (ParserConfigurationException | SAXException | IOException e) { + } catch (ParserConfigurationException | SAXException | IOException e) { throw new InvalidConfigException(e.getMessage()); } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java index 55273ab4f..c928c1209 100644 --- a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java +++ b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java @@ -11,16 +11,6 @@ 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 javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import java.awt.*; import java.io.File; import java.io.FileInputStream; @@ -31,11 +21,20 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +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; public class GameBoardFacade implements IHistorySubject { - private final static Logger LOGGER = LogManager.getLogger(GameBoardFacade.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(GameBoardFacade.class.getName()); - protected volatile static GameBoardFacade instance; + protected static volatile GameBoardFacade instance; private Config config; @@ -52,9 +51,7 @@ public class GameBoardFacade implements IHistorySubject { private History history; private List historyListeners; - /** - * Private GameBoardFacade Constructor creates a game board facade - */ + /** Private GameBoardFacade Constructor creates a game board facade */ protected GameBoardFacade() { history = new History(); historyListeners = new ArrayList<>(); @@ -68,7 +65,7 @@ protected GameBoardFacade() { * * @return single instance of GameBoardFacade */ - public synchronized static GameBoardFacade getInstance() { + public static synchronized GameBoardFacade getInstance() { if (instance == null) { instance = new GameBoardFacade(); } @@ -76,13 +73,14 @@ public synchronized static GameBoardFacade getInstance() { } public void initializeUI() { - EventQueue.invokeLater(() -> { - legupUI = new LegupUI(); - puzzleSolver = legupUI.getProofEditor(); - puzzleEditor = legupUI.getPuzzleEditor(); - addHistoryListener(legupUI.getProofEditor()); - addHistoryListener(legupUI.getPuzzleEditor()); - }); + EventQueue.invokeLater( + () -> { + legupUI = new LegupUI(); + puzzleSolver = legupUI.getProofEditor(); + puzzleEditor = legupUI.getPuzzleEditor(); + addHistoryListener(legupUI.getProofEditor()); + addHistoryListener(legupUI.getPuzzleEditor()); + }); } public void setPuzzle(Puzzle puzzle) { @@ -101,14 +99,12 @@ public static void setupConfig() { Config config = null; try { config = new Config(); - } - catch (InvalidConfigException e) { + } catch (InvalidConfigException e) { System.exit(1); } GameBoardFacade.getInstance().setConfig(config); } - public void setPuzzleEditor(Puzzle puzzle) { this.puzzle = puzzle; this.puzzleEditor.setPuzzleView(puzzle); @@ -121,11 +117,11 @@ public void setConfig(Config config) { /** * Validates the given dimensions for the given puzzle * - * @param game name of the puzzle - * @param rows the number of rows on the board + * @param game name of the puzzle + * @param rows the number of rows on the board * @param columns the number of columns on the board * @return true if it is possible to create a board for the given game with the given number of - * rows and columns, false otherwise + * rows and columns, false otherwise * @throws RuntimeException if any of the given input is invalid */ public boolean validateDimensions(String game, int rows, int columns) throws RuntimeException { @@ -135,9 +131,11 @@ public boolean validateDimensions(String game, int rows, int columns) throws Run Constructor constructor = c.getConstructor(); Puzzle puzzle = (Puzzle) constructor.newInstance(); return puzzle.isValidDimensions(rows, columns); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | - InstantiationException e) { + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { LOGGER.error(e); throw new RuntimeException("Error validating puzzle dimensions"); } @@ -146,10 +144,10 @@ 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 + * @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 { @@ -159,9 +157,11 @@ public boolean validateTextInput(String game, String[] statements) throws Runtim Constructor constructor = c.getConstructor(); Puzzle puzzle = (Puzzle) constructor.newInstance(); return puzzle.isValidTextInput(statements); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | - InstantiationException e) { + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { LOGGER.error(e); throw new RuntimeException("Error validating puzzle text input"); } @@ -170,8 +170,8 @@ public boolean validateTextInput(String game, String[] statements) throws Runtim /** * Loads an empty puzzle * - * @param game name of the puzzle - * @param rows the number of rows on the board + * @param game name of the puzzle + * @param rows the number of rows on the board * @param columns the number of columns on the board */ public void loadPuzzle(String game, int rows, int columns) throws RuntimeException { @@ -192,21 +192,24 @@ public void loadPuzzle(String game, int rows, int columns) throws RuntimeExcepti // 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"); + 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(); -// puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); + // + // puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); setPuzzleEditor(puzzle); - } - catch (IllegalArgumentException exception) { + } catch (IllegalArgumentException exception) { throw new IllegalArgumentException(exception.getMessage()); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | - IllegalAccessException | InstantiationException e) { + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { LOGGER.error(e); throw new RuntimeException("Puzzle creation error"); } @@ -230,25 +233,27 @@ public void loadPuzzle(String game, String[] statements) { // 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"); + 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()); + // + // puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); setPuzzleEditor(puzzle); - } - catch (IllegalArgumentException exception) { + } catch (IllegalArgumentException exception) { throw new IllegalArgumentException(exception.getMessage()); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | - IllegalAccessException | InstantiationException e) { + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { LOGGER.error(e); throw new RuntimeException("Puzzle creation error"); } - } /** @@ -262,8 +267,7 @@ public void loadPuzzle(String fileName) throws InvalidFileFormatException { loadPuzzle(new FileInputStream(fileName)); curFileName = fileName; setWindowTitle(puzzle.getName(), fileName); - } - catch (IOException e) { + } catch (IOException e) { LOGGER.error("Invalid file " + fileName, e); throw new InvalidFileFormatException("Could not find file"); } @@ -274,8 +278,7 @@ public void loadPuzzleEditor(String fileName) throws InvalidFileFormatException loadPuzzleEditor(new FileInputStream(fileName)); curFileName = fileName; setWindowTitle(puzzle.getName(), fileName); - } - catch (IOException e) { + } catch (IOException e) { LOGGER.error("Invalid file " + fileName, e); throw new InvalidFileFormatException("Could not find file"); } @@ -287,8 +290,7 @@ public void loadPuzzleEditor(InputStream inputStream) throws InvalidFileFormatEx DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.parse(inputStream); - } - catch (IOException | SAXException | ParserConfigurationException e) { + } catch (IOException | SAXException | ParserConfigurationException e) { LOGGER.error("Invalid file", e); throw new InvalidFileFormatException("Could not find file"); } @@ -297,12 +299,16 @@ public void loadPuzzleEditor(InputStream inputStream) throws InvalidFileFormatEx if (rootNode.getTagName().equals("Legup")) { try { Node node = rootNode.getElementsByTagName("puzzle").item(0); - String qualifiedClassName = config.getPuzzleClassForName(node.getAttributes().getNamedItem("name").getNodeValue()); + String qualifiedClassName = + config.getPuzzleClassForName( + node.getAttributes().getNamedItem("name").getNodeValue()); if (qualifiedClassName == null) { - throw new InvalidFileFormatException("Puzzle creation error: cannot find puzzle with that name"); + throw new InvalidFileFormatException( + "Puzzle creation error: cannot find puzzle with that name"); } - //Check if puzzle is a "FileCreationEnabled" puzzle (meaning it is editable). - String[] editablePuzzles = config.getFileCreationEnabledPuzzles().toArray(new String[0]); + // Check if puzzle is a "FileCreationEnabled" puzzle (meaning it is editable). + String[] editablePuzzles = + config.getFileCreationEnabledPuzzles().toArray(new String[0]); boolean isEditablePuzzle = false; for (int i = 0; i < editablePuzzles.length; i++) { if (qualifiedClassName.contains(editablePuzzles[i])) { @@ -314,7 +320,7 @@ public void loadPuzzleEditor(InputStream inputStream) throws InvalidFileFormatEx LOGGER.error("Puzzle is not editable"); throw new InvalidFileFormatException("Puzzle is not editable"); } - //If it is editable, start loading it + // If it is editable, start loading it LOGGER.debug("Loading " + qualifiedClassName); Class c = Class.forName(qualifiedClassName); @@ -330,14 +336,15 @@ public void loadPuzzleEditor(InputStream inputStream) throws InvalidFileFormatEx puzzle.initializeView(); puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); setPuzzleEditor(puzzle); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | - IllegalAccessException | InstantiationException e) { + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { LOGGER.error(e); throw new InvalidFileFormatException("Puzzle creation error"); } - } - else { + } else { LOGGER.error("Invalid file"); throw new InvalidFileFormatException("Invalid file: must be a Legup file"); } @@ -345,6 +352,7 @@ public void loadPuzzleEditor(InputStream inputStream) throws InvalidFileFormatEx /** * Loads a puzzle file from the input stream + * * @throws InvalidFileFormatException if input is invalid * @param inputStream input stream for the puzzle file */ @@ -354,8 +362,7 @@ public void loadPuzzle(InputStream inputStream) throws InvalidFileFormatExceptio DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.parse(inputStream); - } - catch (IOException | SAXException | ParserConfigurationException e) { + } catch (IOException | SAXException | ParserConfigurationException e) { LOGGER.error("Invalid file", e); throw new InvalidFileFormatException("Could not find file"); } @@ -364,9 +371,12 @@ public void loadPuzzle(InputStream inputStream) throws InvalidFileFormatExceptio if (rootNode.getTagName().equals("Legup")) { try { Node node = rootNode.getElementsByTagName("puzzle").item(0); - String qualifiedClassName = config.getPuzzleClassForName(node.getAttributes().getNamedItem("name").getNodeValue()); + String qualifiedClassName = + config.getPuzzleClassForName( + node.getAttributes().getNamedItem("name").getNodeValue()); if (qualifiedClassName == null) { - throw new InvalidFileFormatException("Puzzle creation error: cannot find puzzle with that name"); + throw new InvalidFileFormatException( + "Puzzle creation error: cannot find puzzle with that name"); } LOGGER.debug("Loading " + qualifiedClassName); @@ -383,25 +393,25 @@ public void loadPuzzle(InputStream inputStream) throws InvalidFileFormatExceptio puzzle.initializeView(); puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); setPuzzle(puzzle); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | - IllegalAccessException | InstantiationException e) { + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { LOGGER.error(e); throw new InvalidFileFormatException("Puzzle creation error"); } - } - else { + } else { LOGGER.error("Invalid file"); throw new InvalidFileFormatException("Invalid file: must be a Legup file"); } } /** - * Sets the window title to 'PuzzleName - FileName' - * Removes the extension + * Sets the window title to 'PuzzleName - FileName' Removes the extension * * @param puzzleName puzzle name for the file - * @param fileName file name of the edu.rpi.legup.puzzle + * @param fileName file name of the edu.rpi.legup.puzzle */ public void setWindowTitle(String puzzleName, String fileName) { File file = new File(fileName); @@ -514,4 +524,3 @@ public History getHistory() { return history; } } - diff --git a/src/main/java/edu/rpi/legup/app/LegupPreferences.java b/src/main/java/edu/rpi/legup/app/LegupPreferences.java index 578cefc5b..12433d7e4 100644 --- a/src/main/java/edu/rpi/legup/app/LegupPreferences.java +++ b/src/main/java/edu/rpi/legup/app/LegupPreferences.java @@ -10,7 +10,8 @@ public class LegupPreferences { private static String SAVED_PATH = ""; - private static final Preferences preferences = Preferences.userNodeForPackage(LegupPreferences.class); + private static final Preferences preferences = + Preferences.userNodeForPackage(LegupPreferences.class); private static final Map preferencesMap = new HashMap<>(); private static final Map defaultPreferencesMap = new HashMap<>(); @@ -26,7 +27,6 @@ public class LegupPreferences { public static final String IMMEDIATE_FEEDBACK = "immediate-feedback"; public static final String COLOR_BLIND = "color-blind"; - static { defaultPreferencesMap.put(WORK_DIRECTORY, System.getProperty("user.home")); defaultPreferencesMap.put(START_FULL_SCREEN, Boolean.toString(false)); @@ -41,16 +41,35 @@ public class LegupPreferences { } static { - preferencesMap.put(WORK_DIRECTORY, preferences.get(WORK_DIRECTORY, defaultPreferencesMap.get(WORK_DIRECTORY))); - preferencesMap.put(START_FULL_SCREEN, preferences.get(START_FULL_SCREEN, defaultPreferencesMap.get(START_FULL_SCREEN))); - preferencesMap.put(AUTO_UPDATE, preferences.get(AUTO_UPDATE, defaultPreferencesMap.get(AUTO_UPDATE))); - preferencesMap.put(DARK_MODE, preferences.get(DARK_MODE, defaultPreferencesMap.get(DARK_MODE))); - preferencesMap.put(SHOW_MISTAKES, preferences.get(SHOW_MISTAKES, defaultPreferencesMap.get(SHOW_MISTAKES))); - preferencesMap.put(SHOW_ANNOTATIONS, preferences.get(SHOW_ANNOTATIONS, defaultPreferencesMap.get(SHOW_ANNOTATIONS))); - preferencesMap.put(ALLOW_DEFAULT_RULES, preferences.get(ALLOW_DEFAULT_RULES, defaultPreferencesMap.get(ALLOW_DEFAULT_RULES))); - preferencesMap.put(AUTO_GENERATE_CASES, preferences.get(AUTO_GENERATE_CASES, defaultPreferencesMap.get(AUTO_GENERATE_CASES))); - preferencesMap.put(IMMEDIATE_FEEDBACK, preferences.get(IMMEDIATE_FEEDBACK, defaultPreferencesMap.get(IMMEDIATE_FEEDBACK))); - preferencesMap.put(COLOR_BLIND, preferences.get(COLOR_BLIND, defaultPreferencesMap.get(COLOR_BLIND))); + preferencesMap.put( + WORK_DIRECTORY, + preferences.get(WORK_DIRECTORY, defaultPreferencesMap.get(WORK_DIRECTORY))); + preferencesMap.put( + START_FULL_SCREEN, + preferences.get(START_FULL_SCREEN, defaultPreferencesMap.get(START_FULL_SCREEN))); + preferencesMap.put( + AUTO_UPDATE, preferences.get(AUTO_UPDATE, defaultPreferencesMap.get(AUTO_UPDATE))); + preferencesMap.put( + DARK_MODE, preferences.get(DARK_MODE, defaultPreferencesMap.get(DARK_MODE))); + preferencesMap.put( + SHOW_MISTAKES, + preferences.get(SHOW_MISTAKES, defaultPreferencesMap.get(SHOW_MISTAKES))); + preferencesMap.put( + SHOW_ANNOTATIONS, + preferences.get(SHOW_ANNOTATIONS, defaultPreferencesMap.get(SHOW_ANNOTATIONS))); + preferencesMap.put( + ALLOW_DEFAULT_RULES, + preferences.get( + ALLOW_DEFAULT_RULES, defaultPreferencesMap.get(ALLOW_DEFAULT_RULES))); + preferencesMap.put( + AUTO_GENERATE_CASES, + preferences.get( + AUTO_GENERATE_CASES, defaultPreferencesMap.get(AUTO_GENERATE_CASES))); + preferencesMap.put( + IMMEDIATE_FEEDBACK, + preferences.get(IMMEDIATE_FEEDBACK, defaultPreferencesMap.get(IMMEDIATE_FEEDBACK))); + preferencesMap.put( + COLOR_BLIND, preferences.get(COLOR_BLIND, defaultPreferencesMap.get(COLOR_BLIND))); } /** @@ -65,12 +84,8 @@ public static LegupPreferences getInstance() { return instance; } - /** - * Private LegupPreferences Singleton Constructor - */ - private LegupPreferences() { - - } + /** Private LegupPreferences Singleton Constructor */ + private LegupPreferences() {} /** * Gets the user preference by the string key @@ -85,7 +100,7 @@ public String getUserPref(String key) { /** * Gets the user preference by the string key, value pair * - * @param key key name of the preference + * @param key key name of the preference * @param value value of the preference */ public void setUserPref(String key, String value) { @@ -96,18 +111,15 @@ public void setUserPref(String key, String value) { public boolean getUserPrefAsBool(String key) { if (preferencesMap.get(key).equalsIgnoreCase(Boolean.toString(true))) { return true; - } - else { + } else { if (preferencesMap.get(key).equalsIgnoreCase(Boolean.toString(false))) { return false; - } - else { + } else { throw new RuntimeException("Cannot get user preference - " + key); } } } - public String getSavedPath() { return SAVED_PATH; } diff --git a/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java b/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java index 36ffb08aa..f3945be22 100644 --- a/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java +++ b/src/main/java/edu/rpi/legup/app/PuzzleKeyAccelerator.java @@ -1,22 +1,21 @@ package edu.rpi.legup.app; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.history.ICommand; -import edu.rpi.legup.history.ValidateDirectRuleCommand; import edu.rpi.legup.history.ValidateContradictionRuleCommand; -import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.history.ValidateDirectRuleCommand; import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.model.rules.DirectRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.rules.RuleType; import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; - -import javax.swing.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.HashMap; import java.util.Map; - -import static edu.rpi.legup.app.GameBoardFacade.getInstance; +import javax.swing.*; public class PuzzleKeyAccelerator implements KeyListener { @@ -39,21 +38,17 @@ public void clearKeyMap() { } /** - * Invoked when a key has been typed. - * See the class description for {@link KeyEvent} for a definition of - * a key typed event. + * Invoked when a key has been typed. See the class description for {@link KeyEvent} for a + * definition of a key typed event. * * @param e the event to be processed */ @Override - public void keyTyped(KeyEvent e) { - - } + public void keyTyped(KeyEvent e) {} /** - * Invoked when a key has been pressed. - * See the class description for {@link KeyEvent} for a definition of - * a key pressed event. + * Invoked when a key has been pressed. See the class description for {@link KeyEvent} for a + * definition of a key pressed event. * * @param e the event to be processed */ @@ -62,35 +57,35 @@ public void keyPressed(KeyEvent e) { KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e); Rule rule = keyStrokeMap.get(keyStroke); if (rule != null) { - TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); + TreeView treeView = + GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); String update = ""; if (rule.getRuleType() == RuleType.CASE) { - // TODO: review this line of code and figure out what it's supposed to do (remove if necessary) -// handleCaseRule((CaseRule)rule); - } - else { + // TODO: review this line of code and figure out what it's supposed to do (remove if + // necessary) + // handleCaseRule((CaseRule)rule); + } else { if (rule.getRuleType() == RuleType.CONTRADICTION) { TreeViewSelection selection = treeView.getSelection(); - ICommand validate = new ValidateContradictionRuleCommand(selection, (ContradictionRule) rule); + ICommand validate = + new ValidateContradictionRuleCommand( + selection, (ContradictionRule) rule); if (validate.canExecute()) { getInstance().getHistory().pushChange(validate); validate.execute(); - } - else { + } else { update = validate.getError(); } - } - else { + } else { TreeViewSelection selection = treeView.getSelection(); ICommand validate = new ValidateDirectRuleCommand(selection, (DirectRule) rule); if (validate.canExecute()) { getInstance().getHistory().pushChange(validate); validate.execute(); - } - else { + } else { update = validate.getError(); } } @@ -100,14 +95,11 @@ public void keyPressed(KeyEvent e) { } /** - * Invoked when a key has been released. - * See the class description for {@link KeyEvent} for a definition of - * a key released event. + * Invoked when a key has been released. See the class description for {@link KeyEvent} for a + * definition of a key released event. * * @param e the event to be processed */ @Override - public void keyReleased(KeyEvent e) { - - } + public void keyReleased(KeyEvent e) {} } diff --git a/src/main/java/edu/rpi/legup/controller/BoardController.java b/src/main/java/edu/rpi/legup/controller/BoardController.java index f9c328a99..eaae3851d 100644 --- a/src/main/java/edu/rpi/legup/controller/BoardController.java +++ b/src/main/java/edu/rpi/legup/controller/BoardController.java @@ -3,16 +3,13 @@ import java.awt.*; import java.awt.event.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - public class BoardController extends Controller { protected Point lastLeftMousePoint; protected Point lastRightMousePoint; /** - * BoardController Constructor creates a controller object to listen - * to ui events from a ScrollView + * BoardController Constructor creates a controller object to listen to ui events from a + * ScrollView */ public BoardController() { super(); @@ -26,13 +23,10 @@ public BoardController() { * @param e MouseEvent object */ @Override - public void mouseClicked(MouseEvent e) { - - } + public void mouseClicked(MouseEvent e) {} /** - * Mouse Pressed event - sets the cursor to the move cursor and stores - * info for possible panning + * Mouse Pressed event - sets the cursor to the move cursor and stores info for possible panning * * @param e MouseEvent object */ @@ -42,8 +36,7 @@ public void mousePressed(MouseEvent e) { } /** - * Mouse Released event - sets the cursor back to the default cursor and reset - * info for panning + * Mouse Released event - sets the cursor back to the default cursor and reset info for panning * * @param e MouseEvent object */ @@ -58,9 +51,7 @@ public void mouseReleased(MouseEvent e) { * @param e MouseEvent object */ @Override - public void mouseEntered(MouseEvent e) { - - } + public void mouseEntered(MouseEvent e) {} /** * Mouse Exited event - no default action @@ -68,9 +59,7 @@ public void mouseEntered(MouseEvent e) { * @param e MouseEvent object */ @Override - public void mouseExited(MouseEvent e) { - - } + public void mouseExited(MouseEvent e) {} /** * Mouse Dragged event - adjusts the viewport @@ -88,9 +77,7 @@ public void mouseDragged(MouseEvent e) { * @param e MouseEvent object */ @Override - public void mouseMoved(MouseEvent e) { - - } + public void mouseMoved(MouseEvent e) {} /** * Mouse Wheel Moved event - zooms in on the viewport diff --git a/src/main/java/edu/rpi/legup/controller/Controller.java b/src/main/java/edu/rpi/legup/controller/Controller.java index 5eef8cc17..57ce107ac 100644 --- a/src/main/java/edu/rpi/legup/controller/Controller.java +++ b/src/main/java/edu/rpi/legup/controller/Controller.java @@ -1,10 +1,9 @@ package edu.rpi.legup.controller; import edu.rpi.legup.ui.ScrollView; - -import javax.swing.*; import java.awt.*; import java.awt.event.*; +import javax.swing.*; public abstract class Controller implements MouseMotionListener, MouseListener, MouseWheelListener { protected ScrollView viewer; @@ -12,7 +11,8 @@ public abstract class Controller implements MouseMotionListener, MouseListener, private boolean pan; /** - * Controller Constructor creates a controller object to listen to ui events from a {@link ScrollView} + * Controller Constructor creates a controller object to listen to ui events from a {@link + * ScrollView} */ public Controller() { x = y = -1; @@ -29,13 +29,10 @@ public void setViewer(ScrollView viewer) { * @param e MouseEvent object */ @Override - public void mouseClicked(MouseEvent e) { - - } + public void mouseClicked(MouseEvent e) {} /** - * Mouse Pressed event sets the cursor to the move cursor and stores - * info for possible panning + * Mouse Pressed event sets the cursor to the move cursor and stores info for possible panning * * @param e MouseEvent object */ @@ -50,8 +47,7 @@ public void mousePressed(MouseEvent e) { } /** - * Mouse Released event sets the cursor back to the default cursor and reset - * info for panning + * Mouse Released event sets the cursor back to the default cursor and reset info for panning * * @param e MouseEvent object */ @@ -69,9 +65,7 @@ public void mouseReleased(MouseEvent e) { * @param e MouseEvent object */ @Override - public void mouseEntered(MouseEvent e) { - - } + public void mouseEntered(MouseEvent e) {} /** * Mouse Exited event no default action @@ -79,9 +73,7 @@ public void mouseEntered(MouseEvent e) { * @param e MouseEvent object */ @Override - public void mouseExited(MouseEvent e) { - - } + public void mouseExited(MouseEvent e) {} /** * Mouse Dragged event adjusts the viewport @@ -106,9 +98,7 @@ public void mouseDragged(MouseEvent e) { * @param e MouseEvent object */ @Override - public void mouseMoved(MouseEvent e) { - - } + public void mouseMoved(MouseEvent e) {} /** * Mouse Wheel Moved event zooms in on the viewport @@ -122,11 +112,10 @@ public void mouseWheelMoved(MouseWheelEvent e) { if (e.getWheelRotation() != 0) { viewer.zoom(e.getWheelRotation() * 2, e.getPoint()); } - } - else { + } else { if (e.getWheelRotation() != 0) { viewer.zoom(e.getWheelRotation(), e.getPoint()); } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/controller/CursorController.java b/src/main/java/edu/rpi/legup/controller/CursorController.java index 0c9a644ed..2706bd522 100644 --- a/src/main/java/edu/rpi/legup/controller/CursorController.java +++ b/src/main/java/edu/rpi/legup/controller/CursorController.java @@ -2,7 +2,6 @@ import java.awt.Component; import java.awt.Cursor; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Timer; import java.util.TimerTask; @@ -10,7 +9,7 @@ public class CursorController { public static final Cursor BUSY_CURSOR = new Cursor(Cursor.WAIT_CURSOR); public static final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); - public static final int DELAY = 200; // in milliseconds + public static final int DELAY = 200; // in milliseconds private CursorController() { // Intentionally left empty @@ -18,36 +17,38 @@ private CursorController() { /** * Creates an ActionListener that will still do the same action processing as the given - * ActionListener while also displaying a loading cursor if the time it takes to execute - * the given process exceeds the time (in milliseconds) specified in this.DELAY - *

- * Sources consulted: http://www.catalysoft.com/articles/busycursor.html + * ActionListener while also displaying a loading cursor if the time it takes to execute the + * given process exceeds the time (in milliseconds) specified in this.DELAY * - * @param component The component you want to set the cursor for + *

Sources consulted: http://www.catalysoft.com/articles/busycursor.html + * + * @param component The component you want to set the cursor for * @param mainActionListener The ActionListener that does the intended action processing - * @return An ActionListener object that does the same action processing - * as mainActionListener while also modifying the cursor if needed + * @return An ActionListener object that does the same action processing as mainActionListener + * while also modifying the cursor if needed */ - public static ActionListener createListener(final Component component, final ActionListener mainActionListener) { - ActionListener actionListener = e -> { - TimerTask timerTask = new TimerTask() { - @Override - public void run() { - component.setCursor(BUSY_CURSOR); - } - }; + public static ActionListener createListener( + final Component component, final ActionListener mainActionListener) { + ActionListener actionListener = + e -> { + TimerTask timerTask = + new TimerTask() { + @Override + public void run() { + component.setCursor(BUSY_CURSOR); + } + }; - Timer timer = new Timer(); - try { - timer.schedule(timerTask, DELAY); - mainActionListener.actionPerformed(e); - } - finally { - timer.cancel(); - component.setCursor(DEFAULT_CURSOR); - } - }; + Timer timer = new Timer(); + try { + timer.schedule(timerTask, DELAY); + mainActionListener.actionPerformed(e); + } finally { + timer.cancel(); + component.setCursor(DEFAULT_CURSOR); + } + }; return actionListener; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/controller/EditorElementController.java b/src/main/java/edu/rpi/legup/controller/EditorElementController.java index 80e7dd35a..5a23885af 100644 --- a/src/main/java/edu/rpi/legup/controller/EditorElementController.java +++ b/src/main/java/edu/rpi/legup/controller/EditorElementController.java @@ -1,31 +1,13 @@ package edu.rpi.legup.controller; -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.history.*; -import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.elements.Element; -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.*; -import edu.rpi.legup.model.tree.TreeElement; -import edu.rpi.legup.model.tree.TreeElementType; -import edu.rpi.legup.ui.proofeditorui.rulesview.RuleButton; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import edu.rpi.legup.ui.puzzleeditorui.elementsview.ElementButton; - -import javax.swing.*; -import javax.swing.border.Border; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.List; - -import static edu.rpi.legup.app.GameBoardFacade.getInstance; +import javax.swing.*; public class EditorElementController implements ActionListener { protected Object lastSource; @@ -66,6 +48,5 @@ public void actionPerformed(ActionEvent e) { button.setBorderToSelected(); this.prevButton = button; - } } diff --git a/src/main/java/edu/rpi/legup/controller/ElementController.java b/src/main/java/edu/rpi/legup/controller/ElementController.java index 50fa7d1b9..5840650e1 100644 --- a/src/main/java/edu/rpi/legup/controller/ElementController.java +++ b/src/main/java/edu/rpi/legup/controller/ElementController.java @@ -1,10 +1,12 @@ package edu.rpi.legup.controller; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.ui.ScrollView; +import static edu.rpi.legup.app.GameBoardFacade.*; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.history.AutoCaseRuleCommand; +import edu.rpi.legup.history.EditDataCommand; +import edu.rpi.legup.history.ICommand; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.Board; @@ -14,26 +16,24 @@ import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.ui.DynamicView; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementSelection; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.SelectionItemView; import edu.rpi.legup.ui.proofeditorui.treeview.*; -import edu.rpi.legup.history.ICommand; -import edu.rpi.legup.history.EditDataCommand; - import java.awt.*; import java.awt.event.*; -import static edu.rpi.legup.app.GameBoardFacade.*; - -public class ElementController implements MouseListener, MouseMotionListener, ActionListener, KeyListener { +public class ElementController + implements MouseListener, MouseMotionListener, ActionListener, KeyListener { protected BoardView boardView; private Element selectedElement; /** - * ElementController Constructor controller to handles ui events associated interacting with a {@link BoardView} + * ElementController Constructor controller to handles ui events associated interacting with a + * {@link BoardView} */ public ElementController() { this.boardView = null; @@ -59,9 +59,7 @@ public void setBoardView(BoardView boardView) { * @param e the event to be processed */ @Override - public void mouseClicked(MouseEvent e) { - - } + public void mouseClicked(MouseEvent e) {} /** * Invoked when a mouse button has been pressed on a component. @@ -69,9 +67,7 @@ public void mouseClicked(MouseEvent e) { * @param e the event to be processed */ @Override - public void mousePressed(MouseEvent e) { - - } + public void mousePressed(MouseEvent e) {} /** * Invoked when a mouse button has been released on a component. @@ -100,21 +96,21 @@ public void mouseReleased(MouseEvent e) { if (elementView != null) { if (board instanceof CaseBoard) { CaseBoard caseBoard = (CaseBoard) board; - AutoCaseRuleCommand autoCaseRuleCommand = new AutoCaseRuleCommand(elementView, selection, caseBoard.getCaseRule(), caseBoard, e); + AutoCaseRuleCommand autoCaseRuleCommand = + new AutoCaseRuleCommand( + elementView, selection, caseBoard.getCaseRule(), caseBoard, e); if (autoCaseRuleCommand.canExecute()) { autoCaseRuleCommand.execute(); getInstance().getHistory().pushChange(autoCaseRuleCommand); if (treePanel != null) { treePanel.updateError(""); } - } - else { + } else { if (treePanel != null) { treePanel.updateError(autoCaseRuleCommand.getError()); } } - } - else { + } else { if (selection != null) { ICommand edit = new EditDataCommand(elementView, selection, e); if (edit.canExecute()) { @@ -123,8 +119,7 @@ public void mouseReleased(MouseEvent e) { if (treePanel != null) { treePanel.updateError(""); } - } - else { + } else { if (treePanel != null) { treePanel.updateError(edit.getError()); } @@ -132,20 +127,27 @@ public void mouseReleased(MouseEvent e) { } } } -// if (selectedElement != null) { + // if (selectedElement != null) { GridBoard b = (GridBoard) this.boardView.getBoard(); Point point = e.getPoint(); - Point scaledPoint = new Point((int) Math.floor(point.x / (30 * this.boardView.getScale())), (int) Math.floor(point.y / (30 * this.boardView.getScale()))); + Point scaledPoint = + new Point( + (int) Math.floor(point.x / (30 * this.boardView.getScale())), + (int) Math.floor(point.y / (30 * this.boardView.getScale()))); if (this.boardView.getBoard() instanceof TreeTentBoard) { scaledPoint.setLocation(scaledPoint.getX() - 1, scaledPoint.getY() - 1); } - System.out.printf("selected Element is NOT null, attempting to change board at (%d, %d)\n", scaledPoint.x, scaledPoint.y); -// System.out.println("Before: " + b.getCell(scaledPoint.x, scaledPoint.y).getData()); + System.out.printf( + "selected Element is NOT null, attempting to change board at (%d, %d)\n", + scaledPoint.x, scaledPoint.y); + // System.out.println("Before: " + b.getCell(scaledPoint.x, + // scaledPoint.y).getData()); b.setCell(scaledPoint.x, scaledPoint.y, this.selectedElement, e); -// System.out.println("After: " + b.getCell(scaledPoint.x, scaledPoint.y).getData()); -// } else { -// System.out.println("selected Element is null!"); -// } + // System.out.println("After: " + b.getCell(scaledPoint.x, + // scaledPoint.y).getData()); + // } else { + // System.out.println("selected Element is null!"); + // } boardView.repaint(); } @@ -175,7 +177,9 @@ public void mouseEntered(MouseEvent e) { selection.newHover(elementView); if (LegupPreferences.getInstance().getUserPrefAsBool(LegupPreferences.SHOW_MISTAKES)) { PuzzleElement element = elementView.getPuzzleElement(); - if (treeElement != null && treeElement.getType() == TreeElementType.TRANSITION && board.getModifiedData().contains(element)) { + if (treeElement != null + && treeElement.getType() == TreeElementType.TRANSITION + && board.getModifiedData().contains(element)) { TreeTransition transition = (TreeTransition) treeElement; if (transition.isJustified() && !transition.isCorrect()) { error = transition.getRule().checkRuleAt(transition, element); @@ -183,8 +187,7 @@ public void mouseEntered(MouseEvent e) { } if (error != null) { dynamicView.updateError(error); - } - else { + } else { dynamicView.resetStatus(); } } @@ -222,9 +225,7 @@ public void mouseExited(MouseEvent e) { * @param e the event to be processed */ @Override - public void mouseDragged(MouseEvent e) { - - } + public void mouseDragged(MouseEvent e) {} /** * Invoked when the mouse moved @@ -250,7 +251,9 @@ public void mouseMoved(MouseEvent e) { selection.newHover(elementView); if (LegupPreferences.getInstance().getUserPrefAsBool(LegupPreferences.SHOW_MISTAKES)) { PuzzleElement element = elementView.getPuzzleElement(); - if (treeElement != null && treeElement.getType() == TreeElementType.TRANSITION && board.getModifiedData().contains(element)) { + if (treeElement != null + && treeElement.getType() == TreeElementType.TRANSITION + && board.getModifiedData().contains(element)) { TreeTransition transition = (TreeTransition) treeElement; if (transition.isJustified() && !transition.isCorrect()) { error = transition.getRule().checkRuleAt(transition, element); @@ -258,8 +261,7 @@ public void mouseMoved(MouseEvent e) { } if (error != null) { dynamicView.updateError(error); - } - else { + } else { dynamicView.resetStatus(); } } @@ -267,9 +269,7 @@ public void mouseMoved(MouseEvent e) { } } - public void changeCell(MouseEvent e, PuzzleElement data) { - - } + public void changeCell(MouseEvent e, PuzzleElement data) {} /** * Invoked when an action occurs. @@ -299,8 +299,7 @@ public void actionPerformed(ActionEvent e) { if (puzzleElement.equalsData(prevBord.getPuzzleElement(puzzleElement))) { puzzleElement.setModified(false); - } - else { + } else { puzzleElement.setModified(true); } @@ -311,33 +310,26 @@ public void actionPerformed(ActionEvent e) { } /** - * Invoked when a key has been typed. - * See the class description for {@link KeyEvent} for a definition of - * a key typed event. + * Invoked when a key has been typed. See the class description for {@link KeyEvent} for a + * definition of a key typed event. * * @param e the event to be processed */ @Override - public void keyTyped(KeyEvent e) { - - } + public void keyTyped(KeyEvent e) {} /** - * Invoked when a key has been pressed. - * See the class description for {@link KeyEvent} for a definition of - * a key pressed event. + * Invoked when a key has been pressed. See the class description for {@link KeyEvent} for a + * definition of a key pressed event. * * @param e the event to be processed */ @Override - public void keyPressed(KeyEvent e) { - - } + public void keyPressed(KeyEvent e) {} /** - * Invoked when a key has been released. - * See the class description for {@link KeyEvent} for a definition of - * a key released event. + * Invoked when a key has been released. See the class description for {@link KeyEvent} for a + * definition of a key released event. * * @param e the event to be processed */ diff --git a/src/main/java/edu/rpi/legup/controller/RuleController.java b/src/main/java/edu/rpi/legup/controller/RuleController.java index fef6fca45..6e88dc4be 100644 --- a/src/main/java/edu/rpi/legup/controller/RuleController.java +++ b/src/main/java/edu/rpi/legup/controller/RuleController.java @@ -1,5 +1,7 @@ package edu.rpi.legup.controller; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.history.*; @@ -10,19 +12,16 @@ import edu.rpi.legup.ui.proofeditorui.rulesview.RuleButton; import edu.rpi.legup.ui.proofeditorui.rulesview.RulePanel; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class RuleController implements ActionListener { protected Object lastSource; /** - * RuleController Constructor creates a controller object to listen - * to ui events from a {@link RulePanel} + * RuleController Constructor creates a controller object to listen to ui events from a {@link + * RulePanel} */ public RuleController() { super(); @@ -51,56 +50,56 @@ public void buttonPressed(Rule rule) { if (caseRuleCommand.canExecute()) { caseRuleCommand.execute(); getInstance().getHistory().pushChange(caseRuleCommand); - } - else { + } else { updateErrorString = caseRuleCommand.getError(); } - } - else { - if (LegupPreferences.getInstance().getUserPref(LegupPreferences.AUTO_GENERATE_CASES).equalsIgnoreCase(Boolean.toString(true))) { + } else { + if (LegupPreferences.getInstance() + .getUserPref(LegupPreferences.AUTO_GENERATE_CASES) + .equalsIgnoreCase(Boolean.toString(true))) { CaseBoard caseBoard = caseRule.getCaseBoard(element.getBoard()); if (caseBoard != null && caseBoard.getCount() > 0) { - puzzle.notifyBoardListeners(listener -> listener.onCaseBoardAdded(caseBoard)); - } - else { + puzzle.notifyBoardListeners( + listener -> listener.onCaseBoardAdded(caseBoard)); + } else { updateErrorString = "This board cannot be applied with this case rule."; } - } - else { - updateErrorString = "Auto generated case rules are turned off in preferences."; + } else { + updateErrorString = + "Auto generated case rules are turned off in preferences."; } } - } - else { + } else { ICommand caseRuleCommand = new ValidateCaseRuleCommand(selection, caseRule); if (caseRuleCommand.canExecute()) { caseRuleCommand.execute(); getInstance().getHistory().pushChange(caseRuleCommand); - } - else { + } else { updateErrorString = caseRuleCommand.getError(); } } - } - else { + } else { if (rule.getRuleType() == RuleType.CONTRADICTION) { - ICommand validate = new ValidateContradictionRuleCommand(selection, (ContradictionRule) rule); + ICommand validate = + new ValidateContradictionRuleCommand(selection, (ContradictionRule) rule); if (validate.canExecute()) { getInstance().getHistory().pushChange(validate); validate.execute(); - } - else { + } else { updateErrorString = validate.getError(); } - } - else { - boolean def = LegupPreferences.getInstance().getUserPrefAsBool(LegupPreferences.ALLOW_DEFAULT_RULES); - ICommand validate = def ? new ApplyDefaultDirectRuleCommand(selection, (DirectRule) rule) : new ValidateDirectRuleCommand(selection, (DirectRule) rule); + } else { + boolean def = + LegupPreferences.getInstance() + .getUserPrefAsBool(LegupPreferences.ALLOW_DEFAULT_RULES); + ICommand validate = + def + ? new ApplyDefaultDirectRuleCommand(selection, (DirectRule) rule) + : new ValidateDirectRuleCommand(selection, (DirectRule) rule); if (validate.canExecute()) { getInstance().getHistory().pushChange(validate); validate.execute(); - } - else { + } else { updateErrorString = validate.getError(); } } diff --git a/src/main/java/edu/rpi/legup/controller/ToolbarController.java b/src/main/java/edu/rpi/legup/controller/ToolbarController.java index 2ee4ee25e..6cbb5ee60 100644 --- a/src/main/java/edu/rpi/legup/controller/ToolbarController.java +++ b/src/main/java/edu/rpi/legup/controller/ToolbarController.java @@ -1,19 +1,15 @@ package edu.rpi.legup.controller; import edu.rpi.legup.ui.LegupUI; - import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - public class ToolbarController implements ActionListener { private LegupUI legupUI; /** - * ToolbarController Constructor - creates a new {@link ToolbarController} to listen - * for button pressed from the tool mBar + * ToolbarController Constructor - creates a new {@link ToolbarController} to listen for button + * pressed from the tool mBar * * @param legupUI legupUI */ @@ -27,7 +23,5 @@ public ToolbarController(LegupUI legupUI) { * @param e action event */ @Override - public void actionPerformed(ActionEvent e) { - - } + public void actionPerformed(ActionEvent e) {} } diff --git a/src/main/java/edu/rpi/legup/controller/TreeController.java b/src/main/java/edu/rpi/legup/controller/TreeController.java index 6eae4ac3b..80fdee1af 100644 --- a/src/main/java/edu/rpi/legup/controller/TreeController.java +++ b/src/main/java/edu/rpi/legup/controller/TreeController.java @@ -1,25 +1,22 @@ package edu.rpi.legup.controller; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + 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.*; - -import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; - -import static edu.rpi.legup.app.GameBoardFacade.getInstance; +import javax.swing.*; public class TreeController extends Controller { /** - * TreeController Constructor creates a controller object to listen to ui events from a {@link TreePanel} + * TreeController Constructor creates a controller object to listen to ui events from a {@link + * TreePanel} */ - public TreeController() { - - } + public TreeController() {} /** * Mouse Clicked event no default action @@ -27,9 +24,7 @@ public TreeController() { * @param e MouseEvent object */ @Override - public void mouseClicked(MouseEvent e) { - - } + public void mouseClicked(MouseEvent e) {} /** * Mouse Pressed event sets the cursor to the move cursor and stores info for possible panning @@ -44,6 +39,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 @@ -58,19 +54,19 @@ public void mouseReleased(MouseEvent e) { if (treeElementView != null) { if (e.isShiftDown()) { selection.addToSelection(treeElementView); - } - else { + } else { if (e.isControlDown()) { - if (!(selection.getSelectedViews().size() == 1 && treeElementView == selection.getFirstSelection())) { + if (!(selection.getSelectedViews().size() == 1 + && treeElementView == selection.getFirstSelection())) { selection.toggleSelection(treeElementView); } - } - else { + } else { selection.newSelection(treeElementView); } } puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(treeElementView.getTreeElement())); + puzzle.notifyBoardListeners( + listener -> listener.onTreeElementChanged(treeElementView.getTreeElement())); } } @@ -88,7 +84,8 @@ public void mouseEntered(MouseEvent e) { TreeElementView treeElementView = treeView.getTreeElementView(point); Puzzle puzzle = getInstance().getPuzzleModule(); if (treeElementView != null) { - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(treeElementView.getTreeElement())); + puzzle.notifyBoardListeners( + listener -> listener.onTreeElementChanged(treeElementView.getTreeElement())); } } @@ -108,7 +105,8 @@ public void mouseExited(MouseEvent e) { selection.setMousePoint(null); if (elementView != null) { TreeElementView selectedView = selection.getFirstSelection(); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(selectedView.getTreeElement())); + puzzle.notifyBoardListeners( + listener -> listener.onTreeElementChanged(selectedView.getTreeElement())); } } @@ -137,15 +135,20 @@ public void mouseMoved(MouseEvent e) { TreeViewSelection selection = treeView.getSelection(); selection.setMousePoint(treeView.getActualPoint(e.getPoint())); if (treeElementView != null && treeElementView != selection.getHover()) { - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(treeElementView.getTreeElement())); + puzzle.notifyBoardListeners( + listener -> + listener.onTreeElementChanged(treeElementView.getTreeElement())); selection.newHover(treeElementView); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); - } - else { + } else { if (treeElementView == null && selection.getHover() != null) { - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(selection.getFirstSelection().getTreeElement())); + puzzle.notifyBoardListeners( + listener -> + listener.onTreeElementChanged( + selection.getFirstSelection().getTreeElement())); selection.clearHover(); - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); + puzzle.notifyTreeListeners( + listener -> listener.onTreeSelectionChanged(selection)); } } } diff --git a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java index 0aa6f416d..12b39cd85 100644 --- a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java @@ -6,7 +6,6 @@ import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; - import java.util.HashMap; import java.util.List; import java.util.Map; @@ -18,7 +17,8 @@ public class AddTreeElementCommand extends PuzzleCommand { private Map addChild; /** - * AddTreeElementCommand Constructor creates a command for adding a tree element to the proof tree + * AddTreeElementCommand Constructor creates a command for adding a tree element to the proof + * tree * * @param selection selection of tree elements views */ @@ -27,9 +27,7 @@ public AddTreeElementCommand(TreeViewSelection selection) { this.addChild = new HashMap<>(); } - /** - * Executes an command - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -43,12 +41,11 @@ public void executeCommand() { TreeElement child = addChild.get(treeElement); if (child == null) { child = tree.addTreeElement(treeElement); - } - else { + } else { + if (treeElement.getType() == TreeElementType.NODE) { child = tree.addTreeElement((TreeNode) treeElement, (TreeTransition) child); - } - else { + } else { child = tree.addTreeElement((TreeTransition) treeElement, (TreeNode) child); } } @@ -69,15 +66,14 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { List selectedViews = selection.getSelectedViews(); if (selectedViews.isEmpty()) { return CommandError.NO_SELECTED_VIEWS.toString(); - } - else { + } else { for (TreeElementView view : selectedViews) { TreeElement element = view.getTreeElement(); if (element.getType() == TreeElementType.TRANSITION) { @@ -85,8 +81,7 @@ public String getErrorString() { if (transition.getChildNode() != null) { return CommandError.ADD_WITH_CHILD.toString(); } - } - else { + } else { TreeNode node = (TreeNode) element; if (!node.getChildren().isEmpty()) { TreeTransition transition = node.getChildren().get(0); @@ -100,9 +95,7 @@ public String getErrorString() { return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -119,4 +112,4 @@ public void undoCommand() { } puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java b/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java index bc4c89741..02dffae44 100644 --- a/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java @@ -1,128 +1,129 @@ -package edu.rpi.legup.history; - -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.rules.DirectRule; -import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.proofeditorui.treeview.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ApplyDefaultDirectRuleCommand extends PuzzleCommand { - - private TreeViewSelection selection; - private DirectRule rule; - private Map addMap; - - /** - * ApplyDefaultDirectRuleCommand Constructor creates a command for applying the default of a basic rule - * - * @param selection selection of tree element views - * @param rule basic rule for the command - */ - public ApplyDefaultDirectRuleCommand(TreeViewSelection selection, DirectRule rule) { - this.selection = selection.copy(); - this.rule = rule; - this.addMap = new HashMap<>(); - } - - /** - * Gets the reason why the command cannot be executed - * - * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed - */ - @Override - public String getErrorString() { - List selectedViews = selection.getSelectedViews(); - if (selectedViews.isEmpty()) { - return CommandError.DEFAULT_APPLICATION + " - " + CommandError.NO_SELECTED_VIEWS.toString(); - } - else { - for (TreeElementView view : selectedViews) { - TreeElement element = view.getTreeElement(); - if (element.getType() == TreeElementType.NODE) { - TreeNode node = (TreeNode) element; - if (!node.getChildren().isEmpty()) { - return CommandError.DEFAULT_APPLICATION + " - " + CommandError.NO_CHILDREN.toString(); - } - else { - if (rule.getDefaultBoard(node) == null) { - return CommandError.DEFAULT_APPLICATION + " - " + "This selection contains a tree element that this rule cannot be applied to."; - } - } - } - else { - return CommandError.DEFAULT_APPLICATION + " - " + CommandError.SELECTION_CONTAINS_TRANSITION.toString(); - } - } - } - return null; - } - - /** - * Executes an command - */ - @Override - public void executeCommand() { - Tree tree = GameBoardFacade.getInstance().getTree(); - TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - final TreeViewSelection newSelection = new TreeViewSelection(); - - for (TreeElementView selectedView : selection.getSelectedViews()) { - TreeNodeView nodeView = (TreeNodeView) selectedView; - TreeNode node = nodeView.getTreeElement(); - TreeTransition transition = addMap.get(node); - TreeNode childNode; - if (transition == null) { - transition = (TreeTransition) tree.addTreeElement(node); - childNode = (TreeNode) tree.addTreeElement(transition); - addMap.put(node, transition); - } - else { - tree.addTreeElement(node, transition); - childNode = transition.getChildNode(); - } - - transition.setRule(rule); - Board defaultBoard = rule.getDefaultBoard(node); - transition.setBoard(defaultBoard); - Board copyBoard = defaultBoard.copy(); - copyBoard.setModifiable(false); - childNode.setBoard(copyBoard); - - final TreeTransition finalTran = transition; - puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalTran)); - - newSelection.addToSelection(treeView.getElementView(childNode)); - } - - final TreeElement finalTreeElement = newSelection.getFirstSelection().getTreeElement(); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); - } - - /** - * Undoes an command - */ - @Override - public void undoCommand() { - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - - for (TreeElementView selectedView : selection.getSelectedViews()) { - TreeNodeView nodeView = (TreeNodeView) selectedView; - TreeNode node = nodeView.getTreeElement(); - final TreeTransition transition = addMap.get(node); - - puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(transition)); - } - - final TreeElement finalTreeElement = selection.getFirstSelection().getTreeElement(); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); - } -} +package edu.rpi.legup.history; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ApplyDefaultDirectRuleCommand extends PuzzleCommand { + + private TreeViewSelection selection; + private DirectRule rule; + private Map addMap; + + /** + * ApplyDefaultDirectRuleCommand Constructor creates a command for applying the default of a + * basic rule + * + * @param selection selection of tree element views + * @param rule basic rule for the command + */ + public ApplyDefaultDirectRuleCommand(TreeViewSelection selection, DirectRule rule) { + this.selection = selection.copy(); + this.rule = rule; + this.addMap = new HashMap<>(); + } + + /** + * Gets the reason why the command cannot be executed + * + * @return if command cannot be executed, returns reason for why the command cannot be executed, + * otherwise null if command can be executed + */ + @Override + public String getErrorString() { + List selectedViews = selection.getSelectedViews(); + if (selectedViews.isEmpty()) { + return CommandError.DEFAULT_APPLICATION + + " - " + + CommandError.NO_SELECTED_VIEWS.toString(); + } else { + for (TreeElementView view : selectedViews) { + TreeElement element = view.getTreeElement(); + if (element.getType() == TreeElementType.NODE) { + TreeNode node = (TreeNode) element; + if (!node.getChildren().isEmpty()) { + + return CommandError.DEFAULT_APPLICATION + + " - " + + CommandError.NO_CHILDREN.toString(); + } else { + if (rule.getDefaultBoard(node) == null) { + return CommandError.DEFAULT_APPLICATION + + " - This selection contains a tree element that this rule" + + " cannot be applied to."; + } + } + } else { + return CommandError.DEFAULT_APPLICATION + + " - " + + CommandError.SELECTION_CONTAINS_TRANSITION.toString(); + } + } + } + return null; + } + + /** Executes an command */ + @Override + public void executeCommand() { + Tree tree = GameBoardFacade.getInstance().getTree(); + TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + final TreeViewSelection newSelection = new TreeViewSelection(); + + for (TreeElementView selectedView : selection.getSelectedViews()) { + TreeNodeView nodeView = (TreeNodeView) selectedView; + TreeNode node = nodeView.getTreeElement(); + TreeTransition transition = addMap.get(node); + TreeNode childNode; + if (transition == null) { + transition = (TreeTransition) tree.addTreeElement(node); + childNode = (TreeNode) tree.addTreeElement(transition); + addMap.put(node, transition); + } else { + tree.addTreeElement(node, transition); + childNode = transition.getChildNode(); + } + + transition.setRule(rule); + Board defaultBoard = rule.getDefaultBoard(node); + transition.setBoard(defaultBoard); + Board copyBoard = defaultBoard.copy(); + copyBoard.setModifiable(false); + childNode.setBoard(copyBoard); + + final TreeTransition finalTran = transition; + puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalTran)); + + newSelection.addToSelection(treeView.getElementView(childNode)); + } + + final TreeElement finalTreeElement = newSelection.getFirstSelection().getTreeElement(); + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); + puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); + } + + /** Undoes an command */ + @Override + public void undoCommand() { + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + + for (TreeElementView selectedView : selection.getSelectedViews()) { + TreeNodeView nodeView = (TreeNodeView) selectedView; + TreeNode node = nodeView.getTreeElement(); + final TreeTransition transition = addMap.get(node); + + puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(transition)); + } + + final TreeElement finalTreeElement = selection.getFirstSelection().getTreeElement(); + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); + puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); + } +} diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index a4c157c77..54e33756a 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -1,5 +1,7 @@ package edu.rpi.legup.history; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; @@ -8,12 +10,9 @@ import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.awt.event.MouseEvent; import java.util.*; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class AutoCaseRuleCommand extends PuzzleCommand { private ElementView elementView; @@ -30,12 +29,17 @@ public class AutoCaseRuleCommand extends PuzzleCommand { * AutoCaseRuleCommand Constructor creates a command for validating a case rule * * @param elementView currently selected puzzle puzzleElement view that is being edited - * @param selection currently selected tree puzzleElement views that is being edited + * @param selection currently selected tree puzzleElement views that is being edited * @param caseRule currently selected caseRule puzzleElement view that is being edited * @param caseBoard currently selected caseBoard puzzleElement view that is being edited * @param mouseEvent currently selected mouseEvent puzzleElement view that is being edited */ - public AutoCaseRuleCommand(ElementView elementView, TreeViewSelection selection, CaseRule caseRule, CaseBoard caseBoard, MouseEvent mouseEvent) { + public AutoCaseRuleCommand( + ElementView elementView, + TreeViewSelection selection, + CaseRule caseRule, + CaseBoard caseBoard, + MouseEvent mouseEvent) { this.elementView = elementView; this.selection = selection.copy(); this.caseRule = caseRule; @@ -44,9 +48,7 @@ public AutoCaseRuleCommand(ElementView elementView, TreeViewSelection selection, this.caseTrans = new ArrayList<>(); } - /** - * Executes an command - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -56,7 +58,8 @@ public void executeCommand() { TreeNode node = (TreeNode) selection.getFirstSelection().getTreeElement(); if (caseTrans.isEmpty()) { - List cases = caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()); + List cases = + caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()); for (Board board : cases) { final TreeTransition transition = (TreeTransition) tree.addTreeElement(node); board.setModifiable(false); @@ -70,8 +73,7 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(transition)); newSelection.addToSelection(treeView.getElementView(childNode)); } - } - else { + } else { for (final TreeTransition transition : caseTrans) { tree.addTreeElement(node, transition); TreeNode childNode = transition.getChildNode(); @@ -89,7 +91,7 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -111,20 +113,20 @@ public String getErrorString() { return "The selected data element is not pickable with this case rule."; } - if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() == 0) { + if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() + == 0) { return "The selection must produce at least one case"; } - if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() > MAX_CASES) { + if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() + > MAX_CASES) { return "The selection can produce a max of " + MAX_CASES + " cases"; } return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/history/CommandError.java b/src/main/java/edu/rpi/legup/history/CommandError.java index 90c9db526..35b7bb15b 100644 --- a/src/main/java/edu/rpi/legup/history/CommandError.java +++ b/src/main/java/edu/rpi/legup/history/CommandError.java @@ -1,7 +1,6 @@ package edu.rpi.legup.history; public enum CommandError { - NO_SELECTED_VIEWS("The selection does not have any tree elements."), ONE_SELECTED_VIEW("The selection must have exactly one tree element."), UNMODIFIABLE_BOARD("The selection contains a board which is not modifiable."), diff --git a/src/main/java/edu/rpi/legup/history/CommandState.java b/src/main/java/edu/rpi/legup/history/CommandState.java index b7c2ff938..f47c0405d 100644 --- a/src/main/java/edu/rpi/legup/history/CommandState.java +++ b/src/main/java/edu/rpi/legup/history/CommandState.java @@ -1,7 +1,10 @@ package edu.rpi.legup.history; public enum CommandState { - CREATED("Created"), EXECUTED("Executed"), UNDOED("Undoed"), REDOED("Redoed"); + CREATED("Created"), + EXECUTED("Executed"), + UNDOED("Undoed"), + REDOED("Redoed"); private String value; diff --git a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java index 80cad9b24..0469685c1 100644 --- a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java @@ -5,14 +5,14 @@ import edu.rpi.legup.model.observer.ITreeListener; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.util.List; public class DeleteTreeElementCommand extends PuzzleCommand { private TreeViewSelection selection; /** - * DeleteTreeElementCommand Constructor creates a PuzzleCommand for deleting a tree puzzleElement + * DeleteTreeElementCommand Constructor creates a PuzzleCommand for deleting a tree + * puzzleElement * * @param selection the currently selected tree elements before the command is executed */ @@ -20,9 +20,7 @@ public DeleteTreeElementCommand(TreeViewSelection selection) { this.selection = selection.copy(); } - /** - * Executes an command - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -35,8 +33,7 @@ public void executeCommand() { if (firstSelectedView.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) firstSelectedView; newSelectedView = nodeView.getParentView(); - } - else { + } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; newSelectedView = transitionView.getParentViews().get(0); } @@ -48,15 +45,17 @@ public void executeCommand() { } final TreeViewSelection newSelection = new TreeViewSelection(newSelectedView); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(newSelectedView.getTreeElement())); - puzzle.notifyTreeListeners((ITreeListener listener) -> listener.onTreeSelectionChanged(newSelection)); + puzzle.notifyBoardListeners( + listener -> listener.onTreeElementChanged(newSelectedView.getTreeElement())); + puzzle.notifyTreeListeners( + (ITreeListener listener) -> listener.onTreeSelectionChanged(newSelection)); } /** * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -74,9 +73,7 @@ public String getErrorString() { return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); @@ -89,8 +86,7 @@ public void undoCommand() { node.getParent().setChildNode(node); puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(node)); - } - else { + } else { TreeTransition transition = (TreeTransition) element; transition.getParents().forEach(node -> node.addChild(transition)); transition.getParents().get(0).getChildren().forEach(TreeTransition::reverify); @@ -99,7 +95,10 @@ public void undoCommand() { } } - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(selection.getFirstSelection().getTreeElement())); + puzzle.notifyBoardListeners( + listener -> + listener.onTreeElementChanged( + selection.getFirstSelection().getTreeElement())); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); } } diff --git a/src/main/java/edu/rpi/legup/history/EditDataCommand.java b/src/main/java/edu/rpi/legup/history/EditDataCommand.java index 328cc050d..d65f03d66 100644 --- a/src/main/java/edu/rpi/legup/history/EditDataCommand.java +++ b/src/main/java/edu/rpi/legup/history/EditDataCommand.java @@ -1,5 +1,7 @@ package edu.rpi.legup.history; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; @@ -8,12 +10,9 @@ import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.awt.event.MouseEvent; import java.util.List; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class EditDataCommand extends PuzzleCommand { private TreeTransition transition; private PuzzleElement savePuzzleElement; @@ -27,8 +26,8 @@ public class EditDataCommand extends PuzzleCommand { * EditDataCommand Constructor create a puzzle command for editing a board * * @param elementView currently selected puzzle puzzleElement view that is being edited - * @param selection currently selected tree puzzleElement views that is being edited - * @param event mouse event + * @param selection currently selected tree puzzleElement views that is being edited + * @param event mouse event */ public EditDataCommand(ElementView elementView, TreeViewSelection selection, MouseEvent event) { this.elementView = elementView; @@ -39,9 +38,7 @@ public EditDataCommand(ElementView elementView, TreeViewSelection selection, Mou this.transition = null; } - /** - * Executes a command - */ + /** Executes a command */ @SuppressWarnings("unchecked") @Override public void executeCommand() { @@ -69,8 +66,7 @@ public void executeCommand() { puzzleElement = board.getPuzzleElement(selectedPuzzleElement); savePuzzleElement = puzzleElement.copy(); - } - else { + } else { transition = (TreeTransition) treeElement; puzzleElement = board.getPuzzleElement(selectedPuzzleElement); savePuzzleElement = puzzleElement.copy(); @@ -82,8 +78,7 @@ public void executeCommand() { if (prevBoard.getPuzzleElement(selectedPuzzleElement).equalsData(puzzleElement)) { board.removeModifiedData(puzzleElement); - } - else { + } else { board.addModifiedData(puzzleElement); } transition.propagateChange(puzzleElement); @@ -92,7 +87,8 @@ public void executeCommand() { puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(puzzleElement)); - final TreeViewSelection newSelection = new TreeViewSelection(treeView.getElementView(transition)); + final TreeViewSelection newSelection = + new TreeViewSelection(treeView.getElementView(transition)); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } @@ -100,7 +96,7 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -116,19 +112,16 @@ public String getErrorString() { TreeNodeView nodeView = (TreeNodeView) selectedView; if (!nodeView.getChildrenViews().isEmpty()) { return CommandError.UNMODIFIABLE_BOARD.toString(); - } - else { + } else { if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { return CommandError.UNMODIFIABLE_DATA.toString(); } } - } - else { + } else { TreeTransitionView transitionView = (TreeTransitionView) selectedView; if (!transitionView.getTreeElement().getBoard().isModifiable()) { return CommandError.UNMODIFIABLE_BOARD.toString(); - } - else { + } else { if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { return CommandError.UNMODIFIABLE_DATA.toString(); } @@ -137,9 +130,7 @@ public String getErrorString() { return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @SuppressWarnings("unchecked") @Override public void undoCommand() { @@ -152,7 +143,8 @@ public void undoCommand() { if (selectedView.getType() == TreeElementType.NODE) { tree.removeTreeElement(transition); - puzzle.notifyTreeListeners((ITreeListener listener) -> listener.onTreeElementRemoved(transition)); + puzzle.notifyTreeListeners( + (ITreeListener listener) -> listener.onTreeElementRemoved(transition)); } Board prevBoard = transition.getParents().get(0).getBoard(); @@ -162,8 +154,7 @@ public void undoCommand() { if (prevBoard.getPuzzleElement(selectedPuzzleElement).equalsData(puzzleElement)) { board.removeModifiedData(puzzleElement); - } - else { + } else { board.addModifiedData(puzzleElement); } transition.propagateChange(puzzleElement); diff --git a/src/main/java/edu/rpi/legup/history/History.java b/src/main/java/edu/rpi/legup/history/History.java index 77e3fae94..371284f8c 100644 --- a/src/main/java/edu/rpi/legup/history/History.java +++ b/src/main/java/edu/rpi/legup/history/History.java @@ -1,10 +1,8 @@ package edu.rpi.legup.history; import edu.rpi.legup.app.GameBoardFacade; - import java.util.ArrayList; import java.util.List; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,10 +14,9 @@ public class History { private int curIndex; /** - * History Constructor this holds information about changes to the board - * and Tree structure for undoing and redoing operations. Though history is - * an List, it is implemented like a stack. The curIndex points to the - * top of the stack (where the last change was made). + * History Constructor this holds information about changes to the board and Tree structure for + * undoing and redoing operations. Though history is an List, it is implemented like a stack. + * The curIndex points to the top of the stack (where the last change was made). */ public History() { history = new ArrayList<>(); @@ -27,9 +24,9 @@ public History() { } /** - * Pushes a change to the history list and increments the current index. - * If the current index does not point to the top of the stack, then at least - * 1 undo operation was called and that information will be lost by the next change + * Pushes a change to the history list and increments the current index. If the current index + * does not point to the top of the stack, then at least 1 undo operation was called and that + * information will be lost by the next change * * @param command command to be pushed onto the stack */ @@ -47,37 +44,35 @@ public void pushChange(ICommand command) { } } - /** - * Undoes an action - */ + /** Undoes an action */ public void undo() { synchronized (lock) { if (curIndex > -1) { ICommand command = history.get(curIndex--); command.undo(); LOGGER.info("Undoed " + command.getClass().getSimpleName()); - GameBoardFacade.getInstance().notifyHistoryListeners(l -> l.onUndo(curIndex < 0, curIndex == history.size() - 1)); + GameBoardFacade.getInstance() + .notifyHistoryListeners( + l -> l.onUndo(curIndex < 0, curIndex == history.size() - 1)); } } } - /** - * Redoes an action - */ + /** Redoes an action */ public void redo() { synchronized (lock) { if (curIndex < history.size() - 1) { ICommand command = history.get(++curIndex); command.redo(); LOGGER.info("Redoed " + command.getClass().getSimpleName()); - GameBoardFacade.getInstance().notifyHistoryListeners(l -> l.onRedo(curIndex < 0, curIndex == history.size() - 1)); + GameBoardFacade.getInstance() + .notifyHistoryListeners( + l -> l.onRedo(curIndex < 0, curIndex == history.size() - 1)); } } } - /** - * Clears all actions from the history stack - */ + /** Clears all actions from the history stack */ public void clear() { history.clear(); curIndex = -1; diff --git a/src/main/java/edu/rpi/legup/history/ICommand.java b/src/main/java/edu/rpi/legup/history/ICommand.java index bc82c4df5..913d9daaf 100644 --- a/src/main/java/edu/rpi/legup/history/ICommand.java +++ b/src/main/java/edu/rpi/legup/history/ICommand.java @@ -1,13 +1,12 @@ package edu.rpi.legup.history; public interface ICommand { - /** - * Executes a command - */ + /** Executes a command */ void execute(); /** * Determines whether this command can be executed + * * @return true if can execute, false otherwise */ boolean canExecute(); @@ -16,17 +15,13 @@ public interface ICommand { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ String getError(); - /** - * Undoes a command - */ + /** Undoes a command */ void undo(); - /** - * Redoes a command - */ + /** Redoes a command */ void redo(); -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/history/IHistoryListener.java b/src/main/java/edu/rpi/legup/history/IHistoryListener.java index 5201a09c5..f464941d6 100644 --- a/src/main/java/edu/rpi/legup/history/IHistoryListener.java +++ b/src/main/java/edu/rpi/legup/history/IHistoryListener.java @@ -12,7 +12,7 @@ public interface IHistoryListener { * Called when an action is undone * * @param isBottom true if there are no more actions to undo, false otherwise - * @param isTop true if there are no more changes to redo, false otherwise + * @param isTop true if there are no more changes to redo, false otherwise */ void onUndo(boolean isBottom, boolean isTop); @@ -20,12 +20,10 @@ public interface IHistoryListener { * Called when an action is redone * * @param isBottom true if there are no more actions to undo, false otherwise - * @param isTop true if there are no more changes to redo, false otherwise + * @param isTop true if there are no more changes to redo, false otherwise */ void onRedo(boolean isBottom, boolean isTop); - /** - * Called when the edu.rpi.legup.history is cleared - */ + /** Called when the edu.rpi.legup.history is cleared */ void onClearHistory(); } diff --git a/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java b/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java index 7e79814a6..71d072328 100644 --- a/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java +++ b/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java @@ -2,7 +2,14 @@ public class InvalidCommandStateTransition extends RuntimeException { - public InvalidCommandStateTransition(PuzzleCommand puzzleCommand, CommandState from, CommandState to) { - super("PuzzleCommand - " + puzzleCommand.getClass().getSimpleName() + " - Attempted invalid command state transition from " + from + " to " + to); + public InvalidCommandStateTransition( + PuzzleCommand puzzleCommand, CommandState from, CommandState to) { + super( + "PuzzleCommand - " + + puzzleCommand.getClass().getSimpleName() + + " - Attempted invalid command state transition from " + + from + + " to " + + to); } } diff --git a/src/main/java/edu/rpi/legup/history/MergeCommand.java b/src/main/java/edu/rpi/legup/history/MergeCommand.java index 2091ecf0a..f234a0884 100644 --- a/src/main/java/edu/rpi/legup/history/MergeCommand.java +++ b/src/main/java/edu/rpi/legup/history/MergeCommand.java @@ -6,7 +6,6 @@ import edu.rpi.legup.model.rules.MergeRule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -25,9 +24,7 @@ public MergeCommand(TreeViewSelection selection) { this.transition = null; } - /** - * Executes an command - */ + /** Executes an command */ @Override public void executeCommand() { List selectedViews = selection.getSelectedViews(); @@ -55,8 +52,7 @@ public void executeCommand() { transition.setRule(new MergeRule()); transition.setChildNode(mergedNode); mergedNode.setParent(transition); - } - else { + } else { mergedNode = transition.getChildNode(); } @@ -75,15 +71,14 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - TreeTransition transition = ((TreeNode) selection.getFirstSelection().getTreeElement()).getChildren().get(0); + TreeTransition transition = + ((TreeNode) selection.getFirstSelection().getTreeElement()).getChildren().get(0); tree.removeTreeElement(transition); puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(transition)); @@ -94,7 +89,7 @@ public void undoCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -112,8 +107,7 @@ public String getErrorString() { return CommandError.NO_CHILDREN.toString(); } nodeList.add(nodeView.getTreeElement()); - } - else { + } else { return CommandError.SELECTION_CONTAINS_TRANSITION.toString(); } } @@ -130,12 +124,12 @@ public String getErrorString() { } Set leafNodes = tree.getLeafTreeElements(lca); if (leafNodes.size() != mergingNodes.size()) { -// return "Unable to merge tree elements."; + // return "Unable to merge tree elements."; } for (TreeNode node : mergingNodes) { if (!leafNodes.contains(node)) { -// return "Unable to merge tree elements."; + // return "Unable to merge tree elements."; } } diff --git a/src/main/java/edu/rpi/legup/history/PuzzleCommand.java b/src/main/java/edu/rpi/legup/history/PuzzleCommand.java index 0c96b16f5..3768e3cbd 100644 --- a/src/main/java/edu/rpi/legup/history/PuzzleCommand.java +++ b/src/main/java/edu/rpi/legup/history/PuzzleCommand.java @@ -1,25 +1,18 @@ package edu.rpi.legup.history; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - public abstract class PuzzleCommand implements ICommand { private CommandState state; private boolean isCached; private String cachedError; - /** - * Puzzle Command Constructor for creating an undoable and redoable change to the model. - */ + /** Puzzle Command Constructor for creating an undoable and redoable change to the model. */ protected PuzzleCommand() { this.state = CommandState.CREATED; this.isCached = false; this.cachedError = null; } - /** - * Executes an command - */ + /** Executes an command */ @Override public final void execute() { if (canExecute()) { @@ -28,9 +21,7 @@ public final void execute() { } } - /** - * Determines whether this command can be executed - */ + /** Determines whether this command can be executed */ @Override public final boolean canExecute() { cachedError = getError(); @@ -42,14 +33,13 @@ public final boolean canExecute() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public final String getError() { if (isCached) { return cachedError; - } - else { + } else { return getErrorString(); } } @@ -58,57 +48,44 @@ public final String getError() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ public abstract String getErrorString(); - /** - * Executes an command - */ + /** Executes an command */ public abstract void executeCommand(); - /** - * Undoes an command - */ + /** Undoes an command */ public abstract void undoCommand(); - /** - * Redoes an command - */ + /** Redoes an command */ public void redoCommand() { if (state == CommandState.UNDOED) { executeCommand(); state = CommandState.REDOED; - } - else { + } else { throw new InvalidCommandStateTransition(this, state, CommandState.REDOED); } } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public final void undo() { if (state == CommandState.EXECUTED || state == CommandState.REDOED) { undoCommand(); state = CommandState.UNDOED; - } - else { + } else { throw new InvalidCommandStateTransition(this, state, CommandState.UNDOED); } } - /** - * Redoes an command - */ + /** Redoes an command */ public final void redo() { if (state == CommandState.UNDOED) { redoCommand(); state = CommandState.REDOED; - } - else { + } else { throw new InvalidCommandStateTransition(this, state, CommandState.REDOED); } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java index 398f17478..7737ecfd3 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java @@ -1,18 +1,17 @@ package edu.rpi.legup.history; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.util.HashMap; import java.util.List; import java.util.Map; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class ValidateCaseRuleCommand extends PuzzleCommand { private TreeViewSelection selection; @@ -34,9 +33,7 @@ public ValidateCaseRuleCommand(TreeViewSelection selection, CaseRule caseRule) { this.addNode = new HashMap<>(); } - /** - * Executes an command - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -58,8 +55,7 @@ public void executeCommand() { if (childNode == null) { childNode = (TreeNode) tree.addTreeElement(transition); addNode.put(transition, childNode); - } - else { + } else { childNode = (TreeNode) tree.addTreeElement(transition, childNode); } @@ -75,8 +71,7 @@ public void executeCommand() { if (firstSelectedView.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) firstSelectedView; finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); - } - else { + } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; finalTreeElement = transitionView.getChildView().getTreeElement(); } @@ -88,7 +83,7 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -100,8 +95,7 @@ public String getErrorString() { for (TreeElementView view : selectedViews) { if (view.getType() == TreeElementType.NODE) { return CommandError.SELECTION_CONTAINS_NODE.toString(); - } - else { + } else { TreeTransitionView transView = (TreeTransitionView) view; if (transView.getParentViews().size() > 1) { return CommandError.CONTAINS_MERGE.toString(); @@ -111,9 +105,7 @@ public String getErrorString() { return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); @@ -136,4 +128,4 @@ public void undoCommand() { puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java index 23f8dce21..8737b4008 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java @@ -5,7 +5,6 @@ import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -19,10 +18,11 @@ public class ValidateContradictionRuleCommand extends PuzzleCommand { private Map addTran; /** - * ValidateContradictionRuleCommand Constructor creates a puzzle command for verifying a contradiction rule + * ValidateContradictionRuleCommand Constructor creates a puzzle command for verifying a + * contradiction rule * * @param selection currently selected tree puzzleElement views - * @param rule contradiction rule to be set to all the tree elements + * @param rule contradiction rule to be set to all the tree elements */ public ValidateContradictionRuleCommand(TreeViewSelection selection, ContradictionRule rule) { this.selection = selection.copy(); @@ -31,9 +31,7 @@ public ValidateContradictionRuleCommand(TreeViewSelection selection, Contradicti this.addTran = new HashMap<>(); } - /** - * Executes a command - */ + /** Executes a command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -48,8 +46,7 @@ public void executeCommand() { if (treeElement.getType() == TreeElementType.TRANSITION) { TreeTransition transition = (TreeTransition) treeElement; treeNode = transition.getParents().get(0); - } - else { + } else { treeNode = (TreeNode) treeElement; } @@ -58,7 +55,11 @@ public void executeCommand() { saveElements.put(treeNode, save); } - treeNode.getChildren().forEach(n -> puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(n))); + treeNode.getChildren() + .forEach( + n -> + puzzle.notifyTreeListeners( + listener -> listener.onTreeElementRemoved(n))); treeNode.getChildren().clear(); @@ -68,8 +69,7 @@ public void executeCommand() { transition.setRule(newRule); transition.getBoard().setModifiable(false); tree.addTreeElement(transition); - } - else { + } else { transition.getBoard().setModifiable(false); tree.addTreeElement(treeNode, transition); } @@ -85,19 +85,18 @@ public void executeCommand() { if (firstSelectedView.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) firstSelectedView; finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); - } - else { + } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; if (transitionView.getChildView() != null) { finalTreeElement = transitionView.getChildView().getTreeElement(); - } - else { + } else { finalTreeElement = null; } } if (finalTreeElement != null) { - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); + puzzle.notifyBoardListeners( + listener -> listener.onTreeElementChanged(finalTreeElement)); } puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } @@ -106,7 +105,7 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -126,9 +125,7 @@ public String getErrorString() { return null; } - /** - * Undoes a command - */ + /** Undoes a command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); @@ -140,18 +137,25 @@ public void undoCommand() { if (element.getType() == TreeElementType.TRANSITION) { TreeTransition transition = (TreeTransition) element; node = transition.getParents().get(0); - } - else { + } else { node = (TreeNode) element; } - node.getChildren().forEach(n -> puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(n))); + node.getChildren() + .forEach( + n -> + puzzle.notifyTreeListeners( + listener -> listener.onTreeElementRemoved(n))); node.getChildren().clear(); ArrayList save = saveElements.get(node); if (save != null) { node.getChildren().addAll(save); - node.getChildren().forEach(n -> puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(n))); + node.getChildren() + .forEach( + n -> + puzzle.notifyTreeListeners( + listener -> listener.onTreeElementAdded(n))); } } diff --git a/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java index 61babe883..d9c063464 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java @@ -1,152 +1,142 @@ -package edu.rpi.legup.history; - -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.model.rules.DirectRule; -import edu.rpi.legup.model.rules.Rule; -import edu.rpi.legup.model.tree.*; -import edu.rpi.legup.ui.proofeditorui.treeview.*; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ValidateDirectRuleCommand extends PuzzleCommand { - private TreeViewSelection selection; - - private Map oldRules; - private Map addNode; - private DirectRule newRule; - - /** - * ValidateDesireRuleCommand Constructor creates a command for verifying a basic rule - * - * @param selection selection of tree elements - * @param rule basic rule - */ - public ValidateDirectRuleCommand(TreeViewSelection selection, DirectRule rule) { - this.selection = selection.copy(); - this.newRule = rule; - this.oldRules = new HashMap<>(); - this.addNode = new HashMap<>(); - } - - /** - * Executes an command - */ - @Override - public void executeCommand() { - Tree tree = GameBoardFacade.getInstance().getTree(); - TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - final TreeViewSelection newSelection = new TreeViewSelection(); - - List selectedViews = selection.getSelectedViews(); - for (TreeElementView selectedView : selectedViews) { - TreeElement element = selectedView.getTreeElement(); - TreeTransitionView transitionView; - if (element.getType() == TreeElementType.NODE) { - TreeNodeView nodeView = (TreeNodeView) selectedView; - transitionView = nodeView.getChildrenViews().get(0); - } - else { - transitionView = (TreeTransitionView) selectedView; - } - TreeTransition transition = transitionView.getTreeElement(); - - oldRules.put(transition, transition.getRule()); - transition.setRule(newRule); - - TreeNode childNode = transition.getChildNode(); - if (childNode == null) { - childNode = addNode.get(transition); - if (childNode == null) { - childNode = (TreeNode) tree.addTreeElement(transition); - addNode.put(transition, childNode); - } - else { - tree.addTreeElement(transition, childNode); - } - - final TreeNode finalNode = childNode; - puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalNode)); - } - newSelection.addToSelection(treeView.getElementView(childNode)); - } - TreeElementView firstSelectedView = selection.getFirstSelection(); - final TreeElement finalTreeElement; - if (firstSelectedView.getType() == TreeElementType.NODE) { - TreeNodeView nodeView = (TreeNodeView) firstSelectedView; - finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); - } - else { - TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; - finalTreeElement = transitionView.getChildView().getTreeElement(); - } - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); - } - - /** - * Gets the reason why the command cannot be executed - * - * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed - */ - @Override - public String getErrorString() { - List selectedViews = selection.getSelectedViews(); - if (selectedViews.isEmpty()) { - return CommandError.NO_SELECTED_VIEWS.toString(); - } - - for (TreeElementView view : selectedViews) { - if (view.getType() == TreeElementType.NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - if (nodeView.getChildrenViews().size() != 1) { - return CommandError.ONE_CHILD.toString(); - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - if (transView.getParentViews().size() > 1) { - return CommandError.CONTAINS_MERGE.toString(); - } - } - } - return null; - } - - /** - * Undoes an command - */ - @Override - public void undoCommand() { - Tree tree = GameBoardFacade.getInstance().getTree(); - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - - for (TreeElementView selectedView : selection.getSelectedViews()) { - TreeElement element = selectedView.getTreeElement(); - TreeTransitionView transitionView; - if (element.getType() == TreeElementType.NODE) { - TreeNodeView nodeView = (TreeNodeView) selectedView; - transitionView = nodeView.getChildrenViews().get(0); - } - else { - transitionView = (TreeTransitionView) selectedView; - } - TreeTransition transition = transitionView.getTreeElement(); - transition.setRule(oldRules.get(transition)); - - if (addNode.get(transition) != null) { - final TreeNode childNode = transition.getChildNode(); - tree.removeTreeElement(childNode); - puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(childNode)); - } - } - - final TreeElement finalTreeElement = selection.getFirstSelection().getTreeElement(); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); - puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); - } -} +package edu.rpi.legup.history; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.rules.Rule; +import edu.rpi.legup.model.tree.*; +import edu.rpi.legup.ui.proofeditorui.treeview.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ValidateDirectRuleCommand extends PuzzleCommand { + private TreeViewSelection selection; + + private Map oldRules; + private Map addNode; + private DirectRule newRule; + + /** + * ValidateDesireRuleCommand Constructor creates a command for verifying a basic rule + * + * @param selection selection of tree elements + * @param rule basic rule + */ + public ValidateDirectRuleCommand(TreeViewSelection selection, DirectRule rule) { + this.selection = selection.copy(); + this.newRule = rule; + this.oldRules = new HashMap<>(); + this.addNode = new HashMap<>(); + } + + /** Executes an command */ + @Override + public void executeCommand() { + Tree tree = GameBoardFacade.getInstance().getTree(); + TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + final TreeViewSelection newSelection = new TreeViewSelection(); + + List selectedViews = selection.getSelectedViews(); + for (TreeElementView selectedView : selectedViews) { + TreeElement element = selectedView.getTreeElement(); + TreeTransitionView transitionView; + if (element.getType() == TreeElementType.NODE) { + TreeNodeView nodeView = (TreeNodeView) selectedView; + transitionView = nodeView.getChildrenViews().get(0); + } else { + transitionView = (TreeTransitionView) selectedView; + } + TreeTransition transition = transitionView.getTreeElement(); + + oldRules.put(transition, transition.getRule()); + transition.setRule(newRule); + + TreeNode childNode = transition.getChildNode(); + if (childNode == null) { + childNode = addNode.get(transition); + if (childNode == null) { + childNode = (TreeNode) tree.addTreeElement(transition); + addNode.put(transition, childNode); + } else { + tree.addTreeElement(transition, childNode); + } + + final TreeNode finalNode = childNode; + puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalNode)); + } + newSelection.addToSelection(treeView.getElementView(childNode)); + } + TreeElementView firstSelectedView = selection.getFirstSelection(); + final TreeElement finalTreeElement; + if (firstSelectedView.getType() == TreeElementType.NODE) { + TreeNodeView nodeView = (TreeNodeView) firstSelectedView; + finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); + } else { + TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; + finalTreeElement = transitionView.getChildView().getTreeElement(); + } + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); + puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); + } + + /** + * Gets the reason why the command cannot be executed + * + * @return if command cannot be executed, returns reason for why the command cannot be executed, + * otherwise null if command can be executed + */ + @Override + public String getErrorString() { + List selectedViews = selection.getSelectedViews(); + if (selectedViews.isEmpty()) { + return CommandError.NO_SELECTED_VIEWS.toString(); + } + + for (TreeElementView view : selectedViews) { + if (view.getType() == TreeElementType.NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + if (nodeView.getChildrenViews().size() != 1) { + return CommandError.ONE_CHILD.toString(); + } + } else { + TreeTransitionView transView = (TreeTransitionView) view; + if (transView.getParentViews().size() > 1) { + return CommandError.CONTAINS_MERGE.toString(); + } + } + } + return null; + } + + /** Undoes an command */ + @Override + public void undoCommand() { + Tree tree = GameBoardFacade.getInstance().getTree(); + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + + for (TreeElementView selectedView : selection.getSelectedViews()) { + TreeElement element = selectedView.getTreeElement(); + TreeTransitionView transitionView; + if (element.getType() == TreeElementType.NODE) { + TreeNodeView nodeView = (TreeNodeView) selectedView; + transitionView = nodeView.getChildrenViews().get(0); + } else { + transitionView = (TreeTransitionView) selectedView; + } + TreeTransition transition = transitionView.getTreeElement(); + transition.setRule(oldRules.get(transition)); + + if (addNode.get(transition) != null) { + final TreeNode childNode = transition.getChildNode(); + tree.removeTreeElement(childNode); + puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(childNode)); + } + } + + final TreeElement finalTreeElement = selection.getFirstSelection().getTreeElement(); + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); + puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(selection)); + } +} diff --git a/src/main/java/edu/rpi/legup/model/Puzzle.java b/src/main/java/edu/rpi/legup/model/Puzzle.java index 18614131b..7971c95af 100644 --- a/src/main/java/edu/rpi/legup/model/Puzzle.java +++ b/src/main/java/edu/rpi/legup/model/Puzzle.java @@ -1,6 +1,7 @@ package edu.rpi.legup.model; import edu.rpi.legup.model.elements.*; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.observer.IBoardListener; @@ -12,32 +13,27 @@ import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -import edu.rpi.legup.ui.puzzleeditorui.elementsview.NonPlaceableElementPanel; -import edu.rpi.legup.utility.LegupUtils; -import org.w3c.dom.Document; -import edu.rpi.legup.model.elements.Element; -import org.w3c.dom.Node; import edu.rpi.legup.save.InvalidFileFormatException; import edu.rpi.legup.ui.boardview.BoardView; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; +import edu.rpi.legup.utility.LegupUtils; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; - +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; public abstract class Puzzle implements IBoardSubject, ITreeSubject { private static final Logger LOGGER = LogManager.getLogger(Puzzle.class.getName()); @@ -59,9 +55,7 @@ public abstract class Puzzle implements IBoardSubject, ITreeSubject { protected List placeableElements; protected List nonPlaceableElements; - /** - * Puzzle Constructor - creates a new Puzzle - */ + /** Puzzle Constructor - creates a new Puzzle */ public Puzzle() { this.boardListeners = new ArrayList<>(); this.treeListeners = new ArrayList<>(); @@ -87,7 +81,7 @@ private void registerPuzzleElements() { System.out.println("possible element: " + c.getName()); - //check that the element is not abstract + // check that the element is not abstract if (Modifier.isAbstract(c.getModifiers())) continue; for (Annotation a : c.getAnnotations()) { @@ -107,8 +101,7 @@ private void registerPuzzleElements() { default: break; } - } - catch (InvocationTargetException e) { + } catch (InvocationTargetException e) { System.out.println(" Failed "); e.getTargetException().printStackTrace(); } @@ -116,12 +109,14 @@ private void registerPuzzleElements() { } } -// } catch (IOException | ClassNotFoundException | NoSuchMethodException | -// InstantiationException | IllegalAccessException | InvocationTargetException e) { -// LOGGER.error("Unable to find rules for " + this.getClass().getSimpleName(), e); -// } - } - catch (Exception e) { + // } catch (IOException | ClassNotFoundException | NoSuchMethodException | + // InstantiationException | IllegalAccessException | + // InvocationTargetException + // e) { + // LOGGER.error("Unable to find rules for " + + // this.getClass().getSimpleName(), e); + // } + } catch (Exception e) { LOGGER.error("Unable to find elements for " + this.getClass().getSimpleName(), e); } } @@ -136,7 +131,7 @@ private void registerRules() { System.out.println("possible rule: " + c.getName()); - //check that the rule is not abstract + // check that the rule is not abstract if (Modifier.isAbstract(c.getModifiers())) continue; for (Annotation a : c.getAnnotations()) { @@ -161,8 +156,7 @@ private void registerRules() { default: break; } - } - catch (InvocationTargetException e) { + } catch (InvocationTargetException e) { System.out.println(" Failed "); e.getTargetException().printStackTrace(); } @@ -170,19 +164,19 @@ private void registerRules() { } } -// } catch (IOException | ClassNotFoundException | NoSuchMethodException | -// InstantiationException | IllegalAccessException | InvocationTargetException e) { -// LOGGER.error("Unable to find rules for " + this.getClass().getSimpleName(), e); -// } - } - catch (Exception e) { + // } catch (IOException | ClassNotFoundException | NoSuchMethodException | + // InstantiationException | IllegalAccessException | + // InvocationTargetException + // e) { + // LOGGER.error("Unable to find rules for " + + // this.getClass().getSimpleName(), e); + // } + } catch (Exception e) { LOGGER.error("Unable to find rules for " + this.getClass().getSimpleName(), e); } } - /** - * Initializes the view. Called by the invoker of the class - */ + /** Initializes the view. Called by the invoker of the class */ public abstract void initializeView(); /** @@ -196,7 +190,7 @@ private void registerRules() { /** * Checks if the given height and width are valid board dimensions for the given puzzle * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @return true if the given dimensions are valid for the given puzzle, false otherwise */ @@ -230,13 +224,13 @@ public boolean isPuzzleComplete() { if (leaf.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) leaf; if (!node.isRoot()) { - isComplete &= node.getParent().isContradictoryBranch() || isBoardComplete(node.getBoard()); - } - else { + isComplete &= + node.getParent().isContradictoryBranch() + || isBoardComplete(node.getBoard()); + } else { isComplete &= isBoardComplete(node.getBoard()); } - } - else { + } else { isComplete = false; } } @@ -268,8 +262,7 @@ public boolean isPuzzleComplete() { public void importPuzzle(String fileName) throws InvalidFileFormatException { try { importPuzzle(new FileInputStream(fileName)); - } - catch (IOException e) { + } catch (IOException e) { LOGGER.error("Importing puzzle error", e); throw new InvalidFileFormatException("Could not find file"); } @@ -287,8 +280,7 @@ public void importPuzzle(InputStream inputStream) throws InvalidFileFormatExcept DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.parse(inputStream); - } - catch (IOException | SAXException | ParserConfigurationException e) { + } catch (IOException | SAXException | ParserConfigurationException e) { LOGGER.error("Importing puzzle error", e); throw new InvalidFileFormatException("Could not find file"); } @@ -300,8 +292,7 @@ public void importPuzzle(InputStream inputStream) throws InvalidFileFormatExcept throw new InvalidFileFormatException("Puzzle importer null"); } importer.initializePuzzle(node); - } - else { + } else { LOGGER.error("Invalid file"); throw new InvalidFileFormatException("Invalid file: must be a Legup file"); } @@ -351,7 +342,6 @@ public List getNonPlaceableElements() { return nonPlaceableElements; } - /** * Sets the list of direct rules * diff --git a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java index 613d2ed1c..a052a736a 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java @@ -3,23 +3,21 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.w3c.dom.Document; import edu.rpi.legup.save.ExportFileException; - +import java.io.File; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import java.io.File; -import java.util.*; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.ZoneId; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.w3c.dom.Document; import org.w3c.dom.Element; public abstract class PuzzleExporter { @@ -60,7 +58,8 @@ public void exportPuzzle(String fileName) throws ExportFileException { legupElement.appendChild(puzzleElement); puzzleElement.appendChild(createBoardElement(newDocument)); - if (puzzle.getTree() != null && !puzzle.getTree().getRootNode().getChildren().isEmpty()) { + if (puzzle.getTree() != null + && !puzzle.getTree().getRootNode().getChildren().isEmpty()) { puzzleElement.appendChild(createProofElement(newDocument)); } @@ -84,13 +83,11 @@ public void exportPuzzle(String fileName) throws ExportFileException { StreamResult result = new StreamResult(new File(fileName)); transformer.transform(source, result); - } - catch (ParserConfigurationException | TransformerException e) { + } catch (ParserConfigurationException | TransformerException e) { throw new ExportFileException("Puzzle Exporter: parser configuration exception"); - } - catch (Exception e) { + } catch (Exception e) { throw e; - //throw new ExportFileException(e.getMessage()); + // throw new ExportFileException(e.getMessage()); } } diff --git a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java index 327a92773..0cc163200 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -6,14 +6,13 @@ import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.util.*; - public abstract class PuzzleImporter { private static final Logger LOGGER = LogManager.getLogger(PuzzleImporter.class.getName()); @@ -35,20 +34,20 @@ public PuzzleImporter(Puzzle puzzle) { /** * Initializes an empty puzzle * - * @param rows number of rows on the puzzle + * @param rows number of rows on the puzzle * @param columns number of columns on the puzzle * @throws RuntimeException if puzzle can not be made */ public void initializePuzzle(int rows, int columns) throws RuntimeException { if (this.puzzle.isValidDimensions(rows, columns)) { initializeBoard(rows, columns); - } - else { + } else { throw new IllegalArgumentException("Invalid dimensions provided"); } } - public void initializePuzzle(String[] statements) throws InputMismatchException, IllegalArgumentException { + 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. @@ -72,46 +71,49 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { Node n = childNodes.item(i); if (n.getNodeName().equalsIgnoreCase("board")) { if (initBoard) { - throw new InvalidFileFormatException("Puzzle creation error: duplicate board puzzleElement found"); + throw new InvalidFileFormatException( + "Puzzle creation error: duplicate board puzzleElement found"); } initializeBoard(n); initBoard = true; - } - else { + } else { if (n.getNodeName().equalsIgnoreCase("proof")) { if (initProof) { - throw new InvalidFileFormatException("Puzzle creation error: duplicate proof puzzleElement found"); + throw new InvalidFileFormatException( + "Puzzle creation error: duplicate proof puzzleElement found"); } if (!initBoard) { - throw new InvalidFileFormatException("Puzzle creation error: could not find board puzzleElement"); + throw new InvalidFileFormatException( + "Puzzle creation error: could not find board puzzleElement"); } initializeProof(n); initProof = true; - } - else { + } else { if (!n.getNodeName().equalsIgnoreCase("#text")) { - throw new InvalidFileFormatException("Puzzle creation error: unknown node found in file"); + throw new InvalidFileFormatException( + "Puzzle creation error: unknown node found in file"); } } } } if (!initBoard) { - throw new InvalidFileFormatException("Puzzle creation error: could not find board puzzleElement"); + throw new InvalidFileFormatException( + "Puzzle creation error: could not find board puzzleElement"); } if (!initProof) { createDefaultTree(); } - } - else { - throw new InvalidFileFormatException("Invalid file format; does not contain \"puzzle\" node"); + } else { + throw new InvalidFileFormatException( + "Invalid file format; does not contain \"puzzle\" node"); } } /** * Creates the board for building * - * @param rows number of rows on the puzzle + * @param rows number of rows on the puzzle * @param columns number of columns on the puzzle * @throws RuntimeException if board can not be created */ @@ -125,7 +127,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; + public abstract void initializeBoard(String[] statements) + throws UnsupportedOperationException, IllegalArgumentException; /** * Creates the proof for building @@ -143,21 +146,22 @@ public void initializeProof(Node node) throws InvalidFileFormatException { Node n = treeList.item(i); if (n.getNodeName().equalsIgnoreCase("tree")) { if (initTree) { - throw new InvalidFileFormatException("Proof Tree construction error: duplicate tree puzzleElement"); + throw new InvalidFileFormatException( + "Proof Tree construction error: duplicate tree puzzleElement"); } createTree(n); initTree = true; - } - else { - throw new InvalidFileFormatException("Proof Tree construction error: unknown puzzleElement found"); + } else { + throw new InvalidFileFormatException( + "Proof Tree construction error: unknown puzzleElement found"); } } if (!initTree) { createDefaultTree(); } - } - else { - throw new InvalidFileFormatException("Invalid file format; does not contain \"proof\" node"); + } else { + throw new InvalidFileFormatException( + "Invalid file format; does not contain \"proof\" node"); } } @@ -171,7 +175,8 @@ protected void setCells(Node node) throws InvalidFileFormatException { NodeList dataList = ((org.w3c.dom.Element) node).getElementsByTagName("cell"); Board board = puzzle.getCurrentBoard(); for (int i = 0; i < dataList.getLength(); i++) { - PuzzleElement data = puzzle.getFactory().importCell(dataList.item(i), puzzle.getCurrentBoard()); + PuzzleElement data = + puzzle.getFactory().importCell(dataList.item(i), puzzle.getCurrentBoard()); board.setPuzzleElement(data.getIndex(), data); } } @@ -199,15 +204,18 @@ protected void createTree(Node node) throws InvalidFileFormatException { String nodeId = treeNodeElement.getAttribute("id"); String isRoot = treeNodeElement.getAttribute("root"); if (nodeId.isEmpty()) { - throw new InvalidFileFormatException("Proof Tree construction error: cannot find node ID"); + throw new InvalidFileFormatException( + "Proof Tree construction error: cannot find node ID"); } if (treeNodes.containsKey(nodeId)) { - throw new InvalidFileFormatException("Proof Tree construction error: duplicate tree node ID found"); + throw new InvalidFileFormatException( + "Proof Tree construction error: duplicate tree node ID found"); } TreeNode treeNode = new TreeNode(puzzle.getCurrentBoard().copy()); if (isRoot.equalsIgnoreCase("true")) { if (tree.getRootNode() != null) { - throw new InvalidFileFormatException("Proof Tree construction error: multiple root nodes declared"); + throw new InvalidFileFormatException( + "Proof Tree construction error: multiple root nodes declared"); } treeNode.setRoot(true); tree.setRootNode(treeNode); @@ -215,7 +223,6 @@ protected void createTree(Node node) throws InvalidFileFormatException { treeNodes.put(nodeId, treeNode); } - for (int i = 0; i < nodeList.getLength(); i++) { org.w3c.dom.Element treeNodeElement = (org.w3c.dom.Element) nodeList.item(i); String nodeId = treeNodeElement.getAttribute("id"); @@ -231,11 +238,10 @@ protected void createTree(Node node) throws InvalidFileFormatException { transition.addParent(treeNode); treeNode.addChild(transition); continue; + } else { + throw new InvalidFileFormatException( + "Proof Tree construction error: duplicate transition ID found"); } - else { - throw new InvalidFileFormatException("Proof Tree construction error: duplicate transition ID found"); - } - } String childId = trans.getAttribute("child"); @@ -250,7 +256,8 @@ protected void createTree(Node node) throws InvalidFileFormatException { if (!ruleName.isEmpty()) { rule = puzzle.getRuleByID(ruleId); if (rule == null) { - throw new InvalidFileFormatException("Proof Tree construction error: could not find rule by ID"); + throw new InvalidFileFormatException( + "Proof Tree construction error: could not find rule by ID"); } transition.setRule(rule); } @@ -266,14 +273,16 @@ protected void createTree(Node node) throws InvalidFileFormatException { } } - //validateTreeStructure(treeNodes, treeTransitions); + // validateTreeStructure(treeNodes, treeTransitions); System.err.println("Tree Size: " + treeTransitions.size()); for (Map.Entry entry : nodeChanges.entrySet()) { makeTransitionChanges(entry.getKey(), entry.getValue()); } } - protected void validateTreeStructure(HashMap nodes, HashMap transitions) throws InvalidFileFormatException { + protected void validateTreeStructure( + HashMap nodes, HashMap transitions) + throws InvalidFileFormatException { Tree tree = puzzle.getTree(); if (tree == null) { @@ -300,25 +309,27 @@ protected void validateTreeStructure(HashMap nodes, HashMap nodes, HashMap mergingNodes = transition.getParents(); List mergingBoards = new ArrayList<>(); @@ -349,7 +363,8 @@ protected void makeTransitionChanges(TreeTransition transition, Node transElemen TreeNode lca = Tree.getLowestCommonAncestor(mergingNodes); if (lca == null) { - throw new InvalidFileFormatException("Proof Tree construction error: unable to find merge node"); + throw new InvalidFileFormatException( + "Proof Tree construction error: unable to find merge node"); } Board lcaBoard = lca.getBoard(); @@ -360,8 +375,7 @@ protected void makeTransitionChanges(TreeTransition transition, Node transElemen if (childNode != null) { childNode.setBoard(mergedBoard.copy()); } - } - else { + } else { NodeList cellList = transElement.getChildNodes(); for (int i = 0; i < cellList.getLength(); i++) { Node node = cellList.item(i); @@ -372,10 +386,10 @@ protected void makeTransitionChanges(TreeTransition transition, Node transElemen board.setPuzzleElement(cell.getIndex(), cell); board.addModifiedData(cell); transition.propagateChange(cell); - } - else { + } else { if (!node.getNodeName().equalsIgnoreCase("#text")) { - throw new InvalidFileFormatException("Proof Tree construction error: unknown node in transition"); + throw new InvalidFileFormatException( + "Proof Tree construction error: unknown node in transition"); } } } diff --git a/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java b/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java index 32f01edbc..c4c1ed273 100644 --- a/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java +++ b/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java @@ -7,6 +7,4 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface RegisterPuzzle { - -} +public @interface RegisterPuzzle {} diff --git a/src/main/java/edu/rpi/legup/model/elements/Element.java b/src/main/java/edu/rpi/legup/model/elements/Element.java index 7ab502c63..8b75d075d 100644 --- a/src/main/java/edu/rpi/legup/model/elements/Element.java +++ b/src/main/java/edu/rpi/legup/model/elements/Element.java @@ -1,10 +1,8 @@ package edu.rpi.legup.model.elements; -import edu.rpi.legup.model.rules.RuleType; - -import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; +import javax.swing.*; @RegisterElement public abstract class Element { @@ -31,10 +29,11 @@ public Element(String elementID, String elementName, String description, String private void loadImage() { if (imageName != null) { this.image = new ImageIcon(ClassLoader.getSystemClassLoader().getResource(imageName)); - //Resize images to be 100px wide + // Resize images to be 100px wide Image image = this.image.getImage(); if (this.image.getIconWidth() < 120) return; - int height = (int) (100 * ((double) this.image.getIconHeight() / this.image.getIconWidth())); + int height = + (int) (100 * ((double) this.image.getIconHeight() / this.image.getIconWidth())); if (height == 0) { System.out.println("height is 0 error"); System.out.println("height: " + this.image.getIconHeight()); diff --git a/src/main/java/edu/rpi/legup/model/elements/ElementType.java b/src/main/java/edu/rpi/legup/model/elements/ElementType.java index ea47ca0eb..dff4fe04f 100644 --- a/src/main/java/edu/rpi/legup/model/elements/ElementType.java +++ b/src/main/java/edu/rpi/legup/model/elements/ElementType.java @@ -1,5 +1,6 @@ package edu.rpi.legup.model.elements; public enum ElementType { - PLACEABLE, NONPLACEABLE + PLACEABLE, + NONPLACEABLE } diff --git a/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java b/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java index 87859991f..4ab0ab509 100644 --- a/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java @@ -1,7 +1,8 @@ package edu.rpi.legup.model.elements; public abstract class NonPlaceableElement extends Element { - public NonPlaceableElement(String elementID, String elementName, String description, String imageName) { + public NonPlaceableElement( + String elementID, String elementName, String description, String imageName) { super(elementID, elementName, description, imageName); this.elementType = ElementType.NONPLACEABLE; } diff --git a/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java index 2d018ddbb..133658700 100644 --- a/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java @@ -1,7 +1,8 @@ package edu.rpi.legup.model.elements; public abstract class PlaceableElement extends Element { - public PlaceableElement(String elementID, String elementName, String description, String imageName) { + public PlaceableElement( + String elementID, String elementName, String description, String imageName) { super(elementID, elementName, description, imageName); this.elementType = ElementType.PLACEABLE; } diff --git a/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java b/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java index bda3f3070..368ecc8d1 100644 --- a/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java @@ -6,6 +6,4 @@ @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface RegisterElement { - -} +public @interface RegisterElement {} diff --git a/src/main/java/edu/rpi/legup/model/gameboard/Board.java b/src/main/java/edu/rpi/legup/model/gameboard/Board.java index 4bf2ff548..d8bdf5199 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/Board.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/Board.java @@ -11,9 +11,7 @@ public abstract class Board { protected Set modifiedData; protected boolean isModifiable; - /** - * Board Constructor creates an empty board. - */ + /** Board Constructor creates an empty board. */ public Board() { this.puzzleElements = new ArrayList<>(); this.modifiedData = new HashSet<>(); @@ -46,7 +44,7 @@ public PuzzleElement getPuzzleElement(PuzzleElement puzzleElement) { /** * Sets a specific {@link PuzzleElement} on the board. * - * @param index index of the puzzleElement + * @param index index of the puzzleElement * @param puzzleElement new puzzleElement at the index */ public void setPuzzleElement(int index, PuzzleElement puzzleElement) { @@ -139,8 +137,8 @@ public void removeModifiedData(PuzzleElement data) { } /** - * Called when a {@link PuzzleElement} data on this has changed and passes in the equivalent puzzle element with - * the new data. + * Called when a {@link PuzzleElement} data on this has changed and passes in the equivalent + * puzzle element with the new data. * * @param puzzleElement equivalent puzzle element with the new data. */ @@ -150,22 +148,20 @@ public void notifyChange(PuzzleElement puzzleElement) { } /** - * Called when a {@link PuzzleElement} has been added and passes in the equivalent puzzle element with the data. + * Called when a {@link PuzzleElement} has been added and passes in the equivalent puzzle + * element with the data. * * @param puzzleElement equivalent puzzle element with the data. */ - public void notifyAddition(PuzzleElement puzzleElement) { - - } + public void notifyAddition(PuzzleElement puzzleElement) {} /** - * Called when a {@link PuzzleElement} has been deleted and passes in the equivalent puzzle element with the data. + * Called when a {@link PuzzleElement} has been deleted and passes in the equivalent puzzle + * element with the data. * * @param puzzleElement equivalent puzzle element with the data. */ - public void notifyDeletion(PuzzleElement puzzleElement) { - - } + public void notifyDeletion(PuzzleElement puzzleElement) {} @SuppressWarnings("unchecked") public Board mergedBoard(Board lca, List boards) { diff --git a/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java b/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java index 799825025..fa3625a43 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java @@ -1,7 +1,6 @@ package edu.rpi.legup.model.gameboard; import edu.rpi.legup.model.rules.CaseRule; - import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java b/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java index 99e9bf65a..bfc785bdd 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java @@ -1,28 +1,30 @@ package edu.rpi.legup.model.gameboard; +import edu.rpi.legup.save.InvalidFileFormatException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import edu.rpi.legup.save.InvalidFileFormatException; public abstract class ElementFactory { /** * Creates a {@link PuzzleElement} based on the xml document Node and adds it to the board. * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node - * @throws InvalidFileFormatException thrown if the xml node is invalid for the specific puzzle element + * @throws InvalidFileFormatException thrown if the xml node is invalid for the specific puzzle + * element */ - public abstract PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException; + public abstract PuzzleElement importCell(Node node, Board board) + throws InvalidFileFormatException; /** * Creates a xml document {@link PuzzleElement} from a cell for exporting. * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ public abstract Element exportCell(Document document, PuzzleElement puzzleElement); -} \ No newline at end of file +} 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 d31e8185a..9593690ce 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java @@ -1,10 +1,8 @@ package edu.rpi.legup.model.gameboard; import edu.rpi.legup.model.elements.Element; -import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentClue; - import java.awt.*; import java.awt.event.MouseEvent; @@ -15,7 +13,7 @@ public class GridBoard extends Board { /** * GridBoard Constructor creates a board for grid using puzzles from a width and height. * - * @param width width of the board + * @param width width of the board * @param height height of the board */ public GridBoard(int width, int height) { @@ -43,83 +41,88 @@ public GridBoard(int size) { * @return grid cell at location (x, y) */ public GridCell getCell(int x, int y) { - if (y * dimension.width + x >= puzzleElements.size() || x >= dimension.width || - y >= dimension.height || x < 0 || y < 0) { - System.err.printf("not in bounds, bounds are %dx%d\n", dimension.width, dimension.height); + if (y * dimension.width + x >= puzzleElements.size() + || x >= dimension.width + || y >= dimension.height + || x < 0 + || y < 0) { + System.err.printf( + "not in bounds, bounds are %dx%d\n", dimension.width, dimension.height); return null; } return (GridCell) puzzleElements.get(y * dimension.width + x); } /** - * Sets the {@link GridCell} at the location (x,y). This method does not set the cell if the location specified is - * out of bounds. + * Sets the {@link GridCell} at the location (x,y). This method does not set the cell if the + * location specified is out of bounds. * - * @param x x location of the cell - * @param y y location of the cell + * @param x x location of the cell + * @param y y location of the cell * @param cell grid cell to set at location (x,y) */ public void setCell(int x, int y, GridCell cell) { - if (y * dimension.width + x >= puzzleElements.size() || x >= dimension.width || - y >= dimension.height || x < 0 || y < 0) { + if (y * dimension.width + x >= puzzleElements.size() + || x >= dimension.width + || y >= dimension.height + || x < 0 + || y < 0) { return; } puzzleElements.set(y * dimension.width + x, cell); } 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))) { + 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) { if (m.getButton() == MouseEvent.BUTTON1) { if (clue.getData() < dimension.height) { clue.setData(clue.getData() + 1); - } - else { + } else { clue.setData(0); } - } - else { + } else { if (clue.getData() > 0) { clue.setData(clue.getData() - 1); - } - else { + } else { clue.setData(dimension.height); } } - } - else { //x == dimension.width + } else { // x == dimension.width if (m.getButton() == MouseEvent.BUTTON1) { if (clue.getData() < dimension.width) { clue.setData(clue.getData() + 1); - } - else { + } else { clue.setData(0); } - } - else { + } else { if (clue.getData() > 0) { clue.setData(clue.getData() - 1); - } - else { + } else { clue.setData(dimension.width); } } } - } - else { - if (e != null && y * dimension.width + x >= puzzleElements.size() || x >= dimension.width || - y >= dimension.height || x < 0 || y < 0) { + } else { + if (e != null && y * dimension.width + x >= puzzleElements.size() + || x >= dimension.width + || y >= dimension.height + || x < 0 + || y < 0) { return; - } - else { + } else { if (e != null) { puzzleElements.get(y * dimension.width + x).setType(e, m); } } } -// puzzleElements.set(y * dimension.width + x, puzzleElements.get(y * dimension.width + x)); + // puzzleElements.set(y * dimension.width + x, puzzleElements.get(y * dimension.width + // + + // x)); } /** @@ -163,4 +166,4 @@ public GridBoard copy() { } return newGridBoard; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java b/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java index 8209c0b6f..a33c3ec80 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java @@ -8,7 +8,7 @@ public class GridCell extends PuzzleElement { /** * GridCell Constructor creates a grid cell at the specified location given as a {@link Point} * - * @param value data value that represents the grid cell + * @param value data value that represents the grid cell * @param location location on the board */ public GridCell(T value, Point location) { @@ -20,8 +20,8 @@ public GridCell(T value, Point location) { * GridCell Constructor creates a grid cell at the specified location given as x,y pair * * @param value data value that represents the grid cell - * @param x x location - * @param y y location + * @param x x location + * @param y y location */ public GridCell(T value, int x, int y) { this(value, new Point(x, y)); @@ -59,4 +59,3 @@ public GridCell copy() { return copy; } } - 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 3d84287e3..4ce030a04 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java @@ -1,7 +1,6 @@ package edu.rpi.legup.model.gameboard; import edu.rpi.legup.model.elements.Element; - import java.awt.event.MouseEvent; public abstract class PuzzleElement { @@ -13,9 +12,7 @@ public abstract class PuzzleElement { protected boolean isValid; protected int casesDepended; - /** - * PuzzleElement Constructor creates a new puzzle element. - */ + /** PuzzleElement Constructor creates a new puzzle element. */ public PuzzleElement() { this.index = -1; this.data = null; @@ -131,8 +128,8 @@ public void setGiven(boolean given) { } /** - * Get whether this puzzle element data is a valid change according to the rule applied to the transition that - * this puzzle element is contained in. + * Get whether this puzzle element data is a valid change according to the rule applied to the + * transition that this puzzle element is contained in. * * @return true if the puzzle element logically follows from the rule, otherwise false. */ @@ -141,8 +138,8 @@ public boolean isValid() { } /** - * Sets whether this puzzle element data is a valid change according to the rule applied to the transition that - * this puzzle element is contained in. + * Sets whether this puzzle element data is a valid change according to the rule applied to the + * transition that this puzzle element is contained in. * * @param isValid true if the puzzle element logically follows from the rule, otherwise false. */ diff --git a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java index d8fa24166..d5e7fdb2d 100644 --- a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java +++ b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java @@ -25,8 +25,6 @@ public interface ITreeListener { */ void onTreeSelectionChanged(TreeViewSelection selection); - /** - * Called when the model has finished updating the tree. - */ + /** Called when the model has finished updating the tree. */ void onUpdateTree(); } 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 a01db2b6d..e87896fcc 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -1,17 +1,16 @@ package edu.rpi.legup.model.rules; +import static edu.rpi.legup.model.rules.RuleType.CASE; + 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.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.ArrayList; import java.util.List; import java.util.Set; -import static edu.rpi.legup.model.rules.RuleType.CASE; - public abstract class CaseRule extends Rule { private final String INVALID_USE_MESSAGE; @@ -19,10 +18,10 @@ public abstract class CaseRule extends Rule { /** * CaseRule Constructor creates a new case rule. * - * @param ruleID ID of the rule - * @param ruleName name of the rule + * @param ruleID ID of the rule + * @param ruleName name of the rule * @param description description of the rule - * @param imageName file name of the image + * @param imageName file name of the image */ public CaseRule(String ruleID, String ruleName, String description, String imageName) { super(ruleID, ruleName, description, imageName); @@ -31,7 +30,8 @@ public CaseRule(String ruleID, String ruleName, String description, String image } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -39,16 +39,18 @@ public CaseRule(String ruleID, String ruleName, String description, String image public abstract CaseBoard getCaseBoard(Board board); /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ public abstract List getCases(Board board, PuzzleElement puzzleElement); /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. + * Checks whether the {@link TreeTransition} 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 @@ -61,7 +63,8 @@ public String checkRule(TreeTransition transition) { } for (TreeTransition childTrans : parentNodes.get(0).getChildren()) { - if (childTrans.getRule() == null || !childTrans.getRule().getClass().equals(this.getClass())) { + if (childTrans.getRule() == null + || !childTrans.getRule().getClass().equals(this.getClass())) { return "All children nodes must be justified with the same case rule."; } } @@ -81,8 +84,8 @@ public String checkRule(TreeTransition transition) { } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -91,13 +94,13 @@ public String checkRule(TreeTransition transition) { public abstract String checkRuleRaw(TreeTransition transition); /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. + * 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 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 + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message */ @Override public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement) { @@ -105,34 +108,35 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message */ @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 + * 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 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 + * @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); + List cases = getCases(board, puzzleElement); for (Board caseBoard : cases) { Set data = caseBoard.getModifiedData(); for (PuzzleElement element : data) { - if(!elements.contains(board.getPuzzleElement(element))){ + if (!elements.contains(board.getPuzzleElement(element))) { elements.add(board.getPuzzleElement(element)); } } @@ -141,5 +145,3 @@ public List dependentElements(Board board, PuzzleElement puzzleEl return elements; } } - - diff --git a/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java b/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java index 0d3a79a98..b38a95fd2 100644 --- a/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java @@ -1,22 +1,23 @@ package edu.rpi.legup.model.rules; +import static edu.rpi.legup.model.rules.RuleType.CONTRADICTION; + import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeTransition; -import static edu.rpi.legup.model.rules.RuleType.CONTRADICTION; - public abstract class ContradictionRule extends Rule { - private final String NO_CONTRADICTION_MESSAGE = "No instance of the contradiction " + this.ruleName + " here"; + private final String NO_CONTRADICTION_MESSAGE = + "No instance of the contradiction " + this.ruleName + " here"; /** * ContradictionRule Constructor creates a new contradiction rule * - * @param ruleID ID of the rule - * @param ruleName name of the rule + * @param ruleID ID of the rule + * @param ruleName name of the rule * @param description description of the rule - * @param imageName file name of the image + * @param imageName file name of the image */ public ContradictionRule(String ruleID, String ruleName, String description, String imageName) { super(ruleID, ruleName, description, imageName); @@ -35,13 +36,13 @@ public String checkRule(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message */ @Override public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement) { @@ -49,8 +50,8 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement } /** - * Checks whether the transition logically follows from the parent node using this rule. - * This method is the one that should overridden in child classes + * Checks whether the transition logically follows from the parent node using this rule. This + * method is the one that should overridden in child classes * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -61,14 +62,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes * - * @param transition transition to check + * @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 + * @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) { @@ -96,13 +97,13 @@ public String getNoContradictionMessage() { } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ public abstract String checkContradictionAt(Board board, PuzzleElement puzzleElement); - } 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 304a2ca90..d550bc02c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -1,106 +1,104 @@ -package edu.rpi.legup.model.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -import static edu.rpi.legup.model.rules.RuleType.BASIC; - -public abstract class DirectRule extends Rule { - /** - * DirectRule Constructor creates a new basic rule. - * - * @param ruleID ID of the rule - * @param ruleName name of the rule - * @param description description of the rule - * @param imageName file name of the image - */ - public DirectRule(String ruleID, String ruleName, String description, String imageName) { - super(ruleID, ruleName, description, imageName); - this.ruleType = BASIC; - } - - /** - * Checks whether the {@link TreeTransition} 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 - */ - public String checkRule(TreeTransition transition) { - Board finalBoard = transition.getBoard(); - // System.out.println(finalBoard.getModifiedData().size()); - if (transition.getParents().size() != 1 || - 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); - } - } - - /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. - * - * @param transition transition to check - * @return null if the child node logically follow from the parent node, otherwise error message - */ - 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) { - checkStr = tempStr; - } - } - return checkStr; - } - - /** - * Checks whether the child node logically follows from the parent node at the specific {@link PuzzleElement} 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 - */ - public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement) { - Board finalBoard = transition.getBoard(); - puzzleElement = finalBoard.getPuzzleElement(puzzleElement); - String checkStr; - if (!puzzleElement.isModified()) { - checkStr = "PuzzleElement must be modified"; - } - else { - if (transition.getParents().size() != 1 || - transition.getParents().get(0).getChildren().size() != 1) { - checkStr = "State must have only 1 parent and 1 child"; - } - else { - checkStr = checkRuleRawAt(transition, puzzleElement); - } - } - puzzleElement.setValid(checkStr == null); - return checkStr; - } - - /** - * 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 - */ - public abstract Board getDefaultBoard(TreeNode node); -} +package edu.rpi.legup.model.rules; + +import static edu.rpi.legup.model.rules.RuleType.BASIC; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public abstract class DirectRule extends Rule { + /** + * DirectRule Constructor creates a new basic rule. + * + * @param ruleID ID of the rule + * @param ruleName name of the rule + * @param description description of the rule + * @param imageName file name of the image + */ + public DirectRule(String ruleID, String ruleName, String description, String imageName) { + super(ruleID, ruleName, description, imageName); + this.ruleType = BASIC; + } + + /** + * Checks whether the {@link TreeTransition} 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 + */ + public String checkRule(TreeTransition transition) { + Board finalBoard = transition.getBoard(); + // System.out.println(finalBoard.getModifiedData().size()); + if (transition.getParents().size() != 1 + || 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); + } + } + + /** + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. + * + * @param transition transition to check + * @return null if the child node logically follow from the parent node, otherwise error message + */ + 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) { + checkStr = tempStr; + } + } + return checkStr; + } + + /** + * Checks whether the child node logically follows from the parent node at the specific {@link + * PuzzleElement} 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 + */ + public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement) { + Board finalBoard = transition.getBoard(); + puzzleElement = finalBoard.getPuzzleElement(puzzleElement); + String checkStr; + if (!puzzleElement.isModified()) { + checkStr = "PuzzleElement must be modified"; + } else { + if (transition.getParents().size() != 1 + || transition.getParents().get(0).getChildren().size() != 1) { + checkStr = "State must have only 1 parent and 1 child"; + } else { + checkStr = checkRuleRawAt(transition, puzzleElement); + } + } + puzzleElement.setValid(checkStr == null); + return checkStr; + } + + /** + * 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 + */ + public abstract Board getDefaultBoard(TreeNode node); +} diff --git a/src/main/java/edu/rpi/legup/model/rules/MergeRule.java b/src/main/java/edu/rpi/legup/model/rules/MergeRule.java index 9ae18648e..f7badcd8b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/MergeRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/MergeRule.java @@ -1,33 +1,29 @@ package edu.rpi.legup.model.rules; +import static edu.rpi.legup.model.rules.RuleType.MERGE; + import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.Tree; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.ArrayList; import java.util.List; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import static edu.rpi.legup.model.rules.RuleType.MERGE; - public class MergeRule extends Rule { - /** - * MergeRule Constructor merges to board states together - */ + /** MergeRule Constructor merges to board states together */ public MergeRule() { - super("MERGE", "Merge Rule", + super( + "MERGE", + "Merge Rule", "Merge any number of nodes into one", "edu/rpi/legup/images/Legup/MergeRule.png"); this.ruleType = MERGE; } /** - * Checks whether the transition logically follows from the parent node using this rule. - * This method is the one that should overridden in child classes + * Checks whether the transition logically follows from the parent node using this rule. This + * method is the one that should overridden in child classes * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -61,14 +57,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes * - * @param transition transition to check + * @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 + * @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) { @@ -87,13 +83,13 @@ public String checkRule(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message */ @Override public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java b/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java index 40def6edd..c1fe0b88c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java @@ -5,6 +5,4 @@ @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface RegisterRule { - -} +public @interface RegisterRule {} diff --git a/src/main/java/edu/rpi/legup/model/rules/Rule.java b/src/main/java/edu/rpi/legup/model/rules/Rule.java index 50f2cf962..f70bb2889 100644 --- a/src/main/java/edu/rpi/legup/model/rules/Rule.java +++ b/src/main/java/edu/rpi/legup/model/rules/Rule.java @@ -1,17 +1,12 @@ package edu.rpi.legup.model.rules; +import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeTransition; - -import edu.rpi.legup.app.LegupPreferences; - -import javax.swing.ImageIcon; -import java.awt.image.BufferedImage; -import java.awt.Image; import java.awt.Graphics2D; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import java.awt.Image; +import java.awt.image.BufferedImage; +import javax.swing.ImageIcon; @RegisterRule public abstract class Rule { @@ -27,10 +22,10 @@ public abstract class Rule { /** * Rule Constructor creates a new rule * - * @param ruleID ID of the rule - * @param ruleName name of the rule + * @param ruleID ID of the rule + * @param ruleName name of the rule * @param description description of the rule - * @param imageName file name of the image + * @param imageName file name of the image */ public Rule(String ruleID, String ruleName, String description, String imageName) { this.ruleID = ruleID; @@ -50,8 +45,8 @@ public Rule(String ruleID, String ruleName, String description, String imageName public abstract String checkRule(TreeTransition transition); /** - * Checks whether the transition logically follows from the parent node using this rule. - * This method is the one that should overridden in child classes + * Checks whether the transition logically follows from the parent node using this rule. This + * method is the one that should overridden in child classes * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -59,43 +54,44 @@ public Rule(String ruleID, String ruleName, String description, String imageName protected abstract String checkRuleRaw(TreeTransition transition); /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message */ public abstract String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement); /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes * - * @param transition transition to check + * @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 + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message */ - protected abstract String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement); + protected abstract String checkRuleRawAt( + TreeTransition transition, PuzzleElement puzzleElement); - /** - * Loads the image file - */ + /** Loads the image file */ public void loadImage() { if (imageName != null) { String name = imageName; LegupPreferences prefs = LegupPreferences.getInstance(); - if (name.contains("shorttruthtable") && prefs.getUserPref(LegupPreferences.COLOR_BLIND).equals("true")) { + if (name.contains("shorttruthtable") + && prefs.getUserPref(LegupPreferences.COLOR_BLIND).equals("true")) { name = name.replace("ruleimages", "ruleimages_cb"); } this.image = new ImageIcon(ClassLoader.getSystemClassLoader().getResource(name)); - //Resize images to be 100px wide + // Resize images to be 100px wide Image image = this.image.getImage(); if (this.image.getIconWidth() < 120) return; - int height = (int) (100 * ((double) this.image.getIconHeight() / this.image.getIconWidth())); + int height = + (int) (100 * ((double) this.image.getIconHeight() / this.image.getIconWidth())); if (height == 0) { System.out.println("height is 0 error"); System.out.println("height: " + this.image.getIconHeight()); @@ -166,4 +162,4 @@ public RuleType getRuleType() { public String getInvalidUseOfRuleMessage() { return this.INVALID_USE_MESSAGE; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/model/rules/RuleType.java b/src/main/java/edu/rpi/legup/model/rules/RuleType.java index b7ce315cf..06aa1844b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/RuleType.java +++ b/src/main/java/edu/rpi/legup/model/rules/RuleType.java @@ -1,5 +1,8 @@ package edu.rpi.legup.model.rules; public enum RuleType { - BASIC, CASE, CONTRADICTION, MERGE + BASIC, + CASE, + CONTRADICTION, + MERGE } 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 79c0bcece..a0746db87 100644 --- a/src/main/java/edu/rpi/legup/model/tree/Tree.java +++ b/src/main/java/edu/rpi/legup/model/tree/Tree.java @@ -1,7 +1,6 @@ package edu.rpi.legup.model.tree; import edu.rpi.legup.model.gameboard.Board; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -20,9 +19,7 @@ public Tree(Board initBoard) { this.rootNode.setRoot(true); } - /** - * Tree Constructor creates the tree structure with null root node - */ + /** Tree Constructor creates the tree structure with null root node */ public Tree() { this.rootNode = null; } @@ -44,9 +41,9 @@ public TreeNode addNode(TreeTransition transition) { public TreeElement addTreeElement(TreeElement element) { if (element.getType() == TreeElementType.NODE) { TreeNode treeNode = (TreeNode) element; - return addTreeElement(treeNode, new TreeTransition(treeNode, treeNode.getBoard().copy())); - } - else { + return addTreeElement( + treeNode, new TreeTransition(treeNode, treeNode.getBoard().copy())); + } else { TreeTransition transition = (TreeTransition) element; Board copyBoard = transition.board.copy(); copyBoard.setModifiable(false); @@ -70,8 +67,7 @@ public void removeTreeElement(TreeElement element) { if (element.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) element; node.getParent().setChildNode(null); - } - else { + } else { TreeTransition transition = (TreeTransition) element; transition.getParents().forEach(n -> n.removeChild(transition)); transition.getParents().get(0).getChildren().forEach(TreeTransition::reverify); @@ -79,8 +75,8 @@ public void removeTreeElement(TreeElement element) { } /** - * Determines if the tree is valid by checking whether this tree puzzleElement and - * all descendants of this tree puzzleElement is justified and justified correctly + * Determines if the tree is valid by checking whether this tree puzzleElement and all + * descendants of this tree puzzleElement is justified and justified correctly * * @return true if tree is valid, false otherwise */ @@ -114,7 +110,7 @@ public Set getLeafTreeElements(TreeNode node) { /** * Recursively gets a Set of TreeNodes that are leaf nodes * - * @param leafs Set of TreeNodes that are leaf nodes + * @param leafs Set of TreeNodes that are leaf nodes * @param element current TreeNode being evaluated */ private void getLeafTreeElements(Set leafs, TreeElement element) { @@ -123,41 +119,37 @@ private void getLeafTreeElements(Set leafs, TreeElement element) { List childTrans = node.getChildren(); if (childTrans.isEmpty()) { leafs.add(node); - } - else { + } else { childTrans.forEach(t -> getLeafTreeElements(leafs, t)); } - } - else { + } else { TreeTransition transition = (TreeTransition) element; TreeNode childNode = transition.getChildNode(); if (childNode == null) { leafs.add(transition); - } - else { + } else { getLeafTreeElements(leafs, childNode); } } } /** - * Gets the lowest common ancestor (LCA) among the list of {@link TreeNode} passed into - * the function. This lowest common ancestor is the most immediate ancestor - * node such that the list of tree nodes specified are descendants of the node. - * This will return null if no such ancestor exists + * Gets the lowest common ancestor (LCA) among the list of {@link TreeNode} passed into the + * function. This lowest common ancestor is the most immediate ancestor node such that the list + * of tree nodes specified are descendants of the node. This will return null if no such + * ancestor exists * * @param nodes list of tree nodes to find the LCA - * @return the first ancestor node that all tree nodes have in common, otherwise null if none exists + * @return the first ancestor node that all tree nodes have in common, otherwise null if none + * exists */ public static TreeNode getLowestCommonAncestor(List nodes) { if (nodes.isEmpty()) { return null; - } - else { + } else { if (nodes.size() == 1) { return nodes.get(0); - } - else { + } else { List> ancestors = new ArrayList<>(); for (TreeNode node : nodes) { ancestors.add(node.getAncestors()); diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java index 17168ac98..59f75acf3 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java @@ -16,8 +16,8 @@ public TreeElement(TreeElementType type) { } /** - * Determines if this tree node leads to a contradiction. Every path from this tree node - * must lead to a contradiction including all of its children + * Determines if this tree node leads to a contradiction. Every path from this tree node must + * lead to a contradiction including all of its children * * @return true if this tree node leads to a contradiction, false otherwise */ @@ -28,15 +28,16 @@ public TreeElement(TreeElementType type) { * whether this tree puzzleElement and all descendants of this tree puzzleElement is justified * and justified correctly * - * @return true if this tree puzzleElement and all descendants of this tree puzzleElement is valid, - * false otherwise + * @return true if this tree puzzleElement and all descendants of this tree puzzleElement is + * valid, false otherwise */ public abstract boolean isValidBranch(); /** * Gets the type of tree puzzleElement * - * @return NODE if this tree puzzleElement is a tree node, TRANSITION, if this tree puzzleElement is a transition + * @return NODE if this tree puzzleElement is a tree node, TRANSITION, if this tree + * puzzleElement is a transition */ public TreeElementType getType() { return type; diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java b/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java index ad93f16b6..67437a535 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java @@ -1,5 +1,6 @@ package edu.rpi.legup.model.tree; public enum TreeElementType { - NODE, TRANSITION + NODE, + TRANSITION } diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeNode.java b/src/main/java/edu/rpi/legup/model/tree/TreeNode.java index 59f6e736e..a2ac7cb21 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeNode.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeNode.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.utility.DisjointSets; - import java.util.*; public class TreeNode extends TreeElement { @@ -24,8 +23,8 @@ public TreeNode(Board board) { } /** - * Determines if this tree node leads to a contradiction. Every path from this tree node - * must lead to a contradiction including all of its children + * Determines if this tree node leads to a contradiction. Every path from this tree node must + * lead to a contradiction including all of its children * * @return true if this tree node leads to a contradiction, false otherwise */ @@ -43,8 +42,8 @@ public boolean isContradictoryBranch() { * whether this tree puzzleElement and all descendants of this tree puzzleElement is justified * and justified correctly * - * @return true if this tree puzzleElement and all descendants of this tree puzzleElement is valid, - * false otherwise + * @return true if this tree puzzleElement and all descendants of this tree puzzleElement is + * valid, false otherwise */ @Override public boolean isValidBranch() { @@ -104,8 +103,7 @@ public List getDescendants() { it.add(transition); } } - } - else { + } else { TreeTransition trans = (TreeTransition) next; TreeNode childNode = trans.getChildNode(); if (childNode != null && !descendants.contains(childNode)) { @@ -118,9 +116,9 @@ public List getDescendants() { } /** - * Gets a DisjointSets containing the children of this node such that the sets contained within the DisjointSets - * are such that elements in the same set are branches of this tree node that will eventually merge. This could - * mean that multiple merges take place before this happens. + * Gets a DisjointSets containing the children of this node such that the sets contained within + * the DisjointSets are such that elements in the same set are branches of this tree node that + * will eventually merge. This could mean that multiple merges take place before this happens. * * @return DisjointSets of tree transitions containing unique non-merging branches */ @@ -143,8 +141,7 @@ public DisjointSets findMergingBranches() { if (element.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) element; nodes.addAll(node.getChildren()); - } - else { + } else { TreeTransition childTran = (TreeTransition) element; if (childTran.getChildNode() != null) { nodes.add(childTran.getChildNode()); @@ -169,8 +166,8 @@ public DisjointSets findMergingBranches() { } /** - * Finds the point at which the set of tree elements passed in will merge. This must be a set gotten from - * findMergingBranches method DisjointSets + * Finds the point at which the set of tree elements passed in will merge. This must be a set + * gotten from findMergingBranches method DisjointSets * * @param branches tree elements to find the merging point * @return tree transition of the merging point or null if no such point exists @@ -186,14 +183,15 @@ public static TreeTransition findMergingPoint(Set branche mergeSet.createSet(element); if (element.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) element; - node.getDescendants().forEach((TreeElement e) -> { - if (!mergeSet.contains(e)) { - mergeSet.createSet(e); - } - mergeSet.union(element, e); - }); - } - else { + node.getDescendants() + .forEach( + (TreeElement e) -> { + if (!mergeSet.contains(e)) { + mergeSet.createSet(e); + } + mergeSet.union(element, e); + }); + } else { TreeTransition transition = (TreeTransition) element; TreeNode childNode = transition.getChildNode(); if (childNode != null) { @@ -228,8 +226,7 @@ public static TreeTransition findMergingPoint(Set branche if (element.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) element; next.addAll(node.getChildren()); - } - else { + } else { TreeTransition tran = (TreeTransition) element; next.add(tran.getChildNode()); } @@ -332,5 +329,4 @@ public void setRoot(boolean isRoot) { public void clearChildren() { this.children.clear(); } - -} \ No newline at end of file +} 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 72572ac72..e79cd4b96 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java @@ -5,7 +5,6 @@ import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.rules.RuleType; - import java.util.ArrayList; import java.util.List; @@ -38,7 +37,7 @@ public TreeTransition(Board board) { * TreeTransition Constructor - create a transition from one node to another * * @param parent parent tree node associated with the transition - * @param board board state of the transition + * @param board board state of the transition */ public TreeTransition(TreeNode parent, Board board) { this(board); @@ -71,8 +70,7 @@ public void propagateChange(PuzzleElement element) { board.removeModifiedData(element); board.notifyChange(element); changed = true; - } - else { + } else { if (!lcaElement.equalsData(element)) { mergedData.setData(element.getData()); board.addModifiedData(mergedData); @@ -89,8 +87,7 @@ public void propagateChange(PuzzleElement element) { } } } - } - else { + } else { // Overwrite previous modifications to this element board.removeModifiedData(board.getPuzzleElement(element)); @@ -98,7 +95,8 @@ public void propagateChange(PuzzleElement element) { board.notifyChange(element); // mark first transition as modified - if (!board.getPuzzleElement(element).equalsData(parents.get(0).getBoard().getPuzzleElement(element))) { + if (!board.getPuzzleElement(element) + .equalsData(parents.get(0).getBoard().getPuzzleElement(element))) { board.addModifiedData(element); } @@ -116,8 +114,7 @@ public void propagateChange(PuzzleElement element) { // Set as modifiable if reverted to starting value (and started modifiable) if (headBoard.getPuzzleElement(element).equalsData(element)) { copy.setModifiable(headBoard.getPuzzleElement(element).isModifiable()); - } - else{ + } else { copy.setModifiable(false); } @@ -159,8 +156,7 @@ public void propagateAddition(PuzzleElement element) { board.removeModifiedData(element); board.notifyDeletion(element); changed = true; - } - else { + } else { if (!lcaElement.equalsData(element)) { mergedData.setData(element.getData()); board.addModifiedData(mergedData); @@ -175,8 +171,7 @@ public void propagateAddition(PuzzleElement element) { } } } - } - else { + } else { if (childNode != null) { board.notifyAddition(element); childNode.getBoard().notifyAddition(element.copy()); @@ -214,8 +209,7 @@ public void propagateDeletion(PuzzleElement element) { board.removeModifiedData(element); board.notifyDeletion(element); changed = true; - } - else { + } else { if (!lcaElement.equalsData(element)) { mergedData.setData(element.getData()); board.addModifiedData(mergedData); @@ -230,8 +224,7 @@ public void propagateDeletion(PuzzleElement element) { } } } - } - else { + } else { if (childNode != null) { board.notifyDeletion(element); childNode.getBoard().notifyDeletion(element.copy()); @@ -244,8 +237,8 @@ public void propagateDeletion(PuzzleElement element) { } /** - * Determines if this tree node leads to a contradiction. Every path from this tree node - * must lead to a contradiction including all of its children + * Determines if this tree node leads to a contradiction. Every path from this tree node must + * lead to a contradiction including all of its children * * @return true if this tree node leads to a contradiction, false otherwise */ @@ -253,12 +246,10 @@ public void propagateDeletion(PuzzleElement element) { public boolean isContradictoryBranch() { if (isJustified() && isCorrect() && rule.getRuleType() == RuleType.CONTRADICTION) { return true; - } - else { + } else { if (childNode == null) { return false; - } - else { + } else { return childNode.isContradictoryBranch() && isJustified() && isCorrect(); } } @@ -269,8 +260,8 @@ public boolean isContradictoryBranch() { * whether this tree puzzleElement and all descendants of this tree puzzleElement is justified * and justified correctly * - * @return true if this tree puzzleElement and all descendants of this tree puzzleElement is valid, - * false otherwise + * @return true if this tree puzzleElement and all descendants of this tree puzzleElement is + * valid, false otherwise */ @Override public boolean isValidBranch() { diff --git a/src/main/java/edu/rpi/legup/puzzle/PuzzleElementTypes.java b/src/main/java/edu/rpi/legup/puzzle/PuzzleElementTypes.java index f2ddb056c..f0dd5a6e9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/PuzzleElementTypes.java +++ b/src/main/java/edu/rpi/legup/puzzle/PuzzleElementTypes.java @@ -1,4 +1,3 @@ package edu.rpi.legup.puzzle; -public enum PuzzleElementTypes { -} +public enum PuzzleElementTypes {} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java b/src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java index 22a980251..41af3f626 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/Battleship.java @@ -17,9 +17,7 @@ public Battleship() { this.factory = new BattleshipCellFactory(); } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { boardView = new BattleshipView((BattleshipBoard) currentBoard); @@ -35,8 +33,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Battleship * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Battleship, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -74,84 +72,98 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { + public void onBoardChange(Board board) {} - } -// -// @Override -// public void onTreeSelectionChange(ArrayList newSelection) -// { -// -// } + // + // @Override + // public void onTreeSelectionChange(ArrayList newSelection) + // { + // + // } @Override public void importPuzzle(String fileName) { -// if(fileName != null) -// { -// InputStream inputStream = new FileInputStream(fileName); -// DocumentBuilder builder = null;//factory.newDocumentBuilder(); -// Document document = builder.parse(inputStream); -// -// BattleShipBoard battleShipBoard; -// -// PuzzleElement rootNode = document.getDocumentElement(); -// PuzzleElement puzzleElement = (PuzzleElement) rootNode.getElementsByTagName("edu.rpi.legup.puzzle").item(0); -// PuzzleElement boardElement = (PuzzleElement) puzzleElement.getElementsByTagName("board").item(0); -// PuzzleElement axesElement = (PuzzleElement) boardElement.getElementsByTagName("axes").item(0); -// PuzzleElement shipElement = (PuzzleElement) boardElement.getElementsByTagName("ships").item(0); -// PuzzleElement cellElement = (PuzzleElement) boardElement.getElementsByTagName("cells").item(0); -// PuzzleElement rightElement = (PuzzleElement) axesElement.getElementsByTagName("right").item(0); -// PuzzleElement bottomElement = (PuzzleElement) axesElement.getElementsByTagName("bottom").item(0); -// NodeList rightClueList = rightElement.getElementsByTagName("clue"); -// NodeList bottomClueList = bottomElement.getElementsByTagName("clue"); -// NodeList shipList = shipElement.getElementsByTagName("ship"); -// NodeList cells = cellElement.getElementsByTagName("cell"); -// -// int size = Integer.valueOf(boardElement.getAttribute("size")); -// battleShipBoard = new BattleShipBoard(size); -// -// ArrayList battleShipData = new ArrayList<>(); -// for(int i = 0; i < size * size; i++) -// { -// battleShipData.add(null); -// } -// -// for (int i = 0; i < rightClueList.getLength(); i++) { -// battleShipBoard.getRight()[i] = Integer.valueOf(rightClueList.item(i).getAttributes().getNamedItem("value").getNodeValue()); -// } -// -// for (int i = 0; i < bottomClueList.getLength(); i++) { -// battleShipBoard.getBottom()[i] = Integer.valueOf(bottomClueList.item(i).getAttributes().getNamedItem("value").getNodeValue()); -// } -// -// for (int i = 0; i < shipList.getLength(); i++) { -// int length = Integer.valueOf(shipList.item(i).getAttributes().getNamedItem("length").getNodeValue()); -// int count = Integer.valueOf(shipList.item(i).getAttributes().getNamedItem("count").getNodeValue()); -// battleShipBoard.getShips().add(new Ship(length, count)); -// } -// -// for (int i = 0; i < cells.getLength(); i++) { -// int x = Integer.valueOf(cells.item(i).getAttributes().getNamedItem("x").getNodeValue()); -// int y = Integer.valueOf(cells.item(i).getAttributes().getNamedItem("y").getNodeValue()); -// String value = cells.item(i).getAttributes().getNamedItem("value").getNodeValue().toUpperCase(); -// -// BattleShipCell cell = new BattleShipCell(BattleShipType.valueOf(value).ordinal(), new Point(x, y)); -// battleShipBoard.setCell(x, y, cell); -// cell.setModifiable(false); -// cell.setGiven(true); -// } -// -// for (int x = 0; x < size; x++) { -// for (int y = 0; y < size; y++) { -// if (battleShipBoard.getCell(x, y) == null) { -// BattleShipCell cell = new BattleShipCell(9, new Point(x, y)); -// cell.setModifiable(true); -// battleShipBoard.setCell(x, y, cell); -// } -// } -// } -// this.currentBoard = battleShipBoard; -// this.tree = new Tree(currentBoard); -// } + // if(fileName != null) + // { + // InputStream inputStream = new FileInputStream(fileName); + // DocumentBuilder builder = null;//factory.newDocumentBuilder(); + // Document document = builder.parse(inputStream); + // + // BattleShipBoard battleShipBoard; + // + // PuzzleElement rootNode = document.getDocumentElement(); + // PuzzleElement puzzleElement = (PuzzleElement) + // rootNode.getElementsByTagName("edu.rpi.legup.puzzle").item(0); + // PuzzleElement boardElement = (PuzzleElement) + // puzzleElement.getElementsByTagName("board").item(0); + // PuzzleElement axesElement = (PuzzleElement) + // boardElement.getElementsByTagName("axes").item(0); + // PuzzleElement shipElement = (PuzzleElement) + // boardElement.getElementsByTagName("ships").item(0); + // PuzzleElement cellElement = (PuzzleElement) + // boardElement.getElementsByTagName("cells").item(0); + // PuzzleElement rightElement = (PuzzleElement) + // axesElement.getElementsByTagName("right").item(0); + // PuzzleElement bottomElement = (PuzzleElement) + // axesElement.getElementsByTagName("bottom").item(0); + // NodeList rightClueList = rightElement.getElementsByTagName("clue"); + // NodeList bottomClueList = bottomElement.getElementsByTagName("clue"); + // NodeList shipList = shipElement.getElementsByTagName("ship"); + // NodeList cells = cellElement.getElementsByTagName("cell"); + // + // int size = Integer.valueOf(boardElement.getAttribute("size")); + // battleShipBoard = new BattleShipBoard(size); + // + // ArrayList battleShipData = new ArrayList<>(); + // for(int i = 0; i < size * size; i++) + // { + // battleShipData.add(null); + // } + // + // for (int i = 0; i < rightClueList.getLength(); i++) { + // battleShipBoard.getRight()[i] = + // Integer.valueOf(rightClueList.item(i).getAttributes().getNamedItem("value").getNodeValue()); + // } + // + // for (int i = 0; i < bottomClueList.getLength(); i++) { + // battleShipBoard.getBottom()[i] = + // Integer.valueOf(bottomClueList.item(i).getAttributes().getNamedItem("value").getNodeValue()); + // } + // + // for (int i = 0; i < shipList.getLength(); i++) { + // int length = + // Integer.valueOf(shipList.item(i).getAttributes().getNamedItem("length").getNodeValue()); + // int count = + // Integer.valueOf(shipList.item(i).getAttributes().getNamedItem("count").getNodeValue()); + // battleShipBoard.getShips().add(new Ship(length, count)); + // } + // + // for (int i = 0; i < cells.getLength(); i++) { + // int x = + // Integer.valueOf(cells.item(i).getAttributes().getNamedItem("x").getNodeValue()); + // int y = + // Integer.valueOf(cells.item(i).getAttributes().getNamedItem("y").getNodeValue()); + // String value = + // cells.item(i).getAttributes().getNamedItem("value").getNodeValue().toUpperCase(); + // + // BattleShipCell cell = new + // BattleShipCell(BattleShipType.valueOf(value).ordinal(), new Point(x, y)); + // battleShipBoard.setCell(x, y, cell); + // cell.setModifiable(false); + // cell.setGiven(true); + // } + // + // for (int x = 0; x < size; x++) { + // for (int y = 0; y < size; y++) { + // if (battleShipBoard.getCell(x, y) == null) { + // BattleShipCell cell = new BattleShipCell(9, new Point(x, y)); + // cell.setModifiable(true); + // battleShipBoard.setCell(x, y, cell); + // } + // } + // } + // this.currentBoard = battleShipBoard; + // this.tree = new Tree(currentBoard); + // } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java index 5965f7055..555c8471f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipBoard.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -15,7 +14,7 @@ public class BattleshipBoard extends GridBoard { /** * Constructor for creating a rectangular battleship board. * - * @param width width of the board + * @param width width of the board * @param height height of the board */ public BattleshipBoard(int width, int height) { @@ -44,8 +43,7 @@ public BattleshipBoard(int size) { /** * Gets the east {@link BattleshipClue} * - * @return List of BattleShipClue objects on the east - * side of the board + * @return List of BattleShipClue objects on the east side of the board */ public List getEast() { return east; @@ -90,8 +88,7 @@ public BattleshipBoard copy() { * Get a list of all orthogonally adjacent cells. * * @param cell The cell to get adjacent cells from. - * @return List of adjacent cells in clockwise order: - * { up, right, down, left } + * @return List of adjacent cells in clockwise order: { up, right, down, left } */ public List getAdjOrthogonals(BattleshipCell cell) { List adj = new ArrayList<>(); @@ -111,8 +108,8 @@ public List getAdjOrthogonals(BattleshipCell cell) { * Get a list of all diagonally adjacent cells. * * @param cell The cell to get diagonally adjacent cells from. - * @return List of diagonally adjacent cells in clockwise order: - * { upRight, downRight, downLeft, upLeft } + * @return List of diagonally adjacent cells in clockwise order: + * { upRight, downRight, downLeft, upLeft } */ public List getAdjDiagonals(BattleshipCell cell) { List dia = new ArrayList<>(); @@ -155,4 +152,4 @@ public List getColumn(int x) { } return column; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java index 5f2c5b975..5a5b86094 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCell.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.battleship; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; public class BattleshipCell extends GridCell { @@ -9,7 +8,7 @@ public class BattleshipCell extends GridCell { /** * BattleShipCell Constructor - creates a BattleShipCell from the specified value and location * - * @param value value of the BattleShipCell + * @param value value of the BattleShipCell * @param location position of the BattleShipCell */ public BattleshipCell(BattleshipType value, Point location) { diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java index 89b5fa19a..9db0cca84 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellController.java @@ -2,39 +2,39 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class BattleshipCellController extends ElementController { /** - * Controller class for the Battleship puzzle - - * receives user mouse input and changes what's shown on the GUI + * Controller class for the Battleship puzzle - receives user mouse input and changes what's + * shown on the GUI * * @param data the PuzzleElement to be changed - * @param e the user mouse input + * @param e the user mouse input */ @Override public void changeCell(MouseEvent e, PuzzleElement data) { BattleshipCell cell = (BattleshipCell) data; if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); - } - else { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { if (cell.getData() == BattleshipType.SHIP_MIDDLE) { cell.setData(BattleshipType.UNKNOWN); - } - else { + } else { cell.setData(BattleshipType.getType(cell.getData().value + 1)); } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() == BattleshipType.UNKNOWN) { cell.setData(BattleshipType.SHIP_MIDDLE); - } - else { + } else { cell.setData(BattleshipType.getType(cell.getData().value - 1)); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java index 81629c360..1b3b6c427 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipCellFactory.java @@ -4,23 +4,23 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class BattleshipCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid */ @Override - public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { + public PuzzleElement importCell(Node node, Board board) + throws InvalidFileFormatException { try { BattleshipBoard battleShipBoard = (BattleshipBoard) board; int width = battleShipBoard.getWidth(); @@ -32,24 +32,25 @@ public PuzzleElement importCell(Node node, Board board) throws I int x = Integer.parseInt(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.parseInt(attributeList.getNamedItem("y").getNodeValue()); if (x >= width || y >= height) { - throw new InvalidFileFormatException("BattleShip Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "BattleShip Factory: cell location out of bounds"); } if (value < 0 || value > 3) { throw new InvalidFileFormatException("BattleShip Factory: cell unknown value"); } - BattleshipCell cell = new BattleshipCell(BattleshipType.getType(value), new Point(x, y)); + BattleshipCell cell = + new BattleshipCell(BattleshipType.getType(value), new Point(x, y)); cell.setIndex(y * height + x); return cell; + } else { + throw new InvalidFileFormatException( + "BattleShip Factory: unknown puzzleElement puzzleElement"); } - else { - throw new InvalidFileFormatException("BattleShip Factory: unknown puzzleElement puzzleElement"); - } - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("BattleShip Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "BattleShip Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("BattleShip Factory: could not find attribute(s)"); } } @@ -57,7 +58,7 @@ public PuzzleElement importCell(Node node, Board board) throws I /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java index 6a29eb7a3..788c860f3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipClueView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.battleship; import edu.rpi.legup.ui.boardview.ElementView; - import java.awt.*; public class BattleshipClueView extends ElementView { @@ -26,6 +25,7 @@ public BattleshipClue getPuzzleElement() { @Override /** * Draws the clue from the PuzzleElement associated with this view on the given frame + * * @param graphics2D the frame the clue is to be drawn on */ public void draw(Graphics2D graphics2D) { diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java index d66f159d7..1898468ce 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.battleship; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class BattleshipElementView extends GridElementView { @@ -22,6 +21,7 @@ public BattleshipElementView(BattleshipCell cell) { @Override /** * Draws on the given frame based on the type of the cell of the current puzzleElement + * * @param graphics2D the frame to be drawn on */ public void drawElement(Graphics2D graphics2D) { @@ -39,37 +39,60 @@ public void drawElement(Graphics2D graphics2D) { break; case SHIP_UNKNOWN: graphics2D.setColor(SHIP_COLOR); - graphics2D.fillRect(location.x + 3 * size.width / 8, location.y + 3 * size.height / 8, - size.width / 4, size.height / 4); + graphics2D.fillRect( + location.x + 3 * size.width / 8, + location.y + 3 * size.height / 8, + size.width / 4, + size.height / 4); graphics2D.setColor(FONT_COLOR); graphics2D.setFont(FONT); FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = "?"; int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + + ((size.height - metrics.getHeight()) / 2) + + metrics.getAscent(); graphics2D.drawString(value, xText, yText); break; case SUBMARINE: graphics2D.setColor(SHIP_COLOR); - graphics2D.fillOval(location.x + size.width / 4, location.y + size.width / 4, - size.width / 2, size.height / 2); + graphics2D.fillOval( + location.x + size.width / 4, + location.y + size.width / 4, + size.width / 2, + size.height / 2); break; case SHIP_TOP: graphics2D.setColor(SHIP_COLOR); - graphics2D.fillArc(location.x, location.y - size.height / 2, size.width, size.height, 180, 180); + graphics2D.fillArc( + location.x, + location.y - size.height / 2, + size.width, + size.height, + 180, + 180); break; case SHIP_RIGHT: graphics2D.setColor(SHIP_COLOR); - graphics2D.fillArc(location.x + size.height / 2, location.y, size.width, size.height, 90, 180); + graphics2D.fillArc( + location.x + size.height / 2, location.y, size.width, size.height, 90, 180); break; case SHIP_BOTTOM: graphics2D.setColor(SHIP_COLOR); - graphics2D.fillArc(location.x, location.y + size.height / 2, size.width, size.height, 0, 180); + graphics2D.fillArc( + location.x, location.y + size.height / 2, size.width, size.height, 0, 180); break; case SHIP_LEFT: graphics2D.setColor(SHIP_COLOR); - graphics2D.fillArc(location.x - size.height / 2, location.y, size.width, size.height, 270, 180); + graphics2D.fillArc( + location.x - size.height / 2, + location.y, + size.width, + size.height, + 270, + 180); break; case SHIP_MIDDLE: graphics2D.setColor(SHIP_COLOR); @@ -84,4 +107,4 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.setStroke(OUTLINE_STROKE); graphics2D.drawRect(location.x, location.y, size.width, size.height); } -} \ No newline at end of file +} 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 4205d0125..cbc364842 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipExporter.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 BattleshipExporter extends PuzzleExporter { @@ -22,8 +21,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { BattleshipBoard board; if (puzzle.getTree() != null) { board = (BattleshipBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (BattleshipBoard) puzzle.getBoardView().getBoard(); } @@ -35,7 +33,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { BattleshipCell cell = (BattleshipCell) puzzleElement; if (cell.getData() != BattleshipType.getType(0)) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } 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 749ceaaa9..cbc1dd02b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class BattleshipImporter extends PuzzleImporter { public BattleshipImporter(Battleship battleShip) { super(battleShip); @@ -26,14 +25,12 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @Override - public void initializeBoard(int rows, int columns) { - - } + public void initializeBoard(int rows, int columns) {} /** * Creates the board for building @@ -45,46 +42,43 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "no puzzleElement found for board"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "no puzzleElement found for board"); } - Element dataElement = (Element) boardElement.getElementsByTagName( - "cells").item(0); + Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); BattleshipBoard battleShipBoard = null; if (!boardElement.getAttribute("size").isEmpty()) { - int size = Integer.valueOf(boardElement.getAttribute( - "size")); + int size = Integer.valueOf(boardElement.getAttribute("size")); battleShipBoard = new BattleshipBoard(size); - } - else { + } else { if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { - int width = Integer.valueOf(boardElement.getAttribute( - "width")); - int height = Integer.valueOf(boardElement.getAttribute( - "height")); + int width = Integer.valueOf(boardElement.getAttribute("width")); + int height = Integer.valueOf(boardElement.getAttribute("height")); battleShipBoard = new BattleshipBoard(width, height); } } if (battleShipBoard == null) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "invalid board dimensions"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "invalid board dimensions"); } int width = battleShipBoard.getWidth(); int height = battleShipBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - BattleshipCell cell = (BattleshipCell) puzzle.getFactory() - .importCell(elementDataList.item(i), battleShipBoard); + BattleshipCell cell = + (BattleshipCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), battleShipBoard); Point loc = cell.getLocation(); if (cell.getData() != BattleshipType.getType(0)) { cell.setModifiable(false); @@ -96,8 +90,8 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (battleShipBoard.getCell(x, y) == null) { - BattleshipCell cell = new BattleshipCell( - BattleshipType.UNKNOWN, new Point(x, y)); + BattleshipCell cell = + new BattleshipCell(BattleshipType.UNKNOWN, new Point(x, y)); cell.setIndex(y * height + x); cell.setModifiable(true); battleShipBoard.setCell(x, y, cell); @@ -107,59 +101,58 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { NodeList axes = boardElement.getElementsByTagName("axis"); if (axes.getLength() != 2) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "cannot find axes"); + throw new InvalidFileFormatException("BattleShip Importer: " + "cannot find axes"); } Element axis1 = (Element) axes.item(0); Element axis2 = (Element) axes.item(1); - if (!axis1.hasAttribute("side") || !axis2.hasAttribute( - "side")) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "side attribute of axis not specified"); + if (!axis1.hasAttribute("side") || !axis2.hasAttribute("side")) { + throw new InvalidFileFormatException( + "BattleShip Importer: " + "side attribute of axis not specified"); } String side1 = axis1.getAttribute("side"); String side2 = axis2.getAttribute("side"); if (side1.equalsIgnoreCase(side2) - || !(side1.equalsIgnoreCase("east") - || side1.equalsIgnoreCase("south")) - || !(side2.equalsIgnoreCase("east") - || side2.equalsIgnoreCase("south"))) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "axes must be different and be {east | south}"); + || !(side1.equalsIgnoreCase("east") || side1.equalsIgnoreCase("south")) + || !(side2.equalsIgnoreCase("east") || side2.equalsIgnoreCase("south"))) { + throw new InvalidFileFormatException( + "BattleShip Importer: " + "axes must be different and be {east | south}"); } - NodeList eastClues = side1.equalsIgnoreCase("east") - ? axis1.getElementsByTagName("clue") : - axis2.getElementsByTagName("clue"); - NodeList southClues = side1.equalsIgnoreCase("south") - ? axis1.getElementsByTagName("clue") : - axis2.getElementsByTagName("clue"); + NodeList eastClues = + side1.equalsIgnoreCase("east") + ? axis1.getElementsByTagName("clue") + : axis2.getElementsByTagName("clue"); + NodeList southClues = + side1.equalsIgnoreCase("south") + ? axis1.getElementsByTagName("clue") + : axis2.getElementsByTagName("clue"); if (eastClues.getLength() != battleShipBoard.getHeight() || southClues.getLength() != battleShipBoard.getWidth()) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "there must be same number of clues as the dimension " + - "of the board"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + + "there must be same number of clues as the dimension " + + "of the board"); } for (int i = 0; i < eastClues.getLength(); i++) { Element clue = (Element) eastClues.item(i); int value = Integer.valueOf(clue.getAttribute("value")); - int index = BattleshipClue.colStringToColNum( - clue.getAttribute("index")); + int index = BattleshipClue.colStringToColNum(clue.getAttribute("index")); if (index - 1 < 0 || index - 1 > battleShipBoard.getHeight()) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "clue index out of bounds"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "clue index out of bounds"); } if (battleShipBoard.getEast().get(index - 1) != null) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "duplicate clue index"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "duplicate clue index"); } - battleShipBoard.getEast().set(index - 1, new BattleshipClue( - value, index, BattleshipType.CLUE_EAST)); + battleShipBoard + .getEast() + .set(index - 1, new BattleshipClue(value, index, BattleshipType.CLUE_EAST)); } for (int i = 0; i < southClues.getLength(); i++) { @@ -168,23 +161,25 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int index = Integer.valueOf(clue.getAttribute("index")); if (index - 1 < 0 || index - 1 > battleShipBoard.getWidth()) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "clue index out of bounds"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "clue index out of bounds"); } if (battleShipBoard.getSouth().get(index - 1) != null) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "duplicate clue index"); + throw new InvalidFileFormatException( + "BattleShip Importer: " + "duplicate clue index"); } - battleShipBoard.getSouth().set(index - 1, new BattleshipClue( - value, index, BattleshipType.CLUE_SOUTH)); + battleShipBoard + .getSouth() + .set( + index - 1, + new BattleshipClue(value, index, BattleshipType.CLUE_SOUTH)); } puzzle.setCurrentBoard(battleShipBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("BattleShip Importer: " + - "unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "BattleShip Importer: " + "unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java index 6994222e3..0d505f43c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipType.java @@ -1,9 +1,19 @@ package edu.rpi.legup.puzzle.battleship; public enum BattleshipType { - UNKNOWN, WATER, SUBMARINE, SHIP_UNKNOWN, - SHIP_TOP, SHIP_RIGHT, SHIP_BOTTOM, SHIP_LEFT, SHIP_MIDDLE, - CLUE_NORTH, CLUE_EAST, CLUE_SOUTH, CLUE_WEST; + UNKNOWN, + WATER, + SUBMARINE, + SHIP_UNKNOWN, + SHIP_TOP, + SHIP_RIGHT, + SHIP_BOTTOM, + SHIP_LEFT, + SHIP_MIDDLE, + CLUE_NORTH, + CLUE_EAST, + CLUE_SOUTH, + CLUE_WEST; public int value; @@ -13,6 +23,7 @@ public enum BattleshipType { /** * Gets the enum of this BattleShipType + * * @param value the integer value input * @return enum equivalent BattleShipType of integer value */ @@ -31,7 +42,11 @@ public static BattleshipType getType(int value) { * @return true if the type is a ship, false otherwise */ public static boolean isShip(BattleshipType type) { - return type == SHIP_UNKNOWN || type == SHIP_TOP || type == SHIP_RIGHT - || type == SHIP_BOTTOM || type == SHIP_LEFT || type == SHIP_MIDDLE; + return type == SHIP_UNKNOWN + || type == SHIP_TOP + || type == SHIP_RIGHT + || type == SHIP_BOTTOM + || type == SHIP_LEFT + || type == SHIP_MIDDLE; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java index fbae2fa99..4095db54a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/BattleshipView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.GridBoardView; - import java.awt.*; public class BattleshipView extends GridBoardView { @@ -17,7 +16,8 @@ public BattleshipView(BattleshipBoard board) { BattleshipElementView elementView = new BattleshipElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } } @@ -26,4 +26,4 @@ public BattleshipView(BattleshipBoard board) { public void drawBoard(Graphics2D graphics2D) { super.drawBoard(graphics2D); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java index 49bee101a..f1ecd6685 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/AdjacentShipsContradictionRule.java @@ -6,31 +6,29 @@ import edu.rpi.legup.puzzle.battleship.BattleshipBoard; import edu.rpi.legup.puzzle.battleship.BattleshipCell; import edu.rpi.legup.puzzle.battleship.BattleshipType; - import java.util.List; public class AdjacentShipsContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE - = "No instance of the contradiction " + this.ruleName + " here"; + private final String NO_CONTRADICTION_MESSAGE = + "No instance of the contradiction " + this.ruleName + " here"; public AdjacentShipsContradictionRule() { - super("BTSP-CONT-0001", + super( + "BTSP-CONT-0001", "Adjacent Ships", "Cells next to the battleship must be water.", - "edu/rpi/legup/images/battleship/contradictions" + - "/AdjacentShips.png"); + "edu/rpi/legup/images/battleship/contradictions" + "/AdjacentShips.png"); } /** - * Checks whether the transition has a contradiction at the specific - * {@link PuzzleElement} index using this rule. + * Checks whether the transition has a contradiction at the specific {@link PuzzleElement} index + * using this rule. * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent {@link PuzzleElement} - * @return null if the transition contains a - * contradiction at the specified {@link PuzzleElement}, - * otherwise return a no contradiction message. + * @return null if the transition contains a contradiction at the specified {@link + * PuzzleElement}, otherwise return a no contradiction message. */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -43,19 +41,20 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } // check orthogonally adjacent cells - List orthoAdjCells - = bsBoard.getAdjOrthogonals(cell); + List orthoAdjCells = bsBoard.getAdjOrthogonals(cell); BattleshipCell up = orthoAdjCells.get(0); BattleshipCell right = orthoAdjCells.get(1); BattleshipCell down = orthoAdjCells.get(2); BattleshipCell left = orthoAdjCells.get(3); - boolean isVertical = (up != null && BattleshipType.isShip(up.getData())) - || (down != null && BattleshipType.isShip(down.getData())); + boolean isVertical = + (up != null && BattleshipType.isShip(up.getData())) + || (down != null && BattleshipType.isShip(down.getData())); - boolean isHorizontal = (left != null && BattleshipType.isShip(left.getData())) - || (right != null && BattleshipType.isShip(right.getData())); + boolean isHorizontal = + (left != null && BattleshipType.isShip(left.getData())) + || (right != null && BattleshipType.isShip(right.getData())); // ships cannot be both vertical and horizontal if (isVertical && isHorizontal) { @@ -63,8 +62,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } // check diagonally adjacent cells - List diagAdjCells - = bsBoard.getAdjDiagonals(cell); + List diagAdjCells = bsBoard.getAdjDiagonals(cell); BattleshipCell upRight = diagAdjCells.get(0); BattleshipCell downRight = diagAdjCells.get(1); diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipDirectRule.java index 18c55d635..670a136a4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ContinueShipDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.battleship.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class ContinueShipDirectRule extends DirectRule { - - public ContinueShipDirectRule() { - super("BTSP-BASC-0001", - "Continue Ship", - "", - "edu/rpi/legup/images/battleship/rules/ContinueShip.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should be overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.battleship.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class ContinueShipDirectRule extends DirectRule { + + public ContinueShipDirectRule() { + super( + "BTSP-BASC-0001", + "Continue Ship", + "", + "edu/rpi/legup/images/battleship/rules/ContinueShip.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should be overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithShipsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithShipsDirectRule.java index 3374d1806..9bc4065aa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithShipsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithShipsDirectRule.java @@ -1,116 +1,108 @@ -package edu.rpi.legup.puzzle.battleship.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.battleship.BattleshipBoard; -import edu.rpi.legup.puzzle.battleship.BattleshipCell; -import edu.rpi.legup.puzzle.battleship.BattleshipClue; -import edu.rpi.legup.puzzle.battleship.BattleshipType; - -import java.awt.*; -import java.util.List; - -public class FinishWithShipsDirectRule extends DirectRule { - - public FinishWithShipsDirectRule() { - super("BTSP-BASC-0002", - "Finish with Ships", - "The number of undetermined squares is equal to the number " + - "of segments remaining for each clue.", - "edu/rpi/legup/images/battleship/rules/finishShip.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should be overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, - PuzzleElement puzzleElement) { - BattleshipBoard initBoard = (BattleshipBoard) transition.getParents() - .get(0).getBoard(); - BattleshipCell initCell = (BattleshipCell) initBoard - .getPuzzleElement(puzzleElement); - BattleshipBoard finalBoard = (BattleshipBoard) transition.getBoard(); - BattleshipCell finalCell = (BattleshipCell) finalBoard - .getPuzzleElement(puzzleElement); - if (!(initCell.getType() == BattleshipType.UNKNOWN - && BattleshipType.isShip(finalCell.getType()))) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be a ship."; - } - - if (isForced(initBoard, initCell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to" + - "be a ship segment."; - } - } - - private boolean isForced(BattleshipBoard board, BattleshipCell cell) { - Point loc = cell.getLocation(); - - // count the number of ship segments and unknowns in the row - List row = board.getRow(loc.y); - int rowCount = 0; - for (BattleshipCell c : row) { - if (c.getType() == BattleshipType.SHIP_UNKNOWN - || BattleshipType.isShip(c.getType())) { - rowCount++; - } - } - - // count the number of ship segments and unknowns in the column - List col = board.getColumn(loc.x); - int colCount = 0; - for (BattleshipCell c : col) { - if (c.getType() == BattleshipType.SHIP_UNKNOWN - || BattleshipType.isShip(c.getType())) { - colCount++; - } - } - - // compare the counts with the clues - BattleshipClue east = board.getEast().get(loc.y); - BattleshipClue south = board.getSouth().get(loc.x); - - return rowCount <= east.getData() && colCount <= south.getData(); - } - - /** - * 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) { - BattleshipBoard board = (BattleshipBoard) node.getBoard().copy(); - for (PuzzleElement element : board.getPuzzleElements()) { - BattleshipCell cell = (BattleshipCell) element; - if (cell.getType() == BattleshipType.UNKNOWN && isForced(board, cell)) { - cell.setData(BattleshipType.SHIP_UNKNOWN); - board.addModifiedData(cell); - } - } - - if (board.getModifiedData().isEmpty()) { - return null; - } - else { - return board; - } - } -} +package edu.rpi.legup.puzzle.battleship.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.battleship.BattleshipBoard; +import edu.rpi.legup.puzzle.battleship.BattleshipCell; +import edu.rpi.legup.puzzle.battleship.BattleshipClue; +import edu.rpi.legup.puzzle.battleship.BattleshipType; +import java.awt.*; +import java.util.List; + +public class FinishWithShipsDirectRule extends DirectRule { + + public FinishWithShipsDirectRule() { + super( + "BTSP-BASC-0002", + "Finish with Ships", + "The number of undetermined squares is equal to the number " + + "of segments remaining for each clue.", + "edu/rpi/legup/images/battleship/rules/finishShip.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should be overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + BattleshipBoard initBoard = (BattleshipBoard) transition.getParents().get(0).getBoard(); + BattleshipCell initCell = (BattleshipCell) initBoard.getPuzzleElement(puzzleElement); + BattleshipBoard finalBoard = (BattleshipBoard) transition.getBoard(); + BattleshipCell finalCell = (BattleshipCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == BattleshipType.UNKNOWN + && BattleshipType.isShip(finalCell.getType()))) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be a ship."; + } + + if (isForced(initBoard, initCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + + ": This cell is not forced to" + + "be a ship segment."; + } + } + + private boolean isForced(BattleshipBoard board, BattleshipCell cell) { + Point loc = cell.getLocation(); + + // count the number of ship segments and unknowns in the row + List row = board.getRow(loc.y); + int rowCount = 0; + for (BattleshipCell c : row) { + if (c.getType() == BattleshipType.SHIP_UNKNOWN || BattleshipType.isShip(c.getType())) { + rowCount++; + } + } + + // count the number of ship segments and unknowns in the column + List col = board.getColumn(loc.x); + int colCount = 0; + for (BattleshipCell c : col) { + if (c.getType() == BattleshipType.SHIP_UNKNOWN || BattleshipType.isShip(c.getType())) { + colCount++; + } + } + + // compare the counts with the clues + BattleshipClue east = board.getEast().get(loc.y); + BattleshipClue south = board.getSouth().get(loc.x); + + return rowCount <= east.getData() && colCount <= south.getData(); + } + + /** + * 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) { + BattleshipBoard board = (BattleshipBoard) node.getBoard().copy(); + for (PuzzleElement element : board.getPuzzleElements()) { + BattleshipCell cell = (BattleshipCell) element; + if (cell.getType() == BattleshipType.UNKNOWN && isForced(board, cell)) { + cell.setData(BattleshipType.SHIP_UNKNOWN); + board.addModifiedData(cell); + } + } + + if (board.getModifiedData().isEmpty()) { + return null; + } else { + return board; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithWaterDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithWaterDirectRule.java index 157b13d01..99e5925e0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithWaterDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/FinishWithWaterDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.battleship.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class FinishWithWaterDirectRule extends DirectRule { - - public FinishWithWaterDirectRule() { - super("BTSP-BASC-0003", - "Finish with Water", - "", - "edu/rpi/legup/images/battleship/rules/finishWater.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.battleship.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class FinishWithWaterDirectRule extends DirectRule { + + public FinishWithWaterDirectRule() { + super( + "BTSP-BASC-0003", + "Finish with Water", + "", + "edu/rpi/legup/images/battleship/rules/finishWater.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/IncompleteShipContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/IncompleteShipContradictionRule.java index 4a6cb3d15..e8cfebb90 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/IncompleteShipContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/IncompleteShipContradictionRule.java @@ -7,19 +7,21 @@ public class IncompleteShipContradictionRule extends ContradictionRule { public IncompleteShipContradictionRule() { - super("BTSP-CONT-0002", + super( + "BTSP-CONT-0002", "Incomplete Ship", "", "edu/rpi/legup/images/battleship/contradictions/IncompleteShip.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeCaseRule.java index 3850ee39f..93079fb71 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeCaseRule.java @@ -5,20 +5,20 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class SegmentTypeCaseRule extends CaseRule { public SegmentTypeCaseRule() { - super("BTSP-CASE-0001", + super( + "BTSP-CASE-0001", "Segment Type", "", "edu/rpi/legup/images/battleship/cases/SegmentType.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -29,13 +29,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -43,7 +44,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -54,9 +56,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeDirectRule.java index f90dea1bd..8576ef722 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SegmentTypeDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.battleship.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class SegmentTypeDirectRule extends DirectRule { - - public SegmentTypeDirectRule() { - super("BTSP-BASC-0004", - "Segment Type", - "", - "edu/rpi/legup/images/battleship/rules/SegmentChoice.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.battleship.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class SegmentTypeDirectRule extends DirectRule { + + public SegmentTypeDirectRule() { + super( + "BTSP-BASC-0004", + "Segment Type", + "", + "edu/rpi/legup/images/battleship/rules/SegmentChoice.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipLocationCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipLocationCaseRule.java index f21488cca..da8f33cc2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipLocationCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipLocationCaseRule.java @@ -5,21 +5,21 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class ShipLocationCaseRule extends CaseRule { public ShipLocationCaseRule() { - super("BTSP-CASE-0002", + super( + "BTSP-CASE-0002", "Ship Location", "", "edu/rpi/legup/images/battleship/cases/ShipLocations.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -30,13 +30,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -44,7 +45,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -55,9 +57,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipOrWaterCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipOrWaterCaseRule.java index a419b831f..3c123d7c1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipOrWaterCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/ShipOrWaterCaseRule.java @@ -5,21 +5,21 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class ShipOrWaterCaseRule extends CaseRule { public ShipOrWaterCaseRule() { - super("BTSP-CASE-0003", + super( + "BTSP-CASE-0003", "Ship or Water", "", "edu/rpi/legup/images/battleship/cases/ShipOrWater.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -30,13 +30,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -44,7 +45,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -55,9 +57,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SurroundShipDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SurroundShipDirectRule.java index 57ad42121..d26c3ed29 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SurroundShipDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/SurroundShipDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.battleship.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class SurroundShipDirectRule extends DirectRule { - - public SurroundShipDirectRule() { - super("BTSP-BASC-0005", - "Surround Ship", - "", - "edu/rpi/legup/images/battleship/rules/SurroundShip.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.battleship.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class SurroundShipDirectRule extends DirectRule { + + public SurroundShipDirectRule() { + super( + "BTSP-BASC-0005", + "Surround Ship", + "", + "edu/rpi/legup/images/battleship/rules/SurroundShip.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewInFleetContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewInFleetContradictionRule.java index 5a4eb187e..d10e086d2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewInFleetContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewInFleetContradictionRule.java @@ -7,19 +7,21 @@ public class TooFewInFleetContradictionRule extends ContradictionRule { public TooFewInFleetContradictionRule() { - super("BTSP-CONT-0003", + super( + "BTSP-CONT-0003", "Too Few in Fleet", "", "edu/rpi/legup/images/battleship/contradictions/too_few_in_fleet.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewRowColContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewRowColContradictionRule.java index 7c2fa7819..382ba6e39 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewRowColContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooFewRowColContradictionRule.java @@ -7,19 +7,21 @@ public class TooFewRowColContradictionRule extends ContradictionRule { public TooFewRowColContradictionRule() { - super("BTSP-CONT-0004", + super( + "BTSP-CONT-0004", "Too few in row/col", "", "edu/rpi/legup/images/battleship/contradictions/too_few_segments.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyInFleetContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyInFleetContradictionRule.java index 3aad88613..d58701c0b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyInFleetContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyInFleetContradictionRule.java @@ -7,19 +7,21 @@ public class TooManyInFleetContradictionRule extends ContradictionRule { public TooManyInFleetContradictionRule() { - super("BTSP-CONT-0005", + super( + "BTSP-CONT-0005", "Too Many in Fleet", "", "edu/rpi/legup/images/battleship/contradictions/too_many_in_fleet.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyRowColContradiction.java b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyRowColContradiction.java index 0970c9c69..27caa0524 100644 --- a/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyRowColContradiction.java +++ b/src/main/java/edu/rpi/legup/puzzle/battleship/rules/TooManyRowColContradiction.java @@ -7,19 +7,21 @@ public class TooManyRowColContradiction extends ContradictionRule { public TooManyRowColContradiction() { - super("BTSP-CONT-0006", + super( + "BTSP-CONT-0006", "Too Many row/col", "", "edu/rpi/legup/images/battleship/contradictions/too_many_segments.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { 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 ef78f66aa..79574caa6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/Fillapix.java @@ -6,9 +6,7 @@ import edu.rpi.legup.model.rules.ContradictionRule; public class Fillapix extends Puzzle { - /** - * Fillapix Constructor - */ + /** Fillapix Constructor */ public Fillapix() { super(); @@ -20,9 +18,7 @@ public Fillapix() { this.factory = new FillapixCellFactory(); } - /** - * Initializes the game board - */ + /** Initializes the game board */ @Override public void initializeView() { boardView = new FillapixView((FillapixBoard) currentBoard); @@ -39,8 +35,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Fillapix * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Fillapix, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -65,7 +61,5 @@ public boolean isBoardComplete(Board board) { } @Override - public void onBoardChange(Board board) { - - } -} \ No newline at end of file + public void onBoardChange(Board board) {} +} 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 67987a6fd..a6672bd4f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixBoard.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.util.logging.Logger; public class FillapixBoard extends GridBoard { - private final static Logger LOGGER = Logger.getLogger(FillapixBoard.class.getName()); + private static final Logger LOGGER = Logger.getLogger(FillapixBoard.class.getName()); public FillapixBoard(int width, int height) { super(width, height); 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 5e6d4b9ed..a9e5aa2df 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java @@ -2,7 +2,6 @@ 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; @@ -42,7 +41,7 @@ public void setCellType(FillapixCellType type) { @Override public void setType(Element e, MouseEvent m) { - switch(e.getElementID()) { + switch (e.getElementID()) { case "FPIX-PLAC-0001": this.setCellType(FillapixCellType.BLACK); break; @@ -88,9 +87,11 @@ public FillapixCell copy() { } 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.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; } @@ -101,4 +102,4 @@ public int compareTo(FillapixCell otherCell) { 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 df3bba403..f59c0df47 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellController.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class FillapixCellController extends ElementController { @@ -11,34 +10,33 @@ public void changeCell(MouseEvent e, PuzzleElement puzzleElement) { FillapixCell cell = (FillapixCell) puzzleElement; if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); - } - else { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { if (cell.getType() == FillapixCellType.UNKNOWN) { cell.setCellType(FillapixCellType.BLACK); - } - else { + } else { if (cell.getType() == FillapixCellType.BLACK) { cell.setCellType(FillapixCellType.WHITE); - } - else { + } else { if (cell.getType() == FillapixCellType.WHITE) { cell.setCellType(FillapixCellType.UNKNOWN); } } } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getType() == FillapixCellType.UNKNOWN) { cell.setCellType(FillapixCellType.WHITE); - } - else { + } else { if (cell.getType() == FillapixCellType.BLACK) { cell.setCellType(FillapixCellType.UNKNOWN); - } - else { + } else { if (cell.getType() == FillapixCellType.WHITE) { cell.setCellType(FillapixCellType.BLACK); } @@ -47,4 +45,4 @@ public void changeCell(MouseEvent e, PuzzleElement puzzleElement) { } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellFactory.java index fcc48bccd..9f689bc96 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class FillapixCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -23,7 +22,8 @@ public class FillapixCellFactory extends ElementFactory { public FillapixCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("Fillapix Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "Fillapix Factory: unknown puzzleElement puzzleElement"); } FillapixBoard fillapixBoard = (FillapixBoard) board; @@ -35,7 +35,8 @@ public FillapixCell importCell(Node node, Board board) throws InvalidFileFormatE int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); if (x >= width || y >= height) { - throw new InvalidFileFormatException("Fillapix Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "Fillapix Factory: cell location out of bounds"); } if (value / 100 > 2 || value % 100 > 10) { throw new InvalidFileFormatException("Fillapix Factory: cell unknown value"); @@ -44,11 +45,10 @@ public FillapixCell importCell(Node node, Board board) throws InvalidFileFormatE FillapixCell cell = new FillapixCell(value, new Point(x, y)); cell.setIndex(y * height + x); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Fillapix Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Fillapix Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("Fillapix Factory: could not find attribute(s)"); } } @@ -56,7 +56,7 @@ public FillapixCell importCell(Node node, Board board) throws InvalidFileFormatE /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellType.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellType.java index 3789fe339..28a263467 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCellType.java @@ -1,7 +1,9 @@ package edu.rpi.legup.puzzle.fillapix; public enum FillapixCellType { - UNKNOWN(0), BLACK(1), WHITE(2); + UNKNOWN(0), + BLACK(1), + WHITE(2); public int value; 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 93fa0d451..47465a522 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.fillapix; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class FillapixElementView extends GridElementView { @@ -53,10 +52,11 @@ public void drawElement(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(cell.getNumber()); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(value, xText, yText); } graphics2D.setColor(BLACK_COLOR); graphics2D.drawRect(location.x, location.y, size.width, size.height); } -} \ No newline at end of file +} 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..ffabd8762 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,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { FillapixBoard board; if (puzzle.getTree() != null) { board = (FillapixBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (FillapixBoard) puzzle.getBoardView().getBoard(); } @@ -29,7 +27,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { FillapixCell cell = (FillapixCell) puzzleElement; if (cell.getNumber() != -1 || cell.getType() != FillapixCellType.UNKNOWN) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } 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 5213e7139..d16bc8219 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class FillapixImporter extends PuzzleImporter { public FillapixImporter(Fillapix fillapix) { super(fillapix); @@ -26,7 +25,7 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be made */ @@ -36,7 +35,8 @@ public void initializeBoard(int rows, int columns) { 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)); + FillapixCell cell = + new FillapixCell(FillapixCellType.UNKNOWN.value, new Point(x, y)); cell.setIndex(y * columns + x); cell.setNumber(FillapixCell.DEFAULT_VALUE); cell.setModifiable(true); @@ -56,11 +56,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("Fillapix Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "Fillapix Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("Fillapix Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "Fillapix Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -69,9 +71,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); fillapixBoard = new FillapixBoard(size); - } - else { - if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); fillapixBoard = new FillapixBoard(width, height); @@ -86,7 +88,10 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = fillapixBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - FillapixCell cell = (FillapixCell) puzzle.getFactory().importCell(elementDataList.item(i), fillapixBoard); + FillapixCell cell = + (FillapixCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), fillapixBoard); Point loc = cell.getLocation(); cell.setModifiable(true); cell.setGiven(true); @@ -96,7 +101,8 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (fillapixBoard.getCell(x, y) == null) { - FillapixCell cell = new FillapixCell(FillapixCell.DEFAULT_VALUE, new Point(x, y)); + FillapixCell cell = + new FillapixCell(FillapixCell.DEFAULT_VALUE, new Point(x, y)); cell.setIndex(y * height + x); cell.setModifiable(true); fillapixBoard.setCell(x, y, cell); @@ -104,9 +110,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } puzzle.setCurrentBoard(fillapixBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Fillapix Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Fillapix Importer: unknown value where integer expected"); } } 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 a7feac91d..571a0c7f7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixUtilities.java @@ -3,14 +3,14 @@ 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(); + TooFewBlackCellsContradictionRule tooManyBlackCells = + new TooFewBlackCellsContradictionRule(); FillapixBoard whiteCaseBoard = board.copy(); FillapixCell whiteCell = (FillapixCell) whiteCaseBoard.getPuzzleElement(cell); whiteCell.setCellType(FillapixCellType.WHITE); @@ -24,7 +24,8 @@ public static boolean isForcedBlack(FillapixBoard board, FillapixCell cell) { } public static boolean isForcedWhite(FillapixBoard board, FillapixCell cell) { - TooManyBlackCellsContradictionRule tooManyBlackCells = new TooManyBlackCellsContradictionRule(); + TooManyBlackCellsContradictionRule tooManyBlackCells = + new TooManyBlackCellsContradictionRule(); FillapixBoard blackCaseBoard = board.copy(); FillapixCell blackCell = (FillapixCell) blackCaseBoard.getPuzzleElement(cell); blackCell.setCellType(FillapixCellType.BLACK); @@ -59,15 +60,16 @@ public static boolean hasEmptyAdjacent(FillapixBoard board, FillapixCell cell) { return false; } - /** - * Gets all cells adjacent to a specific cell. The cell itself will be included. - */ + /** 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()) { + 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); @@ -81,22 +83,34 @@ public static ArrayList getAdjacentCells(FillapixBoard board, Fill } /** - * 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 + * 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) { + 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()) { + 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); @@ -106,8 +120,13 @@ public static ArrayList getCellsAtDistance(FillapixBoard board, Fi 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()) { + 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); @@ -115,10 +134,15 @@ public static ArrayList getCellsAtDistance(FillapixBoard board, Fi 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()) { + 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); @@ -126,10 +150,15 @@ public static ArrayList getCellsAtDistance(FillapixBoard board, Fi 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()) { + 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); @@ -137,25 +166,25 @@ public static ArrayList getCellsAtDistance(FillapixBoard board, Fi 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: + * 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 if the corresponding item is - * included in that combination, and false otherwise. + * @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 if the corresponding item is included in that combination, and + * + * false otherwise. */ public static ArrayList getCombinations(int chosenNumItems, int totalNumItems) { ArrayList combinations = new ArrayList(); @@ -165,9 +194,15 @@ public static ArrayList getCombinations(int chosenNumItems, int total 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) { + 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) { @@ -184,19 +219,19 @@ private static void recurseCombinations(ArrayList result, int curInde if (numBlack < maxBlack) { workingArray[curIndex] = true; - recurseCombinations(result, curIndex+1, maxBlack, numBlack+1, len, workingArray); + recurseCombinations(result, curIndex + 1, maxBlack, numBlack + 1, len, workingArray); } workingArray[curIndex] = false; - recurseCombinations(result, curIndex+1, maxBlack, numBlack, len, workingArray); + 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) { + 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; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixView.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixView.java index 7e6453f58..55332e47d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixView.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.GridBoardView; - import java.awt.*; public class FillapixView extends GridBoardView { @@ -16,8 +15,9 @@ public FillapixView(FillapixBoard board) { FillapixElementView elementView = new FillapixElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } } -} \ No newline at end of file +} 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 index 7e43fc6b4..1d7c038a3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java @@ -4,6 +4,10 @@ public class BlackTile extends PlaceableElement { public BlackTile() { - super("FPIX-PLAC-0001", "Black Tile", "The black tile", "edu/rpi/legup/images/fillapix/tiles/BlackTile.png"); + 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 index beee70e21..e869aeaf9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java @@ -6,7 +6,11 @@ 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"); + super( + "FPIX-UNPL-0001", + "Number Tile", + "A numbered tile", + "edu/rpi/legup/images/fillapix/tiles/NumberTile.png"); object_num = 0; } @@ -23,5 +27,4 @@ public int getTileNumber() { 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 index ef754782f..6778c1758 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java @@ -4,6 +4,10 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("FPIX-UNPL-0002", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/fillapix/tiles/UnknownTile.png"); + 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 index dd27d2834..67065a7e9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java @@ -4,6 +4,10 @@ public class WhiteTile extends PlaceableElement { public WhiteTile() { - super("FPIX-PLAC-0002", "White Tile", "The white tile", "edu/rpi/legup/images/fillapix/tiles/WhiteTile.png"); + 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/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java index 1e5151c48..860a6c011 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 @@ -8,13 +8,13 @@ import edu.rpi.legup.puzzle.fillapix.FillapixBoard; import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; - import java.util.ArrayList; import java.util.List; public class BlackOrWhiteCaseRule extends CaseRule { public BlackOrWhiteCaseRule() { - super("FPIX-CASE-0001", + super( + "FPIX-CASE-0001", "Black or White", "Each cell is either black or white.", "edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png"); @@ -62,20 +62,24 @@ public String checkRuleRaw(TreeTransition transition) { TreeTransition case1 = childTransitions.get(0); TreeTransition case2 = childTransitions.get(1); - if (case1.getBoard().getModifiedData().size() != 1 || - case2.getBoard().getModifiedData().size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; + if (case1.getBoard().getModifiedData().size() != 1 + || case2.getBoard().getModifiedData().size() != 1) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case."; } FillapixCell mod1 = (FillapixCell) case1.getBoard().getModifiedData().iterator().next(); FillapixCell mod2 = (FillapixCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; } if (!((mod1.getType() == FillapixCellType.BLACK && mod2.getType() == FillapixCellType.WHITE) - || (mod2.getType() == FillapixCellType.BLACK && mod1.getType() == FillapixCellType.WHITE))) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must an empty cell and a lit cell."; + || (mod2.getType() == FillapixCellType.BLACK + && mod1.getType() == FillapixCellType.WHITE))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must an empty cell and a lit cell."; } return null; @@ -85,4 +89,4 @@ public String checkRuleRaw(TreeTransition transition) { 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/FinishWithBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java index 06a8045ed..ccc002f46 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 @@ -1,63 +1,66 @@ -package edu.rpi.legup.puzzle.fillapix.rules; - -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.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 FinishWithBlackDirectRule extends DirectRule { - public FinishWithBlackDirectRule() { - super("FPIX-BASC-0001", - "Finish with Black", - "The remaining unknowns around and on a cell must be black to satisfy the number", - "edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png"); - } - - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - FillapixBoard board = (FillapixBoard) transition.getBoard(); - FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard(); - FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); - FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); - - if (!(parentCell.getType() == FillapixCellType.UNKNOWN && cell.getType() == FillapixCellType.BLACK)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be black to be applicable with this rule."; - } - - if (FillapixUtilities.isForcedBlack(parentBoard, cell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be black"; - } - } - - /** - * 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) { - FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy(); - for (PuzzleElement element : fillapixBoard.getPuzzleElements()) { - FillapixCell cell = (FillapixCell) element; - if (cell.getType() == FillapixCellType.UNKNOWN && FillapixUtilities.isForcedBlack((FillapixBoard) node.getBoard(), cell)) { - cell.setCellType(FillapixCellType.BLACK); - fillapixBoard.addModifiedData(cell); - } - } - if (fillapixBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return fillapixBoard; - } - } -} \ No newline at end of file +package edu.rpi.legup.puzzle.fillapix.rules; + +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.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 FinishWithBlackDirectRule extends DirectRule { + public FinishWithBlackDirectRule() { + super( + "FPIX-BASC-0001", + "Finish with Black", + "The remaining unknowns around and on a cell must be black to satisfy the number", + "edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + FillapixBoard board = (FillapixBoard) transition.getBoard(); + FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard(); + FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); + FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); + + if (!(parentCell.getType() == FillapixCellType.UNKNOWN + && cell.getType() == FillapixCellType.BLACK)) { + return super.getInvalidUseOfRuleMessage() + + ": This cell must be black to be applicable with this rule."; + } + + if (FillapixUtilities.isForcedBlack(parentBoard, cell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be black"; + } + } + + /** + * 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) { + FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy(); + for (PuzzleElement element : fillapixBoard.getPuzzleElements()) { + FillapixCell cell = (FillapixCell) element; + if (cell.getType() == FillapixCellType.UNKNOWN + && FillapixUtilities.isForcedBlack((FillapixBoard) node.getBoard(), cell)) { + cell.setCellType(FillapixCellType.BLACK); + fillapixBoard.addModifiedData(cell); + } + } + if (fillapixBoard.getModifiedData().isEmpty()) { + return null; + } else { + return fillapixBoard; + } + } +} 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 7e213a59c..6735fb8f4 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 @@ -1,63 +1,66 @@ -package edu.rpi.legup.puzzle.fillapix.rules; - -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.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 FinishWithWhiteDirectRule extends DirectRule { - public FinishWithWhiteDirectRule() { - 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"); - } - - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - FillapixBoard board = (FillapixBoard) transition.getBoard(); - FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard(); - FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); - FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); - - if (!(parentCell.getType() == FillapixCellType.UNKNOWN && cell.getType() == FillapixCellType.WHITE)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be white to be applicable with this rule"; - } - - if (FillapixUtilities.isForcedWhite(parentBoard, cell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be white"; - } - } - - /** - * 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) { - FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy(); - for (PuzzleElement element : fillapixBoard.getPuzzleElements()) { - FillapixCell cell = (FillapixCell) element; - if (cell.getType() == FillapixCellType.UNKNOWN && FillapixUtilities.isForcedWhite((FillapixBoard) node.getBoard(), cell)) { - cell.setCellType(FillapixCellType.WHITE); - fillapixBoard.addModifiedData(cell); - } - } - if (fillapixBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return fillapixBoard; - } - } -} \ No newline at end of file +package edu.rpi.legup.puzzle.fillapix.rules; + +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.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 FinishWithWhiteDirectRule extends DirectRule { + public FinishWithWhiteDirectRule() { + 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"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + FillapixBoard board = (FillapixBoard) transition.getBoard(); + FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard(); + FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement); + FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement); + + if (!(parentCell.getType() == FillapixCellType.UNKNOWN + && cell.getType() == FillapixCellType.WHITE)) { + return super.getInvalidUseOfRuleMessage() + + ": This cell must be white to be applicable with this rule"; + } + + if (FillapixUtilities.isForcedWhite(parentBoard, cell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be white"; + } + } + + /** + * 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) { + FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy(); + for (PuzzleElement element : fillapixBoard.getPuzzleElements()) { + FillapixCell cell = (FillapixCell) element; + if (cell.getType() == FillapixCellType.UNKNOWN + && FillapixUtilities.isForcedWhite((FillapixBoard) node.getBoard(), cell)) { + cell.setCellType(FillapixCellType.WHITE); + fillapixBoard.addModifiedData(cell); + } + } + if (fillapixBoard.getModifiedData().isEmpty()) { + return null; + } else { + return fillapixBoard; + } + } +} 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 index 656cedb3f..e91931bd4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java @@ -1,103 +1,107 @@ -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 +package edu.rpi.legup.puzzle.fillapix.rules; + +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; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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; + } +} 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 index fe94dbcb4..71a85a95b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java @@ -1,99 +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 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 +package edu.rpi.legup.puzzle.fillapix.rules; + +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; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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; + } +} 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 index 4520add31..7db833f76 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java @@ -1,12 +1,5 @@ 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; @@ -17,13 +10,20 @@ import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; 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"); + super( + "FPIX-CASE-0002", + "Satisfy Clue", + "Each clue must touch that number of squares.", + "edu/rpi/legup/images/fillapix/cases/SatisfyClue.png"); } @Override @@ -33,7 +33,9 @@ public CaseBoard getCaseBoard(Board board) { fillapixBoard.setModifiable(false); for (PuzzleElement data : fillapixBoard.getPuzzleElements()) { FillapixCell cell = (FillapixCell) data; - if (cell.getNumber() >= 0 && cell.getNumber() <= 9 && FillapixUtilities.hasEmptyAdjacent(fillapixBoard, cell)) { + if (cell.getNumber() >= 0 + && cell.getNumber() <= 9 + && FillapixUtilities.hasEmptyAdjacent(fillapixBoard, cell)) { caseBoard.addPickableElement(data); } } @@ -70,26 +72,25 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { 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++) { + for (int i = 0; i < combinations.size(); i++) { Board case_ = board.copy(); - for (int j=0; j < combinations.get(i).length; j++) { + 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 { + } else { cell.setCellType(FillapixCellType.WHITE); } case_.addModifiedData(cell); } cases.add(case_); } - + return cases; } @@ -99,26 +100,24 @@ public String checkRuleRaw(TreeTransition transition) { List childTransitions = parent.getChildren(); /* - * In order for the transition to be valid, it can only be applied to + * 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 + * * 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; @@ -141,14 +140,16 @@ public String checkRuleRaw(TreeTransition transition) { return super.getInvalidUseOfRuleMessage(); } - - /* get the center of all possible 3X3 squares, + /* 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())); + possibleCenters.addAll( + FillapixUtilities.getAdjacentCells( + board, (FillapixCell) modCells.iterator().next())); for (PuzzleElement modCell : modCells) { - possibleCenters.retainAll((FillapixUtilities.getAdjacentCells(board, (FillapixCell) modCell))); + possibleCenters.retainAll( + (FillapixUtilities.getAdjacentCells(board, (FillapixCell) modCell))); } // removing all elements without a valid number possibleCenters.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10); @@ -156,7 +157,6 @@ public String checkRuleRaw(TreeTransition transition) { return super.getInvalidUseOfRuleMessage(); } - /* Now go through the remaining centers, and check if their combinations * match the transitions */ for (FillapixCell possibleCenter : possibleCenters) { @@ -176,7 +176,8 @@ public String checkRuleRaw(TreeTransition transition) { continue; } - ArrayList combinations = FillapixUtilities.getCombinations(maxBlack - numBlack, numEmpty); + ArrayList combinations = + FillapixUtilities.getCombinations(maxBlack - numBlack, numEmpty); if (combinations.size() != childTransitions.size()) { // not this center because combinations do not match transitions continue; @@ -192,11 +193,10 @@ public String checkRuleRaw(TreeTransition transition) { } boolean[] translatedModCells = new boolean[transModCells.size()]; - for (int i=0; i < transModCells.size(); i++) { + for (int i = 0; i < transModCells.size(); i++) { if (transModCells.get(i).getType() == FillapixCellType.BLACK) { translatedModCells[i] = true; - } - else { + } else { translatedModCells[i] = false; } } @@ -226,9 +226,8 @@ public String checkRuleRaw(TreeTransition transition) { 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 c37050978..df5954d63 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 @@ -7,25 +7,26 @@ import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; - import java.util.ArrayList; public class TooFewBlackCellsContradictionRule extends ContradictionRule { public TooFewBlackCellsContradictionRule() { - super("FPIX-CONT-0001", + super( + "FPIX-CONT-0001", "Too Few Black Cells", "There may not be fewer black cells than the number.", "edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { 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 68395ce7f..38b47c972 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 @@ -7,25 +7,26 @@ import edu.rpi.legup.puzzle.fillapix.FillapixCell; import edu.rpi.legup.puzzle.fillapix.FillapixCellType; import edu.rpi.legup.puzzle.fillapix.FillapixUtilities; - import java.util.ArrayList; public class TooManyBlackCellsContradictionRule extends ContradictionRule { public TooManyBlackCellsContradictionRule() { - super("FPIX-CONT-0002", + super( + "FPIX-CONT-0002", "Too Many Black Cells", "There may not be more black cells than the number", "edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -45,8 +46,8 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } 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 index 6f4be7842..72e2def47 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java @@ -1,108 +1,113 @@ -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 +package edu.rpi.legup.puzzle.fillapix.rules; + +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; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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; + } +} 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 index bd6dd0169..281dd5392 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java @@ -1,115 +1,126 @@ -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 +package edu.rpi.legup.puzzle.fillapix.rules; + +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; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java index fc52a6fff..afe3dd652 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/Heyawake.java @@ -16,9 +16,7 @@ public Heyawake() { this.importer = new HeyawakeImporter(this); } - /** - * Initializes the view. Called by the invoker of the class - */ + /** Initializes the view. Called by the invoker of the class */ @Override public void initializeView() { boardView = new HeyawakeView((HeyawakeBoard) currentBoard); @@ -39,8 +37,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for HeyAwake * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for HeyAwake, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -65,7 +63,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} } diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeBoard.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeBoard.java index 2f15213ec..cedb3a91b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeBoard.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeCell.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeCell.java index ce8c73424..480e2e39b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeCell.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.heyawake; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; public class HeyawakeCell extends GridCell { diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeController.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeController.java index 2bad91933..2d081d7da 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeController.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeController.java @@ -2,5 +2,4 @@ import edu.rpi.legup.controller.ElementController; -public class HeyawakeController extends ElementController { -} +public class HeyawakeController extends ElementController {} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeElementView.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeElementView.java index 5efec44ff..d30159856 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.heyawake; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class HeyawakeElementView extends GridElementView { @@ -39,7 +38,8 @@ public void drawElement(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(puzzleElement.getData()); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); } } 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 344a0be92..4470d77be 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeExporter.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 HeyawakeExporter extends PuzzleExporter { @@ -16,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { HeyawakeBoard board; if (puzzle.getTree() != null) { board = (HeyawakeBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (HeyawakeBoard) puzzle.getBoardView().getBoard(); } @@ -29,7 +27,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { HeyawakeCell cell = (HeyawakeCell) puzzleElement; if (cell.getData() != -2) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeFactory.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeFactory.java index 96fc20e6f..8ecf24135 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class HeyawakeFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -23,7 +22,8 @@ public class HeyawakeFactory extends ElementFactory { public HeyawakeCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("Heyawake Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "Heyawake Factory: unknown puzzleElement puzzleElement"); } HeyawakeBoard heyawakeBoard = (HeyawakeBoard) board; @@ -36,7 +36,8 @@ public HeyawakeCell importCell(Node node, Board board) throws InvalidFileFormatE int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); int regionIndex = Integer.valueOf(attributeList.getNamedItem("region").getNodeValue()); if (x >= width || y >= height) { - throw new InvalidFileFormatException("Heyawake Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "Heyawake Factory: cell location out of bounds"); } if (value < -4 || value > 4) { throw new InvalidFileFormatException("Heyawake Factory: cell unknown value"); @@ -46,11 +47,10 @@ public HeyawakeCell importCell(Node node, Board board) throws InvalidFileFormatE cell.setIndex(y * height + x); heyawakeBoard.getRegions(); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Heyawake Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Heyawake Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("Heyawake Factory: could not find attribute(s)"); } } @@ -58,7 +58,7 @@ public HeyawakeCell importCell(Node node, Board board) throws InvalidFileFormatE /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ 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 7527c717f..6bbabd54d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class HeyawakeImporter extends PuzzleImporter { public HeyawakeImporter(Heyawake heyawake) { @@ -27,14 +26,12 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @Override - public void initializeBoard(int rows, int columns) { - - } + public void initializeBoard(int rows, int columns) {} /** * Creates the board for building @@ -46,11 +43,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("Heyawake Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "Heyawake Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("Heyawake Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "Heyawake Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -59,9 +58,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); heyawakeBoard = new HeyawakeBoard(size); - } - else { - if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); heyawakeBoard = new HeyawakeBoard(width, height); @@ -76,7 +75,10 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = heyawakeBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - HeyawakeCell cell = (HeyawakeCell) puzzle.getFactory().importCell(elementDataList.item(i), heyawakeBoard); + HeyawakeCell cell = + (HeyawakeCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), heyawakeBoard); Point loc = cell.getLocation(); if (cell.getData() != -2) { cell.setModifiable(false); @@ -96,9 +98,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } puzzle.setCurrentBoard(heyawakeBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Heyawake Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Heyawake Importer: unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeView.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeView.java index 00506df0f..fba5f0eb7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeView.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/HeyawakeView.java @@ -4,7 +4,6 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.GridBoardView; - import java.awt.*; import java.awt.geom.Area; import java.util.HashMap; @@ -25,14 +24,14 @@ public HeyawakeView(HeyawakeBoard board) { HeyawakeElementView elementView = new HeyawakeElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); int regionIndex = cell.getRegionIndex(); if (regionsBoundaries.get(regionIndex) == null) { regionsBoundaries.put(regionIndex, new Area(elementView.getBounds())); - } - else { + } else { regionsBoundaries.get(regionIndex).add(new Area(elementView.getBounds())); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/AdjacentBlacksContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/AdjacentBlacksContradictionRule.java index d2eceb7a1..88d293f5e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/AdjacentBlacksContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/AdjacentBlacksContradictionRule.java @@ -7,18 +7,21 @@ public class AdjacentBlacksContradictionRule extends ContradictionRule { public AdjacentBlacksContradictionRule() { - super("HEYA-CONT-0001", "Adjacent Blacks", + super( + "HEYA-CONT-0001", + "Adjacent Blacks", "", "edu/rpi/legup/images/heyawake/contradictions/adjacentBlacks.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackOrWhiteCaseRule.java index 0e7552dd2..251be46ee 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackOrWhiteCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackOrWhiteCaseRule.java @@ -5,21 +5,21 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class BlackOrWhiteCaseRule extends CaseRule { public BlackOrWhiteCaseRule() { - super("HEYA-CASE-0001", + super( + "HEYA-CASE-0001", "Black or White", "", "edu/rpi/legup/images/heyawake/cases/BlackOrWhite.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -30,13 +30,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -44,7 +45,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -55,9 +57,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackPathDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackPathDirectRule.java index 474bdccdd..1fc3d0776 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackPathDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BlackPathDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class BlackPathDirectRule { - public BlackPathDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class BlackPathDirectRule { + public BlackPathDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BottleNeckDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BottleNeckDirectRule.java index f2e7fef5c..41d950378 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BottleNeckDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/BottleNeckDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class BottleNeckDirectRule { - public BottleNeckDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class BottleNeckDirectRule { + public BottleNeckDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomBlackDirectRule.java index 828739160..aee1ae4bf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomBlackDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomBlackDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class FillRoomBlackDirectRule extends DirectRule { - - public FillRoomBlackDirectRule() { - super("HEYA-BASC-0003", - "Fill Room Black", - "", - "edu/rpi/legup/images/heyawake/rules/FillRoomBlack.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class FillRoomBlackDirectRule extends DirectRule { + + public FillRoomBlackDirectRule() { + super( + "HEYA-BASC-0003", + "Fill Room Black", + "", + "edu/rpi/legup/images/heyawake/rules/FillRoomBlack.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomWhiteDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomWhiteDirectRule.java index 6b1a11c29..c4609b239 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomWhiteDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/FillRoomWhiteDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class FillRoomWhiteDirectRule extends DirectRule { - - public FillRoomWhiteDirectRule() { - super("HEYA-BASC-0004", - "Fill Room White", - "", - "edu/rpi/legup/images/heyawake/rules/FillRoomWhite.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class FillRoomWhiteDirectRule extends DirectRule { + + public FillRoomWhiteDirectRule() { + super( + "HEYA-BASC-0004", + "Fill Room White", + "", + "edu/rpi/legup/images/heyawake/rules/FillRoomWhite.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/OneRowDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/OneRowDirectRule.java index 3e8c07a4d..8b6f21564 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/OneRowDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/OneRowDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class OneRowDirectRule { - public OneRowDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class OneRowDirectRule { + public OneRowDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/PreventWhiteLineDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/PreventWhiteLineDirectRule.java index 0efd90694..f5d4e78b7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/PreventWhiteLineDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/PreventWhiteLineDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class PreventWhiteLineDirectRule { - public PreventWhiteLineDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class PreventWhiteLineDirectRule { + public PreventWhiteLineDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooEmptyContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooEmptyContradictionRule.java index 0e0beaeb2..762b435a2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooEmptyContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooEmptyContradictionRule.java @@ -7,19 +7,21 @@ public class RoomTooEmptyContradictionRule extends ContradictionRule { public RoomTooEmptyContradictionRule() { - super("HEYA-CONT-0002", + super( + "HEYA-CONT-0002", "Room too Empty", "", "edu/rpi/legup/images/heyawake/contradictions/RoomTooEmpty.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooFullContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooFullContradictionRule.java index 1de746cb2..643f75f13 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooFullContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/RoomTooFullContradictionRule.java @@ -7,19 +7,21 @@ public class RoomTooFullContradictionRule extends ContradictionRule { public RoomTooFullContradictionRule() { - super("HEYA-CONT-0003", + super( + "HEYA-CONT-0003", "Room too Full", "", "edu/rpi/legup/images/heyawake/contradictions/RoomTooFull.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ThreeByThreeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ThreeByThreeDirectRule.java index 6dee3fc83..cf2c241e3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ThreeByThreeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ThreeByThreeDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class ThreeByThreeDirectRule { - public ThreeByThreeDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class ThreeByThreeDirectRule { + public ThreeByThreeDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/TwoInCornerDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/TwoInCornerDirectRule.java index 8e2776fc7..83f1ca72f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/TwoInCornerDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/TwoInCornerDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class TwoInCornerDirectRule { - public TwoInCornerDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class TwoInCornerDirectRule { + public TwoInCornerDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAreaContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAreaContradictionRule.java index a287d9c78..c0987d4f9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAreaContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAreaContradictionRule.java @@ -7,19 +7,21 @@ public class WhiteAreaContradictionRule extends ContradictionRule { public WhiteAreaContradictionRule() { - super("HEYA-CONT-0004", + super( + "HEYA-CONT-0004", "White Area", "", "edu/rpi/legup/images/heyawake/contradictions/WhiteArea.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAroundBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAroundBlackDirectRule.java index 21a698d41..2e3bc1812 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAroundBlackDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteAroundBlackDirectRule.java @@ -1,43 +1,45 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class WhiteAroundBlackDirectRule extends DirectRule { - - public WhiteAroundBlackDirectRule() { - super("HEYA-BASC-0009", - "White Around Black", - "", - "edu/rpi/legup/images/heyawake/rules/WhiteAroundBlack.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class WhiteAroundBlackDirectRule extends DirectRule { + + public WhiteAroundBlackDirectRule() { + super( + "HEYA-BASC-0009", + "White Around Black", + "", + "edu/rpi/legup/images/heyawake/rules/WhiteAroundBlack.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteEscapeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteEscapeDirectRule.java index 231d353e6..f151ec12a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteEscapeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteEscapeDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class WhiteEscapeDirectRule { - public WhiteEscapeDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class WhiteEscapeDirectRule { + public WhiteEscapeDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteLineContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteLineContradictionRule.java index 835bce98c..b695b8206 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteLineContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/WhiteLineContradictionRule.java @@ -7,20 +7,21 @@ public class WhiteLineContradictionRule extends ContradictionRule { public WhiteLineContradictionRule() { - super("HEYA-CONT-0005", + super( + "HEYA-CONT-0005", "White Line", "", "edu/rpi/legup/images/heyawake/contradictions/WhiteLine.png"); - } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ZigZagWhiteDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ZigZagWhiteDirectRule.java index 22a76b9a9..9df735b0b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ZigZagWhiteDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/heyawake/rules/ZigZagWhiteDirectRule.java @@ -1,7 +1,7 @@ -package edu.rpi.legup.puzzle.heyawake.rules; - -public class ZigZagWhiteDirectRule { - public ZigZagWhiteDirectRule() { - throw new RuntimeException("This rule has not been implemented"); - } -} +package edu.rpi.legup.puzzle.heyawake.rules; + +public class ZigZagWhiteDirectRule { + public ZigZagWhiteDirectRule() { + throw new RuntimeException("This rule has not been implemented"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java index 6a67d28ca..ab95c4658 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java @@ -19,9 +19,7 @@ public LightUp() { this.factory = new LightUpCellFactory(); } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { boardView = new LightUpView((LightUpBoard) currentBoard); @@ -44,8 +42,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Light Up * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Light Up, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -71,7 +69,9 @@ public boolean isBoardComplete(Board board) { } for (PuzzleElement data : lightUpBoard.getPuzzleElements()) { LightUpCell cell = (LightUpCell) data; - if ((cell.getType() == LightUpCellType.UNKNOWN || cell.getType() == LightUpCellType.EMPTY) && !cell.isLite()) { + if ((cell.getType() == LightUpCellType.UNKNOWN + || cell.getType() == LightUpCellType.EMPTY) + && !cell.isLite()) { return false; } } @@ -84,7 +84,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java index b15f49919..217ef79a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.util.HashSet; import java.util.Set; @@ -17,7 +16,8 @@ public LightUpBoard(int size) { } /** - * Sets cells in board to lite depending on whether there is a bulb cell in the current row or column + * Sets cells in board to lite depending on whether there is a bulb cell in the current row or + * column */ public void fillWithLight() { for (int y = 0; y < this.dimension.height; y++) { @@ -33,28 +33,32 @@ public void fillWithLight() { cell.setLite(true); for (int i = x + 1; i < this.dimension.width; i++) { LightUpCell c = getCell(i, y); - if (c.getType() == LightUpCellType.NUMBER || c.getType() == LightUpCellType.BLACK) { + if (c.getType() == LightUpCellType.NUMBER + || c.getType() == LightUpCellType.BLACK) { break; } c.setLite(true); } for (int i = x - 1; i >= 0; i--) { LightUpCell c = getCell(i, y); - if (c.getType() == LightUpCellType.NUMBER || c.getType() == LightUpCellType.BLACK) { + if (c.getType() == LightUpCellType.NUMBER + || c.getType() == LightUpCellType.BLACK) { break; } c.setLite(true); } for (int i = y + 1; i < this.dimension.height; i++) { LightUpCell c = getCell(x, i); - if (c.getType() == LightUpCellType.NUMBER || c.getType() == LightUpCellType.BLACK) { + if (c.getType() == LightUpCellType.NUMBER + || c.getType() == LightUpCellType.BLACK) { break; } c.setLite(true); } for (int i = y - 1; i >= 0; i--) { LightUpCell c = getCell(x, i); - if (c.getType() == LightUpCellType.NUMBER || c.getType() == LightUpCellType.BLACK) { + if (c.getType() == LightUpCellType.NUMBER + || c.getType() == LightUpCellType.BLACK) { break; } c.setLite(true); 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 36e5a5088..8adf84cb4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; import java.awt.event.MouseEvent; @@ -27,20 +26,18 @@ public void setType(Element e, MouseEvent m) { this.data = -2; break; case "LTUP-UNPL-0001": - switch (m.getButton()){ + switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data < 0 || this.data > 3) { this.data = 0; - } - else { + } else { this.data = this.data + 1; } break; case MouseEvent.BUTTON3: if (this.data > 0) { this.data = this.data - 1; - } - else { + } else { this.data = 4; } break; diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellController.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellController.java index 9c94b24f7..d4049897d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellController.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class LightUpCellController extends ElementController { @@ -11,32 +10,31 @@ public void changeCell(MouseEvent e, PuzzleElement data) { LightUpCell cell = (LightUpCell) data; if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); - } - else { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { if (cell.getData() == -2) { data.setData(-4); - } - else { + } else { if (cell.getData() == -4) { data.setData(-3); - } - else { + } else { data.setData(-2); } } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() == -4) { data.setData(-2); - } - else { + } else { if (cell.getData() == -2) { data.setData(-3); - } - else { + } else { data.setData(-4); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellFactory.java index 4914facfa..384aa2b74 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class LightUpCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -23,7 +22,8 @@ public class LightUpCellFactory extends ElementFactory { public LightUpCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("lightup Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "lightup Factory: unknown puzzleElement puzzleElement"); } LightUpBoard lightUpBoard = (LightUpBoard) board; @@ -35,7 +35,8 @@ public LightUpCell importCell(Node node, Board board) throws InvalidFileFormatEx int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); if (x >= width || y >= height) { - throw new InvalidFileFormatException("lightup Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "lightup Factory: cell location out of bounds"); } if (value < -4 || value > 4) { throw new InvalidFileFormatException("lightup Factory: cell unknown value"); @@ -44,11 +45,10 @@ public LightUpCell importCell(Node node, Board board) throws InvalidFileFormatEx LightUpCell cell = new LightUpCell(value, new Point(x, y)); cell.setIndex(y * height + x); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("lightup Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "lightup Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("lightup Factory: could not find attribute(s)"); } } @@ -56,7 +56,7 @@ public LightUpCell importCell(Node node, Board board) throws InvalidFileFormatEx /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellType.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellType.java index b0825910d..8472be7a6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCellType.java @@ -1,7 +1,11 @@ package edu.rpi.legup.puzzle.lightup; public enum LightUpCellType { - BULB(-4), EMPTY(-3), UNKNOWN(-2), BLACK(-1), NUMBER(0); + BULB(-4), + EMPTY(-3), + UNKNOWN(-2), + BLACK(-1), + NUMBER(0); public int value; diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpElementView.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpElementView.java index 7f35d46a6..1b00b007d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.lightup; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class LightUpElementView extends GridElementView { @@ -40,37 +39,45 @@ public void drawElement(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(puzzleElement.getData()); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); - } - else { + } else { if (type == LightUpCellType.BLACK) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(BLACK_COLOR); graphics2D.fillRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == LightUpCellType.EMPTY) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(cell.isLite() ? LITE : WHITE_COLOR); graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.setColor(BLACK_COLOR); - graphics2D.fillRect(location.x + size.width * 7 / 16, location.y + size.height * 7 / 16, size.width / 8, size.height / 8); + graphics2D.fillRect( + location.x + size.width * 7 / 16, + location.y + size.height * 7 / 16, + size.width / 8, + size.height / 8); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == LightUpCellType.UNKNOWN) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(cell.isLite() ? LITE : Color.LIGHT_GRAY); graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == LightUpCellType.BULB) { graphics2D.setColor(Color.LIGHT_GRAY); graphics2D.fillRect(location.x, location.y, size.width, size.height); - graphics2D.drawImage(LightUpView.lightImage, location.x, location.y, size.width, size.height, LITE, null); + graphics2D.drawImage( + LightUpView.lightImage, + location.x, + location.y, + size.width, + size.height, + LITE, + null); graphics2D.setColor(BLACK_COLOR); graphics2D.drawRect(location.x, location.y, size.width, size.height); } 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 89024ad6c..0e8987020 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpExporter.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 LightUpExporter extends PuzzleExporter { @@ -16,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { LightUpBoard board; if (puzzle.getTree() != null) { board = (LightUpBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (LightUpBoard) puzzle.getBoardView().getBoard(); } @@ -29,7 +27,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { LightUpCell cell = (LightUpCell) puzzleElement; if (cell.getData() != -2) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } 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 7ef24ca69..336b063f6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class LightUpImporter extends PuzzleImporter { public LightUpImporter(LightUp lightUp) { super(lightUp); @@ -26,7 +25,7 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @@ -57,11 +56,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("lightup Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "lightup Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("lightup Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "lightup Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -70,9 +71,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); lightUpBoard = new LightUpBoard(size); - } - else { - if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); lightUpBoard = new LightUpBoard(width, height); @@ -87,7 +88,10 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = lightUpBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - LightUpCell cell = (LightUpCell) puzzle.getFactory().importCell(elementDataList.item(i), lightUpBoard); + LightUpCell cell = + (LightUpCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), lightUpBoard); Point loc = cell.getLocation(); if (cell.getData() != -2) { cell.setModifiable(false); @@ -107,9 +111,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } puzzle.setCurrentBoard(lightUpBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("lightup Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "lightup Importer: unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpView.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpView.java index 0fd57e705..ebce6a682 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpView.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpView.java @@ -7,23 +7,24 @@ import edu.rpi.legup.ui.boardview.DataSelectionView; import edu.rpi.legup.ui.boardview.GridBoardView; import edu.rpi.legup.ui.boardview.SelectionItemView; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.imageio.ImageIO; -import javax.swing.*; import java.awt.*; import java.io.IOException; +import javax.imageio.ImageIO; +import javax.swing.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class LightUpView extends GridBoardView { - private final static Logger LOGGER = LogManager.getLogger(LightUpView.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(LightUpView.class.getName()); static Image lightImage; static { try { - lightImage = ImageIO.read(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/lightup/light.png")); - } - catch (IOException e) { + lightImage = + ImageIO.read( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/lightup/light.png")); + } catch (IOException e) { LOGGER.error("Failed to open TreeTent images"); } } @@ -37,7 +38,8 @@ public LightUpView(LightUpBoard board) { LightUpElementView elementView = new LightUpElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } } @@ -50,14 +52,15 @@ public LightUpView(LightUpBoard board) { @Override public void onTreeElementChanged(TreeElement treeElement) { super.onTreeElementChanged(treeElement); - LightUpBoard lightUpBoard = board instanceof CaseBoard ? (LightUpBoard) ((CaseBoard) board).getBaseBoard() : (LightUpBoard) board; + LightUpBoard lightUpBoard = + board instanceof CaseBoard + ? (LightUpBoard) ((CaseBoard) board).getBaseBoard() + : (LightUpBoard) board; lightUpBoard.fillWithLight(); repaint(); } - /** - * Returns a DataSelectionView popup menu - */ + /** Returns a DataSelectionView popup menu */ public DataSelectionView getSelectionPopupMenu() { DataSelectionView selectionView = new DataSelectionView(elementController); GridLayout layout = new GridLayout(3, 1); @@ -69,7 +72,9 @@ public DataSelectionView getSelectionPopupMenu() { LightUpElementView element1 = new LightUpElementView(new LightUpCell(-2, null)); element1.setSize(iconSize); element1.setLocation(loc); - SelectionItemView item1 = new SelectionItemView(element1.getPuzzleElement(), new ImageIcon(element1.getImage())); + SelectionItemView item1 = + new SelectionItemView( + element1.getPuzzleElement(), new ImageIcon(element1.getImage())); item1.addActionListener(elementController); item1.setHorizontalTextPosition(SwingConstants.CENTER); selectionView.add(item1); @@ -77,7 +82,9 @@ public DataSelectionView getSelectionPopupMenu() { LightUpElementView element2 = new LightUpElementView(new LightUpCell(-4, null)); element2.setSize(iconSize); element2.setLocation(loc); - SelectionItemView item2 = new SelectionItemView(element2.getPuzzleElement(), new ImageIcon(element2.getImage())); + SelectionItemView item2 = + new SelectionItemView( + element2.getPuzzleElement(), new ImageIcon(element2.getImage())); item2.addActionListener(elementController); item2.setHorizontalTextPosition(SwingConstants.CENTER); selectionView.add(item2); @@ -85,7 +92,9 @@ public DataSelectionView getSelectionPopupMenu() { LightUpElementView element3 = new LightUpElementView(new LightUpCell(-3, null)); element3.setSize(iconSize); element3.setLocation(loc); - SelectionItemView item3 = new SelectionItemView(element3.getPuzzleElement(), new ImageIcon(element3.getImage())); + SelectionItemView item3 = + new SelectionItemView( + element3.getPuzzleElement(), new ImageIcon(element3.getImage())); item3.addActionListener(elementController); item3.setHorizontalTextPosition(SwingConstants.CENTER); selectionView.add(item3); 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 d3e8cf506..2ddb4f754 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 @@ -4,6 +4,10 @@ public class BlackTile extends NonPlaceableElement { public BlackTile() { - super("LTUP-UNPL-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"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java index 5fc4a334f..d238baa56 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java @@ -4,6 +4,10 @@ public class BulbTile extends PlaceableElement { public BulbTile() { - super("LTUP-PLAC-0001", "Bulb Tile", "The bulb tile", "edu/rpi/legup/images/lightup/light.png"); + super( + "LTUP-PLAC-0001", + "Bulb Tile", + "The bulb tile", + "edu/rpi/legup/images/lightup/light.png"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java index e96a969e5..ae314a4cf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java @@ -8,11 +8,19 @@ public class NumberTile extends NonPlaceableElement { // Follow the default format and resolves the NoSuchMethod error public NumberTile() { - super("LTUP-UNPL-0001", "Number Tile", "The number tile", "edu/rpi/legup/images/lightup/1.gif"); + super( + "LTUP-UNPL-0001", + "Number Tile", + "The number tile", + "edu/rpi/legup/images/lightup/1.gif"); } public NumberTile(int num) { - super("LTUP-UNPL-0001", "Number Tile", "The number tile", "edu/rpi/legup/images/lightup/" + num + ".gif"); + super( + "LTUP-UNPL-0001", + "Number Tile", + "The number tile", + "edu/rpi/legup/images/lightup/" + num + ".gif"); if (num > 3 || num < 1) num = 1; object_number = num; } 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 6839e70de..24d420fe8 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,10 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("LTUP-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"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/BulbsInPathContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/BulbsInPathContradictionRule.java index 2804e6eab..90652888c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/BulbsInPathContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/BulbsInPathContradictionRule.java @@ -6,24 +6,26 @@ import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; - import java.awt.*; public class BulbsInPathContradictionRule extends ContradictionRule { public BulbsInPathContradictionRule() { - super("LTUP-CONT-0001", "Bulbs In Path", + super( + "LTUP-CONT-0001", + "Bulbs In Path", "A bulb cannot be placed in another bulb's path.", "edu/rpi/legup/images/lightup/contradictions/BulbsInPath.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -38,8 +40,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(i, location.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.BULB) { return null; } @@ -49,8 +50,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(i, location.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.BULB) { return null; } @@ -60,8 +60,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(location.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.BULB) { return null; } @@ -71,8 +70,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(location.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.BULB) { return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/CannotLightACellContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/CannotLightACellContradictionRule.java index 011bf1c0a..0ed88636c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/CannotLightACellContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/CannotLightACellContradictionRule.java @@ -6,24 +6,26 @@ import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; - import java.awt.*; public class CannotLightACellContradictionRule extends ContradictionRule { public CannotLightACellContradictionRule() { - super("LTUP-CONT-0002", "Cannot Light A Cell", + super( + "LTUP-CONT-0002", + "Cannot Light A Cell", "All cells must be able to be lit.", "edu/rpi/legup/images/lightup/contradictions/CannotLightACell.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -42,8 +44,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(i, location.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { hor_count += 1; } @@ -53,8 +54,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(i, location.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { hor_count += 1; } @@ -64,8 +64,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(location.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { ver_count += 1; } @@ -75,8 +74,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { LightUpCell c = lightUpBoard.getCell(location.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else { + } else { if (c.getType() == LightUpCellType.UNKNOWN && !c.isLite()) { ver_count += 1; } @@ -88,4 +86,4 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } return super.getNoContradictionMessage(); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCellinLightDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCellinLightDirectRule.java index a40ede284..269ef0ad5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCellinLightDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCellinLightDirectRule.java @@ -1,64 +1,68 @@ -package edu.rpi.legup.puzzle.lightup.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.lightup.LightUpCellType; - -public class EmptyCellinLightDirectRule extends DirectRule { - - public EmptyCellinLightDirectRule() { - super("LTUP-BASC-0002", "Empty Cells in Light", - "Cells in light must be empty.", - "edu/rpi/legup/images/lightup/rules/EmptyCellInLight.png"); - } - - /** - * 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 index of the 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) { - LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); - initialBoard.fillWithLight(); - LightUpCell initCell = (LightUpCell) initialBoard.getPuzzleElement(puzzleElement); - LightUpCell finalCell = (LightUpCell) transition.getBoard().getPuzzleElement(puzzleElement); - if (finalCell.getType() == LightUpCellType.EMPTY && initCell.getType() == LightUpCellType.UNKNOWN && initCell.isLite()) { - return null; - } - return super.getInvalidUseOfRuleMessage() + ": Cell is not forced to be empty"; - } - - /** - * 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) { - LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - LightUpCell cell = (LightUpCell) element; - if (cell.getType() == LightUpCellType.UNKNOWN && cell.isLite()) { - cell.setData(LightUpCellType.EMPTY.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.lightup.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.lightup.LightUpCellType; + +public class EmptyCellinLightDirectRule extends DirectRule { + + public EmptyCellinLightDirectRule() { + super( + "LTUP-BASC-0002", + "Empty Cells in Light", + "Cells in light must be empty.", + "edu/rpi/legup/images/lightup/rules/EmptyCellInLight.png"); + } + + /** + * 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 index of the 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) { + LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); + initialBoard.fillWithLight(); + LightUpCell initCell = (LightUpCell) initialBoard.getPuzzleElement(puzzleElement); + LightUpCell finalCell = (LightUpCell) transition.getBoard().getPuzzleElement(puzzleElement); + if (finalCell.getType() == LightUpCellType.EMPTY + && initCell.getType() == LightUpCellType.UNKNOWN + && initCell.isLite()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Cell is not forced to be empty"; + } + + /** + * 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) { + LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + LightUpCell cell = (LightUpCell) element; + if (cell.getType() == LightUpCellType.UNKNOWN && cell.isLite()) { + cell.setData(LightUpCellType.EMPTY.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCornersDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCornersDirectRule.java index 04c493f08..702c116c1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCornersDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/EmptyCornersDirectRule.java @@ -1,115 +1,117 @@ -package edu.rpi.legup.puzzle.lightup.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.lightup.LightUpCellType; - -import java.awt.*; -import java.util.ArrayList; -import java.util.List; - -public class EmptyCornersDirectRule extends DirectRule { - - public EmptyCornersDirectRule() { - super("LTUP-BASC-0003", "Empty Corners", - "Cells on the corners of a number must be empty if placing bulbs would prevent the number from being satisfied.", - "edu/rpi/legup/images/lightup/rules/EmptyCorners.png"); - } - - /** - * 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 index of the 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) { - LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); - LightUpCell cell = (LightUpCell) initialBoard.getPuzzleElement(puzzleElement); - LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); - LightUpCell finalCell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); - - if (!(cell.getType() == LightUpCellType.UNKNOWN && finalCell.getType() == LightUpCellType.EMPTY)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be an empty cell"; - } - - Point loc = finalCell.getLocation(); - List numberedCells = new ArrayList<>(); - LightUpCell upperRight = finalBoard.getCell(loc.x + 1, loc.y - 1); - if (upperRight != null && upperRight.getType() == LightUpCellType.NUMBER) { - numberedCells.add(upperRight); - } - LightUpCell upperLeft = finalBoard.getCell(loc.x - 1, loc.y - 1); - if (upperLeft != null && upperLeft.getType() == LightUpCellType.NUMBER) { - numberedCells.add(upperLeft); - } - LightUpCell lowerRight = finalBoard.getCell(loc.x + 1, loc.y + 1); - if (lowerRight != null && lowerRight.getType() == LightUpCellType.NUMBER) { - numberedCells.add(lowerRight); - } - LightUpCell lowerLeft = finalBoard.getCell(loc.x - 1, loc.y + 1); - if (lowerLeft != null && lowerLeft.getType() == LightUpCellType.NUMBER) { - numberedCells.add(lowerLeft); - } - if (numberedCells.isEmpty()) { - return super.getInvalidUseOfRuleMessage() + ": This cell must diagonal to a numbered cell"; - } - - TooFewBulbsContradictionRule tooFew = new TooFewBulbsContradictionRule(); - LightUpBoard bulbCaseBoard = finalBoard.copy(); - LightUpCell bulbCaseCell = (LightUpCell) bulbCaseBoard.getPuzzleElement(puzzleElement); - bulbCaseCell.setData(LightUpCellType.BULB.value); - bulbCaseBoard.fillWithLight(); - - boolean createsContra = false; - for (LightUpCell c : numberedCells) { - createsContra |= tooFew.checkContradictionAt(bulbCaseBoard, c) == null; - } - if (createsContra) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be empty"; - } - } - - /** - * 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) { - LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); - LightUpBoard lightUpBoardCopy = (LightUpBoard) node.getBoard().copy(); - TreeTransition transition = new TreeTransition(node, lightUpBoardCopy); - for (PuzzleElement element : lightUpBoardCopy.getPuzzleElements()) { - LightUpCell cell = (LightUpCell) element; - int temp = cell.getData(); - cell.setData(LightUpCellType.EMPTY.value); - if (checkRuleRawAt(transition, cell) == null) { - LightUpCell modCell = (LightUpCell) lightUpBoard.getPuzzleElement(cell); - modCell.setData(LightUpCellType.EMPTY.value); - lightUpBoard.addModifiedData(modCell); - } - else { - cell.setData(temp); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.lightup.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +public class EmptyCornersDirectRule extends DirectRule { + + public EmptyCornersDirectRule() { + super( + "LTUP-BASC-0003", + "Empty Corners", + "Cells on the corners of a number must be empty if placing bulbs would prevent the" + + " number from being satisfied.", + "edu/rpi/legup/images/lightup/rules/EmptyCorners.png"); + } + + /** + * 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 index of the 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) { + LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); + LightUpCell cell = (LightUpCell) initialBoard.getPuzzleElement(puzzleElement); + LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); + LightUpCell finalCell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); + + if (!(cell.getType() == LightUpCellType.UNKNOWN + && finalCell.getType() == LightUpCellType.EMPTY)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be an empty cell"; + } + + Point loc = finalCell.getLocation(); + List numberedCells = new ArrayList<>(); + LightUpCell upperRight = finalBoard.getCell(loc.x + 1, loc.y - 1); + if (upperRight != null && upperRight.getType() == LightUpCellType.NUMBER) { + numberedCells.add(upperRight); + } + LightUpCell upperLeft = finalBoard.getCell(loc.x - 1, loc.y - 1); + if (upperLeft != null && upperLeft.getType() == LightUpCellType.NUMBER) { + numberedCells.add(upperLeft); + } + LightUpCell lowerRight = finalBoard.getCell(loc.x + 1, loc.y + 1); + if (lowerRight != null && lowerRight.getType() == LightUpCellType.NUMBER) { + numberedCells.add(lowerRight); + } + LightUpCell lowerLeft = finalBoard.getCell(loc.x - 1, loc.y + 1); + if (lowerLeft != null && lowerLeft.getType() == LightUpCellType.NUMBER) { + numberedCells.add(lowerLeft); + } + if (numberedCells.isEmpty()) { + return super.getInvalidUseOfRuleMessage() + + ": This cell must diagonal to a numbered cell"; + } + + TooFewBulbsContradictionRule tooFew = new TooFewBulbsContradictionRule(); + LightUpBoard bulbCaseBoard = finalBoard.copy(); + LightUpCell bulbCaseCell = (LightUpCell) bulbCaseBoard.getPuzzleElement(puzzleElement); + bulbCaseCell.setData(LightUpCellType.BULB.value); + bulbCaseBoard.fillWithLight(); + + boolean createsContra = false; + for (LightUpCell c : numberedCells) { + createsContra |= tooFew.checkContradictionAt(bulbCaseBoard, c) == null; + } + if (createsContra) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be empty"; + } + } + + /** + * 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) { + LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); + LightUpBoard lightUpBoardCopy = (LightUpBoard) node.getBoard().copy(); + TreeTransition transition = new TreeTransition(node, lightUpBoardCopy); + for (PuzzleElement element : lightUpBoardCopy.getPuzzleElements()) { + LightUpCell cell = (LightUpCell) element; + int temp = cell.getData(); + cell.setData(LightUpCellType.EMPTY.value); + if (checkRuleRawAt(transition, cell) == null) { + LightUpCell modCell = (LightUpCell) lightUpBoard.getPuzzleElement(cell); + modCell.setData(LightUpCellType.EMPTY.value); + lightUpBoard.addModifiedData(modCell); + } else { + cell.setData(temp); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithBulbsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithBulbsDirectRule.java index cdea7880f..3f884d459 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithBulbsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithBulbsDirectRule.java @@ -1,107 +1,110 @@ -package edu.rpi.legup.puzzle.lightup.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.lightup.LightUpCellType; - -import java.util.Set; - -public class FinishWithBulbsDirectRule extends DirectRule { - - public FinishWithBulbsDirectRule() { - super("LTUP-BASC-0004", "Finish with Bulbs", - "The remaining unknowns around a block must be bulbs to satisfy the number.", - "edu/rpi/legup/images/lightup/rules/FinishWithBulbs.png"); - } - - /** - * 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 index of the 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) { - LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); - LightUpCell initCell = (LightUpCell) initialBoard.getPuzzleElement(puzzleElement); - LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); - LightUpCell finalCell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(initCell.getType() == LightUpCellType.UNKNOWN && finalCell.getType() == LightUpCellType.BULB)) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must be bulbs"; - } - - Set adjCells = finalBoard.getAdj(finalCell); - adjCells.removeIf(cell -> cell.getType() != LightUpCellType.NUMBER); - if (adjCells.isEmpty()) { - return super.getInvalidUseOfRuleMessage() + ": This cell is not adjacent to a numbered cell"; - } - - LightUpBoard emptyCase = initialBoard.copy(); - emptyCase.getPuzzleElement(finalCell).setData(LightUpCellType.EMPTY.value); - TooFewBulbsContradictionRule tooFew = new TooFewBulbsContradictionRule(); - for (LightUpCell c : adjCells) { - if (tooFew.checkContradictionAt(emptyCase, c) == null) { - return null; - } - } - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be a bulb"; - } - - /** - * Determines whether the specified cell is forced to be a bulb or not - * - * @param board the entire board - * @param cell specified cell - * @return whether cell is forced to be a bulb or not - */ - private boolean isForced(LightUpBoard board, LightUpCell cell) { - Set adjCells = board.getAdj(cell); - adjCells.removeIf(c -> c.getType() != LightUpCellType.NUMBER); - if (adjCells.isEmpty()) { - return false; - } - - LightUpBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(LightUpCellType.EMPTY.value); - TooFewBulbsContradictionRule tooFew = new TooFewBulbsContradictionRule(); - for (LightUpCell c : adjCells) { - if (tooFew.checkContradictionAt(emptyCase, c) == null) { - return true; - } - } - return false; - } - - /** - * 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) { - LightUpBoard initialBoard = (LightUpBoard) node.getBoard(); - LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - LightUpCell cell = (LightUpCell) element; - if (cell.getType() == LightUpCellType.UNKNOWN && isForced(initialBoard, cell)) { - cell.setData(LightUpCellType.BULB.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.lightup.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import java.util.Set; + +public class FinishWithBulbsDirectRule extends DirectRule { + + public FinishWithBulbsDirectRule() { + super( + "LTUP-BASC-0004", + "Finish with Bulbs", + "The remaining unknowns around a block must be bulbs to satisfy the number.", + "edu/rpi/legup/images/lightup/rules/FinishWithBulbs.png"); + } + + /** + * 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 index of the 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) { + LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); + LightUpCell initCell = (LightUpCell) initialBoard.getPuzzleElement(puzzleElement); + LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); + LightUpCell finalCell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == LightUpCellType.UNKNOWN + && finalCell.getType() == LightUpCellType.BULB)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be bulbs"; + } + + Set adjCells = finalBoard.getAdj(finalCell); + adjCells.removeIf(cell -> cell.getType() != LightUpCellType.NUMBER); + if (adjCells.isEmpty()) { + return super.getInvalidUseOfRuleMessage() + + ": This cell is not adjacent to a numbered cell"; + } + + LightUpBoard emptyCase = initialBoard.copy(); + emptyCase.getPuzzleElement(finalCell).setData(LightUpCellType.EMPTY.value); + TooFewBulbsContradictionRule tooFew = new TooFewBulbsContradictionRule(); + for (LightUpCell c : adjCells) { + if (tooFew.checkContradictionAt(emptyCase, c) == null) { + return null; + } + } + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be a bulb"; + } + + /** + * Determines whether the specified cell is forced to be a bulb or not + * + * @param board the entire board + * @param cell specified cell + * @return whether cell is forced to be a bulb or not + */ + private boolean isForced(LightUpBoard board, LightUpCell cell) { + Set adjCells = board.getAdj(cell); + adjCells.removeIf(c -> c.getType() != LightUpCellType.NUMBER); + if (adjCells.isEmpty()) { + return false; + } + + LightUpBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(LightUpCellType.EMPTY.value); + TooFewBulbsContradictionRule tooFew = new TooFewBulbsContradictionRule(); + for (LightUpCell c : adjCells) { + if (tooFew.checkContradictionAt(emptyCase, c) == null) { + return true; + } + } + return false; + } + + /** + * 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) { + LightUpBoard initialBoard = (LightUpBoard) node.getBoard(); + LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + LightUpCell cell = (LightUpCell) element; + if (cell.getType() == LightUpCellType.UNKNOWN && isForced(initialBoard, cell)) { + cell.setData(LightUpCellType.BULB.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithEmptyDirectRule.java index f7433150c..678ee67a2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithEmptyDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/FinishWithEmptyDirectRule.java @@ -1,118 +1,122 @@ -package edu.rpi.legup.puzzle.lightup.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.lightup.LightUpCellType; - -import java.awt.*; - -public class FinishWithEmptyDirectRule extends DirectRule { - - public FinishWithEmptyDirectRule() { - super("LTUP-BASC-0005", "Finish with Empty", - "The remaining unknowns around a block must be empty if the number is satisfied.", - "edu/rpi/legup/images/lightup/rules/FinishWithEmpty.png"); - } - - /** - * 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 index of the 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) { - LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); - LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); - LightUpCell cell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); - if (cell.getType() != LightUpCellType.EMPTY) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must be empty"; - } - - if (isForced(initialBoard, cell.getLocation())) { - return null; - } - return super.getInvalidUseOfRuleMessage() + ": Empty is not forced"; - } - - /** - * Checks whether a certain cell is forced to not be a bulb - * - * @param board specified board - * @param location location of cell to check - * @return boolean value based on whether a certain cell has an adjacent cell that has the required amount of adjacent bulbs - */ - private boolean isForced(LightUpBoard board, Point location) { - return isForcedEmpty(board, new Point(location.x + 1, location.y)) || - isForcedEmpty(board, new Point(location.x, location.y + 1)) || - isForcedEmpty(board, new Point(location.x - 1, location.y)) || - isForcedEmpty(board, new Point(location.x, location.y - 1)); - } - - /** - * Checks whether a certain cell has the required amount of adjacent bulbs - * - * @param board specified board - * @param loc location of cell to check - * @return boolean value based on whether a certain cell has the required amount of adjacent bulbs - */ - private boolean isForcedEmpty(LightUpBoard board, Point loc) { - LightUpCell cell = board.getCell(loc.x, loc.y); - if (cell == null || cell.getType() != LightUpCellType.NUMBER) { - return false; - } - - int bulbs = 0; - int bulbsNeeded = cell.getData(); - cell = board.getCell(loc.x + 1, loc.y); - if (cell != null && cell.getType() == LightUpCellType.BULB) { - bulbs++; - } - cell = board.getCell(loc.x, loc.y + 1); - if (cell != null && cell.getType() == LightUpCellType.BULB) { - bulbs++; - } - cell = board.getCell(loc.x - 1, loc.y); - if (cell != null && cell.getType() == LightUpCellType.BULB) { - bulbs++; - } - cell = board.getCell(loc.x, loc.y - 1); - if (cell != null && cell.getType() == LightUpCellType.BULB) { - bulbs++; - } - return bulbs == bulbsNeeded; - } - - /** - * 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) { - LightUpBoard initialBoard = (LightUpBoard) node.getBoard(); - LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - LightUpCell cell = (LightUpCell) element; - if (cell.getType() == LightUpCellType.UNKNOWN && isForced(initialBoard, cell.getLocation())) { - cell.setData(LightUpCellType.EMPTY.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.lightup.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import java.awt.*; + +public class FinishWithEmptyDirectRule extends DirectRule { + + public FinishWithEmptyDirectRule() { + super( + "LTUP-BASC-0005", + "Finish with Empty", + "The remaining unknowns around a block must be empty if the number is satisfied.", + "edu/rpi/legup/images/lightup/rules/FinishWithEmpty.png"); + } + + /** + * 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 index of the 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) { + LightUpBoard initialBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); + LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); + LightUpCell cell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); + if (cell.getType() != LightUpCellType.EMPTY) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be empty"; + } + + if (isForced(initialBoard, cell.getLocation())) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Empty is not forced"; + } + + /** + * Checks whether a certain cell is forced to not be a bulb + * + * @param board specified board + * @param location location of cell to check + * @return boolean value based on whether a certain cell has an adjacent cell that has the + * required amount of adjacent bulbs + */ + private boolean isForced(LightUpBoard board, Point location) { + return isForcedEmpty(board, new Point(location.x + 1, location.y)) + || isForcedEmpty(board, new Point(location.x, location.y + 1)) + || isForcedEmpty(board, new Point(location.x - 1, location.y)) + || isForcedEmpty(board, new Point(location.x, location.y - 1)); + } + + /** + * Checks whether a certain cell has the required amount of adjacent bulbs + * + * @param board specified board + * @param loc location of cell to check + * @return boolean value based on whether a certain cell has the required amount of adjacent + * bulbs + */ + private boolean isForcedEmpty(LightUpBoard board, Point loc) { + LightUpCell cell = board.getCell(loc.x, loc.y); + if (cell == null || cell.getType() != LightUpCellType.NUMBER) { + return false; + } + + int bulbs = 0; + int bulbsNeeded = cell.getData(); + cell = board.getCell(loc.x + 1, loc.y); + if (cell != null && cell.getType() == LightUpCellType.BULB) { + bulbs++; + } + cell = board.getCell(loc.x, loc.y + 1); + if (cell != null && cell.getType() == LightUpCellType.BULB) { + bulbs++; + } + cell = board.getCell(loc.x - 1, loc.y); + if (cell != null && cell.getType() == LightUpCellType.BULB) { + bulbs++; + } + cell = board.getCell(loc.x, loc.y - 1); + if (cell != null && cell.getType() == LightUpCellType.BULB) { + bulbs++; + } + return bulbs == bulbsNeeded; + } + + /** + * 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) { + LightUpBoard initialBoard = (LightUpBoard) node.getBoard(); + LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + LightUpCell cell = (LightUpCell) element; + if (cell.getType() == LightUpCellType.UNKNOWN + && isForced(initialBoard, cell.getLocation())) { + cell.setData(LightUpCellType.EMPTY.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java index 410c30e9c..4ba754731 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java @@ -8,14 +8,15 @@ import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; - import java.util.ArrayList; import java.util.List; public class LightOrEmptyCaseRule extends CaseRule { public LightOrEmptyCaseRule() { - super("LTUP-CASE-0001", "Light or Empty", + super( + "LTUP-CASE-0001", + "Light or Empty", "Each blank cell is either a light or empty.", "edu/rpi/legup/images/lightup/cases/LightOrEmpty.png"); } @@ -36,7 +37,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement puzzleElement to determine the possible cases for * @return a list of elements the specified could be */ @@ -73,33 +74,37 @@ public String checkRuleRaw(TreeTransition transition) { TreeTransition case1 = childTransitions.get(0); TreeTransition case2 = childTransitions.get(1); - if (case1.getBoard().getModifiedData().size() != 1 || - case2.getBoard().getModifiedData().size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case"; + if (case1.getBoard().getModifiedData().size() != 1 + || case2.getBoard().getModifiedData().size() != 1) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case"; } LightUpCell mod1 = (LightUpCell) case1.getBoard().getModifiedData().iterator().next(); LightUpCell mod2 = (LightUpCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case"; } - if (!((mod1.getType() == LightUpCellType.EMPTY && mod2.getType() == LightUpCellType.BULB) || - (mod2.getType() == LightUpCellType.EMPTY && mod1.getType() == LightUpCellType.BULB))) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must an empty cell and a bulb cell"; + if (!((mod1.getType() == LightUpCellType.EMPTY && mod2.getType() == LightUpCellType.BULB) + || (mod2.getType() == LightUpCellType.EMPTY + && mod1.getType() == LightUpCellType.BULB))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must an empty cell and a bulb cell"; } return null; } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 transition transition to check * @param puzzleElement index of the puzzleElement - * @return null if the child node logically follow from the parent node at the specified puzzleElement, - * otherwise error message + * @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) { diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightDirectRule.java index f0f943401..bf1843728 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/MustLightDirectRule.java @@ -1,149 +1,153 @@ -package edu.rpi.legup.puzzle.lightup.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.lightup.LightUpCellType; - -import java.awt.*; - -public class MustLightDirectRule extends DirectRule { - - public MustLightDirectRule() { - super("LTUP-BASC-0006", "Must Light", - "A cell must be a bulb if it is the only cell to be able to light another.", - "edu/rpi/legup/images/lightup/rules/MustLight.png"); - } - - /** - * 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 index of the 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) { - LightUpBoard parentBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); - LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); - LightUpCell parentCell = (LightUpCell) parentBoard.getPuzzleElement(puzzleElement); - LightUpCell finalCell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(parentCell.getType() == LightUpCellType.UNKNOWN && !parentCell.isLite() && finalCell.getType() == LightUpCellType.BULB)) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must be bulbs"; - } - - finalBoard.fillWithLight(); - boolean isForced = isForcedBulb(parentBoard, parentCell.getLocation()); - finalCell.setData(LightUpCellType.BULB.value); - finalBoard.fillWithLight(); - - if (isForced) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell can be lit by another cell"; - } - } - - private boolean isForcedBulb(LightUpBoard board, Point loc) { - CannotLightACellContradictionRule cannotLite = new CannotLightACellContradictionRule(); - LightUpBoard modifiedBoard = board.copy(); - LightUpCell modifiedCell = modifiedBoard.getCell(loc.x, loc.y); - modifiedCell.setData(LightUpCellType.EMPTY.value); - //Check if this cell itself (the one with the bulb) has no other lighting option - if ((modifiedCell.getType() == LightUpCellType.EMPTY || modifiedCell.getType() == LightUpCellType.UNKNOWN) && - !modifiedCell.isLite() && cannotLite.checkContradictionAt(modifiedBoard, modifiedCell) == null) { - return true; - } - //Look right - for (int i = loc.x + 1; i < modifiedBoard.getWidth(); i++) { - LightUpCell c = modifiedBoard.getCell(i, loc.y); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { - return true; - } - } - } - //Look left - for (int i = loc.x - 1; i >= 0; i--) { - LightUpCell c = modifiedBoard.getCell(i, loc.y); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { - return true; - } - } - } - //Look down - for (int i = loc.y + 1; i < modifiedBoard.getHeight(); i++) { - LightUpCell c = modifiedBoard.getCell(loc.x, i); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { - return true; - } - } - } - //Look up - for (int i = loc.y - 1; i >= 0; i--) { - LightUpCell c = modifiedBoard.getCell(loc.x, i); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else { - if (c.getType() == LightUpCellType.EMPTY && - !c.isLite() && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { - return true; - } - } - } - return false; - } - - /** - * 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) { - LightUpBoard initialBoard = (LightUpBoard) node.getBoard(); - LightUpBoard tempBoard = (LightUpBoard) node.getBoard().copy(); - LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); - for (PuzzleElement element : tempBoard.getPuzzleElements()) { - LightUpCell cell = (LightUpCell) element; - if (cell.getType() == LightUpCellType.UNKNOWN && !cell.isLite()) { - cell.setData(LightUpCellType.EMPTY.value); - if (isForcedBulb(initialBoard, cell.getLocation())) { - LightUpCell modCell = (LightUpCell) lightUpBoard.getPuzzleElement(cell); - modCell.setData(LightUpCellType.BULB.value); - lightUpBoard.addModifiedData(modCell); - } - cell.setData(LightUpCellType.UNKNOWN.value); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.lightup.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import java.awt.*; + +public class MustLightDirectRule extends DirectRule { + + public MustLightDirectRule() { + super( + "LTUP-BASC-0006", + "Must Light", + "A cell must be a bulb if it is the only cell to be able to light another.", + "edu/rpi/legup/images/lightup/rules/MustLight.png"); + } + + /** + * 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 index of the 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) { + LightUpBoard parentBoard = (LightUpBoard) transition.getParents().get(0).getBoard(); + LightUpBoard finalBoard = (LightUpBoard) transition.getBoard(); + LightUpCell parentCell = (LightUpCell) parentBoard.getPuzzleElement(puzzleElement); + LightUpCell finalCell = (LightUpCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(parentCell.getType() == LightUpCellType.UNKNOWN + && !parentCell.isLite() + && finalCell.getType() == LightUpCellType.BULB)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be bulbs"; + } + + finalBoard.fillWithLight(); + boolean isForced = isForcedBulb(parentBoard, parentCell.getLocation()); + finalCell.setData(LightUpCellType.BULB.value); + finalBoard.fillWithLight(); + + if (isForced) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell can be lit by another cell"; + } + } + + private boolean isForcedBulb(LightUpBoard board, Point loc) { + CannotLightACellContradictionRule cannotLite = new CannotLightACellContradictionRule(); + LightUpBoard modifiedBoard = board.copy(); + LightUpCell modifiedCell = modifiedBoard.getCell(loc.x, loc.y); + modifiedCell.setData(LightUpCellType.EMPTY.value); + // Check if this cell itself (the one with the bulb) has no other lighting option + if ((modifiedCell.getType() == LightUpCellType.EMPTY + || modifiedCell.getType() == LightUpCellType.UNKNOWN) + && !modifiedCell.isLite() + && cannotLite.checkContradictionAt(modifiedBoard, modifiedCell) == null) { + return true; + } + // Look right + for (int i = loc.x + 1; i < modifiedBoard.getWidth(); i++) { + LightUpCell c = modifiedBoard.getCell(i, loc.y); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } else { + if (c.getType() == LightUpCellType.EMPTY + && !c.isLite() + && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { + return true; + } + } + } + // Look left + for (int i = loc.x - 1; i >= 0; i--) { + LightUpCell c = modifiedBoard.getCell(i, loc.y); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } else { + if (c.getType() == LightUpCellType.EMPTY + && !c.isLite() + && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { + return true; + } + } + } + // Look down + for (int i = loc.y + 1; i < modifiedBoard.getHeight(); i++) { + LightUpCell c = modifiedBoard.getCell(loc.x, i); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } else { + if (c.getType() == LightUpCellType.EMPTY + && !c.isLite() + && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { + return true; + } + } + } + // Look up + for (int i = loc.y - 1; i >= 0; i--) { + LightUpCell c = modifiedBoard.getCell(loc.x, i); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } else { + if (c.getType() == LightUpCellType.EMPTY + && !c.isLite() + && cannotLite.checkContradictionAt(modifiedBoard, c) == null) { + return true; + } + } + } + return false; + } + + /** + * 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) { + LightUpBoard initialBoard = (LightUpBoard) node.getBoard(); + LightUpBoard tempBoard = (LightUpBoard) node.getBoard().copy(); + LightUpBoard lightUpBoard = (LightUpBoard) node.getBoard().copy(); + for (PuzzleElement element : tempBoard.getPuzzleElements()) { + LightUpCell cell = (LightUpCell) element; + if (cell.getType() == LightUpCellType.UNKNOWN && !cell.isLite()) { + cell.setData(LightUpCellType.EMPTY.value); + if (isForcedBulb(initialBoard, cell.getLocation())) { + LightUpCell modCell = (LightUpCell) lightUpBoard.getPuzzleElement(cell); + modCell.setData(LightUpCellType.BULB.value); + lightUpBoard.addModifiedData(modCell); + } + cell.setData(LightUpCellType.UNKNOWN.value); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} 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 cf7b70ccd..ac656721d 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 @@ -9,7 +9,6 @@ import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; - import java.awt.*; import java.util.ArrayList; import java.util.Iterator; @@ -19,7 +18,9 @@ public class SatisfyNumberCaseRule extends CaseRule { public SatisfyNumberCaseRule() { - super("LTUP-CASE-0002", "Satisfy Number", + super( + "LTUP-CASE-0002", + "Satisfy Number", "The different ways a blocks number can be satisfied.", "edu/rpi/legup/images/lightup/cases/SatisfyNumber.png"); } @@ -40,7 +41,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement puzzleElement to determine the possible cases for * @return a list of elements the specified could be */ @@ -58,8 +59,7 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { if (checkCell != null) { if (checkCell.getType() == LightUpCellType.UNKNOWN && !checkCell.isLite()) { openSpots.add(checkCell); - } - else { + } else { if (checkCell.getType() == LightUpCellType.BULB) { numNeeded--; } @@ -69,8 +69,7 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { if (checkCell != null) { if (checkCell.getType() == LightUpCellType.UNKNOWN && !checkCell.isLite()) { openSpots.add(checkCell); - } - else { + } else { if (checkCell.getType() == LightUpCellType.BULB) { numNeeded--; } @@ -80,8 +79,7 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { if (checkCell != null) { if (checkCell.getType() == LightUpCellType.UNKNOWN && !checkCell.isLite()) { openSpots.add(checkCell); - } - else { + } else { if (checkCell.getType() == LightUpCellType.BULB) { numNeeded--; } @@ -91,8 +89,7 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { if (checkCell != null) { if (checkCell.getType() == LightUpCellType.UNKNOWN && !checkCell.isLite()) { openSpots.add(checkCell); - } - else { + } else { if (checkCell.getType() == LightUpCellType.BULB) { numNeeded--; } @@ -109,7 +106,11 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { return cases; } - private void generateCases(final LightUpBoard board, final int num, List openSpots, List cases) { + private void generateCases( + final LightUpBoard board, + final int num, + List openSpots, + List cases) { if (num > openSpots.size()) { return; } @@ -128,7 +129,13 @@ private void generateCases(final LightUpBoard board, final int num, List openSpots, List cases, LightUpBoard curBoard, int index) { + private void generateCases( + final LightUpBoard board, + final int num, + List openSpots, + List cases, + LightUpBoard curBoard, + int index) { if (num <= curBoard.getModifiedData().size()) { cases.add(curBoard); return; @@ -197,8 +204,9 @@ public String checkRuleRaw(TreeTransition transition) { boolean foundCell = false; for (PuzzleElement posEle : posCase.getModifiedData()) { LightUpCell posCell = (LightUpCell) posEle; - if (actCell.getType() == posCell.getType() && - actCell.getLocation().equals(posCell.getLocation())) { + if (actCell.getType() == posCell.getType() + && actCell.getLocation() + .equals(posCell.getLocation())) { foundCell = true; break; } @@ -228,13 +236,13 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 transition transition to check * @param puzzleElement index of the puzzleElement - * @return null if the child node logically follow from the parent node at the specified puzzleElement, - * otherwise error message + * @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) { @@ -245,7 +253,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem * Gets all cells in the TreeTransition board that are adjacent to all modified cells * * @param transition TreeTransition object - * @return list of cells that are adjacent to all modified cells, returns null if the number of modified cells is =0 || >4 + * @return list of cells that are adjacent to all modified cells, returns null if the number of + * modified cells is =0 || >4 */ private List getPossibleSpots(TreeTransition transition) { LightUpBoard board = (LightUpBoard) transition.getBoard(); @@ -254,8 +263,7 @@ private List getPossibleSpots(TreeTransition transition) { int size = modCells.size(); if (size == 0 || size > 4) { return null; - } - else { + } else { Iterator it = modCells.iterator(); List spots = getAdjacentCells(board, (LightUpCell) it.next()); @@ -289,33 +297,32 @@ private List getAdjacentCells(LightUpBoard board, LightUpCell cell) } /** - * 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 + * 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 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 + * @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); + LightUpCell point = (LightUpCell) puzzleBoard.getPuzzleElement(puzzleElement); - List cells = getAdjacentCells(puzzleBoard,point); + List cells = getAdjacentCells(puzzleBoard, point); for (LightUpCell cell : cells) { - //add cells that can light adjacents from any direction + // 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))) { + } else if (!elements.contains(board.getPuzzleElement(c))) { elements.add(board.getPuzzleElement(c)); } } @@ -323,8 +330,7 @@ else if (!elements.contains(board.getPuzzleElement(c))) { LightUpCell c = puzzleBoard.getCell(i, location.y); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { + } else if (!elements.contains(board.getPuzzleElement(c))) { elements.add(board.getPuzzleElement(c)); } } @@ -332,8 +338,7 @@ else if (!elements.contains(board.getPuzzleElement(c))) { LightUpCell c = puzzleBoard.getCell(location.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { + } else if (!elements.contains(board.getPuzzleElement(c))) { elements.add(board.getPuzzleElement(c)); } } @@ -341,8 +346,7 @@ else if (!elements.contains(board.getPuzzleElement(c))) { LightUpCell c = puzzleBoard.getCell(location.x, i); if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { + } else if (!elements.contains(board.getPuzzleElement(c))) { elements.add(board.getPuzzleElement(c)); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java index f36c4ca57..8cf68e570 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java @@ -6,24 +6,26 @@ import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; - import java.awt.*; public class TooFewBulbsContradictionRule extends ContradictionRule { public TooFewBulbsContradictionRule() { - super("LTUP-CONT-0003", "Too Few Bulbs", + super( + "LTUP-CONT-0003", + "Too Few Bulbs", "There cannot be less bulbs around a block than its number states.", "edu/rpi/legup/images/lightup/contradictions/TooFewBulbs.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooManyBulbsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooManyBulbsContradictionRule.java index ffe7ca4d1..64cd4adcb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooManyBulbsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooManyBulbsContradictionRule.java @@ -6,24 +6,26 @@ import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; - import java.awt.*; public class TooManyBulbsContradictionRule extends ContradictionRule { public TooManyBulbsContradictionRule() { - super("LTUP-CONT-0004", "Too Many Bulbs", + super( + "LTUP-CONT-0004", + "Too Many Bulbs", "There cannot be more bulbs around a block than its number states.", "edu/rpi/legup/images/lightup/contradictions/TooManyBulbs.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java b/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java index 827be37ff..9986a1eda 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/EditLineCommand.java @@ -1,5 +1,7 @@ package edu.rpi.legup.puzzle.masyu; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.history.PuzzleCommand; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; @@ -12,11 +14,8 @@ import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.proofeditorui.treeview.*; - import java.awt.event.MouseEvent; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class EditLineCommand extends PuzzleCommand { private TreeTransition transition; private PuzzleElement oldData; @@ -29,7 +28,11 @@ public class EditLineCommand extends PuzzleCommand { private TreeTransitionView transitionView; - public EditLineCommand(ElementView elementView, TreeElementView selectedView, MouseEvent event, MasyuLine line) { + public EditLineCommand( + ElementView elementView, + TreeElementView selectedView, + MouseEvent event, + MasyuLine line) { this.elementView = elementView; this.selectedView = selectedView; this.event = event; @@ -38,9 +41,7 @@ public EditLineCommand(ElementView elementView, TreeElementView selectedView, Mo this.transition = null; } - /** - * Executes a command - */ + /** Executes a command */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -61,18 +62,19 @@ public void executeCommand() { } treeNode.getChildren().add(transition); - puzzle.notifyTreeListeners((ITreeListener listener) -> listener.onTreeElementAdded(transition)); + puzzle.notifyTreeListeners( + (ITreeListener listener) -> listener.onTreeElementAdded(transition)); transitionView = (TreeTransitionView) treeView.getElementView(transition); selection.newSelection(transitionView); - puzzle.notifyTreeListeners((ITreeListener listener) -> listener.onTreeSelectionChanged(selection)); + puzzle.notifyTreeListeners( + (ITreeListener listener) -> listener.onTreeSelectionChanged(selection)); getInstance().getLegupUI().repaintTree(); board = (MasyuBoard) transition.getBoard(); getInstance().getPuzzleModule().setCurrentBoard(board); oldData = newData.copy(); - } - else { + } else { transitionView = (TreeTransitionView) selectedView; transition = transitionView.getTreeElement(); } @@ -100,13 +102,14 @@ public void executeCommand() { System.out.println("delete"); board.getModifiedData().remove(dup_line); board.getLines().remove(dup_line); -// puzzle.notifyBoardListeners((IBoardListener listener) -> listener.onTreeElementChanged(editBoard)); - } - else { + // puzzle.notifyBoardListeners((IBoardListener listener) -> + // listener.onTreeElementChanged(editBoard)); + } else { System.out.println("adding"); board.getModifiedData().add(newData); board.getLines().add((MasyuLine) newData); -// puzzle.notifyBoardListeners((IBoardListener listener) -> listener.onTreeElementChanged(editBoard)); + // puzzle.notifyBoardListeners((IBoardListener listener) -> + // listener.onTreeElementChanged(editBoard)); } transition.propagateChange(newData); @@ -116,15 +119,14 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { Board board = selectedView.getTreeElement().getBoard(); if (!board.isModifiable()) { return "Board is not modifiable"; - } - else { + } else { if (!board.getPuzzleElement(elementView.getPuzzleElement()).isModifiable()) { return "Data is not modifiable"; } @@ -132,9 +134,7 @@ public String getErrorString() { return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Tree tree = getInstance().getTree(); @@ -156,17 +156,16 @@ public void undoCommand() { getInstance().getPuzzleModule().setCurrentBoard(treeNode.getBoard()); } - Board prevBoard = null;// transition.getParentNode().getBoard(); + Board prevBoard = null; // transition.getParentNode().getBoard(); newData.setData(oldData.getData()); board.notifyChange(newData); - //System.err.println(newData.getData() + " : " + oldData.getData()); + // System.err.println(newData.getData() + " : " + oldData.getData()); if (prevBoard.getPuzzleElement(elementView.getPuzzleElement()).equalsData(newData)) { board.removeModifiedData(newData); - } - else { + } else { board.addModifiedData(newData); } transition.propagateChange(newData); diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java b/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java index b339630e9..1ab74d56b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/Masyu.java @@ -14,12 +14,9 @@ public Masyu() { this.exporter = new MasyuExporter(this); this.factory = new MasyuCellFactory(); - } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { boardView = new MasyuView((MasyuBoard) currentBoard); @@ -40,8 +37,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Masyu * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Masyu, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -66,7 +63,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuBoard.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuBoard.java index 96183bcfe..f347ff4c4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuBoard.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.util.ArrayList; import java.util.List; @@ -36,8 +35,7 @@ public void setLines(List lines) { public void notifyChange(PuzzleElement puzzleElement) { if (puzzleElement instanceof MasyuLine) { lines.add((MasyuLine) puzzleElement); - } - else { + } else { super.notifyChange(puzzleElement); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java index af32dbfb6..af0811c55 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCell.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.masyu; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; public class MasyuCell extends GridCell { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCellFactory.java index 5278e0036..50e3c4cc0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class MasyuCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -23,7 +22,8 @@ public class MasyuCellFactory extends ElementFactory { public MasyuCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("Masyu Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "Masyu Factory: unknown puzzleElement puzzleElement"); } MasyuBoard masyuBoard = (MasyuBoard) board; @@ -44,11 +44,10 @@ public MasyuCell importCell(Node node, Board board) throws InvalidFileFormatExce MasyuCell cell = new MasyuCell(MasyuType.convertToMasyuType(value), new Point(x, y)); cell.setIndex(y * height + x); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Masyu Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Masyu Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("Masyu Factory: could not find attribute(s)"); } } @@ -56,7 +55,7 @@ public MasyuCell importCell(Node node, Board board) throws InvalidFileFormatExce /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java index 02328c691..202c43a1e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuController.java @@ -1,18 +1,17 @@ package edu.rpi.legup.puzzle.masyu; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.BoardView; - import java.awt.*; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class MasyuController extends ElementController { private MasyuElementView mousePressedCell; @@ -52,19 +51,25 @@ public void mouseDragged(MouseEvent e) { if (Math.abs(p1.x - p2.x) == 1 ^ Math.abs(p1.y - p2.y) == 1) { masyuLine.add(elementView); - MasyuLine newLine = new MasyuLine(mousePressedCell.getPuzzleElement(), mouseDraggedCell.getPuzzleElement()); + MasyuLine newLine = + new MasyuLine( + mousePressedCell.getPuzzleElement(), + mouseDraggedCell.getPuzzleElement()); puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(newLine)); } - } - else { + } else { if (mouseDraggedCell != elementView) { Point p1 = mouseDraggedCell.getPuzzleElement().getLocation(); Point p2 = elementView.getPuzzleElement().getLocation(); if (Math.abs(p1.x - p2.x) == 1 ^ Math.abs(p1.y - p2.y) == 1) { masyuLine.add(elementView); - MasyuLine newLine = new MasyuLine(mouseDraggedCell.getPuzzleElement(), elementView.getPuzzleElement()); - puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(newLine)); + MasyuLine newLine = + new MasyuLine( + mouseDraggedCell.getPuzzleElement(), + elementView.getPuzzleElement()); + puzzle.notifyBoardListeners( + listener -> listener.onBoardDataChanged(newLine)); } mouseDraggedCell = elementView; } @@ -83,7 +88,7 @@ public void mouseReleased(MouseEvent e) { /** * Alters the cells as they are being dragged over or clicked * - * @param e Mouse event being used + * @param e Mouse event being used * @param data Data of selected cell */ @Override @@ -94,8 +99,7 @@ public void changeCell(MouseEvent e, PuzzleElement data) { } if (cell.getData() == MasyuType.UNKNOWN) { data.setData(3); - } - else { + } else { data.setData(0); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuElementView.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuElementView.java index b2f1f8902..942e1ffd2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.masyu; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class MasyuElementView extends GridElementView { @@ -24,8 +23,7 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == MasyuType.BLACK) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.LIGHT_GRAY); @@ -34,8 +32,7 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.fillOval(location.x + 5, location.y + 5, 20, 20); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == MasyuType.WHITE) { graphics2D.setStroke(new BasicStroke(2)); graphics2D.setColor(Color.LIGHT_GRAY); 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 e5fb071b4..0c692193a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuExporter.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 MasyuExporter extends PuzzleExporter { @@ -16,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { MasyuBoard board; if (puzzle.getTree() != null) { board = (MasyuBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (MasyuBoard) puzzle.getBoardView().getBoard(); } @@ -28,7 +26,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); for (PuzzleElement puzzleElement : board.getPuzzleElements()) { MasyuCell cell = (MasyuCell) puzzleElement; - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } 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 3e0d328c4..2170da3ee 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class MasyuImporter extends PuzzleImporter { public MasyuImporter(Masyu masyu) { super(masyu); @@ -26,14 +25,12 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @Override - public void initializeBoard(int rows, int columns) { - - } + public void initializeBoard(int rows, int columns) {} /** * Creates the board for building @@ -45,11 +42,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("Masyu Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "Masyu Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("Masyu Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "Masyu Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -58,9 +57,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); masyuBoard = new MasyuBoard(size); - } - else { - if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); masyuBoard = new MasyuBoard(width, height); @@ -75,7 +74,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = masyuBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - MasyuCell cell = (MasyuCell) puzzle.getFactory().importCell(elementDataList.item(i), masyuBoard); + MasyuCell cell = + (MasyuCell) + puzzle.getFactory().importCell(elementDataList.item(i), masyuBoard); Point loc = cell.getLocation(); if (cell.getData() != MasyuType.UNKNOWN) { cell.setModifiable(false); @@ -95,9 +96,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } puzzle.setCurrentBoard(masyuBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Masyu Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Masyu Importer: unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLine.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLine.java index d8c3240c7..eda51c839 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLine.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLine.java @@ -25,8 +25,10 @@ public void setC2(MasyuCell c2) { } public boolean compare(MasyuLine line) { - return ((line.getC1().getLocation().equals(data.getKey().getLocation()) && line.getC2().getLocation().equals(data.getValue().getLocation())) || - (line.getC1().getLocation().equals(data.getValue().getLocation()) && line.getC2().getLocation().equals(data.getKey().getLocation()))); + return ((line.getC1().getLocation().equals(data.getKey().getLocation()) + && line.getC2().getLocation().equals(data.getValue().getLocation())) + || (line.getC1().getLocation().equals(data.getValue().getLocation()) + && line.getC2().getLocation().equals(data.getKey().getLocation()))); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLineView.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLineView.java index d527ca414..c967f4865 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLineView.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuLineView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.masyu; import edu.rpi.legup.ui.boardview.ElementView; - import java.awt.*; public class MasyuLineView extends ElementView { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java index 264350a95..4029d259d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuType.java @@ -1,9 +1,10 @@ package edu.rpi.legup.puzzle.masyu; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - public enum MasyuType { - UNKNOWN, BLACK, WHITE, LINE; + UNKNOWN, + BLACK, + WHITE, + LINE; public static MasyuType convertToMasyuType(int num) { switch (num) { @@ -18,4 +19,3 @@ public static MasyuType convertToMasyuType(int num) { } } } - diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuView.java b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuView.java index ac60dbe66..e962a6ebf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuView.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/MasyuView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.GridBoardView; - import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -20,7 +19,8 @@ public MasyuView(MasyuBoard board) { MasyuElementView elementView = new MasyuElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } lineViews = new ArrayList<>(); @@ -33,7 +33,8 @@ public MasyuView(MasyuBoard board) { @Override public void drawBoard(Graphics2D graphics2D) { - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); super.drawBoard(graphics2D); lineViews.forEach(masyuLineView -> masyuLineView.draw(graphics2D)); } diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BadLoopingContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BadLoopingContradictionRule.java index 0da4dffe2..1681446cd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BadLoopingContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BadLoopingContradictionRule.java @@ -7,18 +7,21 @@ public class BadLoopingContradictionRule extends ContradictionRule { public BadLoopingContradictionRule() { - super("MASY-CONT-0001", "Bad Looping", + super( + "MASY-CONT-0001", + "Bad Looping", "", "edu/rpi/legup/images/masyu/ContradictionBadLooping.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackContradictionRule.java index b4d32a46a..c219eeaa3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackContradictionRule.java @@ -7,18 +7,17 @@ public class BlackContradictionRule extends ContradictionRule { public BlackContradictionRule() { - super("MASY-CONT-0002", "Black", - "", - "edu/rpi/legup/images/masyu/ContradictionBlack.png"); + super("MASY-CONT-0002", "Black", "", "edu/rpi/legup/images/masyu/ContradictionBlack.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackEdgeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackEdgeDirectRule.java index b35dceaa7..665a74204 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackEdgeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackEdgeDirectRule.java @@ -1,42 +1,41 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class BlackEdgeDirectRule extends DirectRule { - - public BlackEdgeDirectRule() { - super("MASY-BASC-0001", "Black Edge", - "", - "edu/rpi/legup/images/masyu/RuleBlackEdge.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class BlackEdgeDirectRule extends DirectRule { + + public BlackEdgeDirectRule() { + super("MASY-BASC-0001", "Black Edge", "", "edu/rpi/legup/images/masyu/RuleBlackEdge.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackSplitCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackSplitCaseRule.java index 3db89ef58..c7e3ff500 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackSplitCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlackSplitCaseRule.java @@ -5,20 +5,17 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class BlackSplitCaseRule extends CaseRule { public BlackSplitCaseRule() { - super("MASY-CASE-0001", "Black Split", - "", - "edu/rpi/legup/images/masyu/CaseBlackSplit.png"); + super("MASY-CASE-0001", "Black Split", "", "edu/rpi/legup/images/masyu/CaseBlackSplit.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -29,13 +26,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -43,7 +41,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -54,9 +53,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlockedBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlockedBlackDirectRule.java index 4364d016c..5c284eb45 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlockedBlackDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/BlockedBlackDirectRule.java @@ -1,42 +1,45 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class BlockedBlackDirectRule extends DirectRule { - - public BlockedBlackDirectRule() { - super("MASY-BASC-0002", "Blocked Black", - "", - "edu/rpi/legup/images/masyu/RuleBlockedBlack.gif"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class BlockedBlackDirectRule extends DirectRule { + + public BlockedBlackDirectRule() { + super( + "MASY-BASC-0002", + "Blocked Black", + "", + "edu/rpi/legup/images/masyu/RuleBlockedBlack.gif"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/ConnectedCellsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/ConnectedCellsDirectRule.java index 49949ecec..6aee8ba5f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/ConnectedCellsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/ConnectedCellsDirectRule.java @@ -1,42 +1,45 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class ConnectedCellsDirectRule extends DirectRule { - - public ConnectedCellsDirectRule() { - super("MASY-BASC-0003", "Connected Cells", - "", - "edu/rpi/legup/images/masyu/RuleConnectedCells.gif"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class ConnectedCellsDirectRule extends DirectRule { + + public ConnectedCellsDirectRule() { + super( + "MASY-BASC-0003", + "Connected Cells", + "", + "edu/rpi/legup/images/masyu/RuleConnectedCells.gif"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/FinishPathDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/FinishPathDirectRule.java index e04301ce2..27b39bc04 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/FinishPathDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/FinishPathDirectRule.java @@ -1,42 +1,45 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class FinishPathDirectRule extends DirectRule { - - public FinishPathDirectRule() { - super("MASY-BASC-0004", "Finished Path", - "", - "edu/rpi/legup/images/masyu/RuleFinishPath.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class FinishPathDirectRule extends DirectRule { + + public FinishPathDirectRule() { + super( + "MASY-BASC-0004", + "Finished Path", + "", + "edu/rpi/legup/images/masyu/RuleFinishPath.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NearWhiteDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NearWhiteDirectRule.java index 7cf707d0d..6a565bc82 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NearWhiteDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NearWhiteDirectRule.java @@ -1,42 +1,41 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class NearWhiteDirectRule extends DirectRule { - - public NearWhiteDirectRule() { - super("MASY-BASC-0005", "Near White", - "", - "edu/rpi/legup/images/masyu/RuleNearWhite.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class NearWhiteDirectRule extends DirectRule { + + public NearWhiteDirectRule() { + super("MASY-BASC-0005", "Near White", "", "edu/rpi/legup/images/masyu/RuleNearWhite.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NoOptionsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NoOptionsContradictionRule.java index 19da790aa..c41999b9c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NoOptionsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NoOptionsContradictionRule.java @@ -7,18 +7,21 @@ public class NoOptionsContradictionRule extends ContradictionRule { public NoOptionsContradictionRule() { - super("MASY-CONT-0003", "No Options", + super( + "MASY-CONT-0003", + "No Options", "", "edu/rpi/legup/images/masyu/ContradictionNoOptions.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NormalSplitCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NormalSplitCaseRule.java index ab47adc1b..382ac6466 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NormalSplitCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/NormalSplitCaseRule.java @@ -5,20 +5,21 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class NormalSplitCaseRule extends CaseRule { public NormalSplitCaseRule() { - super("MASY-CASE-0002", "Normal Split", + super( + "MASY-CASE-0002", + "Normal Split", "", "edu/rpi/legup/images/masyu/CaseNormalSplit.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -29,13 +30,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -43,7 +45,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -54,9 +57,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyOneChoiceDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyOneChoiceDirectRule.java index b0311e741..bc7353e5d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyOneChoiceDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyOneChoiceDirectRule.java @@ -1,42 +1,45 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class OnlyOneChoiceDirectRule extends DirectRule { - - public OnlyOneChoiceDirectRule() { - super("MASY-BASC-0006", "Only One Choice", - "", - "edu/rpi/legup/images/masyu/RuleOnlyOneChoice.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class OnlyOneChoiceDirectRule extends DirectRule { + + public OnlyOneChoiceDirectRule() { + super( + "MASY-BASC-0006", + "Only One Choice", + "", + "edu/rpi/legup/images/masyu/RuleOnlyOneChoice.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyTwoContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyTwoContradictionRule.java index ab587a0ec..ee71ee2bc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyTwoContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/OnlyTwoContradictionRule.java @@ -7,18 +7,21 @@ public class OnlyTwoContradictionRule extends ContradictionRule { public OnlyTwoContradictionRule() { - super("MASY-CONT-0004", "Only Two", + super( + "MASY-CONT-0004", + "Only Two", "", "edu/rpi/legup/images/masyu/ContradictionOnly2.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteContradictionRule.java index 091c2b039..efc523b8d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteContradictionRule.java @@ -7,18 +7,17 @@ public class WhiteContradictionRule extends ContradictionRule { public WhiteContradictionRule() { - super("MASY-CONT-0005", "White", - "", - "edu/rpi/legup/images/masyu/ContradictionWhite.png"); + super("MASY-CONT-0005", "White", "", "edu/rpi/legup/images/masyu/ContradictionWhite.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteEdgeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteEdgeDirectRule.java index bd894e1d4..4d2982d00 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteEdgeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteEdgeDirectRule.java @@ -1,41 +1,40 @@ -package edu.rpi.legup.puzzle.masyu.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; - -public class WhiteEdgeDirectRule extends DirectRule { - public WhiteEdgeDirectRule() { - super("MASY-BASC-0007", "White Edge", - "", - "edu/rpi/legup/images/masyu/RuleWhiteEdge.png"); - } - - /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule - * This method is the one that should overridden in child classes - * - * @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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.masyu.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; + +public class WhiteEdgeDirectRule extends DirectRule { + public WhiteEdgeDirectRule() { + super("MASY-BASC-0007", "White Edge", "", "edu/rpi/legup/images/masyu/RuleWhiteEdge.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule This method is the one that should overridden in child + * classes + * + * @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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteSplitCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteSplitCaseRule.java index e45fde0a1..d50ddacfa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteSplitCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/masyu/rules/WhiteSplitCaseRule.java @@ -5,20 +5,17 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; - import java.util.List; public class WhiteSplitCaseRule extends CaseRule { public WhiteSplitCaseRule() { - super("MASY-CASE-0003", "White Split", - "", - "edu/rpi/legup/images/masyu/CaseWhiteSplit.png"); + super("MASY-CASE-0003", "White Split", "", "edu/rpi/legup/images/masyu/CaseWhiteSplit.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -29,13 +26,14 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node at the specific puzzleElement index using - * this rule. This method is the one that should overridden in child classes. + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule. This method is the one that should overridden in child + * classes. * - * @param transition transition to check + * @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 + * @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) { @@ -43,7 +41,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Gets the case board that indicates where this case rule can be applied on the given {@link Board}. + * Gets the case board that indicates where this case rule can be applied on the given {@link + * Board}. * * @param board board to find locations where this case rule can be applied * @return a case board @@ -54,9 +53,10 @@ public CaseBoard getCaseBoard(Board board) { } /** - * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on this case rule. + * Gets the possible cases for this {@link Board} at a specific {@link PuzzleElement} based on + * this case rule. * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java index 2edbcb039..8366d905f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/Nurikabe.java @@ -17,9 +17,7 @@ public Nurikabe() { this.factory = new NurikabeCellFactory(); } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { boardView = new NurikabeView((NurikabeBoard) currentBoard); @@ -42,8 +40,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Nurikabe * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Nurikabe, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -80,7 +78,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeBoard.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeBoard.java index d5b83d1e8..1f80611e8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeBoard.java @@ -1,6 +1,5 @@ package edu.rpi.legup.puzzle.nurikabe; -import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; @@ -15,8 +14,11 @@ public NurikabeBoard(int size) { @Override public NurikabeCell getCell(int x, int y) { - if (y * dimension.width + x >= puzzleElements.size() || x >= dimension.width || - y >= dimension.height || x < 0 || y < 0) { + if (y * dimension.width + x >= puzzleElements.size() + || x >= dimension.width + || y >= dimension.height + || x < 0 + || y < 0) { return null; } return (NurikabeCell) super.getCell(x, y); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java index f84a3c9f1..c6cd2c64e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java @@ -1,8 +1,7 @@ package edu.rpi.legup.puzzle.nurikabe; -import edu.rpi.legup.model.gameboard.GridCell; import edu.rpi.legup.model.elements.Element; - +import edu.rpi.legup.model.gameboard.GridCell; import java.awt.*; import java.awt.event.MouseEvent; @@ -11,7 +10,7 @@ public class NurikabeCell extends GridCell { /** * NurikabeCell Constructor - creates a NurikabeCell from the specified value and location * - * @param value value of the NurikabeCell + * @param value value of the NurikabeCell * @param location position of the NurikabeCell */ public NurikabeCell(int value, Point location) { @@ -46,7 +45,7 @@ public NurikabeType getType() { */ @Override public void setType(Element e, MouseEvent m) { - switch (e.getElementID()){ + switch (e.getElementID()) { case "NURI-PLAC-0001": this.data = -1; break; @@ -54,20 +53,18 @@ public void setType(Element e, MouseEvent m) { this.data = 0; break; case "NURI-UNPL-0001": - switch (m.getButton()){ + switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data <= 0 || this.data > 8) { this.data = 1; - } - else { + } else { this.data = this.data + 1; } break; case MouseEvent.BUTTON3: if (this.data > 1) { this.data = this.data - 1; - } - else { + } else { this.data = 9; } break; @@ -92,4 +89,4 @@ public NurikabeCell copy() { copy.setGiven(isGiven); return copy; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCellFactory.java index b754f3e46..7241608ac 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class NurikabeCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -23,7 +22,8 @@ public class NurikabeCellFactory extends ElementFactory { public NurikabeCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("nurikabe Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "nurikabe Factory: unknown puzzleElement puzzleElement"); } NurikabeBoard nurikabeBoard = (NurikabeBoard) board; @@ -35,7 +35,8 @@ public NurikabeCell importCell(Node node, Board board) throws InvalidFileFormatE int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); if (x >= width || y >= height) { - throw new InvalidFileFormatException("nurikabe Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "nurikabe Factory: cell location out of bounds"); } if (value < -2) { throw new InvalidFileFormatException("nurikabe Factory: cell unknown value"); @@ -44,11 +45,10 @@ public NurikabeCell importCell(Node node, Board board) throws InvalidFileFormatE NurikabeCell cell = new NurikabeCell(value, new Point(x, y)); cell.setIndex(y * height + x); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("nurikabe Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "nurikabe Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("nurikabe Factory: could not find attribute(s)"); } } @@ -56,7 +56,7 @@ public NurikabeCell importCell(Node node, Board board) throws InvalidFileFormatE /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java index c115498a8..158abe7b4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class NurikabeController extends ElementController { @@ -12,32 +11,31 @@ public void changeCell(MouseEvent e, PuzzleElement data) { NurikabeCell cell = (NurikabeCell) data; if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); - } - else { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { if (cell.getData() == -2) { data.setData(0); - } - else { + } else { if (cell.getData() == 0) { data.setData(-1); - } - else { + } else { data.setData(-2); } } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() == -2) { data.setData(-1); - } - else { + } else { if (cell.getData() == 0) { data.setData(-2); - } - else { + } else { data.setData(0); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeElementView.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeElementView.java index f122a7a5b..116b727d2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.nurikabe; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class NurikabeElementView extends GridElementView { @@ -40,24 +39,22 @@ public void drawElement(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(puzzleElement.getData()); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); - } - else { + } else { if (type == NurikabeType.BLACK) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.BLACK); graphics2D.fillRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == NurikabeType.WHITE) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.WHITE); graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == NurikabeType.UNKNOWN) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.LIGHT_GRAY); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java index 095642a6e..23efd4724 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java @@ -15,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { NurikabeBoard board; if (puzzle.getTree() != null) { board = (NurikabeBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (NurikabeBoard) puzzle.getBoardView().getBoard(); } @@ -28,7 +27,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { NurikabeCell cell = (NurikabeCell) puzzleElement; if (cell.getData() != -2) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } 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 2cbcc9ad0..ec4ed3ac1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class NurikabeImporter extends PuzzleImporter { public NurikabeImporter(Nurikabe nurikabe) { super(nurikabe); @@ -26,7 +25,7 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @@ -36,7 +35,8 @@ public void initializeBoard(int rows, int columns) { for (int y = 0; y < rows; y++) { for (int x = 0; x < columns; x++) { - NurikabeCell cell = new NurikabeCell(NurikabeType.UNKNOWN.toValue(), new Point(x, y)); + NurikabeCell cell = + new NurikabeCell(NurikabeType.UNKNOWN.toValue(), new Point(x, y)); cell.setIndex(y * columns + x); cell.setModifiable(true); nurikabeBoard.setCell(x, y, cell); @@ -55,11 +55,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("nurikabe Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "nurikabe Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("nurikabe Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "nurikabe Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -68,9 +70,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); nurikabeBoard = new NurikabeBoard(size); - } - else { - if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); nurikabeBoard = new NurikabeBoard(width, height); @@ -85,7 +87,10 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = nurikabeBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - NurikabeCell cell = (NurikabeCell) puzzle.getFactory().importCell(elementDataList.item(i), nurikabeBoard); + NurikabeCell cell = + (NurikabeCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), nurikabeBoard); Point loc = cell.getLocation(); if (cell.getData() != NurikabeType.UNKNOWN.toValue()) { cell.setModifiable(false); @@ -97,7 +102,8 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (nurikabeBoard.getCell(x, y) == null) { - NurikabeCell cell = new NurikabeCell(NurikabeType.UNKNOWN.toValue(), new Point(x, y)); + NurikabeCell cell = + new NurikabeCell(NurikabeType.UNKNOWN.toValue(), new Point(x, y)); cell.setIndex(y * height + x); cell.setModifiable(true); nurikabeBoard.setCell(x, y, cell); @@ -105,9 +111,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } puzzle.setCurrentBoard(nurikabeBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("nurikabe Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "nurikabe Importer: unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeType.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeType.java index 1dbab645d..8acf9fc02 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeType.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeType.java @@ -1,7 +1,10 @@ package edu.rpi.legup.puzzle.nurikabe; public enum NurikabeType { - UNKNOWN, BLACK, WHITE, NUMBER; + UNKNOWN, + BLACK, + WHITE, + NUMBER; public int toValue() { return this.ordinal() - 2; 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 024cf6bb2..71ffa08e5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.utility.DisjointSets; - import java.awt.*; import java.util.HashMap; import java.util.HashSet; @@ -50,18 +49,18 @@ public static DisjointSets getNurikabeRegions(NurikabeBoard board) NurikabeCell rightCell = board.getCell(x + 1, y); NurikabeCell downCell = board.getCell(x, y + 1); - if (cell.getType() == NurikabeType.NUMBER || - cell.getType() == NurikabeType.WHITE) { - if (rightCell != null && (rightCell.getType() == NurikabeType.NUMBER || - rightCell.getType() == NurikabeType.WHITE)) { + if (cell.getType() == NurikabeType.NUMBER || cell.getType() == NurikabeType.WHITE) { + if (rightCell != null + && (rightCell.getType() == NurikabeType.NUMBER + || rightCell.getType() == NurikabeType.WHITE)) { regions.union(cell, rightCell); } - if (downCell != null && (downCell.getType() == NurikabeType.NUMBER || - downCell.getType() == NurikabeType.WHITE)) { + if (downCell != null + && (downCell.getType() == NurikabeType.NUMBER + || downCell.getType() == NurikabeType.WHITE)) { regions.union(cell, downCell); } - } - else { + } else { if (cell.getType() == NurikabeType.BLACK) { if (rightCell != null && rightCell.getType() == NurikabeType.BLACK) { regions.union(cell, rightCell); @@ -69,8 +68,7 @@ public static DisjointSets getNurikabeRegions(NurikabeBoard board) if (downCell != null && downCell.getType() == NurikabeType.BLACK) { regions.union(cell, downCell); } - } - else { + } else { if (cell.getType() == NurikabeType.UNKNOWN) { if (rightCell != null && rightCell.getType() == NurikabeType.UNKNOWN) { regions.union(cell, rightCell); @@ -110,13 +108,16 @@ public static DisjointSets getPossibleBlackRegions(NurikabeBoard b NurikabeCell cell = board.getCell(x, y); NurikabeCell rightCell = board.getCell(x + 1, y); NurikabeCell downCell = board.getCell(x, y + 1); - if (cell.getType() == NurikabeType.BLACK || cell.getType() == NurikabeType.UNKNOWN) { - if (rightCell != null && (rightCell.getType() == NurikabeType.BLACK || - rightCell.getType() == NurikabeType.UNKNOWN)) { + if (cell.getType() == NurikabeType.BLACK + || cell.getType() == NurikabeType.UNKNOWN) { + if (rightCell != null + && (rightCell.getType() == NurikabeType.BLACK + || rightCell.getType() == NurikabeType.UNKNOWN)) { blackRegions.union(cell, rightCell); } - if (downCell != null && (downCell.getType() == NurikabeType.BLACK || - downCell.getType() == NurikabeType.UNKNOWN)) { + if (downCell != null + && (downCell.getType() == NurikabeType.BLACK + || downCell.getType() == NurikabeType.UNKNOWN)) { blackRegions.union(cell, downCell); } } @@ -139,7 +140,9 @@ public static DisjointSets getPossibleWhiteRegions(NurikabeBoard b DisjointSets whiteRegions = new DisjointSets<>(); for (PuzzleElement data : board.getPuzzleElements()) { NurikabeCell cell = (NurikabeCell) data; - if (cell.getType() == NurikabeType.WHITE || cell.getType() == NurikabeType.NUMBER || cell.getType() == NurikabeType.UNKNOWN) { + if (cell.getType() == NurikabeType.WHITE + || cell.getType() == NurikabeType.NUMBER + || cell.getType() == NurikabeType.UNKNOWN) { whiteRegions.createSet(cell); } } @@ -149,14 +152,19 @@ public static DisjointSets getPossibleWhiteRegions(NurikabeBoard b NurikabeCell cell = board.getCell(x, y); NurikabeCell rightCell = board.getCell(x + 1, y); NurikabeCell downCell = board.getCell(x, y + 1); - if (cell.getType() == NurikabeType.WHITE || cell.getType() == NurikabeType.NUMBER || - cell.getType() == NurikabeType.UNKNOWN) { - if (rightCell != null && (rightCell.getType() == NurikabeType.WHITE || - rightCell.getType() == NurikabeType.NUMBER || rightCell.getType() == NurikabeType.UNKNOWN)) { + if (cell.getType() == NurikabeType.WHITE + || cell.getType() == NurikabeType.NUMBER + || cell.getType() == NurikabeType.UNKNOWN) { + if (rightCell != null + && (rightCell.getType() == NurikabeType.WHITE + || rightCell.getType() == NurikabeType.NUMBER + || rightCell.getType() == NurikabeType.UNKNOWN)) { whiteRegions.union(cell, rightCell); } - if (downCell != null && (downCell.getType() == NurikabeType.WHITE || - downCell.getType() == NurikabeType.NUMBER || downCell.getType() == NurikabeType.UNKNOWN)) { + if (downCell != null + && (downCell.getType() == NurikabeType.WHITE + || downCell.getType() == NurikabeType.NUMBER + || downCell.getType() == NurikabeType.UNKNOWN)) { whiteRegions.union(cell, downCell); } } @@ -166,9 +174,8 @@ public static DisjointSets getPossibleWhiteRegions(NurikabeBoard b } /** - * Makes a map where the keys are white/numbered cells - * and the values are the amount of cells that need - * to be added to the region + * Makes a map where the keys are white/numbered cells and the values are the amount of cells + * that need to be added to the region * * @param board nurikabe board * @return a map of cell keys to integer values @@ -181,7 +188,7 @@ public static HashMap getWhiteRegionMap(NurikabeBoard boa // Final mapping of cell to size HashMap whiteRegionMap = new HashMap<>(); for (NurikabeCell center : numberedCells) { - //BFS for each center to find the size of the region + // BFS for each center to find the size of the region int size = 1; // Mark all the vertices as not visited(By default // set as false) @@ -224,8 +231,7 @@ public static HashMap getWhiteRegionMap(NurikabeBoard boa // If a adjacent has not been visited, then mark it // visited and enqueue it for (NurikabeCell n : adj) { - if (!visited.getOrDefault(n, false) - && n.getType() == NurikabeType.WHITE) { + if (!visited.getOrDefault(n, false) && n.getType() == NurikabeType.WHITE) { connected.add(n); visited.put(n, true); queue.add(n); @@ -249,7 +255,8 @@ public static HashMap getWhiteRegionMap(NurikabeBoard boa * @param center nurikabe cell * @return a set of all white/numbered cells in the region */ - public static Set getSurroundedRegionOf(NurikabeBoard board, NurikabeCell center) { + public static Set getSurroundedRegionOf( + NurikabeBoard board, NurikabeCell center) { int width = board.getWidth(); int height = board.getHeight(); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeView.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeView.java index cef287b92..0bac7e3e5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeView.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.GridBoardView; - import java.awt.*; public class NurikabeView extends GridBoardView { @@ -17,7 +16,8 @@ public NurikabeView(NurikabeBoard board) { NurikabeElementView elementView = new NurikabeElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java index 1609fbf99..459a809e0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java @@ -4,6 +4,10 @@ public class BlackTile extends PlaceableElement { public BlackTile() { - super("NURI-PLAC-0001", "Black Tile", "The black tile", "edu/rpi/legup/images/nurikabe/tiles/BlackTile.png"); + super( + "NURI-PLAC-0001", + "Black Tile", + "The black tile", + "edu/rpi/legup/images/nurikabe/tiles/BlackTile.png"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java index 0b1ee9656..475b278da 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java @@ -6,7 +6,11 @@ public class NumberTile extends NonPlaceableElement { private int object_num; public NumberTile() { - super("NURI-UNPL-0001", "Number Tile", "A numbered tile", "edu/rpi/legup/images/nurikabe/tiles/NumberTile.png"); + super( + "NURI-UNPL-0001", + "Number Tile", + "A numbered tile", + "edu/rpi/legup/images/nurikabe/tiles/NumberTile.png"); object_num = 0; } @@ -23,5 +27,4 @@ public int getTileNumber() { public void setTileNumber(int num) { object_num = num; } - } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java index 259e17e74..85d47e208 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java @@ -4,6 +4,10 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("NURI-UNPL-0002", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/nurikabe/tiles/UnknownTile.png"); + super( + "NURI-UNPL-0002", + "Unknown Tile", + "A blank tile", + "edu/rpi/legup/images/nurikabe/tiles/UnknownTile.png"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java index 434e0663e..35eb63b81 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java @@ -4,6 +4,10 @@ public class WhiteTile extends PlaceableElement { public WhiteTile() { - super("NURI-PLAC-0002", "White Tile", "The white tile", "edu/rpi/legup/images/nurikabe/tiles/WhiteTile.png"); + super( + "NURI-PLAC-0002", + "White Tile", + "The white tile", + "edu/rpi/legup/images/nurikabe/tiles/WhiteTile.png"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBetweenRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBetweenRegionsDirectRule.java index bb527e4e2..7428ac204 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBetweenRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBetweenRegionsDirectRule.java @@ -1,112 +1,122 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -public class BlackBetweenRegionsDirectRule extends DirectRule { - - public BlackBetweenRegionsDirectRule() { - super("NURI-BASC-0001", - "Black Between Regions", - "Any unknowns between two regions must be black.", - "edu/rpi/legup/images/nurikabe/rules/BetweenRegions.png"); - } - - /** - * 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) { - Set contras = new LinkedHashSet<>(); - contras.add(new MultipleNumbersContradictionRule()); - contras.add(new TooManySpacesContradictionRule()); - - NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); - - NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.BLACK) { - return super.getInvalidUseOfRuleMessage() + ": Only black cells are allowed for this rule!"; - } - - int x = cell.getLocation().x; - int y = cell.getLocation().y; - - DisjointSets regions = NurikabeUtilities.getNurikabeRegions(destBoardState); - Set adjacentWhiteRegions = new HashSet<>(); - NurikabeCell upCell = destBoardState.getCell(x, y - 1); - NurikabeCell rightCell = destBoardState.getCell(x + 1, y); - NurikabeCell downCell = destBoardState.getCell(x, y + 1); - NurikabeCell leftCell = destBoardState.getCell(x - 1, y); - - if (upCell != null && (upCell.getType() == NurikabeType.WHITE || upCell.getType() == NurikabeType.NUMBER)) { - NurikabeCell repCell = regions.find(upCell); - if (!adjacentWhiteRegions.contains(repCell)) { - adjacentWhiteRegions.add(repCell); - } - } - if (rightCell != null && (rightCell.getType() == NurikabeType.WHITE || rightCell.getType() == NurikabeType.NUMBER)) { - NurikabeCell repCell = regions.find(rightCell); - if (!adjacentWhiteRegions.contains(repCell)) { - adjacentWhiteRegions.add(repCell); - } - } - if (downCell != null && (downCell.getType() == NurikabeType.WHITE || downCell.getType() == NurikabeType.NUMBER)) { - NurikabeCell repCell = regions.find(downCell); - if (!adjacentWhiteRegions.contains(repCell)) { - adjacentWhiteRegions.add(repCell); - } - } - if (leftCell != null && (leftCell.getType() == NurikabeType.WHITE || leftCell.getType() == NurikabeType.NUMBER)) { - NurikabeCell repCell = regions.find(leftCell); - if (!adjacentWhiteRegions.contains(repCell)) { - adjacentWhiteRegions.add(repCell); - } - } - - if (adjacentWhiteRegions.size() < 2) { - return "The new black cell must separate two white regions for this rule!"; - } - - NurikabeBoard modified = origBoardState.copy(); - modified.getCell(x, y).setData(NurikabeType.WHITE.toValue()); - - for (ContradictionRule c : contras) { - if (c.checkContradiction(modified) == null) { - return null; - } - } - return "Does not follow from the rule"; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.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.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +public class BlackBetweenRegionsDirectRule extends DirectRule { + + public BlackBetweenRegionsDirectRule() { + super( + "NURI-BASC-0001", + "Black Between Regions", + "Any unknowns between two regions must be black.", + "edu/rpi/legup/images/nurikabe/rules/BetweenRegions.png"); + } + + /** + * 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) { + Set contras = new LinkedHashSet<>(); + contras.add(new MultipleNumbersContradictionRule()); + contras.add(new TooManySpacesContradictionRule()); + + NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); + + NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.BLACK) { + return super.getInvalidUseOfRuleMessage() + + ": Only black cells are allowed for this rule!"; + } + + int x = cell.getLocation().x; + int y = cell.getLocation().y; + + DisjointSets regions = NurikabeUtilities.getNurikabeRegions(destBoardState); + Set adjacentWhiteRegions = new HashSet<>(); + NurikabeCell upCell = destBoardState.getCell(x, y - 1); + NurikabeCell rightCell = destBoardState.getCell(x + 1, y); + NurikabeCell downCell = destBoardState.getCell(x, y + 1); + NurikabeCell leftCell = destBoardState.getCell(x - 1, y); + + if (upCell != null + && (upCell.getType() == NurikabeType.WHITE + || upCell.getType() == NurikabeType.NUMBER)) { + NurikabeCell repCell = regions.find(upCell); + if (!adjacentWhiteRegions.contains(repCell)) { + adjacentWhiteRegions.add(repCell); + } + } + if (rightCell != null + && (rightCell.getType() == NurikabeType.WHITE + || rightCell.getType() == NurikabeType.NUMBER)) { + NurikabeCell repCell = regions.find(rightCell); + if (!adjacentWhiteRegions.contains(repCell)) { + adjacentWhiteRegions.add(repCell); + } + } + if (downCell != null + && (downCell.getType() == NurikabeType.WHITE + || downCell.getType() == NurikabeType.NUMBER)) { + NurikabeCell repCell = regions.find(downCell); + if (!adjacentWhiteRegions.contains(repCell)) { + adjacentWhiteRegions.add(repCell); + } + } + if (leftCell != null + && (leftCell.getType() == NurikabeType.WHITE + || leftCell.getType() == NurikabeType.NUMBER)) { + NurikabeCell repCell = regions.find(leftCell); + if (!adjacentWhiteRegions.contains(repCell)) { + adjacentWhiteRegions.add(repCell); + } + } + + if (adjacentWhiteRegions.size() < 2) { + return "The new black cell must separate two white regions for this rule!"; + } + + NurikabeBoard modified = origBoardState.copy(); + modified.getCell(x, y).setData(NurikabeType.WHITE.toValue()); + + for (ContradictionRule c : contras) { + if (c.checkContradiction(modified) == null) { + return null; + } + } + return "Does not follow from the rule"; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckDirectRule.java index c4cf0d3fd..a8a94178b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackBottleNeckDirectRule.java @@ -1,65 +1,69 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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; - -public class BlackBottleNeckDirectRule extends DirectRule { - - public BlackBottleNeckDirectRule() { - super("NURI-BASC-0002", - "Black Bottle Neck", - "If there is only one path for a black to escape, then those unknowns must be black.", - "edu/rpi/legup/images/nurikabe/rules/OneUnknownBlack.png"); - } - - /** - * 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) { - ContradictionRule contraRule = new IsolateBlackContradictionRule(); - - NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); - - NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.BLACK) { - return super.getInvalidUseOfRuleMessage() + ": Only black cells are allowed for this rule!"; - } - NurikabeBoard modified = origBoardState.copy(); - NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); - modCell.setData(NurikabeType.WHITE.toValue()); - - if (contraRule.checkContradiction(modified) == null) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This is not the only way for black to escape!"; - } - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; + +public class BlackBottleNeckDirectRule extends DirectRule { + + public BlackBottleNeckDirectRule() { + super( + "NURI-BASC-0002", + "Black Bottle Neck", + "If there is only one path for a black to escape, then those unknowns must be" + + " black.", + "edu/rpi/legup/images/nurikabe/rules/OneUnknownBlack.png"); + } + + /** + * 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) { + ContradictionRule contraRule = new IsolateBlackContradictionRule(); + + NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); + + NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.BLACK) { + return super.getInvalidUseOfRuleMessage() + + ": Only black cells are allowed for this rule!"; + } + NurikabeBoard modified = origBoardState.copy(); + NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); + modCell.setData(NurikabeType.WHITE.toValue()); + + if (contraRule.checkContradiction(modified) == null) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + + ": This is not the only way for black to escape!"; + } + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java index 641b76322..fe1ffcf91 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java @@ -8,22 +8,22 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; import edu.rpi.legup.puzzle.nurikabe.NurikabeType; - import java.util.ArrayList; import java.util.List; public class BlackOrWhiteCaseRule extends CaseRule { public BlackOrWhiteCaseRule() { - super("NURI-CASE-0001", + super( + "NURI-CASE-0001", "Black or White", "Each blank cell is either black or white.", "edu/rpi/legup/images/nurikabe/cases/BlackOrWhite.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * Checks whether the {@link TreeTransition} logically follows from the parent node using this + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -37,20 +37,24 @@ public String checkRuleRaw(TreeTransition transition) { TreeTransition case1 = childTransitions.get(0); TreeTransition case2 = childTransitions.get(1); - if (case1.getBoard().getModifiedData().size() != 1 || - case2.getBoard().getModifiedData().size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; + if (case1.getBoard().getModifiedData().size() != 1 + || case2.getBoard().getModifiedData().size() != 1) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case."; } NurikabeCell mod1 = (NurikabeCell) case1.getBoard().getModifiedData().iterator().next(); NurikabeCell mod2 = (NurikabeCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; } - if (!((mod1.getType() == NurikabeType.WHITE && mod2.getType() == NurikabeType.BLACK) || - (mod2.getType() == NurikabeType.WHITE && mod1.getType() == NurikabeType.BLACK))) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must an empty white and black cell."; + if (!((mod1.getType() == NurikabeType.WHITE && mod2.getType() == NurikabeType.BLACK) + || (mod2.getType() == NurikabeType.WHITE + && mod1.getType() == NurikabeType.BLACK))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must an empty white and black cell."; } return null; @@ -72,7 +76,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -95,13 +99,13 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackSquareContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackSquareContradictionRule.java index 2dfb817e2..751c88881 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackSquareContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackSquareContradictionRule.java @@ -13,20 +13,21 @@ public class BlackSquareContradictionRule extends ContradictionRule { private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; public BlackSquareContradictionRule() { - super("NURI-CONT-0001", + super( + "NURI-CONT-0001", "Black Square", "There cannot be a 2x2 square of black.", "edu/rpi/legup/images/nurikabe/contradictions/BlackSquare.png"); } - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -39,12 +40,16 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } - for (int x = cell.getLocation().x - 1; x >= 0 && x < cell.getLocation().x + 1 && x < width - 1; x++) { - for (int y = cell.getLocation().y - 1; y >= 0 && y < cell.getLocation().y + 1 && y < height - 1; y++) { - if (nurikabeBoard.getCell(x, y).getType() == NurikabeType.BLACK && - nurikabeBoard.getCell(x + 1, y).getType() == NurikabeType.BLACK && - nurikabeBoard.getCell(x, y + 1).getType() == NurikabeType.BLACK && - nurikabeBoard.getCell(x + 1, y + 1).getType() == NurikabeType.BLACK) { + for (int x = cell.getLocation().x - 1; + x >= 0 && x < cell.getLocation().x + 1 && x < width - 1; + x++) { + for (int y = cell.getLocation().y - 1; + y >= 0 && y < cell.getLocation().y + 1 && y < height - 1; + y++) { + if (nurikabeBoard.getCell(x, y).getType() == NurikabeType.BLACK + && nurikabeBoard.getCell(x + 1, y).getType() == NurikabeType.BLACK + && nurikabeBoard.getCell(x, y + 1).getType() == NurikabeType.BLACK + && nurikabeBoard.getCell(x + 1, y + 1).getType() == NurikabeType.BLACK) { return null; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellDirectRule.java index e86724ed9..f3d3ee8f2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CannotReachCellDirectRule.java @@ -1,61 +1,64 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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; - -public class CannotReachCellDirectRule extends DirectRule { - public CannotReachCellDirectRule() { - super("NURI-BASC-0008", - "Can't Reach Cell", - "A cell must be black if it cannot be reached by any white region", - "edu/rpi/legup/images/nurikabe/rules/Unreachable.png"); - } - - /** - * 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 - protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - ContradictionRule contraRule = new UnreachableWhiteCellContradictionRule(); - - NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); - NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); - if (cell.getType() != NurikabeType.BLACK) { - return super.getInvalidUseOfRuleMessage() + ": Only black cells are allowed for this rule!"; - } - - NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); - NurikabeBoard modified = origBoardState.copy(); - - NurikabeCell modifiedCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); - modifiedCell.setData(NurikabeType.WHITE.toValue()); - if (contraRule.checkContradictionAt(modified, modifiedCell) == null) { - return null; - } - return super.getInvalidUseOfRuleMessage() + ": Cell at this index can be reached"; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; + +public class CannotReachCellDirectRule extends DirectRule { + public CannotReachCellDirectRule() { + super( + "NURI-BASC-0008", + "Can't Reach Cell", + "A cell must be black if it cannot be reached by any white region", + "edu/rpi/legup/images/nurikabe/rules/Unreachable.png"); + } + + /** + * 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 + protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + ContradictionRule contraRule = new UnreachableWhiteCellContradictionRule(); + + NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); + NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); + if (cell.getType() != NurikabeType.BLACK) { + return super.getInvalidUseOfRuleMessage() + + ": Only black cells are allowed for this rule!"; + } + + NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); + NurikabeBoard modified = origBoardState.copy(); + + NurikabeCell modifiedCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); + modifiedCell.setData(NurikabeType.WHITE.toValue()); + if (contraRule.checkContradictionAt(modified, modifiedCell) == null) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Cell at this index can be reached"; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CornerBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CornerBlackDirectRule.java index 55316fcbb..84ec45e99 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CornerBlackDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/CornerBlackDirectRule.java @@ -1,108 +1,133 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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.utility.ConnectedRegions; - -import java.awt.*; -import java.util.Set; - -public class CornerBlackDirectRule extends DirectRule { - - public CornerBlackDirectRule() { - super("NURI-BASC-0003", - "Corners Black", - "If there is only one white square connected to unknowns and one more white is needed then the angles of that white square are black", - "edu/rpi/legup/images/nurikabe/rules/CornerBlack.png"); - } - - /** - * 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) { - NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - NurikabeCell cell = (NurikabeCell) board.getPuzzleElement(puzzleElement); - if (cell.getType() != NurikabeType.BLACK) { - return "Only black cells are allowed for this rule!"; - } - - ContradictionRule tooFewContra = new TooFewSpacesContradictionRule(); - Point cellLocation = cell.getLocation(); - // 1. Find the coordinates of the white space (should be a corner of cell) - for (int i = -1; i < 2; i += 2) { - for (int j = -1; j < 2; j += 2) { - // If the corner does not exist, skip the corner - if (!(cellLocation.x + i >= 0 && cellLocation.x + i < board.getWidth() && cellLocation.y + j >= 0 && cellLocation.y + j < board.getHeight())) { - continue; - } - - NurikabeCell corner = board.getCell(cellLocation.x + i, cellLocation.y + j); - NurikabeType cornerType = corner.getType(); - if (cornerType == NurikabeType.WHITE || cornerType == NurikabeType.NUMBER) { - Point cornerLocation = corner.getLocation(); - // 2. Check if the intersecting adjacent spaces of the white space and the black corner are empty - if (board.getCell(cornerLocation.x, cellLocation.y).getType() == NurikabeType.UNKNOWN && board.getCell(cellLocation.x, cornerLocation.y).getType() == NurikabeType.UNKNOWN) { - // System.out.println("Went inside if statement"); - NurikabeBoard modified = board.copy(); - modified.getCell(cornerLocation.x, cellLocation.y).setData(NurikabeType.BLACK.toValue()); - modified.getCell(cellLocation.x, cornerLocation.y).setData(NurikabeType.BLACK.toValue()); - boolean containsContradiction = tooFewContra.checkContradiction(modified) == null; - if (containsContradiction) { - // 3. Check if the connected region is 1 under what is needed - Set region = ConnectedRegions.getRegionAroundPoint(cornerLocation, NurikabeType.BLACK.toValue(), modified.getIntArray(), modified.getWidth(), modified.getHeight()); - int regionNumber = 0; - // System.out.println("Region set size: " + region.size()); - for (Point p : region) { - NurikabeCell pCell = modified.getCell(p.x, p.y); - if (pCell.getType() == NurikabeType.NUMBER) { - if (regionNumber == 0) { - regionNumber = pCell.getData(); - } - else { - return "There is a MultipleNumbers Contradiction on the board."; - } - } - } - // If the region size is 0, there is a possibility that there was only 1 cell in the white - // region, and that white cell was a NurikabeType.NUMBER cell - if (regionNumber == 0 && corner.getType() == NurikabeType.NUMBER && corner.getData() == 2) { - return null; - } - // If the region size is not 0, make sure the regionNumber and the region size match (need - // to add 1 to account for the cell that was surrounded - if (regionNumber != 0 && region.size() + 1 == regionNumber) { - return null; - } - } - } - } - } - } - return "This is not a valid use of the corner black rule!"; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.utility.ConnectedRegions; +import java.awt.*; +import java.util.Set; + +public class CornerBlackDirectRule extends DirectRule { + + public CornerBlackDirectRule() { + super( + "NURI-BASC-0003", + "Corners Black", + "If there is only one white square connected to unknowns and one more white is" + + " needed then the angles of that white square are black", + "edu/rpi/legup/images/nurikabe/rules/CornerBlack.png"); + } + + /** + * 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) { + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeCell cell = (NurikabeCell) board.getPuzzleElement(puzzleElement); + if (cell.getType() != NurikabeType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + + ContradictionRule tooFewContra = new TooFewSpacesContradictionRule(); + Point cellLocation = cell.getLocation(); + // 1. Find the coordinates of the white space (should be a corner of cell) + for (int i = -1; i < 2; i += 2) { + for (int j = -1; j < 2; j += 2) { + // If the corner does not exist, skip the corner + if (!(cellLocation.x + i >= 0 + && cellLocation.x + i < board.getWidth() + && cellLocation.y + j >= 0 + && cellLocation.y + j < board.getHeight())) { + continue; + } + + NurikabeCell corner = board.getCell(cellLocation.x + i, cellLocation.y + j); + NurikabeType cornerType = corner.getType(); + if (cornerType == NurikabeType.WHITE || cornerType == NurikabeType.NUMBER) { + Point cornerLocation = corner.getLocation(); + // 2. Check if the intersecting adjacent spaces of the white space and the black + // corner + // are empty + if (board.getCell(cornerLocation.x, cellLocation.y).getType() + == NurikabeType.UNKNOWN + && board.getCell(cellLocation.x, cornerLocation.y).getType() + == NurikabeType.UNKNOWN) { + // System.out.println("Went inside if statement"); + NurikabeBoard modified = board.copy(); + modified.getCell(cornerLocation.x, cellLocation.y) + .setData(NurikabeType.BLACK.toValue()); + modified.getCell(cellLocation.x, cornerLocation.y) + .setData(NurikabeType.BLACK.toValue()); + boolean containsContradiction = + tooFewContra.checkContradiction(modified) == null; + if (containsContradiction) { + // 3. Check if the connected region is 1 under what is needed + Set region = + ConnectedRegions.getRegionAroundPoint( + cornerLocation, + NurikabeType.BLACK.toValue(), + modified.getIntArray(), + modified.getWidth(), + modified.getHeight()); + int regionNumber = 0; + // System.out.println("Region set size: " + region.size()); + for (Point p : region) { + NurikabeCell pCell = modified.getCell(p.x, p.y); + if (pCell.getType() == NurikabeType.NUMBER) { + if (regionNumber == 0) { + regionNumber = pCell.getData(); + } else { + return "There is a MultipleNumbers Contradiction on the" + + " board."; + } + } + } + // If the region size is 0, there is a possibility that there was only 1 + // cell in the + // white + // region, and that white cell was a NurikabeType.NUMBER cell + if (regionNumber == 0 + && corner.getType() == NurikabeType.NUMBER + && corner.getData() == 2) { + return null; + } + // If the region size is not 0, make sure the regionNumber and the + // region size match + // (need + // to add 1 to account for the cell that was surrounded + if (regionNumber != 0 && region.size() + 1 == regionNumber) { + return null; + } + } + } + } + } + } + return "This is not a valid use of the corner black rule!"; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinBlackDirectRule.java index aab389e4a..7c4629f65 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinBlackDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinBlackDirectRule.java @@ -1,60 +1,62 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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; - -public class FillinBlackDirectRule extends DirectRule { - - public FillinBlackDirectRule() { - super("NURI-BASC-0004", - "Fill In Black", - "If there an unknown region surrounded by black, it must be black.", - "edu/rpi/legup/images/nurikabe/rules/FillInBlack.png"); - } - - /** - * 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) { - NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoard = (NurikabeBoard) transition.getParents().get(0).getBoard(); - ContradictionRule contraRule = new NoNumberContradictionRule(); - - NurikabeCell cell = (NurikabeCell) board.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.BLACK) { - return "Only black cells are allowed for this rule!"; - } - NurikabeBoard modified = origBoard.copy(); - modified.getPuzzleElement(puzzleElement).setData(NurikabeType.WHITE.toValue()); - if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { - return "Black cells must be placed in a region of black cells!"; - } - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; + +public class FillinBlackDirectRule extends DirectRule { + + public FillinBlackDirectRule() { + super( + "NURI-BASC-0004", + "Fill In Black", + "If there an unknown region surrounded by black, it must be black.", + "edu/rpi/legup/images/nurikabe/rules/FillInBlack.png"); + } + + /** + * 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) { + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoard = (NurikabeBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new NoNumberContradictionRule(); + + NurikabeCell cell = (NurikabeCell) board.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + NurikabeBoard modified = origBoard.copy(); + modified.getPuzzleElement(puzzleElement).setData(NurikabeType.WHITE.toValue()); + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { + return "Black cells must be placed in a region of black cells!"; + } + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinWhiteDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinWhiteDirectRule.java index 4f57602d5..05bb2f046 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinWhiteDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FillinWhiteDirectRule.java @@ -1,60 +1,62 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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; - -public class FillinWhiteDirectRule extends DirectRule { - - public FillinWhiteDirectRule() { - super("NURI-BASC-0005", - "Fill In White", - "If there an unknown region surrounded by white, it must be white.", - "edu/rpi/legup/images/nurikabe/rules/FillInWhite.png"); - } - - /** - * 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) { - NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoard = (NurikabeBoard) transition.getParents().get(0).getBoard(); - ContradictionRule contraRule = new IsolateBlackContradictionRule(); - - NurikabeCell cell = (NurikabeCell) board.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.WHITE) { - return "Only white cells are allowed for this rule!"; - } - NurikabeBoard modified = origBoard.copy(); - modified.getPuzzleElement(puzzleElement).setData(NurikabeType.BLACK.toValue()); - if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { - return "white cells must be placed in a region of white cells!"; - } - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; + +public class FillinWhiteDirectRule extends DirectRule { + + public FillinWhiteDirectRule() { + super( + "NURI-BASC-0005", + "Fill In White", + "If there an unknown region surrounded by white, it must be white.", + "edu/rpi/legup/images/nurikabe/rules/FillInWhite.png"); + } + + /** + * 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) { + NurikabeBoard board = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoard = (NurikabeBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new IsolateBlackContradictionRule(); + + NurikabeCell cell = (NurikabeCell) board.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.WHITE) { + return "Only white cells are allowed for this rule!"; + } + NurikabeBoard modified = origBoard.copy(); + modified.getPuzzleElement(puzzleElement).setData(NurikabeType.BLACK.toValue()); + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { + return "white cells must be placed in a region of white cells!"; + } + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java index c7331330b..f26f0c3fe 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/IsolateBlackContradictionRule.java @@ -8,30 +8,30 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; import edu.rpi.legup.utility.DisjointSets; - import java.util.Set; public class IsolateBlackContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Contradiction applied incorrectly. No isolated Blacks."; + private final String NO_CONTRADICTION_MESSAGE = + "Contradiction applied incorrectly. No isolated Blacks."; private final String INVALID_USE_MESSAGE = "Contradiction must be a black cell"; public IsolateBlackContradictionRule() { - super("NURI-CONT-0003", + super( + "NURI-CONT-0003", "Isolated Black", "There must still be a possibility to connect every Black cell", "edu/rpi/legup/images/nurikabe/contradictions/BlackArea.png"); } /** - * Checks whether the transition has a contradiction at the specific - * {@link PuzzleElement} index using this rule. + * Checks whether the transition has a contradiction at the specific {@link PuzzleElement} index + * using this rule. * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a - * contradiction at the specified {@link PuzzleElement}, - * otherwise return a no contradiction message. + * @return null if the transition contains a contradiction at the specified {@link + * PuzzleElement}, otherwise return a no contradiction message. */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -41,15 +41,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } - DisjointSets blackRegions = NurikabeUtilities.getPossibleBlackRegions(nurikabeBoard); + DisjointSets blackRegions = + NurikabeUtilities.getPossibleBlackRegions(nurikabeBoard); boolean oneRegion = false; for (Set region : blackRegions.getAllSets()) { for (NurikabeCell c : region) { if (c.getType() == NurikabeType.BLACK) { if (oneRegion) { return null; - } - else { + } else { oneRegion = true; break; } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/MultipleNumbersContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/MultipleNumbersContradictionRule.java index e83204b99..1925a68ec 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/MultipleNumbersContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/MultipleNumbersContradictionRule.java @@ -8,28 +8,30 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; import edu.rpi.legup.utility.DisjointSets; - import java.util.Set; public class MultipleNumbersContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Contradiction must be a numbered cell"; public MultipleNumbersContradictionRule() { - super("NURI-CONT-0004", + super( + "NURI-CONT-0004", "Multiple Numbers", "All white regions cannot have more than one number.", "edu/rpi/legup/images/nurikabe/contradictions/MultipleNumbers.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { 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 c2752da7a..6c69136b2 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 @@ -7,31 +7,31 @@ 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.Set; -import java.util.List; public class NoNumberContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Contradiction must be a white cell"; private final String NOT_SURROUNDED_BY_BLACK_MESSAGE = "Must be surrounded by black cells"; public NoNumberContradictionRule() { - super("NURI-CONT-0005", + super( + "NURI-CONT-0005", "No Number", "All enclosed white regions must have a number.", "edu/rpi/legup/images/nurikabe/contradictions/NoNumber.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/PreventBlackSquareDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/PreventBlackSquareDirectRule.java index 2502ee62f..106c22f7a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/PreventBlackSquareDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/PreventBlackSquareDirectRule.java @@ -1,66 +1,67 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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; - -public class PreventBlackSquareDirectRule extends DirectRule { - - public PreventBlackSquareDirectRule() { - super("NURI-BASC-0006", - "Prevent Black Square", - "There cannot be a 2x2 square of black. (3 blacks = fill in last corner white)", - "edu/rpi/legup/images/nurikabe/rules/NoBlackSquare.png"); - } - - /** - * 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) { - ContradictionRule contraRule = new BlackSquareContradictionRule(); - - NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); - - NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.WHITE) { - return "Only white cells are allowed for this rule!"; - } - - NurikabeBoard modified = origBoardState.copy(); - NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); - modCell.setData(NurikabeType.BLACK.toValue()); - - if (contraRule.checkContradiction(modified) == null) { - return null; - } - else { - return "Does not contain a contradiction at this index"; - } - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; + +public class PreventBlackSquareDirectRule extends DirectRule { + + public PreventBlackSquareDirectRule() { + super( + "NURI-BASC-0006", + "Prevent Black Square", + "There cannot be a 2x2 square of black. (3 blacks = fill in last corner white)", + "edu/rpi/legup/images/nurikabe/rules/NoBlackSquare.png"); + } + + /** + * 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) { + ContradictionRule contraRule = new BlackSquareContradictionRule(); + + NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); + + NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.WHITE) { + return "Only white cells are allowed for this rule!"; + } + + NurikabeBoard modified = origBoardState.copy(); + NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); + modCell.setData(NurikabeType.BLACK.toValue()); + + if (contraRule.checkContradiction(modified) == null) { + return null; + } else { + return "Does not contain a contradiction at this index"; + } + } + + /** + * 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; + } +} 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 d992fd22c..8e260cf42 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 @@ -1,95 +1,103 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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.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 { - - public SurroundRegionDirectRule() { - super("NURI-BASC-0007", "Surround Region", - "Surround Region", - "edu/rpi/legup/images/nurikabe/rules/SurroundBlack.png"); - } - - /** - * 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) { - - NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); - - NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.BLACK) { - return "Only black cells are allowed for this rule!"; - } - - NurikabeBoard modified = origBoardState.copy(); - NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); - modCell.setData(NurikabeType.WHITE.toValue()); - - 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"; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +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.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.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class SurroundRegionDirectRule extends DirectRule { + + public SurroundRegionDirectRule() { + super( + "NURI-BASC-0007", + "Surround Region", + "Surround Region", + "edu/rpi/legup/images/nurikabe/rules/SurroundBlack.png"); + } + + /** + * 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) { + + NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); + + NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + + NurikabeBoard modified = origBoardState.copy(); + NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); + modCell.setData(NurikabeType.WHITE.toValue()); + + 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"; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooFewSpacesContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooFewSpacesContradictionRule.java index d01dd806c..e7995165b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooFewSpacesContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooFewSpacesContradictionRule.java @@ -8,28 +8,30 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; import edu.rpi.legup.utility.DisjointSets; - import java.util.Set; public class TooFewSpacesContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Contradiction must be a white or a numbered cell"; public TooFewSpacesContradictionRule() { - super("NURI-CONT-0006", + super( + "NURI-CONT-0006", "Too Few Spaces", "A region cannot contain less spaces than its number.", "edu/rpi/legup/images/nurikabe/contradictions/TooFewSpaces.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -40,7 +42,8 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } - DisjointSets regions = NurikabeUtilities.getPossibleWhiteRegions(nurikabeBoard); + DisjointSets regions = + NurikabeUtilities.getPossibleWhiteRegions(nurikabeBoard); Set whiteRegion = regions.getSet(cell); NurikabeCell numberedCell = null; for (NurikabeCell c : whiteRegion) { @@ -51,7 +54,9 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } if (numberedCell != null && whiteRegion.size() < numberedCell.getData()) { - // System.err.println("Cell Value: " + numberedCell.getData() + ", Loc: " + cell.getLocation() + ", region: " + whiteRegion.size()); + // System.err.println("Cell Value: " + numberedCell.getData() + ", Loc: " + + // cell.getLocation() + // + ", region: " + whiteRegion.size()); return null; } return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooManySpacesContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooManySpacesContradictionRule.java index e5aa9f18b..7bd0883ef 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooManySpacesContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/TooManySpacesContradictionRule.java @@ -8,29 +8,31 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; import edu.rpi.legup.utility.DisjointSets; - import java.util.ArrayList; import java.util.Set; public class TooManySpacesContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Contradiction must be a white or a numbered cell"; public TooManySpacesContradictionRule() { - super("NURI-CONT-0007", + super( + "NURI-CONT-0007", "Too Many Spaces", "A region cannot contain more spaces than its number.", "edu/rpi/legup/images/nurikabe/contradictions/TooManySpaces.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java index ec16475f1..c578d317f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/UnreachableWhiteCellContradictionRule.java @@ -7,7 +7,6 @@ import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; - import java.awt.*; import java.util.*; @@ -17,19 +16,21 @@ public class UnreachableWhiteCellContradictionRule extends ContradictionRule { private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; public UnreachableWhiteCellContradictionRule() { - super("NURI-CONT-0002", + super( + "NURI-CONT-0002", "Unreachable White Cell", "A white cell must be able to reach a white region", "edu/rpi/legup/images/nurikabe/contradictions/CantReach.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -44,7 +45,8 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int width = nurikabeBoard.getWidth(); // Get regions - HashMap whiteRegionMap = NurikabeUtilities.getWhiteRegionMap(nurikabeBoard); + HashMap whiteRegionMap = + NurikabeUtilities.getWhiteRegionMap(nurikabeBoard); if (whiteRegionMap.containsKey(cell)) { return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } @@ -83,15 +85,17 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { for (NurikabeCell n : adj) { int regionNeed = whiteRegionMap.getOrDefault(n, -1); if (pathLength <= regionNeed) { - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + return super.getNoContradictionMessage() + + ": " + + this.NO_CONTRADICTION_MESSAGE; } } } for (NurikabeCell n : adj) { if (!visited.getOrDefault(n, false) - && (n.getType() == NurikabeType.UNKNOWN || - n.getType() == NurikabeType.WHITE)) { + && (n.getType() == NurikabeType.UNKNOWN + || n.getType() == NurikabeType.WHITE)) { visited.put(n, true); queue.add(n); } diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/WhiteBottleNeckDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/WhiteBottleNeckDirectRule.java index dd337858c..434e7ccde 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/WhiteBottleNeckDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/WhiteBottleNeckDirectRule.java @@ -1,69 +1,72 @@ -package edu.rpi.legup.puzzle.nurikabe.rules; - -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 java.util.LinkedHashSet; -import java.util.Set; - -public class WhiteBottleNeckDirectRule extends DirectRule { - - public WhiteBottleNeckDirectRule() { - super("NURI-BASC-0009", - "White Bottle Neck", - "If a region needs more whites and there is only one path for the region to expand, then those unknowns must be white.", "edu/rpi/legup/images/nurikabe/rules/OneUnknownWhite.png"); - } - - /** - * 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) { - Set contras = new LinkedHashSet<>(); - contras.add(new NoNumberContradictionRule()); - contras.add(new TooFewSpacesContradictionRule()); - - NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); - NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); - - NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); - - if (cell.getType() != NurikabeType.WHITE) { - return "Only white cells are allowed for this rule!"; - } - NurikabeBoard modified = origBoardState.copy(); - NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); - modCell.setData(NurikabeType.BLACK.toValue()); - - for (ContradictionRule contraRule : contras) { - if (contraRule.checkContradiction(modified) == null) { - return null; - } - } - return "This is not the only way for white to escape!"; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.nurikabe.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +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.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import java.util.LinkedHashSet; +import java.util.Set; + +public class WhiteBottleNeckDirectRule extends DirectRule { + + public WhiteBottleNeckDirectRule() { + super( + "NURI-BASC-0009", + "White Bottle Neck", + "If a region needs more whites and there is only one path for the region to expand," + + " then those unknowns must be white.", + "edu/rpi/legup/images/nurikabe/rules/OneUnknownWhite.png"); + } + + /** + * 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) { + Set contras = new LinkedHashSet<>(); + contras.add(new NoNumberContradictionRule()); + contras.add(new TooFewSpacesContradictionRule()); + + NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); + NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard(); + + NurikabeCell cell = (NurikabeCell) destBoardState.getPuzzleElement(puzzleElement); + + if (cell.getType() != NurikabeType.WHITE) { + return "Only white cells are allowed for this rule!"; + } + NurikabeBoard modified = origBoardState.copy(); + NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement); + modCell.setData(NurikabeType.BLACK.toValue()); + + for (ContradictionRule contraRule : contras) { + if (contraRule.checkContradiction(modified) == null) { + return null; + } + } + return "This is not the only way for white to escape!"; + } + + /** + * 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; + } +} 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 e8f9ffc0d..8b0b8e6d5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTable.java @@ -18,9 +18,7 @@ public ShortTruthTable() { this.factory = new ShortTruthTableCellFactory(); } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) currentBoard; @@ -44,8 +42,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Short Truth Table * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Short Truth Table, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -100,7 +98,6 @@ public boolean isBoardComplete(Board board) { } } return true; - } /** @@ -109,7 +106,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } -} \ No newline at end of file + public void onBoardChange(Board board) {} +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableBoard.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableBoard.java index e5011182a..519a61974 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableBoard.java @@ -1,17 +1,12 @@ package edu.rpi.legup.puzzle.shorttruthtable; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.shorttruthtable.*; - import java.awt.*; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.HashSet; public class ShortTruthTableBoard extends GridBoard { @@ -22,10 +17,8 @@ public ShortTruthTableBoard(int width, int height, ShortTruthTableStatement[] st super(width, height); this.statements = statements; - } - public Set getCellsWithSymbol(char symbol) { Set cells = new HashSet(); for (int x = 0; x < this.dimension.width; x++) { @@ -48,23 +41,23 @@ public ShortTruthTableCell getCell(int x, int y) { return (ShortTruthTableCell) super.getCell(x, y); } - @Override public ShortTruthTableBoard copy() { - //Copy the statements - ShortTruthTableStatement[] statementsCopy = new ShortTruthTableStatement[this.statements.length]; + // Copy the statements + ShortTruthTableStatement[] statementsCopy = + new ShortTruthTableStatement[this.statements.length]; for (int i = 0; i < statements.length; i++) { statementsCopy[i] = this.statements[i].copy(); } - //copy the board and set the cells - ShortTruthTableBoard boardCopy = new ShortTruthTableBoard(getWidth(), getHeight(), statementsCopy); + // copy the board and set the cells + ShortTruthTableBoard boardCopy = + new ShortTruthTableBoard(getWidth(), getHeight(), statementsCopy); for (int r = 0; r < this.dimension.height; r++) { for (int c = 0; c < this.dimension.width; c++) { if (r % 2 == 0 && c < statementsCopy[r / 2].getLength()) { boardCopy.setCell(c, r, statementsCopy[r / 2].getCell(c)); - } - else { + } else { boardCopy.setCell(c, r, getCell(c, r).copy()); } } @@ -76,14 +69,14 @@ public ShortTruthTableBoard copy() { System.out.println("original:\n" + this); System.out.println("copy:\n" + boardCopy); return boardCopy; - } public ShortTruthTableStatement[] getStatements() { return statements; } - public static List copyStatementList(List statements) { + public static List copyStatementList( + List statements) { List copy = new ArrayList(); for (int i = 0; i < statements.size(); i++) { copy.add(statements.get(i).copy()); @@ -91,13 +84,12 @@ public static List copyStatementList(List { - //The symbol on the cell + // The symbol on the cell private char symbol; - //This is a reference to the statement that contains this cell + // This is a reference to the statement that contains this cell private ShortTruthTableStatement statement; - //Constructors + // Constructors public ShortTruthTableCell(char symbol, ShortTruthTableCellType cellType, Point location) { super(cellType, location); this.symbol = symbol; } - /** * Constructs a new NOT_IN_PLAY Cell * @@ -33,8 +29,7 @@ public ShortTruthTableCell(Point location) { this(' ', ShortTruthTableCellType.NOT_IN_PLAY, location); } - - //Getters + // Getters public ShortTruthTableStatement getStatementReference() { return statement; @@ -65,12 +60,12 @@ public int getY() { return (int) location.getY(); } - public boolean isAssigned() { - return getType() == ShortTruthTableCellType.TRUE || getType() == ShortTruthTableCellType.FALSE; + return getType() == ShortTruthTableCellType.TRUE + || getType() == ShortTruthTableCellType.FALSE; } - //Setters + // Setters void setStatementReference(ShortTruthTableStatement statement) { this.statement = statement; @@ -86,7 +81,7 @@ public void setGiven(ShortTruthTableCellType type) { setGiven(true); } - //Modifiers + // Modifiers public void cycleTypeForward() { switch (data) { @@ -109,16 +104,14 @@ public void cycleTypeBackward() { cycleTypeForward(); } - - //TO STRING + // TO STRING @Override public String toString() { return String.format("STTCell: %c %2d %-11s %s", symbol, index, data, location.toString()); } - - //Copy function + // Copy function @Override public ShortTruthTableCell copy() { @@ -168,8 +161,7 @@ public void setType(Element e, MouseEvent m) { if (this.symbol > 'Z') { this.symbol = 'A'; } - } - else { + } else { if (m.getButton() == MouseEvent.BUTTON3) { this.symbol -= 1; if (this.symbol < 'A') { @@ -184,37 +176,30 @@ public void setType(Element e, MouseEvent m) { if (m.getButton() == MouseEvent.BUTTON1) { if (this.symbol == '^') { this.symbol = '|'; - } - else { + } else { if (this.symbol == '|') { this.symbol = '>'; - } - else { + } else { if (this.symbol == '>') { this.symbol = '-'; - } - else { + } else { if (this.symbol == '-') { this.symbol = '^'; } } } } - } - else { + } else { if (m.getButton() == MouseEvent.BUTTON3) { if (this.symbol == '^') { this.symbol = '-'; - } - else { + } else { if (this.symbol == '|') { this.symbol = '^'; - } - else { + } else { if (this.symbol == '>') { this.symbol = '|'; - } - else { + } else { if (this.symbol == '-') { this.symbol = '>'; } @@ -229,4 +214,4 @@ public void setType(Element e, MouseEvent m) { } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellFactory.java index 99d626447..df74afdc9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellFactory.java @@ -4,57 +4,58 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class ShortTruthTableCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid */ @Override - public ShortTruthTableCell importCell(Node node, Board board) throws InvalidFileFormatException { + public ShortTruthTableCell importCell(Node node, Board board) + throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("ShortTruthTable Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "ShortTruthTable Factory: unknown puzzleElement puzzleElement"); } ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board; - //get the attributes for the cell + // get the attributes for the cell NamedNodeMap attributeList = node.getAttributes(); int rowIndex = Integer.valueOf(attributeList.getNamedItem("row_index").getNodeValue()); - int charIndex = Integer.valueOf(attributeList.getNamedItem("char_index").getNodeValue()); + int charIndex = + Integer.valueOf(attributeList.getNamedItem("char_index").getNodeValue()); String cellType = attributeList.getNamedItem("type").getNodeValue(); - //modify the appropriate cell - ShortTruthTableCell cell = (ShortTruthTableCell) sttBoard.getCell(charIndex, rowIndex * 2); + // modify the appropriate cell + ShortTruthTableCell cell = + (ShortTruthTableCell) sttBoard.getCell(charIndex, rowIndex * 2); cell.setData(ShortTruthTableCellType.valueOf(cellType)); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("nurikabe Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "nurikabe Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("nurikabe Factory: could not find attribute(s)"); } - } /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ 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 c997faf5f..d5a8292a2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCellType.java @@ -1,11 +1,14 @@ package edu.rpi.legup.puzzle.shorttruthtable; -import java.util.Map; import java.util.HashMap; +import java.util.Map; public enum ShortTruthTableCellType { - - FALSE(0), TRUE(1), UNKNOWN(-1), NOT_IN_PLAY(-2), PARENTHESIS(-3); + FALSE(0), + TRUE(1), + UNKNOWN(-1), + NOT_IN_PLAY(-2), + PARENTHESIS(-3); public int value; private static Map map = new HashMap<>(); @@ -37,7 +40,6 @@ public static char toChar(ShortTruthTableCellType type) { return ' '; } - /** * Returns true if this cell holds the value either TRUE or FALSE * @@ -47,7 +49,6 @@ public boolean isTrueOrFalse() { return value == 0 || value == 1; } - public ShortTruthTableCellType getNegation() { switch (value) { case 1: @@ -63,5 +64,4 @@ public static ShortTruthTableCellType getDefaultType(char c) { if (c == '(' || c == ')') return PARENTHESIS; return UNKNOWN; } - -} \ No newline at end of file +} 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 bddde44a5..172867f5a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableController.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableController.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class ShortTruthTableController extends ElementController { @@ -17,13 +16,16 @@ public void changeCell(MouseEvent e, PuzzleElement data) { if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); - } - else { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { cell.cycleTypeForward(); } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { cell.cycleTypeBackward(); } diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableElementView.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableElementView.java index b787921ad..43e1d2299 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableElementView.java @@ -1,20 +1,19 @@ package edu.rpi.legup.puzzle.shorttruthtable; -import edu.rpi.legup.ui.boardview.GridElementView; import edu.rpi.legup.app.LegupPreferences; - +import edu.rpi.legup.ui.boardview.GridElementView; import java.awt.*; public class ShortTruthTableElementView extends GridElementView { - //Font + // Font private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); private static final Color FONT_COLOR = Color.BLACK; - //Square Colors - private static final Color TRUE_COLOR = new Color(0, 130, 0);//green + // Square Colors + private static final Color TRUE_COLOR = new Color(0, 130, 0); // green private static final Color TRUE_COLOR_COLORBLIND = new Color(0, 0, 255); - private static final Color FALSE_COLOR = new Color(200, 0, 0);//red + private static final Color FALSE_COLOR = new Color(200, 0, 0); // red private static final Color FALSE_COLOR_COLORBLIND = new Color(255, 0, 0); private static final Color UNKNOWN_COLOR = Color.WHITE; @@ -23,7 +22,6 @@ public ShortTruthTableElementView(ShortTruthTableCell cell) { super(cell); } - /** * Gets the PuzzleElement associated with this view * @@ -37,14 +35,14 @@ public ShortTruthTableCell getPuzzleElement() { @Override public void drawElement(Graphics2D graphics2D) { - //get information about the cell + // get information about the cell ShortTruthTableCell cell = (ShortTruthTableCell) puzzleElement; ShortTruthTableCellType type = cell.getData(); - //do not draw the cell if it is not in play + // do not draw the cell if it is not in play if (type == ShortTruthTableCellType.NOT_IN_PLAY) return; - //fill in background color of the cell + // fill in background color of the cell graphics2D.setStroke(new BasicStroke(1)); LegupPreferences prefs = LegupPreferences.getInstance(); switch (type) { @@ -68,15 +66,14 @@ public void drawElement(Graphics2D graphics2D) { } graphics2D.fillRect(location.x, location.y, size.width, size.height); - //Draw the symbol on the cell + // Draw the symbol on the cell graphics2D.setColor(FONT_COLOR); graphics2D.setFont(FONT); FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(cell.getSymbol()); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); - graphics2D.drawString(ShortTruthTableOperation.getLogicSymbol(cell.getSymbol()), xText, yText); - - + graphics2D.drawString( + ShortTruthTableOperation.getLogicSymbol(cell.getSymbol()), xText, yText); } } 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 9d6553c7c..0914c159a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableExporter.java @@ -2,7 +2,6 @@ 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 { @@ -16,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { ShortTruthTableBoard board; if (puzzle.getTree() != null) { board = (ShortTruthTableBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (ShortTruthTableBoard) puzzle.getBoardView().getBoard(); } @@ -33,12 +31,12 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { dataElement.appendChild(statementElement); } - for (PuzzleElement puzzleElement : board.getPuzzleElements()) { ShortTruthTableCell cell = board.getCellFromElement(puzzleElement); if (!cell.getType().isTrueOrFalse()) continue; - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); dataElement.appendChild(cellElement); } 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 84d04fb45..396668380 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableImporter.java @@ -2,16 +2,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 javax.swing.*; import java.awt.*; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import javax.swing.*; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; class ShortTruthTableImporter extends PuzzleImporter { @@ -19,9 +18,9 @@ public ShortTruthTableImporter(ShortTruthTable stt) { super(stt); } - /** - * Parse a string into all the cells, the y position of the statement is passed so the y position can be set + * Parse a string into all the cells, the y position of the statement is passed so the y + * position can be set * * @param statement * @param y @@ -29,83 +28,90 @@ public ShortTruthTableImporter(ShortTruthTable stt) { */ private List getCells(String statement, int y) { List cells = new ArrayList(); - //go through each char in the statement and make a cell for it + // go through each char in the statement and make a cell for it for (int i = 0; i < statement.length(); i++) { char c = statement.charAt(i); - ShortTruthTableCell cell = new ShortTruthTableCell(c, ShortTruthTableCellType.getDefaultType(c), new Point(i, y)); - //it is modifiable if the type is unknown + ShortTruthTableCell cell = + new ShortTruthTableCell( + c, ShortTruthTableCellType.getDefaultType(c), new Point(i, y)); + // it is modifiable if the type is unknown cell.setModifiable(cell.getType() == ShortTruthTableCellType.UNKNOWN); cells.add(cell); } return cells; } - /** - * Parses the statementData into all the cells (with symbols) and statements for the - * puzzle. All cells are set to UNKNWON, it their value is given, it will be set later - * in the import process. - *

- * Both allCells and statements act as returns, They should be passed as empty arrays + * Parses the statementData into all the cells (with symbols) and statements for the puzzle. All + * cells are set to UNKNWON, it their value is given, it will be set later in the import + * process. + * + *

Both allCells and statements act as returns, They should be passed as empty arrays * * @param statementData The data to be imported - * @param allCells returns all the cells as a jagged 2d array - * @param statements returns all the statements + * @param allCells returns all the cells as a jagged 2d array + * @param statements returns all the statements * @return the length, in chars, of the longest statement */ - private int parseAllStatementsAndCells(final NodeList statementData, - List> allCells, - List statements) throws InvalidFileFormatException { + private int parseAllStatementsAndCells( + final NodeList statementData, + List> allCells, + List statements) + throws InvalidFileFormatException { int maxStatementLength = 0; - //get a 2D arraylist of all the cells + // get a 2D arraylist of all the cells for (int i = 0; i < statementData.getLength(); i++) { - //Get the atributes from the statement i in the file + // 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 + System.out.println( + "ROW INDEX: " + attributeList.getNamedItem("row_index").getNodeValue()); + // parser time (on statementRep) + // if (!validGrammar(statementRep)) throw some error if (!validGrammar(statementRep)) { JOptionPane.showMessageDialog(null, "ERROR: Invalid file syntax"); - throw new InvalidFileFormatException("shorttruthtable importer: invalid sentence syntax"); + throw new InvalidFileFormatException( + "shorttruthtable importer: invalid sentence syntax"); } int rowIndex = Integer.valueOf(attributeList.getNamedItem("row_index").getNodeValue()); - //get the cells for the statement + // get the cells for the statement List rowOfCells = getCells(statementRep, rowIndex * 2); allCells.add(rowOfCells); statements.add(new ShortTruthTableStatement(statementRep, rowOfCells)); - //keep track of the length of the longest statement + // keep track of the length of the longest statement maxStatementLength = Math.max(maxStatementLength, statementRep.length()); - } return maxStatementLength; } - private int parseAllStatementsAndCells(String[] statementData, - List> allCells, - List statements) throws IllegalArgumentException { + 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"); + throw new IllegalArgumentException( + "shorttruthtable importer: invalid sentence syntax"); } - //get the cells for the statement + // 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 + // keep track of the length of the longest statement maxStatementLength = Math.max(maxStatementLength, statementData[i].length()); } @@ -115,7 +121,7 @@ private int parseAllStatementsAndCells(String[] statementData, protected boolean validGrammar(String sentence) { int open = 0; int close = 0; - char[] valid_characters = new char[]{'^', 'v', '!', '>', '-', '&', '|', '~', '$', '%'}; + char[] valid_characters = new char[] {'^', 'v', '!', '>', '-', '&', '|', '~', '$', '%'}; for (int i = 0; i < sentence.length(); i++) { char s = sentence.charAt(i); if (s == '(' || s == ')') { @@ -155,8 +161,7 @@ protected boolean validGrammar(String sentence) { } } } - } - else { + } else { if (i != sentence.length() - 1) { if (Character.isLetter(sentence.charAt(i + 1))) { System.out.println("Invalid next character"); @@ -170,75 +175,81 @@ protected boolean validGrammar(String sentence) { return open == close; } - private ShortTruthTableBoard generateBoard(List> allCells, - List statements, - int width) { + private ShortTruthTableBoard generateBoard( + List> allCells, + List statements, + int width) { - //calculate the height for the board + // calculate the height for the board int height = statements.size() * 2 - 1; - //instantiate the board with the correct width and height - ShortTruthTableBoard sttBoard = new ShortTruthTableBoard(width, height, - statements.toArray(new ShortTruthTableStatement[statements.size()])); + // instantiate the board with the correct width and height + ShortTruthTableBoard sttBoard = + new ShortTruthTableBoard( + width, + height, + statements.toArray(new ShortTruthTableStatement[statements.size()])); - //set the cells in the board. create not_in_play cells where needed + // set the cells in the board. create not_in_play cells where needed for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - //get the statement index for this row of the table + // get the statement index for this row of the table int statementIndex = y / 2; - //get the cell at this location; or create a not_in_play one if necessary + // get the cell at this location; or create a not_in_play one if necessary ShortTruthTableCell cell = null; - //for a cell to exist at (x, y), it must be a valid row and within the statement length + // for a cell to exist at (x, y), it must be a valid row and within the statement + // length if (y % 2 == 0 && x < statements.get(statementIndex).getLength()) { cell = allCells.get(statementIndex).get(x); - System.out.println("Importer: check cell statement ref: " + cell.getStatementReference()); - } - else { - //if it is not a valid cell space, add a NOT_IN_PLAY cell - cell = new ShortTruthTableCell(' ', ShortTruthTableCellType.NOT_IN_PLAY, new Point(x, y)); + System.out.println( + "Importer: check cell statement ref: " + cell.getStatementReference()); + } else { + // if it is not a valid cell space, add a NOT_IN_PLAY cell + cell = + new ShortTruthTableCell( + ' ', ShortTruthTableCellType.NOT_IN_PLAY, new Point(x, y)); cell.setModifiable(false); } - //add the cell to the table + // add the cell to the table cell.setIndex(y * width + x); sttBoard.setCell(x, y, cell); } } return sttBoard; - } + private void setGivenCells( + ShortTruthTableBoard sttBoard, + Element dataElement, + NodeList cellData, + List statements) + throws InvalidFileFormatException { - private void setGivenCells(ShortTruthTableBoard sttBoard, - Element dataElement, - NodeList cellData, - List statements) throws InvalidFileFormatException { - - - //if it is normal, set all predicates to true and the conclusion to false + // if it is normal, set all predicates to true and the conclusion to false if (dataElement.getAttribute("normal").equalsIgnoreCase("true")) { - //set all predicates to true (all but the last one) + // set all predicates to true (all but the last one) for (int i = 0; i < statements.size() - 1; i++) { statements.get(i).getCell().setGiven(ShortTruthTableCellType.TRUE); } - //set the conclusion to false (the last one) + // set the conclusion to false (the last one) statements.get(statements.size() - 1).getCell().setGiven(ShortTruthTableCellType.FALSE); } - //set the given cell values + // set the given cell values for (int i = 0; i < cellData.getLength(); i++) { - //set the value with the factory importer - ShortTruthTableCell cell = (ShortTruthTableCell) puzzle.getFactory().importCell(cellData.item(i), sttBoard); - //set the modifiable and given flags + // set the value with the factory importer + ShortTruthTableCell cell = + (ShortTruthTableCell) + puzzle.getFactory().importCell(cellData.item(i), sttBoard); + // set the modifiable and given flags cell.setModifiable(false); cell.setGiven(true); } - - } @Override @@ -254,16 +265,14 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException */ @Override - public void initializeBoard(int rows, int columns) { - - } + public void initializeBoard(int rows, int columns) {} - //STATEMENT IMPORTER + // STATEMENT IMPORTER /** * Creates the board for building @@ -276,41 +285,42 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { try { - //Check File formatting + // Check File formatting if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("short truth table Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "short truth table Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("data").getLength() == 0) { - throw new InvalidFileFormatException("short truth table Importer: no statements found for board"); + throw new InvalidFileFormatException( + "short truth table Importer: no statements found for board"); } - - //get all the cells in a 2D arraylist + // get all the cells in a 2D arraylist List> allCells = new ArrayList>(); - //store the statement data structors + // store the statement data structors List statements = new ArrayList(); - //get the elements from the file + // get the elements from the file Element dataElement = (Element) boardElement.getElementsByTagName("data").item(0); NodeList statementData = dataElement.getElementsByTagName("statement"); NodeList cellData = dataElement.getElementsByTagName("cell"); + // Parse the data + int maxStatementLength = + parseAllStatementsAndCells(statementData, allCells, statements); - //Parse the data - int maxStatementLength = parseAllStatementsAndCells(statementData, allCells, statements); - - //generate the board + // generate the board ShortTruthTableBoard sttBoard = generateBoard(allCells, statements, maxStatementLength); - //set the given cell values + // set the given cell values setGivenCells(sttBoard, dataElement, cellData, statements); puzzle.setCurrentBoard(sttBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("short truth table Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "short truth table Importer: unknown value where integer expected"); } } @@ -321,7 +331,8 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { * @throws UnsupportedOperationException * @throws IllegalArgumentException */ - public void initializeBoard(String[] statementInput) throws UnsupportedOperationException, IllegalArgumentException { + public void initializeBoard(String[] statementInput) + throws UnsupportedOperationException, IllegalArgumentException { List statementsList = new LinkedList<>(); for (String s : statementInput) { if (s.strip().length() > 0) { @@ -331,7 +342,8 @@ public void initializeBoard(String[] statementInput) throws UnsupportedOperation String[] statementData = statementsList.toArray(new String[statementsList.size()]); if (statementData.length == 0) { - throw new IllegalArgumentException("short truth table Importer: no statements found for board"); + throw new IllegalArgumentException( + "short truth table Importer: no statements found for board"); } // Store all cells and statements 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 bc713d407..8284474da 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableOperation.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableOperation.java @@ -1,57 +1,48 @@ -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; + +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/ShortTruthTableStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableStatement.java index e40a10cf0..2059e3ca5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableStatement.java @@ -1,149 +1,143 @@ package edu.rpi.legup.puzzle.shorttruthtable; - import edu.rpi.legup.model.gameboard.PuzzleElement; - -import java.util.Set; -import java.util.HashSet; import java.awt.Point; -import java.util.List; import java.util.ArrayList; - +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class ShortTruthTableStatement extends PuzzleElement { - //the cell that this statement holds + // the cell that this statement holds private final ShortTruthTableCell cell; - //child nodes of the tree + // child nodes of the tree private final ShortTruthTableStatement parentStatement; private final ShortTruthTableStatement leftStatement; private final ShortTruthTableStatement rightStatement; - //the representation string for this statement + // the representation string for this statement private final String stringRep; private final List cells; - - //constructor for root statement, sets parent to null + // constructor for root statement, sets parent to null public ShortTruthTableStatement(String statement, List cells) { this(statement, null, cells); } - //recursive constructor; constructs child statement nodes if necessary - private ShortTruthTableStatement(String statement, ShortTruthTableStatement parent, List cells) { + // recursive constructor; constructs child statement nodes if necessary + private ShortTruthTableStatement( + String statement, ShortTruthTableStatement parent, List cells) { this.parentStatement = parent; - //set the string rep to the statement (include parens in case this is a sub statement) + // set the string rep to the statement (include parens in case this is a sub statement) this.stringRep = statement; this.cells = new ArrayList(cells); - //remove the parens for parsing the statement + // remove the parens for parsing the statement statement = removeParens(statement); removeParens(cells); - //get the index of the char that this statement represents + // get the index of the char that this statement represents int index = parse(statement); - //construct the cell for this node in the tree + // construct the cell for this node in the tree cell = cells.get(index); - //give the cell a reference back to this statement + // give the cell a reference back to this statement cell.setStatementReference(this); - //get the strings on either side of this char in the string rep + // get the strings on either side of this char in the string rep String left = statement.substring(0, index); String right = statement.substring(index + 1); - List leftCells = new ArrayList(cells.subList(0, index)); - List rightCells = new ArrayList(cells.subList(index + 1, cells.size())); + List leftCells = + new ArrayList(cells.subList(0, index)); + List rightCells = + new ArrayList(cells.subList(index + 1, cells.size())); - //construct sub-statements if necessary + // construct sub-statements if necessary if (left.length() > 0) { leftStatement = new ShortTruthTableStatement(left, this, leftCells); - } - else { + } else { leftStatement = null; } if (right.length() > 0) { rightStatement = new ShortTruthTableStatement(right, this, rightCells); - } - else { + } else { rightStatement = null; } - } - - //parsing for the constructor + // parsing for the constructor static String removeParens(String statement) { if (statement.charAt(0) != '(') { return statement; } - //if the statement does start with a paren, check that it matches with the last paren + // if the statement does start with a paren, check that it matches with the last paren int openParenCount = 1; int i = 1; while (i < statement.length() - 1) { char c = statement.charAt(i); if (c == '(') { openParenCount++; - } - else { + } else { if (c == ')') openParenCount--; } - //if the first paren has been closed, and it is not the end of the string, - //then there is no whole statement parens to remove + // if the first paren has been closed, and it is not the end of the string, + // then there is no whole statement parens to remove if (openParenCount == 0 && i != statement.length() - 1) { return statement; } i++; } - //if the while loop made it through the entire statement, there are parens around the whole thing + // if the while loop made it through the entire statement, there are parens around the whole + // thing return statement.substring(1, statement.length() - 1); - } int parse(String statement) { - - //Split by and, or, CONDITIONAL, or biconditional - //keep track of the parens, it must be equal to zero to split + // Split by and, or, CONDITIONAL, or biconditional + // keep track of the parens, it must be equal to zero to split int openParenCount = 0; - //index for stepping through the string + // index for stepping through the string int i = 0; - //step through each char in the statement + // step through each char in the statement while (i < statement.length()) { - //get the char + // get the char char c = statement.charAt(i); - //keep track of the open parens + // keep track of the open parens if (c == '(') { openParenCount++; - } - else { + } else { if (c == ')') { openParenCount--; } - //if the char is an operator, and there are no open parens, split the statement here + // if the char is an operator, and there are no open parens, split the statement + // here else { - if (openParenCount == 0 && ShortTruthTableOperation.isOperation(c) && c != ShortTruthTableOperation.NOT) { + if (openParenCount == 0 + && ShortTruthTableOperation.isOperation(c) + && c != ShortTruthTableOperation.NOT) { return i; } } } - //increment the index + // increment the index i++; } - //if it made it through the while loop: - //this is an atomic statement or a negation - //either way, the important char is the first character in the string + // if it made it through the while loop: + // this is an atomic statement or a negation + // either way, the important char is the first character in the string return 0; - } static void removeParens(List cells) { @@ -152,20 +146,19 @@ static void removeParens(List cells) { return; } - //if the statement does start with a paren, check that it matches with the last paren + // if the statement does start with a paren, check that it matches with the last paren int openParenCount = 1; int i = 1; while (i < cells.size() - 1) { char c = cells.get(i).getSymbol(); if (c == '(') { openParenCount++; - } - else { + } else { if (c == ')') openParenCount--; } - //if the first paren has been closed, and it is not the end of the string, - //then there is no whole statement parens to remove + // if the first paren has been closed, and it is not the end of the string, + // then there is no whole statement parens to remove if (openParenCount == 0 && i != cells.size() - 1) { return; } @@ -173,15 +166,13 @@ static void removeParens(List cells) { i++; } - //if the while loop made it through the entire statement, there are parens around the whole thing + // if the while loop made it through the entire statement, there are parens around the whole + // thing cells.remove(cells.size() - 1); cells.remove(0); - } - - //Getters - + // Getters public ShortTruthTableCell getCell() { return cell; @@ -220,15 +211,15 @@ public int getLength() { return stringRep.length(); } - public ShortTruthTableCell getCell(int i) { return cells.get(i); } + // Getters (recursive) - //Getters (recursive) - - //returns all cells in this statement with the symbol 'symbol'; runs recursively on both sides of the tree + // returns all cells in this statement with the symbol 'symbol'; runs recursively on both sides + // of + // the tree public Set getCellsWithSymbol(char symbol) { Set set = new HashSet(getLength()); if (cell.getSymbol() == symbol) set.add(cell); @@ -238,38 +229,37 @@ public Set getCellsWithSymbol(char symbol) { } /** - * Returns an array of three elements where [0] is the left - * statement type, [1] is this statement type, and [2] is the - * right statement type. null means either the statement doesn't + * Returns an array of three elements where [0] is the left statement type, [1] is this + * statement type, and [2] is the right statement type. null means either the statement doesn't * exist or is an unknown value. * * @return the assigned values to this statement and its sub-statements */ public ShortTruthTableCellType[] getCellTypePattern() { - //get this type and the right type, they will always be used + // get this type and the right type, they will always be used ShortTruthTableCellType type = this.cell.getType(); System.out.println("Right statement: " + rightStatement.getCell()); ShortTruthTableCellType rightType = this.rightStatement.getCell().getType(); System.out.println("Right type: " + rightType); - //if this is a not statement, there is no left side + // if this is a not statement, there is no left side if (cell.getSymbol() == ShortTruthTableOperation.NOT) { - return new ShortTruthTableCellType[]{null, type, rightType}; + return new ShortTruthTableCellType[] {null, type, rightType}; } - //if it is any other operation, get the left side too and return it + // if it is any other operation, get the left side too and return it ShortTruthTableCellType leftType = this.leftStatement.getCell().getType(); - return new ShortTruthTableCellType[]{leftType, type, rightType}; + return new ShortTruthTableCellType[] {leftType, type, rightType}; } - //Setters + // Setters private void setCellLocations(int rowIndex, int offset) { - //set the location of this cell + // set the location of this cell int xLoc = offset; if (leftStatement != null) { xLoc += leftStatement.getLength(); } cell.setLocation(new Point(xLoc, rowIndex)); - //recurse on both sides of the tree + // recurse on both sides of the tree if (leftStatement != null) { leftStatement.setCellLocations(rowIndex, offset); } @@ -282,32 +272,29 @@ public void setCellLocations(int rowIndex) { setCellLocations(rowIndex, 0); } - public ShortTruthTableStatement copy() { - //copy all the cells + // copy all the cells List cellsCopy = new ArrayList<>(); for (ShortTruthTableCell c : cells) { cellsCopy.add(c.copy()); } - //make a copy of the statement with all the copied cells - //return the new statement + // make a copy of the statement with all the copied cells + // return the new statement return new ShortTruthTableStatement(stringRep, cellsCopy); } public ShortTruthTableStatement replace(int column, ShortTruthTableCell cell) { - //copy all the cells (replacing one) + // copy all the cells (replacing one) List cellsCopy = new ArrayList<>(); for (ShortTruthTableCell c : cells) { if (c.getX() == column) { cellsCopy.add(cell); - } - else { + } else { cellsCopy.add(c); } } - //make a copy of the statement with all the copied cells - //return the new statement + // make a copy of the statement with all the copied cells + // return the new statement return new ShortTruthTableStatement(stringRep, cellsCopy); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableView.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableView.java index 64e9fda75..15becf443 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableView.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.ui.boardview.GridBoardView; - import java.awt.*; public class ShortTruthTableView extends GridBoardView { @@ -13,12 +12,13 @@ public ShortTruthTableView(ShortTruthTableBoard board) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { ShortTruthTableCell cell = (ShortTruthTableCell) puzzleElement; -// System.out.println("STTView :"+cell); + // System.out.println("STTView :"+cell); Point loc = cell.getLocation(); ShortTruthTableElementView elementView = new ShortTruthTableElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } } 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 index 9f238a9bf..9294fba4e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java @@ -4,6 +4,10 @@ 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"); + 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 index 605f6a207..783186baa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java @@ -4,6 +4,10 @@ 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"); + 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 index b2adfddef..5fed4b1df 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java @@ -4,6 +4,10 @@ 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"); + 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 index ecc7d5a02..e2a589b65 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java @@ -4,6 +4,10 @@ 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"); + 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 index 9a9ab8b84..d475bc05d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java @@ -4,6 +4,10 @@ public class UnknownElement extends PlaceableElement { public UnknownElement() { - super("STTT-PLAC-0003", "Unknown Element", "A blank tile", "edu/rpi/legup/images/shorttruthtable/tiles/UnknownTile.png"); + 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/rules/basic/DirectRuleAtomic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRuleAtomic.java index 4b40c833d..38048b5b0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRuleAtomic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/DirectRuleAtomic.java @@ -5,12 +5,12 @@ public class DirectRuleAtomic extends DirectRule_Generic { public DirectRuleAtomic() { - super("STTT-BASC-0001", "Atomic Rule", + super( + "STTT-BASC-0001", + "Atomic Rule", "All identical atoms have the same T/F value", "Atomic", new ContradictionRuleAtomic(), - false - ); + false); } - -} \ No newline at end of file +} 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 e1ac78b8c..470ba7c29 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 @@ -2,21 +2,30 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; 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.shorttruthtable.ShortTruthTableBoard; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; -import edu.rpi.legup.model.rules.ContradictionRule; public abstract class DirectRule_Generic extends DirectRule { final ContradictionRule CORRESPONDING_CONTRADICTION_RULE; final boolean ELIMINATION_RULE; - public DirectRule_Generic(String ruleID, String ruleName, String description, String imageName, ContradictionRule contraRule, boolean eliminationRule) { - super(ruleID, ruleName, description, "edu/rpi/legup/images/shorttruthtable/ruleimages/basic/" + imageName + ".png"); + public DirectRule_Generic( + String ruleID, + String ruleName, + String description, + String imageName, + ContradictionRule contraRule, + boolean eliminationRule) { + super( + ruleID, + ruleName, + description, + "edu/rpi/legup/images/shorttruthtable/ruleimages/basic/" + imageName + ".png"); this.CORRESPONDING_CONTRADICTION_RULE = contraRule; this.ELIMINATION_RULE = eliminationRule; } @@ -26,17 +35,23 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement element) { 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(); + ShortTruthTableBoard parentBoard = + (ShortTruthTableBoard) transition.getParents().get(0).getBoard(); ShortTruthTableBoard finalBoard = (ShortTruthTableBoard) transition.getBoard(); - ShortTruthTableCell parentCell = (ShortTruthTableCell) parentBoard.getPuzzleElement(element); + ShortTruthTableCell parentCell = + (ShortTruthTableCell) parentBoard.getPuzzleElement(element); ShortTruthTableCell finalCell = (ShortTruthTableCell) finalBoard.getPuzzleElement(element); if (!finalCell.isAssigned()) { - return super.getInvalidUseOfRuleMessage() + ": Only assigned cells are allowed for basic rules"; + return super.getInvalidUseOfRuleMessage() + + ": Only assigned cells are allowed for basic rules"; } - // Strategy: Negate the modified cell and check if there is a contradiction. If there is one, then the - // original statement must be true. If there isn't one, then the original statement must be false. + // Strategy: Negate the modified cell and check if there is a contradiction. If there is + // one, + // then the + // original statement must be true. If there isn't one, then the original statement must be + // false. ShortTruthTableBoard modifiedBoard = parentBoard.copy(); @@ -47,13 +62,16 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement element) { ShortTruthTableCell checkCell = this.ELIMINATION_RULE - ? (ShortTruthTableCell) modifiedBoard.getCell(parentCell.getX(), parentCell.getY()) + ? (ShortTruthTableCell) + modifiedBoard.getCell(parentCell.getX(), parentCell.getY()) : (ShortTruthTableCell) modifiedBoard.getPuzzleElement(element); checkCell.setType(finalCell.getType().getNegation()); - String contradictionMessage = CORRESPONDING_CONTRADICTION_RULE.checkContradictionAt(modifiedBoard, checkElement); - if (contradictionMessage == null) { // A contradiction exists in the modified statement; this is good! + String contradictionMessage = + CORRESPONDING_CONTRADICTION_RULE.checkContradictionAt(modifiedBoard, checkElement); + if (contradictionMessage + == null) { // A contradiction exists in the modified statement; this is good! return null; } @@ -61,7 +79,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement element) { } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. * * @param node short truth table board used to create default transition board * @return default board or null if this rule cannot be applied to this tree node diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleAndElimination.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleAndElimination.java index 63e3c8dd9..6f48784a3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleAndElimination.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleAndElimination.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleAnd; - -public class DirectRuleAndElimination extends DirectRule_GenericElimination { - - public DirectRuleAndElimination() { - super("STTT-BASC-0002", "And", new ContradictionRuleAnd()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleAnd; + +public class DirectRuleAndElimination extends DirectRule_GenericElimination { + + public DirectRuleAndElimination() { + super("STTT-BASC-0002", "And", new ContradictionRuleAnd()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleBiconditionalElimination.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleBiconditionalElimination.java index 7f7336a4c..168877ca6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleBiconditionalElimination.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleBiconditionalElimination.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleBiconditional; - -public class DirectRuleBiconditionalElimination extends DirectRule_GenericElimination { - - public DirectRuleBiconditionalElimination() { - super("STTT-BASC-0003", "Biconditional", new ContradictionRuleBiconditional()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleBiconditional; + +public class DirectRuleBiconditionalElimination extends DirectRule_GenericElimination { + + public DirectRuleBiconditionalElimination() { + super("STTT-BASC-0003", "Biconditional", new ContradictionRuleBiconditional()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleConditionalElimination.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleConditionalElimination.java index 8f15be021..4a287b195 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleConditionalElimination.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleConditionalElimination.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleConditional; - -public class DirectRuleConditionalElimination extends DirectRule_GenericElimination { - - public DirectRuleConditionalElimination() { - super("STTT-BASC-0004", "Conditional", new ContradictionRuleConditional()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleConditional; + +public class DirectRuleConditionalElimination extends DirectRule_GenericElimination { + + public DirectRuleConditionalElimination() { + super("STTT-BASC-0004", "Conditional", new ContradictionRuleConditional()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleNotElimination.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleNotElimination.java index a2f08635c..5acff5837 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleNotElimination.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleNotElimination.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleNot; - -public class DirectRuleNotElimination extends DirectRule_GenericElimination { - - public DirectRuleNotElimination() { - super("STTT-BASC-0005", "Not", new ContradictionRuleNot()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleNot; + +public class DirectRuleNotElimination extends DirectRule_GenericElimination { + + public DirectRuleNotElimination() { + super("STTT-BASC-0005", "Not", new ContradictionRuleNot()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleOrElimination.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleOrElimination.java index eca1848bd..fba43762a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleOrElimination.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRuleOrElimination.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleOr; - -public class DirectRuleOrElimination extends DirectRule_GenericElimination { - - public DirectRuleOrElimination() { - super("STTT-BASC-0006", "Or", new ContradictionRuleOr()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleOr; + +public class DirectRuleOrElimination extends DirectRule_GenericElimination { + + public DirectRuleOrElimination() { + super("STTT-BASC-0006", "Or", new ContradictionRuleOr()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRule_GenericElimination.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRule_GenericElimination.java index 49dc43510..1f85e5b79 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRule_GenericElimination.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/elimination/DirectRule_GenericElimination.java @@ -1,18 +1,19 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.DirectRule_Generic; -import edu.rpi.legup.model.rules.ContradictionRule; - -public abstract class DirectRule_GenericElimination extends DirectRule_Generic { - - public DirectRule_GenericElimination(String ruleID, String ruleName, ContradictionRule contradictionRule) { - - super(ruleID, ruleName + " Elimination", - ruleName + " statements must have a valid pattern", - "elimination/" + ruleName, - contradictionRule, - true - ); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination; + +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.DirectRule_Generic; + +public abstract class DirectRule_GenericElimination extends DirectRule_Generic { + + public DirectRule_GenericElimination( + String ruleID, String ruleName, ContradictionRule contradictionRule) { + + super( + ruleID, + ruleName + " Elimination", + ruleName + " statements must have a valid pattern", + "elimination/" + ruleName, + contradictionRule, + true); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleAndIntroduction.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleAndIntroduction.java index 0ca2a7dcf..667b97a0f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleAndIntroduction.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleAndIntroduction.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleAnd; - -public class DirectRuleAndIntroduction extends DirectRule_GenericIntroduction { - - public DirectRuleAndIntroduction() { - super("STTT-BASC-0007", "And", new ContradictionRuleAnd()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleAnd; + +public class DirectRuleAndIntroduction extends DirectRule_GenericIntroduction { + + public DirectRuleAndIntroduction() { + super("STTT-BASC-0007", "And", new ContradictionRuleAnd()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleBiconditionalIntroduction.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleBiconditionalIntroduction.java index b72d5299c..c2dfeebba 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleBiconditionalIntroduction.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleBiconditionalIntroduction.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleBiconditional; - -public class DirectRuleBiconditionalIntroduction extends DirectRule_GenericIntroduction { - - public DirectRuleBiconditionalIntroduction() { - super("STTT-BASC-0008", "Biconditional", new ContradictionRuleBiconditional()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleBiconditional; + +public class DirectRuleBiconditionalIntroduction extends DirectRule_GenericIntroduction { + + public DirectRuleBiconditionalIntroduction() { + super("STTT-BASC-0008", "Biconditional", new ContradictionRuleBiconditional()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleConditionalIntroduction.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleConditionalIntroduction.java index 0e27d267c..728d7f244 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleConditionalIntroduction.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleConditionalIntroduction.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleConditional; - -public class DirectRuleConditionalIntroduction extends DirectRule_GenericIntroduction { - - public DirectRuleConditionalIntroduction() { - super("STTT-BASC-0009", "Conditional", new ContradictionRuleConditional()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleConditional; + +public class DirectRuleConditionalIntroduction extends DirectRule_GenericIntroduction { + + public DirectRuleConditionalIntroduction() { + super("STTT-BASC-0009", "Conditional", new ContradictionRuleConditional()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleNotIntroduction.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleNotIntroduction.java index 980a132fe..80b963a56 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleNotIntroduction.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleNotIntroduction.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleNot; - -public class DirectRuleNotIntroduction extends DirectRule_GenericIntroduction { - - public DirectRuleNotIntroduction() { - super("STTT-BASC-0010", "Not", new ContradictionRuleNot()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleNot; + +public class DirectRuleNotIntroduction extends DirectRule_GenericIntroduction { + + public DirectRuleNotIntroduction() { + super("STTT-BASC-0010", "Not", new ContradictionRuleNot()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleOrIntroduction.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleOrIntroduction.java index 0fd7108bd..66a569655 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleOrIntroduction.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRuleOrIntroduction.java @@ -1,11 +1,10 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleOr; - -public class DirectRuleOrIntroduction extends DirectRule_GenericIntroduction { - - public DirectRuleOrIntroduction() { - super("STTT-BASC-0011", "Or", new ContradictionRuleOr()); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; + +import edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction.ContradictionRuleOr; + +public class DirectRuleOrIntroduction extends DirectRule_GenericIntroduction { + + public DirectRuleOrIntroduction() { + super("STTT-BASC-0011", "Or", new ContradictionRuleOr()); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRule_GenericIntroduction.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRule_GenericIntroduction.java index 123165d7a..8469ff0b9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRule_GenericIntroduction.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/basic/introduction/DirectRule_GenericIntroduction.java @@ -1,18 +1,19 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; - -import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.DirectRule_Generic; -import edu.rpi.legup.model.rules.ContradictionRule; - -public abstract class DirectRule_GenericIntroduction extends DirectRule_Generic { - - protected DirectRule_GenericIntroduction(String ruleID, String ruleName, ContradictionRule contradictionRule) { - - super(ruleID, ruleName + " Introduction", - ruleName + " statements must have a valid pattern", - "introduction/" + ruleName, - contradictionRule, - false - ); - } - -} \ No newline at end of file +package edu.rpi.legup.puzzle.shorttruthtable.rules.basic.introduction; + +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.DirectRule_Generic; + +public abstract class DirectRule_GenericIntroduction extends DirectRule_Generic { + + protected DirectRule_GenericIntroduction( + String ruleID, String ruleName, ContradictionRule contradictionRule) { + + super( + ruleID, + ruleName + " Introduction", + ruleName + " statements must have a valid pattern", + "introduction/" + ruleName, + contradictionRule, + false); + } +} 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 12fac9c4c..9b589f14e 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,17 @@ -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} - }; - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + +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 f168499cc..58d2068b2 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 @@ -3,21 +3,18 @@ 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.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 class CaseRuleAtomic extends CaseRule_Generic { public CaseRuleAtomic() { - super("STTT-CASE-0002", "Atomic", + super( + "STTT-CASE-0002", + "Atomic", "True or False", "Each unknown cell must either be true or false"); } @@ -39,7 +36,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleBiconditional.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleBiconditional.java index 85c6f5447..1bb5f0332 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleBiconditional.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleBiconditional.java @@ -1,24 +1,25 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; public class CaseRuleBiconditional extends CaseRule_GenericStatement { public CaseRuleBiconditional() { - super("STTT-CASE-0003", ShortTruthTableOperation.BICONDITIONAL, + super( + "STTT-CASE-0003", + ShortTruthTableOperation.BICONDITIONAL, "Biconditional", trueCases, falseCases); } private static final ShortTruthTableCellType[][] trueCases = { - {T, T}, - {F, F} + {T, T}, + {F, F} }; private static final ShortTruthTableCellType[][] falseCases = { - {T, F}, - {F, T} + {T, F}, + {F, T} }; - } 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 a9fee2b4e..397341f0e 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,24 @@ -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}, - }; - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + +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 82f814cc8..36f4e6f87 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,19 @@ -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}, - }; - -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + +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 46163c2a3..5885f98f8 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,71 +1,63 @@ -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() == 0) { - return "ERROR: This case rule must spawn at least 1 child."; - } - - // // 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); - } -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.tree.TreeTransition; +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) { + // Validate that two children are generated + List childTransitions = transition.getParents().get(0).getChildren(); + if (childTransitions.size() == 0) { + return "ERROR: This case rule must spawn at least 1 child."; + } + + // // 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 0e25586a8..99f771246 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 @@ -3,22 +3,25 @@ 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 edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; import java.util.ArrayList; +import java.util.List; 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), + 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"); @@ -40,25 +43,33 @@ public CaseRule_GenericStatement(String ruleID, char operation, String title, // 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 + // 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 + // add all elements that can be selected for the case rule statement for (PuzzleElement element : sttBoard.getPuzzleElements()) { - //get the cell object + // get the cell object ShortTruthTableCell cell = sttBoard.getCellFromElement(element); - //the cell must match the symbol + // the cell must match the symbol if (cell.getSymbol() != this.operation) continue; - //the statement must be assigned with unassigned sub-statements + // 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()) { + 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 + // if the element has passed all the checks, it can be selected caseBoard.addPickableElement(element); } return caseBoard; @@ -67,7 +78,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -86,12 +97,16 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { /** * 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) { + 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++) { @@ -116,19 +131,19 @@ private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElem } 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 + + /** + * 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 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 + * @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); + List elements = super.dependentElements(board, puzzleElement); elements.add(board.getPuzzleElement(puzzleElement)); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAnd.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAnd.java index aa0da52c3..595dd0cb2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAnd.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAnd.java @@ -1,22 +1,22 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; public class ContradictionRuleAnd extends ContradictionRule_GenericStatement { public ContradictionRuleAnd() { - super("STTT-CONT-0001", "Contradicting And", + super( + "STTT-CONT-0001", + "Contradicting And", "An AND statement must have a contradicting pattern", "edu/rpi/legup/images/shorttruthtable/ruleimages/contradiction/And.png", ShortTruthTableOperation.AND, - new ShortTruthTableCellType[][]{ - {n, T, F}, - {F, T, n}, - // {F, T, F}, - {T, F, T}, - } - ); + new ShortTruthTableCellType[][] { + {n, T, F}, + {F, T, n}, + // {F, T, F}, + {T, F, T}, + }); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAtomic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAtomic.java index 2d1efb945..879b7c9a5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAtomic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleAtomic.java @@ -1,36 +1,31 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; - import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.ContradictionRule; - 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 java.util.Set; import java.util.Iterator; - +import java.util.Set; public class ContradictionRuleAtomic extends ContradictionRule { - public ContradictionRuleAtomic() { - super("STTT-CONT-0002", "Contradicting Variable", + super( + "STTT-CONT-0002", + "Contradicting Variable", "A single variable can not be both True and False", "edu/rpi/legup/images/shorttruthtable/ruleimages/contradiction/Atomic.png"); } - @Override public String checkContradictionAt(Board puzzleBoard, PuzzleElement puzzleElement) { - //cast the board toa shortTruthTableBoard + // cast the board toa shortTruthTableBoard ShortTruthTableBoard board = (ShortTruthTableBoard) puzzleBoard; - //get the cell that contradicts another cell in the board + // get the cell that contradicts another cell in the board ShortTruthTableCell cell = (ShortTruthTableCell) board.getPuzzleElement(puzzleElement); if (!cell.isVariable()) { @@ -40,26 +35,25 @@ public String checkContradictionAt(Board puzzleBoard, PuzzleElement puzzleElemen ShortTruthTableCellType cellType = cell.getType(); if (!cellType.isTrueOrFalse()) { - return "Can only check for a contradiction against a cell that is assigned a value of True or False"; + return "Can only check for a contradiction against a cell that is assigned a value of" + + " True or False"; } - //get all the cells with the same value + // get all the cells with the same value Set varCells = board.getCellsWithSymbol(cell.getSymbol()); - //check if there are any contradictions + // check if there are any contradictions Iterator itr = varCells.iterator(); while (itr.hasNext()) { ShortTruthTableCell checkCell = itr.next(); ShortTruthTableCellType checkCellType = checkCell.getType(); - //if there is an assigned contradiction, return null + // if there is an assigned contradiction, return null if (checkCellType.isTrueOrFalse() && checkCellType != cellType) { return null; } } - //if it made it through the while loop, thene there is no contradiction + // if it made it through the while loop, thene there is no contradiction return "There is no contradiction for the variable " + cell.getSymbol(); - } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleBiconditional.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleBiconditional.java index bf215358a..bae07acce 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleBiconditional.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleBiconditional.java @@ -1,22 +1,22 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; public class ContradictionRuleBiconditional extends ContradictionRule_GenericStatement { public ContradictionRuleBiconditional() { - super("STTT-CONT-0003", "Contradicting Biconditional", + super( + "STTT-CONT-0003", + "Contradicting Biconditional", "A Biconditional statement must have a contradicting pattern", "edu/rpi/legup/images/shorttruthtable/ruleimages/contradiction/Biconditional.png", ShortTruthTableOperation.BICONDITIONAL, - new ShortTruthTableCellType[][]{ - {T, T, F}, - {F, T, T}, - {T, F, T}, - {F, F, F} - } - ); + new ShortTruthTableCellType[][] { + {T, T, F}, + {F, T, T}, + {T, F, T}, + {F, F, F} + }); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleConditional.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleConditional.java index caa65afa4..e3a51a00e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleConditional.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleConditional.java @@ -1,21 +1,21 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; public class ContradictionRuleConditional extends ContradictionRule_GenericStatement { public ContradictionRuleConditional() { - super("STTT-CONT-0004", "Contradicting Conditional", + super( + "STTT-CONT-0004", + "Contradicting Conditional", "A Conditional statement must have a contradicting pattern", "edu/rpi/legup/images/shorttruthtable/ruleimages/contradiction/Conditional.png", ShortTruthTableOperation.CONDITIONAL, - new ShortTruthTableCellType[][]{ - {n, F, T}, - {F, F, n}, - {T, T, F} - } - ); + new ShortTruthTableCellType[][] { + {n, F, T}, + {F, F, n}, + {T, T, F} + }); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleNot.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleNot.java index 4be059a06..4e20485bc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleNot.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleNot.java @@ -1,20 +1,20 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; public class ContradictionRuleNot extends ContradictionRule_GenericStatement { public ContradictionRuleNot() { - super("STTT-CONT-0005", "Contradicting Negation", + super( + "STTT-CONT-0005", + "Contradicting Negation", "A negation and its following statement can not have the same truth value", "edu/rpi/legup/images/shorttruthtable/ruleimages/contradiction/Not.png", ShortTruthTableOperation.NOT, - new ShortTruthTableCellType[][]{ - {n, T, T}, - {n, F, F} - } - ); + new ShortTruthTableCellType[][] { + {n, T, T}, + {n, F, F} + }); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleOr.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleOr.java index 46fee2a44..2ab197dad 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleOr.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRuleOr.java @@ -1,21 +1,21 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; public class ContradictionRuleOr extends ContradictionRule_GenericStatement { public ContradictionRuleOr() { - super("STTT-CONT-0006", "Contradicting Or", + super( + "STTT-CONT-0006", + "Contradicting Or", "An OR statement must have a contradicting pattern", "edu/rpi/legup/images/shorttruthtable/ruleimages/contradiction/Or.png", ShortTruthTableOperation.OR, - new ShortTruthTableCellType[][]{ - {n, F, T}, - {T, F, n}, - {F, T, F} - } - ); + new ShortTruthTableCellType[][] { + {n, F, T}, + {T, F, n}, + {F, T, F} + }); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRule_GenericStatement.java index 4554e5100..87b1ba628 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRule_GenericStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/contradiction/ContradictionRule_GenericStatement.java @@ -1,31 +1,36 @@ package edu.rpi.legup.puzzle.shorttruthtable.rules.contradiction; - import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.ContradictionRule; - 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; - public abstract class ContradictionRule_GenericStatement extends ContradictionRule { private final char operationSymbol; private final ShortTruthTableCellType[][] contradictionPatterns; - final static ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; - final static ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; - final static ShortTruthTableCellType n = null; - - private final String NOT_RIGHT_OPERATOR_ERROR_MESSAGE = "This cell does not contain the correct operation"; - private final String NOT_TRUE_FALSE_ERROR_MESSAGE = "Can only check for a contradiction on a cell that is assigned a value of True or False"; - - public ContradictionRule_GenericStatement(String ruleID, String ruleName, String description, String imageName, - char operationSymbol, ShortTruthTableCellType[][] contradictionPatterns) { + static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; + static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; + static final ShortTruthTableCellType n = null; + + private final String NOT_RIGHT_OPERATOR_ERROR_MESSAGE = + "This cell does not contain the correct operation"; + private final String NOT_TRUE_FALSE_ERROR_MESSAGE = + "Can only check for a contradiction on a cell that is assigned a value of True or" + + " False"; + + public ContradictionRule_GenericStatement( + String ruleID, + String ruleName, + String description, + String imageName, + char operationSymbol, + ShortTruthTableCellType[][] contradictionPatterns) { super(ruleID, ruleName, description, imageName); this.operationSymbol = operationSymbol; this.contradictionPatterns = contradictionPatterns; @@ -42,7 +47,9 @@ public String checkContradictionAt(Board puzzleBoard, PuzzleElement operatorPuzz ShortTruthTableStatement statement = cell.getStatementReference(); if (cell.getSymbol() != this.operationSymbol) { - return super.getInvalidUseOfRuleMessage() + ": " + this.NOT_RIGHT_OPERATOR_ERROR_MESSAGE; + return super.getInvalidUseOfRuleMessage() + + ": " + + this.NOT_RIGHT_OPERATOR_ERROR_MESSAGE; } // check that the initial statement is assigned @@ -63,13 +70,14 @@ public String checkContradictionAt(Board puzzleBoard, PuzzleElement operatorPuzz if (pattern[i] == null) { continue; } - //if it is not null, it must match the test pattern + // if it is not null, it must match the test pattern if (pattern[i] != testPattern[i]) { matches = false; break; } } - // if testPattern matches one of the valid contradiction patterns, the contradiction is correct + // if testPattern matches one of the valid contradiction patterns, the contradiction is + // correct if (matches) { return null; } @@ -77,4 +85,4 @@ public String checkContradictionAt(Board puzzleBoard, PuzzleElement operatorPuzz return super.getNoContradictionMessage(); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java index f87462978..df5ba78a3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java @@ -18,9 +18,7 @@ public Skyscrapers() { this.factory = new SkyscrapersCellFactory(); } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { boardView = new SkyscrapersView((SkyscrapersBoard) currentBoard); @@ -43,8 +41,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Skyscrapers * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Skyscrapers, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -82,7 +80,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} } 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 65f5eb561..4cd09b254 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -3,7 +3,6 @@ import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.awt.event.MouseEvent; import java.util.ArrayList; @@ -11,15 +10,14 @@ public class SkyscrapersBoard extends GridBoard { - private ArrayList eastClues; - //EAST clues + // EAST clues private ArrayList southClues; - //SOUTH clues + // SOUTH clues private ArrayList westClues; - //WEST clues + // WEST clues private ArrayList northClues; - //NORTH clues + // NORTH clues private boolean viewFlag = false; private boolean dupeFlag = false; @@ -40,10 +38,9 @@ public SkyscrapersBoard(int size) { } } - /** - * @return eastClues a list of the eastern clues ordered from loc.y = 0 to max - */ + * @return eastClues a list of the eastern clues ordered from loc.y = 0 to max + */ public ArrayList getEastClues() { return eastClues; } @@ -103,7 +100,6 @@ public PuzzleElement getPuzzleElement(PuzzleElement element) { return super.getPuzzleElement(element); } - /** * Gets the cells of a certain type directly adjacent to a given cell * @@ -176,8 +172,7 @@ public List getRowCol(int index, SkyscrapersType type, boolean SkyscrapersCell cell; if (isRow) { cell = getCell(i, index); - } - else { + } else { cell = getCell(index, i); } @@ -188,16 +183,13 @@ public List getRowCol(int index, SkyscrapersType type, boolean return list; } - /** - * Prints a semblance of the board to console (helps in debugging) - */ + /** Prints a semblance of the board to console (helps in debugging) */ public void printBoard() { for (int i = 0; i < this.dimension.height; i++) { for (SkyscrapersCell cell : this.getRowCol(i, SkyscrapersType.ANY, true)) { if (cell.getType() == SkyscrapersType.Number) { System.out.print(cell.getData() + " "); - } - else { + } else { System.out.print(0 + " "); } } @@ -206,13 +198,11 @@ public void printBoard() { } /** - * * @param x position of cell * @param y position of cell * @param e Element to be placed (null if nothing selected) - * @param m MouseEvent - * Increases clue values if in editor mode. Currently allows for - * presetting tile values, though they will not be saved. + * @param m MouseEvent Increases clue values if in editor mode. Currently allows for presetting + * tile values, though they will not be saved. */ @Override public void setCell(int x, int y, Element e, MouseEvent m) { @@ -226,27 +216,22 @@ public void setCell(int x, int y, Element e, MouseEvent m) { if (m.getButton() == MouseEvent.BUTTON1) { if (clue.getData() < dimension.height) { clue.setData(clue.getData() + 1); - } - else { + } else { clue.setData(0); } - } - else { + } else { if (clue.getData() > 0) { clue.setData(clue.getData() - 1); - } - else { + } else { clue.setData(dimension.height); } } - } - else { + } else { super.setCell(x - 1, y - 1, e, m); } } /** - * * @param x position of element on boardView * @param y position of element on boardView * @return The clue at the given position @@ -254,16 +239,13 @@ public void setCell(int x, int y, Element e, MouseEvent m) { public SkyscrapersClue getClue(int x, int y) { int viewIndex = getSize() + 1; if (x == 0 && y > 0 && y < viewIndex) { - return westClues.get(y-1); - } - else if (x == viewIndex && y > 0 && y < viewIndex) { - return eastClues.get(y-1); - } - else if (y == 0 && x > 0 && x < viewIndex) { - return northClues.get(x-1); - } - else if (y == viewIndex && x > 0 && x < viewIndex) { - return southClues.get(x-1); + return westClues.get(y - 1); + } else if (x == viewIndex && y > 0 && y < viewIndex) { + return eastClues.get(y - 1); + } else if (y == 0 && x > 0 && x < viewIndex) { + return northClues.get(x - 1); + } else if (y == viewIndex && x > 0 && x < viewIndex) { + return southClues.get(x - 1); } return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java index 409555b83..1cf9a357b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java @@ -1,13 +1,12 @@ package edu.rpi.legup.puzzle.skyscrapers; +import static edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType.convertToSkyType; + import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; import java.awt.event.MouseEvent; -import static edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType.convertToSkyType; - public class SkyscrapersCell extends GridCell { private int max; @@ -17,7 +16,7 @@ public SkyscrapersCell(Integer value, Point location, int size) { } public SkyscrapersType getType() { - switch (convertToSkyType(data)){ + switch (convertToSkyType(data)) { case UNKNOWN: return SkyscrapersType.UNKNOWN; default: @@ -27,25 +26,23 @@ public SkyscrapersType getType() { @Override public void setType(Element e, MouseEvent m) { - switch (e.getElementID()){ + switch (e.getElementID()) { case "SKYS-UNPL-0001": this.data = 0; break; case "SKYS-UNPL-0002": - switch (m.getButton()){ + switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data <= 0 || this.data >= this.max) { this.data = 1; - } - else { + } else { this.data = this.data + 1; } break; case MouseEvent.BUTTON3: if (this.data > 1) { this.data = this.data - 1; - } - else { + } else { this.data = this.max; } break; @@ -54,7 +51,6 @@ public void setType(Element e, MouseEvent m) { } } - public int getMax() { return max; } 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 5f6de7369..03572b816 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class SkyscrapersCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if input is invalid @@ -23,7 +22,8 @@ public class SkyscrapersCellFactory extends ElementFactory { public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("Skyscrapers Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "Skyscrapers Factory: unknown puzzleElement puzzleElement"); } SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; @@ -34,7 +34,8 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); if (x >= size || y >= size) { - throw new InvalidFileFormatException("Skyscrapers Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "Skyscrapers Factory: cell location out of bounds"); } if (value < 0 || value > size) { throw new InvalidFileFormatException("Skyscrapers Factory: cell unknown value"); @@ -44,19 +45,19 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat cell.setIndex(y * size + x); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Skyscrapers Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { - throw new InvalidFileFormatException("Skyscrapers Factory: could not find attribute(s)"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Skyscrapers Factory: unknown value where integer expected"); + } catch (NullPointerException e) { + throw new InvalidFileFormatException( + "Skyscrapers Factory: could not find attribute(s)"); } } /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ 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 1e7b1b45e..7b8bf635b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java @@ -9,7 +9,7 @@ public class SkyscrapersClue extends PuzzleElement { public SkyscrapersClue(int value, int clueIndex, SkyscrapersType type) { super(value); this.index = -2; - this.clueIndex = clueIndex;//index in list + this.clueIndex = clueIndex; // index in list this.type = type; this.setModifiable(false); } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java index 649f85f4e..5a49a1476 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.skyscrapers; import edu.rpi.legup.ui.boardview.ElementView; - import java.awt.*; public class SkyscrapersClueView extends ElementView { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java index 8e338a351..f78774e35 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersController.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class SkyscrapersController extends ElementController { @@ -18,18 +17,15 @@ public void changeCell(MouseEvent e, PuzzleElement element) { if (cell.getData() < cell.getMax()) { int num = cell.getData() + 1; cell.setData(num); - } - else { + } else { cell.setData(SkyscrapersType.UNKNOWN.toValue()); } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() > 0) { int num = cell.getData() - 1; cell.setData(num); - } - else { + } else { cell.setData(cell.getMax()); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java index 9884b4a3c..2f33017c3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.skyscrapers; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class SkyscrapersElementView extends GridElementView { @@ -30,7 +29,8 @@ public void drawElement(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(val); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(value, xText, yText); } } 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 f784fb2d9..75d2bd04f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersExporter.java @@ -15,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { SkyscrapersBoard board; if (puzzle.getTree() != null) { board = (SkyscrapersBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (SkyscrapersBoard) puzzle.getBoardView().getBoard(); } @@ -27,7 +26,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; if (cell.getData() != 0) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } @@ -35,29 +35,33 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element axisEast = newDocument.createElement("axis"); axisEast.setAttribute("side", "east"); - for (int i=0; i northClues; private ArrayList eastClues; @@ -34,7 +33,8 @@ public SkyscrapersView(SkyscrapersBoard board) { SkyscrapersElementView elementView = new SkyscrapersElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point((loc.x + 1) * elementSize.width, (loc.y + 1) * elementSize.height)); + elementView.setLocation( + new Point((loc.x + 1) * elementSize.width, (loc.y + 1) * elementSize.height)); elementViews.add(elementView); } @@ -44,7 +44,10 @@ public SkyscrapersView(SkyscrapersBoard board) { row.setSize(elementSize); SkyscrapersClueView clue = new SkyscrapersClueView(board.getEastClues().get(i)); - clue.setLocation(new Point((gridSize.height + 1) * elementSize.height, (i + 1) * elementSize.height)); + clue.setLocation( + new Point( + (gridSize.height + 1) * elementSize.height, + (i + 1) * elementSize.height)); clue.setSize(elementSize); westClues.add(row); @@ -57,7 +60,9 @@ public SkyscrapersView(SkyscrapersBoard board) { col.setSize(elementSize); SkyscrapersClueView clue = new SkyscrapersClueView(board.getSouthClues().get(i)); - clue.setLocation(new Point((i + 1) * elementSize.width, (gridSize.width + 1) * elementSize.width)); + clue.setLocation( + new Point( + (i + 1) * elementSize.width, (gridSize.width + 1) * elementSize.width)); clue.setSize(elementSize); northClues.add(col); @@ -66,15 +71,18 @@ public SkyscrapersView(SkyscrapersBoard board) { } /** - * Gets the ElementView from the location specified or - * null if one does not exists at that location + * Gets the ElementView from the location specified or null if one does not exists at that + * location * * @param point location on the viewport * @return ElementView at the specified location */ @Override public ElementView getElement(Point point) { - Point scaledPoint = new Point((int) Math.round(point.x / getScale()), (int) Math.round(point.y / getScale())); + Point scaledPoint = + new Point( + (int) Math.round(point.x / getScale()), + (int) Math.round(point.y / getScale())); for (ElementView element : elementViews) { if (element.isWithinBounds(scaledPoint)) { return element; @@ -131,10 +139,10 @@ public void setBoard(Board board) { if (board instanceof CaseBoard) { setCasePickable(); - } - else { + } else { for (ElementView elementView : elementViews) { - elementView.setPuzzleElement(board.getPuzzleElement(elementView.getPuzzleElement())); + elementView.setPuzzleElement( + board.getPuzzleElement(elementView.getPuzzleElement())); elementView.setShowCasePicker(false); } for (SkyscrapersClueView clueView : northClues) { @@ -155,7 +163,8 @@ protected void setCasePickable() { Board baseBoard = caseBoard.getBaseBoard(); for (ElementView elementView : elementViews) { - PuzzleElement puzzleElement = baseBoard.getPuzzleElement(elementView.getPuzzleElement()); + PuzzleElement puzzleElement = + baseBoard.getPuzzleElement(elementView.getPuzzleElement()); elementView.setPuzzleElement(puzzleElement); elementView.setShowCasePicker(true); elementView.setCaseRulePickable(caseBoard.isPickable(puzzleElement, null)); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java index 8505de523..64c9033e6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java @@ -5,6 +5,10 @@ public class ClueTile extends NonPlaceableElement { public ClueTile() { - super("SKYS-UNPL-0003", "Clue Tile", "Clue Updater", "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); + super( + "SKYS-UNPL-0003", + "Clue Tile", + "Clue Updater", + "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java index 8f78fb1cf..4d6b37c9a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java @@ -4,6 +4,10 @@ public class NumberTile extends NonPlaceableElement { public NumberTile() { - super("SKYS-UNPL-0002", "Number Tile", "A numbered tile", "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); + super( + "SKYS-UNPL-0002", + "Number Tile", + "A numbered tile", + "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java index 3559dc332..2fb21193a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java @@ -4,6 +4,10 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("SKYS-UNPL-0001", "Unknown", "A blank tile", "edu/rpi/legup/images/skyscrapers/tiles/UnknownTile.png"); + super( + "SKYS-UNPL-0001", + "Unknown", + "A blank tile", + "edu/rpi/legup/images/skyscrapers/tiles/UnknownTile.png"); } } 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 01527294a..45bdadea3 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 @@ -6,18 +6,16 @@ import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.*; -import org.apache.commons.lang3.ObjectUtils; - -import javax.swing.*; import java.awt.*; import java.util.ArrayList; import java.util.List; -import java.util.Set; -import java.util.TreeSet; +import javax.swing.*; public class CellForNumberCaseRule extends CaseRule { public CellForNumberCaseRule() { - super("SKYS-CASE-0002", "Cell For Number", + super( + "SKYS-CASE-0002", + "Cell For Number", "A number (1-n) must appear in any given row/column", "edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png"); } @@ -30,15 +28,15 @@ public CaseBoard getCaseBoard(Board board) { currentBoard.setModifiable(false); CaseBoard caseBoard = new CaseBoard(currentBoard, this); for (SkyscrapersClue data : currentBoard.getWestClues()) { - //System.out.println(data.getType()); + // System.out.println(data.getType()); caseBoard.addPickableElement(data); } for (SkyscrapersClue data : currentBoard.getNorthClues()) { - //System.out.println(data.getType()); + // System.out.println(data.getType()); caseBoard.addPickableElement(data); } - //selects integer before checking Command.canExecute for use in Command.getErrorString + // selects integer before checking Command.canExecute for use in Command.getErrorString int size = ((SkyscrapersBoard) board).getWidth(); Object[] possibleValues = new Object[size]; for (int i = 0; i < size; i++) { @@ -46,12 +44,16 @@ public CaseBoard getCaseBoard(Board board) { } Object selectedValue; do { - selectedValue = JOptionPane.showInputDialog(null, - "Pick the number to be added", "Cell For Number", - JOptionPane.INFORMATION_MESSAGE, null, - possibleValues, possibleValues[0]); - } - while (selectedValue == null); + selectedValue = + JOptionPane.showInputDialog( + null, + "Pick the number to be added", + "Cell For Number", + JOptionPane.INFORMATION_MESSAGE, + null, + possibleValues, + possibleValues[0]); + } while (selectedValue == null); selectedNumber = (Integer) selectedValue; return caseBoard; @@ -63,28 +65,31 @@ public ArrayList getCasesFor(Board board, PuzzleElement puzzleElement, In SkyscrapersClue clue = (SkyscrapersClue) puzzleElement; SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; - List openCells = skyscrapersboard.getRowCol(clue.getClueIndex(), SkyscrapersType.UNKNOWN, clue.getType() == SkyscrapersType.CLUE_WEST); + List openCells = + skyscrapersboard.getRowCol( + clue.getClueIndex(), + SkyscrapersType.UNKNOWN, + clue.getType() == SkyscrapersType.CLUE_WEST); for (SkyscrapersCell cell : openCells) { SkyscrapersBoard newCase = skyscrapersboard.copy(); PuzzleElement newCell = newCase.getPuzzleElement(cell); newCell.setData(number); newCase.addModifiedData(newCell); - //if flags + // if flags boolean passed = true; if (skyscrapersboard.getDupeFlag()) { DuplicateNumberContradictionRule DupeRule = new DuplicateNumberContradictionRule(); passed = passed && DupeRule.checkContradictionAt(newCase, newCell) != null; } if (skyscrapersboard.getViewFlag()) { - PreemptiveVisibilityContradictionRule ViewRule = new PreemptiveVisibilityContradictionRule(); + PreemptiveVisibilityContradictionRule ViewRule = + new PreemptiveVisibilityContradictionRule(); passed = passed && ViewRule.checkContradictionAt(newCase, newCell) != null; } if (passed) { cases.add(newCase); } - - } return cases; } @@ -102,20 +107,33 @@ public String checkRuleRaw(TreeTransition transition) { return "This case rule must have at least one child."; } - 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."); + 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."; } for (TreeTransition newTree : childTransitions) { SkyscrapersBoard newBoard = (SkyscrapersBoard) newTree.getBoard(); if (newBoard.getModifiedData().size() != 1) { - //System.out.println("Only one cell should be modified."); + // System.out.println("Only one cell should be modified."); return "Only one cell should be modified."; } - SkyscrapersCell newCell = (SkyscrapersCell) newBoard.getModifiedData().iterator().next(); + SkyscrapersCell newCell = + (SkyscrapersCell) newBoard.getModifiedData().iterator().next(); if (newCell.getType() != SkyscrapersType.Number) { - //System.out.println("Changed value should be a number."); + // System.out.println("Changed value should be a number."); return "Changed value should be a number."; } } @@ -128,37 +146,45 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * 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 + * 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 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 + * @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); + 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); + 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) { + 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 { + } else { index = point.getLocation().y; } - cells.addAll(puzzleBoard.getRowCol(index,SkyscrapersType.ANY,clue.getType() != SkyscrapersType.CLUE_WEST)); + cells.addAll( + puzzleBoard.getRowCol( + index, + SkyscrapersType.ANY, + clue.getType() != SkyscrapersType.CLUE_WEST)); } // add all to result diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java index 620a87f68..b13a62bc6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/DuplicateNumberContradictionRule.java @@ -6,7 +6,6 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - import java.awt.*; import java.util.HashSet; import java.util.Set; @@ -14,18 +13,21 @@ public class DuplicateNumberContradictionRule extends ContradictionRule { public DuplicateNumberContradictionRule() { - super("SKYS-CONT-0001", "Duplicate Number", + super( + "SKYS-CONT-0001", + "Duplicate Number", "Skyscrapers of same height cannot be placed in the same row or column.", "edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -35,12 +37,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Set candidates = new HashSet(); - //check row + // check row for (int i = 0; i < skyscrapersboard.getWidth(); i++) { SkyscrapersCell c = skyscrapersboard.getCell(i, loc.y); - if (i != loc.x && cell.getType() == SkyscrapersType.Number && c.getType() == SkyscrapersType.Number && c.getData() == cell.getData()) { - //System.out.print(c.getData()); - //System.out.println(cell.getData()); + if (i != loc.x + && cell.getType() == SkyscrapersType.Number + && c.getType() == SkyscrapersType.Number + && c.getData() == cell.getData()) { + // System.out.print(c.getData()); + // System.out.println(cell.getData()); return null; } } @@ -48,9 +53,12 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { // check column for (int i = 0; i < skyscrapersboard.getHeight(); i++) { SkyscrapersCell c = skyscrapersboard.getCell(loc.x, i); - if (i != loc.y && cell.getType() == SkyscrapersType.Number && c.getType() == SkyscrapersType.Number && c.getData() == cell.getData()) { - //System.out.print(c.getData()); - //System.out.println(cell.getData()); + if (i != loc.y + && cell.getType() == SkyscrapersType.Number + && c.getType() == SkyscrapersType.Number + && c.getData() == cell.getData()) { + // System.out.print(c.getData()); + // System.out.println(cell.getData()); return null; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java index e38018745..742bdb253 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/ExceedingVisibilityContradictionRule.java @@ -6,28 +6,28 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - import java.awt.*; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class ExceedingVisibilityContradictionRule extends ContradictionRule { public ExceedingVisibilityContradictionRule() { - super("SKYS-CONT-0002", "Exceeding Visibility", + super( + "SKYS-CONT-0002", + "Exceeding Visibility", "More skyscrapers are visible than there should be.", "edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -35,22 +35,22 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; Point loc = cell.getLocation(); - //get borders + // get borders int west = skyscrapersboard.getWestClues().get(loc.y).getData(); int east = skyscrapersboard.getEastClues().get(loc.y).getData(); int north = skyscrapersboard.getNorthClues().get(loc.x).getData(); int south = skyscrapersboard.getSouthClues().get(loc.x).getData(); - //check row + // check row int max = 0; int count = 0; List row = skyscrapersboard.getRowCol(loc.y, SkyscrapersType.Number, true); if (row.size() == skyscrapersboard.getWidth()) { - //from west border + // from west border for (SkyscrapersCell c : row) { if (c.getData() > max) { - //System.out.print(c.getData()); - //System.out.println(cell.getData()); + // System.out.print(c.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -61,12 +61,12 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { max = 0; count = 0; - //from east border + // from east border Collections.reverse(row); for (SkyscrapersCell c : row) { if (c.getData() > max) { - //System.out.print(c.getData()); - //System.out.println(cell.getData()); + // System.out.print(c.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -76,17 +76,18 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - //check column - List col = skyscrapersboard.getRowCol(loc.x, SkyscrapersType.Number, false); + // check column + List col = + skyscrapersboard.getRowCol(loc.x, SkyscrapersType.Number, false); if (col.size() == skyscrapersboard.getHeight()) { - //from north border + // from north border max = 0; count = 0; for (SkyscrapersCell c : col) { - //System.out.println(c.getData()); + // System.out.println(c.getData()); if (c.getData() > max) { - //System.out.println(cell.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -95,15 +96,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return null; } - //from south border + // from south border max = 0; count = 0; Collections.reverse(col); for (SkyscrapersCell c : col) { - //System.out.println(c.getData()); + // System.out.println(c.getData()); if (c.getData() > max) { - //System.out.println(cell.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -113,7 +114,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - //System.out.print("Does not contain a contradiction at this index"); + // System.out.print("Does not contain a contradiction at this index"); return super.getNoContradictionMessage(); } @@ -127,7 +128,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { public String checkContradiction(Board board) { SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) + // checks the middle diagonal (checkContradictionAt checks row/col off each) String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i, i)); if (checkStr == null) { return checkStr; @@ -135,5 +136,4 @@ public String checkContradiction(Board board) { } return "No instance of the contradiction " + this.ruleName + " here"; } - } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java index 7d405f122..fa7c9074e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/InsufficientVisibilityContradictionRule.java @@ -6,28 +6,28 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - import java.awt.*; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class InsufficientVisibilityContradictionRule extends ContradictionRule { public InsufficientVisibilityContradictionRule() { - super("SKYS-CONT-0003", "Insufficient Visibility", + super( + "SKYS-CONT-0003", + "Insufficient Visibility", "Less skyscrapers are visible than there should be.", "edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -35,22 +35,23 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; Point loc = cell.getLocation(); - //get borders + // get borders int west = skyscrapersboard.getWestClues().get(loc.y).getData(); int east = skyscrapersboard.getEastClues().get(loc.y).getData(); int north = skyscrapersboard.getNorthClues().get(loc.x).getData(); int south = skyscrapersboard.getSouthClues().get(loc.x).getData(); - //check row + // check row int max = 0; int count = 0; - java.util.List row = skyscrapersboard.getRowCol(loc.y, SkyscrapersType.Number, true); + java.util.List row = + skyscrapersboard.getRowCol(loc.y, SkyscrapersType.Number, true); if (row.size() == skyscrapersboard.getWidth()) { - //from west border + // from west border for (SkyscrapersCell c : row) { if (c.getData() > max) { - //System.out.print(c.getData()); - //System.out.println(cell.getData()); + // System.out.print(c.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -61,12 +62,12 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { max = 0; count = 0; - //from east border + // from east border Collections.reverse(row); for (SkyscrapersCell c : row) { if (c.getData() > max) { - //System.out.print(c.getData()); - //System.out.println(cell.getData()); + // System.out.print(c.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -76,17 +77,18 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - //check column - List col = skyscrapersboard.getRowCol(loc.x, SkyscrapersType.Number, false); + // check column + List col = + skyscrapersboard.getRowCol(loc.x, SkyscrapersType.Number, false); if (col.size() == skyscrapersboard.getHeight()) { - //from north border + // from north border max = 0; count = 0; for (SkyscrapersCell c : col) { - //System.out.println(c.getData()); + // System.out.println(c.getData()); if (c.getData() > max) { - //System.out.println(cell.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -95,15 +97,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return null; } - //from south border + // from south border max = 0; count = 0; Collections.reverse(col); for (SkyscrapersCell c : col) { - //System.out.println(c.getData()); + // System.out.println(c.getData()); if (c.getData() > max) { - //System.out.println(cell.getData()); + // System.out.println(cell.getData()); max = c.getData(); count++; } @@ -113,7 +115,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - //System.out.print("Does not contain a contradiction at this index"); + // System.out.print("Does not contain a contradiction at this index"); return super.getNoContradictionMessage(); } @@ -127,7 +129,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { public String checkContradiction(Board board) { SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) + // checks the middle diagonal (checkContradictionAt checks row/col off each) String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i, i)); if (checkStr == null) { return checkStr; diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java index 2cede4117..391448781 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularCellDirectRule.java @@ -1,114 +1,129 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.tree.TreeNode; -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.SkyscrapersType; - -import java.util.ArrayList; - -public class LastSingularCellDirectRule extends DirectRule { - - public LastSingularCellDirectRule() { - super("SKYS-BASC-0002", "Last Cell for Number", - "There is only one cell on this row/col for this number that does not create a duplicate contradiction", - "edu/rpi/legup/images/skyscrapers/rules/LastCell.png"); - } - - /** - * 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 index of the 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); - SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); - SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(initCell.getType() == SkyscrapersType.UNKNOWN && finalCell.getType() == SkyscrapersType.Number)) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; - } - - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(true); - initialBoard.setViewFlag(false); - CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - ArrayList XCandidates = caseRule.getCasesFor(initialBoard, initialBoard.getWestClues().get(finalCell.getLocation().y), (Integer) finalCell.getData()); - ArrayList YCandidates = caseRule.getCasesFor(initialBoard, initialBoard.getNorthClues().get(finalCell.getLocation().x), (Integer) finalCell.getData()); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); - - //System.out.println(XCandidates.size()); - //System.out.println(YCandidates.size()); - - //return null if either pass, both messages otherwise - String xCheck = candidateCheck(XCandidates, puzzleElement, finalCell); - String yCheck = candidateCheck(YCandidates, puzzleElement, finalCell); - if (xCheck == null || yCheck == null) { - return null; - } - return super.getInvalidUseOfRuleMessage() + "\nRow" + xCheck + "\nCol" + yCheck; - } - - //helper to check if candidate list is valid - private String candidateCheck(ArrayList candidates, PuzzleElement puzzleElement, SkyscrapersCell finalCell) { - if (candidates.size() == 1) { - if (((SkyscrapersCell) candidates.get(0).getPuzzleElement(puzzleElement)).getType() == SkyscrapersType.Number) { - if (candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()) { - return null; - } - return ": Wrong number in the cell."; - } - return ": No case for this cell."; - } - return ": This cell is not forced."; - } - - private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { - SkyscrapersBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); - DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); - if (duplicate.checkContradictionAt(emptyCase, cell) == null) { - System.out.println("no contradiction ln"); - return true; - } - return false; - } - - /** - * 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); - SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - //System.out.println(lightUpBoard.getPuzzleElements().size()); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - SkyscrapersCell cell = (SkyscrapersCell) element; - if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { - //cell.setData(SkyscrapersType.BULB.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.skyscrapers.rules; + +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.tree.TreeNode; +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.SkyscrapersType; +import java.util.ArrayList; + +public class LastSingularCellDirectRule extends DirectRule { + + public LastSingularCellDirectRule() { + super( + "SKYS-BASC-0002", + "Last Cell for Number", + "There is only one cell on this row/col for this number that does not create a" + + " duplicate contradiction", + "edu/rpi/legup/images/skyscrapers/rules/LastCell.png"); + } + + /** + * 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 index of the 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) { + SkyscrapersBoard initialBoard = + (SkyscrapersBoard) transition.getParents().get(0).getBoard(); + SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); + SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == SkyscrapersType.UNKNOWN + && finalCell.getType() == SkyscrapersType.Number)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; + } + + // set all rules used by case rule to false except for dupe, get all cases + boolean dupeTemp = initialBoard.getDupeFlag(); + boolean viewTemp = initialBoard.getViewFlag(); + initialBoard.setDupeFlag(true); + initialBoard.setViewFlag(false); + CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); + ArrayList XCandidates = + caseRule.getCasesFor( + initialBoard, + initialBoard.getWestClues().get(finalCell.getLocation().y), + (Integer) finalCell.getData()); + ArrayList YCandidates = + caseRule.getCasesFor( + initialBoard, + initialBoard.getNorthClues().get(finalCell.getLocation().x), + (Integer) finalCell.getData()); + initialBoard.setDupeFlag(dupeTemp); + initialBoard.setViewFlag(viewTemp); + + // System.out.println(XCandidates.size()); + // System.out.println(YCandidates.size()); + + // return null if either pass, both messages otherwise + String xCheck = candidateCheck(XCandidates, puzzleElement, finalCell); + String yCheck = candidateCheck(YCandidates, puzzleElement, finalCell); + if (xCheck == null || yCheck == null) { + return null; + } + return super.getInvalidUseOfRuleMessage() + "\nRow" + xCheck + "\nCol" + yCheck; + } + + // helper to check if candidate list is valid + private String candidateCheck( + ArrayList candidates, PuzzleElement puzzleElement, SkyscrapersCell finalCell) { + if (candidates.size() == 1) { + if (((SkyscrapersCell) candidates.get(0).getPuzzleElement(puzzleElement)).getType() + == SkyscrapersType.Number) { + if (candidates.get(0).getPuzzleElement(puzzleElement).getData() + == finalCell.getData()) { + return null; + } + return ": Wrong number in the cell."; + } + return ": No case for this cell."; + } + return ": This cell is not forced."; + } + + private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { + SkyscrapersBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (duplicate.checkContradictionAt(emptyCase, cell) == null) { + System.out.println("no contradiction ln"); + return true; + } + return false; + } + + /** + * 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) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); + SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); + // System.out.println(lightUpBoard.getPuzzleElements().size()); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + SkyscrapersCell cell = (SkyscrapersCell) element; + if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { + // cell.setData(SkyscrapersType.BULB.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java index 280325190..fceee0cd1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastSingularNumberDirectRule.java @@ -1,97 +1,103 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.tree.TreeNode; -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.SkyscrapersType; - -import java.util.ArrayList; - -public class LastSingularNumberDirectRule extends DirectRule { - - public LastSingularNumberDirectRule() { - super("SKYS-BASC-0003", "Last Number for Cell", - "There is only one number for this cell that does not create a duplicate contradiction", - "edu/rpi/legup/images/skyscrapers/rules/LastNumber.png"); - } - - /** - * 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 index of the 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); - SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); - SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (initCell.getType() != SkyscrapersType.UNKNOWN || finalCell.getType() != SkyscrapersType.Number) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must transition from unknown to number"; - } - - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(true); - initialBoard.setViewFlag(false); - NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); - ArrayList candidates = caseRule.getCases(initialBoard, puzzleElement); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); - - //check if given value is the only remaining value - if (candidates.size() == 1) { - if (candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()) { - return null; - } - return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; - } - return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; - } - - private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { - SkyscrapersBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); - DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); - if (duplicate.checkContradictionAt(emptyCase, cell) == null) { - System.out.println("no contradiction ln"); - return true; - } - return false; - } - - /** - * 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); - SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - //System.out.println(lightUpBoard.getPuzzleElements().size()); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - SkyscrapersCell cell = (SkyscrapersCell) element; - if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { - //cell.setData(SkyscrapersType.BULB.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.skyscrapers.rules; + +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.tree.TreeNode; +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.SkyscrapersType; +import java.util.ArrayList; + +public class LastSingularNumberDirectRule extends DirectRule { + + public LastSingularNumberDirectRule() { + super( + "SKYS-BASC-0003", + "Last Number for Cell", + "There is only one number for this cell that does not create a duplicate" + + " contradiction", + "edu/rpi/legup/images/skyscrapers/rules/LastNumber.png"); + } + + /** + * 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 index of the 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) { + SkyscrapersBoard initialBoard = + (SkyscrapersBoard) transition.getParents().get(0).getBoard(); + SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); + SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); + if (initCell.getType() != SkyscrapersType.UNKNOWN + || finalCell.getType() != SkyscrapersType.Number) { + return super.getInvalidUseOfRuleMessage() + + ": Modified cells must transition from unknown to number"; + } + + // set all rules used by case rule to false except for dupe, get all cases + boolean dupeTemp = initialBoard.getDupeFlag(); + boolean viewTemp = initialBoard.getViewFlag(); + initialBoard.setDupeFlag(true); + initialBoard.setViewFlag(false); + NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); + ArrayList candidates = caseRule.getCases(initialBoard, puzzleElement); + initialBoard.setDupeFlag(dupeTemp); + initialBoard.setViewFlag(viewTemp); + + // check if given value is the only remaining value + if (candidates.size() == 1) { + if (candidates.get(0).getPuzzleElement(puzzleElement).getData() + == finalCell.getData()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + } + return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; + } + + private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { + SkyscrapersBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (duplicate.checkContradictionAt(emptyCase, cell) == null) { + System.out.println("no contradiction ln"); + return true; + } + return false; + } + + /** + * 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) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); + SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); + // System.out.println(lightUpBoard.getPuzzleElements().size()); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + SkyscrapersCell cell = (SkyscrapersCell) element; + if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { + // cell.setData(SkyscrapersType.BULB.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java index 3aa28bab8..a853c657d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleCellDirectRule.java @@ -1,116 +1,132 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.tree.TreeNode; -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.SkyscrapersType; - -import java.util.ArrayList; - -public class LastVisibleCellDirectRule extends DirectRule { - - public LastVisibleCellDirectRule() { - super("SKYS-BASC-0001", "Last Visible Cell", - "There is only one cell on this row/col for this number that does not create a visibility contradiction", - "edu/rpi/legup/images/skyscrapers/rules/FixedMax.png"); - } - - /** - * 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 index of the 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) { - //last cell for number based on preemptive visibility rules - SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); - SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); - SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (initCell.getType() != SkyscrapersType.UNKNOWN || finalCell.getType() != SkyscrapersType.Number) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must transition from unknown to number"; - } - - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(false); - initialBoard.setViewFlag(true); - CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - ArrayList XCandidates = caseRule.getCasesFor(initialBoard, initialBoard.getWestClues().get(finalCell.getLocation().y), (Integer) finalCell.getData()); - ArrayList YCandidates = caseRule.getCasesFor(initialBoard, initialBoard.getNorthClues().get(finalCell.getLocation().x), (Integer) finalCell.getData()); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); - - //System.out.println(XCandidates.size()); - //System.out.println(YCandidates.size()); - - //return null if either pass, both messages otherwise - String xCheck = candidateCheck(XCandidates, puzzleElement, finalCell); - String yCheck = candidateCheck(YCandidates, puzzleElement, finalCell); - if (xCheck == null || yCheck == null) { - return null; - } - return super.getInvalidUseOfRuleMessage() + "\nRow" + xCheck + "\nCol" + yCheck; - } - - //helper to check if candidate list is valid - private String candidateCheck(ArrayList candidates, PuzzleElement puzzleElement, SkyscrapersCell finalCell) { - if (candidates.size() == 1) { - if (((SkyscrapersCell) candidates.get(0).getPuzzleElement(puzzleElement)).getType() == SkyscrapersType.Number) { - if (candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()) { - return null; - } - return ": Wrong number in the cell."; - } - return ": No case for this cell."; - } - return ": This cell is not forced."; - } - - private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { - SkyscrapersBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); - DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); - if (duplicate.checkContradictionAt(emptyCase, cell) == null) { - System.out.println("no contradiction ln"); - return true; - } - return false; - } - - /** - * 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); - SkyscrapersBoard modBoard = (SkyscrapersBoard) node.getBoard().copy(); - //System.out.println(modBoard.getPuzzleElements().size()); - for (PuzzleElement element : modBoard.getPuzzleElements()) { - SkyscrapersCell cell = (SkyscrapersCell) element; - if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { - //cell.setData(SkyscrapersType.BULB.value); - modBoard.addModifiedData(cell); - } - } - //System.out.println(modBoard.getModifiedData().isEmpty()); - if (modBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return modBoard; - } - } -} +package edu.rpi.legup.puzzle.skyscrapers.rules; + +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.tree.TreeNode; +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.SkyscrapersType; +import java.util.ArrayList; + +public class LastVisibleCellDirectRule extends DirectRule { + + public LastVisibleCellDirectRule() { + super( + "SKYS-BASC-0001", + "Last Visible Cell", + "There is only one cell on this row/col for this number that does not create a" + + " visibility contradiction", + "edu/rpi/legup/images/skyscrapers/rules/FixedMax.png"); + } + + /** + * 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 index of the 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) { + // last cell for number based on preemptive visibility rules + SkyscrapersBoard initialBoard = + (SkyscrapersBoard) transition.getParents().get(0).getBoard(); + SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); + SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); + if (initCell.getType() != SkyscrapersType.UNKNOWN + || finalCell.getType() != SkyscrapersType.Number) { + return super.getInvalidUseOfRuleMessage() + + ": Modified cells must transition from unknown to number"; + } + + // set all rules used by case rule to false except for dupe, get all cases + boolean dupeTemp = initialBoard.getDupeFlag(); + boolean viewTemp = initialBoard.getViewFlag(); + initialBoard.setDupeFlag(false); + initialBoard.setViewFlag(true); + CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); + ArrayList XCandidates = + caseRule.getCasesFor( + initialBoard, + initialBoard.getWestClues().get(finalCell.getLocation().y), + (Integer) finalCell.getData()); + ArrayList YCandidates = + caseRule.getCasesFor( + initialBoard, + initialBoard.getNorthClues().get(finalCell.getLocation().x), + (Integer) finalCell.getData()); + initialBoard.setDupeFlag(dupeTemp); + initialBoard.setViewFlag(viewTemp); + + // System.out.println(XCandidates.size()); + // System.out.println(YCandidates.size()); + + // return null if either pass, both messages otherwise + String xCheck = candidateCheck(XCandidates, puzzleElement, finalCell); + String yCheck = candidateCheck(YCandidates, puzzleElement, finalCell); + if (xCheck == null || yCheck == null) { + return null; + } + return super.getInvalidUseOfRuleMessage() + "\nRow" + xCheck + "\nCol" + yCheck; + } + + // helper to check if candidate list is valid + private String candidateCheck( + ArrayList candidates, PuzzleElement puzzleElement, SkyscrapersCell finalCell) { + if (candidates.size() == 1) { + if (((SkyscrapersCell) candidates.get(0).getPuzzleElement(puzzleElement)).getType() + == SkyscrapersType.Number) { + if (candidates.get(0).getPuzzleElement(puzzleElement).getData() + == finalCell.getData()) { + return null; + } + return ": Wrong number in the cell."; + } + return ": No case for this cell."; + } + return ": This cell is not forced."; + } + + private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { + SkyscrapersBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (duplicate.checkContradictionAt(emptyCase, cell) == null) { + System.out.println("no contradiction ln"); + return true; + } + return false; + } + + /** + * 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) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); + SkyscrapersBoard modBoard = (SkyscrapersBoard) node.getBoard().copy(); + // System.out.println(modBoard.getPuzzleElements().size()); + for (PuzzleElement element : modBoard.getPuzzleElements()) { + SkyscrapersCell cell = (SkyscrapersCell) element; + if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { + // cell.setData(SkyscrapersType.BULB.value); + modBoard.addModifiedData(cell); + } + } + // System.out.println(modBoard.getModifiedData().isEmpty()); + if (modBoard.getModifiedData().isEmpty()) { + return null; + } else { + return modBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java index e5524418d..da1057b56 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/LastVisibleNumberDirectRule.java @@ -1,98 +1,104 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.tree.TreeNode; -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.SkyscrapersType; - -import java.util.ArrayList; - -public class LastVisibleNumberDirectRule extends DirectRule { - - public LastVisibleNumberDirectRule() { - super("SKYS-BASC-0005", "Last Visible Number", - "There is only one number for this cell that does not create a visibility contradiction", - "edu/rpi/legup/images/skyscrapers/rules/OneEdge.png"); - } - - /** - * 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 index of the 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) { - //last number for cell based upon preemptive visibility rules - SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); - SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); - SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (initCell.getType() != SkyscrapersType.UNKNOWN || finalCell.getType() != SkyscrapersType.Number) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must transition from unknown to number"; - } - - //set all rules used by case rule to false except for dupe, get all cases - boolean dupeTemp = initialBoard.getDupeFlag(); - boolean viewTemp = initialBoard.getViewFlag(); - initialBoard.setDupeFlag(false); - initialBoard.setViewFlag(true); - NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); - ArrayList candidates = caseRule.getCases(initialBoard, puzzleElement); - initialBoard.setDupeFlag(dupeTemp); - initialBoard.setViewFlag(viewTemp); - - //check if given value is the only remaining value - if (candidates.size() == 1) { - if (candidates.get(0).getPuzzleElement(puzzleElement).getData() == finalCell.getData()) { - return null; - } - return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; - } - return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; - } - - private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { - SkyscrapersBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); - DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); - if (duplicate.checkContradictionAt(emptyCase, cell) == null) { - System.out.println("no contradiction ln"); - return true; - } - return false; - } - - /** - * 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); - SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - //System.out.println(lightUpBoard.getPuzzleElements().size()); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - SkyscrapersCell cell = (SkyscrapersCell) element; - if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { - //cell.setData(SkyscrapersType.BULB.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.skyscrapers.rules; + +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.tree.TreeNode; +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.SkyscrapersType; +import java.util.ArrayList; + +public class LastVisibleNumberDirectRule extends DirectRule { + + public LastVisibleNumberDirectRule() { + super( + "SKYS-BASC-0005", + "Last Visible Number", + "There is only one number for this cell that does not create a visibility" + + " contradiction", + "edu/rpi/legup/images/skyscrapers/rules/OneEdge.png"); + } + + /** + * 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 index of the 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) { + // last number for cell based upon preemptive visibility rules + SkyscrapersBoard initialBoard = + (SkyscrapersBoard) transition.getParents().get(0).getBoard(); + SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); + SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); + if (initCell.getType() != SkyscrapersType.UNKNOWN + || finalCell.getType() != SkyscrapersType.Number) { + return super.getInvalidUseOfRuleMessage() + + ": Modified cells must transition from unknown to number"; + } + + // set all rules used by case rule to false except for dupe, get all cases + boolean dupeTemp = initialBoard.getDupeFlag(); + boolean viewTemp = initialBoard.getViewFlag(); + initialBoard.setDupeFlag(false); + initialBoard.setViewFlag(true); + NumberForCellCaseRule caseRule = new NumberForCellCaseRule(); + ArrayList candidates = caseRule.getCases(initialBoard, puzzleElement); + initialBoard.setDupeFlag(dupeTemp); + initialBoard.setViewFlag(viewTemp); + + // check if given value is the only remaining value + if (candidates.size() == 1) { + if (candidates.get(0).getPuzzleElement(puzzleElement).getData() + == finalCell.getData()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + ": Wrong number in the cell."; + } + return super.getInvalidUseOfRuleMessage() + ":This cell is not forced."; + } + + private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { + SkyscrapersBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (duplicate.checkContradictionAt(emptyCase, cell) == null) { + System.out.println("no contradiction ln"); + return true; + } + return false; + } + + /** + * 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) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); + SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); + // System.out.println(lightUpBoard.getPuzzleElements().size()); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + SkyscrapersCell cell = (SkyscrapersCell) element; + if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { + // cell.setData(SkyscrapersType.BULB.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java index 78533a819..bbc202e72 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NEdgeDirectRule.java @@ -1,99 +1,106 @@ -package edu.rpi.legup.puzzle.skyscrapers.rules; - -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.tree.TreeNode; -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.SkyscrapersType; - -import java.awt.Point; - -public class NEdgeDirectRule extends DirectRule { - - public NEdgeDirectRule() { - super("SKYS-BASC-0004", "N Edge", - "If the maximum number appears on an edge, the row or column's numbers appear in ascending order, starting at that edge.", - "edu/rpi/legup/images/skyscrapers/rules/NEdge.png"); - } - - /** - * 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 index of the 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) transition.getParents().get(0).getBoard(); - SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); - SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); - SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(initCell.getType() == SkyscrapersType.UNKNOWN && finalCell.getType() == SkyscrapersType.Number)) { - return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; - } - - SkyscrapersBoard emptyCase = initialBoard.copy(); - emptyCase.getPuzzleElement(finalCell).setData(SkyscrapersType.UNKNOWN.value); - Point loc = finalCell.getLocation(); - int max = initialBoard.getHeight(); - - if (initialBoard.getWestClues().get(loc.y).getData() == max && finalCell.getData() == loc.x + 1) { - return null; - } - if (initialBoard.getEastClues().get(loc.y).getData() == max && finalCell.getData() == max - loc.x) { - return null; - } - if (initialBoard.getNorthClues().get(loc.x).getData() == max && finalCell.getData() == loc.y + 1) { - return null; - } - if (initialBoard.getSouthClues().get(loc.x).getData() == max && finalCell.getData() == max - loc.y) { - return null; - } - - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced."; - - } - - private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { - SkyscrapersBoard emptyCase = board.copy(); - emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); - DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); - if (duplicate.checkContradictionAt(emptyCase, cell) == null) { - System.out.println("no contradiction ln"); - return true; - } - return false; - } - - /** - * 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) { - SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); - SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); - //System.out.println(lightUpBoard.getPuzzleElements().size()); - for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { - SkyscrapersCell cell = (SkyscrapersCell) element; - if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { - //cell.setData(SkyscrapersType.BULB.value); - lightUpBoard.addModifiedData(cell); - } - } - if (lightUpBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return lightUpBoard; - } - } -} +package edu.rpi.legup.puzzle.skyscrapers.rules; + +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.tree.TreeNode; +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.SkyscrapersType; +import java.awt.Point; + +public class NEdgeDirectRule extends DirectRule { + + public NEdgeDirectRule() { + super( + "SKYS-BASC-0004", + "N Edge", + "If the maximum number appears on an edge, the row or column's numbers appear in" + + " ascending order, starting at that edge.", + "edu/rpi/legup/images/skyscrapers/rules/NEdge.png"); + } + + /** + * 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 index of the 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) { + SkyscrapersBoard initialBoard = + (SkyscrapersBoard) transition.getParents().get(0).getBoard(); + SkyscrapersCell initCell = (SkyscrapersCell) initialBoard.getPuzzleElement(puzzleElement); + SkyscrapersBoard finalBoard = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell finalCell = (SkyscrapersCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == SkyscrapersType.UNKNOWN + && finalCell.getType() == SkyscrapersType.Number)) { + return super.getInvalidUseOfRuleMessage() + ": Modified cells must be number"; + } + + SkyscrapersBoard emptyCase = initialBoard.copy(); + emptyCase.getPuzzleElement(finalCell).setData(SkyscrapersType.UNKNOWN.value); + Point loc = finalCell.getLocation(); + int max = initialBoard.getHeight(); + + if (initialBoard.getWestClues().get(loc.y).getData() == max + && finalCell.getData() == loc.x + 1) { + return null; + } + if (initialBoard.getEastClues().get(loc.y).getData() == max + && finalCell.getData() == max - loc.x) { + return null; + } + if (initialBoard.getNorthClues().get(loc.x).getData() == max + && finalCell.getData() == loc.y + 1) { + return null; + } + if (initialBoard.getSouthClues().get(loc.x).getData() == max + && finalCell.getData() == max - loc.y) { + return null; + } + + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced."; + } + + private boolean isForced(SkyscrapersBoard board, SkyscrapersCell cell) { + SkyscrapersBoard emptyCase = board.copy(); + emptyCase.getPuzzleElement(cell).setData(SkyscrapersType.UNKNOWN.value); + DuplicateNumberContradictionRule duplicate = new DuplicateNumberContradictionRule(); + if (duplicate.checkContradictionAt(emptyCase, cell) == null) { + System.out.println("no contradiction ln"); + return true; + } + return false; + } + + /** + * 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) { + SkyscrapersBoard initialBoard = (SkyscrapersBoard) node.getBoard(); + SkyscrapersBoard lightUpBoard = (SkyscrapersBoard) node.getBoard().copy(); + // System.out.println(lightUpBoard.getPuzzleElements().size()); + for (PuzzleElement element : lightUpBoard.getPuzzleElements()) { + SkyscrapersCell cell = (SkyscrapersCell) element; + if (cell.getType() == SkyscrapersType.UNKNOWN && isForced(initialBoard, cell)) { + // cell.setData(SkyscrapersType.BULB.value); + lightUpBoard.addModifiedData(cell); + } + } + if (lightUpBoard.getModifiedData().isEmpty()) { + return null; + } else { + return lightUpBoard; + } + } +} 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 02000cd6e..145dd6ee2 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,9 +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.*; import java.util.ArrayList; import java.util.HashSet; @@ -19,7 +17,9 @@ public class NumberForCellCaseRule extends CaseRule { public NumberForCellCaseRule() { - super("SKYS-CASE-0001", "Number For Cell", + super( + "SKYS-CASE-0001", + "Number For Cell", "A blank cell must have height of 1 to n.", "edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png"); } @@ -40,7 +40,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement puzzleElement to determine the possible cases for * @return a list of elements the specified could be */ @@ -59,17 +59,18 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { newCell.setData(i); newCase.addModifiedData(newCell); - //if flags + // if flags boolean passed = true; if (skyscrapersboard.getDupeFlag()) { DuplicateNumberContradictionRule DupeRule = new DuplicateNumberContradictionRule(); passed = passed && DupeRule.checkContradictionAt(newCase, newCell) != null; } if (skyscrapersboard.getViewFlag()) { - PreemptiveVisibilityContradictionRule ViewRule = new PreemptiveVisibilityContradictionRule(); + PreemptiveVisibilityContradictionRule ViewRule = + new PreemptiveVisibilityContradictionRule(); passed = passed && ViewRule.checkContradictionAt(newCase, newCell) != null; } - //how should unresolved be handled? should it be? + // how should unresolved be handled? should it be? if (passed) { cases.add(newCase); } @@ -89,43 +90,55 @@ public String checkRuleRaw(TreeTransition transition) { List childTransitions = transition.getParents().get(0).getChildren(); if (childTransitions.size() == 0) { return "This case rule must have at least one child."; - } - else { - if (childTransitions.size() != getCases(transition.getBoard(), childTransitions.get(0).getBoard().getModifiedData().iterator().next()).size()) { + } else { + if (childTransitions.size() + != getCases( + transition.getBoard(), + childTransitions + .get(0) + .getBoard() + .getModifiedData() + .iterator() + .next()) + .size()) { return "Wrong number of children."; } } - - //TreeTransition case1 = childTransitions.get(0); - //TreeTransition case2 = childTransitions.get(1); + // TreeTransition case1 = childTransitions.get(0); + // TreeTransition case2 = childTransitions.get(1); TreeTransition case1 = childTransitions.get(0); - SkyscrapersCell mod1 = (SkyscrapersCell) case1.getBoard().getModifiedData().iterator().next(); + SkyscrapersCell mod1 = + (SkyscrapersCell) case1.getBoard().getModifiedData().iterator().next(); for (int i = 0; i < childTransitions.size(); i++) { TreeTransition case2 = childTransitions.get(i); if (case2.getBoard().getModifiedData().size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case."; } - SkyscrapersCell mod2 = (SkyscrapersCell) case2.getBoard().getModifiedData().iterator().next(); + SkyscrapersCell mod2 = + (SkyscrapersCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; } if (!(mod2.getType() == SkyscrapersType.Number)) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must assign a number."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must assign a number."; } } - //System.out.println("no contradiction"); + // System.out.println("no contradiction"); return null; } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 transition transition to check * @param puzzleElement index of the puzzleElement - * @return null if the child node logically follow from the parent node at the specified puzzleElement, - * otherwise error message + * @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) { @@ -133,28 +146,28 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * 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 + * 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 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 + * @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); + 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)); + cells.addAll(puzzleBoard.getRowCol(point.getLocation().x, SkyscrapersType.ANY, false)); + cells.addAll(puzzleBoard.getRowCol(point.getLocation().y, SkyscrapersType.ANY, true)); } for (SkyscrapersCell cell : cells) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java index 1be26f49a..dc274eb15 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/PreemptiveVisibilityContradictionRule.java @@ -6,28 +6,29 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - import java.awt.*; -import java.util.Queue; import java.util.LinkedList; import java.util.List; +import java.util.Queue; public class PreemptiveVisibilityContradictionRule extends ContradictionRule { public PreemptiveVisibilityContradictionRule() { - super("SKYS-CONT-0006", "Preemptive Visibility", + super( + "SKYS-CONT-0006", + "Preemptive Visibility", "Visibility constraints are not met given an incomplete row/col", "edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png"); } /** - * Checks whether there is an instance of a visibility contradiction in every possible row/col based on the specific - * puzzleElement index using this rule + * Checks whether there is an instance of a visibility contradiction in every possible row/col + * based on the specific puzzleElement index using this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement - * @return null if the all possible rows/cols contain a contradiction at the specified puzzleElement, - * otherwise error message + * @return null if the all possible rows/cols contain a contradiction at the specified + * puzzleElement, otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -35,12 +36,13 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; Point loc = cell.getLocation(); - //Initialize instances of necessary contradiction and case rules - InsufficientVisibilityContradictionRule tooFew = new InsufficientVisibilityContradictionRule(); + // Initialize instances of necessary contradiction and case rules + InsufficientVisibilityContradictionRule tooFew = + new InsufficientVisibilityContradictionRule(); ExceedingVisibilityContradictionRule tooMany = new ExceedingVisibilityContradictionRule(); CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); - //Initialize skyscraperBoard queues for rows and cols + // Initialize skyscraperBoard queues for rows and cols Queue rowQ = new LinkedList<>(); rowQ.add(skyscrapersBoard); Queue colQ = new LinkedList<>(); @@ -48,18 +50,18 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { // find all cases for the corresponding row and column for each possible skyscraper height - //Add every possible case for all heights for each corresponding row and column + // Add every possible case for all heights for each corresponding row and column for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { int num = i + 1; - //check row west clue + // check row west clue List rows; int size = rowQ.size(); for (int j = 0; j < size; j++) { - SkyscrapersBoard temp = rowQ.poll(); //get row from the top of the stack + SkyscrapersBoard temp = rowQ.poll(); // get row from the top of the stack - //don't do anything if already in row + // don't do anything if already in row boolean exists = false; for (SkyscrapersCell c : temp.getRowCol(loc.y, SkyscrapersType.Number, true)) { if (c.getData() == num) { @@ -70,36 +72,37 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (exists) { rowQ.add(temp); - } - else { - //set flags + } else { + // set flags boolean dupeTemp = temp.getDupeFlag(); boolean viewTemp = temp.getViewFlag(); temp.setDupeFlag(false); temp.setViewFlag(false); - //get all cases for corresponding row based on west clue - rows = caseRule.getCasesFor(temp, skyscrapersBoard.getWestClues().get(loc.y), num); + // get all cases for corresponding row based on west clue + rows = + caseRule.getCasesFor( + temp, skyscrapersBoard.getWestClues().get(loc.y), num); - //reset flags + // reset flags temp.setDupeFlag(dupeTemp); temp.setViewFlag(viewTemp); - //add all row cases to row queue + // add all row cases to row queue for (Board k : rows) { rowQ.add((SkyscrapersBoard) k); } } } - //check col north clue + // check col north clue List cols; size = colQ.size(); for (int j = 0; j < size; j++) { - SkyscrapersBoard temp = colQ.poll(); //get row from the top of the stack + SkyscrapersBoard temp = colQ.poll(); // get row from the top of the stack - //don't do anything if already in col + // don't do anything if already in col boolean exists = false; for (SkyscrapersCell c : temp.getRowCol(loc.x, SkyscrapersType.Number, false)) { if (c.getData() == num) { @@ -110,22 +113,23 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (exists) { colQ.add(temp); - } - else { - //set flags + } else { + // set flags boolean dupeTemp = temp.getDupeFlag(); boolean viewTemp = temp.getViewFlag(); temp.setDupeFlag(false); temp.setViewFlag(false); - //get all cases for corresponding col based on north clue - cols = caseRule.getCasesFor(temp, skyscrapersBoard.getNorthClues().get(loc.x), num); + // get all cases for corresponding col based on north clue + cols = + caseRule.getCasesFor( + temp, skyscrapersBoard.getNorthClues().get(loc.x), num); - //reset flags + // reset flags temp.setDupeFlag(dupeTemp); temp.setViewFlag(viewTemp); - //add all row cases to row queue + // add all row cases to row queue for (Board k : cols) { colQ.add((SkyscrapersBoard) k); } @@ -136,16 +140,24 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { String rowTooFew; String rowTooMany; boolean rowContradiction = true; - //check if each case board has a contradiction + // check if each case board has a contradiction while (rowQ.size() > 0) { SkyscrapersBoard fullRow = rowQ.poll(); - //checks if there is a contradiction given the row based on the west clue - rowTooFew = tooFew.checkContradictionAt(fullRow, cell); // is cell the correct puzzle element to check? + // checks if there is a contradiction given the row based on the west clue + rowTooFew = + tooFew.checkContradictionAt( + fullRow, cell); // is cell the correct puzzle element to check? rowTooMany = tooMany.checkContradictionAt(fullRow, cell); - //boolean that checks if there is a contradiction within all rows - rowContradiction = rowContradiction && (rowTooFew == null || rowTooMany == null);// !null means there isn't a contradiction, so there must be a valid permutation of the array + // boolean that checks if there is a contradiction within all rows + rowContradiction = + rowContradiction + && (rowTooFew == null + || rowTooMany + == null); // !null means there isn't a contradiction, so + // there must be a valid + // permutation of the array } String colTooFew; @@ -154,15 +166,16 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { while (colQ.size() > 0) { SkyscrapersBoard fullCol = colQ.poll(); - //checks if there is a contradiction given the col baesd on the north clue + // checks if there is a contradiction given the col baesd on the north clue colTooFew = tooFew.checkContradictionAt(fullCol, cell); colTooMany = tooMany.checkContradictionAt(fullCol, cell); - //boolean that checks if there is a contradiction within all the cols + // boolean that checks if there is a contradiction within all the cols colContradiction = colContradiction && (colTooFew == null || colTooMany == null); } - //if every possible permutation results in contradictions return null, else no contradiction + // if every possible permutation results in contradictions return null, else no + // contradiction if (rowContradiction || colContradiction) { return null; } @@ -179,7 +192,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { public String checkContradiction(Board board) { SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) + // checks the middle diagonal (checkContradictionAt checks row/col off each) String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i, i)); if (checkStr == null) { return checkStr; @@ -187,6 +200,4 @@ public String checkContradiction(Board board) { } return "No instance of the contradiction " + this.ruleName + " here"; } - - } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java index ada47efde..d4a3d0486 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedCellContradictionRule.java @@ -3,30 +3,27 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.ContradictionRule; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - import java.awt.*; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; public class UnresolvedCellContradictionRule extends ContradictionRule { public UnresolvedCellContradictionRule() { - super("SKYS-CONT-0004", "Unresolved Cell", + super( + "SKYS-CONT-0004", + "Unresolved Cell", "Elimination leaves no possible number for a cell.", "edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java index 21a48caaf..fed429988 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/UnresolvedNumberContradictionRule.java @@ -1,34 +1,33 @@ package edu.rpi.legup.puzzle.skyscrapers.rules; - import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; - import java.awt.*; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; public class UnresolvedNumberContradictionRule extends ContradictionRule { public UnresolvedNumberContradictionRule() { - super("SKYS-CONT-0005", "Unresolved Number", + super( + "SKYS-CONT-0005", + "Unresolved Number", "No possible cell for a number without a duplicate contradiction.", - //specify a number? defaulting to every number for now. expand to more than duplicate? + // specify a number? defaulting to every number for now. expand to more than + // duplicate? "edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -39,39 +38,45 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { CellForNumberCaseRule caseRule = new CellForNumberCaseRule(); for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { int num = i + 1; - //check row - //isn't already present + // check row + // isn't already present boolean exists = false; - for (SkyscrapersCell presentCell : skyscrapersBoard.getRowCol(loc.y, SkyscrapersType.Number, true)) { + for (SkyscrapersCell presentCell : + skyscrapersBoard.getRowCol(loc.y, SkyscrapersType.Number, true)) { if (presentCell.getData() == num) { exists = true; break; } } if (!exists) { - //and no possible cases - if (caseRule.getCasesFor(board, skyscrapersBoard.getWestClues().get(loc.y), num).size() == 0) { + // and no possible cases + if (caseRule.getCasesFor(board, skyscrapersBoard.getWestClues().get(loc.y), num) + .size() + == 0) { return null; } } - //check col - //same process as for row + // check col + // same process as for row exists = false; - for (SkyscrapersCell presentCell : skyscrapersBoard.getRowCol(loc.x, SkyscrapersType.Number, false)) { + for (SkyscrapersCell presentCell : + skyscrapersBoard.getRowCol(loc.x, SkyscrapersType.Number, false)) { if (presentCell.getData() == num) { exists = true; break; } } if (!exists) { - if (caseRule.getCasesFor(board, skyscrapersBoard.getNorthClues().get(loc.x), num).size() == 0) { + if (caseRule.getCasesFor(board, skyscrapersBoard.getNorthClues().get(loc.x), num) + .size() + == 0) { return null; } } } - //System.out.print("Does not contain a contradiction at this index"); + // System.out.print("Does not contain a contradiction at this index"); return super.getNoContradictionMessage(); } @@ -85,7 +90,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { public String checkContradiction(Board board) { SkyscrapersBoard skyscrapersBoard = (SkyscrapersBoard) board; for (int i = 0; i < skyscrapersBoard.getWidth(); i++) { - //checks the middle diagonal (checkContradictionAt checks row/col off each) + // checks the middle diagonal (checkContradictionAt checks row/col off each) String checkStr = checkContradictionAt(board, skyscrapersBoard.getCell(i, i)); if (checkStr == null) { return null; @@ -94,4 +99,3 @@ public String checkContradiction(Board board) { return "No instance of the contradiction " + this.ruleName + " here"; } } - diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/GroupType.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/GroupType.java index a716258fa..a7cd9ac12 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/GroupType.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/GroupType.java @@ -1,5 +1,7 @@ package edu.rpi.legup.puzzle.sudoku; public enum GroupType { - REGION, ROW, COLUMN + REGION, + ROW, + COLUMN } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java index 8af2aca2e..0b6971235 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java @@ -3,7 +3,6 @@ import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.puzzle.sudoku.rules.PossibleNumberCaseRule; - import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.Set; @@ -15,8 +14,8 @@ public class PossibleNumberCaseBoard extends CaseBoard { private Set pickableRows; private Set pickableCols; - - public PossibleNumberCaseBoard(SudokuBoard baseBoard, PossibleNumberCaseRule caseRule, SudokuCell cell) { + public PossibleNumberCaseBoard( + SudokuBoard baseBoard, PossibleNumberCaseRule caseRule, SudokuCell cell) { super(baseBoard, caseRule); this.cell = cell; this.pickableRegions = new HashSet<>(); @@ -37,16 +36,14 @@ public boolean isPickable(PuzzleElement puzzleElement, MouseEvent e) { return true; } } - } - else { + } else { if (e.isControlDown()) { for (int c : pickableCols) { if (c == sudokuCell.getLocation().x) { return true; } } - } - else { + } else { for (int r : pickableRegions) { if (r == sudokuCell.getGroupIndex()) { return true; diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java index ac082e427..877c92665 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java @@ -9,9 +9,7 @@ public class Sudoku extends Puzzle { private SudokuView boardView; - /** - * Sudoku Constructor - */ + /** Sudoku Constructor */ public Sudoku() { super(); @@ -27,9 +25,7 @@ public BoardView getBoardView() { return boardView; } - /** - * Initializes the game board - */ + /** Initializes the game board */ @Override public void initializeView() { boardView = new SudokuView((SudokuBoard) currentBoard); @@ -50,8 +46,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Sudoku * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Sudoku, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -66,7 +62,8 @@ public boolean isValidDimensions(int rows, int columns) { } // For Sudoku, the number of rows and columns must be a perfect square - // Note: we don't need to check the columns since by this point, we have verified that the number of rows + // Note: we don't need to check the columns since by this point, we have verified that the + // number of rows // equals the number of columns double sqrtRows = Math.sqrt(rows); if (sqrtRows - Math.floor(sqrtRows) != 0) { @@ -107,7 +104,5 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuBoard.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuBoard.java index ed3048c88..46e6020bb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuBoard.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.util.HashSet; import java.util.Set; @@ -35,23 +34,23 @@ public SudokuCell getCell(int x, int y) { } /** - * Gets the SudokuCell in the specified group index at the x and y location given - * The group index must be by less than the width (or height) of the board and the - * x and y location is relative to the group. This means the x and y values must be - * less the square root of the width (or height) of the board. + * Gets the SudokuCell in the specified group index at the x and y location given The group + * index must be by less than the width (or height) of the board and the x and y location is + * relative to the group. This means the x and y values must be less the square root of the + * width (or height) of the board. * * @param groupIndex group index of the cell - * @param x x location relative to the group - * @param y y location relative to the group + * @param x x location relative to the group + * @param y y location relative to the group * @return cell in the specified group index at the given x and y location */ public SudokuCell getCell(int groupIndex, int x, int y) { - return getCell(x + (groupIndex % groupSize) * groupSize, y + (groupIndex / groupSize) * groupSize); + return getCell( + x + (groupIndex % groupSize) * groupSize, y + (groupIndex / groupSize) * groupSize); } /** - * Gets the size of the sudoku board - * Standard board is 9x9 + * Gets the size of the sudoku board Standard board is 9x9 * * @return size of the board */ @@ -60,8 +59,7 @@ public int getSize() { } /** - * Gets the minor group size of the sudoku board - * Standard board is 3x3x3x3 + * Gets the minor group size of the sudoku board Standard board is 3x3x3x3 * * @return minor group size */ @@ -139,8 +137,8 @@ public Set getPossibleValues(SudokuCell cell) { } /** - * Called when a {@link PuzzleElement} data on this has changed and passes in the equivalent puzzle element with - * the new data. + * Called when a {@link PuzzleElement} data on this has changed and passes in the equivalent + * puzzle element with the new data. * * @param puzzleElement equivalent puzzle element with the new data. */ diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java index 0e1595d03..006e6c0a5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.sudoku; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; import java.util.HashSet; import java.util.Set; @@ -14,10 +13,10 @@ public class SudokuCell extends GridCell { /** * SudokuCell Constructor - creates a new Sudoku cell to hold the puzzleElement * - * @param value value of the sudoku cell - * @param location location of the cell on the board + * @param value value of the sudoku cell + * @param location location of the cell on the board * @param groupIndex index of the group the cell is in on the board - * @param size size of the sudoku cell + * @param size size of the sudoku cell */ public SudokuCell(int value, Point location, int groupIndex, int size) { super(value, location); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java index 18b6cd0a8..9b24f13da 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java @@ -1,46 +1,37 @@ package edu.rpi.legup.puzzle.sudoku; -/*import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; - - -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.puzzle.treetent.TreeTentCell;*/ - import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - - import java.awt.event.MouseEvent; public class SudokuCellController extends ElementController { @Override - public void changeCell(MouseEvent e, PuzzleElement data) { SudokuCell cell = (SudokuCell) data; System.out.print(111); if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); - } - else { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { if (cell.getData() < cell.getMax()) { data.setData(cell.getData() + 1); - } - else { + } else { data.setData(0); } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() > 0) { data.setData(cell.getData() - 1); - } - else { + } else { data.setData(cell.getMax()); } } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellFactory.java index 90f84d519..1ef24ea59 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class SudokuCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -23,7 +22,8 @@ public class SudokuCellFactory extends ElementFactory { public SudokuCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("Sudoku Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "Sudoku Factory: unknown puzzleElement puzzleElement"); } SudokuBoard sudokuBoard = (SudokuBoard) board; @@ -44,11 +44,10 @@ public SudokuCell importCell(Node node, Board board) throws InvalidFileFormatExc SudokuCell cell = new SudokuCell(value, new Point(x, y), groupIndex, size); cell.setIndex(y * size + x); return cell; - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Sudoku Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Sudoku Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("Sudoku Factory: could not find attribute(s)"); } } @@ -56,7 +55,7 @@ public SudokuCell importCell(Node node, Board board) throws InvalidFileFormatExc /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuElementView.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuElementView.java index 46d7e4ec7..c3d236b53 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuElementView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.model.gameboard.GridCell; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class SudokuElementView extends GridElementView { @@ -50,18 +49,24 @@ public void drawElement(Graphics2D graphics2D) { FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(val); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); graphics2D.drawString(value, xText, yText); - } - else { - boolean annotate = LegupPreferences.getInstance().getUserPref(LegupPreferences.SHOW_ANNOTATIONS).equalsIgnoreCase(Boolean.toString(true)); + } else { + boolean annotate = + LegupPreferences.getInstance() + .getUserPref(LegupPreferences.SHOW_ANNOTATIONS) + .equalsIgnoreCase(Boolean.toString(true)); if (annotate) { graphics2D.setColor(FONT_COLOR); graphics2D.setFont(ANNOTATE_FONT); FontMetrics metrics = graphics2D.getFontMetrics(FONT); String value = String.valueOf(cell.getAnnotations()); int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + int yText = + location.y + + ((size.height - metrics.getHeight()) / 2) + + metrics.getAscent(); graphics2D.drawString(value, xText, yText); } } 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 45e320293..f10aabae1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuExporter.java @@ -2,7 +2,6 @@ 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 { @@ -16,8 +15,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { SudokuBoard board; if (puzzle.getTree() != null) { board = (SudokuBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (SudokuBoard) puzzle.getBoardView().getBoard(); } @@ -28,7 +26,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { SudokuCell cell = (SudokuCell) puzzleElement; if (cell.getData() != 0) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } 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 dccec52d4..68bf1e795 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class SudokuImporter extends PuzzleImporter { public SudokuImporter(Sudoku sudoku) { super(sudoku); @@ -26,7 +25,7 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @@ -61,11 +60,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("Sudoku Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "Sudoku Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("Sudoku Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "Sudoku Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -77,16 +78,19 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { size = Integer.valueOf(boardElement.getAttribute("size")); minorSize = (int) Math.sqrt(size); if (minorSize * minorSize != size) { - throw new InvalidFileFormatException("Sudoku Importer: invalid board dimensions"); + throw new InvalidFileFormatException( + "Sudoku Importer: invalid board dimensions"); } sudokuBoard = new SudokuBoard(size); - } - else { + } else { throw new InvalidFileFormatException("Sudoku Importer: invalid board dimensions"); } for (int i = 0; i < elementDataList.getLength(); i++) { - SudokuCell cell = (SudokuCell) puzzle.getFactory().importCell(elementDataList.item(i), sudokuBoard); + SudokuCell cell = + (SudokuCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), sudokuBoard); Point loc = cell.getLocation(); if (cell.getData() != 0) { cell.setModifiable(false); @@ -106,20 +110,21 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } } -// -// for(int y = 0; y < size; y++) -// { -// for(int x = 0; x < size; x++) -// { -// SudokuCell cell = sudokuBoard.getCell(x, y); -// System.err.println("(" + x + ", " + y + ") - " + cell.getGroupIndex()); -// } -// } + // + // for(int y = 0; y < size; y++) + // { + // for(int x = 0; x < size; x++) + // { + // SudokuCell cell = sudokuBoard.getCell(x, y); + // System.err.println("(" + x + ", " + y + ") - " + + // cell.getGroupIndex()); + // } + // } puzzle.setCurrentBoard(sudokuBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("Sudoku Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Sudoku Importer: unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java index bb25c3e21..aa58f9a23 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java @@ -5,10 +5,9 @@ import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.GridBoardView; import edu.rpi.legup.ui.boardview.SelectionItemView; - -import javax.swing.*; import java.awt.*; import java.util.Set; +import javax.swing.*; public class SudokuView extends GridBoardView { private static final Color STROKE_COLOR = new Color(0, 0, 0); @@ -21,8 +20,10 @@ public SudokuView(SudokuBoard board) { int minorSize = (int) Math.sqrt(gridSize.width); for (int i = 0; i < gridSize.height; i++) { for (int k = 0; k < gridSize.width; k++) { - Point location = new Point(k * elementSize.width + (k / minorSize) * 4 + 5, - i * elementSize.height + (i / minorSize) * 4 + 5); + Point location = + new Point( + k * elementSize.width + (k / minorSize) * 4 + 5, + i * elementSize.height + (i / minorSize) * 4 + 5); SudokuElementView element = new SudokuElementView(board.getCell(k, i)); element.setIndex(i * gridSize.width + k); element.setSize(elementSize); @@ -30,12 +31,10 @@ public SudokuView(SudokuBoard board) { elementViews.add(element); } } - } /** - * Gets the SudokuElementView from the puzzleElement index or - * null if out of bounds + * Gets the SudokuElementView from the puzzleElement index or null if out of bounds * * @param index index of the ElementView * @return SudokuElementView at the specified index @@ -49,7 +48,9 @@ public void drawGrid(Graphics2D graphics2D) { int minorSize = (int) Math.sqrt(gridSize.width); graphics2D.setColor(STROKE_COLOR); graphics2D.setStroke(MAJOR_STOKE); - graphics2D.drawRect(3, 3, + graphics2D.drawRect( + 3, + 3, gridSize.width * (elementSize.width + 1) + 3, gridSize.height * (elementSize.height + 1) + 3); @@ -81,8 +82,7 @@ public void drawBoard(Graphics2D graphics2D) { ElementView element = elementViews.get(i * gridSize.height + k); if (!element.isHover()) { element.draw(graphics2D); - } - else { + } else { hover = element; } } @@ -107,8 +107,7 @@ public void drawCaseBoard(Graphics2D graphics2D) { ElementView element = elementViews.get(i * gridSize.height + k); if (!element.isHover()) { element.draw(graphics2D); - } - else { + } else { hover = element; } } @@ -117,7 +116,10 @@ public void drawCaseBoard(Graphics2D graphics2D) { graphics2D.setColor(new Color(0x1A, 0x23, 0x7E, 200)); for (int r : caseBoard.getPickableRegions()) { Set region = sudokuBoard.getRegion(r); - int x = Integer.MAX_VALUE, y = Integer.MAX_VALUE, w = Integer.MIN_VALUE, h = Integer.MIN_VALUE; + int x = Integer.MAX_VALUE, + y = Integer.MAX_VALUE, + w = Integer.MIN_VALUE, + h = Integer.MIN_VALUE; for (SudokuCell c : region) { x = Math.min(x, c.getLocation().x); y = Math.min(y, c.getLocation().y); @@ -134,8 +136,8 @@ public void drawCaseBoard(Graphics2D graphics2D) { graphics2D.fillRect(x + 4, y + 4, w - 8, h - 8); } -// if(hover != null) -// hover.draw(graphics2D); + // if(hover != null) + // hover.draw(graphics2D); } protected Dimension getProperSize() { @@ -151,10 +153,14 @@ public DataSelectionView getSelectionPopupMenu() { selectionView.setLayout(layout); for (int r = 1; r <= 3; r++) { for (int c = 1; c <= 3; c++) { - SudokuElementView element = new SudokuElementView(new SudokuCell((r - 1) * 3 + c, null, 0, gridSize.width)); + SudokuElementView element = + new SudokuElementView( + new SudokuCell((r - 1) * 3 + c, null, 0, gridSize.width)); element.setSize(new Dimension(32, 32)); element.setLocation(new Point(0, 0)); - SelectionItemView item = new SelectionItemView(element.getPuzzleElement(), new ImageIcon(element.getImage())); + SelectionItemView item = + new SelectionItemView( + element.getPuzzleElement(), new ImageIcon(element.getImage())); item.addActionListener(elementController); item.setHorizontalTextPosition(SwingConstants.CENTER); selectionView.add(item); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java index e3159d87c..12183d70d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java @@ -23,6 +23,4 @@ public int getTileNumber() { public void setTileNumber(int num) { object_num = num; } - - } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java index 0be30fa36..190679b41 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java @@ -1,96 +1,99 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; - -public class AdvancedDeductionDirectRule extends DirectRule { - - public AdvancedDeductionDirectRule() { - super("SUDO-BASC-0001", "Advanced Deduction", - "Use of group logic deduces more answers by means of forced by Location and forced by Deduction", - "edu/rpi/legup/images/sudoku/AdvancedDeduction.png"); - } - - /** - * 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 - */ - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); - SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); - - SudokuCell cell = (SudokuCell) finalBoard.getPuzzleElement(puzzleElement); - int index = cell.getIndex(); - int groupSize = initialBoard.getWidth(); - int groupDim = (int) Math.sqrt(groupSize); - int rowIndex = index / groupSize; - int colIndex = index % groupSize; - int relX = rowIndex / groupDim; - int relY = colIndex % groupDim; - int groupNum = rowIndex / groupDim * groupDim + colIndex / groupDim; - boolean[][] possible = new boolean[groupDim][groupDim]; - for (int y = 0; y < groupDim; y++) { - for (int x = 0; x < groupDim; x++) { - SudokuCell c = initialBoard.getCell(groupNum, x, y); - if (c.getData() == cell.getData() && x != relX && y != relY) { - return super.getRuleName() + ": Duplicate value in sub-region"; - } - possible[y][x] = c.getData() == 0; - } - } - for (int y = 0; y < groupDim; y++) { - for (int x = 0; x < groupSize; x++) { - SudokuCell r = initialBoard.getCell(x, (groupNum / groupDim) * groupDim + y); - SudokuCell c = initialBoard.getCell((groupNum % groupDim) * groupDim + y, x); - if (r.getData() == cell.getData()) { - for (int i = 0; i < groupDim; i++) { - possible[y][i] = false; - } - } - if (c.getData() == cell.getData()) { - for (int i = 0; i < groupDim; i++) { - possible[i][y] = false; - } - } - } - } - boolean isForced = false; - for (int y = 0; y < groupDim; y++) { - for (int x = 0; x < groupDim; x++) { - if (possible[y][x] && !isForced) { - isForced = true; - } - else { - if (possible[y][x]) { - return super.getInvalidUseOfRuleMessage() + ": Not forced"; - } - } - } - } - if (!isForced) { - return super.getInvalidUseOfRuleMessage() + ": Not forced"; - } - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.sudoku.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.sudoku.SudokuBoard; +import edu.rpi.legup.puzzle.sudoku.SudokuCell; + +public class AdvancedDeductionDirectRule extends DirectRule { + + public AdvancedDeductionDirectRule() { + super( + "SUDO-BASC-0001", + "Advanced Deduction", + "Use of group logic deduces more answers by means of forced by Location and forced" + + " by Deduction", + "edu/rpi/legup/images/sudoku/AdvancedDeduction.png"); + } + + /** + * 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 + */ + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); + SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); + + SudokuCell cell = (SudokuCell) finalBoard.getPuzzleElement(puzzleElement); + int index = cell.getIndex(); + int groupSize = initialBoard.getWidth(); + int groupDim = (int) Math.sqrt(groupSize); + int rowIndex = index / groupSize; + int colIndex = index % groupSize; + int relX = rowIndex / groupDim; + int relY = colIndex % groupDim; + int groupNum = rowIndex / groupDim * groupDim + colIndex / groupDim; + boolean[][] possible = new boolean[groupDim][groupDim]; + for (int y = 0; y < groupDim; y++) { + for (int x = 0; x < groupDim; x++) { + SudokuCell c = initialBoard.getCell(groupNum, x, y); + if (c.getData() == cell.getData() && x != relX && y != relY) { + return super.getRuleName() + ": Duplicate value in sub-region"; + } + possible[y][x] = c.getData() == 0; + } + } + for (int y = 0; y < groupDim; y++) { + for (int x = 0; x < groupSize; x++) { + SudokuCell r = initialBoard.getCell(x, (groupNum / groupDim) * groupDim + y); + SudokuCell c = initialBoard.getCell((groupNum % groupDim) * groupDim + y, x); + if (r.getData() == cell.getData()) { + for (int i = 0; i < groupDim; i++) { + possible[y][i] = false; + } + } + if (c.getData() == cell.getData()) { + for (int i = 0; i < groupDim; i++) { + possible[i][y] = false; + } + } + } + } + boolean isForced = false; + for (int y = 0; y < groupDim; y++) { + for (int x = 0; x < groupDim; x++) { + if (possible[y][x] && !isForced) { + isForced = true; + } else { + if (possible[y][x]) { + return super.getInvalidUseOfRuleMessage() + ": Not forced"; + } + } + } + } + if (!isForced) { + return super.getInvalidUseOfRuleMessage() + ": Not forced"; + } + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java index 51d963247..fd03ef36c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java @@ -1,93 +1,95 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; - -import java.util.Set; - -public class LastCellForNumberDirectRule extends DirectRule { - public LastCellForNumberDirectRule() { - super("SUDO-BASC-0002", "Last Cell for Number", - "This is the only cell open in its group for some number.", - "edu/rpi/legup/images/sudoku/forcedByElimination.png"); - } - - /** - * 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 - */ - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); - SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); - - SudokuCell cell = (SudokuCell) finalBoard.getPuzzleElement(puzzleElement); - if (cell.getData() == 0) { - return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; - } - - int size = initialBoard.getSize(); - - Set region = initialBoard.getRegion(cell.getGroupIndex()); - Set row = initialBoard.getRow(cell.getLocation().y); - Set col = initialBoard.getCol(cell.getLocation().x); - - boolean contains = false; - if (region.size() == size - 1) { - for (SudokuCell c : region) { - if (cell.getData() == c.getData()) { - contains = true; - break; - } - } - if (!contains) { - return null; - } - } - if (row.size() == size - 1) { - contains = false; - for (SudokuCell c : row) { - if (cell.getData() == c.getData()) { - contains = true; - break; - } - } - if (!contains) { - return null; - } - } - if (col.size() == size - 1) { - contains = false; - for (SudokuCell c : col) { - if (cell.getData() == c.getData()) { - contains = true; - break; - } - } - if (!contains) { - return null; - } - } - return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.sudoku.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.sudoku.SudokuBoard; +import edu.rpi.legup.puzzle.sudoku.SudokuCell; +import java.util.Set; + +public class LastCellForNumberDirectRule extends DirectRule { + public LastCellForNumberDirectRule() { + super( + "SUDO-BASC-0002", + "Last Cell for Number", + "This is the only cell open in its group for some number.", + "edu/rpi/legup/images/sudoku/forcedByElimination.png"); + } + + /** + * 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 + */ + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); + SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); + + SudokuCell cell = (SudokuCell) finalBoard.getPuzzleElement(puzzleElement); + if (cell.getData() == 0) { + return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; + } + + int size = initialBoard.getSize(); + + Set region = initialBoard.getRegion(cell.getGroupIndex()); + Set row = initialBoard.getRow(cell.getLocation().y); + Set col = initialBoard.getCol(cell.getLocation().x); + + boolean contains = false; + if (region.size() == size - 1) { + for (SudokuCell c : region) { + if (cell.getData() == c.getData()) { + contains = true; + break; + } + } + if (!contains) { + return null; + } + } + if (row.size() == size - 1) { + contains = false; + for (SudokuCell c : row) { + if (cell.getData() == c.getData()) { + contains = true; + break; + } + } + if (!contains) { + return null; + } + } + if (col.size() == size - 1) { + contains = false; + for (SudokuCell c : col) { + if (cell.getData() == c.getData()) { + contains = true; + break; + } + } + if (!contains) { + return null; + } + } + return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java index 28e64ce7b..ca0ac3023 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java @@ -1,77 +1,81 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; - -import java.util.HashSet; - -public class LastNumberForCellDirectRule extends DirectRule { - - public LastNumberForCellDirectRule() { - super("SUDO-BASC-0003", "Last Number for Cell", - "This is the only number left that can fit in the cell of a group.", - "edu/rpi/legup/images/sudoku/forcedByDeduction.png"); - } - - /** - * 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 - */ - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); - SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); - - int index = puzzleElement.getIndex(); - int groupSize = initialBoard.getWidth(); - int groupDim = (int) Math.sqrt(groupSize); - int rowIndex = index / groupSize; - int colIndex = index % groupSize; - int groupNum = rowIndex / groupDim * groupDim + colIndex % groupDim; - HashSet numbers = new HashSet<>(); - for (int i = 1; i <= groupSize; i++) { - numbers.add(i); - } - for (int i = 0; i < groupSize; i++) { - SudokuCell cell = initialBoard.getCell(groupNum, i % groupDim, i / groupDim); - numbers.remove(cell.getData()); - } - for (int i = 0; i < groupSize; i++) { - SudokuCell cell = initialBoard.getCell(i, colIndex); - numbers.remove(cell.getData()); - } - for (int i = 0; i < groupSize; i++) { - SudokuCell cell = initialBoard.getCell(rowIndex, i); - numbers.remove(cell.getData()); - } - if (numbers.size() > 1) { - return super.getInvalidUseOfRuleMessage() + ": The number at the index is not forced"; - } - else { - if (numbers.size() == 1 && numbers.iterator().next() != finalBoard.getPuzzleElement(puzzleElement).getData()) { - return super.getInvalidUseOfRuleMessage() + ": The number at the index is forced but not correct"; - } - } - return null; - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.sudoku.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.sudoku.SudokuBoard; +import edu.rpi.legup.puzzle.sudoku.SudokuCell; +import java.util.HashSet; + +public class LastNumberForCellDirectRule extends DirectRule { + + public LastNumberForCellDirectRule() { + super( + "SUDO-BASC-0003", + "Last Number for Cell", + "This is the only number left that can fit in the cell of a group.", + "edu/rpi/legup/images/sudoku/forcedByDeduction.png"); + } + + /** + * 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 + */ + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); + SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); + + int index = puzzleElement.getIndex(); + int groupSize = initialBoard.getWidth(); + int groupDim = (int) Math.sqrt(groupSize); + int rowIndex = index / groupSize; + int colIndex = index % groupSize; + int groupNum = rowIndex / groupDim * groupDim + colIndex % groupDim; + HashSet numbers = new HashSet<>(); + for (int i = 1; i <= groupSize; i++) { + numbers.add(i); + } + for (int i = 0; i < groupSize; i++) { + SudokuCell cell = initialBoard.getCell(groupNum, i % groupDim, i / groupDim); + numbers.remove(cell.getData()); + } + for (int i = 0; i < groupSize; i++) { + SudokuCell cell = initialBoard.getCell(i, colIndex); + numbers.remove(cell.getData()); + } + for (int i = 0; i < groupSize; i++) { + SudokuCell cell = initialBoard.getCell(rowIndex, i); + numbers.remove(cell.getData()); + } + if (numbers.size() > 1) { + return super.getInvalidUseOfRuleMessage() + ": The number at the index is not forced"; + } else { + if (numbers.size() == 1 + && numbers.iterator().next() + != finalBoard.getPuzzleElement(puzzleElement).getData()) { + return super.getInvalidUseOfRuleMessage() + + ": The number at the index is forced but not correct"; + } + } + return null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java index fb87764fe..e44728d3e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java @@ -5,25 +5,27 @@ import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.puzzle.sudoku.SudokuBoard; import edu.rpi.legup.puzzle.sudoku.SudokuCell; - import java.util.HashSet; import java.util.Set; public class NoSolutionContradictionRule extends ContradictionRule { public NoSolutionContradictionRule() { - super("SUDO-CONT-0001", "No Solution for Cell", + super( + "SUDO-CONT-0001", + "No Solution for Cell", "Process of elimination yields no valid numbers for an empty cell.", "edu/rpi/legup/images/sudoku/NoSolution.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java index 373b60457..fb6da62d4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java @@ -7,14 +7,15 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.sudoku.SudokuBoard; import edu.rpi.legup.puzzle.sudoku.SudokuCell; - import java.util.ArrayList; import java.util.HashSet; import java.util.Set; public class PossibleCellCaseRule extends CaseRule { public PossibleCellCaseRule() { - super("SUDO-CASE-0001", "Possible Cells for Number", + super( + "SUDO-CASE-0001", + "Possible Cells for Number", "A number has a limited set of cells in which it can be placed.", "edu/rpi/legup/images/sudoku/possible_cells_number.png"); } @@ -31,13 +32,13 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -59,7 +60,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java index 27d6b58b7..e6ab0e64c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java @@ -9,7 +9,6 @@ import edu.rpi.legup.puzzle.sudoku.PossibleNumberCaseBoard; import edu.rpi.legup.puzzle.sudoku.SudokuBoard; import edu.rpi.legup.puzzle.sudoku.SudokuCell; - import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -17,7 +16,9 @@ public class PossibleNumberCaseRule extends CaseRule { public PossibleNumberCaseRule() { - super("SUDO-CASE-0002", "Possible Numbers for Cell", + super( + "SUDO-CASE-0002", + "Possible Numbers for Cell", "An empty cell has a limited set of possible numbers that can fill it.", "edu/rpi/legup/images/sudoku/PossibleValues.png"); } @@ -34,13 +35,13 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -62,7 +63,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -74,13 +75,14 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement - * @param value value that the rule will be applied from - * @param groupType group type + * @param value value that the rule will be applied from + * @param groupType group type * @return a list of elements the specified could be */ - public ArrayList getCases(Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { + public ArrayList getCases( + Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { ArrayList cases = new ArrayList<>(); SudokuBoard sudokuBoard = (SudokuBoard) board; List caseCells = new ArrayList<>(); @@ -89,12 +91,10 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement, int v Set group; if (groupType == GroupType.REGION) { group = sudokuBoard.getRegion(cell.getGroupIndex()); - } - else { + } else { if (groupType == GroupType.ROW) { group = sudokuBoard.getRow(cell.getLocation().y); - } - else { + } else { group = sudokuBoard.getCol(cell.getLocation().x); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java index 4a5cc2074..955414e8e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java @@ -5,25 +5,27 @@ import edu.rpi.legup.model.rules.ContradictionRule; import edu.rpi.legup.puzzle.sudoku.SudokuBoard; import edu.rpi.legup.puzzle.sudoku.SudokuCell; - import java.util.HashSet; import java.util.Set; public class RepeatedNumberContradictionRule extends ContradictionRule { public RepeatedNumberContradictionRule() { - super("SUDO-CONT-0002", "Repeated Numbers", + super( + "SUDO-CONT-0002", + "Repeated Numbers", "Two identical numbers are placed in the same group.", "edu/rpi/legup/images/sudoku/RepeatedNumber.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java b/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java index 30af5af51..a11f5f05b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/ClueCommand.java @@ -1,5 +1,7 @@ package edu.rpi.legup.puzzle.treetent; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.history.CommandError; import edu.rpi.legup.history.PuzzleCommand; import edu.rpi.legup.model.Puzzle; @@ -7,14 +9,11 @@ import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class ClueCommand extends PuzzleCommand { private TreeViewSelection selection; private TreeTentClueView clueView; @@ -28,9 +27,7 @@ public ClueCommand(TreeViewSelection selection, TreeTentClueView clueView) { this.emptyCells = new ArrayList<>(); } - /** - * Executes a command - */ + /** Executes a command */ @Override public void executeCommand() { Puzzle puzzle = getInstance().getPuzzleModule(); @@ -52,8 +49,7 @@ public void executeCommand() { if (transition == null) { transition = tree.addNewTransition(treeNode); addTran.put(treeNode, transition); - } - else { + } else { treeNode.addChild(transition); } @@ -62,8 +58,7 @@ public void executeCommand() { newSelection.addToSelection(treeView.getElementView(finalTran)); board = (TreeTentBoard) finalTran.getBoard(); - } - else { + } else { finalTran = (TreeTransition) treeElement; newSelection.addToSelection(treeView.getElementView(treeElement)); } @@ -88,7 +83,7 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -105,8 +100,7 @@ public String getErrorString() { if (!node.getChildren().isEmpty()) { return CommandError.UNMODIFIABLE_BOARD.toString(); } - } - else { + } else { if (!board.isModifiable()) { return CommandError.UNMODIFIABLE_BOARD.toString(); } @@ -114,17 +108,23 @@ public String getErrorString() { List tempList = new ArrayList<>(); TreeTentClue clue = clueView.getPuzzleElement(); - if (clue.getType() == TreeTentType.CLUE_NORTH || clue.getType() == TreeTentType.CLUE_SOUTH) { - int col = clue.getType() == TreeTentType.CLUE_NORTH ? clue.getClueIndex() : clue.getClueIndex() - 1; + if (clue.getType() == TreeTentType.CLUE_NORTH + || clue.getType() == TreeTentType.CLUE_SOUTH) { + int col = + clue.getType() == TreeTentType.CLUE_NORTH + ? clue.getClueIndex() + : clue.getClueIndex() - 1; for (int i = 0; i < board.getWidth(); i++) { TreeTentCell cell = board.getCell(col, i); if (cell.getType() == TreeTentType.UNKNOWN && cell.isModifiable()) { tempList.add(cell); } } - } - else { - int row = clue.getType() == TreeTentType.CLUE_WEST ? clue.getClueIndex() : clue.getClueIndex() - 1; + } else { + int row = + clue.getType() == TreeTentType.CLUE_WEST + ? clue.getClueIndex() + : clue.getClueIndex() - 1; for (int i = 0; i < board.getWidth(); i++) { TreeTentCell cell = board.getCell(i, row); if (cell.getType() == TreeTentType.UNKNOWN && cell.isModifiable()) { @@ -140,9 +140,7 @@ public String getErrorString() { return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = getInstance().getPuzzleModule(); @@ -163,8 +161,7 @@ public void undoCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(finalTran)); board = (TreeTentBoard) finalTran.getBoard(); - } - else { + } else { finalTran = (TreeTransition) treeElement; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java b/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java index d2f8575f1..feece58a9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/EditLineCommand.java @@ -1,5 +1,7 @@ package edu.rpi.legup.puzzle.treetent; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.history.CommandError; import edu.rpi.legup.history.PuzzleCommand; import edu.rpi.legup.model.Puzzle; @@ -8,27 +10,23 @@ import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; - import java.awt.*; import java.util.List; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class EditLineCommand extends PuzzleCommand { private TreeTentElementView start; private TreeTentElementView end; private TreeViewSelection selection; - public EditLineCommand(TreeViewSelection selection, TreeTentElementView start, ElementView endDrag) { + public EditLineCommand( + TreeViewSelection selection, TreeTentElementView start, ElementView endDrag) { this.selection = selection; this.start = start; this.end = getViewInDirection(endDrag); } - /** - * Executes a command - */ + /** Executes a command */ @Override public void executeCommand() { Puzzle puzzle = getInstance().getPuzzleModule(); @@ -49,8 +47,7 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(transition)); board = (TreeTentBoard) transition.getBoard(); - } - else { + } else { transition = (TreeTransition) treeElement; } @@ -73,8 +70,7 @@ public void executeCommand() { board.getLines().add(line); notifyLine = line; transition.propagateAddition(notifyLine); - } - else { + } else { board.removeModifiedData(dupLine); board.getLines().remove(dupLine); notifyLine = dupLine; @@ -85,7 +81,8 @@ public void executeCommand() { puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(transition)); - final TreeViewSelection newSelection = new TreeViewSelection(treeView.getElementView(transition)); + final TreeViewSelection newSelection = + new TreeViewSelection(treeView.getElementView(transition)); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } @@ -93,7 +90,7 @@ public void executeCommand() { * Gets the reason why the command cannot be executed * * @return if command cannot be executed, returns reason for why the command cannot be executed, - * otherwise null if command can be executed + * otherwise null if command can be executed */ @Override public String getErrorString() { @@ -114,13 +111,15 @@ public String getErrorString() { if (!node.getChildren().isEmpty()) { return CommandError.UNMODIFIABLE_BOARD.toString(); } - } - else { + } else { if (!board.isModifiable()) { return CommandError.UNMODIFIABLE_BOARD.toString(); } } - TreeTentLine line = new TreeTentLine((TreeTentCell) start.getPuzzleElement(), (TreeTentCell) end.getPuzzleElement()); + TreeTentLine line = + new TreeTentLine( + (TreeTentCell) start.getPuzzleElement(), + (TreeTentCell) end.getPuzzleElement()); for (TreeTentLine l : board.getLines()) { if (line.compare(l) && !l.isModifiable()) { return CommandError.UNMODIFIABLE_DATA.toString(); @@ -129,17 +128,16 @@ public String getErrorString() { TreeTentCell startCell = (TreeTentCell) start.getPuzzleElement(); TreeTentCell endCell = (TreeTentCell) end.getPuzzleElement(); - if (!((startCell.getType() == TreeTentType.TENT && endCell.getType() == TreeTentType.TREE) || - (endCell.getType() == TreeTentType.TENT && startCell.getType() == TreeTentType.TREE))) { + if (!((startCell.getType() == TreeTentType.TENT && endCell.getType() == TreeTentType.TREE) + || (endCell.getType() == TreeTentType.TENT + && startCell.getType() == TreeTentType.TREE))) { return "The line must connect a tree to a tent."; } return null; } - /** - * Undoes an command - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = getInstance().getPuzzleModule(); @@ -159,8 +157,7 @@ public void undoCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(transition)); board = (TreeTentBoard) transition.getBoard(); - } - else { + } else { transition = (TreeTransition) treeElement; } @@ -182,8 +179,7 @@ public void undoCommand() { board.addModifiedData(line); board.getLines().add(line); notifyLine = line; - } - else { + } else { board.removeModifiedData(dupLine); board.getLines().remove(dupLine); notifyLine = dupLine; @@ -206,24 +202,21 @@ private TreeTentElementView getViewInDirection(ElementView endDrag) { Point endLoc = endDrag.getLocation(); double radians = Math.atan2(startLoc.y - endLoc.y, endLoc.x - startLoc.x); if (radians >= Math.PI / 4 && radians < 3 * Math.PI / 4) { - //up + // up xIndex = startLoc.x / size.width; yIndex = (startLoc.y / size.height) - 1; - } - else { + } else { if (radians >= -Math.PI / 4 && radians < Math.PI / 4) { - //right + // right xIndex = (startLoc.x / size.width) + 1; yIndex = startLoc.y / size.height; - } - else { + } else { if (radians >= -3 * Math.PI / 4 && radians < -Math.PI / 4) { - //down + // down xIndex = startLoc.x / size.width; yIndex = (startLoc.y / size.height) + 1; - } - else { - //left + } else { + // left xIndex = (startLoc.x / size.width) - 1; yIndex = startLoc.y / size.height; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java index 172584cd0..68c97865d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java @@ -3,7 +3,6 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.util.List; public class TreeTent extends Puzzle { @@ -19,9 +18,7 @@ public TreeTent() { this.factory = new TreeTentCellFactory(); } - /** - * Initializes the game board. Called by the invoker of the class - */ + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { TreeTentBoard board = (TreeTentBoard) currentBoard; @@ -44,8 +41,8 @@ public Board generatePuzzle(int difficulty) { /** * Determines if the given dimensions are valid for Tree Tent * - * @param rows the number of rows - * @param columns the number of columns + * @param rows the number of rows + * @param columns the number of columns * @return true if the given dimensions are valid for Tree Tent, false otherwise */ public boolean isValidDimensions(int rows, int columns) { @@ -70,13 +67,10 @@ public boolean isBoardComplete(Board board) { * @param board the board that has changed */ @Override - public void onBoardChange(Board board) { - - } + public void onBoardChange(Board board) {} /** - * @return if it is valid - * TreeTent puzzle must have same number of clues as the dimension size + * @return if it is valid TreeTent puzzle must have same number of clues as the dimension size */ @Override public boolean checkValidity() { 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 dc809f34d..09706f92a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -3,7 +3,6 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -75,8 +74,7 @@ public PuzzleElement getPuzzleElement(PuzzleElement element) { public TreeTentClue getClue(int x, int y) { if (x == getWidth() && 0 <= y && y < getHeight()) { return rowClues.get(y); - } - else { + } else { if (y == getHeight() && 0 <= x && x < getWidth()) { return colClues.get(x); } @@ -85,7 +83,8 @@ public TreeTentClue getClue(int x, int y) { } /** - * Called when a {@link PuzzleElement} has been added and passes in the equivalent puzzle element with the data. + * Called when a {@link PuzzleElement} has been added and passes in the equivalent puzzle + * element with the data. * * @param puzzleElement equivalent puzzle element with the data. */ @@ -97,7 +96,8 @@ public void notifyAddition(PuzzleElement puzzleElement) { } /** - * Called when a {@link PuzzleElement} has been deleted and passes in the equivalent puzzle element with the data. + * Called when a {@link PuzzleElement} has been deleted and passes in the equivalent puzzle + * element with the data. * * @param puzzleElement equivalent puzzle element with the data. */ @@ -118,8 +118,8 @@ public void notifyDeletion(PuzzleElement puzzleElement) { * * @param cell The cell to get adjacent cells from. * @param type The cell types to get. - * @return List of adjacent cells in the form { up, right, down, left }. - * If an adjacent cell is null, it will not be added to the list. + * @return List of adjacent cells in the form { up, right, down, left }. If an adjacent cell is + * null, it will not be added to the list. */ public List getAdjacent(TreeTentCell cell, TreeTentType type) { List adj = new ArrayList<>(); @@ -138,7 +138,8 @@ public List getAdjacent(TreeTentCell cell, TreeTentType type) { * * @param cell the base cell * @param type the type to look for - * @return a list of TreeTentCells that are diagonals of the given TreeTentCell and are of the given TreeTentType + * @return a list of TreeTentCells that are diagonals of the given TreeTentCell and are of the + * given TreeTentType */ public List getDiagonals(TreeTentCell cell, TreeTentType type) { List dia = new ArrayList<>(); @@ -166,7 +167,7 @@ public List getDiagonals(TreeTentCell cell, TreeTentType type) { * Creates and returns a list of TreeTentCells that match the given TreeTentType * * @param index the row or column number - * @param type type of TreeTent element + * @param type type of TreeTent element * @param isRow boolean value beased on whether a row of column is being checked * @return List of TreeTentCells that match the given TreeTentType */ @@ -179,8 +180,7 @@ public List getRowCol(int index, TreeTentType type, boolean isRow) list.add(cell); } } - } - else { + } else { for (int i = 0; i < dimension.width; i++) { TreeTentCell cell = getCell(index, i); if (cell.getType() == type) { 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 b6411a5cf..c7c9f0d21 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCell.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; import java.awt.event.MouseEvent; 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 20b8066a4..1697b8bd5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java @@ -4,17 +4,16 @@ import edu.rpi.legup.model.gameboard.ElementFactory; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class TreeTentCellFactory extends ElementFactory { /** * Creates a puzzleElement based on the xml document Node and adds it to the board * - * @param node node that represents the puzzleElement + * @param node node that represents the puzzleElement * @param board board to add the newly created cell * @return newly created cell from the xml document Node * @throws InvalidFileFormatException if file is invalid @@ -32,7 +31,8 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); if (x >= width || y >= height) { - throw new InvalidFileFormatException("TreeTent Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "TreeTent Factory: cell location out of bounds"); } if (value < 0 || value > 3) { throw new InvalidFileFormatException("TreeTent Factory: cell unknown value"); @@ -41,30 +41,29 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat TreeTentCell cell = new TreeTentCell(TreeTentType.valueOf(value), new Point(x, y)); cell.setIndex(y * height + x); return cell; - } - else { + } else { if (node.getNodeName().equalsIgnoreCase("line")) { int x1 = Integer.valueOf(attributeList.getNamedItem("x1").getNodeValue()); int y1 = Integer.valueOf(attributeList.getNamedItem("y1").getNodeValue()); int x2 = Integer.valueOf(attributeList.getNamedItem("x2").getNodeValue()); int y2 = Integer.valueOf(attributeList.getNamedItem("y2").getNodeValue()); if (x1 >= width || y1 >= height || x2 >= width || y2 >= height) { - throw new InvalidFileFormatException("TreeTent Factory: line location out of bounds"); + throw new InvalidFileFormatException( + "TreeTent Factory: line location out of bounds"); } TreeTentCell c1 = treeTentBoard.getCell(x1, y1); TreeTentCell c2 = treeTentBoard.getCell(x2, y2); return new TreeTentLine(c1, c2); - } - else { - throw new InvalidFileFormatException("TreeTent Factory: unknown puzzleElement puzzleElement"); + } else { + throw new InvalidFileFormatException( + "TreeTent Factory: unknown puzzleElement puzzleElement"); } } - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("TreeTent Factory: unknown value where integer expected"); - } - catch (NullPointerException e) { + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "TreeTent Factory: unknown value where integer expected"); + } catch (NullPointerException e) { throw new InvalidFileFormatException("TreeTent Factory: could not find attribute(s)"); } } @@ -72,7 +71,7 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat /** * Creates a xml document puzzleElement from a cell for exporting * - * @param document xml document + * @param document xml document * @param puzzleElement PuzzleElement cell * @return xml PuzzleElement */ @@ -88,8 +87,7 @@ public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleEle cellElement.setAttribute("y", String.valueOf(loc.y)); return cellElement; - } - else { + } else { org.w3c.dom.Element lineElement = document.createElement("line"); TreeTentLine line = (TreeTentLine) puzzleElement; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClueView.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClueView.java index 4e7b24b13..d1b230d20 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClueView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.treetent; import edu.rpi.legup.ui.boardview.ElementView; - import java.awt.*; public class TreeTentClueView extends ElementView { 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 1f431594c..79e074657 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java @@ -1,5 +1,7 @@ package edu.rpi.legup.puzzle.treetent; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.history.AutoCaseRuleCommand; @@ -13,11 +15,8 @@ import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; - import java.awt.event.MouseEvent; -import static edu.rpi.legup.app.GameBoardFacade.getInstance; - public class TreeTentController extends ElementController { private ElementView lastCellPressed; @@ -40,9 +39,11 @@ public void mousePressed(MouseEvent e) { @Override public void mouseReleased(MouseEvent e) { - if (GameBoardFacade.getInstance().getLegupUI().getTreePanel() != null && e.getButton() != MouseEvent.BUTTON2) { + if (GameBoardFacade.getInstance().getLegupUI().getTreePanel() != null + && e.getButton() != MouseEvent.BUTTON2) { TreePanel treePanel = GameBoardFacade.getInstance().getLegupUI().getTreePanel(); - TreeView treeView = GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); + TreeView treeView = + GameBoardFacade.getInstance().getLegupUI().getTreePanel().getTreeView(); BoardView boardView = getInstance().getLegupUI().getBoardView(); lastCellPressed = boardView.getElement(e.getPoint()); Board board = boardView.getBoard(); @@ -51,17 +52,17 @@ public void mouseReleased(MouseEvent e) { if (dragStart != null) { if (board instanceof CaseBoard) { CaseBoard caseBoard = (CaseBoard) board; - AutoCaseRuleCommand autoCaseRuleCommand = new AutoCaseRuleCommand(dragStart, selection, caseBoard.getCaseRule(), caseBoard, e); + AutoCaseRuleCommand autoCaseRuleCommand = + new AutoCaseRuleCommand( + dragStart, selection, caseBoard.getCaseRule(), caseBoard, e); if (autoCaseRuleCommand.canExecute()) { autoCaseRuleCommand.execute(); getInstance().getHistory().pushChange(autoCaseRuleCommand); treePanel.updateError(""); - } - else { + } else { treePanel.updateError(autoCaseRuleCommand.getError()); } - } - else { + } else { if (dragStart == lastCellPressed) { if (dragStart.getPuzzleElement().getIndex() >= 0) { ICommand edit = new EditDataCommand(lastCellPressed, selection, e); @@ -69,32 +70,32 @@ public void mouseReleased(MouseEvent e) { edit.execute(); getInstance().getHistory().pushChange(edit); treePanel.updateError(""); - } - else { + } else { treePanel.updateError(edit.getError()); } - } - else { - ClueCommand edit = new ClueCommand(selection, (TreeTentClueView) dragStart); + } else { + ClueCommand edit = + new ClueCommand(selection, (TreeTentClueView) dragStart); if (edit.canExecute()) { edit.execute(); getInstance().getHistory().pushChange(edit); treePanel.updateError(""); - } - else { + } else { treePanel.updateError(edit.getError()); } } - } - else { + } else { if (lastCellPressed != null) { if (dragStart instanceof TreeTentElementView) { - ICommand editLine = new EditLineCommand(selection, (TreeTentElementView) dragStart, lastCellPressed); + ICommand editLine = + new EditLineCommand( + selection, + (TreeTentElementView) dragStart, + lastCellPressed); if (editLine.canExecute()) { editLine.execute(); getInstance().getHistory().pushChange(editLine); - } - else { + } else { treePanel.updateError(editLine.getError()); } } @@ -113,26 +114,21 @@ public void changeCell(MouseEvent e, PuzzleElement element) { if (e.getButton() == MouseEvent.BUTTON1) { if (cell.getData() == TreeTentType.UNKNOWN) { element.setData(TreeTentType.GRASS); - } - else { + } else { if (cell.getData() == TreeTentType.GRASS) { element.setData(TreeTentType.TENT); - } - else { + } else { element.setData(TreeTentType.UNKNOWN); } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() == TreeTentType.UNKNOWN) { element.setData(TreeTentType.TENT); - } - else { + } else { if (cell.getData() == TreeTentType.GRASS) { element.setData(TreeTentType.UNKNOWN); - } - else { + } else { element.setData(TreeTentType.GRASS); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentElementView.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentElementView.java index 0c8698f83..1196ab075 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.treetent; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; import java.awt.geom.Rectangle2D; @@ -23,25 +22,47 @@ public void drawElement(Graphics2D graphics2D) { if (type == TreeTentType.UNKNOWN) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.LIGHT_GRAY); - graphics2D.fill(new Rectangle2D.Double(location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); + graphics2D.fill( + new Rectangle2D.Double( + location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); graphics2D.setColor(Color.BLACK); - graphics2D.draw(new Rectangle2D.Double(location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); - } - else { + graphics2D.draw( + new Rectangle2D.Double( + location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); + } else { if (type == TreeTentType.TREE) { - graphics2D.drawImage(TreeTentView.TREE, location.x, location.y, size.width, size.height, null, null); + graphics2D.drawImage( + TreeTentView.TREE, + location.x, + location.y, + size.width, + size.height, + null, + null); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == TreeTentType.GRASS) { - graphics2D.drawImage(TreeTentView.GRASS, location.x, location.y, size.width, size.height, null, null); + graphics2D.drawImage( + TreeTentView.GRASS, + location.x, + location.y, + size.width, + size.height, + null, + null); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - else { + } else { if (type == TreeTentType.TENT) { - graphics2D.drawImage(TreeTentView.TENT, location.x, location.y, size.width, size.height, null, null); + graphics2D.drawImage( + TreeTentView.TENT, + location.x, + location.y, + size.width, + size.height, + null, + null); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java index c438d8ee4..475aaab1e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java @@ -21,8 +21,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { TreeTentBoard board; if (puzzle.getTree() != null) { board = (TreeTentBoard) puzzle.getTree().getRootNode().getBoard(); - } - else { + } else { board = (TreeTentBoard) puzzle.getBoardView().getBoard(); } @@ -34,7 +33,8 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (PuzzleElement puzzleElement : board.getPuzzleElements()) { TreeTentCell cell = (TreeTentCell) puzzleElement; if (cell.getData() != TreeTentType.UNKNOWN) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } 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 2b4861c9f..0117f41ce 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java @@ -2,12 +2,11 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; - public class TreeTentImporter extends PuzzleImporter { public TreeTentImporter(TreeTent treeTent) { super(treeTent); @@ -26,7 +25,7 @@ public boolean acceptsTextInput() { /** * Creates an empty board for building * - * @param rows the number of rows on the board + * @param rows the number of rows on the board * @param columns the number of columns on the board * @throws RuntimeException if board can not be created */ @@ -66,11 +65,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("TreeTent Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "TreeTent Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("TreeTent Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "TreeTent Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -79,9 +80,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); treeTentBoard = new TreeTentBoard(size); - } - else { - if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { int width = Integer.valueOf(boardElement.getAttribute("width")); int height = Integer.valueOf(boardElement.getAttribute("height")); treeTentBoard = new TreeTentBoard(width, height); @@ -96,7 +97,10 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = treeTentBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - TreeTentCell cell = (TreeTentCell) puzzle.getFactory().importCell(elementDataList.item(i), treeTentBoard); + TreeTentCell cell = + (TreeTentCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), treeTentBoard); Point loc = cell.getLocation(); if (cell.getData() != TreeTentType.UNKNOWN) { cell.setModifiable(false); @@ -125,19 +129,31 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { Element axis2 = (Element) axes.item(1); if (!axis1.hasAttribute("side") || !axis1.hasAttribute("side")) { - throw new InvalidFileFormatException("TreeTent Importer: side attribute of axis not specified"); + throw new InvalidFileFormatException( + "TreeTent Importer: side attribute of axis not specified"); } String side1 = axis1.getAttribute("side"); String side2 = axis2.getAttribute("side"); - if (side1.equalsIgnoreCase(side2) || !(side1.equalsIgnoreCase("east") || side1.equalsIgnoreCase("south")) || - !(side2.equalsIgnoreCase("east") || side2.equalsIgnoreCase("south"))) { - throw new InvalidFileFormatException("TreeTent Importer: axes must be different and be {east | south}"); + if (side1.equalsIgnoreCase(side2) + || !(side1.equalsIgnoreCase("east") || side1.equalsIgnoreCase("south")) + || !(side2.equalsIgnoreCase("east") || side2.equalsIgnoreCase("south"))) { + throw new InvalidFileFormatException( + "TreeTent Importer: axes must be different and be {east | south}"); } - NodeList eastClues = side1.equalsIgnoreCase("east") ? axis1.getElementsByTagName("clue") : axis2.getElementsByTagName("clue"); - NodeList southClues = side1.equalsIgnoreCase("south") ? axis1.getElementsByTagName("clue") : axis2.getElementsByTagName("clue"); - - if (eastClues.getLength() != treeTentBoard.getHeight() || southClues.getLength() != treeTentBoard.getWidth()) { - throw new InvalidFileFormatException("TreeTent Importer: there must be same number of clues as the dimension of the board"); + NodeList eastClues = + side1.equalsIgnoreCase("east") + ? axis1.getElementsByTagName("clue") + : axis2.getElementsByTagName("clue"); + NodeList southClues = + side1.equalsIgnoreCase("south") + ? axis1.getElementsByTagName("clue") + : axis2.getElementsByTagName("clue"); + + if (eastClues.getLength() != treeTentBoard.getHeight() + || southClues.getLength() != treeTentBoard.getWidth()) { + throw new InvalidFileFormatException( + "TreeTent Importer: there must be same number of clues as the dimension of" + + " the board"); } for (int i = 0; i < eastClues.getLength(); i++) { @@ -146,13 +162,16 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int index = TreeTentClue.colStringToColNum(clue.getAttribute("index")); if (index - 1 < 0 || index - 1 > treeTentBoard.getHeight()) { - throw new InvalidFileFormatException("TreeTent Importer: clue index out of bounds"); + throw new InvalidFileFormatException( + "TreeTent Importer: clue index out of bounds"); } if (treeTentBoard.getRowClues().get(index - 1) != null) { throw new InvalidFileFormatException("TreeTent Importer: duplicate clue index"); } - treeTentBoard.getRowClues().set(index - 1, new TreeTentClue(value, index, TreeTentType.CLUE_EAST)); + treeTentBoard + .getRowClues() + .set(index - 1, new TreeTentClue(value, index, TreeTentType.CLUE_EAST)); } for (int i = 0; i < southClues.getLength(); i++) { @@ -161,27 +180,35 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int index = Integer.valueOf(clue.getAttribute("index")); if (index - 1 < 0 || index - 1 > treeTentBoard.getWidth()) { - throw new InvalidFileFormatException("TreeTent Importer: clue index out of bounds"); + throw new InvalidFileFormatException( + "TreeTent Importer: clue index out of bounds"); } if (treeTentBoard.getColClues().get(index - 1) != null) { throw new InvalidFileFormatException("TreeTent Importer: duplicate clue index"); } - treeTentBoard.getColClues().set(index - 1, new TreeTentClue(value, index, TreeTentType.CLUE_SOUTH)); + treeTentBoard + .getColClues() + .set(index - 1, new TreeTentClue(value, index, TreeTentType.CLUE_SOUTH)); } if (boardElement.getElementsByTagName("lines").getLength() == 1) { Element linesElement = (Element) boardElement.getElementsByTagName("lines").item(0); NodeList linesList = linesElement.getElementsByTagName("line"); for (int i = 0; i < linesList.getLength(); i++) { - treeTentBoard.getLines().add((TreeTentLine) puzzle.getFactory().importCell(linesList.item(i), treeTentBoard)); + treeTentBoard + .getLines() + .add( + (TreeTentLine) + puzzle.getFactory() + .importCell(linesList.item(i), treeTentBoard)); } } puzzle.setCurrentBoard(treeTentBoard); - } - catch (NumberFormatException e) { - throw new InvalidFileFormatException("TreeTent Importer: unknown value where integer expected"); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "TreeTent Importer: unknown value where integer expected"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLine.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLine.java index 9444cb425..dae10a1ae 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLine.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLine.java @@ -26,8 +26,10 @@ public void setC2(TreeTentCell c2) { } public boolean compare(TreeTentLine line) { - return ((line.getC1().getLocation().equals(data.getKey().getLocation()) && line.getC2().getLocation().equals(data.getValue().getLocation())) || - (line.getC1().getLocation().equals(data.getValue().getLocation()) && line.getC2().getLocation().equals(data.getKey().getLocation()))); + return ((line.getC1().getLocation().equals(data.getKey().getLocation()) + && line.getC2().getLocation().equals(data.getValue().getLocation())) + || (line.getC1().getLocation().equals(data.getValue().getLocation()) + && line.getC2().getLocation().equals(data.getKey().getLocation()))); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLineView.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLineView.java index 833dcb5b5..6d478cff8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLineView.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentLineView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.treetent; import edu.rpi.legup.ui.boardview.ElementView; - import java.awt.*; public class TreeTentLineView extends ElementView { @@ -24,7 +23,7 @@ public void draw(Graphics2D graphics2D) { int x2 = (p2.x + 1) * size.width + size.width / 2; int y2 = (p2.y + 1) * size.height + size.height / 2; - //graphics2D.setColor(LINE_COLOR); + // graphics2D.setColor(LINE_COLOR); graphics2D.setColor(line.isModified() ? Color.GREEN : Color.WHITE); graphics2D.setStroke(LINE_STROKE); graphics2D.drawLine(x1, y1, x2, y2); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java index 890cdfe29..230283418 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentType.java @@ -1,10 +1,14 @@ package edu.rpi.legup.puzzle.treetent; -import edu.rpi.legup.puzzle.masyu.MasyuType; - public enum TreeTentType { - UNKNOWN, TREE, GRASS, TENT, - CLUE_NORTH, CLUE_EAST, CLUE_SOUTH, CLUE_WEST; + UNKNOWN, + TREE, + GRASS, + TENT, + CLUE_NORTH, + CLUE_EAST, + CLUE_SOUTH, + CLUE_WEST; public static TreeTentType valueOf(int num) { switch (num) { @@ -18,4 +22,4 @@ public static TreeTentType valueOf(int num) { return UNKNOWN; } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentView.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentView.java index 23a500231..3b8cadad6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentView.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentView.java @@ -6,26 +6,33 @@ import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.GridBoardView; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.imageio.ImageIO; import java.awt.*; import java.io.IOException; import java.util.ArrayList; +import javax.imageio.ImageIO; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class TreeTentView extends GridBoardView { - private final static Logger LOGGER = LogManager.getLogger(TreeTentView.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(TreeTentView.class.getName()); static Image TREE, GRASS, TENT; static { try { - TREE = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/treetent/tree.png")); - GRASS = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/treetent/grass.png")); - TENT = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/treetent/tent.png")); - } - catch (IOException e) { + TREE = + ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/treetent/tree.png")); + GRASS = + ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/treetent/grass.png")); + TENT = + ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/treetent/tent.png")); + } catch (IOException e) { LOGGER.error("Failed to open TreeTent images"); } } @@ -53,7 +60,8 @@ public TreeTentView(TreeTentBoard board) { TreeTentElementView elementView = new TreeTentElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point((loc.x + 1) * elementSize.width, (loc.y + 1) * elementSize.height)); + elementView.setLocation( + new Point((loc.x + 1) * elementSize.width, (loc.y + 1) * elementSize.height)); elementViews.add(elementView); } @@ -64,12 +72,16 @@ public TreeTentView(TreeTentBoard board) { } for (int i = 0; i < gridSize.height; i++) { - TreeTentClueView row = new TreeTentClueView(new TreeTentClue(i, i, TreeTentType.CLUE_WEST)); + TreeTentClueView row = + new TreeTentClueView(new TreeTentClue(i, i, TreeTentType.CLUE_WEST)); row.setLocation(new Point(0, (i + 1) * elementSize.height)); row.setSize(elementSize); TreeTentClueView clue = new TreeTentClueView(board.getRowClues().get(i)); - clue.setLocation(new Point((gridSize.width + 1) * elementSize.width, (i + 1) * elementSize.height)); + clue.setLocation( + new Point( + (gridSize.width + 1) * elementSize.width, + (i + 1) * elementSize.height)); clue.setSize(elementSize); westClues.add(row); @@ -77,12 +89,16 @@ public TreeTentView(TreeTentBoard board) { } for (int i = 0; i < gridSize.width; i++) { - TreeTentClueView col = new TreeTentClueView(new TreeTentClue(i, i, TreeTentType.CLUE_NORTH)); + TreeTentClueView col = + new TreeTentClueView(new TreeTentClue(i, i, TreeTentType.CLUE_NORTH)); col.setLocation(new Point((i + 1) * elementSize.width, 0)); col.setSize(elementSize); TreeTentClueView clue = new TreeTentClueView(board.getColClues().get(i)); - clue.setLocation(new Point((i + 1) * elementSize.width, (gridSize.height + 1) * elementSize.height)); + clue.setLocation( + new Point( + (i + 1) * elementSize.width, + (gridSize.height + 1) * elementSize.height)); clue.setSize(elementSize); northClues.add(col); @@ -91,15 +107,18 @@ public TreeTentView(TreeTentBoard board) { } /** - * Gets the ElementView from the location specified or - * null if one does not exists at that location + * Gets the ElementView from the location specified or null if one does not exists at that + * location * * @param point location on the viewport * @return ElementView at the specified location */ @Override public ElementView getElement(Point point) { - Point scaledPoint = new Point((int) Math.round(point.x / getScale()), (int) Math.round(point.y / getScale())); + Point scaledPoint = + new Point( + (int) Math.round(point.x / getScale()), + (int) Math.round(point.y / getScale())); for (ElementView element : elementViews) { if (element.isWithinBounds(scaledPoint)) { return element; @@ -167,8 +186,7 @@ public void onTreeElementChanged(TreeElement treeElement) { TreeTentBoard treeTentBoard; if (board instanceof CaseBoard) { treeTentBoard = (TreeTentBoard) ((CaseBoard) board).getBaseBoard(); - } - else { + } else { treeTentBoard = (TreeTentBoard) board; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java index b85b5e337..5356120a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java @@ -5,7 +5,10 @@ public class GrassTile extends PlaceableElement { public GrassTile() { - super("TREE-PlAC-0002", "Grass Tile", "The grass crest tile", "edu/rpi/legup/images/treetent/grass.png"); + super( + "TREE-PlAC-0002", + "Grass Tile", + "The grass crest tile", + "edu/rpi/legup/images/treetent/grass.png"); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java index 2f4fee1eb..950aebfa7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java @@ -5,7 +5,10 @@ public class TentTile extends PlaceableElement { public TentTile() { - super("TREE-PLAC-0001", "Tent Tile", "The tent tile", "edu/rpi/legup/images/treetent/tent.png"); + super( + "TREE-PLAC-0001", + "Tent Tile", + "The tent tile", + "edu/rpi/legup/images/treetent/tent.png"); } - -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java index a91928fe4..d04886ed5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java @@ -1,12 +1,14 @@ package edu.rpi.legup.puzzle.treetent.elements; import edu.rpi.legup.model.elements.NonPlaceableElement; -import edu.rpi.legup.model.elements.PlaceableElement; public class TreeTile extends NonPlaceableElement { public TreeTile() { - super("TREE-UNPL-0001", "Tree Tile", "The tree tile", "edu/rpi/legup/images/treetent/tree.png"); + super( + "TREE-UNPL-0001", + "Tree Tile", + "The tree tile", + "edu/rpi/legup/images/treetent/tree.png"); } - } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java index 73c8ca263..a54240efd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java @@ -4,6 +4,10 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("TREE-UNPL-0002", "Unknown Tile", "The blank tile", "edu/rpi/legup/images/treetent/UnknownTile.png"); + super( + "TREE-UNPL-0002", + "Unknown Tile", + "The blank tile", + "edu/rpi/legup/images/treetent/UnknownTile.png"); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldDirectRule.java index adfad52f6..c91a60b1a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/EmptyFieldDirectRule.java @@ -1,87 +1,89 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.util.List; - -public class EmptyFieldDirectRule extends DirectRule { - public EmptyFieldDirectRule() { - super("TREE-BASC-0001", "Empty Field", - "Blank cells not adjacent to an unlinked tree are grass.", - "edu/rpi/legup/images/treetent/noTreesAround.png"); - } - - /** - * 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) { - if (puzzleElement instanceof TreeTentLine) { - return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule"; - } - TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); - TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); - TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); - TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(finalCell.getType() == TreeTentType.GRASS && initCell.getType() == TreeTentType.UNKNOWN)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be grass"; - } - - if (isForced(finalBoard, finalCell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be empty."; - } - } - - /** - * Returns a boolean value based on whether the specified cell has adjacent cells (true - no adjacent, false - has adjacent) - * - * @param board the TreeTent board - * @param cell the specified TreeTent cell - * @return true - no adjacent, false - has adjacent - */ - private boolean isForced(TreeTentBoard board, TreeTentCell cell) { - List adjCells = board.getAdjacent(cell, TreeTentType.TREE); - return adjCells.isEmpty(); - } - - /** - * 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) { - TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); - for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - TreeTentCell cell = (TreeTentCell) element; - if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { - cell.setData(TreeTentType.GRASS); - treeTentBoard.addModifiedData(cell); - } - } - if (treeTentBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return treeTentBoard; - } - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.util.List; + +public class EmptyFieldDirectRule extends DirectRule { + public EmptyFieldDirectRule() { + super( + "TREE-BASC-0001", + "Empty Field", + "Blank cells not adjacent to an unlinked tree are grass.", + "edu/rpi/legup/images/treetent/noTreesAround.png"); + } + + /** + * 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) { + if (puzzleElement instanceof TreeTentLine) { + return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule"; + } + TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); + TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); + TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); + TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(finalCell.getType() == TreeTentType.GRASS + && initCell.getType() == TreeTentType.UNKNOWN)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be grass"; + } + + if (isForced(finalBoard, finalCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be empty."; + } + } + + /** + * Returns a boolean value based on whether the specified cell has adjacent cells (true - no + * adjacent, false - has adjacent) + * + * @param board the TreeTent board + * @param cell the specified TreeTent cell + * @return true - no adjacent, false - has adjacent + */ + private boolean isForced(TreeTentBoard board, TreeTentCell cell) { + List adjCells = board.getAdjacent(cell, TreeTentType.TREE); + return adjCells.isEmpty(); + } + + /** + * 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) { + TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); + for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { + TreeTentCell cell = (TreeTentCell) element; + if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { + cell.setData(TreeTentType.GRASS); + treeTentBoard.addModifiedData(cell); + } + } + if (treeTentBoard.getModifiedData().isEmpty()) { + return null; + } else { + return treeTentBoard; + } + } +} 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 698b3aa5e..0116c0dcd 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 @@ -7,17 +7,18 @@ import edu.rpi.legup.model.tree.TreeTransition; 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.TreeTentClue; - +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.awt.*; import java.util.ArrayList; import java.util.List; -import java.awt.*; public class FillinRowCaseRule extends CaseRule { public FillinRowCaseRule() { - super("TREE-CASE-0001", "Fill In row", + super( + "TREE-CASE-0001", + "Fill In row", "A row must have the number of tents of its clue.", "edu/rpi/legup/images/treetent/case_rowcount.png"); } @@ -36,8 +37,14 @@ public CaseBoard getCaseBoard(Board board) { ArrayList clues = treeTentBoard.getRowClues(); clues.addAll(treeTentBoard.getColClues()); for (PuzzleElement element : clues) { - // if ((((TreeTentCell) element).getType() == TreeTentType.CLUE_SOUTH && treeTentBoard.getRowCol(((TreeTentCell)element).getLocation().y, TreeTentType.UNKNOWN, true).size() != 0) || - // (((TreeTentCell) element).getType() == TreeTentType.CLUE_EAST && treeTentBoard.getRowCol(((TreeTentCell)element).getLocation().x, TreeTentType.UNKNOWN, false).size() != 0)) { + // if ((((TreeTentCell) element).getType() == TreeTentType.CLUE_SOUTH && + // treeTentBoard.getRowCol(((TreeTentCell)element).getLocation().y, + // TreeTentType.UNKNOWN, + // true).size() != 0) || + // (((TreeTentCell) element).getType() == TreeTentType.CLUE_EAST && + // treeTentBoard.getRowCol(((TreeTentCell)element).getLocation().x, + // TreeTentType.UNKNOWN, + // false).size() != 0)) { // caseBoard.addPickableElement(element); // } caseBoard.addPickableElement(element); @@ -48,7 +55,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -62,29 +69,46 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { TreeTentBoard tBoard = (TreeTentBoard) board; if (clue.getType() == TreeTentType.CLUE_SOUTH) { group = tBoard.getRowCol(clueIndex, TreeTentType.UNKNOWN, false); - tentsLeft = tBoard.getRowClues().get(clueIndex).getData() - tBoard.getRowCol(clueIndex, TreeTentType.TENT, false).size(); + tentsLeft = + tBoard.getRowClues().get(clueIndex).getData() + - tBoard.getRowCol(clueIndex, TreeTentType.TENT, false).size(); cases = genCombinations(tBoard, group, tentsLeft, clueIndex, false); - } - else { + } else { group = tBoard.getRowCol(clueIndex, TreeTentType.UNKNOWN, true); - tentsLeft = tBoard.getRowClues().get(clueIndex).getData() - tBoard.getRowCol(clueIndex, TreeTentType.TENT, true).size(); + tentsLeft = + tBoard.getRowClues().get(clueIndex).getData() + - tBoard.getRowCol(clueIndex, TreeTentType.TENT, true).size(); cases = genCombinations(tBoard, group, tentsLeft, clueIndex, true); } - //generate every combination (nCr) - //call goodBoard for each generated combination - //alternitive would be to implement collision avoidance while generating instead of after + // generate every combination (nCr) + // call goodBoard for each generated combination + // alternitive would be to implement collision avoidance while generating instead of after if (cases.size() > 0) { return cases; } return null; } - private ArrayList genCombinations(TreeTentBoard iBoard, List tiles, int target, Integer index, boolean isRow) { - return genCombRecursive(iBoard, tiles, tiles, target, 0, new ArrayList(), index, isRow); + private ArrayList genCombinations( + TreeTentBoard iBoard, + List tiles, + int target, + Integer index, + boolean isRow) { + return genCombRecursive( + iBoard, tiles, tiles, target, 0, new ArrayList(), index, isRow); } - private ArrayList genCombRecursive(TreeTentBoard iBoard, List original, List tiles, int target, int current, List selected, Integer index, boolean isRow) { + private ArrayList genCombRecursive( + TreeTentBoard iBoard, + List original, + List tiles, + int target, + int current, + List selected, + Integer index, + boolean isRow) { ArrayList b = new ArrayList<>(); if (target == current) { TreeTentBoard temp = iBoard.copy(); @@ -93,13 +117,11 @@ private ArrayList genCombRecursive(TreeTentBoard iBoard, List genCombRecursive(TreeTentBoard iBoard, List sub = tiles.subList(i + 1, tiles.size()); List next = new ArrayList(selected); next.add(tiles.get(i)); - b.addAll(genCombRecursive(iBoard, original, sub, target, current + 1, next, index, isRow)); + b.addAll( + genCombRecursive( + iBoard, original, sub, target, current + 1, next, index, isRow)); } return b; } - //Effectively runs TouchingTents check on all the added tents to make sure that the proposed board is valid. - //Could check more or less in the future depending on how "smart" this case rule should be. + // Effectively runs TouchingTents check on all the added tents to make sure that the proposed + // board is valid. + // Could check more or less in the future depending on how "smart" this case rule should be. private boolean goodBoard(TreeTentBoard board, Integer index, boolean isRow) { List tents; if (isRow) { tents = board.getRowCol(index, TreeTentType.TENT, true); - } - else { + } else { tents = board.getRowCol(index, TreeTentType.TENT, false); } @@ -148,13 +172,13 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -162,13 +186,13 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * 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 + * 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 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 + * @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) { @@ -179,7 +203,7 @@ public List dependentElements(Board board, PuzzleElement puzzleEl // add all elements of filled row for (int i = 0; i < treeTentBoard.getWidth(); i++) { - TreeTentCell cell = treeTentBoard.getCell(i, clue.getClueIndex()-1); + TreeTentCell cell = treeTentBoard.getCell(i, clue.getClueIndex() - 1); elements.add(board.getPuzzleElement((cell))); } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithGrassDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithGrassDirectRule.java index 40ee11e99..838a3ca24 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithGrassDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithGrassDirectRule.java @@ -1,86 +1,87 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.awt.*; -import java.util.List; - -public class FinishWithGrassDirectRule extends DirectRule { - - public FinishWithGrassDirectRule() { - super("TREE-BASC-0002", "Finish with Grass", - "Grass can be added to finish a row or column that has reached its tent limit.", - "edu/rpi/legup/images/treetent/finishGrass.png"); - } - - /** - * 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) { - if (puzzleElement instanceof TreeTentLine) { - return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule"; - } - TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); - TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); - TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); - TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(finalCell.getType() == TreeTentType.GRASS && initCell.getType() == TreeTentType.UNKNOWN)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be grass."; - } - - if (isForced(initialBoard, initCell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be grass."; - } - } - - private boolean isForced(TreeTentBoard board, TreeTentCell cell) { - Point loc = cell.getLocation(); - List tentsRow = board.getRowCol(loc.y, TreeTentType.TENT, true); - List tentsCol = board.getRowCol(loc.x, TreeTentType.TENT, false); - - return tentsRow.size() >= board.getRowClues().get(loc.y).getData() || - tentsCol.size() >= board.getColClues().get(loc.x).getData(); - } - - /** - * 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) { - TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); - for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - TreeTentCell cell = (TreeTentCell) element; - if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { - cell.setData(TreeTentType.GRASS); - treeTentBoard.addModifiedData(cell); - } - } - if (treeTentBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return treeTentBoard; - } - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.awt.*; +import java.util.List; + +public class FinishWithGrassDirectRule extends DirectRule { + + public FinishWithGrassDirectRule() { + super( + "TREE-BASC-0002", + "Finish with Grass", + "Grass can be added to finish a row or column that has reached its tent limit.", + "edu/rpi/legup/images/treetent/finishGrass.png"); + } + + /** + * 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) { + if (puzzleElement instanceof TreeTentLine) { + return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule"; + } + TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); + TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); + TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); + TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(finalCell.getType() == TreeTentType.GRASS + && initCell.getType() == TreeTentType.UNKNOWN)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be grass."; + } + + if (isForced(initialBoard, initCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be grass."; + } + } + + private boolean isForced(TreeTentBoard board, TreeTentCell cell) { + Point loc = cell.getLocation(); + List tentsRow = board.getRowCol(loc.y, TreeTentType.TENT, true); + List tentsCol = board.getRowCol(loc.x, TreeTentType.TENT, false); + + return tentsRow.size() >= board.getRowClues().get(loc.y).getData() + || tentsCol.size() >= board.getColClues().get(loc.x).getData(); + } + + /** + * 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) { + TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); + for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { + TreeTentCell cell = (TreeTentCell) element; + if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { + cell.setData(TreeTentType.GRASS); + treeTentBoard.addModifiedData(cell); + } + } + if (treeTentBoard.getModifiedData().isEmpty()) { + return null; + } else { + return treeTentBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithTentsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithTentsDirectRule.java index 145a9760a..ae1a72e9d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithTentsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FinishWithTentsDirectRule.java @@ -1,88 +1,90 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.awt.*; -import java.util.List; - -public class FinishWithTentsDirectRule extends DirectRule { - - public FinishWithTentsDirectRule() { - super("TREE-BASC-0003", "Finish with Tents", - "Tents can be added to finish a row or column that has one open spot per required tent.", - "edu/rpi/legup/images/treetent/finishTent.png"); - } - - /** - * 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) { - if (puzzleElement instanceof TreeTentLine) { - return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; - } - TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); - TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); - TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); - TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(initCell.getType() == TreeTentType.UNKNOWN && finalCell.getType() == TreeTentType.TENT)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; - } - - if (isForced(initialBoard, initCell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; - } - } - - private boolean isForced(TreeTentBoard board, TreeTentCell cell) { - Point loc = cell.getLocation(); - List tentsRow = board.getRowCol(loc.y, TreeTentType.TENT, true); - List unknownsRow = board.getRowCol(loc.y, TreeTentType.UNKNOWN, true); - List tentsCol = board.getRowCol(loc.x, TreeTentType.TENT, false); - List unknownsCol = board.getRowCol(loc.x, TreeTentType.UNKNOWN, false); - - return unknownsRow.size() <= board.getRowClues().get(loc.y).getData() - tentsRow.size() || - unknownsCol.size() <= board.getColClues().get(loc.x).getData() - tentsCol.size(); - } - - /** - * 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) { - TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); - for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - TreeTentCell cell = (TreeTentCell) element; - if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { - cell.setData(TreeTentType.TENT); - treeTentBoard.addModifiedData(cell); - } - } - if (treeTentBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return treeTentBoard; - } - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.awt.*; +import java.util.List; + +public class FinishWithTentsDirectRule extends DirectRule { + + public FinishWithTentsDirectRule() { + super( + "TREE-BASC-0003", + "Finish with Tents", + "Tents can be added to finish a row or column that has one open spot per required" + + " tent.", + "edu/rpi/legup/images/treetent/finishTent.png"); + } + + /** + * 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) { + if (puzzleElement instanceof TreeTentLine) { + return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; + } + TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); + TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); + TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); + TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == TreeTentType.UNKNOWN + && finalCell.getType() == TreeTentType.TENT)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; + } + + if (isForced(initialBoard, initCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; + } + } + + private boolean isForced(TreeTentBoard board, TreeTentCell cell) { + Point loc = cell.getLocation(); + List tentsRow = board.getRowCol(loc.y, TreeTentType.TENT, true); + List unknownsRow = board.getRowCol(loc.y, TreeTentType.UNKNOWN, true); + List tentsCol = board.getRowCol(loc.x, TreeTentType.TENT, false); + List unknownsCol = board.getRowCol(loc.x, TreeTentType.UNKNOWN, false); + + return unknownsRow.size() <= board.getRowClues().get(loc.y).getData() - tentsRow.size() + || unknownsCol.size() <= board.getColClues().get(loc.x).getData() - tentsCol.size(); + } + + /** + * 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) { + TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); + for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { + TreeTentCell cell = (TreeTentCell) element; + if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { + cell.setData(TreeTentType.TENT); + treeTentBoard.addModifiedData(cell); + } + } + if (treeTentBoard.getModifiedData().isEmpty()) { + return null; + } else { + return treeTentBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotDirectRule.java index ffb33e4c0..53f7b9831 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LastCampingSpotDirectRule.java @@ -1,102 +1,104 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.util.List; - -public class LastCampingSpotDirectRule extends DirectRule { - - public LastCampingSpotDirectRule() { - super("TREE-BASC-0004", "Last Camping Spot", - "If an unlinked tree is adjacent to only one blank cell and not adjacent to any unlinked tents, the blank cell must be a tent.", - "edu/rpi/legup/images/treetent/oneTentPosition.png"); - } - - /** - * 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) { - if (puzzleElement instanceof TreeTentLine) { - return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; - } - TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); - TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); - TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); - TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(initCell.getType() == TreeTentType.UNKNOWN && finalCell.getType() == TreeTentType.TENT)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; - } - - if (isForced(finalBoard, finalCell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; - } - } - - private boolean isForced(TreeTentBoard board, TreeTentCell cell) { - List adjTrees = board.getAdjacent(cell, TreeTentType.TREE); - for (TreeTentCell c : adjTrees) { - List unkAroundTree = board.getAdjacent(c, TreeTentType.UNKNOWN); - List tntAroundTree = board.getAdjacent(c, TreeTentType.TENT); - if (unkAroundTree.size() == 0) { - if (tntAroundTree.size() == 1) { - return true; - } - else { - for (TreeTentCell t : tntAroundTree) { - if (t == cell) { - continue; - } - List treesAroundTents = board.getAdjacent(t, TreeTentType.TREE); - if (treesAroundTents.size() == 1) { - return false; - } - } - return true; - } - } - } - return false; - } - - /** - * 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) { - TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); - for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - TreeTentCell cell = (TreeTentCell) element; - if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { - cell.setData(TreeTentType.TENT); - treeTentBoard.addModifiedData(cell); - } - } - if (treeTentBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return treeTentBoard; - } - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.util.List; + +public class LastCampingSpotDirectRule extends DirectRule { + + public LastCampingSpotDirectRule() { + super( + "TREE-BASC-0004", + "Last Camping Spot", + "If an unlinked tree is adjacent to only one blank cell and not adjacent to any" + + " unlinked tents, the blank cell must be a tent.", + "edu/rpi/legup/images/treetent/oneTentPosition.png"); + } + + /** + * 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) { + if (puzzleElement instanceof TreeTentLine) { + return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; + } + TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); + TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); + TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); + TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == TreeTentType.UNKNOWN + && finalCell.getType() == TreeTentType.TENT)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; + } + + if (isForced(finalBoard, finalCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; + } + } + + private boolean isForced(TreeTentBoard board, TreeTentCell cell) { + List adjTrees = board.getAdjacent(cell, TreeTentType.TREE); + for (TreeTentCell c : adjTrees) { + List unkAroundTree = board.getAdjacent(c, TreeTentType.UNKNOWN); + List tntAroundTree = board.getAdjacent(c, TreeTentType.TENT); + if (unkAroundTree.size() == 0) { + if (tntAroundTree.size() == 1) { + return true; + } else { + for (TreeTentCell t : tntAroundTree) { + if (t == cell) { + continue; + } + List treesAroundTents = + board.getAdjacent(t, TreeTentType.TREE); + if (treesAroundTents.size() == 1) { + return false; + } + } + return true; + } + } + } + return false; + } + + /** + * 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) { + TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); + for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { + TreeTentCell cell = (TreeTentCell) element; + if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { + cell.setData(TreeTentType.TENT); + treeTentBoard.addModifiedData(cell); + } + } + if (treeTentBoard.getModifiedData().isEmpty()) { + return null; + } else { + return treeTentBoard; + } + } +} 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 39b1d0251..bd303174a 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 @@ -6,10 +6,9 @@ import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.TreeTentCell; import edu.rpi.legup.puzzle.treetent.TreeTentLine; - +import edu.rpi.legup.puzzle.treetent.TreeTentType; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -17,7 +16,9 @@ public class LinkTentCaseRule extends CaseRule { public LinkTentCaseRule() { - super("TREE-CASE-0002", "Links from tent", + super( + "TREE-CASE-0002", + "Links from tent", "A tent must link to exactly one adjacent tree.", "edu/rpi/legup/images/treetent/caseLinkTent.png"); } @@ -28,11 +29,15 @@ public CaseBoard getCaseBoard(Board board) { treeTentBoard.setModifiable(false); CaseBoard caseBoard = new CaseBoard(treeTentBoard, this); for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - if (((TreeTentCell) element).getType() == TreeTentType.TENT && !getCases(board, element).isEmpty()) { + if (((TreeTentCell) element).getType() == TreeTentType.TENT + && !getCases(board, element).isEmpty()) { Boolean canAdd = true; List lines = treeTentBoard.getLines(); for (TreeTentLine l : lines) { - if (l.getC1().getLocation().equals(((TreeTentCell) element).getLocation()) || l.getC2().getLocation().equals(((TreeTentCell) element).getLocation())) { + if (l.getC1().getLocation().equals(((TreeTentCell) element).getLocation()) + || l.getC2() + .getLocation() + .equals(((TreeTentCell) element).getLocation())) { canAdd = false; break; } @@ -48,7 +53,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -62,13 +67,17 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { for (TreeTentCell tree : adj) { Boolean makeline = true; for (TreeTentLine l : ((TreeTentBoard) board).getLines()) { - if (l.getC1().getLocation().equals(tree.getLocation()) || l.getC2().getLocation().equals(tree.getLocation())) { + if (l.getC1().getLocation().equals(tree.getLocation()) + || l.getC2().getLocation().equals(tree.getLocation())) { makeline = false; } } if (makeline) { TreeTentBoard temp = ((TreeTentBoard) board).copy(); - TreeTentLine l = new TreeTentLine((TreeTentCell) temp.getPuzzleElement(cell), (TreeTentCell) temp.getPuzzleElement(tree)); + TreeTentLine l = + new TreeTentLine( + (TreeTentCell) temp.getPuzzleElement(cell), + (TreeTentCell) temp.getPuzzleElement(tree)); temp.getLines().add(l); temp.addModifiedData(l); cases.add(temp); @@ -87,12 +96,14 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { public String checkRuleRaw(TreeTransition transition) { Set modCells = transition.getBoard().getModifiedData(); if (modCells.size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case"; } PuzzleElement mod = modCells.iterator().next(); TreeTentLine line = mod instanceof TreeTentLine ? (TreeTentLine) mod : null; if (line == null) { - return super.getInvalidUseOfRuleMessage() + ": This case rule only involves tree and tent connection lines"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule only involves tree and tent connection lines"; } TreeTentCell tent = null; if (line.getC1().getType() == TreeTentType.TENT) { @@ -122,7 +133,8 @@ public String checkRuleRaw(TreeTransition transition) { } PuzzleElement tElement = tBoard.getModifiedData().iterator().next(); if (!(tElement instanceof TreeTentLine)) { - return super.getInvalidUseOfRuleMessage() + ": This case rule only involves tree and tent connection lines"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule only involves tree and tent connection lines"; } if (cLine.compare((TreeTentLine) tElement)) { hasLine = true; @@ -138,13 +150,13 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -152,13 +164,13 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * 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 + * 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 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 + * @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) { 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 72ffd62eb..03d039898 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 @@ -9,7 +9,6 @@ import edu.rpi.legup.puzzle.treetent.TreeTentCell; 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; @@ -18,7 +17,9 @@ public class LinkTreeCaseRule extends CaseRule { public LinkTreeCaseRule() { - super("TREE-CASE-0003", "Links from tree", + super( + "TREE-CASE-0003", + "Links from tree", "A tree must link to exactly one adjacent tent.", "edu/rpi/legup/images/treetent/caseLinkTree.png"); } @@ -29,13 +30,16 @@ public CaseBoard getCaseBoard(Board board) { treeTentBoard.setModifiable(false); CaseBoard caseBoard = new CaseBoard(treeTentBoard, this); for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - if (((TreeTentCell) element).getType() == TreeTentType.TREE && - !getCases(treeTentBoard, element).isEmpty()) { + if (((TreeTentCell) element).getType() == TreeTentType.TREE + && !getCases(treeTentBoard, element).isEmpty()) { Boolean canAdd = true; List lines = treeTentBoard.getLines(); for (TreeTentLine l : lines) { - if (l.getC1().getLocation().equals(((TreeTentCell) element).getLocation()) || l.getC2().getLocation().equals(((TreeTentCell) element).getLocation())) { + if (l.getC1().getLocation().equals(((TreeTentCell) element).getLocation()) + || l.getC2() + .getLocation() + .equals(((TreeTentCell) element).getLocation())) { canAdd = false; break; } @@ -43,7 +47,6 @@ public CaseBoard getCaseBoard(Board board) { if (canAdd) { caseBoard.addPickableElement(element); } - } } return caseBoard; @@ -52,7 +55,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -65,7 +68,8 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { for (TreeTentCell c : adjCells) { Boolean makeline = true; for (TreeTentLine l : treeTentBoard.getLines()) { - if (l.getC1().getLocation().equals(c.getLocation()) || l.getC2().getLocation().equals(c.getLocation())) { + if (l.getC1().getLocation().equals(c.getLocation()) + || l.getC2().getLocation().equals(c.getLocation())) { makeline = false; } } @@ -90,12 +94,14 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { public String checkRuleRaw(TreeTransition transition) { Set modCells = transition.getBoard().getModifiedData(); if (modCells.size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case"; } PuzzleElement mod = modCells.iterator().next(); TreeTentLine line = mod instanceof TreeTentLine ? (TreeTentLine) mod : null; if (line == null) { - return super.getInvalidUseOfRuleMessage() + ": This case rule only involves tree and tent connection lines"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule only involves tree and tent connection lines"; } TreeTentCell tree = null; if (line.getC1().getType() == TreeTentType.TREE) { @@ -125,7 +131,8 @@ public String checkRuleRaw(TreeTransition transition) { } PuzzleElement tElement = tBoard.getModifiedData().iterator().next(); if (!(tElement instanceof TreeTentLine)) { - return super.getInvalidUseOfRuleMessage() + ": This case rule only involves tree and tent connection lines"; + return super.getInvalidUseOfRuleMessage() + + ": This case rule only involves tree and tent connection lines"; } if (cLine.compare((TreeTentLine) tElement)) { hasLine = true; @@ -141,13 +148,13 @@ public String checkRuleRaw(TreeTransition transition) { } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -155,17 +162,18 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * 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 + * 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 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 + * @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))); + List elements = + new ArrayList<>(List.of(board.getPuzzleElement(puzzleElement))); TreeTentBoard treeTentBoard = (TreeTentBoard) board; TreeTentCell point = (TreeTentCell) puzzleElement; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTentForTreeContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTentForTreeContradictionRule.java index 2e5c2f043..912fa6d20 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTentForTreeContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTentForTreeContradictionRule.java @@ -5,27 +5,29 @@ import edu.rpi.legup.model.rules.ContradictionRule; 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.TreeTentLine; - -import java.util.List; +import edu.rpi.legup.puzzle.treetent.TreeTentType; import java.util.Iterator; +import java.util.List; public class NoTentForTreeContradictionRule extends ContradictionRule { public NoTentForTreeContradictionRule() { - super("TREE-CONT-0001", "No Tent For Tree", + super( + "TREE-CONT-0001", + "No Tent For Tree", "Each tree must link to a tent.", "edu/rpi/legup/images/treetent/contra_NoTentForTree.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -38,8 +40,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int adjUnknown = treeTentBoard.getAdjacent(cell, TreeTentType.UNKNOWN).size(); if (adjTent == 0 && adjUnknown == 0) { return null; - } - else { + } else { if (adjTent != 0) { List lines = treeTentBoard.getLines(); List adjTents = treeTentBoard.getAdjacent(cell, TreeTentType.TENT); @@ -47,10 +48,12 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Iterator i = adjTents.iterator(); while (i.hasNext()) { TreeTentCell t = i.next(); - if (t.getLocation().equals(l.getC1().getLocation()) && !(cell.getLocation().equals(l.getC2().getLocation()))) { + if (t.getLocation().equals(l.getC1().getLocation()) + && !(cell.getLocation().equals(l.getC2().getLocation()))) { i.remove(); } - if (t.getLocation().equals(l.getC2().getLocation()) && !(cell.getLocation().equals(l.getC2().getLocation()))) { + if (t.getLocation().equals(l.getC2().getLocation()) + && !(cell.getLocation().equals(l.getC2().getLocation()))) { i.remove(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java index 67f023dd7..9bc3fddea 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/NoTreeForTentContradictionRule.java @@ -7,25 +7,27 @@ import edu.rpi.legup.puzzle.treetent.TreeTentCell; import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.util.List; import java.util.Iterator; +import java.util.List; public class NoTreeForTentContradictionRule extends ContradictionRule { public NoTreeForTentContradictionRule() { - super("TREE-CONT-0002", "No Tree For Tent", + super( + "TREE-CONT-0002", + "No Tree For Tent", "Each tent must link to a tree.", "edu/rpi/legup/images/treetent/contra_NoTreeForTent.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -40,10 +42,12 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Iterator i = adjTrees.iterator(); while (i.hasNext()) { TreeTentCell t = i.next(); - if (t.getLocation().equals(l.getC1().getLocation()) && !(cell.getLocation().equals(l.getC2().getLocation()))) { + if (t.getLocation().equals(l.getC1().getLocation()) + && !(cell.getLocation().equals(l.getC2().getLocation()))) { i.remove(); } - if (t.getLocation().equals(l.getC2().getLocation()) && !(cell.getLocation().equals(l.getC2().getLocation()))) { + if (t.getLocation().equals(l.getC2().getLocation()) + && !(cell.getLocation().equals(l.getC2().getLocation()))) { i.remove(); } } @@ -51,8 +55,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int adjTree = adjTrees.size(); if (adjTree == 0) { return null; - } - else { + } else { return super.getNoContradictionMessage(); } } 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 44e9dfcc4..e800dc416 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 @@ -1,85 +1,86 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.util.List; - -public class SurroundTentWithGrassDirectRule extends DirectRule { - - public SurroundTentWithGrassDirectRule() { - super("TREE-BASC-0005", "Surround Tent with Grass", - "Blank cells adjacent or diagonal to a tent are grass.", - "edu/rpi/legup/images/treetent/aroundTent.png"); - } - - /** - * 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) { - if (puzzleElement == null) { - return null; - } - if (puzzleElement instanceof TreeTentLine) { - return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; - } - TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); - TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); - TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); - TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); - if (!(initCell.getType() == TreeTentType.UNKNOWN && finalCell.getType() == TreeTentType.GRASS)) { - return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; - } - - if (isForced(initialBoard, initCell)) { - return null; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; - } - } - - private boolean isForced(TreeTentBoard board, TreeTentCell cell) { - List tents = board.getAdjacent(cell, TreeTentType.TENT); - tents.addAll(board.getDiagonals(cell, TreeTentType.TENT)); - return !tents.isEmpty(); - } - - /** - * 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) { - TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); - for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { - TreeTentCell cell = (TreeTentCell) element; - if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { - cell.setData(TreeTentType.GRASS); - treeTentBoard.addModifiedData(cell); - } - } - if (treeTentBoard.getModifiedData().isEmpty()) { - return null; - } - else { - return treeTentBoard; - } - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.util.List; + +public class SurroundTentWithGrassDirectRule extends DirectRule { + + public SurroundTentWithGrassDirectRule() { + super( + "TREE-BASC-0005", + "Surround Tent with Grass", + "Blank cells adjacent or diagonal to a tent are grass.", + "edu/rpi/legup/images/treetent/aroundTent.png"); + } + + /** + * 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) { + if (puzzleElement == null) { + return null; + } + if (puzzleElement instanceof TreeTentLine) { + return super.getInvalidUseOfRuleMessage() + ": Line is not valid for this rule."; + } + TreeTentBoard initialBoard = (TreeTentBoard) transition.getParents().get(0).getBoard(); + TreeTentCell initCell = (TreeTentCell) initialBoard.getPuzzleElement(puzzleElement); + TreeTentBoard finalBoard = (TreeTentBoard) transition.getBoard(); + TreeTentCell finalCell = (TreeTentCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(initCell.getType() == TreeTentType.UNKNOWN + && finalCell.getType() == TreeTentType.GRASS)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be a tent."; + } + + if (isForced(initialBoard, initCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be tent."; + } + } + + private boolean isForced(TreeTentBoard board, TreeTentCell cell) { + List tents = board.getAdjacent(cell, TreeTentType.TENT); + tents.addAll(board.getDiagonals(cell, TreeTentType.TENT)); + return !tents.isEmpty(); + } + + /** + * 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) { + TreeTentBoard treeTentBoard = (TreeTentBoard) node.getBoard().copy(); + for (PuzzleElement element : treeTentBoard.getPuzzleElements()) { + TreeTentCell cell = (TreeTentCell) element; + if (cell.getType() == TreeTentType.UNKNOWN && isForced(treeTentBoard, cell)) { + cell.setData(TreeTentType.GRASS); + treeTentBoard.addModifiedData(cell); + } + } + if (treeTentBoard.getModifiedData().isEmpty()) { + return null; + } else { + return treeTentBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentForTreeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentForTreeDirectRule.java index 03a83e516..8044faa0e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentForTreeDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentForTreeDirectRule.java @@ -1,124 +1,125 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentType; - -import java.util.ArrayList; - -import java.util.List; - -public class TentForTreeDirectRule extends DirectRule { - - public TentForTreeDirectRule() { - super("TREE-BASC-0006", "Tent for Tree", - "If only one unlinked tent and no blank cells are adjacent to an unlinked tree, the unlinked tree must link to the unlinked tent.", - "edu/rpi/legup/images/treetent/NewTreeLink.png"); - } - - /** - * 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) { - if (!(puzzleElement instanceof TreeTentLine)) { - return super.getInvalidUseOfRuleMessage() + ": Lines must be created for this rule."; - } - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - TreeTentLine line = (TreeTentLine) board.getPuzzleElement(puzzleElement); - TreeTentCell tree, tent; - if (line.getC1().getType() == TreeTentType.TREE && line.getC2().getType() == TreeTentType.TENT) { - tree = line.getC1(); - tent = line.getC2(); - } - else { - if (line.getC2().getType() == TreeTentType.TREE && line.getC1().getType() == TreeTentType.TENT) { - tree = line.getC2(); - tent = line.getC1(); - } - else { - return super.getInvalidUseOfRuleMessage() + ": This line must connect a tree to a tent."; - } - } - int forced = isForced(board, tree, tent, line); - if (forced == 1) { - return null; - } - else { - if (forced == -1) { - return super.getInvalidUseOfRuleMessage() + ": This tree already has a link"; - } - else { - if (forced == -2) { - return super.getInvalidUseOfRuleMessage() + ": This tent already has a link"; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This tree and tent don't need to be linked."; - } - } - } - } - - private Integer isForced(TreeTentBoard board, TreeTentCell tree, TreeTentCell tent, TreeTentLine line) { - List adjTents = board.getAdjacent(tree, TreeTentType.TENT); - adjTents.remove(tent); - List lines = board.getLines(); - lines.remove(line); - for (TreeTentLine l : lines) { - ArrayList toRemove = new ArrayList<>(); - if (l.getC1().getLocation().equals(tree.getLocation()) || l.getC2().getLocation().equals(tree.getLocation())) { - return -2; - } - for (TreeTentCell c : adjTents) { - if (l.getC1().getLocation().equals(c.getLocation())) { - if (l.getC2().getLocation().equals(tree.getLocation())) { - return -1; - } - toRemove.add(c); - - } - else { - if (l.getC2().getLocation().equals(c.getLocation())) { - if (l.getC1().getLocation().equals(tree.getLocation())) { - return -1; - } - toRemove.add(c); - } - } - } - for (TreeTentCell c : toRemove) { - adjTents.remove(c); - } - toRemove.clear(); - } - if (adjTents.size() == 0) { - return 1; - } - else { - return 0; - } - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.util.ArrayList; +import java.util.List; + +public class TentForTreeDirectRule extends DirectRule { + + public TentForTreeDirectRule() { + super( + "TREE-BASC-0006", + "Tent for Tree", + "If only one unlinked tent and no blank cells are adjacent to an unlinked tree, the" + + " unlinked tree must link to the unlinked tent.", + "edu/rpi/legup/images/treetent/NewTreeLink.png"); + } + + /** + * 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) { + if (!(puzzleElement instanceof TreeTentLine)) { + return super.getInvalidUseOfRuleMessage() + ": Lines must be created for this rule."; + } + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentLine line = (TreeTentLine) board.getPuzzleElement(puzzleElement); + TreeTentCell tree, tent; + if (line.getC1().getType() == TreeTentType.TREE + && line.getC2().getType() == TreeTentType.TENT) { + tree = line.getC1(); + tent = line.getC2(); + } else { + if (line.getC2().getType() == TreeTentType.TREE + && line.getC1().getType() == TreeTentType.TENT) { + tree = line.getC2(); + tent = line.getC1(); + } else { + return super.getInvalidUseOfRuleMessage() + + ": This line must connect a tree to a tent."; + } + } + int forced = isForced(board, tree, tent, line); + if (forced == 1) { + return null; + } else { + if (forced == -1) { + return super.getInvalidUseOfRuleMessage() + ": This tree already has a link"; + } else { + if (forced == -2) { + return super.getInvalidUseOfRuleMessage() + ": This tent already has a link"; + } else { + return super.getInvalidUseOfRuleMessage() + + ": This tree and tent don't need to be linked."; + } + } + } + } + + private Integer isForced( + TreeTentBoard board, TreeTentCell tree, TreeTentCell tent, TreeTentLine line) { + List adjTents = board.getAdjacent(tree, TreeTentType.TENT); + adjTents.remove(tent); + List lines = board.getLines(); + lines.remove(line); + for (TreeTentLine l : lines) { + ArrayList toRemove = new ArrayList<>(); + if (l.getC1().getLocation().equals(tree.getLocation()) + || l.getC2().getLocation().equals(tree.getLocation())) { + return -2; + } + for (TreeTentCell c : adjTents) { + if (l.getC1().getLocation().equals(c.getLocation())) { + if (l.getC2().getLocation().equals(tree.getLocation())) { + return -1; + } + toRemove.add(c); + + } else { + if (l.getC2().getLocation().equals(c.getLocation())) { + if (l.getC1().getLocation().equals(tree.getLocation())) { + return -1; + } + toRemove.add(c); + } + } + } + for (TreeTentCell c : toRemove) { + adjTents.remove(c); + } + toRemove.clear(); + } + if (adjTents.size() == 0) { + return 1; + } else { + return 0; + } + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentOrGrassCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentOrGrassCaseRule.java index b0e9db109..63478f83e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentOrGrassCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TentOrGrassCaseRule.java @@ -8,14 +8,15 @@ import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; import edu.rpi.legup.puzzle.treetent.TreeTentType; - import java.util.ArrayList; import java.util.List; public class TentOrGrassCaseRule extends CaseRule { public TentOrGrassCaseRule() { - super("TREE-CASE-0004", "Tent or Grass", + super( + "TREE-CASE-0004", + "Tent or Grass", "Each blank cell is either a tent or grass.", "edu/rpi/legup/images/treetent/caseTentOrGrass.png"); } @@ -36,7 +37,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ @@ -74,33 +75,36 @@ public String checkRuleRaw(TreeTransition transition) { TreeTransition case1 = childTransitions.get(0); TreeTransition case2 = childTransitions.get(1); - if (case1.getBoard().getModifiedData().size() != 1 || - case2.getBoard().getModifiedData().size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; + if (case1.getBoard().getModifiedData().size() != 1 + || case2.getBoard().getModifiedData().size() != 1) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case."; } TreeTentCell mod1 = (TreeTentCell) case1.getBoard().getModifiedData().iterator().next(); TreeTentCell mod2 = (TreeTentCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; } - if (!((mod1.getType() == TreeTentType.TENT && mod2.getType() == TreeTentType.GRASS) || - (mod2.getType() == TreeTentType.TENT && mod1.getType() == TreeTentType.GRASS))) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have a tent and a grass cell."; + if (!((mod1.getType() == TreeTentType.TENT && mod2.getType() == TreeTentType.GRASS) + || (mod2.getType() == TreeTentType.TENT && mod1.getType() == TreeTentType.GRASS))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have a tent and a grass cell."; } return null; } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooFewTentsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooFewTentsContradictionRule.java index 92b951feb..7b3f43dbe 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooFewTentsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooFewTentsContradictionRule.java @@ -6,24 +6,26 @@ import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; import edu.rpi.legup.puzzle.treetent.TreeTentType; - import java.awt.*; public class TooFewTentsContradictionRule extends ContradictionRule { public TooFewTentsContradictionRule() { - super("TREE-CONT-0003", "Too Few Tents", + super( + "TREE-CONT-0003", + "Too Few Tents", "Rows and columns cannot have fewer tents than their clue.", "edu/rpi/legup/images/treetent/too_few_tents.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -36,11 +38,10 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int rowUnknowns = treeTentBoard.getRowCol(loc.y, TreeTentType.UNKNOWN, true).size(); int colUnknowns = treeTentBoard.getRowCol(loc.x, TreeTentType.UNKNOWN, false).size(); - if (rowTents + rowUnknowns < treeTentBoard.getRowClues().get(loc.y).getData() || - colTents + colUnknowns < treeTentBoard.getColClues().get(loc.x).getData()) { + if (rowTents + rowUnknowns < treeTentBoard.getRowClues().get(loc.y).getData() + || colTents + colUnknowns < treeTentBoard.getColClues().get(loc.x).getData()) { return null; - } - else { + } else { return super.getNoContradictionMessage(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooManyTentsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooManyTentsContradictionRule.java index 8bc908d0c..dcc65feb6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooManyTentsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TooManyTentsContradictionRule.java @@ -6,24 +6,26 @@ import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; import edu.rpi.legup.puzzle.treetent.TreeTentType; - import java.awt.*; public class TooManyTentsContradictionRule extends ContradictionRule { public TooManyTentsContradictionRule() { - super("TREE-CONT-0004", "Too Many Tents", + super( + "TREE-CONT-0004", + "Too Many Tents", "Rows and columns cannot have more tents than their clue.", "edu/rpi/legup/images/treetent/too_many_tents.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent puzzleElement * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -34,11 +36,10 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int rowTents = treeTentBoard.getRowCol(loc.y, TreeTentType.TENT, true).size(); int colTents = treeTentBoard.getRowCol(loc.x, TreeTentType.TENT, false).size(); - if (rowTents > treeTentBoard.getRowClues().get(loc.y).getData() || - colTents > treeTentBoard.getColClues().get(loc.x).getData()) { + if (rowTents > treeTentBoard.getRowClues().get(loc.y).getData() + || colTents > treeTentBoard.getColClues().get(loc.x).getData()) { return null; - } - else { + } else { return super.getNoContradictionMessage(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java index a07153a31..d9dd36e12 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TouchingTentsContradictionRule.java @@ -10,20 +10,21 @@ public class TouchingTentsContradictionRule extends ContradictionRule { public TouchingTentsContradictionRule() { - super("TREE-CONT-0005", "Touching Tents", + super( + "TREE-CONT-0005", + "Touching Tents", "Tents cannot touch other tents.", "edu/rpi/legup/images/treetent/contra_adjacentTents.png"); } /** - * Checks whether the transition has a contradiction at the specific - * {@link PuzzleElement} index using this rule + * Checks whether the transition has a contradiction at the specific {@link PuzzleElement} index + * using this rule * - * @param board board to check contradiction + * @param board board to check contradiction * @param puzzleElement equivalent {@link PuzzleElement} - * @return null if the transition contains a - * contradiction at the specified puzzleElement, - * otherwise error message. + * @return null if the transition contains a contradiction at the specified + * puzzleElement, otherwise error message. */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -36,8 +37,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int diagTree = treeTentBoard.getDiagonals(cell, TreeTentType.TENT).size(); if (adjTree > 0 || diagTree > 0) { return null; - } - else { + } else { return super.getNoContradictionMessage(); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentDirectRule.java index 63d43e9a4..d4360c7f6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/TreeForTentDirectRule.java @@ -1,122 +1,124 @@ -package edu.rpi.legup.puzzle.treetent.rules; - -import java.util.List; -import java.util.ArrayList; - -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.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTentLine; -import edu.rpi.legup.puzzle.treetent.TreeTentBoard; -import edu.rpi.legup.puzzle.treetent.TreeTentType; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; - -public class TreeForTentDirectRule extends DirectRule { - public TreeForTentDirectRule() { - super("TREE-BASC-0007", "Tree for Tent", - "If only one unlinked tree is adjacent to an unlinked tent, the unlinked tent must link to the unlinked tree.", - "edu/rpi/legup/images/treetent/NewTentLink.png"); - } - - /** - * 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) { - if (!(puzzleElement instanceof TreeTentLine)) { - return super.getInvalidUseOfRuleMessage() + ": Lines must be created for this rule."; - } - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - TreeTentLine line = (TreeTentLine) board.getPuzzleElement(puzzleElement); - TreeTentCell tree, tent; - if (line.getC1().getType() == TreeTentType.TREE && line.getC2().getType() == TreeTentType.TENT) { - tree = line.getC1(); - tent = line.getC2(); - } - else { - if (line.getC2().getType() == TreeTentType.TREE && line.getC1().getType() == TreeTentType.TENT) { - tree = line.getC2(); - tent = line.getC1(); - } - else { - return super.getInvalidUseOfRuleMessage() + ": This line must connect a tree to a tent."; - } - } - int forced = isForced(board, tree, tent, line); - if (forced == 1) { - return null; - } - else { - if (forced == -1) { - return super.getInvalidUseOfRuleMessage() + ": This tent already has a link"; - } - else { - if (forced == -2) { - return super.getInvalidUseOfRuleMessage() + ": This tree already has a link"; - } - else { - return super.getInvalidUseOfRuleMessage() + ": This tree and tent don't need to be linked."; - } - } - } - } - - private Integer isForced(TreeTentBoard board, TreeTentCell tree, TreeTentCell tent, TreeTentLine line) { - List adjTrees = board.getAdjacent(tent, TreeTentType.TREE); - adjTrees.remove(tree); - List lines = board.getLines(); - lines.remove(line); - for (TreeTentLine l : lines) { - ArrayList toRemove = new ArrayList<>(); - if (l.getC1().getLocation().equals(tree.getLocation()) || l.getC2().getLocation().equals(tree.getLocation())) { - return -2; - } - for (TreeTentCell c : adjTrees) { - if (l.getC1().getLocation().equals(c.getLocation())) { - if (l.getC2().getLocation().equals(tent.getLocation())) { - return -1; - } - toRemove.add(c); - - } - else { - if (l.getC2().getLocation().equals(c.getLocation())) { - if (l.getC1().getLocation().equals(tent.getLocation())) { - return -1; - } - toRemove.add(c); - } - } - } - for (TreeTentCell c : toRemove) { - adjTrees.remove(c); - } - toRemove.clear(); - } - if (adjTrees.size() == 0) { - return 1; - } - else { - return 0; - } - } - - /** - * 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; - } -} +package edu.rpi.legup.puzzle.treetent.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.util.ArrayList; +import java.util.List; + +public class TreeForTentDirectRule extends DirectRule { + public TreeForTentDirectRule() { + super( + "TREE-BASC-0007", + "Tree for Tent", + "If only one unlinked tree is adjacent to an unlinked tent, the unlinked tent must" + + " link to the unlinked tree.", + "edu/rpi/legup/images/treetent/NewTentLink.png"); + } + + /** + * 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) { + if (!(puzzleElement instanceof TreeTentLine)) { + return super.getInvalidUseOfRuleMessage() + ": Lines must be created for this rule."; + } + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentLine line = (TreeTentLine) board.getPuzzleElement(puzzleElement); + TreeTentCell tree, tent; + if (line.getC1().getType() == TreeTentType.TREE + && line.getC2().getType() == TreeTentType.TENT) { + tree = line.getC1(); + tent = line.getC2(); + } else { + if (line.getC2().getType() == TreeTentType.TREE + && line.getC1().getType() == TreeTentType.TENT) { + tree = line.getC2(); + tent = line.getC1(); + } else { + return super.getInvalidUseOfRuleMessage() + + ": This line must connect a tree to a tent."; + } + } + int forced = isForced(board, tree, tent, line); + if (forced == 1) { + return null; + } else { + if (forced == -1) { + return super.getInvalidUseOfRuleMessage() + ": This tent already has a link"; + } else { + if (forced == -2) { + return super.getInvalidUseOfRuleMessage() + ": This tree already has a link"; + } else { + return super.getInvalidUseOfRuleMessage() + + ": This tree and tent don't need to be linked."; + } + } + } + } + + private Integer isForced( + TreeTentBoard board, TreeTentCell tree, TreeTentCell tent, TreeTentLine line) { + List adjTrees = board.getAdjacent(tent, TreeTentType.TREE); + adjTrees.remove(tree); + List lines = board.getLines(); + lines.remove(line); + for (TreeTentLine l : lines) { + ArrayList toRemove = new ArrayList<>(); + if (l.getC1().getLocation().equals(tree.getLocation()) + || l.getC2().getLocation().equals(tree.getLocation())) { + return -2; + } + for (TreeTentCell c : adjTrees) { + if (l.getC1().getLocation().equals(c.getLocation())) { + if (l.getC2().getLocation().equals(tent.getLocation())) { + return -1; + } + toRemove.add(c); + + } else { + if (l.getC2().getLocation().equals(c.getLocation())) { + if (l.getC1().getLocation().equals(tent.getLocation())) { + return -1; + } + toRemove.add(c); + } + } + } + for (TreeTentCell c : toRemove) { + adjTrees.remove(c); + } + toRemove.clear(); + } + if (adjTrees.size() == 0) { + return 1; + } else { + return 0; + } + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/save/ExportFileException.java b/src/main/java/edu/rpi/legup/save/ExportFileException.java index 46ebac9e0..9def1f392 100644 --- a/src/main/java/edu/rpi/legup/save/ExportFileException.java +++ b/src/main/java/edu/rpi/legup/save/ExportFileException.java @@ -5,5 +5,4 @@ public class ExportFileException extends Exception { public ExportFileException(String message) { super("Export File Exception: " + message); } - } diff --git a/src/main/java/edu/rpi/legup/save/InvalidFileFormatException.java b/src/main/java/edu/rpi/legup/save/InvalidFileFormatException.java index 94c229411..1e24c5762 100644 --- a/src/main/java/edu/rpi/legup/save/InvalidFileFormatException.java +++ b/src/main/java/edu/rpi/legup/save/InvalidFileFormatException.java @@ -5,5 +5,4 @@ public class InvalidFileFormatException extends Exception { public InvalidFileFormatException(String message) { super("InvalidFileFormatException: " + message); } - } diff --git a/src/main/java/edu/rpi/legup/save/SavableBoard.java b/src/main/java/edu/rpi/legup/save/SavableBoard.java index a7799a15a..bd246303b 100644 --- a/src/main/java/edu/rpi/legup/save/SavableBoard.java +++ b/src/main/java/edu/rpi/legup/save/SavableBoard.java @@ -1,10 +1,5 @@ package edu.rpi.legup.save; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; import java.io.FileInputStream; import java.io.InputStream; @@ -17,7 +12,5 @@ public class SavableBoard { public SavableBoard(String filePath) throws Exception { this.filePath = filePath; this.inputStream = new FileInputStream(filePath); - - } } diff --git a/src/main/java/edu/rpi/legup/save/SavableProof.java b/src/main/java/edu/rpi/legup/save/SavableProof.java index 101e56125..4f6e4697c 100644 --- a/src/main/java/edu/rpi/legup/save/SavableProof.java +++ b/src/main/java/edu/rpi/legup/save/SavableProof.java @@ -1,5 +1,3 @@ package edu.rpi.legup.save; -public class SavableProof { - -} +public class SavableProof {} diff --git a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java index fa049ab38..70fbea033 100644 --- a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java +++ b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java @@ -3,40 +3,39 @@ import edu.rpi.legup.app.Config; import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.CursorController; - -import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Arrays; import java.util.Objects; +import javax.swing.*; 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 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; private JLabel rowsLabel; @@ -48,53 +47,64 @@ public void actionPerformed(ActionEvent e) { 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 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("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 { - if (game.equals("ShortTruthTable")) { - homePanel.openEditorWithNewPuzzle("ShortTruthTable", textArea.getText().split("\n")); - } - else { - homePanel.openEditorWithNewPuzzle(game, Integer.valueOf(rows.getText()), Integer.valueOf(columns.getText())); + private ActionListener okButtonListener = + new ActionListener() { + /** + * Attempts to open the puzzle editor interface for the given game with the given + * dimensions + * + * @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("ShortTruthTable") && textArea.getText().isEmpty()) { + System.out.println("Unfilled fields"); + return; + } + if (!game.equals("ShortTruthTable") + && (game.isEmpty() + || rows.getText().isEmpty() + || columns.getText().isEmpty())) { + System.out.println("Unfilled fields"); + return; + } + + try { + 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) { + System.out.println("Failed to open editor with new puzzle"); + e.printStackTrace(System.out); + } } - setVisible(false); - } - catch (IllegalArgumentException e) { - System.out.println("Failed to open editor with new puzzle"); - e.printStackTrace(System.out); - } - } - }; + }; private JButton cancel = new JButton("Cancel"); - private ActionListener cancelButtonListener = new ActionListener() { - /** - * Dispose the puzzle creation dialog - * - * @param e the event to be processed - */ - @Override - public void actionPerformed(ActionEvent e) { - dispose(); - } - }; + private ActionListener cancelButtonListener = + new ActionListener() { + /** + * Dispose the puzzle creation dialog + * + * @param e the event to be processed + */ + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }; public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { super(parent, true); @@ -140,7 +150,11 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { c.add(columns); textArea = new JTextArea(); - textInputScrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + 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); @@ -153,8 +167,7 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { rows.setVisible(false); columnsLabel.setVisible(false); columns.setVisible(false); - } - else { + } else { textInputScrollPane.setVisible(false); rowsLabel.setVisible(true); rows.setVisible(true); @@ -166,12 +179,17 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { gameBox.addActionListener(cursorSelectedGame); ActionListener cursorPressedOk = CursorController.createListener(this, okButtonListener); ok.addActionListener(cursorPressedOk); - ActionListener cursorPressedCancel = CursorController.createListener(this, cancelButtonListener); + ActionListener cursorPressedCancel = + CursorController.createListener(this, cancelButtonListener); cancel.addActionListener(cursorPressedCancel); } public void initPuzzles() { - this.games = GameBoardFacade.getInstance().getConfig().getFileCreationEnabledPuzzles().toArray(new String[0]); + this.games = + GameBoardFacade.getInstance() + .getConfig() + .getFileCreationEnabledPuzzles() + .toArray(new String[0]); Arrays.sort(this.games); gameBox = new JComboBox(this.games); } @@ -183,25 +201,26 @@ public void actionPerformed(ActionEvent e) { try { 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.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) { + // Don't do anything. This is here to prevent the dialog from closing if the + // dimensions are + // invalid. } - catch (IllegalArgumentException exception) { - // Don't do anything. This is here to prevent the dialog from closing if the dimensions are invalid. - } - } - else { + } else { if (e.getSource() == cancel) { this.setVisible(false); - } - else { + } else { // Unknown Action Event } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/DynamicView.java b/src/main/java/edu/rpi/legup/ui/DynamicView.java index 038f87d23..8d3024c86 100644 --- a/src/main/java/edu/rpi/legup/ui/DynamicView.java +++ b/src/main/java/edu/rpi/legup/ui/DynamicView.java @@ -1,21 +1,20 @@ package edu.rpi.legup.ui; +import static java.awt.BorderLayout.*; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialFonts; - -import javax.imageio.ImageIO; -import javax.swing.*; -import javax.swing.event.ChangeEvent; import java.awt.*; import java.awt.event.*; import java.io.IOException; import java.util.Hashtable; import java.util.Objects; - -import static java.awt.BorderLayout.*; +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.swing.event.ChangeEvent; public class DynamicView extends JPanel { @@ -42,16 +41,14 @@ public DynamicView(ScrollView scrollView, DynamicViewType type) { /** * Sets up the zoomer for the given DynamicViewType * - * @param type The DynamicView that we are setting up the zoomer for (so - * the zoomer for the board view or the zoomer for the proof - * tree view) + * @param type The DynamicView that we are setting up the zoomer for (so the zoomer for the + * board view or the zoomer for the proof tree view) * @return A JPanel containing the zoomer */ private JPanel setUpZoomer(DynamicViewType type) { if (type == DynamicViewType.BOARD) { return setUpBoardZoomer(); - } - else { + } else { if (type == DynamicViewType.PROOF_TREE) { return setUpProofTreeZoomer(); } @@ -79,17 +76,20 @@ private JPanel setUpBoardZoomer() { */ private JPanel setUpProofTreeZoomer() { final String label = "Resize Proof"; - ActionListener listener = (ActionListener) -> GameBoardFacade.getInstance().getLegupUI().getProofEditor().fitTreeViewToScreen(); + ActionListener listener = + (ActionListener) -> + GameBoardFacade.getInstance() + .getLegupUI() + .getProofEditor() + .fitTreeViewToScreen(); return this.setUpZoomerHelper(label, listener); } /** * Creates the zoomer * - * @param label A string containing the label to be displayed - * on the fit to screen button - * @param listener A listener that determines what the resize - * button will do + * @param label A string containing the label to be displayed on the fit to screen button + * @param listener A listener that determines what the resize button will do * @return A JPanel containing the zoomer */ private JPanel setUpZoomerHelper(final String label, ActionListener listener) { @@ -111,39 +111,51 @@ private JPanel setUpZoomerHelper(final String label, ActionListener listener) { JSlider zoomSlider = new JSlider(25, 400, 100); - JButton plus = new JButton(new ImageIcon(ImageIO.read( - Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource( - "edu/rpi/legup/imgs/add.png"))))); + JButton plus = + new JButton( + new ImageIcon( + ImageIO.read( + Objects.requireNonNull( + ClassLoader.getSystemClassLoader() + .getResource( + "edu/rpi/legup/imgs/add.png"))))); plus.setFocusPainted(false); plus.setFont(MaterialFonts.getRegularFont(10f)); plus.setPreferredSize(new Dimension(20, 20)); - plus.addActionListener((ActionEvent e) -> zoomSlider.setValue(zoomSlider.getValue() + 25)); - - - JButton minus = new JButton(new ImageIcon(ImageIO.read( - Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource( - "edu/rpi/legup/imgs/remove.png"))))); + plus.addActionListener( + (ActionEvent e) -> zoomSlider.setValue(zoomSlider.getValue() + 25)); + + JButton minus = + new JButton( + new ImageIcon( + ImageIO.read( + Objects.requireNonNull( + ClassLoader.getSystemClassLoader() + .getResource( + "edu/rpi/legup/imgs/remove.png"))))); minus.setFocusPainted(false); minus.setPreferredSize(new Dimension(20, 20)); minus.setFont(MaterialFonts.getRegularFont(10f)); - minus.addActionListener((ActionEvent e) -> zoomSlider.setValue(zoomSlider.getValue() - 25)); + minus.addActionListener( + (ActionEvent e) -> zoomSlider.setValue(zoomSlider.getValue() - 25)); this.scrollView.setWheelScrollingEnabled(true); zoomSlider.setPreferredSize(new Dimension(160, 30)); - scrollView.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - zoomSlider.setValue(scrollView.getZoom()); - zoomLabel.setText(zoomSlider.getValue() + "%"); - } - }); - - zoomSlider.addChangeListener((ChangeEvent e) -> { - scrollView.zoomTo(zoomSlider.getValue() / 100.0); - zoomLabel.setText(zoomSlider.getValue() + "%"); - }); - + scrollView.addComponentListener( + new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + zoomSlider.setValue(scrollView.getZoom()); + zoomLabel.setText(zoomSlider.getValue() + "%"); + } + }); + + zoomSlider.addChangeListener( + (ChangeEvent e) -> { + scrollView.zoomTo(zoomSlider.getValue() / 100.0); + zoomLabel.setText(zoomSlider.getValue() + "%"); + }); zoomSlider.setMajorTickSpacing(100); zoomSlider.setMinorTickSpacing(25); @@ -167,8 +179,7 @@ public void componentResized(ComponentEvent e) { zoomWrapper.setLayout(new BorderLayout()); zoomWrapper.add(status, WEST); zoomWrapper.add(zoomer, EAST); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } return zoomWrapper; diff --git a/src/main/java/edu/rpi/legup/ui/HomePanel.java b/src/main/java/edu/rpi/legup/ui/HomePanel.java index 2270c92b8..11f51eb0e 100644 --- a/src/main/java/edu/rpi/legup/ui/HomePanel.java +++ b/src/main/java/edu/rpi/legup/ui/HomePanel.java @@ -3,35 +3,30 @@ 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.model.Puzzle; -import edu.rpi.legup.model.PuzzleExporter; -import edu.rpi.legup.save.ExportFileException; - -import javax.swing.*; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.parsers.ParserConfigurationException; +import edu.rpi.legup.save.InvalidFileFormatException; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; +import java.io.FileWriter; +import java.net.URI; +import java.net.URL; import java.util.Objects; - +import javax.swing.*; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -import java.io.FileWriter; -import java.net.URI; -import java.net.URL; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - public class HomePanel extends LegupPanel { - private final static Logger LOGGER = LogManager.getLogger(HomePanel.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(HomePanel.class.getName()); private LegupUI legupUI; private JFrame frame; private JButton[] buttons; @@ -41,33 +36,35 @@ public class HomePanel extends LegupPanel { private final int buttonSize = 100; - private ActionListener openProofListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Object[] items = legupUI.getProofEditor().promptPuzzle(); - if (items == null) { - // The attempt to prompt a puzzle ended gracefully (cancel) - return; - } - String fileName = (String) items[0]; - File puzzleFile = (File) items[1]; - legupUI.getProofEditor().loadPuzzle(fileName, puzzleFile); - } - }; - - private ActionListener openPuzzleListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Object[] items = legupUI.getPuzzleEditor().promptPuzzle(); - if (items == null) { - // The attempt to prompt a puzzle ended gracefully (cancel) - return; - } - String fileName = (String) items[0]; - File puzzleFile = (File) items[1]; - legupUI.getPuzzleEditor().loadPuzzle(fileName, puzzleFile); - } - }; + private ActionListener openProofListener = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Object[] items = legupUI.getProofEditor().promptPuzzle(); + if (items == null) { + // The attempt to prompt a puzzle ended gracefully (cancel) + return; + } + String fileName = (String) items[0]; + File puzzleFile = (File) items[1]; + legupUI.getProofEditor().loadPuzzle(fileName, puzzleFile); + } + }; + + private ActionListener openPuzzleListener = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Object[] items = legupUI.getPuzzleEditor().promptPuzzle(); + if (items == null) { + // The attempt to prompt a puzzle ended gracefully (cancel) + return; + } + String fileName = (String) items[0]; + File puzzleFile = (File) items[1]; + legupUI.getPuzzleEditor().loadPuzzle(fileName, puzzleFile); + } + }; public HomePanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.legupUI = legupUI; @@ -82,22 +79,24 @@ public JMenuBar getMenuBar() { JMenu settings = new JMenu("Settings"); menuBar.add(settings); JMenuItem preferences = new JMenuItem("Preferences"); - preferences.addActionListener(a -> { - PreferencesDialog preferencesDialog = new PreferencesDialog(this.frame); - System.out.println("Preferences clicked"); - }); + preferences.addActionListener( + a -> { + PreferencesDialog preferencesDialog = new PreferencesDialog(this.frame); + System.out.println("Preferences clicked"); + }); settings.addSeparator(); settings.add(preferences); JMenuItem contribute = new JMenuItem("Contribute to Legup"); - contribute.addActionListener(l -> { - try { - java.awt.Desktop.getDesktop().browse(URI.create("https://github.com/Bram-Hub/Legup")); - } - catch (IOException e) { - LOGGER.error("Can't open web page"); - } - }); + contribute.addActionListener( + l -> { + try { + java.awt.Desktop.getDesktop() + .browse(URI.create("https://github.com/Bram-Hub/Legup")); + } catch (IOException e) { + LOGGER.error("Can't open web page"); + } + }); settings.add(contribute); return this.menuBar; @@ -118,14 +117,17 @@ private static ImageIcon resizeButtonIcon(ImageIcon icon, int width, int height) private void initButtons() { this.buttons = new JButton[4]; - this.buttons[0] = new JButton("Solve Puzzle") { - { - setSize(buttonSize, buttonSize); - setMaximumSize(getSize()); - } - }; + this.buttons[0] = + new JButton("Solve Puzzle") { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; - URL button0IconLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/homepanel/proof_file.png"); + URL button0IconLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/homepanel/proof_file.png"); ImageIcon button0Icon = new ImageIcon(button0IconLocation); this.buttons[0].setFocusPainted(false); this.buttons[0].setIcon(resizeButtonIcon(button0Icon, this.buttonSize, this.buttonSize)); @@ -133,13 +135,16 @@ private void initButtons() { this.buttons[0].setVerticalTextPosition(AbstractButton.BOTTOM); this.buttons[0].addActionListener(CursorController.createListener(this, openProofListener)); - this.buttons[1] = new JButton("Create Puzzle") { - { - setSize(buttonSize, buttonSize); - setMaximumSize(getSize()); - } - }; - URL button1IconLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/homepanel/new_puzzle_file.png"); + this.buttons[1] = + new JButton("Create Puzzle") { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; + URL button1IconLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/homepanel/new_puzzle_file.png"); ImageIcon button1Icon = new ImageIcon(button1IconLocation); this.buttons[1].setFocusPainted(false); this.buttons[1].setIcon(resizeButtonIcon(button1Icon, this.buttonSize, this.buttonSize)); @@ -147,21 +152,25 @@ private void initButtons() { this.buttons[1].setVerticalTextPosition(AbstractButton.BOTTOM); this.buttons[1].addActionListener(l -> this.openNewPuzzleDialog()); - this.buttons[2] = new JButton("Edit Puzzle") { - { - setSize(buttonSize, buttonSize); - setMaximumSize(getSize()); - } - }; - URL button2IconLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/homepanel/puzzle_file.png"); + this.buttons[2] = + new JButton("Edit Puzzle") { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; + URL button2IconLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/homepanel/puzzle_file.png"); ImageIcon button2Icon = new ImageIcon(button2IconLocation); this.buttons[2].setFocusPainted(false); this.buttons[2].setIcon(resizeButtonIcon(button2Icon, this.buttonSize, this.buttonSize)); this.buttons[2].setHorizontalTextPosition(AbstractButton.CENTER); this.buttons[2].setVerticalTextPosition(AbstractButton.BOTTOM); - this.buttons[2].addActionListener(CursorController.createListener(this, openPuzzleListener)); // PLACEHOLDER + this.buttons[2].addActionListener( + CursorController.createListener(this, openPuzzleListener)); // PLACEHOLDER - for (int i = 0; i < this.buttons.length-1; i++) { // -1 to avoid the batch grader button + for (int i = 0; i < this.buttons.length - 1; i++) { // -1 to avoid the batch grader button this.buttons[i].setBounds(200, 200, 700, 700); } this.buttons[3] = new JButton("Batch Grader"); @@ -169,19 +178,18 @@ private void initButtons() { this.buttons[3].setHorizontalTextPosition(AbstractButton.CENTER); this.buttons[3].setVerticalTextPosition(AbstractButton.BOTTOM); - this.buttons[3].addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - try { - use_xml_to_check(); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - System.out.println("finished checking the folder"); - - } - }); + this.buttons[3].addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + use_xml_to_check(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + System.out.println("finished checking the folder"); + } + }); } public void checkFolder() { @@ -230,7 +238,8 @@ public void checkFolder() { } writer.append(fileEntry.getName()); writer.append(","); - String fileName = folderEntry.getAbsolutePath() + File.separator + fileEntry.getName(); + String fileName = + folderEntry.getAbsolutePath() + File.separator + fileEntry.getName(); System.out.println("This is path " + fileName); File puzzleFile = new File(fileName); if (puzzleFile != null && puzzleFile.exists()) { @@ -238,21 +247,20 @@ public void checkFolder() { legupUI.displayPanel(1); legupUI.getProofEditor(); GameBoardFacade.getInstance().loadPuzzle(fileName); - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); + String puzzleName = + GameBoardFacade.getInstance().getPuzzleModule().getName(); legupUI.setTitle(puzzleName + " - " + puzzleFile.getName()); facade = GameBoardFacade.getInstance(); Puzzle puzzle = facade.getPuzzleModule(); if (puzzle.isPuzzleComplete()) { writer.append("Solved"); System.out.println(fileEntry.getName() + " solved"); - } - else { + } else { writer.append("Not Solved"); System.out.println(fileEntry.getName() + " not solved"); } writer.append("\n"); - } - catch (InvalidFileFormatException e) { + } catch (InvalidFileFormatException e) { LOGGER.error(e.getMessage()); } } @@ -262,15 +270,15 @@ public void checkFolder() { writer.append("\n"); } } - } - catch (IOException ex) { + } catch (IOException ex) { LOGGER.error(ex.getMessage()); this.buttons[3].addActionListener((ActionEvent e) -> use_xml_to_check()); } } /** - * @effect batch grade using .xml parser - go through a collection of files and report their "solved?" status + * @effect batch grade using .xml parser - go through a collection of files and report their + * "solved?" status */ private void use_xml_to_check() { /* Select a folder, go through each .xml file in the subfolders, look for "isSolved" flag */ @@ -288,26 +296,24 @@ private void use_xml_to_check() { try (BufferedWriter writer = new BufferedWriter(new FileWriter(resultFile))) { writer.append("Name,File Name,Puzzle Type,Solved?,Last Saved\n"); // Go through student folders, recurse for inner folders - for (final File folderEntry : Objects.requireNonNull(folder.listFiles(File::isDirectory))) { + for (final File folderEntry : + Objects.requireNonNull(folder.listFiles(File::isDirectory))) { String path = folderEntry.getName(); // use this helper function to write to the .csv file recursive_parser(folderEntry, writer, path, path); } - } - catch (IOException ex) { + } catch (IOException ex) { LOGGER.error(ex.getMessage()); } if (resultFile.exists()) { try { Desktop desktop = Desktop.getDesktop(); desktop.open(resultFile); - } - catch (IOException ex) { + } catch (IOException ex) { LOGGER.error(ex.getMessage()); } } JOptionPane.showMessageDialog(null, "Batch grading complete."); - } /** @@ -321,8 +327,7 @@ public boolean isxmlfile(File file) { DocumentBuilder builder = factory.newDocumentBuilder(); builder.parse(file); flag = true; - } - catch (Exception e) { + } catch (Exception e) { flag = false; } return flag; @@ -335,7 +340,8 @@ public boolean isxmlfile(File file) { * @param name - student's name (the first subfolders of the main folder) * @throws IOException */ - private void recursive_parser(File folder, BufferedWriter writer, String path, String name) throws IOException { + private void recursive_parser(File folder, BufferedWriter writer, String path, String name) + throws IOException { // Empty folder if (Objects.requireNonNull(folder.listFiles()).length == 0) { writer.append(path).append(",Empty folder,Ungradeable\n"); @@ -368,94 +374,97 @@ private void recursive_parser(File folder, BufferedWriter writer, String path, S path = folder.getAbsolutePath() + File.separator + fileEntry.getName(); System.out.println(path); if (isxmlfile(fileEntry)) { - saxParser.parse(path, new DefaultHandler() { - @Override - public void startDocument() throws SAXException { - } - boolean solvedFlagExists = false; - boolean puzzleTypeExists = false; - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - // append file type to the writer - if (qName.equals("puzzle") && attributes.getQName(0) == "name" && !puzzleTypeExists) { - try { - writer.write(attributes.getValue(0)); - writer.write(","); - puzzleTypeExists = true; - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - // append the "solved?" status of the proof to the writer - else if (qName.equals("solved") && !solvedFlagExists) { - String isSolved = attributes.getValue(0); - String lastSaved = attributes.getValue(1); - if (isSolved != null) { - if (isSolved.equals("true")) { + saxParser.parse( + path, + new DefaultHandler() { + @Override + public void startDocument() throws SAXException {} + + boolean solvedFlagExists = false; + boolean puzzleTypeExists = false; + + @Override + public void startElement( + String uri, + String localName, + String qName, + Attributes attributes) + throws SAXException { + // append file type to the writer + if (qName.equals("puzzle") + && attributes.getQName(0) == "name" + && !puzzleTypeExists) { try { - writer.write("Solved"); - } - catch (IOException e) { + writer.write(attributes.getValue(0)); + writer.write(","); + puzzleTypeExists = true; + } catch (IOException e) { throw new RuntimeException(e); } } - else if (isSolved.equals("false")) { - try { - writer.write("Not Solved"); + // append the "solved?" status of the proof to the writer + else if (qName.equals("solved") && !solvedFlagExists) { + String isSolved = attributes.getValue(0); + String lastSaved = attributes.getValue(1); + if (isSolved != null) { + if (isSolved.equals("true")) { + try { + writer.write("Solved"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else if (isSolved.equals("false")) { + try { + writer.write("Not Solved"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + try { + writer.write("Error"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } - catch (IOException e) { - throw new RuntimeException(e); + // append when is this proof last saved + if (lastSaved != null) { + try { + writer.write(","); + writer.write(lastSaved); + } catch (IOException e) { + throw new RuntimeException(e); + } } + solvedFlagExists = true; } - else { - try { - writer.write("Error"); + } + + @Override + public void characters(char[] ch, int start, int length) + throws SAXException {} + + @Override + public void endElement(String uri, String localName, String qName) + throws SAXException {} + + @Override + public void endDocument() throws SAXException { + if (!puzzleTypeExists) { + try { + writer.write("not a LEGUP puzzle!"); + } catch (IOException e) { + throw new RuntimeException(e); } - catch (IOException e) { + } else if (!solvedFlagExists) { + try { + writer.write("missing flag!"); + } catch (IOException e) { throw new RuntimeException(e); } } } - // append when is this proof last saved - if (lastSaved != null) { - try { - writer.write(","); - writer.write(lastSaved); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - solvedFlagExists = true; - } - } - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - } - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - } - @Override - public void endDocument() throws SAXException { - if (!puzzleTypeExists) { - try { - writer.write("not a LEGUP puzzle!"); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - else if (!solvedFlagExists) { - try { - writer.write("missing flag!"); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - } - }); + }); } // If wrong file type, ungradeable else { @@ -463,14 +472,14 @@ else if (!solvedFlagExists) { } writer.write("\n"); } - } - catch (ParserConfigurationException | SAXException | IOException e) { + } catch (ParserConfigurationException | SAXException | IOException e) { LOGGER.error(e.getMessage()); } } private void initText() { - // TODO: add version text after auto-changing version label is implemented. (text[2] = version) + // TODO: add version text after auto-changing version label is implemented. (text[2] = + // version) this.text = new JLabel[2]; JLabel welcome = new JLabel("Welcome to LEGUP"); @@ -508,7 +517,6 @@ private void render() { batchGraderButton.add(this.buttons[3]); batchGraderButton.setAlignmentX(Component.CENTER_ALIGNMENT); - this.add(Box.createRigidArea(new Dimension(0, 5))); for (int i = 0; i < this.text.length; i++) { this.add(this.text[i]); @@ -535,7 +543,8 @@ private void checkProofAll() { */ LegupPreferences preferences = LegupPreferences.getInstance(); - File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); + File preferredDirectory = + new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); folderBrowser = new JFileChooser(preferredDirectory); folderBrowser.showOpenDialog(this); @@ -553,13 +562,13 @@ private void checkProofAll() { writer.append("Name,File Name,Puzzle Type,Score,Solved?\n"); // Go through student folders - for (final File folderEntry : Objects.requireNonNull(folder.listFiles(File::isDirectory))) { + for (final File folderEntry : + Objects.requireNonNull(folder.listFiles(File::isDirectory))) { // Write path String path = folderEntry.getName(); traverseDir(folderEntry, writer, path); } - } - catch (IOException ex) { + } catch (IOException ex) { LOGGER.error(ex.getMessage()); } JOptionPane.showMessageDialog(null, "Batch grading complete."); @@ -605,29 +614,28 @@ private void traverseDir(File folder, BufferedWriter writer, String path) throws writer.append(puzzle.getName()).append(","); if (puzzle.isPuzzleComplete()) { writer.append("Solved\n"); - } - else { + } else { writer.append("Unsolved\n"); } - } - catch (InvalidFileFormatException e) { + } catch (InvalidFileFormatException e) { writer.append(fName).append("InvalidFile,Ungradeable\n"); } - } - else { + } else { LOGGER.debug("Failed to run sim"); } } } - public void openEditorWithNewPuzzle(String game, int rows, int columns) throws IllegalArgumentException { + public void openEditorWithNewPuzzle(String game, int rows, int columns) + throws IllegalArgumentException { // Validate the dimensions GameBoardFacade facade = GameBoardFacade.getInstance(); boolean isValidDimensions = facade.validateDimensions(game, rows, columns); if (!isValidDimensions) { - JOptionPane.showMessageDialog(null, - "The dimensions you entered are invalid. Please double check \n" + - "the number of rows and columns and try again.", + JOptionPane.showMessageDialog( + null, + "The dimensions you entered are invalid. Please double check \n" + + "the number of rows and columns and try again.", "ERROR: Invalid Dimensions", JOptionPane.ERROR_MESSAGE); throw new IllegalArgumentException("ERROR: Invalid dimensions given"); @@ -641,17 +649,18 @@ public void openEditorWithNewPuzzle(String game, int rows, int columns) throws I /** * 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 + * @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.", + 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"); diff --git a/src/main/java/edu/rpi/legup/ui/LegupPanel.java b/src/main/java/edu/rpi/legup/ui/LegupPanel.java index 708348145..d16167b3d 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupPanel.java +++ b/src/main/java/edu/rpi/legup/ui/LegupPanel.java @@ -3,10 +3,7 @@ import javax.swing.*; public abstract class LegupPanel extends JPanel { - /** - * Alerts panel that it will be going visible now - */ - + /** Alerts panel that it will be going visible now */ protected final int TOOLBAR_ICON_SCALE = 40; public abstract void makeVisible(); diff --git a/src/main/java/edu/rpi/legup/ui/LegupUI.java b/src/main/java/edu/rpi/legup/ui/LegupUI.java index 452bbb487..eb8b1663c 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupUI.java +++ b/src/main/java/edu/rpi/legup/ui/LegupUI.java @@ -1,25 +1,21 @@ package edu.rpi.legup.ui; -import java.awt.*; -import java.awt.event.*; -import java.security.InvalidParameterException; -import java.util.Objects; - -import javax.swing.*; - - -import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLightLaf; import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; - +import java.awt.*; +import java.awt.event.*; +import java.security.InvalidParameterException; +import java.util.Objects; +import javax.swing.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class LegupUI extends JFrame implements WindowListener { - private final static Logger LOGGER = LogManager.getLogger(LegupUI.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(LegupUI.class.getName()); protected FileDialog fileDialog; protected JPanel window; @@ -27,22 +23,20 @@ public class LegupUI extends JFrame implements WindowListener { /** * Identifies operating system + * * @return operating system, either mac or win */ public static String getOS() { String os = System.getProperty("os.name").toLowerCase(); if (os.contains("mac")) { os = "mac"; - } - else { + } else { os = "win"; } return os; } - /** - * LegupUI Constructor - creates a new LegupUI to setup the menu and toolbar - */ + /** LegupUI Constructor - creates a new LegupUI to setup the menu and toolbar */ public LegupUI() { setTitle("LEGUP"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -51,12 +45,10 @@ public LegupUI() { try { if (Boolean.valueOf(prefs.getUserPref(LegupPreferences.DARK_MODE))) { UIManager.setLookAndFeel(new FlatDarkLaf()); - } - else { + } else { UIManager.setLookAndFeel(new FlatLightLaf()); } - } - catch (UnsupportedLookAndFeelException e) { + } catch (UnsupportedLookAndFeelException e) { System.err.println("Not supported ui look and feel"); } @@ -65,27 +57,36 @@ public LegupUI() { initPanels(); displayPanel(0); - setIconImage(new ImageIcon(Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource( - "edu/rpi/legup/images/Legup/Direct Rules.gif"))).getImage()); - - if (LegupPreferences.getInstance().getUserPref(LegupPreferences.START_FULL_SCREEN).equals(Boolean.toString(true))) { + setIconImage( + new ImageIcon( + Objects.requireNonNull( + ClassLoader.getSystemClassLoader() + .getResource( + "edu/rpi/legup/images/Legup/Direct" + + " Rules.gif"))) + .getImage()); + + if (LegupPreferences.getInstance() + .getUserPref(LegupPreferences.START_FULL_SCREEN) + .equals(Boolean.toString(true))) { setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH); } this.addWindowListener(this); - addKeyListener(new KeyAdapter() { - /** - * Invoked when a key has been typed. - * This event occurs when a key press is followed by a key release. - * - * @param e - */ - @Override - public void keyTyped(KeyEvent e) { - System.err.println(e.getKeyChar()); - super.keyTyped(e); - } - }); + addKeyListener( + new KeyAdapter() { + /** + * Invoked when a key has been typed. This event occurs when a key press is + * followed by a key release. + * + * @param e + */ + @Override + public void keyTyped(KeyEvent e) { + System.err.println(e.getKeyChar()); + super.keyTyped(e); + } + }); setMinimumSize(getPreferredSize()); setVisible(true); } @@ -99,7 +100,6 @@ private void initPanels() { panels[0] = new HomePanel(this.fileDialog, this, this); panels[1] = new ProofEditorPanel(this.fileDialog, this, this); panels[2] = new PuzzleEditorPanel(this.fileDialog, this, this); - } protected void displayPanel(int option) { @@ -128,7 +128,14 @@ public void repaintTree() { } private void directions() { - JOptionPane.showMessageDialog(null, "For every move you make, you must provide a rules for it (located in the Rules panel).\n" + "While working on the edu.rpi.legup.puzzle, you may click on the \"Check\" button to test your proof for correctness.", "Directions", JOptionPane.PLAIN_MESSAGE); + JOptionPane.showMessageDialog( + null, + "For every move you make, you must provide a rules for it (located in the Rules" + + " panel).\n" + + "While working on the edu.rpi.legup.puzzle, you may click on the \"Check\"" + + " button to test your proof for correctness.", + "Directions", + JOptionPane.PLAIN_MESSAGE); } public void showStatus(String status, boolean error) { @@ -143,27 +150,23 @@ public void showStatus(String status, boolean error, int timer) { // TODO: implement } - //ask to edu.rpi.legup.save current proof + // ask to edu.rpi.legup.save current proof public boolean noquit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } @Override - public void windowOpened(WindowEvent e) { - - } + public void windowOpened(WindowEvent e) {} public void windowClosing(WindowEvent e) { if (GameBoardFacade.getInstance().getHistory().getIndex() > -1) { if (noquit("Exiting LEGUP?")) { this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - } - else { + } else { this.setDefaultCloseOperation(EXIT_ON_CLOSE); } - } - else { + } else { this.setDefaultCloseOperation(EXIT_ON_CLOSE); } } @@ -172,21 +175,13 @@ public void windowClosed(WindowEvent e) { System.exit(0); } - public void windowIconified(WindowEvent e) { - - } + public void windowIconified(WindowEvent e) {} - public void windowDeiconified(WindowEvent e) { - - } + public void windowDeiconified(WindowEvent e) {} - public void windowActivated(WindowEvent e) { + public void windowActivated(WindowEvent e) {} - } - - public void windowDeactivated(WindowEvent e) { - - } + public void windowDeactivated(WindowEvent e) {} public BoardView getBoardView() { return getProofEditor().getBoardView(); diff --git a/src/main/java/edu/rpi/legup/ui/ManualPuzzleCreatorDialog.java b/src/main/java/edu/rpi/legup/ui/ManualPuzzleCreatorDialog.java index 6e3fb4f67..01d696f19 100644 --- a/src/main/java/edu/rpi/legup/ui/ManualPuzzleCreatorDialog.java +++ b/src/main/java/edu/rpi/legup/ui/ManualPuzzleCreatorDialog.java @@ -1,13 +1,9 @@ package edu.rpi.legup.ui; -import javax.swing.*; import java.awt.*; +import javax.swing.*; public class ManualPuzzleCreatorDialog extends JDialog { - - public ManualPuzzleCreatorDialog() { - - } - + public ManualPuzzleCreatorDialog() {} } diff --git a/src/main/java/edu/rpi/legup/ui/PickGameDialog.java b/src/main/java/edu/rpi/legup/ui/PickGameDialog.java index 24fc4602f..f703ffcbc 100644 --- a/src/main/java/edu/rpi/legup/ui/PickGameDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PickGameDialog.java @@ -1,15 +1,11 @@ package edu.rpi.legup.ui; - import edu.rpi.legup.app.GameBoardFacade; - import java.awt.Container; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; - import java.io.File; - import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; @@ -36,7 +32,6 @@ public class PickGameDialog extends JDialog implements ActionListener { JButton ok = new JButton("Ok"); JButton cancel = new JButton("Cancel"); - JCheckBox autotreeCheckBox = new JCheckBox("Auto-tree"); JCheckBox showtreeCheckBox = new JCheckBox("Show tree"); JCheckBox autojustifyCheckBox = new JCheckBox("Auto-justify"); @@ -47,9 +42,9 @@ public class PickGameDialog extends JDialog implements ActionListener { /** * Initialize the dialog * - * @param parent the parent JFrame - * @param pickBothAtOnce if true they can pick a game type and a specific edu.rpi.legup.puzzle, if - * false they can only pick a game type + * @param parent the parent JFrame + * @param pickBothAtOnce if true they can pick a game type and a specific edu.rpi.legup.puzzle, + * if false they can only pick a game type */ public PickGameDialog(JFrame parent, boolean pickBothAtOnce) { super(parent, true); @@ -75,15 +70,13 @@ public PickGameDialog(JFrame parent, boolean pickBothAtOnce) { if (pickBoth) { gameLabel.setBounds(10, 10, 70, 25); gameBox.setBounds(80, 10, 190, 25); - } - else { + } else { gameLabel.setBounds(10, 30, 70, 25); gameBox.setBounds(80, 30, 190, 25); } puzzleLabel.setBounds(10, 40, 70, 25); - puzzleBox.setBounds(80, 40, 190, 25); puzzleButton.setBounds(270, 40, 25, 25); @@ -93,7 +86,6 @@ public PickGameDialog(JFrame parent, boolean pickBothAtOnce) { c.add(gameLabel); c.add(gameBox); - if (pickBoth) { c.add(puzzleLabel); c.add(puzzleBox); @@ -147,24 +139,25 @@ public String getGame() { public void actionPerformed(ActionEvent e) { if (e.getSource() == gameBox) { int index = gameBox.getSelectedIndex(); - } - else { + } else { if (e.getSource() == ok) { okPressed = true; setVisible(false); - } - else { + } else { if (e.getSource() == cancel) { okPressed = false; setVisible(false); - } - else { + } else { if (e.getSource() == puzzleButton) { - File f = new File("puzzlefiles" + File.separator + gameBox.getSelectedItem().toString().toLowerCase() + File.separator); + File f = + new File( + "puzzlefiles" + + File.separator + + gameBox.getSelectedItem().toString().toLowerCase() + + File.separator); if (f.exists() && f.isDirectory()) { puzzleChooser = new JFileChooser(f); - } - else { + } else { puzzleChooser = new JFileChooser(); } if (puzzleChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { diff --git a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java index 4eee69d4d..475f4bb68 100644 --- a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java @@ -1,33 +1,37 @@ package edu.rpi.legup.ui; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLightLaf; import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialBorders; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialFonts; - -import javax.imageio.ImageIO; -import javax.swing.*; +import edu.rpi.legup.ui.proofeditorui.rulesview.RuleFrame; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; - -import com.formdev.flatlaf.FlatLightLaf; -import com.formdev.flatlaf.FlatDarkLaf; -import edu.rpi.legup.ui.proofeditorui.rulesview.RuleFrame; -import edu.rpi.legup.ui.proofeditorui.rulesview.RulePanel; +import javax.imageio.ImageIO; +import javax.swing.*; public class PreferencesDialog extends JDialog { - private RuleFrame rulesFrame; - private final static Logger LOGGER = Logger.getLogger(PreferencesDialog.class.getName()); + private static final Logger LOGGER = Logger.getLogger(PreferencesDialog.class.getName()); - private JCheckBox fullScreen, autoUpdate, darkMode, showMistakes, showAnnotations, allowDefault, generateCases, immFeedback, colorBlind; + private JCheckBox fullScreen, + autoUpdate, + darkMode, + showMistakes, + showAnnotations, + allowDefault, + generateCases, + immFeedback, + colorBlind; private JTextField workDirectory; @@ -35,9 +39,10 @@ public class PreferencesDialog extends JDialog { static { try { - folderIcon = ImageIO.read(PreferencesDialog.class.getResource("/edu/rpi/legup/imgs/folder.png")); - } - catch (IOException e) { + folderIcon = + ImageIO.read( + PreferencesDialog.class.getResource("/edu/rpi/legup/imgs/folder.png")); + } catch (IOException e) { LOGGER.log(Level.SEVERE, "Unable to locate icons"); } } @@ -66,22 +71,25 @@ public PreferencesDialog(Frame frame) { JToolBar toolbar = new JToolBar(); toolbar.setBorder(null); JButton okButton = new JButton("Ok"); - okButton.addActionListener(l -> { - applyPreferences(); - this.setVisible(false); - this.dispose(); - }); + okButton.addActionListener( + l -> { + applyPreferences(); + this.setVisible(false); + this.dispose(); + }); toolbar.add(okButton); JButton cancelButton = new JButton("Cancel"); - cancelButton.addActionListener(l -> { - this.setVisible(false); - this.dispose(); - }); + cancelButton.addActionListener( + l -> { + this.setVisible(false); + this.dispose(); + }); toolbar.add(cancelButton); JButton applyButton = new JButton("Apply"); - applyButton.addActionListener(l -> { - applyPreferences(); - }); + applyButton.addActionListener( + l -> { + applyPreferences(); + }); toolbar.add(applyButton); bottomPanel.add(toolbar, BorderLayout.EAST); @@ -98,13 +106,11 @@ private void toggleDarkMode(LegupPreferences prefs) { try { if (Boolean.valueOf(prefs.getUserPref(LegupPreferences.DARK_MODE))) { UIManager.setLookAndFeel(new FlatDarkLaf()); - } - else { + } else { UIManager.setLookAndFeel(new FlatLightLaf()); } com.formdev.flatlaf.FlatLaf.updateUI(); - } - catch (UnsupportedLookAndFeelException e) { + } catch (UnsupportedLookAndFeelException e) { System.err.println("Not supported ui look and feel"); } } @@ -126,115 +132,162 @@ private JScrollPane createGeneralTab() { workDirectory = new JTextField(prefs.getUserPref(LegupPreferences.WORK_DIRECTORY)); workRow.add(workDirectory, BorderLayout.CENTER); JButton openDir = new JButton(new ImageIcon(folderIcon)); - openDir.addActionListener(a -> { - JFileChooser chooser = new JFileChooser(); - chooser.setCurrentDirectory(new File(workDirectory.getText())); - chooser.setDialogTitle("Choose work directory"); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setAcceptAllFileFilterUsed(false); - chooser.setVisible(true); - - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - File newFile = chooser.getSelectedFile(); - workDirectory.setText(newFile.toString()); - } - }); + openDir.addActionListener( + a -> { + JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(workDirectory.getText())); + chooser.setDialogTitle("Choose work directory"); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + chooser.setVisible(true); + + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + File newFile = chooser.getSelectedFile(); + workDirectory.setText(newFile.toString()); + } + }); workRow.add(openDir, BorderLayout.EAST); workRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, workRow.getPreferredSize().height)); contentPane.add(workRow); - fullScreen = new JCheckBox("Full Screen", Boolean.valueOf(prefs.getUserPref(LegupPreferences.START_FULL_SCREEN))); + fullScreen = + new JCheckBox( + "Full Screen", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.START_FULL_SCREEN))); fullScreen.setToolTipText("If checked this starts Legup in full screen."); JPanel fullScreenRow = new JPanel(); fullScreenRow.setLayout(new BorderLayout()); fullScreenRow.add(fullScreen, BorderLayout.WEST); - fullScreenRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, fullScreenRow.getPreferredSize().height)); + fullScreenRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, fullScreenRow.getPreferredSize().height)); contentPane.add(fullScreenRow); - autoUpdate = new JCheckBox("Automatically Check for Updates", Boolean.valueOf(prefs.getUserPref(LegupPreferences.AUTO_UPDATE))); - autoUpdate.setToolTipText("If checked this automatically checks for updates on startup of Legup"); + autoUpdate = + new JCheckBox( + "Automatically Check for Updates", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.AUTO_UPDATE))); + autoUpdate.setToolTipText( + "If checked this automatically checks for updates on startup of Legup"); JPanel autoUpdateRow = new JPanel(); autoUpdateRow.setLayout(new BorderLayout()); autoUpdateRow.add(autoUpdate, BorderLayout.WEST); - autoUpdateRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, autoUpdateRow.getPreferredSize().height)); + autoUpdateRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, autoUpdateRow.getPreferredSize().height)); contentPane.add(autoUpdateRow); -// contentPane.add(Box.createRigidArea(new Dimension(0, 10))); + // contentPane.add(Box.createRigidArea(new Dimension(0, 10))); - darkMode = new JCheckBox("Dark Mode", Boolean.valueOf(prefs.getUserPref(LegupPreferences.DARK_MODE))); + darkMode = + new JCheckBox( + "Dark Mode", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.DARK_MODE))); darkMode.setToolTipText("This turns dark mode on and off"); JPanel darkModeRow = new JPanel(); darkModeRow.setLayout(new BorderLayout()); darkModeRow.add(darkMode, BorderLayout.WEST); - darkModeRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, darkModeRow.getPreferredSize().height)); + darkModeRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, darkModeRow.getPreferredSize().height)); contentPane.add(darkModeRow); contentPane.add(Box.createRigidArea(new Dimension(0, 10))); contentPane.add(createLeftLabel("Board View Preferences")); contentPane.add(createLineSeparator()); - showMistakes = new JCheckBox("Show Mistakes", Boolean.valueOf(prefs.getUserPref(LegupPreferences.SHOW_MISTAKES))); - showMistakes.setToolTipText("If checked this show incorrectly applied rule applications in red on the board"); + showMistakes = + new JCheckBox( + "Show Mistakes", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.SHOW_MISTAKES))); + showMistakes.setToolTipText( + "If checked this show incorrectly applied rule applications in red on the board"); JPanel showMistakesRow = new JPanel(); showMistakesRow.setLayout(new BorderLayout()); showMistakesRow.add(showMistakes, BorderLayout.WEST); - showMistakesRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, showMistakesRow.getPreferredSize().height)); + showMistakesRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, showMistakesRow.getPreferredSize().height)); contentPane.add(showMistakesRow); - showAnnotations = new JCheckBox("Show Annotations", Boolean.valueOf(prefs.getUserPref(LegupPreferences.SHOW_ANNOTATIONS))); - showAnnotations.setToolTipText("If checked this show incorrectly applied rule applications in red on the board"); + showAnnotations = + new JCheckBox( + "Show Annotations", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.SHOW_ANNOTATIONS))); + showAnnotations.setToolTipText( + "If checked this show incorrectly applied rule applications in red on the board"); JPanel showAnnotationsRow = new JPanel(); showAnnotationsRow.setLayout(new BorderLayout()); showAnnotationsRow.add(showAnnotations, BorderLayout.WEST); - showAnnotationsRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, showAnnotationsRow.getPreferredSize().height)); + showAnnotationsRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, showAnnotationsRow.getPreferredSize().height)); contentPane.add(showAnnotationsRow); contentPane.add(Box.createRigidArea(new Dimension(0, 10))); contentPane.add(createLeftLabel("Tree View Preferences")); contentPane.add(createLineSeparator()); - allowDefault = new JCheckBox("Allow Default Rule Applications", Boolean.valueOf(prefs.getUserPref(LegupPreferences.ALLOW_DEFAULT_RULES))); + allowDefault = + new JCheckBox( + "Allow Default Rule Applications", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.ALLOW_DEFAULT_RULES))); allowDefault.setEnabled(false); - allowDefault.setToolTipText("If checked this automatically applies a rule where it can on the board"); + allowDefault.setToolTipText( + "If checked this automatically applies a rule where it can on the board"); JPanel allowDefaultRow = new JPanel(); allowDefaultRow.setLayout(new BorderLayout()); allowDefaultRow.add(allowDefault, BorderLayout.WEST); - allowDefaultRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, allowDefaultRow.getPreferredSize().height)); + allowDefaultRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, allowDefaultRow.getPreferredSize().height)); contentPane.add(allowDefaultRow); - generateCases = new JCheckBox("Automatically Generate Cases", Boolean.valueOf(prefs.getUserPref(LegupPreferences.AUTO_GENERATE_CASES))); - generateCases.setToolTipText("If checked this automatically generates all cases for a case rule"); + generateCases = + new JCheckBox( + "Automatically Generate Cases", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.AUTO_GENERATE_CASES))); + generateCases.setToolTipText( + "If checked this automatically generates all cases for a case rule"); JPanel generateCasesRow = new JPanel(); generateCasesRow.setLayout(new BorderLayout()); generateCasesRow.add(generateCases, BorderLayout.WEST); - generateCasesRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, generateCasesRow.getPreferredSize().height)); + generateCasesRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, generateCasesRow.getPreferredSize().height)); contentPane.add(generateCasesRow); contentPane.add(Box.createRigidArea(new Dimension(0, 10))); - immFeedback = new JCheckBox("Provide Immediate Feedback", Boolean.valueOf(prefs.getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK))); - immFeedback.setToolTipText("If checked this will update the colors of the tree view elements immediately"); + immFeedback = + new JCheckBox( + "Provide Immediate Feedback", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK))); + immFeedback.setToolTipText( + "If checked this will update the colors of the tree view elements immediately"); JPanel immFeedbackRow = new JPanel(); immFeedbackRow.setLayout(new BorderLayout()); immFeedbackRow.add(immFeedback, BorderLayout.WEST); - immFeedbackRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, immFeedbackRow.getPreferredSize().height)); + immFeedbackRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, immFeedbackRow.getPreferredSize().height)); contentPane.add(immFeedbackRow); contentPane.add(createLeftLabel("Instructor Preferences")); contentPane.add(createLineSeparator()); - immFeedback = new JCheckBox("Instructor Mode", Boolean.valueOf(prefs.getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK))); + immFeedback = + new JCheckBox( + "Instructor Mode", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK))); immFeedback.setToolTipText("Currently unimplemented, this does nothing right now"); immFeedbackRow.setLayout(new BorderLayout()); immFeedbackRow.add(immFeedback, BorderLayout.WEST); - immFeedbackRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, immFeedbackRow.getPreferredSize().height)); + immFeedbackRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, immFeedbackRow.getPreferredSize().height)); contentPane.add(immFeedbackRow); contentPane.add(createLeftLabel("Color Preferences")); contentPane.add(createLineSeparator()); - colorBlind = new JCheckBox("Deuteranomaly(red/green colorblindness)", Boolean.valueOf(prefs.getUserPref(LegupPreferences.COLOR_BLIND))); + colorBlind = + new JCheckBox( + "Deuteranomaly(red/green colorblindness)", + Boolean.valueOf(prefs.getUserPref(LegupPreferences.COLOR_BLIND))); JPanel colorBlindRow = new JPanel(); colorBlindRow.setLayout(new BorderLayout()); colorBlindRow.add(colorBlind, BorderLayout.WEST); - colorBlindRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, showMistakesRow.getPreferredSize().height)); + colorBlindRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, showMistakesRow.getPreferredSize().height)); contentPane.add(colorBlindRow); scrollPane.setViewportView(contentPane); @@ -293,38 +346,40 @@ private JPanel createRuleRow(Rule rule) { ruleAcc.setHorizontalAlignment(JLabel.CENTER); ruleAcc.setBorder(MaterialBorders.LIGHT_LINE_BORDER); ruleAcc.setPreferredSize(new Dimension(60, 20)); - ruleAcc.addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - ruleAcc.requestFocusInWindow(); - } - }); - - ruleAcc.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - int keyCode = e.getKeyCode(); - String combo = ""; - if (e.isControlDown()) { - combo += "Ctrl + "; - } - else { - if (e.isShiftDown()) { - combo += "Shift + "; + ruleAcc.addMouseListener( + new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + ruleAcc.requestFocusInWindow(); } - else { - if (e.isAltDown()) { - combo += "Alt + "; + }); + + ruleAcc.addKeyListener( + new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + String combo = ""; + if (e.isControlDown()) { + combo += "Ctrl + "; + } else { + if (e.isShiftDown()) { + combo += "Shift + "; + } else { + if (e.isAltDown()) { + combo += "Alt + "; + } + } } + if (keyCode == KeyEvent.VK_CONTROL + || keyCode == KeyEvent.VK_SHIFT + || keyCode == KeyEvent.VK_ALT) { + return; + } + combo += KeyEvent.getKeyText(keyCode); + ruleAcc.setText(combo); } - } - if (keyCode == KeyEvent.VK_CONTROL || keyCode == KeyEvent.VK_SHIFT || keyCode == KeyEvent.VK_ALT) { - return; - } - combo += KeyEvent.getKeyText(keyCode); - ruleAcc.setText(combo); - } - }); + }); ruleRow.add(ruleAcc, BorderLayout.EAST); @@ -340,7 +395,8 @@ private JPanel createLeftLabel(String text) { label.setHorizontalAlignment(JLabel.LEFT); labelRow.add(label, BorderLayout.WEST); - labelRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, labelRow.getPreferredSize().height)); + labelRow.setMaximumSize( + new Dimension(Integer.MAX_VALUE, labelRow.getPreferredSize().height)); return labelRow; } @@ -353,17 +409,23 @@ private JSeparator createLineSeparator() { public void applyPreferences() { LegupPreferences prefs = LegupPreferences.getInstance(); prefs.setUserPref(LegupPreferences.WORK_DIRECTORY, workDirectory.getText()); - prefs.setUserPref(LegupPreferences.START_FULL_SCREEN, Boolean.toString(fullScreen.isSelected())); + prefs.setUserPref( + LegupPreferences.START_FULL_SCREEN, Boolean.toString(fullScreen.isSelected())); prefs.setUserPref(LegupPreferences.AUTO_UPDATE, Boolean.toString(autoUpdate.isSelected())); prefs.setUserPref(LegupPreferences.DARK_MODE, Boolean.toString(darkMode.isSelected())); - prefs.setUserPref(LegupPreferences.SHOW_MISTAKES, Boolean.toString(showMistakes.isSelected())); - prefs.setUserPref(LegupPreferences.SHOW_ANNOTATIONS, Boolean.toString(showAnnotations.isSelected())); - prefs.setUserPref(LegupPreferences.ALLOW_DEFAULT_RULES, Boolean.toString(allowDefault.isSelected())); - prefs.setUserPref(LegupPreferences.AUTO_GENERATE_CASES, Boolean.toString(generateCases.isSelected())); - prefs.setUserPref(LegupPreferences.IMMEDIATE_FEEDBACK, Boolean.toString(immFeedback.isSelected())); + prefs.setUserPref( + LegupPreferences.SHOW_MISTAKES, Boolean.toString(showMistakes.isSelected())); + prefs.setUserPref( + LegupPreferences.SHOW_ANNOTATIONS, Boolean.toString(showAnnotations.isSelected())); + prefs.setUserPref( + LegupPreferences.ALLOW_DEFAULT_RULES, Boolean.toString(allowDefault.isSelected())); + prefs.setUserPref( + LegupPreferences.AUTO_GENERATE_CASES, Boolean.toString(generateCases.isSelected())); + prefs.setUserPref( + LegupPreferences.IMMEDIATE_FEEDBACK, Boolean.toString(immFeedback.isSelected())); prefs.setUserPref(LegupPreferences.COLOR_BLIND, Boolean.toString(colorBlind.isSelected())); - if(rulesFrame != null) { + if (rulesFrame != null) { rulesFrame.getCasePanel().updateRules(); rulesFrame.getDirectRulePanel().updateRules(); rulesFrame.getContradictionPanel().updateRules(); @@ -372,4 +434,4 @@ public void applyPreferences() { // toggle dark mode based on updated NIGHT_MODE variable toggleDarkMode(prefs); } -} \ 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 e83f660e7..956f83ba4 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -17,14 +17,8 @@ import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.proofeditorui.rulesview.RuleFrame; import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import edu.rpi.legup.user.Submission; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; -import javax.swing.border.TitledBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -37,9 +31,13 @@ import java.net.URL; import java.util.List; import java.util.Objects; +import javax.swing.*; +import javax.swing.border.TitledBorder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ProofEditorPanel extends LegupPanel implements IHistoryListener { - private final static Logger LOGGER = LogManager.getLogger(ProofEditorPanel.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(ProofEditorPanel.class.getName()); private JMenuBar mBar; private TreePanel treePanel; private FileDialog fileDialog; @@ -51,7 +49,13 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private JButton[] toolBarButtons; private JMenu file; - private JMenuItem newPuzzle, resetPuzzle, saveProofAs, saveProofChange, helpTutorial, preferences, exit; + private JMenuItem newPuzzle, + resetPuzzle, + saveProofAs, + saveProofChange, + helpTutorial, + preferences, + exit; private JMenu edit; private JMenuItem undo, redo, fitBoardToScreen, fitTreeToScreen; @@ -60,7 +64,6 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private JMenu proof; private JMenuItem add, delete, merge, collapse; private JCheckBoxMenuItem allowDefault, caseRuleGen, imdFeedback; - private JMenu about, help; private JMenuItem helpLegup, aboutLegup; @@ -78,9 +81,25 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { public static final int IMD_FEEDBACK = 32; public static final int INTERN_RO = 64; public static final int AUTO_JUST = 128; - final static int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; - private static final String[] PROFILES = {"No Assistance", "Rigorous Proof", "Casual Proof", "Assisted Proof", "Guided Proof", "Training-Wheels Proof", "No Restrictions"}; - private static final int[] PROF_FLAGS = {0, ALLOW_JUST | REQ_STEP_JUST, ALLOW_JUST, ALLOW_HINTS | ALLOW_JUST | AUTO_JUST, ALLOW_HINTS | ALLOW_JUST | REQ_STEP_JUST, ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_JUST | IMD_FEEDBACK | INTERN_RO, ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_FULLAI | ALLOW_JUST}; + static final int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; + private static final String[] PROFILES = { + "No Assistance", + "Rigorous Proof", + "Casual Proof", + "Assisted Proof", + "Guided Proof", + "Training-Wheels Proof", + "No Restrictions" + }; + private static final int[] PROF_FLAGS = { + 0, + ALLOW_JUST | REQ_STEP_JUST, + ALLOW_JUST, + ALLOW_HINTS | ALLOW_JUST | AUTO_JUST, + ALLOW_HINTS | ALLOW_JUST | REQ_STEP_JUST, + ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_JUST | IMD_FEEDBACK | INTERN_RO, + ALLOW_HINTS | ALLOW_DEFAPP | ALLOW_FULLAI | ALLOW_JUST + }; private JMenu proofMode = new JMenu("Proof Mode"); private JCheckBoxMenuItem[] proofModeItems = new JCheckBoxMenuItem[PROF_FLAGS.length]; @@ -116,7 +135,8 @@ public JMenuBar getMenuBar() { file = new JMenu("File"); newPuzzle = new JMenuItem("Open"); resetPuzzle = new JMenuItem("Reset Puzzle"); -// genPuzzle = new JMenuItem("Puzzle Generators"); // TODO: implement puzzle generator + // genPuzzle = new JMenuItem("Puzzle Generators"); // TODO: implement puzzle + // generator saveProofAs = new JMenuItem("Save As"); // create a new file to save saveProofChange = new JMenuItem("Save"); // save to the current file preferences = new JMenuItem("Preferences"); @@ -139,9 +159,10 @@ public JMenuBar getMenuBar() { add = new JMenuItem("Add"); add.addActionListener(a -> treePanel.add()); if (os.equals("mac")) { - add.setAccelerator(KeyStroke.getKeyStroke('A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + add.setAccelerator( + KeyStroke.getKeyStroke( + 'A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { add.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.CTRL_DOWN_MASK)); } proof.add(add); @@ -149,9 +170,10 @@ public JMenuBar getMenuBar() { delete = new JMenuItem("Delete"); delete.addActionListener(a -> treePanel.delete()); if (os.equals("mac")) { - delete.setAccelerator(KeyStroke.getKeyStroke('D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + delete.setAccelerator( + KeyStroke.getKeyStroke( + 'D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { delete.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); } proof.add(delete); @@ -159,9 +181,10 @@ public JMenuBar getMenuBar() { merge = new JMenuItem("Merge"); merge.addActionListener(a -> treePanel.merge()); if (os.equals("mac")) { - merge.setAccelerator(KeyStroke.getKeyStroke('M', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + merge.setAccelerator( + KeyStroke.getKeyStroke( + 'M', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { merge.setAccelerator(KeyStroke.getKeyStroke('M', InputEvent.CTRL_DOWN_MASK)); } proof.add(merge); @@ -169,33 +192,58 @@ public JMenuBar getMenuBar() { collapse = new JMenuItem("Collapse"); collapse.addActionListener(a -> treePanel.collapse()); if (os.equals("mac")) { - collapse.setAccelerator(KeyStroke.getKeyStroke('C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + collapse.setAccelerator( + KeyStroke.getKeyStroke( + 'C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { collapse.setAccelerator(KeyStroke.getKeyStroke('C', InputEvent.CTRL_DOWN_MASK)); } collapse.setEnabled(false); proof.add(collapse); - allowDefault = new JCheckBoxMenuItem("Allow Default Rule Applications", - LegupPreferences.getInstance().getUserPref(LegupPreferences.ALLOW_DEFAULT_RULES).equalsIgnoreCase(Boolean.toString(true))); - allowDefault.addChangeListener(e -> { - LegupPreferences.getInstance().setUserPref(LegupPreferences.ALLOW_DEFAULT_RULES, Boolean.toString(allowDefault.isSelected())); - }); + allowDefault = + new JCheckBoxMenuItem( + "Allow Default Rule Applications", + LegupPreferences.getInstance() + .getUserPref(LegupPreferences.ALLOW_DEFAULT_RULES) + .equalsIgnoreCase(Boolean.toString(true))); + allowDefault.addChangeListener( + e -> { + LegupPreferences.getInstance() + .setUserPref( + LegupPreferences.ALLOW_DEFAULT_RULES, + Boolean.toString(allowDefault.isSelected())); + }); proof.add(allowDefault); - caseRuleGen = new JCheckBoxMenuItem("Automatically generate cases for CaseRule", - LegupPreferences.getInstance().getUserPref(LegupPreferences.AUTO_GENERATE_CASES).equalsIgnoreCase(Boolean.toString(true))); - caseRuleGen.addChangeListener(e -> { - LegupPreferences.getInstance().setUserPref(LegupPreferences.AUTO_GENERATE_CASES, Boolean.toString(caseRuleGen.isSelected())); - }); + caseRuleGen = + new JCheckBoxMenuItem( + "Automatically generate cases for CaseRule", + LegupPreferences.getInstance() + .getUserPref(LegupPreferences.AUTO_GENERATE_CASES) + .equalsIgnoreCase(Boolean.toString(true))); + caseRuleGen.addChangeListener( + e -> { + LegupPreferences.getInstance() + .setUserPref( + LegupPreferences.AUTO_GENERATE_CASES, + Boolean.toString(caseRuleGen.isSelected())); + }); proof.add(caseRuleGen); - imdFeedback = new JCheckBoxMenuItem("Provide immediate feedback", - LegupPreferences.getInstance().getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK).equalsIgnoreCase(Boolean.toString(true))); - imdFeedback.addChangeListener(e -> { - LegupPreferences.getInstance().setUserPref(LegupPreferences.IMMEDIATE_FEEDBACK, Boolean.toString(imdFeedback.isSelected())); - }); + imdFeedback = + new JCheckBoxMenuItem( + "Provide immediate feedback", + LegupPreferences.getInstance() + .getUserPref(LegupPreferences.IMMEDIATE_FEEDBACK) + .equalsIgnoreCase(Boolean.toString(true))); + imdFeedback.addChangeListener( + e -> { + LegupPreferences.getInstance() + .setUserPref( + LegupPreferences.IMMEDIATE_FEEDBACK, + Boolean.toString(imdFeedback.isSelected())); + }); proof.add(imdFeedback); about = new JMenu("About"); @@ -206,38 +254,60 @@ public JMenuBar getMenuBar() { file.add(newPuzzle); newPuzzle.addActionListener((ActionEvent) -> loadPuzzle()); if (os.equals("mac")) { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + newPuzzle.setAccelerator( + KeyStroke.getKeyStroke( + 'N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); } file.add(resetPuzzle); - resetPuzzle.addActionListener(a -> { - Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); - if (puzzle != null) { - Tree tree = GameBoardFacade.getInstance().getTree(); - TreeNode rootNode = tree.getRootNode(); - if (rootNode != null) { - int confirmReset = JOptionPane.showConfirmDialog(this, "Reset Puzzle to Root Node?", "Confirm Reset", JOptionPane.YES_NO_OPTION); - if (confirmReset == JOptionPane.YES_OPTION) { - - List children = rootNode.getChildren(); - children.forEach(t -> puzzle.notifyTreeListeners(l -> l.onTreeElementRemoved(t))); - children.forEach(t -> puzzle.notifyBoardListeners(l -> l.onTreeElementChanged(t))); - rootNode.clearChildren(); - final TreeViewSelection selection = new TreeViewSelection(treePanel.getTreeView().getElementView(rootNode)); - puzzle.notifyTreeListeners(l -> l.onTreeSelectionChanged(selection)); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(selection.getFirstSelection().getTreeElement())); - GameBoardFacade.getInstance().getHistory().clear(); + resetPuzzle.addActionListener( + a -> { + Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); + if (puzzle != null) { + Tree tree = GameBoardFacade.getInstance().getTree(); + TreeNode rootNode = tree.getRootNode(); + if (rootNode != null) { + int confirmReset = + JOptionPane.showConfirmDialog( + this, + "Reset Puzzle to Root Node?", + "Confirm Reset", + JOptionPane.YES_NO_OPTION); + if (confirmReset == JOptionPane.YES_OPTION) { + + List children = rootNode.getChildren(); + children.forEach( + t -> + puzzle.notifyTreeListeners( + l -> l.onTreeElementRemoved(t))); + children.forEach( + t -> + puzzle.notifyBoardListeners( + l -> l.onTreeElementChanged(t))); + rootNode.clearChildren(); + final TreeViewSelection selection = + new TreeViewSelection( + treePanel.getTreeView().getElementView(rootNode)); + puzzle.notifyTreeListeners( + l -> l.onTreeSelectionChanged(selection)); + puzzle.notifyBoardListeners( + listener -> + listener.onTreeElementChanged( + selection + .getFirstSelection() + .getTreeElement())); + GameBoardFacade.getInstance().getHistory().clear(); + } + } } - } - } - }); + }); if (os.equals("mac")) { - resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + resetPuzzle.setAccelerator( + KeyStroke.getKeyStroke( + 'R', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', InputEvent.CTRL_DOWN_MASK)); } file.addSeparator(); @@ -245,40 +315,44 @@ public JMenuBar getMenuBar() { file.add(saveProofAs); saveProofAs.addActionListener((ActionEvent) -> saveProofAs()); - - //save proof as... + // save proof as... if (os.equals("mac")) { - saveProofAs.setAccelerator(KeyStroke.getKeyStroke('S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + saveProofAs.setAccelerator( + KeyStroke.getKeyStroke( + 'S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { saveProofAs.setAccelerator(KeyStroke.getKeyStroke('S', InputEvent.CTRL_DOWN_MASK)); } // save proof change if (os.equals("mac")) { - saveProofChange.setAccelerator(KeyStroke.getKeyStroke('A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + saveProofChange.setAccelerator( + KeyStroke.getKeyStroke( + 'A', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { saveProofChange.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.CTRL_DOWN_MASK)); } - file.add(saveProofChange); saveProofChange.addActionListener((ActionEvent) -> saveProofChange()); file.addSeparator(); // preference file.add(preferences); - preferences.addActionListener(a -> { - PreferencesDialog preferencesDialog = PreferencesDialog.CreateDialogForProofEditor(this.frame, this.ruleFrame); - }); + preferences.addActionListener( + a -> { + PreferencesDialog preferencesDialog = + PreferencesDialog.CreateDialogForProofEditor( + this.frame, this.ruleFrame); + }); file.addSeparator(); // help function if (os.equals("mac")) { - helpTutorial.setAccelerator(KeyStroke.getKeyStroke('H', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + helpTutorial.setAccelerator( + KeyStroke.getKeyStroke( + 'H', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { helpTutorial.setAccelerator(KeyStroke.getKeyStroke('H', InputEvent.CTRL_DOWN_MASK)); } file.add(helpTutorial); @@ -286,56 +360,75 @@ public JMenuBar getMenuBar() { helpTutorial.addActionListener((ActionEvent) -> helpTutorial()); file.addSeparator(); - - //exit + // exit file.add(exit); exit.addActionListener((ActionEvent) -> exitEditor()); if (os.equals("mac")) { - exit.setAccelerator(KeyStroke.getKeyStroke('Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + exit.setAccelerator( + KeyStroke.getKeyStroke( + 'Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); } mBar.add(edit); - edit.add(undo); - undo.addActionListener((ActionEvent) -> - GameBoardFacade.getInstance().getHistory().undo()); + undo.addActionListener((ActionEvent) -> GameBoardFacade.getInstance().getHistory().undo()); if (os.equals("mac")) { - undo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + undo.setAccelerator( + KeyStroke.getKeyStroke( + 'Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { undo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK)); } edit.add(redo); // Created action to support two keybinds (CTRL-SHIFT-Z, CTRL-Y) - Action redoAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - GameBoardFacade.getInstance().getHistory().redo(); - } - }; + Action redoAction = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + GameBoardFacade.getInstance().getHistory().redo(); + } + }; if (os.equals("mac")) { - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put( - KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + InputEvent.SHIFT_DOWN_MASK), "redoAction"); - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put( - KeyStroke.getKeyStroke('Y', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "redoAction"); - redo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + InputEvent.SHIFT_DOWN_MASK)); - } - else { - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('Y', InputEvent.CTRL_DOWN_MASK), "redoAction"); - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK), "redoAction"); + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put( + KeyStroke.getKeyStroke( + 'Z', + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + + InputEvent.SHIFT_DOWN_MASK), + "redoAction"); + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put( + KeyStroke.getKeyStroke( + 'Y', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + "redoAction"); + redo.setAccelerator( + KeyStroke.getKeyStroke( + 'Z', + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + + InputEvent.SHIFT_DOWN_MASK)); + } else { + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put(KeyStroke.getKeyStroke('Y', InputEvent.CTRL_DOWN_MASK), "redoAction"); + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put( + KeyStroke.getKeyStroke( + 'Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK), + "redoAction"); redo.getActionMap().put("redoAction", redoAction); // Button in menu will show CTRL-SHIFT-Z as primary keybind - redo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)); + redo.setAccelerator( + KeyStroke.getKeyStroke( + 'Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)); } edit.add(fitBoardToScreen); - fitBoardToScreen.addActionListener((ActionEvent) -> dynamicBoardView.fitBoardViewToScreen()); + fitBoardToScreen.addActionListener( + (ActionEvent) -> dynamicBoardView.fitBoardViewToScreen()); edit.add(fitTreeToScreen); fitTreeToScreen.addActionListener((ActionEvent) -> this.fitTreeViewToScreen()); @@ -343,19 +436,21 @@ public void actionPerformed(ActionEvent e) { mBar.add(proof); about.add(aboutLegup); - aboutLegup.addActionListener(l -> { - JOptionPane.showMessageDialog(null, "Version: 5.1.0"); - }); + aboutLegup.addActionListener( + l -> { + JOptionPane.showMessageDialog(null, "Version: 5.1.0"); + }); about.add(helpLegup); - helpLegup.addActionListener(l -> { - try { - java.awt.Desktop.getDesktop().browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki")); - } - catch (IOException e) { - LOGGER.error("Can't open web page"); - } - }); + helpLegup.addActionListener( + l -> { + try { + java.awt.Desktop.getDesktop() + .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki")); + } catch (IOException e) { + LOGGER.error("Can't open web page"); + } + }); mBar.add(about); @@ -402,16 +497,15 @@ public Object[] promptPuzzle() { if (puzzlePath != null) { fileName = puzzlePath.getAbsolutePath(); - String lastDirectoryPath = fileName.substring(0, fileName.lastIndexOf(File.separator)); + String lastDirectoryPath = fileName.substring(0, fileName.lastIndexOf(File.separator)); preferences.setSavedPath(lastDirectoryPath); puzzleFile = puzzlePath; - } - else { + } else { // The attempt to prompt a puzzle ended gracefully (cancel) return null; } - return new Object[]{fileName, puzzleFile}; + return new Object[] {fileName, puzzleFile}; } public void loadPuzzle() { @@ -432,25 +526,35 @@ public void loadPuzzle(String fileName, File puzzleFile) { GameBoardFacade.getInstance().loadPuzzle(fileName); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); frame.setTitle(puzzleName + " - " + puzzleFile.getName()); - } - catch (InvalidFileFormatException e) { + } catch (InvalidFileFormatException e) { legupUI.displayPanel(0); LOGGER.error(e.getMessage()); - if (e.getMessage().contains("Proof Tree construction error: could not find rule by ID")) { // TO DO: make error message not hardcoded - JOptionPane.showMessageDialog(null, "This file runs on an outdated version of Legup\nand is not compatible with the current version.", "Error", JOptionPane.ERROR_MESSAGE); + if (e.getMessage() + .contains( + "Proof Tree construction error: could not find rule by ID")) { // TO + // DO: make error + // message not + // hardcoded + JOptionPane.showMessageDialog( + null, + "This file runs on an outdated version of Legup\n" + + "and is not compatible with the current version.", + "Error", + JOptionPane.ERROR_MESSAGE); loadPuzzle(); - } - else { - JOptionPane.showMessageDialog(null, "File does not exist or it cannot be read", "Error", JOptionPane.ERROR_MESSAGE); + } else { + JOptionPane.showMessageDialog( + null, + "File does not exist or it cannot be read", + "Error", + JOptionPane.ERROR_MESSAGE); loadPuzzle(); } } } } - /** - * save the proof in the current file - */ + /** save the proof in the current file */ private void direct_save() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -464,16 +568,13 @@ private void direct_save() { throw new ExportFileException("Puzzle exporter null"); } exporter.exportPuzzle(fileName); - } - catch (ExportFileException e) { + } catch (ExportFileException e) { e.printStackTrace(); } } } - /** - * Create a new file and save proof to it - */ + /** Create a new file and save proof to it */ private void saveProofAs() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -484,9 +585,9 @@ private void saveProofAs() { fileDialog.setTitle("Save As"); String curFileName = GameBoardFacade.getInstance().getCurFileName(); if (curFileName == null) { - fileDialog.setDirectory(LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); - } - else { + fileDialog.setDirectory( + LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); + } else { File curFile = new File(curFileName); fileDialog.setDirectory(curFile.getParent()); } @@ -504,8 +605,7 @@ private void saveProofAs() { throw new ExportFileException("Puzzle exporter null"); } exporter.exportPuzzle(fileName); - } - catch (ExportFileException e) { + } catch (ExportFileException e) { e.printStackTrace(); } } @@ -513,7 +613,7 @@ private void saveProofAs() { // Hyperlink for help button; links to wiki page for tutorials private void helpTutorial() { - //redirecting to certain help link in wiki + // redirecting to certain help link in wiki Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { return; @@ -541,32 +641,30 @@ private void helpTutorial() { } Runtime rt = Runtime.getRuntime(); try { - //rt.exec("rundll32 url.dll,FileProtocolHandler "+url); + // rt.exec("rundll32 url.dll,FileProtocolHandler "+url); java.awt.Desktop.getDesktop().browse(java.net.URI.create(url)); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); } } - //add the new function need to implement + // add the new function need to implement public void add_drop() { // add the mouse event then we can use the new listener to implement and // we should create a need jbuttom for it to ship the rule we select. JPanel panel = new JPanel(); JButton moveing_buttom = new JButton(); moveing_buttom.setFocusPainted(false); - moveing_buttom.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - //get the selected rule - } - }); + moveing_buttom.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // get the selected rule + } + }); panel.add(moveing_buttom); - } - // Quick save proof to the current file with a pop window to show "successfully saved" private void saveProofChange() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); @@ -582,27 +680,23 @@ private void saveProofChange() { } exporter.exportPuzzle(fileName); // Save confirmation - JOptionPane.showMessageDialog(null, "Successfully Saved", "Confirm", JOptionPane.INFORMATION_MESSAGE); - } - catch (ExportFileException e) { + JOptionPane.showMessageDialog( + null, "Successfully Saved", "Confirm", JOptionPane.INFORMATION_MESSAGE); + } catch (ExportFileException e) { e.printStackTrace(); } } - } - - //ask to edu.rpi.legup.save current proof + // ask to edu.rpi.legup.save current proof public boolean noquit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } - /** - * Sets the main content for the edu.rpi.legup.user interface - */ + /** Sets the main content for the edu.rpi.legup.user interface */ protected void setupContent() { -// JPanel consoleBox = new JPanel(new BorderLayout()); + // JPanel consoleBox = new JPanel(new BorderLayout()); JPanel treeBox = new JPanel(new BorderLayout()); JPanel ruleBox = new JPanel(new BorderLayout()); @@ -612,13 +706,15 @@ protected void setupContent() { treePanel = new TreePanel(); - dynamicBoardView = new DynamicView(new ScrollView(new BoardController()), DynamicViewType.BOARD); + dynamicBoardView = + new DynamicView(new ScrollView(new BoardController()), DynamicViewType.BOARD); TitledBorder titleBoard = BorderFactory.createTitledBorder("Board"); titleBoard.setTitleJustification(TitledBorder.CENTER); dynamicBoardView.setBorder(titleBoard); JPanel boardPanel = new JPanel(new BorderLayout()); - topHalfPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, ruleFrame, dynamicBoardView); + topHalfPanel = + new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, ruleFrame, dynamicBoardView); mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, topHalfPanel, treePanel); topHalfPanel.setPreferredSize(new Dimension(600, 400)); mainPanel.setPreferredSize(new Dimension(600, 600)); @@ -631,16 +727,16 @@ protected void setupContent() { ruleBox.add(boardPanel); treeBox.add(ruleBox); this.add(treeBox); -// consoleBox.add(treeBox); -// -// getContentPane().add(consoleBox); + // consoleBox.add(treeBox); + // + // getContentPane().add(consoleBox); -// JPopupPanel popupPanel = new JPopupPanel(); -// setGlassPane(popupPanel); -// popupPanel.setVisible(true); + // JPopupPanel popupPanel = new JPopupPanel(); + // setGlassPane(popupPanel); + // popupPanel.setVisible(true); mainPanel.setDividerLocation(mainPanel.getMaximumDividerLocation() + 100); -// frame.pack(); + // frame.pack(); revalidate(); } @@ -648,12 +744,19 @@ private void setupToolBar() { setToolBarButtons(new JButton[ToolbarName.values().length]); for (int i = 0; i < ToolbarName.values().length; i++) { String toolBarName = ToolbarName.values()[i].toString(); - URL resourceLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); + URL resourceLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); // Scale the image icons down to make the buttons smaller ImageIcon imageIcon = new ImageIcon(resourceLocation); Image image = imageIcon.getImage(); - imageIcon = new ImageIcon(image.getScaledInstance(this.TOOLBAR_ICON_SCALE, this.TOOLBAR_ICON_SCALE, Image.SCALE_SMOOTH)); + imageIcon = + new ImageIcon( + image.getScaledInstance( + this.TOOLBAR_ICON_SCALE, + this.TOOLBAR_ICON_SCALE, + Image.SCALE_SMOOTH)); JButton button = new JButton(toolBarName, imageIcon); button.setFocusPainted(false); @@ -679,23 +782,27 @@ private void setupToolBar() { getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); } -// toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent e) -> promptPuzzle()); -// toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> saveProof()); -// toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().undo()); -// toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().redo()); - toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> { - }); - toolBarButtons[ToolbarName.CHECK.ordinal()].addActionListener((ActionEvent e) -> checkProof()); - toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> { - }); - toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> { - }); - - toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].addActionListener((ActionEvent e) -> checkProofAll()); - -// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent + // e) -> + // promptPuzzle()); + // toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> + // saveProof()); + // toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().undo()); + // toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().redo()); + toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.CHECK.ordinal()].addActionListener( + (ActionEvent e) -> checkProof()); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> {}); + + toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].addActionListener( + (ActionEvent e) -> checkProofAll()); + + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(false); toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); @@ -723,15 +830,13 @@ public JButton[] getToolBarButtons() { return toolBarButtons; } - /** - * Checks the proof for correctness - */ + /** Checks the proof for correctness */ private void checkProof() { GameBoardFacade facade = GameBoardFacade.getInstance(); Tree tree = GameBoardFacade.getInstance().getTree(); Board board = facade.getBoard(); Board finalBoard = null; - boolean delayStatus = true; //board.evalDelayStatus(); + boolean delayStatus = true; // board.evalDelayStatus(); repaintAll(); @@ -745,10 +850,10 @@ private void checkProof() { submission.submit(); }*/ JOptionPane.showMessageDialog(null, "Congratulations! Your proof is correct."); - } - else { + } else { String message = "\nThe game board is not solved."; - JOptionPane.showMessageDialog(null, message, "Invalid proof.", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog( + null, message, "Invalid proof.", JOptionPane.ERROR_MESSAGE); } } @@ -780,9 +885,8 @@ public void setPuzzleView(Puzzle puzzle) { ruleFrame.getContradictionPanel().setRules(puzzle.getContradictionRules()); ruleFrame.getSearchPanel().setSearchBar(puzzle); - toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); -// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); reloadGui(); } @@ -795,9 +899,7 @@ public void repaintTree() { treePanel.repaintTreeView(GameBoardFacade.getInstance().getTree()); } - /** - * Checks the proof for all files - */ + /** Checks the proof for all files */ private void checkProofAll() { GameBoardFacade facade = GameBoardFacade.getInstance(); @@ -812,7 +914,8 @@ private void checkProofAll() { */ LegupPreferences preferences = LegupPreferences.getInstance(); - File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); + File preferredDirectory = + new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); folderBrowser = new JFileChooser(preferredDirectory); folderBrowser.showOpenDialog(this); @@ -830,13 +933,13 @@ private void checkProofAll() { writer.append("Name,File Name,Puzzle Type,Score,Solved?\n"); // Go through student folders - for (final File folderEntry : Objects.requireNonNull(folder.listFiles(File::isDirectory))) { + for (final File folderEntry : + Objects.requireNonNull(folder.listFiles(File::isDirectory))) { // Write path String path = folderEntry.getName(); traverseDir(folderEntry, writer, path); } - } - catch (IOException ex) { + } catch (IOException ex) { LOGGER.error(ex.getMessage()); } JOptionPane.showMessageDialog(null, "Batch grading complete."); @@ -888,16 +991,13 @@ private void traverseDir(File folder, BufferedWriter writer, String path) throws writer.append(puzzle.getName()).append(","); if (puzzle.isPuzzleComplete()) { writer.append("1,Solved\n"); - } - else { + } else { writer.append("0,Unsolved\n"); } - } - catch (InvalidFileFormatException e) { + } catch (InvalidFileFormatException e) { writer.append(fName).append(",Invalid,,Ungradeable\n"); } - } - else { + } else { LOGGER.debug("Failed to run sim"); } } @@ -924,44 +1024,41 @@ public TreePanel getTreePanel() { public void onPushChange(ICommand command) { LOGGER.info("Pushing " + command.getClass().getSimpleName() + " to stack."); undo.setEnabled(true); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(true); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(true); redo.setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); } - /** - * Called when the history is cleared - */ + /** Called when the history is cleared */ @Override public void onClearHistory() { - //undo.setEnabled(false); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); - //redo.setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + // undo.setEnabled(false); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + // redo.setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); } /** * Called when an action is redone * * @param isBottom true if there are no more actions to undo, false otherwise - * @param isTop true if there are no more changes to redo, false otherwise + * @param isTop true if there are no more changes to redo, false otherwise */ @Override public void onRedo(boolean isBottom, boolean isTop) { undo.setEnabled(!isBottom); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); redo.setEnabled(!isTop); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); if (isBottom) { String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); frame.setTitle(puzzleName + " - " + puzzleFile.getName()); - } - else { + } else { String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); @@ -972,44 +1069,49 @@ public void onRedo(boolean isBottom, boolean isTop) { * Called when an action is undone * * @param isBottom true if there are no more actions to undo, false otherwise - * @param isTop true if there are no more changes to redo, false otherwise + * @param isTop true if there are no more changes to redo, false otherwise */ @Override public void onUndo(boolean isBottom, boolean isTop) { undo.setEnabled(!isBottom); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); redo.setEnabled(!isTop); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); if (isBottom) { frame.setTitle(puzzleName + " - " + puzzleFile.getName()); - } - else { + } else { frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); } } - /** - * Submits the proof file - */ + /** Submits the proof file */ private void submit() { GameBoardFacade facade = GameBoardFacade.getInstance(); Board board = facade.getBoard(); - boolean delayStatus = true; //board.evalDelayStatus(); + boolean delayStatus = true; // board.evalDelayStatus(); repaintAll(); Puzzle pm = facade.getPuzzleModule(); if (pm.isPuzzleComplete() && delayStatus) { // 0 means yes, 1 means no (Java's fault...) - int confirm = JOptionPane.showConfirmDialog(null, "Are you sure you wish to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); + int confirm = + JOptionPane.showConfirmDialog( + null, + "Are you sure you wish to submit?", + "Proof Submission", + JOptionPane.YES_NO_OPTION); if (confirm == 0) { Submission submission = new Submission(board); submission.submit(); } - } - else { - JOptionPane.showConfirmDialog(null, "Your proof is incorrect! Are you sure you wish to submit?", "Proof Submission", JOptionPane.YES_NO_OPTION); + } else { + JOptionPane.showConfirmDialog( + null, + "Your proof is incorrect! Are you sure you wish to submit?", + "Proof Submission", + JOptionPane.YES_NO_OPTION); Submission submit = new Submission(board); } } diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java index 2c4f37c85..b3cd30ffb 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -1,27 +1,19 @@ package edu.rpi.legup.ui; +import static java.lang.System.exit; + import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.controller.EditorElementController; -import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.history.ICommand; import edu.rpi.legup.history.IHistoryListener; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.PuzzleExporter; -import edu.rpi.legup.model.gameboard.PuzzleElement; -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.save.ExportFileException; import edu.rpi.legup.save.InvalidFileFormatException; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.puzzleeditorui.elementsview.ElementFrame; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.*; -import javax.swing.border.TitledBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -30,14 +22,15 @@ import java.io.IOException; import java.net.URI; import java.net.URL; -import java.util.List; import java.util.Objects; - -import static java.lang.System.exit; +import javax.swing.*; +import javax.swing.border.TitledBorder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { - private final static Logger LOGGER = LogManager.getLogger(PuzzleEditorPanel.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(PuzzleEditorPanel.class.getName()); private JMenu[] menus; private JMenuItem helpLegup, aboutLegup; private JMenuBar menuBar; @@ -50,14 +43,14 @@ public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { private DynamicView dynamicBoardView; private BoardView boardView; private TitledBorder boardBorder; - //private JSplitPane splitPanel, topHalfPanel; + // private JSplitPane splitPanel, topHalfPanel; private FileDialog fileDialog; private JMenuItem undo, redo, fitBoardToScreen; private ElementFrame elementFrame; private JPanel treePanel; private LegupUI legupUI; private EditorElementController editorElementController; - final static int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; + static final int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; public PuzzleEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.fileDialog = fileDialog; @@ -75,13 +68,15 @@ protected void setupContent() { elementFrame = new ElementFrame(editorElementController); elementBox.add(elementFrame, BorderLayout.WEST); - dynamicBoardView = new DynamicView(new ScrollView(new BoardController()), DynamicViewType.BOARD); + dynamicBoardView = + new DynamicView(new ScrollView(new BoardController()), DynamicViewType.BOARD); TitledBorder titleBoard = BorderFactory.createTitledBorder("Board"); titleBoard.setTitleJustification(TitledBorder.CENTER); dynamicBoardView.setBorder(titleBoard); JPanel boardPanel = new JPanel(new BorderLayout()); - splitPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, elementFrame, dynamicBoardView); + splitPanel = + new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, elementFrame, dynamicBoardView); splitPanel.setPreferredSize(new Dimension(600, 400)); boardPanel.add(splitPanel); @@ -110,9 +105,10 @@ public void setMenuBar() { JMenuItem newPuzzle = new JMenuItem("New"); newPuzzle.addActionListener((ActionEvent) -> loadPuzzle()); if (os.equals("mac")) { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + newPuzzle.setAccelerator( + KeyStroke.getKeyStroke( + 'N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); } // file>save @@ -121,18 +117,20 @@ public void setMenuBar() { JMenuItem directSavePuzzle = new JMenuItem("Direct Save Proof "); directSavePuzzle.addActionListener((ActionEvent) -> direct_save()); if (os.equals("mac")) { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + newPuzzle.setAccelerator( + KeyStroke.getKeyStroke( + 'D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { newPuzzle.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); } JMenuItem exit = new JMenuItem("Exit"); exit.addActionListener((ActionEvent) -> exitEditor()); if (os.equals("mac")) { - exit.setAccelerator(KeyStroke.getKeyStroke('Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + exit.setAccelerator( + KeyStroke.getKeyStroke( + 'Q', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); } menus[0].add(newPuzzle); @@ -149,43 +147,62 @@ public void setMenuBar() { fitBoardToScreen = new JMenuItem("Fit Board to Screen"); menus[1].add(undo); - undo.addActionListener((ActionEvent) -> - GameBoardFacade.getInstance().getHistory().undo()); + undo.addActionListener((ActionEvent) -> GameBoardFacade.getInstance().getHistory().undo()); if (os.equals("mac")) { - undo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - } - else { + undo.setAccelerator( + KeyStroke.getKeyStroke( + 'Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + } else { undo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK)); } menus[1].add(redo); // Created action to support two keybinds (CTRL-SHIFT-Z, CTRL-Y) - Action redoAction = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - GameBoardFacade.getInstance().getHistory().redo(); - } - }; + Action redoAction = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + GameBoardFacade.getInstance().getHistory().redo(); + } + }; if (os.equals("mac")) { - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put( - KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + InputEvent.SHIFT_DOWN_MASK), "redoAction"); - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put( - KeyStroke.getKeyStroke('Y', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "redoAction"); - redo.setAccelerator(KeyStroke.getKeyStroke('Z', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + InputEvent.SHIFT_DOWN_MASK)); - } - else { - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('Y', InputEvent.CTRL_DOWN_MASK), "redoAction"); - redo.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK), "redoAction"); + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put( + KeyStroke.getKeyStroke( + 'Z', + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + + InputEvent.SHIFT_DOWN_MASK), + "redoAction"); + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put( + KeyStroke.getKeyStroke( + 'Y', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + "redoAction"); + redo.setAccelerator( + KeyStroke.getKeyStroke( + 'Z', + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() + + InputEvent.SHIFT_DOWN_MASK)); + } else { + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put(KeyStroke.getKeyStroke('Y', InputEvent.CTRL_DOWN_MASK), "redoAction"); + redo.getInputMap(WHEN_IN_FOCUSED_WINDOW) + .put( + KeyStroke.getKeyStroke( + 'Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK), + "redoAction"); redo.getActionMap().put("redoAction", redoAction); // Button in menu will show CTRL-SHIFT-Z as primary keybind - redo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)); + redo.setAccelerator( + KeyStroke.getKeyStroke( + 'Z', InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)); } - menus[1].add(fitBoardToScreen); - fitBoardToScreen.addActionListener((ActionEvent) -> dynamicBoardView.fitBoardViewToScreen()); + fitBoardToScreen.addActionListener( + (ActionEvent) -> dynamicBoardView.fitBoardViewToScreen()); // HELP menus[2] = new JMenu("Help"); @@ -193,18 +210,20 @@ public void actionPerformed(ActionEvent e) { aboutLegup = new JMenuItem("About Legup"); menus[2].add(helpLegup); menus[2].add(aboutLegup); - helpLegup.addActionListener(l -> { - try { - java.awt.Desktop.getDesktop().browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki")); - } - catch (IOException e) { - LOGGER.error("Can't open web page"); - } - }); + helpLegup.addActionListener( + l -> { + try { + java.awt.Desktop.getDesktop() + .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki")); + } catch (IOException e) { + LOGGER.error("Can't open web page"); + } + }); menus[2].add(aboutLegup); - aboutLegup.addActionListener(l -> { - JOptionPane.showMessageDialog(null, "Version: 5.1.0"); - }); + aboutLegup.addActionListener( + l -> { + JOptionPane.showMessageDialog(null, "Version: 5.1.0"); + }); // add menus to menubar for (JMenu menu : menus) { menuBar.add(menu); @@ -234,12 +253,19 @@ private void setupToolBar() { int lastone = 0; for (int i = 0; i < ToolbarName.values().length - 1; i++) { String toolBarName = ToolbarName.values()[i].toString(); - URL resourceLocation = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); + URL resourceLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); // Scale the image icons down to make the buttons smaller ImageIcon imageIcon = new ImageIcon(resourceLocation); Image image = imageIcon.getImage(); - imageIcon = new ImageIcon(image.getScaledInstance(this.TOOLBAR_ICON_SCALE, this.TOOLBAR_ICON_SCALE, Image.SCALE_SMOOTH)); + imageIcon = + new ImageIcon( + image.getScaledInstance( + this.TOOLBAR_ICON_SCALE, + this.TOOLBAR_ICON_SCALE, + Image.SCALE_SMOOTH)); JButton button = new JButton(toolBarName, imageIcon); button.setFocusPainted(false); @@ -247,33 +273,42 @@ private void setupToolBar() { lastone = i; } - - URL check_and_save = ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Check.png"); + URL check_and_save = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Check.png"); ImageIcon imageIcon = new ImageIcon(check_and_save); Image image = imageIcon.getImage(); - imageIcon = new ImageIcon(image.getScaledInstance(this.TOOLBAR_ICON_SCALE, this.TOOLBAR_ICON_SCALE, Image.SCALE_SMOOTH)); + imageIcon = + new ImageIcon( + image.getScaledInstance( + this.TOOLBAR_ICON_SCALE, + this.TOOLBAR_ICON_SCALE, + Image.SCALE_SMOOTH)); JButton checkandsave = new JButton("check and Save", imageIcon); checkandsave.setFocusPainted(false); - checkandsave.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - //savePuzzle(); - String filename = savePuzzle(); - File puzzlename = new File(filename); - System.out.println(filename); - - - GameBoardFacade.getInstance().getLegupUI().displayPanel(1); - GameBoardFacade.getInstance().getLegupUI().getProofEditor().loadPuzzle(filename, new File(filename)); - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - frame.setTitle(puzzleName + " - " + puzzlename.getName()); - } - }); + checkandsave.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + // savePuzzle(); + String filename = savePuzzle(); + File puzzlename = new File(filename); + System.out.println(filename); + + GameBoardFacade.getInstance().getLegupUI().displayPanel(1); + GameBoardFacade.getInstance() + .getLegupUI() + .getProofEditor() + .loadPuzzle(filename, new File(filename)); + String puzzleName = + GameBoardFacade.getInstance().getPuzzleModule().getName(); + frame.setTitle(puzzleName + " - " + puzzlename.getName()); + } + }); getToolBarButtons()[lastone + 1] = checkandsave; System.out.println("it is create new file"); - toolBar = new JToolBar(); toolBar.setFloatable(false); toolBar.setRollover(true); @@ -293,20 +328,22 @@ public void actionPerformed(ActionEvent e) { getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); } -// toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent e) -> promptPuzzle()); -// toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> saveProof()); -// toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().undo()); -// toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> GameBoardFacade.getInstance().getHistory().redo()); - toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> { - }); - toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> { - }); - toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> { - }); - -// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); -// toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); -// toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent + // e) -> + // promptPuzzle()); + // toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> + // saveProof()); + // toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().undo()); + // toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().redo()); + toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> {}); + + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].setEnabled(false); @@ -314,15 +351,14 @@ public void actionPerformed(ActionEvent e) { this.add(toolBar, BorderLayout.NORTH); } - public void loadPuzzleFromHome(String game, int rows, int columns) throws IllegalArgumentException { + public void loadPuzzleFromHome(String game, int rows, int columns) + throws IllegalArgumentException { GameBoardFacade facade = GameBoardFacade.getInstance(); try { facade.loadPuzzle(game, rows, columns); - } - catch (IllegalArgumentException exception) { + } catch (IllegalArgumentException exception) { throw new IllegalArgumentException(exception.getMessage()); - } - catch (RuntimeException e) { + } catch (RuntimeException e) { e.printStackTrace(); LOGGER.error(e.getMessage()); } @@ -332,11 +368,9 @@ public void loadPuzzleFromHome(String game, String[] statements) { GameBoardFacade facade = GameBoardFacade.getInstance(); try { facade.loadPuzzle(game, statements); - } - catch (IllegalArgumentException exception) { + } catch (IllegalArgumentException exception) { throw new IllegalArgumentException(exception.getMessage()); - } - catch (RuntimeException e) { + } catch (RuntimeException e) { e.printStackTrace(); LOGGER.error(e.getMessage()); } @@ -365,13 +399,12 @@ public Object[] promptPuzzle() { if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); puzzleFile = new File(fileName); - } - else { + } else { // The attempt to prompt a puzzle ended gracefully (cancel) return null; } - return new Object[]{fileName, puzzleFile}; + return new Object[] {fileName, puzzleFile}; } public void loadPuzzle() { @@ -392,11 +425,14 @@ public void loadPuzzle(String fileName, File puzzleFile) { GameBoardFacade.getInstance().loadPuzzleEditor(fileName); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); frame.setTitle(puzzleName + " - " + puzzleFile.getName()); - } - catch (InvalidFileFormatException e) { + } catch (InvalidFileFormatException e) { legupUI.displayPanel(0); LOGGER.error(e.getMessage()); - JOptionPane.showMessageDialog(null, "File does not exist, cannot be read, or cannot be edited", "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog( + null, + "File does not exist, cannot be read, or cannot be edited", + "Error", + JOptionPane.ERROR_MESSAGE); loadPuzzle(); } } @@ -408,24 +444,18 @@ public boolean noQuit(String instr) { } @Override - public void onPushChange(ICommand command) { - - } + public void onPushChange(ICommand command) {} @Override - public void onUndo(boolean isBottom, boolean isTop) { - - } + public void onUndo(boolean isBottom, boolean isTop) {} @Override - public void onRedo(boolean isBottom, boolean isTop) { - - } + public void onRedo(boolean isBottom, boolean isTop) {} @Override public void onClearHistory() { - //undo.setEnabled(false); - //redo.setEnabled(false); + // undo.setEnabled(false); + // redo.setEnabled(false); } public BoardView getBoardView() { @@ -453,7 +483,8 @@ public void setPuzzleView(Puzzle puzzle) { this.splitPanel.setVisible(true); } - TitledBorder titleBoard = BorderFactory.createTitledBorder(boardView.getClass().getSimpleName()); + TitledBorder titleBoard = + BorderFactory.createTitledBorder(boardView.getClass().getSimpleName()); titleBoard.setTitleJustification(TitledBorder.CENTER); dynamicBoardView.setBorder(titleBoard); @@ -464,13 +495,10 @@ public void setPuzzleView(Puzzle puzzle) { } toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); -// toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); } - /** - * Saves a puzzle - */ - + /** Saves a puzzle */ private void direct_save() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -484,8 +512,7 @@ private void direct_save() { throw new ExportFileException("Puzzle exporter null"); } exporter.exportPuzzle(fileName); - } - catch (ExportFileException e) { + } catch (ExportFileException e) { e.printStackTrace(); } } @@ -500,8 +527,11 @@ private String savePuzzle() { // for TreeTent, need to check validity before saving if (Objects.equals(puzzle.getName(), "TreeTent")) { if (!puzzle.checkValidity()) { - int input = JOptionPane.showConfirmDialog(null, "The puzzle you edited is not " + - "valid, would you still like to save? "); + int input = + JOptionPane.showConfirmDialog( + null, + "The puzzle you edited is not " + + "valid, would you still like to save? "); if (input != 0) { return ""; } @@ -516,9 +546,9 @@ private String savePuzzle() { fileDialog.setTitle("Save Proof"); String curFileName = GameBoardFacade.getInstance().getCurFileName(); if (curFileName == null) { - fileDialog.setDirectory(LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); - } - else { + fileDialog.setDirectory( + LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); + } else { File curFile = new File(curFileName); fileDialog.setDirectory(curFile.getParent()); } @@ -536,18 +566,14 @@ private String savePuzzle() { throw new ExportFileException("Puzzle exporter null"); } exporter.exportPuzzle(fileName); - } - catch (ExportFileException e) { + } catch (ExportFileException e) { e.printStackTrace(); } } return fileName; } - public DynamicView getDynamicBoardView() { return dynamicBoardView; } - - } diff --git a/src/main/java/edu/rpi/legup/ui/ScrollView.java b/src/main/java/edu/rpi/legup/ui/ScrollView.java index 8f9144fda..0bf8335a2 100644 --- a/src/main/java/edu/rpi/legup/ui/ScrollView.java +++ b/src/main/java/edu/rpi/legup/ui/ScrollView.java @@ -1,17 +1,13 @@ package edu.rpi.legup.ui; import edu.rpi.legup.controller.Controller; - import java.awt.*; -import java.lang.Double; - import java.util.TreeSet; import java.util.logging.Logger; - import javax.swing.*; public class ScrollView extends JScrollPane { - private final static Logger LOGGER = Logger.getLogger(ScrollView.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ScrollView.class.getName()); private static final double minScale = 0.25; private static final double maxScale = 4.0; @@ -28,8 +24,8 @@ public class ScrollView extends JScrollPane { private ZoomWidget widget; /** - * ScrollView Constructor - creates a ScrollView object using - * the controller handle the ui events + * ScrollView Constructor - creates a ScrollView object using the controller handle the ui + * events * * @param controller controller that handles the ui events */ @@ -107,9 +103,7 @@ public void layoutContainer(Container parent) { }; } - /** - * Updates zoomSize and view viewSize with the new scale - */ + /** Updates zoomSize and view viewSize with the new scale */ private void updateSize() { zoomSize.setSize((int) (viewSize.width * scale), (int) (viewSize.height * scale)); viewport.setViewSize(zoomSize); @@ -118,7 +112,7 @@ private void updateSize() { /** * Updates the viewport position * - * @param point point to set the viewport to + * @param point point to set the viewport to * @param magnification magnification to set the viewport to */ public void updatePosition(Point point, double magnification) { @@ -131,13 +125,16 @@ public void updatePosition(Point point, double magnification) { /** * Zooms in or out on a position within the dynamicView * - * @param n level of zoom - n less than 0 is zoom in, n greater than 0 is zoom out + * @param n level of zoom - n less than 0 is zoom in, n greater than 0 is zoom out * @param point position to zoom in on */ public void zoom(int n, Point point) { // if no Point is given, keep current center if (point == null) { - point = new Point(viewport.getWidth() / 2 + viewport.getX(), viewport.getHeight() / 2 + viewport.getY()); + point = + new Point( + viewport.getWidth() / 2 + viewport.getX(), + viewport.getHeight() / 2 + viewport.getY()); } // magnification level double mag = (double) n * 1.05; @@ -153,8 +150,7 @@ public void zoom(int n, Point point) { updateSize(); updatePosition(point, mag); // zoom out - } - else { + } else { mag = 1 / mag; // check zoom bounds if (scale * mag < minScale) { @@ -182,8 +178,10 @@ public void zoomTo(double newScale) { } // calculate the newScale and center point double mag = newScale / scale; - Point p = new Point(viewport.getWidth() / 2 + viewport.getX(), - viewport.getHeight() / 2 + viewport.getY()); + Point p = + new Point( + viewport.getWidth() / 2 + viewport.getX(), + viewport.getHeight() / 2 + viewport.getY()); // set scale directly scale = newScale; @@ -192,8 +190,7 @@ public void zoomTo(double newScale) { updateSize(); updatePosition(p, mag); // zoom out - } - else { + } else { updatePosition(p, mag); updateSize(); } @@ -201,9 +198,7 @@ public void zoomTo(double newScale) { revalidate(); } - /** - * Get the ideal zoom based on the viewSize - */ + /** Get the ideal zoom based on the viewSize */ public void zoomFit() { if (viewport.getWidth() != 0 && viewport.getHeight() != 0) { double fitWidth = (viewport.getWidth() - 8.0) / viewSize.width; @@ -213,9 +208,7 @@ public void zoomFit() { } } - /** - * Zooms in to the next zoom level - */ + /** Zooms in to the next zoom level */ public void zoomIn() { // find the next valid zoom level Double newScale = zoomLevels.higher(scale); @@ -224,9 +217,7 @@ public void zoomIn() { } } - /** - * Zooms out to the previous zoom level - */ + /** Zooms out to the previous zoom level */ public void zoomOut() { // find the next valid zoom level Double newScale = zoomLevels.lower(scale); @@ -301,17 +292,18 @@ public ZoomablePane getCanvas() { * @param graphics2D Graphics2D object used for drawing */ protected void draw(Graphics2D graphics2D) { - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + graphics2D.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.setRenderingHint( + RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); canvas.paint(graphics2D); } /** * Scroll up or down on the ScrollView * - * @param mag The magnitude for scroll up - * positive is scroll up, negative is scroll down, - * recommend to use getWheelRotation() as the mag + * @param mag The magnitude for scroll up positive is scroll up, negative is scroll down, + * recommend to use getWheelRotation() as the mag */ public void scroll(int mag) { Point point = super.viewport.getViewPosition(); diff --git a/src/main/java/edu/rpi/legup/ui/ToolbarName.java b/src/main/java/edu/rpi/legup/ui/ToolbarName.java index 3df635b05..ba02ebd2e 100644 --- a/src/main/java/edu/rpi/legup/ui/ToolbarName.java +++ b/src/main/java/edu/rpi/legup/ui/ToolbarName.java @@ -1,7 +1,11 @@ package edu.rpi.legup.ui; public enum ToolbarName { - HINT, CHECK, SUBMIT, DIRECTIONS, CHECK_ALL; + HINT, + CHECK, + SUBMIT, + DIRECTIONS, + CHECK_ALL; /** * Gets the String representation of the ToolbarName enum @@ -15,12 +19,12 @@ public String toString() { str = str.substring(0, 1).toUpperCase() + str.substring(1); for (int i = 0; i < str.length(); i++) { if (str.charAt(i) == ' ') { - str = str.substring(0, i + 1) + - str.substring(i + 1, i + 2).toUpperCase() + - str.substring(i + 2); + str = + str.substring(0, i + 1) + + str.substring(i + 1, i + 2).toUpperCase() + + str.substring(i + 2); } } return str; } - } diff --git a/src/main/java/edu/rpi/legup/ui/WrapLayout.java b/src/main/java/edu/rpi/legup/ui/WrapLayout.java index c37eb02f4..1e25b36d2 100644 --- a/src/main/java/edu/rpi/legup/ui/WrapLayout.java +++ b/src/main/java/edu/rpi/legup/ui/WrapLayout.java @@ -1,29 +1,25 @@ package edu.rpi.legup.ui; +import java.awt.*; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; -import java.awt.*; -/** - * FlowLayout subclass that fully supports wrapping of components. - */ +/** FlowLayout subclass that fully supports wrapping of components. */ public class WrapLayout extends FlowLayout { private Dimension preferredLayoutSize; /** - * Constructs a new WrapLayout with a left - * alignment and a default 5-unit horizontal and vertical gap. + * Constructs a new WrapLayout with a left alignment and a default 5-unit + * horizontal and vertical gap. */ public WrapLayout() { super(LEFT); } /** - * Constructs a new FlowLayout with the specified - * alignment and a default 5-unit horizontal and vertical gap. - * The value of the alignment argument must be one of - * WrapLayout, WrapLayout, - * or WrapLayout. + * Constructs a new FlowLayout with the specified alignment and a default 5-unit + * horizontal and vertical gap. The value of the alignment argument must be one of + * WrapLayout, WrapLayout, or WrapLayout. * * @param align the alignment value */ @@ -32,28 +28,27 @@ public WrapLayout(int align) { } /** - * Creates a new flow layout manager with the indicated alignment - * and the indicated horizontal and vertical gaps. - *

- * The value of the alignment argument must be one of - * WrapLayout, WrapLayout, - * or WrapLayout. + * Creates a new flow layout manager with the indicated alignment and the indicated horizontal + * and vertical gaps. + * + *

The value of the alignment argument must be one of WrapLayout, + * WrapLayout + * , or WrapLayout. * * @param align the alignment value - * @param hgap the horizontal gap between components - * @param vgap the vertical gap between components + * @param hgap the horizontal gap between components + * @param vgap the vertical gap between components */ public WrapLayout(int align, int hgap, int vgap) { super(align, hgap, vgap); } /** - * Returns the preferred dimensions for this layout given the - * visible components in the specified target container. + * Returns the preferred dimensions for this layout given the visible components in the + * specified target container. * * @param target the component which needs to be laid out - * @return the preferred dimensions to lay out the - * subcomponents of the specified container + * @return the preferred dimensions to lay out the subcomponents of the specified container */ @Override public Dimension preferredLayoutSize(Container target) { @@ -61,12 +56,11 @@ public Dimension preferredLayoutSize(Container target) { } /** - * Returns the minimum dimensions needed to layout the visible - * components contained in the specified target container. + * Returns the minimum dimensions needed to layout the visible components contained in the + * specified target container. * * @param target the component which needs to be laid out - * @return the minimum dimensions to lay out the - * subcomponents of the specified container + * @return the minimum dimensions to lay out the subcomponents of the specified container */ @Override public Dimension minimumLayoutSize(Container target) { @@ -76,10 +70,9 @@ public Dimension minimumLayoutSize(Container target) { } /** - * Returns the minimum or preferred dimension needed to layout the target - * container. + * Returns the minimum or preferred dimension needed to layout the target container. * - * @param target target to get layout dimension for + * @param target target to get layout dimension for * @param preferred should preferred dimension be calculated * @return the dimension to layout the target container */ @@ -155,11 +148,11 @@ private Dimension layoutSize(Container target, boolean preferred) { } /** - * A new row has been completed. Use the dimensions of this row - * to update the preferred dimension for the container. + * A new row has been completed. Use the dimensions of this row to update the preferred + * dimension for the container. * - * @param dim update the width and height when appropriate - * @param rowWidth the width of the row to add + * @param dim update the width and height when appropriate + * @param rowWidth the width of the row to add * @param rowHeight the height of the row to add */ private void addRow(Dimension dim, int rowWidth, int rowHeight) { @@ -171,4 +164,4 @@ private void addRow(Dimension dim, int rowWidth, int rowHeight) { dim.height += rowHeight; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java index 1e423c346..aa5b65c4e 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java @@ -2,25 +2,24 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; - +import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPopupMenu; import javax.swing.JSlider; import javax.swing.SwingConstants; -import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; -import javax.swing.ImageIcon; - +import javax.swing.event.ChangeListener; public class ZoomWidget extends JLabel { private ScrollView parent; private PopupSlider palette = new PopupSlider(); - private MouseAdapter open = new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - palette.slider.setValue(parent.getZoom()); - palette.show(e.getComponent(), 0, 0); - } - }; + private MouseAdapter open = + new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + palette.slider.setValue(parent.getZoom()); + palette.show(e.getComponent(), 0, 0); + } + }; /** * ZoomWidget Constructor creates a zoom widget for a ScrollView object @@ -33,9 +32,7 @@ public ZoomWidget(ScrollView parent) { addMouseListener(open); } - /** - * - */ + /** */ private class PopupSlider extends JPopupMenu implements ChangeListener { private static final long serialVersionUID = 8225019381200459814L; diff --git a/src/main/java/edu/rpi/legup/ui/ZoomablePane.java b/src/main/java/edu/rpi/legup/ui/ZoomablePane.java index 86cfd866f..934d31c53 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomablePane.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomablePane.java @@ -1,9 +1,9 @@ package edu.rpi.legup.ui; -import javax.swing.*; +import java.awt.AWTEvent; import java.awt.Graphics; import java.awt.Graphics2D; -import java.awt.AWTEvent; +import javax.swing.*; public class ZoomablePane extends JLayeredPane { private ScrollView viewer; diff --git a/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java b/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java index 602672a9e..ca03f1e25 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java @@ -8,7 +8,6 @@ import edu.rpi.legup.model.observer.IBoardListener; import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.ui.ScrollView; - import java.awt.*; import java.util.ArrayList; @@ -19,9 +18,9 @@ public abstract class BoardView extends ScrollView implements IBoardListener { protected ElementController elementController; protected ElementSelection selection; - /** - * BoardView Constructor creates a view for the board object using the controller handle the ui events + * BoardView Constructor creates a view for the board object using the controller handle the ui + * events * * @param boardController controller that handles the ui events * @param elementController controller that handles the ui events @@ -40,9 +39,7 @@ public BoardView(BoardController boardController, ElementController elementContr addKeyListener(elementController); } - /** - * Initializes the initial dimension of the viewport for the BoardView - */ + /** Initializes the initial dimension of the viewport for the BoardView */ public abstract void initSize(); /** @@ -70,13 +67,17 @@ public void setElementViews(ArrayList elements) { } /** - * Gets the ElementView from the location specified or null if one does not exists at that location + * Gets the ElementView from the location specified or null if one does not exists at that + * location * * @param point location on the viewport * @return ElementView at the specified location */ public ElementView getElement(Point point) { - Point scaledPoint = new Point((int) Math.round(point.x / getScale()), (int) Math.round(point.y / getScale())); + Point scaledPoint = + new Point( + (int) Math.round(point.x / getScale()), + (int) Math.round(point.y / getScale())); for (ElementView element : elementViews) { if (element.isWithinBounds(scaledPoint)) { return element; @@ -114,10 +115,10 @@ public void setBoard(Board board) { if (board instanceof CaseBoard) { setCasePickable(); - } - else { + } else { for (ElementView elementView : elementViews) { - elementView.setPuzzleElement(board.getPuzzleElement(elementView.getPuzzleElement())); + elementView.setPuzzleElement( + board.getPuzzleElement(elementView.getPuzzleElement())); elementView.setShowCasePicker(false); } } @@ -129,7 +130,8 @@ protected void setCasePickable() { Board baseBoard = caseBoard.getBaseBoard(); for (ElementView elementView : elementViews) { - PuzzleElement puzzleElement = baseBoard.getPuzzleElement(elementView.getPuzzleElement()); + PuzzleElement puzzleElement = + baseBoard.getPuzzleElement(elementView.getPuzzleElement()); elementView.setPuzzleElement(puzzleElement); elementView.setShowCasePicker(true); elementView.setCaseRulePickable(caseBoard.isPickable(puzzleElement, null)); diff --git a/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java b/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java index 8257fd47c..cedfa08fe 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java @@ -1,10 +1,9 @@ package edu.rpi.legup.ui.boardview; import edu.rpi.legup.controller.ElementController; - +import java.awt.*; import javax.swing.*; import javax.swing.border.BevelBorder; -import java.awt.*; public class DataSelectionView extends JPopupMenu { diff --git a/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java b/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java index d8f88f979..8e6f2cb18 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java @@ -26,8 +26,7 @@ public void toggleSelection(ElementView elementView) { if (selection.contains(elementView)) { selection.remove(elementView); elementView.setSelected(false); - } - else { + } else { selection.add(elementView); elementView.setSelected(true); } diff --git a/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java b/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java index 008c1a5f5..83b2cb099 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java @@ -1,14 +1,13 @@ package edu.rpi.legup.ui.boardview; import edu.rpi.legup.model.gameboard.PuzzleElement; - -import javax.swing.*; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import javax.swing.*; public abstract class ElementView implements Shape { protected int index; @@ -47,8 +46,10 @@ public ElementView(PuzzleElement puzzleElement) { * @return true if the point is within the ElementView, false otherwise */ public boolean isWithinBounds(Point point) { - return point.x >= location.x && point.x <= location.x + size.width && - point.y >= location.y && point.y <= location.y + size.height; + return point.x >= location.x + && point.x <= location.x + size.width + && point.y >= location.y + && point.y <= location.y + size.height; } /** @@ -74,7 +75,9 @@ public void draw(Graphics2D graphics2D) { public void drawElement(Graphics2D graphics2D) { graphics2D.setStroke(new BasicStroke(1)); - graphics2D.draw(new Rectangle2D.Double(location.x + 0.5f, location.y + 0.5f, size.width - 2, size.height - 2)); + graphics2D.draw( + new Rectangle2D.Double( + location.x + 0.5f, location.y + 0.5f, size.width - 2, size.height - 2)); graphics2D.setColor(Color.BLACK); FontMetrics metrics = graphics2D.getFontMetrics(graphics2D.getFont()); @@ -84,29 +87,34 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); } - public void drawGiven(Graphics2D graphics2D) { - - } + public void drawGiven(Graphics2D graphics2D) {} public void drawHover(Graphics2D graphics2D) { graphics2D.setColor(hoverColor); graphics2D.setStroke(new BasicStroke(2)); - graphics2D.draw(new Rectangle2D.Double(location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); + graphics2D.draw( + new Rectangle2D.Double( + location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); } public void drawModified(Graphics2D graphics2D) { graphics2D.setColor(puzzleElement.isValid() ? modifiedColor : invalidColor); graphics2D.setStroke(new BasicStroke(2)); - graphics2D.draw(new Rectangle2D.Double(location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); + graphics2D.draw( + new Rectangle2D.Double( + location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); } public void drawCase(Graphics2D graphics2D) { graphics2D.setColor(caseColor); - graphics2D.fill(new Rectangle2D.Double(location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); + graphics2D.fill( + new Rectangle2D.Double( + location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); } public BufferedImage getImage() { - BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB); + BufferedImage image = + new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB); Graphics2D graphics2D = image.createGraphics(); drawElement(graphics2D); graphics2D.dispose(); @@ -194,9 +202,8 @@ public void setShowCasePicker(boolean showCasePicker) { } /** - * Gets the isCaseRulePickable field to determine if this ElementView - * should be highlighted in some way to indicate if it can be chosen by - * the CaseRule + * Gets the isCaseRulePickable field to determine if this ElementView should be highlighted in + * some way to indicate if it can be chosen by the CaseRule * * @return true if the ElementView can be chosen for the CaseRule, false otherwise */ @@ -205,11 +212,11 @@ public boolean isCaseRulePickable() { } /** - * Sets the isCaseRulePickable field to determine if this ElementView - * should be highlighted in some way to indicate if it can be chosen by - * the CaseRule + * Sets the isCaseRulePickable field to determine if this ElementView should be highlighted in + * some way to indicate if it can be chosen by the CaseRule * - * @param isCaseRulePickable true if the ElementView can be chosen for the CaseRule, false otherwise + * @param isCaseRulePickable true if the ElementView can be chosen for the CaseRule, false + * otherwise */ public void setCaseRulePickable(boolean isCaseRulePickable) { this.isCaseRulePickable = isCaseRulePickable; @@ -276,8 +283,10 @@ public JMenuItem getSelectionMenuItem() { @Override public boolean contains(double x, double y) { - return x >= location.x && x <= location.x + size.width && - y >= location.y && y <= location.y + size.height; + return x >= location.x + && x <= location.x + size.width + && y >= location.y + && y <= location.y + size.height; } @Override @@ -287,24 +296,32 @@ public boolean contains(Point2D point) { @Override public boolean intersects(double x, double y, double width, double height) { - return (x + width >= location.x && x <= location.x + size.width) || - (y + height >= location.y && y <= location.y + size.height); + return (x + width >= location.x && x <= location.x + size.width) + || (y + height >= location.y && y <= location.y + size.height); } @Override public boolean intersects(Rectangle2D rectangle2D) { - return intersects(rectangle2D.getX(), rectangle2D.getY(), rectangle2D.getWidth(), rectangle2D.getHeight()); + return intersects( + rectangle2D.getX(), + rectangle2D.getY(), + rectangle2D.getWidth(), + rectangle2D.getHeight()); } @Override public boolean contains(double x, double y, double width, double height) { - return (x + width >= location.x && x <= location.x + size.width) && - (y + height >= location.y && y <= location.y + size.height); + return (x + width >= location.x && x <= location.x + size.width) + && (y + height >= location.y && y <= location.y + size.height); } @Override public boolean contains(Rectangle2D rectangle2D) { - return contains(rectangle2D.getX(), rectangle2D.getY(), rectangle2D.getWidth(), rectangle2D.getHeight()); + return contains( + rectangle2D.getX(), + rectangle2D.getY(), + rectangle2D.getWidth(), + rectangle2D.getHeight()); } @Override @@ -314,7 +331,8 @@ public PathIterator getPathIterator(AffineTransform at) { @Override public PathIterator getPathIterator(AffineTransform at, double flatness) { - return new Rectangle(location.x, location.y, size.width, size.height).getPathIterator(at, flatness); + return new Rectangle(location.x, location.y, size.width, size.height) + .getPathIterator(at, flatness); } @Override diff --git a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java index 5fdacc6b0..c40303192 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.controller.ElementController; - import java.awt.Color; import java.awt.Dimension; @@ -11,13 +10,17 @@ public class GridBoardView extends BoardView { protected Dimension elementSize; /** - * GridBoardView Constructor creates a GridBoardView object using the controller handle the ui events + * GridBoardView Constructor creates a GridBoardView object using the controller handle the ui + * events * * @param boardController controller that handles the ui events * @param gridSize dimension of the grid * @param elementController controller that handles the ui events */ - public GridBoardView(BoardController boardController, ElementController elementController, Dimension gridSize) { + public GridBoardView( + BoardController boardController, + ElementController elementController, + Dimension gridSize) { this(boardController, elementController); this.gridSize = gridSize; this.elementSize = new Dimension(30, 30); @@ -25,7 +28,8 @@ public GridBoardView(BoardController boardController, ElementController elementC } /** - * GridBoardView Constructor creates a GridBoardView object using the controller handle the ui events + * GridBoardView Constructor creates a GridBoardView object using the controller handle the ui + * events * * @param boardController controller that handles the ui events */ @@ -35,8 +39,7 @@ private GridBoardView(BoardController boardController, ElementController element } /** - * Gets the GridElementView from the puzzleElement index or - * null if out of bounds + * Gets the GridElementView from the puzzleElement index or null if out of bounds * * @param index index of the ElementView * @return GridElementView at the specified index @@ -55,9 +58,7 @@ public GridElementView getElement(int xIndex, int yIndex) { return null; } - /** - * Initializes the initial dimension of the viewport for the GridBoardView - */ + /** Initializes the initial dimension of the viewport for the GridBoardView */ @Override public void initSize() { setSize(getProperSize()); @@ -83,6 +84,4 @@ public DataSelectionView getSelectionPopupMenu() { public Dimension getElementSize() { return this.elementSize; } - } - diff --git a/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java b/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java index 1ce5033c7..b2d3e31dd 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.ui.boardview; import edu.rpi.legup.model.gameboard.PuzzleElement; - import javax.swing.*; public class SelectionItemView extends JMenuItem { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/LegupLookAndFeel.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/LegupLookAndFeel.java index 84804e1a0..8469343f3 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/LegupLookAndFeel.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/LegupLookAndFeel.java @@ -5,20 +5,17 @@ import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialFonts; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialImages; - +import java.awt.*; import javax.swing.*; import javax.swing.plaf.basic.BasicLookAndFeel; -import java.awt.*; public class LegupLookAndFeel extends BasicLookAndFeel { /** - * Return a short string that identifies this look and feel, e.g. - * "CDE/Motif". This string should be appropriate for a menu item. - * Distinct look and feels should have different names, e.g. - * a subclass of MotifLookAndFeel that changes the way a few components - * are rendered should be called "CDE/Motif My Way"; something - * that would be useful to a user trying to select a L&F from a list - * of names. + * Return a short string that identifies this look and feel, e.g. "CDE/Motif". This string + * should be appropriate for a menu item. Distinct look and feels should have different names, + * e.g. a subclass of MotifLookAndFeel that changes the way a few components are rendered should + * be called "CDE/Motif My Way"; something that would be useful to a user trying to select a + * L&F from a list of names. * * @return short identifier for the look and feel */ @@ -28,13 +25,11 @@ public String getName() { } /** - * Return a string that identifies this look and feel. This string - * will be used by applications/services that want to recognize - * well known look and feel implementations. Presently - * the well known names are "Motif", "Windows", "Mac", "Metal". Note - * that a LookAndFeel derived from a well known superclass - * that doesn't make any fundamental changes to the look or feel - * shouldn't override this method. + * Return a string that identifies this look and feel. This string will be used by + * applications/services that want to recognize well known look and feel implementations. + * Presently the well known names are "Motif", "Windows", "Mac", "Metal". Note that a + * LookAndFeel derived from a well known superclass that doesn't make any fundamental changes to + * the look or feel shouldn't override this method. * * @return identifier for the look and feel */ @@ -44,9 +39,9 @@ public String getID() { } /** - * Return a one line description of this look and feel implementation, - * e.g. "The CDE/Motif Look and Feel". This string is intended for - * the user, e.g. in the title of a window or in a ToolTip message. + * Return a one line description of this look and feel implementation, e.g. "The CDE/Motif Look + * and Feel". This string is intended for the user, e.g. in the title of a window or in a + * ToolTip message. * * @return short description for the look and feel */ @@ -56,14 +51,11 @@ public String getDescription() { } /** - * If the underlying platform has a "native" look and feel, and - * this is an implementation of it, return {@code true}. For - * example, when the underlying platform is Solaris running CDE - * a CDE/Motif look and feel implementation would return {@code - * true}. + * If the underlying platform has a "native" look and feel, and this is an implementation of it, + * return {@code true}. For example, when the underlying platform is Solaris running CDE a + * CDE/Motif look and feel implementation would return {@code true}. * - * @return {@code true} if this look and feel represents the underlying - * platform look and feel + * @return {@code true} if this look and feel represents the underlying platform look and feel */ @Override public boolean isNativeLookAndFeel() { @@ -71,10 +63,9 @@ public boolean isNativeLookAndFeel() { } /** - * Return {@code true} if the underlying platform supports and or permits - * this look and feel. This method returns {@code false} if the look - * and feel depends on special resources or legal agreements that - * aren't defined for the current platform. + * Return {@code true} if the underlying platform supports and or permits this look and feel. + * This method returns {@code false} if the look and feel depends on special resources or legal + * agreements that aren't defined for the current platform. * * @return {@code true} if this is a supported look and feel * @see UIManager#setLookAndFeel @@ -85,16 +76,12 @@ public boolean isSupportedLookAndFeel() { } /** - * Initializes the look and feel. While this method is public, - * it should only be invoked by the {@code UIManager} when a - * look and feel is installed as the current look and feel. This - * method is invoked before the {@code UIManager} invokes - * {@code getDefaults}. This method is intended to perform any - * initialization for the look and feel. Subclasses - * should do any one-time setup they need here, rather than - * in a static initializer, because look and feel class objects - * may be loaded just to discover that {@code isSupportedLookAndFeel()} - * returns {@code false}. + * Initializes the look and feel. While this method is public, it should only be invoked by the + * {@code UIManager} when a look and feel is installed as the current look and feel. This method + * is invoked before the {@code UIManager} invokes {@code getDefaults}. This method is intended + * to perform any initialization for the look and feel. Subclasses should do any one-time setup + * they need here, rather than in a static initializer, because look and feel class objects may + * be loaded just to discover that {@code isSupportedLookAndFeel()} returns {@code false}. * * @see #uninitialize * @see UIManager#setLookAndFeel @@ -105,15 +92,12 @@ public void initialize() { } /** - * Populates {@code table} with mappings from {@code uiClassID} to the - * fully qualified name of the ui class. The value for a - * particular {@code uiClassID} is {@code - * "javax.swing.plaf.basic.Basic + uiClassID"}. For example, the - * value for the {@code uiClassID} {@code TreeUI} is {@code - * "javax.swing.plaf.basic.BasicTreeUI"}. + * Populates {@code table} with mappings from {@code uiClassID} to the fully qualified name of + * the ui class. The value for a particular {@code uiClassID} is {@code + * "javax.swing.plaf.basic.Basic + uiClassID"}. For example, the value for the {@code uiClassID} + * {@code TreeUI} is {@code "javax.swing.plaf.basic.BasicTreeUI"}. * - * @param table the {@code UIDefaults} instance the entries are - * added to + * @param table the {@code UIDefaults} instance the entries are added to * @throws NullPointerException if {@code table} is {@code null} * @see LookAndFeel * @see #getDefaults @@ -132,7 +116,7 @@ protected void initClassDefaults(UIDefaults table) { table.put("PanelUI", MaterialPanelUI.class.getCanonicalName()); table.put("LabelUI", MaterialLabelUI.class.getCanonicalName()); table.put("MenuItemUI", MaterialMenuItemUI.class.getCanonicalName()); -// table.put ("MenuBarUI", .class.getCanonicalName()); + // table.put ("MenuBarUI", .class.getCanonicalName()); table.put("MenuUI", MaterialMenuUI.class.getCanonicalName()); table.put("CheckBoxUI", MaterialCheckBoxUI.class.getCanonicalName()); table.put("RadioButtonUI", MaterialRadioButtonUI.class.getCanonicalName()); @@ -152,7 +136,7 @@ protected void initClassDefaults(UIDefaults table) { table.put("FileChooserUI", MaterialFileChooserUI.class.getCanonicalName()); table.put("ToolTipUI", MaterialToolTipUI.class.getCanonicalName()); table.put("SplitPaneUI", MaterialSplitPaneUI.class.getCanonicalName()); -// table.put ("ColorChooserUI", ); + // table.put ("ColorChooserUI", ); } @Override @@ -175,7 +159,11 @@ protected void initComponentDefaults(UIDefaults table) { table.put("ComboBox.font", MaterialFonts.REGULAR); table.put("ComboBox.background", Color.WHITE); table.put("ComboBox.foreground", Color.BLACK); - table.put("ComboBox.border", BorderFactory.createCompoundBorder(MaterialBorders.LIGHT_LINE_BORDER, BorderFactory.createEmptyBorder(0, 5, 0, 0))); + table.put( + "ComboBox.border", + BorderFactory.createCompoundBorder( + MaterialBorders.LIGHT_LINE_BORDER, + BorderFactory.createEmptyBorder(0, 5, 0, 0))); table.put("ComboBox.buttonBackground", MaterialColors.GRAY_300); table.put("ComboBox.selectionBackground", Color.WHITE); table.put("ComboBox.selectionForeground", Color.BLACK); @@ -251,7 +239,9 @@ protected void initComponentDefaults(UIDefaults table) { table.put("Slider.background", Color.WHITE); table.put("Slider.foreground", MaterialColors.GRAY_700); table.put("Slider.trackColor", Color.BLACK); -// table.put ("Slider.border", BorderFactory.createCompoundBorder(MaterialBorders.LIGHT_LINE_BORDER, BorderFactory.createEmptyBorder (5, 5, 5, 5))); + // table.put ("Slider.border", + // BorderFactory.createCompoundBorder(MaterialBorders.LIGHT_LINE_BORDER, + // BorderFactory.createEmptyBorder (5, 5, 5, 5))); table.put("SplitPane.border", MaterialBorders.LIGHT_LINE_BORDER); table.put("SplitPane.background", Color.WHITE); @@ -275,7 +265,11 @@ protected void initComponentDefaults(UIDefaults table) { table.put("Table.gridColor", MaterialColors.GRAY_200); table.put("TableHeader.background", MaterialColors.GRAY_200); table.put("TableHeader.font", MaterialFonts.BOLD); - table.put("TableHeader.cellBorder", BorderFactory.createCompoundBorder(MaterialBorders.LIGHT_LINE_BORDER, BorderFactory.createEmptyBorder(5, 5, 5, 5))); + table.put( + "TableHeader.cellBorder", + BorderFactory.createCompoundBorder( + MaterialBorders.LIGHT_LINE_BORDER, + BorderFactory.createEmptyBorder(5, 5, 5, 5))); table.put("TextArea.background", MaterialColors.GRAY_200); table.put("TextArea.border", BorderFactory.createEmptyBorder()); @@ -306,21 +300,27 @@ protected void initComponentDefaults(UIDefaults table) { table.put("RadioButtonMenuItem.foreground", Color.BLACK); table.put("RadioButtonMenuItem.selectionForeground", Color.BLACK); - //If it changes the background of the menuitem it must change this too, irrespective of its setting + // If it changes the background of the menuitem it must change this too, irrespective of its + // setting table.put("RadioButtonMenuItem.background", UIManager.getColor("MenuItem.background")); table.put("RadioButtonMenuItem.selectionBackground", MaterialColors.GRAY_200); table.put("RadioButtonMenuItem.border", BorderFactory.createEmptyBorder(5, 5, 5, 5)); table.put("RadioButtonMenuItem.checkIcon", new ImageIcon(MaterialImages.RADIO_BUTTON_OFF)); - table.put("RadioButtonMenuItem.selectedCheckIcon", new ImageIcon(MaterialImages.RADIO_BUTTON_ON)); + table.put( + "RadioButtonMenuItem.selectedCheckIcon", + new ImageIcon(MaterialImages.RADIO_BUTTON_ON)); - //If it changes the background of the menuitem it must change this too, irrespective of its setting + // If it changes the background of the menuitem it must change this too, irrespective of its + // setting table.put("CheckBoxMenuItem.background", UIManager.getColor("MenuItem.background")); table.put("CheckBoxMenuItem.selectionBackground", MaterialColors.GRAY_200); table.put("CheckBoxMenuItem.foreground", Color.BLACK); table.put("CheckBoxMenuItem.selectionForeground", Color.BLACK); table.put("CheckBoxMenuItem.border", BorderFactory.createEmptyBorder(5, 5, 5, 5)); table.put("CheckBoxMenuItem.checkIcon", new ImageIcon(MaterialImages.UNCHECKED_BOX)); - table.put("CheckBoxMenuItem.selectedCheckIcon", new ImageIcon(MaterialImages.PAINTED_CHECKED_BOX)); + table.put( + "CheckBoxMenuItem.selectedCheckIcon", + new ImageIcon(MaterialImages.PAINTED_CHECKED_BOX)); table.put("TextPane.border", MaterialBorders.LIGHT_LINE_BORDER); table.put("TextPane.background", MaterialColors.GRAY_50); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUIMovement.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUIMovement.java index fe82238ab..db7d604a0 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUIMovement.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUIMovement.java @@ -1,12 +1,11 @@ package edu.rpi.legup.ui.lookandfeel.animation; -import javax.swing.JComponent; import java.awt.Color; +import javax.swing.JComponent; public class MaterialUIMovement { - private MaterialUIMovement() { - } + private MaterialUIMovement() {} public static void add(JComponent c, Color fadeTo, int steps, int interval) { new MaterialUITimer(c, fadeTo, steps, interval); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUITimer.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUITimer.java index b5b3bbd76..f989185c7 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUITimer.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/animation/MaterialUITimer.java @@ -1,12 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.animation; -import javax.swing.JComponent; -import javax.swing.Timer; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; +import javax.swing.JComponent; +import javax.swing.Timer; public class MaterialUITimer implements MouseListener, ActionListener { @@ -72,14 +72,10 @@ public void mousePressed(MouseEvent me) { } @Override - public void mouseReleased(MouseEvent me) { - - } + public void mouseReleased(MouseEvent me) {} @Override - public void mouseClicked(MouseEvent me) { - - } + public void mouseClicked(MouseEvent me) {} @Override public void mouseExited(MouseEvent me) { @@ -100,8 +96,7 @@ public void actionPerformed(ActionEvent ae) { if (forward) { component.setBackground(nextColor()); ++alpha; - } - else { + } else { component.setBackground(previousColor()); --alpha; } @@ -110,4 +105,4 @@ public void actionPerformed(ActionEvent ae) { timer.stop(); } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialButtonUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialButtonUI.java index fe23536d3..debe6e215 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialButtonUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialButtonUI.java @@ -3,13 +3,12 @@ import edu.rpi.legup.ui.lookandfeel.animation.MaterialUIMovement; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.AbstractButton; import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicButtonUI; -import java.awt.Graphics; public class MaterialButtonUI extends BasicButtonUI { @@ -42,4 +41,4 @@ private void paintBackground(Graphics g, JComponent c) { g.setColor(c.getBackground()); g.fillRoundRect(0, 0, c.getWidth(), c.getHeight(), 7, 7); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxMenuItemUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxMenuItemUI.java index e5068a8ed..8f0df3604 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxMenuItemUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxMenuItemUI.java @@ -1,20 +1,18 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Color; +import java.awt.Graphics; import javax.swing.Icon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; -import java.awt.Color; -import java.awt.Graphics; /** * @author https://github.com/vincenzopalazzo */ - public class MaterialCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { public static ComponentUI createUI(JComponent c) { @@ -32,12 +30,33 @@ public void paint(Graphics g, JComponent c) { } @Override - protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, int defaultTextIconGap) { + protected void paintMenuItem( + Graphics g, + JComponent c, + Icon checkIcon, + Icon arrowIcon, + Color background, + Color foreground, + int defaultTextIconGap) { JCheckBoxMenuItem checkBoxMenuItem = (JCheckBoxMenuItem) c; if (checkBoxMenuItem.isSelected()) { - super.paintMenuItem(MaterialDrawingUtils.getAliasedGraphics(g), checkBoxMenuItem, UIManager.getIcon("CheckBoxMenuItem.selectedCheckIcon"), arrowIcon, background, foreground, defaultTextIconGap); + super.paintMenuItem( + MaterialDrawingUtils.getAliasedGraphics(g), + checkBoxMenuItem, + UIManager.getIcon("CheckBoxMenuItem.selectedCheckIcon"), + arrowIcon, + background, + foreground, + defaultTextIconGap); return; } - super.paintMenuItem(MaterialDrawingUtils.getAliasedGraphics(g), checkBoxMenuItem, UIManager.getIcon("CheckBoxMenuItem.checkIcon"), arrowIcon, background, foreground, defaultTextIconGap); + super.paintMenuItem( + MaterialDrawingUtils.getAliasedGraphics(g), + checkBoxMenuItem, + UIManager.getIcon("CheckBoxMenuItem.checkIcon"), + arrowIcon, + background, + foreground, + defaultTextIconGap); } } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxUI.java index a85e9079e..4a9725d52 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialCheckBoxUI.java @@ -1,15 +1,14 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicCheckBoxUI; -import java.awt.Graphics; -//TODO cambio colore icone combo box +// TODO cambio colore icone combo box public class MaterialCheckBoxUI extends BasicCheckBoxUI { public static ComponentUI createUI(JComponent c) { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxRenderer.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxRenderer.java index d6df89d44..73573d8f8 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxRenderer.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxRenderer.java @@ -1,23 +1,28 @@ package edu.rpi.legup.ui.lookandfeel.components; +import java.awt.Component; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicComboBoxRenderer; -import java.awt.Component; public class MaterialComboBoxRenderer extends BasicComboBoxRenderer { @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - JComponent component = (JComponent) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + public Component getListCellRendererComponent( + JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JComponent component = + (JComponent) + super.getListCellRendererComponent( + list, value, index, isSelected, cellHasFocus); component.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); component.setForeground(UIManager.getColor("ComboBox.foreground")); - component.setBackground(isSelected || cellHasFocus ? - UIManager.getColor("ComboBox.selectedInDropDownBackground") : - UIManager.getColor("ComboBox.background")); + component.setBackground( + isSelected || cellHasFocus + ? UIManager.getColor("ComboBox.selectedInDropDownBackground") + : UIManager.getColor("ComboBox.background")); return component; } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxUI.java index f9d6fa090..8cd52995b 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialComboBoxUI.java @@ -1,7 +1,7 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; @@ -12,7 +12,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicComboBoxUI; -import java.awt.Graphics; public class MaterialComboBoxUI extends BasicComboBoxUI { @@ -39,8 +38,7 @@ protected JButton createArrowButton() { JButton button; if (icon != null) { button = new JButton(icon); - } - else { + } else { button = new BasicArrowButton(SwingConstants.SOUTH); } button.setFocusPainted(false); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialEditorPaneUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialEditorPaneUI.java index 92d6770e0..786f289bb 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialEditorPaneUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialEditorPaneUI.java @@ -7,7 +7,6 @@ /** * @author https://github.com/vincenzopalazzo */ - public class MaterialEditorPaneUI extends BasicEditorPaneUI { public static ComponentUI createUI(JComponent c) { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialFileChooserUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialFileChooserUI.java index 9a4ffc9d4..b8046c569 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialFileChooserUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialFileChooserUI.java @@ -2,13 +2,12 @@ import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialImages; - +import java.awt.Graphics; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.metal.MetalFileChooserUI; -import java.awt.Graphics; public class MaterialFileChooserUI extends MetalFileChooserUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialLabelUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialLabelUI.java index e06509a82..e19fc1fed 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialLabelUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialLabelUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicLabelUI; -import java.awt.Graphics; public class MaterialLabelUI extends BasicLabelUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuBarUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuBarUI.java index 8d5625559..011d61862 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuBarUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuBarUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JMenuBar; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuBarUI; -import java.awt.Graphics; public class MaterialMenuBarUI extends BasicMenuBarUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuItemUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuItemUI.java index d7a23d1a3..a8c572b62 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuItemUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuItemUI.java @@ -1,14 +1,13 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuItemUI; -import java.awt.Graphics; public class MaterialMenuItemUI extends BasicMenuItemUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuUI.java index 7461cbea0..16c1b9270 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialMenuUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuUI; -import java.awt.Graphics; public class MaterialMenuUI extends BasicMenuUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPanelUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPanelUI.java index cf786be3f..ae7a9be6b 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPanelUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPanelUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicPanelUI; -import java.awt.Graphics; public class MaterialPanelUI extends BasicPanelUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPasswordFieldUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPasswordFieldUI.java index 61aa84fc7..d4778402b 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPasswordFieldUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPasswordFieldUI.java @@ -2,18 +2,6 @@ import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.JComponent; -import javax.swing.JPasswordField; -import javax.swing.KeyStroke; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicPasswordFieldUI; -import javax.swing.text.Element; -import javax.swing.text.PasswordView; -import javax.swing.text.View; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; @@ -26,8 +14,20 @@ import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPasswordField; +import javax.swing.KeyStroke; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicPasswordFieldUI; +import javax.swing.text.Element; +import javax.swing.text.PasswordView; +import javax.swing.text.View; -public class MaterialPasswordFieldUI extends BasicPasswordFieldUI implements FocusListener, PropertyChangeListener { +public class MaterialPasswordFieldUI extends BasicPasswordFieldUI + implements FocusListener, PropertyChangeListener { private Color focusedBackground; private Color unfocusedBackground; @@ -64,47 +64,65 @@ protected void installListeners() { protected void installKeyboardActions() { super.installKeyboardActions(); - Action selectAll = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - getComponent().selectAll(); - } - }; - - Action delete = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - if (getComponent().getSelectedText() == null) { - int pos = getComponent().getCaretPosition() - 1; - - if (pos >= 0) { - getComponent().select(pos, pos + 1); - getComponent().replaceSelection(""); + Action selectAll = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + getComponent().selectAll(); } - } - else { - getComponent().replaceSelection(""); - } - } - }; - - Action left = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - getComponent().setCaretPosition(Math.max(0, getComponent().getCaretPosition() - 1)); - } - }; - - Action right = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - getComponent().setCaretPosition(Math.min(getComponent().getText().length(), getComponent().getCaretPosition() + 1)); - } - }; - - // note getMenuShortcutKeyMask() is deprecated in Java 10 - change to getMenuShortcutKeyMaskEx() - getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "selectAll"); - getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "delete"); + }; + + Action delete = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (getComponent().getSelectedText() == null) { + int pos = getComponent().getCaretPosition() - 1; + + if (pos >= 0) { + getComponent().select(pos, pos + 1); + getComponent().replaceSelection(""); + } + } else { + getComponent().replaceSelection(""); + } + } + }; + + Action left = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + getComponent() + .setCaretPosition( + Math.max(0, getComponent().getCaretPosition() - 1)); + } + }; + + Action right = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + getComponent() + .setCaretPosition( + Math.min( + getComponent().getText().length(), + getComponent().getCaretPosition() + 1)); + } + }; + + // note getMenuShortcutKeyMask() is deprecated in Java 10 - change to + // getMenuShortcutKeyMaskEx() + getComponent() + .getInputMap() + .put( + KeyStroke.getKeyStroke( + KeyEvent.VK_A, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + "selectAll"); + getComponent() + .getInputMap() + .put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "delete"); getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left"); getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right"); @@ -122,15 +140,17 @@ public void paintSafely(Graphics g) { if (getComponent().hasFocus()) { c.setBackground(focusedBackground); c.setSelectionColor(focusedSelectionBackground); - } - else { + } else { c.setBackground(unfocusedBackground); c.setSelectionColor(unfocusedSelectionBackground); } int x = getComponent().getInsets().left; int y = getComponent().getInsets().top; - int w = getComponent().getWidth() - getComponent().getInsets().left - getComponent().getInsets().right; + int w = + getComponent().getWidth() + - getComponent().getInsets().left + - getComponent().getInsets().right; g.setColor(c.getBackground()); g.fillRect(x, c.getHeight() - y, w, 2); @@ -160,7 +180,8 @@ public void propertyChange(PropertyChangeEvent pce) { if (!newColor.equals(focusedBackground) && !newColor.equals(unfocusedBackground)) { this.focusedBackground = (Color) pce.getNewValue(); - this.focusedSelectionBackground = MaterialColors.bleach(this.focusedBackground, 0.3f); + this.focusedSelectionBackground = + MaterialColors.bleach(this.focusedBackground, 0.3f); } } } @@ -176,7 +197,9 @@ private MaterialPasswordView(Element elem) { super(elem); } - // depreciated in Java 9 and above - replace method with float drawEchoCharacter(Graphics2D g, float x, float y, char c) + // depreciated in Java 9 and above - replace method with float drawEchoCharacter(Graphics2D + // g, + // float x, float y, char c) @Override protected int drawEchoCharacter(Graphics g, int x, int y, char c) { Graphics2D g2 = (Graphics2D) g.create(); @@ -192,4 +215,4 @@ protected int drawEchoCharacter(Graphics g, int x, int y, char c) { return x + fm.charWidth(c); } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPopupMenuUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPopupMenuUI.java index 3fdb79961..d7baaf5df 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPopupMenuUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialPopupMenuUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JPopupMenu; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicPopupMenuUI; -import java.awt.Graphics; public class MaterialPopupMenuUI extends BasicPopupMenuUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialProgressBarUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialProgressBarUI.java index 111b7ef92..7f91a779f 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialProgressBarUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialProgressBarUI.java @@ -3,17 +3,15 @@ import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialBorders; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JProgressBar; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicProgressBarUI; -import java.awt.Graphics; /** * @author https://github.com/vincenzopalazzo */ - public class MaterialProgressBarUI extends BasicProgressBarUI { public static ComponentUI createUI(JComponent c) { @@ -34,5 +32,4 @@ public void installUI(JComponent c) { public void paint(Graphics g, JComponent c) { super.paint(MaterialDrawingUtils.getAliasedGraphics(g), c); } - } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonMenuItemUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonMenuItemUI.java index d08e52f05..69a9cae53 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonMenuItemUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonMenuItemUI.java @@ -1,20 +1,18 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Color; +import java.awt.Graphics; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JRadioButtonMenuItem; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; -import java.awt.Color; -import java.awt.Graphics; /** * @author https://github.com/vincenzopalazzo */ - public class MaterialRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { public static ComponentUI createUI(JComponent c) { @@ -25,7 +23,7 @@ public static ComponentUI createUI(JComponent c) { public void installUI(JComponent c) { super.installUI(c); JRadioButtonMenuItem j = (JRadioButtonMenuItem) c; - //j.setBackground(MaterialColors.WHITE); + // j.setBackground(MaterialColors.WHITE); j.setBorder(UIManager.getBorder("MenuItem.border")); } @@ -35,12 +33,33 @@ public void paint(Graphics g, JComponent c) { } @Override - protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, Color foreground, int defaultTextIconGap) { + protected void paintMenuItem( + Graphics g, + JComponent c, + Icon checkIcon, + Icon arrowIcon, + Color background, + Color foreground, + int defaultTextIconGap) { JRadioButtonMenuItem j = (JRadioButtonMenuItem) c; if (j.isSelected()) { - super.paintMenuItem(MaterialDrawingUtils.getAliasedGraphics(g), c, UIManager.getIcon("RadioButtonMenuItem.selectedCheckIcon"), arrowIcon, background, foreground, defaultTextIconGap); + super.paintMenuItem( + MaterialDrawingUtils.getAliasedGraphics(g), + c, + UIManager.getIcon("RadioButtonMenuItem.selectedCheckIcon"), + arrowIcon, + background, + foreground, + defaultTextIconGap); return; } - super.paintMenuItem(MaterialDrawingUtils.getAliasedGraphics(g), c, UIManager.getIcon("RadioButtonMenuItem.checkIcon"), arrowIcon, background, foreground, defaultTextIconGap); + super.paintMenuItem( + MaterialDrawingUtils.getAliasedGraphics(g), + c, + UIManager.getIcon("RadioButtonMenuItem.checkIcon"), + arrowIcon, + background, + foreground, + defaultTextIconGap); } } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonUI.java index 047270369..6418d4f17 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialRadioButtonUI.java @@ -1,18 +1,17 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JRadioButton; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRadioButtonUI; -import java.awt.Graphics; /* * Contributed by https://github.com/downToHell * */ -//TODO cambio colore del radio button. +// TODO cambio colore del radio button. public class MaterialRadioButtonUI extends BasicRadioButtonUI { public static ComponentUI createUI(JComponent c) { @@ -35,4 +34,4 @@ public void installUI(JComponent c) { public void paint(Graphics g, JComponent c) { super.paint(MaterialDrawingUtils.getAliasedGraphics(g), c); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialScrollBarUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialScrollBarUI.java index 4bf5bbb0f..164209b3f 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialScrollBarUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialScrollBarUI.java @@ -1,7 +1,7 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JScrollBar; @@ -9,7 +9,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicScrollBarUI; -import java.awt.Graphics; /* * Contributed by https://github.com/downToHell @@ -62,4 +61,4 @@ protected JButton createIncreaseButton(int orientation) { return button; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSeparatorUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSeparatorUI.java index 05cd5b60e..042b53d1b 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSeparatorUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSeparatorUI.java @@ -1,11 +1,10 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSeparatorUI; -import java.awt.Graphics; public class MaterialSeparatorUI extends BasicSeparatorUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSliderUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSliderUI.java index ab9d75484..7ac2fe350 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSliderUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSliderUI.java @@ -2,21 +2,21 @@ import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Dimension; +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JSlider; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSliderUI; -import java.awt.Dimension; -import java.awt.Graphics; -//TODO cambio grafica slider +// TODO cambio grafica slider public class MaterialSliderUI extends BasicSliderUI { private static final int NORMAL_THUMB_RADIUS = 6; private static final int DRAG_THUMB_RADIUS = 10; - private static final Dimension THUMB_SIZE = new Dimension(DRAG_THUMB_RADIUS * 2, DRAG_THUMB_RADIUS * 2); + private static final Dimension THUMB_SIZE = + new Dimension(DRAG_THUMB_RADIUS * 2, DRAG_THUMB_RADIUS * 2); public MaterialSliderUI(JSlider slider) { super(slider); @@ -81,33 +81,48 @@ public void paintTrack(Graphics g) { private Line getTrack(boolean loaded) { if (slider.getOrientation() == JSlider.HORIZONTAL) { - Line left = new Line(trackRect.x, thumbRect.y + thumbRect.height / 2, thumbRect.x + thumbRect.width / 2, thumbRect.y + thumbRect.height / 2); - Line right = new Line(thumbRect.x + thumbRect.width / 2, thumbRect.y + thumbRect.height / 2, trackRect.x + trackRect.width, thumbRect.y + thumbRect.height / 2); + Line left = + new Line( + trackRect.x, + thumbRect.y + thumbRect.height / 2, + thumbRect.x + thumbRect.width / 2, + thumbRect.y + thumbRect.height / 2); + Line right = + new Line( + thumbRect.x + thumbRect.width / 2, + thumbRect.y + thumbRect.height / 2, + trackRect.x + trackRect.width, + thumbRect.y + thumbRect.height / 2); if (loaded) { return slider.getInverted() ? right : left; - } - else { + } else { return slider.getInverted() ? left : right; } - } - else { - Line top = new Line(thumbRect.x + thumbRect.width / 2, trackRect.y, thumbRect.x + thumbRect.width / 2, thumbRect.y + thumbRect.height / 2); - Line bottom = new Line(thumbRect.x + thumbRect.width / 2, thumbRect.y + thumbRect.height / 2, thumbRect.x + thumbRect.width / 2, trackRect.y + trackRect.height); + } else { + Line top = + new Line( + thumbRect.x + thumbRect.width / 2, + trackRect.y, + thumbRect.x + thumbRect.width / 2, + thumbRect.y + thumbRect.height / 2); + Line bottom = + new Line( + thumbRect.x + thumbRect.width / 2, + thumbRect.y + thumbRect.height / 2, + thumbRect.x + thumbRect.width / 2, + trackRect.y + trackRect.height); if (loaded) { return slider.getInverted() ? top : bottom; - } - else { + } else { return slider.getInverted() ? bottom : top; } } } @Override - public void paintFocus(Graphics g) { - - } + public void paintFocus(Graphics g) {} @Override public void paint(Graphics g, JComponent c) { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSpinnerUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSpinnerUI.java index 0a74c9d99..ecfcbab17 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSpinnerUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSpinnerUI.java @@ -1,7 +1,8 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Component; +import java.awt.Graphics; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; @@ -11,8 +12,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicSpinnerUI; -import java.awt.Component; -import java.awt.Graphics; public class MaterialSpinnerUI extends BasicSpinnerUI { @@ -51,8 +50,7 @@ protected Component createNextButton() { JButton button; if (icon != null) { button = new JButton(icon); - } - else { + } else { button = new BasicArrowButton(SwingConstants.NORTH); } button.setFocusPainted(false); @@ -70,8 +68,7 @@ protected Component createPreviousButton() { JButton button; if (icon != null) { button = new JButton(icon); - } - else { + } else { button = new BasicArrowButton(SwingConstants.SOUTH); } button.setFocusPainted(false); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneDivider.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneDivider.java index 23d798644..0ec5db987 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneDivider.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneDivider.java @@ -6,18 +6,18 @@ public class MaterialSplitPaneDivider extends BasicSplitPaneDivider { public MaterialSplitPaneDivider(BasicSplitPaneUI ui) { super(ui); -// oneTouchSize = DefaultLookup.getInt(ui.getSplitPane(), ui, -// "SplitPane.oneTouchButtonSize", ONE_TOUCH_SIZE); -// oneTouchOffset = DefaultLookup.getInt(ui.getSplitPane(), ui, -// "SplitPane.oneTouchButtonOffset", ONE_TOUCH_OFFSET); -// centerOneTouchButtons = DefaultLookup.getBoolean(ui.getSplitPane(), -// ui, "SplitPane.centerOneTouchButtons", true); -// setLayout(new DividerLayout()); -// setBasicSplitPaneUI(ui); -// orientation = splitPane.getOrientation(); -// setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ? -// Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) : -// Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); -// setBackground(UIManager.getColor("SplitPane.background")); + // oneTouchSize = DefaultLookup.getInt(ui.getSplitPane(), ui, + // "SplitPane.oneTouchButtonSize", ONE_TOUCH_SIZE); + // oneTouchOffset = DefaultLookup.getInt(ui.getSplitPane(), ui, + // "SplitPane.oneTouchButtonOffset", ONE_TOUCH_OFFSET); + // centerOneTouchButtons = DefaultLookup.getBoolean(ui.getSplitPane(), + // ui, "SplitPane.centerOneTouchButtons", true); + // setLayout(new DividerLayout()); + // setBasicSplitPaneUI(ui); + // orientation = splitPane.getOrientation(); + // setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ? + // Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) : + // Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); + // setBackground(UIManager.getColor("SplitPane.background")); } } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneUI.java index 90bd79b5e..33cee1316 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialSplitPaneUI.java @@ -15,13 +15,13 @@ public void installUI(JComponent c) { splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); } -// /** -// * Creates the default divider. -// * -// * @return the default divider -// */ -// @Override -// public BasicSplitPaneDivider createDefaultDivider() { -// return new MaterialSplitPaneDivider(this); -// } + // /** + // * Creates the default divider. + // * + // * @return the default divider + // */ + // @Override + // public BasicSplitPaneDivider createDefaultDivider() { + // return new MaterialSplitPaneDivider(this); + // } } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTabbedPaneUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTabbedPaneUI.java index 0441cca35..878ed8479 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTabbedPaneUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTabbedPaneUI.java @@ -1,14 +1,13 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; +import java.awt.Rectangle; import javax.swing.JComponent; import javax.swing.JTabbedPane; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; -import java.awt.Graphics; -import java.awt.Rectangle; public class MaterialTabbedPaneUI extends BasicTabbedPaneUI { @@ -38,25 +37,60 @@ public void paint(Graphics g, JComponent c) { } @Override - protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { + protected void paintTabBackground( + Graphics g, + int tabPlacement, + int tabIndex, + int x, + int y, + int w, + int h, + boolean isSelected) { g.setColor(isSelected ? lightHighlight : tabPane.getBackground()); g.fillRect(x, y, w, h); } @Override - protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { + protected void paintTabBorder( + Graphics g, + int tabPlacement, + int tabIndex, + int x, + int y, + int w, + int h, + boolean isSelected) { g.setColor(UIManager.getColor("TabbedPane.borderHighlightColor")); g.drawRect(x, y, w, h); } @Override - protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) { + protected void paintFocusIndicator( + Graphics g, + int tabPlacement, + Rectangle[] rects, + int tabIndex, + Rectangle iconRect, + Rectangle textRect, + boolean isSelected) { // do nothing } @Override - protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect) { + protected void paintTab( + Graphics g, + int tabPlacement, + Rectangle[] rects, + int tabIndex, + Rectangle iconRect, + Rectangle textRect) { // for some reason tabs aren't painted properly by paint() - super.paintTab(MaterialDrawingUtils.getAliasedGraphics(g), tabPlacement, rects, tabIndex, iconRect, textRect); + super.paintTab( + MaterialDrawingUtils.getAliasedGraphics(g), + tabPlacement, + rects, + tabIndex, + iconRect, + textRect); } } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellEditor.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellEditor.java index 329102fbe..b4e07e95f 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellEditor.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellEditor.java @@ -1,9 +1,9 @@ package edu.rpi.legup.ui.lookandfeel.components; +import java.awt.Component; import javax.swing.DefaultCellEditor; import javax.swing.JTable; import javax.swing.JTextField; -import java.awt.Component; public class MaterialTableCellEditor extends DefaultCellEditor { @@ -19,8 +19,12 @@ private static JTextField init() { } @Override - public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex) { - JTextField textField = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, rowIndex, vColIndex); + public Component getTableCellEditorComponent( + JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex) { + JTextField textField = + (JTextField) + super.getTableCellEditorComponent( + table, value, isSelected, rowIndex, vColIndex); textField.setText(value.toString()); return textField; diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellRenderer.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellRenderer.java index 86c76e005..8fe0438e8 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellRenderer.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableCellRenderer.java @@ -1,17 +1,21 @@ package edu.rpi.legup.ui.lookandfeel.components; +import java.awt.Component; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.SwingConstants; import javax.swing.table.DefaultTableCellRenderer; -import java.awt.Component; public class MaterialTableCellRenderer extends DefaultTableCellRenderer { @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JComponent component = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + public Component getTableCellRendererComponent( + JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JComponent component = + (JComponent) + super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); // hides yellow selection highlight component.setBorder(BorderFactory.createEmptyBorder()); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderCellRenderer.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderCellRenderer.java index 2cdfb0fa9..01f4db191 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderCellRenderer.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderCellRenderer.java @@ -1,17 +1,21 @@ package edu.rpi.legup.ui.lookandfeel.components; +import java.awt.Component; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.table.DefaultTableCellRenderer; -import java.awt.Component; public class MaterialTableHeaderCellRenderer extends DefaultTableCellRenderer { @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JComponent component = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + public Component getTableCellRendererComponent( + JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + JComponent component = + (JComponent) + super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); component.setBorder(UIManager.getBorder("TableHeader.cellBorder")); component.setFont(UIManager.getFont("TableHeader.font")); component.setBackground(UIManager.getColor("TableHeader.background")); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderUI.java index 82144615b..7a1eadb92 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableHeaderUI.java @@ -1,12 +1,11 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTableHeaderUI; import javax.swing.table.JTableHeader; -import java.awt.Graphics; public class MaterialTableHeaderUI extends BasicTableHeaderUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableUI.java index dc0b82a34..91c68c973 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTableUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTableUI; -import java.awt.Graphics; public class MaterialTableUI extends BasicTableUI { @@ -32,8 +31,7 @@ public void installUI(JComponent c) { int rowHeight = UIManager.getInt("Table.rowHeight"); if (rowHeight > 0) { table.setRowHeight(rowHeight); - } - else { + } else { table.setRowHeight(table.getRowHeight() + 25); } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextFieldUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextFieldUI.java index 019c4b047..5cfd2fc88 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextFieldUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextFieldUI.java @@ -3,15 +3,6 @@ import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialFonts; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.JComponent; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicTextFieldUI; import java.awt.Color; import java.awt.Graphics; import java.awt.Toolkit; @@ -21,9 +12,17 @@ import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTextFieldUI; - -public class MaterialTextFieldUI extends BasicTextFieldUI implements FocusListener, PropertyChangeListener { +public class MaterialTextFieldUI extends BasicTextFieldUI + implements FocusListener, PropertyChangeListener { private Color focusedBackground; private Color unfocusedBackground; @@ -50,9 +49,10 @@ public void installUI(JComponent c) { JTextField textField = (JTextField) c; textField.setOpaque(false); - textField.setBorder(drawLine ? - BorderFactory.createEmptyBorder(5, 2, 10, 0) : - BorderFactory.createEmptyBorder(2, 2, 2, 2)); + textField.setBorder( + drawLine + ? BorderFactory.createEmptyBorder(5, 2, 10, 0) + : BorderFactory.createEmptyBorder(2, 2, 2, 2)); textField.setBackground(MaterialColors.LIGHT_BLUE_400); textField.setFont(MaterialFonts.REGULAR); @@ -75,54 +75,73 @@ protected void installListeners() { protected void installKeyboardActions() { super.installKeyboardActions(); - Action selectAll = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - getComponent().selectAll(); - } - }; - - Action delete = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - if (getComponent().getSelectedText() == null) { - int pos = getComponent().getCaretPosition() - 1; - - if (pos >= 0) { - getComponent().select(pos, pos + 1); - getComponent().replaceSelection(""); + Action selectAll = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + getComponent().selectAll(); } - } - else { - getComponent().replaceSelection(""); - } - } - }; - - Action left = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - getComponent().setCaretPosition(Math.max(0, getComponent().getCaretPosition() - 1)); - } - }; - - Action right = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - getComponent().setCaretPosition(Math.min(getComponent().getText().length(), getComponent().getCaretPosition() + 1)); - } - }; - - Action enter = new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - ((JTextField) getComponent()).postActionEvent(); - } - }; + }; + + Action delete = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (getComponent().getSelectedText() == null) { + int pos = getComponent().getCaretPosition() - 1; + + if (pos >= 0) { + getComponent().select(pos, pos + 1); + getComponent().replaceSelection(""); + } + } else { + getComponent().replaceSelection(""); + } + } + }; + + Action left = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + getComponent() + .setCaretPosition( + Math.max(0, getComponent().getCaretPosition() - 1)); + } + }; + + Action right = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + getComponent() + .setCaretPosition( + Math.min( + getComponent().getText().length(), + getComponent().getCaretPosition() + 1)); + } + }; - // note getMenuShortcutKeyMask() is deprecated in Java 10 - change to getMenuShortcutKeyMaskEx() - getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "selectAll"); - getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "delete"); + Action enter = + new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + ((JTextField) getComponent()).postActionEvent(); + } + }; + + // note getMenuShortcutKeyMask() is deprecated in Java 10 - change to + // getMenuShortcutKeyMaskEx() + getComponent() + .getInputMap() + .put( + KeyStroke.getKeyStroke( + KeyEvent.VK_A, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + "selectAll"); + getComponent() + .getInputMap() + .put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "delete"); getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left"); getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right"); getComponent().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "enter"); @@ -144,8 +163,7 @@ public void paintSafely(Graphics g) { if (getComponent().hasFocus()) { lineColor = focusedBackground; c.setSelectionColor(focusedSelectionBackground); - } - else { + } else { lineColor = unfocusedBackground; c.setSelectionColor(unfocusedSelectionBackground); } @@ -156,7 +174,10 @@ public void paintSafely(Graphics g) { if (drawLine) { int x = getComponent().getInsets().left; int y = getComponent().getInsets().top; - int w = getComponent().getWidth() - getComponent().getInsets().left - getComponent().getInsets().right; + int w = + getComponent().getWidth() + - getComponent().getInsets().left + - getComponent().getInsets().right; g.fillRect(x, c.getHeight() - y, w, 2); } @@ -188,8 +209,9 @@ public void propertyChange(PropertyChangeEvent pce) { if (!newColor.equals(focusedBackground) && !newColor.equals(unfocusedBackground)) { this.focusedBackground = (Color) pce.getNewValue(); - this.focusedSelectionBackground = MaterialColors.bleach(this.focusedBackground, 0.3f); + this.focusedSelectionBackground = + MaterialColors.bleach(this.focusedBackground, 0.3f); } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextPaneUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextPaneUI.java index 2adea67b2..f70048468 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextPaneUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTextPaneUI.java @@ -7,7 +7,6 @@ /** * @author https://github.com/vincenzopalazzo */ - public class MaterialTextPaneUI extends BasicTextPaneUI { public static ComponentUI createUI(JComponent c) { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToggleButtonUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToggleButtonUI.java index 928b2e688..3749366e9 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToggleButtonUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToggleButtonUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JToggleButton; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToggleButtonUI; -import java.awt.Graphics; public class MaterialToggleButtonUI extends BasicToggleButtonUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolBarUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolBarUI.java index d95d52455..10251eb73 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolBarUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolBarUI.java @@ -1,13 +1,12 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JToolBar; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolBarUI; -import java.awt.Graphics; public class MaterialToolBarUI extends BasicToolBarUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolTipUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolTipUI.java index adbd78925..87e415e4c 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolTipUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialToolTipUI.java @@ -1,11 +1,10 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.*; import javax.swing.*; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolTipUI; -import java.awt.*; public class MaterialToolTipUI extends BasicToolTipUI { diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellEditor.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellEditor.java index 6ac2230f4..ee9b0fcff 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellEditor.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellEditor.java @@ -1,13 +1,13 @@ package edu.rpi.legup.ui.lookandfeel.components; +import java.awt.Component; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellEditor; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeCellEditor; -import java.awt.Component; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; public class MaterialTreeCellEditor extends DefaultTreeCellEditor { @@ -18,7 +18,8 @@ public MaterialTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { init(); } - public MaterialTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer, TreeCellEditor editor) { + public MaterialTreeCellEditor( + JTree tree, DefaultTreeCellRenderer renderer, TreeCellEditor editor) { super(tree, renderer, editor); init(); } @@ -27,18 +28,20 @@ private void init() { textField = new JTextField(); textField.setUI(new MaterialTextFieldUI()); - textField.addKeyListener(new KeyAdapter() { - @Override - public void keyTyped(KeyEvent e) { - if (e.getKeyChar() == KeyEvent.VK_ENTER) { - stopCellEditing(); - } - } - }); + textField.addKeyListener( + new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + if (e.getKeyChar() == KeyEvent.VK_ENTER) { + stopCellEditing(); + } + } + }); } @Override - public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { + public Component getTreeCellEditorComponent( + JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { textField.setText(value.toString()); return textField; } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellRenderer.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellRenderer.java index f9ddcbf45..6ac5acabb 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellRenderer.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeCellRenderer.java @@ -1,11 +1,11 @@ package edu.rpi.legup.ui.lookandfeel.components; +import java.awt.Component; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.tree.DefaultTreeCellRenderer; -import java.awt.Component; public class MaterialTreeCellRenderer extends DefaultTreeCellRenderer { @@ -22,13 +22,22 @@ public MaterialTreeCellRenderer() { setOpenIcon(UIManager.getIcon("Tree.openIcon")); setLeafIcon(null); - setFont(UIManager.getFont("Tree.font")); } @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row, boolean hasFocus) { - JComponent component = (JComponent) super.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, hasFocus); + public Component getTreeCellRendererComponent( + JTree tree, + Object value, + boolean isSelected, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + JComponent component = + (JComponent) + super.getTreeCellRendererComponent( + tree, value, isSelected, expanded, leaf, row, hasFocus); component.setBorder(BorderFactory.createEmptyBorder(5, 2, 5, 2)); return component; diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeUI.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeUI.java index b1b9839bd..7d38c62f3 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeUI.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/components/MaterialTreeUI.java @@ -1,7 +1,7 @@ package edu.rpi.legup.ui.lookandfeel.components; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialDrawingUtils; - +import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JTree; import javax.swing.UIManager; @@ -9,7 +9,6 @@ import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeCellEditor; -import java.awt.Graphics; public class MaterialTreeUI extends BasicTreeUI { @@ -42,4 +41,4 @@ public void paint(Graphics g, JComponent c) { g = MaterialDrawingUtils.getAliasedGraphics(g); super.paint(g, c); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/DropShadowBorder.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/DropShadowBorder.java index 2543b8664..ce3244d4d 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/DropShadowBorder.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/DropShadowBorder.java @@ -1,8 +1,5 @@ package edu.rpi.legup.ui.lookandfeel.materialdesign; -import javax.swing.UIManager; -import javax.swing.border.AbstractBorder; -import javax.swing.border.Border; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; @@ -18,19 +15,22 @@ import java.awt.image.Kernel; import java.util.HashMap; import java.util.Map; +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; +import javax.swing.border.Border; /** - * Implements a DropShadow for components. In general, the mdlaf.shadows.DropShadowBorder will - * work with any rectangular components that do not have a default border installed - * as part of the look and feel, or otherwise. For example, mdlaf.shadows.DropShadowBorder works - * wonderfully with JPanel, but horribly with JComboBox. + * Implements a DropShadow for components. In general, the mdlaf.shadows.DropShadowBorder will work + * with any rectangular components that do not have a default border installed as part of the look + * and feel, or otherwise. For example, mdlaf.shadows.DropShadowBorder works wonderfully with + * JPanel, but horribly with JComboBox. * * @author rbair */ public class DropShadowBorder extends AbstractBorder implements Border { - private static final Map> CACHE - = new HashMap>(); + private static final Map> CACHE = + new HashMap>(); private Color lineColor; private int lineWidth; private int shadowSize; @@ -53,9 +53,16 @@ public DropShadowBorder(Color lineColor, int lineWidth, boolean showLeftShadow) this(lineColor, lineWidth, 5, .5f, 12, false, showLeftShadow, true, true); } - public DropShadowBorder(Color lineColor, int lineWidth, int shadowSize, - float shadowOpacity, int cornerSize, boolean showTopShadow, - boolean showLeftShadow, boolean showBottomShadow, boolean showRightShadow) { + public DropShadowBorder( + Color lineColor, + int lineWidth, + int shadowSize, + float shadowOpacity, + int cornerSize, + boolean showTopShadow, + boolean showLeftShadow, + boolean showBottomShadow, + boolean showRightShadow) { this.lineColor = lineColor; this.lineWidth = lineWidth; this.shadowSize = shadowSize; @@ -67,9 +74,7 @@ public DropShadowBorder(Color lineColor, int lineWidth, int shadowSize, this.showRightShadow = showRightShadow; } - /** - * - */ + /** */ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, int height) { /* * 1) Get images for this border @@ -77,8 +82,8 @@ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, */ Map images = getImages(null); - //compute the edges of the components -- not including the border - //Insets borderInsets = getBorderInsets (c); + // compute the edges of the components -- not including the border + // Insets borderInsets = getBorderInsets (c); // int leftEdge = x + borderInsets.left - lineWidth; // int rightEdge = x + width - borderInsets.right; // int topEdge = y + borderInsets.top - lineWidth; @@ -86,24 +91,22 @@ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, Graphics2D g2 = (Graphics2D) graphics; g2.setColor(lineColor); - //The location and size of the shadows depends on which shadows are being - //drawn. For instance, if the left & bottom shadows are being drawn, then - //the left shadows extends all the way down to the corner, a corner is drawn, - //and then the bottom shadows begins at the corner. If, however, only the - //bottom shadows is drawn, then the bottom-left corner is drawn to the - //right of the corner, and the bottom shadows is somewhat shorter than before. + // The location and size of the shadows depends on which shadows are being + // drawn. For instance, if the left & bottom shadows are being drawn, then + // the left shadows extends all the way down to the corner, a corner is drawn, + // and then the bottom shadows begins at the corner. If, however, only the + // bottom shadows is drawn, then the bottom-left corner is drawn to the + // right of the corner, and the bottom shadows is somewhat shorter than before. Point topLeftShadowPoint = null; if (showLeftShadow || showTopShadow) { topLeftShadowPoint = new Point(); if (showLeftShadow && !showTopShadow) { topLeftShadowPoint.setLocation(x, y + shadowSize); - } - else { + } else { if (showLeftShadow && showTopShadow) { topLeftShadowPoint.setLocation(x, y); - } - else { + } else { if (!showLeftShadow && showTopShadow) { topLeftShadowPoint.setLocation(x + shadowSize, y); } @@ -116,12 +119,10 @@ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, bottomLeftShadowPoint = new Point(); if (showLeftShadow && !showBottomShadow) { bottomLeftShadowPoint.setLocation(x, y + height - shadowSize - shadowSize); - } - else { + } else { if (showLeftShadow && showBottomShadow) { bottomLeftShadowPoint.setLocation(x, y + height - shadowSize); - } - else { + } else { if (!showLeftShadow && showBottomShadow) { bottomLeftShadowPoint.setLocation(x + shadowSize, y + height - shadowSize); } @@ -133,15 +134,16 @@ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, if (showRightShadow || showBottomShadow) { bottomRightShadowPoint = new Point(); if (showRightShadow && !showBottomShadow) { - bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize - shadowSize); - } - else { + bottomRightShadowPoint.setLocation( + x + width - shadowSize, y + height - shadowSize - shadowSize); + } else { if (showRightShadow && showBottomShadow) { - bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize); - } - else { + bottomRightShadowPoint.setLocation( + x + width - shadowSize, y + height - shadowSize); + } else { if (!showRightShadow && showBottomShadow) { - bottomRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y + height - shadowSize); + bottomRightShadowPoint.setLocation( + x + width - shadowSize - shadowSize, y + height - shadowSize); } } } @@ -152,12 +154,10 @@ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, topRightShadowPoint = new Point(); if (showRightShadow && !showTopShadow) { topRightShadowPoint.setLocation(x + width - shadowSize, y + shadowSize); - } - else { + } else { if (showRightShadow && showTopShadow) { topRightShadowPoint.setLocation(x + width - shadowSize, y); - } - else { + } else { if (!showRightShadow && showTopShadow) { topRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y); } @@ -166,42 +166,118 @@ public void paintBorder(Component c, Graphics graphics, int x, int y, int width, } if (showLeftShadow) { - Rectangle leftShadowRect = new Rectangle(x, (int) (topLeftShadowPoint.getY() + shadowSize), shadowSize, (int) (bottomLeftShadowPoint.getY() - topLeftShadowPoint.getY() - shadowSize)); - g2.drawImage(images.get(Position.LEFT).getScaledInstance(leftShadowRect.width, leftShadowRect.height, Image.SCALE_FAST), leftShadowRect.x, leftShadowRect.y, null); + Rectangle leftShadowRect = + new Rectangle( + x, + (int) (topLeftShadowPoint.getY() + shadowSize), + shadowSize, + (int) + (bottomLeftShadowPoint.getY() + - topLeftShadowPoint.getY() + - shadowSize)); + g2.drawImage( + images.get(Position.LEFT) + .getScaledInstance( + leftShadowRect.width, leftShadowRect.height, Image.SCALE_FAST), + leftShadowRect.x, + leftShadowRect.y, + null); } if (showBottomShadow) { - Rectangle bottomShadowRect = new Rectangle((int) (bottomLeftShadowPoint.getX() + shadowSize), y + height - shadowSize, (int) (bottomRightShadowPoint.getX() - bottomLeftShadowPoint.getX() - shadowSize), shadowSize); - g2.drawImage(images.get(Position.BOTTOM).getScaledInstance(bottomShadowRect.width, bottomShadowRect.height, Image.SCALE_FAST), bottomShadowRect.x, bottomShadowRect.y, null); + Rectangle bottomShadowRect = + new Rectangle( + (int) (bottomLeftShadowPoint.getX() + shadowSize), + y + height - shadowSize, + (int) + (bottomRightShadowPoint.getX() + - bottomLeftShadowPoint.getX() + - shadowSize), + shadowSize); + g2.drawImage( + images.get(Position.BOTTOM) + .getScaledInstance( + bottomShadowRect.width, + bottomShadowRect.height, + Image.SCALE_FAST), + bottomShadowRect.x, + bottomShadowRect.y, + null); } if (showRightShadow) { - Rectangle rightShadowRect = new Rectangle(x + width - shadowSize, (int) (topRightShadowPoint.getY() + shadowSize), shadowSize, (int) (bottomRightShadowPoint.getY() - topRightShadowPoint.getY() - shadowSize)); - g2.drawImage(images.get(Position.RIGHT).getScaledInstance(rightShadowRect.width, rightShadowRect.height, Image.SCALE_FAST), rightShadowRect.x, rightShadowRect.y, null); + Rectangle rightShadowRect = + new Rectangle( + x + width - shadowSize, + (int) (topRightShadowPoint.getY() + shadowSize), + shadowSize, + (int) + (bottomRightShadowPoint.getY() + - topRightShadowPoint.getY() + - shadowSize)); + g2.drawImage( + images.get(Position.RIGHT) + .getScaledInstance( + rightShadowRect.width, + rightShadowRect.height, + Image.SCALE_FAST), + rightShadowRect.x, + rightShadowRect.y, + null); } if (showTopShadow) { - Rectangle topShadowRect = new Rectangle((int) topLeftShadowPoint.getX() + shadowSize, y, (int) (topRightShadowPoint.getX() - topLeftShadowPoint.getX() - shadowSize), shadowSize); - g2.drawImage(images.get(Position.TOP).getScaledInstance(topShadowRect.width, topShadowRect.height, Image.SCALE_FAST), topShadowRect.x, topShadowRect.y, null); + Rectangle topShadowRect = + new Rectangle( + (int) topLeftShadowPoint.getX() + shadowSize, + y, + (int) + (topRightShadowPoint.getX() + - topLeftShadowPoint.getX() + - shadowSize), + shadowSize); + g2.drawImage( + images.get(Position.TOP) + .getScaledInstance( + topShadowRect.width, topShadowRect.height, Image.SCALE_FAST), + topShadowRect.x, + topShadowRect.y, + null); } if (showLeftShadow || showTopShadow) { - g2.drawImage(images.get(Position.TOP_LEFT), null, (int) topLeftShadowPoint.getX(), (int) topLeftShadowPoint.getY()); + g2.drawImage( + images.get(Position.TOP_LEFT), + null, + (int) topLeftShadowPoint.getX(), + (int) topLeftShadowPoint.getY()); } if (showLeftShadow || showBottomShadow) { - g2.drawImage(images.get(Position.BOTTOM_LEFT), null, (int) bottomLeftShadowPoint.getX(), (int) bottomLeftShadowPoint.getY()); + g2.drawImage( + images.get(Position.BOTTOM_LEFT), + null, + (int) bottomLeftShadowPoint.getX(), + (int) bottomLeftShadowPoint.getY()); } if (showRightShadow || showBottomShadow) { - g2.drawImage(images.get(Position.BOTTOM_RIGHT), null, (int) bottomRightShadowPoint.getX(), (int) bottomRightShadowPoint.getY()); + g2.drawImage( + images.get(Position.BOTTOM_RIGHT), + null, + (int) bottomRightShadowPoint.getX(), + (int) bottomRightShadowPoint.getY()); } if (showRightShadow || showTopShadow) { - g2.drawImage(images.get(Position.TOP_RIGHT), null, (int) topRightShadowPoint.getX(), (int) topRightShadowPoint.getY()); + g2.drawImage( + images.get(Position.TOP_RIGHT), + null, + (int) topRightShadowPoint.getX(), + (int) topRightShadowPoint.getY()); } } private Map getImages(Graphics2D g2) { - //first, check to see if an image for this size has already been rendered - //if so, use the cache. Else, draw and save + // first, check to see if an image for this size has already been rendered + // if so, use the cache. Else, draw and save Map images = CACHE.get(shadowSize); if (images == null) { images = new HashMap(); @@ -221,25 +297,35 @@ private Map getImages(Graphics2D g2) { * drawing the Border */ int rectWidth = cornerSize + 1; - RoundRectangle2D rect = new RoundRectangle2D.Double(0, 0, rectWidth, rectWidth, cornerSize, cornerSize); + RoundRectangle2D rect = + new RoundRectangle2D.Double(0, 0, rectWidth, rectWidth, cornerSize, cornerSize); int imageWidth = rectWidth + shadowSize * 2; - BufferedImage image = new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_ARGB); + BufferedImage image = + new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_ARGB); Graphics2D buffer = (Graphics2D) image.getGraphics(); - buffer.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - buffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - buffer.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - buffer.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + buffer.setRenderingHint( + RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + buffer.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + buffer.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + buffer.setRenderingHint( + RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); buffer.setColor(new Color(0.0f, 0.0f, 0.0f, shadowOpacity)); buffer.translate(shadowSize, shadowSize); buffer.fill(rect); - float blurry = 1.0f / (float) (shadowSize * shadowSize);//1.0f / (float)(shadowSize * shadowSize); + float blurry = 1.0f / (float) (shadowSize * shadowSize); // 1.0f / (float)(shadowSize * + // shadowSize); float[] blurKernel = new float[shadowSize * shadowSize]; for (int i = 0; i < blurKernel.length; i++) { blurKernel[i] = blurry; } ConvolveOp blur = new ConvolveOp(new Kernel(shadowSize, shadowSize, blurKernel)); - BufferedImage targetImage = new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_ARGB); - ((Graphics2D) targetImage.getGraphics()).drawImage(image, blur, -(shadowSize / 2), -(shadowSize / 2)); + BufferedImage targetImage = + new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_ARGB); + ((Graphics2D) targetImage.getGraphics()) + .drawImage(image, blur, -(shadowSize / 2), -(shadowSize / 2)); int x = 1; int y = 1; @@ -288,9 +374,7 @@ private Map getImages(Graphics2D g2) { return images; } - /** - * - */ + /** */ public Insets getBorderInsets(Component c) { int top = 4 + (showTopShadow ? lineWidth + shadowSize : lineWidth); int left = 4 + (showLeftShadow ? lineWidth + shadowSize : lineWidth); @@ -300,9 +384,7 @@ public Insets getBorderInsets(Component c) { return new Insets(top, left, bottom, right); } - /** - * - */ + /** */ public boolean isBorderOpaque() { return true; } @@ -344,7 +426,13 @@ public int getCornerSize() { } private enum Position { - TOP, TOP_LEFT, LEFT, BOTTOM_LEFT, - BOTTOM, BOTTOM_RIGHT, RIGHT, TOP_RIGHT + TOP, + TOP_LEFT, + LEFT, + BOTTOM_LEFT, + BOTTOM, + BOTTOM_RIGHT, + RIGHT, + TOP_RIGHT } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialBorders.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialBorders.java index c78d20e34..8c2e3eeec 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialBorders.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialBorders.java @@ -1,17 +1,20 @@ package edu.rpi.legup.ui.lookandfeel.materialdesign; +import java.awt.Color; import javax.swing.BorderFactory; import javax.swing.border.Border; -import java.awt.Color; public class MaterialBorders { - public static final Border LIGHT_LINE_BORDER = BorderFactory.createLineBorder(MaterialColors.GRAY_200, 1); - public static final Border THICK_LINE_BORDER = BorderFactory.createLineBorder(MaterialColors.GRAY_200, 2); + public static final Border LIGHT_LINE_BORDER = + BorderFactory.createLineBorder(MaterialColors.GRAY_200, 1); + public static final Border THICK_LINE_BORDER = + BorderFactory.createLineBorder(MaterialColors.GRAY_200, 2); - public static final Border LIGHT_SHADOW_BORDER = new DropShadowBorder(Color.BLACK, 0, 4, 0.3f, 12, true, true, true, true); - public static final Border DEFAULT_SHADOW_BORDER = new DropShadowBorder(Color.BLACK, 5, 5, 0.3f, 12, true, true, true, true); + public static final Border LIGHT_SHADOW_BORDER = + new DropShadowBorder(Color.BLACK, 0, 4, 0.3f, 12, true, true, true, true); + public static final Border DEFAULT_SHADOW_BORDER = + new DropShadowBorder(Color.BLACK, 5, 5, 0.3f, 12, true, true, true, true); - private MaterialBorders() { - } -} \ No newline at end of file + private MaterialBorders() {} +} diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialColors.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialColors.java index 30229b936..00cb0720a 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialColors.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialColors.java @@ -261,8 +261,7 @@ public class MaterialColors { public static final Color WHITE = new Color(255, 255, 255); public static final Color TRANSPARENT = new Color(0, 0, 0, 255); - private MaterialColors() { - } + private MaterialColors() {} public static Color bleach(Color color, float amount) { int red = (int) ((color.getRed() * (1 - amount) / 255 + amount) * 255); diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialDrawingUtils.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialDrawingUtils.java index fbc330faf..f5e209b0d 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialDrawingUtils.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialDrawingUtils.java @@ -15,17 +15,19 @@ public class MaterialDrawingUtils { } public static Graphics getAliasedGraphics(Graphics g) { - Map hints = (Map) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints"); + Map hints = + (Map) + Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints"); if (hints != null) { hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Graphics2D g2d = (Graphics2D) g; g2d.addRenderingHints(hints); - //g2d.addRenderingHints (new RenderingHints (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); + // g2d.addRenderingHints (new RenderingHints (RenderingHints.KEY_ANTIALIASING, + // RenderingHints.VALUE_ANTIALIAS_ON)); return g2d; - } - else { + } else { // Desktop hints not supported on this platform return g; } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialFonts.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialFonts.java index 9bbc4e884..e5d1de56c 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialFonts.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialFonts.java @@ -1,6 +1,5 @@ package edu.rpi.legup.ui.lookandfeel.materialdesign; - import java.awt.*; import java.awt.font.TextAttribute; import java.io.IOException; @@ -13,17 +12,22 @@ public class MaterialFonts { private static final Map fontSettings = new HashMap<>(); public static final Font BLACK = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Black.ttf"); - public static final Font BLACK_ITALIC = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-BlackItalic.ttf"); + public static final Font BLACK_ITALIC = + loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-BlackItalic.ttf"); public static final Font BOLD = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Bold.ttf"); - public static final Font BOLD_ITALIC = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-BoldItalic.ttf"); + public static final Font BOLD_ITALIC = + loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-BoldItalic.ttf"); public static final Font ITALIC = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Italic.ttf"); public static final Font LIGHT = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Light.ttf"); - public static final Font LIGHT_ITALIC = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-LightItalic.ttf"); + public static final Font LIGHT_ITALIC = + loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-LightItalic.ttf"); public static final Font MEDIUM = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Medium.ttf"); - public static final Font MEDIUM_ITALIC = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-MediumItalic.ttf"); + public static final Font MEDIUM_ITALIC = + loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-MediumItalic.ttf"); public static final Font REGULAR = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Regular.ttf"); public static final Font THIN = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-Thin.ttf"); - public static final Font THIN_ITALIC = loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-ThinItalic.ttf"); + public static final Font THIN_ITALIC = + loadFont("/edu/rpi/legup/fonts/Roboto/Roboto-ThinItalic.ttf"); private static Font loadFont(String fontPath) { if (fontSettings.isEmpty()) { @@ -33,8 +37,7 @@ private static Font loadFont(String fontPath) { try (InputStream inputStream = MaterialFonts.class.getResourceAsStream(fontPath)) { return Font.createFont(Font.TRUETYPE_FONT, inputStream).deriveFont(fontSettings); - } - catch (IOException | FontFormatException e) { + } catch (IOException | FontFormatException e) { e.printStackTrace(); throw new RuntimeException("Font " + fontPath + " wasn't loaded"); } @@ -45,10 +48,11 @@ public static Font getRegularFont(float size) { map.put(TextAttribute.SIZE, size); map.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); - try (InputStream inputStream = MaterialFonts.class.getResourceAsStream("/edu/rpi/legup/fonts/Roboto/Roboto-Regular.ttf")) { + try (InputStream inputStream = + MaterialFonts.class.getResourceAsStream( + "/edu/rpi/legup/fonts/Roboto/Roboto-Regular.ttf")) { return Font.createFont(Font.TRUETYPE_FONT, inputStream).deriveFont(map); - } - catch (IOException | FontFormatException e) { + } catch (IOException | FontFormatException e) { e.printStackTrace(); throw new RuntimeException("Font regular wasn't loaded"); } diff --git a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialImages.java b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialImages.java index dabf2139d..a1a716c76 100644 --- a/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialImages.java +++ b/src/main/java/edu/rpi/legup/ui/lookandfeel/materialdesign/MaterialImages.java @@ -1,26 +1,34 @@ package edu.rpi.legup.ui.lookandfeel.materialdesign; -import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; +import javax.imageio.ImageIO; public class MaterialImages { public static final BufferedImage RIGHT_ARROW = loadImg("/edu/rpi/legup/imgs/right_arrow.png"); public static final BufferedImage DOWN_ARROW = loadImg("/edu/rpi/legup/imgs/down_arrow.png"); public static final BufferedImage UP_ARROW = loadImg("/edu/rpi/legup/imgs/up_arrow.png"); - public static final BufferedImage PAINTED_CHECKED_BOX = loadImg("/edu/rpi/legup/imgs/painted_checked_box.png"); - public static final BufferedImage OUTLINED_CHECKED_BOX = loadImg("/edu/rpi/legup/imgs/outlined_checked_box.png"); - public static final BufferedImage UNCHECKED_BOX = loadImg("/edu/rpi/legup/imgs/unchecked_box.png"); - public static final BufferedImage RADIO_BUTTON_ON = loadImg("/edu/rpi/legup/imgs/radio_button_on.png"); - public static final BufferedImage RADIO_BUTTON_OFF = loadImg("/edu/rpi/legup/imgs/radio_button_off.png"); - public static final BufferedImage TOGGLE_BUTTON_ON = loadImg("/edu/rpi/legup/imgs/toggle_on.png"); - public static final BufferedImage TOGGLE_BUTTON_OFF = loadImg("/edu/rpi/legup/imgs/toggle_off.png"); + public static final BufferedImage PAINTED_CHECKED_BOX = + loadImg("/edu/rpi/legup/imgs/painted_checked_box.png"); + public static final BufferedImage OUTLINED_CHECKED_BOX = + loadImg("/edu/rpi/legup/imgs/outlined_checked_box.png"); + public static final BufferedImage UNCHECKED_BOX = + loadImg("/edu/rpi/legup/imgs/unchecked_box.png"); + public static final BufferedImage RADIO_BUTTON_ON = + loadImg("/edu/rpi/legup/imgs/radio_button_on.png"); + public static final BufferedImage RADIO_BUTTON_OFF = + loadImg("/edu/rpi/legup/imgs/radio_button_off.png"); + public static final BufferedImage TOGGLE_BUTTON_ON = + loadImg("/edu/rpi/legup/imgs/toggle_on.png"); + public static final BufferedImage TOGGLE_BUTTON_OFF = + loadImg("/edu/rpi/legup/imgs/toggle_off.png"); public static final BufferedImage BACK_ARROW = loadImg("/edu/rpi/legup/imgs/back_arrow.png"); public static final BufferedImage COMPUTER = loadImg("/edu/rpi/legup/imgs/computer.png"); public static final BufferedImage FILE = loadImg("/edu/rpi/legup/imgs/file.png"); - public static final BufferedImage FLOPPY_DRIVE = loadImg("/edu/rpi/legup/imgs/floppy_drive.png"); + public static final BufferedImage FLOPPY_DRIVE = + loadImg("/edu/rpi/legup/imgs/floppy_drive.png"); public static final BufferedImage FOLDER = loadImg("/edu/rpi/legup/imgs/folder.png"); public static final BufferedImage HARD_DRIVE = loadImg("/edu/rpi/legup/imgs/hard_drive.png"); public static final BufferedImage HOME = loadImg("/edu/rpi/legup/imgs/home.png"); @@ -28,14 +36,12 @@ public class MaterialImages { public static final BufferedImage NEW_FOLDER = loadImg("/edu/rpi/legup/imgs/new_folder.png"); public static final BufferedImage DETAILS = loadImg("/edu/rpi/legup/imgs/details.png"); - private MaterialImages() { - } + private MaterialImages() {} private static BufferedImage loadImg(String imgPath) { try (InputStream inputStream = MaterialImages.class.getResourceAsStream(imgPath)) { return ImageIO.read(inputStream); - } - catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("Image " + imgPath + " wasn't loaded"); } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java index 5f22ac62c..1fb0a16ab 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java @@ -10,8 +10,11 @@ public class CaseRulePanel extends RulePanel { */ CaseRulePanel(RuleFrame ruleFrame) { super(ruleFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Case Rules.gif")); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Case Rules.gif")); this.name = "Case Rules"; this.toolTip = "Case Rules"; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java index 276583a99..ae9444db2 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRuleSelectionView.java @@ -1,9 +1,8 @@ package edu.rpi.legup.ui.proofeditorui.rulesview; import edu.rpi.legup.ui.WrapLayout; - -import javax.swing.*; import java.awt.*; +import javax.swing.*; public class CaseRuleSelectionView extends JPanel { diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java index 908ffc49b..f695491fb 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java @@ -10,7 +10,10 @@ public class ContradictionRulePanel extends RulePanel { */ ContradictionRulePanel(RuleFrame ruleFrame) { super(ruleFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Contradictions.gif")); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Contradictions.gif")); this.name = "Contradiction Rules"; this.toolTip = "Contradiction Rules"; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java index 3d0672525..2795f2df7 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java @@ -1,22 +1,24 @@ -package edu.rpi.legup.ui.proofeditorui.rulesview; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.swing.ImageIcon; - -public class DirectRulePanel extends RulePanel { - private static final Logger LOGGER = LogManager.getLogger(DirectRulePanel.class.getName()); - - /** - * DirectRulePanel Constructor creates a basic rule panel - * - * @param ruleFrame rule frame that this basic rule panel is contained in - */ - DirectRulePanel(RuleFrame ruleFrame) { - super(ruleFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); - this.name = "Direct Rules"; - this.toolTip = "Direct Rules"; - } -} \ No newline at end of file +package edu.rpi.legup.ui.proofeditorui.rulesview; + +import javax.swing.ImageIcon; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DirectRulePanel extends RulePanel { + private static final Logger LOGGER = LogManager.getLogger(DirectRulePanel.class.getName()); + + /** + * DirectRulePanel Constructor creates a basic rule panel + * + * @param ruleFrame rule frame that this basic rule panel is contained in + */ + DirectRulePanel(RuleFrame ruleFrame) { + super(ruleFrame); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); + this.name = "Direct Rules"; + this.toolTip = "Direct Rules"; + } +} diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java index 30545f413..e9c274250 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java @@ -1,7 +1,6 @@ package edu.rpi.legup.ui.proofeditorui.rulesview; import edu.rpi.legup.model.rules.Rule; - import javax.swing.*; public class RuleButton extends JButton { @@ -13,7 +12,9 @@ public class RuleButton extends JButton { * @param rule rule to create the button */ RuleButton(Rule rule) { - super(rule.getRuleName(), rule.getImageIcon()); // display rules' name under rule when load the icon + super( + rule.getRuleName(), + rule.getImageIcon()); // display rules' name under rule when load the icon this.rule = rule; this.setFocusPainted(false); } 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 0fe03d476..6279f93a4 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 @@ -5,12 +5,9 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.ui.lookandfeel.components.MaterialTabbedPaneUI; - import java.awt.BorderLayout; import java.awt.Dimension; - import javax.swing.*; - import javax.swing.border.TitledBorder; public class RuleFrame extends JPanel { @@ -33,13 +30,14 @@ public class RuleFrame extends JPanel { public RuleFrame(RuleController controller) { - MaterialTabbedPaneUI tabOverride = new MaterialTabbedPaneUI() { - //this prevents the tabs from moving around when you select them - @Override - protected boolean shouldRotateTabRuns(int i) { - return false; - } - }; + MaterialTabbedPaneUI tabOverride = + new MaterialTabbedPaneUI() { + // this prevents the tabs from moving around when you select them + @Override + protected boolean shouldRotateTabRuns(int i) { + return false; + } + }; this.controller = controller; @@ -51,7 +49,11 @@ protected boolean shouldRotateTabRuns(int i) { DirectRulePanel = new DirectRulePanel(this); JScrollPane newbrp = new JScrollPane(DirectRulePanel); newbrp.getVerticalScrollBar().setUnitIncrement(16); - tabbedPane.addTab(DirectRulePanel.getName(), DirectRulePanel.getIcon(), newbrp, DirectRulePanel.getToolTip()); + tabbedPane.addTab( + DirectRulePanel.getName(), + DirectRulePanel.getIcon(), + newbrp, + DirectRulePanel.getToolTip()); casePanel = new CaseRulePanel(this); JScrollPane newcp = new JScrollPane(casePanel); @@ -61,7 +63,8 @@ protected boolean shouldRotateTabRuns(int i) { contradictionPanel = new ContradictionRulePanel(this); JScrollPane newp = new JScrollPane(contradictionPanel); newp.getVerticalScrollBar().setUnitIncrement(16); - tabbedPane.addTab(contradictionPanel.name, contradictionPanel.icon, newp, contradictionPanel.toolTip); + tabbedPane.addTab( + contradictionPanel.name, contradictionPanel.icon, newp, contradictionPanel.toolTip); searchPanel = new SearchBarPanel(this); JScrollPane newsp = new JScrollPane(searchPanel); @@ -95,25 +98,20 @@ public void setSelectionByRule(Rule rule) { contradictionPanel.setSelectionByRule(rule); } - /** - * Reset the rules button and status string - */ + /** Reset the rules button and status string */ public void resetRuleButtons() { resetStatus(); } - /** - * Reset the status label to the empty string - */ + /** Reset the status label to the empty string */ public void resetStatus() { - //((GridUI)GameBoardFacade.getInstance().getLegupUI()).getTreePanel().updateStatus(); + // ((GridUI)GameBoardFacade.getInstance().getLegupUI()).getTreePanel().updateStatus(); } - /** - * Resets the dimension of the rule frame - */ + /** Resets the dimension of the rule frame */ public void resetSize() { - int buttonWidth = ((RulePanel) tabbedPane.getSelectedComponent()).getRuleButtons()[0].getWidth(); + int buttonWidth = + ((RulePanel) tabbedPane.getSelectedComponent()).getRuleButtons()[0].getWidth(); this.setMinimumSize(new Dimension(2 * buttonWidth + 64, this.getHeight())); } @@ -121,12 +119,12 @@ public void resetSize() { * Set the status label to a value. Use resetStatus to clear it. * * @param check true iff we want a check box, if false we'll have a red x box - * @param text the text we're setting the label to display + * @param text the text we're setting the label to display */ public void setStatus(boolean check, String text) { String box = (check ? checkBox : xBox); - //status.setText(htmlHead + box + text + htmlTail); - //((GridUI)GameBoardFacade.getInstance().getLegupUI()).getTreePanel().getStatus().setText(htmlHead + box + text + htmlTail); + // status.setText(htmlHead + box + text + htmlTail); + // ((GridUI)GameBoardFacade.getInstance().getLegupUI()).getTreePanel().getStatus().setText(htmlHead + box + text + htmlTail); } /** diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java index f11fee5b4..5d985d5c2 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java @@ -3,12 +3,11 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.ui.WrapLayout; - -import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.List; +import javax.swing.*; public abstract class RulePanel extends JPanel { protected ImageIcon icon; @@ -56,38 +55,37 @@ public void setRules(List rules) { Rule rule = rules.get(i); ruleButtons[i] = new RuleButton(rule); - ruleButtons[i].setPreferredSize(new Dimension(150, 150));// adjust the size of each RuleButton + ruleButtons[i].setPreferredSize( + new Dimension(150, 150)); // adjust the size of each RuleButton ruleButtons[i].setHorizontalTextPosition(JButton.CENTER); ruleButtons[i].setVerticalTextPosition(JButton.BOTTOM); ruleFrame.getButtonGroup().add(ruleButtons[i]); - ruleButtons[i].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); // showing description + ruleButtons[i].setToolTipText( + rule.getRuleName() + ": " + rule.getDescription()); // showing description ruleButtons[i].addActionListener(ruleFrame.getController()); add(ruleButtons[i]); - } revalidate(); } public void updateRules() { - for (Rule rule : rules){ + for (Rule rule : rules) { rule.loadImage(); } setRules(rules); } - /** * Search a certain rule in all the puzzles and set it for the searchBarPanel * * @param puzzle puzzle where the rule is being searched for * @param ruleName rule that is being compared to each puzzle - * - * This function is the searching algorithm for "public void setSearchBar(Puzzle allPuzzle)" (below) - * - * It takes two param Puzzle puzzle and String ruleName - * puzzle contains rules, this function will compare each rule of puzzle with ruleName, - * to find exact same, similar rules, or all the rules with same start letter (if input is a signal letter) + *

This function is the searching algorithm for "public void setSearchBar(Puzzle + * allPuzzle)" (below) + *

It takes two param Puzzle puzzle and String ruleName puzzle contains rules, this + * function will compare each rule of puzzle with ruleName, to find exact same, similar + * rules, or all the rules with same start letter (if input is a signal letter) */ public void searchForRule(Puzzle puzzle, String ruleName) { @@ -99,7 +97,6 @@ public void searchForRule(Puzzle puzzle, String ruleName) { ruleButtons = new RuleButton[100]; int similarfound = 0; - for (int i = 0; i < allrules.size(); i++) { for (int j = 0; j < allrules.get(i).size(); j++) { Rule rule = allrules.get(i).get(j); @@ -108,42 +105,46 @@ public void searchForRule(Puzzle puzzle, String ruleName) { ruleButtons[0] = new RuleButton(rule); ruleFrame.getButtonGroup().add(ruleButtons[0]); - ruleButtons[0].setPreferredSize(new Dimension(150, 150));// adjust the size of each RuleButton + ruleButtons[0].setPreferredSize( + new Dimension(150, 150)); // adjust the size of each RuleButton ruleButtons[0].setHorizontalTextPosition(JButton.CENTER); ruleButtons[0].setVerticalTextPosition(JButton.BOTTOM); - ruleButtons[0].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); + ruleButtons[0].setToolTipText( + rule.getRuleName() + ": " + rule.getDescription()); ruleButtons[0].addActionListener(ruleFrame.getController()); add(ruleButtons[0]); revalidate(); return; - } - else { + } else { if (similarityCheck(ruleName, rule.getRuleName().toUpperCase()) > 0.2) { ruleButtons[similarfound] = new RuleButton(rule); ruleFrame.getButtonGroup().add(ruleButtons[similarfound]); - ruleButtons[similarfound].setPreferredSize(new Dimension(150, 150));// adjust the size of each RuleButton + ruleButtons[similarfound].setPreferredSize( + new Dimension(150, 150)); // adjust the size of each RuleButton ruleButtons[similarfound].setHorizontalTextPosition(JButton.CENTER); ruleButtons[similarfound].setVerticalTextPosition(JButton.BOTTOM); - ruleButtons[similarfound].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); + ruleButtons[similarfound].setToolTipText( + rule.getRuleName() + ": " + rule.getDescription()); ruleButtons[similarfound].addActionListener(ruleFrame.getController()); add(ruleButtons[similarfound]); similarfound += 1; revalidate(); - } - else { + } else { if ((ruleName.charAt(0)) == (rule.getRuleName().toUpperCase()).charAt(0)) { ruleButtons[similarfound] = new RuleButton(rule); ruleFrame.getButtonGroup().add(ruleButtons[similarfound]); - ruleButtons[similarfound].setPreferredSize(new Dimension(150, 150));// adjust the size of each RuleButton + ruleButtons[similarfound].setPreferredSize( + new Dimension(150, 150)); // adjust the size of each RuleButton ruleButtons[similarfound].setHorizontalTextPosition(JButton.CENTER); ruleButtons[similarfound].setVerticalTextPosition(JButton.BOTTOM); - ruleButtons[similarfound].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); + ruleButtons[similarfound].setToolTipText( + rule.getRuleName() + ": " + rule.getDescription()); ruleButtons[similarfound].addActionListener(ruleFrame.getController()); add(ruleButtons[similarfound]); similarfound += 1; @@ -155,19 +156,24 @@ public void searchForRule(Puzzle puzzle, String ruleName) { } if (ruleButtons[0] == null) { - JOptionPane.showMessageDialog(null, "Please input the correct rule name", "Confirm", JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog( + null, + "Please input the correct rule name", + "Confirm", + JOptionPane.INFORMATION_MESSAGE); } } /** - * Calculates the similarity (a number within 0 and 1) between two strings. - * This function will take two para String s1 and String s2, which s1 is the user's input - * and s2 is the compared really rule name + * Calculates the similarity (a number within 0 and 1) between two strings. This function will + * take two para String s1 and String s2, which s1 is the user's input and s2 is the compared + * really rule name + * * @param s1 user's input * @param s2 the compared really rule name - * @return a similarity degree between 0 and 1 - * similarityCheck will use a helper function to calculate a similarity degree(from 0 to 1). - * closer to 0 means less similar, and closer to 1 means more similar. + * @return a similarity degree between 0 and 1 similarityCheck will use a helper function to + * calculate a similarity degree(from 0 to 1). closer to 0 means less similar, and closer to + * 1 means more similar. */ public static double similarityCheck(String s1, String s2) { String longer = s1, shorter = s2; @@ -184,6 +190,7 @@ public static double similarityCheck(String s1, String s2) { /** * Help function for similarityCheck(); + * * @param s1 user's input * @param s2 the compared really rule name * @return a similarity degree between 0 and 1 @@ -198,8 +205,7 @@ public static int editDistance(String s1, String s2) { for (int j = 0; j <= s2.length(); j++) { if (i == 0) { costs[j] = j; - } - else { + } else { if (j > 0) { int newValue = costs[j - 1]; if (s1.charAt(i - 1) != s2.charAt(j - 1)) { @@ -218,23 +224,24 @@ public static int editDistance(String s1, String s2) { } /** - * Sets the search bar for SearchBarPanel - * search bar allows user to input a name to get relative rules - * once a name is entered and click ok will load (a/several) rule icon, - * which has all the functions just as other rule icons. + * Sets the search bar for SearchBarPanel search bar allows user to input a name to get relative + * rules once a name is entered and click ok will load (a/several) rule icon, which has all the + * functions just as other rule icons. + * * @param allPuzzle name of rule input */ public void setSearchBar(Puzzle allPuzzle) { searchBarPanel = new JPanel(new FlowLayout(SwingConstants.LEADING, 6, 6)); - textField=new JTextField(); - ruleFrame.addComponentListener(new ComponentAdapter() { - public void componentResized(ComponentEvent componentEvent) { - Component c= componentEvent.getComponent(); - textField.setColumns((8+(c.getWidth()-250)/10)-1); - } - }); + textField = new JTextField(); + ruleFrame.addComponentListener( + new ComponentAdapter() { + public void componentResized(ComponentEvent componentEvent) { + Component c = componentEvent.getComponent(); + textField.setColumns((8 + (c.getWidth() - 250) / 10) - 1); + } + }); add(searchBarPanel); JLabel findLabel = new JLabel("Search:"); @@ -243,37 +250,40 @@ public void componentResized(ComponentEvent componentEvent) { searchBarPanel.add(textField); searchBarPanel.add(Box.createRigidArea(new Dimension(1, 0))); JButton findButton = new JButton("Go"); - ActionListener action = new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - if (ruleButtons != null) { - for (int i = 0; i != ruleButtons.length; i++) { - if (ruleButtons[i] == null) { - continue; + ActionListener action = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + if (ruleButtons != null) { + for (int i = 0; i != ruleButtons.length; i++) { + if (ruleButtons[i] == null) { + continue; + } + ruleButtons[i].removeActionListener(ruleFrame.getController()); + } } - ruleButtons[i].removeActionListener(ruleFrame.getController()); - } - } - String inputRule = textField.getText().toUpperCase().trim(); + String inputRule = textField.getText().toUpperCase().trim(); - if (!inputRule.isEmpty()) { - if (ruleButtons != null) { + if (!inputRule.isEmpty()) { + if (ruleButtons != null) { - for (int x = 0; x < ruleButtons.length; ++x) { - if (ruleButtons[x] == null) { - continue; + for (int x = 0; x < ruleButtons.length; ++x) { + if (ruleButtons[x] == null) { + continue; + } + remove(ruleButtons[x]); + } } - remove(ruleButtons[x]); + searchForRule(allPuzzle, inputRule); + } else { + JOptionPane.showMessageDialog( + null, + "Please give a name", + "Confirm", + JOptionPane.INFORMATION_MESSAGE); } } - searchForRule(allPuzzle, inputRule); - } - else { - JOptionPane.showMessageDialog(null, "Please give a name", "Confirm", JOptionPane.INFORMATION_MESSAGE); - } - - } - }; + }; textField.addActionListener(action); findButton.addActionListener(action); searchBarPanel.add(findButton); @@ -295,9 +305,7 @@ public void setSelectionByRule(Rule rule) { } } - /** - * Clears the rule buttons off this panel - */ + /** Clears the rule buttons off this panel */ protected void clearButtons() { if (ruleButtons != null) { removeAll(); @@ -341,4 +349,4 @@ public String getToolTip() { public void setToolTip(String toolTip) { this.toolTip = toolTip; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java index ff8a5d259..aba4707cd 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java @@ -7,12 +7,14 @@ public class SearchBarPanel extends RulePanel { * SearchBarPanel Constructor creates a SearchBarPanel * * @param ruleFrame rule frame that this SearchBarPanel is contained in - *

- * This class is used to create a panel named "search bar" + *

This class is used to create a panel named "search bar" */ SearchBarPanel(RuleFrame ruleFrame) { super(ruleFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Zoom In.png")); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Zoom In.png")); this.name = "Search Rules"; this.toolTip = "Search Rules"; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java index 676992654..33c04717d 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.model.tree.TreeElementType; - import java.awt.*; public abstract class TreeElementView implements Shape { @@ -18,7 +17,7 @@ public abstract class TreeElementView implements Shape { /** * TreeElementView Constructor creates a tree puzzleElement view * - * @param type tree puzzleElement type + * @param type tree puzzleElement type * @param treeElement tree puzzleElement puzzleElement associated with this view */ protected TreeElementView(TreeElementType type, TreeElement treeElement) { @@ -136,8 +135,8 @@ public void setHover(boolean isHovered) { } /** - * Gets the visibility of the tree puzzleElement. - * Tells the TreeView whether or not to draw the tree puzzleElement + * Gets the visibility of the tree puzzleElement. Tells the TreeView whether or not to draw the + * tree puzzleElement * * @return visibility of the tree puzzleElement */ diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java index 2fce4d2d1..990d96620 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java @@ -3,7 +3,6 @@ import edu.rpi.legup.model.rules.RuleType; import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.model.tree.TreeNode; - import java.awt.*; import java.awt.geom.*; import java.util.ArrayList; @@ -31,7 +30,6 @@ public class TreeNodeView extends TreeElementView { private static final Color HOVER_COLOR = new Color(0x90CAF9); private static final Color OUTLINE_HOVER_COLOR = new Color(0xBDBDBD); - private Point location; private TreeTransitionView parentView; @@ -63,48 +61,69 @@ public TreeNodeView(TreeNode treeNode) { */ public void draw(Graphics2D graphics2D) { if (isVisible() && treeElement != null) { - if (getTreeElement().getParent() != null && - getTreeElement().getParent().isJustified() && - getTreeElement().getParent().getRule().getRuleType() == RuleType.CONTRADICTION) { + if (getTreeElement().getParent() != null + && getTreeElement().getParent().isJustified() + && getTreeElement().getParent().getRule().getRuleType() + == RuleType.CONTRADICTION) { isContradictoryState = true; graphics2D.setColor(NODE_COLOR_CONTRADICTION); - graphics2D.drawLine(location.x - RADIUS, location.y - RADIUS, location.x + RADIUS, location.y + RADIUS); - graphics2D.drawLine(location.x + RADIUS, location.y - RADIUS, location.x - RADIUS, location.y + RADIUS); - } - else { + graphics2D.drawLine( + location.x - RADIUS, + location.y - RADIUS, + location.x + RADIUS, + location.y + RADIUS); + graphics2D.drawLine( + location.x + RADIUS, + location.y - RADIUS, + location.x - RADIUS, + location.y + RADIUS); + } else { isContradictoryState = false; graphics2D.setStroke(MAIN_STROKE); boolean isContraBranch = getTreeElement().isContradictoryBranch(); if (isSelected) { graphics2D.setColor(SELECTION_COLOR); - graphics2D.fillOval(location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); + graphics2D.fillOval( + location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); graphics2D.setColor(OUTLINE_COLOR); - graphics2D.drawOval(location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); + graphics2D.drawOval( + location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); graphics2D.setStroke(SELECTION_STROKE); graphics2D.setColor(OUTLINE_SELECTION_COLOR); - graphics2D.drawOval(location.x - RADIUS - 4, location.y - RADIUS - 4, DIAMETER + 8, DIAMETER + 8); - } - else { + graphics2D.drawOval( + location.x - RADIUS - 4, + location.y - RADIUS - 4, + DIAMETER + 8, + DIAMETER + 8); + } else { if (isHover) { graphics2D.setColor(HOVER_COLOR); - graphics2D.fillOval(location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); + graphics2D.fillOval( + location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); graphics2D.setColor(OUTLINE_COLOR); - graphics2D.drawOval(location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); + graphics2D.drawOval( + location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); graphics2D.setStroke(SELECTION_STROKE); graphics2D.setColor(OUTLINE_HOVER_COLOR); - graphics2D.drawOval(location.x - RADIUS - 4, location.y - RADIUS - 4, DIAMETER + 8, DIAMETER + 8); - } - else { - graphics2D.setColor(isContraBranch ? NODE_COLOR_CONTRADICTION : NODE_COLOR_DEFAULT); - graphics2D.fillOval(location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); + graphics2D.drawOval( + location.x - RADIUS - 4, + location.y - RADIUS - 4, + DIAMETER + 8, + DIAMETER + 8); + } else { + graphics2D.setColor( + isContraBranch ? NODE_COLOR_CONTRADICTION : NODE_COLOR_DEFAULT); + graphics2D.fillOval( + location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); graphics2D.setColor(OUTLINE_COLOR); - graphics2D.drawOval(location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); + graphics2D.drawOval( + location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); } } } @@ -290,4 +309,4 @@ public PathIterator getPathIterator(AffineTransform at) { public PathIterator getPathIterator(AffineTransform at, double flatness) { return null; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java index ac7accaee..b6a29f2b5 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java @@ -10,15 +10,11 @@ import edu.rpi.legup.model.tree.Tree; import edu.rpi.legup.ui.DynamicView; import edu.rpi.legup.ui.DynamicViewType; -import edu.rpi.legup.ui.LegupUI; import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialFonts; - import java.awt.*; - +import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; - -import javax.swing.BorderFactory; import javax.swing.border.TitledBorder; public class TreePanel extends JPanel { @@ -33,8 +29,8 @@ public class TreePanel extends JPanel { private JLabel status; - public TreePanel(/*LegupUI legupUI*/) { - //this.legupUI = legupUI; + public TreePanel(/*LegupUI legupUI*/ ) { + // this.legupUI = legupUI; main = new JPanel(); @@ -72,7 +68,7 @@ public void boardDataChanged(Board board) { modifiedSinceSave = true; modifiedSinceUndoPush = true; updateStatus(); - //colorTransitions(); + // colorTransitions(); } public void updateStatus() { @@ -106,8 +102,7 @@ public void add() { if (add.canExecute()) { add.execute(); GameBoardFacade.getInstance().getHistory().pushChange(add); - } - else { + } else { updateError(add.getError()); } } @@ -119,8 +114,7 @@ public void delete() { if (del.canExecute()) { del.execute(); GameBoardFacade.getInstance().getHistory().pushChange(del); - } - else { + } else { updateError(del.getError()); } } @@ -132,8 +126,7 @@ public void merge() { if (merge.canExecute()) { merge.execute(); GameBoardFacade.getInstance().getHistory().pushChange(merge); - } - else { + } else { updateError(merge.getError()); } } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java index c2f40e21a..002092155 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java @@ -1,7 +1,7 @@ package edu.rpi.legup.ui.proofeditorui.treeview; -import javax.swing.*; import java.awt.Dimension; +import javax.swing.*; public class TreeToolBarButton extends JButton { diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java index dd8a37c15..c805021be 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java @@ -1,5 +1,8 @@ package edu.rpi.legup.ui.proofeditorui.treeview; public enum TreeToolBarName { - ADD_CHILD, DEL_CHILD, MERGE, COLLAPSE + ADD_CHILD, + DEL_CHILD, + MERGE, + COLLAPSE } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java index 332fd64a0..8f3ebfc23 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java @@ -1,7 +1,7 @@ package edu.rpi.legup.ui.proofeditorui.treeview; -import javax.swing.*; import java.awt.*; +import javax.swing.*; public class TreeToolbarPanel extends JPanel { private TreePanel treePanel; @@ -9,17 +9,37 @@ public class TreeToolbarPanel extends JPanel { /** * TreeToolbarPanel Constructor - creates the tree tool mBar panel + * * @param treePanel treePanel input */ public TreeToolbarPanel(TreePanel treePanel) { this.treePanel = treePanel; this.setLayout(new GridLayout(4, 1, 0, 2)); - - addChild = new TreeToolBarButton(new ImageIcon(ClassLoader.getSystemResource("edu/rpi/legup/images/Legup/AddChild.png")), TreeToolBarName.ADD_CHILD); - delChild = new TreeToolBarButton(new ImageIcon(ClassLoader.getSystemResource("edu/rpi/legup/images/Legup/DelChild.png")), TreeToolBarName.DEL_CHILD); - merge = new TreeToolBarButton(new ImageIcon(ClassLoader.getSystemResource("edu/rpi/legup/images/Legup/Merge.png")), TreeToolBarName.MERGE); - collapse = new TreeToolBarButton(new ImageIcon(ClassLoader.getSystemResource("edu/rpi/legup/images/Legup/Collapse.png")), TreeToolBarName.COLLAPSE); + addChild = + new TreeToolBarButton( + new ImageIcon( + ClassLoader.getSystemResource( + "edu/rpi/legup/images/Legup/AddChild.png")), + TreeToolBarName.ADD_CHILD); + delChild = + new TreeToolBarButton( + new ImageIcon( + ClassLoader.getSystemResource( + "edu/rpi/legup/images/Legup/DelChild.png")), + TreeToolBarName.DEL_CHILD); + merge = + new TreeToolBarButton( + new ImageIcon( + ClassLoader.getSystemResource( + "edu/rpi/legup/images/Legup/Merge.png")), + TreeToolBarName.MERGE); + collapse = + new TreeToolBarButton( + new ImageIcon( + ClassLoader.getSystemResource( + "edu/rpi/legup/images/Legup/Collapse.png")), + TreeToolBarName.COLLAPSE); add(addChild); addChild.addActionListener(a -> treePanel.add()); @@ -38,4 +58,4 @@ public TreeToolbarPanel(TreePanel treePanel) { collapse.setToolTipText("Collapse nodes"); collapse.setEnabled(false); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java index 749af0c79..b022ac596 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java @@ -1,15 +1,14 @@ package edu.rpi.legup.ui.proofeditorui.treeview; +import static java.lang.Math.*; + +import edu.rpi.legup.app.LegupPreferences; import edu.rpi.legup.model.tree.TreeElementType; import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.app.LegupPreferences; - import java.awt.*; import java.awt.geom.*; -import java.util.List; import java.util.ArrayList; - -import static java.lang.Math.*; +import java.util.List; public class TreeTransitionView extends TreeElementView { static final int RADIUS = 25; @@ -86,8 +85,15 @@ public void draw(Graphics2D graphics2D) { double ctrlx2 = lineEndPoint.x - 25; double ctrly2 = lineEndPoint.y; - c.setCurve(lineStartPoint.x, lineStartPoint.y, ctrlx1, - ctrly1, ctrlx2, ctrly2, lineEndPoint.x, lineEndPoint.y); + c.setCurve( + lineStartPoint.x, + lineStartPoint.y, + ctrlx1, + ctrly1, + ctrlx2, + ctrly2, + lineEndPoint.x, + lineEndPoint.y); graphics2D.draw(c); } @@ -100,16 +106,13 @@ public void draw(Graphics2D graphics2D) { if (getTreeElement().isCorrect()) { if (colorBlind) { c = CORRECT_COLOR_COLORBLIND; - } - else { + } else { c = CORRECT_COLOR; } - } - else { + } else { if (colorBlind) { c = INCORRECT_COLOR_COLORBLIND; - } - else { + } else { c = INCORRECT_COLOR; } } @@ -127,8 +130,7 @@ public void draw(Graphics2D graphics2D) { graphics2D.setStroke(SELECTION_STROKE); graphics2D.setColor(OUTLINE_SELECTION_COLOR); graphics2D.drawPolygon(selection_triangle); - } - else { + } else { if (isHover) { graphics2D.setColor(HOVER_COLOR); graphics2D.fillPolygon(arrowhead); @@ -142,23 +144,19 @@ public void draw(Graphics2D graphics2D) { graphics2D.setStroke(SELECTION_STROKE); graphics2D.setColor(OUTLINE_HOVER_COLOR); graphics2D.drawPolygon(selection_triangle); - } - else { + } else { Color c = DEFAULT_COLOR; if (getTreeElement().isJustified()) { if (getTreeElement().isCorrect()) { if (colorBlind) { c = CORRECT_COLOR_COLORBLIND; - } - else { + } else { c = CORRECT_COLOR; } - } - else { + } else { if (colorBlind) { c = INCORRECT_COLOR_COLORBLIND; - } - else { + } else { c = INCORRECT_COLOR; } } @@ -172,9 +170,7 @@ public void draw(Graphics2D graphics2D) { } } - /** - * Constructs the arrowhead shape from the start and end points - */ + /** Constructs the arrowhead shape from the start and end points */ private Polygon createTransitionTriangle(int radius) { double thetaArrow = Math.toRadians(30); 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 9bfffe60a..f491009b4 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,9 +1,13 @@ package edu.rpi.legup.ui.proofeditorui.treeview; +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; + 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; @@ -14,23 +18,16 @@ 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 javax.swing.*; 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 Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); private static final int TRANS_GAP = 5; @@ -88,7 +85,8 @@ public void setNodeHover(TreeNodeView nodeHover) { } /** - * Gets the TreeElementView by the specified point or null if no view exists at the specified point + * 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 @@ -98,25 +96,24 @@ public TreeElementView getTreeElementView(Point point) { } /** - * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or - * the view specified is null + * 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 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 { + } else { if (elementView.contains(point) && elementView.isVisible()) { - if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { + if (elementView.getType() == NODE + && ((TreeNodeView) elementView).isContradictoryState()) { return null; } return elementView; - } - else { + } else { if (elementView.getType() == NODE) { TreeNodeView nodeView = (TreeNodeView) elementView; for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { @@ -125,8 +122,7 @@ private TreeElementView getTreeElementView(Point point, TreeElementView elementV return view; } } - } - else { + } else { TreeTransitionView transitionView = (TreeTransitionView) elementView; return getTreeElementView(point, transitionView.getChildView()); } @@ -220,15 +216,17 @@ 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); + // 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); + // graphics2D.drawRect(0,0, dimension.width, dimension.height); if (selection.getHover() != null) { drawMouseOver(graphics2D); @@ -255,10 +253,11 @@ public void removeTreeElement(TreeElementView view) { if (view.getType() == NODE) { TreeNodeView nodeView = (TreeNodeView) view; nodeView.getParentView().setChildView(null); - } - else { + } else { TreeTransitionView transitionView = (TreeTransitionView) view; - transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); + transitionView + .getParentViews() + .forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); } } @@ -268,13 +267,16 @@ public void removeTreeElement(TreeElementView view) { * @param g the graphics to use to draw */ public void drawMouseOver(Graphics2D g) { - if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { + 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); + 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); } @@ -296,8 +298,7 @@ public void resetView() { public void onTreeElementAdded(TreeElement treeElement) { if (treeElement.getType() == NODE) { addTreeNode((TreeNode) treeElement); - } - else { + } else { addTreeTransition((TreeTransition) treeElement); } repaint(); @@ -316,8 +317,7 @@ public void onTreeElementRemoved(TreeElement element) { nodeView.getParentView().setChildView(null); removeTreeNode(node); - } - else { + } else { TreeTransition trans = (TreeTransition) element; TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); @@ -330,7 +330,7 @@ public void onTreeElementRemoved(TreeElement element) { continue; } - CaseRule caseRule = (CaseRule)rule; + 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) { @@ -339,22 +339,29 @@ public void onTreeElementRemoved(TreeElement element) { continue; } - for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), trans.getSelection())) { + for (PuzzleElement pelement : + caseRule.dependentElements(node.getBoard(), trans.getSelection())) { // decrement, unlock if 0 cases depended - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); + PuzzleElement oldElement = + ancestor.getParent().getBoard().getPuzzleElement(pelement); oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); if (oldElement.getCasesDepended() != 0) { - continue; + continue; } // set modifiable if started modifiable - boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); + boolean modifiable = + tree.getRootNode() + .getBoard() + .getPuzzleElement(oldElement) + .isModifiable(); // unmodifiable if already modified TreeNode modNode = ancestor.getParent().getParents().get(0); - while (modNode.getParent()!=null) { + while (modNode.getParent() != null) { Board modBoard = modNode.getParent().getBoard(); - if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { + if (modBoard.getModifiedData() + .contains(modBoard.getPuzzleElement(oldElement))) { modifiable = false; break; } @@ -364,7 +371,7 @@ public void onTreeElementRemoved(TreeElement element) { } } } - + transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); removeTreeTransition(trans); } @@ -384,9 +391,7 @@ public void onTreeSelectionChanged(TreeViewSelection selection) { repaint(); } - /** - * Called when the model has finished updating the tree. - */ + /** Called when the model has finished updating the tree. */ @Override public void onUpdateTree() { repaint(); @@ -410,7 +415,7 @@ private void removeTreeNode(TreeNode node) { if (!children.isEmpty()) { Rule rule = children.get(0).getRule(); if (rule instanceof CaseRule) { - CaseRule caseRule = (CaseRule)rule; + 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) { @@ -418,22 +423,30 @@ private void removeTreeNode(TreeNode node) { if (ancestor.getParent() == null) { continue; } - for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), children.get(0).getSelection())) { + for (PuzzleElement pelement : + caseRule.dependentElements( + node.getBoard(), children.get(0).getSelection())) { // decrement, unlock if 0 cases depended - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); + 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(); + 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))) { + if (modBoard.getModifiedData() + .contains(modBoard.getPuzzleElement(oldElement))) { modifiable = false; break; } @@ -466,11 +479,11 @@ private void addTreeNode(TreeNode node) { 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; + CaseRule caseRule = (CaseRule) rule; List ancestors = node.getAncestors(); for (TreeNode ancestor : ancestors) { @@ -478,15 +491,18 @@ private void addTreeNode(TreeNode node) { if (ancestor.getParent() == null) { continue; } - for (PuzzleElement element : caseRule.dependentElements(node.getBoard(), node.getChildren().get(0).getSelection())) { + 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); + PuzzleElement oldElement = + ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended() + 1); oldElement.setModifiable(false); } } } - + node.getChildren().forEach(t -> addTreeTransition(t)); } } @@ -499,11 +515,11 @@ private void addTreeTransition(TreeTransition trans) { 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; + if (rule instanceof CaseRule && parent.getChildren().size() == 1) { + CaseRule caseRule = (CaseRule) rule; List ancestors = parent.getAncestors(); for (TreeNode ancestor : ancestors) { @@ -511,10 +527,12 @@ private void addTreeTransition(TreeTransition trans) { if (ancestor.getParent() == null) { continue; } - for (PuzzleElement element : caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { + for (PuzzleElement element : + caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { // increment and lock - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); - oldElement.setCasesDepended(oldElement.getCasesDepended()+1); + PuzzleElement oldElement = + ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended() + 1); oldElement.setModifiable(false); } } @@ -528,13 +546,12 @@ private void addTreeTransition(TreeTransition trans) { } } - ///New Draw Methods + /// New Draw Methods public void drawTree(Graphics2D graphics2D) { if (tree == null) { LOGGER.error("Unable to draw tree."); - } - else { + } else { if (rootNodeView == null) { rootNodeView = new TreeNodeView(tree.getRootNode()); @@ -596,52 +613,29 @@ public void calculateViewLocations(TreeNodeView nodeView, int depth) { dimension.width = Math.max(dimension.width, xLoc); TreeTransitionView parentTransView = nodeView.getParentView(); - int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); + 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); + case 1: + { + TreeTransitionView childView = children.get(0); - 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); + List parentsViews = childView.getParentViews(); + if (parentsViews.size() == 1) { + childView.setEndY(yLoc); childView.setDepth(depth); - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + 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()); @@ -649,36 +643,71 @@ public void calculateViewLocations(TreeNodeView nodeView, int depth) { 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; } - 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); + default: + { + int span = 0; + for (TreeTransitionView childView : children) { + span += childView.getSpan(); + } - childView.setDepth(depth); + span = (int) ((nodeView.getSpan() - span) / 2); + for (int i = 0; i < children.size(); i++) { + TreeTransitionView childView = children.get(i); - 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)); + childView.setDepth(depth); - span += childView.getSpan(); - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); + 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; } - break; - } } } @@ -688,19 +717,16 @@ public void calcSpan(TreeElementView view) { TreeNode node = nodeView.getTreeElement(); if (nodeView.getChildrenViews().size() == 0) { nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { + } 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 { + } else { nodeView.setSpan(childView.getSpan()); } - } - else { + } else { DisjointSets branches = node.findMergingBranches(); List children = node.getChildren(); @@ -719,17 +745,18 @@ public void calcSpan(TreeElementView view) { for (Set mergeSet : mergingSets) { if (mergeSet.size() > 1) { TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + TreeTransitionView mergePointView = + (TreeTransitionView) viewMap.get(mergePoint); double subSpan = 0.0; for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + TreeTransitionView branchView = + (TreeTransitionView) viewMap.get(branch); subCalcSpan(branchView, mergePointView); subSpan += branchView.getSpan(); } calcSpan(mergePointView); span += Math.max(mergePointView.getSpan(), subSpan); - } - else { + } else { TreeTransition trans = mergeSet.iterator().next(); TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); calcSpan(transView); @@ -739,14 +766,12 @@ public void calcSpan(TreeElementView view) { nodeView.setSpan(span); } } - } - else { + } else { TreeTransitionView transView = (TreeTransitionView) view; TreeNodeView nodeView = transView.getChildView(); if (nodeView == null) { transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { + } else { calcSpan(nodeView); transView.setSpan(nodeView.getSpan()); } @@ -754,14 +779,15 @@ public void calcSpan(TreeElementView view) { } /** - * 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 + * 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 + // safe-guard for infinite loop if (view == stop) { return; } @@ -771,24 +797,20 @@ private void subCalcSpan(TreeElementView view, TreeElementView stop) { TreeNode node = nodeView.getTreeElement(); if (nodeView.getChildrenViews().size() == 0) { nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { + } else { if (nodeView.getChildrenViews().size() == 1) { TreeTransitionView childView = nodeView.getChildrenViews().get(0); if (childView == stop) { nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { + } else { subCalcSpan(childView, stop); if (childView.getParentViews().size() > 1) { nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { + } else { nodeView.setSpan(childView.getSpan()); } } - } - else { + } else { DisjointSets branches = node.findMergingBranches(); List children = node.getChildren(); @@ -802,17 +824,18 @@ private void subCalcSpan(TreeElementView view, TreeElementView stop) { for (Set mergeSet : mergingSets) { if (mergeSet.size() > 1) { TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + TreeTransitionView mergePointView = + (TreeTransitionView) viewMap.get(mergePoint); double subSpan = 0.0; for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + TreeTransitionView branchView = + (TreeTransitionView) viewMap.get(branch); subCalcSpan(branchView, mergePointView); subSpan += branchView.getSpan(); } subCalcSpan(mergePointView, stop); span += Math.max(mergePointView.getSpan(), subSpan); - } - else { + } else { TreeTransition trans = mergeSet.iterator().next(); TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); subCalcSpan(transView, stop); @@ -823,14 +846,12 @@ private void subCalcSpan(TreeElementView view, TreeElementView stop) { nodeView.setSpan(span); } } - } - else { + } else { TreeTransitionView transView = (TreeTransitionView) view; TreeNodeView nodeView = transView.getChildView(); if (nodeView == null || nodeView == stop) { transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { + } else { calcSpan(nodeView); transView.setSpan(nodeView.getSpan()); } @@ -838,11 +859,12 @@ private void subCalcSpan(TreeElementView view, TreeElementView stop) { } /** - * 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 + * 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 + * @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(); @@ -852,35 +874,38 @@ private void reorderBranches(TreeNode node, DisjointSets branche 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); + 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; - }); + 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 +} diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java index 042eb8880..71a65b49e 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java @@ -1,17 +1,15 @@ package edu.rpi.legup.ui.proofeditorui.treeview; import java.awt.*; -import java.util.List; import java.util.ArrayList; +import java.util.List; public class TreeViewSelection { private ArrayList selectedViews; private TreeElementView hover; private Point mousePoint; - /** - * TreeViewSelection Constructor creates a tree view selection - */ + /** TreeViewSelection Constructor creates a tree view selection */ public TreeViewSelection() { this.selectedViews = new ArrayList<>(); this.hover = null; @@ -38,7 +36,6 @@ public TreeViewSelection(List views) { this.selectedViews.addAll(views); } - /** * Gets the list of selected tree puzzleElement views * @@ -66,8 +63,7 @@ public void toggleSelection(TreeElementView treeElementView) { if (selectedViews.contains(treeElementView)) { selectedViews.remove(treeElementView); treeElementView.setSelected(false); - } - else { + } else { selectedViews.add(treeElementView); treeElementView.setSelected(true); } @@ -96,9 +92,7 @@ public void newSelection(TreeElementView treeElementView) { treeElementView.setSelected(true); } - /** - * Clears all selected views - */ + /** Clears all selected views */ public void clearSelection() { for (TreeElementView treeElementView : selectedViews) { treeElementView.setSelected(false); @@ -109,7 +103,8 @@ public void clearSelection() { /** * Gets tree puzzleElement view that the mouse is hovering over or null is no such view exists * - * @return tree puzzleElement view that the mouse is hovering over or null is no such view exists + * @return tree puzzleElement view that the mouse is hovering over or null is no such view + * exists */ public TreeElementView getHover() { return hover; @@ -128,9 +123,7 @@ public void newHover(TreeElementView newHovered) { hover = newHovered; } - /** - * Clears the current hover tree puzzleElement view - */ + /** Clears the current hover tree puzzleElement view */ public void clearHover() { if (hover != null) { hover.setHover(false); diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java index de2281ad1..552d517b9 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementButton.java @@ -1,11 +1,9 @@ package edu.rpi.legup.ui.puzzleeditorui.elementsview; import edu.rpi.legup.model.elements.Element; -import edu.rpi.legup.model.rules.Rule; - +import java.awt.*; import javax.swing.*; import javax.swing.border.Border; -import java.awt.*; public class ElementButton extends JButton { diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java index 1fc2b3792..e0524f84d 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java @@ -3,10 +3,9 @@ import edu.rpi.legup.controller.EditorElementController; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.ui.lookandfeel.components.MaterialTabbedPaneUI; - +import java.awt.*; import javax.swing.*; import javax.swing.border.TitledBorder; -import java.awt.*; public class ElementFrame extends JPanel { private static final String checkBox = " \u2714 "; @@ -23,13 +22,14 @@ public class ElementFrame extends JPanel { public ElementFrame(EditorElementController controller) { this.controller = controller; - MaterialTabbedPaneUI tabOverride = new MaterialTabbedPaneUI() { - //this prevents the tabs from moving around when you select them - @Override - protected boolean shouldRotateTabRuns(int i) { - return false; - } - }; + MaterialTabbedPaneUI tabOverride = + new MaterialTabbedPaneUI() { + // this prevents the tabs from moving around when you select them + @Override + protected boolean shouldRotateTabRuns(int i) { + return false; + } + }; this.tabbedPane = new JTabbedPane(); tabbedPane.setUI(tabOverride); @@ -37,15 +37,22 @@ protected boolean shouldRotateTabRuns(int i) { this.buttonGroup = new ButtonGroup(); nonPlaceableElementPanel = new NonPlaceableElementPanel(this); - //nonPlaceableElementPanel.setMinimumSize(new Dimension(100,200)); - tabbedPane.addTab(nonPlaceableElementPanel.getName(), nonPlaceableElementPanel.getIcon(), new JScrollPane(nonPlaceableElementPanel), nonPlaceableElementPanel.getToolTip()); + // nonPlaceableElementPanel.setMinimumSize(new Dimension(100,200)); + tabbedPane.addTab( + nonPlaceableElementPanel.getName(), + nonPlaceableElementPanel.getIcon(), + new JScrollPane(nonPlaceableElementPanel), + nonPlaceableElementPanel.getToolTip()); placeableElementPanel = new PlaceableElementPanel(this); - //placeableElementPanel.setMinimuSize(new Dimension(100,200)); - tabbedPane.addTab(placeableElementPanel.getName(), placeableElementPanel.getIcon(), new JScrollPane(placeableElementPanel), placeableElementPanel.getToolTip()); + // placeableElementPanel.setMinimuSize(new Dimension(100,200)); + tabbedPane.addTab( + placeableElementPanel.getName(), + placeableElementPanel.getIcon(), + new JScrollPane(placeableElementPanel), + placeableElementPanel.getToolTip()); tabbedPane.setTabPlacement(JTabbedPane.TOP); - setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); setPreferredSize(new Dimension(330, 256)); @@ -63,14 +70,15 @@ public ButtonGroup getButtonGroup() { } public void resetSize() { - int buttonWidth = ((ElementPanel) tabbedPane.getSelectedComponent()).getElementButtons()[0].getWidth(); + int buttonWidth = + ((ElementPanel) tabbedPane.getSelectedComponent()) + .getElementButtons()[0].getWidth(); this.setMinimumSize(new Dimension(2 * buttonWidth + 64, this.getHeight())); } public void setElements(Puzzle puzzle) { nonPlaceableElementPanel.setElements(puzzle.getNonPlaceableElements()); placeableElementPanel.setElements(puzzle.getPlaceableElements()); - } public EditorElementController getController() { diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java index 2b0c4c63e..46198e226 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java @@ -1,13 +1,10 @@ package edu.rpi.legup.ui.puzzleeditorui.elementsview; -import edu.rpi.legup.model.rules.Rule; -import edu.rpi.legup.ui.WrapLayout; import edu.rpi.legup.model.elements.Element; -import edu.rpi.legup.ui.proofeditorui.rulesview.RuleButton; - -import javax.swing.*; +import edu.rpi.legup.ui.WrapLayout; import java.util.ArrayList; import java.util.List; +import javax.swing.*; public abstract class ElementPanel extends JPanel { protected ImageIcon icon; @@ -35,7 +32,8 @@ public void setElements(List elements) { elementFrame.getButtonGroup().add(elementButtons[i]); System.out.printf("added button: %d, element %s\n", i, element.getElementName()); - elementButtons[i].setToolTipText(element.getElementName() + ": " + element.getDescription()); + elementButtons[i].setToolTipText( + element.getElementName() + ": " + element.getDescription()); elementButtons[i].addActionListener(elementFrame.getController()); add(elementButtons[i]); } @@ -81,4 +79,3 @@ public void setToolTip(String toolTip) { this.toolTip = toolTip; } } - diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java index 7920f564c..00b4f5379 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java @@ -5,7 +5,10 @@ public class NonPlaceableElementPanel extends ElementPanel { public NonPlaceableElementPanel(ElementFrame elementFrame) { super(elementFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); this.name = "Non-Placeable Elements"; this.toolTip = "Non-Placeable Elements"; } diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java index 1fa8eabfb..088e18f8c 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/PlaceableElementPanel.java @@ -5,7 +5,10 @@ public class PlaceableElementPanel extends ElementPanel { public PlaceableElementPanel(ElementFrame elementFrame) { super(elementFrame); - this.icon = new ImageIcon(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); this.name = "Placeable Elements"; this.toolTip = "Placeable Elements"; } diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java index 7cb7856e7..902d9dbd3 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/resizeview/ResizePanel.java @@ -1,4 +1,3 @@ package edu.rpi.legup.ui.puzzleeditorui.resizeview; -public class ResizePanel { -} +public class ResizePanel {} diff --git a/src/main/java/edu/rpi/legup/user/Submission.java b/src/main/java/edu/rpi/legup/user/Submission.java index eb2b8446c..42373d2ec 100644 --- a/src/main/java/edu/rpi/legup/user/Submission.java +++ b/src/main/java/edu/rpi/legup/user/Submission.java @@ -3,11 +3,7 @@ import edu.rpi.legup.model.gameboard.Board; public class Submission { - public Submission(Board board) { + public Submission(Board board) {} - } - - public void submit() { - - } + public void submit() {} } diff --git a/src/main/java/edu/rpi/legup/user/UsageStatistics.java b/src/main/java/edu/rpi/legup/user/UsageStatistics.java index 192db593f..0bc5d1c62 100644 --- a/src/main/java/edu/rpi/legup/user/UsageStatistics.java +++ b/src/main/java/edu/rpi/legup/user/UsageStatistics.java @@ -1,27 +1,19 @@ package edu.rpi.legup.user; +import java.io.IOException; +import java.io.InputStream; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; public class UsageStatistics { private static final String url = "https://legup-3b4a5.firebaseio.com/databases/test.json"; - public UsageStatistics() { - - } + public UsageStatistics() {} public boolean sendErrorReport() { try { @@ -31,12 +23,12 @@ public boolean sendErrorReport() { // Request parameters and other properties. httppost.setEntity(new StringEntity("{\"test\": \"jeff\"}")); -// List params = new ArrayList<>(2); -// params.add(new BasicNameValuePair("param-1", "12345")); -// params.add(new BasicNameValuePair("param-2", "Hello!")); -// httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); + // List params = new ArrayList<>(2); + // params.add(new BasicNameValuePair("param-1", "12345")); + // params.add(new BasicNameValuePair("param-2", "Hello!")); + // httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); - //Execute and get the response. + // Execute and get the response. HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); @@ -44,14 +36,12 @@ public boolean sendErrorReport() { InputStream instream = entity.getContent(); try { -// System.err.println(new String(instream.readAllBytes())); - } - finally { + // System.err.println(new String(instream.readAllBytes())); + } finally { instream.close(); } } - } - catch (IOException e) { + } catch (IOException e) { return false; } return false; diff --git a/src/main/java/edu/rpi/legup/user/User.java b/src/main/java/edu/rpi/legup/user/User.java index 2e542a96c..63280a72d 100644 --- a/src/main/java/edu/rpi/legup/user/User.java +++ b/src/main/java/edu/rpi/legup/user/User.java @@ -1,5 +1,3 @@ package edu.rpi.legup.user; -public class User { - -} +public class User {} diff --git a/src/main/java/edu/rpi/legup/utility/ConnectedRegions.java b/src/main/java/edu/rpi/legup/utility/ConnectedRegions.java index ce4b74bdc..14b68ba40 100644 --- a/src/main/java/edu/rpi/legup/utility/ConnectedRegions.java +++ b/src/main/java/edu/rpi/legup/utility/ConnectedRegions.java @@ -7,13 +7,15 @@ import java.util.Set; public final class ConnectedRegions { - public static List> getConnectedRegions(int boundaryCell, int[][] cells, int width, int height) { + public static List> getConnectedRegions( + int boundaryCell, int[][] cells, int width, int height) { Set boundaryCells = new HashSet<>(); boundaryCells.add(boundaryCell); return getConnectedRegions(boundaryCells, cells, width, height); } - public static List> getConnectedRegions(Set boundaryCells, int[][] cells, int width, int height) { + public static List> getConnectedRegions( + Set boundaryCells, int[][] cells, int width, int height) { boolean[][] visited = new boolean[height][width]; List> results = new ArrayList<>(); for (int y = 0; y < height; y++) { @@ -36,17 +38,26 @@ public static boolean regionContains(int toFind, int[][] cells, Set regio return false; } - public static Set getRegionAroundPoint(Point p, int boundaryCell, int[][] cells, int width, int height) { + public static Set getRegionAroundPoint( + Point p, int boundaryCell, int[][] cells, int width, int height) { Set boundaryCells = new HashSet<>(); boundaryCells.add(boundaryCell); return getRegionAroundPoint(p, boundaryCells, cells, width, height); } - public static Set getRegionAroundPoint(Point p, Set boundaryCells, int[][] cells, int width, int height) { + public static Set getRegionAroundPoint( + Point p, Set boundaryCells, int[][] cells, int width, int height) { return floodfill(boundaryCells, cells, new boolean[height][width], width, height, p.x, p.y); } - private static Set floodfill(Set boundaryCells, int[][] cells, boolean[][] visited, int w, int h, int x, int y) { + private static Set floodfill( + Set boundaryCells, + int[][] cells, + boolean[][] visited, + int w, + int h, + int x, + int y) { HashSet result = new HashSet<>(); if ((x < 0) || (x >= w)) { return result; @@ -64,4 +75,4 @@ private static Set floodfill(Set boundaryCells, int[][] cells, b } return result; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/utility/DisjointSets.java b/src/main/java/edu/rpi/legup/utility/DisjointSets.java index 1e2270318..f33b8993c 100644 --- a/src/main/java/edu/rpi/legup/utility/DisjointSets.java +++ b/src/main/java/edu/rpi/legup/utility/DisjointSets.java @@ -7,9 +7,7 @@ public class DisjointSets { private Map depths; private Map> sets; - /** - * DisjointSets Constructor creates an empty DisjointSets - */ + /** DisjointSets Constructor creates an empty DisjointSets */ public DisjointSets() { this.parents = new HashMap<>(); this.depths = new HashMap<>(); @@ -17,8 +15,9 @@ public DisjointSets() { } /** - * Creates a unique set that contains the specified puzzleElement. If the specified puzzleElement is null or another set already - * contains that puzzleElement, this method returns false, indicating that a set was not created + * Creates a unique set that contains the specified puzzleElement. If the specified + * puzzleElement is null or another set already contains that puzzleElement, this method returns + * false, indicating that a set was not created * * @param u puzzleElement to create the set from * @return true if the set was created, false otherwise @@ -26,8 +25,7 @@ public DisjointSets() { public boolean createSet(T u) { if (u == null || parents.containsKey(u)) { return false; - } - else { + } else { parents.put(u, u); depths.put(u, 0); Set newSet = new HashSet<>(); @@ -38,16 +36,17 @@ public boolean createSet(T u) { } /** - * Finds and returns the representative set puzzleElement of the set that the specified puzzleElement contains + * Finds and returns the representative set puzzleElement of the set that the specified + * puzzleElement contains * * @param p puzzleElement of the set of which to find - * @return representative set puzzleElement or null if the specified puzzleElement is null or is not in the DisjointSets + * @return representative set puzzleElement or null if the specified puzzleElement is null or is + * not in the DisjointSets */ public T find(T p) { if (p == null || parents.get(p) == null) { return null; - } - else { + } else { if (p != parents.get(p)) { parents.put(p, find(parents.get(p))); } @@ -56,7 +55,8 @@ public T find(T p) { } /** - * Unions two sets together. If the set are non-null and disjoint, then it returns true, false otherwise + * Unions two sets together. If the set are non-null and disjoint, then it returns true, false + * otherwise * * @param p set one * @param q set two @@ -67,14 +67,12 @@ public boolean union(T p, T q) { T qid = find(q); if (pid == null || qid == null || pid == qid) { return false; - } - else { + } else { if (depths.get(pid) > depths.get(qid)) { parents.put(qid, pid); sets.get(pid).addAll(sets.get(qid)); sets.remove(qid); - } - else { + } else { parents.put(pid, qid); sets.get(qid).addAll(sets.get(pid)); sets.remove(pid); @@ -87,8 +85,9 @@ public boolean union(T p, T q) { } /** - * Unions to elements together, if either puzzleElement is not already in the DisjointSets, it creates a set for the - * puzzleElement then unions the sets together. If either puzzleElement is null, no action is taken. + * Unions to elements together, if either puzzleElement is not already in the DisjointSets, it + * creates a set for the puzzleElement then unions the sets together. If either puzzleElement is + * null, no action is taken. * * @param p puzzleElement one * @param q puzzleElement two @@ -118,17 +117,18 @@ public boolean contains(T u) { } /** - * Gets the set of elements that the specified puzzleElement is contained in, or null if no such set exists. + * Gets the set of elements that the specified puzzleElement is contained in, or null if no such + * set exists. * * @param p puzzleElement to get the set of - * @return the set of elements that the specified puzzleElement if contained in, or null if no such set exists + * @return the set of elements that the specified puzzleElement if contained in, or null if no + * such set exists */ public Set getSet(T p) { T pid = find(p); if (pid != null) { return new HashSet<>(sets.get(pid)); - } - else { + } else { return null; } } diff --git a/src/main/java/edu/rpi/legup/utility/Entry.java b/src/main/java/edu/rpi/legup/utility/Entry.java index 1819f7eb0..0686f0dbf 100644 --- a/src/main/java/edu/rpi/legup/utility/Entry.java +++ b/src/main/java/edu/rpi/legup/utility/Entry.java @@ -7,7 +7,7 @@ public class Entry { /** * Entry Constructor creates a key value pair * - * @param key key + * @param key key * @param value value */ public Entry(K key, V value) { diff --git a/src/main/java/edu/rpi/legup/utility/LegupUtils.java b/src/main/java/edu/rpi/legup/utility/LegupUtils.java index dbdd24e7f..94f119a5e 100644 --- a/src/main/java/edu/rpi/legup/utility/LegupUtils.java +++ b/src/main/java/edu/rpi/legup/utility/LegupUtils.java @@ -17,7 +17,8 @@ public class LegupUtils { private static final Logger LOGGER = Logger.getLogger(LegupUtils.class.getName()); /** - * Scans all classes accessible from the context class loader which belong to the given package and subpackages. + * Scans all classes accessible from the context class loader which belong to the given package + * and subpackages. * * @param packageName The base package * @return The classes @@ -54,12 +55,13 @@ public static Class[] getClasses(String packageName) /** * Recursive method used to find all classes in a given directory and subdirs. * - * @param directory The base directory + * @param directory The base directory * @param packageName The package name for classes found inside the base directory * @return The classes * @throws ClassNotFoundException */ - private static List findClasses(File directory, String packageName) throws ClassNotFoundException { + private static List findClasses(File directory, String packageName) + throws ClassNotFoundException { List classes = new ArrayList<>(); if (!directory.exists()) { return classes; @@ -69,23 +71,32 @@ private static List findClasses(File directory, String packageName) throw if (file.isDirectory()) { assert !file.getName().contains("."); classes.addAll(findClasses(file, packageName + "." + file.getName())); - } - else { + } else { if (file.getName().endsWith(".class")) { - classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); + classes.add( + Class.forName( + packageName + + '.' + + file.getName() + .substring(0, file.getName().length() - 6))); } } } return classes; } - private static List findClassesZip(String path, String packageName) throws IOException, ClassNotFoundException { + private static List findClassesZip(String path, String packageName) + throws IOException, ClassNotFoundException { List classes = new ArrayList<>(); ZipInputStream zip = new ZipInputStream(new FileInputStream(path)); for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { - if (!entry.isDirectory() && entry.getName().endsWith(".class") && entry.getName().startsWith(packageName)) { + if (!entry.isDirectory() + && entry.getName().endsWith(".class") + && entry.getName().startsWith(packageName)) { String className = entry.getName().replace('/', '.'); - classes.add(Class.forName(className.substring(0, className.length() - ".class".length()))); + classes.add( + Class.forName( + className.substring(0, className.length() - ".class".length()))); } } return classes; diff --git a/src/main/java/edu/rpi/legup/utility/Logger.java b/src/main/java/edu/rpi/legup/utility/Logger.java index 1feb56f04..67048e5b4 100644 --- a/src/main/java/edu/rpi/legup/utility/Logger.java +++ b/src/main/java/edu/rpi/legup/utility/Logger.java @@ -1,6 +1,6 @@ package edu.rpi.legup.utility; -import edu.rpi.legup.Legup; +import java.io.File; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.ConsoleAppender; @@ -10,27 +10,31 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.layout.PatternLayout; -import java.io.File; - public class Logger { - private static final String LEGUP_HOME = System.getProperty("user.home") + File.separator + ".legup" + File.separator; + private static final String LEGUP_HOME = + System.getProperty("user.home") + File.separator + ".legup" + File.separator; public static void initLogger() { LoggerContext context = (LoggerContext) LogManager.getContext(false); Configuration config = context.getConfiguration(); ConsoleAppender consoleAppender = config.getAppender("console"); PatternLayout consolePattern = (PatternLayout) consoleAppender.getLayout(); - TimeBasedTriggeringPolicy triggeringPolicy = TimeBasedTriggeringPolicy.newBuilder().withInterval(1).withModulate(true).build(); - PatternLayout patternLayout = PatternLayout.newBuilder().withPattern(consolePattern.getConversionPattern()).build(); - RollingFileAppender rollingFileAppender = RollingFileAppender.newBuilder() - .setName("fileLogger") - .withFileName(LEGUP_HOME + "legup.log") - .withFilePattern(LEGUP_HOME + "legup-%d{yyyy-MM-dd}.log.gz") - .withPolicy(triggeringPolicy) - .setLayout(patternLayout) - .setConfiguration(config) - .build(); + TimeBasedTriggeringPolicy triggeringPolicy = + TimeBasedTriggeringPolicy.newBuilder().withInterval(1).withModulate(true).build(); + PatternLayout patternLayout = + PatternLayout.newBuilder() + .withPattern(consolePattern.getConversionPattern()) + .build(); + RollingFileAppender rollingFileAppender = + RollingFileAppender.newBuilder() + .setName("fileLogger") + .withFileName(LEGUP_HOME + "legup.log") + .withFilePattern(LEGUP_HOME + "legup-%d{yyyy-MM-dd}.log.gz") + .withPolicy(triggeringPolicy) + .setLayout(patternLayout) + .setConfiguration(config) + .build(); rollingFileAppender.start(); config.addAppender(rollingFileAppender); LoggerConfig rootLogger = config.getRootLogger(); @@ -39,5 +43,4 @@ public static void initLogger() { System.setProperty("sun.java2d.noddraw", Boolean.TRUE.toString()); } - } diff --git a/src/test/java/legup/MockGameBoardFacade.java b/src/test/java/legup/MockGameBoardFacade.java index 7840b61b7..f4f1db938 100644 --- a/src/test/java/legup/MockGameBoardFacade.java +++ b/src/test/java/legup/MockGameBoardFacade.java @@ -11,8 +11,7 @@ protected MockGameBoardFacade() { Config config = null; try { config = new Config(); - } - catch (InvalidConfigException e) { + } catch (InvalidConfigException e) { System.exit(1); } setConfig(config); @@ -23,7 +22,7 @@ protected MockGameBoardFacade() { * * @return single instance of GameBoardFacade */ - public synchronized static GameBoardFacade getInstance() { + public static synchronized GameBoardFacade getInstance() { if (instance == null) { instance = new MockGameBoardFacade(); } @@ -31,9 +30,7 @@ public synchronized static GameBoardFacade getInstance() { } @Override - public void initializeUI() { - - } + public void initializeUI() {} @Override public void setPuzzle(Puzzle puzzle) { @@ -41,7 +38,5 @@ public void setPuzzle(Puzzle puzzle) { } @Override - public void setWindowTitle(String puzzleName, String fileName) { - - } + public void setWindowTitle(String puzzleName, String fileName) {} } diff --git a/src/test/java/legup/TestRunner.java b/src/test/java/legup/TestRunner.java index 5e95a16bf..40cc5fa6c 100644 --- a/src/test/java/legup/TestRunner.java +++ b/src/test/java/legup/TestRunner.java @@ -1,6 +1,5 @@ package legup; - import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; @@ -10,9 +9,7 @@ import puzzles.skyscrapers.rules.*; import puzzles.treetent.rules.*; -/** - * This class runs all of the tests for the project without needing to run build scripts. - */ +/** 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 @@ -43,7 +40,7 @@ public static void main(String[] args) { Result result12 = JUnitCore.runClasses(TooManyBulbsContradictionRuleTest.class); printTestResults(result12); - //nurikabe tests + // nurikabe tests Result result13 = JUnitCore.runClasses(BlackBetweenRegionsDirectRuleTest.class); printTestResults(result13); Result result14 = JUnitCore.runClasses(BlackBottleNeckDirectRuleTest.class); diff --git a/src/test/java/legup/TestUtilities.java b/src/test/java/legup/TestUtilities.java index 9f203b223..83ce773d4 100644 --- a/src/test/java/legup/TestUtilities.java +++ b/src/test/java/legup/TestUtilities.java @@ -8,7 +8,8 @@ import edu.rpi.legup.save.InvalidFileFormatException; public final class TestUtilities { - public static void importTestBoard(String fileName, Puzzle puzzle) throws InvalidFileFormatException { + public static void importTestBoard(String fileName, Puzzle puzzle) + throws InvalidFileFormatException { puzzle.importPuzzle(ClassLoader.getSystemResourceAsStream(fileName)); Tree tree = puzzle.getTree(); TreeNode rootNode = tree.getRootNode(); diff --git a/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java b/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java index c9e65e9c1..08b6633db 100644 --- a/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java +++ b/src/test/java/puzzles/battleship/rules/AdjacentShipsContradictionRuleTest.java @@ -1,24 +1,20 @@ package puzzles.battleship.rules; -import edu.rpi.legup.puzzle.battleship.BattleshipType; -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; import edu.rpi.legup.puzzle.battleship.Battleship; import edu.rpi.legup.puzzle.battleship.BattleshipBoard; -import edu.rpi.legup.puzzle.battleship.BattleshipCell; import edu.rpi.legup.puzzle.battleship.rules.AdjacentShipsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; - import java.awt.*; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class AdjacentShipsContradictionRuleTest { - private static final AdjacentShipsContradictionRule RULE - = new AdjacentShipsContradictionRule(); + private static final AdjacentShipsContradictionRule RULE = new AdjacentShipsContradictionRule(); private static Battleship battleship; @@ -30,8 +26,9 @@ public static void setUp() { @Test public void OrthogonalAdjacentTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/AdjacentShipsContradictionRule/OrthogonalAdjacentBoards", + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + + "/AdjacentShipsContradictionRule/OrthogonalAdjacentBoards", battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -39,37 +36,38 @@ public void OrthogonalAdjacentTest() throws InvalidFileFormatException { BattleshipBoard board = (BattleshipBoard) transition.getBoard(); - Assert.assertNotNull(RULE.checkContradiction( - board)); + Assert.assertNotNull(RULE.checkContradiction(board)); } @Test public void InvalidOrthogonalAdjacentTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/AdjacentShipsContradictionRule" + - "/InvalidOrthogonalAdjacentBoards", battleship); + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + + "/AdjacentShipsContradictionRule" + + "/InvalidOrthogonalAdjacentBoards", + battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); BattleshipBoard board = (BattleshipBoard) transition.getBoard(); - Assert.assertNull(RULE.checkContradiction( - board)); + Assert.assertNull(RULE.checkContradiction(board)); } @Test public void DiagonalAdjacentTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/AdjacentShipsContradictionRule" + - "/DiagonalAdjacentBoards", battleship); + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + + "/AdjacentShipsContradictionRule" + + "/DiagonalAdjacentBoards", + battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); BattleshipBoard board = (BattleshipBoard) transition.getBoard(); - Assert.assertNull(RULE.checkContradiction( - board)); + Assert.assertNull(RULE.checkContradiction(board)); } } diff --git a/src/test/java/puzzles/battleship/rules/FinishWithShipsDirectRuleTests.java b/src/test/java/puzzles/battleship/rules/FinishWithShipsDirectRuleTests.java index ac7e8cb8e..1548445fd 100644 --- a/src/test/java/puzzles/battleship/rules/FinishWithShipsDirectRuleTests.java +++ b/src/test/java/puzzles/battleship/rules/FinishWithShipsDirectRuleTests.java @@ -1,18 +1,16 @@ package puzzles.battleship.rules; -import org.junit.*; - -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.battleship.*; import edu.rpi.legup.puzzle.battleship.rules.*; import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.*; public class FinishWithShipsDirectRuleTests { - private static final FinishWithShipsDirectRule RULE - = new FinishWithShipsDirectRule(); + private static final FinishWithShipsDirectRule RULE = new FinishWithShipsDirectRule(); private static Battleship battleship; @@ -22,10 +20,10 @@ public static void setUp() { battleship = new Battleship(); } - //@Test + // @Test public void HorizontalValidTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/FinishWithShipsBasicRuleTests/HorizontalValidBoard", + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + "/FinishWithShipsBasicRuleTests/HorizontalValidBoard", battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -34,10 +32,10 @@ public void HorizontalValidTest() throws InvalidFileFormatException { Assert.assertNull(RULE.checkRule(transition)); } - //@Test + // @Test public void VerticaValidTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/FinishWithShipsBasicRuleTests/VerticalValidBoard", + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + "/FinishWithShipsBasicRuleTests/VerticalValidBoard", battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -46,10 +44,11 @@ public void VerticaValidTest() throws InvalidFileFormatException { Assert.assertNull(RULE.checkRule(transition)); } - //@Test + // @Test public void HorizontalInvalidTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/FinishWithShipsBasicRuleTests/HorizontalInvalidBoard", + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + + "/FinishWithShipsBasicRuleTests/HorizontalInvalidBoard", battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -58,10 +57,10 @@ public void HorizontalInvalidTest() throws InvalidFileFormatException { Assert.assertNotNull(RULE.checkRule(transition)); } - //@Test + // @Test public void VerticalInvalidTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/battleship/rules" + - "/FinishWithShipsBasicRuleTests/VerticalInvalidBoard", + TestUtilities.importTestBoard( + "puzzles/battleship/rules" + "/FinishWithShipsBasicRuleTests/VerticalInvalidBoard", battleship); TreeNode rootNode = battleship.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); diff --git a/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java index 0e7930751..316a86f43 100644 --- a/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/BulbsInPathContradictionRuleTest.java @@ -1,14 +1,13 @@ package puzzles.lightup.rules; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; import edu.rpi.legup.puzzle.lightup.LightUp; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.rules.BulbsInPathContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; - +import legup.TestUtilities; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -22,31 +21,35 @@ public static void setUp() { } @Test - public void BulbsInPathContradictionRule_LightInHorizontalPath() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath", lightUp); + public void BulbsInPathContradictionRule_LightInHorizontalPath() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/lightup/rules/BulbsInPathContradictionRule/LightInHorizontalPath", + lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); - //confirm there is a contradiction somewhere on the board + // confirm there is a contradiction somewhere on the board Assert.assertNull(RULE.checkContradiction(board)); 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))); } @Test - public void BulbsInPathContradictionRule_LightInVerticalPath() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath", lightUp); + public void BulbsInPathContradictionRule_LightInVerticalPath() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/lightup/rules/BulbsInPathContradictionRule/LightInVerticalPath", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); - //confirm there is a contradiction somewhere on the board + // confirm there is a contradiction somewhere on the board Assert.assertNull(RULE.checkContradiction(board)); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); @@ -55,8 +58,10 @@ public void BulbsInPathContradictionRule_LightInVerticalPath() throws InvalidFil } @Test - public void BulbsInPathContradictionRule_BlockInVerticalPath() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath", lightUp); + public void BulbsInPathContradictionRule_BlockInVerticalPath() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInVerticalPath", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -70,17 +75,19 @@ public void BulbsInPathContradictionRule_BlockInVerticalPath() throws InvalidFil } @Test - public void BulbsInPathContradictionRule_BlockInHorizontalPath() throws InvalidFileFormatException{ - TestUtilities.importTestBoard("puzzles/lightup/rules/BulbsInPathContradictionRule/BlockInHorizontalPath", lightUp); + 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))); - + 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 7b3ffd2b9..6db42d39b 100644 --- a/src/test/java/puzzles/lightup/rules/CannotLightACellContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/CannotLightACellContradictionRuleTest.java @@ -1,19 +1,19 @@ package puzzles.lightup.rules; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; import edu.rpi.legup.puzzle.lightup.LightUp; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.rules.CannotLightACellContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; - +import legup.TestUtilities; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; public class CannotLightACellContradictionRuleTest { - private static final CannotLightACellContradictionRule RULE = new CannotLightACellContradictionRule(); + private static final CannotLightACellContradictionRule RULE = + new CannotLightACellContradictionRule(); private static LightUp lightUp; @BeforeClass @@ -22,22 +22,23 @@ public static void setUp() { } @Test - //extensive full testing of null and non-null in a 5x5 board + // 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); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/CannotLightACellContradictionRule/FullLightTest", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); - //confirm there is a contradiction somewhere on the board + // confirm there is a contradiction somewhere on the board Assert.assertNull(RULE.checkContradiction(board)); - //confirm it is impossible to light up these squares + // 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 + // 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))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); @@ -45,21 +46,22 @@ public void FullLightTest() throws InvalidFileFormatException { } @Test - //simple contradiction testing for null and non-null in a 3x3 board + // 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); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/CannotLightACellContradictionRule/CannotLight", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); - //confirm there is a contradiction somewhere on the board + // confirm there is a contradiction somewhere on the board Assert.assertNull(RULE.checkContradiction(board)); - //confirm it is impossible to light up the center square + // confirm it is impossible to light up the center square Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); - //every square except the center + // every square except the center Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); @@ -71,16 +73,17 @@ public void CannotLightMiddleTest() throws InvalidFileFormatException { @Test public void CanLightTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/CannotLightACellContradictionRule/CanLightTest", lightUp); + 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 + // 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 + // 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))); diff --git a/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java b/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java index b0a2d10ed..264cf928c 100644 --- a/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/EmptyCellinLightDirectRuleTest.java @@ -1,17 +1,17 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; -import edu.rpi.legup.puzzle.lightup.LightUp; -import edu.rpi.legup.puzzle.lightup.rules.EmptyCellinLightDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUp; import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import edu.rpi.legup.puzzle.lightup.rules.EmptyCellinLightDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class EmptyCellinLightDirectRuleTest { private static final EmptyCellinLightDirectRule RULE = new EmptyCellinLightDirectRule(); @@ -23,72 +23,80 @@ public static void setUp() { } @Test - //tests a 3x3 board with with a 0 black tile in the center and lightbulbs in top left and bototm right - //confirms the rest of the tiles must be empty - public void EmptyCellinLightDirectRule() throws InvalidFileFormatException{ - TestUtilities.importTestBoard("puzzles/lightup/rules/EmptyCellinLightDirectRule/EmptyCells", lightUp); + // tests a 3x3 board with with a 0 black tile in the center and lightbulbs in top left and + // bototm + // right + // confirms the rest of the tiles must be empty + public void EmptyCellinLightDirectRule() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/lightup/rules/EmptyCellinLightDirectRule/EmptyCells", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - //get board state + transition.setRule(RULE); + + // get board state LightUpBoard board = (LightUpBoard) transition.getBoard(); - //change the board's cells considering the emptycellinlight rule - LightUpCell cell2 = board.getCell(1,0); + // change the board's cells considering the emptycellinlight rule + LightUpCell cell2 = board.getCell(1, 0); cell2.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell2); - LightUpCell cell3 = board.getCell(0,1); + LightUpCell cell3 = board.getCell(0, 1); cell3.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell3); - LightUpCell cell4 = board.getCell(2,0); + LightUpCell cell4 = board.getCell(2, 0); cell4.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell4); - LightUpCell cell5 = board.getCell(0,2); + LightUpCell cell5 = board.getCell(0, 2); cell5.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell5); - LightUpCell cell6 = board.getCell(1,2); + LightUpCell cell6 = board.getCell(1, 2); cell6.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell6); - LightUpCell cell7 = board.getCell(2,1); + LightUpCell cell7 = board.getCell(2, 1); cell7.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell7); - LightUpCell cell8 = board.getCell(3,0); + LightUpCell cell8 = board.getCell(3, 0); cell8.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell8); - LightUpCell cell9 = board.getCell(3,2); + LightUpCell cell9 = board.getCell(3, 2); cell9.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell9); - LightUpCell cell10 = board.getCell(2,3); + LightUpCell cell10 = board.getCell(2, 3); cell10.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell10); - LightUpCell cell11 = board.getCell(0,3); + LightUpCell cell11 = board.getCell(0, 3); cell11.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell11); - //confirm there is a logical following of the EmptyCellinLight rule + // 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, (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 + // 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) || (i == 1 && j == 3) || (i == 3 && j == 1) - || (i==3 && j==3)){ + 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, c)); } } diff --git a/src/test/java/puzzles/lightup/rules/EmptyCornersDirectRuleTest.java b/src/test/java/puzzles/lightup/rules/EmptyCornersDirectRuleTest.java index bea69b95f..a4f958860 100644 --- a/src/test/java/puzzles/lightup/rules/EmptyCornersDirectRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/EmptyCornersDirectRuleTest.java @@ -1,23 +1,22 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; -import edu.rpi.legup.puzzle.lightup.LightUp; -import edu.rpi.legup.puzzle.lightup.rules.EmptyCornersDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUp; import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import edu.rpi.legup.puzzle.lightup.rules.EmptyCornersDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class EmptyCornersDirectRuleTest { private static final EmptyCornersDirectRule RULE = new EmptyCornersDirectRule(); private static LightUp lightUp; - @BeforeClass public static void setUp() { lightUp = new LightUp(); @@ -25,55 +24,55 @@ public static void setUp() { @Test public void EmptyCornersTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/EmptyCornersDirectRule/EmptyCorners", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/EmptyCornersDirectRule/EmptyCorners", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - //get board state + // get board state LightUpBoard board = (LightUpBoard) transition.getBoard(); - //change the board's cells considering the EmptyCorners rule to empty - LightUpCell cell1 = board.getCell(2,2); + // change the board's cells considering the EmptyCorners rule to empty + LightUpCell cell1 = board.getCell(2, 2); cell1.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell1); - LightUpCell cell2 = board.getCell(0,2); + LightUpCell cell2 = board.getCell(0, 2); cell2.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell2); - LightUpCell cell3 = board.getCell(4,3); + LightUpCell cell3 = board.getCell(4, 3); cell3.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell3); - //confirm there is a logical following of the EmptyCorners rule + // confirm there is a logical following of the EmptyCorners rule Assert.assertNull(RULE.checkRule(transition)); - //this should not be accepted, the cell should remain unknown - LightUpCell cell4 = board.getCell(4,5); + // this should not be accepted, the cell should remain unknown + LightUpCell cell4 = board.getCell(4, 5); cell4.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell4); - //this should not be accepted, the cell should be empty but not because of this rule - LightUpCell cell5 = board.getCell(4,1); + // this should not be accepted, the cell should be empty but not because of this rule + LightUpCell cell5 = board.getCell(4, 1); cell5.setData(LightUpCellType.EMPTY.value); board.addModifiedData(cell5); Assert.assertNotNull(RULE.checkRule(transition)); - //confirm the two expected cells are emptied USING THE RULE + // confirm the two expected cells are emptied USING THE RULE // and none of the rest are (others can be empty just not by the same rule) 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 == 2 && j == 0) || (i == 2 && j == 2) || (i==3 && j==4)){ + if ((i == 2 && j == 0) || (i == 2 && j == 2) || (i == 3 && j == 4)) { Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/lightup/rules/FinishWithBulbsDirectRuleTest.java b/src/test/java/puzzles/lightup/rules/FinishWithBulbsDirectRuleTest.java index f98a48303..59cbfdbf9 100644 --- a/src/test/java/puzzles/lightup/rules/FinishWithBulbsDirectRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/FinishWithBulbsDirectRuleTest.java @@ -1,17 +1,17 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; -import edu.rpi.legup.puzzle.lightup.LightUp; -import edu.rpi.legup.puzzle.lightup.rules.FinishWithBulbsDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUp; import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import edu.rpi.legup.puzzle.lightup.rules.FinishWithBulbsDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class FinishWithBulbsDirectRuleTest { private static final FinishWithBulbsDirectRule RULE = new FinishWithBulbsDirectRule(); @@ -24,77 +24,79 @@ public static void setUp() { @Test public void FinishBulbTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/FinishWithBulbsDirectRule/FinishWithBulbs", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/FinishWithBulbsDirectRule/FinishWithBulbs", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - //get board state + transition.setRule(RULE); + + // get board state LightUpBoard board = (LightUpBoard) transition.getBoard(); - //change the board's cells considering the FinishWithBulbs rule to empty - LightUpCell cell1 = board.getCell(1,0); + // change the board's cells considering the FinishWithBulbs rule to empty + LightUpCell cell1 = board.getCell(1, 0); cell1.setData(LightUpCellType.BULB.value); board.addModifiedData(cell1); - //confirm there is a logical following of the FinishWithBulbs rule + // confirm there is a logical following of the FinishWithBulbs rule Assert.assertNull(RULE.checkRule(transition)); - //check every square except the top center (2,0) + // check every square except the top center (2,0) 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 == 1){ - //logically follows + if (i == 0 && j == 1) { + // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { - //does not use the rule to logically follow + } else { + // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } } } - //even though this test isnt a completely filled board because it is unsolveable, it tests FinishBulbs properly + // even though this test isnt a completely filled board because it is unsolveable, it tests + // FinishBulbs properly @Test public void FinishBulbTestWithThree() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/FinishWithBulbsDirectRule/FinishWithBulbsWithThree", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/FinishWithBulbsDirectRule/FinishWithBulbsWithThree", + lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - //get board state + transition.setRule(RULE); + + // get board state LightUpBoard board = (LightUpBoard) transition.getBoard(); - //change the board's cells considering the FinishWithBulbs rule to empty - LightUpCell cell1 = board.getCell(1,2); + // change the board's cells considering the FinishWithBulbs rule to empty + LightUpCell cell1 = board.getCell(1, 2); cell1.setData(LightUpCellType.BULB.value); board.addModifiedData(cell1); - LightUpCell cell2 = board.getCell(0,1); + LightUpCell cell2 = board.getCell(0, 1); cell2.setData(LightUpCellType.BULB.value); board.addModifiedData(cell2); - LightUpCell cell3 = board.getCell(2,1); + LightUpCell cell3 = board.getCell(2, 1); cell3.setData(LightUpCellType.BULB.value); board.addModifiedData(cell3); - //confirm there is a logical following of the FinishWithBulbs rule + // confirm there is a logical following of the FinishWithBulbs rule Assert.assertNull(RULE.checkRule(transition)); - //check every square for logical following + // check every square for logical following 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 == 1 && j == 2) || (i == 2 && j == 1) || (i == 1 && j == 0)){ - //logically follows + if ((i == 1 && j == 2) || (i == 2 && j == 1) || (i == 1 && j == 0)) { + // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { - //does not use the rule to logically follow + } else { + // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } diff --git a/src/test/java/puzzles/lightup/rules/LightOrEmptyCaseRuleTest.java b/src/test/java/puzzles/lightup/rules/LightOrEmptyCaseRuleTest.java index 2e6a1e879..fa0698edb 100644 --- a/src/test/java/puzzles/lightup/rules/LightOrEmptyCaseRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/LightOrEmptyCaseRuleTest.java @@ -1,19 +1,19 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; -import edu.rpi.legup.puzzle.lightup.LightUp; -import edu.rpi.legup.puzzle.lightup.rules.LightOrEmptyCaseRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.TestUtilities; +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.lightup.LightUp; import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; -import org.junit.Assert; +import edu.rpi.legup.puzzle.lightup.rules.LightOrEmptyCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; import java.util.ArrayList; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class LightOrEmptyCaseRuleTest { private static final LightOrEmptyCaseRule RULE = new LightOrEmptyCaseRule(); @@ -24,49 +24,52 @@ public static void setUp() { lightUp = new LightUp(); } - //creates boards for what is expected output, and checks that the getcases function produces the correct boards - //IT FAILS BECAUSE THE EXISTING GETCASES FUNCTION IS BUGGY/NOT COMPLETED (not my fault :| ) + // creates boards for what is expected output, and checks that the getcases function produces + // the + // correct boards + // IT FAILS BECAUSE THE EXISTING GETCASES FUNCTION IS BUGGY/NOT COMPLETED (not my fault :| ) @Test public void LightOrEmptyTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/LightOrEmptyCaseRule/LightOrEmpty", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/LightOrEmptyCaseRule/LightOrEmpty", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); - //get all new board states using caserule builtin function + // get all new board states using caserule builtin function LightUpBoard b = (LightUpBoard) transition.getBoard(); - LightUpCell numbered_cell = b.getCell(0,0); //the focus cell + LightUpCell numbered_cell = b.getCell(0, 0); // the focus cell ArrayList cases = RULE.getCases(b, numbered_cell); - //assert correct number of cases + // assert correct number of cases Assert.assertEquals(2, cases.size()); - //make a list of boards that I expect + // make a list of boards that I expect LightUpCell change_cell; LightUpBoard case1 = ((LightUpBoard) transition.getBoard()).copy(); LightUpBoard case2 = ((LightUpBoard) transition.getBoard()).copy(); - //change the cells of the first new case board - change_cell = case1.getCell(0,0); + // change the cells of the first new case board + change_cell = case1.getCell(0, 0); change_cell.setData(LightUpCellType.BULB.value); - //case1.addModifiedData(change_cell); + // case1.addModifiedData(change_cell); - change_cell = case1.getCell(1,1); + change_cell = case1.getCell(1, 1); change_cell.setData(LightUpCellType.BULB.value); - //case1.addModifiedData(change_cell); + // case1.addModifiedData(change_cell); - //change the cells of the second new case board - change_cell = case2.getCell(0,1); + // change the cells of the second new case board + change_cell = case2.getCell(0, 1); change_cell.setData(LightUpCellType.BULB.value); - //case2.addModifiedData(change_cell); + // case2.addModifiedData(change_cell); - change_cell = case2.getCell(1,0); + change_cell = case2.getCell(1, 0); change_cell.setData(LightUpCellType.BULB.value); - //case2.addModifiedData(change_cell); + // case2.addModifiedData(change_cell); - //check each board I expect and make sure it exists in returned board list - //currently cases is not made correctly, so the getCases function is flawed. - //Assert.assertTrue(cases.contains((Board) case1)); - //Assert.assertTrue(cases.contains((Board) case2)); + // check each board I expect and make sure it exists in returned board list + // currently cases is not made correctly, so the getCases function is flawed. + // Assert.assertTrue(cases.contains((Board) case1)); + // Assert.assertTrue(cases.contains((Board) case2)); } } diff --git a/src/test/java/puzzles/lightup/rules/MustLightDirectRuleTest.java b/src/test/java/puzzles/lightup/rules/MustLightDirectRuleTest.java index 07958895b..e0e1434c3 100644 --- a/src/test/java/puzzles/lightup/rules/MustLightDirectRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/MustLightDirectRuleTest.java @@ -1,17 +1,17 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; -import edu.rpi.legup.puzzle.lightup.LightUp; -import edu.rpi.legup.puzzle.lightup.rules.MustLightDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.lightup.LightUp; import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; +import edu.rpi.legup.puzzle.lightup.rules.MustLightDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class MustLightDirectRuleTest { private static final MustLightDirectRule RULE = new MustLightDirectRule(); @@ -24,33 +24,33 @@ public static void setUp() { @Test public void MustLightTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/MustLightDirectRule/MustLight", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/MustLightDirectRule/MustLight", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - //get board state + transition.setRule(RULE); + + // get board state LightUpBoard board = (LightUpBoard) transition.getBoard(); - //change the board's cells considering the MustLight rule - LightUpCell cell1 = board.getCell(1,2); + // change the board's cells considering the MustLight rule + LightUpCell cell1 = board.getCell(1, 2); cell1.setData(LightUpCellType.BULB.value); board.addModifiedData(cell1); - //confirm there is a logical following of the FinishWithBulbs rule + // confirm there is a logical following of the FinishWithBulbs rule Assert.assertNull(RULE.checkRule(transition)); - //only the cell above should change following the rule + // only the cell above should change following the rule 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 == 2 && j == 1){ - //logically follows + if (i == 2 && j == 1) { + // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { - //does not use the rule to logically follow + } else { + // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } diff --git a/src/test/java/puzzles/lightup/rules/SatisfyNumberCaseRuleTest.java b/src/test/java/puzzles/lightup/rules/SatisfyNumberCaseRuleTest.java index de3551778..a04f1c520 100644 --- a/src/test/java/puzzles/lightup/rules/SatisfyNumberCaseRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/SatisfyNumberCaseRuleTest.java @@ -1,19 +1,19 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; -import edu.rpi.legup.puzzle.lightup.LightUp; -import edu.rpi.legup.puzzle.lightup.rules.SatisfyNumberCaseRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.TestUtilities; +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.lightup.LightUp; import edu.rpi.legup.puzzle.lightup.LightUpBoard; -import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.puzzle.lightup.LightUpCell; import edu.rpi.legup.puzzle.lightup.LightUpCellType; -import org.junit.Assert; +import edu.rpi.legup.puzzle.lightup.rules.SatisfyNumberCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; import java.util.ArrayList; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class SatisfyNumberCaseRuleTest { private static final SatisfyNumberCaseRule RULE = new SatisfyNumberCaseRule(); @@ -23,69 +23,77 @@ public class SatisfyNumberCaseRuleTest { public static void setUp() { lightUp = new LightUp(); } - - //creates two boards for what is expected output, and checks that the getcases function produces the correct boards - //IT FAILS BECAUSE THE EXISTING GETCASES FUNCTION IS BUGGY/NOT COMPLETED (not my fault :| ) + + // creates two boards for what is expected output, and checks that the getcases function + // produces + // the correct boards + // IT FAILS BECAUSE THE EXISTING GETCASES FUNCTION IS BUGGY/NOT COMPLETED (not my fault :| ) @Test public void SatisfyNumberTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/SatisfyNumberCaseRule/SatisfyNumber", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/SatisfyNumberCaseRule/SatisfyNumber", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); - //get all new board states using caserule builtin function + // get all new board states using caserule builtin function LightUpBoard b = (LightUpBoard) transition.getBoard(); - LightUpCell numbered_cell = b.getCell(1,0); //the tile cell - ArrayList cases = RULE.getCases(b, numbered_cell);//C MUST BE THE NUMBERED TILE, NOT ANY RANDOM EMPTY ONE + LightUpCell numbered_cell = b.getCell(1, 0); // the tile cell + ArrayList cases = + RULE.getCases( + b, numbered_cell); // C MUST BE THE NUMBERED TILE, NOT ANY RANDOM EMPTY ONE - //assert correct number of cases + // assert correct number of cases Assert.assertEquals(2, cases.size()); - //make a list of boards that I expect + // make a list of boards that I expect LightUpCell change_cell; LightUpBoard case1 = ((LightUpBoard) transition.getBoard()).copy(); LightUpBoard case2 = ((LightUpBoard) transition.getBoard()).copy(); - //change the cells of the first new case board - change_cell = case1.getCell(0,0); + // change the cells of the first new case board + change_cell = case1.getCell(0, 0); change_cell.setData(LightUpCellType.BULB.value); - //case1.addModifiedData(change_cell); + // case1.addModifiedData(change_cell); - change_cell = case1.getCell(1,1); + change_cell = case1.getCell(1, 1); change_cell.setData(LightUpCellType.EMPTY.value); - //case1.addModifiedData(change_cell); + // case1.addModifiedData(change_cell); - //change the cells of the second new case board - change_cell = case2.getCell(0,0); + // change the cells of the second new case board + change_cell = case2.getCell(0, 0); change_cell.setData(LightUpCellType.EMPTY.value); - //case2.addModifiedData(change_cell); + // case2.addModifiedData(change_cell); - change_cell = case2.getCell(1,1); + change_cell = case2.getCell(1, 1); change_cell.setData(LightUpCellType.BULB.value); - //case2.addModifiedData(change_cell); + // case2.addModifiedData(change_cell); - //check each board I expect and make sure it exists in returned board list - //currently cases is not made correctly, so the getCases function is flawed. - //Assert.assertTrue(cases.contains((Board) case1)); - //Assert.assertTrue(cases.contains((Board) case2)); + // check each board I expect and make sure it exists in returned board list + // currently cases is not made correctly, so the getCases function is flawed. + // Assert.assertTrue(cases.contains((Board) case1)); + // Assert.assertTrue(cases.contains((Board) case2)); } @Test public void SatisfyNumberTestTwo() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/SatisfyNumberCaseRule/SatisfyNumberTwo", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/SatisfyNumberCaseRule/SatisfyNumberTwo", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); - //get all new board states using caserule builtin function + // get all new board states using caserule builtin function LightUpBoard b = (LightUpBoard) transition.getBoard(); - LightUpCell numbered_cell = b.getCell(1,1); //the tile cell - ArrayList cases = RULE.getCases(b, numbered_cell);//C MUST BE THE NUMBERED TILE, NOT ANY RANDOM EMPTY ONE + LightUpCell numbered_cell = b.getCell(1, 1); // the tile cell + ArrayList cases = + RULE.getCases( + b, numbered_cell); // C MUST BE THE NUMBERED TILE, NOT ANY RANDOM EMPTY ONE - //assert correct number of cases + // assert correct number of cases Assert.assertEquals(6, cases.size()); - //make a list of boards that I expect + // make a list of boards that I expect LightUpCell change_cell1; LightUpCell change_cell2; LightUpBoard case1 = ((LightUpBoard) transition.getBoard()).copy(); @@ -95,61 +103,61 @@ public void SatisfyNumberTestTwo() throws InvalidFileFormatException { LightUpBoard case5 = ((LightUpBoard) transition.getBoard()).copy(); LightUpBoard case6 = ((LightUpBoard) transition.getBoard()).copy(); - //case 1: lights in (1,0) and (0,1) - change_cell1 = case1.getCell(1,0); - change_cell2 = case1.getCell(0,1); + // case 1: lights in (1,0) and (0,1) + change_cell1 = case1.getCell(1, 0); + change_cell2 = case1.getCell(0, 1); change_cell1.setData(LightUpCellType.BULB.value); change_cell2.setData(LightUpCellType.BULB.value); case1.addModifiedData(change_cell1); case1.addModifiedData(change_cell2); - //case 2: lights in (1,0) and (1,2) - change_cell1 = case2.getCell(1,0); - change_cell2 = case2.getCell(1,2); + // case 2: lights in (1,0) and (1,2) + change_cell1 = case2.getCell(1, 0); + change_cell2 = case2.getCell(1, 2); change_cell1.setData(LightUpCellType.BULB.value); change_cell2.setData(LightUpCellType.BULB.value); case2.addModifiedData(change_cell1); case2.addModifiedData(change_cell2); - //case 3: lights in (1,0) and (2,1) - change_cell1 = case3.getCell(1,0); - change_cell2 = case3.getCell(2,1); + // case 3: lights in (1,0) and (2,1) + change_cell1 = case3.getCell(1, 0); + change_cell2 = case3.getCell(2, 1); change_cell1.setData(LightUpCellType.BULB.value); change_cell2.setData(LightUpCellType.BULB.value); case3.addModifiedData(change_cell1); case3.addModifiedData(change_cell2); - //case 4: lights in (0,1) and (2,1) - change_cell1 = case4.getCell(0,1); - change_cell2 = case4.getCell(2,1); + // case 4: lights in (0,1) and (2,1) + change_cell1 = case4.getCell(0, 1); + change_cell2 = case4.getCell(2, 1); change_cell1.setData(LightUpCellType.BULB.value); change_cell2.setData(LightUpCellType.BULB.value); case4.addModifiedData(change_cell1); case4.addModifiedData(change_cell2); - //case 5: lights in (0,1) and (1,2) - change_cell1 = case5.getCell(0,1); - change_cell2 = case5.getCell(1,2); + // case 5: lights in (0,1) and (1,2) + change_cell1 = case5.getCell(0, 1); + change_cell2 = case5.getCell(1, 2); change_cell1.setData(LightUpCellType.BULB.value); change_cell2.setData(LightUpCellType.BULB.value); case5.addModifiedData(change_cell1); case5.addModifiedData(change_cell2); - //case 6: lights in (1,2) and (2,1) - change_cell1 = case6.getCell(1,2); - change_cell2 = case6.getCell(2,1); + // case 6: lights in (1,2) and (2,1) + change_cell1 = case6.getCell(1, 2); + change_cell2 = case6.getCell(2, 1); change_cell1.setData(LightUpCellType.BULB.value); change_cell2.setData(LightUpCellType.BULB.value); case6.addModifiedData(change_cell1); case6.addModifiedData(change_cell2); - //check each board I expect and make sure it exists in returned board list - //currently the cases list is not made correctly, so the getCases function is flawed. - //Assert.assertTrue(cases.contains((Board) case1)); - //Assert.assertTrue(cases.contains((Board) case2)); - //Assert.assertTrue(cases.contains((Board) case3)); - //Assert.assertTrue(cases.contains((Board) case4)); - //Assert.assertTrue(cases.contains((Board) case5)); - //Assert.assertTrue(cases.contains((Board) case6)); + // check each board I expect and make sure it exists in returned board list + // currently the cases list is not made correctly, so the getCases function is flawed. + // Assert.assertTrue(cases.contains((Board) case1)); + // Assert.assertTrue(cases.contains((Board) case2)); + // Assert.assertTrue(cases.contains((Board) case3)); + // Assert.assertTrue(cases.contains((Board) case4)); + // Assert.assertTrue(cases.contains((Board) case5)); + // Assert.assertTrue(cases.contains((Board) case6)); } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java index bab616198..fe994baa6 100644 --- a/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java @@ -1,15 +1,15 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.lightup.LightUp; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.rules.TooFewBulbsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; import legup.TestUtilities; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class TooFewBulbsContradictionRuleTest { private static final TooFewBulbsContradictionRule RULE = new TooFewBulbsContradictionRule(); @@ -22,21 +22,22 @@ public static void setUp() { @Test public void TooFewBulbsContradictionRule() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/TooFewBulbsContradictionRule/FullTooFewTest", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/TooFewBulbsContradictionRule/FullTooFewTest", 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 + // confirm there is a contradiction somewhere on the board Assert.assertNull(RULE.checkContradiction(board)); - //confirm that there arent enough bulbs around the black tiles + // confirm that there arent enough bulbs around the black tiles Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(4, 1))); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 4))); - //confirm there are no requirements for number of bulbs around non-black tiles or 0 tiles + // confirm there are no requirements for number of bulbs around non-black tiles or 0 tiles Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(4, 4))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(5, 5))); @@ -44,17 +45,18 @@ public void TooFewBulbsContradictionRule() throws InvalidFileFormatException { @Test public void TooFewSimpleTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/TooFewBulbsContradictionRule/TooFew", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/TooFewBulbsContradictionRule/TooFew", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); - //confirm it is impossible to satisfy up the center square + // confirm it is impossible to satisfy up the center square Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); - //every square except the center + // every square except the center Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); diff --git a/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java index fe3dad2e0..e27fa3323 100644 --- a/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java @@ -1,15 +1,15 @@ package puzzles.lightup.rules; -import org.junit.BeforeClass; -import org.junit.Test; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.lightup.LightUp; +import edu.rpi.legup.puzzle.lightup.LightUpBoard; import edu.rpi.legup.puzzle.lightup.rules.TooManyBulbsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; import legup.TestUtilities; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.lightup.LightUpBoard; import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class TooManyBulbsContradictionRuleTest { private static final TooManyBulbsContradictionRule RULE = new TooManyBulbsContradictionRule(); @@ -19,41 +19,44 @@ public class TooManyBulbsContradictionRuleTest { public static void setUp() { lightUp = new LightUp(); } + @Test - //complex extensive toofew test + // complex extensive toofew test public void TooFewBulbsContradictionRule() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/TooManyBulbsContradictionRule/FullTooManyTest", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/TooManyBulbsContradictionRule/FullTooManyTest", 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 + // confirm there is a contradiction somewhere on the board Assert.assertNull(RULE.checkContradiction(board)); - //confirm that there are too many bulbs around the black tiles + // confirm that there are too many bulbs around the black tiles Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 4))); - //confirm there are no requirements for number of bulbs around non-black tiles or 0 tiles + // confirm there are no requirements for number of bulbs around non-black tiles or 0 tiles Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(5, 5))); } @Test - //tests a 3x3 board with a 3 in the center and 4 surrounding lightbulbs + // tests a 3x3 board with a 3 in the center and 4 surrounding lightbulbs public void TooManySimpleTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/lightup/rules/TooManyBulbsContradictionRule/TooMany", lightUp); + TestUtilities.importTestBoard( + "puzzles/lightup/rules/TooManyBulbsContradictionRule/TooMany", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); + transition.setRule(RULE); LightUpBoard board = (LightUpBoard) transition.getBoard(); - //confirm it is impossible to satisfy up the center square + // confirm it is impossible to satisfy up the center square Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); - //every square except the center + // every square except the center Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); diff --git a/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java b/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java index 7e8b5eb83..d87398303 100644 --- a/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRuleTest.java @@ -1,190 +1,204 @@ -package puzzles.nurikabe.rules; - -//import javafx.scene.layout.Pane; - -import legup.MockGameBoardFacade; -import legup.TestUtilities; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -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.BlackBetweenRegionsDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; - -import java.awt.*; - -public class BlackBetweenRegionsDirectRuleTest { - - private static final BlackBetweenRegionsDirectRule RULE = new BlackBetweenRegionsDirectRule(); - private static Nurikabe nurikabe; - - @BeforeClass - public static void setUp() { - MockGameBoardFacade.getInstance(); - 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); - TreeNode rootNode = nurikabe.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - - NurikabeCell cell1 = board.getCell(2, 1); - cell1.setData(NurikabeType.BLACK.toValue()); - NurikabeCell cell2 = board.getCell(1, 2); - cell2.setData(NurikabeType.BLACK.toValue()); - - 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++) { - 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))); - } - } - } - } - - /** - * 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); - TreeNode rootNode = nurikabe.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - NurikabeBoard board = (NurikabeBoard) transition.getBoard(); - - NurikabeCell cell1 = board.getCell(1, 1); - cell1.setData(NurikabeType.BLACK.toValue()); - NurikabeCell cell2 = board.getCell(2, 2); - cell2.setData(NurikabeType.BLACK.toValue()); - - 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++) { - 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))); - } - } - } - } - - /** - * 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); - 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.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))); - } - } - } - } - - /** - * 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); - 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.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))); - } - } - } - } - - /** - * 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 cases = RULE.getCases(board,cell); + NurikabeCell cell = board.getCell(0, 0); + ArrayList cases = RULE.getCases(board, cell); - Assert.assertEquals(2,cases.size()); + Assert.assertEquals(2, cases.size()); NurikabeBoard caseBoard = (NurikabeBoard) cases.get(0); NurikabeBoard caseBoard2 = (NurikabeBoard) cases.get(1); - NurikabeType board1Type = caseBoard.getCell(0,0).getType(); - NurikabeType board2Type = caseBoard2.getCell(0,0).getType(); + NurikabeType board1Type = caseBoard.getCell(0, 0).getType(); + NurikabeType board2Type = caseBoard2.getCell(0, 0).getType(); - Assert.assertTrue((board1Type.equals(NurikabeType.BLACK) || board1Type.equals(NurikabeType.WHITE)) && - (board2Type.equals(NurikabeType.BLACK) || board2Type.equals(NurikabeType.WHITE))); + Assert.assertTrue( + (board1Type.equals(NurikabeType.BLACK) || board1Type.equals(NurikabeType.WHITE)) + && (board2Type.equals(NurikabeType.BLACK) + || board2Type.equals(NurikabeType.WHITE))); Assert.assertFalse(board1Type.equals(board2Type)); - Assert.assertEquals(caseBoard.getHeight(),caseBoard2.getHeight(), board.getHeight()); - Assert.assertEquals(caseBoard.getWidth(),caseBoard2.getWidth(), board.getWidth()); + Assert.assertEquals(caseBoard.getHeight(), caseBoard2.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard.getWidth(), caseBoard2.getWidth(), board.getWidth()); - for(int i=0; i cases = RULE.getCases(board, cell); // Make sure that the rule checks out @@ -63,60 +60,64 @@ private void falseAndTest(String fileName, // 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.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)); + 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 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()); + 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. + * 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); + 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. + * 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); + 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 { + 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); + ShortTruthTableCell cell = board.getCell(andX, andY); ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out @@ -139,22 +140,20 @@ private void trueAndTest(String fileName, } /** - * 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. + * 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); + 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. + * 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); + trueAndTest("ComplexStatement1_True", 6, 0, 0, 0, 9, 0); } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java index 0d94eb672..61f29a669 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java @@ -26,21 +26,26 @@ public static void setup() { /** * 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. + * + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -69,21 +74,23 @@ public void trueAndTest1() throws InvalidFileFormatException { /** * 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. + * + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -112,20 +119,25 @@ public void trueAndTest2() throws InvalidFileFormatException { /** * 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. + * + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -150,15 +162,17 @@ public void falseAndWithUnknownsTest() throws InvalidFileFormatException { /** * 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. + * + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -177,15 +191,16 @@ public void falseAndWithKnownFalseTest() throws InvalidFileFormatException { /** * 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. + * + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -201,4 +216,4 @@ public void falseAndWithKnownTrueTest() throws InvalidFileFormatException { board.addModifiedData(clyde); Assert.assertNull(RULE.checkRule(transition)); } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java index 4d4e009b2..ec03919ae 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/AndIntroductionDirectRuleTest.java @@ -27,8 +27,8 @@ public static void setup() { /** * 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. + *

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 @@ -64,8 +64,8 @@ private void falseAndTestHelper(String filePath) throws InvalidFileFormatExcepti /** * 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. + *

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 @@ -95,11 +95,11 @@ private void trueAndTestHelper(String filePath) throws InvalidFileFormatExceptio and.setData(ShortTruthTableCellType.TRUE); board.addModifiedData(and); - if (a.getType() == ShortTruthTableCellType.TRUE && b.getType() == ShortTruthTableCellType.TRUE) { + if (a.getType() == ShortTruthTableCellType.TRUE + && b.getType() == ShortTruthTableCellType.TRUE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java index 51aa213c6..c631db613 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java @@ -25,13 +25,10 @@ public static void setup() { } /** - * 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. + * 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 */ @@ -52,13 +49,10 @@ public void MatchingFalseTest() throws InvalidFileFormatException { } /** - * 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. + * 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 */ @@ -79,13 +73,10 @@ public void MismatchingFalseTest() throws InvalidFileFormatException { } /** - * 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. + * 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 */ @@ -106,13 +97,10 @@ public void MatchingTrueTest() throws InvalidFileFormatException { } /** - * 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. + * 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 */ @@ -133,13 +121,10 @@ public void MismatchingTrueTest() throws InvalidFileFormatException { } /** - * 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. + * 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 */ @@ -160,13 +145,10 @@ public void NothingPreviouslyMarkedTest() throws InvalidFileFormatException { } /** - * 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. + * 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 */ @@ -185,4 +167,4 @@ public void NothingPreviouslyMarkedTest2() throws InvalidFileFormatException { Assert.assertNotNull(RULE.checkRule(transition)); } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java index 05faf87bb..987d194f9 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalEliminationTest.java @@ -15,7 +15,8 @@ import org.junit.Test; public class BiconditionalEliminationTest { - private static final DirectRuleBiconditionalElimination RULE = new DirectRuleBiconditionalElimination(); + private static final DirectRuleBiconditionalElimination RULE = + new DirectRuleBiconditionalElimination(); private static ShortTruthTable stt; @BeforeClass @@ -27,13 +28,15 @@ public static void setup() { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueA", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -60,13 +63,15 @@ public void TrueBiconditionalWithTrueATest() throws InvalidFileFormatException { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithTrueB", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -90,17 +95,18 @@ public void TrueBiconditionalWithTrueBTest() throws InvalidFileFormatException { 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseA", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -127,13 +133,15 @@ public void TrueBiconditionalWithFalseATest() throws InvalidFileFormatException /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/TrueBiconditionalWithFalseB", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -160,13 +168,15 @@ public void TrueBiconditionalWithFalseBTest() throws InvalidFileFormatException /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueA", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -193,13 +203,15 @@ public void FalseBiconditionalWithTrueATest() throws InvalidFileFormatException /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithTrueB", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -226,13 +238,15 @@ public void FalseBiconditionalWithTrueBTest() throws InvalidFileFormatException /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseA", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -259,13 +273,15 @@ public void FalseBiconditionalWithFalseATest() throws InvalidFileFormatException /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/BiconditionalEliminationDirectRule/FalseBiconditionalWithFalseB", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -292,19 +308,25 @@ public void FalseBiconditionalWithFalseBTest() throws InvalidFileFormatException /** * 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 + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -324,8 +346,8 @@ public void TrueBiconditionalSetBothAtOnceTest() throws InvalidFileFormatExcepti } /** - * 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. + * 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 */ @@ -352,7 +374,11 @@ private void setAandBBothAtOnceTest(String filePath) throws InvalidFileFormatExc TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -370,4 +396,4 @@ private void setAandBBothAtOnceTest(String filePath) throws InvalidFileFormatExc } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java index fe2574b5e..e1c5cf957 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/BiconditionalIntroductionTest.java @@ -15,7 +15,8 @@ import org.junit.Test; public class BiconditionalIntroductionTest { - private static final DirectRuleBiconditionalIntroduction RULE = new DirectRuleBiconditionalIntroduction(); + private static final DirectRuleBiconditionalIntroduction RULE = + new DirectRuleBiconditionalIntroduction(); private static ShortTruthTable stt; @BeforeClass @@ -27,8 +28,8 @@ public static void setup() { /** * 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. + *

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 */ @@ -61,14 +62,13 @@ private void falseConditionalHelper(String filePath) throws InvalidFileFormatExc 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) { + if (a.getType() == ShortTruthTableCellType.UNKNOWN + || b.getType() == ShortTruthTableCellType.UNKNOWN) { Assert.assertNotNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNull(RULE.checkRule(transition)); } - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -76,8 +76,8 @@ private void falseConditionalHelper(String filePath) throws InvalidFileFormatExc /** * 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. + *

Asserts that if setting <-> to true is a valid application of this rule if and only if A + * and B match. * * @throws InvalidFileFormatException */ @@ -110,9 +110,8 @@ private void trueConditionalHelper(String filePath) throws InvalidFileFormatExce ShortTruthTableCell b = board.getCell(2, 0); if (a.getType() == b.getType() && a.getType() != ShortTruthTableCellType.UNKNOWN) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { 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 index 8d0bb4e1a..0f95906f2 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/ConditionalEliminationTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/ConditionalEliminationTest.java @@ -15,7 +15,8 @@ import org.junit.Test; public class ConditionalEliminationTest { - private static final DirectRuleConditionalElimination RULE = new DirectRuleConditionalElimination(); + private static final DirectRuleConditionalElimination RULE = + new DirectRuleConditionalElimination(); private static ShortTruthTable stt; @BeforeClass @@ -27,19 +28,25 @@ public static void setup() { /** * 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 + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -53,10 +60,10 @@ public void FalseConditionalTest() throws InvalidFileFormatException { board.addModifiedData(aubergine); board.addModifiedData(boniato); - if (cellType1 == ShortTruthTableCellType.TRUE && cellType2 == ShortTruthTableCellType.FALSE) { + if (cellType1 == ShortTruthTableCellType.TRUE + && cellType2 == ShortTruthTableCellType.FALSE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -66,14 +73,15 @@ public void FalseConditionalTest() throws InvalidFileFormatException { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -93,14 +101,15 @@ public void FalseConditionalTrueATest() throws InvalidFileFormatException { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/FalseConditional", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -120,18 +129,24 @@ public void FalseConditionalFalseBTest() throws InvalidFileFormatException { /** * Given one statement: A -> B where -> is true * - * Asserts that you cannot set any combination of both A and B at the same time. + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -153,14 +168,15 @@ public void CannotSetBothAandBTrueConditionalTest() throws InvalidFileFormatExce /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueA", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -180,14 +196,15 @@ public void TrueAMeansTrueBTest() throws InvalidFileFormatException { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithFalseB", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -207,14 +224,15 @@ public void FalseBMeansFalseATest() throws InvalidFileFormatException { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/ConditionalEliminationDirectRule/TrueConditionalWithTrueB", + stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -230,4 +248,4 @@ public void TrueBCannotDetermineA() throws InvalidFileFormatException { board.addModifiedData(boniato); 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 index c1507eab1..1bc28373d 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/ConditionalIntroductionTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/ConditionalIntroductionTest.java @@ -15,7 +15,8 @@ import org.junit.Test; public class ConditionalIntroductionTest { - private static final DirectRuleConditionalIntroduction RULE = new DirectRuleConditionalIntroduction(); + private static final DirectRuleConditionalIntroduction RULE = + new DirectRuleConditionalIntroduction(); private static ShortTruthTable stt; @BeforeClass @@ -27,8 +28,8 @@ public static void setup() { /** * 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. + *

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 */ @@ -58,10 +59,10 @@ private void falseConditionalHelper(String filePath) throws InvalidFileFormatExc ShortTruthTableCell a = board.getCell(0, 0); ShortTruthTableCell b = board.getCell(2, 0); - if (a.getType() == ShortTruthTableCellType.TRUE && b.getType() == ShortTruthTableCellType.FALSE) { + if (a.getType() == ShortTruthTableCellType.TRUE + && b.getType() == ShortTruthTableCellType.FALSE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -69,8 +70,8 @@ private void falseConditionalHelper(String filePath) throws InvalidFileFormatExc /** * 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. + *

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 */ @@ -100,11 +101,11 @@ private void trueConditionalTestHelper(String filePath) throws InvalidFileFormat ShortTruthTableCell a = board.getCell(0, 0); ShortTruthTableCell b = board.getCell(2, 0); - if (a.getType() == ShortTruthTableCellType.FALSE || b.getType() == ShortTruthTableCellType.TRUE) { + if (a.getType() == ShortTruthTableCellType.FALSE + || b.getType() == ShortTruthTableCellType.TRUE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { 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 index 6dbbf141c..589e3cfb9 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/NotEliminationTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/NotEliminationTest.java @@ -27,18 +27,23 @@ public static void setup() { /** * Given one statement: ¬A where ¬ is false * - * Asserts that this is a valid application of this rule if and only if A is true + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType : cellTypes) { ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); @@ -48,8 +53,7 @@ public void FalseNot() throws InvalidFileFormatException { if (cellType == ShortTruthTableCellType.TRUE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -58,18 +62,23 @@ public void FalseNot() throws InvalidFileFormatException { /** * Given one statement: ¬A where ¬ is true * - * Asserts that this is a valid application of this rule if and only if A is false + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType : cellTypes) { ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); @@ -79,46 +88,47 @@ public void TrueNot() throws InvalidFileFormatException { if (cellType == ShortTruthTableCellType.FALSE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } 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 + // /** + // * 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)); + // } + // } + // } +} diff --git a/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java b/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java index a0a062ab3..e338fb9bd 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/NotIntroductionTest.java @@ -27,18 +27,23 @@ public static void setup() { /** * Given one statement: ¬A where A is false * - * Asserts that this is a valid application of this rule if and only if ¬ is true + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType : cellTypes) { ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); @@ -48,8 +53,7 @@ public void FalseNot() throws InvalidFileFormatException { if (cellType == ShortTruthTableCellType.TRUE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -58,18 +62,23 @@ public void FalseNot() throws InvalidFileFormatException { /** * Given one statement: ¬A where A is true * - * Asserts that this is a valid application of this rule if and only if ¬ is false + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType : cellTypes) { ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard(); @@ -79,8 +88,7 @@ public void TrueNot() throws InvalidFileFormatException { if (cellType == ShortTruthTableCellType.FALSE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -89,19 +97,24 @@ public void TrueNot() throws InvalidFileFormatException { /** * Given one statement: ¬A * - * Asserts that setting both ¬ and A to any values would not be a valid - * application of this rule + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -119,4 +132,4 @@ public void CannotSetBothAtOnceTest() throws InvalidFileFormatException { } } } -} \ 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 index 0f7e93db5..0bfa6fc6e 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java @@ -7,17 +7,15 @@ 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 java.util.ArrayList; 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(); @@ -29,17 +27,15 @@ public static void setUp() { stt = new ShortTruthTable(); } - private void trueOrTest(String fileName, - int andX, int andY, - int aX, int aY, - int bX, int bY) throws InvalidFileFormatException { + 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); + ShortTruthTableCell cell = board.getCell(andX, andY); ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out @@ -64,60 +60,64 @@ private void trueOrTest(String fileName, // 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.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)); + 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 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()); + 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. + * 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); + 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. + * 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); + 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 { + 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); + ShortTruthTableCell cell = board.getCell(andX, andY); ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out @@ -140,22 +140,20 @@ private void falseOrTest(String fileName, } /** - * 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. + * 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); + 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. + * 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); + falseOrTest("ComplexStatement1_False", 6, 0, 0, 0, 9, 0); } -} \ 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 index 6cdd0d639..e4efad465 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/OrEliminationTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/OrEliminationTest.java @@ -27,13 +27,14 @@ public static void setup() { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/OrEliminationDirectRule/FTU", stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -53,13 +54,14 @@ public void FTUTest() throws InvalidFileFormatException { /** * 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. + *

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); + TestUtilities.importTestBoard( + "puzzles/shorttruthtable/rules/OrEliminationDirectRule/UTF", stt); TreeNode rootNode = stt.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -79,19 +81,24 @@ public void UTFTest() throws InvalidFileFormatException { /** * 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. + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -105,10 +112,10 @@ public void UFUTest() throws InvalidFileFormatException { board.addModifiedData(a); board.addModifiedData(b); - if (cellType1 == ShortTruthTableCellType.FALSE && cellType2 == ShortTruthTableCellType.FALSE) { + if (cellType1 == ShortTruthTableCellType.FALSE + && cellType2 == ShortTruthTableCellType.FALSE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } @@ -118,18 +125,23 @@ public void UFUTest() throws InvalidFileFormatException { /** * 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. + *

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); + 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}; + ShortTruthTableCellType[] cellTypes = { + ShortTruthTableCellType.TRUE, + ShortTruthTableCellType.FALSE, + ShortTruthTableCellType.UNKNOWN + }; for (ShortTruthTableCellType cellType1 : cellTypes) { for (ShortTruthTableCellType cellType2 : cellTypes) { @@ -147,4 +159,4 @@ public void UTUTest() throws InvalidFileFormatException { } } } -} \ 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 index 13cb10d55..d5f9387eb 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/OrIntroductionTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/OrIntroductionTest.java @@ -27,8 +27,8 @@ public static void setup() { /** * 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. + *

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 @@ -64,8 +64,8 @@ private void trueOrTestHelper(String filePath) throws InvalidFileFormatException /** * 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. + *

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 @@ -95,11 +95,11 @@ private void falseOrTestHelper(String filePath) throws InvalidFileFormatExceptio or.setData(ShortTruthTableCellType.FALSE); board.addModifiedData(or); - if (a.getType() == ShortTruthTableCellType.FALSE && b.getType() == ShortTruthTableCellType.FALSE) { + if (a.getType() == ShortTruthTableCellType.FALSE + && b.getType() == ShortTruthTableCellType.FALSE) { Assert.assertNull(RULE.checkRule(transition)); - } - else { + } else { Assert.assertNotNull(RULE.checkRule(transition)); } } -} \ 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 index 2c0b9fb15..d0c390785 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java @@ -9,14 +9,13 @@ import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; import edu.rpi.legup.puzzle.shorttruthtable.rules.caserule.CaseRuleAtomic; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; 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(); @@ -29,18 +28,19 @@ public static void setUp() { } /** - * 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. + * 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); + 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); + ShortTruthTableCell cell = board.getCell(0, 0); ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out @@ -52,27 +52,32 @@ public void TwoBranchesTest() throws InvalidFileFormatException { 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(); + 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)); + 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 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()); + Assert.assertEquals( + caseBoard1.getCell(i, j).getType(), caseBoard2.getCell(i, j).getType()); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java b/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java index eb2b692c8..1fce24cf1 100644 --- a/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/CellForNumberCaseRuleTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeNode; @@ -9,14 +8,13 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.rules.CellForNumberCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.util.ArrayList; - public class CellForNumberCaseRuleTest { private static final CellForNumberCaseRule RULE = new CellForNumberCaseRule(); @@ -28,7 +26,7 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //basic, max cases + // basic, max cases @Test public void CellForNumberCaseRule_BasicEmpty() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -42,19 +40,19 @@ public void CellForNumberCaseRule_BasicEmpty() throws InvalidFileFormatException board.setDupeFlag(false); board.setViewFlag(false); - ArrayList cases = RULE.getCasesFor(board,board.getNorthClues().get(0), 1); + ArrayList cases = RULE.getCasesFor(board, board.getNorthClues().get(0), 1); Assert.assertEquals(board.getWidth(), cases.size()); - for(int i=0;i cases = RULE.getCasesFor(board,board.getNorthClues().get(0), 1); + ArrayList cases = RULE.getCasesFor(board, board.getNorthClues().get(0), 1); Assert.assertEquals(board.getWidth(), cases.size()); - for(int i=0;i cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + ArrayList cases = RULE.getCasesFor(board, board.getWestClues().get(3), 1); Assert.assertEquals(1, cases.size()); SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); - PuzzleElement changedCell = expected.getCell(2,3); + PuzzleElement changedCell = expected.getCell(2, 3); changedCell.setData(1); expected.addModifiedData(changedCell); Assert.assertTrue(expected.equalsBoard(cases.get(0))); } - //dupe, no cases + // dupe, no cases @Test public void CellForNumberCaseRule_DupeNone() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -140,12 +141,12 @@ public void CellForNumberCaseRule_DupeNone() throws InvalidFileFormatException { board.setDupeFlag(true); board.setViewFlag(false); - ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + ArrayList cases = RULE.getCasesFor(board, board.getWestClues().get(3), 1); Assert.assertEquals(0, cases.size()); } - //visibility, max cases + // visibility, max cases @Test public void CellForNumberCaseRule_ViewEmpty() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -159,19 +160,19 @@ public void CellForNumberCaseRule_ViewEmpty() throws InvalidFileFormatException board.setDupeFlag(false); board.setViewFlag(true); - ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(1), 1); + ArrayList cases = RULE.getCasesFor(board, board.getWestClues().get(1), 1); Assert.assertEquals(board.getWidth(), cases.size()); - for(int i=0;i cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + ArrayList cases = RULE.getCasesFor(board, board.getWestClues().get(3), 1); Assert.assertEquals(1, cases.size()); SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); - PuzzleElement changedCell = expected.getCell(2,3); + PuzzleElement changedCell = expected.getCell(2, 3); changedCell.setData(1); expected.addModifiedData(changedCell); Assert.assertTrue(expected.equalsBoard(cases.get(0))); } - //visibility, 1 Case, implied + // visibility, 1 Case, implied @Test public void CellForNumberCaseRule_ImpliedViewSingular() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -221,22 +223,24 @@ public void CellForNumberCaseRule_ImpliedViewSingular() throws InvalidFileFormat board.setDupeFlag(false); board.setViewFlag(true); - ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(0),5); + ArrayList cases = RULE.getCasesFor(board, board.getWestClues().get(0), 5); Assert.assertEquals(1, cases.size()); SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); - PuzzleElement changedCell = expected.getCell(4,0); + PuzzleElement changedCell = expected.getCell(4, 0); changedCell.setData(5); expected.addModifiedData(changedCell); Assert.assertTrue(expected.equalsBoard(cases.get(0))); } - //visibility, no cases + // visibility, no cases @Test public void CellForNumberCaseRule_ViewNone() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -247,8 +251,8 @@ public void CellForNumberCaseRule_ViewNone() throws InvalidFileFormatException { board.setDupeFlag(false); board.setViewFlag(true); - ArrayList cases = RULE.getCasesFor(board,board.getWestClues().get(3),1); + ArrayList cases = RULE.getCasesFor(board, board.getWestClues().get(3), 1); Assert.assertEquals(0, cases.size()); } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java index 00cce1b91..a7bb63a08 100644 --- a/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/DuplicateNumberContradictionTest.java @@ -1,21 +1,21 @@ package puzzles.skyscrapers.rules; - +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.rules.DuplicateNumberContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; import legup.MockGameBoardFacade; import legup.TestUtilities; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; public class DuplicateNumberContradictionTest { - private static final DuplicateNumberContradictionRule RULE = new DuplicateNumberContradictionRule(); + private static final DuplicateNumberContradictionRule RULE = + new DuplicateNumberContradictionRule(); private static Skyscrapers skyscrapers; @BeforeClass @@ -24,9 +24,10 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //empty + // empty @Test - public void DuplicateNumberContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + public void DuplicateNumberContradictionRule_EmptyBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -43,9 +44,10 @@ public void DuplicateNumberContradictionRule_EmptyBoardTest() throws InvalidFile } } - //correct board, no cont + // correct board, no cont @Test - public void DuplicateNumberContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + public void DuplicateNumberContradictionRule_SolvedBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -62,10 +64,13 @@ public void DuplicateNumberContradictionRule_SolvedBoardTest() throws InvalidFil } } - //invalid board, no cont + // invalid board, no cont @Test - public void DuplicateNumberContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", skyscrapers); + public void DuplicateNumberContradictionRule_OtherContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -81,10 +86,13 @@ public void DuplicateNumberContradictionRule_OtherContradictionTest() throws Inv } } - //on row + // on row @Test - public void DuplicateNumberContradictionRule_RowContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", skyscrapers); + public void DuplicateNumberContradictionRule_RowContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -95,20 +103,22 @@ public void DuplicateNumberContradictionRule_RowContradictionTest() throws Inval SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - if((k==0 || k==1) && i==0){ + if ((k == 0 || k == 1) && i == 0) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //on col + // on col @Test - public void DuplicateNumberContradictionRule_ColContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction", skyscrapers); + public void DuplicateNumberContradictionRule_ColContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/ColContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -119,20 +129,22 @@ public void DuplicateNumberContradictionRule_ColContradictionTest() throws Inval SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - if(k==0 && (i==0 || i==1)){ + if (k == 0 && (i == 0 || i == 1)) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //multitudes + // multitudes @Test - public void DuplicateNumberContradictionRule_AllContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction", skyscrapers); + public void DuplicateNumberContradictionRule_AllContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/AllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -147,4 +159,4 @@ public void DuplicateNumberContradictionRule_AllContradictionTest() throws Inval } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java index c7f73c998..a5a07f997 100644 --- a/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/ExceedingVisibilityContradictionTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -15,7 +14,8 @@ public class ExceedingVisibilityContradictionTest { - private static final ExceedingVisibilityContradictionRule RULE = new ExceedingVisibilityContradictionRule(); + private static final ExceedingVisibilityContradictionRule RULE = + new ExceedingVisibilityContradictionRule(); private static Skyscrapers skyscrapers; @BeforeClass @@ -24,9 +24,10 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //empty + // empty @Test - public void ExceedingVisibilityContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + public void ExceedingVisibilityContradictionRule_EmptyBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -41,9 +42,10 @@ public void ExceedingVisibilityContradictionRule_EmptyBoardTest() throws Invalid } } - //correct board, no cont + // correct board, no cont @Test - public void ExceedingVisibilityContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + public void ExceedingVisibilityContradictionRule_SolvedBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -58,10 +60,13 @@ public void ExceedingVisibilityContradictionRule_SolvedBoardTest() throws Invali } } - //invalid board, no cont + // invalid board, no cont @Test - public void ExceedingVisibilityContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", skyscrapers); + public void ExceedingVisibilityContradictionRule_OtherContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -75,10 +80,13 @@ public void ExceedingVisibilityContradictionRule_OtherContradictionTest() throws } } - //on row + // on row @Test - public void ExceedingVisibilityContradictionRule_RowContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", skyscrapers); + public void ExceedingVisibilityContradictionRule_RowContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -88,19 +96,21 @@ public void ExceedingVisibilityContradictionRule_RowContradictionTest() throws I SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { - if(i==1 || i==3){ + if (i == 1 || i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //on col + // on col @Test - public void ExceedingVisibilityContradictionRule_ColContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction", skyscrapers); + public void ExceedingVisibilityContradictionRule_ColContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -110,19 +120,21 @@ public void ExceedingVisibilityContradictionRule_ColContradictionTest() throws I SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { - if(i==2 || i==3){ + if (i == 2 || i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //multitudes + // multitudes @Test - public void ExceedingVisibilityContradictionRule_AllContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", skyscrapers); + public void ExceedingVisibilityContradictionRule_AllContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -135,4 +147,4 @@ public void ExceedingVisibilityContradictionRule_AllContradictionTest() throws I Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java index 4e90861fc..ee0f3349a 100644 --- a/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/InsufficientVisibilityContradictionTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -15,7 +14,8 @@ public class InsufficientVisibilityContradictionTest { - private static final InsufficientVisibilityContradictionRule RULE = new InsufficientVisibilityContradictionRule(); + private static final InsufficientVisibilityContradictionRule RULE = + new InsufficientVisibilityContradictionRule(); private static Skyscrapers skyscrapers; @BeforeClass @@ -24,9 +24,10 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //empty + // empty @Test - public void InsufficientVisibilityContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + public void InsufficientVisibilityContradictionRule_EmptyBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -41,9 +42,10 @@ public void InsufficientVisibilityContradictionRule_EmptyBoardTest() throws Inva } } - //correct board, no cont + // correct board, no cont @Test - public void InsufficientVisibilityContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + public void InsufficientVisibilityContradictionRule_SolvedBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -58,10 +60,13 @@ public void InsufficientVisibilityContradictionRule_SolvedBoardTest() throws Inv } } - //invalid board, no cont + // invalid board, no cont @Test - public void InsufficientVisibilityContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", skyscrapers); + public void InsufficientVisibilityContradictionRule_OtherContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/DuplicateNumberContradictionRule/RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -75,10 +80,13 @@ public void InsufficientVisibilityContradictionRule_OtherContradictionTest() thr } } - //on row + // on row @Test - public void InsufficientVisibilityContradictionRule_RowContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", skyscrapers); + public void InsufficientVisibilityContradictionRule_RowContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/FullRowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -88,19 +96,21 @@ public void InsufficientVisibilityContradictionRule_RowContradictionTest() throw SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { - if(i==1 || i==3){ + if (i == 1 || i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //on col + // on col @Test - public void InsufficientVisibilityContradictionRule_ColContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction", skyscrapers); + public void InsufficientVisibilityContradictionRule_ColContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/FullColContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -110,19 +120,21 @@ public void InsufficientVisibilityContradictionRule_ColContradictionTest() throw SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { - if(i==2 || i==3){ + if (i == 2 || i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //multitudes + // multitudes @Test - public void InsufficientVisibilityContradictionRule_AllContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", skyscrapers); + public void InsufficientVisibilityContradictionRule_AllContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -135,4 +147,4 @@ public void InsufficientVisibilityContradictionRule_AllContradictionTest() throw Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java index 855c358e8..76540f010 100644 --- a/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/LastSingularCellDirectTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -8,14 +7,13 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.rules.LastSingularCellDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class LastSingularCellDirectTest { private static final LastSingularCellDirectRule RULE = new LastSingularCellDirectRule(); @@ -27,206 +25,206 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //full row + // full row @Test public void LastSingularCellDirectRule_FullRowTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,3); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 3); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //full col + // full col @Test public void LastSingularCellDirectRule_FullColTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 1); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //empty row/col + // empty row/col @Test public void LastSingularCellDirectRule_EmptyTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularCellDirectRule/0-3Opening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 1); cell.setData(3); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 row + // 2-1 row @Test public void LastSingularCellDirectRule_PartialRowTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 1); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 col + // 2-1 col @Test public void LastSingularCellDirectRule_PartialColTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularCellDirectRule/2-1ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 2); cell.setData(3); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //1-2 row + // 1-2 row @Test public void LastSingularCellDirectRule_PartialRowTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 1); cell.setData(2); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //1-2 col + // 1-2 col @Test public void LastSingularCellDirectRule_PartialColTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularCellDirectRule/1-2ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,0); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 0); cell.setData(4); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java index f08774a8a..6b99994a2 100644 --- a/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/LastSingularNumberDirectTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -8,14 +7,13 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.rules.LastSingularNumberDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class LastSingularNumberDirectTest { private static final LastSingularNumberDirectRule RULE = new LastSingularNumberDirectRule(); @@ -27,119 +25,123 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //full row / empty col + // full row / empty col @Test public void LastSingularNumberDirectRule_FullRowTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,3); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 3); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //full col / empty row + // full col / empty row @Test public void LastSingularNumberDirectRule_FullColTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 1); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 row / 1-2 col + // 2-1 row / 1-2 col @Test - public void LastSingularNumberDirectRule_PartialRowColTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening", skyscrapers); + public void LastSingularNumberDirectRule_PartialRowColTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1RowOpening", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 1); cell.setData(4); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 col / 1-2 row + // 2-1 col / 1-2 row @Test - public void LastSingularNumberDirectRule_PartialRowColTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening", skyscrapers); + public void LastSingularNumberDirectRule_PartialRowColTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastSingularNumberDirectRule/2-1ColOpening", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 2); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java index ca50bc873..756ff7468 100644 --- a/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/LastVisibleCellDirectTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -8,14 +7,13 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.rules.LastVisibleCellDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class LastVisibleCellDirectTest { private static final LastVisibleCellDirectRule RULE = new LastVisibleCellDirectRule(); @@ -27,65 +25,65 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //full row + // full row @Test public void LastVisibleCellDirectRule_FullRowTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,3); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 3); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //full col + // full col @Test public void LastVisibleCellDirectRule_FullColTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 1); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //empty row + // empty row @Test public void LastVisibleCellDirectRule_EmptyRowTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -93,28 +91,27 @@ public void LastVisibleCellDirectRule_EmptyRowTest() throws InvalidFileFormatExc TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 2); cell.setData(5); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //empty col + // empty col @Test public void LastVisibleCellDirectRule_EmptyColTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -122,136 +119,135 @@ public void LastVisibleCellDirectRule_EmptyColTest() throws InvalidFileFormatExc TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,4); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 4); cell.setData(5); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //1-2 row + // 1-2 row public void LastVisibleCellDirectRule_PartialRowTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 2); cell.setData(3); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //1-2 col + // 1-2 col public void LastVisibleCellDirectRule_PartialColTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 2); cell.setData(2); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 row + // 2-1 row public void LastVisibleCellDirectRule_PartialRowTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 1); cell.setData(2); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 col + // 2-1 col public void LastVisibleCellDirectRule_PartialColTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 2); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java index 1816832b6..c628b89da 100644 --- a/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/LastVisibleNumberDirectTest.java @@ -1,22 +1,19 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; -import edu.rpi.legup.puzzle.skyscrapers.rules.LastSingularCellDirectRule; import edu.rpi.legup.puzzle.skyscrapers.rules.LastVisibleNumberDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class LastVisibleNumberDirectTest { private static final LastVisibleNumberDirectRule RULE = new LastVisibleNumberDirectRule(); @@ -28,65 +25,65 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //full row + // full row @Test public void LastVisibleNumberDirectRule_FullRowTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,3); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 3); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //full col + // full col @Test public void LastVisibleNumberDirectRule_FullColTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/common/3-0ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 1); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //empty row + // empty row @Test public void LastVisibleNumberDirectRule_EmptyRowTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -94,28 +91,27 @@ public void LastVisibleNumberDirectRule_EmptyRowTest() throws InvalidFileFormatE TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 2); cell.setData(5); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //empty col + // empty col @Test public void LastVisibleNumberDirectRule_EmptyColTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -123,136 +119,135 @@ public void LastVisibleNumberDirectRule_EmptyColTest() throws InvalidFileFormatE TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(3,4); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(3, 4); cell.setData(5); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //1-2 row + // 1-2 row public void LastVisibleNumberDirectRule_PartialRowTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 2); cell.setData(3); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //1-2 col + // 1-2 col public void LastVisibleNumberDirectRule_PartialColTest1() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/1-2ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(2,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(2, 2); cell.setData(2); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 row + // 2-1 row public void LastVisibleNumberDirectRule_PartialRowTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1ColOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,1); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 1); cell.setData(2); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2-1 col + // 2-1 col public void LastVisibleNumberDirectRule_PartialColTest2() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/LastVisibleDirectRules/2-1RowOpening", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(0,2); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(0, 2); cell.setData(1); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java b/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java index cc39d8183..ed70fce40 100644 --- a/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/NEdgeDirectTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -8,14 +7,13 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import edu.rpi.legup.puzzle.skyscrapers.rules.NEdgeDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class NEdgeDirectTest { private static final NEdgeDirectRule RULE = new NEdgeDirectRule(); @@ -27,7 +25,7 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //-> row, empty -> full + // -> row, empty -> full @Test public void NEdgeDirectRule_RightRowTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -35,111 +33,110 @@ public void NEdgeDirectRule_RightRowTest() throws InvalidFileFormatException { TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - for(int i = 0; i < 5; i++){ - SkyscrapersCell cell = board.getCell(i,0); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < 5; i++) { + SkyscrapersCell cell = board.getCell(i, 0); cell.setData(i + 1); board.addModifiedData(cell); } Assert.assertNull(RULE.checkRule(transition)); - for(int i = 0; i < board.getHeight(); i++) { - for(int k = 0; k < board.getWidth(); k++) { - if(i == 0) { + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if (i == 0) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //<-row, partial -> partial + // <-row, partial -> partial @Test public void NEdgeDirectRule_LeftRowTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/NEdgeDirectRule/LeftRowPartial", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - SkyscrapersCell cell = board.getCell(1,3); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + SkyscrapersCell cell = board.getCell(1, 3); cell.setData(2); 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //up col, partial -> full + // up col, partial -> full @Test public void NEdgeDirectRule_UpColTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/NEdgeDirectRule/UpColPartial", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - for(int i = 0; i < 2; i++){ - SkyscrapersCell cell = board.getCell(1,i); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 0; i < 2; i++) { + SkyscrapersCell cell = board.getCell(1, i); cell.setData(i + 1); board.addModifiedData(cell); } Assert.assertNull(RULE.checkRule(transition)); - for(int i = 0; i < board.getHeight(); i++) { - for(int k = 0; k < board.getWidth(); k++) { - if(k == 1 && i < 2) { + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if (k == 1 && i < 2) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //down col, empty -> partial + // down col, empty -> partial @Test public void NEdgeDirectRule_DownColTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/NEdgeDirectRule/DownColEmpty", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); - SkyscrapersBoard board = (SkyscrapersBoard)transition.getBoard(); - for(int i = 1; i < 5; i++){ - SkyscrapersCell cell = board.getCell(3,i); + SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); + for (int i = 1; i < 5; i++) { + SkyscrapersCell cell = board.getCell(3, i); cell.setData(5 - i); board.addModifiedData(cell); } Assert.assertNull(RULE.checkRule(transition)); - for(int i = 0; i < board.getHeight(); i++) { - for(int k = 0; k < board.getWidth(); k++) { - if(k == 3 && i > 0) { + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + if (k == 3 && i > 0) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java b/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java index 40c21e604..b417a5227 100644 --- a/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/NumberForCellCaseRuleTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeNode; @@ -9,14 +8,13 @@ import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.rules.NumberForCellCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.util.ArrayList; - public class NumberForCellCaseRuleTest { private static final NumberForCellCaseRule RULE = new NumberForCellCaseRule(); @@ -28,7 +26,7 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //basic, max cases + // basic, max cases @Test public void NumberForCellCaseRule_BasicEmpty() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -42,19 +40,19 @@ public void NumberForCellCaseRule_BasicEmpty() throws InvalidFileFormatException board.setDupeFlag(false); board.setViewFlag(false); - ArrayList cases = RULE.getCases(board,board.getCell(0,0)); + ArrayList cases = RULE.getCases(board, board.getCell(0, 0)); Assert.assertEquals(board.getWidth(), cases.size()); - for(int i=0;i cases = RULE.getCases(board,board.getCell(0,0)); + ArrayList cases = RULE.getCases(board, board.getCell(0, 0)); Assert.assertEquals(board.getWidth(), cases.size()); - for(int i=0;i cases = RULE.getCases(board,board.getCell(2,3)); + ArrayList cases = RULE.getCases(board, board.getCell(2, 3)); Assert.assertEquals(1, cases.size()); SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); - PuzzleElement changedCell = expected.getCell(2,3); + PuzzleElement changedCell = expected.getCell(2, 3); changedCell.setData(1); expected.addModifiedData(changedCell); Assert.assertTrue(expected.equalsBoard(cases.get(0))); } - //dupe, no cases + // dupe, no cases @Test public void NumberForCellCaseRule_DupeNone() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -140,12 +141,12 @@ public void NumberForCellCaseRule_DupeNone() throws InvalidFileFormatException { board.setDupeFlag(true); board.setViewFlag(false); - ArrayList cases = RULE.getCases(board,board.getCell(2,3)); + ArrayList cases = RULE.getCases(board, board.getCell(2, 3)); Assert.assertEquals(0, cases.size()); } - //visibility, max cases + // visibility, max cases @Test public void NumberForCellCaseRule_ViewEmpty() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -159,19 +160,19 @@ public void NumberForCellCaseRule_ViewEmpty() throws InvalidFileFormatException board.setDupeFlag(false); board.setViewFlag(true); - ArrayList cases = RULE.getCases(board,board.getCell(1,4)); + ArrayList cases = RULE.getCases(board, board.getCell(1, 4)); Assert.assertEquals(4, cases.size()); - for(int i=0;i cases = RULE.getCases(board,board.getCell(2,3)); + ArrayList cases = RULE.getCases(board, board.getCell(2, 3)); Assert.assertEquals(1, cases.size()); SkyscrapersBoard expected = ((SkyscrapersBoard) transition.getBoard()).copy(); - PuzzleElement changedCell = expected.getCell(2,3); + PuzzleElement changedCell = expected.getCell(2, 3); changedCell.setData(1); expected.addModifiedData(changedCell); Assert.assertTrue(expected.equalsBoard(cases.get(0))); } - //visibility, no cases + // visibility, no cases @Test public void NumberForCellCaseRule_ViewNone() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -221,8 +225,8 @@ public void NumberForCellCaseRule_ViewNone() throws InvalidFileFormatException { board.setDupeFlag(false); board.setViewFlag(true); - ArrayList cases = RULE.getCases(board,board.getCell(2,3)); + ArrayList cases = RULE.getCases(board, board.getCell(2, 3)); Assert.assertEquals(0, cases.size()); } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java index 69f4e593a..ec49a8194 100644 --- a/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/PreemptiveVisibilityContradictionTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -15,7 +14,8 @@ public class PreemptiveVisibilityContradictionTest { - private static final PreemptiveVisibilityContradictionRule RULE = new PreemptiveVisibilityContradictionRule(); + private static final PreemptiveVisibilityContradictionRule RULE = + new PreemptiveVisibilityContradictionRule(); private static Skyscrapers skyscrapers; @BeforeClass @@ -24,9 +24,10 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //empty + // empty @Test - public void PreemptiveVisibilityContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + public void PreemptiveVisibilityContradictionRule_EmptyBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -41,9 +42,10 @@ public void PreemptiveVisibilityContradictionRule_EmptyBoardTest() throws Invali } } - //correct board, no cont + // correct board, no cont @Test - public void PreemptiveVisibilityContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + public void PreemptiveVisibilityContradictionRule_SolvedBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -58,10 +60,13 @@ public void PreemptiveVisibilityContradictionRule_SolvedBoardTest() throws Inval } } - //invalid board, no cont + // invalid board, no cont @Test - public void PreemptiveVisibilityContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction", skyscrapers); + public void PreemptiveVisibilityContradictionRule_OtherContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -75,10 +80,13 @@ public void PreemptiveVisibilityContradictionRule_OtherContradictionTest() throw } } - //on row + // on row @Test - public void PreemptiveVisibilityContradictionRule_RowContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction", skyscrapers); + public void PreemptiveVisibilityContradictionRule_RowContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedRowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -88,19 +96,21 @@ public void PreemptiveVisibilityContradictionRule_RowContradictionTest() throws SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { - if(i==1){ + if (i == 1) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //on col + // on col @Test - public void PreemptiveVisibilityContradictionRule_ColContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction", skyscrapers); + public void PreemptiveVisibilityContradictionRule_ColContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedColContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -110,19 +120,21 @@ public void PreemptiveVisibilityContradictionRule_ColContradictionTest() throws SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { - if(i==2){ + if (i == 2) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //multitudes + // multitudes @Test - public void PreemptiveVisibilityContradictionRule_AllContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", skyscrapers); + public void PreemptiveVisibilityContradictionRule_AllContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/AllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -136,10 +148,13 @@ public void PreemptiveVisibilityContradictionRule_AllContradictionTest() throws } } - //multitudes - preemptive + // multitudes - preemptive @Test - public void PreemptiveVisibilityContradictionRule_ImpliedAllContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", skyscrapers); + public void PreemptiveVisibilityContradictionRule_ImpliedAllContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -152,4 +167,4 @@ public void PreemptiveVisibilityContradictionRule_ImpliedAllContradictionTest() Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java index d5f21a8d7..398bba6e5 100644 --- a/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/UnresolvedCellContradictionTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -15,7 +14,8 @@ public class UnresolvedCellContradictionTest { - private static final UnresolvedCellContradictionRule RULE = new UnresolvedCellContradictionRule(); + private static final UnresolvedCellContradictionRule RULE = + new UnresolvedCellContradictionRule(); private static Skyscrapers skyscrapers; @BeforeClass @@ -24,7 +24,7 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //empty + // empty @Test public void UnresolvedCellContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); @@ -43,9 +43,10 @@ public void UnresolvedCellContradictionRule_EmptyBoardTest() throws InvalidFileF } } - //correct board, no cont + // correct board, no cont @Test - public void UnresolvedCellContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + public void UnresolvedCellContradictionRule_SolvedBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -62,10 +63,13 @@ public void UnresolvedCellContradictionRule_SolvedBoardTest() throws InvalidFile } } - //invalid board, no cont + // invalid board, no cont @Test - public void UnresolvedCellContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", skyscrapers); + public void UnresolvedCellContradictionRule_OtherContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -81,10 +85,13 @@ public void UnresolvedCellContradictionRule_OtherContradictionTest() throws Inva } } - //3 in a row, 1 in col creates contradiction + // 3 in a row, 1 in col creates contradiction @Test - public void UnresolvedCellContradictionRule_RowContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + public void UnresolvedCellContradictionRule_RowContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -95,20 +102,22 @@ public void UnresolvedCellContradictionRule_RowContradictionTest() throws Invali SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - if(k==2 && i==3){ + if (k == 2 && i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //3 in a col, 1 in row creates contradiction + // 3 in a col, 1 in row creates contradiction @Test - public void UnresolvedCellContradictionRule_ColContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction", skyscrapers); + public void UnresolvedCellContradictionRule_ColContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -119,20 +128,22 @@ public void UnresolvedCellContradictionRule_ColContradictionTest() throws Invali SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - if(k==1 && i==0){ + if (k == 1 && i == 0) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - //2 in a col, 2 in row creates cell contradiction + // 2 in a col, 2 in row creates cell contradiction @Test - public void UnresolvedCellContradictionRule_MixedContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction", skyscrapers); + public void UnresolvedCellContradictionRule_MixedContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2CellContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -143,13 +154,12 @@ public void UnresolvedCellContradictionRule_MixedContradictionTest() throws Inva SkyscrapersBoard board = (SkyscrapersBoard) transition.getBoard(); for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - if(k==2 && i==3){ + if (k == 2 && i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else{ + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java b/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java index fe4a4865a..17139fb60 100644 --- a/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java +++ b/src/test/java/puzzles/skyscrapers/rules/UnresolvedNumberContradictionTest.java @@ -1,6 +1,5 @@ package puzzles.skyscrapers.rules; - import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.Skyscrapers; @@ -15,7 +14,8 @@ public class UnresolvedNumberContradictionTest { - private static final UnresolvedNumberContradictionRule RULE = new UnresolvedNumberContradictionRule(); + private static final UnresolvedNumberContradictionRule RULE = + new UnresolvedNumberContradictionRule(); private static Skyscrapers skyscrapers; @BeforeClass @@ -24,9 +24,10 @@ public static void setUp() { skyscrapers = new Skyscrapers(); } - //empty + // empty @Test - public void UnresolvedNumberContradictionRule_EmptyBoardTest() throws InvalidFileFormatException { + public void UnresolvedNumberContradictionRule_EmptyBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/empty", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -41,9 +42,10 @@ public void UnresolvedNumberContradictionRule_EmptyBoardTest() throws InvalidFil } } - //correct board, no cont + // correct board, no cont @Test - public void UnresolvedNumberContradictionRule_SolvedBoardTest() throws InvalidFileFormatException { + public void UnresolvedNumberContradictionRule_SolvedBoardTest() + throws InvalidFileFormatException { TestUtilities.importTestBoard("puzzles/skyscrapers/rules/common/Solved", skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); @@ -58,10 +60,13 @@ public void UnresolvedNumberContradictionRule_SolvedBoardTest() throws InvalidFi } } - //invalid board, no cont + // invalid board, no cont @Test - public void UnresolvedNumberContradictionRule_OtherContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", skyscrapers); + public void UnresolvedNumberContradictionRule_OtherContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/VisibilityContradictionRules/ImpliedAllContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -75,10 +80,13 @@ public void UnresolvedNumberContradictionRule_OtherContradictionTest() throws In } } - //3 in a row, 1 in col creates contradiction + // 3 in a row, 1 in col creates contradiction @Test - public void UnresolvedNumberContradictionRule_RowContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", skyscrapers); + public void UnresolvedNumberContradictionRule_RowContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1RowContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -90,17 +98,19 @@ public void UnresolvedNumberContradictionRule_RowContradictionTest() throws Inva for (int i = 0; i < board.getHeight(); i++) { if (i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //3 in a col, 1 in row creates contradiction + // 3 in a col, 1 in row creates contradiction @Test - public void UnresolvedNumberContradictionRule_ColContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction", skyscrapers); + public void UnresolvedNumberContradictionRule_ColContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/3-1ColContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -112,17 +122,19 @@ public void UnresolvedNumberContradictionRule_ColContradictionTest() throws Inva for (int i = 0; i < board.getHeight(); i++) { if (i == 1) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //2 in a row/col, 2 in other row/cols creates number contradiction + // 2 in a row/col, 2 in other row/cols creates number contradiction @Test - public void UnresolvedNumberContradictionRule_TwoContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction", skyscrapers); + public void UnresolvedNumberContradictionRule_TwoContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/2-2NumberContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -134,17 +146,19 @@ public void UnresolvedNumberContradictionRule_TwoContradictionTest() throws Inva for (int i = 0; i < board.getHeight(); i++) { if (i == 1 || i == 3) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } - //1 in a row/col, 3 in other row/cols creates number contradiction + // 1 in a row/col, 3 in other row/cols creates number contradiction @Test - public void UnresolvedNumberContradictionRule_ThreeContradictionTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction", skyscrapers); + public void UnresolvedNumberContradictionRule_ThreeContradictionTest() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/skyscrapers/rules/UnresolvedContradictionRules/1-3NumberContradiction", + skyscrapers); TreeNode rootNode = skyscrapers.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -156,10 +170,9 @@ public void UnresolvedNumberContradictionRule_ThreeContradictionTest() throws In for (int i = 0; i < board.getHeight(); i++) { if (i == 1 || i == 2) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(i, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(i, i))); } } } -} \ No newline at end of file +} diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java index b7ec8eb02..38284c410 100644 --- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java @@ -8,14 +8,13 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.EmptyFieldDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class EmptyFieldDirectRuleTest { private static final EmptyFieldDirectRule RULE = new EmptyFieldDirectRule(); @@ -32,7 +31,8 @@ public static void setUp() { // checks if tiles logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyField", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyField", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -56,8 +56,7 @@ public void EmptyFieldTest() throws InvalidFileFormatException { if (c.getLocation().equals(cell1.getLocation())) { // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } @@ -71,7 +70,8 @@ public void EmptyFieldTest() throws InvalidFileFormatException { // checks if tiles logically follow the EmptyFieldDirectRule @Test public void DiagonalTreeTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/DiagonalTree", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/DiagonalTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -79,7 +79,7 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - //change the board's cells considering the EmptyField rule + // change the board's cells considering the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); board.addModifiedData(cell1); @@ -95,8 +95,7 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { if (c.getLocation().equals(cell1.getLocation())) { // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } @@ -110,7 +109,8 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { // checks if tiles don't logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTestFail() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -118,7 +118,7 @@ public void EmptyFieldTestFail() throws InvalidFileFormatException { // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - //change the board's cells considering breaking the EmptyField rule + // change the board's cells considering breaking the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); board.addModifiedData(cell1); @@ -143,7 +143,8 @@ public void EmptyFieldTestFail() throws InvalidFileFormatException { // checks if tiles don't logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTestFailTop() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -151,7 +152,7 @@ public void EmptyFieldTestFailTop() throws InvalidFileFormatException { // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - //change the board's cells considering breaking the EmptyField rule + // change the board's cells considering breaking the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); board.addModifiedData(cell1); @@ -176,7 +177,8 @@ public void EmptyFieldTestFailTop() throws InvalidFileFormatException { // checks if tiles don't logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTestFailTopBottom() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -184,7 +186,7 @@ public void EmptyFieldTestFailTopBottom() throws InvalidFileFormatException { // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - //change the board's cells considering breaking the EmptyField rule + // change the board's cells considering breaking the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); board.addModifiedData(cell1); @@ -209,7 +211,8 @@ public void EmptyFieldTestFailTopBottom() throws InvalidFileFormatException { // checks if tiles don't logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTestFailLeft() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -217,7 +220,7 @@ public void EmptyFieldTestFailLeft() throws InvalidFileFormatException { // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - //change the board's cells considering breaking the EmptyField rule + // change the board's cells considering breaking the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); board.addModifiedData(cell1); @@ -242,7 +245,8 @@ public void EmptyFieldTestFailLeft() throws InvalidFileFormatException { // checks if tiles don't logically follow the EmptyFieldDirectRule @Test public void EmptyFieldTestFailRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -250,7 +254,7 @@ public void EmptyFieldTestFailRight() throws InvalidFileFormatException { // get board state TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - //change the board's cells considering breaking the EmptyField rule + // change the board's cells considering breaking the EmptyField rule TreeTentCell cell1 = board.getCell(1, 1); cell1.setData(TreeTentType.GRASS); board.addModifiedData(cell1); @@ -269,4 +273,3 @@ public void EmptyFieldTestFailRight() throws InvalidFileFormatException { } } } - diff --git a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java index 2517df563..0783ab8b8 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java @@ -8,16 +8,15 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.FinishWithGrassDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; -import java.util.List; -import java.util.ArrayList; - public class FinishWithGrassDirectRuleTest { private static final FinishWithGrassDirectRule RULE = new FinishWithGrassDirectRule(); @@ -30,15 +29,15 @@ public static void setUp() { } /** - * 3x3 TreeTent puzzle with a tent at (0,0) - * Tests FinishWithGrassDirectRule on GRASS tiles horizontal of the tent - * at (1,0) and (2,0) - * + * 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 FinishWithGrassHorizontalTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -63,11 +62,11 @@ public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { 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())) { + if (c.getLocation().equals(cell1.getLocation()) + || c.getLocation().equals(cell2.getLocation())) { // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } @@ -76,15 +75,15 @@ public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { } /** - * 3x3 TreeTent puzzle with a tent at (0,0) - * Tests FinishWithGrassDirectRule on GRASS tiles vertical of the tent - * at (0,1) and (0,2) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -109,11 +108,11 @@ public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { 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())) { + if (c.getLocation().equals(cell1.getLocation()) + || c.getLocation().equals(cell2.getLocation())) { // logically follows Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } @@ -122,15 +121,15 @@ public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { } /** - * 3x3 TreeTent puzzle with a tent at (0,0) - * Tests FinishWithGrassDirectRule on GRASS tiles - * at (1,0), (2,0), (0,1), and (0,2) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -161,14 +160,13 @@ public void FinishWithGrassTest() throws InvalidFileFormatException { 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())) { + 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 { + } else { // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } @@ -177,15 +175,15 @@ public void FinishWithGrassTest() throws InvalidFileFormatException { } /** - * 3x3 TreeTent puzzle with no tents - * Tests FinishWithGrassDirectRule on GRASS tiles - * GRASS tiles fill entire board - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -216,16 +214,17 @@ public void NoTentTest() throws InvalidFileFormatException { 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) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -257,14 +256,13 @@ public void MiddleTentTest() throws InvalidFileFormatException { 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())) { + 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 { + } else { // does not use the rule to logically follow Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } @@ -273,15 +271,15 @@ public void MiddleTentTest() throws InvalidFileFormatException { } /** - * 3x3 TreeTent puzzle with missing tents - * Tests FinishWithGrassDirectRule on GRASS tiles filling the puzzle - * all GRASS tiles should fail the FinishWithGrassDirectRule - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -312,16 +310,17 @@ public void FailTentTest() throws InvalidFileFormatException { 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) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -353,18 +352,17 @@ public void SpacedOutTentTest() throws InvalidFileFormatException { 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())) { + 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 { + } 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/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java index 725c3c9de..652af615f 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java @@ -8,15 +8,14 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.FinishWithTentsDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; +import java.util.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; -import java.util.*; - public class FinishWithTentsDirectRuleTest { private static final FinishWithTentsDirectRule RULE = new FinishWithTentsDirectRule(); @@ -27,17 +26,18 @@ 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) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -57,10 +57,10 @@ public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { 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())) { + if ((c.getLocation()).equals(cell1.getLocation()) + || (c.getLocation()).equals(cell2.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } @@ -68,15 +68,16 @@ public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { } /** - * 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) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -96,10 +97,10 @@ public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { 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())) { + if ((c.getLocation()).equals(cell1.getLocation()) + || (c.getLocation()).equals(cell2.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, c)); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } @@ -107,15 +108,15 @@ public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { } /** - * 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) - * + * 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 FinishWithTentsTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -126,7 +127,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); @@ -142,13 +143,12 @@ public void FinishWithTentsTest() throws InvalidFileFormatException { 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } @@ -156,15 +156,15 @@ public void FinishWithTentsTest() throws InvalidFileFormatException { } /** - * 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) - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -191,13 +191,12 @@ public void AdditionalTentsTest() throws InvalidFileFormatException { 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, c)); } } @@ -205,16 +204,15 @@ public void AdditionalTentsTest() throws InvalidFileFormatException { } /** - * 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 - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -242,17 +240,16 @@ public void FinishWithTentsFailTest() throws InvalidFileFormatException { } /** - * 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 - * + * 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -263,7 +260,7 @@ public void TooManyTentsTest() throws InvalidFileFormatException { for (int i = 0; i < board.getHeight(); i++) { for (int k = 0; k < board.getWidth(); k++) { - if((k == 1)&&(i == 1)) { + if ((k == 1) && (i == 1)) { continue; } TreeTentCell c = board.getCell(k, i); @@ -283,18 +280,17 @@ public void TooManyTentsTest() throws InvalidFileFormatException { } /** - * 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 - * + * 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); + 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); @@ -309,7 +305,7 @@ public void AmbiguousTentsTest () throws InvalidFileFormatException { 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))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -323,7 +319,7 @@ public void AmbiguousTentsTest () throws InvalidFileFormatException { 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))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -337,7 +333,7 @@ public void AmbiguousTentsTest () throws InvalidFileFormatException { 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))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -351,11 +347,8 @@ public void AmbiguousTentsTest () throws InvalidFileFormatException { 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))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } - - - diff --git a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java index 3e1b390fb..92d6e4a59 100644 --- a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java @@ -8,14 +8,13 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.LastCampingSpotDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class LastCampingSpotDirectRuleTest { private static final LastCampingSpotDirectRule RULE = new LastCampingSpotDirectRule(); @@ -29,12 +28,13 @@ public static void setUp() { /** * @throws InvalidFileFormatException - * - * Checks if a test works for an empty square above a tree which is surrounded on all other sides. + *

Checks if a test works for an empty square above a tree which is surrounded on all + * other sides. */ @Test public void EmptyFieldTest_Up() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -53,8 +53,7 @@ public void EmptyFieldTest_Up() throws InvalidFileFormatException { Point point = new Point(k, i); if (point.equals(cell1.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -63,12 +62,13 @@ public void EmptyFieldTest_Up() throws InvalidFileFormatException { /** * @throws InvalidFileFormatException - * - * Checks if a test works for an empty square below a tree which is surrounded on all other sides. + *

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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -87,8 +87,7 @@ public void EmptyFieldTest_Down() throws InvalidFileFormatException { Point point = new Point(k, i); if (point.equals(cell1.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -97,12 +96,13 @@ public void EmptyFieldTest_Down() throws InvalidFileFormatException { /** * @throws InvalidFileFormatException - * - * Checks if a test works for an empty square to the left of a tree which is surrounded on all other sides. + *

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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -121,8 +121,7 @@ public void EmptyFieldTest_Left() throws InvalidFileFormatException { Point point = new Point(k, i); if (point.equals(cell1.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -131,12 +130,13 @@ public void EmptyFieldTest_Left() throws InvalidFileFormatException { /** * @throws InvalidFileFormatException - * - * Checks if a test works for an empty square to the right of a tree which is surrounded on all other sides. + *

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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -155,14 +155,10 @@ public void EmptyFieldTest_Right() throws InvalidFileFormatException { Point point = new Point(k, i); if (point.equals(cell1.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } } - - - diff --git a/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java index 52aea28c0..b17c92486 100644 --- a/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java @@ -1,21 +1,17 @@ package puzzles.treetent.rules; -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - 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.NoTentForTreeContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; - import java.awt.*; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class NoTentForTreeContradictionRuleTest { @@ -29,12 +25,13 @@ public static void setUp() { } /** - * @throws InvalidFileFormatException - * Tests if a tree is next to only grass in a 2x2 grid triggers the contradiction + * @throws InvalidFileFormatException Tests if a tree is next to only grass in a 2x2 grid + * triggers the contradiction */ @Test public void NoTentForTreeContradictionRule_Basic() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTree", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -49,12 +46,14 @@ public void NoTentForTreeContradictionRule_Basic() throws InvalidFileFormatExcep } /** - * @throws InvalidFileFormatException - * Tests similarly to above, but now with a tent diagonally next to the tree, which should still contradict + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -69,12 +68,13 @@ public void NoTentForTreeContradictionRule_Diagonal() throws InvalidFileFormatEx } /** - * @throws InvalidFileFormatException - * Tests that adjacent trees do not allow a pass + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -89,12 +89,15 @@ public void NoTentForTreeContradictionRule_TwoTrees() throws InvalidFileFormatEx } /** - * @throws InvalidFileFormatException - * Tests similarly to above, but now with a tent diagonally next to two trees, which should still contradict on one. + * @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); + 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); @@ -108,4 +111,3 @@ public void NoTentForTreeContradictionRule_TwoTreesDiagonal() throws InvalidFile Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } } - diff --git a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java index 2ad2ac90e..fd6be92de 100644 --- a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java @@ -1,17 +1,16 @@ package puzzles.treetent.rules; -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.rules.NoTreeForTentContradictionRule; 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 NoTreeForTentContradictionRuleTest { @@ -25,12 +24,13 @@ public static void setUp() { } /** - * @throws InvalidFileFormatException - * Tests if, in a 2x2 Grid, a Tent in the NW corner has no adjacent trees + * @throws InvalidFileFormatException Tests if, in a 2x2 Grid, a Tent in the NW corner has no + * adjacent trees */ @Test public void NoTreeForTentContradictionRule_NW() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -45,12 +45,13 @@ public void NoTreeForTentContradictionRule_NW() throws InvalidFileFormatExceptio } /** - * @throws InvalidFileFormatException - * Tests if, in a 2x2 Grid, a Tent in the NE corner has no adjacent trees + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -65,12 +66,13 @@ public void NoTreeForTentContradictionRule_NE() throws InvalidFileFormatExceptio } /** - * @throws InvalidFileFormatException - * Tests if, in a 2x2 Grid, a Tent in the NW corner has no adjacent trees + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -85,12 +87,13 @@ public void NoTreeForTentContradictionRule_SW() throws InvalidFileFormatExceptio } /** - * @throws InvalidFileFormatException - * Tests if, in a 2x2 Grid, a Tent in the SE corner has no adjacent trees + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -105,12 +108,13 @@ public void NoTreeForTentContradictionRule_SE() throws InvalidFileFormatExceptio } /** - * @throws InvalidFileFormatException - * Tests if, in a 3x3 Grid with no trees, a Tent in the center cell has no adjacent trees + * @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); + 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); @@ -130,12 +134,15 @@ public void NoTreeForTentContradictionRule_3x3() throws InvalidFileFormatExcepti } /** - * @throws InvalidFileFormatException - * Tests if, in a 3x3 Grid with diagonal trees, a Tent in the center cell has no adjacent trees + * @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); + 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); @@ -155,12 +162,14 @@ public void NoTreeForTentContradictionRule_3x3WithDiagonalTrees() throws Invalid } /** - * @throws InvalidFileFormatException - * Tests if, in a 3x3 Grid with an adjacent tree, test does not assert null. + * @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); + 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); @@ -180,12 +189,14 @@ public void NoTreeForTentContradictionRule_YesTree() throws InvalidFileFormatExc } /** - * @throws InvalidFileFormatException - * Tests if, in a 3x3 Grid with touching tents, a Tent in the center cell has no adjacent trees + * @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); + 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); @@ -204,4 +215,3 @@ public void NoTreeForTentContradictionRule_JustTent() throws InvalidFileFormatEx Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2))); } } - diff --git a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java index 999405747..7ff57a052 100644 --- a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java @@ -8,17 +8,17 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.SurroundTentWithGrassDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.awt.*; - public class SurroundTentWithGrassDirectRuleTest { - private static final SurroundTentWithGrassDirectRule RULE = new SurroundTentWithGrassDirectRule(); + private static final SurroundTentWithGrassDirectRule RULE = + new SurroundTentWithGrassDirectRule(); private static TreeTent treetent; @BeforeClass @@ -28,12 +28,14 @@ public static void setUp() { } /** - * @throws InvalidFileFormatException - * Test to check if all adjacent and diagonals not filled with a tree are filled with grass + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrass", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -55,8 +57,7 @@ public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatExcepti 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -65,13 +66,15 @@ 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 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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -86,7 +89,7 @@ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileForm cell3.setData(TreeTentType.GRASS); TreeTentCell cell4 = board.getCell(0, 1); cell4.setData(TreeTentType.GRASS); - //Skip (1,1) due to being the Tent + // Skip (1,1) due to being the Tent TreeTentCell cell5 = board.getCell(2, 1); cell5.setData(TreeTentType.GRASS); TreeTentCell cell6 = board.getCell(0, 2); @@ -99,7 +102,7 @@ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileForm board.addModifiedData(cell1); board.addModifiedData(cell2); board.addModifiedData(cell3); - //board.addModifiedData(cell4); + // board.addModifiedData(cell4); board.addModifiedData(cell5); board.addModifiedData(cell6); board.addModifiedData(cell7); @@ -110,14 +113,16 @@ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileForm 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()) - ) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -126,12 +131,14 @@ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileForm /** * @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 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); + 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); @@ -140,13 +147,10 @@ public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFor Assert.assertNull(RULE.checkRule(transition)); - for (int i = 0; i < board.getHeight(); i++){ - for (int k = 0; k < board.getWidth(); k++){ + 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/TentForTreeDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java index 33f37910c..68dbeaf48 100644 --- a/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java @@ -3,15 +3,12 @@ // This feature is no longer supported public class TentForTreeDirectRuleTest { -// private static final TentForTreeBasicRule RULE = new TentForTreeBasicRule(); -// private static TreeTent treetent; -// -// @BeforeClass -// public static void setUp() { -// MockGameBoardFacade.getInstance(); -// treetent = new TreeTent(); -// } + // private static final TentForTreeBasicRule RULE = new TentForTreeBasicRule(); + // private static TreeTent treetent; + // + // @BeforeClass + // public static void setUp() { + // MockGameBoardFacade.getInstance(); + // treetent = new TreeTent(); + // } } - - - diff --git a/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java index 4ec8b4e36..1fe7e4bd0 100644 --- a/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TentOrGrassCaseRuleTest.java @@ -9,19 +9,18 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.rules.TentOrGrassCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; +import java.util.ArrayList; 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(); @@ -29,20 +28,17 @@ public static void setUp() { } /** - * 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 + * 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 */ @Test public void TentOrTreeTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TentOrGrassCaseRule/TestPuzzle", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -66,9 +62,9 @@ public void TentOrTreeTest() throws InvalidFileFormatException { TreeTentCell original_cell; TreeTentCell case_cell; - for (int w =0; w < board.getWidth(); w++) { + for (int w = 0; w < board.getWidth(); w++) { for (int h = 0; h < board.getHeight(); h++) { - if (w == 0 && h ==0) { + if (w == 0 && h == 0) { continue; } original_cell = board.getCell(w, h); diff --git a/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java index a5999dc6e..e1773827d 100644 --- a/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TooFewTentsContradictionRuleTest.java @@ -1,20 +1,18 @@ package puzzles.treetent.rules; -import edu.rpi.legup.puzzle.treetent.TreeTentCell; -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - 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.rules.TooFewTentsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; - import java.awt.*; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class TooFewTentsContradictionRuleTest { @@ -28,12 +26,13 @@ public static void setUp() { } /** - * @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. + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsJustY", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -44,13 +43,15 @@ public void TooFewTentsContradictionRule_JustY() throws InvalidFileFormatExcepti } /** - * @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) + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsWithTent", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -61,12 +62,14 @@ public void TooFewTentsContradictionRule_WithTent() throws InvalidFileFormatExce } /** - * @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. + * @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_DoubleBad() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad", treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsDoubleBad", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -77,15 +80,14 @@ public void TooFewTentsContradictionRule_DoubleBad() throws InvalidFileFormatExc } /** - * @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) + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Column", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -94,16 +96,15 @@ public void TooFewTentsContradictionRule_2x2ColumnOnly() throws InvalidFileForma Assert.assertNull(RULE.checkContradiction(board)); - TreeTentCell cell1 = board.getCell(1,0); - TreeTentCell cell2 = board.getCell(1,1); + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -111,15 +112,13 @@ public void TooFewTentsContradictionRule_2x2ColumnOnly() throws InvalidFileForma } /** - * @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) + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents2x2Row", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -128,16 +127,15 @@ public void TooFewTentsContradictionRule_2x2RowOnly() throws InvalidFileFormatEx Assert.assertNull(RULE.checkContradiction(board)); - TreeTentCell cell1 = board.getCell(0,0); - TreeTentCell cell2 = board.getCell(1,0); + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -145,16 +143,15 @@ public void TooFewTentsContradictionRule_2x2RowOnly() throws InvalidFileFormatEx } /** - * @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) + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3Column", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -163,17 +160,18 @@ public void TooFewTentsContradictionRule_3x3OneColumn() throws InvalidFileFormat Assert.assertNull(RULE.checkContradiction(board)); - TreeTentCell cell1 = board.getCell(1,0); - TreeTentCell cell2 = board.getCell(1,1); - TreeTentCell cell3 = board.getCell(1,2); + 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())) { + if (point.equals(cell1.getLocation()) + || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation())) { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -181,16 +179,15 @@ public void TooFewTentsContradictionRule_3x3OneColumn() throws InvalidFileFormat } /** - * @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) + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTents3x3DoubleColumn", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -199,21 +196,24 @@ public void TooFewTentsContradictionRule_3x3TwoColumn() throws InvalidFileFormat 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); + 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())) { + 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 { + } else { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -221,15 +221,14 @@ public void TooFewTentsContradictionRule_3x3TwoColumn() throws InvalidFileFormat } /** - * @throws InvalidFileFormatException - * Looks at a 2x2 Board in the format: - * Tn [] - * Tr [] - * This should fail the contradiction as it is a legal board. + * @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); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TooFewTentsContradictionRule/TooFewTentsNoContradiction", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -244,8 +243,4 @@ public void TooFewTentsContradictionRule_NoContradiction() throws InvalidFileFor } } } - - - } - diff --git a/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java index 2a351556b..2e542b3d2 100644 --- a/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java @@ -1,21 +1,18 @@ package puzzles.treetent.rules; -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - 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.TooManyTentsContradictionRule; import edu.rpi.legup.save.InvalidFileFormatException; - import java.awt.*; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; public class TooManyTentsContradictionRuleTest { @@ -37,21 +34,21 @@ There are tents at (0,1) and (2,2) */ /** - * @throws InvalidFileFormatException - * Tests for TooManyTents if: - * Row Tent Counts: 0,0,0 - * Column Tent Counts: 0,0,0 + * @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); + 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); + TreeTentCell cell1 = board.getCell(0, 1); Assert.assertNull(RULE.checkContradiction(board)); @@ -60,8 +57,7 @@ public void TooManyTentsContradictionRule_TotalFail() throws InvalidFileFormatEx Point point = new Point(k, i); if (point.equals(cell1.getLocation())) { Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -69,22 +65,22 @@ public void TooManyTentsContradictionRule_TotalFail() throws InvalidFileFormatEx } /** - * @throws InvalidFileFormatException - * Tests for TooManyTents if: - * Row Tent Counts: 1,0,0 - * Column Tent Counts: 0,0,0 + * @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); + 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); + TreeTentCell cell1 = board.getCell(0, 0); + TreeTentCell cell2 = board.getCell(0, 1); Assert.assertNull(RULE.checkContradiction(board)); @@ -93,8 +89,7 @@ public void TooManyTentsContradictionRule_TopRight() throws InvalidFileFormatExc 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -102,22 +97,22 @@ public void TooManyTentsContradictionRule_TopRight() throws InvalidFileFormatExc } /** - * @throws InvalidFileFormatException - * Tests for TooManyTests if: - * Row Tent Counts: 0,0,1 - * Column Tent Counts: 0,0,0 + * @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); + 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); + TreeTentCell cell1 = board.getCell(0, 1); + TreeTentCell cell2 = board.getCell(0, 2); Assert.assertNull(RULE.checkContradiction(board)); @@ -126,8 +121,7 @@ public void TooManyTentsContradictionRule_BottomRight() throws InvalidFileFormat 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -135,22 +129,22 @@ public void TooManyTentsContradictionRule_BottomRight() throws InvalidFileFormat } /** - * @throws InvalidFileFormatException - * Tests for TooManyTents if: - * Row Tent Counts: 0,0,0 - * Column Tent Counts: 0,1,0 + * @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); + 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); + TreeTentCell cell1 = board.getCell(0, 1); + TreeTentCell cell2 = board.getCell(1, 1); Assert.assertNull(RULE.checkContradiction(board)); @@ -159,8 +153,7 @@ public void TooManyTentsContradictionRule_TopDown() throws InvalidFileFormatExce 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -168,22 +161,22 @@ public void TooManyTentsContradictionRule_TopDown() throws InvalidFileFormatExce } /** - * @throws InvalidFileFormatException - * Tests for TooManyTents if: - * Row Tent Counts: 0,0,0 - * Column Tent Counts: 0,0,1 + * @throws InvalidFileFormatException Tests for TooManyTents if: Row Tent Counts: 0,0,0 Column + * Tent Counts: 0,0,1 */ @Test - public void TooManyTentsContradictionRule_BottomDown() throws InvalidFileFormatException{ - TestUtilities.importTestBoard("puzzles/treetent/rules/TooManyTentsContradictionRule/TooManyTentsBottomDown",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)); @@ -192,8 +185,7 @@ public void TooManyTentsContradictionRule_BottomDown() throws InvalidFileFormatE 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -201,35 +193,36 @@ public void TooManyTentsContradictionRule_BottomDown() throws InvalidFileFormatE } /** - * @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 + * @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); + 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); + 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())) { + 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } @@ -237,39 +230,39 @@ public void TooManyTentsContradictionRule_TopAccount() throws InvalidFileFormatE } /** - * @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 + * @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); + 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); + 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())) { + 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 { + } else { Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); } } } } } - diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java index f48afe5d7..9f5455a92 100644 --- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java @@ -1,17 +1,16 @@ package puzzles.treetent.rules; -import legup.MockGameBoardFacade; -import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.rules.TouchingTentsContradictionRule; 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 TouchingTentsContradictionRuleTest { @@ -24,15 +23,16 @@ public static void setUp() { treetent = new TreeTent(); } - //DIAGONAL TESTS + // DIAGONAL TESTS /** - * @throws InvalidFileFormatException - * Tests a tent diagonal of orientation T - * T - **/ + * @throws InvalidFileFormatException Tests a tent diagonal of orientation T T + */ @Test - public void TouchingTentsContradictionRule_DiagonalUpLeftToDownRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonal", treetent); + 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); transition.setRule(RULE); @@ -47,13 +47,14 @@ public void TouchingTentsContradictionRule_DiagonalUpLeftToDownRight() throws In } /** - * @throws InvalidFileFormatException - * Tests a tent diagonal of orientation T - * T - **/ + * @throws InvalidFileFormatException Tests a tent diagonal of orientation T T + */ @Test - public void TouchingTentsContradictionRule_DiagonalDownLeftToUpRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt",treetent); + 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); @@ -61,21 +62,22 @@ public void TouchingTentsContradictionRule_DiagonalDownLeftToUpRight() throws In 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.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 + // ADJACENT TESTS /** - * @throws InvalidFileFormatException - * Tests a tent adjacent of orientation T - * T - **/ + * @throws InvalidFileFormatException Tests a tent adjacent of orientation T T + */ @Test - public void TouchingTentsContradictionRule_AdjacentVertical() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacent", treetent); + 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); transition.setRule(RULE); @@ -90,12 +92,14 @@ public void TouchingTentsContradictionRule_AdjacentVertical() throws InvalidFile } /** - * @throws InvalidFileFormatException - * Tests a tent adjacent of orientation TT - **/ + * @throws InvalidFileFormatException Tests a tent adjacent of orientation TT + */ @Test - public void TouchingTentsContradictionRule_AdjacentHorizontal() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt", treetent); + 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); @@ -108,15 +112,16 @@ 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 + + // MIXED TESTS /** - * @throws InvalidFileFormatException - * Tests a tent of orientation TT - * TT - **/ + * @throws InvalidFileFormatException Tests a tent of orientation TT TT + */ @Test public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2",treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -129,14 +134,15 @@ public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormat Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } + /** - * @throws InvalidFileFormatException - * Tests a tent of orientation TT - * T - **/ + * @throws InvalidFileFormatException Tests a tent of orientation TT T + */ @Test public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft",treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -149,14 +155,15 @@ public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatExce Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } + /** - * @throws InvalidFileFormatException - * Tests a tent of orientation TT - * T - **/ + * @throws InvalidFileFormatException Tests a tent of orientation TT T + */ @Test public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight",treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -169,14 +176,15 @@ public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatExc Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); } + /** - * @throws InvalidFileFormatException - * Tests a tent of orientation T - * TT - **/ + * @throws InvalidFileFormatException Tests a tent of orientation T TT + */ @Test public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft",treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -189,14 +197,15 @@ public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatEx Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); } + /** - * @throws InvalidFileFormatException - * Tests a tent of orientation T - * TT - **/ + * @throws InvalidFileFormatException Tests a tent of orientation T TT + */ @Test public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight",treetent); + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight", + treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -209,13 +218,15 @@ public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatE Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); } + /** - * @throws InvalidFileFormatException - * Tests if tree adjacent triggers a null + * @throws InvalidFileFormatException Tests if tree adjacent triggers a null */ @Test - public void TouchingTentsContradictionRule_TreeAdjacent() throws InvalidFileFormatException{ - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent",treetent); + 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); @@ -226,13 +237,15 @@ public void TouchingTentsContradictionRule_TreeAdjacent() throws InvalidFileForm Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); } + /** - * @throws InvalidFileFormatException - * Tests if tree diagonal triggers a null + * @throws InvalidFileFormatException Tests if tree diagonal triggers a null */ @Test - public void TouchingTentsContradictionRule_TreeDiagonal() throws InvalidFileFormatException{ - TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal",treetent); + 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); @@ -243,7 +256,4 @@ public void TouchingTentsContradictionRule_TreeDiagonal() throws InvalidFileForm Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); } - } - - diff --git a/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java index 5b96fcf1c..f4ea6703b 100644 --- a/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java @@ -3,15 +3,12 @@ // This feature is no longer supported public class TreeForTentDirectRuleTest { -// private static final TreeForTentBasicRule RULE = new TreeForTentBasicRule(); -// private static TreeTent treetent; - -// @BeforeClass -// public static void setUp() { -// MockGameBoardFacade.getInstance(); -// treetent = new TreeTent(); -// } + // private static final TreeForTentBasicRule RULE = new TreeForTentBasicRule(); + // private static TreeTent treetent; + + // @BeforeClass + // public static void setUp() { + // MockGameBoardFacade.getInstance(); + // treetent = new TreeTent(); + // } } - - - From b1e2f9341b5d8398253960456ac8b2a29ad83084 Mon Sep 17 00:00:00 2001 From: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:51:57 -0500 Subject: [PATCH 13/89] Java21 (#714) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Change Java version to 21 and setup JPackage to bundle Java with the app * Setup gradle action for java 21 * Fix distribution in gradle.yml * Fix java version in tests * Fix java distribution in tests * Add jpackage task * Add Linux to jpackage task * Fix jpackage task * Add java setup to jpackage tasks * Separate build into separate tasks for different operating systems * Fix mac jpackage not working and changed names of artifacts * Potential macos installer build fix * Potential macos installer build fix attempt 2 * Potential macos installer build fix attempt 3 * Add quotes around executable name for macos installer * Add logo and shortcut prompt * Update version in build.gradle * Make installer name different to app (It's a weird way to do it, it renames the file in a gradle task). * Update java-autoformat.yml Added check to make sure the pull request is not from a fork --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: charlestian23 --- .github/workflows/gradle.yml | 62 +++- .github/workflows/java-autoformat.yml | 1 + .github/workflows/run-tests.yml | 14 +- .idea/codeStyles/codeStyleConfig.xml | 1 + build.gradle | 40 ++- gradle/wrapper/gradle-wrapper.jar | Bin 56172 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 6 +- gradlew | 301 +++++++++++------- gradlew.bat | 56 ++-- .../edu/rpi/legup/images/Legup/logo.ico | Bin 0 -> 2174 bytes 10 files changed, 327 insertions(+), 154 deletions(-) create mode 100644 src/main/resources/edu/rpi/legup/images/Legup/logo.ico diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ec895971f..856fcd8f0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -8,30 +8,82 @@ on: pull_request: jobs: - build: + build-ubuntu: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 + distribution: temurin - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build -x test + - name: JPackage + run: ./gradlew copyInstaller + - uses: actions/upload-artifact@v4 + with: + name: Ubuntu-Artifact + path: installer + + build-windows: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 21 + uses: actions/setup-java@v1 + with: + java-version: 21 + distribution: temurin + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build -x test + - name: JPackage + run: ./gradlew copyInstaller + - uses: actions/upload-artifact@v4 + with: + name: Windows-Artifact + path: installer + + build-macos: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 21 + uses: actions/setup-java@v1 + with: + java-version: 21 + distribution: temurin + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build -x test + - name: JPackage + run: ./gradlew copyInstaller + - uses: actions/upload-artifact@v4 + with: + name: Mac Artifact + path: installer checkstyle: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 + distribution: temurin - name: gradlew executable run: chmod +x gradlew - name: Run checkstyle diff --git a/.github/workflows/java-autoformat.yml b/.github/workflows/java-autoformat.yml index 5afb1f7a1..0a2f42112 100644 --- a/.github/workflows/java-autoformat.yml +++ b/.github/workflows/java-autoformat.yml @@ -3,6 +3,7 @@ on: pull_request jobs: format: + if: github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest steps: - name: Checkout code diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index bed9d7ad2..03092ecdf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -18,10 +18,11 @@ jobs: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 + distribution: temurin - name: Grant execute permission for gradlew run: chmod +x gradlew @@ -40,10 +41,11 @@ jobs: - name: Check Out Code uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 + distribution: temurin - name: Grant execute permission for gradlew run: chmod +x gradlew @@ -65,8 +67,8 @@ jobs: - name: Set up JDK and Gradle on Windows uses: actions/setup-java@v2 with: - java-version: 11 - distribution: 'adopt' + java-version: 21 + distribution: temurin - name: Run JUnit Tests on Windows run: | diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index f23d52492..38a6942c4 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 49829e473..445a7f8ea 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,7 @@ plugins { id 'com.diffplug.spotless' version '6.25.0' } - -version '2.0.0' +version '5.3.2' apply plugin: 'java' apply plugin: 'application' @@ -41,7 +40,7 @@ apply plugin: 'checkstyle' mainClassName = 'edu.rpi.legup.Legup' -sourceCompatibility = 11 +sourceCompatibility = 21 dependencies { implementation 'org.jetbrains:annotations:20.1.0' @@ -113,7 +112,7 @@ createExe() { bundledJrePath = 'jre' bundledJre64Bit = true jdkPreference = 'preferJre' - jreMinVersion = '11' + jreMinVersion = '21' jreRuntimeBits = '64/32' } @@ -138,4 +137,35 @@ task buildNativeWindows(type: Exec, dependsOn: 'createExe') { repositories { mavenCentral() } -targetCompatibility = JavaVersion.VERSION_11 +targetCompatibility = JavaVersion.VERSION_21 + +tasks.register("jpackage") { + group("jpackage") + doLast { + var operatingSystem = System.getProperty("os.name").toLowerCase() + if (operatingSystem.contains("windows")) { + exec { + commandLine 'cmd', '/c', 'jpackage', '--input', 'build/libs', '--main-jar', 'Legup.jar', '--win-dir-chooser', '--win-shortcut-prompt', '--win-shortcut', '--dest', 'build/installer', '-n', "LEGUP", '--app-version', "${project.version}", '--icon', "src/main/resources/edu/rpi/legup/images/Legup/logo.ico" + } + } else if (operatingSystem.contains("linux")) { + exec { + commandLine 'sh', '-c', "jpackage --input build/libs --main-jar Legup.jar --dest build/installer -n LEGUP --icon src/main/resources/edu/rpi/legup/images/Legup/logo.ico --app-version ${project.version}" + } + } else if (operatingSystem.contains("mac")) { + exec { + commandLine 'bash', '-c', "jpackage --input build/libs --main-jar Legup.jar --dest build/installer -n \"LEGUP\" --icon src/main/resources/edu/rpi/legup/images/Legup/logo.ico --app-version ${project.version}" + } + } else { + println("JPackage task is not set up for " + System.getProperty("os.name")) + } + } +} + +tasks.register('copyInstaller', Sync) { + group("jpackage") + from ("build/installer/") + into("installer") + rename("LEGUP-${project.version}", "LEGUP-installer-${project.version}") +} + +copyInstaller.dependsOn(jpackage) \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 28861d273a5d270fd8f65dd74570c17c9c507736..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 56172 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

ZM+x{iQDVZRnLYbdXrQjU&=u%hsN4|smH&B~F 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 b+=;!jU|P+J`>$CfFsoU|bwOU-ceCsYH7qU$ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2d2fd7c74..1af9e0930 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists \ No newline at end of file +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d51..1aa94a426 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,127 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f9553162f..93e3f59f1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -9,19 +25,23 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,38 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/resources/edu/rpi/legup/images/Legup/logo.ico b/src/main/resources/edu/rpi/legup/images/Legup/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..746d1ba26f98e86c7e21a34e941cb51b7277a473 GIT binary patch literal 2174 zcmb7G%}*Ow5O;4q_SAEZ{fnwfJydFxgr;c%0c;bDzhM2fw%1Z{C}kH}ih;TRR+|(f7(14*qoveeQ64 z>2Nr{BDwOfuwFj2cJb3{weTrvx7)C#+wID8r_(vt@tQs7(H7Q!$~UjCt*xQiY+fq7 z)@U@ava*8ZbYUZD3IeZOM&YPmU zGxRMi(?DZo74Gq2xJRzbntS9L++(+}MzN+QU1H-IAHoFrxO@8sYIG+VJ%yWBzs5|! zgLOKi9h*mv-)?u1R2AfP1$sh(k&3`GF^XJTftd;;tE)&v1DKu7!S8V)9i2orO*Kx2 zks}*Toq?)G5e9a)MH+BxhrcpT2vC-Q8Wm^eo}3 z+wI`}`@g9UdBHa0;Vs#)?cSqDI668KEHFm+&bPO>aeRC%SUf#F#b0mU;LksPC%HPix3$(}XdT z^4XAHn9CbTCRC{j*T~4EQ7YyT4ERwb%yUmxs};F3L)TzsG|8X)iSIO>O3=NS@cX=& zpR>R*c6J`1Om?Yc43?21`zqDDi*RTfKF>IG!s_Pc7E)Rg(}bM~*BI1TjLs34Ix0aA zVag4+=%aGC%s117ZJ)<^9)FE+Ju~ei%umwZaYPg^T;q2UjYh;a6r4oR?~z{M^C~mb z@K3psN@)_GF%zEgAr?8)-@bK2`rV`+RLF0HupIDCVECsW$R>r*On`dWCH1#?CK&KR z33@R$I)ZpqK{Oo1)IBGnp@78ZeqyZfe)b!UgoKCI*ViSFL_7wQI4MgUqiG4L6=RLL zhk55rpOkyh(<#K&nD8|FW64n8aUQy^LrW&a7jp>v}nO>Dl(;|AGHI{|749!YKd% literal 0 HcmV?d00001 From 2b036c0e6b947683e06d659f2ee9385649fc62f5 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:33:00 -0500 Subject: [PATCH 14/89] Update build.gradle (#741) * Update build.gradle Fixing the deprecation issues in the autoformatter * Update build.gradle * Update build.gradle * Update to use Java 21 * Automated Java code formatting changes --------- Co-authored-by: Bram van Heuveln --- .github/workflows/java-autoformat.yml | 5 +++-- build.gradle | 17 ++++++++++------- .../rules/caserule/CaseRule_Generic.java | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/java-autoformat.yml b/.github/workflows/java-autoformat.yml index 0a2f42112..7aa2487cb 100644 --- a/.github/workflows/java-autoformat.yml +++ b/.github/workflows/java-autoformat.yml @@ -11,10 +11,11 @@ jobs: with: ref: ${{ github.head_ref }} - - name: Set up JDK 11 + - name: Set up JDK 21 uses: actions/setup-java@v1 with: - java-version: 11 + java-version: 21 + distribution: temurin - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/build.gradle b/build.gradle index 445a7f8ea..75a2ec77e 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ version '5.3.2' apply plugin: 'java' apply plugin: 'application' -spotless{ +spotless { enforceCheck false format 'misc', { @@ -23,7 +23,7 @@ spotless{ endWithNewline() } - java{ + java { // Use the default importOrder configuration importOrder() @@ -38,9 +38,14 @@ spotless{ apply plugin: 'checkstyle' -mainClassName = 'edu.rpi.legup.Legup' +application { + mainClass.set('edu.rpi.legup.Legup') +} -sourceCompatibility = 21 +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} dependencies { implementation 'org.jetbrains:annotations:20.1.0' @@ -133,11 +138,9 @@ task buildNativeWindows(type: Exec, dependsOn: 'createExe') { commandLine 'cmd', '/c', 'make_windows_installer.bat' } - repositories { mavenCentral() } -targetCompatibility = JavaVersion.VERSION_21 tasks.register("jpackage") { group("jpackage") @@ -168,4 +171,4 @@ tasks.register('copyInstaller', Sync) { rename("LEGUP-${project.version}", "LEGUP-installer-${project.version}") } -copyInstaller.dependsOn(jpackage) \ No newline at end of file +copyInstaller.dependsOn(jpackage) 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 600a9898d..5885f98f8 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 @@ -60,4 +60,4 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } -} \ No newline at end of file +} From 5189a0b7196cd1a5bc51ed78d050da313a1eca8f Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Wed, 21 Feb 2024 13:34:57 -0500 Subject: [PATCH 15/89] Updating to version 6.0.0 Changing to 6.0.0 since Java upgrade may break backwards compatibility --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 75a2ec77e..319cefa4f 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'com.diffplug.spotless' version '6.25.0' } -version '5.3.2' +version '6.0.0' apply plugin: 'java' apply plugin: 'application' From f6e11dcc1eed36b44a4bae8aead8da03f4e53496 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 23 Feb 2024 11:49:24 -0500 Subject: [PATCH 16/89] Update allfiles.txt Fixed some temporary documentation to reference star battle instead of sudoku. --- src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt index 1b1824f21..7970eadbf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -85,11 +85,11 @@ public class StarBattleCell extends GridCell { private int max; /** - * SudokuCell Constructor - creates a new Sudoku cell to hold the puzzleElement + * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement * * @param valueInt value of the star battle cell denoting its state * @param location location of the cell on the board - * @param size size of the sudoku cell + * @param size size of the star battle cell */ public StarBattleCell(int value, Point location, int groupIndex, int size) { super(value, location); From 9c3daa796877a9ade90c6a3cba0378ad09faca1f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 23 Feb 2024 15:29:26 -0500 Subject: [PATCH 17/89] Direct Rule Classes Created skeletons of classes for direct rules. --- .../starbattle/rules/BlackoutDirectRule.java | 49 +++++++++++++++++++ .../rules/FinishWithStarsDirectRule.java | 47 ++++++++++++++++++ .../RegionsWithinRowsColumnsDirectRule.java | 46 +++++++++++++++++ .../RowsColumnsWithinRegionsDirectRule.java | 46 +++++++++++++++++ .../rules/SurroundStarDirectRule.java | 48 ++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java new file mode 100644 index 000000000..3c89b0800 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -0,0 +1,49 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class BlackoutDirectRule extends DirectRule { + + public BlackoutDirectRule() { + super("STBL-BASC-0001", + "Blackout", + "If a row, column, or region has enough stars, its unknown spaces are black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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 null; + } + + /** + * 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; + } +} + + diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java new file mode 100644 index 000000000..d2711eced --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -0,0 +1,47 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class FinishWithStarsDirectRule extends DirectRule { + + public FinishWithStarsDirectRule() { + super("STBL-BASC-0002", + "Finish With Stars", + "Unknown spaces must be stars if there are just enough in a row, column, or region to satisfy the puzzle number.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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 null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java new file mode 100644 index 000000000..81645a2ad --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java @@ -0,0 +1,46 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class RegionsWithinRowsColumnsDirectRule extends DirectRule { + public RegionsWithinRowsColumnsDirectRule() { + super("STBL-BASC-0003", + "Regions Within Rows/Columns", + "If a number of regions is fully contained by an equal number of rows or columns, spaces of other regions in those rows or columns must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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 null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java new file mode 100644 index 000000000..53a7b27ee --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java @@ -0,0 +1,46 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class RowsColumnsWithinRegionsDirectRule extends DirectRule { + public RowsColumnsWithinRegionsDirectRule() { + super("STBL-BASC-0004", + "Rows/Columns Within Regions", + "If a number of rows or columns is fully contained by an equal number of regions, spaces of other rows or columns, respectively, in those regions must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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 null; + } + + /** + * 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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java new file mode 100644 index 000000000..932ef7ddd --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -0,0 +1,48 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +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.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class SurroundStarDirectRule extends DirectRule { + + public SurroundStarDirectRule() { + super("STBL-BASC-0005", + "Surround Star", + "Any space adjacent to a star must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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 null; + } + + /** + * 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; + } +} + From cda9403b1f9bb82880db422e7e18128b3bd53ba4 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:01:39 -0500 Subject: [PATCH 18/89] Added javadoc to cell --- .../java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index 22d74dd7f..2e89649e2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -15,6 +15,7 @@ public class StarBattleCell extends GridCell { * * @param valueInt value of the star battle cell denoting its state * @param location location of the cell on the board + * @param groupIndex indicates what group # the cell is in. * @param size size of the star battle cell */ public StarBattleCell(int value, Point location, int groupIndex, int size) { From 0fb71deb5b70bd388e02b2eaa3b3658a81f19fb7 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:24:04 -0500 Subject: [PATCH 19/89] Adding barebones Contradiction Rules Files Might have to separate the too few and too many rules into 3 files, for row, column, and region. --- .../rules/ClashingOrbitContradictionRule | 33 +++++++++++++++++++ .../rules/TooFewStarsContradictionRule | 33 +++++++++++++++++++ .../rules/TooManyStarsContradictionRule | 33 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule new file mode 100644 index 000000000..2eb584ea1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class ClashingOrbitContradictionRule extends ContradictionRule { + + public ClashingOrbitContradictionRule() { + super("STBL-CONT-0003", + "Clashing Orbit", + "No two stars can be adjacent to each other.", + "INSERT IMAGE NAME HERE"); + } + + /** + * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * + * @param board board to check contradiction + * @param puzzleElement equivalent puzzleElement + * @return null if the transition contains a contradiction at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + return super.getNoContradictionMessage(); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule new file mode 100644 index 000000000..a063b2b6b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class TooFewStarsContradictionRule extends ContradictionRule { + + public TooFewStarsContradictionRule() { + super("STBL-CONT-0002", + "Too Few Stars", + "There are too few stars in this region/row/column and there are not enough places to put the remaining stars.", + "INSERT IMAGE NAME HERE"); + } + + /** + * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * + * @param board board to check contradiction + * @param puzzleElement equivalent puzzleElement + * @return null if the transition contains a contradiction at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + return super.getNoContradictionMessage(); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule new file mode 100644 index 000000000..56cd71ba7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class TooManyStarsContradictionRule extends ContradictionRule { + + public TooManyStarsContradictionRule() { + super("STBL-CONT-0001", + "Too Many Stars", + "There are too many stars in this region/row/column.", + "INSERT IMAGE NAME HERE"); + } + + /** + * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * + * @param board board to check contradiction + * @param puzzleElement equivalent puzzleElement + * @return null if the transition contains a contradiction at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + return super.getNoContradictionMessage(); + } +} \ No newline at end of file From 90aa66b081f76c55e714e2f8d943d89b931c065f Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Fri, 23 Feb 2024 16:55:05 -0500 Subject: [PATCH 20/89] Removed all Inno Setup related code --- build.gradle | 35 -- native/linux/.keep | 0 native/macos/.keep | 0 native/windows/OLD_inno_setup.iss | 53 --- native/windows/inno-setup/Compil32.exe | Bin 2870072 -> 0 bytes native/windows/inno-setup/Default.isl | 384 ------------------ native/windows/inno-setup/ISCC.exe | Bin 873272 -> 0 bytes native/windows/inno-setup/ISCmplr.dll | Bin 1687352 -> 0 bytes native/windows/inno-setup/ISPP.chm | Bin 78412 -> 0 bytes native/windows/inno-setup/ISPP.dll | Bin 1016120 -> 0 bytes native/windows/inno-setup/ISPPBuiltins.iss | 323 --------------- native/windows/inno-setup/ISetup.chm | Bin 392738 -> 0 bytes native/windows/inno-setup/LICENSE | 32 -- native/windows/inno-setup/Setup.e32 | Bin 3194368 -> 0 bytes .../windows/inno-setup/SetupClassicIcon.ico | Bin 4710 -> 0 bytes native/windows/inno-setup/SetupLdr.e32 | Bin 831488 -> 0 bytes .../windows/inno-setup/WizClassicImage-IS.bmp | Bin 52574 -> 0 bytes native/windows/inno-setup/WizClassicImage.bmp | Bin 26494 -> 0 bytes .../inno-setup/WizClassicSmallImage-IS.bmp | Bin 4158 -> 0 bytes .../inno-setup/WizClassicSmallImage.bmp | Bin 1658 -> 0 bytes native/windows/inno-setup/isbunzip.dll | Bin 35616 -> 0 bytes native/windows/inno-setup/isbzip.dll | Bin 39200 -> 0 bytes native/windows/inno-setup/islzma.dll | Bin 90400 -> 0 bytes native/windows/inno-setup/islzma32.exe | Bin 88352 -> 0 bytes native/windows/inno-setup/islzma64.exe | Bin 116000 -> 0 bytes native/windows/inno-setup/isscint.dll | Bin 290592 -> 0 bytes native/windows/inno-setup/isunzlib.dll | Bin 29472 -> 0 bytes native/windows/inno-setup/iszlib.dll | Bin 34592 -> 0 bytes native/windows/legup_inno_setup.iss | 55 --- native/windows/legup_launch4j.xml | 26 -- native/windows/make_windows_installer.bat | 2 - 31 files changed, 910 deletions(-) delete mode 100644 native/linux/.keep delete mode 100644 native/macos/.keep delete mode 100644 native/windows/OLD_inno_setup.iss delete mode 100644 native/windows/inno-setup/Compil32.exe delete mode 100644 native/windows/inno-setup/Default.isl delete mode 100644 native/windows/inno-setup/ISCC.exe delete mode 100644 native/windows/inno-setup/ISCmplr.dll delete mode 100644 native/windows/inno-setup/ISPP.chm delete mode 100644 native/windows/inno-setup/ISPP.dll delete mode 100644 native/windows/inno-setup/ISPPBuiltins.iss delete mode 100644 native/windows/inno-setup/ISetup.chm delete mode 100644 native/windows/inno-setup/LICENSE delete mode 100644 native/windows/inno-setup/Setup.e32 delete mode 100644 native/windows/inno-setup/SetupClassicIcon.ico delete mode 100644 native/windows/inno-setup/SetupLdr.e32 delete mode 100644 native/windows/inno-setup/WizClassicImage-IS.bmp delete mode 100644 native/windows/inno-setup/WizClassicImage.bmp delete mode 100644 native/windows/inno-setup/WizClassicSmallImage-IS.bmp delete mode 100644 native/windows/inno-setup/WizClassicSmallImage.bmp delete mode 100644 native/windows/inno-setup/isbunzip.dll delete mode 100644 native/windows/inno-setup/isbzip.dll delete mode 100644 native/windows/inno-setup/islzma.dll delete mode 100644 native/windows/inno-setup/islzma32.exe delete mode 100644 native/windows/inno-setup/islzma64.exe delete mode 100644 native/windows/inno-setup/isscint.dll delete mode 100644 native/windows/inno-setup/isunzlib.dll delete mode 100644 native/windows/inno-setup/iszlib.dll delete mode 100644 native/windows/legup_inno_setup.iss delete mode 100644 native/windows/legup_launch4j.xml delete mode 100644 native/windows/make_windows_installer.bat diff --git a/build.gradle b/build.gradle index fafa54cac..2c8afa24d 100644 --- a/build.gradle +++ b/build.gradle @@ -71,41 +71,6 @@ jar { archiveFileName = 'Legup.jar' } -/* - * CREATES NATIVE WINDOWS EXECUTABLE - * Launches launch4j to create an executable (.exe) file wrapping the jar - * THIS IS NOT THE INSTALLER - * Add "icon = 'path/to/icon.ico'" to set an icon for the executable - */ -createExe() { - mainClassName = 'edu.rpi.legup.Legup' - outputDir = '../native/windows' - outfile = 'bin/Legup.exe' - bundledJrePath = 'jre' - bundledJre64Bit = true - jdkPreference = 'preferJre' - jreMinVersion = '11' - jreRuntimeBits = '64/32' -} - -/* - * CREATES NATIVE WINDOWS INSTALLER -- ONLY RUNS ON WINDOWS - * Runs the shipped version of Inno Setup (6.2) to compile the installer - * Modify the setup settings in native/windows/legup_inno_setup.iss - * - * Modifications are likely required to run the setup script on your computer: - * Edit the "CHANGE ME" line in native/windows/legup_inno_setup.iss to reflect - * the path to the Java installation you want to ship inside the executable. - */ -task buildNativeWindows(type: Exec, dependsOn: 'createExe') { - jar - createExe - - workingDir = "${buildDir}/../native/windows" - commandLine 'cmd', '/c', 'make_windows_installer.bat' -} - - repositories { mavenCentral() } diff --git a/native/linux/.keep b/native/linux/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/native/macos/.keep b/native/macos/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/native/windows/OLD_inno_setup.iss b/native/windows/OLD_inno_setup.iss deleted file mode 100644 index 528b0b660..000000000 --- a/native/windows/OLD_inno_setup.iss +++ /dev/null @@ -1,53 +0,0 @@ -; Script generated by the Inno Setup Script Wizard. -; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! - -#define MyAppName "Legup" -#define MyAppVersion "2.0" -#define MyAppPublisher "Bram, Inc." -#define MyAppURL "https://github.com/jpoegs/legup2.0" -#define MyAppExeName "Legup.jar" - -[Setup] -; NOTE: The value of AppId uniquely identifies this application. -; Do not use the same AppId value in installers for other applications. -; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) -AppId={{C1DFC564-DE07-4A5D-BF85-DDCF514B57BA} -AppName={#MyAppName} -AppVersion={#MyAppVersion} -;AppVerName={#MyAppName} {#MyAppVersion} -AppPublisher={#MyAppPublisher} -AppPublisherURL={#MyAppURL} -AppSupportURL={#MyAppURL} -AppUpdatesURL={#MyAppURL} -DefaultDirName={pf}\{#MyAppName} -DisableProgramGroupPage=yes -OutputBaseFilename=setup -Compression=lzma -SolidCompression=yes -ChangesAssociations=yes - -[Languages] -Name: "english"; MessagesFile: "compiler:Default.isl" - -[Tasks] -Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked -Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1 - -[Files] -Source: "D:\GitHub\Legup\build\libs\Legup.jar"; DestDir: "{app}"; Flags: ignoreversion -; NOTE: Don't use "Flags: ignoreversion" on any shared system files - -[Icons] -Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" -Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon -Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon - -[Run] -Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: shellexec postinstall skipifsilent - -[Registry] - -Root: HKCR; Subkey: ".legup"; ValueData: "{#MyAppName}"; Flags: uninsdeletevalue; ValueType: string; ValueName: "" -Root: HKCR; Subkey: "{#MyAppName}"; ValueData: "{#MyAppName}"; Flags: uninsdeletekey; ValueType: string; ValueName: "" -Root: HKCR; Subkey: "{#MyAppName}\DefaultIcon"; ValueData: "{app}\{#MyAppExeName},0"; ValueType: string; ValueName: "" -Root: HKCR; Subkey: "{#MyAppName}\shell\open\command"; ValueData: """{app}\{#MyAppExeName}"" ""%1"""; ValueType: string; ValueName: "" \ No newline at end of file diff --git a/native/windows/inno-setup/Compil32.exe b/native/windows/inno-setup/Compil32.exe deleted file mode 100644 index 544993108b832127c9f2466e9e8bc5c0e1bdafb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2870072 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

{c9kGAiTo*g0 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


9ls#}It8ClE?-DH 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%0D}AXGM~BMoM*@(hkTkchbO7;oOwp)q`m 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 q>ANV9bk|lTl*9$nE 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 z zJbpry5p)gr0)AVD@Nh|6t7w?^0Dq+ZGBLo4NHz|HNVL(nM%K_&rOO+gppr>ekDzS;J# zmAC1j5Dnp*dgI6(og$x#3q+of0<{BX7n@JNX!W?*gj(!khl+oeVY~x$W}D#@AD@8q z+VymMOG%Q<9cd4tP@09?RI|8rM%VJ<3llyxjw8{yYsCL46$(mOOIQ1CP>#5S<;FX( zx6p>-M28R(>1vBbxRZb}8}g2}m5q-~oj~G4y$%8)NFav1?J=yo;QTe1`$`If@w8AJ z#|q9&MJEj%Wm2Xf6D_Q-$!9p_L#}f96=@=Z3gevuw3-@W{8ZDhcqb+ZoNJ8qzbHa( z95>I}akRb@lSLkS*D@9Oew5`R-(!mMSRW9iB0wSxv5G@TCMM_xB@|KYyiaq5Xciz< z$oB<&Yq1}RM1=S{FwNHin3!QbHk}DQ7MU@!VVD*kFh_HSw+7a= zbgf&1tO=Ss@Nj_;5~*<^LDF5Q6%mXqc)4GB9hwA&X%do7lTb?&T9Wt9>v%mfkQ8YT zgFzRX3&RZMiK%c)8;J`?FbnN5nVP3_41+btFf8C0hC3X?0hVJRN1(Y-8jbEkX>_y~ zN~5d4P#QIn^^s0!HIRc)-MoNf7@V3$qGn1X;ThA&09#m1^BQDgQ9S0rzzYS)0T@O| zfdjN$P@x=X3X32`0ShgO!-9Y`76hiV&~evGm8-?tE$+-oO_KN2Ac=vo#t8`OHK4j^ zM}Ucx1YjR|iVv-7XltGq#qH>IL7d4MLj9;`h5|#p&PWaNBZXUfnDv^9w%(3CdDYqP zG8Z9a6zBkETlnGH%f=m_wo%XHC-QFuU8BPzSi>te{%kYBn-T|ZI#(}R$nNtxg# zWn{(SAWvhUe)-@2dN)+lfn~YINQ@{bx{l9BLY{RjimqMK+)M=hrI$SXIadl8Vo}@> zYLL-7^{tKZu1;LQr&Myu9nDxMfw+FIQ%=RYNxaqH3hlG6%dV z$>Q3wDol5Y#A7&E+wm&rO(1=;ux}|qOa)FUcy=Mxti6ZF?6;%wHb>RN2n0s?17>>MGuWqbpy-- zpdB-1K?bn8?AV>!P*popf_i4p;A%4qQpb4F?J&Zi6%Do33yq5gux91PW2{t2qv;;# zL+LfX5GSP;oey5a%{UjOM;OsoMHK=@#^V54F2lq}7KQQ^>{S-{e&}Sh@3DCjKx~#2 z`8Xy=Cinoh(tK`NE5A4m4i0Ce0WGKW5X*65S{%bU2#_u(U_H){;Rk^Cndwl=JVTJ> z9?0d>N=rEUy_SG`kZ=nRCENln?|^{~v8;pi*$~5d3{n#9K>h~#$b9ud;71{RzhxBy zS}tQ%Zx5M@fn04q!8 zP%BJGQlDrhwb(q-!i3afgHqLE;Z~FoGaG0n2_@YI8bNa7aW)Nbcsw6fa&DwEdPD2` zxRwtAEvn*a}H(Eu6C^E#Mj_$F>5ykn(lDCa$1iY2O&XP zE`}V|mXBuFDPnN7tqFmsLWZS(vzdGhDutk+feMfT7}_#c0F&YY6 zb$7I=hY(mMTqs6isOk65N`>2$=+ckYBOJ{J3(>O&3N^%0rkXGxYi*`+A*6^>BjcMY zQQq2dN{q>N{k*e+M%xefhqOVOA`%$sBr5e`S5HyYsNn_cu=p7bW`kp^hwMVd zN-aUX|$kXPB*iwb834knA;1mF~>cSC?8KW?JHTBXI{$h1X z`17gM$p{9S82L+x!#@BT{;`xo4DH06GL4Z>jE$u{gNVYdJs^y<#QcIuPAFHgs!b?w zJVi)=P7g9O3`TR0QWb`oj7MJ;hTB%2jBOFh$G7Mkxf^4}K}B#bRgty}wIothsG2T= z4y`Q;*SeLcaX8et8W^PJ23k!rRW6*$L_vzliV~(!wi5^-NVY`cA{P*>SX85qWLBLq zB((rUK>@Ko8-p1nYjBzeRU=+t3o$PFS=^LFuecm(o?zE}Q%rbCy7Q3kI$&XN-5TT} zCQaf43ryMN)8rUtw&Jx689cWOwzI5o#*3Ill^_!pgM96`al^vqTmf1AzDZkC6A|A7 z5fuWVnl;ACn#~oc#)3?fonk8oeNGom(`j89kC6x2X8=|psMA2qAu?oO7!abf7zzad z#nK=xiMK>LYjNWaP!yy!b}r;LyQrrra3G@|qyUYK7zL*USXtAf7@09ZN{M0uGV(EL zK>M8%$xRDocBXhbtEoN*XcXZerjvVC90MSia&&?d&O*3nV-xunX%J|8i zlScYjqJ0#TD#>1pW785sG%`w7TEsx^5UP<@GSZ^DGKOey5mw6?qM_Gjr3(475@zHL z(I(u=%sLl}U}ThHVufLZK=!mKMt&(iZWK2S476oN*KNmUS$dU~KvT9dM03hkhESWf z^cpH;O-fj{GDHs2IY4x$Y-Nbg z2o0zf=f&$gIKMzU<-oY0Oei{@xU<8LK3RK_%M?X|g3?L?lr5lo0{|$-;Q)PymWQTO z0%Rt{C}`W=D3rzzLm^KMvjnM0M+7E-)F|l<*vM}**7h*;GkzokeWCG~bc6~3vib2@ zCt}~Lcr6ZyAaAcEkhj)x@{?7dt_lO1{mCFKbY$e)=%lzIj+%J4)w0FLmnZ( zAS0IVH){aw!v)t?oMH>mQ%p^fotI!~AkxeJ$blH@DNw_MZJ2{=Ir&5}Pt|PvbN~V# zGJ>J(@EQg#$WRxx87xcMN27K$Z9m}zfw&)yenUM3Gvs}J5HF2B%SH-)f?WFar_v`Z zl|EsXzAuOrSIxlBDS?55FPQ)gi!jje@UulQEfXn1qPW(^m`}Y99ws%l{KjXa{Q7GJ zzIk3L!JO1}ARK(!QCf}VUR#Ed&kmHO7|o3wK-Gtk7*5-k!IDTS?-S%5)E#XNSUA&K z)p3L(!|OUV+_FVsvLNzAYJ+u7tYp&zB?60Ib{)!it%^``-B5c601}(%HXc%>`p{6dyrYQGA1j+VmD! z08?8S-%!@j!Nmc6v1_#Nx7WD>h)ry9d_yfvu(gFEjt>KE>=dviF($sDy0tw-08|5z zjUEEp$P)~!0A0&|MjG6bNe{8KC`iM`G(mijr9eS^ppEPc;)kRN;=?T&Ul2dU(t?`B zk|ArirZU!n*!U1vu%v!!G)NYJES`<4n_-PO&LC=-#k3ie!hE99r4P3FHq8OgaGC?S zjS(W{3UsE+BPnL& zl`Uw;!!gfxV@fv^4xbe2Wx`=o-MG5J4WH*#{s(pzmCzR zDvNJrz=-g1;+VYz;^10QdCdZgImcV>V=6VVQpQZ?)eT%6jq2^XGeNT$VIw2vs;LBx zh&1x3xBcdFu6!3o8o3R>sC8|N1{7;R6#xNnv`0!UG}_GpDTlCu%x!GMrjf8B1aW-2^h0g5r-FUWD|~nWyV8dto@}((Y)UCi z#Z}1rf;yA;%s?3~bHTm{K_erPWeu$un=ha(UHEEo{y&1DbmfR;Q2-R>tlgBc$PL35^jeW~4CH*AQ6tS9Dh`b_ zW73-!X_l&(A2Ej_hY6Sn!{tjt3v4LiBdte*Kn?d4iHzoEZM40itvL9sXm0`!MjDq2 zl*ki8P+hNyJuw5^ido&-)YKLMTb}2F*mIk1?TW?WM?&e~Zg@1Tuoo5}(W6s!5*tco zKxa9}5srNhldshcoomhUOd1&X5SWkIn{0~@gUtfHPs}gfaE5u!TIiM$#L{Jilx8Am zSR{tgSL?d2b@`DPhQD16Z2;BAVG6ggB}@sn%}Hxio$Md7jQTlAGwt07EQrOM{lYs6 z9;arE^t}{zi{a@R_*s348kQpedg!h&EJG}d%vsuC;RNBMMhD?bWJVa9 z3JK+pl_Li@{L3E76ySB5!$aocsb~O(`O<43U&G2C;iMv4Jy2AtI0erhuE7*hBjbT! zAUmnbRy%)Rs%U8~yFi0=>NJtbgy-8-W>bFrd62DmT#Yu#Z%TkR@)FBmB#DG+SA2{b zXZGyEIFwBOxIV$|gn!w221zrPl;-dzjrL0-hw?BT5|Rxi4x4zqQRS9lUpyXBifo4n zt$>VZ!H|&eAUgOx$`~Vb9!MJ~)qM&=9zjTc%ruxq_~_!+n;jw)T7X%@8DaexWQ?-k zdQjw}nl+%&u}1rHgr*srk_NFo%)EwJWb%NaXd;S(KyLJFyH>*iG-mI|1fN#)+!#90 zR!ZsKxC9g@5Szxr!Kz4St78LVoDXxw0%wD1p%4e`*lAb@#Yb{o5Rit&8hMZxhei=* zzN~N%oe-Y;%!@TjlAu3v9TeHqLT3QU%pR)C5t5}q!4!nK-bBL4RlPP7uC8kLVw8p9 zx-3+psgj~lhobN}0*YhJI=5KutSDn$peYH3E{tPW*RM-ND4Ho1<6_OGQ6vL9iwC9y z)v>1#rk~devRvCd1G}jMhwA8O3&Pq+@H&^Jro+z}Fx;-DK^2JNQUKoxX+TZxj5N3< zGaezd1B7zm>XeFbz(%X1!%#4!6b_!|mJ-?MmYhs|;KHEIJt8|`hm!*}oE(VeltR-c z3N%@(Qqlxj9jIYc6as<|GTXuO6Xd`w7nCVhRwhQQi6Sx>hd{oRA*fLZSX;2ha|l`< zNT3|MOHYam*3l=$nsouhks5pC$!ZyN(!wAw%0(Jj6FW0QNRW|i9hmNvBIAOCjC7$o zBVCBEA{Xc+p9c<8S)m-dU`^D8_(WZxC5jxDD(ealiayNOrWlBFDLC3p3)9+60}o8h zii13G4vM)#O-u_hN<~Rdi30AZ`zb)pJDrNjM?|Q#V?eUC%^eDH2(HOMOnrRb*OS!- z>BuvOeu$0%IF7QCvR)>&_$cNfIAjOQ5Xfg~pjI?X0t!e8(F#ZbYBI=g3L)paECI@3 zVMeK=91y6XMEDq*4(bqlv^r8Wx51x%!?uLp6wdruv$=lhohEovbQWGJJ~qh`48|m&H9>U9^husj5rd zbJTalJy(sqneg+}jpCN5FNk}-`m?wfsB3Rw_=T!h+-d43;$EcA`6t3JR_nx_uAUTk zh8nt>@JrNEaW7S$756fAOx(-WyBZjNg}O`JE7gCBdzCt44dF9YMBI0&Z-{%f`iHph zRtp*#K1+RE-1n$oi+hc_q>1p^YLmEg)HC8_3)L_x# z+d6|qiw}$p7A-!dF<7+tIKW`hqM7-@qD2$NgGGy`=?9AzO&<>yEt+Z_ELt>CI#{%5 zaBr|^(ZtnY(V`Wo!J+m4b;@!@{l8cup2a6VO&J7kV-hvw}TD%H2ShRR|X|QPVZ2e%-;t}n^ zqQ%3qgGGzSUk8g850(x@>pGcRjan3KXsYatuH&|7!@41A<7TcqU@j$pW@43psIQEK zG!kc1wGw+o4Q>RRvj^Oi!-iVwoGibke+i=InLCOkj7`RDHL{`;dp(Pml|?(Tdqk3R zgk(W&!}>^F)QY2NW{2l_>Eg`LakcqM*HtSwOr zqmoAvcgWdwWnD4c{0>c4FEtxM_6PtYJpyd3omb(aM_Z&C<_mG}Hs%n{R;PiHJM}H- z#j;2U5@f0F*6_N~9mubzZ(Xi_i68$;+OYKm_XO6${&(GIaMr?3ymEuTu6Fu&H>fK3 zAB4LT?&HUA5WUL={q7}YvS>G|a!I?C!m^fz_O)0<#5$gO4{~M^{{0i&dFo{H!^Zu& zanD<);rAN1uwDB8uu0BM%HWiAB}tQ2JLS(?iY=F&PEOYrg2|0?sc1%tLs&@x&!V)HDCQu+{J3d z`!xPF#$5}y40`#zasLAMD{7pYjuQSAHC`=I#hI$f1(FqJM?a|aY{zgKLN5$utIO3P zWLS}y&~UU3B=CpJ4u4&A+5CA!)KRosY~DhY<#--I~<7c9L^cNr3jr`-J*rI$U2;P(~2lox$jgFMWP6!`TR22 zGu2v`<0U9lI04B-cd9BRdc@#y6y+AEfjLHloTiPA|6u~zP@|W$ zw{2R0jVb?dX0{Uj=c`;P`4Ertd<+U7OnJtz+P+PBOwkX?vvH8+c_;cwm*)d&m#u}| zh|_sAS>?Zj@}JH9vxzFJ7EKY`m~fk5&ZwC-Ay>Rt`US_ z0{xR(1)UTHieDC4i|ypMt9fbrvXzlx>QS_!XbCk9wuHB-dj}@}-zrZ7vf8Y6gVm@Y zB|K&N-^yAZ@-0#$OxEhds+S-A4Tm>5+LzM7_NwCpvHl-r={pu=j=CIkhmp{MOd+M@ z50SAX=HB5-BisT|@lr>|9|h)(tEXWia3E^DUC zd$Uvv%FTwjVk2#R{dclXdW9mDLn^3yY=(pJm}z>3Yx26*WGh`GGqHYWStqQEL#%@y zAoJijvp`iU4nzlmvSWeSt_3>`hW`;b2!7zyqFW3?Yxu498Qdf>%|-`4yVa!X~`tc zWm3or^)k-f^*CJ9u!iYjbsDMHB{j#exDH34h69ZWlT zQrMZ5@)Toue1zB!0-SYDki%jk-dfqrZY2e-;$G9AH~v$z#F~%K5|i#z@xN=GCAOie zVcRbud8s>Eyr@z*NRijdm@zZF8`->XCiLjCJi;v~Y7;}}`LQ6*NLs1nuq zn~M<7sTTDJ4G7yxs>Ke3_<(A$@!i$p5W0QZLxHsJT3 ze8eLp5gH1Rwin9mT`f*BUAJnn3h+>@TI@z0`*DA$d$lO(Q7uaIs>RlLwP?fhb@*M{ z2W6atXNA>b1>lCWtHm_nF#J5ELzrgMzL3XVoOtlmHGoRHQtdf#hrowC4yK?3d z$zQ2pE)gNRsG_2m6}~@?sxEq2;rrvLGNYFjzCVttH+os&`{Ss> zqn8!FKaQp#dRgK7<7iT%mleK0j%F!(S>b!yXy&4q6~5PwW-@wN;d|O>ZljkKzNd{Q zJbGE-`}Q~lh+bCs{x}XLqL&rEKaN9^=w*fPkK@oLdRgK7<2Xc$URL=2I1aUgNRIE0K|R`~un4n?Dv6}~@?L)z$Nh3}8!&^UTo;rrt_#ExE8 z`2IK!)uWdczCVsb{^(_e?~miuA$nQi`{OtTiC$Lt{y0u$qL&rEKaNwP=w*fPkK@!T zdRgK7<2Xf&URL=2I8ODVmleK0j#I|yWrgpLHS>gNRIE9T~R`~unPKBeF6}~@? zQ|jnth3}8!)I54w;rrt_#gATA`2IM$3c9*KZoG6bq}j}7Ov-{bv(Yh85WJbq$crgI z{Y81^6q7C`<+e2(cWMh;1Ja+R-!U>}(W1MgH>zc?^hT}1vu^z>`69mgwe!mt)z(d# z-=q#`OJ^7Vtw5SS%^zOCH^*RO3}33mTlu4M5o2AI0>6>#O#jxThh~W}h-*gp7Q&AZ zxL+-sbB^uv;2T0v7iP!DZ!DK*G`mk$s~n;fwea+0-ek4U0EqABTP^TLkfqjl z7_N}Wd8YWWIB~M4_!1p#6q;Zm6NB%B?efq0jeJ}#pu4+2_fT;46@pI6{k%`xyo~Mfr1yXk#nGk4fu@oTVJlgECudW;0p@;K!N{OAf>>& z*UNmR3f!i^y$WnrV3z`aR3QIhnc*@8sucLR0$UV#NrAs9Q2dC@FjfIb3kAF4b|vYu z3&Jsu=vKI+De2_T!MdQwJ(S#2=!Xa9FHi}T`02ipLjUl%wR@-pbc?s$;j0K;KrZ1` zqMEU&rV>(RqKS9sEN!Uc^GSF<$@e_3Y>7-k2|ekr01fS4HNUo{p3k=iw{%J!l@Jr` z4or|srNqa31IzD*mu6AkThWVonfUj_vYdJs64R^+tiW1>V2Zz*O-#j1dQruH9 zC9sTqYzo6#Di=HD_7o7xk4(PW1;s?Ht1}Se-oRKD#tnCD_rSm!3sfS9dLsjq^-{?s z6DS1@{#1~HBJM%KhiRlW;j*HX2sEPB&+)2-{2b9d8j|6GEr?szhsd}3>w{4A)b&lB zL~88_EC%ne!Vbq~Ni8AT(5I~HIDXfD4*RYjoh9~ez}u!Dn=}XVjTqWJ6m9<0GBMN7TfWA4~TIbzl)Kk6lwVJA|-I7>_~aX ze`hIc#kUT)tra>EZCdZiBp<(7gqKirZ!xt1EnBb2jtxRISx_XSZ)mzh6P-p6M5Vz! zYw`_u*EQj#Fm-Y#{xx^qv9MM@?vKaV!mJ<5J^uR10OmKJT#rqrvQU2fptt=@8)DWr z5Dy9`Pg=AXJ>Z(fx8GjZRA<$64j%P`jm9a08x|UPaOVNvB9<>ry4<9IdPY{2ixR~4 zn0!ss;^ul?^I*ipwOO}H9)h?6ZT+p>lODf8@VmBsCQF^;TFqf?!(_QuT58_mh_Qzh z7K^%1)|(dCv@u9gIGN+9x>_|>YFJ97;$jTsX`1LI?gHdG%tL_Jf=xRm>@AWK_7R1j zRRfL{8lKRw5bq@`6f421eN9;9f`nB-VtR9!A(3aDS4fHS4K(ILw8IA)6kPv6!^juI zS`B42f9k@!xm1o9NWu2Xrsc#vDThmmE^ty_KQV3Ep;Gemrq|uxRM%XeHrZl1R4C+7 zLw9vjT_xuRFav}5_QmDU+dU@M09P!m`Sv{P?IzrWD0lxAI2)6(@5~++sU5#X*7(Ny zeuNs&_-d6~QA2lGt8KsfH6yG%ruL9KHQp(kDYrSR_EF_!SCf8wiKlHbZmDs%jrtDr z1h@E5B?gR;`iWSdjcVYcz$^upDDZg&exkr%6d3S%H8fJ-4h7aL(5Aov1x_k(_7`M^ zD;2n1fi((1$T%Q@`b$yZOoOZkB-|EU3f1aQU<_>7N1+{33}!ibYG6~Vm1n@vC*RSg zT5+U~qeios-i9X8+Aal+=c%rbss5M{S~hjSU!b9OnGQNP#b8!kXr#M-B2z*Grx?tX z1y)L`WF~|5PcfLu`dP^wHB7BI9vDIodlL6#SyELp%~+&a?`w(L(b04KXk6|VJc zYL73!9N|iQ@yO3-Yev8n5S4)aBmLA6_nNytgb@c#OkFJ#!!o28%;sbF6E~#GHc{12 z^H&NjE=Rj!JLL4NenwxC10O0SYX5zBj`wsoNMcs)crsm ze?1`4&SpH-IJeY3XSHK4_J!1t@jXNh%6b96c1xHe`flc0drUM+*iHNbkSB+(1P+9` zqMx52^Q5Z9=-71>4~&37U>~Axhqd*~duFk(D4mG<^7IKl5We{NNoZdFq`KSfkyQ+K zp7Y}ZH?=JTr=Vk%NqoGF6rTxclHdF!FQN0WLrU!eiHX|;Nzj*DbjbjlC+SKYeirBW z1@3`33a!T|J0=1|r$$outeFeXR0Z{t7{zw+u5nAcCa~Y-h(bs2YC)HM0H)h*yzD-p zOy~etL(Wu9f-;^LAX@AT0frQPR>{>No4ki_CeNckmXhqG_^JJwTfqE#;*vr@)-S*6MMky#mI1^A%B zqQy<~>MHSB)0o(|Oie3d`BL_5g5&E}z_s|jLAY8kZc@EWVt{{#*tT=L>19>aixO)q z?KY&9zja3-7E&wvut>D0y0^YoD|U(XTTERvew(_t;-^2+HC-fNQRgBrb;=VO#=orY zM`_ro;g2;;X;}3YmF_1R_S>rNuh;OG8qRuB#jnwDmxlk;aM@SY^A|OIOT)2WQ}L}D zz5tj5JYUxkb1{S9WwJZ=m2W_02jeD0PShbvP=VNXDyeQOD7`N?e}bKm_GX?)UV%x- znbe^di)mx!tOL8KG4GHk1ArR5ksbuGS4(Q!pzXN9jWPB--=hf(# zTd*n3>1gXJFea)cIz?7)t0#67sss;8a(s+U+>R4K@(U+xtc=#y?uB9H;+TX<)Trf( zBB^Zb+LXee0XzWaohAb~UK~SVy=?=+L5ZS+kD_u*I2_zkQBhD=$Y%F5@=8fo1z^L! z*>ElHZf&V4YU)ZEiNmuNnN`E3s>Eo()RSil2TA;hIJ{rdAwEN&6-h%r-Fp4POD+$v zXJyLeBl_4#*`!2cvaXZ}nRUO+cUEQ7Qos1C@tKEPS&6BSQaP$ixe%Bx5$FX3T!fNZ z(bMltLP^aT!+52B(LH5NP56)rG}a>Q!;QgnwCPGN;o*Z65%pN1YXFmtq)(B!7khHM zWZllPJd51YWk2yY{WqfT`=UFpi;>?+4y0yC?0H(9l|<@k|U`HwQA|6 zYh=29Ryu1?B99X)y{U7ka}z3Cg}|d6eNY{pn6{pN4bn`Hn`rja4976O?vH7lr6_U- z;HFUKb!v}^!+QLnb%YqSLyaejzOLXxz^=?~CXIs^4`DV!J;DZrH3(Y}wj=CB*n_Yi z0q$I}67^h%Un9Rkm?M55VN6W_Chxn6HzdpzpWV*;?&4kFBJ3falQ2)*^c3&o;$IT> z6iTXOkwab9^oWbgj<(fDvgO+bcHCjbxR7n=w2ol{u)*t&W4)N_O|G zWAqP8mP)!g$LLE$&+IT6WMDGT#6+K)reh5A)%y7h@Wx?D5}cdU=j%NbdR)qw%eP2f zdfJ#NyQVtVo&G>|rlZVH$Sx3el=*bC)AQO3d9trSgi2bg?4!X)p5dwu=P~`cP&)AF z@SG#>Am&=a;0abNMxrgVxbRUgt zx?|Iyk$No6Ddp46OZN$5ymf+uJ-%Pt(+##rwHkQb_)O6*>*K)RlWITcZ0qUnukI7K z&~?^}52)aZyzhk3ox?bX>mlxK(pIHEyXu=^cm|EKIJ19X(L(nRuu@3di+6Py=dUbt zMLV6w(92%AP1|kVMaABymHj@T<4*w6TC71iQP0-VM@b_&@I$5@Ux$0kj_;rBw&UGI z|L-U}{5=VC#AM(>d)-T%Z=DUYKB=NU8Dp(XZV@c%KU;R!N>coX7~)nzKD=|fT1Xko z%fMF3V9se|7SLJr(R3*3V*Q)Vn?RAdaR!not zZLWA3^>EyIdmCXl(erysZdXefhy1?RE5Gu)8uEKDmJF{ItnxoZJ{}ir_kZ-5s8x_9 z@0}Y@LIS!9I}}LHyX4!ur}i-M6g1w(a|>uXXD9eB5`nuX*Qs#Wy&8&7AAy zh@q%Q*@QuMpW@f5_^Ba$QOW8}t%8NTbJ8J5pS(~MNzwEaFWqVCwn0Bo_YFG!qkyN2 z-fL#0r}x9CN74ITHoeszwrgsd9BQ+OcaYi`|A0PuAt;ib>8Xvi8cknIzIXpf(fgo| ze+#gy^ge`f5PcaXS1Vhx9?%*CnevUjOg|*ObHqjoyNM414@LiCi~jt=m__-uf`$Lb zSPi!DKiq1N`0t>kQm*hG5q;F%%}PpOdI_y)H}%2bAFKOiI{uS@U6oI-{@oj*559(ASL`DVUzV+ciC{{y%b;?*BUe?|`RD-!hcNMEQGy67m0`YZ3j3{U$HHh_aid z?S@-*pQEljh~G>0&JuqCyzXbS#A<{eBMio~Z1yLLnK-vGt6D6p##$Z1$eVE117R#e z1H#}rfR*qK0&YP%fw%)mXP)!msRnf+p4(%&3Bx(iz`ci54)Mu)D<7RgSU2GA(W1!{ zYZ@A`>9A&TLkmxV=x4q0EX@fE)*Z(gig}Bh?Nb+ph$~QW>X0dl;Xd9X#GB_WSTPTa zcu!aGgh)TU;-$L zfsVN^sN)xx0A34~cNZYfcOH`W`Qm(t#kEj`QzR@9y^*G1d%x}K=tUk%D8TY(+w5ww z51|nZ9d-*|B8IR5_gfK?2m%b(-yP+)*|AazeCINF%GTahBwwY7VbKJe%RB)}3G*M! zFfzsjPSl`2F0x=F7rtyvu zN2KxUo_)7sj{%;oLl}(T>cWnWC8H*Z65RE{@ue2Ld34@gmGc+yODs~`P&F1#E?)$# zG1b~4t{-9VJTmD=*H4^)@5b;6&O@VF>~mM3xjgdJmjsJ#5`$26J;J$_GLx)JOTFBB zp}Gu__jQ@MC@MpjAz}Po#>hzY$b-Bv49GMkAAwH!Ey&+zXeG`OG>uo??$W6^QGji_ z!}aa;*vNf%-Au$@P5mt|*34eXE7tA0IvftuM_WX!J6=J$82fDdiLn=yiuE^@Vl7!^ z#O54sz|z!^nMEwda&jlgGGZ*G#{^vfD-n}knkD*PS2>}nsb;B#;D_q)X&q>WOR==v zfMs1Qk2YT#u?cn?Qn)su@jX;C`I8mK_n9)jQQsG7cs?LolZ}0K@BinypG7Ex${L@i zs4QE!7}8mT4bZ2hb$X9d`J%h)u2%=kr0&d#Z;<%g-whFLSKeu_p43yC=3Fg3LA)Xj zVi)|vQavO+u7Hy+c@q~m@$?FwZbEYRwdI%Ueyfgu4lrB36fJ@2yBVh)0lE47Y?R3k zel;NZa~I&X#MR*wj(VOJKH>B~D)sm8O|n@woAf(?VGC3Szjd8>4ArZKL+ImVlP(@30jb4VI1(_eE;lAher@)@;@`{?!w`3qj$Z`G zady$?CPlXW)Qb)G;e0uQx=`!*q&gVUYqBE`h*o1jPAIHhVQ9lPR5}SA^)*1HP{(J< zMVK9@XN?offQipo7on-PvCb!cG0|&+8$0d|ZAaF)U+)^3(GJor@0744Cw7C%B2?0( zj(rf7so@>TSFOpcPaL$Ogw|BbrDJn{Yw~#H0i2XU+~qoc1|X$<{Kn`!F24Lj;@HPA z#VtS(AoiEw9o2J7Q2~|lI#fp9z5qzxo&;3NxC_*Cdc`<#9N74HJRD8U#N!cS zBpN}NgCjEC1Q|?aY?Khg8;jXuM;UJ!rRFe3vQox6%9!--iflNoBL`=HK9X!aj>;U_ zI1HljGGzltT^Gap?m^PN=a^$KNB3M!11SXcB5T`0T&I23Kr!bxl~Ie zZ4w5LTu5hj7Ej_)f@nFNWj6$lBNroCDP!%G&Cdv~dEhh&sQetRA}`??*vU7mPrV{zG)2je7`{QV;$dPzm|fS=u-`opJM&~8HyCxe>>&Qc;9x}?tT2$v)x?0U3 zQ#Q1v!~Fk{DI1-*FG8501?vt?*8H! zQd?IiewSiMKj437iI)+0-S-E)%Mox2X0cIuyn7kWI(-alC>Th!0q#Rk>myW$TQKkA zFsbyhNm8@mCob4<>j9=Um~=HGtz!h_;0rNUrk+?iSP)5Q=j7Ck@|ySl!>H;S(DE9gu^KbN;C0w_3x5hA(RPx`yZdNu{gR@ar1> zRl}jLsps8f9@1Wt5dqXHZOj09boY ziN1Qlhmq5VMNrN_G=>WH1^)oiz=NNA6Zck&ZWC(xQ7c^h1hJWfBAz3ZcUd|nV#4w* z$HZvW*y(i(;OewW@HAGsmhBSox&wNjd*RIySQTiYM(if2F2ZZBP@ZA+1>qs07D#uW z*icu)k3&kdb3ABcQHS;9S2oRGP_|?qK8~I?<%h(0s0$v@#iUa9FeH@@+b~*`sF{^R zi>b20-XP@-P1tQ>6_hf|gB_KQVz#*z=>w9Eg zqzqxzqLv3{s60wi!kFr#B(l&hNgzHcFeQfVDz3jG{eRALvlXg+MgZsO$)Q+(YuTSsT27^5d4i(?%`@L+5?r zmCNdBlb0&-rRr_iK+t*{n%eih+Q%_#Uc;s8YgYPQHC)P*=fG`e2fe*~36WktQtu-| zIaB`68C&31?9lfW8r}++?cCk#?|1-ZsA1qxw}T=C>$HxK?Gi0})~@i)i&`uj8)bqG z2S~r8)(DNH!WH5GS%b9htM-_>U$5hz0?d|%V=-#wTF-1i`agb$GB_m3LD{6`rGRSM zWjCHqg%Fw!6)e+l=jKXmZIT8%LP&S&8c*U;5M(=bXX1-RIEof|6I(hNJc_e+QM%)Hr z?A0})DStKl^2m-^VlLtmI({=C=P@0S>8Nup>cgE6V7n56x=7T2sVP?X$&^<_Xe(u| z7`=q*IAh5L8cKzZh(ZT@k^XXV5S1DuW%keED9)ZC=%dl7l({&_;@!; zfxXYz-&{(c*x#B$;<@G|b0?hXG znH;>n#nopxh2H5;6*E)illR^!bed_eQG7lPsBhYfO&E|szXdn4#spR+k*vFX6_PcT zQ_$}6@~t75X_hzhbESMkx$BLW*w=^&L}-4oZM2;02Ie0nR-t}1Q&vjNLzWmjw0lOR zW?^wm;-=Ruyu&p;Hd;17Wz&Ohss=R+%akvF66^=|a=E=hk@J!fzPxZ)fkHj-+PAtU z{W5G$$_;F#K1U<1?Vd#5?GU(0%{)~Zu4dz{GNA=YD#V0M1#LIfOol|cHci3wkc46A zEjT8wPg7E+wn~Egq_@kZ1@?u(5;wfiW!_zujQhH%`p$`w#v_UO>!oPqO>LG#2a-=^ z!dpy~qb8aFt51RFt4wMNtulH@?vZ@4=@0CAJ?+o?xDc3B*HGuY>)nI5SGZskD3N$c z9y7V5DIKeNOt7}45Ko8`Jabq&XZ^~#X1SeIV<^17qM|hB{Sq&HT}1k@y2v9TUSVuB9fOJin>gZi7!swIolg2}z?1^7!#gBI1(@QY)1$ zX~esgfj8_Gr)-{7`lW)F=L+ex)5=VzbgkpG(u|Z%yWw_ck9&A7ElWDrDRvBHID3&$ zS)wsiN#XQ5d`hF)W~q-fniDe?IZEA^Mv*VqoyH@T(&*!z(uS{FxjYN5nZFR*1!uq; zJCEO7(EiFG8tSQ4iRzxV6%n&#QvwRYD}FqzRxAuINFwQV$cH$BO%&n6CYe z^ZsDB0w)v!GOC5mc40>A{p!`aekZ1Zlvii4Xt!DBydf|17?INr+NDgJ+PaI_N|Xqt zvQ>dS3cRL3{@d~y2SXQ&OALqQ$Q9UxhG0&3;?^pTGA|-k>r0|W0y}|RF`n@T$Xx@lG^F3A#TlCQ1%$KpwDz;Gkc}&3B@ zGq>gaHc>@@X^o1H;XYgcqSr_Jag?DP3?Fk%*R4%t|1L_T_4{s>O}3qsiUxV_B&uXe z6yGF#$+zZIQ3HTuWGSu?U-AK@bziyN)cqkH|2AN@yx#es@+BVvq%ZlSu`20I*bGRX zzX+&&$u>b<8`S>4;Nswv{J1*unoj7vXLMs58pF%@ENZk6ae@66)r zJL0e%1Pb#t@;UJ91a9$}Y5eY9pS|Nrz&Wl=?$4|JuK9d9D9b#`h~%uNHKYT+y}N7( zg#tBFgzRGy(%1z%>!E9vTsbYW-;}nc^i$Ph3WB;o;Nn45 z+B@n#i6Dn#lojKgL<*JC>-_;Bqm0rWXHIIc#OvN^Rsp0vzQ0(!7Kqr+vwSus=t#wS zWDPh{VPs}kAdU0>T`eMmhksxistxM}(&OGEZrLez=;4}s=IGJdmDY;1?(0C$DzOT2 z-_r3f0di037E~S8-@f7YX`=nV@Lfh*ZculS+<4DkT!G3dFHL}ymoEY;gLY=A?C61U zVh<4a$<2Nsl1Xkxh+MSSySalxLxX0Nz`Y|Rj$_bD(Jg7vjAW&ZwG0~SCvVQMyg8Jh zVC)_jN#5?xx8#kpnKlrRmnm=7M%Rr<+qW#r_&lRN=G1`~huc)1dbrJ>IRDYhlriiXe@w^c<33wn%wDI)!mWTD3(rIuTv%HMNS-_is8okdpp1jSjJz2t zSgn6(dk~e`>LDhAhqmM&HGwDbD8VK;nqUJu2sMF`tdy~~CLsMBzMp!t$e$>D*F#mY zqmRkY{e@LxC*uCB| z1yZ}ODgCO%NyNRUuZsU5AjcbPAd^vLhn=z%^{%&NC*pYHNmNFeISfdd=?i3((m2zU zcI@mpF(pWLCIOX9vNJ-gKvwUFgMvdnW0b%V2g&2tGe$B~&yY7~T;?1~P&Aa*%P4b6 zV5U4V9C3`*d^crIjX36>Wy)J?ah0e?++#Zan}F0+>){59DsR+PE$FqY5Y&~Wt{Mm= zD0edem9DBkbGpiV1ZN$v_Nl66z$BBn4HNr=Rh5w$q#%v+{%tKHhm~6~qu9K=za=(w z;7!x)GsVVHRbnHOTh})H9*FtkbAYP{sQAmyu9D|_NgPc`IN$ zf^%MHHoO-c)>d9I%{;GTJqSCmV`VZnD6xM~zWwKQwxf=Lc;Y>;a}qd|$Q;IAW$=}g zlVS3S_q-03$Z$MKb6#f^2t|4Sv4(GHIC-FoZ`JVUfE>gf2V}n(eU5^+YxtywFKT#F z!wyT3{2iQHb4 z`Dl-kF`q2=?n{~`iXdY~jodO~WDKZLmgQYZOtZ(yC@Wt`&zomkj#@De$08_aAR0pj zyN!R`ZQ#M9Jsn799_=xc&>vzQ%(1ph+UGga9PLS8LM-rT4^<2ISftNap;m;fqdk>t z{YQJu%wb1+JSjgU*Oh~h_LxcCv>2u)&CWJ~@S{EH3-S>)&C#ALN*F7KLs!aAO8JiV zq%X$W7?$p6kC9Vi?cT^XO6q54uw<<_7`cq~h7n>bu=I|_Xc;*cGxG6h&+bn`p6zvU zQlE1^Gntz~mZUzU zJBAyF1YF-*@!{6nR~$fmfQd~9RhnM8#|jpF>_`ge&nd?rh9g*g2VAg%j4{2q0GNXNeom@Th5KBIho z#p{*N?`D+A;o|*(d>)%4vaSVreq_PiS^h=+eLa!9t&W?iroko=`V@Fd1^0NUi( zn`-nj8U!O*DPt`sAnEDI#TjTO85l(_v{SIdlj zdcB_1PmEKCw5g&fOWt+r_yy2a69Y!}S>R-^LpP#))fV4^rpWe-`hoKu{NIZ5SJUUHJ+i!TDqppiN=M_>&7d9lre2# zKir6l?CDxQQ4Lx*-h-?G>&8ZAv{aCWd8Z3wGHA@}k=E(XztGa(>_AgA^@H6X8<5t0 z?HdYpfVk&%{A+;p({4l6QGFjL-$?r!2m7+g2 zDmuQ$apEu#^YMKzD$2z75uya`@s9k-bSiqI1bSiFct_C>`h$8spOLJTv6iAo{iOXV z%8S!dGIsCrNb++1FiT#jg!h9$yi9qq)^%Huw(pRjbL?K7WgKOfqf50dhd*PY=ps|@ zTJ`-v4KD=DcHH0{y%4vse%OqyD-3Nt;b6yrjv&~UVErwvPBB^qG zlU%q$hG1g$#U?GsUZU=6b^Ip)v!&&_uk4nVHEejR0S47MMTG|fRBs%zQ=;NT>j zwnGJr^$)4WNQ7o&3n`y#h%`e%EK)-UIxAot1^hYQODl_NL{fXbHb?dLS3-~y=o7Fy0X+2+fgay<|v@j z6;rydE4=HH`+`(OF`Au8T!x9_F9fOzBQu&x8s?ogjL9M8uF1&uM^Uk2I~^yjn*1i) z9aSNUM>)J7jNgrzZ(I$SGg`%e1(5FvY{e8kD(|Z|REtTE;{690#Z3dOMR3*~b1_@x zjJx`OuQS&jtp{Q2j#j3K>yG>X7QF7*3LHv+13qQsc~2k*$HU|k@46#9`EWc*v+j5Z zgrdBjGe*Jb8h$~;0~!vwTn%ET0aC6L8vanjocF8v3pK3J@KFs9Y53kNRJxlrd<2ks zb&tM(12Bg2FOm~TybVMIz5PFucj8L13>@^SS89K|zXYuErF~_fkujev^{*@L@Ycw6 zMI&QCjk2tW>xxEM`E-8yPM@b)SIn3*5RIXN-NHZaG)m*TA`j%6>xyO)&Zn()Md?d% zA4N>Ft{756;JPAJ2iFzT=c`bja%)|2J=*4dWuTckY+cclGHhMZOzNgZiJH_!T~|zB zkdLTo))liTA?%fbA;svIRis;2G;^}~=DH&FuQOP&{Jcgk!_PZH>_!gnScsO5V<97- z|CNEPo^u{EnVUiB*A+wZc$rH3l-FfVMr)`1+~IZ<>xP9_N;`U)0@o|BNP&kGXj6bx z&*s&EG%+gz9UOAqkalKE;A6dRsBK`_x}mmvse!(i0`_vfI#64|jO&JDKj4fJpz*O) zA7cT;-J#=G0**)gPDo0Z#t0ZvEkjTjhAP`SuM;D2*K6_(_uw;F&Gqven=z(>3t%^@ zqLu9n6bNtCFjriyzZ>J66tWJYMzV2fbbUL@cWfN-f|W3kqPt3(9M^uDt`-((IOa-= zU|3(i?$1;$H!1It(Z#JkGXcXzr-Kgx*e#^F87@=uH%k2RdpYAw17Z#Zb2e*69b%drl5vk=!JG)g}VT>Dl4j+iWD9p(W%j+wtJVEZ9se`HZ2f9pALUS#sis)7-9F0D6O3{#GrN|i z^=ekgHrcK>!8YcdQB+9PDBd)2D1&~`J}8@f8waRdDmKxgEz)MkzoCMN_(!mfvWYhPf<33?F-H-e#G?co zD9WtI&gX>f(xkgZ7WzLrf^_uF;+i-6hEZQYmkS{fi3_E(fa8!`Z8lYSEcl?|~Q zPQ@BTN35Ws5WgK*BGI(T7;FbfYuW)%VyvLCO#8c@2iSFB2aIH;jCI)o(#7FZI&?|R zD11`7eDH@%+7F%ruOH&(==f$p`oagI9Z}~xl-c87!MYiOy3jk4H%H%tvJ;hZw(}1_ zCG3W0IeDf8P!2gk@wDG5|+(?l`*qqpQ}VonRq`!v>~h4RYbTHaUY0CTY$-iDk#(snL`U`6FWd(Cl#DK`Hj+RhWGO!6H_ zkngtwst$X~lyqzgzA*~ieEeUBx-#*9gcyq^d95dz&z^3SK6MH!(|@`pGxUoe1A%y%GGlGy6zh=KeXW~OB{m@Lc^&_IKusItHQw?|Juu zn821G-oNjJW8q=)iFe-#`+3N|lbb*&%IiZKzM$dRRVw~Q4Zi@$LCjA8Dc8Bx3SOb% zM>YJShRXe+Pm?@ z%pA7y#FH{?aY2G$3gx4E*JHXDOotK>BY7@owV1RLgzUux0yIKVKHl`__GtqRiB zk&`pkTyistoG2x?5jgcR-bs+jBKbOs+M7~rNl?VSDO6un2DT2Q2EjX3YN5ljTD?kx zkKCVPAw`9?0WA$v`+Ob~+faXeOoaUyzG`8JJsU~-$yej|t@I|1v@e77;l7Ly)+yym z;&^tIND~nJ`hBqn+1%IO+c9WETn-?wvjMxZM!n?Q)uI(aUEtaNrP0@@Yf&jjMqdO} zN_<*2^L{TKulB7|uL5R1SHK?GWZlp6iEq$4^M(Ol9o@3dk~PDNa+zTpl_ zuX7+!tJz=<(H-}h*o)-uD?YzU>_^U~NV*D)OvX zlm4eecAVCd!x7i46R6x-v+e*3d6}}Kz3$tQwC~`6HEUl7w0xx0uW~%9>^Fa+uS~ns zl)pB8U#a1T0Z(;2xCUh?RdSJgJm_9$^KHCe(@!4UN!SPuylude$1ytSBCV+P_*hkl) zf~EQgS`89ko2K+8y0%2;QhMfx~=%ZN6~8-;IbzyxXy zUSRTL_(FK!5jRK2Hv`hP9tT!YeGQ!FlssK6#vrIGOWXP^Dy4q?7oh48*Po(o^{fhy z6XSuK&$}m>^w|9@vd*P&xfpUU$t1t?cExFQ^S91c=_Gx z`6mE5$axZw@}1Q1+Mlr_WcRyzbT-_ja_+ zyT)y14qM~)qzqf*Hj}z(F;Y$YqONhLFUUvKG;7>hlo0lEh4jT_TH`iyO03N_ZYp7C z0Au-xja-J0c!W5F9NzH}EgZ*3Mm`Ga)^DPC`=#xqeg=_I3{oO9QLZKWm%Ghc-2C;i zh4w1%)HEASpz@iA+g7Z1FZ-ypts52iz5)joNGUMnW6Jzyx!z5?vnkLaBGKVdBHGyCLWeLqRV z>44e#>AdUT-$5B_YW88bzccf?X;)gC)=_#$w#fGAfhcTi-oF7Q(m0B<4!)dwTa`6X z90QJFPS%r1?7j*d48NYcBa2MNTZI z?iS$Hr_4P|>jIJ0N;us%mj2^p8=Mb*TZ7bPc;?cH6ft8(eEf3gG~+}{m+_J-;zVu} z+DZjR7-kUooVvRmIK; zm7+KNw_W1C=QQ^+;O28CbjU_)J&;d~gI3Agku|^xXJn?x2-4E@#hyCmZ$@A&cef=Ikf*|E>39Yo^3!vogJ(t9oj8Eh?rUd4-5=8NZv$pa*Y)jceAfm@D?buta(q_>Ncye- zR9)95gm*DIEuDu7mhK;}wMo2fG6xUW$P4yFPvTL6m^wZ20A!cLH6vLmW9{LZAe|gO zMn<3Hj>1Q4_$8LCG5OQF7B8ko+_!Z6PXTF*hoNav{TY<|+-IxBKm>JVX^RH}3F_h* zfU3h(obm|Kx2j$O?0i~y8EVX=g@=h+WDT&!MrP_16qa||F(=1Vrw$A} zlRpm9sLryOSb^m3Yxk$B#2UnXN5}sPkoLG0LKamP*qfH2;(fYusnDa?eQ!+qK=IkCsqJEpDeVa#!Rv>LbM~R*CJC8s0WM^Xpu<;zaB7>dGr8zaJpp3 zX)!siv`XLb=lydY1K&4A=IAvYpU0v@8QYCv(VJ7hzCdTXvR3%1cs1iY1=ACZL$uQ+o$|wfY zAF{-NKDUjU8>Sdos$?(HTGv7R9*;@Q3Bd1tM#bN@UahZhg0CQ|98CKGz8AI=-z&hd za4cX2g0sFp7Bgi|`K?b7sM;~EupWf1uUnZSo*z&5-GZIKp#&BC*Vj4X9wwi7*Voze zhvP|_?-qO!gre*o(6H~r>VA%fk7{@rP;Mf8M9KKI8s4j6yM}+%u<%ipZkC2$)bLde z&)J}!&jh5-eNf-;0E_{j*EQtHI@jAzx-d7oQZ#~*KGjPDVfUSY_3pGQ>_$fUN@f2k z@~z$)xx#K_45%^i^~`Bj*p0IC>6DAQ!k#f_AR0pjdxw9_YLphd!fqzvoZUJ_E*&@# zR@l>*5WK=p^}*%R^!X~3SKnG;Uxl`LSJ=(WVJqyOlwm9EW>PmTMyg3-)D`yh1^I}Y zW`#YA62ey4(-)Izh26+0u{KxOsfL|_3_Z=9&=|Q4|MLj36FI!&Bw9I+lZ>Y&$!nxN|({40`$|oOgU$M&mztZs+i+xs_+4m@LjRJKF zBosgo&FWM+tQ31LoAkk6Ao7P=&69YP#Mx1r zJ0Rs$nntox##&w=(#zp%7n(r^MB%IU295jz`rXx))8;p~m@iB;m*s;9c4|9KP8~CD`!K>!g#Rm*o7Ex`xKOCUDzIXSfdrqNkQ1TTgt%@cKn9 z^KV~Dxfm{nKB45IT*C)6YzL$ndkye9R5tX>RrvVZ@zfoFl%K~m{Dp?SzoO!&YPeRz zKWR8}t9t%%4fkr;q2Z7x)$^M*{IZ6xXn5gQb$$(hq+#r9D*jpxAJXty4gaR$fNd(> zcnzC1d{V;`8jk$BN>`=f$25FO!+%=OzoF9I4@mR=ZGC?TFb4X(M~Xeaq$g4<%oR8<()-d zGQyX4lB~DK)P>D;O)VC--Avv~TYyGtLm=DicA?xP%jl)ba5bCX3^N$&DM(TwCTuEb zo1+%P*|2NV6im-nHU-DT^=V4t{5z~9NpPR^cDb~`zA#wgmRnIX?=Fi0zAk#XKaEGE z^6RB&REa^sHTdvi)6yB>;=Fo`@*bJhl-IMq5+$*g@rjAq=?Y5MtAh)YNP6E3 zc(E)yzp`om0(7jpW(o(c03jMBhu40$haqc*+auVk!ud)-@mj;!r?IkD4`qtsGwLk0 zC!67+v`4>4!n+N-kn@qeu8lo8wNXx>@Hta=5zE;w=S+hYn54iw1s+g5QJCQozOQJ>?!!e>=g=f6XB62y+)Dxhv*N*DxP--{^ z*=7lGYkq{Xx1$ApFAnhgPF&pFvS@+avsJe6j`<6f*Vp)Z%fT<%zFz)>0>>ElTh5%3 z_j~mHYz^-O%+~+t_4R%hWvB`MW$ApqX|PW_Gv6)Ivz?3z@4Re_XXB&z{xB22^eHJ- zpb$xiE5xU}6=~hqs&A|N?K=KNz-(z+yhr(T#{zPu{Wi+rWOxY5CT*t!Dxa=R1g{e- z4ULBimhGQCt&mvTBo5X%;1p8~#)Yl~+Z9D3Te;|U7Hch3N zvUvQmF=^t?t_;o)$rno^&=-5#ci3sv2s|I zR4O^Z9;c!3r7q=txoAV>J}=KY6l*(E@`+L;KdX@~CP<^8aY6*VNN!~t!a6BUbWF6l zHTo(|OVjdNyS4D$AEdUfHTb;*ZTkY?YdXIC8Rc88fJ%zW$74UkJ}rb6)I9}oIH2QO ztwhg`;#;-S8NSt>$Xo(^y}s3ZfF}9>c@2M~VbS-MIk^ImzSVmG>0A9SAm!wUhVR*_ z?&oOun1=tY;roA}p0{fFqK1QhsNzdCT&&?&H9Vo=B|lQ>?$&UZhHq;4zIOF|sfIt+ z@J|}%|5!c0Ov9NPuF>#W4GW)D>85JfsNs4IU(m34mrB>D;b#G9X7}p*Hvway&t+27 z>07-hvVOTx%mvdueJCf+USszm`cpUfQ^m|w`ILc({#2v*d>Uk*M{1~`Wco)0M%I`p zl>!^;4~?B!e`+l{jOkA`QKXHD4)(?86HV)jHS>}YzPxGt zsYXuEv`gAm_p~d_pBh$>q(WEyso4}9?oSOXNfO*AybR&qa3erh5A#2a)$U*wKL1`r?S0t%^F2Xk#@RqU}LmD z&32-yf;?RC6ciI`eZ}oL4b11GLRxpJfw#_eoe57g8 z1Up6zjAFv$80Aw=0X?bGI!4WOM8x53ddFyZt}w@FSk6FM3UiEx6&4fe9HV9~5$G88 zqzQD48fm1_&&n}sWY?V`*q@ofF&e!fiDVXzQL|*62IAP*??*6_8GeLdHw7mc0Wzw! zhV2-Q*89_WLk*<7I)g>K%`)c?cn`2|A6c3?4yPZ=c@jRaq5tzIa$Yl7fk_I?Q{Vvw z*yFtZ&w}Tx#UKQ80_5|5mim$?|7Q~8mk#Nh3iN;KNlKXiQ%_k^8+Ab$r&!7x7(H`!aJ{wyH zT`adTPwV*HDRHyCauVmftcqriFjX zv!!Pz`ak!wmh4r2&(~1~{hsea*`(`-0F~d6U zCU3AdArGicJc&n1Bwfl1qDrTyk*stZVlAg9h(#JX{ELD*sT_rW%ISFs<@dcTRL|H% z33Pap^I9i@cd>>w8a}GwVGYm! ztx8v>;d~7@X?VnXen6#L1V}TwS>L}17z2HBrKHoZd0{lYa-o<4j(hq~PMkf)?mu*D zw)#`W%vAXlfQU{_qxgIpT%TKLsGwN-ExwU8CQ7BWzLQSPY3MDcQ`10EniThpIqfD< zBijI4q8w*?WQbGK$d}&&qvCT+rgdtXdC3T0-ZV~4Bd2HfB-`wsJ%u?n!wQmA=&DmQ zn}WlgnqehLg8QU5%&8e#T3}xYbZQ#Kq;YB*Y4Z0-(TL#GG_xs5bPfuKI5opE2KsLF zE>-U)QKKA>aD{IY4a%9NQgotj2E4{y^uHN$g-IW@y_2Fg;H zQ!}iv^iEA9S1j16=}8ml)HKpaqo0*i)6A|qLzkSI(F>ADX5rK{O6Dt$I577635;Zh zpCH%`!O2B{jC!4#(RzP6r>23FM`!rVGKP4ZKjcLmD{}6koFn0L8aOq7Bgdh!Hx)SN zcXEa^R)Jd-z(~XA)I1KaQSQ$(IyHy+k|?LlJl>NXP#PkTa&?>n7N9u=t#7MBBVlaRA{U!XbqH2qkk%@vZApF?RMfVmrbz z1YR-Gzs$8clpB&8d9p&FuDsw!M62?V3CNu9oHkZv39&}w$a`Kp^!<7bKMQy|aCSLI zZJb#ba1MGzw9?{SdeEeEgT9}l;q`#Tc?=4%=DOL_d0^S3#f=T~=hd{-DT3RiW_@{S z9}~Ons5y|_;=^LGXu;niv5@2z^F#xF&l7ja-;2Zoq*x@w68XGV)F5@8XcY5hsyfjm zSjHWIEr3=#8*zUJ%H!1t>=xmU*f!&j z7k^uLH={hJXuzKnM+zvK5l^XO9 zh#xP~ehL1w?4^ij3&=fUXW?euv%QV-c@xSYPpuTp!?>lub`gHh7q{a#FUHs5&rSDx zWJ_z2Qt^S*B_4MmrV07nGQ^g8pF30`jmdvv#dMSa;$?B0vZmy09)6LRitY~HO8!V? z^3_|jkX)Rv<)L2Utz^x?fo&uono+lUX3?}k#w+eE0FJz0id2+frj2=U^=`niXyMDmBF zM^cY7=1%cF%66hZZxXwbeK(v;Ek9XXxW4_CU2rr_5;Me1Nhea0eWDg|lwwLeC6Tg} zJQuxxSE7B!EL~{@D_vZW?1uP6QEK|Mqp{KLE81Ifx91!kBM}b)>pq;C2CV6Ygd;few8RYnf&4FZ=@1s@ruN63ZF(M#8k(G7~OvJ zO*hr9E~>u`$w&W^(GSanCzBW5mrU(C^7uo^)X_iPddp2W&3QIgtdsQ~m@%tr^z*CB z53DXfnE2i3U(T$*{|~HeRPE~bCT7MH(@(CRc~bs3zItY-{L!&`W{3RIxq9YtG-v=& zNmL~Hzw}t5bW-xH|9&HNv{=R-c4Gf^P$GSzG@jFPX`-xSb=jdr+40q7GD!srO(rT( z$Fa3D5s#t{eJ_Vhxb$xzWmz2AV$6n`ko1sDikv4`m!aa$)n(pn|L726?aYI!q~zr+ zs8B=$^|Pmd*jlNgzr)u@s#Z_iCGFTo3WU-7qV99rOS*AT>} z$~%x9IOY;ZAcZCXDb+Mk5KRNIyg1GPjw)MRHw{VAG$ch%5C}~JfxOuU$)+7p_$F^= zA&UY>bo9AEj=vLb=!b>@y`?C<> z4<-Hks7*_H83p$lDR57|LVk9p%1@r?k=s)8On;m-fC~9?cji0%%m8)!sJu-cJ^z^^ z74d+6UaW5KQO{9C;lt1oi@Trb5pVg>O#Cl=_}S?ry812hx>6R+n zgCDv4u@66D{IM55;{36HZCS_i=TpmnmP(%A|48aYj~o^lYiTBm7vQ$V#^Y@qk072J zkH5;>Br1-fVx$B*$yiq_-&}kol`88@Q7vg@^DKy%C_9vV?xU2P(*J_wbjWsH01-*d z+>=uz;oh8zT=`?)MHO-RWB)}J1xm!Aj0z!qT=wmO&=Z00?6rP_k|G~r#;p5ycm69S zm1KPmc(r!Y;M|HC$xT-rNhS6q_9vc8j(9AYx_@^rL>a0T5(|BLlCpVcpQ>cfaY%Lm zI{5_%X)Y4NjEqe5NIw5WGF7p5Vot@Ky{nQ3KX4><$$^!xe0)Hnhtr+*UGggwKJqKc z6c9$aD}LD$U%tB|`3c?Wc&hSiZX;@to50uw98= zum}$g2JY(DF#M7>US-0$3H{gnTb&EMAEdmMc# zQC@r_6o1i;iSh!3_>Jg%<;U66b2UMWgk@LEd`=dfA+9qNh(tT;nUR=NG^6n8IR*F; zCr&epZkh9JfwUFov*e4gX7;n?yAKn;#l>syJvO@w(M|4qO2(SgZodU zlH2ePckv2^_HWR+$a0NIv9gEzbbF~^5^uql&MCP6pQ!TPi_czL)>*N3X2-W;Ao><$ zgbC`FnA52qw-iD*tVlvPl~tU znKnpQoU3@Se7DIn4~qxt)NzHIJMEpqkP3}QiOx`9%R~n|hC*XC+CQ^nI?VZWBuB^S zh4$TcEA&1(rGd9CdmelSXN%7;A3p9j_+(qR!pc=7)}bFIE6VZBl>>4-w=NM+-8dZM zG{TV@jsYxux_{;WK{-9Ui<}f*8-FLQ{+N6Fm$H&b^Wt4OZD654)utZ>CcC=<3CLfe^qt>>GmisVPeK%bQ# z{Kw0QnFs#yJ3LDcEkq2S|5QG|63;n)grVDV%rbN^%m>!qn`9rob;T=%D_#Iym}Nb( z+}Tl)9E|#&#G!dnxMDjV;b#ecK7pUlaD0HIAls++@#o2BsIc8J2iBG)zxOse=`;M7 zZ0V!WS=D-yErLOY#G(s9X86XhMr zex7u97ADUgeFU1jED55VtVrJUK2~>lc`}CkLkb*R-p;!N3Lw$65l2#M%lAG7-GM&; znKftusVc`W<$Dw0-*d^`Z>5|u($_#b7EdBAij(g1LDyYJsvhK6T8T2`2QqRaB_zj@ zkp3emA&`QZ^(jezIerI&cael8;HNDhJ^su}I+LGhrhs%lKmoZ0!lBEx?akFNF{@^# zw83e4GnLw0xnq`CJ-KLe!FLIZH!pdHaNy?T_X!7WuKoex;LRt0L^y17^^XaMZ?4@% zICAse-GpP3yMA^8)^XLXV`0GF-L@A9B`ZGqf2pfzh6_K_zH0K7ayFxE*K@7+rUdL< z58z|kIUp^z!~TN*DcW$(M;G9K3ub(y;cC4UGcEc9c+HrV>`$8s11ig&K6~@Ng|(t} ztXSMV*}UgO>Z#86((cY~N@6(v@jU!oyCj){G3A`BD)As&9}+&S6er$P~J+l@0;gV4WA%q(iXw?MS?TKUVC zJgKvf#2tfSzA7~N8y_P zfmp6BTQ&OmXG-xaS$}6T_0zId{5`t;nTk2UcTek^dH3WmfAu~{-=4z9Uuf>0$nROc zrgZtUx#N2*K9nfin>_JDx&YQC%JwCX%Ae~KW&2l`ZG}cl{!T`0z)*U1*(NBw)n!|t z{Z^OlJWke2I&$QGsKldinoN;xP_kn-fXLs8d?!`e zuRrERfAgvUNjqKrttd)=6CZJjsqXMbf5V`R<6Sy}I$S#Ge6|XzMU9&6j(!|ksG|5N zog(sS_m{D&3w+Ss460Krz_9@hxBNe0{rqBgKS%xPl+D1S<5Wj4CLTqJ+?DkZa0e5g z=IaPhJhIP+>zxRCZ%`CItjoj)jCIKeuWg7*j}k=>O^X&q3!ehCJ7UG=l0bSCKK<L-;eo zn+UHXq!7BFs1m&p`XZc-a2~=12p1t-hHyE;ID`oZZJ3LvcC{5o_}|{%E_StbbadeV zrcGP$AM;@3P4{1w>CQD!rdR*{x!gFw)T4ZNUX@;@!P9r!<7+AB+FzBxXjx7n(Q^`| zo!^Qj=lq?n*~Q&f&*_A!Q(e;WMc({Jm3Y@lPzYf*!o3Jz zKzJ76PY4|dZz1HqiI0UM3`BS@!f=FggoOy7LfDS*3xqci&Uvd!T!m1B5EQo(@t;C? z4B>HvuOoaH;Q+#$2v%qDI+as_7IGC?eS){(HM;4@8+p2fn6HGFWrF=y0(hz%9Ap)3GQqd41R96W6r*br&$EcedYR@^ zRvKN4orbcLY?Nv4LYg+9n>tcs|EjWomD|52+P|)}e_dw(8iHSvB4@eLWwFVgvYK2g zL0?~jBt3vf{(x>ROYkT7sZ8*!m7vI1!k1-&%~pbbz62{}f>tYm=Bu7^Y1Y=rG_$QV zJyaof8rhbsWSX&78jJL5>elg$OmmJ*Q*5QNNN=Qh1MZ`nj&wjn$R=wZn|1v{rrC=$ ziapQTzo@@d+&AoBTkKz-#V=VZOq3%z619)ZB9~eT2Ky4skqN4;1QzZaF~p*1$wID_ zX~tM-EE*VT`pYy$R+_+a{)VaTO-GKwCv4EbDCg%g%^oX_#kZ{*AK4|JlxenDX)JuK zG!o~PGEHk}ni`pAww1=h*(m2KnP#k&#^Rfi<{X)(*h*8VdP&=Ib)eRx3@PpfsyxniWW+WagvxuSP2$26dE_&lot~DDzFT(wyf> zBXJxd(+soH3<^pEZw}g;Yo!S+=Z`0u<`Cuuk{-QOIWE1sTPFCCm7u3PfyCr-ncxfQ z6MRA@xYtSmzrUw*)W`(0tpxeL1f?>;I4gmrAS}I$2BwmOWSXn_8mk;Djl>7jweAAUCZ#5dnaS>rnR1zh9&#c|9mI}$%@{qdpJBl*o2 zCCZP(nST<&aqx4wW!Ccly)RL@j!vM5QWb-bRwQ2JXrC8*m3zx8yx3UaM;lcWVHI{n*QnH zm!Hfzw_SO)<(kvzcC0teO8o8HIV(@J3{O1w%#Iwak6zQ7Os$=qo4k1$VLUlq!h+U8p=W?({i|Na#!UsEXTljSAt0){}=i*p3?sMc7 z>z3QShv&-kT=96kXV0GbFCkkg!i@;GAuK@n7{Y3VM-iSt_%_0i5q>%$7vd?}(DB8Q zmH3AU{-#KXqxawrYD2VPJSL9L#~rMOXyXFo(OYl_`zhME3Uag@cNkHKwgTRL5O-{8 z+aTV(4|goCZ8+~Pz#WTgyNq}JamV7?M)59hbo=TF*z*Coxn$opt8a|&dM+oPcsBR# z;j!jENNJ-QkTTK!Tuz>(V{zewPoOl=@uF^$j#mRnI>utLZr!@&=63H+TIS`oU`ZObS~ggcMiPn*mZXuyV1p%TdNU;(EJ=gKZ`EK)8VrMj z8oag?@_>an$iu~j4<18{Aqf8mTwdy~!XvrBQHQSpwjn%^@H>Q~2%S3gM*5&|KH@J! zDD{LJac_m&@OyzLd<^%ib$Aqz;RzkS4ao3g9nfN%RYxwvPej9%7`Z6l%;Cg~kvGp!h77Uz4nQt9DaDoT%E^;Mze z+ru~-NSM_O5|JnaAee6H($I_GRQ`F(%?*Z=h&*SYWedb+RcbA2AKhtC~c znrOnsxRemHnD+XDCi+A%O^n9{G(l{I{)n@Hn}CBb3yF@7u7kg>wMHoqBjXDQYY;v{ z*pBcmf`)Js!GSyt5E2lqjnl!)?W3{Q*$a<5jWx3u9(1NC0xweyjWV+r9&UrZ@K_VX z8N58u1XTtvk1}?f2=MX{H>D>6FOP3adI|>6(vq;c3|~i9bdn=04&f4iCu=u&ytohC zEUJ(@K<@}qVM!R))Iqy3F^kKQ1GM`J;PRAHg#Y6H6oM1aO%XaET#1l^Fcjf7gnJO? z$l$@1g0(?99-DBLcx=E`2eb)V;^QW03D68$f+z!;LCcJc@R&hM5M@9!Xt}q9#|&B? z62fB!EpvQ=3)2k|NJzNUlW?vF7{?>DK}hoVL^%_m=?x~V=?x~V=?&bYO5Q-Y9>(v8 zsFth%Enu7ybK~-h&*f`TS#9$*XI4_a7MFFU|Ni{q16UNzy2hP4fYJu%Ymr$a{dd?J zYsdowaNJnt@`gM}Bybq=pglbq^1xsK4E`>m0mt$noZEUy%;Js61R4&DW*SbwMd$}4 z)*#ndvgx$U#Jm<+`XdZOxE*0S!d!%ZAUuum3WBXygdFA&P#?Khj4Fg2JOqpY)JH}t zrb+4}BNfXv>Ld563H6GGj**JlXfPFW9}`*jqoba_PA}-PQ^0eC7~AvHDKis;P8pAb zUs)0s$uI?9B-;lt9Z`p2IvW>@t{}iFw1ryE+JY~-Zgq`P-ay9xAZ$nY4#AJ`CqgZx zH9}~Ma5=*D2!jy-0|U`*G)(Q)nI*%TplhRH#(0BIgp>BN;^0 zaRw0sg*wh4VlYt08AL|M8APo^cbxGviu(?}{vI-+)AP{IuJuIt2>07%_!d`d(C|#* zB!UC^8z3aefL0hd!9!{=b!N-lPex~h@)!mN8AXq+r4 zh#6g3WOcD~eZT@HN8E2qzH2kQRf`5&^F_(Ve=>oj1D6=uZ7&wcaVu`?l$gcy@dAuX zWCI?T8Ms*Fl~4g#nWv#4HQwc_i@$pKi@{$k{_5ke0sb1|FAjf=@YfiBP4E|wzoz(W zhQH?cYk|L(m$XV~-KK3~yY{mijZ*3~1j9z?f^Z$e%?P(6+>5Xf;c9z zX}l0`VR|F$!*^6fFv$@)`o0knOm&0>zO5pH-pF>O?>!Mg_*--d<#l-? zMIwSU!=h0X@v4ZRnWLhy6d}zUlGH`FQ$zuN@D>7A(FFXkhR}+h?w}{)oC)+ak)DWi zwx*{^^hEU8hMqF%iRiN}J>5x9M4trSqPysc=(8O?O{OQJ&-U~*g`PsvXZrxT(l+3^ zW59EVfaipO=hgwwZ33R#*`717`OCYZV`Gron|Es!m~q7SR)GGP(ybtT4W4tng!SpD zth!0zSxww+2jEhvIYtQ15MlCRPDc=NAX-fS1w({oNlSH8Qn)+n+*Ws`GZ$222!NJ^ zHDP{NJ7)0<$N>R#9DRGRi3p8xO+>f?AsJx^!dQf<2ssF78_-)$k{A{Zzl!jIdqIt_ zOz$9)XMAAVC-`yi=|)V9Qtt^UVq!G7_u$tJx&7^97Jr7Op#5*5U0F>11w=A?k6EwVW!hQ_?=Qj z(9&t&fSgYIi~m1;2_|w1e3vF+`pAnSBzw z(`ldCC+qyAMlkO*%pzu-#H5(^nQO8YO0>@`lUNJWKJ!ar4m{S(EQvYrSTm<22Eeq> zY?2s22vo&bH~1gSZPaIc>X_R#zr0y)1D>>q%en=p^Rg!T4xnYMt~jzL(Nx7f^fTM{ z2}PG*LAm!)Ocwpj@x8^lchJLJ{KPC=KoOcVX5oXl@jZec8!{A#2u22mtD&h98)tsY zXg~~^Va_!}G7Va(l!wDgrA&iXDrGVx>eH!|DUmG}!WgDRQZE=|m=f7CCyvaN$hbuq z!<5K0OBlnHNGb(m3{#?E!?U)+SV$Pds!COJOpSPqlVTRv!5Bk_9tfC&HP6OH&R<}K zh}Ve3Y+5BYU!Al|Fmr}N?;&hO*o$x&;dcZDY4s3VAap|LiGa4@g>scm>N7m9JSLL9 zcw7mF=DfJU(|AM;p2ow;{Um7dSSC<8f))>CJ9;Ae;!*5GPefllgq`V$pvB{tL{A~< zt0}{(o|tn4kfx&_9b1X;2JVXxN)YxS96~4!2yvxZ9@D&>o{HO* zmji%A<>g$XWJh3ieN7ncHJGYo(0=$Pt)Y|f8UDf%XI9R|mTc|ByI4ved@QHxQp;`KG5w;qQt0`5!xW)1HxNj(h?mU>#nA*n?R zX&iCdU{OA<+6RC?4xUqw?`fk0(jSjwpg5kO^}C&pVw721b4*)@NbKo7U^Kyvg88|} zaJu%LFqERr@n^ighlipentW}ccrBm2*We;xga+RT(b~~ITE)54(|Nh4yTlZXf%(Db z;&I+9Zw>~$7N6xEHddRs;$+2=lxyjH($M@qw;!1VpBa~8bm>l;jXKnwTO5gF@ua%2 zG*BAzc6|V`7WSohkT}MRjw)?ODfOF6f9e_c&%y^WyAHp9X6CG}!_#L>neM)hxB>l< z5w!G3vJxdS(JgV`Whig`E0@p9<@F6*sfvzGk`13a#S@dO^j#F_L;W+i?(BSVJ{SdRkUmgV*k@)IyR_1g! zp0*>q^>0Nx@w`XI|Bh=-;db~7;Va#_lj@348lp~eetRvh1i!m*6~5Aqj+4XjP;l&^ zP-Vg1QkQcx4KRjwOh&LK$7BJSuTZFArr$Gi0zv-KZP}73*5V(X;9`zbWz8jMR%e`q zPxLa>S--9DXB(>Xh9C4m+^@<_nIwqx_L!=@#Pow6s{RY{ef}Eib#|`A_j#z71p7O0 zz;5xP1YK9l!#VYdJe+svK)5abBAf(h`OV01r2*v$>ET>a#-dnEg4M@)sD_^Z!;Pz` zo_1A?*G2GUKDMKs9=pMH$0Wo(BI94il@#pWz-`?(SatYV(U;I_5yT&W*yAd!Y2Ya` zjlTVX2>E4UB`QsR{+y6>J6=crti#X@!TO|6y-*Oe`!6rU+XDPAhdYx{kKungn%Yn; z|K+w~i~$4}-M`=Y+CnfDe77pXAto7Kh-yvlxk3W067K2jq{CBD;&I;--RXquEE)ez zXUy23yKZoW>Wg-7l8|R%Iov; z`mMY+?kdv9$m_fEdRAUwJ0LDzAxGiu574k~qtg&u`)y z4mcf`SBKK9a-Pv%>9{+HPQsi-83vt@pYq#-bA{V;Mbn^H(@(jr`e+h-L7tvFD1?f^ zlGj!=TuEjO{lDp_Jn73oKjpS2vS6_LDNjZTXWvPahE14$S0)*)3d?_6vBo4%s^_QN zRxWxJghY_9@k;%a+so2G1eLACPr0pRfJ0R7v)xbmaP+y(!0EFxv)q<$$0`mx5(QMm zP^j8Z`OJ_NyCC+fYGGi-Rr@KgswM__5cXE(r@YGALefG=KjpS+D)Ccp%M-nVQKK4u z%5BAh5V#6I5jP)KjpT10&t>gKjooHR^g`{Q?kbj^rom@eSvjc*bmlSp{@Nw&FaF%)4=yBIVZ^MRx*Z2)AFidM@Mq4)LMJ^Jfq3-rUyL zAou2!N)}o`u1ejT+wuhrU*+!2t1eZQd-JMFhCsn^r7HL4RaI8GdvjZ#W|p*TDkcZL!SHRa7U%b&=|?3ydS&lbm>M79hYKl+p*igQJ~^D$8dH|# zbCEiKT}8!96sI<#Us2>nv~DFmdh`1X=ls#yCQL@YL=P+Jhw9RPK$PzNwk}8ailkEd z#5nR`4x5$}{#1YOzW-$8BqWBpQ}hP5)80P{A7X=04dzIbDzaS_*-rHfZY(Bd;eScp z=Wi4;TJ>UPe%vM}`2d3Vzdb!iDKAp(C6S}(9VL;OxS+R_>5|=$7Yf{uVFLaHrkY=( z*!(`zIITV)vGqlyj3`73#pU;L8!2bt@LzlC38YY5exI2}N@5XGp1}IWOBBcRx)?Xv zH8?@#DPW}{-675*Y8fNR1+NDGyO`h3zebg9AZ5S$kvai36sk=pm-mkCmjB)-QwuA9%*8I~>a~=#WNh4P~vd zC@`y+Wv$^KcrARXX?OIewT7H)X{|vxfE~KTkr=byk@#XRIx|N4gQcM>Of6Fzt>pNt0veD`8T4qOlnPyctzO9Alw2aFmTFHd0l0F@ z6*rCymuq*o$J9Aun_wjfGy40D8T~Yr%+D>AGy0`+M*o9rXhwe}y#`AXjresPQN>Cc zK#HNO)|e`-OTYz8dg60#S|Wy0t}%RVX%a0JXc+uN_K{J64h6 zRPjH<(W{?x?p~u^-*dYHR~iyUy9gzy6?D~>q8=VXilN46^C*T8gktEbO+$=P_0<5X z3HO13yVv(wPjvXG^jbn30w;P^td)%0)I$2fqWd_@eZ0KS-+WNI$2w@Z$LbDWWaR(R zT2}X`Rare%#art!eM*u~P2t}wH4d&$aqjkPp3jlhHqRFkv+#5DAa7$SB4QS%g5PlD z3f%As?!2QB?t14wbvH)15@EQ1mYN+~n0pK^dtUb61)p3PDd0aYIE6bnRvqYoA1}25 z=s=sc0ifz%WtM@IrFE5A+9L~5mG1WpoBG zp$Qd;Hk^DNZTN@)dbAmXEgo%XgOKFE%52BT9Dhf%5qN55wg68qdWz)sjLZr7nn&;& z|Cqd^mvBF+{#bt}OZ}5XCqp_g3Go40`MxtlMQv_!kIr2vv ziGj1cwFS`=`(;(XT@!|b;<5REHH}$Z+d;eFo>+SwcEI!#p~dwe&wx;j=NpnmxC7T! z0l|mo2E5yqcQh=^>Fxp$5B85%8?no|Yoq4YgOj=~;pBM9 zmxb9Jemdscr}!q(6Zf@D$GX?3uOrId4ntoj5P~G@B_MBG`L4iI_hMgXTmVp4Qh0Wv zUV#coRr}^OR8xfV-15)16e+$p z=&ACSze*(@)Jt&7bMS;_+=~mm28L$~pm5SSku(w)vxRYJA@*7ED>>KINH(7?wLLW# zHN+V;guCkyWd~LAJ4oKFkb7oJey)FJp;4inEZ>o>1@!;`BdVllw8uPX*N_C=Ap=z&h z=8Hl;{+a`cO&9Ts1)E0!20Du}T~B%j!^8g>wH$7?)$I8E+!o>P$dlf1_mv~bePKpn z>Q-oKg@w1HFi4fXr3eQ_97XPFu^VLa%Ru{gfP}g%hse6sHrW|r`CgK5y;5|k{4UxQ4qd0>o3SHMS)hNf8jd> zdH%XZSZ%A&GN+M4$~-O>e%@kBPz<}-DzvU#Y)N4OLB%4p_Pzt5#gXLYvS2JyJ*msK zq&|f1DCF5S531dQLTbB`0FRS^l>UhBYd4*!s3rocaB@2)_r&bIp-*FqcAQr`NIAb zwRA1)Ufug8lSpbD1P~m`6_M5fjmfJOqZUoY4?JMvV+fXhKD>^EGe8{zX0;`#vn8y; z?mD&)myOpMD(W|ACe&p>7|s9bE7DLe?FyX={yU`jzyB&BZro8xE_XwJ8-YEgQ}E%C5l&CIzlp%YAB>17lnKYSBrkiS z7d9p71+i+bi^hfCNSd$shQ&dtbN&uz@=!KlSGw>G3-vVR9Pe)~K4NiMW zS9c z7%fGP{%r=6_CAynNaT>R!&2fuNHoj8i6@jG#v)3PF-;a+bAO_lYq7PA zf3A_>zZV#H4;9S+tJZzvK`rkL8Ku;|d6aTaUg=)L{dWi_5OyP&#jh}LJD9g-WBtKq zh0f!J3?`V7`O*&I8iaAaJfkP!R??V`hE5edw8f7^{4k0~;UJUg!i^v4b6=k;9z%Pe z<-GBd{)y1^@PoPIjFA-E{Un^==LI;yZ;?n&@Wbm|!5;9P**~czW3a;>-+CCT?!$EQ zs&qIrb0ij#2y5$wGrMd@o-5HoGY0$|&d<&wOv>=HgP+6r;2!07@^d&ja^>_$eoki3 zZ3I1$zljUq%+=+DM`h>kj) z`!r?|i2z?T`YGtbs-d1l64=GBxad?r6;%Hk#;8sPPDBa`%-F3 z-q8?pIyBc3_L#}HY6ITa`4&h}5+`e|EBke=voVv*c8@l1hX5EiiWY=r(Ue&d6&6!D zNoKa1uvPw!jQ%mVAL~O{f2)>zZg!U3;8IF%XoAofAs(SFLL-Ex2yqB8v}%+v5})b3 zism+MaA?O5LD-gJ8K6YF`w86cR1x{zJ%3badpLz1IU{jA5s$2L=ahvf{%NKOE zKtsd%DDA9hOQ{M+ocGkx^e^EI!>#-hPCjyQUOI$=&!f21Td@>1s3KzkVoOq7Vd^ZW znl`9ndN zqV;^7zh639MG>6s$-z943->4UmVk1?+;#AcQ|KMq>$juN2Y)1Jn0j`(w!R_eSSU+! zJgr80wd{F#;m*Rr#{iVtk3l=^4XqJh$IAnLXe)xs&!+N|F(cE8OJ8j%{n?|2c0u2< z8>#+|13m+sCl^q|9N95yy#*WRqh!AC(plm7R$sDgb9i2Aihe6sR#OcoU@`nfH5X-9tPs_ zym^h3>>+BcJ_YE={Ce&Ls<=5-+}MA0p47?}GrC_&(sU0jR9D ztavDV`HY9fLx{mLf)~t@bBk7l70moOPQ~39zFQaF0=46LECgo9a$GdZr#Q8>b;~Pg zg%U@pi)h(gdvDwc3=B*Tm-+=I?A1I|PVi?9ZN3#!@Fm`v_-d2>KrE~)?yIqz!==3^~d}CS4<|E!Cz-YRyw|yu6?!z)FnRBxTFJYn0o4t z4RNEut-M-C3{7E53$?LUaw&bj5qrJx3A$UoR$mVx+C#Le-sjAlhi?eC1`fTvfWVE&q{qV==v}a}7{=hWoKH}YHU(nk*RhdjU zf*LbXDqXv4D|xxl^L-`qU6MB^-Z*2{jpnG3|4PpPY6<5jKfVGt@c9%TpA?6h9IK|s zt7!wYF*I4WhlU!v0%p=zGU;Y`{^nSKn9K3p)S3)&@Gmc?2_3@3_^JJ9&jHvRe~tKv zn3kjifpNFeG$b7eCo<4= z?gK>llnD)SEh>&F_=%v9dL}QYF5dkX9;o7&f>pS~`v*M^HoIj9O-teng(D`vF-CvC zc#%3_teQRw^PuF2c_yyf@vkj!z=w#-v;)?dSSmNli7C{0N-IX1cI198;_bdoJ2o9O zgIQIY2t<3S*8b+?CSFd3zK*1M&7nXl!ayW(;@1QXczqH5u53y&q-iTF;Mp z##_g{dw{@lr^uYK#ss+7C$bI=Gog=>1|XW^TVZBgtkIc;C@dg5pGpC;4bf^bQH{60 z^AU9+56dS0Z)6wH^)1ORkb=VJJxh}7h2`bMDDFrSe@R|Ep5v_%qbIog>r~NBV*- z$_Mb3GeQf)aC=f*{%h2b4 zn11&87$1FxEvKtauQ-tx$HD>L676;PVJ}HbR7%p?A=HER>`6;d#fNdfLPm?aLm$Q^ z9ZaiRTU3xgFM)VniAIa@^7DVcpgyVeJdEN(4hrAp_-wkmZ@Bg`5T?SN=%Aow5mw`X zx>g_Ggy>ZYuDm2AaWokK^d0y7Is5{z)(bD-onMYsBNQzR-(^utJ+six4U>?fExY$@ zMTQ!oCiei860|4qh%3JPc{lE;E;7|p5+DLE!ADa^BC;?gL0EtgvvG-hZ1~+Uoa&|b z%62xfw)0c3Z08hwtD#q^yA@5Q68R-#ixZ|!wSR)gm!u?u-4inP<}R>o;9RA7a}t&8 zW_fekDcNzzMl1QvQ9|VId8?OF-0_slO|{)*uZ`hO_)2dSbv2N+d__{8--%jCu29g_ zvlSIOJ(p(SGos}ck^QkdDW;$t@LHKn8Nf#uUygJ#@@Qf&+((S{$4Hax{`2;5M1w`c>u=ZA_T6)V%} zjbGtTY>)$dScdL{mehT}XWRkLxP82>5u4jKPtXC8--jBnZ#eAV2s_S>cET1xidFXt z2zKeh4N3yORVMi{==8rh;C%F?BTtt_zrZriWXygC&76wVEV=x zg6UdMYEen*2Y~Y$;NX)Sx5krNT#~vOoh@QG&&8K4_0Fc3qy1mN&NJZL9UX@HAmH4z zIl=i0e16n`v*{nrgg{u9I=>ze|HDu z&CuEW$Khzpe$OZe{Wv`XTu6w~!Xm6Zo#frfp;~W5l0?X#2m7Q6z9SLH8=s=?r#Zkt zBQ+KGlG)G1tz`Bq@it=H#PjmW#EsU@y{}dDN^!ZdT@2;{%T_Gl!U%&R3^9$$df}Fo z@pd$K#1lyF_K5lgsqK^mio%b(A)>zJC?%P6`(!0M{9SNRAzOqTe@mq2QFVJNvvRVarfQb zi?yAP)0%dQD_xsiq{~r}2OK)Zr7h2^AknsTY8h>u)S5zCvCQmVtj=;3oSf|%spVqh zqxg&nwbgtt?X6NbYh52jGQE1KC9N-|ITB?&&2e@=$tG%(!jvGi$omUu;niSt(P%a! zm{FIuf+qGo0)|N6Pa{E%ujn#va7(wKJK!->OZjO6DRNV>TNiy8LHEp?mlN;V?HREg z_yV@`)LlB;5lC1|-RGlKWwNiPUaqF0>^JDE%JvUFHm)5R44n}lxL$z`He=Bw?a8tu zApaHVQ^($-&3hNSEk-QI$E~%+ui`7zc#Bt?l0mxZ3f#Q_rD{R$nljWsx4D{vY2@VA zOH#2*rO|6>M)U~k@ejzIm%CPRn;Fg_!)nfO4jDe;4C|~J&LYDa&TtkPp5qMbtr^ZB z!&=TjyHV#N1D1mRlNbT#Vln5oX2)Tg#H+oJXn$?m8K}L5ThDk<*O<|xCwXEgKaaA7 zsmrw?jR8jABo|l>G%Lx}c`X@Zt2huadkY$15@1})nbX=Yas}ogD_yCO^CJ|sPJ%DzGN#qzLpJiLU z+7{?V(7s$FQJNam85sl}t~?CELnbNA39t4zzOL3g%ue&bPS~>D;1o&jln{f1}TL?<2LYB&7$b#wjk(ECAe-0z<`<;-nv_nJXV6oSStM zsoqH##G)xYQ?eKQqa+1tbqeMrQ<{=FMFU3N^>%UABJXtrf}a%9T}_5$4d&+T!A8C(a9qfYydPX zOiR$$QJ_r^Pjs+4J&Dtk6gSe7knSW-$LUT^?~imTMlQvz6eD9GQSvvtFsAB_&Y|j! zj-hIUVQRW#7^XC7kFRNtbTtZ@uqB~B7Pg+9}3=Yq3(eJ5)Xw~@StbO23xhorxQX$!t#$*-0WY^NULc`!A(qgPFCfdI4i zU1Ww#z^GW}E$84f@$Tm4?Bp$44{&ZdE%_TX#lnRL2(X~xhcaf!6dnbQ7RN1_`mEsn$`DeX_Dv=5JY zViN}PWAG_FIbxoGJ=+*}VSLww?Mt;(oAD{|f$TZhiZp*5r;U>_+V;5>6+Obl9uO=# zV9ws5C4G2`&Q(QrF9u<(2_xzNtY|GE4$EeFa~eU6Xw9hxv83@d>HxZVwG*ISziUGb zc8cKXXFTQ2X`;BBYR9)xW_^_-2l+iIEl@9UG`gaG=J^_Z^RJzfOj;Cvcr-aJZ%$)l zp0fvI2u)9Tfvu#I5R2u>QX1UrFlvEwo?h(?=>|J#&hyUU5WSf5bZO<7JP4fgWPM;Y z(d#$-h3CQ%ABZ`8pfe;0&Fr=W!4P$w(cT5(()C~Xpx(*N;W3Lmkm9g)2=`gs9K}vS zb?|{x->4(@ox+EO_h~CYA$sasq!LME+A?t_ku*)4!g}6Um`k6ae|h|({V|K^bcQHE zlg=waj;|}wUt<=%j0D^!;FdOwu--4`Z#$~x8^D1S{Rx_h_C5xqVgvnXfBpkaDAE3b z2iUhAh^PxwSEsAlnz|$63v5N{3^#V##~xsO_G;M_e>)=^r)QU{C7rjZJG7^5*`~&5 zFZ>&<052pak*@v7n_LcehPei&);V=EE(an~i*eZ(k@|tiwC8oL0&4((<-Wqf+xZv* zzJ19XcGn>mrspskVx(anQtNjgHZto&m0}|C{x@vXmu~?VY5Ecv@anxf24DopvOGBvcuIJb z1X`?12nQ85nb`%m1T#1ZxY0U6`6wu!jrq+|?K&!hoM$u_M%r(k>f|z*{%itpEy!__ zNU%Om05l7In}f0TH)OYY_+lz;>O~hH z#Ateb!(pKLi||rF6b|)lbEU8W;p&csQW@+ss;M=ggih2SiP4Em*B(H{`PbEtS%wuA zQst!_l@rDKASIyZG)Qc@)8NqjG40(U9cc=5q?<`)6$JKSmH}R-O(xIogq;9ax#qbe z5ZD+|32bBTn|9u!M>GU+2Dmn(VyeMAK}>^lckXx#qKhZNyK%+k+U8$KZe52-KmRTX z6`BMW`_EQq1R-F5AS?B4y^d>*(6`sExTbe<8O;b3320`)JH&2i2xz5& z*b%)=%-`z@Oq+EA*I{7w;&vKq4lZrSZ=?ZmYqSPs%&AjLN6RIfQmxlVmiNUHqF-u_ zp>qgwLFSl!L1r*sMA_4h5%h~ODMt%3P`!8%O`Ly;^9c;c;tXjy24tRGEynVgHi^0o z;FbHyx&y~FGwlL^y}sM%1@9r6kY23A039CFPOLd)%#ZRKJIw|}^@)XnG`~m8qLCQZ ze63E*qP2WeJ7&?XxamUMnqwASLvh7_V8uCRAx%5@Qcx=LKae~e^%p1a!pFN}7EZyv zJ9a=!K?1VCgb4HX#0_{-noCRAL|h_fQG3K_4e=o6DFK8Qe zl?0VaX1xn@!jo&kL?o_!i*^P$IJE)+VQ_RDE5t`0(08L2|X5}W9l$-46 zyg5w)`ELmTY0d>biBrWCe1_jG3Ksko-;Z>g=DLsWzrqjA&TAV09za^3sXyl9t%W$} zNlO&P5D2^F|s?-kD)d%g}B^0*teHu7Zl}NkB2Y z%0Cvb?eGUg`q7@md{CFRbQQAANsucxyN&n+ipOF_5t)rB+S+I*uNBne)}8vDyWn?_ z4JySaWGBl=%Yhz$*p>*&8?$f7fU=7)h^`T#m z3xiUccQhT{pRkC_es!Dx6fH~g5yNZprgKW;c}zC~#tUTx&pke?z~93d{%jPl9T z^{jWpR_-<4nH#C?%s)|qiEtMrONrJBc!$36UcX-Z=0!ym_u4oAFL31&jpkgCM6yK? z6RER*K?$KT>Gyy>CPb0RJjn{D`f#7TC1%mbC|OW}SN(F(2NrjR!~w>o?y4^_#$gy^ z9P2Naui+6}@Geh3F!wV7HFec%I-|xd%>S#x-4t$-)jH)QM>t z93*n^Sty#9Vp5?a)Q*90V4&un@$_DZ8;={!#xoUK=!Amy`7SKQk#p^p^-D2Ndj@IRcqE{+TeK;#4Gb(>g&HYiN2Gdv<4+Oo%I$lR z_U6&{Jpp6ztZQVa(4mK^Lunng7tt6sElGO@SINp+k%GH_9<6z=@i8j{9^5k21FRtp zBE{J&C3Y^YSzblfEy(1CbR

    p3;b>!F*W%tV1k-xD6t3I{?kQE}3)>!7R??_g+}xZqJP4j0zfgI}%1LD8y+* zIrS)4O2y#v?5)N~^gs)#ZA|Z4EL!39?f3)Zp*z1I*wM->AV<)71H%Yagl=ix3ChuP zn}9X-3LQ);{TDbrwaE8?2R^uo1eq68;T<#`>ec>>sR~3BkDxWEj%4$F2GO#5FL>oZ z45}xkqngr@aGBDR^z>!lA^mmmBh22aDM?7@6^u1MAo^P(mZjjz*v{cys$Gv-_`L=? zkA{el=u8NF-H%5qJoh;y!YxChg&ugns5-!bHf`O5%Aq_da@_}g0O_YwH!fRuoVtOB z!Z=KO;?3BizSKeef*|E}eeH&zcI^7EtsUzR9;w)JgA>^;&4~=roTdKw3hY!7FB{;1 z_l^ACk5;%N;0+(su9$#TcNy#$yEJd?_37L%FvVu_H(dv@C?K2np>t>21t=g)?>Tuo z)?%jtWI=3215gJkAfDc9pm$J=)H@Cuh%_ICf)&dA6F?=>JH*~4&~P@v0K!=k>+T;6 zy(0oCfT>PL%uF!~y&FU6sDnM>(k30bfLdo&I#@z?OzqVjI!T9Gq5B?3cx8o|bBMAQrkSCBFE*SqZfQ^N&f$AuSdaFYnq&hs& zGDecj5~ru1Ltj^Au7WvF?pbjN`gxR?y%0-8$Fsi+(f&`6=i;@0q(=2w@U;!zDTPZ0 zQbOP>?=p!YP9^40q85e3*YEQUA`8UVy(nA>zW$6-!T8#;)Pk?Cp|pXob@6h-*Et6W zU;n_Al!32OH!2~`r0xgDfNCS#>g#@14Oi%MBmjENa^^;3!wYsezgvi1Gka&IiVSDm{x!z&T)yH?xCp|$O;qs&>YO8@Dq$OowVM1-7)@Ieo)M2Qhq_Gb{)$-#Y*a*E> z`wFh2^5?WrvhjtBGb<-0f~gD}0XWkxpcdMDNX3FpbkQDaLQ1?J)A+a;a2$c!NMX4T z`Mkb2VIdJ5c|Ru0A>~)VFAGL;b#${&!S@NJcsHy~H*-QVS&)}?GmBdGC>6Be|F_sU zMI-0A!R48c^AFlmumw=j7VTHM5$7S+qL8Id{XE1kvJqs}&x?j4f=zedsZi+n?ql3Z z4qXm0FEM@s%J5h-k1iEjGxT&|7;M*))H1-Rly(^L7g$(tYO}{z;TuP`CN?cC9ZKfF z9>SC%b57v%9Q+)YND=EEq6myPNi+yMuxiJ`P|rdV0z-SHJ7bm?LpB?7{$6lp3w%a? z!+chl?@RqM zW#;qy3?B3aSh&uEFh^#w)n7{jhL>d+Y^ZQaARA&^J(HuQ*tFZ~{~1nfD5R}E4W)v= z`|>1a_zbc6C`udTvkdu(4V8UOY-l>=3u&t-x-WvQe%zyv2{F;iZ1ujoY_|GX2e#pj zz!+O>b&mU{1&=#gA7Ejh&sOKs5vcKE+3MGWz1VE^KhROO)#+8jR*!HIf2s>R>Hl-K zx|COBdF+A+kVeRLxGgBog|#+H?5AX|le81o+7TFkWR^g_^-!!Sh}GUVjD4E3k#x2f(-LIt)8>XioBpFpZ2FE) z@YE`7`ln-&9&FP)8zTC=Hhlp^iAB~=-DS0@ijio+d5cY78i~q7+w?!|6`E|#Z2Gf% zZS6R;GlWfYd5Gp*uua9fS2?sEcD|wRv)J@cN27Bcv9r*&sK7S;prtxy*1)FEK%rop z{@<{B>^A*Mf`O@@ocqGiPcB1>uAg9TOtu!*1=~n}9->xDn?4bx4dvu=(DCft9QE$T>J!e1X~1sV0Zbk|55HW(U8e(?$c;4*1ssrQT=QP zo&O@Znlw3eyFruB?lx)i81_(EPMIT^6HH-^lI(o=g2d(Wl`efR{7E5c1-{~}st)gGr+CW?1@h8H<+sBtu94iJTc2I{M&~ zzv^pppCW;E^xv#HdQa3yI{GrCdVQbnC#sTjCimjimgd=X^p|C)pvvOt9k0pNLt{us zkHS^z=y#iIaxGARbaZ{5-@0Bl?w@+Ebp4sJ`onrw{h6cgq$tv#yKo==XX|B?&_dRq zxfKWN60DwwnG&Qw%k{1&vE>N8#k`h%y(_sr=)Vfxcxe};Z=_jd+j>{=Wr#j^zV)st zVD=W7I;6y^o2)`&z+igCde?wNRE{~(5bIt3F=f;ZbIzF8y|6Y8NASZRb!fN2QZmzZ z8yzuN%MM-#+XlvMY2RC)gpm1XkteX37p${bcmW&++H~fb9ntBs=m3GvI&Yy341)2CB&L`UQ@3!O*7y0*ZH1ad8Ryr4dus3ia6$e>@_3xf9802~Z z`{u4k>0r&<3(Ltq7r$n+ zH+Kq!ZQ8TBgk8=J58O8w%?+34?&m0NkZuBAPNX}zm`L{!%mkfud0xlRjh`NS5p?4R zR_bF?m2SN5eVcClP+yGkwkVr!%yEO^x8;I#V=jFkYP?vwanQcG1?VX2#`G$o8{bz( z0A2uV%g~Lnwq zXs%rBo8zeQ6!m{$-`q5`knI3&#Xg>cc@maQkR3p}@SEm5dNbTsHjf@&iLJ180QliL zto~K9H_nlc^hVf#Yqu484nuSr_Q*?HEA*W4z7*stgBfoVs6zjJMIVKl=)WiwLjT3I zUV#1!@nhA0-#6sw>5ol0YSDjPOCUtAPc`-5SE+Tu`Y%U^)PJLhyeiRuQ7VM~3scF^ zfA`_lh7ip}e%61{Z0Ns~uR8kgM+SSK{Y&eMZ2B*{9YDa+F58}lA*zY~d%k^$1MThT zigpC+zsM8VoD0@}tFXVyZ$#(ZICm0^f6D6@fPb}6C>a09rn2MTFCW_Q@5c8G{F{Rm zU5~Ti->Rp9f1jE7_dT~R2>;fbjSk$`^;qTj_aV1B&{It#u*r?=12JHs^xz z?;@{R)V8<(_}UQd?{iVxA7n!{!{O-wo9ihwqf(nI+>&3~Tz8rInLhE&Onpre=a((Z zij!```0x5N6p6mQ+91&cI;%tn$?B^m5ygbG4X|_kRAKK;IFyag`PerO!+VaLurERt z!329Fj48wLf-S7|*18INJE%@7&utRmxS%R<2)4OY}o_FabKO{u}AAe?tyx?((_7Ox}qE-SH1H}sL6U>sW9lg zQun|;P=BJMYZ}cGurnlX$k0^GF&ML(WRdE~)2SIR(iB2oQ%Ld`Se&T37WI$-Us|+a? zK@igJ%AwN6+Q{Iyb>wHe>*jw;yQ>6WH^JC~9*0quQ-_d)e+eA!V zEB`oRv57e9Mv5Yv=;a!oB_i_E&k`{y+RqYERN?T2m7XQaLkrm^;#O?t>5qk&9}BXH z^;VaQ>I5jP?yZMhAhabr$FohyhDG-dG(%hxikD0-IyefgXc;R>QMEbdFx` z8-l}ni;Q1A1^xTww@@dg+EY;>qM^8rV@p#R(u z^ea(Mu%Mp+v&RthknL(cz+R~y~TLw=I;PPE+XTS)l~InUS;`i$DTH8y;B z<_#-emLf3_5rl5OeHzAXbjUTkI^gZVOQHLEBw~)miJNBX*d;E{7zZw~o-sJ2VO?wZ zyo$Ak`t~-Sd_4rtriN(h1>>yV_Q3N)*4}VG&>g4fj!VcDW9{VqD*DqGlEFfQ?L+k^ z8+igjf8qTJ(&IN-)`oCPOP=4iuRl=A>;5!%*dIWMropo!(ECU}R>oiLFs+nPQ`CbPvG(PFPJg|f(PO}v(*T}cAe z$a!Mw|fR=$Bv zS0g5?Mj6`Ig#AdG*w@59VJ-*lQ;cyW(%jed7|P}6YSNtuha>pyw^#PR6+}HFS63yP`>1_=aN`_VXDLml;*T zirA!kJt!QsQF=YtD7_v~9V{4w7SoO}b-)E7`FD+_9qPNEp89e4Y(HfM}U`YF$)N>JH zBNH|_mGcIta^B$d4lD+-!AXtw3@Ufx05LA?et?hKW6#Foi>23p51txYuWtbfXJM=% z_4*Z5+R*FUeW)WO>-G1&B=!2^*Bg5Mub7Cp==HmdJx_a)z))20vgq~A&=}I|8{jJS`kT!?PaRR9GQEEIM|!Vxy`B+%o6oA( zb5s^ZkzQZFdV8MwCbN$KL0nMmH&wVh$_K(X-6a6dv-5mS;qXlXs{JPhf!vBlvTDx_ z@o7XGufkX{RC}Joz|I=|oPSm&y8ZG8!5^y7?UNox`mUgTPt%qny8Zcd`+Fe_Eb@Hh zN~;dF8i@e|D(BaiEkfm?_dT7sGH~Bh#BMT`Y1`8-hc?+*Co}X7I-5k+K&X9ByTI5j z?OVShg#Hv6qM1wmO~k23yj-gMyQ)6^zZ1v*bskaaUW$E7(CY*t+Q zaI~rGv)%#*0L8pcZ<5#+J;mHh(I2IP^^}ibk{G)Fk9f5~7SoWQs%(Lldwow*zUt`u z+g91|;H~GacVf^pQcH_Ap^E*OT`8U@ybnJw&$8nnsj8A2DAX9%6mK#`oLzL z_-GFMMGSgJVuRlr(fc3JEjBc5vUIk080?sK3u?wH5Y!SOYC5c_L|#{q0Uuhieb_D^j7 z1`eaWHS;$>ZkojW9DF7720a4)zfrB#*T587D=~ZxD8GSYjqHgb5WWU(LTXWe6tOxO zSdVAYL>eQ}>l*|khd2rOx@7+X+6urb3IhRKz1nlCcR;=n8V36rSb-D^!n5uL-BzEB zy_5=?G*j;x_!*$lGW-lY4R*uY6!|Weo5Tm%>*TpJ@c07R2&HY*e=3(kPx zp9uVCh@&uWms*$UF&bwIZ?m*R<~A|B@8^PG-`=8+KWd`npsGFwEvQ?FFh$k>W>VKz zz);+*bSoR~eD(P(!DqC0(dY-huQs3gdVtPn-p8H6XNvJ4_)PVDn+*8W#%l&`t0}Lk z+_y<5RBh!qe`2cLsKv~C z#;ynas_Wb2__G3~f_Y97B!&eE19{FmDroYYXPyAhp^-CqPAK0d`%uVIr_OWg<5h&$ zR-O}fgU)k)N;7zl#kWZTsnIgW=c8fw zgr@Ud$WL_s-s42)ms7rwbbeqf8vWR})u!`b`|EVR3wH*c@54h)=)Btw8=d#~{NmAh z15|CL^ZK+tW9SvVPlC>`!)LOjUU8yS()rWWgkU;virR?I-$g#J&j&NZPUk&8s{x%y zK~{v;E&3zHf`ox|{yDg{1&`l(6m(7_S0$a7qL8Idoz5?njj+;r(-fV~Ph%2PV!VaU zpF>qOr1SAA==`66R{)*=TFLJa&$<0`ZKmJLfX^IP%7A-o(?0(Aa$}$ z4C~?pSueLZR`yJ0j*xzD~-0O>d(I9ML zJ2*N5R>!e%rY5#Sd&r2;^gRhF7UT=0?_a>BE%d#+5cEx>Wzcsh+acCor%vAkWh1Qg zolBFwY&$f^ouqFC4qYUse~92ls=dj7swJ#>wThv9r)5ZoSxQ~94>2LV8%txty}DnjpzaMiwyFR2&ZS5S!}V9 z)@L_5c+v*nP%5`2wMgW&vDiuDNc4o$$G_k}aWpp5RAnQKe#|x|*F6-1rxYTOWmt6^ zf#)@a9(|?)cyz^=)uy>G`{*=xFYXNa`T-uQOLKF-veDeKJr{@OZbP+JntLA;>n6<& zIt`j@f2l!p&my&`j9zV_xjRt>(cGO#^!k>=T#~|6&|E?U>Ugn+H1`hpb!eI^MT%u` z0%`7kDroA?YSe5@G!l@Bd%W$>k6`7C1KcgrJj5oi5^#e*G|CcU`Mp}&pW8}xEFQrX^2 zK(P>d=t@Kr>2yXuuWtfu6T2R|EDrTm*WR25`4XCDK0u0PpaN+ol?s|P(`hkihDOey znNarTWP6=D&8(D-u+q$@UjBwJ&2|tH7WhOz7=ji?Baa2dk`~l&-=~iOsEsD$^P>1jYDP=oH+|@A7!Erd z^Y%ifv}lj9{(gJ4X=VP^I<2(Cok1&;@laivvU9(UPPA`pN++T9xOY*ll}0juveL+Q zWsIUVXlu~O4M;5-fFf3zvH{OT7!8n!{U0!HY%*m)C%~$vGKFM8X!=-=6wBZR(nmWg zXwpaYBG3nomO&q(^tb``I(7P3ARA$&k9Ak-^pSx(NguKQGU($EFy9)|$NF5*$8Tf< z8T29KN4s{xG9{HKX=qQ8O~Jb8y4QD0Y;fGPFa#adM_(;N5kjW8dSUF>Z>u()H0+_% z$sgS`T7SUow}Gjw9AzEJ~fajBnv{*%w(ik1}czdegl)V(9GUE&c5rnbHWT^LOVq zb`{v*vwnUET8Km+EFca+3ws7(EZ-=pHZ44IxlRkdZWda|L3DLxhU1Wp4qE(JQ#uGO zGmfAhD-B%!os|ZhwLk-xv@~d-AySLx(yJ|Y$Z`i)Uk=>?jJGRX&L-y}o!DVZ35UpW1+2t76S?AcT;G zPj#Ief(~<$$1>C*=y1{Pz^yjkYSZETE;=1fz@4eP;h`onX~oYrT3mIwrnFeOOj?Ml zt@OC^8!J5?s||X5tEoYc`AB7%Vo4P?@jkR_pM zat~500~JV<!i8f~3DMllnjiS>V&={t&cqFY;JG9D)`G-HWkoTwHBh zc(0RA3nOr6(83BlR97ZFt=Z^c&5@eYL1>vY2i00>;5kgPo3{Qc7ii$q#s&?{M`}?l zidbdRLOc`V--tx7FHbcPU(AgaxdE%1+A&W;mQ;fONU;n~5dKp^3;y2+{HM_}@IRDH z%CpyL;J<8y75{&M@h$NmcM|{iSnwZZYKs3e3I72$6aR%w8W$fdlXCHa(f`HVSR)7t z3mpH-4nZdak;gI=l1|ipaz5ZdR9|IHn3hr(xKHx|{q_XS2Pg%=jU69Un~pkn(CMfy z?hHDL!$VDEN}1nAOXWvvN=uc?l)b3hN>76^fo{@MUJU5znT7^EU4~SaDPvJAgiQGk z(L`Fm?k3V|2iwwa=iL5a4P*+*g3vUThZM^|2GUe*DrnNw@mZiL8aaceLdldQd!0H> zWy(faX=()QbwN{C<4)4lR_yYWYdqh8N!O63p1B7!^%dDvb~~peE?=DhY^WbV3$5G!eOmYqg2KWpHq8h@3-!?l3y4F|Li{Tj%k}H4O$%)SEAf4@KN2jo z;6ikD^{evVY;+Kd$BVI7ct7f~(mHk*I>>wwiOHC%hV%3Ue`BgHZ}LHG~mZyDc1_W=KCv<&oo9RHo}Vk zqcMI$7IcP1F7f}E1^@B>n&SUmg#Qby_%HOUy0H@9Eo&()@qWYR=T&-~NVv&S*khym zmvC&uXDe_nuvAPW8_8!V8ODDZUGq1`-iBsYZI{0dL1BUAlhZ=b%FoDS8IBONQuaQ^ z{~zyGn^sO;qSMMYtkf8C;~=7|ODlCM(n?+v(8@>A2CbaH z^mx&7dbNdC>Y)mvm5-3<^_9&Fpq07*0jxIFkXEAat&CO%AjL8`fwZy!*TN zXtb)Nl`<5v)Tz@-7ug6at#rZo30kRusVr&bwT}$FX(g(vA+6jo3AFMYz-Gz{K`TGH zf@$Tq7CNm6Qji}7Zn1#2v@D=4Ex0(@$v%tqT=kdL;vP{y%|0!)uVQ{{3WSUW&O@ezprhxIClIXmedA@H0yp}tsWu&T0L+EVSm zE*(uPx6#p}Qx}JhZbr3MI@*TGb5rl?(F}C-WG#b^{)N<{!}Mwk9gRd4L`Soc==H6h z6F^5xb^um2J)g1(vLdw3rA)UCP9Pn5sGwN&|)WSpOAaHTOqY*CcWB1i-+*c`rjtj|6pv| z^uMJC0jrwMuU!RM6q**@NU;n~AT6E+bG6Xop^2bH8m%g6F~MG^F4IQKMp$XFu#rxS zopC2=vDjdsF7^Y>tk30B^z?p4x~RV!zO#$^+v@3UjC4_6Im_u0 zdV08#F3Quo8TG$h@Bhd4{;z(X%X{_oO-8yX-vi$kK>3&S^cRhE@qU`Wr2gya3j@+` ze^&HgPrt)R7vr;bJlCJ1r}s0`T@2stqJ3@k^fm$MV@jdaoe19JRN>HYtR z`mZRXmHHXO2Thsf?wxV}EO+KTU2n*oo|!de;;gPIGp0?;oaml1WBRPF!_y~B$Exe78o+YJ!N+02*h4@UDpX&SrhJ`ap!P)Dg9wngq6S%0vvsjOogX zR>=whb<|%sDTmP;0GIzFoRlKp9j-K>Jp5*4puDnPJjI7&EYb(2PS3t4Gi$;f(=zq3 z|68MRCe&y|iO~?SUrO&CaS`!xc54D}f?CG5&@s_GaSW&{1@Y<2o18NbG!&VFm{e|(bl7~xP zMMbTY?5r%1zp@CK^}l7fKaB3bg6pp`enhR&3J&Bb19nv(7dqmV({N&7Ev|H?VKT1u z_$AbcCE$uJ^j9i?=hLTeVl1h{STZ1U+RV%>v|i6wJ56Vxh>QjCC!lccw40|3E=K)K zQdXdughM6rS|P93I7cgk!31x|^(MS*1+FQ`cMw;?wFb4t^+tJpP+m95YgDvIzfE3W zm)BGBnp#JsPm$N><@HN>ZRHZ_!{zk_c|9esJ?o0}Ir8e2*E;n?d@p&ODzA^o>pFQo zCa-N`ME)7_`ntUCl-DwO?Hnuex#e}Iy#9sjjlhv43_rnTG*dxY#WV8iP;R_elL?Im?|j<9?k(1hp)gF*-6eD{DrUo=IHI<`*?c1~?-I&$zP!99Uq zlgtgQvb?god*$V?P*$Ls5q&33ijW*Npm!_>!<5L86Q*Tn(i?jUCWaEjCQQG}!uxD6 zZ%q6mOo`%Rv#4C}YL$zQgOCdg1ZFkM(m({2?JtIK;PhFUS;TVj=Y*1BYb|#jos^P< z`wYfy^m#PbQAgBa>2|E*sInr4LbwS93F63<8QIez!@@x)BoTrucAck%ffa`4-g8aRv976P9r6g4dK9*ETH zBJd!7Q?w{Vs-<2q04V}6Us>@JQC{u-Q?i8OZ)rgP%xRfcHJ~cw3WxCEK^KKA*i_k2 zMfKG+9MXA!PGC_Ie@2O_Qghr{6DGPvXNdA#frxqj&ZJX0rxF%M{p!p0){^x?)^S?Z z0OAV5Wd$i*L2X$c6W!kL;gak7=s1T;d{5XB(691K#zh@Y>ZzKD7p zq;=`o7rG876d60DokKG3lQH2+H*Qa4+LWwWMt1^Gl-nOBL2w!8cMO_Pz3lBo^(oUb zCm50-h)UXXrl{1MnG>diV-ucX$m~2QT)CQaIa6j#cTboyeHMDndS*al15hGdxr&R` z;tt<@r%n~;D>ax)frUW{zxmF;Nl!xF)*+bHuo)dh8_ZYZ0L&!-69d$Hv=|Dfa&% z?pgq&Dz5fq6WC=5Y(PM&sHvi&f+C=zpa$|FV8B2U2q9QP5+oXum~1pu&`3ao3rc;o zSZRxje`#x5v=-4~8-)V4RPj-Zii-UaFD7c#R8x)a|DBn+yF2%ly8(W$Ue3KU=bV{2 zbLRDB=C1DWqqy#VQ_6#KX^eGJBBLddg^$V}2F%f3%dP{|O`~)VnLIUbo~}R?!(eGc z|H2I)JFxRLiuV}KM`;a2V_OZOv`m#NrLZ{~-HE`S z1BFG9Fj~>mMP5iCr_+Lz;%SA()f!f9$n=@Lbk`u+u>4x(Jjo7xsqxbv9ux zJ{;&Aid$ST^|I65U@k>Sj@HX}g9^-LxI@`v5R?%}%e1uG#tWe%D3k+N2#O+xtdmW= zuJS?F+y+Z^*J6Q&oH#_ZO5v-WZ&vBN%u(lk)#7o6MPp-S}f_klD;76kW|5!E9p&=J}BuclAip1!FQ9S4}lUM z@~V7i+^0Lj7fYJNhT654v)De=v7wD5_^4Gx;Q^o8t35nnfB^T%j9^*bpJ+2HjBP{B5WO6GCB&Zm6H>kX{PEb^FmyR}L%fiUj zZ3NN~iVU@2P^6=su{egMoXHBd;DwTJDHbnyVWp!R@Is;s61)&)FbyQqlLca7!($Co zC^_;l1581&v;;J8*}@bmA7vz3xAriFvy>A|;UsD4OHMF_lQR0V0AYV#6HK8>F7%y&9>t`4Yz*nIa55h$gp=n2O;vwv(rp_C`Rl{W8-k^dnmH;tnZB1?ik zT9{od=`EmtLrelYQyLB%iX6-~hE-yvj>$gX2$ab43?vX5=#oqIY$}P~#Kh>=2LLqw z`dMEJxSV1!Yzq-x#=jA@$EUP zfU0ZGK^_~wo3?h;fGw(IO8)z_q@PMU>Ov7-A?a@=?L9_>=Suo>N!uh%87ty5CH@owW0_SUViZ(6$qN)S4r0o3`nVfQuQpJSvx;{K+Md zt*H02#5oW&ZPOz!Hv`#=6Zg7+L zI&IT))MnWhBv#a^9wA zCFuZUTW!;$mPZ?U6jcT*SQI9gP~R4By-m+anLYlkNO`RBZv&6zHa&%h&lTdrzZG$5`5xil?iKM&#({q;nmxe3 zX?h;TzdL#NU~u>}3e_FndYc}tmn?kLxJ@ta?!gWUSBhciU%2682X>r)L=*CUH*;4; zODIfPx?8gWXfL^puh~`#vPqB~f*ckE)nEeJHv?YXhFe|SnSk~KCKds-)5qo4AdW6o z1qa&Y0?Y|$m&>v6XiU;o4H2Lnx>F_q@0{*p{m?CbY1;osCt>N3vg4{4@f_Y-k9-8O zUE-JB=A}n)BpWsnd6?!JGwU`NE+8ykahEK-eQP{Ap0W z2eGevyn*nn;)6Kvhg)2z?v+=$!L$5Gp75+IK?RY^u^B(g&Q!O1r4RqIa0dD#n8K513j;_)R{DgrJ`s?yy_qA>!|r^leGc%M{^P zOS)3hrzP!`CE_oWbd{v9Na~v+;?pE;mUN$_|B>{2*@Ev1Ny{XCP|}wrJ$0(!E0pwB zNgt8)O-av~CirfZ^jDySH@zj_ecb;$!dFO|#4?UWJ3NaGhc6h~UV@KWTcqs~d{k`a z@~B*Xau1jAk)qyzpr0jtL{4jlj}%dYkI0w5OZaF6+LQ_(DOvPWqjv=#DPlu}m>c*= zk@FA5P-cRFY~dqSmMX#|YXcuCl7?9(FLUfH(+NIuQpBy$Q}`$z#m?}NlO}HPxZop4 zZI*4p5E4jBy{NP-N^Tjph_^a_l}xOF5GjY|$Yl-%>1IbjV6aH|UUrZ{$8mQ3lgM zB0X8`gS6qX#(|U^nQ$PAr8TyJ%N7Sx_zZPxj{`YNIpIJ~l9s;Yv=7ornLQ4qNZH^( z1|CZsNa5jweq1<^DlQsBk8mLOig+gDz=0IagagsEIf?_V_s@Z^8Jcw(g*xLvv@o&o zQR6;Hcdg$J{8935=rc^iGGAMGkI6ieRxLDkb+;Y@lru7iuZOM{q*{=>1bJK#nlKsn zOzs01cM!L_a56zTNm%BnECR}*-#mK~;&iF;V$Y;l135uCa-|b4Hl+}Oa`q$p1Fmr~ zFOS{(g^hd_Ro9MGcBL(?Gis1vY5@wD))nGSqou1j1e{=D@F=t-J^ z=CJ5!_no;jp54B(G0MI&SGrv78zZ~ZzOfHcM!xVz9T@kG5s%%zvDB+kU-MjJVDZx42My zsXuUolcghh!aEv31x{9SylfW(oF(Cx#te&hk8dNg5LvwYDf_N~Geyk9?dd)KWuR)@ zb6WQU2Y|GRv-rS)vQgW~m35?c=20MR8@Tmr51xm^4sU{OtI-i20F?1EF$gdGcdl%Uxzv@*W=oOMSfYQFLUw{&h z_AV&lXs68<{x?(7)sp^I(u6r8K2y@4OR7nFaiNG`Dd}cO-*8 zXxGd4^`IWu=?zJfSiIkTbTv#|0bmWzVQ8BPK5C7Tw#k7tGnYr@@{_-~1lAPw{)6AQ z2G)?I8CX+9Ij{z{?-W==BU6DjC5r>86L1BrDPlu}m>XbCk@FA3cxVF0Y=JdZmMX#| zYXht)l7<;PFSA=<%}EirLQjFUcoaJWYfhTD!Q%qf9JN`t1xsK}QDXzFDLnp-+%;SP zYl@i3fgOM~CqYa6zy?@T^cbAW8CbKDbO6?@_GMAaqkUP5DuWd)3X@B6SU6km%Mx*k zjykyl`+ZrKDh#$!RM;YKO2MRtc#$_N9o-;r>b@*d9K#sG^RL4b%D$}b*{$(7C7&id zj$#SxFmT!8aSEToCG7DyXDKH<&Pme31)cY0IVrQp;}j_yJkG#liN`5Ce6A1|9;b*) z%l8P6bFYYJG7daW(M)(8O~0dfTp<>CgvTjtX%wnEy!E~;TJKo+sBvFb+}(p6G^gax z(7$lQ#}4d_uUEXsXKqSMBN`7~fOT*1)uX3NkYNk>3T%QP*9bzBFe6xZ0ElTDZgp{H z0_#$+mQq;+SVuq5-U4yD)OZfo$pw@XSSOcL;WASS5wH%rn^A%Rqr^Jh8Gp0fS4BVG z-~_)>JcobUkxpPl6n}G0t4DAe%P&D1=f+}2&Z)S7fY@fzd4VhY95tk~cwuzCPW7Hq zI+xWT>_Hj+I4I#c+eWxwuM?irei(j)AEFi)s$*D<8$9RVNRHOsCn6gH&l&B~4l(dd z3eq(!0X@DnXpAfY{gfM5@QfmcVvv2jap#GwyzHFrn?B%{-yi|8Z;AZ)Aej-D+8m>B z3mZ`ut-tYjPDR0{f!-^_|64BbmZWpt*Ixz_U5B5n-wV1CbR}pMZ)roTa)Y<%dqPuaNq;8kE0Ugay@_$wsc zDrvi6GhT6%kqc;+1>ZqoD^{@^b|gcN3k<};-rZi zJTCaeQJZC3u!K((H8${x!sFk>UBd-@qKNUzjCEiK_{2%ja=B*%pD22a&V^yY{_FEr zk`C~R)xIQhUfP$Ws4`f=vM|PdNkWmKn=~l0`aYYboXHBd=!lYUDHbm}Vx^-SbVR}z za$k}tgJ~dgKi-V|ql#y5umlbLHe!H`j6At1eY3WN& z`;wfL+2bIpl=b;E1CJ#RqVVuRKQ0_Z5togjM>vRkMLd&n;2^4I;2<0Bfl1x zapw{7SnoVq52-+LEQWNfYpi?JX;=_>aJ}y&E(>09{!yb;IICG7#W~2nsW;+u1H#f} z_*_uJsfsUmk8==C)pewhjrcGIr`piu2B+GNBnd3Uoc_O3)}4b_hMK8!SxcQ?RfGbcv}b zjdAZ$F|tX%wL;QAN%|j9!or6AP+(!jptSdB11S06i<0)fS-ek{^cG2MiGCNq&G;qS<-hT zJ?}QbS1#!qNw-M)fuy5X2);FvZUrSQOq1_}d5h=>&z3aF{coto*UtexgNGQ}XM&Gf zWTbs^^vulVQMvr&FfP$EMZLc!%^E#}+nUicMUvb+ znUf}N@VL-3M{SmE!4f@F)YzbB3Xgv%=1ZHKkxuL%i^s zm5y%UHFfimD2`za;rZ7w2xaq8_w3f7o03lx=ti*wXc)L`K{tiZ;1c$mkDR5PKsP5z z3m0_WeB`9e9&}TrY&IVmcq~CTg@?}-;sV_iacTJ;fo|><@l3`6x+$7z^AXLzqoCVC z-aQx`K8-?khqvB*L~9-kA2n`1io1KT!{3x*82T4(_}GD+uUEXYkIy=i)#+#2gx>OY$v6Cw(C&W&! zr^2146e5Tnrs1oA*~#BBw3yvT5B$aclH-BilN@n3ipL6i0d(W{7WLnioM@%LC)n9G z(wiEK4_RsM9^+xRL(iGQzpOMI3!G*f(ulp*+<`r12y2z$33w-*W@v`{H8|lk86Oq0 zeB9!Kv2*WpgVT&aaqPu4Nq;KoUnKosNqs*OeCJDAF6o1kYLcFQkKmgs=@LmdN&1l$l22^)DhMW{}EA$leh)1zA=HaA?8$2$|!%>@MTd>4D6g4)Ohr;9U;;!Lh z$B-h%D>K%C9WW0kLCZCo4d$WfF*=vyjv*^a2h782#}GL$?HE#28LVJg7~_s1p~%op z8WdUW7_yWzS-}?4Q1UIs;)OJE8>}q1H4c*1H7Qga1^}QpDuO`DQsyJ>I`1cBErH)jXQ?iwSGIqLdle|&mh$b zV!`;D!Rs(Tq%{VON!_ho@P(om`Fzv!sUQRH=PQ}fg5(K8t<1PnXdC_}z6ZCu$T8s# zZ6+3hJ1`7t8xTjAs)FMVawXz~JIHlOI0MTaRYMH!unYwlFhb0u?e2IZU*i%Uk=u^nDZ?!;Ea}@exxpizMRK%O{R~v#5$APpgBVbv z21yzgTi5`CGDQ|!e#(d|P(l$SP=ajZjVC{3+)fy-yT%P*Vg+;yV1oQLO__WygG=aq z3NA4eJ$EXyW!%T{5wb~jeZqr+=16)wDB%*n1*J1hAAu4sF=~VGi62P%6G>l{H1IPK zpC{>Kl71m+#?M9kJ(6yfv{lm7Ux@fhNgtB5P0~v@iuh_te<|rFl3wzVh%c73QPM{x z{kx=|hXvnwNmohw7fBCG>VHJ=O_KCZNe@e!f@&vRVlpVTm1^$T9pOhMO=72Y({}K) z-Kb_mdr9z73yHKB0w;3KTppFnPc`onoKV#JH&3?&C&(Yo;DjPd-~_qpcL`1mLvvBV z2_=huYRs`SU)v2Cir5e#<_4TlmI(9)B};4Hw{qDrR(G2XMkk&~goC z15PM<49?}eA;U^CPH=+COW=f}%Fv803u6Q)gd$@%X*8$k4`M&FY$<25f-N|qA!;UsD4OHSZ~ zlQMg7LXooBSz+L@1Sb?8KIq2?@02I}a@-@sof;=I} z%Yt+WLX#mQK(Q0(&uh5V#f}M}=rpkifT962&&`OVOI5)E3b`b40x0AHC7g@(kE$UG zPz?JY;1i`{K7I73Ptq4xRF?I#462^4=;eo9!0hUL`KaiLsppDVe1eXOMNWrE2Hte4(6B)8j|6k{#IaQ z8%o^!%BIf>nf7TR8;ztpK@Z_Qijh5uUf2yrrt>KnSq{3&RMegkBg^@n@UdD+em_|r(v!Cez5+>?NxD(ee@UA9 zl;B$?>HmNdMz&wR_u{RhBb+5^5-U1>Z5HqvJjBpW6MWP%Bkh#KYi2Hw%H=1gaS5*} z>itQj*69HAQTQ5OV{sDRTZA43Z{@%obi# zWvL=evNrIVB59b%^D?`I*PIk_EA$jzi$}3Dyym2d8$2#}%~6|WTd;)J6g4*Rn!@An z#2sZGVqQ~G|&hVO*qyxNWb>4|u9-VhmR2i&bQJ7qk!}8hk zypxDabkxZe*q?W@RAI1F5T3Q_nkz;uyvdo_`&rP|iDb&u)#% zDfu*^auiF5hk?r$l~ec(E@6+#IZHXAa!!&KF6eyT$w`?#DyK- z`&js>@w`*q-Gd!Kr{vGjzi`9H4(xo*;ysS@Q(7a@nCJr9oAxwcdo2-UtssvH@{%Al z5i>%2`+%Ju#H}vUOwe8u7E>yVfcA!B*83#H=~Cl4v?o_mPSBoQSA`2rDMX&T+0=6MXEtJMNu~?F0 zD&2!Z&^hQ&(0RTa+Z;W>^BU5My|!!zHh?fMC|#$3628+>;eO3d_|AP_0AR)~ERRlr_Da1e30f4k~J&VV5S@aoq_JnuoNo&fz?hL`+B;4KrccVBn= z{!_>X;VwqX!$DI(qj*apyxk4nqVp+u%jPocJxQyOO{(+XNP0-pQ(FYyk_k$Cl2(Bd z-tt#a@{KPgz2G_Ve!Zl>l(bdSjORuCU6LM<^xPdHe1@b=l5UeUX{U(4RMM4_9+32u zzl!(;l5Uo?UDDKDBK~SggOdJK(w8Om?iPIclHMihA0>T5(v&@dZ?2?&l=NLt!dnKQ z`pGU=aOds_-zsSmlV?jEd#1Bk2CCc8ZW4UdQWW51nVHL@a`~zDT>>YHdVgi5C2&GM zX$DRdQ36iLRliH%Bpd0gz=@K@fz+T~0Vj&s5FzFUI8o&M+ffT9@WU23QDvzjOtLn> zi6Uv7Ux|U(-JvNbMcfKK1y15o>&~l+?1Dq&&49?{YoLEW537nAg5^$oZGFZW)Fu5dW2b_qwL`R)mfjw|y zsls3zMTISJq7+POh!;4q(h(PMB8p=eLwNr6b)*8EbkA-LoGAG;0Vfnoi&6uZEpVdn z8C=31IB}M80#2MHEnLtUIB`;D51c4cHo%F2#}YVEc=%i)F5pBFmzM7laN=GO&tx3H ziK3Z+6Pip%fs@Culp^3nVN0V>-Qlf)6IzQ{_^1&$iMxBS15T9u8TuD)_*meC)*fCr z^GsT_(Aeez0J-XKd@WQZ$X$Z`R*-#yz+@%>qzf=ra)%M!FaaQ=O)LTc=>Rfu5Gv?W z*IgU{kxLsV07Ncy!YfTFL;;XaekPu%6L3QDw9IAe{|*ddK@>abeuqp?^4K%bW!zpgge5)R zwA-JJx2qpl`<$pcXrI%Fe^5&hmvNsH@mTM3Duz^GVJtRvd}`4RsHIU}u66r&|DIIQ z(SIrE;EPc_g>2pN(p;t?>;zD{&H?3noo;cDrw|r)@V_{h_f-rQ*7&v?EbJa6N4Ust zP=SRlKF;=w+3i$={2IUjkFT8@LI=^P6s|xtMGS6CcJ=>q&G3UGY-~J}wzXbKbKI3`& zpl=lwAJ2As7Fz>9F|^eLAGJ;exZ7#w@~B*XavPVxouc0FTVV;@ku#ftJ4KX$JM!`G z61ZE3^i|+a$>Koj3|s+sir5e#<_5S^DR38$VrSsaNfS4CT)>^9Hp{kP3EU}aY=Aq3$A5^shKn6fikQiP9e_J0 zK}(>)2Dnr77@W%)xU-Us6SyPiCE!j`Ww3%pVRA{%4!9F>iH0LP6$99^mk4*JRU zloRMDS6AV^rW7KeUmLPNV4QhhPo5n{6xsXa;~5ispAJ0Yy!VOXJ*@Cu;4K$K@w?c+ z{oAJ3GdPazq_i%K#hF~C<_0!msRyC^;&^O!^n0Q-q#1kdeGlLJLDrkd# z!8(c6GnC40F>eo&@cc65ygP!)k zB7D^$fzdSH>Ao&Eb>U3f*Eo}glsiEW;XR7cS=`KmXk#uaE;BS=l8A;!hwAV)> zexjtSBz;fPzNmJ>Xf6jOyIjYe9DB|rO=4Ex7_;k_amWl+ZfHjdK597%B#kpOF>t%vE9In!TcM|rOFW95Ar~i2+~9FRE{@tP+kz$J zqNuTfTofMvFpNvC_DU&YCI@zaT$}_gmxwlyi=xNiT+Wb-m1LZd3pp=AE{ZCH6)Xyq zOLBIQi-=2f)X5dtLoSvo47O2J*g`H!!K8+GAr~thaX~JkIEFEV=U-oeDv(R}?ADNr zl1~%lLb0@*HE`KNE()K)CF~&=XDKJh#Yxh_1)U)mCuR1Kiy~zMxfpmXAs2;*&lTc= zToiF>`5r+o?iKM&#sRq~n)x9!%nYKC%l>8oxhQOD6skMCHRM8T6AK?TLN0N44|b4? zl0QTL!VMn_xzHNK@|(7H^G#aB&=}X<+6do+y7d#jHhNf)=LLCBkfe68ZnA(+_TkU{ z2XU*5C=+~=gw>A9BH$AZ$i|a$hl3i=;S;&kae`0e!Y5p4N+AND;HzN30w!SJ&yt#w z+KRgBs)-Ib1KBdYvZSsqqr9%PwxT9jU3&%L8)^D#P^UwUn_@D`7nRgk2I~^AuA*fv z+uPLAksD$mc1P0i9Zea-lRAz(^zPxXc=}7^>190a{VnqN6z=D6KZILaLr;v;y$IJ& zvu*l0cKCdXGgk=HuZGXWb1}l{`XT5b&?nDMW7#G6&NJ?o(loXW_lSLIY#i=`czz9c zHl8OgNMnRI)D)#bDv>=2d0G@6mh^n+{~KZw*rn2#<+bseatu2_4OQAIkx@B-jY2D3 z5=+RYP`#mwk+l!Lz=dsW^`${wCQ*p!GX4?VYW`>~dl=WF5AtGa?Ulc1mdG0$eLXVB zttL|*$oK*xDW`l<(8Kr}THvgTYZv2{a!EG!_PqF1W$h!E)=T=&)d zC$ZD4V=ik(n0^iYk9hVWoUXG#kH%N?Ay2+~yQD9I3SS*%?W;EC8^gL3|D?Q|{gXoN z{Zq&o{qrDdldt#mUI_k4oZb27AZ{t?pPmB`(*jreCx27@lkDn^ugFw2u6?or`U4|f z_+(yr?TrBmFU9kE8MYPlX#DX#$df;w0KG)#feL>-w_AUVm~IRk_HAruNf|dgBZb;KqmVH= zW00Fk?JSA2J7?U;&7?!V4L8R)qo3XsXC#|?<0vlGiEBsfgde8H`k@?-^Ox3?*PWlA zTU}8VJa)c!^_9)*PyS& z^B{!NH3sx(d~YV?$>*9R{X3}ey;GyUXJxK2Y@*_Clv%UCQK-GY2^pimb-@*kogZ;_ z=Wi+8H2UDY?t?UF(FbRGQ~ZtW=~XOsjhZY(cA=oQD#Cky_mdtzPWX%(LK{ulX9{CY0y zhx;QuTlwTr#OXfS%;vI9cprgVzxH>D=gl%~7wFOW92U&`!w>f1NT30{}#8E|L#Y)?!Wh)Fqf5m zi#_)U)33#0@w^z}blnbmH2(Vt#i-%ttQcy5WsZ zEMkFauG?L=?dTYd0YBQSx1rxho}R+gr|VA8qiI90LY~@CFX$yY9aOZTY?p1QTeFT~+m$9k#WFVu3bk(%LdMu6 zMsgz>#tZ1_u1Va-jVPOhpM4Y4B>0=!BpBJ;d(6y1HR{^M*`VI=@p-O&Jhnr#Iw8BX zj)QA4gGW!C9)4#r_j%w2-S3}@JiUOYy?;a*{(}1j+`qzY<@c{4T=)CR>2uivZxK6$ zF#X!-6VGJ`r)vf1(fIuV$dkYKhhCzyL51JXb?Nu2v+Z@Ohbt%X;Wdby-F>^|~%D)re~sWLuy+P#j<1Jg1_nq^9D0kh1EV>dw!eGb3Z9 zT^00nVsGKM-|W!+_T-)V(lQNNW-c!zm?e5M929gfANsi0<0r%-!O7cxdqe++J7 zoVXKbcb-0;n^o_e$4!jwoas&RbgBby@{fvGRENx&B+les!%059nXCR!eg`h{{XSYM1{A`<~4Elwioe}LM zY)uBhS#dVXvDw)u)ZW>IjM3RfaQmpkK5=&EY#H1>(%Iy1Cd!$r{>yR^XCqtU`%96& z(6*RV2`(LO19S!!#QNLO4?w1G_a3`GvEO#xSI>()U5=-{lLi&Bt8f?Kz6iIKuP#8i z?yK~T>o&aC;MT9vL&S5t4C_Dm=zMhvXg0 zHy1KSH(w8TG4`p%*`1q@er4ooCZ6_=IuYYP?n$`6kK4-Ea}cikI(^P;Ki-RR>(`;7;`x9K z`wH}E`sLH1k9<8}(hZ=(*Uygnx}({EFe^?=xi&j3h1xr_#$AK=6x{mt+R5U1oecXO=+XFW0Q$&hCrP>* zRQRkuAY#sXe9R5(SaEhLuG!fs)ZW>JjM3T0bK|NL5#sF5+4phd>dsE*)T7Q$Z?Vpf z3Zc629(Oa52e@n~$kXCsXe$@Sww2fp$MHmTn(kvdwcYY5cMMzj{tbW7egE#r(?fXL z`!@3R0q#$6zl__;_q!0T_t&(Qy7?5m^N}#`rD^u>xu1ul!t}JAXu3W}#rMfb4cIV3Lxvg|prv3j>SEjdES0=mS zw;rtL)ONikRgO!y%snkyhg6pvFH>evhB1YiEuHA30x!n{ql3F zOUjT2Zy}ta7Z?s9u772%+)IzWXw|j-BCmNBKP>lG)@IdKFUhK?EJwujlA5s@oU0CR zG1f>%Ty<4-`eN{w8#1@}`$epGe{`$}ilke}jsEBo>7qJalMXU@o=dCQZe(eml>|hqt-pgVu zKgnXvxL^Ayi#>KEixopxC&Jrtm*F`G`E0~*&M0D|C*T>f+b%C+!!9mjg;$}^%_(Bd zh!fX9G}sgz#=x65E4y4AE#N`<2$)JS@O3un#jCR2Nx(?yw=cNeuXGPioLJuTP zVr2;RWlg@G*RKp)jIh30lPgh;dhnA-s}jK}SyO6Dme!S)RF-Fzl;Y?NL{Go;XP6=*MwM62!U7gMv5l0`P6#WtOkc%~QJyHKdFqM2Y2#Sd;lO9F#hUcN!0#G{$gHEFXU+y&~+G-9r3fizXGspx7ODDCk)?LNAeI=ezsd7^6cx*<8yi`XWwl$`e+_&f#4 z$Uefu&Sd-0Ird7gEHA0mlTT1>PABVgz|=bf<9AT{fvIMA;mFK-;jUvXX+5W)&WnK!ngBxrcNY0ZqqhtwA|A z4nyxhs5Q{M!4Ear)xo@)Y8-x~QxB#VktLsFwx5?_=FyAWt z5~}qXw5SbJC(&I+Q)R&h`sh=J*+(+#L!OgWS#mA)DpszrjwerbI29*Ykp+>usd4hj zT>|qFS4mYVc^0iAJVwVG6mh(s<>yDoE(%3x1CxL zN)S7>xJH;uZwL(j>^Eg6tLK zBS8j?;{sy@DHNnZ5adcORKq@)i323SwTOnpKq7VIg#2*R_#(Cpx6T9}0>DJyDA%tR zpTqFV1ugj(FauzxEUc?Y;^}xLh(IG07Vu%Zb&ha2d>V2RUD3Bjm2otm*y8*-c zp44qYp$=fuuB!7&&_Lo7;Re*K&Viub#IO+WEG8AigQ0H>B0_lq786d6;SzZfW52{A!j0h3lYx@P za2Vel5uM<=yxDU??V(+vl<=(Kp?%XryR^@~{X87nFM>vgI%Qno_no2L*{fQXC8v#j zWmm?%iEMIq{g*$uX5Nla3v|p3weLzA&J;zza#l15iW-+e(T%+!n$VgAg`WhrCJiql zt=ivzh-~b;?L(^cV6SPx-mQI__JntABP$?HilWD0f6l_{`)(sn9-qqdwDOgpuW4ty z_U7or;n16*_OY*^aNmElExhY?0$mbzXXTTzc4y@h5joEf?OOR(Fra-4nXf0L;yZZK(_&1bQqy6Mu#O%_tF;a!Hf?6}ZW;JMypigw2 z{wktA-P9Id`PMC7JpLH^h0cOd+jT52w0~?%!Q#JSdQlc0UA8(kl%CtvnXs(i<%A{O z+PmrI{DkG-Ut7@i(8`)JrmguobWiVkBH^K=x#>(x7_No2hAR$-cc&LpBifZ-lJJBl zv_tFaM=zypcX}DwW_S8xBD>Nn*{&vnqY|JY=icc|oABA^;nnFi5V;7!V_S;m&MjM= zvUo4@vo^C!OPz8!yyM8lmmdzdw$Gb8e@9b8F$>(^!fAak?KU)%9VkcB&VzZYGrO*1 zn)idl;nkU)RHjvzM9P%e6)LO=;or2}Tu$;qI&-y`-$QZsA@ME#jxb2V*Qoil?6I$C z)ge6X;J%r=HuGSvcpe?vGxn9SE!xZ24=6&l7r**ETom$!XXZyTzXl1DdSs{h2<89A ziOBy)NVusbxH_{Vg#THnpkwR{tNq%WBaj7(X;_`v9>V{KY3DI5h?y69e?G7Ork$N= z8R_|}@r@i*(dwk(`D-(V`*LS#H(ZLWBXo6oekf`9>U3mPJMwKfJhy1xj&+yiE(|6% z=PndJ6xu%_FF!ak^x5jV{7`!Fs+M5iP+sn=*}2*{QnEw)8M)97?bi?&1v$OhM_64~996##E{O1R zNjAM0c}ANq2>qAZ&8oM8*CJ{TOdQzyWAdBJR-|52#_IbB@nEa0m*J86*%#V9H#8z# zP!rgCI|&4~t|N)iZY?vpBb*0%%6~r(k9`46a%ea&Bea{UIN0h#JK|6CWxo>I-*j^^ z3!cz;^FmhN51E*aeCKLqmvn^3wyaLq^G|({Xh-PGcuV^F(umj970#X=*vkDql(Z@d z*$8aSE9skb{|G$7knqLy{9MfkouSUvHy5u8*YlRvT!)r+`h!YK`$;NVTK@?MOE0^e z+ETC&cib=0yG9((gY_u2nh(>->)2(~YET}u8{S6RQInsB-b6#uTi>%EqxEHWP-}S# zvZ4pjBRBHMc8dIIBoeNw-P(n!n%<7;#LEoSTW)B2N9a|0OXF|U(28@lt1s>lZBaJr zhoA)>u6=MOWv}E?%3dVzf98=L+TVC&29JbBs+1fwrfYahSeF;rTDXAzDb3TU!;$u< zq2$xhSE2oIvvIuQ&JS$OOor9l=9AS6e6V^K9&_iQDns8;RUWwrX5WV%F=c2+7*?A9 zypNGDa`6Dz$~fB(!8UKl*p_)@L-d!kLvU)c$@3V`WtYkOiR=mO%XszOM;&G914{Mn zKNZz?H44SG_n!^*y}0^$lnXAO6EVdL&`i~M#$>Y7rQ^v?ycR<(O@9k(K~%qJ&vPg* zA#$8x3JQ|`UzBT(c5qxrxHWNgU76HN9*w-u%;QxLn_jk_T1F6N?b7a^Ne*4Gcp+ve zvNQ<&hH5yotC=$r+fZaSlD`V@QeZV?cN}@(63QPr8I4ouDz3_{??wI01*@;h4LwiY zQeN?@SJ2g5l{*vt)eFcTickFj$dz9a;kC@ib7}uUh&uE&Rntje*H(9iQ3-H;S>H`N zJ5c3tWMqS6hle_^UKI|kxfkV{vOBXA1l?#SW%B1=@HvIx8y#w&vFeLuQM*W6xX6My`RrM$*YJmE2Wa^oz$)5A>81le3S29*Tu&L#54`u%NTRcCU zx%KmJUTZk=HJ&l-rt|iCK)5wESd@eheJc-Q#A{scW5KLYX0jN|7HiYKq&A4cK8xn3 zO{BN$*mbC*6od}Sg=^&h`15dX9_+y`9_(%87X^Ngj(zM4S|zM}UBY!? z{D%UHLXrY)%IBYlwW|B|Qf{WEpnY*+N4P+!(RRUu=Za@^S1F-_L6~>Z5O8EIx==(k zH3V7x1ij>s9FZjtl1xMJi3F77cS%kq=gNcHvzXCf4)F}?ooOSQMA+U9qIf*f+(gv$+RQeoKXLk? zP(cc6UONYg$ZVZO8m2=7Y@U_<%pgo)wL4efb(eMth3=;r8q3pec?%W>Jr#6umiOP> z$a6$8qDpA)l#+&j2BqcZY1iNrKco;d*|2u0P7=@#x!hhg9R1OMBAJhe)PBfS2 zQy3px^Fs4ea3{xf{ei9XeR-M>o`POGnW5i7d)5{NJ#?dJuS754U1+?Lw>9MNN*g+Doo z9#i+3>JK8LX`e_WEEm|?7&{fo?9iT06g8L46F@`4j1W1h0o5u#{X9I;TkoUiH$Dr8 zb?pPVc0L@pO^zl_1L_cu^h>B~rzSa`>WkF!H#AVpbss+;Oro3I4$Dm$`&QGp;h=BA zJAqZ3@J`8j+Nl6-YYX;=GTZo^mO}R<6dCfPG2?9v8k*kn+O)18373TarKBGWzY^?2 z=y^W!zp^FhN2h?dFR5i9yAOUs$!I(ojVE(mifGE3_M51}5u^~)i~>xUyR^&DtEHnq zodP$Fj6|Qq5Tx<`Q)r|NV1*Pjqewv=P_hj?@-wV^Vj@TJ$S&{2($*#y$tRE z{;j2*c9@J%go@1Ai$&8m^kMKa8g<`|x=BG71Wh5%1gEmPGnE$4EEWT8D>ZRh+N3XRW0>2xDX#6DLUcEj}fgqz*8qh z)~Lg>qSo&eRb8yJt>bKbavsi%vUNy5;q9VR8-n)GoEdEry`ho3eg&j=9O)XRc>NhX zvQsPPk?-n43KJDpjYDi_lvEn&Yn2P)g55ESE01qEb zYXF)aX@A8$1OZ87TUrNgNoY+G!O|juUvQ1IZh~9n$v#)i)+YY6jRq|GBy3tCb&mY0 zIJZDsh;q&i?RlnM6oI-+-@Lh)b+Ro<9-vbIu#nx!tbzscMVS)5pN7%6(6p0KB0l$B zTcABns|K{un_g@UuPr#Fzb?kB-qCJ;kGd!LYr*1`QzLfYp#@(+YiOr3c2bjE5ZaZP zsXYMC+Mz8)lrCPu#d$w{ZB!gmyf0p$vrXn~)V1-Bx`R~eGbxM)cpI;?p2%6-Fa^nM z)6VUpPE-i-J~|@A-+0f@2d{l(zK|TeQXdzC*TzgScukAu45fjm4yUeWTJ8g1{ue$Q zSfjy8X{$32jtw_0KZM!gezKb29L+iN!O17nfi+JcE(@axXMB`1a-d~xW{b=M&u?zv z_y3}YwEV#DcWCb-LxQ1G3t_GWPWUVy))wrWt0mApf_Q1#BZJ0> z#{s9c9*i(oaprdIBrxqbavw!T7qcNA*{OXqgd%U^k#cmAeM(#2Y~lErrcdvrntl~S z!_&WRgX{4o-w{(o9YAUlo3yrEEqFFM!+kJ~_VbTpa{jf<`C6Wym!}6NzGPT6g&=~r zkOUunE&~gYCi3nPSLxm@#+**=@meQyeHNJDbnRL_XZqN{bT+z|kA0b+=&G(Zs6sPN$qwAN1K4M|$VIz$9(c$s z?YdbW{#C>*eIt2BO-0Ny|18}scYG9MmVscV3cYbKRYN=Qw3s@&fJe4#b9v;eJW|>W zLqorIfG|jZQy!}2^#E4yKRSniQyq_+JqG|U5C#*U0r;>(0r}>%z;9(R@raol1b`iueFiybu7(B7@>#6kv8a>l4d6O})-(W>Bu;T>pAyEMSfc-w)urkyD?KJva$ z<9%VMz3KAi56aNJPuR8*Tbi{kCu`wHDC9N~&U&-j)WzE=Y81Y?66z#~Vex*v&(X#q z;E^@pMIHTJucGiTsfu`~*#s^*qrm!pCybKWzPg~J>2i``K{!jNUUAnYAfsKa$Y^*l z#UeZ>)Gy-(Zu*>jn0{V=n0^RV4#u5`9HihQC|)IshcOoQ6)SWW3B7kV^1Va*^A5^@ zwz{A#T;CC{Z%5GI5wy18+o!Sor6GHKcusQT@^7csk7-;U&Z!?s9**p`_?li}ymx;P z{DG&k3Ky-;{5D1otso^_549SNC*XvEL;Ul~j_|ImhZzozn2e1W_LHe>@2> zedsTg#9Alh7;8gZqCcO)CS#A}_0&^AA)o|A^j@-XMs-!WcgaE?=LapR<&RNC;2nK6 z_7J9U9pW!knKxn&Hg*Nc&Gp{=s~28}E%SKio6P%g)~UhrCA9x!VSPnqnRjw^HTLdR zv5lwU%)k#|GyI+GPUxkq(3R+;7^x83ABiWjeUfwQsywWP@-vVpLVpINghY;#OgZvS z61(=i`GThOnluYJ?S)PEr9tM?xB4sDqwRh}Thn-=qjIBJOfqDV_;mdqmO>&zJCzB= zi)g3!AQbsd%AZtrBkj*Aqxv|yy!XlrVyjCapYMbvRs5aXS1-Wkfs@$BJuCppiX=R2 z^i1S^Ft_*g@{+oG?Ea3!iDS`r6D9C1>8u$nenMcn@&;`=Tsq3|X6z;3B;Ny+q*s``>jJmm6o9!e8n<7Po$ zKdw*FMG8q*EccV(jlF_-IGuuf4sN>UgANBR+ohf}px;)o>k~$Qn#CsGg7E`1sj-Ny z2hF(+qY%yzEW`6Y-0R-SVQav%4fjFZg}0*KaCaemBxp14Ew~Th&UqJM(369^689RS z2zv}P2-<*q51!j_CnKGaxYr=85PHRh(^J$)hvLnfm0nqaE1typtPUAMN-C2i1=7$D-U)_=von-B($Ca1;=GLUJehAr5lejz-=%9VVv7%E@-sDq?kHlL5a#Ag z%}V5%&eSwKhVLnaaL)WmY>e4n#6~;=AO921J@E`v328HKnxthsl>SMLM)<_t-B-gF^%h=T~Swo-R00jyKb&RSYI5Xsjjcl-8m&` z5VxeO$o#~B0dK|2%SkN1Y^wko4rVhjuF8M)k~IO-zC__ zVU(+(nPaA;b5FFi!*^iLlgE2^fm zZp228SA#%rmgtIQ5FPvRqG8rv%TFY6_e2m*0`Z_Z75wbe$_msrf_w2M3{k4LMj|-U zgDKFGa-4?alM=+yzQUC}j&B4K(;Mot5J$VMn-SMPE3bYbZ>6-mm@+*Q6OeU?Q4SQ* z&YtxM^wBA!%8F7_u^o&X5!M%B`f!in8Vt&t5qtuI^%+4-^hh|x7DV?)bj*MYK`P2N z5I=Vp?uH0mkB!cRT;oWXezJ?5%sxZQqoYY9io}tmt3ju+!E8Aw@4_VgkEAEfqxd20 zN>GZgm-N???gO30z6qaLEaEdHtpT0IJ`F!C-(TYI?cq-Oe)@dDf3>7PlJpfx6R#2R z7fX7br0XT!2|9~?8SVwAI34*qk$KrC;g7?e;cvp9hTFqmhPVA0eZ#gQ_Sn-HyKoP} zd)HR9chKlu^kPQ+f~iv`FN$``rt?K-<2S_7gW?CBg0YQeiyn;-rF3(F|=XHJzeS zWQd9SNF23+3)_}J97jsZs;I(dYf?bR3KT`W@ncImCg8FIrA()8hcS6KboCVbQ|@(a}7S(b#K|=14Z|cbFZ~?tJPQ=nz{X?=>uV$8fJ42wu^p(E)EP zy=a7|ELcd4V$0ya{UQ?710^!*-7J{T;SS#?yQEI2x3$x`+y(sDl$&2&BP>d@APbe! z6yTg(9mFizk$Ey#dxF@$4iRFWT)qe=D4lD$*Cf4~#n0poR5VOlgq`7T6 zJJCwJajdaC&kh1Z$f0rYi{}yjzK4xt`ADPptnwu>Tj{$=MxIWYRUTZ7aYYXOw)HcT zXDV58lNsfi4~rU_#Cy1b*g9?UDQg1=XOI5c@5O98x=nuC2g9*lLni--c0?F&XpZNN zcC0i7B+Wx(X7NMV-pexBebAoqY;^A~a@9y@F!`iIj%8ttXR$kBG1=}zb0j{v$!Sw~ zKY9Rt21>(V66+f?kCKz9aN%B+s}~zUTx7(^tcqqd!;qyX4cY3=dW)k#auO6~PeqlH zHNC7CpB2d|V>kuztc6d08z~;g>SRwx1;acsyb>I#F&ursAtsJ;W)behgjO#WR5bco zKV_;xnlO{ZuUaU4tS=2n{09Sx1M|=Dex#Kq(;|(@k&}mVW*E*zUBslt`eB(7nJUT& zTzCOgHO6GQKkFMaqm$FQFs5|j1uIL)bv`+)Ym0!kD}%%qOyb%1rKL=9yl~ zS6S79tP^C5Ag^(fSi(pA#1(@4k05&m=@jI93rI`irGm^8WVs-Z3bI>}BZBlV;R53X z$rGeOkPU+TMUeLdNnFSUMhH^C3C_NlZU^z!utgZ3K zNik=oPUbOGrkO$f+9$;if@6(Tn*!FNefZRzj`LD?jb(#HHhPN=63V+^Ti`e}aiPp# z1b-SoiWwe+iJE8-NS*6iuMzayp4_}y|4=P938K{;VMEJ8JI3!sH zq0>PEa{U&gn-*NT#35FsrUe(RKfNMOUuhL0NL^{kQuqZ(Lvt@AN*;oq35(F&T6`^} zy1p(WcPeG3AG-+^K}(_Eg3iL?;VsbV@L$ihV(F1C>1~qk0;Q!+zr`YczNB}8&SD>j z|0>_Vl5`R(o|a5FUdcMcpI~9~ad;1mUyOSr?mhp+gqq_-7;#ZSrbP(N>Kqm!=W|W{ z#YYZwyhtCe6MTv^eaC>D$h>4RuwnMF4+rovD)3#KVCiD%_4>rbqiSW;#QAJqU-Z)n zz6>bnrz^;GvytbqyEw> z+68r`G>63GFY+g>1id3Nb?9{1obaPQ%;Ud~kE7=aGEO?+8bHyS8)3rw~TcMJpikJw2laUB9jhl$HP36;HWI2*j%U+05J zBXWYl%%|5PH5l}`-y%3RQLw}u=tIvN9)^xy%~hXBS1kMCaB)nYh&LSWulkJa-@}io z%8*)eY=;lgbueG-wRZ_7U@a?I1-1@41eT&Q0%GK}rR= zU65Z2@^?WxIHC4~tGF8T>i~X27n6ZWmH0x^5@1~1Lc~yr*xofIG#+|uO8DsLt0_qb zOL=Xy<0sBdq$fEaUz#huesVqVrz)<5{@IAAe!+!YT0|uW7m`ZxqUvcA`Jsk4sj7nh z)QjI+<*JgPN+VTEcz*J$xk9|f4BB`DOqO3Q5HP%nhx}@U_Fhs#n8>Yldd%%=tjis}~*r z2Wh+??VH8xmj6aF%0xAaH5c`ydTmqn!EJ94p#WgG077Yir?COc0&)*bm;?lhD5}#2 zL?vg%)Htb!xt1clKcDJHY7jBBzHdYf{kcU?0MqYYuqRFRCxFrve<3J!+#yi<>&Fo0 z7tTo@aWSK!n`YyCz#HwlDc1yA@%Bc2&e@C%Zk}H&Vjgx5@2>l#S60`RQ(}6bmf$lm zPT+>_#S5V`C8|vFj{%WSPN9FrC#PTqM*icCDH8b?b$}@`9biAbmcpp1SYU!6YK6v+*#0qCam4t8BGhLmo*++<^yEB!hQ-&PXshfkwMyEeOyxqq}{mhNu) zZa{$GEN;mF^GvGeliKSzN&FbDfsTA&9Vg=jDHEhwklzUMvLK%ca#D~BTp`G{f~*x} z3n%1wvN4CzpkqshDPw=0ok;7J9fxTWkFQ2>M&6S!4S9~eHr{*lRAaAzFU)4~1~n)aqZsaL`pR4#n(4oOj$ah;^S z^e%(`z=N;CqsvhLXt0szQGUXfH(cOjylm7)FJe2XlQI6yz*&e<`So^9>5_fuujiP^ zMixjkqIg~fqv#4`X)Fwl2_B&~IXCHLhZnDjJkpvb|P zF@s$VglZ;$o;=LW3fOGyA1!2anXp~n?3r|0=L>8|);9m+KiF!Fv-scR8?^I>`^LV2 z9j|X^e1YQ-IP&BPeID9Xgbl{x9NtWvZ3@>XiLXRt&j6nHlYf5@J~FH8{jfMAJaf_N zLBq$s5_oFQb4PqDU#LGLwC@Xig(KK&(QVJOR6Hh$$22^8#A9RV`67IW;I^F%2TS;& z=UrN_UUX0~uyseSw&ItE!_OT|;h8*dG4-DKHbN3k6E1!9Ft!KN;b?5irf}`u6R;VV zzJswV>2MhO&=!z$I>SYUqs`Huh4?P!&SVE3k$ubZ?!ffaY*`N$9?($qO=`N>T!uYxzQ;vR%%H);OBiiauw3oh`NChera zicS0(!ifA0fBvdT8yZ;g1Tp>t|KJdKW2-N)_0`az`_u5v0PRxW;qZ}XMNDXL4h5Y> zLBH06vMJ~U3i^qZ-;vbVni`|x3oi_?oHXy~*VsBLCguRelp+RUO&^YKdVaKa3N|pe z;s}Nan$-0CB+XAjm+L_bi0jJ%2s(FdK_}TstJeOo;scz!=mgI*BVa}C8!>b%XD`f8y@=wOe@Vr$02dj%Dd>c7g=iS2W0b2VAU>&2~TT%i3@OB(s?mmAgy&P zkKhLVMx5E=ni^Yw#w%&$qCfc-`ql;Tqg|4{gBqe{Dc1&Oq!BJoW112_j(oLImuP zXBTkhfA#cEYW}-2zoajUOlmzpbOQY7l?jZAbGta0e^sc!H(_$ut;4DM3Vh));eyVl zok`7^UxpJKmw$=R^*~}BBqk)@iWB~sUy4H=@>~v`k64YbS$xG0Gw_2lI1xMct+8L= z1VkS=@N7upBL&MlSkO;rOS0&_eOKm(jJLww==@NgU74-n3G>@;8Gw(uKtk?ct_xH+>G8EKu6*|ls?!CD+YKq>#F`A}1Hn=?ObJ&htEil{b2@Mqq?YT@O1XZ`Z--^gKN)N}ud3wK&i9OV~{6khh5VC>0$%()hxcdq` z$b$;>Z6(puh6ZOBz@vV4=zV;tXQ=4s5w|~7NB!zx-wQLCR*Xn^RUni;6sMiH!2*|M zT^EcwN-4e#0Y4hD;4|VJ97<0K@6S)ny(9o(N~;9p zq6sVRR5&ALpDlO?67SZuY2dwVMYF;x(%yOlJ^|Gdx^UlK*TT z&-4FVJs$ltHzggH^dTNDj8^Acy^dS6AbIt=4KS4Wj!Y;wDU{~ZE`_Z4_DbX|&Mvf+ z<^uF`I1|4^OP-65f~8MO*p;4%dx~dQdbUUV1fS?wgl`P(PR~Jm46jWu(}rU9grioS zr=lSa-;18Oef1y6EmvPNXm?gh#`8FDv;(J?;DXw-NFe4g&klYjJa0CRF>9Y8ir4Y$ z58X%I?HA>6gRB~iO~YWx;piQ3K0B+XwLkKO*@gn3c5O`|e*cZfj@K`a%gRDYr6XNB!%?1}nzHRo*|GtmI zZprW%K9+7rUq1G&)$_Nnp5N6x{Ywn0Vruu?Dt#<%ymcB5xBd`w1R4kDq-p!VMR9j) zH{fGrt6FYJ#$#458e3j#owlgy)=Cyk5B;D9Q{kaaH`k!|#IfC*A1aC!;@QhV%&O+UyrmUSn?tjoMr24>*n*dmGSkbOv`mBx z4iM9YF*uOtR1bklJ833W=IS*+5b3M=ge`lx%(=KoZOaZ>d=iSUmakN)ZFr4pz4K+L z;HX39$$}>#H}#b;VgZ}fF{pvl`*KXWUgY&Cvg;hG&>skb8sRkJE<~JhZoqTIbLSqeZP?x#}%6z$86LnSFzouBH z`%n*B0J%?n>NKuvA|_kPRdQk`F7~CL7$c9Qfkw5U9m5%A1<5Nm8q{f5nXWT?9nge` zbHO||bYA(;eVeuf0Pixu1pwF^>KVf^6x_(c$+%AJ=^)%)wfO|bWtz7Fv(I&k zeWTuU@4mx5cPZe1&hYg=C&3pp!WKC@AKy-dR>MAszb~kt(}iAdC@;`O15Up5j_=)D zsJso~9JgCEscSI_$?LLgju{`tO`BYCj`AkLT?n6*wV>>h-L1t~Ln3jk9^_3i3uMf? zX!czXUv<_3`;DH4Wn2IjPw%t5e0Y+0;w*;}SDhrQK3x6iJ`7hc#dk%itv-*DBy#t5 zD(-<#u=q8oGwC802KMnKbh+R+OS-l6{7TRbN%J#aJUpQW6v_8Ig<@6kj~CMaq(jkP zj5D)tXh=Nxn}O4`<*d2aWx;lR{pg24K#NC@vcf`Ce4S!YF-UkUmI^)|JslWWxR3^h z`{L6L!ejCNUGu&i+%@VDiJ%ujgAnWq^ZxxC@5=)72NVyqv3<2UA2V3$}L2CbXpKFzy@NJ%pImo8iV!%OOSZ)8@ zdSFZ;iiphoT*j!i(+SirtXqDAwp3O`3r{vKf#S3NhmogHzbK}vA!nPsTI!Qny1}7O zJv9$*pW^BFNz3VUE!kok^$$Qa#C>&Ds2Re%FKTqV-Q&oJ3UE(5ggZnvbNdLl-LmbJ(L)O#A*-lhg-iuMF+0o zj;<^RAzh$20;y;;V2O$xHg*0L1c&=v(EI6p8!WCzee?HVI7n}_$ZkDwZx0sN+SCqQ zn{eMgAf?vcLH8K^axRhY9{{;*sX8tD8-Is=ie^%WeceMHHu8gk9Y!4jiZHK{Xspps zcOFZ^^h~IRx}~2MF$~$pHX7hJ{L(r}!pl+dPaAvdEJCq&z<_aF8CZFBADOpW=H(ZPfA-$MBV)3}SLRbA>p zj?GHLQuUgp-)Nn?WW5nXm(-n4!^%68%dB_!M??%9nmJL9_flHAUVQ-!?mgoZQj6xRH1Jq8s`qE#AH9<@wbUUwXY7tk% z08JB8MsPg`}ihUIqD`7NYE(f`(S3r(+ zkFKfMhiLS^dd2F0Y9aPrHTJzqg=Mj~slC>-i{_J?D89T!xbrC=TZ>|IDw%5B(M^`v zqacu=1Za9>c!rYMf$;I%yhuG(?UHa)6Nr?R9=@{uTUh zfR1;m6W4Hk7uS}w*H+YV`IH?L15`+Rjs1|s_EYBuc1t&?mqA+9w-bpre4R20M7Rcd zH~S$D211m8gt*QlaS2a|y*|i(NU=BLZqe?CZIW_7Bnkkm7)W=)8Eq(ftokE;I)SN0XihdQ9OiVQS;z}78f>!br& ztEPZa(F?a$wbDGn6|mlO!k9Bmm9XA(fFZad^EnS-C7wnNZxb0PiTGZW9Evwk^79O! zB-NtKBOyjv)DimW$`6@~WF~%O}R4uXdGYshC z9Mi(#t?CnxfgLAvYl%K<6@A@Kueo*0GI;)j+J9v;YTueJryHzVG~JwO4jZr*1W?Cg zzRbQR>aeSrg(`7fX$M~!n(d-Hvv#Rxt@@`HYgb?I!!T_YA@)5qSE>KH1w*wtHnT$? zz9E^rka(%OB9Bs4@^JePt2NeFt=>ggLZ)M3)hY15>i9q3V4&_nWVIvN zow#FtZ7$jqKn&i_7^7oB21Fr7&o_HNj4_(sjBJ*wtAQ&TU0F12rpMVkDyI3pO6HE( z%x$sFdXk{bZT`3r`?ebU4xfK=oLw~~#WiU=V>7p#l5Y2dhS;~)*muI8oK=e~i6~oX z%_nuNobre;^tE(?f5NN{pn>1bF|e{41mz>oy~u?2ie?6Z&l^pdXCA7ND|Tjckh7Y`wE`cx$-s@9TvW=jf#73UmtMTII9#28GBWPDmLJz+m<9F`8WSY2Q zlj}L0cClg;dSj3K*J&^a_(p-3M!Z~w;GmVSI@1G7d+c-6JIs18t1xfNDQnd@8n`+G zyCsrGv9~-QQXMHLNF&pre%>zY=b6Z5W3$O$zgT;Bti4&*vg>lJ#Y%HXb4Vusw*Xit zMvy%AWLHiD5ocOL&@{?`8bvHE9IoaXTqP?JLh7QQk_urH_cr?X5cps!vkTj6b;&Zh z&ScPylhd`I6AwLrg>c8wpz$#ompggnD4A_jozQfd*=gbr9D&dm+-MRi97H`3DjtQd zyrJ_5v=fFD*nncqvINO|HX9^)D25>SvGo8+4}bc#Aw8hv%JOF=(@|M|Sm2g686ph_ z)VCf)H8DS>U`Ve=6blo?TH(d!UZ5T@#g9R-&a|ILO#7=46uO`-FalXvenv_U3}-t$ zg5dHd0B!={NNmA37<>ts^{ACR{&^ zcb#9MrJ{`(rzsMGc@{YU+mtNQnd82H>WDKkP;xX#VDLu7H5m!4TV9k*6zYQ;I)TDh z@QGKvaGj=ci8ReAi&{bwz>|Zv<+9ab1cHG_V0YK~64qNEV}>%klCN*0x?8J`g7&zq zXEA#D>RG!*!<O-lIy`k0>Uuxm?J=SXSB`vJ%0(dxF;=gHF`zE`gs2 zg0N4N#nK^m*VzLZl2hF<6coWYsbopG>Nyffvr$%>jXIyOWvyB%Cnm<&GKQDEcq6|L z=*3OfawxG`$QZPfqQ7BkvGZ#(R~7Ru0ko(Ge*?ZR66_saR!AS6X{3+45YFppyoNqx z8n2424)?60iMIT{%{0xZ-`quf6=zkaR_|eC$N^@?tv=*N5e7dWuLIS|iq#QmfpY)} zVE^8t+}08MmXCu7c`IR&%$zwDhEfS?X?yHRKh((t25MbOmr9cCVM44;y%Qz+^jPB` zhR%mtUZ++ZrAh3IH!+EQ1oZ|MEN^4GzFsj1uqQbrU%9ah+vAQ_5MRf*9USYc6F@?o z?Nl#0G6Ug5cx^Xm-6+S8h1i7+8Ypv|s;FSrfU`+4vLQ8|<7Bx( z6*0Iwe0e}0ECzGO(a{sJ=PyxVvq4ARMk`k7*^DJKcXNn#b)p)ElcrI2OPiksr1Wj- z&JTDAQ+^nu_p2y*}P6=oy;AHk|mkc`wQI8XVJfCQjlA!I_zP zA`HgZqjD{m!+Ai8Si;*t&Wt!F$%XzO#(({=fh+khf~}GJ8$7lpbNGY#FS(xnr~KE4 zml1bUS?1?X2Jm0)atfxUva20I8auIZ~H zkpBvE`XUDMU#1og$$yRie~JHc(`xmZ9PnnY;|K6x6W){j*MFyg3r^E<0KQDJW1t#I zKImBD<4x??n>RABxuR!a$2PyKS59ZgNHxTwh~&!#uw&VQEY?-8aN;IgPo(6~X?BE0 z?p30uAE1$w3?j60U$&X}vgJTE@nze4iKl*6jiZ&$`ebT0QNt3DFN2>j^%V((4k5yKnpRZJ( zuK7lLDw0Jnn3P?Z$rS@P-mLy(n%M|Xz3i_z5zsY>^ah7z2D2qy9OBdliOtlp59(XLuugKFCtzLuu7!(|$WM$~*Fz!5N z!Qlh-!*wv%+Cr~T#j;ELepA(tG4mk-X&e=(qV8`8L`@9)EXw^I(ua>B>gO$Xk^x42 zjlG^UG1u-7;0B1nlqtLEZTDUe^_5cMR3EHHSGLkl2AN4nRa^?`^+WBGF}d&}*8%H+ zc`zG@sZDJGgkgJOZ*g*ERMiG+A}|*)jJRjkHySLGP+=0Id!6dYd@>#F^@sI1)o-vN z51U(m7{FDf+y-NSOQLAbZvz1GYf@*xi`=~BZM2Ci2b9ejXYYkUvEf7RgFrIk>^**v z3={^C%JNgfeVYL*Gw2`{r^ok>ofY1Q0lG7Lf?^0QeX^0*ZAxuZG{KitaMFN+O{r<0 zZg9b&zSKvrp+*Fx9*F-7hs@%6Qm-)iSp)0N`0gMGXjQ&kN3fAS5p)Eef@xMvk^6*S9DAsbJ7Qsu_ybs10-Hysr4N5HnDXZ09ji` zdk2lSlZIJEi*8n7uQLgyEzrM|pl_=aPU>bACncG6SPzz9gX*;VqT0-QSbP;Xl9r%e z$Ai;Gh18!=Tc8u@8ZA9?ge!||>LsuJLwha@h^npFiTyrAqnMM}?wf*%0NZ`zX(ddw)#Onb{$Ed$kZLlr&>CnIus5d0)DJ^lq8*ioF{%^-w&P0R0 zF8DEzZz`rWfvk%BL#`&+C%W7~IJDQRkG)7se8ZphC0>5GcHq8OLXvLlcszYE9kDoG zv*JWeYIjYh3X6N1%HKKQpUOM(p8Hf&-+ml89>a$vuD~l6;o*(;Q$d+3eXDgTExV_L zV((C0@G_^+p&o{bI~((Ns1J;z*LGu(YL@GUuJ6OXW03J4aaakS!T7a1t65(u)L0k2ur~MWX5W;1Eg)L!eh!jdzPz zgwVL{F^1b35CyXyVU2c-tx2$Y(g#-eKE@!H03KD<_LGD_8c!HZyxZ$OuQu~IHgqR7 z&|o@F^>x~6?I+aQR`l!$yumz-2GBoV1}Pq}pTtM0?B_FQe%zMlJw7oH`b#{a)`Ru6 znLUfv2-u+@4TeL9`YtTnVR9R5@A1}$14(ieNQ#A(`x`5nm}_`!P-wS;&?6kmW1$1A z6P}Nygg?~ajy7=(s zQRtoq>j&$C8t@&;40mL;Gehp_IE^!Gi8h>fIs!zv&#LA39WT-ODs&|jEKWY_hR?Yx zWhI!DuEUI!19HN5@ity=t7)8pPq3HZc;*U(#kIP}^2t9~PCFOON?+g!hiW~^ zrxl!*@oh^SESq&xk~_ZorX*!k2`siQIqjGb?3suY(@^8n0Tb_W0rt?boQ{yZ(M;dA z+5-a^82b;e+=QcrbYy)ssApiFc6VO&O?I{V31XXr`D`np54QiqWkDmon_<)*g~st$ zJe^%v4^aU+)?%)}Lasm*Y-n&sK&GRD=^P%uq)2U9;J#AkGaA-pZ>jr9^9J5Wd0GAA zYxp4LO$#bF!9dMl_dj5?>2+6}POzAvNLqlcCHSwm&Q6}QmSt}gw`5=s0 zbPB``MQw@tJh02(_T6~#T$ExjK|=Z#I9p}If`FArd zd`ra?oc~D7S3^hRvuQZ=Jj=0Q-5444=~)!yTmWKz3MV+M2X0(|Puex8caqSGY+l8< zdJ@TMZYrsHXge_4^pkD)TYqGk(9ePJNt`?A1SG!WhAi;yz@z%GO-&g^pKz<0TY^7#Q?WI7Qx(hw2g!nA ztp*v#U$X3XI#*P=0f!{m3f#NFDLfIC&(h&p;W=L^b7C&J7=tNB(@)oW#_6%{5!M6g zNtK^r{!w4U$NloW&;E*1VXvn_eHGRYpvOsgZNMwmpg1obIK`zFCl&8bylMB5ziayG z#0fE;QB*FrYZ9G^#$-0WYrHSJ5rHXa{4B<1no^RFszsnr-U9}d86<9F%44HQW&NLD+vsu6sx8lRE ziCIcwmYgbVc;ogip@FbP^WY%5Q=R&oe-N1@I=j-ikH^q)v5z(NOO!(2L)4t35lJ2J z7wQ1&ikhtGCYz=0&&$N=xf7u2c%F!!&zK#fL^PAkJ-^$SAAvWX_smrr?W-D)gj_AC zp>MI$X`T*k9m3IJ-}jSgx@l7XNI@9Y8JKE5W=eIEydUYkVdvu1wk%Z`XK?*qZg-E^{;3gRvftEC?&r32C8MUf3Gcd+<4XUxR#W=G**v%_jYZfv2FM5eYH^m75=g@B?fmiUf0Hc!c*k8r5VCFxOf06M9$XX4L$I20V2l z_c`Cs(4hVSMdMhM71#1$_%U4-0$aYZ%2N;B)j(z_7zn0o8F(z4i zMkPI$-+o5gF(o}kdT7p4(xC_<=t3l&FT=MYTzi_Q=%={@o2Unp@flZ|C4YHA7ms-6 zJ0z{YjCr9^J%*{E^6MZE2A|rDleBSto+x6&)H19MT^GhZM}_M_qw(4`B5sr06Tu)I z$0VDjk7qA|V)ckp?qm9W`!37{jWtH_cs=+f3g$qJ?LVE(=VE7py_Iwpw~Y%Nre2Dm z1|jwxU4~r!zwr&yZ-gI4yamz!{`cyFL=(~`;!Z7Gk;s=zW|rjQ4%pHZQHVlDB+<9Q za8D~=G)Nx|!4Fq?vg^OeP`_4-Zo9iu|HLM4QYPR4<#X`0v+`;P%;rfOmCHccMES?@NAvXMw~~jl z0pMZ21>qa$JxH$a*QO5!B`Drf_zOXeH-wvlU9F<5E$7@Y4uNs zNLnp}%7gEo3e=}itFGXGf%5AVgqw9PmkjVvg&#EKJ+>eEmltbN_t&Hzs!2UklUiSs z+EJ6*UX$8ZlZtc8DzRYHRgb=8bQS4Ys!n;%h5#CaPoYC}1{F`o)u*(&Ccuk*9%amQ zNQCN3&oZk+A0($skno4^UBLZl&`r;HBQw94bG<6JW)gTP%g-SYDD&uZ%e{toBYW}b zdKtTxy)B0KCieEBeDN7rdHV4DXpoEz_dzt-o6$sQ@|zL+*{3AR&{Z9szOeTYM(Uko zsDk<_43Mo=Cw!65Bhp)LLjvLpWG0Wv1W+-mb1p)l9cqmZHEk*cXMDNdgPn0I>TWdLr<(cF;A58L6D1dI12@|RGdQuiqHa|AX zJ)*vKAywu`mOAw?=$K>2`OAwQSe|BY)>4mKw|oI+tQP7g?BZQZ2~iyy{fw`TI=-mW zGTYSgoN)_jT!>4=W(F@gG6O~Df zjVHOhg>bhL$NhCgE63+ys)q+({N9L8S>Sn<`=uKF!WXZgs5DtrAEOzB{IDG}qV#b; z?FV8_bZy|ik8$ruQxV!Ov2xFE_>#31-U?N?m-vEjm%$EYg=(5A_xpQ9RVUDqmCXG> z_>33TIO6{8Do9n{Vpp4~a_8NvUe91?E-x#%O$o?uSATjClci2~OMp?|oVE^K1z7d6 zpECujli2N6&tbP$9R;_cb1ReO?rOIo__TPxDB0| zlC3h@)Q{h$VfHDaGEDni7NTY4OKuJMeVKvC6D>O09b5zeT+Lz%9nPC*=aFxvA zf-N7pM^siIcT1yPX2E%W(2ZKCU6-=w9eB6~;|*vJK?hNRGd`jMQQRKXeK)0QU7&27 z92)!e=4;Y8^-d%8MeMDGSKSYiM*YWd$OeQM^ALu%FcVQXt4{xhR z=lLVwL?p;RLOn4ri0(p|W9%bmIlHbh-fg7wi=e@q0+4fI_WvB&xAd%8wpcxqg16Si z2s@mPw+8RAnPB%=e_%b3ICplihi`*|y+>k+3QJlIJ5Jbtx{h?J{1%5d(j(wz!vl%Q zKNz0Zcicz0qzW7I-eWzgvIEMt)IPpFOqcVj?#F2CtJ#oL^Ds*xA&MYe-l7Kwo~jLT}$_*DzIK=UjutEm>Xb!Sv;EN8MM*UUSt>4brsN(-AQ;P4tuf#y8yIlLCVVv1gk(XFL~Ih0dy$T|9{1FMtn5m ziushX!O|4j zO3D#x1_s)x-hr`- zN)HDb$4#a=V=9I$#ET1v$Mu};!`A_jBJgD)@_Y%Co6>;(Xf|I`SEF8yzbFg*Outk888+ygYIp{w1$#d_TFet=xJS8I+>Se# zXX1Y4QoOIiy+UR9N55O~UMh<4w-9$VUj@!T7jK0e$28KKPQ|w2ax-1ViaP0I+=ora z_MFITvbPhH-tJTnqnQ|kQ@%_Q>e%Z)BgUJ!^7r4MB*c}I1X2mAQri(*Yi|YD^$eMX zsjb8(rnb^@JQ)S3cZFK3TwTiSCwWo@@}TZ;mAfT}4*K7IoCx8XD2>A3frZo#(WUhL(38&oNJ$%THRyP=BNrJgJV zlnY@5V-8)8JGEW-fWs{a%|XgsF%frc=OCn*Zaj#ANeC?i1(gB<`!L9_6j0z}W}M1@1g`qCj-7x?-#*N>A!y=x{|vAWQBz=hQ<%1bz8~&EpKcJ#jazo?~Xal8c<`?bg0nmV^9m7GIG`~CL0;P z9}TzIVjsxPBu7V*r-fLng5lye%V5ts0DH@ETQsFoZ2f#2YNjPGF5xTpGz3`>I=|wW z-Y0|A9mMLAMxB>PtEMtS!1Gbw2h0 zh8!z*^r=oQ+Y}_!j~@n=5-gqe=k-IsN00R|bRAUhj8keq`WAz93kk`XmZxG^L0kha zda+PKR^w{UV@pd~PI1M2pw4qaZ5Y_+KThWbsBP%OD4|Z?!f&O{`=t>>5 z9}0U;5IW;jS1_$PQV77_Vd_lm;bUIB^VoFs8!4-GQE52(?{8?kcSADXS_unmjh3wR zG}&9gzTno(J|`TtXq&uV^A5QB^_tDo5kc2$HWS-fyM^*5w4%44{fp7td_xV)s1~bL zb9oxe!Rm4wmK)f3-uXAN5@mlCbnY7ovyOVg)q;o6>P!GQ0RX1&`S#(pp^&ElsXdGj zs2#<8uGh<-?=%c8%!Lt3VzN`c-EO*x>?mr{&}rL=O#37*bp8Q%Q`mODxnG z1XR02&>kIW@J?fp%{a~XTf(%sFSbFyBE%{083eIa_S4$@Dyb4GpwB{$|9KTIYj0(M z#hmklWaHI`E7*gvf1lSBGFf;Vg5hH2`J%}t!))fTAQ^U%KddPv&gXH&d3q}%;#Txo z@1mPeFkujf!+lD1Nw3*L&WuxeO@r+Er4DLtTG$J_D%&)4QsSTakzd|Sfjql2oI{TWkWb{fK+Ul>yIna8Ht=y0*Kr9XFb}EPV zJ{JI=n19o8Xb)eb8@>=SFx@McxLix;eM|To)HlMrHL~8r_<%ws333Q0|6rFudF>6?E4q{o6(Cu zKamktpN9Q%j-B!1y=oE10;waE#WvVvThyw@fK$xQkUT;K)=1<{TqgY^T;~vqdT14H#Dr ztcaMGE$UtvO%nHmYt*o-WIZ@a{W*v9E>m9|$4a4*Fvx$gNACVbN5y(|e5WyyuS|fb~p_ZnC4sn$~3Rkq-D|>P3rF zy%pRTW*F2BDy@y>P-m$#PU|xV#jirV(w7Oc3dR1n1=Gx5)Fm`%!E-0c3B^j9^NMRw z2Ew(jE7%)dfCFTDhhDGJLG^k$esI0c)$27!)+<8Kt}Ew1sL^&MF`A99@C}kEdoOUsOQ6&2im`(`P^_V-V)q%m&2a#$_$Q)H;?sFrPaYFHvJq^}R%BOH>!P z0#X>soco|F#!S^U0TFFBG>;51+#}=;uzHV-#U7K_1SDbBQLyuq3Y)bH~k3g53f~ zUBFQFn0Q+05?h|Tp?XdMUJ0eXsyNjLT~75wP=JLK{uTi38s2l8+W~MJf;#^w z7epn?vH*(1t&hT+2<6IOXt6x{(TBJNesQR}0=NDWyZdXz&RyVnzjJe7suFXmtw^OH zQIei$txWW{&iQyD?)S7KEXEBM`*T{hWx*RgC=~o^5G;Qk7GODs%3b7CKSAeW&g+T+ zKMA_Tw}W7$8A`Jt*Ki`$@IdP4FqK!w1My(VXaaFhBI8?P#&gxP=i~E9na2$#KLI;7 zo$mlOXvaNzAjlz>f`5l#6c1+6S263>?FjT?q^=unBW?)1Wn4nEXOyhQFDaN9|6W+- zaK-MX5S&I@6#Zn7x(VtTy=K;`Ei@M}%qj!M0+{~+4CJ?QGAh}KiiS?8n6x`%`1H*v zD66)-VXZnFl>&5_a#uOJ^z>aP(B&2!b{WGu*irbx4Osx(t8T*;_nqB{G-B>m|FsT5 zZzE{#0_&DbfYQ#VD4v>)eJi~`tXm&Mps(4_bWv+sltuVDfLezY$=8~rC03fnc8!{Mp}q#ED#Oo)6rEaYMlG zZ=?k|n<9G%6{83hzXdiK71)>Yq2gWy`cU!shlGk|iHb*Xvdf2x@st*+P+7G)j#|r> z_(8sLYT#-0`#wCib@=hrfq4xT){*pQe++E0QQ!N4j6oRc{g_(Ztk|MqoX;;Y&|X2U zgo8)z5?RDXrchL7dyNx|4_?}8oW2<-zYzsV^vz&&6Z%kUjXKe%=rJt=-!`z}ElGl%^4DoK-DE&ZZ53Mp~Dl>wiQq&`6Pq6#6GQK11=kp8rxn zApvBRLw8|R39#I)inj`H+I^MyE7y3%wvyNS8Yf(|*Dv~9$9%$!TG$ik|BO3gq|PQ3 zd?8tiuCaPM5FQU!W1$|-x~|1Q$8Z)O)>9zH=-4f)! z3$5MN^%KGbS^elLoMQ9eT*A+-}40S^WBEz-aUp;H$}kV->t4B|6h< zVNgR$CyT~lkf278@*=n+-dBd5F!n)Cs>vAf(xQTI$pzC;2!o0{zkw~qa5ylf2uqa4 z7O)K6nDy-?)-9xO*XbVi5{tcG2W`4o3CH(zytMZdg|F)HXXxYyc4Fys0@eqyKixH~ z5r0J`h>%okhzf~WBQa>@StD;_RQ0Zrdd!(&XA1s?_ z2+~r1O_#BL$?GfKnB!p)MxU(1c^UF}`XF7B2)3dJmSgJO7LuT09zf~!J%c&O#P_|r z5ok+8=do%a#E_F4guuOSg1(a1i*HbXF%k4By(;~kK3mvcB*V+`K3flWTHoS-Gb-;KgRNmWdpi&#Q19OaLuG9rz=Z1X1Yr>D{jla`( zG2RZbiP&aH4~HnXX9z{gm_ws%LR|-Qjzh`7syY*UG(J545+yWr7TgWg_QAff7hd!4 z+E1iE<0I+b3pWV><(DP}d-R81W9fV?{XH_L3NfZ~Iy^GIGjpy(OnKIj1PM&{cXh ze?NO{QnCDat+Uj%uE2HsBG+vmSD71JHhvZnWSPmx(Z3uUj6I_9-K)fyq~eTx&)Qpt zP81TDNSuhVL18i?UWZP06|By8gV-oMSPVD9mlWi>^T|*)k;^zsFGGHH{>EZ=LEd_! zTERn>5Jum;otH#eT*+CCOw4z!x;4I}q;P#=eooP9m(O%}P`fVU1d;QK3fu)bg#~xe z{p3Sq;Btl>o>^4tT8$3}yGk>1ZaE`xECWZyuW}cxgZZ!oMYD&%tPn(p;RCygh*f?f zeQf%A6u0&Y^w$-7S0)yhx~Q5oa+Ui0%6J#CyIpp7=;Q|HqJ{Z85?rDa3yg-f zS-Y5g+qO~ob_##l2q)h*@K*WbKgHPDe=*h{wx&tOw46T^Vd?5Y1pej(hG%{j`+rOR zX7X3E{}<#xM*e;5znlEW$=}NU+u?^T$d7j+2zrgkLmB7Ai{Fqh-|`t1uviD!;{%uk z0P^L}WPl&*0DBF9X%_&1eEBOGV6+af&j6S}0OZTx$^f5FmUXBz045Uv`SSNOz#BTi zegmNYLI992zl{N&)Bz3|0KEi2zI@B))QW97z!3u=lv+VPFr(gi@$E{yAIrk zpNIY40>l)E9K;rgJFo}qz*`Q&((yfI`eicA_rsaf-Hb0VyEqTp&!yeuLft~3g!2r9 zl{?Mpy!ZQkuy)iY2Rp~Khip!ffY8z*^QJE!-Ts4M2iJKKaxD_8Q5JnS*N>N>+DEv| zLs|6uK3FH<$Vws*ZYVtbFt{_9JvdKuo3vr|pp%AeHc(Q?h>Y}ArNxDX2|1+;@O5zv zMEL=k*@%oTyCu1}wBU~7B6m(2gPL>00{M06u~R(4gV|eC8Fc z3ZZ;7+%HQ2d#N0&KR(^PzR<-5QS2VXMwFE;a;^3h=9Gq&l{pG?+>vEv$;IxB(wtSd z1{Rjw8@Q%+pI#<15OyA1`o+Obf!luD5C6XWY^GRN%1EDS}yh;TTI%P{E|7Xh3C zV-OhTSVYT(9zthbA{~i2g{zEE>V)wK9p<3E0T?Z1=O7^5k(yJwx`6T$2$}%L;#gcz zR&Yxp?r608ML;zm{vuu+C&6}Ig(t3IqUV1 z$q0#Z$R#ozs}_RWLgEEk@rMC9g~g?MZqpDF4J4ETFGe`Eb~?g`IrOB|wJ=tb3fAe6HV#WED00a#Y6dY*T0` zB$1APmGn16gh~3O=71!$dQ%|;7%HMnG@_3YtgwDM^7qMw+zc&yk771rN@koC}A;%A%S49YBn^%03kP8gzF?=6~VSImjTPcH?tEm<_q$L%#zMB>1>kD z!_s*{I;wOmeVkybbdsf$FP*K@d4e4pvib}IvjjQq!>Oc4(?V&=p=R%>l~bZoUl|=C zX&3z?+E7vRZwjW(@n0_!hw-R*M)xlh$50rVN+;*ogcDEwDh$~Ha!zHV(=dndcH*h)cM27G z&J12CVzxR(%#LhPji-H)QsZ-RS zy-;k&Q{9VpBA?t}A^)Am8isRU)PuW#e5)hg<1Xg0l348FMT>}W52B(tO7pvq6Ldf1rcI+ z5=7O~VxL$T6qqa+s~btHF-ZkwIaq*wh&GBD1pgz3x7v$-82B7<0?mq+$eUNR81l1` z^}rS!JD!G##S-shSb%66kKscig+^|ER~qDc|MC$kroWXYzzS&emAF zUQro3Gr^Syx$rokO8`la89GK-b!cM_*9l9T_Dlcd2jo|d6vG23O(z-c+5vE*#P9$b z3Kbz4Xc{KRyQH&EIxXxFg<;P57QAOn1~ID%#ihBfQa|%K4AB!29d=9Ejm1SS%PnQ; zpqN5eB)#0>qOL($62$c4)zP<k(ib zJ`cnNl)6fji;Hh9KQBoHJ#t>+`5(r9koueHC+y$+=*ez>qDYL9V9`hEEc4O|UDT}O zmq6?B{QJSLytAlzvEpmv~B+6y?%F&OfR%O%e19 z4ko2WTUfjZC10dYLjb3t039Fl7&*e1M|^2%&UzywNch?m#QO^-U$EGNzaf!~e?R7l zcy}Sfk_XI}u7zxoSC*PIFEPKMFgJoMV95H78Dh% z1@7oIr<`8{a@|RJMr33Z6uMH2b7c)F+Z_mxcALdY_mE;`8f zSh}kSBRD)G?UpsJRqmiLS=(p{B|Uu1eVYccCODgk7G)wS9e&r72a6zz*Ao-gk^yRo zk-04NxJuWbsX$9&P7&8AJOPt|E2l^vs>krD{f^RTnf0Zl@(uwwBzV@*2&lmrdx#FAa!>&( zcQ)cIkp1&PAc-Eg-r|uVsb#CfGM1z+aIFstmkQimUt=f7C$fBU2hvhI=odVaLMa*W zN-Z@~0bm!E5t)(fDlFmrgF-}jV)5Fw&wto17SvPIm?P}FH$hCU#ZN(!XdmMakuGLXhZ zSXyCjl5<`-s^?nmDivWjx^6f7iLhw?q~~7bZQvy&kmNXC&kG;~t>*;aaiqf>$#b)B zuuw+F5D`|gmWDGNVk;s`*4oQU3JX>Z9K?=0uMA&_IA`)n|;@P<1Q3k#$Ygb5u%7KdbPuxxNkN&PX*k>biDO$jxfR3D)Z zOzXOz6dzE9=<0fcAf-pRBca&sE?x_uLurX(B7!0uX+^Tb5J<&Mfiv8Zp6_zG)AHsP z=9IBUDpc6f+$1ar8{m@k|0G;uEZ@^z$e$Su?kLS!>$+O(Lh}akh7*B9KW|744hE3k z+F%@jBBTt`76hgmq%F8y>;)Q!bGi0%H_d3dSQ5|{68i%Z2cycwSB@m&D=ov6SEet? zMuOnL1QyBtVJPU@s$iyraC2LUP^eO2rllHU0+%ne9@LGeC9q{^JAUDMUsA82BejVo zJ_wEv5~0+ydbj$b^-O@V5cD&}l5W9BOua|H5J>OT>w1^_;%SYfU#JL^G@$pjR_-so zk^qK*D>ik0xCpjesGCCOqVM1oNT*slk4mReIv=n@Rnf^yn;0m#LkPAQ^j42-joTW!&7|kWqdEIdhdww7!?^lW{u`Mlx>YW7$3# zcl4v#Vi%qmgqip93>mllkB}9ym)9Jroo_svl?#mYSmgQnH@Y@r-T%2gn z79B`?2yKFa1%5Pf(ME0?djANx=^nVfa6{39h2SE08U%@pbWCJICOaqVU)K8;L-Zp zs_$6K#T4Pg^>E(~HzkNcM3f`LRqpl$3ot^X5j=u=O)gxJ309#5BHZza8L3kP%Jvf| z5#j`nOwU>8$~DQdNXc_07X|5F-)=_mbg(K@5PUXP==eM=SA#e#9cVg$&H<2A9t;K5 z3WB;8P{4@fwvjRkoU09C!;LWJ)Ai~k>k&z=9Djv&fc<05=KBSMKm zAzNx}zYx3}4SGI15I)ke$ORD>lZPoQW44Qwi6HCK=%cK5A<1ZrEA%Dg$G|K}N3aL+ zrshBCxxb+n#`RYAXNmjS?bllL@QcS}i$&rkZ7p1a`#t-=(LUAv=Z%%&@o*Pm#^0#> zpJRWMCdSG5i*@%_-Mv?Lo8ivKsqH>?mx@{ACHzfr=Zj_HZg#W9Nj-cdC}%#d0Z(A} zD{VP$46uqV;QlTZ*N8hsmDmlwY-G^5pou{j1cgBq7%4^tjTe)Gr-+fk>0&arHyy$& z&I!6CeIJYV_%B(fb7^pgwRlcuMeW$CLjhxE`cU{&Tk2iuXG5zlwBt>hZMWN%<;UEDr*A zho=fp3!Zksx50JSqh8=>M&sES39c<_xyS(LRQ~sDG5ZO`;i-bZy$#yYUVWu2}m?e z_ywdv%JXFAz#`=6f!qxG$1hrhSiUo%T>iwcB+uHlm^w)bPn_~#C44|G!6Hr&QyRq@ zF;v>=oF@ykK>rf}$^~Ex7gYifm2k000Y-V#V%7rE9JuY=-MT31p=}RKypMGz`JP7F$kbv;2umQx7JfIaj1)GqJuuVC z#f*+&%ub)NjSjH`;*Zr>{^QGdH8;QqG?dr3Aec)^c30MjFn=i&Rm(;AA{R8DWoJg{ z91-&mP%r6f!OgF76%sRkhO%^pg-LZcw)+uv9nFfb+>$NHA3I z4MC5SMK2Ktv2tfDWh%>!5d_1MLWn*$4XREG3zyMLa$xmByBH9?jG7D_uu?}ejKQ!H zQkMn?bm<&X_e!>~mbu(Fo478cjstp&ryiJW=$sH)q>{Q}B=g`Hqu@V1tkXBr532f= zteY4q`Eh1gzb66fKA=VP8FnrQPM6MN>D(@zUrOg0>9k2FXaXmgD4itf6i8>Qbe@z> zvvf{NXZ(4b&L*8)>C{N)ap|;3=PPzX9>8JC&9mkBkBVVq`{s@lIDVXuS>o zGO4oQI8Ab1ohMbCkTn{RVVIQAOT1|!6sn;tJf^B6oAAV(652x|rhOywZOMZ{1&k%| z(I=<_Mo%D8BK@iuT^XTOzU1!! zsxu63NGqVLCN!ug%*7x&%A~8HrDXb${S&UQF1lJlgGOsWKEriAgVaRmqNKJQQ;rG8 zXjTMIJ~Vk#yCBMG^aR30`ei#(K|rw6=rIAI;*li!Nl8yvWoiAy>-s4krA3)^Tf|L4 zkkx1!49qIZG$HD0Ij!o&flwm?^>kw6`8>9WH262|Lh0|mK)RDAO5JTfrm_F4yM<)( zzp3sPn?zy?`ikV-4Bg$JyASJblkRrGodh^=^=y2gvyEi40E1grx%yYcDNGQ)vnQ7_ zPlbuBY*mh*bqTTz0GrHUp*KR~cE+rxFpP!Ouwe4e2&d;o8q_nw%o25t;0vHhz1i6N zg*qriP_+heo4Sk>#NrL8o@hQ?qJoA44Y5+w4v?l;^y|QMy_h!M1VnTAIAQezG0!7{w)oIGz|3cBkdb@5X0l4k1Z}OqXPF}I_=p3lQ9yE*RD^7{Vx2Y3 zInYK}7zm~g^{I*CHh}B`NU~|AIagQ=owKgm*ZQ9K&u?UzGC}C8D-+(C*5LCDC|1QE zYJpNb2-SvFqPa%r8&IqjKGQ;c0YpN%5Yxomu^g6#PovxY&99s&qNSQjs*4G155P7K z0J~5`NiZ(%Oso1N-DsH52tA%=GxZxG{2~!EP_d_L^|WaNKqrea13|f?S8hoo4nnKD zF908v7o&pA=aTxCJ9{)bJLU_gn286!O2XbHsGz%WMiNzUEaFRWd72rkmx>XYWiFC^ zQ*(+SQ-AOl;H#!o6EUEL`tUx(qTc)OV1y7BC;Gp8=_yAwp?r z5?+Sl;ARRF2iLEpK^Z5s+2a?QkyrU+5$?>%&4te8Og!Gzf_XeD1-fO89uxK}uMr~x zmCgFLkv4MxuXrs0*#yeiN1_}{fx?|8qM)3w6C(mQ#R%8+C!ll$Yo-4x;a1KP!vnUl zj+ZVImH{AHq^=}jf9dpe*1rY z=TO7$p>H@+3vU3$`t0Bc4Wq$YPB5{T1O#K7bUyMz07G%I@xsK(63{Qvb~gscY|}iS z0JQ`Z0m+zV2#o`oT{`}ghm<-JArLq}a16zyD;K=m#CqW5iRd z?IJv*po9jp*$~#i3jity8p2YI3s6!E=I$vlk|!AY^zu&>Z@vav_;^`<78xuB_Pfx->Bzm7>QCy$JIU+tq{hkGTcuf(&SQ z&yhf;ARl_nAQ{x%Nw*YoI|&Hfn?{crC{slEHd6^E8S(U(VHQ(M{ae5(ze^JnFUs5V`?01pxGzfwBysv~{l1J-@}(OTZUB<^q>85SC@i>DZ%bq6Yx=nT!2oPpK;uWMxB@=`q8s z5W@+^P3rr3MR~=9sw|+19&@E`f>~n`X~p$Ta?J?I|vltPa=hm}+t zC^eFbZ!gM4)18Q?#|)E0O_z#~vNFmD-=5IuF$3K~6bzuulKMSq;#xGC9y7!$I!C}7 zauze7ugS#>(+oV*V}{J7QN@r{Ev6IXl{^5EE}|2LveVu+f!P2;j~NAdKCl!QtRN<% zcC9Fn9&?$iePyN=!&U+{YbS^SQ2B%|E;F-a`{O_t3ZutdV1chd-U)C&0fYgB9y8b) z5u<0tVAN6qT?sIH%%Hc@Fy&GiG_!=-x)NPYkGTxozWL_f~%#I zEuA~0^N@5Jq|+gt@Jl$skEC;>bjqZ2zjXFX=UwT9T`ChuXNhz+NatbcydWJ_I>V=M zf-9txE}eDKc|bbPO6Oha44cXcE|tz)>8zE`z0x@>oxe(F)McE&CY?3Xc}P0Pr1MYd zSYl=6rL#ghKatMwr1PqDK9kPb(>TE#>8z4YwR9en&g;_omvqj%Tqcmt&C>BoXRmZ# zl1{I5&bfjU#7ie%I`>HDDe1f|ozNdK^vSkk)Mw(KS@(256 zAl~VXl9@M;5KS8)wK4{@ZnZLmBHT~|uo+A5n^^2HCg};xFN#K+E*uj(nLA{u!;SLz zkfClHsKGwaoEutUF^mPleN33?6Gq^J|7z*q`r~ZzD?D%DX@Tth-|A|1Ce3FMYf6WbiO(7!6Nif6TI7l?$)}{#%1%h4^bzG^SPZ3U#2$!W(>Rp& zj}8F^?!XPDT_N=o_GNt9l??0xnSVRsr?8*s;alOx0N~&MPATOIQ8dOPj{=4}3@ZSA z|C`MEboJMb970mS+BPIapehLSF0ed0(wXQfEh{cX<{KI7z#ZHXkr-ooIGyw1 z9}#LUBIy^*8zj;iAe8*W=HbXod9bJqkEae1!FmUgiHQ&`NER~EASB&_Dx;V?gW{VJ zAF(#~Mi&}d<_^OfEN>8?H_A_50CvyvkB>um-yfeXUV{7d^)md*Ik5Uep8wAMYb9qd z|F7;}YZ)ik6?qPUnnl{pWVrH&3tBe*3=#=^VKB>wMjH*GkR*h_?tQJxu#TrRFt3;9 z_y`AFKTw*2;20x#!7V8{>x(@woZ^A&MBHF|^<&tn&p-^U@3|*+;4B2e0G)%38w1EB znaH22CU5_tkd->5VJ%2Cr0`r0r!#&~W_b#^<`kZf@Cal6AYj^BDn#JmV-z%7mmtQl zQ}^4PAQ|yK*!l++B&$Pp(sL_E@F+)0PMJGkZwj%L<3_}eF~|B09d&?807RpWut0L< z>e*7ws}LUJAgg#iE(JHUAq}a@s}YCO3eXNxx9NdfIgnJ#roe3+NV;27Ae8keQnvy$ zh40{SeJ@83r)yVsA{-5;9kmqKI#(eI+<9l3cnHBTOhyfGU6FB8}upk7dO%DcS(x^N?Y4PMQhcz zRDB_WKhebiN)^#6DphPjZ&<8RLx_@kzvnqKce9&B+kWqdZsy)IXa3HdIdkUBnYmnz zJ&uTea7j;~N9-&eF*XCEZw!O2jzx1Y*GYkjA9S-hgr5gk&KGb?B*s7#KlO;|9lI1_ z5;piVL_|J9tl@3oXQ1C*8Nk|ve;d44n z!cZstN*&&z!``s1DxZbR3=YLkYQ<(C{(wvCIR*tlXIIpOWa6Zl1Fgon4}zUUnMkid zm!|`6u8|Gg9{MhbA4HvEY(5{(xq7}Dvv}ccatTMWL4Oc@CpT#%0SHFSsG%`otd~>v_h&^U94h*evKu2*&%~4j5r<8c=TZqGM`I4gi~i zg4^l|-KDqHojEKCJ1j@DQH&(!G>C+1V*7Uq#|hEX;J~N`O{{($da*-^k~=hAhfYbj zAZssb&=WOim$Yc>mGq(gca^xpC`Z#-d)-e?@BF)jml-uk*bNCOC)#}wewbJo0Gxxn zIb)C$wDKBnpSKM-V<%luDC%9A3RbG%F%|4pL5m7b8ZDS3Rd9<6?pHyb3O-UnYLQ?L zR>4FSRI1=ND%hohFI12kGvIx0+yLw-DPGyHCDUx#^^5boZOr<+QYlVO4GUNquP_ZL`v64 zvSG;j&+oGSvJ4rimmzGx&@Rz>akWCU_7+U;5;2ya(}d++qV;g{(`{JWC00)dOB$io zHoLwP8+jZ(jwn##>^+@P9IcKhQ0eUbolzVek0?;^>_eSUdc|jR^ghp~! zAFR>fE^8FGZ1$nyod-kWQj}`WQx|kq?OuBQr-v9zyrjz#)k<3{#5R=T4ud|cOAT48 z$2L^t4)}c>{C0g=>%LvICPS{LgoB8q4C5##q&@Ebm^}A39+{21D5o9~ss7uzSAu|> zb?tK`bm%rjW9==wuPi!UBnkdd&0+KTFDaE#ywr(O&P@nhcVv+S3=iKuQ*KnJLDcfw zm^==L2fXe1$|X-u=Dg&o6$Q7~gm2X7ja5kJ={Z?*bHa5p{u^(ZP*U9aI+^*sM`ATq zVC+Ux$%FOjO=#EG$rw#6!#laC^5)s%Vy;W+cxQ{pRn$F(BW9<`4~{f093NLo;^izJ z#=DB74wHd~GN9Nf4NH~ba%qLjJ$A}9qveFm-EsN3T(qvobh7o#BW?0H+jgmuJU2i%2O+h5Nslzues;&M2ykWDpo@&k{eb(DlP3<`x$m0A;Jq zDWb5@3`$+W71XaBuaKs2sl~li*%eEacqV7vHU=}RY;5bn)I0= z++^l*^>)rF!3?Mlw>dRofd&u0>~Km1z$HgvoHXo=gUqw5H zxCSUOiV~QE*zm@~$6j@Hl30Rd)b{C+N@B5oFg!;gNWGwKd|GA(4NcnFex%4V`oeOf z4^TVg^*z|Uvc`)6G-~dwSkrZ*aW0XueGn^ob%pRa3Oh;e(vPp1vSXTz2hrBV4B7_7t2F zhQ9dU*bYZu{J*mu?n0aS?FMB#q)*i-g#a8}fFNxQXi0@;E^mjRr!!$SwlZ5X!DdMy+KgPQ=(Oiq zzqx1*^6(RyFlNqfZ|>NH156|lI}%-dctfvf8iaIv4Mbj zMK`V<5gz2g`rf2zvjZ^ni)@T5FE-xU!E9a?>`8$I3seql!!E$m^(8U9%TpK(WZMI{ zb9s!W1*ObhfP0Qn*Neu;$-Lh|1nvXD(FFR9aqm)y7grYBAA4fW5Tj^HO%9F;&8tP|)<{;9!;M)@w-$$w=_E2w?3R5!HK)Py^M98xf2= zg2+>G`&-~PP*|}U?%R~{h+feQM#Lx(`^4k|uK2_!WAFt66i?C|MQlc#eO~H-I7fWp z78qR6I}0L9-&aES)n+8h!hIz2VHKxdjs^Uk%SO|$3Aw83EL>1jIs4{-+@r)eS|xDR zhg8*}EDAhIjT2Gnh>EF{fcv0EiIF$hpzb0gFrg>S#`#urAw3ln^#Bj7CvF#Y%7FW2#sn*s-&u~1#mN}QP~a_-4);i z(DH%+vum{EjqN*-8EOz0w*k#dR#pMzT&2{0$%?SfYRO3$M$awn*U_v+T;DPLIKbqE zr5L!*CwNc^^5i zt1${8cQxiBYW7tq%9< z&{MABN9%Bz4j<9sF&$nsL*dQQ;c6Yepu@vD?0vn$yHbaXb@+%5_vtX}28B0VhqvnR zr#fuV;TJkQf2P8prNa$6d{2kzP`*gVLLDyFVQ-xHq7JCF?aK^mNS(HH_5k|_w{)nN zb>7lRNX9!onrC54#~BlpWz-(IrIX}Iz|BzmF5Q(S5;qQWX??lzRK@q}wN6g1#GI0h zVMZO2{eYtTy(zJzL(24#vu4R)pmcX`FX&KTeT6{U$QU9kJlNPZ7U@TEDTzyi7sIsl zy=9Fw+AhVulpb&}6Y|n4xz@5P`gCzi<#|TVP1uJ}_eI2$wi7`n8yORc3B#dZu10n$ zm7GXA!}kfV%IN8cheCBXD8@0@VBK-$(`?L!>H{8wjn+IgRpqH9w3BP6^7RM5MlX!x zJId7WO)$c^`c`bu=;?;+vygj%kT;ze$HOz2*aK5Y?1Ud1Ig_zUQ+0|>Ku%d5`;le@ zBv-d2Gx_;O>$NCbCD*fzlekmM#v7T}VIPu*+4Uv8PBlA{mO6!8IU`s3LiF*%UF^?C9COpFfV$eO1^M|7ZWr_s^H4I#sgfj;b5Qpu!Z;cTpuw=+6QBcp~^6kLJ>WH$Cv zBWHfOk;T`NJ8?i30~<$Brr-{n0I7|C+@nqqXuSHU6$Pegbd_H*SgC>pFQM zjrKo4!LegNK}VtZ-b@D@doITS6^%%I!`o#`r>cVIf!kAX$6$6|Qt<=Gml%VG;x;XH z2|bzxtyM2hV%xEu3b@F~L7LvNH2*fig~maQLNBi~f*DNr{`co!Xk3E)aaj-UZ<-ww z$qsEgj(Hk(T{y@bLu4PrOQBe{$VDS_l6Gpzg+`SsNzndnJAP!$pHyxnW6|@yISw@j zm4WyiMgLY>iP2Teu1UtZ#;Ou{jKIn2=Usv4Z;QO>{_;5Y8*hati)t+dEn{N?@grTSYje| zyd7(+?@gvZMuKGVn(y~^!BpdM%t>jQk4G)UnJ-QV>4v+5)X3<=X~NALNx_tS!#ziZ z>QFi+4JPtn6%V_uXh`twgQC_gL8GG;uCs%{Wk!Au!$e1md7ptID1%4H*S3$iB4yho0jwHBL1(Q{h+E3EB6=yCiPmkhP-1{L%gTz0Q5s`jkJjDZn5O|R{% z_Wl_ORRjv|(nAZB?bEJ8IdNqK;hWBAF@}-_VAizxOT_6(oS2=HcrbGE-i;1;l9w1T z*`ccu&tDgLIVJ|#`Cbbg{u1$eu}r~}47CZ-UOTR117f!Tz!=a4Tl7k_4rmK^7lcSdt1jk^>z?rW-<~Dw!SXPg)h5f{L^+kIkp-{4Rj)00D&YL z1D@nwlaa`6Ln1ZE5RUnu3~>D}fpuKwUMT>0N_iznLiK(G&L(wDD06RVJc)9gCx09%@r$r&E#WGY3=EhxS02={lVbMXMDezXDAp82LJ# zGzA6YPfBYeXQxxi@GOh7dTNuWozdxF7+fa0uz_j=sNKI4K2Dkg-(hOS{n|v?>9DSC zr(U~&>uYW4l+(e9Gz0<#FdJKdviR$afZ?w#u5!vg5rJoO)dn>)nTZVeOT+RulB0M0fSMcy&Q?ZCL%#pj_aQgoLn7Qu{h{werZ2!ZBJum*ejjqx;_`%V zXZ+CjA!q#p-?)4X7c1g>kWHwgd>?W%p8r35A5!%J2<`s~j$;er_hKb7z61$UG z_5iZ|0kbUP$S2+Yeg0%GI<|V_w()7?OFDmnsIe1OfDCoJZM40^Na`M|J7qhlVDpza zY(4z=Ajz@^8OHys1Ua)8PB#LJrW+=HHhw-J?AcS6aGzk^%}|T+JAfa*1@jJm*)t1_ z;rJZ_ISb0B8q23nHJ-i-z%vlPA9#DOpK7eS zVXAR8aB}ec{Hk)JVg+suzq8ypglF}yr(w)IrA)q-q`vCOYXh9UF?PgY;KhIMQ*D|s z%@D+7s4s!ehouYOPgMJ>jJpVN*y_etLQ5ApaMJH$z`gRpC-u2umIhv01vB9#i1Li# zHmDfJWfeFSFdR!-Jo|RFSXD8^9*!6=seGVZL0N2p;IlV6HUk@Z$cZs3O?xxOA|^w{ zbZW*p#HCAIVl&1A-bd{ycKE0%xT`NZ{c0>=uR_SZo-Evc+heog!oQ@Xq>b1t|1Fp> zEh)>D=}N)K$x3^W*xGVq{sZMkaCNz{<3X?j-Us2A zf!~l{m*cas<;GMzS0dhx=Srkogde=4gg7t@#5j1UFiNNKM&@rHZyYdiqly1cD6Uxt zgLU2rNl1upG|2DWx+<9iW9A!WKxucV!3zqKf18o6*F|z2W2~|lOr|10KUtV4;-Lz{ zkE9G#sg;`)RbT##9V4tsEffE#Bz&h?IFpwYd5Ga*uIei*^uI_K7~hrWg{su(#qbXr zH0(dB)Ub=3s?ZVgDYT}&nS5i87V>lc@wxoY>BhGR4+j*yP2ZoE0i;8c?h@h}%~`{7a65ZL_@^uabgu0NSF*IyCu%M--yS%~c} z2mJKJz~xdlfH98?o^Z(lpoe^iM`>_6>p==EJ$m)R*caX?U(&{~X5xPgWTzRa0;S^t z+d=Wr6B5ObYXp?+&=Hgo*ddzg5|1hcodNzXn(k! zo7BXpMlc{A1N9@4^R;e`b}!J7e|+j@s6SZR(zglVxCwMs4Co6isn8kJ0+^v3p2jV3 z_+7{S6mbK`Ou)$_sTx?gLya}w&w!Veh=-DOU$Zf*fsmPipqq>gNLs9lAsazKMFO3Y zdzI@E1CIlI=R_Qn+6D6lMD(U$UV>wgG5YEXZ9*i@LgF^~oBd9Rrp5K@I&+guzhbDTw7xLUt;0X&1817)=neV!_heFXtE>&7Fm8LPCBR zpys#O1SqVSeeKHc@og&`fl=~Di zw1~}Oerycd&BXKs)ZZ&}I*BQ3qMojp?i~4qZGFzZ0||KoR*p$wfkSBH4w;=sn0n z$k;wqoGe_4gyYs`!Ry$5P*iW1gnER1Z!$yXt#1F(NZ>5j`EG~qM$+Ff7SQUXr4pQ@ zf)W)}s^E_*IG}-8O%TNCSRAmNOOPbQhM)zVWHFbR;&!zRh1^ z8JY3etQ4gK(II&4T^l-5tCtweVv?AMn3B3$yUSo`pQGS-hR%rp*9wuAxqQ0uW6A!ila0^3!zg1g@>DnXryP}_VxV@@o3UK;sg;NF+GJ8HAd-|TIh8!_YlBoj zB&kY9l4I{slWRR9q1=ofSK?o|s8ik`5$4bfEpS_w*}_QnHAk7hY^{E>d-d;t4lz60k;KcRy!0{KKOcp z#pGTG(%>i%GEwElv;8;2^Fn8iFJm&>WMdx01y{|#MMy1fH^T8a(tHx6b1FXW*3T7! z>>Y!{&$KmdFg6;Su#SGzcmyX_{s^2LEaL{JoS)Li1rN2)%8i*vuujJBAbw+$XBcg1 zGmPQ*E$=?VSP$4!5GD1!W*DpRyCh?VanL)%sOvq$m#vk13L8JOCw)nE(#qTe`fBCcn6hf1yZf{TK(6wXB8#4vq@)v9xQA z(dfH`F3Er!!qupEdzUExfo>xcicB|>Mmhz;4{s!jOETDA*flzrLn8Rm=tR3lvXS&4 zQXu8iDtJQ$K-2B$Vup2%)-CQTZh|II7jfFFZ?&0q;Immq{yp@H4)ol}mtbXq>)3$$ zh|l`^;lp;stl#LnVU~gL5gnG4nID)<)h+%J^NyBT3pO6Mt3LFnx9ms?4DN~Q8__g8 z`?Jr&#}REGQA&U^@A7$u^Iw_u+^-MY*H1^DC1q14)}-e!Lt>r*OtTf)d5-S{$qoN0 zRUdk**MI~Nm~P`oJ4Z0^R<8sE`J#TIR@Z>~f|}7eNoCfp@$WA0meDz`G(`A4^MHI8 zibTe}SH8(Crm(c_8xGr1@5;|?`-ClkCjjtnap#(1h9oL#P(2BMA+Edfq;Hw@%w)7|!#)zZwe5@=TA~Dt_^GQ9H7_)I3^Z z&hX@)IvF(fUshuM?w5z{>U#g+6024{omoeIw!VJzkZoqo0#-O9Jk|XOpXM3BUk@7W zvZv2@#bcl@$S<^`8B{g1m}9uKRVONKeuRL$dP%u`QPp%jmsZ?XJ>8gP51AFtZb+Gl zco%^e#(*s0*v}5zKI;~gvr&pKYRjv?ZrZe2;f(0Xjs1@vQebNoYuU5ukZqOYkAqU- zM_wvx!z^l+!p)*qsY|5sr~f=;M?W!(+CWM3@C@Mx;jDAakIX{1&szBlp+=8+w!Ch8 z2<(w(Ml2VoQQZ=FUw9~yh7*Lm`Rmyo{(=&#Ol1Mm4p&cu=N&Dv>d=3Wdjh>fBc`I& zM&P9IseFifLnF!skQzRf4-RQd9O@AZL}c9dXVErN=AePbsUw+7npZ#{q%^mp(s7{nr-efPtKp=38d!jYZ&QvnXgFKB7P!! zHINQ&3B7687Pa|GtZ&gN`KJXktdXceeo-3|*$rcmXeS6djVw_T+Rp!18yEy$bBC{D{k=GtvmO!AzI?6ZYjCnvofLe4*`jAl(jltbbNUZMMp+x0cr|K#lmcBXg}v32 zyQQ#?dP-_3bg3s#OJS0F%4jKctEcQ(mC%0j6texw+|RL_5q1W2ZE2g?V1;h9Z45A9 zZBeWFwzVP|1E{F=&#vpNcjvN)zI+D2b4muDUY4hHJT1XFTkqCl&zx|f zC)(Y77oeunBQ{yR#M~cT*53#W25d8=7xFp-V$a&~Ni%8N18t31c}O2kGfSB3Ie4C~ z=MW^FYxt*mw-nws*tIxqaFRduyIuZ*XmN1aZOQ&Dz?YnvGSI{apSEOWBN(<9xPe=I>peWWEAmcvKrISU44ru2Id?XGBN~&T-&5sVd4Vk@Auj zyLCw}r=%KH(yH$(X>xK&dB>8KMy~}*OF`w6d9dNcMFm&)ydLg$v8)n z{$Rat%_d06B2TdLgb|o358oR8v+&@=cRqk|zaLS#Q=5-}7(Gv8 z2hPyg0fp`FE!bDZVVe=K<_ykH?1!2O-`13#0GT0D*sk|)QSF+Gx$pPr1p`eB9lTq* zpiL-|T{>r57G_Lq42xhLBw^*7&35vBGZJNdrZK0^Wmq{082vPcCahuZb?<0lxY9)Nu! znw|$04JiE6#6L%?ZSPMXMWd|1eIQhkWzMk7uX6)OkzM|x>?OUVJr*?P- zA~+;iUDx7X{?q5!yZq<#PkwYfaee8z95!v%N{rX&$&sdWlgX43@E`UfJTv&>SoXHqK5 z*hc_(8H|1^yf5((m+%whuM<8&yxh2iw5df;p;TegS5LA1^B{>a+n29m@{Rwj%uu{OIg*i%p$oCEM_YM07 zH53ds8VZ1h5my5=uEmD=2K#&0_y$8$NP}F##q)1~UDRB?ANfJ|z?-GYr zE35B$i-E zLV~LH1Whwpr3FZ!sxruWcfeP60(MP1_A7#20PNV@c>pp$Hhb1$M!@7zZ?q@E|$Za)K!%eathoI0kY54BJu%WD`#DYY%d4*$HM-Qgiki?otSt}tyjvq^ z96O`s2DW_G0n(NND%IKyAs`Yd)jA9|uXWdpo{bp@ot-)-JQc-rR`d5kE-@9{>^CPo z%A0rkUJh|nmw;2hpunnm8ucN#;)jooyvRdCI8GVqJ z)GR5(!rA%?h+OZ~-pgXKfHv{JVP@v!?GC3|zSj=fwfC~AaXTTVvP4+UmHbL?pZvbRBc z!z&ektjxDQF_^jv$=#YrD8l~$sy3%_9a|33+DNBpl-eV#{RtGB=7=b>) zI(%7@Myg;PK3a)~$T=inVLJrhvnYEmvMCen!p~!nTtHMMVyWt zz$!@BWW9LV5pd_WE3p-0J$O(s4@o6tItsGB)&pOGgPlchH?)ljSMK6^_r!--?>5EH zRZu49ti00g3e0YBxmw1gyZo8v7W2ut}ycDaOjQ+_$XI zH)w8|CwC385Jh&;Q5B5pl#`O#YC$i3c;{c6?UvLe|G6r$wGJE)Ryai!W8JG>dw92` z>_SV9@6Ap{7AVDljvU&)flqh$)5w27!*w>l#iQ`uur(Yv{_|@j#F*@k{;Y>^kLn^Y zow?X)Qg^&Gqu%5>Q;2DL?1`R;8I*>}Gsm{pZR?(8@!slWa56h}1%ydey|?OXK*?8A zei6r44MAd4(NVcsCp8OGoh=Y{+@D=2{tt?Q6b|K(^-Dma)K9OBLImp%k`)*3Q|=ytkN-UHkH%CXy4;5|4FpPR8*sJz_Fgc zjsvw9)agj$CKy7Z4UjXKBdhoMPih%8&=u$nZNwGmfxn(WiZubrBFer3PHwQ)J=bgp zqb{ti`$x|;@6}Rm2P!ZOQf(cLDKY3Sf%aC=b{RIMG_>QO*eKenTf9}GxxNQ`1-faG z;s`Ek&+X4~C zC2z{oFY%IG><$;Fo6ktfi1m?^Id$v?D+0k5+L7(pA|WA_oW;s=WJ@8sFH^OLDv_cg zpR|=CaGGu`q_K75m(CcXaJDAND_evFMl31SJUUx^_}6PHiFe=uxkeh_hRrSd1prY0IBaW*PQ=EAY$Dttg>q+s zV`0&WxIuK6%8D9J&1xo&G*&UYC@_^!p&AtM$}77?v?34l*?@e+5{dSPmU@gpf3t=( z4BiR7DP1MD9%_PSYW@6@V|K*Bkz6zmpRFZ=vyM3EQ;d_N;mjQ9FG_YVBCD@`vDwaV zfHkZMFFWyKJ}Ep>nvuVEh1+b6CYj$(sU4kz9w|*f{((+fkbZo>`G$32ITZ8ZIko;A z_oMg+odKC7wKap8GKHDMjsu&DV+f$dN*3g$6$gJECl$>sUU6vU?hpg(sicXqZQyTU zbG7OPl!k@)&H^`e1^CnKZOu zHkYY6pE3~JG01T%)MHI@~UBF9E+y zut1_srO>@Hw|#0`*S-_CkyeMIkWX*}3;&J<7$`2%IR0&P=%yR4_UZ8%TycUbbj_;?Ds)ks zTHK@j0>e&u2U|sferC^^@FGwf1RFR1rVP&P^=+~ihk0(hR5p;NOt`FTONcGY_V+O( zb6$MPw%&jFkR5!4u#aOp(f*ACAHk)Wg)>E!JU*ZD4dd#(+YDnH^T6{3bRM&-ga?Ec!=nX8*0j9 zcxvX(f{gx~v9}p|8&OHf{?Kyxvkd={%6>ob8=&G(|6Mbrc1kv?T@%^^XhY~POwfRh zC6v+-dIv#rL+B9SWT(t5@P$LIc$ER;Nx^_gh4G7(ejdaKU!SvcO!g3OInvg%!y7{Q zh&9xvzTv&$5dDf8LSeum0m7kY@gNjxg$Yw03U@Ty@^BNTXFjm?13@}im@wB)1_!^b zHthaY3EOArDda$7Ji6dX_(j6ZBKH6Gy_mB(Xq=sxv#4M0l)-cgq?6uCnGTe=^%gfL zIcq^bjbq776n`WiyW`wvG=o(rC~r3im~=k$d3N%hzdmAzw!7J6@OX$1VonKe-^cd* zGZX{41Kog}Jri?6B9DzmBW1r{bF=e!SunW~esGR<&BUHK= zY|I?B#Hc)Fq~u{e08&D{I-{`_)rM?Qr2Xg)!AT*PZ~>105!oyY$8Wb&O0=br;~*-y z$U$6_#C@dWR-x@&!{dL%9hJCObliH0vrJfU5obx(Xw2UY_7kGMk;_kR$_nK<}& z8uhJYs3kE$a5kD8M+QiZ18-1jPzc;5^Km5xDW?kR{E+)f^OzH=TZ&p3{&5u8cM9D^ zM$W)yQ=1uDj!1hAF8YXR7tzxj-*{cxJ?F(oZ0n;JWM)gTUcf%TmCjW-dL>9%kmOHO zw$QVCiAfPEb;q*rk+Mg!>|>qEu9C8Y@nxUXIEiIDOmHg1ytN($gH4gU_^3cJkgcZ4 z=w@)&m5rFkx9>m?8UBky%L)vCDs;h&z}cn%Ir13Ek?@RdWk_qVta=Yg-x~_XTy`t` zN`&Q1-(<7UPs!qu0l4QSs2L&llXdcLnW?%9^RKOt)>2n7--kxuYQB!e;ZCcn8wP9y zJAU2#V;eb)Td>RNt?q-T&`TC8=&hy^IR7rUx4Mlyh~3Jew_>;Q1N1wWVxJMntXBRA zw&M5kmcNw=11xx_bt|CxTbtVf7YhLXbPmS9&Lqa#c0k411k|@D$pwaaADI>=(QN3x zkDYS_*3soz=0gNNV10BJR`^_O{B3B|Ht!<~Ys}>_OadRUo^>#v0cQTz4FCo_p!a=z z%HP^Q_Rc3=35jQQLNCz^-PRQ#nZs99Wx&_@ z-+8OxolW^*t`Cw--(k<`;v$cKL}*#Mar?=7FDZ2)Ch}ckxO z(z=4%IkhAO7VUKB3EAAiwZX@+DVR@#ML7UC74osjs z%atfAd34S|MC9m**CpaC6_JgILCpHES264ND&hUIV=zfc{f}-Zja+$#zB|pkDwDJh zaYF`$dCcvkGqmT{{NvuLeSn6cC4UI}*?w~e-!yW7>^GS%|c-js~C?y zt8qShpBZFyH;gG|rPh82kPCpSLAHN9tG5<3@>z9=56d%Q>jkz+V0?~dLxDd3YIIjD zoZvP~!5rowd)+Ryo}wl&z`WIugRAXzyfO81XI%UI&=%&Y2MSH2jQgvPG&8FF(Myr= zrvOd6&RJJOhATgoDawr{=_yCc4{oRaX81>oguT*S=B~E=Iki~nbhF-ghBHGeJYB4A zMW1q#*p`iiR-Z$MY=2*KNdcS`Gg`*L-6XAL%&BnrQD(_FkC*O1k9oWlLdlIZezQ|l zUG96+<)wWM)PvQ)YG;-K&j;fU{9S*=lVR z=mWL(qAjzq@q?+e<{s+v%;+X$#Mb+fvGOcbMu(psjVpDTE_H;IdS9&6m`@G;VuoOD z6Q-zm{*UxqxoT1HOV_q$CT%c>+Ed&m6*Y(`sn`$~JrK{a=nA|r2QsQ3alixJvZ=ScG%22Cl#}|2a6cyNv^Ufvk>a z!|i+_p8d#^YvVK*K@p%7ZFs}}zzv8t$h3&{&!;F@SKp!q z>sC;TvaZc%Eq7pV0w#hG?o@v4LogoZ79|eL6c)sxzk8fGw6U`VMuBdN*;V{aHoo%{ zY|IRW#)nd$9V^=X$l!La=92wG0b6?+cxj%iB8ZCO^@Y)#7rj3@Ok+9dhgYk1pKJ^3 zRc}2I;lHKmi&+ChiG3Wb+eC>6vAvpRnb6V<+QP&{9je5$-n7x_&ck*U$a1Q8i!3bR z_~jX#>`1y{vM0tjuskwfT>{;F7|4OmIrBC2>s~W79Iy78q4=xguao!;6#jo3YsOnbuUQ<#{IAUV@gj6|#F~j%I#e|+#%n78*Aj5lXMGHSFSK2$ zzU%5+_G8m|N|`li9`FjwbH}77k8%6p)s{QPlbi|z4D*aaWz)WRYtc)T53)vVrM3+n zMtPr7->}@{kJE{d3?*Wn)6>OFkoniwWmVHk?lEN?f zJ(Okb0aDamd{tl%oF=uM6YUsQveZL* z%`m-edItV8qQ%J&8t{AK8E!Nic{;e%!hpLs5%)rc3%^Uq+KJr6ZMk<3FkSwBaPRCM z{3&0OqEmv8@!?<5YldAv*SpT0Q#CM$9BJK3b%)z9U;PQH%e}&@_1<-_oSB*9KNBj2 zComBC^kp;5tY(@l8mx2Bj%W&5d@A>NMW>-T^2ui%%R#VCY!!j?gO$)B&Y`>woB{P^ zd#66CH*Ra(#A;NTKz{Mn0WfF)WM zXvM*%_B-15IA#xyVxg^8ZGFX8F(m6Ys+y1)mtYVn`7Gm=esxM$NjCSc{Pr|-?27!* zw}bD__Etsl5_;v^(2->CJvJ?PKaV9p{Lr2DVtA^rj>^8V3^lNHMUzRRaJB9qHq( z`X695CLspSK4&$d&=J{RBJVR}uS1%EUmXU9*aC3y2FaH?uV%qixb%Cg=Q@C39WL}^ z2~!*hP^_{mIn9in8#@Y9^o8n?3Ehv`!4+Qs{~`z9`pf^IN@wCVIO1x!s~|25FQNQy z-fF3+W9dGIs?%c@MrAT=Qh@some)%T^22)+b|O4mAB;e!H$_hdzzmWt5Sxxl`@2~$ zNfdV-e~-M%tSJz{jgRBeT;k5N;px|4I0N%MksF%*XK?YWx*d2>h;~|U zLH=X=1{0-zO#|or zC?)fG=&D<@`XiVF!Cd_WV+%c)8N6E_psjd(@JP~ggfi6?X=s4*B#iKoUuzLzDm`~EDa z-8>Z!WGgwJLn<(vjT85mFSa=?cgQliM~p1HVv(@b`mC0jF`7Kt9z(CvX6v3)IuYa_ z{lBtn$7J|wuSpk5P^?`YYueMuK~bU@Nfjkf#yuzl^8Y!s^phCwo4{ou&pk>q_htiwVgKW$d8cY@9~90mf+)RSWnzWG#3`e5!- z%6|lv?~NZggZm!h;*F&PwtVUA%{^P^3Eqs#|2wz?!z=cZ$rtPW8zAE3oJDw4dd!Dk zC9sad^rLjGGvt^8GH!#B^bD*ygLTT32kkGKiqi;x=NTHEKD1+1``)n;f^@Fx#;M0j z@n0IUa=legqs}3#$Xoq8fJ?0r(Cnsxg+i6-7#P~Wd}l#AHNHkuSYPi!3rP+FVl}3~ z$+5O{Eg{h67u6Q6Ma!CfRs}c^KxcZ$$8)HT4LAH3))s975@!Tyd$mF}gOcMzlA~ZC zn=>R1+t=wR&1j^hy3|@rH^lbLz19Cj9Nw)6BsgXmZ>c)7L9IA%Ule<9vC^!Wuc9&h zC>I*4Hq=8anvKB8wMEay*Xg3`*&nUpQCJyahvG$Ncnk8V4h^V+(5aoUshI*6(Y8pf=){VhWB8!TV5(K zgOT`gsr4Z2XVbW)LR$#kcfQ0j;0@@`C+~-x3X!IuLGipy6B6StckY`L&1*bnzuea* zPMI{%<+HB8`LM0F#F7GMOjZeemH`GtvW$6LR!b&6>kx?FQaC&4A1{t9bd~w!SE&0M zZa?IX>hFU!IMWBHN$4)MEs;Am)0BNkvn<0LlWrZq0>qgtu0a`-z;8RUxqM>XC(82W zq>oYSh*fokte0sYee_bbQo9uL^(-69|Fdl1oMpcR_fD)<-T}(uFHm5_S_VukRVHy= z=LI%E<+)s1U0ZE0Kff3X3YTQP3CbEh3ws)=kH{LQm%zU{FKS!sPqXbY*9P8o&gx0OXvn< z5cLA^lQ@84N5npi*jVo`M82_-;PA2rrpxF*WBW^3Wx^GZ{?+1Ok4 zCq&R5?a$<2Z`E(`&ULM~Y8{^Rj&z{A-ih%qN76rDtSlB+QfI=78K7Sh1m4b7AP-#W zJQz!en)BjgwzYo)tlJSQ04%x{PollU1606nR4|VWjb_NF_~Z46ABlLMRf0hu zV_0_YNLOo8S!Ok@Jo9cl|L&FN+y!Bad1z&}{a&osoG$BW?yB1DPYE_A2jCXw-Lfn6 zO^WwEw%yLDy&y8(7=z!9_|3)dCvnd_1?sK7mqdB7ndA+uSV;?NFdAT3Z$xinxWkCf z7VCU3Ch&W7T!xvpayPO0x84Y+ncmQD7haH4*lYF_JKPnpAt<~8W99B3(1C{;wom9a z&&t<$2L%3RV;-2`3HNrl4Nh9()y*PD5dVhjxpb_BO^baG8^s!OthcHP#ni~N_Tuwn zgeTh8)T9%!UVKM{+wb5-My}v_p?IzshIhpkJfetxcw&g+fnkumlywnnJt1I$L4+u- zs3z!BpLLc3c_4*7zIfJn6lWHH4cBuTav&{*SDpd@*^NV5k(gkWzftFiSeFqb?1N%< zE7+L|2KGTYuPWHz^F%_=iJ3V^IJV8xkW1Ye3AW8oE+l{D!N)Dy4>q)z&2zWF2LO)K zED>v)yf#n6D{b&AL3Kb47=b$Q>-~cG2z};cpS>QKieaUHFb_7FU)B!K5m&nX=CT6w znGMWr53+l7EhdXX$Y+1w{MLXsUt(axiC9-bAr0-2?Oj?1#JHfNFT--Phmiefq5~25x5@DHG+=^(!+cEwqFnZ#>qAzkJzwp0U@?=H4Hkj1C8-uBlTOl}573e4UG%6SYZO>b#%o$SNy826S1S7%RH|ttxq)>Az zcROlMSLQxAOtPJ@JkX zh6?IREj<#qy1yGm$J+wmx`wvE&=b%plr2#2afP@{%Wv|ZhLPP3j$kkMgPzwA7O)15 z>JzmwsVNzOJ1T-Nu(nBo*`XKIKrF`)s-}HI5@3Nh7Kf&7vzq=a1X=4}{0__t>$W$S z)cx~h+@sOtT^E^BRxt!PW;OG{x$P-(c}!0retI4eTPGb>UaKq{HplgJh^hV%yDA(Z zPxOu|pS9}Gzk|hEaQ5e~VX6L8!V=QlkL{4Rt-FAWB>7-P=*_}!O^{ab&6?1}%?Ijd zoA)^S8N}8iHA4RBGmNp=ez+0a4{hyNaYO*l-^?ZlMmpDX{E4vrI6sKfDn-o=MXhi? zZ5HR#rj0Wf!SMWPcL?4IO&IlJcz)<-Fg)-3{UN*9p3oL})WpeSMJ}9=xUZLuMFY&= zX{d^ahHzZv&=P(|P+HI?+RUHGh~jz{Z`DpP3Ma%sPd%4-tDXf&Wf-iS4;LawR}u#* zzaNyC1}Vb9u_=bhmHAlN<(YdfiQl6Hz0~Tz)ao<8jpMiP#^wdHur&v(SxbobzPJM? zt1=`dmoP&l;onPTO8gOC&`%B4ww~}+__3~npF4{OFvyT&E{0;nIx&c){Q-$^6b5Hv zRGe4hK9;!q5f|M47RY1hE!+VoqkR!;K2~v#;krn4f**^Y*cBZnzV(vL6Uatp$ZED3 zXAqv$mJzRde*^EamBdys1DuDgOR=#OX}s!p)Wx;2f_sPJEfMH0Ov0^Q?bW1sFtmLY zC#XOlbhVuEDqn*$ULgYEpM$@$$INp>>l5)V5j+Cng`)BDSGSU(dxXBtS)Lk}m&mZy zltws$E1_%~q*#$7@GX%2tYK?bBeB1zM#A8?u0WsAcDjy%d@w>qtigDs`yAc57;Zuc za1ll)x}~Qu6Q}k?$WrR6_QONW1F~Xb{zv`^S~%Pf;Shd?5#UrzHR$g?y`u03W)-km z)~S$#7}*Jh^gtmHFOY^LD%!@tpIRd!=3bdYF2I>RGsKw&zpetCPnp@mtH?+_QJ~6yKnO{~8Wi8z_u=iO<*pF2BShngW1+oa%nE(9)@xbh+=0 z{inpMS$WW5> z+3Qh`r98%`HbG+5NNlkYSH$%NuH{eAj zA3ziuEWE3J2VgPxSkZhQ6!j>K!{b#ZOjvy~aL@`4qT%kqb&me`GH|dm%(RXc0r{$_ z6V0p2Ct@de5VFb6eJ%H@^0&UZGdc8@k^I_3fCgb(HamB3ZpqZQz6E%nf%^m^(;T0$ z#%CcSho=UXVjJ9N-dbMjlrkNqSm&Vxp92|3NZV+bgI;~B6*=uSu1nrK$%)NnYy)Dk z&#aZp{sh@fP&iemNVd$s#+QB>l^~7YI1cDlDu;+Q=GnEzqjReDwVER*-PjVijKWP!d(00y7Fl-D(0FzZ;Z&eH4zuV=lGSL-{ z!vU7+#x`o7M09X_9a9C;Br1RE)Buj!g^LF^_H@wAimBC~`xipDygo-H=qy3C4#AdW zTH%EQ6%8Qk=4HeC#PU{C%Z&lgqI=Za)}(J6v3OaL8H4v!@bg+5)s2IuZz^cy?H08g zABd$7lCZ4J-JL)*OWc1gdUeXcv+l+$gR2}TSv3GOmH<~b%im2J6IEA7T8Gz4ZlSwz z8ZXx@9%w!M2RiWJuyXN0pY{59F+%s0mp{sjTSY3P{ZfnW_Mxry#Fc`)^Ds}4Po&@)REe057g z&jeD?Gp(qb$~D$AhdY5l2U4mT?$`;P69u4g3((CcMeX_A-(M7_K47g2QA3hY6e6d) z?pY(M(zbQCr#CKRorVXni`#k?kr8LqP%JH2CmSxE9+Rh2>?U~n4;35 zvr|x;Xu5d}bz5_))D5B}51K+L-MpjbU#;MAp<6G&;KFJHa{aMJar?H$xvAT6a7Ir; zdGWWH`cswGxbhaaZMVI&vsmU^8U204^(cMiwU7>P8jUZZuLPbd@YD3XOCz*Be=6TgF$U^j<5DwPwf;j&%y?Gg~@|6Vk2@EF+I(_IJB68EqWSE&Ar^Pm$%%w za!&VdSwqpyU4BI3%EX<;nW`YTSQ^2>$xE)5ZkvGIsYyXoxgegz{*#=+*Gs?+!Dgg$ zMNgH68QIZHC`cLV#TD(QPnz*|Mp(c}l^^R3B3Hmu_te0V82^LXTL#1P4gUw1ddgL> z%HsqoofcxJOU1BOo1N`H#aTBgp-suwZRNEdTG%0H&Ve}@w@BrMohQB%K&F>?77c~U7^LF29~|@kKfV8Tj4VD%EYu2u?p{jt14=^OozWb1r?ZQ zY`g?s)NF0GZ(H4m*zn(7hiweQ@{fg0SoZ2l%w#h-9PH||zIckapoq(fM9qVvYjhkD zGeJ^iZZy|0eiEbhBPu#D!Nw$(*=!bVU5qxW^&AG|V6)OgbKuF`Sd&E_HlY`N-Dq3g zK5phF+7S)KY8=-Ebl8-NZOMfwhkbhE$=&3u7{?`$pqrzcaHs3PiVn+`T&;qs-K%<> zzyh^hIM1z4KMjC{1^=j3;e&ZHQlYV1r+xd4XSsGA!QipW--QKEPS1XUw+TbYqS_ba1X^EP&9+!yVv8x#*zF69L=4u6^81>2;r^T3n*=eCN_cP7$kfK#1v8B8H=fV6>`RktH|0} z#A%VJEd>xf@GWu>OIxisni_4~0b;_bg{HC#K61J?0qb#K(NNiJo$>{q;xnja6wXUi zh{kAiDYEIhyz7AuJJ<_lZYOc}CyGN?rog3d4BS}g4V_A% z#QS!9*5(lqC1cW~7sN*@e*xee$s*2jg+pDO`|smE6Dx`E4R505&6HxbJo>TI75Q>3 z(q9Sxz6mme6z6~JKT|yXp8P#b9Qy-VGWdt+6O$i>#KTxTW9vEGif|WgoM-H|r5I}$ zf%aj^Skew~>~Itfe@3&pGxnryJ%GGo-TF1E#C_~GC};mT<33gm%07v{gf3RwR?mnJrQ$r zCc7bJBp8T}*;U9MtE@EhRkHD53^Fw*>b(ozHHrQHhN2TVna@k8wOumMA8x^NK3Sbs zjq;qNoLf~obZJ}IH(wsZQ+=fA=iiYlD+tU*`7+^K)tyc!~08QGw7Dmb`O8gtK@Axau3YjJ$WZ z^ixk12^c(9E+NHl$Ei8&q2UA#CZKMR!63*4Y4iC_|FFkyfOYM1xwuT@wHo~T( zgIK0?+VOp7_+~{Kf43Ur?1+%qnm!a46cnAn?fgY2FeZ4yE*mx0_D9^mLjFe~JsmD} z!wPVo*v`*%Kqt?0&67KwP_r)}w&*+@NHxC(rM?DzNB8FN_!LqKFPGy57oZmm!>qMm zA3HdgjcUMM<|4SX-+at_59OF9BGE;HAoEn&giRW6+-T}699O? zP?y^v_C5MXJbrGjO`}YtfvehwStMbi8sN15|r)j%Xd|9D47I!>G&|9O7nO&qrOADwTw-mxy)v zpq$(3g~t!@h++Q>ii8L(Q>XYn-s%Tg!=7Aagzltum);`k-5fVH^qj>8V?RhR@<&ef z&(-_i*kThHu!7IYcAx(mSa*0pAtzL)k_|9x_z=8Pc9`^;i&Rs##oDJ#qR4e;?^@w+ zzT>?4uJte;uGcmuI1GGXTG-UP42<=WI!%g81oe&QzgRBR{1oy1@=y4o56#1FF>Ls~ z$bJ3u z|7yV>tMPFr>n4fQXR>BV>`aNpoTK)?t?qMWcU!IGtsmb2&eV4h;jUxHlN5}=tqiN? zZ(o9?ODhgoaa^zB;M-_D_qmFGE-t#&XMLLo?Lj$<&yS12f%)fA84#zahFU$$LF8#U z*5R$<@+ycU@=719EyeW|%GoryY>*K+A8*0BdNz-@RX61p_JCjH@x=7wtn-$ zmr9o-IRJe+#cKOdfeMye@9#v~SR(6e$!Z{4ud6R5(BoioOfD<|LFhUwwS5`Y!TA?h z5aiM;=a$$E$x=?ZU;qw=d4vZMHrm!-mc}VD^^%ir2IpRs9%&rK%&b^(|AJEp$i2`&(8N_=>cv!hW}j$>e>XuT)K&BSQUM>e$vBS-{G8BO0d(SAG6&=EB z39bY-&c%Sxn1^vD7H4<#algUnCt?f~jN9=R+EKt&BxXOge|7}MGCkS&d&}H|3ul~e z8KU%Qx+GzH-ZA|YI}ewNf_q`Rm^p}StquFZFtEIZ3k5QWlN^5ovG(wOb{aJkSLa>2igiHp7~(DDN(aju#WC+ z7-b(bnM_=e#Q?dyReNF}Eo8k_&jEzRhMM5M>P}h?<`q@&8|9p*a!U@(fS=P4oN6ku z?!o}9fbGYa*R90*N$e%1#F~W{qBRk1ih9YGm+|Vwke3lUMsgd1m*~*=Y9PQaFGY^i2mEG=5nDCN7rgfeLPqXd?brCP8{CqU!%39)|;3M>3D9X z{hb<``o=)szPdETS`RGa4XpkA4csL zTq2?x&RKI-DPSy-Gv#^sK>7u^iD15b%g=!QV*&1Ey9sZzVF}?G^g=w(#T!qfkH$&# zxkd@jx8Eqx1vs$|(+AIor_1^8RH0SGdjB|f?G;aj?s6vu{K4(N1g0x+t8n;iu;K?e zp>I96PAE}2ubc8=(#EbA5r_M{$uoMLS@ByWN2*N{QwF|w^oldV#1MGPVKM#671~Yv zMYp@vs*)T(1q!C{H}4f`3e!thEReTpcLx6~AdUH$B1w5MXiL@vv1F~{)Pp-`4#JZ4 zFN`L1;t-AYuC*Fa=uWR%eLoRnbH7;3?M_VNM~$>sZNcGN&O&}TWb?uLD;bE3q5Z&w z=2Xj*mOAc!#=UF3h?oNJPpjG^HmHbo$TPYKbo2yn;yQJcZ5{DLxbhU-L=heNvgPF; z4Hr4;NY*(^p_W2um|_URlgHVPf^`7`TFIN>Y6u&e5U&IlEM>(h@|tpM z&dR$|js5|AO#*)Ca!Cu9PUynWUGQj!7Rfsy1p)*%Vt@#!h*3d6xp@s}AS57iSqOnZQL!S8h>E(aRBj8KAn9W) z6|ePDsoJ9Tf|qIv6oJGIDpgce6t$?Sow$fmqo#uH`#oo#XR|@uU;F>Owrrlc&zw1P z=FB-~&dAL5lqYwJ=9(FK>|>rgsg+l7EKI%%79x~f=qqSmq!E`_i8Nvbf{M6U^1ybd z(0{HXiQPnd7Sag-Eq|o^gpTpJZEH6bn8y<3@r~`w$X$591=MQmp({007E8F@>b0Uw zz~%;6s*$ar<-}^yC~2x zaJj>-h}R|n*g@=}_HJ#KWD_w`Rye?$Hy9LI)kB(xQ(rJCUS`=wLhbcFZn(Ra$R^lG>%%Rn@mgq@mo{h20Ghi>^2 zq=cNFPO&>Vm+G8g#*^ZO@YMHy5OH(2l!Q!VM>VE933hL1e7t@W*|HFvObd68Zdt^K zFXVq(H2E5f>NvV3q_J(DrdB(j$`8S`x#ePQaM~+HE7Rgf#M&sovsMb3=U9`ecguI- zmlTuCW|5`oozSwcJ=!5QQrJpRN*Ij%jOZeqg$Hr3Z$_Uk3@xu^{Vbn>VsCE7aFk;M zI#97UQ2r4;7H*9NXi-9F6=Hq$ORB*C0cA5nMUkanrG@jILd5gC)F0_?XX36E zA0|*^Ifc8o?aS)4W22a8S@im0F3}U4Hlo;bWA+mh@29l{xU*`rO_sdha&jDI5j_9doo@)Q<06H9ChHYMjA|>7}|?=}HjC40a!INn3jQ zwgIubhHd9%54oUuC~xJXY?Vv>1HPynmU7o}rwF#i51ex$PVRA_L(fLa5B|&`;f*-| z;cujRJ%wSiIp<1P8LAF^D2cpIJi+ls8t&pk^Eth;N92tz4PWFGWb5Z_jCrVQ{VpC= zDA{RzePBwf3Nk=_LgPV>nFM7elYZewej0dAN-S`UI>0NV4nlRt`EGcLx-4l@RR~STtZ#wtH{z`7~h+c`U#z zIi_OoG9&`~2God>tO&)}K5Dn;__TOeQq$9TjHqxQnU|$Yp6MKn>v_2)ry{W|dq5qS zUa4Y(mNE&-EJc#=US46nl&Qznu5b z9;Y(QavIS^WANJHnil7eF*j@#Lba@$b6tj5Xu{lrW53zOS6Y!DL}MSl2t}*ii#2fy zXHnTpCqOOiIa1Gg9_?mt%r`K=~d~J|S)S{RtEU%E6%~YA zmCE(&-%3a7OpzvGnnchhW0CUy8=^hFiFyOi75Ve@EA$Hno};-#vk^-T6$}m?B^glE zOJ0#s4JS6Sb}+xrCXJhvVYxfgd4uff@{WPTQ-OFKzyw(Qlv8k^7VO6_B&o=IV+)we zfaJYM*k3#;#4pqR#cDYFDC}D2!5u@Ln7PF*vA;lpaooWB|{P`%eCmY9RNQVg2CZ^5y+@iTuK_pyOD(VlR z@!Gtu8fVs~_+8O>ZT7UwJ1#=wbuO|KOIR<1+EUDVqUI8GN!ehn*S)uD54Anp*-X`H zyc;P0!7p=AQszJC?OaqbZ_6JO=E|}b@lw1;L&0^l?eVW&X0o+ZisVPgo7xdeQK8@q@#)Vhj=n5Ja=+QzQ#R54=A?u4uyv*B7wngmZj#cv%JeYd z{ds$qwvi%@d5wHX4%nTb{=xS06_A%0mf;R}Akf7?`3nqA+CQI_)KF~$E+{y?ZvRf) zc0?ZniEN)CksjFE7meA7W#2dkztQBmWLV-4H%uYy4^$4iIUXljOArTl4g4bxs4E}-X)AL<0S4L5eXb--pRDo*F;c#Jh zrGc2}h^q>>D)1vB4+)xfeMOr3s1$zULY|-qKU%f)J-PpPSiX6zp_-i%wB2Jv(;qHUm}HQt8L_K?loO0$Q9!)1z1wWPH`QzT}V}EIXcg@qptwcj(6~%}F5~KqOkkXktd)r4@owkc)wyo5%T3ZsMH1kkz&1ig_pFVeuY=3P`|8c zP(LRO-Br8N{1CcN40zRb=U`r}rOz(xxQ)T6Tsx8mD^YdndPb;k*&P5jMLxz*?`;x* zJs|@S@gwqg89$Luqv?ON+(T1$B_>RinYL1eZ1NOSIDa8RB^&jR6Sa&o<`Nh z=Gi{XNWPuNlQlj>JO&$1|R@0NIpQUK07P;0E66|r!vO-7yTim!6(?a2~l<&9oM#S^>Ko#tC7QfpVvazLP z#DCfu_qoaYx*VqESt?W*_a{vL#a55%$}IGawE|^qXbS`=1=H;L7Z(_wv3Z~_x*z=I z|GtI@Kb8#?kqTt-8gHQ8$S%1;?eLJ09`DGnFD~8^((Fy|hZAP|occs$M6M4TAt6R0 z6R^TRvoi=OKZ!IEdDIxWY#PrYm>%wgQW$;5PVNnebVfkzrPi z>>Kj3R&RZ)o2a!C+tx<;>&!(gwy+}ix6E76$V|o@;g{&M6{SMtr8FREsp&TmcYzigFuDW z+W$SpcHkCT5rN>WU&`uPc$KUk*9X0`r*f^xaABfV{7lio+6-4N@dv?`dQctzD1X+E zt#n)0#c>-1)yxp?;|9&gy<|aHdxw3+K&WJ&J0*iMyy2PG^Pxa2a5)9$+R2gOsnwmU zyid9JoF=2(x6Ut5nB0%gd}rc5k7ey9qr8bRm`ie07oo| zLmAtPykD+888Wx>IFn00F`lF|`r?Wlv?JB_%7&!~k!l0#$yEb@b^#u?yv@o)Cf>dc^}Vr^(QRA+UB@|Lzo%J~E*E`HIoZNdUdEr&E6mu1uD zRTdt=8qe#>`QKu#VZ-U94k5`dDPWx0cHK&|N9An>uS6K3X8ym`rRY(HlPJ z@^Dt*xsn4GGP{JO6p6ROX*(gYyX*^eYykyy6yG+>h5_y_b!cGpvR>uN2;)e}<6iE# zMWS&~*SrIziM`+xG(-q0&a*dg+6H%{;2wy;gCyx@~Hkz{*D|qbg^=(Z< zX9m@98bXrfxO#wp7WNG=wlxj$%Ncgd{x+QCs=|Gy8kV4@;T1`Q&1zQ5S?Gnlf#Ebq zp10Q}7w{NER(KxV_R@!BTNi>_T9o1{w$AjE1Wv*iM?y%TAtcNBXL0ff$b)DH$}dKN7+f?4MNuREez>`3JbzBBAaYSCfVIaEA?(_a~ph& z`O6bOs1MuR{K^=2$W@)<GDLFTc3h}aysN#8kQUFfA8nG?6L>p7_Gi@S4L zUxOPSo5{t;60tv-k&_*C3UXdq-*Z=2Y{eU>|+UYjCc9PFLIo(}-7J%%rTfY=^?PB~j9+n&5gz8Knv|n{PIUP=ao9~JIM4&= zowQ5gBV_7NbV2UoG+jt(SoJK^9nK~fpf6T~t57!H&oP=(dzPT%OCZ_}b=J6IbMeQY zCgkFy*zsqIZzOZo1$uX%6{*T59(wcfV63b|S0MS4kl~TzIOHK6`KyvGb33bFWXrsy zBizT+ScuDowE}ivr!lf=wTxR_KWtC{T=Xp>Wd!5h$-0`XD_)V{bSZHZCD4vZ0Zx*8 zXoSmCQmT{xwtaH0B(ahp!+C~3X4LP+E(`xxR1-{elU~KaMKeUI{X{NEXtmp+OJx;^ z!(?kDxvOM1?{(G7=xfeiBkhGct=7N#UITakr5TBnzvg>XEj4*-q|H#LO8sjZB#y;) zwstUd>T3P#4HAbGB6t^Ep`SA@$7*lnD9zj5IV7fqyIWyhdZ;t)T)Kt6nLm!#xzw^lwCn&a3&);V zfyoPC{psQ-YVmudI5^=%RD~c1ryBNvMi_{@-jF5Xy8f&&xAQ)d1Rs|9ZyER0$RaHI zj^_DW(e(tgx$EqAf?52*q$mfQhRpb}>>(g@Lhe<$f7K;9VRclgl(Fx++^pGR{1wm9 zFSYt|YB5I5qCvChe7#GQg?DayUt$>MVL|hCfMai-7=9hE;TK-VPmv1aw^Ay8TuW8T z$RFVr{-$0ES5p|%EV*jLB%+rQzd-x2n}pY5Q@9uJH%dkVH+@eA=%l@|V%DprnsGq;Y0QloEw-=LYtLncLU`iMf+~x*+F?X(DGN_tB{XRljqxjD~?m}9DhFkaz6MO^K({iRmkHRqsXczff7q3w0=qEiVOj+xI9B;f)6yS*!Z} zuB&%iLbW>e#iqo#K*b6M3?pa;Ol9VFjN%l>vg;{l(F+c-{kDz9{f@|g&0C!9&`Z87 znY&2l!b1>X>)VmtaVXEgQxo2zpPM%8hk2DH-@sUZGcWpS$51BZd>Xr`*kURBcmHq6r9L@N96RhY5mYEnPZQS# zW~?N>F>~IQm$*k5tWOk94!=+I`dh@afW5q_Yf0Z)2xnF;ZGPw>t@S&GP_Y#h4%~{X zKJTpr_8qbN5&rMIchnOR&V{i0Nej0}cO&N@SU_l$&8K#2OI*9P(6vfDuApe&50Rjy zVOGtkE+BlfwzX{PVJ2(`_sP=l+=X51FcY>9HDzsPiwWDYif=Qi2>Uheqm3DRP?}i6^Y%Fm*ox12t zJi!1Y|2j#WeP#g{=E8%YsF@Cl|9iDa!iK%`11+poFca$f}dWNXLv&EO09+R39aB80l7^Pl| z(BQ8@=LN%&s}z4tO8h8^XDb@O_N0j^X=0s4+K=MJltK&vROn;s1R$$PyQOAJ7FC zh3>g(y0>p!$I{V#Ll^Ak?Vl#r^~MKUWvRc;cYkjV{{y|yqlCbPb;>82D;a)e!33mR z>NxB8arFded%y~O9l0FPSDpjV2rNY#(RF@i-%2LAK23tDuCpK86)3T53 zGOI~vS(~Z~tq}Vk{x2`jLDV{$Pl~u!U7=00z+{n;2J@(iFKVLplT{`wO%pMjq2!~Q zsDonJl9;K97}1dUYfaQ1kHLer{F7?4H1QfqESw8hDh>y-H6>e9N;PE;DLJI%XiAQz zoTDkTN$E{WZ%yf~DY=@GPf8zB`e;fYP3fp9V@Sy*MFu@B*N(ceP)03iLZY~I>ZghQ zwAdRYI)!DdpJSYb86sKgF7&Oi4B5@5r!jSYiVP69&#DT`C_a$l1GV@-Eq<#Mf59Bh z!cp``pq7P~Pep$B`>8{H<$dl?p67wGz02D3>A+Y=YX^I_zVp+}aegrYAbrsDN*#{8gpLvrB`P4Pna`GVQx z-T#!EH-9!(VM!TK!5q?dCDZ2EG)=!o8?i>vYubPgJIvIqM;vv>W3Q;RC0Av*iv&3vj)$cjhiNtVkFt~@vhN6 z;Z{7G9j9Op=2}uZ?-@!L$Q9VP&8d9b?CZ!*9ubCykI|@TwcsPNB@HV` zoZy(B;O6%XQ$l9u;8F{0d!$&0JACdTGxZp`}!TPMUz{T=I_C{NY`7Hnt zpEasFoteea&2T74%(OZn8n=Fdt1|`ARj1SSwK7*^JwH$7%f+Og*}U(PLneqFcwA#VEC^Dlk{)MpC{x z+V>BU!3J*XL799s6E;cx2k^eLd4~sn?pK|D!7p6pDQco(vet28Na@& zDhX`dLw~9&@QsQ7r4d~VI&WXZeAZMCzZiEp5QNcU|F6Q0EYq|Y$sqYH({{`HgxrYDQ8!-j$JB{*6;{tMCN6LS@Hg{XVB?*wk)oXHE3^524#v_9Idyc|hCyn9_w z2JP70`AF~2Osn3=v<|h!(NPjm*6?7)q?L_aqYl0i?(@F{Nb-FUc{<( zwX9ccm(R;^wh2Ac^PXJp$h&r$HE^9*dF`}$T)n<(nyEfipWsLLpcg z=*th_h%t{dw|OR9;NZi@?$dt$%9fmQf|?%VR?o8qDU(fahIs=r5qNH^Fwkv+Gv@c1 zVg5o){K&xba{e31D#nhJm_$J-%(xO1CnB&CPBEhfXCf)z!XS9@!ee;fcP77eeLu*& z$V09oAoc(rPyl0IA`nNw3N~VC%6FQqqfWWr>i6;|Z+pCZhR?TYHW|hG^qyex{9{dm z{<3NJlCXK|8$h#5kXyTIw%`6mBHm+uXY=B>trH$5!K@c5x~KA9=^_?x$F-7o_01JTe`1pe0rNkf7McjR{w^Ley=53Yu-$BOT9hVw9RX`xKUEw z#`Y4m#fy@vr1gaP6H!T$(3NRPP83rq%1DIhc6rTmmsfw*i8pIq@a%nghSv z8XrpHsa}~AlVy4lo#U1%rA(%lDb+G;UTW{4`y%qJ8G8JYO?2TQESK$WI$$s?WJgp7 z3IF#zmXPjT%iOBKG9OEFF^nG|&WakNOB;Pl@R3-Uz)#w>?OV&c(-e>wa+g@422W8EAjjcGxD z{H$cCe^8{4undSXDH}i5hSCO$fr!NSU+4}DQb$ARH+cMNVv~r+YIrTX2Z=*;-A0SIcfTZ7{#dEaRZGvYl{0*4i1+LHd5tlnyMA?t!#n#JTq zh8JDLB~yF9w@Edp7F+eE5>UvjtlDEETg&Qw`}P_YPL5 zkQ(z!n?fTICZrW<_04$;GnV2}UI+A+Y4=B(-4%G26~j9#?BuZ5Z@H;2vwOWnIzr~j z;T3MA^;ULrN~eNu3LUHdLjy9N5@e1LFA+{p4u2r)fQ4XnVq6KbW8BV_G+55)556VC zZ4`~k9XbVQF9l_{$RaM31r(GWT&RUJD3b{9giS@(;QGx%f`kmNvZRScQDW8#pUE(O z0Vk+Jg9C8Ju6XX*e%`K8vLVeJ1!Y`Wz~wrC}9I{(-YD?_Tv++HbwReH`}gOTP}UYvtaSo>K6Em8Or5xD*6CY z{mWvP=YzkJr=_^8Sbdii3pEnRPz@bwSQl4kcw5v6m8=01`>?v*thTXPd1H+CY*@v~ z)cV7;$&>m6vlkgD=%18_2|d}QoMMkp>k|(&vp2^3NArB4Tqixc2*;|uSeDge?=g4X8GU3Z8QLDToM`ozdKY?KDkRXAIn@;aZ9tgklGt>%io zOS*Z35hu#ph5g6Djp^yl>YVKb=t%Vo3dc*LBc%pkxTm?ipiU5 z5~CxfKe)A*W0CyNlp2yGGCC4rHc7pwN&LEeDOzR_fs*N3$t20MY5<7aPN{Iqb&D*yc@vC`ud;WpX7i$^789mAIR89z3q{?!nUwD^EXI)p1e@EQ`0X`l$r8Mc zJ(pY(108yp81N)1W5JIgZ=?jLLy}LI5B_#f`8#W-8T&WI3HolcC-Fb(l*K9r0kCW+5T<1@mZnoTpkbD=q zlg~a+j>+}mmXde6x!KG2LTbKdXKa~MnsGh|+g;w_A@r8H>q(m3jjJ(VX-J)El)Dz5q>h zs>KDdDaK$LTBqKjGvntv*ZX-jT`YaL>?blKt+{LE@Lau_Y3@3#_8Rj9Vtn4Kf@)Nb z&8!;hM+%fn^=eQovacXQ!%zk&WD56#KG+m^;nO4x9a>R)h#j*yDfnLsscsSc*F1vY zf0sTR!nJXT!Rx*MnRpneh&S@p(OV&E>tM_XoraF!LA*11De|o; z{ie_M#R^6xrR#&sIkYr#My|Xtv#u!P!CA8uJu5c|&7%5F?Wj5;I>p z4Am7Z%;9KLr*iowI>qx9M04=0jLN~MF1niETGmx8swa|IRBUk4V$t35huZtxi^AZ| zqgf1%l}}d_SvGhmBiMtM!+|beB-^FDg>rUZvP30AWodR@e%9OzTDp6>c5Z_d7O_uN zg)k+-WF5omMV=*b^Vp(Ij#b)psS38SvF3w!xXa^aIK^l7?p3W$+j(`==vHQ4VI#uM zg5Rp>gA#w6RA|Lavwl{eC@%9)qVi7DIaox+%|$F~$mUBp!%XWMU{rGT9M0PS8*nUx4epw@~#f=iAs&!3CszI?j^cd}%%(_{!9 zDTa}UAejWk+)U1@tuz6X&@O0>{0L5HmCU!S$4gITEU)u1_ax9tg!eJie?6vbs{~q< zXlp*2mPkx5%Nzky6*E;wQ*700o3<*UF&pH-^ph)XsB~qS%g^pC>`Jm%!52q zA3d|}@@(&m7cqCzf8L#5l4+|7n#VyXH@#glXS;B~&FNdx)?Bg@RMVF6JHnnGT{Z{v z<*K4NW|RZaR>?h5?QeJb>#UMRVLl!xDeL8PkA4a^n!o9%Ag$TXXI0TOcEQ6nIqOM@ zY>^d>HLLkD=bu)e5i^@0<M3;xTF!KI_0~9-kH2q_7E8JWCzxvT;Zn zITMa{J)z>V$T?u;%woYx>38)r++BL0lzphGa8GdEBH56vgJ-jVp(2PrTZ0eGC8cZ{ zKPI%lJ~593Ea`Wz+YF4&blmbf6L+(Er2%W> zK09p*#Fq65$M=8C?iCRsB!XAYT7iiAp$-aGcuu*8u+*N<0YLicLMy9)n!=HM3e( zxZ8Y-4i@|GbL8A)58CKZHi?iZFIb9Y?2DD-O7nuV?bmFlQ{I#X7{GMmsjI6ONN6E9 zmo>D|{s|HJ8v!il$P)jgeCGv2RG>~x)6K*g1|rX0%$v~pigX?Xw$>5OKZSP^riw-1^Slf?Z^mU2VEqOU>kauWm-r<^eu;xX^hnL7i4d=@*ZtU zQIo>23md6-+IfIW(A^iB<-&E@l!PNA^T0gXg@6m9eyu=+hj$M?8<@hN8PB3o_%LZY zw<16CaFNL@UX2N|D=vss>&6$FS!_D7xS+$6^|ikCv9@ziBdW-|_$-e!V(wvb+5VI3 zYZghV`H`cpKbu7s=a?OfXw{w_59>hhON3=1%=b?>2mc{!8wSx{$IH7@U=Yg0dL&qE zx`Ri0)ed>X)V<(57cu);1deO~GT`fE2FpVmU0cWYUrQ{GDOQq6Q(V_tVH?m!!ex2k z%+Mv%O+Q*;$TQt4pdZ|@rAO9&ub^1t+e)~qcbA&pz4 zNM&dx7TioShAFy^W(b3zrSOq1^BpJ`pAY4G0~O+GExP3ye%WcF{IX-e!44lRTL6U3 zKDPeoGF~Vdp~XE4a*DG`8ft01d2%%2ThasisR@q1I%q;nE76H?gR^Rb)Qluea`oZG zM$VBzH}-9FEszVE)4c5a604*?4Uh5Tb?xT+2_$ZocrU6#$-)27x3(#L+XMXntG>N` zi}dX-FZ({X(}LJlcBtKSYJS1g$g+|%L$`W7D@1PS%o}czMWcM-bR;YovmX|p7INcR z*Hh%std}C&=e2EQbKp^6S<{i4p2dt@2%!IN;pogG<{n}+xV$J{aW zV0ybVpl%V(Qjpg8hJTWXEW;09-Mjggg+9@d6LJ=%@YKZw=P@T1vZO#N@aZ`y`_ug!%D$9yx|ogL+1g4W@mba z`R!yDA;KS7bMD@SLx$Xd4+Tb~4}Szmo$Xqhc;UPqiLQdNlM6IAiz++Pr@ zf>Zhvt*r+0toef68VnT4j9Jef&B9EG9M_5Lp!cqXVyH+cl5uaDO}|FGiv1iy41b~r zNY&Y!v4(tlg7o4wNraFd#SzQ7pc%{Lnvq=@V&K<*qS`?DIKHFJ*~>2sl#3VkX!8+) z8~={UU?R8T#B*o+8VYCoemLykt@$>t$EcRHSjxAT{{`|r#=lob&!*m^m!B9Y|Dr8T znC^jcc|HZI87O~NW7^5J(dDDO&o!z7|diWj-%#ewpR5$TPT z80)bX{aM}+ijDju{ME~cj*wQC?YNVi60gd zwkztSZcv{_)?0@`a){pPX+G><=uyK`0ww0>OwG)Ot8E41QY=t0*XD}l z=chH4+HW{S;lRcdGB<6*t3|#WuNZ5N9<2`QGD}RcR@ax*5~~ww=&m`N>wFv+iTo0k z%glx}Tio=Kza@g^4F?#c;BJ|~K+()DhTHyLkDzIj7JG?2CFTkFcEZh7OZQ-x`cQP~ zduhG4=3@l1dZipvLBvs%_x7qR`9?Ls4LGHuYu^2&Xa`)^I5p-4g2?;d6GkdfzCm*GMxRE$i43-T2JCVUY%Om)ejK5mqzt-+c!L%^fM?v;o5@V^t9-BxZ9jc@>)Y@eH zw62>2r-kj>74kZRMXM`W*(K(tW?5On+*;0NJ=QxV<~oh7Al9VceK27rtvi^gD>PFe zII_sFc4qp;*3~^Ao4ZEX`OK7gs4-i(K@-^&XRJQ_D7aFMBj62V1^F;>5k&sH(>y?Y_5&$WaxHbW8pIeZCwl7xqK$p&f!iBx`lg-cKkts?s;rjaYp9`&n8Ga07=GT*O|4~E1rIKa8&5D)xlAA-I~|v%N|kF z9S0=d?a-#f6ZMpZ9uMUG> zFBpS?0wEz~T}}NN3?v5GGq>>NvDRgiDJLcFr9@4`)BWWzluTUUCPp;zc1^VAD6P+F z$v%CT?1l8{{bWd5$UD4GIL4lw?9}(bIoWQ#+ir%odra$2rJZkk97hh${lAn>t%+_O zW8V81;TGp+`R)h$>lr$Knw<#ZMeYRzt%Mm_+&yFJ6QKeRD+VReuSIzK64-#x5|wR% z5ifIW!~p#nVy(`HO3tPC?s**?Zo#tfX)b;a@)T3#?<(cttj@&8c9Y^Alh9E zI?>ThyTCPHXgFr?<_uOixY&({Y$P^les}|kO0zJm@gtLwA0{Ie_uZ$>EKNS!I#q5H z9%B8JL~p=u;sn=c$<$C}qxE}uqe(tzT8a4$ zt>`^5=SCqGK@*W??W^bVpxlqD&#>jy1M|#T*5)6)(KpZ-^q=o!giM->KtQbD$Rn4W z6XdqZZP~JmS*zZyQf7+tDXtUnPzB+;hI}H(-N_}>Xox^!?LvOVF;@5#9>2!;_;_^x zXZ=x#WdWUJ_+80cP^vDxmUfft8=fDK)x6&>&Z=74^6qKoIdy~N{mMYa&9pb&`Kk&d z4B^u+bXL_%*F9>c2K+0D$se}2YQ)c++a=Y>-k66dJUrP3qf0jWeWB5|WMoM-(vfa9 z;EbvEV_TA8x;+aUa!yO6nT82oN^t4r(C=fo^m}(aM=o8+Wj*jq6nwk~j44KCK_DZtYLd~-(v5YVd}sp;meKm?^NEM?owg~Xj~E+NVTjKtGm21CI6dY?k9`8Ict6rIHL zHL8*mB~j;SV5bcX(ZESIkUPV?$?V$xX3Q{yfk8Yl!}O8A?PIHiV?a`8r&efIw6-3S zB{nASvyD;rSJB$u>}xIYFWC4FnrRJ;ux6kvdk5|vT>1vKyZVpNxm6_@ShAG$0a19* z1(@J-BC2cmsAUZa=9yJI%+Eq8$*9EXb0!z9c%qfqTg**M8pO@G_hzP$)SqafR9oQn z6K!GhE?OwGEx0dToS`pWNR^P#%}WvgaOppT4OTAuXNI%Qu5_B$K9S)n8jbsT(_$xx zB8@|f)}5(}Tya2jw0t_f5^s3>WfR@P^|_}ZkRu?MaP%S+aJ;l@>RMu3T)k4X?aHo; zFRTlv%I>PZuxb)PEF&>AML5+|nI${h+9}@Ni-R<-H)Dq@_eZdFv#+7Vdo?_BEPdxv zGgs8KQuD{-k)(dZ4{W)1CM2co#jNxr^kUY}oeP@iTek;TKFBONr?vrBV7oZ_ML-$}H(u@rW5cp6O1u4Fk=I3&d$@=^v!lJ=x`OHY28r9HtV zPg5*X4ZTJca_@0W-5Wq%4iMl|i{GJGn@s)v`$d!hGY_fMCtsONdI zIo%G)6m`WPK#BCa*s~)yCv5fkzTq%T>Nmflc--f8_z1$i^mV zSzgyVWX5pzki6A4PLF+@h2O!)Gj*kz4Rfr?=osze;JKB3q|_D`V{*c}HKZS7K}w43 zt>*l`3QNz2+4El3vT|ynyd68>S=I;e8~{9OuWlCi)9cB+E2HaL_zz$v1W8x}0o9Qh zU*&e^T^>^6+>=RjqO>_)-s8a6KqqZJSOxcnp9Be&G?$vs^6C@eU1)}G+bW`Q z_wk*c)QCuXBfKZ~A5Ip=$bf&z8^w0tR$IE8Ues;Hb8EMhV9*>3;W01yqz6v7fon#| zL2N7vJ~p*8F|D{uBTm%e<%H&`KSr62A@grn*$tV}OxoZm)l;LUwkeu=s5ehIm& zAZUK2afUlvxN-~qysogiAH%OItPTb{DDqK$>Sz_T!90Aq!rZaJeQ?hlyORxP4 zZ_Tgy(qEe5lpw*{JYd&r-x2e=FMSupF{S8Vcgl1$nklEs6@h)`-a+!Wo*ZTtE_wh| zJMjRN#XTq{7+V-iJVb^ToKb8(pk-)C%Y>1`?RcKoqi39wM_c!ZlmGgE+oJ*6qkU#M zJyIC0d|A3!Z#vR7=KpBs{~ngHUANo)_D;FQLs8+WzlHa;r?MjCpit~Le-+VzdWYl* z%e)WR>pwuR;S0sx^m|EW2?E!d zUw$dQY&KP=fo}q}az4P0x|!w^cF-hX>M5ASB_9mdve^btIN(K7z;5$3gcu zaXf8KpbuNl5w}b9`C3^}VVjBF8cWu4Q5uqeJoztOryk_ajXwXLN;e9ten_{uNGN|aJ7XQ zF(|oxeb&ieKop(pL*S@u(9KL{b#6`5`mE?&NKR!t)=aeI(o1@Sx!;{1h_ zAC%V^>i@qK?hj%hcNC|`qK%YhkUP39MTN^wtUXgFQ*JM?8-dk27V*O(hvQ};ak4?* zFw@(hs|vxlI@67#q_bn*MxIWInL{EA*rPUb{RqxP>H~CpJ&g}nA-08C42HUxr?=%H zj!cY~_a-rP1yzgs@bX+i&J{gDP6WX&dbXU4TlXg&GfHdzuKjs+-o`Z1C0^UQBfHRF zq;_O;pmj>6Lr&bxKD*f~TlyXg;b{N7k5++1`Ziv((=}ZaDXck6nKw;_T}lf$%tI z`)aknq|N@(kz+)%9UA7mj0Kl`CxRoy`ce)%PVqu|ds!xY{E?2Dc$$oMa$vXio^oEz z3ohh9geR_r&Kf-*Vd&~?yTuHrknDl>3R}hob2&@E?nhP_^6gf^Ffl!upuSFQ?B67I z51WVYK3!zu5A8U-@&rxZ3%Y@!{4-e^(bGn_J}5OU5zJ=)0=qibH(`+ zh)Jv+$FEwyTr7=M?~#U=i&I831N@Ai9~~*<_Jv1BM`r3r$GW!iF#2CG9ebC6$~y*cn8=Tr+E4RZLrHpm#anju%?$N_hY zaMcCVhYY`|Al*l0PQGhtL~8V+=K_Y0MGLm!l1MR4|=MXmW;1jZtL>#Hhg zsEjLr8$n9oxwqlz%@Ms+{Pv-&Yauf#n@rjQ4r2zMM)-cXE#TWVbYEpUWVl5$U<-Iu z9UVH>p1e#x_lBX3S?Z|G6+vf#pLTs5y@7(~fdndRv3*x@mHMA(9D=OsmsR?`lU0vM zVYJR^mKZ#Y(aFr`H(sKPIQvh<$Pp)f3+Hi2A1)AC#cF0rkYmn;smQVAbMBzGO@Zq# zNS3KNu8n~7ZqQqeB&@Gi!O&E*f%V(GbN1P7mtdh2)$gv3nwmQ8t_r-PELj#m$ddc9&A1949JqL1ON_O$gEatwM;xo#ZOqC*bzX zcxMRDlO2VP_K0n-y3#~bFD+am=MX+~4*rWzCH|s{o zD7k|!=Lhhj^2Ym#UH!Xz`Hem=-#$z_>&!sNC@o0+Jl{yHAeE4+0dz|Uv&6aevnkFn zUp)_q_e2k-_1gRG-qrVsQ5&{88S@h}OwM1e_>#UvYisod>Bi~Kw}_4Usn`g1Y9O46 zdZGzDFHDM9D`y6t&u_zbcGZO3;k*)?1!Q7}z7kQ}aA@t?e|(iNuXFcBFV%O5JbxD* z675mUh|sIyIYN2MLhAHygxi9;rm-!EFPa>;g+6Wzy=h^tv@neZix!!(+v^j<(;yF5 z2A;ie0i)^Uth$wIEB(2gsaA<1g-?t+<$3Z^!VuM&GkY+boU-I5RQTxC7cQt=7E*T6 zJhB-vp%{V2%UzddeK(^Su)q z%Nm2i&KD0%GsRS^QW;Tq%cf&(B^}*Zks@9~rM>P@6f1T2)-%@%eoft?8(o2o6nFEo zwizvsxZR8a%{Y@8w-GRG&1r1X0aQ3NuPPkz<1KiC$NZ{-kia85cbHrU17XBD^Horr zSiu6Hd0ghI#{7x=^m0n&Yn9(s)|Gzsc;$}Dw_P2?9x!Bn7qgEcK~uz5+$=!H6f_6; z6#<=aXL7`S{t}@sV*aD=#5K#gq)mlfhi^{Dt#-{9A5%f2uq2QftrR9lKlg!2di8ei>Si5iqy_IT39ic)VYk6UP1vzKBi{z$0=$!Vc+y_cjd}E>9XAR_4xi7NTo_}^;lB2Z>~2_5(P3N zfLI*A);Xv$iE$a9Kr#Z)4Z~kXC^N+$-p9_%173B{&T=q(+x!Dw$0NwnoD;bp0>%Dr z^5)?7OT=eQd42R-hA@3Vj zl|(Vi%57E!{12SRNLVXwhXY5TW9O^0VH?EzFlWsw#O9C7-0nPbGd~h>&&1 zem}?oo*Wh3Vt2Nzs=@~`n$<1Mob)WSH*x)di5r}f2M3fqDt8S!-3rL676H(zl4opp z`ZdF8R!EeHJ}Q>zhpDoKFU zaH|XhP37jGHiHIqFtpm-e1wAV*1`uOOCJz%LpnU{4YK?~Ig+_*;sdb1&M;@y5+W0~ zFXC5oX4vWTR0GbDt4A+jf)_3D1dla zFNmyGrE8>XHeTE@>=ehX!ZueGu2E4^t$FVjJ2WXfZ_5h_^Xfqb_>r{HWL8QW(XDO! z7q01Fc&7)}-P)@8&DX#v-MLKx8(ievrhpAD@v^;Vv&~P|=b z0~JcYOV%W#={9{99??vZNh;ir-`38My;cwNl}@*1z@LLpY*}>g50tfg5u(fZyY9lpS3Y{#rdW%Pw%Pq=8S7)3Jdhe6 zB+4jNRf&#CuAFsogCnXNekX&~#OR3XM5ud#tG3z>MYT%$yqe`(oGtsvmslmgHg{?= z7U%UOB%@?&Obb?Nq-!D{JP#rErV%i*Z^sgBPVP`TRP3Rf&skR`Qi? zy&PHS-yViM$nKg|wuf-B*-gX);_4kk@i5llm*Ssmm~SXD&AC{4FObGvuU^(;m!w}x zTB;5CC~iiFh`$UEU*gZ}QrD{7Js-LD-@j+#ut(`u%Bi|?w7;%R)`Ho-nspKs?G$>w z8unAxUx`Xk_nOO|q_23wLIu1OPD`YbnR(Tn4Q$$#xVF@$0-TkMaJ~H?Ssf%APJAm3Tou2KJ@8Hu4sRLdTH5r)!*m)FWnw zt!_$eR4b;hpNC$#Sa>gk5p3#4s%tcLfJYT!?-uh%&5vgN?MLQE|GZHc>PpJ~2lFH0 z%Q^p`F?1TwzE2crfpU>KuGVj78S42!`O`!*%=wNm?1AzJC0NAc)AGvc9mznOK!see z;GA)imB9{s5!U>LZgjM$)Xbze&h*m2v*Rxl9)j5TB#9p-@y%xH2JQ(p2X1Ap5IL|V zGgYjVDq5~mTuV?C&#Yszrs*jE#Ny?EP*lo3I!yM_qKag_d&M@pQNEOEkewskErRO0TF9L}=>2e~Zlu$0Wh2<*B0bPILN zR6+<`Jx8{Wu_~P}RQ>QeaJ)qh0@t&Oxi2q2qAMMnEk8N2vHgHFAPksY6iRV)E|-zS zR#D7&U~e+oLaDcXrK=rBQ*7cHtBZLyB$G$p#ZiB8mU!(i#)o`PWpNJPg!-*6=x22t znN*~1x%*zNUgmTiScl35ekL_7TmZ4?E6)_Y;S}VGz#cc#SAX_ZVn6}U12fnlz9m7{ zV~BEg3^+gAp|yfcb9*JQ5&N+x*U51)i*}@L5iejsahf^iTJgTk8~ z6Iskl1HI*?<#6}t;w)bnBe-ntW=0q1u=IHMM4mlSneLvmi!(weGEA+nJH3jy?=4_4 z$J45&|9!G3#LYwI&uXHdrd`u9bkz9JN#jGO#mDB2%M6XI9G5vNd`2w4Us`-f-ngvL zz{+u1qrxX@M4!BI+4vb9mpv-nO(Oz%<8neBd8A=f*e?-UZ_S#sgZ)A$%BukEYl|`y zp`Ou2S&2|~bWwI9)FrwoClSh69qeZ_x41NT>fth-={qR1cJk%1=&!6;{I-DrKT!Jd=u)B}Vl6Azg`@^S(1F+GB0g+o*1 z8t>3Vnmy>q+4n6P#=}GO25il5eo+GQs`_jKV$UJf%+YFIe@sr#s(zcPHMt7uNcDsC z4K-3#$@*l)CKRCT#7B9jzGVHk@50rYA9f3O=Qb?HeQaVA;=`*-?o7s-W6-EN6Yr5y zciK`rexlU44ECAFg?amZ8g$^3WVRd0W)-Z5%v#O%88E47SghGp z-fGa-h~5EMLK%FlQ*Y@%#S%@<`xl=cOvH+E#3e-082K3(D;keVL}z{oLFP1`f?{XFg?;53ncu3SF_yi+gC`OWag{vxGjbOVBv;>-Tq4Oj+#7Qq8Kr&K z0l__VPQ3@m%tX4P7js3j* zvh2C!!CQ)iWxhN@fs9B2ma3Nog=KHx+oy7~R#I)0xATiR{=H=4YkA&q)!m(dp3&(+} zaI}JBt zje~UKzXI7nldlD)KKfsSY@q3^MUJJ2+woimqa7cn9T(q`PQgI)k7WXqQ>AJJ*+BD# z#vDxyScth;zRQ~_o}b-tFcDcit{3FKU|i40;=w)SYjF3-;`7qw>%2CR#rf^!E5BW2 z@sLjPHKZf^S3th{@jkjx{h;u_vgeKWg~2|v^`nC>K8}el9wTyW?hSA<=8vqFWT9C8 zxSp~6^V-Dn^V`Mp2X~L<59!FZ)5YUcxok);uN?!+*oi&%eU=S?@9Hcb2$7%0N_bgg z!skgVp&nIOLhHe!g))q9_!t@dKn%yL3Vw7QR~|kzovz&lAAT4PL^(t6XH`BbH8+r>6cv0f6!9ZpmO{Sx7-3U>yl4aVb?TqEa^ITNnfD_ zm&;PTcA(sb=*4&dVSg5^Fwn9|xFDR*RTU3}XAyTM-MIG-Q#h@!@HRa zZG1oTkEPVS%Yw?3l1ZU-hUT_Vg2!4c02xLfl296Gc5CD@(y$V$|m^g*ZSo#tB*&L5q@o6bwmgUfp*l9ZZdZjd&uJhjGLpk+juYT9WVq3-4`+s1h*ZN$5ps4dUq zN#5AJb}!{#OBcIhB;L^VotnVT}{KQLHJFIZLq>n|D zvkt*U@-gtASd;H_(^i=OAFs)~*%4$-p8w0k*W?OK*=qW`DXDAnh(9IQK}2Pf-r3_bJK%I znJ43Q*W{a&IeblS(>TE!U6W5q9q$w?*8Dt{vL^2$Us>v!TrD}DAg8w!|HGQxe@Dui z{FtrpFt`-wAwHv%*@?35?lvld9h_}NGi@8 zu&c7z&*2~IY?0AnyRnyPB|^@dn`Pq_=XN7xyWnqd{z2+#cHJ}q^)Pk@RI>tQKK&yw!RVBAsE zyeUwsy6;EB&9NDKdl`SL8RwF5Bb$}a9WJgw+NIY?)>cCvlM4U@X#H^M@?QX;kO?^q z07HplGPBIX*Q%hkT$}_j%M-+6gTLc?^Op?cu&DEFIz-A6)#BgwA{I* zz0Hx2oO8`Qs^T3eskNBTDu4a}F24nCzKN=+gm zEW9;$H*tH*^-txaxqEC#{(SJScV_2i+}%>o7&(zVs+G`@5FHsR?TpC{nfI@$PYkH5 zwAHw^?U}%FB=@b46X~Y%N`LzImCG_4z7huO*4%Qcr+7fdQLZ6#H#N9JS2)kJFr#fR@A;nUOi!`tUF0NJ4-it1D8tJ zrH@OcooNOB)L{Ptk(#&A%RezMN-vXjZR0dWT~lpc15yhs9X^W$Z-$i)rxzfwZCz`L ztyOKM4x{g1UZs5;Xc0!O9V~r+hlE!3I>i~RtRq`>_YknRwC^YB`*(D_!)vnQH{J0Q z^nDAZ8}0{$P@u!<`|G=;*gbdsh~OPg-yhbD*OF0RHFODHdXZl!Vrz0|Z(?vX{kIf; zE`U0Wz)c~_Qh1%?e^29Ou)Vc2uiQ?rQ)qm)ve7YBTg|_cF+cFrME-+UWz6gE{Lz>X zC-Q&RjLXSr(ZTL%EbJmbRQ^Zf7BVk8Bvf9!G{e4&1oPhl;UcExFd$4IO6l>-S6C|l z;lHKwvXZ)K|6aW&-YJ@l&5c-V8Gt^AC)Qlar^DJ_lfL19bdhqa>dlZD2&25Hmf|2s(c zOFNJ1yg;B`692XU*d_6IQ0Y&S_)lI*1?=roB)O|UC8V=;=VtsrbZ&C1&bb8sY}+{{ z@ZZfpd=3<{=cjh<|Dx~DQU~>ZS;K?LDh5|I>ytCD#I4M4Lc)Vo@|afg_gh5k zke$}>ge>!Exf4j4Y^m%7)3tS9XOngYMrZVOHhm3|%9#H{Uxl0(X*0h6yRTDhU#0un z*TFe0eNEF!hIxHWw|!ke^*4T@{aHYN>VKjA>CGsXkn%A19O>y2GmUswnrKKefX_`d zK=NC-af8@sXt!B!%l0Mx9-BU2ikEB1bWPT9o}V0#+8q0B4I0{Q=C#zY*`{a8l$Ym9 z$n2Qs^`T!DHSDxGhC)oqp`qO-x21;nZ2C)>`jIXnvnpA`5cjkdTv6LZimo$r26HC3 zjDMY=dx{J^Uj#Z&|4-=rFzX;_u4QumTXaSh)jOO6dZIBM*VCOLMWDG%3hJpGFLiMD z)YKw7Wm!Nz%T8IA*>tG_C6@{7L;PmE7BOLcn98FVEU49vDQh2B)fRaMlV^9M<{503 zx0KB%KI3bR&!_DBq}a0OrAyiK+E7*yeu>Q27@kbYYbiU2_*`3d4rSLR%k~RM*?wIp zyO2Ejw(LTan5%t0nMsTQe3Gt!$7Yf=v>VyB*ZPo7QUiqOEiz@*Aj!EkR~9{?FQvtD z4VlNa!sW-1dcRF=6gZS?$lPh0Lp`Udwx9oohT)S$E|tVgiJhV>?yjxK9m+}`q9NBpGu*Z^ z%bU`e8Ty&dKt$$ax49|VR(?BaE5AK$4fJL+X8bm{&h2h(B=jS&0 zJT~|?s4Lj8gJs<)Io*8yE7=);MCZ)Wj!;W;CAG|Bi}q*uOrmS&BD2 z48)Ws3Jn>NY`43%3wL1~?x+oC2Ft|!ce+`UY`7H(H4hR`HD7PD4QJYhQPpb0SyQ!m z(2OKod>G~d{-k+8M$$YWGie@>Wz7R*gf4N3s&Sm!USc0}rwAgscb22{D<2L{02rt^ zQ;t5|`l2UeS41>_H)~_8&7C)k;c;>QJ#lPLI6-)8u zQan?NPosF_1RABE_KC}o`)@QX($s5 z%W3dj)86?RJXgvXZn8~{8&DwN&@6kARo)Gu8E#&_kYfWfN(#>1yCAppgD)`O889}( z%lW~JdkRKKjeEyt(E7U#cmI9geb=^^kVa-EBX4MP9nqN*?OdOQ@x@T-?ep8Tw^wBj zI8f-k@c%LQK5$hRS^xM4?gcN1UQ{$xN@^)B{6lJHL3QK@^8ViE%zW;>{Ly-z z=lgxVe%9sl`OKU-bLPyMnKLtI&PakInBdC6Uh~4dby4*teUtsSS0(Gg7(#S65W)Bp zj+>Y#p1%-JhSszA-Hi{~dYphF(T@(}=0oN!&{&P;^AcDXG-|b#i1Gv^sAC_IbarER2^uJik%}^}Bdx+OK>upu@@0?!_1CeC4FSlKWccOh zT?vy4@w0T7pWT7fEI%+)oZ23PYBpJ#HXnEWzziWA8LbwEAy94O{RUG$@DDl!Z}LDBxX@A<~Q z!$0d4&YB29xryMJWameimA?5To)=T}Q3@1{s zZfJ|WvIg}hO*F?#dof%O1r^|fTq5Oi%9yN@T-R{us~p;&vKdZhgn#1iH__a&!a%la zH6PMZS)Rae5hF3M^4tdu0&w0UF7fR$o2%K7X$AZ@pzJMTIb-@$Eig+0=5xSS1V{pU zz+cbN8v^iGaX>i&kUuFHYAs{@?sKqXgDG7&TeA&cdA&UQ!OOSJ;+9va4TMlO*t2Pq z)I*2oI{o$~s#Eb_{$SbEM#Mv4P}(g1y$E;?hGZ;W7(xmIm6B(-5p6YlqQA~@k=dI3w ziQ@0kiz|EN`|+ymk^kIs!ncs>U$@2aGAgx0-5HO21j9X5IMN<2pcKNv%6>yOo5i&V zfYK`OlklvG*KnmzCEVDGXQn|x{u&M`VB*yx7G}^INW5;3H{{D6CpG}2rjeY2hPp@V z2q>pCd!!Q!oI8jUw^6{e2sl&o7QjLR2NhUG0dW%`R3uLJAYd#beH+k0#~NrY5NFD6 ze3-cJO54oOR=cbO&sGF-CD^$VYTt8TN+luV&R(2bg)ScQxi3o7Z~}|)(%Gi7I2@Ed zc4pn{YcHP$_uC(vj?#aLAM3?Qdj8Xv}xqjy(K<*vX zYL~5z?{TCG0ZLzD()caJ8#{waqt;(-t$iG!o~E-NVX|(dEq=POqxKa*&}|yEWdG5= z4#DH7My$0f8TwPcX=4fA)A=nMZ|%AGb5jWZtQ!-y043*`8}aQCxvtf?Rm64kxGqL= zJ%NwGpD-U5-572#z9cS-|Lr`aWL)g?XhGxK&*B|63sHlub^pdI$_!7Z?O&-^lqw4L z88G5(KN^TVgx8vC95?Dur!&BuDo6mTk`|`1#({kFP7lP#m0WFhmtprVs50lv${Z2H zm6-;3TGQ3PR+ag+o+c~vbzB@yyrs1}BkV01W?oGP1QNwlqF(HJGL~ik27^^U$9FKRuDS-YKN86= z9fQmU%WC#ULXL4^-D|MWkzzjpby2hL7obyvo46LS6WdlB)+SiCQGf-Cn$HC{XIRWo zDRrdu9EcCt3c!~!T9M~%pI$ze+;vT%3xMA@y+{8gzOSbz>1!Xf)U3Z1r@?C0JLo#qg;+Cty>t544xJIixUTMN%)rq}7e#5eWwB;ECZrKTFl zhSgL9S+sr*3PUuZ`Yar%!jZ%WWa=y+`vBYwy`!GmIn;Y{<>Avy$Ih<%a_uY!2yIrZ zZzk5Q-6#ni$Apg7b8YZ1lpqQ}IZqw@CG{}3;p4=}a0yjRC1q*0&vrLbU*J)W^hjSK zmcXkHN`Vt+a2<%=-y3419(8PbV$0eoHvNd=&=GwW#2XyC7lj&8b1w=Nqn~FEMWcg7 zKRSSONlSOX32A|=-e$1}x(i0`!Pb|M3-*C%-LX(k%+w=Jl6gUXmEO4 zc!J`rGA{oL#0`TBbuP^Sg?%Ms4?1g~Luw>dMH*K{7><5 zM*y*Ff%zkJO5VzDSfr}(4D}rKia<3rg3ow$T1-p4KqlIhV{dNJoS#{u0Ndo$x1TiNA)&*q7 zfsO_XPI7aT?$X3+m>Iq)=Es>f(!#ST%~Eed6XF2sk4?DU&2DBKp@hQ(=cGUojUCb$ zB#*A=)ju{ignPr+r5lxJHd49u!^Za`_)hM`i6DRH?iv+{y&JJuu9+-m--%?j9%S-f z9YB_AM31NFwF%^-DnYns!9t*OZhdk9r@KGLEJ93(33fMfo`X473*TZJQhzDULogjV ziH9M?BSjWAU2ztmv3!3%PGI}Qo#=0IwJpl--^1oFsFd&;^asSM8I8h(sSwA@T#yL! z3h9r^uj#qy@)dg==3Aad|3 z(vW&S6?JYA<8I}xoJc+OJS`oYz2SDxITNm!!#n1xe)-0l^r`w@<4X0|xpls^Su-%M zc$IeJRl_rPE7#Rfw=zNHOE{57G$0yMw?`rWDDj7xoPQLhGH*w$1>?(H_YS~fe2J0y z(;N@`=1DwrDA^MMGOw<&$@BO zEbOp2d>D9fFVaWWmmD;0GH_y@0;4aySh1suNHhjVXZt1^7~$T_T6;O(^ckt*0nUa_ zfFREe_%O9)Ng_N=Ny^iUtpdkv-^Do(&jGrBiXd|V352)@jTD_kQgk>1*R^I4#(>r{ z+oI3x(q|^sr#@(bcGE_g4(Cisr?KAu9hzAV0+Dhsg2k5@!WUpTSl#NtrZPGfnT0*A z)j`|pL^MD@w@4nqttaY4YFO{;MqZ>v+&F#KYIxOzFRc!5xX*EVj{7ywx(**4hGB)n z(V?>Z58%-cl2==}Ci^GGtp%huS?g9JXYV4Sbrm3}q%cNG(Kj z-GuM6F?f&vYz(GKVc&G+`?`$@8$I3%nhygI@C!eTj;9?ZW6=}#(Y^$V8 zP+ShGti_b5joz~8t=uM^2-%!`bOOo51fW6Zkb&ct(OBw)eNYjm@`rT0P2Ap(Jni`z zNmp-*vDgEKDoOe&9OfXK!TzfF6Sk6Za!zuYT;GvK4)>_@xMg;Tch>{mLA^yOP;`k9 z^&i=AbOgkeC{Kvvl%8`cIj6IFxM$rd%{2t*{OB`*KE!bvjraw>BY_rhhzhs=4|Jd- z1$#Q)4$nFPM?G*mWpW5|6&LNt5G^r49@G*Vt-Hwno_&c3)R3z`&ysK&8Af9LCY@>D z1iG-Gh`!fYQ4C1~gIdJ+%Tb(8@QGbhhk3p->r?o$TZWxa4E((91l{p-2tza$JU7^B zjl|Oe>n*?6k5HPs^VaNm+C&n4)6}g-zfNPb{ytWi2|cwB;q)P+W`;5rP_ajQSf$- zw`b|iT6-3M(SSu*Je@~63ac1$T@SzZX)lso^Q9Ppl&-N2v)krbU&=XDb9SIBqUP)X zEP=h}4SgXb69No3+K1#_OTLGqf*ozSP2)-5Y?9M-@ zxS4q(J?OWovz?vd4S?ZTGA9?mz4lzdVeuP)H!^>Qk*(y+r*h^E?zV^{Hm5S?Ozc=0G9S$C#NADNhU>6y zL-&I|;5M17xMbrG;7-mf-2DWRA7h}(HaT{9noRYM9bVKjXP?uU=*-5QAoV$oaO4c1 zbxnW5Ee}GR1J}?^4Mcv_d**bA4ZaT9t;xw%u*l3YL0yCM0bMDjS0 zsGbTL;UBaSOzG%B?O*w${{OXpmV^89`qs~8A?E*@e)bg>GijKEtfz`k@Npsi>{@te z3Fv3L0eJ!a>^Z`CLB)jivwJwxUiw)Df`6iZHUprWOIwdoIB-^fibd50m5dr}WXm3x{j;;Iz6%>ElJ9;l#^DrgIWu&+dY z@nP!FX?P{q096uHcwxl>eG6JI`og6Mq&a{bCga2#R{@`)-|2ZSY|$iQs9yiW52WbD z<2BU;0UtW`&V{(|^Y`pKjE48|2n7~Hr}M00-?ilQ=?+ZOjSJ10^vYz~29?FQ4e#V8 z;q(H!)uS9Q;;FlF*0nj7MvEul^}F72jJ&QV?jG0Y3H*2L_kZRKx`Ck$`|c zlJtXosx|L3PdFO67X=%uH{Ym^8L_O#eTZ@u+WCz28E0loBDY~)J4 z^k5ft;HO0m=bR4bcT4)vR5zYbn(9$6tO>E!HDRU6*wqHV#X!W1Zyo|?SBBDsK&M3# z@QK_@h}Q#d$Nk>WMM=4IV})BO=|~qCj;xP3j{0@WUH>F8I!?kv6AWYW5vyvp?gkK_ zbG#uYGjTsW^TJb-tnpO{$9K)Tqj2pG8pCw=aWB+PuSs`95RltgzFn^!eLkizZ(-ha z&g7&263=mTQhi(_(e84AjR$3rp54eQbv!I};o!ov?j-!?(5CnfLxsJW1dXsK&2Mn; zXjt1AMY_V0cIo>Tv{WnPu0|Rj0S6DNQ6_lnWu3h~^%V@oEWXc81*QN=9G#5lb>pz# zd9|NCkA6bRJ8=fM;6y9ik2mKeJ_s9S;%ba(=aZW9^GG7cUXX-9z6|d{Ika_mBID)$ zjN|+n8&xt&QU2}qfhq|U%R5v>zt7P$Ls6xbOo&MgVuC>!aj6F_dX7N30&|Z?jzgge z>PTs_Z%Ta-H{z)*V(q@!Z&~Xu#l)VZ!CLnQI!n(xTupLk!+QU21i=q*2X|ta-`|Jn z0agL5wX~@kv^61d#%+|y8&Wq$2{VeLWTAQmZKRIJip1?b(_j}hI%uZ>mtD+EN(Pmf zECe<_7`s0EP~z;9Soxt_)OZ=_5I$nm2Xcf|>)**`0=kHTE0|m{9qKJ1(Q&+6)3eqVpv}Pe z8%}pI!Oq{0!zvyTQZdk#nQpDs!7zDcH%ZRq7_wM-|5GDVrPb@@@OCZ_C_os3jmqFH-viHLYOB0P8@byBryE397b$_aVfEZPd z4l!*o>Osyy4vWh%kS4m1(;}y}ZX4|*@dT(#Pv^GaM8DHXV;6k!DZlnBFyK*jj`}rC z(VzHJkk194!$N1s)fY$W>nxs|Xn`BH7{<%cZ4@A8dTITIC*${pX|R-VL^*RgN_qfy zin0Z0%5WoVty=~Fj)E6NRFdYpgTGp8_-otby9%{4T8R6~4QtG!P-a)01g2RcU1c|h zFLBmQ@LRa}q|X3%U5ab3mo9?W*Se;4eB)g)?S2zj1He2!_-XwTia9nO#*uPvttD@! z*mbwo(mt|BjI`E{#ml%)K#{d>KS1Gc$*`js_Dk4NxDVqxJ~079tKKGyhP9c4kHD0o z&06;$5~xD`f(m7=ThAy?8z|m?4Bjt^S8{NII+=(Co&gk+IL}g#)xVrh%#Ii&5e`jr zIwf=-*6igQ?*IA9U>&Uww86s`@~~NJW4dQmY-H6^L$rh#*@}@M7FT)EO<)~(cH;Rh zW?Px?I=u~mPH{&*Oaxl68sS-EfhoaWym4ZQNWP1rkFeps4N35?p<5Vc!8oul0m zt#%8P49!cn0PcSH8jY_>O*k{?IXl`tSRYCoEfV7rVDt|4oV^lzcR|5FLok7Vaiz6( zHP!SNkc1PVWF}>;J&LC+peBh%E7Jy?i% zI~QW8YF0Epx|{$ZcM~Ch2S+$#MToWTAVPvNi3?=1g-iR7 z`9Y<9<3WFEPpV8l({oM+G8sgfxPg+my~@a}CBW@E=60CppwBxdn2-QR2108KaAem% zF2a#A9L!-PO7O+RU}7%~5PMt^`%mb50b&c$MYSIUL9!}7ZR5IMqR3)O-B`5nOM({2 zPD(;^P7rUN-{R-ZF_qoNdd{&xcK?D=r8mD@q2~l)|Idf~g^`>u3dZgXV8^W;+?4;W zV?oT{lz$2AdW&3|*dL|wB4m|?hL_uxu)QGcx7u-sUYq_ldl)@;677V{ztV77T(cRqYwz~U+{fS#<| zCzDX$n(Wz%X!rD1enJ5upWZPrpO21bgnp(hje6rqB z&m6F^66=WqtZs&U8f0o=wDi zYaN|s^_z;EK~Ny!EtC)&rkqm8@_9Yq5eTi*T<2S-_766D$i~(%>9|b)D;r3@g;r^E z8DINoW@R^H$|Pf-m){VeWVRl3rGW>-sl0Ii!662|amFrrLZ8z1@|PrS%$I90P8ckl zsPg$1y`?Vd@0^@`=TzRjc@Y^ydg|>{J!>ZW+(Ck&y#7mm9;Y4qTH=o~W}&Us4a1Ou zw12hM@ro+~OAsh_pTzNBt2gdIkZFxY-_8~dcgA?L zG0nsVFaM3w-=dvp0<>8~!-IUSRW#*UG#K^KBF_I48FZp5U_cRV>Lf%hBH{!B1yu_| zV@$$yC(h-HU!ViD?ryd2{@bhnjPAc5dSLMoa0c*Sj-k!@ThL1YY&*atp_p`tUxQDE zftMjuhkEj$#Y0112vNC%ivuU0ja-~HbQ~!fh8;caF&fe)qL8tO;XAyjz@V4hJw*kK zFaCWyRT444FxL1SA<4pZX>i#42*Y>0cotI;-NgrXfAcYwwFUR|buTLz=hVsEJu{mN zy9aQRdMfOnXwW>6B=^->e?3h4EB0Vc^Ho8-!z*s{l4MzHTTy0X4QTx$S_3*st2Z5Q z8>=_iy$u75?l>uS7DCM)dYqIv-rEx`VFS1l>o~k-Xpf6ueUR+%v;1Pu=BQ8W2<8r$a#cY;tK|NXA@EeN2xLpdn@>F28_p z?4v&4zR+m|KqAdHmnP3Uknr9k299vjooQI#kN3P2-xGo-zC*^G^j!@(2j(46*XYDZ zoG7hF4dWFp!pVQIUDq4CSp72XLlk5|5IR(PWhIR@hE&`ja-N5`{P#!*aj(c=Y}rlv z8f&_{bMnOzEO2{qE-1Hn5SKPibZ#cOS~!p8Y7G*3FIBy+J4TvahmavQ9NIUF``-sn z+|LQ=3*0H<_ksTWWAd;A`@a9OfqbA_Y2k9yDUzlZF&It%4By)k3gtO%7+K)blm(2! zr*xO@B6!*Yq32WTNec<|P4+|dB<%ptw3L=jq`Yugqk8Vevt<)mtm479^0lO08PbyG zgo*G}hCF~MK0)KG-$bpR-?QsCQN!o=y!uVYjraWew7iy0yYR{0-05bjv~v1Gz1^lJ zgtu%uiNAP!N)L59;@qR%Pe&-*_ETb_;*qDigil74)Wg;b|NGs#H~a!?-qI}1^)n); zhVCZOAEPp7uIzG($Eo6(6{7wn?AKP~nv@X@X)bg@tVJ6 ziaIO{L!ozI7M z-_QJ~kI*`P44BYBXA0W3oh~iZYNldM1H#m0HKYQkYmI9l07tNDKS}57)ah1ws&2mi z^^y0C-irE~M&Jq+l71DW1e5D}pFm<;?a0);s0r}s4(`~ZR%ioMA_=$(b^g6Z9j9S?)v zzk-JWdQlxUd$_r{r`G)2;+~}Qi3`2v8I1Lb9%@!lNBZlB_>|P0=+}Y)>KOpH0P|hd zjEmcx9~ViJfopGfTz_R;0f2fB0WdDIvx6^YIA0axzGc&gq4<7}zAwdhO(QEJfzv2b z$9ay;mGzw@@cJG?ObU!G98X+sGZjx5w=^V3-`rQzXN#y-pCjlK_PIXpx9>vCmUvPw zo`vBf$}bPLt>d89*=T^RqMSc|9*A!hi#h1`6wfN3$l!ow3}dZ*38N~ycNujpN38*D z`>zp>tJ-7f5O@NJn2u8k%EoU!o@DJ!Px2;SgD0)t)BvnG3hbEv&3PZM#s3?{wV_4E zP5KgXzek+@H&KpQk|b*_RXW0dA;a?!F8(;`H13N|QP=)7i%P@+K|D11$xd8ul$wwr zCJ^8z06Il9j0zEz>cmQg?3A3M3x<9;5SNZqbU3lij^HPYKPDg$IB@FMyv_`a)GbY= zGr;_1A^alpa<-i%ps4CW9Q1v>3_^-14){t2P||1xb5sQZCfec4NWkg&fnW~~f|#|n zLy#&#>;MR1U2ACbsR_doZ!zK@m+{d-@gralKLS=ScnsIvKa<#oF}6DZoj4<}Cqjng z1S4bupqB}5pag08M01ZEhHI`Y$ko)#h?|+<943|1;cq@IpXOU*#rLCV&kd*jk({2B zNf&^>2ZkR38RFBH|Gt!x+wn;v1Fsp9=qmyFF-LW#v&7=4IfCBgprHmdm(yBXPr}eD zp6AGe4DYUzMM_#}12JPn1&Cof%JOoU7AB1EvS<}iLf4vOyMeaxw#9MfUD9~Z9FXfcr?{UPQ z(=&(%ILd^Aa&1C}X@wdd> zJaF`eE6(_Ee<<$$665g-wnQZYak>*N09PSE{b~`@;t|1jnaKW8;?%6WH|;>Dx~zWF zD|jG>ah$^pMJ#n#EU1cDYA6Y!{Zb}2S`qtqe3I}pvA4B&sJA6O>YpR2++8pNLvIxg zT4ECC=Oc@sUk-Bn(>FfW;R6ImgJ2629K{5;iY=oMaUb>gvJ;kgLGXQ&;QN^17O|Fd zd5m*$4+F+KIiOAkgmEVZVsbdNOcJvkV5uCiLUcmoGH^zlMO+a5@a-i6kNjK z?=gKNsEC6g9uOqiiJs!1DG2h?34o9I^Lqy~fo^hpivvF60QVS`Xg3GGE1}@tSOst8 z;4e@aXbD((@2^f`Z2dP)uCjS@1xtc9k359pz6brFxD$!^8kV@LGc~bo?{~g>IehXU z#ENj|rDuHgzoyfpt(y+wZ$5_Rl@{q7ll*%=cnDR9Qp*42;3|eNF;{{ZqVIk{2#teo zMUaQbHce8{K0KI$QA`1ksXOT}p98zQ2L;=if|b&XGMu|~r$Ofx5%D1K!Z$f;hbSOi zqMCyaBFM9c6+yR~c(B}{;J_sC;ORZY1B%x9RAmd_dKJ>U#3WMaqI%^{NyQM(>PnS6 z2WcF1D}t0BDbI7a@%7VIAd>?>4f#76vv?<#iXgBCM|}Z1u4blSnnW%6j~x9vN4sV~ z{$$O;)&mXCB8o?#n|YWDo;W?vsUouzViTSDnbe3_Fu7)yF`_C6lsBapn#+OaqN&Gl z>Kl<-SJYW+e=7?xfTKpM0?-V=D5Gm03h>n>R6dI=fQ>t1vv_$pkxg_ZPRB69HK&*7 z&1aJFCl;5W_-phz#;`9HlwYUP--Gl&mdwq`?n5$J$Ha45TLMiBIm$Jin_#7qRM$!+ zK`9(NU&iLBmgLG%5`G!SPL;8A6N{{ns{Gt;#fVimx~PJ%aGV%MtoRpL;W5tQ(^+C1 z_lG8VgftcyC71g(7xa;n=EJYgG&*;J?zG3El|>w_#x-O(83Dy6CIOmni}-LP;|`L& zlR(~yGr!>;5#yTU5oNAvIJxSwrCaOhz8c&HCCWc%<@GYm<6y~bcIx4DNt9TCV3EWm z-;2^tI@+4X_lbFnaX)M!ez%JULg2qZS_Qc>(XU32VnSmm`8(^#whx02BdfueTAuv@ zbtkfeQVz?eUPVkpPy>`DTnaA!)epurbP{q*Fi{xOsRi*|)2ZJ_q6U9UFjj&ia?gP{ zOc(fUHWj%|e5(a$o7G}Ykfh;0I*XX3NW%gKzCK;+C+!Z929n`>omAi!vFUUNDr`G2 zfw1U>u$6(YhXZk(5BxNo!a98iRwH6lfK<1xUg{ zGsFOz9unG&!-zQF>;DdOnRkZ5{w7R}rgZOv1Phre(T4NN5aukn)?X4A1J@5p5dZ_j z>t~qRRNYhMynQ{Lgx#|mHypO4vI)`XmA4~g9Az#|gm3|v_&IC`^tSrj%!&C({%ax@ zKo&nKlC(|)OHzrn7?3tX-0wSkLDI?*2cn6zM=c-?E829{`%iu}674qsH=!K{v~l9P zi=Z7IKzp-7i!04q!~#DaiFc;|oAAa0?`7hn^A{%fRm3TBKSMoBPC~9?NUpG&2H8TL zA5^|M*fYl(OU+-%F++$e(zD)%2@H4Nt>WxpYUn2+nfk*!K!&a#!(SZqip4>uIOsXC zc(>pghbNszvSd2Nl{cz(9?y-4Jo(3y5`||1o-wF#2rP6+7*#pN_cs7JIBHyCFds4R zbIj8>BL=XE=rKYlItFi1A!+9J4Md8E33`|bqGN&Nrj(w5#*=R1qH93mh?@USMi{Jk zn2O1G`l(pXdEM0q6|*_!k_%DcIWUzN+91ylY&byImKZ#3BZtRWWwfaF1AiB5Tr@4_ zrS!uM=vKZTLi;Gz+7M{c`!FTd+P_By5DUS8WnDfvEU6sRus4B*L^j$$0W9#FOy5*C zM(B#gz6_8(%g9`j64CS2xg2h&O(2C_JYd8jRBL}o#^GSX-9~r}h2N{ftHcr`9P?tW z{SF!K*@_QgH=-;QHA5xI7l}srPzs-*lDtJpqKzmNQ)8b~;EMI78d_3FfrEokdWPVj z02n4?sKZGyxhyFmE^~_6JsVf_CZfKaD9>}i;nelxcqC@Pv70);DJo;o-PI;+M>tl4 zpB~IRRFPZ(Mzs zK6*FgJ!AVLiOvz>)YuVWr7)+BPvFsEGnfsV#aA##!umGOrT9Yltl#@R&icW$W9Tv3 zU&+kVAEGl@aCQ@sqwg1+(RyJdAnz0K6&$vhXJEE-sWb5N|nHH+4EBG27VIf(MBbl3o4i#z4GO0B3^Ki^tBmP>%!xDalH*``H zDsdF?-H02CIJFMRCpl5OSTWufs9mCkPCZRGgEjTjQpzu$k@T%f@%wzhI*Ad(a?i5v%v2c=R zh6!b}*!($5ceJGCC5|sfsWDnAUt`N8#8pJ=V;nyf>1i!RrrF40laYpU9Qwn@ zDRgW?;@>bNSj2k=5Y?$ehsJ&j`uCW?s7b3;QP`urWdO!Za*AwsiCeHKidM&-?qU)` zqD@%f4XH8%(sDJ-Ig#LMXB3Rw^oQEOBXpTpg+%*5L!4&+J3$*@NJ4z~j^~1n2^7X1 z$M3S4Ab%8CZ7|4s^81O=oh8y2SvmP43q{t3FEVMfg4t2s88;o+HYchDoFHB(`i9oT%$h4aN8CWc<^;9w8k{z@z<~IWlTKQ~ zUKGb3Mj%{5i%U|$E}SpIJ;mbTV>o;v(PCMk8#?_6G?^2TgszL@fG+2>=?LKj!q*X_ z5VqI@2&V@S2BT9{Cg_I}fp&q=C0Tqo(xhJQb<(+=9@*HzG)&y5*2~PUT@b zH?o!0+h`k3CJhX6DH4Mxuk`lJ&wmc(=dBJ}TxRY2~YkVpka037RmAQr0p^12ci zpRPMvJxG5OYg0yo6vSfc$9L5KU#p*)f%?f0ll3#DTm59>)ICUWifh?nz)!B9VZioN z>ql1OkJQd}4)#-Vs7}tX#hbsQoRrc$yEw zd5Ndx7#&n-J+6nN!|*iHkceSVeL>=O z9>O=JHimlai!?Y68z-}#L5;!bTFzlTT;!iZtphX06;pWEO*pr2zS(Ir?TrK!kBfNo*XoWqGZ#|Vo{H15SAEZ8}Pld^1&k|^OG1FEpa zw3*667Plr5Zy;4DKfqwK104et^8qk;l7+#zmra=90YPEH=n4-ZnU=(jjA|Ct0kb#^ zfnfrJa1~w%*J(o7L4Qt>Jr<7NWm-&(P0|XKh_+c0C9x(NsSF6pYr_GEsuaa#+uNs)*Wkjk#5EIwA!d_ zJz*@}VXFFqIT;#+XG7~&NNZnUe+~xowC48(^F%PrhSs+(fT|)G%+orCP>Gk@(F*0F zzO~klWrl_vbp6Lf1ZKCAIar6{)h4rRXi6CT$vTeJR5>+Xt{$?}q)-(<@BU65V8s#OMVzfZ{H5VenA2e^?t+bXN(naU{Td* zec){r4=dt)JXf3*SjL_G47!W>Z_%r$i`G*(@4aL~I-@gS52rT?y(HRPnb#)%BpNLB{ZAfn%>2dn1l~WkDr#3b!O;p&0u$=ytS;XvC3u>2aVD zIu4imjkYR44h7y=OlGCK*1!a+gSa5EwuvJ*0P7D>$(&-8f%V8&D5Lv$!=GtMg(^*V zJX7y@Tz71*cRZy#o~w6k-A8&WvD#))j(@GSKSv9cU8l)fyUzb%w$_&WKf-X5n;PoQ z&xwuqzYuRQoMIfzqy4M*w2LSyvNpLpQ5jLlsIH*9SgFo*( z;cIB1rU#u&!ikrj!51>Q^@kHQ*_;x5dij_*hGXku67JYWoz^&YiJB_u&Z%JaLw_e$ zr~izqmogP$Kw`ov_olG=I^wqqKGQ|Pr$HL!ttb?22-OdoW_PHWKs2aB-!NZx4uO`q z1lAdjgCUHtM=f0i_RF9^J4;48gNqr6d=siQ2Gbb4S7pbz%{!-fajMVwxXzn~57inc zdvDlzz4yAllii5(8vbNA1h?6#XtT*;=chdyT%i6DBO90rV%WR>&!6xKH^HFF7eM?0 z5y@3vjEVOGs+OJSxLOiOY78;|dJ1?y>_g)HRGGm-@wJy4PX}r+yuPjwTqaJUgN1mJ z&@Vv&{NfEE&L+lTz|ORs*~osdt+mtqat(=}?Ee7pX#WQ!eguE8y6z6JIl^Fb1lT;? zU^9J83udzoY_s?HpoxJ512%sP z{mIYf5S;X7HfNme&F0P5fz5yXfY|)*3C>`l*!)GGY_95y&DeUl3(V@l<|Te+g3a^% zA7FE;{{w6$2f}jB6%xc|i@|0~Fq`S)qHOM#>>7j3l?P+@AjG19QlkRyl2s(-9NxlY1-#kWy8`-6Mg@#N zLKWbo#Iv7SDEiUh)>F=<0?=0-Sgf_N;AF7$V<#xAN01pSwLy&`)HT59`s@BKhX&c1 z3^mw46s)_|;3Iv^4CZ4x_&7^E{mF&+=s}DW*Dq0;Ufi-k7R7A@18&(RKP9&^;e?OH z_37_>bL+#2;Fk6`;?@;1gN5Ql3@5#~1wF{ms!LO$8A|SmEhVhgg>QIMiziK{3E|>I zE-n94-(jo(+^Z)3uDf7%v&!(4Ur2+(d?H~T?w`}&!H{R30b>D26S{jE3Y=NR_&Z8> zG-4=e6Nv_{hJ!#+h+Ua1A;j=$N&p8z^^+Av=c#OYI0qEj%Kisxc8fEVawo2lbq{u-fKEcH*s%dk8dKVkJ6GV#pa4 zP{Iy~b?*_={sX|=VCU1|$O3dhQig$)OM*#>11XpGAO$FP5PRTt0L}*vfiCe6c)=7? zei~Juze`j_7*sXUSaNZyVnNmLV5%+yRih{ojIXG`^vczu!-2?QaA_{l^Sh6J5|?tB zo_heyTuK5xIlMl$z_0D5plLQZn#7KKN!x8HCv8_v_B0KnwSRwS%{#osVC<#QVz~8} zO`Gj7*&&IDtf?A<8Apq4jN1ODW$}~{_X_-mh%-w+#rf>;wGr4kZWguKXzp}Dj%OZT z0-HXm;vEHdBOg1>Cpf$1j4jTB{Y-udagQqAfR2^56x_E02&Xs|chcwWr>vm>Ck>K7 z)DV5XB@^aLnZ>jLZxU)mL6pJlt=!K?FP+%YiT zn2V9$d_9e~)Z?A{10*=S5C7b0G&@}R>2DPlXp4sJkoPE8T?Q_ARklOf;sb=i(j^}I z7*6qPlA1K5C$!d;A{S#Bw6=ghn0)A0l)pTT^cZCxSiQxfzAjHD%@11nh2aHqh?0(> zXN-a7%twCY_Zs;fhrROd5qHRlvDp7cdtZy|BU^FKaw=`d*qnMkE}(nUlz}^QX=UYw z?@stI?KnEghw}t5mw~->l4}CYljF7OF`j*7s(Gm<1ama8i(;COL zl2s1VN-x2*`oHyqL@RwFX&e&~m&o6eR#+A#@;%-}s{%`5u$1m#DGCga8UEZ4feuVZ87NfG_yE+CuUE!J`U{Ksfk4tfr+9c6_>XZD*@+>N8p=~ZCBt&CktZR>&>c*F3q!c2UBGFK zo19e?vcd>+#ujU{)_xBb*X-FvoVM0}fxj&t{?Ku@zEvNZa2ad?R_C6ejFw2_%1}3K zu*o7yb7$!H(K%B(U^)SERPWh>&@5bHU~|In+*&veWPkkrFJEgSD>LA&LJ-)0#B@j-cy?04rVNT0AF_0kJw4 zDY%m6sH6`dDX!$Y1&5_Oa>ZtR5yK`m)vTGSxg0fn$R-6Wcn5!_feK7B$u7lH6|K34 zd8%S?@RSXdB0W_W0R9$nSg3@aY`YIrhsWr1zD2(`_WM>aT60;xZ$)d>+_~yG6zd@M z()tu&nNjs@@`)s8rpJc3ujRDS$!sm>w&>GilI?k}aEhY?i{zlXp5q8EGp0T(Po|7_ zN!Qi50bDq#OQD7*)f+=DluWQllQg~`e5 zJ=8rJzoGhcOT9EyrAq{(>OItROBsSABi$BV?>R0*bX!clJr822?mh66GgNox-74}j z&~~7rIrFBA47~TWiHTuyl*H~Oltl7lP!zevJD_)lxo^3k#Y{ULHMhH9#dI}pb#O*q z4GjlFC*QsIA&k|y@m&rGH0TjEjSa{E-m(a(tH0-UpXUwC#{kCHrNp?bp3VqZ5_ei$ zVV=&R?#q(x*{;ZXJMPkD6%id#8rkNCGuFYLHQAbrzWo4xTOHM%T;EK*4z@>Qn~M%E zS|k?@HRCFEHs%+w&EWYPiP&sLl)$Unnv3E*IdI%{cd4*OPaWGuPko+o0Nh} z3_xn2en9>ZL;tJ*|I-h6c}*jYJGiz;V&o6>qx&CLEWDRVwlm2$U9dJ5ku!k{)P@JM zM=ERItR7MCX(DR+V6bOKaqqh{=4ugh-X*!}TQnado3u1AgKkWUvetf$oIIUYYwc(J5^1gd zh+q0!YY+1ami?$&J)Hxrwfp&HptY6^5j>rPthJ>1c{-!5wX~k$>BJf!*}*4QXUjW~ zJe@J_2zfnssuwkBj&NT?ZJ94CG{aeqfA-;0U>R6!<)Y@{#2nFRK??L-ysR4h1!dWijHcfjtcck{Kut2O2COBuIs=ISCXeQ(v|4x z?C-kD(;4cz9EEqqc{&He$zJCm*I*RbHJ}?x9t(LKzZWAXqFAPkyFOPUjGHW(_uI)a zgA}>m9jI(PM*r{->Wb-`WL3-N^ppvni?BznQ6FD0`?D@U;6C`pLLug z6=OJLGH|0zvv?T6PVwkKx<;mfZKez=nPdp}U4evw>tve6LQa^^31vUrEYc)sW=|C3 z8CHWC9Vi$H3W5<0;>aODcm70CDRMAbT*<>g+3!y)ES8AqKUQDT(NcwiO=uc~9&0-65LOiCSe?>;4 z#qle;D>b|9QmHv@{i#aLMx;RNXcqUVq%R|>q14P5JMo3cT3Cy+><8JJ?!{;TA`*Bw z?Qa7bvMX(YI*o=2%Sf{cD+Be;I1*Xe!}4A~RAJ*%+VFW;)%XUjvlELrt-^aNu9jX6 zL-442SVhBJX-1UZiUVR+aUB4I?s%o^=oas{pb|(eO1-U6oBZ8eyx|Uluv4EGYVNU~ zo5-dc_M_t7Qn)6$sf)k*Z=iE7n3lXRo~}B<{dx=D|8yt6YuXNTsD=eiAJLMJUn9LhS6Sxi%y&iL?*x}= zQWK1344auwGITgvzqqp=9%OO6jCQOWk%6*wXc2eQDh#M`lOJUAw{VCWm>`P!`ClKg|)vdpvg&hs~7Nvv7y0>#8K z3jl#+AR&1NOe$FoG()qB3ols=dn269;p4{QBLvI)2~m=s{DWN)&06GeOuTs&iIqEV z*!H)GJBdAxF8Bka8Pj6^>Rc$wKZK~Z`FU^98 z+Tc4EX^cis69&(ua3s?Szolynth>X-WND9+W8Iw=Dn`qX^gYN`oAhP!xwTWRyHl5( z7XxL)NQdW#&~*uRM2wUX|6=18_m?>QPWRef_2zW%QjafW?WL5ECm-_{nC>m}HN%pS2PIAKA#lj(rXx7i+wg|x6aYqF)<~78cFhfqS z$5Lamcio$MYmja4{9I=v&3!f;Fk>^xneINL*CuH35Ck-!Y%CmFoW)u+H;XM5xMM;* ziM_k}W*8`5vf&&>th3V6B<7!QivK8r7)0Rph&Y7?0+hf*nu2T_VA7Yth3Mo0Mp6@C z*2gjH$GgLHuU%DY1k;X6J*mH@&gaAT4up}`rVkNoI&$@QjMmiZK0#)9ijPzP6n`V* zN1F#>!o3s}T63Y#`qndZ9#KhX9RYZG=Fj(3cH}n950P-qqEtc2x;EgPJ@cOtls|Vf z?CnTz_SaiOj2(0*fZdWO{(0WeHl^1t^I(Q+@-&5lXUY33O>ph1?!spJyn~m)JB|oddLCmd#WT(v=G7B6X))2DKDbM!Y230~ql;gf4_F>#Agn!vy3E|uT4c>Wx}$MK`JcUqfB zFR*D&{F;{9wA79?t#I2St>ib0wBz`0`*@KSf4faf!S5u(8pGykHGf#76(4tKd8T<< z%C~#uIJKHTEz)Yfb!zeWzW;?qTKR(ri^$hD zFL7$M$YXD|Q_CyFbJrp*d*>o;ZxQf5;?x?EZ#i(rA>FYN^EC7JMOr7)9tN(C?||zR z-tp{u)Ty0*dXaYjzC~KzOHS>X_ZMkz9Yr}m1l{iMZe4h3Qeo^Lpt?jWzTH%wx*|A95zITx}^|y<( zUHI8hu5A2@@tgYGBCW}iuO)!L9ijQ!TgMk^Cy}-hWukxiIFS{$WB87!%NA>GsJE!> zoS9j!0#{L5S;Z>EQ{DFK7D%-}=JG{ah1S12XwccNnmt3Qe{sPj-AYsOXp9Vy+@T-P zTG?_k?4vb8>s+i6qOcLtXNt(-dcw-b)| zzRR2KSXJ)2C(T{Dl*sEv;Khoqu)~}xzI*#Wj_)}9W~lJRcw&&yqJNdzt^GGro5$}M ze$@UC<7c%0^4o1%)I{LHPyPcl9v`<@qlyVjqq;)?ah^2Q@U)qwg+)~eK}D=bcB$HF z-!43-;~A=5exb5UuDZ%9uG7XMqi}mg5k#HpTm#-v%P}s2Ijg8-S*R9C*`sw#lbMHV zW3<>)OEt^vvb&4s6jiy9jL8Y7U4Mynf(e+JK&DuN%cSAO8IoC z=LYrsvwDVHCF2*W=QHa0t$Nx<%lJa|d_q0nQO^-$Wc*z9d{R9>SIiLLzHmc{>>N$0+gnL9i_u+Xfq^CoDU&`Wq z+LxuCW^HJn?c^%$FezC*Q$X@S}f)o$cg z^dkxLpH4->$Uvt{(mz@wDeRY8ROzZHyT@p?y(qJ>aE3W5D#|JhAo&cC-#FL?&X{Ib zE{CjT52F6uoLb;2plobR;6>AG&rP~;`iWW_$TUqWEHtSOs|Q;`sS$^0Ve<=0+(ne* zRN1j0MP@>U@zsw65M}LVUsMyh(`J#xJ3!$2y3yQ^I_A+-VhKNvzJoVDO z5l8(9g*_36YI81t>Bq{-4DThq|6^=@wuJ&SqeKJvaX3;7Pb3avpGdQP6nP?^yEF)nLq39v`+SU`)@*+}KR7lk-h=A0hlA_?D zxm#1{DO?x_xYA{!AZfZ#RfSJ86bNcm5GQm|N`7WbRE{cFMZr?n|FSSs7+F7s%-~9o zTIj(Qqd$mHsivWc5tXE%T$~H(oSVf_wX`_6AL&sEih1G8foo|1mMq}!iBts=kC_x5 zL%90JVS&!kospI~MXOavsCFgO6PC83qS9Z6UQ((gIiuhrFba{qq^Q8(rT0Q~73UW2 zs46cgMb}HLvS_`au9rNrmzBB-R+Ltve0{ODSCy!W?b`Tv}{!zK3Kf9-4_+p<29@P5&r@`6@N^J`hFq95*hCh>5iHAo3c<9hqIRVilTD zQ6&jS-#zO@)`R*HTm%MjG;&q+-0g*0EpGp!f7|i?c$s{E2Jf1B--`Fm6<1N`;) zS11E}Hp!Kib&(A?4tqo_{pVL$r@^~$;0+b) z{jgKJgqEE3XtJh>NqB|kipenv9Odf&N)xK5-WjuCAvO%;6SQED0_XGVo$-1Owzv|+ zb0D8~q_T8d9I}a^jH0lP!+RNlos3@twc5$B$ARKH@@2m!PIJdL~P!>}B&*qn)MOhB@Fdn}(#wa5B*IDU)Z23XXM23&LdVmnqpJJ|g@eC`~?smQ(1 zltcxD0`^;6TGxPi*f)NIOpFqIS@CSwW$eY>dmT2Duuo1${IK3S5`v(v5NJe9(B@kU ztxpAQ;!UH3fla)tCYVk4ni8PYd(+OL07!zbot!mPYS1{TO6ms zBRYeo&+YT4eWezBi|d8l;#FiFb!zkcOg*v#KCSj;nvqE9G*WDlNP z;N%Kks>~bv`t~6b4qk9IoYP``s)_sv8jQVsayR~jkLOL!))}-_X0Rg%FhMM4c5Lxo zSrT2O=}l{~>@zSXJ8S8RQkSD}h0D&G(#+CjWwc_&|IG9kT@C#UjgS+R6{VOcQvXAd zp%$!=R=AvHs@tL!snGC@qSECqtW**d4gqVSkr^vWiz=a#%4wyhbs}%e(4Kia?xsbX zP)m4L5!SRS`eo0(Va#eu$l}rV3mVRp=v}5zr2;7=&T0w8;NO^=+^u=ehtpOntDU8$QJu z#)fxo3lf{6C4FftN(<+f(=s=YfAr^mL`PLFE1O;5T3T!`s4Sw7+~Ts5qTI5I!U%s; zB2?Nj0EZ9iiK{Y%kN_a0i}F&uCoL9QIAu1&y`r!PYvA}tN>?YK`cb09+Lw;6EZ!z8Ue z;3`A%5lI9CZU3X^=~KZo zRWDhtdrFFe>R7Q%D*=p2^?mbKRJhy)C0WoWi+ad-F~BaVyt^_}lIt#^;;Q|gK?u*m zO4qcdG$&YrJ(=K&1s#WwIDEL|dG9BasMWLGV3DnnT?*M2>d#R8ej&7Rse*)Vkt#<#ZqU% zax6LKA!H1Nn3on;U}Ph1YT7B}Z~RM0Mx)PJtWCi)>E^}S?`(^;llY~ec_y^Y?Y6H& z`?|ERQ*$(p_jPJou%~*v+}Cw2U(EZuO}m`_ecj?;U~l%`#oDPSu+Q4ZzHTS>bZK9= z5O|L*Tg>~qCD_ls(7tX|DfV{pGxl{Kr@dXZuWQ4;EbZ%V|G?R6UpE_jw;9W^zpM6j zlQxg)TaIp`@`7BjV{AbyAyl0cpCe<+p)L%2=;V?_jOZV2mFuh z>ux>`o~&G~9S45``zy3u{fw+6R1od7puucKz>qYO%5oPCv*ksFJZK`=fFGJR&Fw0q z*$pkGrD4ruPQfbFKR3ATc=iU!T5+!d@EibWr>3w-N{yyL*68Yp$TYcP=XBAg4mI9g zcn(f;EL%qFcy|{Cr$HyW|g%~Q!%Xr45f&3DGv&$}X6q2lRJQ*GhOhao~ zS~RCiM zpzyIv!E`*sIh;Zo=h`)aXDs%mg4yV&5OJrWWp&_5dskAG#77BqpiVraWT4cw@ZE-R z8r~?}uNNVFE$Igc?~iap7eVkT1VR(R~#vL+RF1r5L1W!!&KZFUu5a}aR-()>YG!AS^;NXss`BX>n1 zX}OGVG6ILBp>Jf+X)fq$cen#=z7EmR6pdswfM7Y5cLCo*KR5+(F=>7DmJ0RtoF6F| zxfKN%5;UyFe~tbpL$hjEPLmse8FiMX8o>0%!R0;QxdcLZjU#sUNhm2pRp3kc1=rkF=O+CL+&!5eZ;RWjX8}SBc(;1Kq@Dw2$#9o?9#hYsWytU>_57oHex;r> zX3O{o)$?=pyk(9I-=v<6>KQ&)hF_zekE-Vn>N&+J<5#HXgX;N;dWOxD@!9J6OZEIn zJ;!9q_$u{$Q9Xrv4$qSDrRw>ldcLckS7*!k+3LAbJzrMOf2ijitmX{ZF>puHj)Wa^ z(34CGjnVGYB17{ZSge)fSBu{+{0`!G7{8PFrEgiRx$vvSFH))mG|imC16i+4PYXs^ zsBLmj)lxN)Ha)|q&0P(f6B?99YczcJ-tg>}EL5AJ?E+W?rgTzsMAN}P+WP1YNbNX8 zQ)+*x)EUEN63T6Ywxt+`Bh!m8Imsz4EStvGE(WrY9>}zL+E##@?FA(zaygtlF3R9FTuP)@6~-3mvwQ>?&!EEpQHGIR9{TLFlWsM*tnc(27)+k6vi!XgdHHi@gzS z6uA6qJ&g`D3pcS|(Oa&F>saT;SZ*W51IAjsnq4W*09WAj&`UtK(3^*R}Q_&B^njEgv}s#0@h|B{`Ru+ zd$P+gIWNDX4AaaDGIhK*wVbNJ?fNOuFth>mFu6a8nw71ta&9RpAx0jdJtVAqdsQ3T zQ4+`hs-=(9%0bNl%=dmmJ71@j+_f0G2^I!Wc~ldi7cC-23-6QOpv5s&eSwK3@jIF& zCA1B?Ta?yM&cmQ?CDEM3qDAs51J8b_@!qRx6$qxx|93f4rT@2bhN&lV?k^{3OpmeY z*RxwCW z7dr-a_M*T>p~i-nGmLM3+=Cq$Xt@N~{0lXe z!5pc@^2&v9862@`+g+#u;Gq2FVh3x#7iWhtUTQY&5YiJrjN<(F_-WHRg7QZ>jK!~? zm_KxWY<*IVQvNdiPouzJgsIw8H}n#qP#us-o6OCwZ%Oc{%h2vex)`K$mSGjNyDV+_ zoa(V>s|c1xCxICK>`mHU;EHBka`rK88Ri8SXM$0OH*0SpF|`S!v= z8Hx)3`{)ODPfFPoYlX;0)}rLRtVqpr~<2?ywjoOmG@b6EO*&Jw7S^XCa1jyWc~TVXVNR)bNogw9%8^yH z$|^`y|EKv#5vQ3!w|s8U4kMrbnK0zVs=e%gZkHwr26M- zoj^4L8!N%{Qd$Ldmn%i9Z?;H93p%+UlBCJ5rLV_)s0X^NBAia49WaAMJ$X!fIv~s*g!Y8~ zKkD8Eu&UzP8{c^+5IEtDh#D~}3RVStl+dpMa$-;d2a@nmK|^vNkt8QQ4<40Z^nj3V zQqiilt$kQ~TW{;VA5u#t^ePezTD_L5R#8zAsogPXBSei7Ip6QMX1~r!!1n&{z2Enb z9`>F!Yu2n;vu4ej$DWblsNy8C%_+;C3d?}ajph)9p-H6o4Q)yjR1fs9buknO&MG{W zSj-N>LSy2|S|Jh-L*l+3vErqVT3s#z9fBmX!&a#mW?ZdY`y?pWet|rOB7x)O$r7oR z;uOUtwq0E8ezv_!GComjxE=iuh?Z8Y@{12=k?X_2RoeKd!nG~Ag`223roWHoO-p|x zlI3t6-L1@4(*C&?ZzANy$5=s5=qHmPmg||Z_0dh9ma`cq^0f07S7?P>FxFAC-3fEi z76bj1Q^R~+HuIWpDuXq_kx)*V)j$i89`^!m8j_`79|+XVI(02#3JF*l#F`u7fEj=N zOd(+{pd4tfi9)WyRd>$SE`uqu_NbgIHtT2jFn%}8g)BR3S`$fnw4r_1e`s{DSQN`d z!*L_$Xv5t#W32{J&#@E~o05P&q%S(xhKP=Zp;#`o)J=V;hFx@OrRZSFbZs5>>AkWX z>ws8a!(USx!oxJ|)2j^xX@Rle6Wf|VVmKBP`mO+V&nwaJQB!OzsEx;u)c6~rrLqX- z8c=M10l2`J}kscbvu5?l<+aB&hGr;R$r znw+^H7sH}lT#ECwOlyVCT%LGGFK2|SVfG(o1ofyQCerP&> z?o20A5G8>~$WQ+Belb@&01W=zSeMvX{F;EY4grHdH`doE387WqV6I=ru!b3jb15ZZ zEofe)CAZ(ex!dI9>T$-661qk`R;tJM)Z;1j=v0rKC4zaSdQ_;#o$B#R^>|S|K30#B zOC`b8>T$DrMAhS0>hYXVzH>aj&VcBsb@^~hKxm=~zWBK5dcJswhzed=*kJ#rRHf-BTxp?ch= z9=}$P{p#_ldW^ZA3AiT97XB!vHTmbR&sc1zyQ|?!(kkgWo&|Dsd`E)~Py@x4S92V+ z2#5M5In0G+O>Pi78#9Aj`q0c0Y?^iAcy0?vTno%IYI7;to1c5gru2&>T@%LR+;T=IdqZC=d<>hzk}52<5JC7k!uq>-L{={`ErAM1l8epda$8mxV-W)Z9U#lzRqYP$wme`OUm zMkMsT;KvzE6RB+V5-k_rs!N|xz6^8V%VKd_sIM?FEek7$?^WRSS9X* zfbT~dD8{OrRC%OO_~)`phzYPEH!FAmPwv(_a0fUX`Gugk0D z{teH&muPv%z1kyq7JTZ}9=!)rz88(L*Q-^Z#P36|mUrJ0?I4DLnJGnD?nhp&5Wi~t zG^EMK@9JZKA#L|(UahGIG(Y!hso#S};`um!nO}IdD*PJoTXVvzjs4QAO~h{teh=ce z8~Ld})Gg;}a}i1_;`%s^$w2LP6!cncsP;>F4%6P0=Na0W6>7nL8J^c-(&YPiCS&8r ztLF2ZWhz{0K7VCCKQW)@YvwLNW~G`(=zJ+wnkRNnNAei1dS0dMGtjS`Yde`l5^%aF8RWmaJ8~c&U|D%8;OJp zwWAO`7bx9wwaPUJzPW_g@bXOob(;9=i zb^r@ZZ9*I$D*!(zP!{ooGB&e*S)gSRh^5(jSs4wV%DFa&IU9`?fH{wdW(#e zZI%@Wx!OFW716esnJof^OaZ`XkYnud)66aHGb15v+g2T-$U38+2-b<(1C^-I*sG8F zasNGPhPRr}`|;$y*oj-G+kIr_k{R0R`pBzqoS|*QZykR8OV&JIyCuo-5%`u5bTbmc zWTx!?=}6L-QT09BywU~MCrkCQ!?DffJWP-0m-%pOBEFo;M%y)pZ!~b0S@UlXL0HX8 zbN5BL$&w1+9>v%l7otbGgEFZ)2`#AlX~5-kvNl`*c>l8uk>0`BF?DqLuB|w@xvi-& zrs0Uk`N*^CF3h=TT6Ax=oYA+0-#D{_*vS6WAIs;y0-emp2IwVRRZQ$VaIsC zqxvfB#L2__Dval)y^c=`d-P3Y8K2y}KOS>XJKB6Wj_m|A5uc^)MV9jYnVhNmDVXA= zz&`-=7GUb!=i9ms?H8;opsusjt=&xCW^A+&S@PD|5C(t^08ZZhX_R|LlW-ICfI+28JcMQd(6(C$6RV;P&hhuXbIm_xJI zX!jn)oBbhP$$l{L2$^_FnCOAr@eSUdR4{0t133!sGqnn>7XMAva^c+-YBe~!%db^p zRt=X>;yo9T5dN*lv(}Y%3f{el4{8Cy3IV$UzldO;5Br!4xL+Var1lG>LR$`qUs6~( zkH=8}5zIp8;#p-sFGBbmCJb5eJ5Z|F=knF6H`C}j3uh1V#7+pm7N0w!d@S8osjDkk zR|^1zUh4-0bV%vYbO1N*#sTzl-kuqUZy>24=ey(7H;yVxe5{EUv8VeO&If+-80$EE z-sUA;&5gL9JX_t#jY}q4a8Kc@`kmc~kKNyee@&6&TUB*7weY_2({-Nwlk0Gju(1(+ zQhv|+yjzl*!+EK=%so@Xv5whz$ak(KFSR)f@2jMCYs}iPVskNKh?g~6AawjDD4lUOPNQs7a@;^U4wf+QcW5(S zLYolG+my(T;V{7)+C)~Y9-g(@+g#G!jPpX}u+{I@TA$%UH|=kVc@(1OyHS)tjvqyo zanV>*CtB;B{I0-f%=rzz-NZGKja}Mp*?M~GBcNmWp}hDCS1;j(*@eS!zZE{otsX_p zqk3bnePZfDTs!5%J&rxb%V)+>?fLb4v3zeAPF_uKJxYqhKoM8Y<11ie_6XOhuJ<95 z#1{_X_r7%*{N7j9W}9_}D$FAenu?%JgwZl)Xlq5U%TNys5w1cVuy!g$)0gAP*26Zy zKa0l2crTZ>u|{iQ|B+Zo2ORy+oR8SPwm@hLqMdq__nXd7Cq;p=P+N8pa}$)Vn>bhWFE) zYKXUEtcygcvnKjVi?aN$s*ArbN7e;eM!DW0$B({)laJG)IFI$ra&(AuVZ`u_`17zX zvtfMX1Wot|(!%aw+q>s;v0)3u>e1AB3y#NjPQhMOF?^NZM6a_vub2HVY<3NFiP}he zULpBPqdkB6adkUMvud>eype|_o_Gv-!@*#$gd;>*8NHxuaJ~l_<()yTFs}GlU_IM9 zR;5e%lYr9tit4Dla9*|WJ5z-ZA&lgWaINteszL#HUroDyhMeGl;JzH#THzu8 z#K$r4ZnX#cXfvhPU{B&loz|l@{JlLB`GpO;opQS#`O>EmxkXKfq&ycWxOuR(SM>!~ z07<@QmvrYa-8e~ywu4iV^*7jB{)El2=0O55J-lq`$1O)WB zKi;h$zzO3xet~`I^~y8S%e$I!uVHsHKKgGnyUT3NI?KWA?OEQ%LA$&-$-6uUMf?@d zv4vyh+2PHGje(b?oPi1a*xxyv`evkL;kONB;eVdQ!b@H#em1`OFKv244z7-z+up>N zq~*~k#=t?;!Z~O>`}OEp1Y+Og7ggd5&hzZ}W9tg)%BQ$^+VL;0WNF)scL8+!7XZx% zF%W7q?#7FG7BL+UK(1ocv#s+~+J8b?$iL0F4B!-q3Ccy87!kfe)LaPrMmvDle`WK7NTt%nFp#%FigFf1~9XcLxYusYeQFUn< zyef;(NhQ%jU^Va4)fpiAfu>zb^GA#90xGjy;B4HD6Qv>5ZMaa2`XP?%0^}W5IK8;@ zyE%h^hyrvHa7`*zMami^kFNsAvZCzYVz^zx31#g--z_p*ttdSNB$1i)nK-i=gUUZc z=+CfK_sHv==qV@c%WrE;rXRUJIpu`DPn|t&^Qv1CSO)Su5BveuIn4xhGO5PlrTUW0 z4bO$g!z>NeuT7n&4`@xMFwX*3=hY@8iI9PWT;Rg{D;4b%Tut1WW8%F}$a{duFvP6$ z%USLh0qk==jd!(W&_wv(Ql)FY3SwxOR5qQu^tPFuI5_po(LjkI&>>H^D_JS!6f!=UI=XCVD53oy>&6&Dymn&LZ} zN6yf5#A1i)3wh-LO#OLKmi)IHXX-Cbc%CfpGj2!wYR1qg(oIHQ26q@A@mLt-5M&|u z8BgL^`3cAK+u{FZv`zs@V+*|m)BhXbD$iI%l1w)|KlpzqSRPbgpK-N;__!e#h(5&oU8O~;p{grWVLW5XL&&18XT$F7^U3T3)UMSt)&G=Oxe$_fQJ zKuA%l=(5>=vjW5c$AXXPQb=e0E(TcaTwg@xJO)j(>vJp+rTlO$Q2sCa)~7Tb(_St| zj(O)GL(}gOZthatTVmp@m3`=rAnx>&Pk{fo&be2q3E)6M8JCZpF0qu&rl zQBmSySpIA^@-_D0)@6%7yOI9eNYe&9s#yCmkVJ0LcW;}w^GyIZ{n6OWl%OTBHxw*v z!la}{Y-`J0^dXye3={)Up6D(PX3(X3e*ZnbA>ujX@My=5s=`rq9fovd@PpVwcfndZSnQOC+*Jb;@&@x&a>SJPd*-NI*}9^lKw zyG+y{N{w@H)-BPdBYGKroABF#Ulw5J;dd#1v+!FU-Sq(r?Ab7YUHZ#<^eBR{?{WUB zgO>&Z;R}V(L9`una){I`NZ9(Kb-bjIhB9lgd*NLcZOb+>(h(!fRxS46ZETN-@gn@be;)Ma+E8Q2FQwxuFuofFKgPxZ|}7* zZ?>CsWAdkR(??45#Z*zOzX4Hy#>fDwG&ga4pD{k`L`!{B z|I8ln!n9Sw-f_NDT;FGmSAeo{_qvI4AI39=A08|3@CC6}Xc#VONyTlc*%*3Z>IZK* z>f(>+-r>@nu@8ZTd6RbeJD?Z}*`OK}fwWgSP0nC)FB&!;v>$f;lBa_pl6Y z#!QgGX=-?T9SlGkJV{ON3gRs=uJ*sD(Kb=^w^{R7vCp9~a9r(Lbjr4|dJaiwtgg{$ ztiJxKX{=!bGBxRcHBJ3^&`wn1| z^a`|c)*}20$Yw3;upV)+DQv6E~xQ3ynKp^>BA7O3-o8@VI32lcj#=*)|Qk>NRit~0}3h}|1<}+l|Z!^A+ zejH9VJD?35&vCSJ4q6oGNPB(tZI$eF7b;?2e)~Q>$G!Y)S zS)Rr>{)!Cj=3;kapL&$9IS1^Z2(JOlv^P_?a&>{}`82+KAL*xVrLiN7PlH~AIZlIK zoQWAndQ9I|q_%(*C@zNLqV%jcv%iu?Fi^>81&|itdY)7tP%maJDj#njO--9I7Kx?1 z68lHJxD~{W%cU&#SRi~(dsGGmw&fihZ+Ruhw^gJ31m!Tth6ixXuTLKUEomUtHxYw8 zAAz!v-$eiC4!b&FT&Nds!nUdk`5ex@T_IzEDE<#TKlnc%_w|4Bf6ERw?1jtW);XWXV&n-u z;A$3Od>Zc_p>ECNbx#L8J5uwe%EDH72;Vo$JIeJ3Ai=6_rM)hXFLvfjlIJZ#rXL^pvH3k1#6TGak?XB@x?PQ=xus}2gGRM ztLUf~^cw5nKcaa{0JiCTxPxfqDu{8(yD**k$8XEZZ(En9pO|p+8X0w>F@ZK&Fj1SH zcmmq5h3Dz%&gwwVE)K$R^;W)~?%7c|TDq+Hy_gH|JbcF=fFC*wHN6^^w;hp9xRGnn zOaPXffZJ!{GNWfukR_}GrF+T_Jhs8Dc#791nH+rC2*ZrBYoop`Lcg}yEK3s=jJk;{ zTZ>&Cz!RV)xSvxuul<|q0B9~m-=llGQB0g;THcLeYa}z;-pf9w?M{mW{hiLFnR+@d z{6>LzBkl0w`~!_SWC1l?L=JHK(pwVdP^%gZW3fI`zlZ!Z&nRG0zV0h1q_mviY- zU=5EkQX#JyG4g0~ug@>e-xK!CNA#asCz9;v@5W-+#7cT2|885WKca3SOQQ%B+d8iP z&~^#r>{GWM7HzV%AYKV?5V^FJKv#@T_>%gPUVFU&zL$|Ag;QysSLJH}BQ^^qAWWrf zgZI_e@w=l|52o4y+q8a+E0vACVgkkC$}lbg3Dic0QATI-0A4zw3He=(gY{RhMd%v@ zci{rIR}r{O0(wUD3D%bu9)ZilFbgxdTyHBj?t+I)wf!*v__`U@vXbyJ(qv*(hB-;G zxEaQihY+&U4?;rYd8X43bY?N|5_M^2{_(~q(T1fx3A0TU`yEIt_9?p8U}+jNqx*XG zQTjVAtuzdickcw2cKkQ`2|dHIEel$_hELDXo3JMrON+N|!CAk%!v&K+?VJefa>YlU z9XDR_w~G%1&z!m03&uP<$}#QulIJ&DN%5pNhU1$%qV37ili7^}CBcBgEB+SQ3+7m{ zVk@aR5PM#O+R<g0OqLq4wi0$jBj*&iLs_RG6l-!J7`<-SK*oE`BU zOAVWzq!XfrYbEbn$WXg6k~tlKI<;N~L?eV5FB`u`57v!QO3bk6YHC%}ErC9vnR*SqAgmXRHX`SB(R7V~7>Jg95JUL#BP!nLoruWt zBcN@RV+3eIU%aGC*1|z!Q5bU^1v#D_zH>LD6Kym0e61_C+53r2Up9{*uXV5>()I?J z8U5hRSgagne=O;VTxhp()|V*0U?iIutAPPJmK~ms_ogT4S8CxaCcjBTS|^4ydLlRS zarqN$g2oMmls_?!?{n2V_mLPg`7Xpbw$#Eyq>c>z9tMrI-KuO&9ARR1It45Jo*iku z5a4ES_dahAIEwA__G13Q;`u^St)W%7tL!tNw|I{jDP3&TmU=W}q|??h@O@O>VX>!3 z$f<~O+*irL;|Tbqde>t~GEC4oOz1HK?_e;fca>w%`Wo=Qj*!fnh>Xsnzq{ZK_7xI7 zTl`-MtM#;#i?|`qcnwnJe5qtLbg{tNOqd#bb953(IgX7#y9S7Cc^5jS<}uNfjd1LH z__&J$2b0E?*9(o?6b&4654Z8|DbT65$Q+lPvG2}{JDba^#|UUrRr9!}r#P>GS-EHM@ABwV$>htk;rI9#n+G%R$HA1l znhegn%bpI}HXN7kGv0plW7u;ek9lDw9UWT)jm z#_lAdu>@mc{XRlr(CG_LE4nVSi|=$@p%nR?{b1}fl5Wex}w)*r^uRV zpR^6`w7M&;=n8grnfJpF&+l62(MJ@Av0lhIl|wfhG0CGZ-g-CQ6DFQskc)}yk&qqd zk``K%)6~VgDaV<)7;*g;qdUC^chVwg{1Sm%G|n^5*GqGupZo5SIhw`SLlpE;66W`Q zgbnoO5`;Mf$5*s6%=r_^O+ZtzW&CsC(%{#_oows4`7i`*8hjW^aQa*;9R}mCuYz69 zO*rexM_^VIvoFyLx0wC>JucvpG5Dp@);naLi{*RFPVzFaFm#f#eviq~))7E+H*SPc zu=dKh^9$RF?{_6$fW#_0H*T?wdyC+jg2V;hZr*IZ&Hy@!O9ommVR3u@ETeq(c}ljt zgcwct6Kvvl7I#9qP`TS#Q)t?M!ux+5a7nfoxRmVhPD(cYT}QI^oQZ2rb}l~raOw=4 zyk2bwd(rUl^}O?`A2<`w)B*p>hy&TS6NE zzM&8w(Px*cm?kxeZjJ3ezbkSf#|Xg40~~w0M#mz!5~3Ub!hus)kf(DZZ7}^pP9=96 zK{yWd3egFmUFJe8uNmVoQ)Dwg8f-8%t(;LMCHUYS(C9RqwIg#`m?q~dZO$=xBGK23 zP0--ijO5xIGj@WSB-hl3F-%=I1W9WEr&CajFtEae5|&PzrO7-YR_w+ zv6*^TXXil)Q12jv=bQd{;rF#@Gj%T&#^7tvtvo@>G#sG?tL|G zPj5_n3OxZX=vb6rp4phv3?FW{&**~Lg^k?@mu|xTbvZLUJ7%ZH20XtR{}x;u`50AZ z?=up*uXDRQhHWQ6Pv&_@?~C=x`WK}3b49IfLeILf*LZf6rN=(qb53NCXGck_>F}We ze-yHFBY&~T!rq|oo>^Gz)32Ki^KrkJ$HL~BvpMgm7ca^$F7*5uH_wDqr#BZD;^yp9 zy|^&Hc+qWH`l7O_Wdmbj+srEy7v~$_v!33agP=Q-^0-$@SgLJ9;u{{b>Eg zS(I^(kSV4#^xNl=Tbj!${DtUg}pbXPdUdHbTE%g9|WW&$_IF%{PvW%_xp= zKf5Sav#+q(H@j`?)eqn-{{dxX)&4qv+1ds_RaK(}nSzKx;V{3W4dO&ieAt?8g$gTz zE3?AknY9%-y1z18p~#m&_`V5$TS#-DqA6e@iVbkMDIDvw;Yxl^gaWOFv7&90+4Yf649B#tkKoH^KXME2ISI*)AEpjXc6Q+eLaR^jK zxbm8y-(S!Ca5ZRVxN>fg7y04E7YX7Fcf2h2*VYDB1EUOgV&HKNE+9)N*5f~j|5hgV zhd8GguzxqxC3VG!jvnK(gRN%Ly$S>&5lgsn!q{VNxOA zKRc%Y_;H2gIqJ!`01w}xH>=05)dQ@6HGF+*S2DhXI)XUe-Psrs`|A4*Mmx+Qhv3*14Lx|MCJX-#Spwyw3|$x(h= z53)(Yy%_#5&j$&Ga211j{1E-`Zp5BZ7g{lW8BS-P?+*vp&RS8=Z`tJF;&b|mQHKes zW19O*snU<`C#8@dp&}w-lcD+u&vzQ002MZe!~tmNfzOG0w zBJuuJxSXSEcEvJ(ElJM^23F%3ikgbrz>0o>!rvfbMsR&dJ)5A=Q6Wj~gq6UZFbR)x&AaTohTtQLiWfRH5w{MQH2=IU`sR>Xg{4KevH3(WC{R|kSC z3sFse>WxZZN0-l;>8l7=vwS#&4Cbe>CbUu{IG7+_TckunUL?rUvL56V$my;s9Z4W= zpTNogCV8oU;Syln?t||-aj4MceIS$0GLs5Uh%CsxE!WJD3O+?_iHbc_($+7p3D&7k zw@YzorMews5*rd^(_fsFk@arIOL&+QF0ZdqRnH1jUo<9`3Tj&z@P}}>*qWM9n8lBW z2uaEfU_p^Ov;z`NSpY(1M|U$jP``paBuXF-W|lh+wy+`yepj&h+i6XhWFry$gqBBl zx+_hnm`Vr&c7u{HB1IzrMPJ~te^t0VVfj*-leN3tS-C0d2}SM;6pUc3+@~Vx>%Pe^AJm#WT7WIZ)+?XR!K$u zTG9;7s)sh_5|3>bBngF0XX$zFq3U2oiirr5&hIbTT!?Hn3uzJTm-uyle}jc^!D-3N z4H6ffhUm152UaLgGMOZL*iVG?^XDZHw^F$okosAJ!!caGi72B56Y*UHN)%3_i&DC> zrU6a?F3$?o)%o%LdDVndU@Dg+9+#Cusv?Y#TjuO;zC;-&QQ=h|=dUVOElSLwLZmxjdLM2?!I%@6RS5^j4L5aFq7{#dfC)Ek0c)Tr zQd{fpQ3$ggAG0r@X)X5GHt@rH@GI?jR()k4$Sc%wkZlNEyO>K}RVXKGOBXGJ)7UlP%kU3o4l*h!>W@hZBWRXyOi`-MI2tRZ&}G`EcZTR=tAIFO^LKNP8Ev?xU8XNuWs-Wrdh!vv|p=GhbwEqN>*V#3d{(AIwa%Yym zikz7s;hoj%4o_yIHvy2fm5k659B2TO?GM^TU}D=7WamxU&Iy>kj-bt~_E)Z)G(h0< zVW$IR6olv2msQtz0k>+5B@txG zhEoY0(m+Hb3Ivs;Xjg1RNjjOP$^wN}s(dIf1)GgARRm*yf%gTAzOv6&*roJyc$%mh zV!AkFr{6D*)nxjOR2(`?OARmu<*8B#NO4V}i%;P4!&SI8NL`s9si#+}WP@o9j3Z2t zm}wS z+H{3%8u0{n+3L-B9NoSQ6EJjU6$EEe1KSppQ!=QM*?l^^*l0*1+Uc4yVJ2mmF%6+7 z#f`u4j+lv^0#!s0OO%45az_Y|qOyU772yiID%g`cF*PBKP1aJAc#R6H=~Qtcy8fq% zoIGhpP55*OS55%}HSbgutUiL{hg0FWtUcvpqxq|lnr+VwsjqT(#B2wy*pfgf94Cl` z)*dHQNMC08Xc}x9%HcrUymAU_)PZC0C1$hdkeuU}b@W+*)YTd_^1ygn3Z zXu$NTzY3Ov+F^AbG;r>)<)2yc3|o_x>4-7^(w-EmEc6 zyrlv;-7z&trByL*2bmh=j+35M@R%(;&& zlm4X?y*7lcx^E0bWH$fDSPQ5I(wI;P?IExl{xj|gER68NJJY%kGo#$|rh_e*8t)A^ zgYyG{u>|^m=_odQk!7_ULI;OPgdHrOKU+~@Ve9YWbm*AD z1KidME~xOBMamLVu;C)yp*FzQ6BN(Hu^J^)f|~>=%&L=(g{q7z>bKDAb7ak2thsV? zC`?XjLKUb8#IQ{;TLmbRwHkjFp%9h{b(n1Q4;HFJ^jlM7TM=0c7eL z!Yqx-%#3abbI5M6Ha8 z#)r-d;X(|va4rwgEIZWATVEMm+dxZ(_A0KKNf@^SS?g_~P!W6}N>Po0XjH(F-94~v zL5vKmzJUN#i8b$RY0qq~w1HP^%u&&pY%M<}5$N0h;p->TAOCTgipj?ZD+QTXuYLgJ$#vmz8?L`Hd&>~C%$!*>!K0(kQ z^Nt%oYN!$|02KNWENZ}muk z&@1Xukg9}~V3_XC3_m&*vop^GC=$kWH6)>4W>kcv*6YzsU?&t1T5Tg>mbzv+rbWmx zOsdr2@o zl(WqSVI>Q}G?vz^z`@zJ>B9VfKQIUqG9xP7s9KUVUs3Tjb&64R;)EMB1MQqD6#e_8@BZj`dzk1RKHk!t0keiqJq`h3;04?EAt2I zYS2NO9%5+vYD|csXyghOr>SIOz>xL|0Z;rn58L@hK&c7*sH?F zu|cM?QC3{bfn~CzOU#vsxf?f}>;3461I8IXN)tnV{n?kflYa0TNtd7akqeh~j5W#V3PBtisGDBcL3vWM+g3Ij6rkE-{$#@kCt_@dYxm zp|EOvgL=i-0?LM(QV_8T6;cco+l0v!SH+r|I1$Inf z5kk^)gdq{O-zAybU`ho%x+viMcYnCD+D5Z^gnZ1gM;*pS@kH&sO6@pOmSV{u2b`nf zV|5{n`Zvk&a=VeD2#F&P3{0>;DDx?dO~#f{AKr_R(3%PC8Q zu}$XGq7}w@Zf*cE#>4@_74=n+qPh`cXszAXTh}>dJD^#GgPj0%YEW%xJ_i(Kf!Y4$ zVNWfL7j^^qDM-cnEREo<_=dsoTEwz^fUyxOCn*O3Ij7p{$$ zn>Jk0AMAo4;cz*rTAiqEOCwpk@o8*@96mKsKwq1BId-(mW=S6Coy1*lYX9S@@F$f4 zpT{E$J_RcHIeXMjIz(tUJ@ei2G|GoG9AtTvy2z?0?50jGQpH=miV{)`%%jho~K?p=Zt zv$|vD=JQ_jxgSq%>>Q7p<}++Qhif^BT6U$zsz_I(Onmf%KkoP3ixl>L&rEf~d;0Xb ztLyzZOFkJldc1&&6k^}9GPyUg5yr3T4>T%9KiNglp)Ku)_ z9ofG;aXAIAxEK^C74wFRoW$bcCJ5EY`MEl(P5zqrZ!9*XPx~@kXFs?j`){shgNqfP zxztS@JB$Z0f!zYlR@)EZoWy2qw$xZIPTzF#3zBKlbnUY(u`5|)N5;Zf=UwvSZKqdr z;5=b8zJqh(NQZqL9yT3(?5Rn|mjIJa&;?^lM~7&q!`TyBT-WHr#pwr{tDCgb;Ok9+ zONZ&gXRl>-o_MwK3AcJuH=MknceL?X2I*k&wB$pWbhe2wcDyc_2k!};uuX$bnH|Rr zUFk?)77bl6mfPjR+jMX)p4R^yPCUP&=Pr7uk{9dVnbvS zvuyq~m6%IIr|5!{pXD`c{8cU(PFm1lH93N?)3a`55yVtN1noHLrke3`A}zj1Ke66U z)t3IpdRoo_Ih=d=CDO93aq0kYFDo=S=7#V{rbVJzz!`o_yA5dkQkY+ddXk7+mPer;_ivP@YM zhyGW!4c05|eI@k6O;^$Zjy!9Yua#~f6sdFQG99L%q~6`>Sf1tVKk%Ie_U-w@3HFfo zZ3Xv^qolR94n5i{%<6=Zo__Y05GAk|f49TV~%E(E_{x5_|#KY!~bw^kL%QJ2`?>-7)YkJM{knFdra!E5T zAFj+UgCOCBSa8-;2s=rnF)dq5HLN#P#>Sz4=)$R|du63(I}LM+9#0EKe#ENR2b`j* zin4L+J|KuiNz}1byA}<~Txgh1kLr`0JnZ!UO??RK6cg>7)26AfomJ@MeNIALSpX|_ z{CZ*)w`0wmT?%BY9MsWCUXf-wyj#%&tZ5x~sZJBJVILlLw_Dg^IVo~)BZKZCZq zIKTn1gU5DlEnFnhU>jkaT#tu$i}pX-w@{B0`#;*X)A{2pugq+4cRVbHl{d?k=&ukL z6GkiHd9&-aaoJ~8@^dYC?)bLDhI1TJ32h4_g4W4xnXG)M?_$UMNsns?l+Rw}Uk=j~ z!rEO;eZ3$5C?jmqp#CVM1e9PHUuHSHF%F&+C)*m3SOyJNI@SqXqB?7p0708~D~^kL za1W~7abMLQD33_UU(wKjy&ct#4@jE--D8MUo9(5}So zazNPPo%$8Ojkz|8@#1s{#H%2wecqKH31|lHvwh#Dv;>=lmurKCFkDDp*0SVfP@;F-{|P;$95IfwmZj~y{}H?PU#+*N>LZy?scc)v&QO<} z`qNYD_yT_ro~|R$Q}w4z|M!g1=+jfTu%Qm)FnEfp3%6xP`qlVaX|?Rdb@FA~!e-|R z4g#&ZrQ92^7dx?UrB0xatO&uq=a)d}*UBxh`V|$it888tnx|9*(!3NGIaiya! z{b%}K(!(~-+FC!p%yH2pAIdL}>niN>iC-;#dwn1rq9f<{DdffU=~aY_F7v}89j6oH z<_OuYDM9S$sez|2M%*eh{R2A=b;2Dv*gkIa@;}jz{$Fh`^Q|3kE*by-vAuj%yZC=a z@BhE(DeL9$ox47j-6oC1@eySwo2cA|-lY$e9oKUb*KKIe{;Sp@*jGxc?q|(`Wiea4 z4F9cmO}=SwYwG{8H3s(ciR%fcvMc?`{2!6+|Ag#lTWZiD@cMPU+IEfQgztjche{j9 zz6QfLY`=8yIPR#bsk3odW*nsgh=W5Ib}IUpyI`bWQ!jJ5c3SqU99QBI2lLm-6lXka zO+#GYMc&Zg`LWDn;}M2|8@^X?VB98914b-8V4j$@B5Mh-P#hmD0|*JCm6kBh(=o9H zW1cn3Rbefdly$k9rL$ln_vIX8hvIx?$`c=X+w>U5M4TNaeegk09IPQ&v7!!6vqfL{ zRa1*LjAgGx#aK8jdu76WJkwS2D||dHJo13CvmGXmI4KD@m;UK9cF3k0`(1Xb?v|_2j_-3D>%5B9sJW-dJh9$S~EjpNeUWqR!ZMZ|nigD7g%%oEl zsKl(31E-Bwd8-&J57Hw3@`_db5Fl<{Qfw*XM9K~=i&xg3MThbr9T+Jd+!L3#^a-lI z?N}?FjIEF^RI$omwF162Qd)5qZ!#;5*~EyeJoGzNd7M})oh3JkL9JPEM{ZV3-@L+D zXT{eTadL3-a$+r-%&Q_24p=p6!6_rc6YG;OtJW-B)|tQ)>WuOyyg&I9o+y8T*%FQ~ z8{-^FIdN<=m38qxG~_Bw0B663_lxMOXIDp!Nu*}{>PjyeUqWW@!7E1Yzep4y3kYo)XFhH*?} z!7UvnocUGMI%%x-mMAOA72`*n2IpL7)z^fP7}K7z%VqpUQ*&qF%LIQ#eZGyy@s@+9 zHYex1c;q}rp&vbfn-UN#Uh@!-uZT;-{s#4#TGem#X2wb&1+W~7Jq!BHpMqd1!U=Fz&k4=67jhh!3_P5mtZ^{tDuODD zQ|?Q_+kErzGN1Wj2&Wszy93wx*E(wBq&?q~S*bM0@#^8q%x+nZyv897HdLx@b;IHQv+7G7H^+@GoI?>{_ft<9V_#=wl&^WM(m3SR`mH!6tyQSo}svO zT%U97K)GcF`>o^L{&B5_$}ceDXmbs~Z9{D8uCh-B4s_U#N-!vKa9EbOFk6vj56GJx zc`9WE4vzZfLCm_LKjU0hH9T+QOX0NW2dVl{-1o$*biS$g;QFkqo}^2Q6Opfo>9|yq z$13Z8YObUn_g=Usv2q!Z3+`sPiM)ZZp| zUhvzbCuv##?STrM2wTo;kyqkSXIxf?8_$-ccBV(4$($2;UvHCR?r~N!@2ZZr_1<#f z&heESozJRpx-pJn7yeL&vvv-z68uafncv z97C6g`28w8*f`AakeyR7;f4G>#!C#R?aW>-;UUHzhP9mqDm>KK&T#h5#})o?O<;;T{$C7%66Qo(Hf! z$LK+adrRg{LzQ-jafIa3cjj5rc!gm*_hH6fh6n9jXG!OAgi$)4Wk0WU!%N{z+_-Jr z6vAQGZ{RK%oWzZT-+%E}gI8Y|!%V@~&*eG; zLbfW%@rMM$u@sdbXMWEzc0otwT=l|P^YlM@c8qu&xW;q9Y$FcJV%{zATyx3M=JFnv z1gG;~+ijhHKlxMeB3B0#H+#GDBi*-o^|w=&bpOYRwJFh;HC_YsGBK`>Edk=1w|QyKOthM@MM?$ExsQeFwyqV5wx!OA{6%WJ2enqjVn?GB5@~?@%xQZ--eE7TgO90GE`OXNmX}W!}9iDo=ZMYEYGr0 zc6l--vTu3jqScMFiv z{@HGs4}D>_Ony0M{1nE-F4p@}tX|{i%gOQB_+sUNG>bJz6-z1KG%4G$wJ4k2GTrTv zV;j=A~6YR)c(~$nju`6JW8USQ*M;iCuqpVSrKul}&BdJfP%zl`H?8vNT#Y@jE}G6lThn7Xob^ zza(bAvb+^YZrL`v!tDQV)hKNT2kwZ#mTNUyt>)Kq5q8xDx&~fah90xK*QyNOV}>5q zw1InUxYf;O4QC~FTfr}eG1&DaN1VH|t0}`?bQj0B9**s#;31?Za`3&H@d-2%{`+{` zM>iT_xl{-@QjNzujpCExBM>7jX153#cbKwkR_0SI&96k_v_8gR-b7z+;5EAYX5$de zhG|@TjCP5`y?=-hR=HcA`m1dw1~m1^I8yVQ;G zc@v+##>aTJb!0Hbo663jgjRh}{ugN+9jQMzd^}?KXMFa6dG{9YJKOMFc!0iOHZGeg z<1OI46&u$?#pLc8xera^CR%Y%S#i8-idUZP^Xzz8FT4L{<1I8nSW3Br%pG^{{bIMC zG^5p>UvX)xjfbqX(2IGv$^{0zSMGA9+aT9AMR(!*HC|lzzwy}ksb@fK(82^mv%-eTO_U?iLt)mvWc*ngRTF1@m zy$Q5CNK$Xd^_tP19M7gAgkpIcmz`XqJrq;FU*dfie*5uz2fuFo7?*|LX#6h5kFaTY z^5=e6EN;&-Mu5rYYqC4uN0ugVfT5Yd|6mz+q&w%FaR)A=Ox_5X^9z zF-O8Vo#RYMhB3{Ac%4!*%7juy7n%3_%{&S!pAHLGkK(yj_PX=r_U9%tAEBW-_eJZ;k9BB4c> z>F|A-zrnSZ$T`>kzBQm7#EK}sBXIU!lAe1FPjCW3eCX!nL3+#0FUIj|=S}`r1jw-i zW4YXGmJ)`*EPf@NAk$jjed3})p6UWl{tVaG)Y>#8jBR^vnt|o5_ z!L6iLxg<@(_aM|k+53a1g~xQ5cC*98SzWCbr!5@PT8)FO@UZciZjEdG)`PG!rzR7C zHZJGg-E(3p)}h5U0COVUoYQsUNH<~5+osF7ggI?Hjx^)vmmQjBew=UalONZy;^vDH zw&cJy4A(T94JRK7^T2l8aK!aD4-2r3L%NcunrOA*+`Hk(RIOLI({Zk`|2bEO_OLh_ z_H3b@2lrUI>&C%wXwWk`#eAhrhvl@>x`hO|B|qwcS{-n$Be>#EF)!<$Yqe=nHi`4c z0NL>u;@y(_SIiCDX}Gr<4$b?%ImL0W+9l zl+lZ7!>kw5CH&Bdu}m%jaC<4U|>vJ|&^Z@b zZ4!bh#z*vQ+Ll_`u%Vvf54=bB?&sJK^Hn{V0y4+%1p}5^_ceOEJv+3M&xX_S8`hay zZe+t0dQf) zc%uIdrdoZ-iP(F9u-SX8rKG2|!33oM)YN!P3+J{zD4-sr7`J4y)Y}I#O*+ci@g8j6 zt_dLZWHr2GeIy;(a}`>)LVM?vSPb*Y99|sSMXh~mcnvT|5%V+LS_y*L+cQbD*~WVREe3$OD_( z4Yv&qUl85h9yQW}XBUUh?5HMq_rp;mIXF0KjP-2TizFTM0c~nLere?V?Uyp+ z2U?`q5OJ@u2JhQb__lz&O;)JmBjdMUOkkZt9aC-$6}glYh3Sbz%YH0L75xH)NP7lMC7<=ehiAjXVAvjDT>~aLmH8Yd zzS}x>VU9~Kb>*Bd%Ao#e9#8WZOTo+V(7;dk7fV@>X#Nua zESkz^;#Y9a_kpH=#nUvgT;SMm`b`ZyAGgwhkpUbNXWMIraj;5U7~db*VVXnwo_N?W z(hbP5YYvS27O)VBRaz^?iDTbfQ;!p()jF_EhkPrzY`e4JEF(7B@J_=xn05MtB@2;n z!oD@~flnQmVIxE>WKXv?jz;1>Lhb_z;o!WG9MdEHli4#}sf*^HjbPK76S(AF!S#Yd z++CYLqx&|j_btIas7rWXHmu^b)>pFaZf(SOsDzn5<56Yq(e+4ww>{bTWf!z{tQ`y> zh=m3#_GSt4Fc~#}O#b`(V?XNZ?*>VK_ors$%KokhFJ^ytsqu9cK!1m|o`KTmeepN& zkR`99kfE#l)4r0FB^Ms?JO(@GxQjb zF~hMlz{HwKlJMc5K?dKutN#!C`%!s7zCsCeXq*UjF}*2meyWf)V0 zr|_7j#?A?zd%XsUm2XQj#-fog?sy2*fVs<713Uovdq8&PKlNdp(|HY-K11x+w+&%iQB}|fGT#F{qu>zfNQ=_4UF9M_ttWFyj9>ek==w)zP zWMKK^m$v;c<3bjWfJ{ajA z{t(Q7M@-4TE8N`!K*uZSm0BmV*crx`cM{#RVH2pJ&(APO6CxWVftMu^xk&Un zO1cMOB-jKT)ERo_&WVe}6~F>}c_(7#0ikua%I7{w9Ld_AgNP~MqP12|ErA;FS!+JsFutGa zr@RC>6#3ygI1g#TGddoDFibBYJG%Qw)JXM2?*$Bc*oZq{$9X%#Q0WeaFO7_ZBamVE zH;UM@%8lbEs2Cwpi6Kl887SH?3gzp(5nW=2k2t1aAYrOvj^FUZPZZ!ZvaOj3uEZW$sdG7PR*X}(GWB0D6H+FA~ ztt+}_pZ6%U)v@FY7r)o|UvG)JTFX zvYo_VH>k;nw8#KO_3MagEhYgL`8=zvZ{Lo#Lhk<=_;|bs*9eajLf5mQ0Jhau?p9PA zr|%cYf@<+ij&irft4Jz+O8(-(R!H>!l<`hGvGC)ch=<%x`*+HC`W$LsQR`laLQgT4 zqWswGlVtq!U*+X|;{_Zm2YWuD^@zfF5Eux*2;_e!@_xKL0vx0oV1$sW^S9Q>TgyEy zTN>g|vKebyf&3fy4|r2+m};+q?22iLh|}=pD63($X5&mo)L^<%0pAgi`NExN<0dpT zc61XF5Ag5FAodJkfUs>9mTB51riC*nrlYq>jx0gvlx}pWV*1bm(v3&ohZYd}r`tpw z(v2SiBAEd=RiV;y5_uD%uy4i6YPMvBWxgK6Yh^VUSq;Vl#zhlT!iAj`*j9Ek*64H$ z_7{Oa<1wuKKr0{O#w4Tq!4G4d)1lvtu4VK+5`E+$-T`jRV)VEc?;%DvF*+pChaTd- zB;#U6r$nEP6-Yx*H{SgVY6MMV;y@HQ83j&3cS5$i7{LagZu}cwTQ`H2NOBZ3YWp_; zerdf~_#Y!4;XSCUrp~l*T2p6c1Rd3r;=$y~${yBFeXWmJB&tb*lq-?aZZD~b2IFcg z!xAh6bA1wSjb=1SpvN#|Mf2nhXi2Zy3t^^Kp#AhaHdE23?nB(`Wwjb6%JFBxsC;#R zt#HA}P{_o?EB|uk-~YrvO&_F%hc|tY9`-bSkQ^S+^g&8EZSw9ZNf>0{AANVz7iqi# zeyhI6-hvqINnW4Rv@SVzVp702HEyH#l&yLib zi33E#Znc3;>vB+%m3A0K%0ZDr+_0t>sAPpXUn1Q#Xd5$c$%R_0tv|+_jnF!8j}H)7 zWZ2o(TM6j3{18rcF%rAb3URh3F$#bY^Y&cl*?0q9%8eJGG%}^}SMUthDW;AWEdH50 z`McG#@po~sX*uV4HvU{d*umD_?mez`o&mZx&q#H$k#anq1i#0UMdLx)_R(w9fO9$k zC&pNIs>OSZtjfLt6~({|K{5TExR7Ce7&wVlk191^B}{ zwBHnqE7pbQuJcjq8_X#WmUZe@x@_Tro8@Wz``gH%R6o$VhVp7NrbArdsLlApyGYme z)nnAs1<}1do3rzp%kon7uFk2g**HuY%gdWtGY9api!b}}a{sr{OJHyc^6kbFknbGi z*)cp12BzIufFK^f0xpJ=9-YM6jY0{AAC(jnfU@{{S!6_!tV2#wqP~Bkt3B|)HkZWc zdUffInVr7qH?_z>3?rD)+lM5-a}<+Y4)0kDXW+!Gm?OItp$`xOBgwIhrW1{k5lFf| zC*~oAtnFEd|A|Wf8H=<#GMMVwZrmrK@b|G2x_I#tc(cMsH}-_Rgw<|cc6uE?$Ett5 z2-_W@sou}gb!Ke#_OyFH$4uT5Qo0M%Sa?)HSDQ|xgvY_>f{cVGw61pJD!jsdZ#PDQ zkB(o!K4Frv-MCsYaHRxRs=!5nXR>ThDpx%X1S6@FpX*FUUqupcz5@~!Byj^mAFpAF zJR6!IRUvZ-fX&`d>63PWcc83i=1cJMjNpc+QhzUbnP{IiHGY-^%hvd*7EYVGDUh?#i$-KFiYLyJ>iQ!7eljX zH~xSDe#gH8!?U9;y8Fv$&k)b1pCX8n+k~^1YHRS@gx`jLTi(eDH9#LA^w)f+_8#WEKj+_Dxp#Rd9YjFG#JSVLc@j9Cx3+RK^G>ep z0ihHKoz<<}&uoDT2r3t%ElhB2E4MRSB_7Avt6I6Ec_(KGLBm5(UL0?|FlL=YTDh-z zC)WvqMfh=aHwEgHl3i9-vCN07 zFnReA>OgHzxC~Kg;fAt6LuR;PMj(tYowM*A3Q)1&%D#Rt5Zr&=dKJDs%^=U_c z;L|qXJo?S}z4Bp^_TVy~c4JqO*7I(W)`edc-fzAT%8K~fn|#^<{2s(_Tdz+uKyym+ zJS}I7Ps^_NX*u{En(x!reCpF4z^~~TctE(|ZlBhG-y^U2w9QMAAM*G^nNM3(< z%+qGCK-dqu_xiN4yMYUuW0CG4;%YziX+22$3TSm5_i0;>;vLW4`+Qo}4^f6+g3bAw2x|$Pd4Jf9KOC;uEZ^Ok)wE<Who=i zlgE|nQLY{l_4uKB{F{0lRF7myN5PwV5&MOU8D?Xhq~20s_v;Jk4M%$Ok2NOi$MQeM zzaQ&Mdh_2{pRB);|2qDCT|YVD#Ku_U4BeXpCm^TUm#TZS&9{!9foy)HH*)5L|5&+a z8k~VQVI3R0-}H9B{W%i6HTp-Lv-4k#jGIu>yYlIT_ycL1&!>YcSQq5K7#W9Nc*35Q zyZhj6vGIWY`bTeRZ*=_VokKqfVW}hME$u+sRy)4AQ8IdHP4nJ_k3m0Gn`hzJLzjoQ-=-%!(r{(>~I5zok z?5!pE4r9rYH@!z*HxBx<-+s=xnaDkFd9x3eU{dDocU3GRu0aI6ZQ>aROS0qQCLk_) zGR?E;yWlC7_q+3v?Kkk7gWodzLilaM?*aUBHlRM`CsrhPPn3$Q(qdO7bOCmjtAmAb*fr0`EW+%r#tYbjcBa;tPh}{1H z`OPSe?meWxQPxtHmpvcy5kN3?@6 zz{0%Y@UX`|eK=>}p6K4(8`_jkV~_6tVeWn4qO7w2@rMCqz){dJ$*d@;OwBCIEOY~5 z2n})+P&2hqKrjRrX3|O&8z^a{wav=PT5YYhU3YW)i)cj-i}v5r%+f9YRhQ7DGM9>+ z-}`;<^E@*H=(hX$e15MlzQ*Ty&OP_vx#ymH@44rmYaBZI`ZouCat8?WEvyB6-(gX( z*7|UYCY<6cG;Hk)6uh;wuH2S3Vj<*WvzsCo$`6G!5lFReO6#@l7!x8>Zf#hK!Xv3m zuK3Q^KwBETPr|#$EBA|{#SY{ca5@RL8d!7!FA2Xa*r4tdv+n)Q=bn?$aWl@&iEn`} zEmQAdLBy=l2Yt_XT)?T&Z76K@-o+7kbpnaDs*+EIZqp$+7njP|UYD42ae%yHZ4t`D zm76s&F1qG!7Shes)!Cf(iO{;1U60dBvSJdL*j71<=aY6L_5$L|*uJu1zOunQw;=|! zOH<^lXi==9?e@g^vZNYQ;<_70i!Y|3E|ndgCDtkKZE!C8yTres*JailKnObLWa(JeMdD5)dHW24O^QCW!=VYC$Nmd?& zq$Z&^*0R1#;aWaLh<^pxSD%l1)!BE6w_)z!Z8_iL)CB814Lv5zfjDd4`p)MNvwwhaX^H{!pq+o;hzjX_M4e@0 z*Jvl>x{^Hi`dLJt{2DHgZ}H69HZg~t&TdPzA6bNrG(3-2_LKn=K;zahoX+}47YkP2 z?S;C#%6RAWNPQ)T9g=80Z@7{euq-OfX2Lhii^2o9Do zy82&ijvk4tI5Uc_X~CBd=cami>N)$t0n~Fm+o0UdD@9RvFlEIX>0;C)$9#}wen>+! zqKfkXu7eidfP;mygHs#%p*XWsYDr@KeZi?EHDD>^6Au7PlX{~@JN09JhoKue;(j9( zI#Bo4a5FP-mu`yUz}72%NAMF4BV3GHe+;d+=;%RT%Lk~aBtXrpfdr_2PJRW|K1>vp z1lK-EwAZ4%*%0#5E?`u>tovQ=WAbD{Jk=vVt6%Q;} zidG*)vR~64T!nNIO6-1SHc9CXl2ML47+8gHY`#HDha*&ylB!AAi*iObiXVRlvD6Y6 zXDA@W)Tlu@9Wd^o%l~03gw@Sge+MPq3K)q{;JMku6Wg4F(2u?1d+=oK3j|3?cbF{d zEqLu~pEwIMCrY-)Ygdq{&fd%9)0A{8WWG|ssC-RXlPI%T(l0=mdUkME?N0Q9o4tuE z*u>7>wesmtuMtz}(5P(l>MhRgHDGF65D-~hqTja{Ra}yui`$A}o*h$MBA+m6<^8<* zdA;cFL0%7K&nt2wZtRFzT5Pn#Of#A13WfoQun~CliuUIx!f6D1yaNx7vN>G&BL zV;%9DG1AYU)mMTSN6pe6#&gqE3jRKxPE;Wq|S zO+#>`oi>w{jyiA#Q!019>AhOKi~&f>Ehfivt5wt#+f<*WY7VOA7T-8hNXWLM;h#PODHOM|V}I z1w}(C*mokj$(4PKeZOm;#|^aEyAOYrGuTgN9PhBIHfag7q-!P#s|LXgxj|(6QL4U#a|VF zMf~MjY5bh{L8C|TpEjKDb4Ku=R?0u%l@1?1oX;Tq5x^dyGDK^7$_FLu4~PV)}uOUNZ-DSQ*RK9!%Si$jvR~=^v!SWsH$X8kZfV%uwWiG(quci;P zHOkDw8Si*Q5bMZ!pI3Z(6}FYNP3*Z1%dyi1(0P{;gfrA>>O0!_{*C(f4VO_kiZy(u zKt}>VDGKz70@38D;VA`b2B>f^Ae*M+{?3Lo6?CISEF6Z8jXV!4VDtz~V7I??>m_mJ0U<9D9~J@gv-<623EFATY(xCRx&~T6lkjg4JRl>ftnTQY=SHb zv|WL$5`^q-Q{U~Eq6E8OHVaUQ0)0VHJ3uO;4t<}{f%5>BRMw%J2%SbK(Cz+G?&mSpefv+6!Zo&1*Br8aFv;Ygz71r zC!tJ1LiH3P%@n8^Hp=k*FyyA89pwIHgPy`a39YI!pn3}12$i+qHldNi-wBnqkWf8^ z$7Q-mSZ*mc=qc0yD$gAo=LN`v)9}NZXpiRwP9kegIP*NsP)aPFCD&^yPI2~~Sdh6H zh?VF(1@?;hL!kdtP@GEx)S((JRyE}SWTgj3imMpxXS9<;xw;aMPt?!U8+z2IE>UVq zxozEV2yH9Y&Nv@NSs}+y`fBgtjKR|^y)*3qW`O>)nR-j;IU|P{dQRes+XtC?&RdY0 zuIF5ixWpek7MB_K%udap?V2l>PqgEfe>_FO(S?7ONDdJC6of$Xl_iSf_KmLjvw6od zp{TTG3A>@F1hZ{^vyCm`#>@o(ENwMQFHRaJx}2H0<+rKZnE~C%a-5MpB^Pcgakrl*FIUX(MZk`Ew;Axx$&1hJHr^!(`?KjxlsN8)J%93NBp=H7nrW^jF zj>FE^lWE0Ph$qul3vWA_$0tpmH^CvEJP1T=pIDg}T_hpU^An6x>{fB|I%+j@3ddh( z==Q|DgAe=CT?r^t&0hGOYYp?9?eRj-f^#Nedt?!cGK{&~Z<{>88Bem_i3qXPidp!@VufWBISUzmire(*ISba!t|8^@YD&$1Y~ME($JJ zXrn6GypR1Q`zfVs$%vj@I5MNxoacvx}U?m=qHr89zdgV$lv8Tpu;AsKkVlN$Qd` z--YXA^1%zf3)jgf+Hz^r5!5AXz%?9I(+3sRkSQ!nKw>&7BC1?@9`eOvqDtCIcU|L&b?)uupqkPKQ{$>m*%{ zG{o4!+Xph(cpc(RY%n;)UyyHYC{wAv5fKXq1H}{S5I^rc==)<5GA&HiAuWWY5|XDw zJ|-lckmWk$AB1EOvR;S0K!_s{vX+o+LJsRVzawNCA=WgNdnF+=2pOS6ZX@JYLZ<1E zn+VAxWVsHxo{&O9w&;+n2q`9Hj}93`NEso=bx0pVoP-R#Ughrl5Riq06zY(pge)SY zT8Hc7MrI^<14stIY*A+Hd!jFA002sYAvRvX7AUI%Fsz`w7{vL(U@PFd?=ZRPGUk2tr2d zkRNsca)gjub;uz?+6Y;#Lp~tnI3YW9$m@i(1Hx|gP-B3Y?m+KF-&8t%$2FAIJuah8 zAMybPg6*1cZ*m_Z*mj%tc5k@+?6<-Qi^;O_m3K7Vzf$Hz}kbtrB3doK9^ro^!g5 z>p<@12+4v^sXKwizs~6!IooCaD#g>`WySoQoJNHUHA_&N}8J zg@0F9;&-L19dw*4Yt%JtJW8f=8jtk-?PyRV74hh0s{~KB27F%x;dNI7BW@8WN9Va$5VK5D*`Qo!da$j?HRZ^sb66lPS&q)(S)dLnT_UaNyM~WLx=g*hxZ@G4JASeQ z?gGzGku`Bi8g|atfmADG8~NL;Gfo$z*VVROzvb6R|RhA}P1d>6}f4Qmiiat~vb;}E;IW6JAaEL8%THp!)za{#WnSS^;6 zuVkqo`}jK4;ka7&-;N8K-fi9hGonW~~Rf^iw)YuXekPSE$E}M7;15O=14X z{&iRm&BywT{IGRUf&1hIuJEewLR$Or7%0xwp9M>yx`DT3-CR&R(ih@xv_!u$q5vw6 zorG=n8bJ~(o;WNwh1J z1Hyz+pvn$7p8-N17YjJc0pm9@nk%$Ww-@<_7NAjj6u8^tZ$D7NFNbp(MIjuUS%F4@YSE|8oWTXgR612Q&QYUcIfX+e8SFE1FDr_( zl}FH)I9HDUk*Q;<_>-4Q@aUS4W#!uv?KUSI$>3>?uJKaHR371RDtPCtx%8m#g^-rN z0k{-ESZ9`pi^Gxd66!jV_Q5yo8t=Cd)7ECikb zU^H`Eg+5uZEfwgf+i|kn^I*~!Ld;HaImki8&BNF!KDa=2I-Q_aZC=Pci+B!9t8`U3 zE*Lw;;_9*VJ#JS~vjm+NeldK2I3d*m^4|0ZiQlT=GKAACo+PpkpK9O8NW|3?PuD9%3f zFyla}reyj05#AbJzXVi;b`$@rc{u049Fe(xxByFcaO#JD~w=D{zm-vGC1IwBN57*gF5UBkjb z=>i?x5eLpO!LK%akRKQS1a--62M~j4)|hFcT{bZiIA|T3hukV)C2lJBVC+mK4*&54 zJDYJmS{F?KyEfX8zSf9-)VA~_xyv;M;vY79;qGH$p6ixE%O0XtP6jvC+V{J0l%1Tl zAHF3#8UUWm{h@CnJ>)zDIk(nA_+7+t?+C1AJ=6Es_4~o!M8rUXt@Osle3ni z9-ktbPVw7@B2G2Q0dr+^XK9-mFJI8NY1k~hC=3*+VF?Z~4X=oF0nDMpG}oDU zSlO*rnx!($*6`OJ0PW!PwaI4Oow2m34PnO62p6|(v}4%@BVf=gg;E_=afruouIpLE zuX3W%JH*xV2yb^xft0$}2y$IhSHlF+V(kvG5MYSCz8-JgLxgUkE>8>x)%8J~r&N)& z2PZ3HP1>D6?#y6!5ou>TkF-;kU{bcj&fc>}6!n#S=#1on-IkA#E>Hc|@4@~MJSnhT z2KD&NnioG0*6N2H;>TB|G_3w3IT2O;YKl?Sk$Xh~y6J`BB@JVFczOTAb_5nCOhqsK z3Dk868#!mZ^imrD*icvx@ODfxpPy zT~~?w@Hw^(szhg$IEfmzoPih;*(!QV)P)K)N_@`X<0ur1`=-CRerb;yqH=8xaixlL zmKcg8-0gSbvWvBdEmkv$zq9Fa>%k2t58x~TN=B1WWj3lu|6vPhkD z%IR&n;v8h~m5KF@#6rRaNZ8vF%TV?(pKALQsmGxXCL1&I+aT{sU_wEy&usi(E>~zp zka+w%o&2kh$E-p;4{nt6B#%9|SqqVe#Aj}eQSzO0Mb94c6Igp0=0Cc}?s{;7ZbfeQ zISMoIiT?2?Oav80!Xf?5>b%ZS$sr==O;#vnAS!f}6_QFUa4{W9doj=*MGwQ|&UqHx zMSKfOYzW=qx7$44hOlX1y{VXhLhKvo^1&yN=W<`KO@xx7U`TZI=7t%>UW>GVjdc4B zTam1ZhoTPB^xFp8M>tlx7a|R=uy!M(DKmCG_b`xpZLF5EA>*#37a zd&hJ!h$VW0#XjK;arSi|I+?|BbRCw64skAe^$A(07*`CzbrSsgCJN;2^f8`I+9QW&tWG|AP(2DNRC*P9dh)Zo^vY)XZA3|d##_{PwGMv^PZE4^e^@N$ zCTR}*8s{Ww_4w_sc%kS8fW3`oqI8{42x2Pgt8Yhwt%!Y*JpKg#V z*(5|$R*k$YkIXS#OD{ru%g8j73`!tG#WxbLpwMwg=4fFgYpA}}8*1GmsikKkql9QZ z#<>a#Xv-k6ELwPqGGw$s&prbgMllJ6W0dwkP3THhmaP2z+l(YE!DZ1a++$)vK?M(` zTeML!P7lTzb>%5y#cV-OLk;|~u9dN(CFdlUR1~S}4^I)@vJYmhAzDZl^P3=J_LMPC zTU*&wrz4x>$%w#aC)2JuF9}WL?;RSL#c~qufy@WpDj#X;0W?a9LU0QI^p>r{D=Z_U zV1rtgpO=LTO<8&To#cPPNLIt#l_(Gt-Jpaq1w?E_o`HWe=D^J#V7+n`JPS`+n#7?B zzqUWMgKM1gW@#Jn+lSx4+h%Dq@EeAA^#>Z3=>`cAY3aB#fhG#l)|Y*^w+>QrOolO5-OobfRf`sx%s#7|DO1+QXnm8rNMjV7BGINrBr8b?07aWsvnJ~zkNh=W@>iUS zJ{4Z`4Y8}L?`P5Al8{UmByAAMqFt%XR63L5Xzj zf|tmOMzZ5(m)c7pefY;S2SE`n+F*%jHHM*6;!Tju(|ynElq51bZO_DriFS@qoIuuB z0x_^xhG>?1fWsy;nUrhAPF<{rY7ktoVem9@G;MVLEG-VOwL1J2Jmb-U3X$3WUjKnF z5{d{;NqVIzX=TVj8NeRfQx9NA*E<&{=3!~}QXKh^e>|8Rp`+<2{SdVmZ0#4M6_r;w zX(WdL<`sv4NLcmf<@0_7Vq3Irs2uy+Px`Zm7M-1qTSC}vIBMjVkJJvM<6HT`v_duZ zEDU$nD?ul-za)=`g8vO(*frnC zi{;RH!Is8em!Bv59h)djyAye`iT|6MXN>is*ExBpB;aA{zmfBwXl#9}y7>SGn_uOfm4#(Tn z`MCyAm57@Wn{kG|4)=<_S)Tw@G38H(Z~-d{{W98 zw1Ez%wg*b1s>Rqmiu9ymO?vQI(+)Zd+3I%C)kp)yvV@B8@MrQCqeLd+y4&X?uTXInB7+;JvstKDC)a*gHzwifr>R+2dUdH>XFAnL z2V2s}KFY{GRD2J`!A)lTNs0mV{Ba)};~i1?GrxZTJMqc{$rI<0Co}+LY*~J=PKqrt zC8qr1s=Hz}*ZFl>{Wfu$tlHmNn-W7R+T3@=;KJVKl-LkOCq(m#*T4#Je;zCOi~GaI zPIXGL*_qfyfO%bc>O1ic2mAOUn)FL)dgMbtDsz1p7^MANHGi z=>Div*Y}G3cYJbh5m&XU4e}<8B)XpkuQ(rtW0jm|%dOO!=aIG1Eq=jSZRs)IQ(br+#*+S`qmR0!a zY^A?>{!CN_z}OhidqZ!qfv|O{q5Z_$EznUmy7BbCz(Qq zSM*W={i0lumU3^h0W!*LfVV+RHh?{b4RCc2HUM~* z4k?wr16(x1UcTzb5SQie7F;!*Y2|-DF&ZuqVV*AiVo3gOgOf>eM%7(5ExP7mlmhC` zupluVA$NbAv%&sh4IwP&Ye%7aGtq2#wJAlVS45!o&}IoX-O;F}wSP0dq5^Oz2xD4@ zG{oGdpVUEa64bwHOaeHtp)UhCR_g)HDK>bsG-(U@w;dsH_!0wWBJ8TN&VfAM3&XyD zz-Sg1&SB$sNDtKEXkn9(u|OmtJ9?9l--MZ*jQ$7a`2)>TxVpJ#?xw2zP< zm#9DE$zz{DgQ0#&LD5{?*2SUxBCY+moz|XQ7bT$F{<#$vcq~NM1zsmK=(rG1p4kq_ zg7pvUor&}!d4?bxSH01MN=|DqP8fIMstv$_D2-|jjK~RyDJsE6y4L$Uq^`?_W_tfg zAGsIe62p2O+(~%Fy~{b2qc0zC7>#7aF>K_(4@JW?d{B?;`|~$y`w!m6J0PmyT479z zoEvO18~I0MLEPURJAg)x3}qvmfrF3`w#t-w@a4Ufuz1r(cKF-^O#MD))XkU^P!OYiWejm1O5|5=V~n(z`agznFlmIJens5~pw%UVW~P!sD!)SE--xMSab( zZk1*25JP0~l32Vpid}!%y3700fU>ju@(+FAI82puT+Rd9Hx+^%V7atS^ndmnU*Sf4 zQ9mvEJq3!l)hxzA0khGjiO+#R}IC4)JW0=d22mNWeT}8+)Zej|-TezB{u3 zdo#4@n*n7B=TCX~F2O8nvb2h%4K?j(XJDpPf`6q5QC4{_eJ{>{Um5_v6xgZQYq}K5 z2wI=gigzx)GLa^&85YX=ihin|ZJx9Qh?0I)iwA4Y9u9H)I#e$$!6Cj_V@`hNt^UTC z{M`G!ocy5emNtb!8gLKC@N_lEX|V3L8ivv9F?d(LI|}93CF11YQ0`zy))Sx`;`MFd2RWs9vD|!FiJ`=m%4p8c(Fic3@q^K> z3W(MVxi_ptfaniF=SgwW+U3eL9M?;6HjtmiAMj!F*$`k-DsGKrH`wDlKt7Y+j*T|` zZ};9*-=W=iDOx68%R$@m&q$ZvPqV=jg$SRIC;zgh=*C?brj(JH?o{fo{+w8j_Z{~Y zp!PB7{kzmY(*W+$z@aeQn|+S1qHMp^+(=G?^exT=7YOni5Ew+{=hZv%G|I#B=76VY zrRfA`8n)r+Kf1MrHZK(21V0F?uQA7Ph+R*z!?;iSoM%a+iz(vMKd{Gen`w$zj8M!A z_IkYRBUm>e9_J>Gd7}@>Z0$*hdC2IbJK|1;XgNIt=xxw_V6K} zh)26fK}objyLGg@0JOV__9J%aDQM0_fH47M^RGpwW{aPLKroF%ynuAPEni@5(0WWY zxk-yNKnwP;6xoAmxtb4KUl%Bohn6frYZaLkGU&%UA_Fas(-4;}n*zrLS+d`Djw$6N z1y2?W{jFK(x*V9OiL#F|7gGnUj{Z>{u(B;cD^`IO$n+cGI4Z#Ev>nmSvme=*r+QnW zShX%^KiJOWQ6dNa`A>c5WkFN0Ec1|&e^TKUsVD&QY^Y`d&R%m8%>*Cw0lEJc0qjP1 zVSeUqc>v2On(at|ZP17FM7{Cr>Gw z!~;bXbHUO_F5MPW;>*O?V!LCE))09dZxfPZ_M|kHf^pe(Vq!J8IEQ zD4a4o+O!_YbCy_zlAZgBwAo9!_o-|L@ zGLQme%eQSlFXK2t#|qprei{DFlciBs3&yxO9KE9)>0c%(&yxx)4SU*f#4MDfpgQNE z&dlif9%jCS@9dKA{QoXrN?!Km?(@`nkrn2gTV67*Pv%1IGFa1?={*ReEx0RW|M?i@ z(*+Q`e+EzWjz9{ozME4$*qpY(ZY=`K9clcE<&PM!R-fdlOXb`3S>Df4uGdWo_8C$q z?w`NlBroAdb~ZE6igH1ks!3i3CV=dghcSQhw*1r%%tMgH9N&zypQ>(p{q&oLq%GUT zMMfP)?rpu0{u$vMQ+KX+#FG=_%fZ=UT--(|rlJ|GAt`u6U1JmXEZR|3Y15qNc{1U( z7Eml;@>=`o!F2egqUZp0t0jjFDrUlGILwPl#R~&Upra=4sq76;@@7$5rA1vhsz;Gt zmvb1zy>2Oq3sW{hyUJ(#lDc?phXs}R-I{NFDQ-~nIg?<+kPCxm940k)|0{@^QxFKF zN;N?}8KS89FrMC)4aC%`Nj;Ms%i5$a52m5IhtS@Y1`$!d=vR{B3To{qjH=H5fmMZE zgYY-23cP}CNpVBKa5QW4yxlMuX~_Biliy%v*G4(Fvid9kfYvT6&@LW7gxM`~$vII_ z>xhH=X9>SBE7}j~AW8AD#^N7^*2|dcnByMjI-GaTL62}@tm7QO&1kvak;E_fzV8Y>Td6R+q zbkL*`w|J6Gs!(R})KonF=V>QeWznuKUP@3WaENQe}Z5Ce2z$pKbSV4=`?UG@KC{Q~tEpPNPZRU{R* z<7)~em+E-)qtS=u!JF%^?(o;;OCV9Y^}w{x+cHqrMe62EnuFmqt`lY7rvH~7W@aedtnFkZujE@qS-;jSCVi!_Qq!Wi<;-0Va_})`jwll1+_El zo9$a*L(lLNwf4qldy@v}_GbG#IusY^>}a;{($=a-Ra=oJ1S>30_vsiv!Fs*fei%C? z8|5_<_1P$;xa59}46xwM>5w`+u@)=D)*Fq_`c=fK0}K@jX&qH(-@sDBn=@Qv3>^pC z#dXd0^+<9ZHec2g_meuNx4}q2OhR0e%sB27wP<%n_lJF!s#oUo_?<%beLslJxTVPzLdP(3J z1g_t1kS2{EfUj|=Od)qX3<3}8j41J*1)f6s7)T~hzKYD54~jJP364|tHn8JSxBW=C z%$4nn774M-MMB|&eo_qnElF`FBEeg7T8l-i<-XG8~hCDwT?>A#lY&a}sU3gZU_I)I9W@_>BpFa(h zUdksUP0A`G&Ae{#FGlzf`7a)`bDjz1IHTt8@p2|VA731UsCnYad(Qjwv|)g7p0CCy zhH-xHMA>!OxQqQ8!f{va%ssU;_tnCo@^`RxCo}iI$6yD!Nbj?P?H>c9-6s*5hfz9S zXEjDPdUn@*h;t=hhk5$q{;+&Fpx(>?7!J>(;cze41v$6|7&Dro?@J#uOQP5k`^4k) zSX*~j2$VxZfEVSu0NS6kfxE|#n5h(Z0IZW6tB?D;^se=eu!eu$S0;Kh+##f5-i| zc}Ze}{JiG@8fyaq2Q=WzdzeCY4pEK~@ zS=x!Pgm`CY)!QN1iw?2!&EoN57g}QKe6SL#h+6xqTKh_UMzL_2(fZ=D{agWCT=F#I zQ>cTQd>7*ly$7zWQjwDQXy*anHv0y?7gi&3VH5rn-`A0j;h+>0Tl-D$J_-H?|6_E+ z@sJprgFBHyn}f$gjAOdE7mA!X#w<&neKpQGQ9R>ZC3Z68B3q5Vg@bF+#5pS&^hF(1 zm9CYAqY1LFQmq~j8hh-k#9$RqBrV68jlJI=@)JG<`QRQZ4f~`lNKU)A<@HuFi3iz= z^&ZAw;}Czqi`iI95U0+*QZ2ha!n~0K>U=!CEpEm=Bo|#%(PtOH5c5iCaoJ|Z*>FER z4N#s^%R@N(=^V7G-v#N`j1^ik=!vpyVwV(VM5 zXF%6FbJL4x6m$N+gY$pT37AISX8~QEDbJEpjM{RZ_%8-+ST~*!cOs;-Rt@fyJUap< zk#fw3(~{{Yp@Mz^S8OLwa8WxY*TDWg+%{NOaBefxN@nZhF&H1a2YK@>oRXoq>tV?z zuKL`Zjy-mtf!2B^%rZFFTEMI5_^J=L0A&LNq4Jr@E=`C=+^;?&hvZ^s7Wy*xe(Z&%k@DHngO5xwmQ~;?rO7ng_5Kh~GN= zR^Yc9zxDXNh@bic_wtILBz#4t;YOdr+!AG1*F#G~!D*aTjOPUH3~hsa#%e#vXFu%< zRCI!NrnX!@`@_xKnYxkr`1vr-gVVU;{5iY>>>|yH{CcRg4I`@%YdhivY&a&#QpSw0 zklyZ+Z_h0%rXM@tW4Du5cR~2lksYiolXc&CG;L)KdtaaLyb&&a$K4?V3{$_*)5n&k ze-_>JZ>%H}6Ou`cyMyV4>hAeE6+^c{3r{8v4W6b;`bHx1xFYBL+_F$D_OVQuNQRxh z05Ww8sx!4H&shkTR?YxnahqQ>w@{j*-zHlvEVVp$fudmy42FYfV%_M@U! zGN*X>xZH~TPVF6Hu#^vM6=cL{(X4(Rvy|RXBeBrY@_}4sr7;Me#A_$RMs*xO6F@co z_`dyjv$UV^9D^tSCg4e(fi*TGsIUFIa~Hj3Z1e?b8A^jz^#?+e!cEA=wMo`XKPe#_ z+oJVM?oyC|HkAx%MUxv0DQOh(Z?Y#XouHX^$~Kh;1ukA8t@<+5qgXu?*XWR5p|5R)7Fw`CJ%Aqgnhe|}1C_rG zV=QLsGw{RMzDF!*I*Ludc~~`i-o}Wy0`wL)BXC}bXS;M$`r$v0av-N&7x(-)VxC+5=IeO}y0yn;XI8%8#AG*Y?;TJR8t1&XnafB!Dj z5k3N-W8oft#e1-8TEKhJYOlA}U5~4gtYRk?ZTfzFJ+M3N*UK~ufA8;q%keHAoTWX3 z=S6t(FBQ)OJg;=6{ObO<=8v=FSeJzHj$_@L!*(t23%j-wzvcMtdJ5wwV5*N&Oz4~< zpX}}G4^t&0!0dwLIqspw1a#iO{>s?V0bxeZMYK2sMW*4r8wVkt{-7KmDDe%TlJG21 zs5Tun&(I>Ym3Y$XW;31~JR`M!*;9(jovz$sDF8BX3U0hJfBvU} zIu@ruKnC<73g?S8Qmm_CPEb^fc9GWhGzMLD9fOi6wDReMh0NH75#1M(&3)wd6v)3= ziu%s=2$KRE^!1JYQ}8W>psK&)OM%}rcvwEzT-u0-{Cv#s4G*jDX*&EKJmUekE&26)obaXH zdzna=Ma>|0{E@^GYZqH&Xgwy zRSeRc>v60xIgdx{(1>i_^|B)7g&J2$YxN%EODgiqod%XA7)v%{h-Nq$wrF~!)coRn zV=N+T1|{7k5-#RMVInwbLfL+8R(4sw2;2axVu3KIM(Antk}Ql#&6M0?mz;7T4%-j& zm2SzzwvMk#0amw6%q%hU)55_$AOZ<%(K?VG&b^vFHreGYh3Q;zChX!V*X8V_9Y~S6 z1@mt#EuWuT+!JpGgLaz{8E^d~e!h!cg|lgEAUp9ae^kNa9>d%mc*oD59wa}FwZbaT zja}9X#m~;tj;Bo5R;NwZ-oZQ93f32Jtl$;M=U=f_s6xAOtuO}oBNCBs-HqBZybl~T zU7L2pbj_AAU0aqqT^qa(>xY*y!)E;@0oV`M$uItgSrf(W3cSXqr8>q3PjJSkqW%nD ziSV-nL~b`|oKCC)T1;A}ITMl~7xK#G2o;95lC#(~c=R=;_4T-wsA5j8JkIwb@?1+K zGjyVHeTEz_Sb=CWEDNVliwqOX^)gYD38W~M7k#5dpL9x56<1W)~eF=9`so~RP0V8h++Q7UmP-Dmc z1;>?Ait}^JPl==-Chj6w1C!DPYrwkpRN^==R+r-6p>0Qb)YyOqz$7TVQ<2$uakd+4 zMQ8rp(sJA;BeUlEBFmXuuGYRf&;~r5p)_VrYC+xy1CJr_QcBB;^3fM9&@%DBw?AX9 ze1m8uI~~NWzCh74jR{!P1ZNW+oDIrpfimkq%S4jLqD2Q}5-`)VA|R+LGLHgn{aK@GFjM>Q(mW-VEYMyuNpl|2Ek||c zTv=i&A9PYv_}jGqtf3eD&-0bF!r_Jgr0wU*YqN{hFgVEuvGWWaIeffKrhJL2c-fWF2F*kZHO+h|xlB!YhyV z*|czZyBj{0SctHf=qn}*l-#UffXXUX>k*u>uCfZw3DdDoIjI@HoZWYu5$916T?Zvj zw_>lDuJ!7E>IVx()Br=XZv^H`a$cutRb*}~HjJ@aAEfUE zaa=tHLTP|_W7T7(e2{Yt+l=FeUgUP~0o=L$Gk5Mj#Fl{gI*CS69ov#(utCZw z@~O0d5+5K57+cRJ;y>_yB57cV>n47^f-mZ9@^pKE1z4ghwG{W%k2A1$12Iyn3bpdNc>WZdJ6*CaV-6K!h zK$I<9Y~O-!oM}>Jy#cQSR8g(2-{9j3R1kMdkHo7*FLb)69beT5NNis`0Izr8l}9rV zpgqOvl`QOc?;JDk`u1J-m2v!G+13N{j4E|pP-|s8n5l{N)i^iM_B^iw@3R)KH#j_5 z*-)>+fTPUA8+K&5J$u}D4b)sWLY;LNa&gyl!ml}hgYVn$?MZSt@OrC!&E_lJU3sdA z<;jY7bX6* z83gi*g=k;>Jh9w&h6R$epxgs}m-P5Zww(`K;~551hx)sHFI>wKtEO^J~v zo!GJ>nV;z{pb!CdW&mw65eb`64sK_s#fqY@5BQw02o;4Ul4nXRlg&nxh1aIVz%Zm) zOfW;~DHutBv`C`84oURhcOcM5>8GfSY0X`}hrBfysy|w|V+V`(8?5jog$eFB80`Ym zw16~bO{Et{uYl#e&!oRm(}h6Z!T{A=f0wVaxt<9`SIs3iIIpX^D-M^{B3?Byc8h0w z(LzU=syf-w9b?IS%Kx!A@AqWB{I|eI&ElI)vf<9dZXH^gJa=tYoO98#tT>=2BRMxt zDLRYsF<9o#rJW~Bojnve*eS(nX68Q7eKMr7KhQ#mwt{GNDK>HQS0K!AVz>qaWAPwh ze2%<4Rm5Q5l0H7`aFP6WPxJ3ZB6c8EIdJj>37|47ASeK$?p509$%6s!LQSO@Dlsd@ zIj1%&1_|GTgbk)2kMG-MMr3cY1yI1?WQ)3#SaB^i;AM(Vf4MJZoFhmAI58E|4|@mS3hC$b4`V#g zc@pQZu~- z#V=zp0K550Bf=yr94RiEmBihi6Mm#U5uX-6O7gwc&d_W-j*M>qvL!jZ3aZ>cz=N}X07y;&Fq{iJ2RX6nawwp$&_uL7uW{qWIdH|Os#2L5O-}J%nFg0}Ec|P^QF@=w)pL+4&n*tehg@cuNY0QI2dhtGdRF+@v;W! z=1GbdF(;%f&%o2$GMkv{@G&;~VAe6o<|8(P!N^ z&(Wr!=D7~&uzgh~a{b;{>1Gue>c@Vj2fs!qnMrG|DGRSX9+>Akv(7!r=$FEpc3! ztJL$oC(Q=eC2-9RIgym@5O2@LrgeJ|)Zq{>5t`G{eD{4wK}yk`l891@ZihuD$np~U zpjoL;Wu+j@7y&#sm__@BB5bBQOWFo@k-q*}JZXZz1j|jGTTm(#u{}`@2sPQ@7*MFH zUJkFYr3bYt&>dP58IXq4O_hX6#+Ht$u^s}`dZyUKmJiUS%uY`6KU-#_#6JXtA@dhn zC)W6)mv2RBucy#Af_95{aazoi6q9H_9$oV|f&;_;F2iEkc`tzBzN>#=;7SCF^Kf*i zb*?;0ovR2hXVy446^L47ho#um7rd)Z6l{Pt zK{c85ZXi!|ZwE}+Auc>_IZUJU5uagX1xc)H-X^gozM&IKUfX8cW1echb?u5ZsIjx>GWit!xCE;H z@b)vVU9nb&^C^A-W9#Rz@A>qYuWGa6{76h$?}RwPjMXcyLR`77y>04qLW@&#HRO=5ktNTt2z~RCP zCe&?55+7tkIvrJ;Ris|-%>-yJE9^VcA%a8E$UteQ!zBepk&sF z0XiZo0JWUYNoSTtd?k6~gk(Z$ubnm^K(7NY_uPq@Fw&C&Iq%^ZF@%oVr|2-6_1<%ADX zT+V~3&}tnl$6)^?9Rq?$MjEoxICM+9U9lgN3OW77rkj#Pom5|0ce8SLGz4h9oWSL< z9{Xo@;QO)sfk|&*N%ww>Y7ECoEuXzzPN(|gwbNN#xuj8N$A)6xTICIz0Fzf7fkA(~ z26KaZjq#<-YF;s*U^|5q*?5v{Fh&;d5?7ru`xN>TS2#|;wFX}?a0PPV8L+LGgVmwt z4S0H6c3_>=idjiJnwIK0tYfP-bE%`j_nKGq;bPq@CfEHe*R_}n@W7krZ7hDbiI_`- zuj=OzSD(aJ*b=TtX{0qAE0h2};o42VQVLfj^wf>S65AHhMM$(RPm`>PW!C5?;0z?X z<}D;8=dKsAC1St>vE&1B&p;eZnu5Z-QNk=F=G=upqyj4Fr*R zD-JxJA6&#k3}F$^l|`I^B67w8-&8Tw;kMQL?I=)it!Wb!{rC;XkAJEB#`8b1L)-D@ z>Fv;#Bb;_<>JMFV<1;kul_sE6v11H?%J~-LOgpeg@#Nlpq&A?l<)_-Arggxz8WZiZ zY2GMnVOV{h!U&AT?YeOu6xAZ>5^fmf%UfDogKz7AxD8uPD8{lX<~ z2rSd?%g*5%E9iLyJHlo?AGT zE$3i_`)xVNQEF$81>_(0SPg(_!%0GM+X()ErD=!S&mzP1ktT&&cv@<%GnarcO`y>1 zeixuJ?0)0%Br~}5XQUR}9TQxuv1DC3;%db5Td$m=BeKq2J7Q2&3l0-*0h%qPFn`W% zI>(`|*Z?D+b;7+!ZF;B3qZn$}xf?9mUJ>2ahAqopZRxgGTMQEW)}u%Py_!|!UcF9m zABb*l>>;cwM6(690363({hHim+YK^dv+W1@kdi>Pj1M9>Ct2ej{poewXxcpGbLG zX>l%3iVDnt$yd%XmyJ(J;0cp7<&M3anX>x9PBwuA{ zerv?1ULLeD0D~ivm34PSwtC8;kUQ4=(G>jrH^?^XFU%_E0)crUh?v0#dW>A) zb)HEWd04b!Bxda*-zS#PWaHmd#cCY)iO5#w8Zqj)Pk1&D{b<35dm_e_mcm4>MAn9< zg?K=SV(8evtBo|&FtXB57MbUYm_l$ysQ${W!F_zgHrS~GHqEQxui=>o`XzXRebO7Y zuHBuB8}W`BUwAHV;D-?P|KYi~{pg;wbcG11g*dJ8_=0@El!K++(AZZ zBtVy8Zarf7hq-QyG)eZZLtIca8fmgcx*BN)CEmqI6L;UB4Ni0FFDMsX3I05Y%?o$DaK zPk;1px@6Ao4i)}SQ@%}(J)KjQ)}n!B?nJq6i_EJ>jMIwx)W!;{QPUXgUYS)CMVca# zn%YQ{u)w^2)hWgpFq&Z}N;zwTHI`2Ll04H#uC?w1iY>nvB-6?@-_Z89YJTS}%_2SFD!=L^ThNOBz0rK1lLG6Rj?;mS=19bImo_-FgGao=@xS`^8zqxs$@T{&<77 zjh@zAk%>Pq3Qu%(!0pD&eS7WuYF&Hw+V_0+<3DG%J)Zb|pQRi#+o<4Y>$hBklD zen+T#hZc(4LWlnRfe#-aYoV3nYdMiyx?*fbS)zks5svR>b9 zS(oaGdD3FUw-1?RU5sS)vPP(~DmxNogJXL|**kzkaPRFAPg`?Xf<`MTeZ$%ky?i%0 zgP+{O78WqeaxXwhwQ$}~XOwuyW>$=eFcdH%t+)AW3ZqHbqkb5pLFJZ+D3$$C`Vf`y0%Cl`Hi=E~ zWhB^?I@^b`;S2X`Pk_D?3iAYaQ>;{R`)zz^UsHsJk~(K)U-Nskcc6Vu$zuUL^-^!) zTe}bIZq;tnuzV1IJ&)~{-kg?USh+liC|&vMT|FUBTDlm!(rmN8AxXW>vVeh-EOTUd zYaQv}vvfsh+WtV-{v%C-px``DDL7LT+bUx{De16G8jcEb`)QjP{F1UeH21w3k!M?B zGH?mz6W!>5e;5S4?)5S&r2rnzJ7xjcDRwK;Q~Ucqv6S|AANJn-+TV}NO{Wn^pW5H1 zjY|8w1y66whtC9Se_0o4+a!NH*EO;)2V1ug{uWu%dZI`7QA zSzYI=YqV)BrJLVHS6d*qRXTl{&cYba^-L$kHAF!>Xzh{&f0E)_8o?+|jfbm?D7vg^ zZ`n#lm$dy5nBq+$$xp9}s2T<)IMK@=k-X2%25vj(v3QXu(x=bJPiPe3?+P-NN`G*J zKUimPZ1^SSSQm%m*&2%T$B8?ZP?4?EWn54JxZ_I)*Sy!AMk}p0pZ&Pp4{(wv44pd; zKuen(a)V@|N^W@JUIhqV_JLCRS!(FoapN>@n;wRXHZpMINgOUN0FXQU z$6>+H21fo%Ew_5wsFs(g@t_8*P8`2e$s{2eCOaBWlL0p&fBIV-qz9fYpB>^1JiRUB z*MSlDdzw)V<)B24m&dcFkd0vA3hLE-7`^;+wjqlGXtUT(!oY1}boDmC3*3t*YtA?Z zBXfze)037BgTxl@XBdXQuTR5hgB@+j;Ug059k{_lz9%Hwt*#4bE+t*DqRraHhuHWU z6XCjzR@UueSpG3z>n%q9w6nP%CD3iBU*2KRSMDtN)3{%y5v9@)c>xI z$&8XbzXO`wQW*}1#&$U7QWe&pBRo40^(Ju>HcqEZt#j?uGrMILQ+(V=`aYF3tNjR4 z#(acq^#rD+imfCwWVXSY1)C?>luI?6ejRYQgLC+PR&&-ZV- zMl!S0+N{U=Sp2BlJ|Iw^7+rs(>vd)0 zRRJMnwow*p;IC8yUd1J^VSB#ixix66FBJVS*7?G%EqSUzwM!a%_rS&InyX**%WrwY>SEARaIF@B*I^Li-sqRZBR1mYd9~+zdJP89*N@0%*~<~Euwj?A;z^YHTfa?~KI~VGWlr#` zwluQ?7ohSA4sev490zWAY`4)}I=K{21IM75IHH~t6f_Tv^5%^Pd2C0EQG==+c7u^{ zWxzTht*?rHh{Nt|@eFuN%4f0uZyk~sA+l!5+;vOWUewq-revXY$4hp_Y)p3+nFl;H zytwqa4)6#r;~sGXoBv5HMOwcSx3RfKp;iu~4DX>EU$XcYWaCd`Ez-bsRH*yCkV5eP z#mL{FqpEf`$Nf(*Ow+yBHgPrZJUe&|nXP6FTCHcM6++<&CBrzx2gKo4JV(Q3VaL=G zo7Q41o}Ra-i>ExSPXHzU}%( z&f$IGHUzrD#ZDxv>`3%WFzmK;XL!2v+}1RAB`(lP;-3G!kixlG)3AWz{_)ssA5vG- zj)4b)BE}(_9y=~?pJ+^S?_YwxS(tC&&IT@ARATuVqiD6X=|#+wluO_a*hNSqQ#C2t z_=!P>X8ZT>B?!5G+@pz}DZFt2{@uO=RmCJ^MZT+Y!UcJ|mP)^4-!N@lc5+{lK0unXz|ZJT}1dJs{X^YtWKESI?ZL`Nz) z75;F<$-j_O1sL+3io>W$PT@IbDUq*3{Qc46Ksao+N&@As>=16Y@^zp~-9rpjcahwW zQk(hTk7T4bM_At+Mr<{GjPj`OU7ZP3oS zy%vHIHBJw3eFgSIO9c8j!lz;1N~^?n&*!Z%i1O-sq}`V0X%1}+-BUpKg-U2NLXm0Z@~~1YgNcnM?F|i!%aG{KXIhHXz=fDT8`-q%u}CO_Byy?W;-1Y zVmd+_G?z4O9*0@+0*zwp3K>+-b*FsWQg1~nJ)7ZY8QrVazFjOEN1hYl zx%-|a&w-_K3Pa6;eNuUnfVcJu#JU=mQEXjH);q3--hP;jdRyKm@_z8& zF@2sG%XZE3%#6X$hMzSH_gug^Wmp_aABpNgvf8AL9mRGJaStkq8S9yt`Mxb$kY>%^ z#StkUZ)jsk4eTzf*@U}b(9bD@nc-EwxwdrG05WTFVR}Zk9_yn_a zNiWcpG$2zA{mfu0_t?>#DY&LnEQ~@uP(;@HjM3`WmsDVi+o7#srDy$(0y*iLgT6m9 zmLAvHzilm{5iiZ8NB=zFaOL+~J>-;P+M@8z7@;M4KaBn94!sA~|mROH|3cqQ(N|j|u zM>nEZRQiqOJx-8%V_9X*X~R%hMv}RIgKoh2`jvoV%-xIjHTY28LiGISMiW$xO=4Wv zO(G4nyJvsF*pMFOc8cfQVJGcad)x)OvN&rUWu$QGd|u1s8iW>+%YR4$*O|sgSbZuI zqh0hQL~age(jo4#1ZvR_K})2M1e=gOvp54-SpP%k#<>KgD$m+UU;LQD!J{$nV=bl* zBDlk^Wr{NU!R%jIQ(%%x^@$jOiX!Hi+JOm}fI!b!>r8;i#hcCN4E~)eMJOX<&}+f3 zQn*A+j94Vs75=_SV8aornA2%-Rg-O`u5NPo^tEZ z?}vU{J0kSG+H@<@c8^UbMVt}7eteNW@!VrG3>aS|3_hyGVO67ur8ii?amuE7%mHK1 z7mbg<7Z1(SiVx1xdf_<;zp?ne2+^FdXJWTLnT&nvs%D24e=GJg@muwXL#q#;q&2?i zkUs6Eg-+76R`AjhSmr$H2=d>$dbXpB|JK1RYM;9J7KgS6zt#9{>~LrTX#A zp^RZKAn%R9!*}c}$Pd4jFFCYf`0eOFNsIr%p^bSH_@6kmbx1dTk3-7YOW~|kVz0+D z1_5yVlaW1{ceV$(X+l_RT1IY#GdXXbtHL?1)K!vKF*!R!kNamld#7a;mM%!1Gbg_o zR#)YE$Y1b`Oq*I-o;S5Tw~RoGmH}qqrv2S`a+5s+dtp5`_TFgS*mtAtYdF`%Zwr3= z@#~#^WBvm8(s35e;dS(*G`4D&&G(H+=`@?~!{i>KO_g13uNtmsL##n@SwQkG?&1u} zm;6oYU8?m8f}gAHFPX@duW#5CN8tV+Z4#1;;b}>;B*U||=4%6I*uXvb!`>iBjh)_- zIfbR=6?#+oVB>!>gyF#8vybwh!L}=NPt?M|YSZvrf4@qEczv~ql-x2}5$y|2))pa~ zT?JrXFed)s3`6ss0e?!R#iiv<2)_^C{27_xybY|&xS2dR#Z_KWTD}kW%j`fIR|J%V zDfNTfKp@d3iG^K=;Fb7o*Gt0m&eRN_B|4M&MxeVGIk7bTVbT%6b;l=Va-{GLM1cq6 zM-=|zw6HYrCG!DC`ci{a(awUOv0xS@W-$^Pj9MG{@i{>2>3|_riBr~}ShoSofvf_t z&eoz7mXRrZ68KhiLK>*`R!HRV^r8~y_VVc(n#;EjqQFRvg+E7&bd;94$};myT+9*u z!dDnj1LB^m^$O}J#CsHJIg#m@079InVVbA1TZ494_%BEl{EY0-cD{Jcnw>j)VtHPE zIa9GAx0rT63Nq8q*CK$DuzTKA`kxQAQw z@fV16kwgNkrNWtV(=!wQKla`|KFaD^AKytPFo8ewwzYa!Ali^Vk8*QVv9l*m0GlDUmL`rXj4s{ z-}9`!-tU=J<+m4- z)^T9)abk^Rd_pUyVgk~XjP&qv;*26CxfhJ7IuDEV=xMM>|CM%9PsrC~?G$RKT06hg z&MVsaSUZE>mjG94XR&swwew5u9M;a?v~&Ik5@3>c3bk{ucK$;<2eorbI|KeA0mf@* zk#_FZ&M&m{igr$m!|s#1*x8tFYSmkF@EuHr;{%PZ;4o1RQ6ztI)h_zZ_RqC&p2^ifwwd%il@pS097Q`Vs_P6()4&6P|#il0TJ)zIW za$g@DTVn=&uE0ze43jf1s#0dU{CL4J6@Ej-SmX}y(y^z0OuHUGyg6e}+d{)oYX z^+kK03Rz#QlI4SbLgM~WNdNvRJogk)mOg^`DCSOwWQg<6H57BT@&GcT+!^s!3nHnv z>Jcn_&&1N72&@r@39k+O2jYW)ihAo2L9xUA+o(?v4)ekL>kuKl?-p<9BsASyw+ap3 zOGCOio4+Rm?}n$|xWMm{ZI<@9`*+wFOd6{ZBXZe4@Vi*UUmrcuXyg(c(LDzCoe(>4 zEm#WCYP3ZV5H(Y2$__f98yCoik=zsSV=8)5t&0o61fm>jDp-A5o3rJ)JC_zI)P%_;syI4+p^wVr!fmJy(woqIXv|MgZ1dg(sO z1)Xj!EZc}e&pL()Xjo7mhw#+qg6eU!h58SFcQ4B1BPhr+a6qgk7Sm#RndAxwDL@dG z;fDKIhCe30Z7MEGr<96vKvlz8*Wgh(rntbri_JuNXCY;p`|}wEcK%2G~_!vx!JJXxZa_>*-T}vCm{) z`v4o5H)VskNLk&>Qlo|ecaW!Q=tLt`n)`$-WrAzwoCfjBCwV}Gba=jGXj;yhyLJz<>H|>#> zVCGG$Otj2R8_KkjLd&+)6_gkZS~|TJ7rFJqRhzUY{0%&`A7yj~Sl^aj8+{Yhf<`|C z`yI(ktxaUI|~tS`t^U8dt8V;#masr94; zHZ$*Wpg=9HH5Fc{|Lc)&Tk&Pz#jb7L&z{Y3g|ljj`LrFgbpz-Ho9_;>yplHe@PAYJ zQlX$4zTEXh%+Be++fj;t*4)2971i9sKENA1x*luw6){+|&cWibwd&STsJQeKnAY8l ze`8;Qs`7mhEOU}q|zLamIr58-R)0UMOtHh7cPfW0J9HFZ(+RTgYM3nnE$ikV zA^C@%D}^6;^ArMu|K7y0uSvtPKIo~9$B`oLrMA9vcf{&k@i-I<@8$rGvWC>MKl>dP zFwD(bQ0CRc!`720;GJ_RXPqu|_+wh4Xh+<(ZIp^uLF@Ld+X35#UdyB(t^1_HeiiLl z(QsGf6Fj|#P#oa`Vd$mv@ z%{+uBwT{!o)>(=Wk<{tXOck|zMet!NmOsj{Fb6J8h_zA&nxScp6@x9l5i{!Trz@P2{go0^#mP>vU%-yI`9|IGAMq1Ljdl%o8~Q9>+@g_5cjsKKm((D8-S zMtiHX3|6Gpr^4ugoqv{$U61~!(rKq)KFXi1&W_s|^Z$Qwx8+`he-Kfn-md@;OTaab zy8LzBw%X>=*#hiVx)7}|Ql4yKIwm_FyN5NC`b&DU``aA7J z=A)L{yGz&$@n`2V3GWNRUD_I~05@o5j`r&8zJ`5Oq}OuDG;%Lb4Q_eF>KG-5&oET! z`N7z?A?08Xn#eX-X|fL89O7ESNL-D@JtAkUI{|?B+fQWoW@`)XO@u!z<#NQf9vtS_ zMAIUpV~f=U$*umw9&0GS$#w*eV|r~C@(aZm9Vu2Tutx)t=1Qd4MnZ|i3qzwVIL{Bt z!TQMkLiW7{%z{ZY7PfyMWTJ=ZM2|5N8>A_>zCr+mG)&7^Ac;+=o9pH4t#cRw=YBHz z4yVuT9Y(lVOfu{w?pPpv8Gt?YNZdON%a;f7qNk5KyhBct`8$~X$rm=Wcd9QTA~aZM zAWUF7ow9Sf{^tH~UZnL?be38YJ2j8eRMT_oi2!NpR;-G_Q2ox0I>3bzz}xpZ`6dV6 zMDmJT;2ZL_6eqSg#G7A9(33ld1BkdSh$kpc1m!n_{bPoucZ=5jrxs96SDa<;N^YaA=Ij_M= z1RI}w8oqWxe2H++Nue-_39R|BjHzpo7T#W2=@8!jj*_MDhS@FbFa`7uiccZF)qj|< z8kJvPtXBU|>kko4H)utA0K4D#zp+0u_W#d}O}4Djo$~qbjD20p2bg!+ReO++!w!8BQyDZLTe#G2xCez-0#(Eou?w^dY68{cm6~?{s zxQW*JJD#vuZHrl~Mz}TnS8x}iliWRQQJb~zf=6>z=}&XT9?{Wva>HxiCjjHxxBu~6 zMSE^s`%d|Ju6hJdGGI>IrS{tQs$b`-rgyW|p~Qu1%};XWjCRW}aywl6PTrEMHsG<> zzWq<+hS$C~KA9_P-~NP!vi7|m>+AN~_wJ{2)x4*2!)xC=5U+FX+qEsX!?o`-59g|l zdvay%d+Xs`b?|NE^G&4NtbI@YW3E~QJ~-FDn?3-37i-^%u7&F8_j9qf4w_B5YTNEy zHU4M0>S;U~$k#kP#dyX)maFRA3soBU+Z4M{9Xy_^P9W?K1wy7Nn#|ZULMBNg_XRkW2SG#(n8~zDW1b* zWg5%-q*+p0SUJn*t?=oTP6dX|x3Sal(Np!AS6sM)xJ%1X?L3ncBIowZZc(TOkFf3gUJ@vQF}5dY*x{Y%6PDC^@(a(#)A#4b@MV_ zY-Js^F@wRT)^0;H{%mh;TbuD@qe}q;+cY`j&z3i+{dQ+C*!((cd_W`5*#<%-E-_jss!2*~}NeXtJbQg2~+TZ;Ww=RiQAyV$rtH10ywyji*@COx+*{t&y* z?nE!qjE3;b9CZS_3Nt^Sr*=Gq9d5s!tzcxdz4W4KaR`OE%VVI7hBddavcL%wq!;E#C$hECG(Cv_uI$~9~DJaT%J)^irX>i-Qi=dtA&2HWBlMLZJ zRIi2i#{#3ZV$q+u8nvc>Fu(@7^D&g8H6r(P2s{5ZWJqs71_5GC6x{fvhxckPY@)so zJFsxsBx_{Qe+;yDZUDOMu8x8}(k^s|4aG?WTRy%V1Sdj^$+e2r&^=NUOdY=ul5jpW zk*Sz{+uGpEWu>JAK3t8kS5dfr(9wDc^>vYX(dd6tTm{e>7U7R;H@2*9ujL?$aNe*L z4qFaOutJK>><&1%2Q5B#E@N|;1I`7h$chN{k`Y?Tc&DbCt3UbR4o|#w7B|66co#YlYB(0OI%?WLfv3laNeW+_Puxz#+UN{U83XQAw-&UZC)6izHOCBnk z240Ts!#4Y7t%TVFwauY0S@j#k2e@V_X%SctJnbC<_l*N=7EQU&8KITLC^3CR`?tQ2(bu0 z1<&jvXbA>55?h(`vB!XBt#CiFXitBF7RV`r-2%hAGQMD`V6F6%ai6>Sa0$KZ)k-S*73|y?{!2NZ94S#aLVi7m~Z(izA&Uxv= zA&X^RIv(!7!o0Mv6e4R&>Gviokvi4$D7*jOOw}=Sy@FCYuA!3}HwXK@%k_kka(b-l zrz@ZXt|zTYtd9;T#q>DrYy^~c*)cVtgzkzK=_#MbD>wE|%hiEwwei?7$;k_^{iQS> zuX<;em%)&#zFZKkFJ2|j$Sc9TYMB^(Ct}h+VcNpE45uyONdzV84e@gn+0lj>#*aK{ z%Ms&A#1MR0Q8$FqRcHXoj*>P4XX)T5fiqb(124he+PiqL zN*#)Djyy}a6qN~ZA6u^?u&=2lpmh$rL{TKnC@(AVmRDw0lxUH)V6YlTT8?@l;fAXT z01s?kImFMc-bCA=csn#jyK{}Z(YObVn}Gh1cvZ%I%D5DR9i4TLuoyf%ZjM@V&Kwbk zH-0}yVJCch974(9NAb5A6gv@ruT`yB+3k?mwBmWU17@^eINc6^T|1@N?ZC^sDsD}Q z#(_KKoPBfNtkMFQNJmxX<#le=1HFrU$D(A~tvB6HX;vo{tlLC5-ePXxs7f4>9diTY z!|pToxFxJgXj_%-W=$21En~rTqBxheC-n=H!URhn=Ho-yxz#qxH}Y z-n91C2)Nz3zeaF2f4!$Sz`o1edI6~_GbjM-zTIXo<(y!jCFM9?lp6*$mY+bLgY~1vy1Ooa(27=u&$gnhX!mcmqKLi@qC-0vM zh0&I1wC@@;5%@IPZ-J)I#aWx+D|^U;M;ij{=(w@-SGk~EZ+RKgk(r$qIvq1niez!; zaI|Fy6wCjZWQ{{|Oum1(3uGQ(R}Y#6`&+P~G$Nz+M~qpJlJjJbhTeZ!1*zEH)x5V5#d@2!*8ys zSV>Ey4q10;*X*0uf_ln1wxF&v$4%!<8)8V*r62QQHMtdFJ{`UZ+Bddwd32_*qr-T` zH_nRon!4_49VjmNlvI`mn;xzy!nIHL$fYoHr3>s#1}L3bzvsNFrw$g`wml4~<^o$6lMDZ|^RjivB9HDzuc`G3psm;R|%T?sJ zfe3m+`Ng^kjp$i;$!a)Ytg?XW{A~g90z516ti@A}XA~G$kvyl(dezbV4AtIQP4^pf z+MLxa#`&P}|LKvln%2oVY6I>JKJrnvdZ?YVnkWB&xZwZKI;-hiuWEf(^Wcd&QR`LR zJ*%1W?i@L*=}n8w3+%I+$Nmhl;4IH-CcZsKZN=jO?=~Ktqqg8Vj>rAh9901L)V$eh z+sEJ=-nShCZsQzv0DL@nW{z3|JpDtxPep;IqQH9N%4EA1>AJ8G-ND${El6-wHNq{+ zC}WIG#5PrnwPN3pIqEuyl@aJ=9iDH*HLRVUZ|oO7-?-_kThwkGE8F&`IpMJhsaE~5 zaDa26SU>WvE-mcp03*lveV}px++k?^{QQ@KBar2#z7=J1jxvQu;BZQA93K4qd2^|0V#v%q|dj$RqVBe(9wy{OFKXDN%ka# zPkgdOk06qbjuIuCzb-^%iIxd1!BNK6heAh&4~2Gg;&W7&CqBQ?ofDsz*@rpV>2!4B z^Ku)BCq75*#Pk`t;^U~${?P_$C2Onc5<}<~qmagEBt6o0bawO#$JtS4e>7s^xnTQzI>b*<9KIt)~XWkiJ#%l190v5pQSwT=i|h=is=Oo zR&lGUNN52N_&AZSW?F!hQ*jKuX@)N!C&qY8yvt@3=2iJB3(202PQ+dlKI_`qiP&qy zSe>1SP4C8u*a=Z5VlPv1doV6RmGO>t)VcC?v39bxQ>>j0+Id_%hqUvFc7~lN;jY!r zLhY>6&M&lcP&+5J)Bk)4FkU;iYA2wb$Fy@uJD+MNB~=29)6RVDtk%v$+S#L>aDvDaq943c9T8}2bucrecTX>Lses6s;T9xzD|3+E0!;F zVPa8W#|0dQytm2TP%b$HGh80)Mo@+kVi+r)GkNUi#l4W0+UirF3L|6&Wdu;F>u$fS zwwkfkK1ko^;!#ztmmxFIY^*rp6s92H|@^(D7J{7EtD{qW?$OugneE!McawAD1wX?P1>Pib82GWZxFR zdzN@*e-B*Q%>h>wVM)+22*4J|fj|D@h&;{rO_wZIH^99UE`N8!&4fE*a7L6oy(2$E zQJy}A=Qy4tcv6?>yH6_MM)bZE;8TPa|Mfv3qua(zBP?*R+yTN&Sq+$)S0(q#m*wFe zj*`*>Y@adSJbW@pQa``Au*_VJz-=*w1qOr5UcIMP@uCk23oyCC6&knAC%~RYTwIlj zpI&%-A?^=}<;rd%0CBUHF2|?;Gu*C1e`q+ou<0f@@G}EF=lYhC4nX+AMU04UUNP3K z3nu5~7Za0X)_8#Xn%=6wwuTTpA_UgJrw<8mKa&iU2ttqR;E`;IJNy8}ua}guq=jYE z)A7ynV&XAuwklZ$ltik12opD8o-mGaL$g^adxv6doH}46g%Z2qZX^8Z!A{Lc-Ej*p3VHQ zU>2sSt?u$UoB83?8H@kr=ZB>;(aw?2O>pgXqyNqM;Y0Hg_dKj!Ma>U!%8M18-Dv;p ziTU|xp_q6~E35KyOQR`ycC~%7d6f(W1XeoM98`%B@aUC2w<0fJ?#M9c)D9$OOC`6P zr&OTPfcMsLkuD99aRB$@%|d9lymm-zvZ2e77_Nk8e0Fwe9;+T};dlTBWU~vB>oh20 z*eRm-1bFw&o>5Z9rWh4_27G#E&nhaaDkLq+Wm5s_gITex3`lxStR8p_!otV5d?l5I z(zENC33db;jM>0I&as1zoA;^!U6nss&C^hJYa9U#?gBt@$q_7bVcQK+EBa-qg@P)3 z_H5I7Nd6K)le2Z7SCvySuN;1MDHI5qspTjgT>{K+F@Qs}GfS#y1BQ8)ek>EyM{Zw4 zTtq4bQtxc^>V>5!Vjucf;cW%LN!i%YI0ySYz4OYa7Zyp9&ru1NEtV^ou7`Vrx<=g% zm!|%z5;v%W@wvXKX?U?m3_i|i- zxyD|x>#=__UY!#+BIeo9GoizwS3@b=3uZ4?$MGDPgSBxyiF2_}8_y~{{_@4@2%ZrY zXj5MB3hu#g0k;xmP50U=^hy0^H!iT z#;QvcyWh69r=o(zs>A98(0WfS(7TVZkgJD{RFx1NgE3^}o@nl#LL8J(u)}L&czjos z5r)ISkP#82J`MqfFoXh!KvDn+n>P|jvPzlQt_UN(iE6N>(2fKn`^(%=JLxWtIbR_c zf4Pw*k>+%MXh0}B>r8zN)28pz>M%dFS7h?jPGmoj$oe8O$C{~*5~`v9W|Xk(WJhV( z)0%~eP8PP4tm*1ZDZ~@uJNpk*8x)OO#kEQi`;nNij1WtiCRX*D7?yU(&-prwPElo6 zk*9jD<6M-WZ{f`Unr#DQ9X`z%fnVdFtJDO%J zF0j~AhDGI^iURbarcMND*Z7|YgttXv}&QwMEMv}!;m5?fk|RX3`AXwJRnIs;0} zYsJDg0g(kZig1;W#XNx4tRxM(uEmM=zD;SgA{AvPZK|fh~v8#B?o$wrY~;uPnHU9)47 zq+kGp=~<(rT)S5-7HJBRJUA$HD>J%g^s|SxaBx-*nHRxcT&qEzvnQ^jT-|0ydC9-b zS_%h%6sQPWuau;8u~IUyJ1ZrFx?U+699CQ~C71z(GFC)wZ-6xC;*_AH^^u`^eWat+ zh;z(p1ji7}7eahwvW}Gs>LrG$K2Z}0;$ij>THr_p)hm!+^mL*lCHT2fN^s0JLVWC~ zK*4{WN_0*?NT|Kr5ziOBbQiZHrWy@*r?(@1%|`0%cEk(1q2a!;OAU8gR~l}tnslk* zmMb5-(r{lA)^K;ExgHVLNq45Xj@9XxkWp7Xy@OxQ?H%(wUanr0*uklP1?CrJ3rCR z3)*>4J3X$E03)?CLpxR4`Jr}xqn$r%C*fKNaJhEoX=kl=9@WmP+Bu`0A?Z2~+F2w{ zEY!EIUaB|N&Taj#D-nw2-V}ktvmjSrEG7ylEUCGYaV>Fc4Xmp_GzSR`Y9>dn0Z>=V zswh=K2cOYQX8>eef6$x!w~ z_2>c6c^Pz)LH}h;gRT-6GZcjdf}FD3%CJVKFXh<`8(le4wvaBnWwmOM0&Yx?d{&rul-OJ#TuMu&;Hk^cP+hHA*gS2| z<)-j!K=);Eb+x!I^{Tk>YGwxEJ)ot5avsXAiac+AF?vfgDd76dw6Y=`zV(%PHOrHs z_fSZ6nB_5=Lp}1nCBUSFwm`yjT4{&ZK_Og32bI|tCif*Ks5*f%!GyLnpT(CjefngS zaD15VTA|wqbf;xj%_`z9iz-byRl&rBGf`3XKuwrD8DtYGmsQ;%fy_i0bNqb!n=vDO zI%bt|!(<2{?#g8aUBS@+mjI$LwuXzHU~M<1G|>t&09|8kP9pPNDFpH)BG ztF!;FFy5X1EYkWiYW5f6y4B>#{7z8c7q^!>A#QIqE|YK=t`fJ8`jfbQ)#xn3lhitK zlht3u?WbmB6W$;9BGVn9elKo{x_k=12dcZo9i-G$eh*f+iaSL8THK-P@8X`LrhvCE zLna=MO=8oaKE_T1-UdEd*YahB-o<(O`C9adG`%ed6mEJ6qc33}mRM0?Y7EZXv+VVR zwx1y(OINL0a1U3zVt}k ztjez}k(0S+OSLbkI)XE;T6JURd`>wu$ufwgVi49el!6qWiXqJ}aA`eKrmEL@x8ML~=Vv~gdNI`2FjAcR; zw`Dy|XNBWoR92*{FsPBY(=r)0JBk(I1+UJ-Co;UVEiMr%DdDPFl!uLd6EW(Rmz`}n zB}?UW5MU~GQ%XReqf^)cQ8UK%mLW@HwpUWI>TXGIZ_t}wo-gb6n&K#o)lm$y1&E4O zdYw$Zu~<;C8f6SAj`9W8D=P9=nZ-?k3{eRc`K#@PNU17lAU4mKAza>ow#)>~%)7m0 z88p>80c|RuEryik-JV&3!P8Oi&^Rj5rAnEmqUQm97&jnwt@`W*>QhU+^9rj>O{0tx zl{-T`-egWbA>!z~30;UGk-DN)i2Xq}vploVQA9fJ!&}$3w*79nA>&FDjv!~%#UiU6 zbg_oo*>$moTvQjUC{th$v}4E+tNx`;t4bwg|TwPu*&)O~S3f zvjxvMJlg=icElPGD>{Y$1T2sj$$*Wl zvodjuPtWYR-hzqwT*QGKAA|g7Vmzu1uB!4Bb9<7@fca$@Cd z&vbiNUEsQfw65j51E=?`|KJfNZD{V7FZ}~|U$*Q#3QVidBp&E*u|B(=hUVTNeOi{B zP}~IHxz=Sby&L*z0mu}mU}1T#bsoQq@g2C1P~{yExQ?I7+iQRP61*Uq(EfNv%(uXG zgvAVddvbWs+>4;g>jYT3*1co6Ywo&BuxqZ7w$N~z-9Ed-d+o}h^5Q+zJpF$e?qTJ* zfBvS+i6-6)z*~{C@x_~KXW^{na^Ta79q(4K@2xL{aOXwni9$!;B2WTc(vA)TJ_h!k zv2BFJ4t+PYA?HgPdKP28-`?;kE@gXk`U5kRwfyZ$9^&1u*Pl5B?w-Mco;jnd>tIr|F7AcqSnNHl`Sxg39^;kv94pcf|MqwB;MSKg zw@!gL3H@7tGAiX_9_c|EN*7L6)=hv(2yPic8SeLU=L)sY@{xfLwmW<@__^04=1JIk zXtDNx?^Bleem^&Zu#n^_MQoLiBR${g$tIo5gv>AsU1;h<{x7LC89i{B8*Fkk8U^s~&){ zFCm`&Rx;vwvgHC=M85LgPjN5mx}JFb-5@c&ioK-dY1n!~Q*hC6KMV)^#+c(_s9-5H zqWoKA{T&oKSTX3iV$b5RF^KDW!5GBTX-GeA)t!sWK8Dc3dp%xBSCcz+9K~m;l1;`OS=%QjyII>-f!Ls&)esV@ulyrCuSACbW7vS z9K7R&XAt>rhpz%%p5p|oPxnV{#f8RfEz=q&dF(&<=LL%vz?1Xg7=SIj_?}Wy9Y#3g995~+)oY#nhPHxS4 z@_8pd{i}dDKt7)~zH8qDY21q9O1otdHuQ?^5tyy;@8VW0)d&ZGm()wTH|MD2?&Vw)yjN6wt{*2!V)@}NGZ9m-RoNO&J zz7N+mW-PzhnyEk6Brd}Y>2U*1{t9tRq6K4xK(8?E^Vp9IAe|TpOUkz zXhSsb;Y)<&(qR4AhHWvvp}Zj*mWG4lL-Px2=f>a@A45WOmw@rK^y=TC3%p0*4p)M0 z*aIfGYbGVKPUO%EC(W6xbO+1a)gP^y75oG0!NtgG(lZ$eK`VF@qOu0z8G~gF{~Y&P+}^)^6q7xi7IMJ8*%iSfNzVk?lh$Srx3+DBsS918t!nfG zb#8p<2L&YLO7q-Q{D>yG>k*Rd1CjuX*&W&^m3{cC_`4eC2ZHkdF4uCu6Ui33z!%3rW3XcM?rBrWyg)Qj8CK`Y_>cQt5JyC22#FSFAw!eEEMJQ~TG(L@QaHtT@L zI)GG}sxam*4%{b$*(t&1>JZF^ee4Qe4--f*KXbq{A7HETD&$Jchrha?!K9q(;<651 znsvM}?$Wi#?GlMY+=w+f-i26AC6T_0aZ|i)lNOxj4Qxk+gxw|9w?vbshu1Mp7EUF! zO3_iI2!rL#Iz^Vpx*jR2$sQjJaHLPq9*;31=S)p(WA9-0_&}XYJ|Hf9u426La6=J6 z;%YFSXM=wYSqt``&!r%N8m`B5vq2tlhW*B2U9}s1#}^|5^30!?rxd+I&FR8bP;yz80pe ztTX#KCON?00~kh)f?qnA(c(d~t+zZ^3JqBDxvBx78Uinc&;wa{j3L>&qD>5$62twi zWB7)|aHhQ&PK}coaPj_;7r~;dqEhzR8m6qNy>QRya7`ZTA%xS6-D}$xx_e(K26x>Y z9_DM!!xi`+JYBnX>(B73$+0M8LP)x3akzP8wfJ{XYE$0xPzU@wmhj!zpY`Eq(73{F z@Dmn8Yfuo*r1QvXNqT03^d31UYqPdm%QheyTE1?vHupxLqjo1UJkp84@4!QB^5;Od zugTiih@ln^X_|4km;X+T!#HQ&7`r`YL)KsDwL?mr36q-d8})gXI|jz$V9{*P_K@{j zFBujG1*c5iiB=i1;+LO-UH5}%?xDs@Nx-uPe%7gyQz8G_zbJ14JaOG}s41*xYCvMQ_qm3!25V-RDS7K4h7LRYow`5*Yf(S&5+g)sW^!7EpeOZQIm5# zn6o|jaxiP#$OEtb9p101xC1paj@0BFW6bGIzNIF<5s1&fswZTZVAheEEO?tH;wc`1 zt-k8-jF=c^#OWd~^m314g(!UvbP}{39(IoeqD$7 z0YYSlW;~r#{Ryt(_ut88yM#@ZR5;Nzn{n+SdN3*_!K6-S6+(2SP2 zyO}g3YU|A-Ul4YYb5@JkmWrx(o2`$lWm*x~L8)CORd441XC^0)im4pvOU0WzIIemJ zu9}0XJLsMY!_y6`%3O zhuFyOP0L-9&;8aFtJ&uqec|2EhYLVPqIFg8LrQA0>Lh|BrZcWCIPS$`fHtm)ifMd@ zF-3wMWw5L;l_UH_KhhG|!St%N=L*FFlfp4SQnX_$V&ThNJ zbG8L%?65|N;jLC>I&3{nW^5Kxc5?o_GCDa!BFTyFh~5bxaL(4?e)d!IklJYP=iwd7 z+8WF{XpNgolKZUB;`2D)#+&BX`nJ}t zUC+*V@vXtEjUyYvZLub6eXHcIw~~=uHsGXZ{OtWM$oh*%6GvRu@0&sYQr}~`CliKW zn)R3@Sa+3>8@2{#Y#%ve+sGTon~nsXHEsah^3c2JBo8IWxO4^u9}^(7TUY^|afm|2 z=UMw=Dcv06R9~pJVZ#Knp>E+R=DxO8h8h;VmOLy9w~4`O0Y_M~)f&unx60~P>!NSB z&1x;jFkrSONiYi;On4avO-am_p69Y{`DVxu)l807Go_wSq387!jOdng zY{rm`$^5fymzc8U`k&W*$S{2}H2q$7Bi=rN=cO0(VeZoivpfy=3f6vodK7W&7d*U# z*1zUUfLy)b`V?cH7~0_nZ_b4C7s$4lIVC`uwBPzGK9CYUuZPZwDLJi`O_n24*f`@JZ!1@KKV64nYdggHY-m27g zaRR^z9-Z76JN9k(YaxRbE$C+&f}X^5kK30T_#$I%;!gC+%DSrJgHU5q@IY`cy3Iqu z7p#i_fRgI?MbIxk5ju_rEQ+$w5?i=|zTr#Asv9P`A*3cflasQ+yS>=7+_gNITr-V!%>de}O$#=##>g&GAOxQFR@$hscvu)b^aMADpd z#(ML%4?;X6AhdjR72 zI^uw?EY>%}zjlc7O`$@@I}zm%M)TMdCWUMO9&io%i@=v5ZyNvg<$SZn+6W9(hkhEr zKTghO_*@NHLt>JinK48IKa2;F22){&gNm&iy#(*PYnC_TMl>nB7nk? z^iy(|Y-;U{GY0Fhp5Wx1NqRQxE35a#v?c6Im>inX?0Y5f+lcP(_aKi;W36+Cv5Ag_ zHpOAmDm*W_-}(cRNG*-8hksRa4BBEHRK+p&+=RBPHq7`k_yx6&i`?j**Y;tRHP<$w zk%xOLwkWiZrBVl*;y#~Q>-uJ}xiL2GtLo62G0}MG&1=ucd>W;73k-6gwBACa?vsP| z3j?L0n~|fJ5Qs9lsYxWB-n_;g_L{S-7>^Xdq(LebrBD87(q|p#y&kvSo)^cjcZ4G{3)uL@fB;(`nP?R znD(O6hDI1o92Y@9L8b8i)hPI8G%fF3w2205%3q~PWE-WVcbtEZ9zMsx^9#kn&X*wS zVuVcU_9Q)vRIp2N*L-Tvquso3Lekj`O%w*% zB{m=C+hsM==0lbw9qk=SZ>h~`M$+r8L?k_3iZJQHz4&%P)aj5-y^%0!vu>l0)R^fF z-s@_fViD@7LcYt|;qmV-cjr|Gx;C9nI5 z80#YR`SoL8vd*Q8UM(m1S2XHBfFXy!WP4kc5d5Pw>8Zk{Jp#-~NN>LDY5_bR24Gr5 zK0XA1bex0bb0AWJj*RWUP}={F+C?#ro;_&I7}>aHN|qom2@|{PN&(CY0}S7TBxA_5Ax&Rk9ce*w_}dSRz;z2m z?Kv#_rsXZ##~QK`eL`_BKN{FT`a)A%Z|Voe0a>zZ$ofJxWc?lWN@9bFzXw2E__wHK zvVm%mSY)iGnIk_<+V}|ifo$V>*vgX-WNuRJJ@5&8Of??0^JCKY`ijEv(HicaIx4n` zwVC-mZ2dc+nE1NAmw$D@JErmKuty9$q-g9MAGUg-jb`{#kn)(uzQ{vNqnrQvJ$YtQ zbsj2>f9>Cu_X_{oFO+YTA%)!i8Z95RVthj)utxTen?qK z0)Iu|J(T&}BcBipt_0dJ!T6O8N|etXsPGxSLEwrwhn=hrR$OI zc{@t?=Eh^JuIzWk);JBJ2O3&}Jy27cFl*A>L>2AfU}dm**2pi{d~1qeac)Jd^sJV3 zw+dv3fuKal!7Ks(6hNt6SIOsQ`2<5RkS-wrI z29C5!;UkYMwS%BXAtoy%pKI|MY+~29$iFrO>)gVw25TVME$+3A%2~fDT@-)bzBS@T;MZ=VW|sJLBXEZdbrI5n zLUN{)_45uW4Tpih5xtM+;eUj8LRzS3U@@r5`Pn(i4N49z-4Z&IDE zsj`e?z$i9jiQqAW6E$&j?Yfwee_C_iWG&gR0UYZwVde0zJVU13==-yf-)R~*q{&Wy zWH5zzy^K4%DX^=U<>9+Bc)JUVh0B5gO84m%?zI={SGJ#9F`sM>f8MtUpZl#Rn~od3 z0BJVKnkwk+(e%a#?~!T30=I|&mzvMR)?J#I_C0K6YGQ>TCS&{?$VX&-HJnUait2fH zQQ}?TmDCr+%eBI0|MOb=)UznC&XtPJfvSwg^3$OY=Ykzi#UwqO?J5db3EqBzx)XSb ztxT2wDUIi0E#}nYdNKN8R~p*kD6Y~sUd#pc##Eza@lrP2Z$TeRF=)TFiKLsswWMc$ zKsl1Dko4Y$z`kR_qqURVoHGUSBt5V2ny6nhV)34g_rw}ZwD5{g zSMZ1kx)+u~Mo0CdtgY5uVN1uCravXBtgi({2u=^kmn*#i>MG6*h9E^Xf`VRx30;bW z)?0<|h%}c|J3TE$024_5S2EN4DDGFISxRPg`f~A~id>_O{q3W;%(RqrSK3|Y2zZPJ zXN2MFVuN3y_0?NHLgNeUOS5G}-G5q7U_I|5uGJI;n&XqI@5kHc*{;N->J4}cX1iXn zl9QgViwTq>0YUly!?Alu?hQ1rT2tA0uB>bgWL?2J_}#246aN#F$6pb9RbZDmYu^&Q zm|}ysU+{(!Pv{xE4eQXJ7pPd9j+h&eI$knH_yQwXn#QlhgnJ$oCNN9$Khu_1!>iQS0x((Rz5uacH(dQ^IlBpDX} z_@|KWCr$$~`M7ihMlWGDK~=patyH!s;7VynUm|niI+lyw`N2CZi|fn&ubxw}YowWt1iJXAX+(Ia#)2Whaj+=Xft8pw^0=qv_3SBK4~Nb#sm6P0!31XQv& zQtP?W72J;&o@Dg{y@KyyLdRlCl)*7#4=EMUv)`*oD@S>_65GQ-Mt zAz6KJnc*hQwwi2LFgp>j9v*C*^SEW7S%Y;YGA8@X8muA&)#b`75M9fa$14AW+>eg3 zL>2NSL#qU22^!L0m?SuV`vY;ji8bfV4aS8$Ts}^~%5sk# zu^Gy*_ttxteP#oL0dq4cKAydwso7wvTu*Sse? z-iJ8e)8rkN4}hP-cgiU_DRY83$#@dyKy@bUSh8quve0JKz7Zi`TF93g@(mB>3;|wS z`~{2lrrG#VP|pEs$TuY9O9_EX4!R2$?Hz98b%ZmITkM!GN!xJHGK50dq-4^U7KIZ@ zcPiPS!wyHOCGS9=r=oD%r#CtZCqi#(gdX_Re%J|7VLQ%yh9mDC(aMaX)qZ>Iw_+%lQGf3o{R zdhL&kQxHy;u=DvIcC?$$Cnx0w4B&;(ZaL)l-i`ecY!reuGW<9A(i1P2MhJy_r=$;e z{Ox#1qO^LjpQ=Y6(?LF_JM*!2I2zgfs46g7SJCm%#bfbe3ijpe#f$3CeN!ZFz6@r{ zdu>tR`Z(XX05@t}hDv)waI(vK^h4&sll;}on1j6RbvO13%yK6;y6``hb88>pu;