From e9fe310378721aa4b4fa358aa57ec44f21d086c1 Mon Sep 17 00:00:00 2001 From: Chase-Grajeda Date: Fri, 26 Jan 2024 09:26:53 -0500 Subject: [PATCH 001/359] Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. --- puzzles files/skyscrapers/1646651 | 31 + puzzles files/skyscrapers/easy1.xml | 47 + .../legup/history/AutoCaseRuleCommand.java | 1 - .../legup/model/gameboard/PuzzleElement.java | 20 - .../edu/rpi/legup/model/rules/CaseRule.java | 29 +- .../edu/rpi/legup/model/rules/DirectRule.java | 4 - .../rpi/legup/model/tree/TreeTransition.java | 64 +- .../lightup/rules/SatisfyNumberCaseRule.java | 63 - .../caserule/CaseRule_GenericStatement.java | 256 ++- .../puzzle/skyscrapers/SkyscrapersBoard.java | 11 + .../puzzle/skyscrapers/SkyscrapersClue.java | 5 +- .../rules/CellForNumberCaseRule.java | 53 +- .../rules/NumberForCellCaseRule.java | 35 - .../legup/puzzle/treetent/TreeTentBoard.java | 20 +- .../treetent/rules/FillinRowCaseRule.java | 25 - .../treetent/rules/LinkTentCaseRule.java | 15 +- .../treetent/rules/LinkTreeCaseRule.java | 29 - .../ui/proofeditorui/treeview/TreeView.java | 1640 ++++++++--------- 18 files changed, 994 insertions(+), 1354 deletions(-) create mode 100644 puzzles files/skyscrapers/1646651 create mode 100644 puzzles files/skyscrapers/easy1.xml diff --git a/puzzles files/skyscrapers/1646651 b/puzzles files/skyscrapers/1646651 new file mode 100644 index 000000000..847d8639c --- /dev/null +++ b/puzzles files/skyscrapers/1646651 @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/skyscrapers/easy1.xml b/puzzles files/skyscrapers/easy1.xml new file mode 100644 index 000000000..9d3135bff --- /dev/null +++ b/puzzles files/skyscrapers/easy1.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index a4c157c77..331a3dddf 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -62,7 +62,6 @@ public void executeCommand() { board.setModifiable(false); transition.setBoard(board); transition.setRule(caseRule); - transition.setSelection(elementView.getPuzzleElement().copy()); caseTrans.add(transition); TreeNode childNode = (TreeNode) tree.addTreeElement(transition); diff --git a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java index 3d84287e3..97c9205cf 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java @@ -11,7 +11,6 @@ public abstract class PuzzleElement { protected boolean isModified; protected boolean isGiven; protected boolean isValid; - protected int casesDepended; /** * PuzzleElement Constructor creates a new puzzle element. @@ -23,7 +22,6 @@ public PuzzleElement() { this.isModified = false; this.isGiven = false; this.isValid = true; - this.casesDepended = 0; } /** @@ -150,24 +148,6 @@ public void setValid(boolean isValid) { this.isValid = isValid; } - /** - * Get the number of case rules that depend upon the state of this element - * - * @return number of cases - */ - public int getCasesDepended() { - return this.casesDepended; - } - - /** - * Sets the number of case rules that depend upon the state of this element - * - * @param cases number of cases - */ - public void setCasesDepended(int cases) { - this.casesDepended = cases; - } - /** * Tests whether two puzzle elements objects have the same puzzle element * diff --git a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java index efb96e21a..d9c7e73e5 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -7,8 +7,9 @@ import edu.rpi.legup.model.tree.TreeTransition; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.Set; +import java.util.Map; import static edu.rpi.legup.model.rules.RuleType.CASE; @@ -78,7 +79,6 @@ public String checkRule(TreeTransition transition) { String check = checkRuleRaw(transition); - // Mark transition and new data as valid or not boolean isCorrect = (check == null); for (TreeTransition childTrans : parentNodes.get(0).getChildren()) { childTrans.setCorrect(isCorrect); @@ -125,31 +125,6 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement */ @Override public abstract String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement); - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = new ArrayList<>(); - - List cases = getCases(board,puzzleElement); - for (Board caseBoard : cases) { - Set data = caseBoard.getModifiedData(); - for (PuzzleElement element : data) { - if(!elements.contains(board.getPuzzleElement(element))){ - elements.add(board.getPuzzleElement(element)); - } - } - } - - return elements; - } } diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java index 847764b7b..0465ca88c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -33,10 +33,6 @@ public String checkRule(TreeTransition transition) { transition.getParents().get(0).getChildren().size() != 1) { return "State must have only 1 parent and 1 child"; } - else if (finalBoard.getModifiedData().isEmpty()) { - // null transition - return null; - } else { return checkRuleRaw(transition); } diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java index 72572ac72..e1d042626 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.rules.RuleType; @@ -13,8 +12,6 @@ public class TreeTransition extends TreeElement { private ArrayList parents; private TreeNode childNode; private Rule rule; - - private PuzzleElement selection; private boolean isCorrect; private boolean isVerified; @@ -29,7 +26,6 @@ public TreeTransition(Board board) { this.childNode = null; this.board = board; this.rule = null; - this.selection = null; this.isCorrect = false; this.isVerified = false; } @@ -91,42 +87,13 @@ public void propagateChange(PuzzleElement element) { } } else { - // Overwrite previous modifications to this element - board.removeModifiedData(board.getPuzzleElement(element)); - - // apply changes to tranistion - board.notifyChange(element); - - // mark first transition as modified - if (!board.getPuzzleElement(element).equalsData(parents.get(0).getBoard().getPuzzleElement(element))) { - board.addModifiedData(element); - } - - // propagate to children if (childNode != null) { - - // find starting board - TreeNode head = childNode; - while (head.getParent() != null) { - head = head.getParent().getParents().get(0); - } - Board headBoard = head.getBoard(); - - PuzzleElement copy = element.copy(); - // Set as modifiable if reverted to starting value (and started modifiable) - if (headBoard.getPuzzleElement(element).equalsData(element)) { - copy.setModifiable(headBoard.getPuzzleElement(element).isModifiable()); - } - else{ - copy.setModifiable(false); - } - - // apply changes to result node - childNode.getBoard().notifyChange(copy); - - // apply to all child transitions + board.notifyChange(element); + childNode.getBoard().notifyChange(element.copy()); for (TreeTransition child : childNode.getChildren()) { - child.propagateChange(copy.copy()); + PuzzleElement copy = element.copy(); + copy.setModifiable(false); + child.propagateChange(copy); } } } @@ -360,27 +327,6 @@ public void setRule(Rule rule) { isVerified = false; } - /** - * Gets he selected element associated with this transition - * - * @return If this is a case rule, the selected element for that rule, null otherwise - */ - public PuzzleElement getSelection() { - if (this.rule instanceof CaseRule) { - return selection; - } - return null; - } - - /** - * Sets the selected element associated with this transition - * - * @param selection selected element for this transition - */ - public void setSelection(PuzzleElement selection) { - this.selection = selection; - } - /** * Gets whether this transition is correctly justified * diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java index cf7b70ccd..1f166685b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java @@ -287,67 +287,4 @@ private List getAdjacentCells(LightUpBoard board, LightUpCell cell) } return cells; } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = new ArrayList<>(); - - LightUpBoard puzzleBoard = (LightUpBoard) board; - LightUpCell point = (LightUpCell)puzzleBoard.getPuzzleElement(puzzleElement); - - List cells = getAdjacentCells(puzzleBoard,point); - - for (LightUpCell cell : cells) { - //add cells that can light adjacents from any direction - Point location = cell.getLocation(); - for (int i = location.x; i < puzzleBoard.getWidth(); i++) { - System.out.println(i); - LightUpCell c = puzzleBoard.getCell(i, location.y); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { - elements.add(board.getPuzzleElement(c)); - } - } - for (int i = location.x; i >= 0; i--) { - LightUpCell c = puzzleBoard.getCell(i, location.y); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { - elements.add(board.getPuzzleElement(c)); - } - } - for (int i = location.y; i < puzzleBoard.getHeight(); i++) { - LightUpCell c = puzzleBoard.getCell(location.x, i); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { - elements.add(board.getPuzzleElement(c)); - } - } - for (int i = location.y; i >= 0; i--) { - LightUpCell c = puzzleBoard.getCell(location.x, i); - if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { - break; - } - else if (!elements.contains(board.getPuzzleElement(c))) { - elements.add(board.getPuzzleElement(c)); - } - } - } - - return elements; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java index 0e25586a8..2a40bf45d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java @@ -1,137 +1,119 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; - -import java.util.List; -import java.util.ArrayList; - -public abstract class CaseRule_GenericStatement extends CaseRule_Generic { - - public CaseRule_GenericStatement(String ruleID, char operation, String title, - ShortTruthTableCellType[][] trueCases, - ShortTruthTableCellType[][] falseCases) { - super(ruleID, ShortTruthTableOperation.getRuleName(operation), - title + " case", - "A known " + title.toUpperCase() + " statement can have multiple forms"); - - this.operation = operation; - - this.trueCases = trueCases; - this.falseCases = falseCases; - } - - private final char operation; - - private final ShortTruthTableCellType[][] trueCases; - private final ShortTruthTableCellType[][] falseCases; - - protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; - protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; - protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; - - // Adds all elements that can be selected for this caserule - @Override - public CaseBoard getCaseBoard(Board board) { - //copy the board and add all elements that can be selected - ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); - sttBoard.setModifiable(false); - CaseBoard caseBoard = new CaseBoard(sttBoard, this); - - //add all elements that can be selected for the case rule statement - for (PuzzleElement element : sttBoard.getPuzzleElements()) { - //get the cell object - ShortTruthTableCell cell = sttBoard.getCellFromElement(element); - //the cell must match the symbol - if (cell.getSymbol() != this.operation) continue; - //the statement must be assigned with unassigned sub-statements - if (!cell.getType().isTrueOrFalse()) continue; - if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; - if (this.operation != ShortTruthTableOperation.NOT && - cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { - continue; - } - //if the element has passed all the checks, it can be selected - caseBoard.addPickableElement(element); - } - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ - @SuppressWarnings("unchecked") - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); - ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); - - // If the statement is set to true, collect true cases. Otherwise, collect the false cases - if (cell.getType() == ShortTruthTableCellType.TRUE) { - return getCasesFromCell(sttBoard, puzzleElement, trueCases); - } - return getCasesFromCell(sttBoard, puzzleElement, falseCases); - } - - /** - * Collects a list of boards for each possible outcome of case-rule application - * @param board current board state - * @param puzzleElement case rule operator - * @param possibilities list of possibilities for operator state - * @return ArrayList of Boards - */ - private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { - // Create branch case for each possibility - ArrayList cases = new ArrayList<>(); - for (int i = 0; i < possibilities.length; i++) { - // Create a new board to modify and get statement of selected square - ShortTruthTableBoard b = board.copy(); - ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); - ShortTruthTableStatement statement = cell.getStatementReference(); - - // Modify neighboring cells of case-rule application by the provided logical cases - if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { - ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); - leftCell.setData(possibilities[i][0]); - b.addModifiedData(leftCell); - } - if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { - ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); - rightCell.setData(possibilities[i][1]); - b.addModifiedData(rightCell); - } - - cases.add(b); - } - return cases; - } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = super.dependentElements(board,puzzleElement); - - elements.add(board.getPuzzleElement(puzzleElement)); - - return elements; - } -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + + +import java.util.ArrayList; + +public abstract class CaseRule_GenericStatement extends CaseRule_Generic { + + public CaseRule_GenericStatement(String ruleID, char operation, String title, + ShortTruthTableCellType[][] trueCases, + ShortTruthTableCellType[][] falseCases) { + super(ruleID, ShortTruthTableOperation.getRuleName(operation), + title + " case", + "A known " + title.toUpperCase() + " statement can have multiple forms"); + + this.operation = operation; + + this.trueCases = trueCases; + this.falseCases = falseCases; + } + + private final char operation; + + private final ShortTruthTableCellType[][] trueCases; + private final ShortTruthTableCellType[][] falseCases; + + protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; + protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; + protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; + + // Adds all elements that can be selected for this caserule + @Override + public CaseBoard getCaseBoard(Board board) { + //copy the board and add all elements that can be selected + ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); + sttBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(sttBoard, this); + + //add all elements that can be selected for the case rule statement + for (PuzzleElement element : sttBoard.getPuzzleElements()) { + //get the cell object + ShortTruthTableCell cell = sttBoard.getCellFromElement(element); + //the cell must match the symbol + if (cell.getSymbol() != this.operation) continue; + //the statement must be assigned with unassigned sub-statements + if (!cell.getType().isTrueOrFalse()) continue; + if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; + if (this.operation != ShortTruthTableOperation.NOT && + cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { + continue; + } + //if the element has passed all the checks, it can be selected + caseBoard.addPickableElement(element); + } + return caseBoard; + } + + /** + * Gets the possible cases at a specific location based on this case rule + * + * @param board the current board state + * @param puzzleElement equivalent puzzleElement + * @return a list of elements the specified could be + */ + @SuppressWarnings("unchecked") + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); + ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); + + // If the statement is set to true, collect true cases. Otherwise, collect the false cases + if (cell.getType() == ShortTruthTableCellType.TRUE) { + return getCasesFromCell(sttBoard, puzzleElement, trueCases); + } + return getCasesFromCell(sttBoard, puzzleElement, falseCases); + } + + /** + * Collects a list of boards for each possible outcome of case-rule application + * @param board current board state + * @param puzzleElement case rule operator + * @param possibilities list of possibilities for operator state + * @return ArrayList of Boards + */ + private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { + // Create branch case for each possibility + ArrayList cases = new ArrayList<>(); + for (int i = 0; i < possibilities.length; i++) { + // Create a new board to modify and get statement of selected square + ShortTruthTableBoard b = board.copy(); + ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); + ShortTruthTableStatement statement = cell.getStatementReference(); + + // Modify neighboring cells of case-rule application by the provided logical cases + if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); + leftCell.setData(possibilities[i][0]); + b.addModifiedData(leftCell); + } + if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); + rightCell.setData(possibilities[i][1]); + b.addModifiedData(rightCell); + } + + cases.add(b); + } + return cases; + } +} 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..ca6bcbe02 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -25,6 +25,9 @@ public class SkyscrapersBoard extends GridBoard { private boolean viewFlag = false; private boolean dupeFlag = false; + private SkyscrapersClue modClue = null; + //helper variable for case rule verification, tracks recently modified row/col + public SkyscrapersBoard(int size) { super(size, size); @@ -91,6 +94,14 @@ public void setViewFlag(boolean newFlag) { viewFlag = newFlag; } + public SkyscrapersClue getmodClue() { + return modClue; + } + + public void setModClue(SkyscrapersClue newClue) { + modClue = newClue; + } + @Override public SkyscrapersCell getCell(int x, int y) { return (SkyscrapersCell) super.getCell(x, y); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java index 1e7b1b45e..dc68f45c7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java @@ -47,9 +47,6 @@ public void setType(SkyscrapersType type) { } public SkyscrapersClue copy() { - SkyscrapersClue copy = new SkyscrapersClue(data, clueIndex, type); - copy.setIndex(index); - copy.setModifiable(isModifiable); - return copy; + return null; } } 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..683b742bf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java @@ -69,6 +69,7 @@ public ArrayList getCasesFor(Board board, PuzzleElement puzzleElement, In PuzzleElement newCell = newCase.getPuzzleElement(cell); newCell.setData(number); newCase.addModifiedData(newCell); + newCase.setModClue((SkyscrapersClue) newCase.getPuzzleElement(clue)); //if flags boolean passed = true; @@ -102,7 +103,12 @@ 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()) { + //find changed row/col + SkyscrapersClue modClue = ((SkyscrapersBoard) childTransitions.get(0).getBoard()).getmodClue(); + + //System.out.println(modClue.getType()); + //System.out.println(modClue.getClueIndex()); + if (childTransitions.size() != getCasesFor(oldBoard, modClue, (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) { //System.out.println("Wrong number of cases."); return "Wrong number of cases."; } @@ -126,49 +132,4 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = new ArrayList<>(); - - SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board; - SkyscrapersClue clue = (SkyscrapersClue)puzzleBoard.getPuzzleElement(puzzleElement); - - // check each point in modified row/col - List data = puzzleBoard.getRowCol(clue.getClueIndex(),SkyscrapersType.ANY,clue.getType() == SkyscrapersType.CLUE_WEST); - for (SkyscrapersCell point : data) { - List cells = new ArrayList<>(List.of(point)); - - // if dependent on row/col - if ((puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) && point.getType() == SkyscrapersType.UNKNOWN) { - // get perpendicular row/col intersecting this point - int index; - if (clue.getType() == SkyscrapersType.CLUE_WEST) { - index = point.getLocation().x; - } - else { - index = point.getLocation().y; - } - cells.addAll(puzzleBoard.getRowCol(index,SkyscrapersType.ANY,clue.getType() != SkyscrapersType.CLUE_WEST)); - } - - // add all to result - for (SkyscrapersCell cell : cells) { - if (!elements.contains(board.getPuzzleElement(cell))) { - elements.add(board.getPuzzleElement(cell)); - } - } - } - - return elements; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java index 3bf0de70a..a061c62a3 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,7 +7,6 @@ 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.*; @@ -135,38 +134,4 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = new ArrayList<>(); - - SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board; - SkyscrapersCell point = (SkyscrapersCell)puzzleBoard.getPuzzleElement(puzzleElement); - - List cells = new ArrayList<>(List.of(point)); - - // if dependent on row/col - if (puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) { - // add all cells in row/col intersecting given point - cells.addAll(puzzleBoard.getRowCol(point.getLocation().x,SkyscrapersType.ANY,false)); - cells.addAll(puzzleBoard.getRowCol(point.getLocation().y,SkyscrapersType.ANY,true)); - } - - for (SkyscrapersCell cell : cells) { - if (!elements.contains(board.getPuzzleElement(cell))) { - elements.add(board.getPuzzleElement(cell)); - } - } - - return elements; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java index dc809f34d..2542ea335 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -124,11 +124,21 @@ public void notifyDeletion(PuzzleElement puzzleElement) { public List getAdjacent(TreeTentCell cell, TreeTentType type) { List adj = new ArrayList<>(); Point loc = cell.getLocation(); - for (int i = -2; i < 2; i++) { - TreeTentCell adjCell = getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); - if (adjCell != null && adjCell.getType() == type) { - adj.add(adjCell); - } + TreeTentCell up = getCell(loc.x, loc.y - 1); + TreeTentCell right = getCell(loc.x + 1, loc.y); + TreeTentCell down = getCell(loc.x, loc.y + 1); + TreeTentCell left = getCell(loc.x - 1, loc.y); + if (up != null && up.getType() == type) { + adj.add(up); + } + if (right != null && right.getType() == type) { + adj.add(right); + } + if (down != null && down.getType() == type) { + adj.add(down); + } + if (left != null && left.getType() == type) { + adj.add(left); } return adj; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index 698b3aa5e..538772b74 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -160,29 +160,4 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return null; } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = new ArrayList<>(); - - TreeTentBoard treeTentBoard = (TreeTentBoard) board; - TreeTentClue clue = (TreeTentClue) puzzleElement; - - // add all elements of filled row - for (int i = 0; i < treeTentBoard.getWidth(); i++) { - TreeTentCell cell = treeTentBoard.getCell(i, clue.getClueIndex()-1); - elements.add(board.getPuzzleElement((cell))); - } - - return elements; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java index 39b1d0251..36f466f87 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java @@ -5,6 +5,7 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.TreeTentCell; @@ -150,18 +151,4 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - return List.of(board.getPuzzleElement(puzzleElement)); - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java index 72ffd62eb..249547301 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java @@ -10,7 +10,6 @@ import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.TreeTentType; -import java.awt.Point; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -153,32 +152,4 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } - - /** - * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid - * Overridden by case rules dependent on more than just the modified data - * - * @param board board state at application - * @param puzzleElement selected puzzleElement - * @return List of puzzle elements (typically cells) this application of the case rule depends upon. - * Defaults to any element modified by any case - */ - @Override - public List dependentElements(Board board, PuzzleElement puzzleElement) { - List elements = new ArrayList<>(List.of(board.getPuzzleElement(puzzleElement))); - - TreeTentBoard treeTentBoard = (TreeTentBoard) board; - TreeTentCell point = (TreeTentCell) puzzleElement; - - // get all adjacent cells - Point loc = point.getLocation(); - for (int i = -2; i < 2; i++) { - TreeTentCell cell = treeTentBoard.getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); - if (cell != null) { - elements.add(board.getPuzzleElement(cell)); - } - } - - return elements; - } } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java index 9bfffe60a..ecf59146d 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java @@ -1,886 +1,756 @@ -package edu.rpi.legup.ui.proofeditorui.treeview; - -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.controller.TreeController; -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.GridCell; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.observer.ITreeListener; -import edu.rpi.legup.model.rules.CaseRule; -import edu.rpi.legup.model.rules.Rule; -import edu.rpi.legup.model.tree.Tree; -import edu.rpi.legup.model.tree.TreeElement; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.ui.ScrollView; -import edu.rpi.legup.utility.DisjointSets; - -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.*; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import static edu.rpi.legup.model.tree.TreeElementType.NODE; -import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; - -public class TreeView extends ScrollView implements ITreeListener { - private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); - - private static final int TRANS_GAP = 5; - - private static final int NODE_GAP_WIDTH = 70; - private static final int NODE_GAP_HEIGHT = 15; - - private static final int BORDER_GAP_HEIGHT = 20; - private static final int BORDER_GAP_WIDTH = 20; - - private static final int BORDER_SPACING = 100; - - private TreeNodeView nodeHover; - - private ArrayList currentStateBoxes; - private Rectangle bounds = new Rectangle(0, 0, 0, 0); - - private Tree tree; - private TreeNodeView rootNodeView; - private Map viewMap; - private Dimension dimension; - - private TreeViewSelection selection; - - public TreeView(TreeController treeController) { - super(treeController); - currentStateBoxes = new ArrayList<>(); - setSize(dimension = new Dimension(100, 200)); - setPreferredSize(new Dimension(640, 160)); - - viewMap = new HashMap<>(); - - selection = new TreeViewSelection(); - } - - public TreeViewSelection getSelection() { - return selection; - } - - /** - * Gets the tree node puzzleElement that the mouse is hovering over - * - * @return tree node puzzleElement that the mouse is hovering over - */ - public TreeNodeView getNodeHover() { - return nodeHover; - } - - /** - * Sets the tree node puzzleElement that the mouse is hovering over - * - * @param nodeHover tree node puzzleElement the mouse is hovering over - */ - public void setNodeHover(TreeNodeView nodeHover) { - this.nodeHover = nodeHover; - } - - /** - * Gets the TreeElementView by the specified point or null if no view exists at the specified point - * - * @param point location to query for a view - * @return TreeElementView at the point specified, otherwise null - */ - public TreeElementView getTreeElementView(Point point) { - return getTreeElementView(point, rootNodeView); - } - - /** - * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or - * the view specified is null - * - * @param point location to query for a view - * @param elementView view to determine if the point is contained within it - * @return TreeElementView at the point specified, otherwise null - */ - private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { - if (elementView == null) { - return null; - } - else { - if (elementView.contains(point) && elementView.isVisible()) { - if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { - return null; - } - return elementView; - } - else { - if (elementView.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) elementView; - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - TreeElementView view = getTreeElementView(point, transitionView); - if (view != null) { - return view; - } - } - } - else { - TreeTransitionView transitionView = (TreeTransitionView) elementView; - return getTreeElementView(point, transitionView.getChildView()); - } - } - } - return null; - } - - public void updateTreeView(Tree tree) { - this.tree = tree; - if (selection.getSelectedViews().size() == 0) { - selection.newSelection(new TreeNodeView(tree.getRootNode())); - } - repaint(); - } - - /** - * Sets the tree associated with this TreeView - * - * @param tree tree - */ - public void setTree(Tree tree) { - this.tree = tree; - } - - public void updateTreeSize() { - if (GameBoardFacade.getInstance().getTree() == null) { - return; - } - setSize(bounds.getSize()); - } - - public void reset() { - if (bounds.x != 0 || bounds.y != 0) { - updateTreeSize(); - } - } - - public void zoomFit() { - double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); - double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); - zoomTo(Math.min(fitWidth, fitHeight)); - viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); - } - - /** - * Creates a customized viewport for the scroll pane - * - * @return viewport for the scroll pane - */ - @Override - protected JViewport createViewport() { - return new JViewport() { - @Override - protected LayoutManager createLayoutManager() { - return new ViewportLayout() { - @Override - public void layoutContainer(Container parent) { - Point point = viewport.getViewPosition(); - // determine the maximum x and y view positions - int mx = getCanvas().getWidth() - viewport.getWidth(); - int my = getCanvas().getHeight() - viewport.getHeight(); - // obey edge boundaries - if (point.x < 0) { - point.x = 0; - } - if (point.x > mx) { - point.x = mx; - } - if (point.y < 0) { - point.y = 0; - } - if (point.y > my) { - point.y = my; - } - // center margins - if (mx < 0) { - point.x = 0; - } - if (my < 0) { - point.y = my / 2; - } - viewport.setViewPosition(point); - } - }; - } - }; - } - - public void draw(Graphics2D graphics2D) { - currentStateBoxes.clear(); - Tree tree = GameBoardFacade.getInstance().getTree(); - if (tree != null) { - //setSize(bounds.getDimension()); - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - drawTree(graphics2D); - - dimension.width += BORDER_SPACING; - setSize(dimension); -// graphics2D.drawRect(0,0, dimension.width, dimension.height); - - if (selection.getHover() != null) { - drawMouseOver(graphics2D); - } - } - } - - public void zoomReset() { - zoomTo(1.0); - viewport.setViewPosition(new Point(0, 0)); - } - - private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { - if (nodeView != null) { - nodeView.draw(graphics2D); - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - transitionView.draw(graphics2D); - redrawTree(graphics2D, transitionView.getChildView()); - } - } - } - - public void removeTreeElement(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - nodeView.getParentView().setChildView(null); - } - else { - TreeTransitionView transitionView = (TreeTransitionView) view; - transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); - } - } - - /** - * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image - * - * @param g the graphics to use to draw - */ - public void drawMouseOver(Graphics2D g) { - if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { - TreeTransition transition = (TreeTransition) selection.getHover().treeElement; - int imgWidth = 100; - int imgHeight = 100; - - BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); - image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); - Point mousePoint = selection.getMousePoint(); - g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); - } - } - - public void resetView() { - this.tree = null; - this.rootNodeView = null; - this.selection.clearSelection(); - this.selection.clearHover(); - } - - /** - * Called when a tree puzzleElement is added to the tree - * - * @param treeElement TreeElement that was added to the tree - */ - @Override - public void onTreeElementAdded(TreeElement treeElement) { - if (treeElement.getType() == NODE) { - addTreeNode((TreeNode) treeElement); - } - else { - addTreeTransition((TreeTransition) treeElement); - } - repaint(); - } - - /** - * Called when a tree puzzleElement is removed from the tree - * - * @param element TreeElement that was removed to the tree - */ - @Override - public void onTreeElementRemoved(TreeElement element) { - if (element.getType() == NODE) { - TreeNode node = (TreeNode) element; - TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); - - nodeView.getParentView().setChildView(null); - removeTreeNode(node); - } - else { - TreeTransition trans = (TreeTransition) element; - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - - // unlock ancestor elements if case rule deleted - Rule rule = trans.getRule(); - for (TreeNode node : trans.getParents()) { - - // only if the last case of a case rule will be deleted - if (!(rule instanceof CaseRule && node.getChildren().isEmpty())) { - continue; - } - - CaseRule caseRule = (CaseRule)rule; - // set dependent elements to be modifiable by ancestors (if not dependent on others) - List ancestors = node.getAncestors(); - for (TreeNode ancestor : ancestors) { - // for all ancestors but root - if (ancestor.getParent() == null) { - continue; - } - - for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), trans.getSelection())) { - // decrement, unlock if 0 cases depended - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); - oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); - if (oldElement.getCasesDepended() != 0) { - continue; - } - - // set modifiable if started modifiable - boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); - - // unmodifiable if already modified - TreeNode modNode = ancestor.getParent().getParents().get(0); - while (modNode.getParent()!=null) { - Board modBoard = modNode.getParent().getBoard(); - if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { - modifiable = false; - break; - } - modNode = modNode.getParent().getParents().get(0); - } - oldElement.setModifiable(modifiable); - } - } - } - - transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); - removeTreeTransition(trans); - } - repaint(); - } - - /** - * Called when the tree selection was changed - * - * @param selection tree selection that was changed - */ - @Override - public void onTreeSelectionChanged(TreeViewSelection selection) { - this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); - selection.getSelectedViews().forEach(v -> v.setSelected(true)); - this.selection = selection; - repaint(); - } - - /** - * Called when the model has finished updating the tree. - */ - @Override - public void onUpdateTree() { - repaint(); - } - - /** - * Gets the TreeElementView by the corresponding TreeElement associated with it - * - * @param element TreeElement of the view - * @return TreeElementView of the TreeElement associated with it - */ - public TreeElementView getElementView(TreeElement element) { - return viewMap.get(element); - } - - private void removeTreeNode(TreeNode node) { - viewMap.remove(node); - List children = node.getChildren(); - - // if child is a case rule, unlock ancestor elements - if (!children.isEmpty()) { - Rule rule = children.get(0).getRule(); - if (rule instanceof CaseRule) { - CaseRule caseRule = (CaseRule)rule; - // set dependent elements to be modifiable by ancestors (if not dependent on others) - List ancestors = node.getAncestors(); - for (TreeNode ancestor : ancestors) { - // for all ancestors but root - if (ancestor.getParent() == null) { - continue; - } - for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), children.get(0).getSelection())) { - // decrement, unlock if 0 cases depended - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); - oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); - if (oldElement.getCasesDepended() == 0) { - continue; - } - - // set modifiable if started modifiable - boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); - - // unmodifiable if already modified - TreeNode modNode = ancestor.getParent().getParents().get(0); - while (modNode.getParent() != null) { - Board modBoard = modNode.getParent().getBoard(); - if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { - modifiable = false; - break; - } - modNode = modNode.getParent().getParents().get(0); - } - oldElement.setModifiable(modifiable); - } - } - } - } - node.getChildren().forEach(t -> removeTreeTransition(t)); - } - - private void removeTreeTransition(TreeTransition trans) { - viewMap.remove(trans); - if (trans.getChildNode() != null) { - removeTreeNode(trans.getChildNode()); - } - } - - private void addTreeNode(TreeNode node) { - TreeTransition parent = node.getParent(); - - TreeNodeView nodeView = new TreeNodeView(node); - TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); - - nodeView.setParentView(parentView); - parentView.setChildView(nodeView); - - viewMap.put(node, nodeView); - - if (!node.getChildren().isEmpty()) { - - // if adding a case rule, lock dependent ancestor elements - Rule rule = node.getChildren().get(0).getRule(); - if (rule instanceof CaseRule) { - CaseRule caseRule = (CaseRule)rule; - - List ancestors = node.getAncestors(); - for (TreeNode ancestor : ancestors) { - // for all ancestors but root - if (ancestor.getParent() == null) { - continue; - } - for (PuzzleElement element : caseRule.dependentElements(node.getBoard(), node.getChildren().get(0).getSelection())) { - // increment and lock - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); - oldElement.setCasesDepended(oldElement.getCasesDepended()+1); - oldElement.setModifiable(false); - } - } - } - - node.getChildren().forEach(t -> addTreeTransition(t)); - } - } - - private void addTreeTransition(TreeTransition trans) { - List parents = trans.getParents(); - - TreeTransitionView transView = new TreeTransitionView(trans); - for (TreeNode parent : parents) { - TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); - transView.addParentView(parentNodeView); - parentNodeView.addChildrenView(transView); - - // if transition is a new case rule, lock dependent ancestor elements - Rule rule = trans.getRule(); - if (rule instanceof CaseRule && parent.getChildren().size()==1) { - CaseRule caseRule = (CaseRule)rule; - - List ancestors = parent.getAncestors(); - for (TreeNode ancestor : ancestors) { - // for all ancestors but root - if (ancestor.getParent() == null) { - continue; - } - for (PuzzleElement element : caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { - // increment and lock - PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); - oldElement.setCasesDepended(oldElement.getCasesDepended()+1); - oldElement.setModifiable(false); - } - } - } - } - - viewMap.put(trans, transView); - - if (trans.getChildNode() != null) { - addTreeNode(trans.getChildNode()); - } - } - - ///New Draw Methods - - public void drawTree(Graphics2D graphics2D) { - if (tree == null) { - LOGGER.error("Unable to draw tree."); - } - else { - if (rootNodeView == null) { - rootNodeView = new TreeNodeView(tree.getRootNode()); - - LOGGER.debug("Creating new views for tree view."); - createViews(rootNodeView); - - selection.newSelection(rootNodeView); - } - - dimension = new Dimension(0, 0); - calcSpan(rootNodeView); - rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); - - calculateViewLocations(rootNodeView, 0); - dimension.height = (int) rootNodeView.getSpan(); - - redrawTree(graphics2D, rootNodeView); - LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); - } - } - - public void createViews(TreeNodeView nodeView) { - if (nodeView != null) { - viewMap.put(nodeView.getTreeElement(), nodeView); - - TreeNode node = nodeView.getTreeElement(); - for (TreeTransition trans : node.getChildren()) { - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - if (transView != null) { - nodeView.addChildrenView(transView); - transView.addParentView(nodeView); - break; - } - transView = new TreeTransitionView(trans); - - viewMap.put(transView.getTreeElement(), transView); - - transView.addParentView(nodeView); - nodeView.addChildrenView(transView); - - TreeNode childNode = trans.getChildNode(); - if (childNode != null) { - TreeNodeView childNodeView = new TreeNodeView(childNode); - viewMap.put(childNodeView.getTreeElement(), childNodeView); - - childNodeView.setParentView(transView); - transView.setChildView(childNodeView); - - createViews(childNodeView); - } - } - } - } - - public void calculateViewLocations(TreeNodeView nodeView, int depth) { - nodeView.setDepth(depth); - int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; - nodeView.setX(xLoc); - dimension.width = Math.max(dimension.width, xLoc); - - TreeTransitionView parentTransView = nodeView.getParentView(); - int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); - nodeView.setY(yLoc); - - ArrayList children = nodeView.getChildrenViews(); - switch (children.size()) { - case 0: - break; - case 1: { - TreeTransitionView childView = children.get(0); - - List parentsViews = childView.getParentViews(); - if (parentsViews.size() == 1) { - childView.setEndY(yLoc); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - else { - if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { - int yAvg = 0; - for (int i = 0; i < parentsViews.size(); i++) { - TreeNodeView parentNodeView = parentsViews.get(i); - depth = Math.max(depth, parentNodeView.getDepth()); - yAvg += parentNodeView.getY(); - - Point lineStartPoint = childView.getLineStartPoint(i); - lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = parentNodeView.getY(); - } - yAvg /= parentsViews.size(); - childView.setEndY(yAvg); - - childView.setDepth(depth); - - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - } - break; - } - default: { - int span = 0; - for (TreeTransitionView childView : children) { - span += childView.getSpan(); - } - - span = (int) ((nodeView.getSpan() - span) / 2); - for (int i = 0; i < children.size(); i++) { - TreeTransitionView childView = children.get(i); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); - - span += childView.getSpan(); - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - break; - } - } - } - - public void calcSpan(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - calcSpan(childView); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - ArrayList newChildrenViews = new ArrayList<>(); - for (TreeTransition trans : node.getChildren()) { - newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); - } - nodeView.setChildrenViews(newChildrenViews); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - calcSpan(mergePointView); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - calcSpan(transView); - span += transView.getSpan(); - } - } - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view - * specified as stop. Stop tree puzzleElement is NOT included in the span calculation - * - * @param view - * @param stop - */ - private void subCalcSpan(TreeElementView view, TreeElementView stop) { - //safe-guard for infinite loop - if (view == stop) { - return; - } - - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - if (childView == stop) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - subCalcSpan(childView, stop); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - subCalcSpan(mergePointView, stop); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - subCalcSpan(transView, stop); - span += transView.getSpan(); - } - } - - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null || nodeView == stop) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in - * relative order in the list of child transitions of the specified node - * - * @param node root node of the branches - * @param branches DisjointSets of the child branches of the specified node which determine which branches merge - */ - private void reorderBranches(TreeNode node, DisjointSets branches) { - List children = node.getChildren(); - List> mergingSets = branches.getAllSets(); - - List> newOrder = new ArrayList<>(); - for (Set set : mergingSets) { - List mergeBranch = new ArrayList<>(); - newOrder.add(mergeBranch); - children.forEach(t -> { - if (set.contains(t)) { - mergeBranch.add(t); - } - }); - mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> - children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); - } - - newOrder.sort((List b1, List b2) -> { - int low1 = -1; - int low2 = -1; - for (TreeTransition t1 : b1) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - for (TreeTransition t1 : b2) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - return low1 < low2 ? -1 : 1; - }); - - List newChildren = new ArrayList<>(); - newOrder.forEach(l -> newChildren.addAll(l)); - node.setChildren(newChildren); - } +package edu.rpi.legup.ui.proofeditorui.treeview; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.controller.TreeController; +import edu.rpi.legup.model.observer.ITreeListener; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.ui.ScrollView; +import edu.rpi.legup.utility.DisjointSets; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.*; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import static edu.rpi.legup.model.tree.TreeElementType.NODE; +import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; + +public class TreeView extends ScrollView implements ITreeListener { + private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); + + private static final int TRANS_GAP = 5; + + private static final int NODE_GAP_WIDTH = 70; + private static final int NODE_GAP_HEIGHT = 15; + + private static final int BORDER_GAP_HEIGHT = 20; + private static final int BORDER_GAP_WIDTH = 20; + + private static final int BORDER_SPACING = 100; + + private TreeNodeView nodeHover; + + private ArrayList currentStateBoxes; + private Rectangle bounds = new Rectangle(0, 0, 0, 0); + + private Tree tree; + private TreeNodeView rootNodeView; + private Map viewMap; + private Dimension dimension; + + private TreeViewSelection selection; + + public TreeView(TreeController treeController) { + super(treeController); + currentStateBoxes = new ArrayList<>(); + setSize(dimension = new Dimension(100, 200)); + setPreferredSize(new Dimension(640, 160)); + + viewMap = new HashMap<>(); + + selection = new TreeViewSelection(); + } + + public TreeViewSelection getSelection() { + return selection; + } + + /** + * Gets the tree node puzzleElement that the mouse is hovering over + * + * @return tree node puzzleElement that the mouse is hovering over + */ + public TreeNodeView getNodeHover() { + return nodeHover; + } + + /** + * Sets the tree node puzzleElement that the mouse is hovering over + * + * @param nodeHover tree node puzzleElement the mouse is hovering over + */ + public void setNodeHover(TreeNodeView nodeHover) { + this.nodeHover = nodeHover; + } + + /** + * Gets the TreeElementView by the specified point or null if no view exists at the specified point + * + * @param point location to query for a view + * @return TreeElementView at the point specified, otherwise null + */ + public TreeElementView getTreeElementView(Point point) { + return getTreeElementView(point, rootNodeView); + } + + /** + * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or + * the view specified is null + * + * @param point location to query for a view + * @param elementView view to determine if the point is contained within it + * @return TreeElementView at the point specified, otherwise null + */ + private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { + if (elementView == null) { + return null; + } + else { + if (elementView.contains(point) && elementView.isVisible()) { + if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { + return null; + } + return elementView; + } + else { + if (elementView.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) elementView; + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + TreeElementView view = getTreeElementView(point, transitionView); + if (view != null) { + return view; + } + } + } + else { + TreeTransitionView transitionView = (TreeTransitionView) elementView; + return getTreeElementView(point, transitionView.getChildView()); + } + } + } + return null; + } + + public void updateTreeView(Tree tree) { + this.tree = tree; + if (selection.getSelectedViews().size() == 0) { + selection.newSelection(new TreeNodeView(tree.getRootNode())); + } + repaint(); + } + + /** + * Sets the tree associated with this TreeView + * + * @param tree tree + */ + public void setTree(Tree tree) { + this.tree = tree; + } + + public void updateTreeSize() { + if (GameBoardFacade.getInstance().getTree() == null) { + return; + } + setSize(bounds.getSize()); + } + + public void reset() { + if (bounds.x != 0 || bounds.y != 0) { + updateTreeSize(); + } + } + + public void zoomFit() { + double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); + double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); + zoomTo(Math.min(fitWidth, fitHeight)); + viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); + } + + /** + * Creates a customized viewport for the scroll pane + * + * @return viewport for the scroll pane + */ + @Override + protected JViewport createViewport() { + return new JViewport() { + @Override + protected LayoutManager createLayoutManager() { + return new ViewportLayout() { + @Override + public void layoutContainer(Container parent) { + Point point = viewport.getViewPosition(); + // determine the maximum x and y view positions + int mx = getCanvas().getWidth() - viewport.getWidth(); + int my = getCanvas().getHeight() - viewport.getHeight(); + // obey edge boundaries + if (point.x < 0) { + point.x = 0; + } + if (point.x > mx) { + point.x = mx; + } + if (point.y < 0) { + point.y = 0; + } + if (point.y > my) { + point.y = my; + } + // center margins + if (mx < 0) { + point.x = 0; + } + if (my < 0) { + point.y = my / 2; + } + viewport.setViewPosition(point); + } + }; + } + }; + } + + public void draw(Graphics2D graphics2D) { + currentStateBoxes.clear(); + Tree tree = GameBoardFacade.getInstance().getTree(); + if (tree != null) { + //setSize(bounds.getDimension()); + graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + drawTree(graphics2D); + + dimension.width += BORDER_SPACING; + setSize(dimension); +// graphics2D.drawRect(0,0, dimension.width, dimension.height); + + if (selection.getHover() != null) { + drawMouseOver(graphics2D); + } + } + } + + public void zoomReset() { + zoomTo(1.0); + viewport.setViewPosition(new Point(0, 0)); + } + + private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { + if (nodeView != null) { + nodeView.draw(graphics2D); + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + transitionView.draw(graphics2D); + redrawTree(graphics2D, transitionView.getChildView()); + } + } + } + + public void removeTreeElement(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + nodeView.getParentView().setChildView(null); + } + else { + TreeTransitionView transitionView = (TreeTransitionView) view; + transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); + } + } + + /** + * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image + * + * @param g the graphics to use to draw + */ + public void drawMouseOver(Graphics2D g) { + if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { + TreeTransition transition = (TreeTransition) selection.getHover().treeElement; + int imgWidth = 100; + int imgHeight = 100; + + BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); + image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); + Point mousePoint = selection.getMousePoint(); + g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); + } + } + + public void resetView() { + this.tree = null; + this.rootNodeView = null; + this.selection.clearSelection(); + this.selection.clearHover(); + } + + /** + * Called when a tree puzzleElement is added to the tree + * + * @param treeElement TreeElement that was added to the tree + */ + @Override + public void onTreeElementAdded(TreeElement treeElement) { + if (treeElement.getType() == NODE) { + addTreeNode((TreeNode) treeElement); + } + else { + addTreeTransition((TreeTransition) treeElement); + } + repaint(); + } + + /** + * Called when a tree puzzleElement is removed from the tree + * + * @param element TreeElement that was removed to the tree + */ + @Override + public void onTreeElementRemoved(TreeElement element) { + if (element.getType() == NODE) { + TreeNode node = (TreeNode) element; + TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); + + nodeView.getParentView().setChildView(null); + removeTreeNode(node); + } + else { + TreeTransition trans = (TreeTransition) element; + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + + transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); + removeTreeTransition(trans); + } + repaint(); + } + + /** + * Called when the tree selection was changed + * + * @param selection tree selection that was changed + */ + @Override + public void onTreeSelectionChanged(TreeViewSelection selection) { + this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); + selection.getSelectedViews().forEach(v -> v.setSelected(true)); + this.selection = selection; + repaint(); + } + + /** + * Called when the model has finished updating the tree. + */ + @Override + public void onUpdateTree() { + repaint(); + } + + /** + * Gets the TreeElementView by the corresponding TreeElement associated with it + * + * @param element TreeElement of the view + * @return TreeElementView of the TreeElement associated with it + */ + public TreeElementView getElementView(TreeElement element) { + return viewMap.get(element); + } + + private void removeTreeNode(TreeNode node) { + viewMap.remove(node); + node.getChildren().forEach(t -> removeTreeTransition(t)); + } + + private void removeTreeTransition(TreeTransition trans) { + viewMap.remove(trans); + if (trans.getChildNode() != null) { + removeTreeNode(trans.getChildNode()); + } + } + + private void addTreeNode(TreeNode node) { + TreeTransition parent = node.getParent(); + + TreeNodeView nodeView = new TreeNodeView(node); + TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); + + nodeView.setParentView(parentView); + parentView.setChildView(nodeView); + + viewMap.put(node, nodeView); + + if (!node.getChildren().isEmpty()) { + node.getChildren().forEach(t -> addTreeTransition(t)); + } + } + + private void addTreeTransition(TreeTransition trans) { + List parents = trans.getParents(); + + TreeTransitionView transView = new TreeTransitionView(trans); + for (TreeNode parent : parents) { + TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); + transView.addParentView(parentNodeView); + parentNodeView.addChildrenView(transView); + } + + viewMap.put(trans, transView); + + if (trans.getChildNode() != null) { + addTreeNode(trans.getChildNode()); + } + } + + ///New Draw Methods + + public void drawTree(Graphics2D graphics2D) { + if (tree == null) { + LOGGER.error("Unable to draw tree."); + } + else { + if (rootNodeView == null) { + rootNodeView = new TreeNodeView(tree.getRootNode()); + + LOGGER.debug("Creating new views for tree view."); + createViews(rootNodeView); + + selection.newSelection(rootNodeView); + } + + dimension = new Dimension(0, 0); + calcSpan(rootNodeView); + rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); + + calculateViewLocations(rootNodeView, 0); + dimension.height = (int) rootNodeView.getSpan(); + + redrawTree(graphics2D, rootNodeView); + LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); + } + } + + public void createViews(TreeNodeView nodeView) { + if (nodeView != null) { + viewMap.put(nodeView.getTreeElement(), nodeView); + + TreeNode node = nodeView.getTreeElement(); + for (TreeTransition trans : node.getChildren()) { + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + if (transView != null) { + nodeView.addChildrenView(transView); + transView.addParentView(nodeView); + break; + } + transView = new TreeTransitionView(trans); + + viewMap.put(transView.getTreeElement(), transView); + + transView.addParentView(nodeView); + nodeView.addChildrenView(transView); + + TreeNode childNode = trans.getChildNode(); + if (childNode != null) { + TreeNodeView childNodeView = new TreeNodeView(childNode); + viewMap.put(childNodeView.getTreeElement(), childNodeView); + + childNodeView.setParentView(transView); + transView.setChildView(childNodeView); + + createViews(childNodeView); + } + } + } + } + + public void calculateViewLocations(TreeNodeView nodeView, int depth) { + nodeView.setDepth(depth); + int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; + nodeView.setX(xLoc); + dimension.width = Math.max(dimension.width, xLoc); + + TreeTransitionView parentTransView = nodeView.getParentView(); + int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); + nodeView.setY(yLoc); + + ArrayList children = nodeView.getChildrenViews(); + switch (children.size()) { + case 0: + break; + case 1: { + TreeTransitionView childView = children.get(0); + + List parentsViews = childView.getParentViews(); + if (parentsViews.size() == 1) { + childView.setEndY(yLoc); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + else { + if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { + int yAvg = 0; + for (int i = 0; i < parentsViews.size(); i++) { + TreeNodeView parentNodeView = parentsViews.get(i); + depth = Math.max(depth, parentNodeView.getDepth()); + yAvg += parentNodeView.getY(); + + Point lineStartPoint = childView.getLineStartPoint(i); + lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = parentNodeView.getY(); + } + yAvg /= parentsViews.size(); + childView.setEndY(yAvg); + + childView.setDepth(depth); + + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + } + break; + } + default: { + int span = 0; + for (TreeTransitionView childView : children) { + span += childView.getSpan(); + } + + span = (int) ((nodeView.getSpan() - span) / 2); + for (int i = 0; i < children.size(); i++) { + TreeTransitionView childView = children.get(i); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); + + span += childView.getSpan(); + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + break; + } + } + } + + public void calcSpan(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + calcSpan(childView); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + ArrayList newChildrenViews = new ArrayList<>(); + for (TreeTransition trans : node.getChildren()) { + newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); + } + nodeView.setChildrenViews(newChildrenViews); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + calcSpan(mergePointView); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + calcSpan(transView); + span += transView.getSpan(); + } + } + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view + * specified as stop. Stop tree puzzleElement is NOT included in the span calculation + * + * @param view + * @param stop + */ + private void subCalcSpan(TreeElementView view, TreeElementView stop) { + //safe-guard for infinite loop + if (view == stop) { + return; + } + + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + if (childView == stop) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + subCalcSpan(childView, stop); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + subCalcSpan(mergePointView, stop); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + subCalcSpan(transView, stop); + span += transView.getSpan(); + } + } + + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null || nodeView == stop) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in + * relative order in the list of child transitions of the specified node + * + * @param node root node of the branches + * @param branches DisjointSets of the child branches of the specified node which determine which branches merge + */ + private void reorderBranches(TreeNode node, DisjointSets branches) { + List children = node.getChildren(); + List> mergingSets = branches.getAllSets(); + + List> newOrder = new ArrayList<>(); + for (Set set : mergingSets) { + List mergeBranch = new ArrayList<>(); + newOrder.add(mergeBranch); + children.forEach(t -> { + if (set.contains(t)) { + mergeBranch.add(t); + } + }); + mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> + children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); + } + + newOrder.sort((List b1, List b2) -> { + int low1 = -1; + int low2 = -1; + for (TreeTransition t1 : b1) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + for (TreeTransition t1 : b2) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + return low1 < low2 ? -1 : 1; + }); + + List newChildren = new ArrayList<>(); + newOrder.forEach(l -> newChildren.addAll(l)); + node.setChildren(newChildren); + } } \ No newline at end of file From cfaabb43701558d59877dbd64dc319332e142ef6 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Fri, 26 Jan 2024 10:11:05 -0500 Subject: [PATCH 002/359] Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> --- .../java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java | 4 ++-- .../java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java index 5f0a1243a..4d4e95762 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/AndCaseRuleTest.java @@ -42,7 +42,7 @@ private void falseAndTest(String fileName, ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out - Assert.assertNotNull(RULE.checkRule(transition)); + Assert.assertNull(RULE.checkRule(transition)); // Make sure there are two branches Assert.assertEquals(2, cases.size()); @@ -120,7 +120,7 @@ private void trueAndTest(String fileName, ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out - Assert.assertNotNull(RULE.checkRule(transition)); + Assert.assertNull(RULE.checkRule(transition)); // There should only be 1 branch Assert.assertEquals(1, cases.size()); diff --git a/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java index 276e822cf..0f7e93db5 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/OrCaseRuleTest.java @@ -43,7 +43,7 @@ private void trueOrTest(String fileName, ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out - Assert.assertNotNull(RULE.checkRule(transition)); + Assert.assertNull(RULE.checkRule(transition)); // Make sure there are two branches Assert.assertEquals(2, cases.size()); @@ -121,7 +121,7 @@ private void falseOrTest(String fileName, ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out - Assert.assertNotNull(RULE.checkRule(transition)); + Assert.assertNull(RULE.checkRule(transition)); // There should only be 1 branch Assert.assertEquals(1, cases.size()); From f20bfe1423da68c49876ecf715ca268465bba688 Mon Sep 17 00:00:00 2001 From: Chase-Grajeda Date: Fri, 26 Jan 2024 12:55:07 -0500 Subject: [PATCH 003/359] 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 --- bin/main/edu/rpi/legup/log4j2.properties | 28 +++++++++---------- .../edu/rpi/legup/model/rules/CaseRule.java | 10 ------- .../rules/caserule/CaseRule_Generic.java | 22 +++++++-------- .../rules/TrueOrFalseCaseRuleTest.java | 2 +- 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/bin/main/edu/rpi/legup/log4j2.properties b/bin/main/edu/rpi/legup/log4j2.properties index 4f2556c2d..de1fa02ed 100644 --- a/bin/main/edu/rpi/legup/log4j2.properties +++ b/bin/main/edu/rpi/legup/log4j2.properties @@ -1,15 +1,15 @@ -# Logging level -# Root logger option -log4j.rootLogger=DEBUG, stdout, file -# Redirect log messages to console -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n -# Redirect log messages to a log file, support file rolling. -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=Legup.log -log4j.appender.file.MaxFileSize=5MB -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout +# Logging level +# Root logger option +log4j.rootLogger=DEBUG, stdout, file +# Redirect log messages to console +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n +# Redirect log messages to a log file, support file rolling. +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.File=Legup.log +log4j.appender.file.MaxFileSize=5MB +log4j.appender.file.MaxBackupIndex=10 +log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java index d9c7e73e5..6563f4d97 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -61,20 +61,10 @@ public String checkRule(TreeTransition transition) { return "Must not have multiple parent nodes"; } - /*if (transition.getBoard().getModifiedData().size() != 0){ - return "Should not modify before case rule"; - }*/ - - for (TreeTransition childTrans : parentNodes.get(0).getChildren()) { if (childTrans.getRule() == null || !childTrans.getRule().getClass().equals(this.getClass())) { return "All children nodes must be justified with the same case rule."; } - else { - if (childTrans.getBoard().getModifiedData().isEmpty()) { - return "You must modify the board in each case node"; - } - } } String check = checkRuleRaw(transition); 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 1409f4baa..46163c2a3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_Generic.java @@ -38,20 +38,20 @@ public CaseRule_Generic(String ruleID, String ruleName, String title, String des public String checkRuleRaw(TreeTransition transition) { // Validate that two children are generated List childTransitions = transition.getParents().get(0).getChildren(); - if (childTransitions.size() >= 1) { + 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."; - } - } + // // 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; } diff --git a/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java index 849e9f15c..2c0b9fb15 100644 --- a/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java +++ b/src/test/java/puzzles/shorttruthtable/rules/TrueOrFalseCaseRuleTest.java @@ -44,7 +44,7 @@ public void TwoBranchesTest() throws InvalidFileFormatException { ArrayList cases = RULE.getCases(board, cell); // Make sure that the rule checks out - Assert.assertNotNull(RULE.checkRule(transition)); + Assert.assertNull(RULE.checkRule(transition)); // Make sure there are two branches Assert.assertEquals(2, cases.size()); From 36e1dcf5d869276f5d55a99b85e781bb79beebc3 Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:57:11 -0500 Subject: [PATCH 004/359] Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --- puzzles files/skyscrapers/1646651 | 31 - puzzles files/skyscrapers/easy1.xml | 47 - .../legup/history/AutoCaseRuleCommand.java | 1 + .../legup/model/gameboard/PuzzleElement.java | 20 + .../edu/rpi/legup/model/rules/CaseRule.java | 29 +- .../edu/rpi/legup/model/rules/DirectRule.java | 4 + .../rpi/legup/model/tree/TreeTransition.java | 64 +- .../lightup/rules/SatisfyNumberCaseRule.java | 63 + .../caserule/CaseRule_GenericStatement.java | 256 +-- .../puzzle/skyscrapers/SkyscrapersBoard.java | 11 - .../puzzle/skyscrapers/SkyscrapersClue.java | 5 +- .../rules/CellForNumberCaseRule.java | 53 +- .../rules/NumberForCellCaseRule.java | 35 + .../legup/puzzle/treetent/TreeTentBoard.java | 20 +- .../treetent/rules/FillinRowCaseRule.java | 25 + .../treetent/rules/LinkTentCaseRule.java | 15 +- .../treetent/rules/LinkTreeCaseRule.java | 29 + .../ui/proofeditorui/treeview/TreeView.java | 1640 +++++++++-------- 18 files changed, 1354 insertions(+), 994 deletions(-) delete mode 100644 puzzles files/skyscrapers/1646651 delete mode 100644 puzzles files/skyscrapers/easy1.xml diff --git a/puzzles files/skyscrapers/1646651 b/puzzles files/skyscrapers/1646651 deleted file mode 100644 index 847d8639c..000000000 --- a/puzzles files/skyscrapers/1646651 +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/skyscrapers/easy1.xml b/puzzles files/skyscrapers/easy1.xml deleted file mode 100644 index 9d3135bff..000000000 --- a/puzzles files/skyscrapers/easy1.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index 331a3dddf..a4c157c77 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -62,6 +62,7 @@ public void executeCommand() { board.setModifiable(false); transition.setBoard(board); transition.setRule(caseRule); + transition.setSelection(elementView.getPuzzleElement().copy()); caseTrans.add(transition); TreeNode childNode = (TreeNode) tree.addTreeElement(transition); diff --git a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java index 97c9205cf..3d84287e3 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java @@ -11,6 +11,7 @@ public abstract class PuzzleElement { protected boolean isModified; protected boolean isGiven; protected boolean isValid; + protected int casesDepended; /** * PuzzleElement Constructor creates a new puzzle element. @@ -22,6 +23,7 @@ public PuzzleElement() { this.isModified = false; this.isGiven = false; this.isValid = true; + this.casesDepended = 0; } /** @@ -148,6 +150,24 @@ public void setValid(boolean isValid) { this.isValid = isValid; } + /** + * Get the number of case rules that depend upon the state of this element + * + * @return number of cases + */ + public int getCasesDepended() { + return this.casesDepended; + } + + /** + * Sets the number of case rules that depend upon the state of this element + * + * @param cases number of cases + */ + public void setCasesDepended(int cases) { + this.casesDepended = cases; + } + /** * Tests whether two puzzle elements objects have the same puzzle element * diff --git a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java index 6563f4d97..a01db2b6d 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -7,9 +7,8 @@ import edu.rpi.legup.model.tree.TreeTransition; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Set; import static edu.rpi.legup.model.rules.RuleType.CASE; @@ -69,6 +68,7 @@ public String checkRule(TreeTransition transition) { String check = checkRuleRaw(transition); + // Mark transition and new data as valid or not boolean isCorrect = (check == null); for (TreeTransition childTrans : parentNodes.get(0).getChildren()) { childTrans.setCorrect(isCorrect); @@ -115,6 +115,31 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement */ @Override public abstract String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement); + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + List cases = getCases(board,puzzleElement); + for (Board caseBoard : cases) { + Set data = caseBoard.getModifiedData(); + for (PuzzleElement element : data) { + if(!elements.contains(board.getPuzzleElement(element))){ + elements.add(board.getPuzzleElement(element)); + } + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java index 0465ca88c..847764b7b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -33,6 +33,10 @@ public String checkRule(TreeTransition transition) { transition.getParents().get(0).getChildren().size() != 1) { return "State must have only 1 parent and 1 child"; } + else if (finalBoard.getModifiedData().isEmpty()) { + // null transition + return null; + } else { return checkRuleRaw(transition); } diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java index e1d042626..72572ac72 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java @@ -2,6 +2,7 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.rules.RuleType; @@ -12,6 +13,8 @@ public class TreeTransition extends TreeElement { private ArrayList parents; private TreeNode childNode; private Rule rule; + + private PuzzleElement selection; private boolean isCorrect; private boolean isVerified; @@ -26,6 +29,7 @@ public TreeTransition(Board board) { this.childNode = null; this.board = board; this.rule = null; + this.selection = null; this.isCorrect = false; this.isVerified = false; } @@ -87,13 +91,42 @@ public void propagateChange(PuzzleElement element) { } } else { + // Overwrite previous modifications to this element + board.removeModifiedData(board.getPuzzleElement(element)); + + // apply changes to tranistion + board.notifyChange(element); + + // mark first transition as modified + if (!board.getPuzzleElement(element).equalsData(parents.get(0).getBoard().getPuzzleElement(element))) { + board.addModifiedData(element); + } + + // propagate to children if (childNode != null) { - board.notifyChange(element); - childNode.getBoard().notifyChange(element.copy()); - for (TreeTransition child : childNode.getChildren()) { - PuzzleElement copy = element.copy(); + + // find starting board + TreeNode head = childNode; + while (head.getParent() != null) { + head = head.getParent().getParents().get(0); + } + Board headBoard = head.getBoard(); + + PuzzleElement copy = element.copy(); + // Set as modifiable if reverted to starting value (and started modifiable) + if (headBoard.getPuzzleElement(element).equalsData(element)) { + copy.setModifiable(headBoard.getPuzzleElement(element).isModifiable()); + } + else{ copy.setModifiable(false); - child.propagateChange(copy); + } + + // apply changes to result node + childNode.getBoard().notifyChange(copy); + + // apply to all child transitions + for (TreeTransition child : childNode.getChildren()) { + child.propagateChange(copy.copy()); } } } @@ -327,6 +360,27 @@ public void setRule(Rule rule) { isVerified = false; } + /** + * Gets he selected element associated with this transition + * + * @return If this is a case rule, the selected element for that rule, null otherwise + */ + public PuzzleElement getSelection() { + if (this.rule instanceof CaseRule) { + return selection; + } + return null; + } + + /** + * Sets the selected element associated with this transition + * + * @param selection selected element for this transition + */ + public void setSelection(PuzzleElement selection) { + this.selection = selection; + } + /** * Gets whether this transition is correctly justified * diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java index 1f166685b..cf7b70ccd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java @@ -287,4 +287,67 @@ private List getAdjacentCells(LightUpBoard board, LightUpCell cell) } return cells; } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + LightUpBoard puzzleBoard = (LightUpBoard) board; + LightUpCell point = (LightUpCell)puzzleBoard.getPuzzleElement(puzzleElement); + + List cells = getAdjacentCells(puzzleBoard,point); + + for (LightUpCell cell : cells) { + //add cells that can light adjacents from any direction + Point location = cell.getLocation(); + for (int i = location.x; i < puzzleBoard.getWidth(); i++) { + System.out.println(i); + LightUpCell c = puzzleBoard.getCell(i, location.y); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + for (int i = location.x; i >= 0; i--) { + LightUpCell c = puzzleBoard.getCell(i, location.y); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + for (int i = location.y; i < puzzleBoard.getHeight(); i++) { + LightUpCell c = puzzleBoard.getCell(location.x, i); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + for (int i = location.y; i >= 0; i--) { + LightUpCell c = puzzleBoard.getCell(location.x, i); + if (c.getType() == LightUpCellType.BLACK || c.getType() == LightUpCellType.NUMBER) { + break; + } + else if (!elements.contains(board.getPuzzleElement(c))) { + elements.add(board.getPuzzleElement(c)); + } + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java index 2a40bf45d..0e25586a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java @@ -1,119 +1,137 @@ -package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; - -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; -import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; - - -import java.util.ArrayList; - -public abstract class CaseRule_GenericStatement extends CaseRule_Generic { - - public CaseRule_GenericStatement(String ruleID, char operation, String title, - ShortTruthTableCellType[][] trueCases, - ShortTruthTableCellType[][] falseCases) { - super(ruleID, ShortTruthTableOperation.getRuleName(operation), - title + " case", - "A known " + title.toUpperCase() + " statement can have multiple forms"); - - this.operation = operation; - - this.trueCases = trueCases; - this.falseCases = falseCases; - } - - private final char operation; - - private final ShortTruthTableCellType[][] trueCases; - private final ShortTruthTableCellType[][] falseCases; - - protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; - protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; - protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; - - // Adds all elements that can be selected for this caserule - @Override - public CaseBoard getCaseBoard(Board board) { - //copy the board and add all elements that can be selected - ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); - sttBoard.setModifiable(false); - CaseBoard caseBoard = new CaseBoard(sttBoard, this); - - //add all elements that can be selected for the case rule statement - for (PuzzleElement element : sttBoard.getPuzzleElements()) { - //get the cell object - ShortTruthTableCell cell = sttBoard.getCellFromElement(element); - //the cell must match the symbol - if (cell.getSymbol() != this.operation) continue; - //the statement must be assigned with unassigned sub-statements - if (!cell.getType().isTrueOrFalse()) continue; - if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; - if (this.operation != ShortTruthTableOperation.NOT && - cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { - continue; - } - //if the element has passed all the checks, it can be selected - caseBoard.addPickableElement(element); - } - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ - @SuppressWarnings("unchecked") - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); - ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); - - // If the statement is set to true, collect true cases. Otherwise, collect the false cases - if (cell.getType() == ShortTruthTableCellType.TRUE) { - return getCasesFromCell(sttBoard, puzzleElement, trueCases); - } - return getCasesFromCell(sttBoard, puzzleElement, falseCases); - } - - /** - * Collects a list of boards for each possible outcome of case-rule application - * @param board current board state - * @param puzzleElement case rule operator - * @param possibilities list of possibilities for operator state - * @return ArrayList of Boards - */ - private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { - // Create branch case for each possibility - ArrayList cases = new ArrayList<>(); - for (int i = 0; i < possibilities.length; i++) { - // Create a new board to modify and get statement of selected square - ShortTruthTableBoard b = board.copy(); - ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); - ShortTruthTableStatement statement = cell.getStatementReference(); - - // Modify neighboring cells of case-rule application by the provided logical cases - if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { - ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); - leftCell.setData(possibilities[i][0]); - b.addModifiedData(leftCell); - } - if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { - ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); - rightCell.setData(possibilities[i][1]); - b.addModifiedData(rightCell); - } - - cases.add(b); - } - return cases; - } -} +package edu.rpi.legup.puzzle.shorttruthtable.rules.caserule; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableStatement; +import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableOperation; + +import java.util.List; +import java.util.ArrayList; + +public abstract class CaseRule_GenericStatement extends CaseRule_Generic { + + public CaseRule_GenericStatement(String ruleID, char operation, String title, + ShortTruthTableCellType[][] trueCases, + ShortTruthTableCellType[][] falseCases) { + super(ruleID, ShortTruthTableOperation.getRuleName(operation), + title + " case", + "A known " + title.toUpperCase() + " statement can have multiple forms"); + + this.operation = operation; + + this.trueCases = trueCases; + this.falseCases = falseCases; + } + + private final char operation; + + private final ShortTruthTableCellType[][] trueCases; + private final ShortTruthTableCellType[][] falseCases; + + protected static final ShortTruthTableCellType T = ShortTruthTableCellType.TRUE; + protected static final ShortTruthTableCellType F = ShortTruthTableCellType.FALSE; + protected static final ShortTruthTableCellType U = ShortTruthTableCellType.UNKNOWN; + + // Adds all elements that can be selected for this caserule + @Override + public CaseBoard getCaseBoard(Board board) { + //copy the board and add all elements that can be selected + ShortTruthTableBoard sttBoard = (ShortTruthTableBoard) board.copy(); + sttBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(sttBoard, this); + + //add all elements that can be selected for the case rule statement + for (PuzzleElement element : sttBoard.getPuzzleElements()) { + //get the cell object + ShortTruthTableCell cell = sttBoard.getCellFromElement(element); + //the cell must match the symbol + if (cell.getSymbol() != this.operation) continue; + //the statement must be assigned with unassigned sub-statements + if (!cell.getType().isTrueOrFalse()) continue; + if (cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) continue; + if (this.operation != ShortTruthTableOperation.NOT && + cell.getStatementReference().getRightStatement().getCell().getType().isTrueOrFalse()) { + continue; + } + //if the element has passed all the checks, it can be selected + caseBoard.addPickableElement(element); + } + return caseBoard; + } + + /** + * Gets the possible cases at a specific location based on this case rule + * + * @param board the current board state + * @param puzzleElement equivalent puzzleElement + * @return a list of elements the specified could be + */ + @SuppressWarnings("unchecked") + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); + ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); + + // If the statement is set to true, collect true cases. Otherwise, collect the false cases + if (cell.getType() == ShortTruthTableCellType.TRUE) { + return getCasesFromCell(sttBoard, puzzleElement, trueCases); + } + return getCasesFromCell(sttBoard, puzzleElement, falseCases); + } + + /** + * Collects a list of boards for each possible outcome of case-rule application + * @param board current board state + * @param puzzleElement case rule operator + * @param possibilities list of possibilities for operator state + * @return ArrayList of Boards + */ + private ArrayList getCasesFromCell(ShortTruthTableBoard board, PuzzleElement puzzleElement, ShortTruthTableCellType[][] possibilities) { + // Create branch case for each possibility + ArrayList cases = new ArrayList<>(); + for (int i = 0; i < possibilities.length; i++) { + // Create a new board to modify and get statement of selected square + ShortTruthTableBoard b = board.copy(); + ShortTruthTableCell cell = b.getCellFromElement(puzzleElement); + ShortTruthTableStatement statement = cell.getStatementReference(); + + // Modify neighboring cells of case-rule application by the provided logical cases + if (possibilities[i][0] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell leftCell = statement.getLeftStatement().getCell(); + leftCell.setData(possibilities[i][0]); + b.addModifiedData(leftCell); + } + if (possibilities[i][1] != ShortTruthTableCellType.UNKNOWN) { + ShortTruthTableCell rightCell = statement.getRightStatement().getCell(); + rightCell.setData(possibilities[i][1]); + b.addModifiedData(rightCell); + } + + cases.add(b); + } + return cases; + } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = super.dependentElements(board,puzzleElement); + + elements.add(board.getPuzzleElement(puzzleElement)); + + return elements; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java index ca6bcbe02..52e8a6400 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -25,9 +25,6 @@ public class SkyscrapersBoard extends GridBoard { private boolean viewFlag = false; private boolean dupeFlag = false; - private SkyscrapersClue modClue = null; - //helper variable for case rule verification, tracks recently modified row/col - public SkyscrapersBoard(int size) { super(size, size); @@ -94,14 +91,6 @@ public void setViewFlag(boolean newFlag) { viewFlag = newFlag; } - public SkyscrapersClue getmodClue() { - return modClue; - } - - public void setModClue(SkyscrapersClue newClue) { - modClue = newClue; - } - @Override public SkyscrapersCell getCell(int x, int y) { return (SkyscrapersCell) super.getCell(x, y); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java index dc68f45c7..1e7b1b45e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClue.java @@ -47,6 +47,9 @@ public void setType(SkyscrapersType type) { } public SkyscrapersClue copy() { - return null; + SkyscrapersClue copy = new SkyscrapersClue(data, clueIndex, type); + copy.setIndex(index); + copy.setModifiable(isModifiable); + return copy; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java index 683b742bf..01527294a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java @@ -69,7 +69,6 @@ public ArrayList getCasesFor(Board board, PuzzleElement puzzleElement, In PuzzleElement newCell = newCase.getPuzzleElement(cell); newCell.setData(number); newCase.addModifiedData(newCell); - newCase.setModClue((SkyscrapersClue) newCase.getPuzzleElement(clue)); //if flags boolean passed = true; @@ -103,12 +102,7 @@ public String checkRuleRaw(TreeTransition transition) { return "This case rule must have at least one child."; } - //find changed row/col - SkyscrapersClue modClue = ((SkyscrapersBoard) childTransitions.get(0).getBoard()).getmodClue(); - - //System.out.println(modClue.getType()); - //System.out.println(modClue.getClueIndex()); - if (childTransitions.size() != getCasesFor(oldBoard, modClue, (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) { + if (childTransitions.size() != getCasesFor(oldBoard, oldBoard.getPuzzleElement(transition.getSelection()), (Integer) childTransitions.get(0).getBoard().getModifiedData().iterator().next().getData()).size()) { //System.out.println("Wrong number of cases."); return "Wrong number of cases."; } @@ -132,4 +126,49 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board; + SkyscrapersClue clue = (SkyscrapersClue)puzzleBoard.getPuzzleElement(puzzleElement); + + // check each point in modified row/col + List data = puzzleBoard.getRowCol(clue.getClueIndex(),SkyscrapersType.ANY,clue.getType() == SkyscrapersType.CLUE_WEST); + for (SkyscrapersCell point : data) { + List cells = new ArrayList<>(List.of(point)); + + // if dependent on row/col + if ((puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) && point.getType() == SkyscrapersType.UNKNOWN) { + // get perpendicular row/col intersecting this point + int index; + if (clue.getType() == SkyscrapersType.CLUE_WEST) { + index = point.getLocation().x; + } + else { + index = point.getLocation().y; + } + cells.addAll(puzzleBoard.getRowCol(index,SkyscrapersType.ANY,clue.getType() != SkyscrapersType.CLUE_WEST)); + } + + // add all to result + for (SkyscrapersCell cell : cells) { + if (!elements.contains(board.getPuzzleElement(cell))) { + elements.add(board.getPuzzleElement(cell)); + } + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java index a061c62a3..3bf0de70a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java @@ -7,6 +7,7 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersBoard; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersClue; import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersType; import java.awt.*; @@ -134,4 +135,38 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + SkyscrapersBoard puzzleBoard = (SkyscrapersBoard) board; + SkyscrapersCell point = (SkyscrapersCell)puzzleBoard.getPuzzleElement(puzzleElement); + + List cells = new ArrayList<>(List.of(point)); + + // if dependent on row/col + if (puzzleBoard.getDupeFlag() || puzzleBoard.getViewFlag()) { + // add all cells in row/col intersecting given point + cells.addAll(puzzleBoard.getRowCol(point.getLocation().x,SkyscrapersType.ANY,false)); + cells.addAll(puzzleBoard.getRowCol(point.getLocation().y,SkyscrapersType.ANY,true)); + } + + for (SkyscrapersCell cell : cells) { + if (!elements.contains(board.getPuzzleElement(cell))) { + elements.add(board.getPuzzleElement(cell)); + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java index 2542ea335..dc809f34d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -124,21 +124,11 @@ public void notifyDeletion(PuzzleElement puzzleElement) { public List getAdjacent(TreeTentCell cell, TreeTentType type) { List adj = new ArrayList<>(); Point loc = cell.getLocation(); - TreeTentCell up = getCell(loc.x, loc.y - 1); - TreeTentCell right = getCell(loc.x + 1, loc.y); - TreeTentCell down = getCell(loc.x, loc.y + 1); - TreeTentCell left = getCell(loc.x - 1, loc.y); - if (up != null && up.getType() == type) { - adj.add(up); - } - if (right != null && right.getType() == type) { - adj.add(right); - } - if (down != null && down.getType() == type) { - adj.add(down); - } - if (left != null && left.getType() == type) { - adj.add(left); + for (int i = -2; i < 2; i++) { + TreeTentCell adjCell = getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); + if (adjCell != null && adjCell.getType() == type) { + adj.add(adjCell); + } } return adj; } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index 538772b74..698b3aa5e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -160,4 +160,29 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return null; } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(); + + TreeTentBoard treeTentBoard = (TreeTentBoard) board; + TreeTentClue clue = (TreeTentClue) puzzleElement; + + // add all elements of filled row + for (int i = 0; i < treeTentBoard.getWidth(); i++) { + TreeTentCell cell = treeTentBoard.getCell(i, clue.getClueIndex()-1); + elements.add(board.getPuzzleElement((cell))); + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java index 36f466f87..39b1d0251 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java @@ -5,7 +5,6 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.treetent.TreeTent; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentType; import edu.rpi.legup.puzzle.treetent.TreeTentCell; @@ -151,4 +150,18 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + return List.of(board.getPuzzleElement(puzzleElement)); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java index 249547301..72ffd62eb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java @@ -10,6 +10,7 @@ import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.TreeTentType; +import java.awt.Point; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -152,4 +153,32 @@ public String checkRuleRaw(TreeTransition transition) { public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return checkRuleRaw(transition); } + + /** + * Returns the elements necessary for the cases returned by getCases(board,puzzleElement) to be valid + * Overridden by case rules dependent on more than just the modified data + * + * @param board board state at application + * @param puzzleElement selected puzzleElement + * @return List of puzzle elements (typically cells) this application of the case rule depends upon. + * Defaults to any element modified by any case + */ + @Override + public List dependentElements(Board board, PuzzleElement puzzleElement) { + List elements = new ArrayList<>(List.of(board.getPuzzleElement(puzzleElement))); + + TreeTentBoard treeTentBoard = (TreeTentBoard) board; + TreeTentCell point = (TreeTentCell) puzzleElement; + + // get all adjacent cells + Point loc = point.getLocation(); + for (int i = -2; i < 2; i++) { + TreeTentCell cell = treeTentBoard.getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); + if (cell != null) { + elements.add(board.getPuzzleElement(cell)); + } + } + + return elements; + } } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java index ecf59146d..9bfffe60a 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java @@ -1,756 +1,886 @@ -package edu.rpi.legup.ui.proofeditorui.treeview; - -import edu.rpi.legup.app.GameBoardFacade; -import edu.rpi.legup.controller.TreeController; -import edu.rpi.legup.model.observer.ITreeListener; -import edu.rpi.legup.model.tree.Tree; -import edu.rpi.legup.model.tree.TreeElement; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.ui.ScrollView; -import edu.rpi.legup.utility.DisjointSets; - -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.util.*; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import static edu.rpi.legup.model.tree.TreeElementType.NODE; -import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; -import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; - -public class TreeView extends ScrollView implements ITreeListener { - private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); - - private static final int TRANS_GAP = 5; - - private static final int NODE_GAP_WIDTH = 70; - private static final int NODE_GAP_HEIGHT = 15; - - private static final int BORDER_GAP_HEIGHT = 20; - private static final int BORDER_GAP_WIDTH = 20; - - private static final int BORDER_SPACING = 100; - - private TreeNodeView nodeHover; - - private ArrayList currentStateBoxes; - private Rectangle bounds = new Rectangle(0, 0, 0, 0); - - private Tree tree; - private TreeNodeView rootNodeView; - private Map viewMap; - private Dimension dimension; - - private TreeViewSelection selection; - - public TreeView(TreeController treeController) { - super(treeController); - currentStateBoxes = new ArrayList<>(); - setSize(dimension = new Dimension(100, 200)); - setPreferredSize(new Dimension(640, 160)); - - viewMap = new HashMap<>(); - - selection = new TreeViewSelection(); - } - - public TreeViewSelection getSelection() { - return selection; - } - - /** - * Gets the tree node puzzleElement that the mouse is hovering over - * - * @return tree node puzzleElement that the mouse is hovering over - */ - public TreeNodeView getNodeHover() { - return nodeHover; - } - - /** - * Sets the tree node puzzleElement that the mouse is hovering over - * - * @param nodeHover tree node puzzleElement the mouse is hovering over - */ - public void setNodeHover(TreeNodeView nodeHover) { - this.nodeHover = nodeHover; - } - - /** - * Gets the TreeElementView by the specified point or null if no view exists at the specified point - * - * @param point location to query for a view - * @return TreeElementView at the point specified, otherwise null - */ - public TreeElementView getTreeElementView(Point point) { - return getTreeElementView(point, rootNodeView); - } - - /** - * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or - * the view specified is null - * - * @param point location to query for a view - * @param elementView view to determine if the point is contained within it - * @return TreeElementView at the point specified, otherwise null - */ - private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { - if (elementView == null) { - return null; - } - else { - if (elementView.contains(point) && elementView.isVisible()) { - if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { - return null; - } - return elementView; - } - else { - if (elementView.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) elementView; - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - TreeElementView view = getTreeElementView(point, transitionView); - if (view != null) { - return view; - } - } - } - else { - TreeTransitionView transitionView = (TreeTransitionView) elementView; - return getTreeElementView(point, transitionView.getChildView()); - } - } - } - return null; - } - - public void updateTreeView(Tree tree) { - this.tree = tree; - if (selection.getSelectedViews().size() == 0) { - selection.newSelection(new TreeNodeView(tree.getRootNode())); - } - repaint(); - } - - /** - * Sets the tree associated with this TreeView - * - * @param tree tree - */ - public void setTree(Tree tree) { - this.tree = tree; - } - - public void updateTreeSize() { - if (GameBoardFacade.getInstance().getTree() == null) { - return; - } - setSize(bounds.getSize()); - } - - public void reset() { - if (bounds.x != 0 || bounds.y != 0) { - updateTreeSize(); - } - } - - public void zoomFit() { - double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); - double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); - zoomTo(Math.min(fitWidth, fitHeight)); - viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); - } - - /** - * Creates a customized viewport for the scroll pane - * - * @return viewport for the scroll pane - */ - @Override - protected JViewport createViewport() { - return new JViewport() { - @Override - protected LayoutManager createLayoutManager() { - return new ViewportLayout() { - @Override - public void layoutContainer(Container parent) { - Point point = viewport.getViewPosition(); - // determine the maximum x and y view positions - int mx = getCanvas().getWidth() - viewport.getWidth(); - int my = getCanvas().getHeight() - viewport.getHeight(); - // obey edge boundaries - if (point.x < 0) { - point.x = 0; - } - if (point.x > mx) { - point.x = mx; - } - if (point.y < 0) { - point.y = 0; - } - if (point.y > my) { - point.y = my; - } - // center margins - if (mx < 0) { - point.x = 0; - } - if (my < 0) { - point.y = my / 2; - } - viewport.setViewPosition(point); - } - }; - } - }; - } - - public void draw(Graphics2D graphics2D) { - currentStateBoxes.clear(); - Tree tree = GameBoardFacade.getInstance().getTree(); - if (tree != null) { - //setSize(bounds.getDimension()); - graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - drawTree(graphics2D); - - dimension.width += BORDER_SPACING; - setSize(dimension); -// graphics2D.drawRect(0,0, dimension.width, dimension.height); - - if (selection.getHover() != null) { - drawMouseOver(graphics2D); - } - } - } - - public void zoomReset() { - zoomTo(1.0); - viewport.setViewPosition(new Point(0, 0)); - } - - private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { - if (nodeView != null) { - nodeView.draw(graphics2D); - for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { - transitionView.draw(graphics2D); - redrawTree(graphics2D, transitionView.getChildView()); - } - } - } - - public void removeTreeElement(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - nodeView.getParentView().setChildView(null); - } - else { - TreeTransitionView transitionView = (TreeTransitionView) view; - transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); - } - } - - /** - * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image - * - * @param g the graphics to use to draw - */ - public void drawMouseOver(Graphics2D g) { - if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { - TreeTransition transition = (TreeTransition) selection.getHover().treeElement; - int imgWidth = 100; - int imgHeight = 100; - - BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); - image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); - Point mousePoint = selection.getMousePoint(); - g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); - } - } - - public void resetView() { - this.tree = null; - this.rootNodeView = null; - this.selection.clearSelection(); - this.selection.clearHover(); - } - - /** - * Called when a tree puzzleElement is added to the tree - * - * @param treeElement TreeElement that was added to the tree - */ - @Override - public void onTreeElementAdded(TreeElement treeElement) { - if (treeElement.getType() == NODE) { - addTreeNode((TreeNode) treeElement); - } - else { - addTreeTransition((TreeTransition) treeElement); - } - repaint(); - } - - /** - * Called when a tree puzzleElement is removed from the tree - * - * @param element TreeElement that was removed to the tree - */ - @Override - public void onTreeElementRemoved(TreeElement element) { - if (element.getType() == NODE) { - TreeNode node = (TreeNode) element; - TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); - - nodeView.getParentView().setChildView(null); - removeTreeNode(node); - } - else { - TreeTransition trans = (TreeTransition) element; - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - - transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); - removeTreeTransition(trans); - } - repaint(); - } - - /** - * Called when the tree selection was changed - * - * @param selection tree selection that was changed - */ - @Override - public void onTreeSelectionChanged(TreeViewSelection selection) { - this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); - selection.getSelectedViews().forEach(v -> v.setSelected(true)); - this.selection = selection; - repaint(); - } - - /** - * Called when the model has finished updating the tree. - */ - @Override - public void onUpdateTree() { - repaint(); - } - - /** - * Gets the TreeElementView by the corresponding TreeElement associated with it - * - * @param element TreeElement of the view - * @return TreeElementView of the TreeElement associated with it - */ - public TreeElementView getElementView(TreeElement element) { - return viewMap.get(element); - } - - private void removeTreeNode(TreeNode node) { - viewMap.remove(node); - node.getChildren().forEach(t -> removeTreeTransition(t)); - } - - private void removeTreeTransition(TreeTransition trans) { - viewMap.remove(trans); - if (trans.getChildNode() != null) { - removeTreeNode(trans.getChildNode()); - } - } - - private void addTreeNode(TreeNode node) { - TreeTransition parent = node.getParent(); - - TreeNodeView nodeView = new TreeNodeView(node); - TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); - - nodeView.setParentView(parentView); - parentView.setChildView(nodeView); - - viewMap.put(node, nodeView); - - if (!node.getChildren().isEmpty()) { - node.getChildren().forEach(t -> addTreeTransition(t)); - } - } - - private void addTreeTransition(TreeTransition trans) { - List parents = trans.getParents(); - - TreeTransitionView transView = new TreeTransitionView(trans); - for (TreeNode parent : parents) { - TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); - transView.addParentView(parentNodeView); - parentNodeView.addChildrenView(transView); - } - - viewMap.put(trans, transView); - - if (trans.getChildNode() != null) { - addTreeNode(trans.getChildNode()); - } - } - - ///New Draw Methods - - public void drawTree(Graphics2D graphics2D) { - if (tree == null) { - LOGGER.error("Unable to draw tree."); - } - else { - if (rootNodeView == null) { - rootNodeView = new TreeNodeView(tree.getRootNode()); - - LOGGER.debug("Creating new views for tree view."); - createViews(rootNodeView); - - selection.newSelection(rootNodeView); - } - - dimension = new Dimension(0, 0); - calcSpan(rootNodeView); - rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); - - calculateViewLocations(rootNodeView, 0); - dimension.height = (int) rootNodeView.getSpan(); - - redrawTree(graphics2D, rootNodeView); - LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); - } - } - - public void createViews(TreeNodeView nodeView) { - if (nodeView != null) { - viewMap.put(nodeView.getTreeElement(), nodeView); - - TreeNode node = nodeView.getTreeElement(); - for (TreeTransition trans : node.getChildren()) { - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - if (transView != null) { - nodeView.addChildrenView(transView); - transView.addParentView(nodeView); - break; - } - transView = new TreeTransitionView(trans); - - viewMap.put(transView.getTreeElement(), transView); - - transView.addParentView(nodeView); - nodeView.addChildrenView(transView); - - TreeNode childNode = trans.getChildNode(); - if (childNode != null) { - TreeNodeView childNodeView = new TreeNodeView(childNode); - viewMap.put(childNodeView.getTreeElement(), childNodeView); - - childNodeView.setParentView(transView); - transView.setChildView(childNodeView); - - createViews(childNodeView); - } - } - } - } - - public void calculateViewLocations(TreeNodeView nodeView, int depth) { - nodeView.setDepth(depth); - int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; - nodeView.setX(xLoc); - dimension.width = Math.max(dimension.width, xLoc); - - TreeTransitionView parentTransView = nodeView.getParentView(); - int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); - nodeView.setY(yLoc); - - ArrayList children = nodeView.getChildrenViews(); - switch (children.size()) { - case 0: - break; - case 1: { - TreeTransitionView childView = children.get(0); - - List parentsViews = childView.getParentViews(); - if (parentsViews.size() == 1) { - childView.setEndY(yLoc); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - else { - if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { - int yAvg = 0; - for (int i = 0; i < parentsViews.size(); i++) { - TreeNodeView parentNodeView = parentsViews.get(i); - depth = Math.max(depth, parentNodeView.getDepth()); - yAvg += parentNodeView.getY(); - - Point lineStartPoint = childView.getLineStartPoint(i); - lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = parentNodeView.getY(); - } - yAvg /= parentsViews.size(); - childView.setEndY(yAvg); - - childView.setDepth(depth); - - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - - dimension.width = Math.max(dimension.width, childView.getEndX()); - - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - } - break; - } - default: { - int span = 0; - for (TreeTransitionView childView : children) { - span += childView.getSpan(); - } - - span = (int) ((nodeView.getSpan() - span) / 2); - for (int i = 0; i < children.size(); i++) { - TreeTransitionView childView = children.get(i); - - childView.setDepth(depth); - - Point lineStartPoint = childView.getLineStartPoint(0); - lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; - lineStartPoint.y = yLoc; - childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); - childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); - - span += childView.getSpan(); - TreeNodeView childNodeView = childView.getChildView(); - if (childNodeView != null) { - calculateViewLocations(childNodeView, depth + 1); - } - } - break; - } - } - } - - public void calcSpan(TreeElementView view) { - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - calcSpan(childView); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - ArrayList newChildrenViews = new ArrayList<>(); - for (TreeTransition trans : node.getChildren()) { - newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); - } - nodeView.setChildrenViews(newChildrenViews); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - calcSpan(mergePointView); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - calcSpan(transView); - span += transView.getSpan(); - } - } - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view - * specified as stop. Stop tree puzzleElement is NOT included in the span calculation - * - * @param view - * @param stop - */ - private void subCalcSpan(TreeElementView view, TreeElementView stop) { - //safe-guard for infinite loop - if (view == stop) { - return; - } - - if (view.getType() == NODE) { - TreeNodeView nodeView = (TreeNodeView) view; - TreeNode node = nodeView.getTreeElement(); - if (nodeView.getChildrenViews().size() == 0) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - if (nodeView.getChildrenViews().size() == 1) { - TreeTransitionView childView = nodeView.getChildrenViews().get(0); - if (childView == stop) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - subCalcSpan(childView, stop); - if (childView.getParentViews().size() > 1) { - nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - nodeView.setSpan(childView.getSpan()); - } - } - } - else { - DisjointSets branches = node.findMergingBranches(); - List children = node.getChildren(); - - if (node == children.get(0).getParents().get(0)) { - reorderBranches(node, branches); - } - - List> mergingSets = branches.getAllSets(); - - double span = 0.0; - for (Set mergeSet : mergingSets) { - if (mergeSet.size() > 1) { - TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); - TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); - double subSpan = 0.0; - for (TreeTransition branch : mergeSet) { - TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); - subCalcSpan(branchView, mergePointView); - subSpan += branchView.getSpan(); - } - subCalcSpan(mergePointView, stop); - span += Math.max(mergePointView.getSpan(), subSpan); - } - else { - TreeTransition trans = mergeSet.iterator().next(); - TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); - subCalcSpan(transView, stop); - span += transView.getSpan(); - } - } - - nodeView.setSpan(span); - } - } - } - else { - TreeTransitionView transView = (TreeTransitionView) view; - TreeNodeView nodeView = transView.getChildView(); - if (nodeView == null || nodeView == stop) { - transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); - } - else { - calcSpan(nodeView); - transView.setSpan(nodeView.getSpan()); - } - } - } - - /** - * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in - * relative order in the list of child transitions of the specified node - * - * @param node root node of the branches - * @param branches DisjointSets of the child branches of the specified node which determine which branches merge - */ - private void reorderBranches(TreeNode node, DisjointSets branches) { - List children = node.getChildren(); - List> mergingSets = branches.getAllSets(); - - List> newOrder = new ArrayList<>(); - for (Set set : mergingSets) { - List mergeBranch = new ArrayList<>(); - newOrder.add(mergeBranch); - children.forEach(t -> { - if (set.contains(t)) { - mergeBranch.add(t); - } - }); - mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> - children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); - } - - newOrder.sort((List b1, List b2) -> { - int low1 = -1; - int low2 = -1; - for (TreeTransition t1 : b1) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - for (TreeTransition t1 : b2) { - int curIndex = children.indexOf(t1); - if (low1 == -1 || curIndex < low1) { - low1 = curIndex; - } - } - return low1 < low2 ? -1 : 1; - }); - - List newChildren = new ArrayList<>(); - newOrder.forEach(l -> newChildren.addAll(l)); - node.setChildren(newChildren); - } +package edu.rpi.legup.ui.proofeditorui.treeview; + +import edu.rpi.legup.app.GameBoardFacade; +import edu.rpi.legup.controller.TreeController; +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.GridCell; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.observer.ITreeListener; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.rules.Rule; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeElement; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.ui.ScrollView; +import edu.rpi.legup.utility.DisjointSets; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.util.*; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import static edu.rpi.legup.model.tree.TreeElementType.NODE; +import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER; +import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS; + +public class TreeView extends ScrollView implements ITreeListener { + private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); + + private static final int TRANS_GAP = 5; + + private static final int NODE_GAP_WIDTH = 70; + private static final int NODE_GAP_HEIGHT = 15; + + private static final int BORDER_GAP_HEIGHT = 20; + private static final int BORDER_GAP_WIDTH = 20; + + private static final int BORDER_SPACING = 100; + + private TreeNodeView nodeHover; + + private ArrayList currentStateBoxes; + private Rectangle bounds = new Rectangle(0, 0, 0, 0); + + private Tree tree; + private TreeNodeView rootNodeView; + private Map viewMap; + private Dimension dimension; + + private TreeViewSelection selection; + + public TreeView(TreeController treeController) { + super(treeController); + currentStateBoxes = new ArrayList<>(); + setSize(dimension = new Dimension(100, 200)); + setPreferredSize(new Dimension(640, 160)); + + viewMap = new HashMap<>(); + + selection = new TreeViewSelection(); + } + + public TreeViewSelection getSelection() { + return selection; + } + + /** + * Gets the tree node puzzleElement that the mouse is hovering over + * + * @return tree node puzzleElement that the mouse is hovering over + */ + public TreeNodeView getNodeHover() { + return nodeHover; + } + + /** + * Sets the tree node puzzleElement that the mouse is hovering over + * + * @param nodeHover tree node puzzleElement the mouse is hovering over + */ + public void setNodeHover(TreeNodeView nodeHover) { + this.nodeHover = nodeHover; + } + + /** + * Gets the TreeElementView by the specified point or null if no view exists at the specified point + * + * @param point location to query for a view + * @return TreeElementView at the point specified, otherwise null + */ + public TreeElementView getTreeElementView(Point point) { + return getTreeElementView(point, rootNodeView); + } + + /** + * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or + * the view specified is null + * + * @param point location to query for a view + * @param elementView view to determine if the point is contained within it + * @return TreeElementView at the point specified, otherwise null + */ + private TreeElementView getTreeElementView(Point point, TreeElementView elementView) { + if (elementView == null) { + return null; + } + else { + if (elementView.contains(point) && elementView.isVisible()) { + if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) { + return null; + } + return elementView; + } + else { + if (elementView.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) elementView; + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + TreeElementView view = getTreeElementView(point, transitionView); + if (view != null) { + return view; + } + } + } + else { + TreeTransitionView transitionView = (TreeTransitionView) elementView; + return getTreeElementView(point, transitionView.getChildView()); + } + } + } + return null; + } + + public void updateTreeView(Tree tree) { + this.tree = tree; + if (selection.getSelectedViews().size() == 0) { + selection.newSelection(new TreeNodeView(tree.getRootNode())); + } + repaint(); + } + + /** + * Sets the tree associated with this TreeView + * + * @param tree tree + */ + public void setTree(Tree tree) { + this.tree = tree; + } + + public void updateTreeSize() { + if (GameBoardFacade.getInstance().getTree() == null) { + return; + } + setSize(bounds.getSize()); + } + + public void reset() { + if (bounds.x != 0 || bounds.y != 0) { + updateTreeSize(); + } + } + + public void zoomFit() { + double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); + double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); + zoomTo(Math.min(fitWidth, fitHeight)); + viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); + } + + /** + * Creates a customized viewport for the scroll pane + * + * @return viewport for the scroll pane + */ + @Override + protected JViewport createViewport() { + return new JViewport() { + @Override + protected LayoutManager createLayoutManager() { + return new ViewportLayout() { + @Override + public void layoutContainer(Container parent) { + Point point = viewport.getViewPosition(); + // determine the maximum x and y view positions + int mx = getCanvas().getWidth() - viewport.getWidth(); + int my = getCanvas().getHeight() - viewport.getHeight(); + // obey edge boundaries + if (point.x < 0) { + point.x = 0; + } + if (point.x > mx) { + point.x = mx; + } + if (point.y < 0) { + point.y = 0; + } + if (point.y > my) { + point.y = my; + } + // center margins + if (mx < 0) { + point.x = 0; + } + if (my < 0) { + point.y = my / 2; + } + viewport.setViewPosition(point); + } + }; + } + }; + } + + public void draw(Graphics2D graphics2D) { + currentStateBoxes.clear(); + Tree tree = GameBoardFacade.getInstance().getTree(); + if (tree != null) { + //setSize(bounds.getDimension()); + graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + drawTree(graphics2D); + + dimension.width += BORDER_SPACING; + setSize(dimension); +// graphics2D.drawRect(0,0, dimension.width, dimension.height); + + if (selection.getHover() != null) { + drawMouseOver(graphics2D); + } + } + } + + public void zoomReset() { + zoomTo(1.0); + viewport.setViewPosition(new Point(0, 0)); + } + + private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { + if (nodeView != null) { + nodeView.draw(graphics2D); + for (TreeTransitionView transitionView : nodeView.getChildrenViews()) { + transitionView.draw(graphics2D); + redrawTree(graphics2D, transitionView.getChildView()); + } + } + } + + public void removeTreeElement(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + nodeView.getParentView().setChildView(null); + } + else { + TreeTransitionView transitionView = (TreeTransitionView) view; + transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView)); + } + } + + /** + * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image + * + * @param g the graphics to use to draw + */ + public void drawMouseOver(Graphics2D g) { + if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) { + TreeTransition transition = (TreeTransition) selection.getHover().treeElement; + int imgWidth = 100; + int imgHeight = 100; + + BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); + image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null); + Point mousePoint = selection.getMousePoint(); + g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null); + } + } + + public void resetView() { + this.tree = null; + this.rootNodeView = null; + this.selection.clearSelection(); + this.selection.clearHover(); + } + + /** + * Called when a tree puzzleElement is added to the tree + * + * @param treeElement TreeElement that was added to the tree + */ + @Override + public void onTreeElementAdded(TreeElement treeElement) { + if (treeElement.getType() == NODE) { + addTreeNode((TreeNode) treeElement); + } + else { + addTreeTransition((TreeTransition) treeElement); + } + repaint(); + } + + /** + * Called when a tree puzzleElement is removed from the tree + * + * @param element TreeElement that was removed to the tree + */ + @Override + public void onTreeElementRemoved(TreeElement element) { + if (element.getType() == NODE) { + TreeNode node = (TreeNode) element; + TreeNodeView nodeView = (TreeNodeView) viewMap.get(node); + + nodeView.getParentView().setChildView(null); + removeTreeNode(node); + } + else { + TreeTransition trans = (TreeTransition) element; + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + + // unlock ancestor elements if case rule deleted + Rule rule = trans.getRule(); + for (TreeNode node : trans.getParents()) { + + // only if the last case of a case rule will be deleted + if (!(rule instanceof CaseRule && node.getChildren().isEmpty())) { + continue; + } + + CaseRule caseRule = (CaseRule)rule; + // set dependent elements to be modifiable by ancestors (if not dependent on others) + List ancestors = node.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + + for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), trans.getSelection())) { + // decrement, unlock if 0 cases depended + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); + oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); + if (oldElement.getCasesDepended() != 0) { + continue; + } + + // set modifiable if started modifiable + boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); + + // unmodifiable if already modified + TreeNode modNode = ancestor.getParent().getParents().get(0); + while (modNode.getParent()!=null) { + Board modBoard = modNode.getParent().getBoard(); + if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { + modifiable = false; + break; + } + modNode = modNode.getParent().getParents().get(0); + } + oldElement.setModifiable(modifiable); + } + } + } + + transView.getParentViews().forEach(n -> n.removeChildrenView(transView)); + removeTreeTransition(trans); + } + repaint(); + } + + /** + * Called when the tree selection was changed + * + * @param selection tree selection that was changed + */ + @Override + public void onTreeSelectionChanged(TreeViewSelection selection) { + this.selection.getSelectedViews().forEach(v -> v.setSelected(false)); + selection.getSelectedViews().forEach(v -> v.setSelected(true)); + this.selection = selection; + repaint(); + } + + /** + * Called when the model has finished updating the tree. + */ + @Override + public void onUpdateTree() { + repaint(); + } + + /** + * Gets the TreeElementView by the corresponding TreeElement associated with it + * + * @param element TreeElement of the view + * @return TreeElementView of the TreeElement associated with it + */ + public TreeElementView getElementView(TreeElement element) { + return viewMap.get(element); + } + + private void removeTreeNode(TreeNode node) { + viewMap.remove(node); + List children = node.getChildren(); + + // if child is a case rule, unlock ancestor elements + if (!children.isEmpty()) { + Rule rule = children.get(0).getRule(); + if (rule instanceof CaseRule) { + CaseRule caseRule = (CaseRule)rule; + // set dependent elements to be modifiable by ancestors (if not dependent on others) + List ancestors = node.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + for (PuzzleElement pelement : caseRule.dependentElements(node.getBoard(), children.get(0).getSelection())) { + // decrement, unlock if 0 cases depended + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(pelement); + oldElement.setCasesDepended(oldElement.getCasesDepended() - 1); + if (oldElement.getCasesDepended() == 0) { + continue; + } + + // set modifiable if started modifiable + boolean modifiable = tree.getRootNode().getBoard().getPuzzleElement(oldElement).isModifiable(); + + // unmodifiable if already modified + TreeNode modNode = ancestor.getParent().getParents().get(0); + while (modNode.getParent() != null) { + Board modBoard = modNode.getParent().getBoard(); + if (modBoard.getModifiedData().contains(modBoard.getPuzzleElement(oldElement))) { + modifiable = false; + break; + } + modNode = modNode.getParent().getParents().get(0); + } + oldElement.setModifiable(modifiable); + } + } + } + } + node.getChildren().forEach(t -> removeTreeTransition(t)); + } + + private void removeTreeTransition(TreeTransition trans) { + viewMap.remove(trans); + if (trans.getChildNode() != null) { + removeTreeNode(trans.getChildNode()); + } + } + + private void addTreeNode(TreeNode node) { + TreeTransition parent = node.getParent(); + + TreeNodeView nodeView = new TreeNodeView(node); + TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); + + nodeView.setParentView(parentView); + parentView.setChildView(nodeView); + + viewMap.put(node, nodeView); + + if (!node.getChildren().isEmpty()) { + + // if adding a case rule, lock dependent ancestor elements + Rule rule = node.getChildren().get(0).getRule(); + if (rule instanceof CaseRule) { + CaseRule caseRule = (CaseRule)rule; + + List ancestors = node.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + for (PuzzleElement element : caseRule.dependentElements(node.getBoard(), node.getChildren().get(0).getSelection())) { + // increment and lock + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended()+1); + oldElement.setModifiable(false); + } + } + } + + node.getChildren().forEach(t -> addTreeTransition(t)); + } + } + + private void addTreeTransition(TreeTransition trans) { + List parents = trans.getParents(); + + TreeTransitionView transView = new TreeTransitionView(trans); + for (TreeNode parent : parents) { + TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); + transView.addParentView(parentNodeView); + parentNodeView.addChildrenView(transView); + + // if transition is a new case rule, lock dependent ancestor elements + Rule rule = trans.getRule(); + if (rule instanceof CaseRule && parent.getChildren().size()==1) { + CaseRule caseRule = (CaseRule)rule; + + List ancestors = parent.getAncestors(); + for (TreeNode ancestor : ancestors) { + // for all ancestors but root + if (ancestor.getParent() == null) { + continue; + } + for (PuzzleElement element : caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { + // increment and lock + PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended()+1); + oldElement.setModifiable(false); + } + } + } + } + + viewMap.put(trans, transView); + + if (trans.getChildNode() != null) { + addTreeNode(trans.getChildNode()); + } + } + + ///New Draw Methods + + public void drawTree(Graphics2D graphics2D) { + if (tree == null) { + LOGGER.error("Unable to draw tree."); + } + else { + if (rootNodeView == null) { + rootNodeView = new TreeNodeView(tree.getRootNode()); + + LOGGER.debug("Creating new views for tree view."); + createViews(rootNodeView); + + selection.newSelection(rootNodeView); + } + + dimension = new Dimension(0, 0); + calcSpan(rootNodeView); + rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING); + + calculateViewLocations(rootNodeView, 0); + dimension.height = (int) rootNodeView.getSpan(); + + redrawTree(graphics2D, rootNodeView); + LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height); + } + } + + public void createViews(TreeNodeView nodeView) { + if (nodeView != null) { + viewMap.put(nodeView.getTreeElement(), nodeView); + + TreeNode node = nodeView.getTreeElement(); + for (TreeTransition trans : node.getChildren()) { + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + if (transView != null) { + nodeView.addChildrenView(transView); + transView.addParentView(nodeView); + break; + } + transView = new TreeTransitionView(trans); + + viewMap.put(transView.getTreeElement(), transView); + + transView.addParentView(nodeView); + nodeView.addChildrenView(transView); + + TreeNode childNode = trans.getChildNode(); + if (childNode != null) { + TreeNodeView childNodeView = new TreeNodeView(childNode); + viewMap.put(childNodeView.getTreeElement(), childNodeView); + + childNodeView.setParentView(transView); + transView.setChildView(childNodeView); + + createViews(childNodeView); + } + } + } + } + + public void calculateViewLocations(TreeNodeView nodeView, int depth) { + nodeView.setDepth(depth); + int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; + nodeView.setX(xLoc); + dimension.width = Math.max(dimension.width, xLoc); + + TreeTransitionView parentTransView = nodeView.getParentView(); + int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY(); + nodeView.setY(yLoc); + + ArrayList children = nodeView.getChildrenViews(); + switch (children.size()) { + case 0: + break; + case 1: { + TreeTransitionView childView = children.get(0); + + List parentsViews = childView.getParentViews(); + if (parentsViews.size() == 1) { + childView.setEndY(yLoc); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + else { + if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) { + int yAvg = 0; + for (int i = 0; i < parentsViews.size(); i++) { + TreeNodeView parentNodeView = parentsViews.get(i); + depth = Math.max(depth, parentNodeView.getDepth()); + yAvg += parentNodeView.getY(); + + Point lineStartPoint = childView.getLineStartPoint(i); + lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = parentNodeView.getY(); + } + yAvg /= parentsViews.size(); + childView.setEndY(yAvg); + + childView.setDepth(depth); + + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + + dimension.width = Math.max(dimension.width, childView.getEndX()); + + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + } + break; + } + default: { + int span = 0; + for (TreeTransitionView childView : children) { + span += childView.getSpan(); + } + + span = (int) ((nodeView.getSpan() - span) / 2); + for (int i = 0; i < children.size(); i++) { + TreeTransitionView childView = children.get(i); + + childView.setDepth(depth); + + Point lineStartPoint = childView.getLineStartPoint(0); + lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2; + lineStartPoint.y = yLoc; + childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2); + childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2)); + + span += childView.getSpan(); + TreeNodeView childNodeView = childView.getChildView(); + if (childNodeView != null) { + calculateViewLocations(childNodeView, depth + 1); + } + } + break; + } + } + } + + public void calcSpan(TreeElementView view) { + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + calcSpan(childView); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + ArrayList newChildrenViews = new ArrayList<>(); + for (TreeTransition trans : node.getChildren()) { + newChildrenViews.add((TreeTransitionView) viewMap.get(trans)); + } + nodeView.setChildrenViews(newChildrenViews); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + calcSpan(mergePointView); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + calcSpan(transView); + span += transView.getSpan(); + } + } + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view + * specified as stop. Stop tree puzzleElement is NOT included in the span calculation + * + * @param view + * @param stop + */ + private void subCalcSpan(TreeElementView view, TreeElementView stop) { + //safe-guard for infinite loop + if (view == stop) { + return; + } + + if (view.getType() == NODE) { + TreeNodeView nodeView = (TreeNodeView) view; + TreeNode node = nodeView.getTreeElement(); + if (nodeView.getChildrenViews().size() == 0) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + if (nodeView.getChildrenViews().size() == 1) { + TreeTransitionView childView = nodeView.getChildrenViews().get(0); + if (childView == stop) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + subCalcSpan(childView, stop); + if (childView.getParentViews().size() > 1) { + nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + nodeView.setSpan(childView.getSpan()); + } + } + } + else { + DisjointSets branches = node.findMergingBranches(); + List children = node.getChildren(); + + if (node == children.get(0).getParents().get(0)) { + reorderBranches(node, branches); + } + + List> mergingSets = branches.getAllSets(); + + double span = 0.0; + for (Set mergeSet : mergingSets) { + if (mergeSet.size() > 1) { + TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet); + TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint); + double subSpan = 0.0; + for (TreeTransition branch : mergeSet) { + TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch); + subCalcSpan(branchView, mergePointView); + subSpan += branchView.getSpan(); + } + subCalcSpan(mergePointView, stop); + span += Math.max(mergePointView.getSpan(), subSpan); + } + else { + TreeTransition trans = mergeSet.iterator().next(); + TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans); + subCalcSpan(transView, stop); + span += transView.getSpan(); + } + } + + nodeView.setSpan(span); + } + } + } + else { + TreeTransitionView transView = (TreeTransitionView) view; + TreeNodeView nodeView = transView.getChildView(); + if (nodeView == null || nodeView == stop) { + transView.setSpan(DIAMETER + NODE_GAP_HEIGHT); + } + else { + calcSpan(nodeView); + transView.setSpan(nodeView.getSpan()); + } + } + } + + /** + * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in + * relative order in the list of child transitions of the specified node + * + * @param node root node of the branches + * @param branches DisjointSets of the child branches of the specified node which determine which branches merge + */ + private void reorderBranches(TreeNode node, DisjointSets branches) { + List children = node.getChildren(); + List> mergingSets = branches.getAllSets(); + + List> newOrder = new ArrayList<>(); + for (Set set : mergingSets) { + List mergeBranch = new ArrayList<>(); + newOrder.add(mergeBranch); + children.forEach(t -> { + if (set.contains(t)) { + mergeBranch.add(t); + } + }); + mergeBranch.sort((TreeTransition t1, TreeTransition t2) -> + children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1); + } + + newOrder.sort((List b1, List b2) -> { + int low1 = -1; + int low2 = -1; + for (TreeTransition t1 : b1) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + for (TreeTransition t1 : b2) { + int curIndex = children.indexOf(t1); + if (low1 == -1 || curIndex < low1) { + low1 = curIndex; + } + } + return low1 < low2 ? -1 : 1; + }); + + List newChildren = new ArrayList<>(); + newOrder.forEach(l -> newChildren.addAll(l)); + node.setChildren(newChildren); + } } \ No newline at end of file From 244869d9658abfa2a935cd41f8013b199a9937ad Mon Sep 17 00:00:00 2001 From: Charles Tian <46334090+charlestian23@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:26:05 -0500 Subject: [PATCH 005/359] Update README.md Updating description about LEGUP web version. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4f99d75c..f32928efb 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ LEGUP (**L**ogic **E**ngine for **G**rid-**U**sing **P**uzzles) is a better way to learn formal logic. It was created by [Dr. Bram van Heuveln](https://science.rpi.edu/itws/faculty/bram-van-heuveln), whose goal for this project is to provide a better interface for students to learn the basic principles of logical reasoning. -> Note: A web version of LEGUP ([Bram-Hub/LegupWeb](https://github.com/Bram-Hub/LegupWeb)) based on this app version of LEGUP was being developed. It is very much in the early stages of development and will not be ready for general use for quite a while. Development on this web version has halted for a while, as no one has been actively working on the project. Contributions to both versions of LEGUP are greatly appreciated. However, if you are interested in using LEGUP for educational purposes, please use this app version. +> Note: A web version of LEGUP ([Bram-Hub/LegupWeb](https://github.com/Bram-Hub/LegupWeb)) was previously under development, but it has been halted due to no one actively working on the project. Contributions to both versions of LEGUP are greatly appreciated. If you are interested in using LEGUP for educational purposes, please use this app version. ## Table of Contents - [Background](#background) From 1328083eedf257c0972fc3d35ced9f5cc802a4e7 Mon Sep 17 00:00:00 2001 From: zachbonagura Date: Tue, 30 Jan 2024 16:27:15 -0500 Subject: [PATCH 006/359] Test commit --- src/main/java/edu/rpi/legup/puzzle/binary/test.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/test.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/test.java b/src/main/java/edu/rpi/legup/puzzle/binary/test.java new file mode 100644 index 000000000..e69de29bb From 3a5ce8ece1bff3d06ae8646cb8f58c1c8f25be25 Mon Sep 17 00:00:00 2001 From: zachbonagura Date: Tue, 30 Jan 2024 16:30:07 -0500 Subject: [PATCH 007/359] Test commit again --- src/main/java/edu/rpi/legup/puzzle/binary/test.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/test.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/test.java b/src/main/java/edu/rpi/legup/puzzle/binary/test.java deleted file mode 100644 index e69de29bb..000000000 From 4a85b7f90e450491aa8a22cbdf9a9ff78ad3b867 Mon Sep 17 00:00:00 2001 From: zachbonagura Date: Tue, 30 Jan 2024 17:05:29 -0500 Subject: [PATCH 008/359] Implementing the constructor and some abstract methods --- .../edu/rpi/legup/puzzle/binary/Binary.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/Binary.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java new file mode 100644 index 000000000..c49d90af6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -0,0 +1,28 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class Binary extends Puzzle { + public Binary() { + super(); + } + + @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) { + } +} \ No newline at end of file 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 009/359] 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 f24c41fd60ddd8bf3aa1d5b0cac6cb37fcb74b52 Mon Sep 17 00:00:00 2001 From: zachbonagura Date: Thu, 1 Feb 2024 18:52:12 -0500 Subject: [PATCH 010/359] Implementing the BinaryType file for the grid cell possibilities --- .../java/edu/rpi/legup/puzzle/binary/BinaryType.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java new file mode 100644 index 000000000..7fa857b30 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.binary; + +public enum BinaryType { + UNKNOWN, ZERO, ONE, NUMBER; + + public int toValue() { + return this.ordinal() - 2; + } +} From 9d33a1c98b2f9fbf8bf80003c9692eec27da96bc Mon Sep 17 00:00:00 2001 From: Brandon McCusker Date: Fri, 2 Feb 2024 17:16:00 -0500 Subject: [PATCH 011/359] initialized puzzle files, began writing the puzzle importer and finished most of binary.java functionality --- .../edu/rpi/legup/puzzle/binary/Binary.java | 33 +++++ .../rpi/legup/puzzle/binary/BinaryBoard.java | 19 +++ .../rpi/legup/puzzle/binary/BinaryCell.java | 36 ++++++ .../legup/puzzle/binary/BinaryExporter.java | 1 + .../legup/puzzle/binary/BinaryImporter.java | 113 ++++++++++++++++++ .../puzzle/binary/elements/BlankTile.java | 1 + .../puzzle/binary/elements/NumberTile.java | 1 + 7 files changed, 204 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java index c49d90af6..a7d14c375 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -6,10 +6,19 @@ public class Binary extends Puzzle { public Binary() { super(); + + this.name = "Binary"; + this.importer = new BinaryImporter(this); + this.exporter = new BinaryExporter(this); + + this.factory = new BinaryCellFactory(this); } @Override public void initializeView() { + boardView = new BinaryView((BinaryBoard) currentBoard); + boardView.setBoard(currentBoard); + addBoardListner(boardView); } @Override @@ -25,4 +34,28 @@ public boolean isBoardComplete(Board board) { @Override public void onBoardChange(Board board) { } + + + @Overrride + public boolean isValidDimensions(int rows, int columns){ + return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; + } + + Override + public boolean isBoardComplete(Board board) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + for (ContradictionRule rule : contradictionRules) { + if (rule.checkContradiction(binaryBoard) == null) { + return false; + } + } + for (PuzzleElement data : binaryBoard.getPuzzleElements()) { + BinaryCell cell = (BinaryCell) data; + if (cell.getType() == BinaryType.UNKNOWN) { + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java new file mode 100644 index 000000000..7707aa068 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java @@ -0,0 +1,19 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +public class BinaryBoard extends GridBoard { + public BinaryBoard(int width, int height) { + super(width, height); + } + + public BinaryBoard(int size) { + super(size, size); + } + + @Override + public BinaryCell getCell(int x, int y) { + return (BinaryCell) super.getCell(x, y); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java new file mode 100644 index 000000000..65464b372 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java @@ -0,0 +1,36 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.Point; + +public class BinaryCell extends GridCell { + public BinaryCell(int valueInt, Point location) { + super(valueInt, location); + } + + public BinaryType getType() { + switch (data) { + case -2: + return BinaryType.UNKNOWN; + case -1: + return BinaryType.ONE; + case 0: + return BinaryType.ZERO; + default: + if (data > 0) { + return BinaryType.NUMBER; + } + } + return null; + } + + @Override + public BinaryCell copy() { + BinaryCell copy = new BinaryCell(data, (Point) location.clone()); + 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/binary/BinaryExporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java new file mode 100644 index 000000000..250267cc6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java @@ -0,0 +1 @@ +BinaryExporter.java \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java new file mode 100644 index 000000000..85e957d43 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java @@ -0,0 +1,113 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.PuzzleImporter; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.awt.*; + +public class BinaryImporter extends PuzzleImporter { + public BinaryImporter(Binary binary) { + super(Binary); + } + + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + + @Override + public void initializeBoard(int rows, int columns) { + BinaryBoard binaryBoard = new BinaryBoard(columns, rows); + + for (int y = 0; y < rows; y++) { + for (int x = 0; x < columns; x++) { + BinaryCell cell = new BinaryCell(BinaryType.UNKNOWN.toValue(), new Point(x, y)); + cell.setIndex(y * columns + x); + cell.setModifiable(true); + binaryBoard.setCell(x, y, cell); + } + } + puzzle.setCurrentBoard(binaryBoard); + } + + /** + * Creates the board for building + * + * @param node xml document node + * @throws InvalidFileFormatException if file is invalid + */ + @Override + public void initializeBoard(Node node) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("board")) { + throw new InvalidFileFormatException("binary Importer: cannot find board puzzleElement"); + } + Element boardElement = (Element) node; + if (boardElement.getElementsByTagName("cells").getLength() == 0) { + throw new InvalidFileFormatException("binary Importer: no puzzleElement found for board"); + } + + Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("cell"); + + BinaryBoard binaryBoard = null; + if (!boardElement.getAttribute("size").isEmpty()) { + int size = Integer.valueOf(boardElement.getAttribute("size")); + binaryBoard = new BinaryBoard(size); + } + else { + if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + int width = Integer.valueOf(boardElement.getAttribute("width")); + int height = Integer.valueOf(boardElement.getAttribute("height")); + binaryBoard = new BinaryBoard(width, height); + } + } + + int width = nurikabeBoard.getWidth(); + int height = nurikabeBoard.getHeight(); + + if (binaryBoard == null || width % 2 != 0 || height % 2 != 0) { + throw new InvalidFileFormatException("binary Importer: invalid board dimensions"); + } + + + for (int i = 0; i < elementDataList.getLength(); i++) { + BinaryCell cell = (BinaryCell) puzzle.getFactory().importCell(elementDataList.item(i), binaryBoard); + Point loc = cell.getLocation(); + if (cell.getData() != BinaryType.UNKNOWN.toValue()) { + cell.setModifiable(false); + cell.setGiven(true); + } + binaryBoard.setCell(loc.x, loc.y, cell); + } + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (binaryBoard.getCell(x, y) == null) { + BinaryCell cell = new BinaryCell(BinaryType.UNKNOWN.toValue(), new Point(x, y)); + cell.setIndex(y * height + x); + cell.setModifiable(true); + binaryBoard.setCell(x, y, cell); + } + } + } + puzzle.setCurrentBoard(binaryBoard); + } + catch (NumberFormatException e) { + throw new InvalidFileFormatException("binary Importer: unknown value where integer expected"); + } + } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Binary cannot accept text input"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java new file mode 100644 index 000000000..ab1895ce5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java @@ -0,0 +1 @@ +BlankTile.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java new file mode 100644 index 000000000..38bc619d6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java @@ -0,0 +1 @@ +NumberTile.java \ No newline at end of file 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 012/359] 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 ae2d93ca6911cd7088535f622c2d346937611b83 Mon Sep 17 00:00:00 2001 From: zachbonagura Date: Thu, 1 Feb 2024 23:13:38 -0500 Subject: [PATCH 013/359] Implemented the BinaryCell class with the constructor, and worked on BinaryType getType() function and BinaryCell copy() function --- .../rpi/legup/puzzle/binary/BinaryCell.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java new file mode 100644 index 000000000..56b37c03e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java @@ -0,0 +1,36 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.Point; + +public class BinaryCell extends GridCell { + public BinaryCell(int valueInt, Point location) { + super(valueInt, location); + } + + public BinaryType getType() { + switch (data) { + case -2: + return BinaryType.UNKNOWN; + case -1: + return BinaryType.ZERO; + case 0: + return BinaryType.ONE; + default: + if (data > 0) { + return BinaryType.NUMBER; + } + } + return null; + } + + @Override + public BinaryCell copy() { + BinaryCell copy = new BinaryCell(data, (Point) location.clone()); + copy.setIndex(index); + copy.setModifiable(isModifiable); + copy.setGiven(isGiven); + return copy; + } +} \ No newline at end of file From 923b93a33e7f7aaa34d292f0a773f90b058a7059 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Tue, 6 Feb 2024 16:52:53 -0500 Subject: [PATCH 014/359] Finished setting up BinaryImporter completely with all helper files (BinaryView, BinaryCellFactory, BinaryController, BinaryElementView). These files create the grid view of the puzzle and are responsible for the GUI of the puzzle. --- .../edu/rpi/legup/puzzle/binary/Binary.java | 2 +- .../puzzle/binary/BinaryCellFactory.java | 58 +++++++++++++++ .../legup/puzzle/binary/BinaryController.java | 47 ++++++++++++ .../puzzle/binary/BinaryElementView.java | 72 +++++++++++++++++++ .../legup/puzzle/binary/BinaryExporter.java | 1 - .../legup/puzzle/binary/BinaryImporter.java | 6 +- .../rpi/legup/puzzle/binary/BinaryView.java | 24 +++++++ .../ThreeAdjacentZerosContradictionRule.java | 20 ++++++ 8 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java index a7d14c375..06428ea18 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -41,7 +41,7 @@ public boolean isValidDimensions(int rows, int columns){ return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; } - Override + @Override public boolean isBoardComplete(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java new file mode 100644 index 000000000..729cae171 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java @@ -0,0 +1,58 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.ElementFactory; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.awt.*; + +public class BinaryCellFactory extends ElementFactory { + public BinaryCell importCell(Node node, Board board) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("cell")) { + throw new InvalidFileFormatException("nurikabe Factory: unknown puzzleElement puzzleElement"); + } + + BinaryBoard binaryBoard = (BinaryBoard) board; + int width = binaryBoard.getWidth(); + int height = binaryBoard.getHeight(); + + NamedNodeMap attributeList = node.getAttributes(); + 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("binary Factory: cell location out of bounds"); + } + if (value < -2) { + throw new InvalidFileFormatException("binary Factory: cell unknown value"); + } + + BinaryCell cell = new BinaryCell(value, new Point(x, y)); + cell.setIndex(y * height + x); + return cell; + } catch (NumberFormatException e) { + throw new InvalidFileFormatException("binary Factory: unknown value where integer expected"); + } catch (NullPointerException e) { + throw new InvalidFileFormatException("binary Factory: could not find attribute(s)"); + } + } + + public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { + org.w3c.dom.Element cellElement = document.createElement("cell"); + + BinaryCell cell = (BinaryCell) 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)); + + return cellElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java new file mode 100644 index 000000000..9189f7ebe --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java @@ -0,0 +1,47 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.event.MouseEvent; + +public class BinaryController extends ElementController { + + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + BinaryCell cell = (BinaryCell) 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() == -2) { + data.setData(0); + } + else { + if (cell.getData() == 0) { + data.setData(-1); + } + else { + data.setData(-2); + } + } + } + } + else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == -2) { + data.setData(-1); + } + else { + if (cell.getData() == 0) { + data.setData(-2); + } + else { + data.setData(0); + } + } + } + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java new file mode 100644 index 000000000..ca37de73f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java @@ -0,0 +1,72 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.ui.boardview.GridElementView; + +import java.awt.*; + +public class BinaryElementView extends GridElementView { + + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public BinaryElementView(BinaryCell cell) { + super(cell); + } + + /** + * Gets the PuzzleElement associated with this view + * + * @return PuzzleElement associated with this view + */ + @Override + public BinaryCell getPuzzleElement() { + return (BinaryCell) super.getPuzzleElement(); + } + + @Override + public void drawElement(Graphics2D graphics2D) { + BinaryCell cell = (BinaryCell) puzzleElement; + BinaryType type = cell.getType(); + if (type == BinaryType.NUMBER) { + 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); + + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + 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(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + } + else { + if (type == BinaryType.ZERO) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.BLACK); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + } + else { + if (type == BinaryType.ONE) { + 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 { + if (type == BinaryType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(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); + } + } + } + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java deleted file mode 100644 index 250267cc6..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java +++ /dev/null @@ -1 +0,0 @@ -BinaryExporter.java \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java index 85e957d43..c6839e36c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java @@ -10,7 +10,7 @@ public class BinaryImporter extends PuzzleImporter { public BinaryImporter(Binary binary) { - super(Binary); + super(binary); } @Override @@ -71,8 +71,8 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } - int width = nurikabeBoard.getWidth(); - int height = nurikabeBoard.getHeight(); + int width = binaryBoard.getWidth(); + int height = binaryBoard.getHeight(); if (binaryBoard == null || width % 2 != 0 || height % 2 != 0) { throw new InvalidFileFormatException("binary Importer: invalid board dimensions"); diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java new file mode 100644 index 000000000..9e678b003 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java @@ -0,0 +1,24 @@ +package edu.rpi.legup.puzzle.binary; + +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 BinaryView extends GridBoardView { + + public BinaryView(BinaryBoard board) { + super(new BoardController(), new BinaryController(), board.getDimension()); + + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + BinaryCell cell = (BinaryCell) puzzleElement; + Point loc = cell.getLocation(); + BinaryElementView elementView = new BinaryElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + 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/binary/rules/ThreeAdjacentZerosContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java new file mode 100644 index 000000000..cf1b6c2b9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java @@ -0,0 +1,20 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +import java.util.Set; +public class ThreeAdjacentZerosContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + + public ThreeAdjacentZerosContradictionRule() { + super("","Three Adjacent Zeros", "There cannot be three adjacent zeros in a row or column", ""); + } + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + return null; + } +} From 5c301f77610c4d6176546d4446eeaf9f27410570 Mon Sep 17 00:00:00 2001 From: Brandon McCusker Date: Tue, 6 Feb 2024 17:03:48 -0500 Subject: [PATCH 015/359] Fixed bugs in binary files, wrote binary exporter --- gradlew | 0 puzzles files/binary/6x6 easy/123456789.xml | 24 +++++++++++ .../edu/rpi/legup/puzzle/binary/Binary.java | 2 +- .../legup/puzzle/binary/BinaryExporter.java | 40 ++++++++++++++++++- .../legup/puzzle/binary/BinaryImporter.java | 6 +-- .../puzzle/binary/elements/BlankTile.java | 2 +- .../puzzle/binary/elements/NumberTile.java | 1 - 7 files changed, 68 insertions(+), 7 deletions(-) mode change 100644 => 100755 gradlew create mode 100644 puzzles files/binary/6x6 easy/123456789.xml diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/puzzles files/binary/6x6 easy/123456789.xml b/puzzles files/binary/6x6 easy/123456789.xml new file mode 100644 index 000000000..18a8cbab3 --- /dev/null +++ b/puzzles files/binary/6x6 easy/123456789.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java index a7d14c375..06428ea18 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -41,7 +41,7 @@ public boolean isValidDimensions(int rows, int columns){ return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; } - Override + @Override public boolean isBoardComplete(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java index 250267cc6..46df98a47 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java @@ -1 +1,39 @@ -BinaryExporter.java \ No newline at end of file +package edu.rpi.legup.puzzle.nurikabe; + +import edu.rpi.legup.model.PuzzleExporter; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import org.w3c.dom.Document; + +public class BinaryExporter extends PuzzleExporter { + + public BinaryExporter(Binary binary) { + super(binary); + } + + @Override + protected org.w3c.dom.Element createBoardElement(Document newDocument) { + BinaryBoard board; + if (puzzle.getTree() != null) { + board = (BinaryBoard) puzzle.getTree().getRootNode().getBoard(); + } + else { + board = (BinaryBoard) puzzle.getBoardView().getBoard(); + } + + org.w3c.dom.Element boardElement = newDocument.createElement("board"); + boardElement.setAttribute("width", String.valueOf(board.getWidth())); + boardElement.setAttribute("height", String.valueOf(board.getHeight())); + + org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + BinaryCell cell = (BinaryCell) puzzleElement; + if (cell.getData() != -2) { + org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + cellsElement.appendChild(cellElement); + } + } + + boardElement.appendChild(cellsElement); + return boardElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java index 85e957d43..c6839e36c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java @@ -10,7 +10,7 @@ public class BinaryImporter extends PuzzleImporter { public BinaryImporter(Binary binary) { - super(Binary); + super(binary); } @Override @@ -71,8 +71,8 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } - int width = nurikabeBoard.getWidth(); - int height = nurikabeBoard.getHeight(); + int width = binaryBoard.getWidth(); + int height = binaryBoard.getHeight(); if (binaryBoard == null || width % 2 != 0 || height % 2 != 0) { throw new InvalidFileFormatException("binary Importer: invalid board dimensions"); diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java index ab1895ce5..8b1378917 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java @@ -1 +1 @@ -BlankTile.java + diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java index 38bc619d6..e69de29bb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java @@ -1 +0,0 @@ -NumberTile.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 016/359] 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 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 QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index 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 a62c000f9d98ef23cb12d83c0e5fe0468180a5dd Mon Sep 17 00:00:00 2001 From: Brandon McCusker Date: Fri, 9 Feb 2024 16:23:55 -0500 Subject: [PATCH 017/359] Fixed compile errors --- puzzles files/binary/6x6 easy/123456789.xml | 24 ------------- puzzles files/binary/6x6 easy/876868768.xml | 24 +++++++++++++ .../edu/rpi/legup/puzzle/binary/Binary.java | 35 +++++++++---------- .../legup/puzzle/binary/BinaryExporter.java | 2 +- 4 files changed, 42 insertions(+), 43 deletions(-) create mode 100644 puzzles files/binary/6x6 easy/876868768.xml diff --git a/puzzles files/binary/6x6 easy/123456789.xml b/puzzles files/binary/6x6 easy/123456789.xml index 18a8cbab3..e69de29bb 100644 --- a/puzzles files/binary/6x6 easy/123456789.xml +++ b/puzzles files/binary/6x6 easy/123456789.xml @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/binary/6x6 easy/876868768.xml b/puzzles files/binary/6x6 easy/876868768.xml new file mode 100644 index 000000000..18a8cbab3 --- /dev/null +++ b/puzzles files/binary/6x6 easy/876868768.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java index 06428ea18..7a48ee190 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -2,6 +2,8 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; public class Binary extends Puzzle { public Binary() { @@ -11,14 +13,14 @@ public Binary() { this.importer = new BinaryImporter(this); this.exporter = new BinaryExporter(this); - this.factory = new BinaryCellFactory(this); + this.factory = new BinaryCellFactory(); } @Override public void initializeView() { boardView = new BinaryView((BinaryBoard) currentBoard); boardView.setBoard(currentBoard); - addBoardListner(boardView); + addBoardListener(boardView); } @Override @@ -26,22 +28,7 @@ public Board generatePuzzle(int difficulty) { return null; } - @Override - public boolean isBoardComplete(Board board) { - return true; - } - - @Override - public void onBoardChange(Board board) { - } - - - @Overrride - public boolean isValidDimensions(int rows, int columns){ - return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; - } - - @Override + @Override public boolean isBoardComplete(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board; @@ -58,4 +45,16 @@ public boolean isBoardComplete(Board board) { } return true; } + + @Override + public void onBoardChange(Board board) { + } + + + @Override + public boolean isValidDimensions(int rows, int columns){ + return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; + } + + } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java index 46df98a47..2f79d61b2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java @@ -1,4 +1,4 @@ -package edu.rpi.legup.puzzle.nurikabe; +package edu.rpi.legup.puzzle.binary; import edu.rpi.legup.model.PuzzleExporter; import edu.rpi.legup.model.gameboard.PuzzleElement; 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 018/359] 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 49b00580400cb36f24768706c482a1b8b253ab90 Mon Sep 17 00:00:00 2001 From: Brandon McCusker Date: Fri, 9 Feb 2024 18:22:49 -0500 Subject: [PATCH 019/359] Added binary puzzle to config, began writing rules --- bin/main/edu/rpi/legup/legup/config | 4 ++++ puzzles files/binary/6x6 easy/123456789.xml | 0 puzzles files/binary/6x6 easy/876868768.xml | 2 +- .../puzzle/binary/BinaryCellFactory.java | 1 + .../rules/CompleteRowColumnDirectRule.java | 21 +++++++++++++++++++ .../binary/rules/OneTileGapDirectRule.java | 21 +++++++++++++++++++ .../binary/rules/OneorZeroCaseRule.java | 20 ++++++++++++++++++ .../binary/rules/SurroundPairDirectRule.java | 21 +++++++++++++++++++ .../ThreeAdjacentZerosContradictionRule.java | 20 ------------------ .../rules/ThreeInARowContradictionRule.java | 20 ++++++++++++++++++ .../UnbalancedRowColumnContractionRule.java | 20 ++++++++++++++++++ .../binary/rules/binary_reference_sheet.txt | 8 +++++++ 12 files changed, 137 insertions(+), 21 deletions(-) delete mode 100644 puzzles files/binary/6x6 easy/123456789.xml create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index 19e63a2a3..b30cb8525 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -39,5 +39,9 @@ qualifiedClassName="edu.rpi.legup.puzzle.skyscrapers.Skyscrapers" fileType=".xml" fileCreationDisabled="true"/> + diff --git a/puzzles files/binary/6x6 easy/123456789.xml b/puzzles files/binary/6x6 easy/123456789.xml deleted file mode 100644 index e69de29bb..000000000 diff --git a/puzzles files/binary/6x6 easy/876868768.xml b/puzzles files/binary/6x6 easy/876868768.xml index 18a8cbab3..ceefaefff 100644 --- a/puzzles files/binary/6x6 easy/876868768.xml +++ b/puzzles files/binary/6x6 easy/876868768.xml @@ -1,7 +1,7 @@ - + diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java index 729cae171..d56e26e7c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java @@ -11,6 +11,7 @@ import java.awt.*; public class BinaryCellFactory extends ElementFactory { + public BinaryCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java new file mode 100644 index 000000000..bab6e7223 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java @@ -0,0 +1,21 @@ +package edu.rpi.legup.puzzle.binary.rules; + +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.DirectRule; +import edu.rpi.legup.model.ContradictionRule; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +public class CompleteRowColumnDirectRule extends DirectRule { + + public CompleteRowColumnDirectRule() { + super("BINA-BASC-0003", + "Complete Row Column" + "If a row/column of length n contains n/2 of a single value, the remaining cells must contain the other value", + "FILL IN WITH IMAGE"); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java new file mode 100644 index 000000000..8a8a1f53e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java @@ -0,0 +1,21 @@ +package edu.rpi.legup.puzzle.binary.rules; + +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.DirectRule; +import edu.rpi.legup.model.ContradictionRule; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +public class OneTileGapDirectRule extends DirectRule { + + public OneTileGapDirectRule() { + super("BINA-BASC-0002", + "One Tile Gap", + "If an empty tile is surrounded by the same value, fill the gap with the other value.", + "FILL IN WITH IMAGE"); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java new file mode 100644 index 000000000..63f882f83 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java @@ -0,0 +1,20 @@ +package edu.rpi.legup.puzzle.binary.rules; + +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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +public class OneOrZeroCaseRule extends CaseRule { + + public OneOrZeroCaseRule() { + super("BINA-CASE-0001", + "One or Zero", + "Each blank cell is either a one or a zero.", + "FILL IN WITH IMAGE"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java new file mode 100644 index 000000000..44bd5beee --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java @@ -0,0 +1,21 @@ +package edu.rpi.legup.puzzle.binary.rules; + +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.DirectRule; +import edu.rpi.legup.model.ContradictionRule; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +public class SurroundPairDirectRule extends DirectRule { + + public SurroundPairDirectRule() { + super("BINA-BASC-0001", + "Surround Pair", + "If two adjacent tiles have the same value, surround the tiles with the other value.", + "FILL IN WITH IMAGE"); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java deleted file mode 100644 index cf1b6c2b9..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java +++ /dev/null @@ -1,20 +0,0 @@ -package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; -import edu.rpi.legup.puzzle.binary.BinaryType; - -import java.util.Set; -public class ThreeAdjacentZerosContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; - - public ThreeAdjacentZerosContradictionRule() { - super("","Three Adjacent Zeros", "There cannot be three adjacent zeros in a row or column", ""); - } - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - return null; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java new file mode 100644 index 000000000..4965ce24a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java @@ -0,0 +1,20 @@ +package edu.rpi.legup.puzzle.binary.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.nurikabe.BinaryBoard; +import edu.rpi.legup.puzzle.nurikabe.BinaryCell; +import edu.rpi.legup.puzzle.nurikabe.BinaryType; + +public class ThreeInARowContradictionRule extends ContradictionRule { + + private final String NO_CONTRADICTION_MESSAGE = ""; + private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; + + public ThreeInARowContradictionRule() { + super("BINA-CONT-0001", + "Three In A Row", + "Three of the same value cannot exist in a row", + "FILL IN WITH IMAGE"); + } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java new file mode 100644 index 000000000..f86377004 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java @@ -0,0 +1,20 @@ +package edu.rpi.legup.puzzle.binary.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.nurikabe.BinaryBoard; +import edu.rpi.legup.puzzle.nurikabe.BinaryCell; +import edu.rpi.legup.puzzle.nurikabe.BinaryType; + +public class UnbalancedRowColumnContradictionRule extends ContradictionRule { + + private final String NO_CONTRADICTION_MESSAGE = ""; + private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; + + public UnbalancedRowColumnContradictionRule() { + super("BINA-CONT-0002", + "Unbalanced Row Column", + "A row or column cannot contain more of more value than the other", + "FILL IN WITH IMAGE"); + } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt new file mode 100644 index 000000000..bab194b2f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt @@ -0,0 +1,8 @@ +BINA-BASC-0001 : SurroundPairDirectRule +BINA-BASC-0002 : OneTileGapDirectRule +BINA-BASC-0003 : CompleteRowCloumnDirectRule + +BINA-CONT-0001 : ThreeInARowContradictionRule +BINA-CONT-0002 : UnbalancedRowColumnContradictionRule + +BINA-CASE-0001 : OneOrZeroCaseRule \ No newline at end of file From 3432007d189a58ea1431a4f6a2eb7414e319fa94 Mon Sep 17 00:00:00 2001 From: Brandon McCusker Date: Fri, 9 Feb 2024 18:51:26 -0500 Subject: [PATCH 020/359] Fixed minor syntax and import error --- .../rules/CompleteRowColumnDirectRule.java | 18 +++++++++++---- ...roCaseRule.java => OneOrZeroCaseRule.java} | 23 +++++++++++++++++++ .../binary/rules/OneTileGapDirectRule.java | 16 ++++++++++--- .../binary/rules/SurroundPairDirectRule.java | 17 +++++++++++--- .../rules/ThreeInARowContradictionRule.java | 14 +++++++---- ...UnbalancedRowColumnContradictionRule.java} | 14 +++++++---- 6 files changed, 84 insertions(+), 18 deletions(-) rename src/main/java/edu/rpi/legup/puzzle/binary/rules/{OneorZeroCaseRule.java => OneOrZeroCaseRule.java} (57%) rename src/main/java/edu/rpi/legup/puzzle/binary/rules/{UnbalancedRowColumnContractionRule.java => UnbalancedRowColumnContradictionRule.java} (71%) diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java index bab6e7223..1a35902f3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java @@ -1,10 +1,10 @@ package edu.rpi.legup.puzzle.binary.rules; 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.DirectRule; -import edu.rpi.legup.model.ContradictionRule; +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.binary.BinaryBoard; import edu.rpi.legup.puzzle.binary.BinaryCell; @@ -14,8 +14,18 @@ public class CompleteRowColumnDirectRule extends DirectRule { public CompleteRowColumnDirectRule() { super("BINA-BASC-0003", - "Complete Row Column" + "Complete Row Column", "If a row/column of length n contains n/2 of a single value, the remaining cells must contain the other value", "FILL IN WITH IMAGE"); } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java similarity index 57% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java index 63f882f83..0e3337d2f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneorZeroCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java @@ -9,6 +9,9 @@ import edu.rpi.legup.puzzle.binary.BinaryCell; import edu.rpi.legup.puzzle.binary.BinaryType; +import java.util.ArrayList; +import java.util.List; + public class OneOrZeroCaseRule extends CaseRule { public OneOrZeroCaseRule() { @@ -17,4 +20,24 @@ public OneOrZeroCaseRule() { "Each blank cell is either a one or a zero.", "FILL IN WITH IMAGE"); } + + @Override + public String checkRuleRaw(TreeTransition transition) { + return null; + } + + @Override + public CaseBoard getCaseBoard(Board board) { + return null; + } + + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + return null; + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java index 8a8a1f53e..704098e56 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java @@ -1,10 +1,10 @@ package edu.rpi.legup.puzzle.binary.rules; 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.DirectRule; -import edu.rpi.legup.model.ContradictionRule; +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.binary.BinaryBoard; import edu.rpi.legup.puzzle.binary.BinaryCell; @@ -18,4 +18,14 @@ public OneTileGapDirectRule() { "If an empty tile is surrounded by the same value, fill the gap with the other value.", "FILL IN WITH IMAGE"); } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java index 44bd5beee..acc774f26 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java @@ -1,10 +1,10 @@ package edu.rpi.legup.puzzle.binary.rules; 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.DirectRule; -import edu.rpi.legup.model.ContradictionRule; +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.binary.BinaryBoard; import edu.rpi.legup.puzzle.binary.BinaryCell; @@ -18,4 +18,15 @@ public SurroundPairDirectRule() { "If two adjacent tiles have the same value, surround the tiles with the other value.", "FILL IN WITH IMAGE"); } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } + } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java index 4965ce24a..201ba00f5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java @@ -3,9 +3,9 @@ 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.nurikabe.BinaryBoard; -import edu.rpi.legup.puzzle.nurikabe.BinaryCell; -import edu.rpi.legup.puzzle.nurikabe.BinaryType; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; public class ThreeInARowContradictionRule extends ContradictionRule { @@ -17,4 +17,10 @@ public ThreeInARowContradictionRule() { "Three In A Row", "Three of the same value cannot exist in a row", "FILL IN WITH IMAGE"); - } \ No newline at end of file + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java similarity index 71% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java index f86377004..1bfe72ecb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContractionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java @@ -3,9 +3,9 @@ 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.nurikabe.BinaryBoard; -import edu.rpi.legup.puzzle.nurikabe.BinaryCell; -import edu.rpi.legup.puzzle.nurikabe.BinaryType; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; public class UnbalancedRowColumnContradictionRule extends ContradictionRule { @@ -17,4 +17,10 @@ public UnbalancedRowColumnContradictionRule() { "Unbalanced Row Column", "A row or column cannot contain more of more value than the other", "FILL IN WITH IMAGE"); - } \ No newline at end of file + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + return null; + } +} \ No newline at end of file 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 021/359] 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 b718f16d48dffdb17420f289228f3e96fec722e8 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Tue, 13 Feb 2024 16:28:35 -0500 Subject: [PATCH 022/359] Fixed checkstyle error message in BinaryCellFactory --- .../java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java index d56e26e7c..a2211f35c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java @@ -37,9 +37,11 @@ public BinaryCell importCell(Node node, Board board) throws InvalidFileFormatExc BinaryCell cell = new BinaryCell(value, new Point(x, y)); cell.setIndex(y * height + x); return cell; - } catch (NumberFormatException e) { + } + catch (NumberFormatException e) { throw new InvalidFileFormatException("binary Factory: unknown value where integer expected"); - } catch (NullPointerException e) { + } + catch (NullPointerException e) { throw new InvalidFileFormatException("binary Factory: could not find attribute(s)"); } } From 8955343d8bbdac96fa80cb31480b1784a58c1db5 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 13 Feb 2024 16:32:48 -0500 Subject: [PATCH 023/359] 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 024/359] 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 025/359] 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 76660a3528d8ec5dd7072dd0816f28e01c07d021 Mon Sep 17 00:00:00 2001 From: EggyMath <55564321+EggyMath@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:24:26 -0500 Subject: [PATCH 026/359] Refactoring and bug fixes Refactored Case Rules to match wiki description and fixed bug that only checked regions and not all group types. --- .../legup/puzzle/sudoku/PossibleNumberCaseBoard.java | 4 ++-- ...le.java => NoCellForNumberContradictionRule.java} | 6 +++--- ...Rule.java => PossibleCellsForNumberCaseRule.java} | 8 ++++---- ...Rule.java => PossibleNumbersForCellCaseRule.java} | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) rename src/main/java/edu/rpi/legup/puzzle/sudoku/rules/{NoSolutionContradictionRule.java => NoCellForNumberContradictionRule.java} (91%) rename src/main/java/edu/rpi/legup/puzzle/sudoku/rules/{PossibleNumberCaseRule.java => PossibleCellsForNumberCaseRule.java} (94%) rename src/main/java/edu/rpi/legup/puzzle/sudoku/rules/{PossibleCellCaseRule.java => PossibleNumbersForCellCaseRule.java} (90%) 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..871b5392b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java @@ -2,7 +2,7 @@ import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.puzzle.sudoku.rules.PossibleNumberCaseRule; +import edu.rpi.legup.puzzle.sudoku.rules.PossibleCellsForNumberCaseRule; import java.awt.event.MouseEvent; import java.util.HashSet; @@ -16,7 +16,7 @@ public class PossibleNumberCaseBoard extends CaseBoard { private Set pickableCols; - public PossibleNumberCaseBoard(SudokuBoard baseBoard, PossibleNumberCaseRule caseRule, SudokuCell cell) { + public PossibleNumberCaseBoard(SudokuBoard baseBoard, PossibleCellsForNumberCaseRule caseRule, SudokuCell cell) { super(baseBoard, caseRule); this.cell = cell; this.pickableRegions = new HashSet<>(); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberContradictionRule.java similarity index 91% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberContradictionRule.java index fb87764fe..a308e1dfc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberContradictionRule.java @@ -9,10 +9,10 @@ import java.util.HashSet; import java.util.Set; -public class NoSolutionContradictionRule extends ContradictionRule { +public class NoCellForNumberContradictionRule extends ContradictionRule { - public NoSolutionContradictionRule() { - super("SUDO-CONT-0001", "No Solution for Cell", + public NoCellForNumberContradictionRule() { + super("SUDO-CONT-0001", "No Cell for Number", "Process of elimination yields no valid numbers for an empty cell.", "edu/rpi/legup/images/sudoku/NoSolution.png"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberCaseRule.java similarity index 94% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberCaseRule.java index 27d6b58b7..9fc047ff4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberCaseRule.java @@ -14,12 +14,12 @@ import java.util.List; import java.util.Set; -public class PossibleNumberCaseRule extends CaseRule { +public class PossibleCellsForNumberCaseRule extends CaseRule { - public PossibleNumberCaseRule() { - super("SUDO-CASE-0002", "Possible Numbers for Cell", + public PossibleCellsForNumberCaseRule() { + super("SUDO-CASE-0002", "Possible Cells for Number", "An empty cell has a limited set of possible numbers that can fill it.", - "edu/rpi/legup/images/sudoku/PossibleValues.png"); + "edu/rpi/legup/images/sudoku/possible_cells_number.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java similarity index 90% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java index 373b60457..7f00eff55 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java @@ -12,11 +12,11 @@ import java.util.HashSet; import java.util.Set; -public class PossibleCellCaseRule extends CaseRule { - public PossibleCellCaseRule() { - super("SUDO-CASE-0001", "Possible Cells for Number", +public class PossibleNumbersForCellCaseRule extends CaseRule { + public PossibleNumbersForCellCaseRule() { + super("SUDO-CASE-0001", "Possible Numbers for Cell", "A number has a limited set of cells in which it can be placed.", - "edu/rpi/legup/images/sudoku/possible_cells_number.png"); + "edu/rpi/legup/images/sudoku/PossibleValues.png"); } /** @@ -82,14 +82,14 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { } int rowNum = cell.getLocation().y; - for (SudokuCell c : sudokuBoard.getRegion(rowNum)) { + for (SudokuCell c : sudokuBoard.getRow(rowNum)) { if (c.getData().equals(c.getData())) { possibleValue.remove(c.getData()); } } int colNum = cell.getLocation().x; - for (SudokuCell c : sudokuBoard.getRegion(colNum)) { + for (SudokuCell c : sudokuBoard.getCol(colNum)) { if (c.getData().equals(c.getData())) { possibleValue.remove(c.getData()); } 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 027/359] 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 028/359] 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 029/359] 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 9d026f4379d6b776bd1a8e26d27602bcfb8e5344 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Sat, 17 Feb 2024 14:20:14 -0500 Subject: [PATCH 030/359] Made slight progress when trying to output puzzle, first edition of Three Adjacent Ones and Zeros Contradiction rules --- bin/main/edu/rpi/legup/legup/config | 8 +-- .../6x6 easy/{876868768.xml => 876868768} | 0 .../edu/rpi/legup/puzzle/binary/Binary.java | 32 ++++++++---- .../ThreeAdjacentOnesContradictionRule.java | 50 +++++++++++++++++++ .../ThreeAdjacentZerosContradictionRule.java | 50 +++++++++++++++++++ .../rules/ThreeInARowContradictionRule.java | 26 ---------- 6 files changed, 127 insertions(+), 39 deletions(-) rename puzzles files/binary/6x6 easy/{876868768.xml => 876868768} (100%) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index b30cb8525..7dc58cdda 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -4,6 +4,10 @@ qualifiedClassName="edu.rpi.legup.puzzle.battleship.Battleship" fileType=".xml" fileCreationDisabled="true"/> + - diff --git a/puzzles files/binary/6x6 easy/876868768.xml b/puzzles files/binary/6x6 easy/876868768 similarity index 100% rename from puzzles files/binary/6x6 easy/876868768.xml rename to puzzles files/binary/6x6 easy/876868768 diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java index 7a48ee190..6730b2806 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -10,12 +10,16 @@ public Binary() { super(); this.name = "Binary"; + this.importer = new BinaryImporter(this); this.exporter = new BinaryExporter(this); this.factory = new BinaryCellFactory(); } + /** + * Initializes the game board. Called by the invoker of the class + */ @Override public void initializeView() { boardView = new BinaryView((BinaryBoard) currentBoard); @@ -23,12 +27,30 @@ public void initializeView() { addBoardListener(boardView); } + /** + * Generates a random edu.rpi.legup.puzzle based on the difficulty + * + * @param difficulty level of difficulty (1-10) + * @return board of the random edu.rpi.legup.puzzle + */ @Override public Board generatePuzzle(int difficulty) { return null; } - @Override +// /** +// * Determines if the given dimensions are valid for Binary +// * +// * @param rows the number of rows +// * @param columns the number of columns +// * @return true if the given dimensions are valid for Binary, false otherwise +// */ +// @Override +// public boolean isValidDimensions(int rows, int columns){ +// return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; +// } + + @Override public boolean isBoardComplete(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board; @@ -49,12 +71,4 @@ public boolean isBoardComplete(Board board) { @Override public void onBoardChange(Board board) { } - - - @Override - public boolean isValidDimensions(int rows, int columns){ - return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; - } - - } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java new file mode 100644 index 000000000..bc2d83ccd --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java @@ -0,0 +1,50 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; +import java.util.Set; +public class ThreeAdjacentOnesContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Contradiction must be a one"; + + public ThreeAdjacentOnesContradictionRule() { + super("BINA-CONT-0002", + "Three Adjacent Ones", + "There must not be three adjacent ones in a row or column", + "currentlynoimage.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + if (cell.getType() != BinaryType.ONE) + { + return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; + } + + BinaryCell upTwo = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y+2); + BinaryCell upOne = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y+1); + BinaryCell leftTwo = binaryBoard.getCell(cell.getLocation().x-2, cell.getLocation().y); + BinaryCell leftOne = binaryBoard.getCell(cell.getLocation().x-1, cell.getLocation().y); + BinaryCell rightTwo = binaryBoard.getCell(cell.getLocation().x+2, cell.getLocation().y); + BinaryCell rightOne = binaryBoard.getCell(cell.getLocation().x+1, cell.getLocation().y); + BinaryCell downTwo = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y-2); + BinaryCell downOne = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y-1); + + if ((upTwo.getType() == BinaryType.ONE && upOne.getType() == BinaryType.ONE) || + (leftTwo.getType() == BinaryType.ONE && leftOne.getType() == BinaryType.ONE) || + (rightTwo.getType() == BinaryType.ONE && rightOne.getType() == BinaryType.ONE) || + (downTwo.getType() == BinaryType.ONE && downOne.getType() == BinaryType.ONE) || + (leftOne.getType() == BinaryType.ONE && rightOne.getType() == BinaryType.ONE) || + (upOne.getType() == BinaryType.ONE && downOne.getType() == BinaryType.ONE)) + { + return null; + } + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java new file mode 100644 index 000000000..6357fcd62 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java @@ -0,0 +1,50 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; +import java.util.Set; +public class ThreeAdjacentZerosContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Contradiction must be a zero"; + + public ThreeAdjacentZerosContradictionRule() { + super("BINA-CONT-0001", + "Three Adjacent Zeros", + "There must not be three adjacent zeros in a row or column", + "currentlynoimage.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + if (cell.getType() != BinaryType.ZERO) + { + return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; + } + + BinaryCell upTwo = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y+2); + BinaryCell upOne = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y+1); + BinaryCell leftTwo = binaryBoard.getCell(cell.getLocation().x-2, cell.getLocation().y); + BinaryCell leftOne = binaryBoard.getCell(cell.getLocation().x-1, cell.getLocation().y); + BinaryCell rightTwo = binaryBoard.getCell(cell.getLocation().x+2, cell.getLocation().y); + BinaryCell rightOne = binaryBoard.getCell(cell.getLocation().x+1, cell.getLocation().y); + BinaryCell downTwo = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y-2); + BinaryCell downOne = binaryBoard.getCell(cell.getLocation().x, cell.getLocation().y-1); + + if ((upTwo.getType() == BinaryType.ZERO && upOne.getType() == BinaryType.ZERO) || + (leftTwo.getType() == BinaryType.ZERO && leftOne.getType() == BinaryType.ZERO) || + (rightTwo.getType() == BinaryType.ZERO && rightOne.getType() == BinaryType.ZERO) || + (downTwo.getType() == BinaryType.ZERO && downOne.getType() == BinaryType.ZERO) || + (leftOne.getType() == BinaryType.ZERO && rightOne.getType() == BinaryType.ZERO) || + (upOne.getType() == BinaryType.ZERO && downOne.getType() == BinaryType.ZERO)) + { + return null; + } + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java deleted file mode 100644 index 201ba00f5..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeInARowContradictionRule.java +++ /dev/null @@ -1,26 +0,0 @@ -package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; -import edu.rpi.legup.puzzle.binary.BinaryType; - -public class ThreeInARowContradictionRule extends ContradictionRule { - - private final String NO_CONTRADICTION_MESSAGE = ""; - private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; - - public ThreeInARowContradictionRule() { - super("BINA-CONT-0001", - "Three In A Row", - "Three of the same value cannot exist in a row", - "FILL IN WITH IMAGE"); - } - - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - return null; - } -} \ No newline at end of file From 6515bc3f410fcbcaaceaa76f2ca722ef2bb98d59 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Sat, 17 Feb 2024 14:26:54 -0500 Subject: [PATCH 031/359] Fixed checkstyle error for if statements --- .../binary/rules/ThreeAdjacentOnesContradictionRule.java | 6 ++---- .../binary/rules/ThreeAdjacentZerosContradictionRule.java | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java index bc2d83ccd..3b24e1ac0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java @@ -22,8 +22,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { BinaryBoard binaryBoard = (BinaryBoard) board; BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - if (cell.getType() != BinaryType.ONE) - { + if (cell.getType() != BinaryType.ONE) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } @@ -41,8 +40,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { (rightTwo.getType() == BinaryType.ONE && rightOne.getType() == BinaryType.ONE) || (downTwo.getType() == BinaryType.ONE && downOne.getType() == BinaryType.ONE) || (leftOne.getType() == BinaryType.ONE && rightOne.getType() == BinaryType.ONE) || - (upOne.getType() == BinaryType.ONE && downOne.getType() == BinaryType.ONE)) - { + (upOne.getType() == BinaryType.ONE && downOne.getType() == BinaryType.ONE)) { return null; } return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java index 6357fcd62..285c9dae6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java @@ -22,8 +22,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { BinaryBoard binaryBoard = (BinaryBoard) board; BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - if (cell.getType() != BinaryType.ZERO) - { + if (cell.getType() != BinaryType.ZERO) { return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; } @@ -41,8 +40,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { (rightTwo.getType() == BinaryType.ZERO && rightOne.getType() == BinaryType.ZERO) || (downTwo.getType() == BinaryType.ZERO && downOne.getType() == BinaryType.ZERO) || (leftOne.getType() == BinaryType.ZERO && rightOne.getType() == BinaryType.ZERO) || - (upOne.getType() == BinaryType.ZERO && downOne.getType() == BinaryType.ZERO)) - { + (upOne.getType() == BinaryType.ZERO && downOne.getType() == BinaryType.ZERO)) { return null; } return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; From 0346d374423d04cb3979192a9baf24758e7708b5 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Sun, 18 Feb 2024 13:38:26 -0500 Subject: [PATCH 032/359] Added functions to BinaryBoard that returns the rows and columns of a specified index. --- .../rpi/legup/puzzle/binary/BinaryBoard.java | 24 ++++++++++++++++--- .../ThreeAdjacentOnesContradictionRule.java | 1 - .../ThreeAdjacentZerosContradictionRule.java | 1 - 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java index 7707aa068..59a588e29 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java @@ -3,17 +3,35 @@ 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 BinaryBoard extends GridBoard { - public BinaryBoard(int width, int height) { - super(width, height); - } + private int size; public BinaryBoard(int size) { super(size, size); + this.size = size; } @Override public BinaryCell getCell(int x, int y) { return (BinaryCell) super.getCell(x, y); } + + 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 col = new HashSet<>(); + for (int i = 0; i < size; i ++) { + col.add(getCell(colNum, i)); + } + return col; + } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java index 3b24e1ac0..318495472 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentOnesContradictionRule.java @@ -5,7 +5,6 @@ import edu.rpi.legup.puzzle.binary.BinaryBoard; import edu.rpi.legup.puzzle.binary.BinaryCell; import edu.rpi.legup.puzzle.binary.BinaryType; -import java.util.Set; public class ThreeAdjacentOnesContradictionRule extends ContradictionRule { private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Contradiction must be a one"; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java index 285c9dae6..04d8181de 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentZerosContradictionRule.java @@ -5,7 +5,6 @@ import edu.rpi.legup.puzzle.binary.BinaryBoard; import edu.rpi.legup.puzzle.binary.BinaryCell; import edu.rpi.legup.puzzle.binary.BinaryType; -import java.util.Set; public class ThreeAdjacentZerosContradictionRule extends ContradictionRule { private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Contradiction must be a zero"; From aaa23c01de99dbb6161aa47ddd82b35668349660 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Sun, 18 Feb 2024 13:45:53 -0500 Subject: [PATCH 033/359] Fixed error that causes BinaryImporter to fail to initialize board --- src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java index 59a588e29..f24667d37 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java @@ -8,6 +8,10 @@ import java.util.Set; public class BinaryBoard extends GridBoard { private int size; + public BinaryBoard(int width, int height) { + super(width, height); + this.size = width; + } public BinaryBoard(int size) { super(size, size); @@ -34,4 +38,4 @@ public Set getCol(int colNum) { } return col; } -} \ No newline at end of file +} From a532c1ce5703c1788d0bfbc932b049977aeebcd9 Mon Sep 17 00:00:00 2001 From: Zachary Bonagura Date: Sun, 18 Feb 2024 14:00:34 -0500 Subject: [PATCH 034/359] First edition of Unbalanced Row and Unbalanced Column Contradiction rules --- .../UnbalancedColumnContradictionRule.java | 50 +++++++++++++++++++ .../UnbalancedRowColumnContradictionRule.java | 26 ---------- .../rules/UnbalancedRowContradictionRule.java | 50 +++++++++++++++++++ 3 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedColumnContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowContradictionRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedColumnContradictionRule.java new file mode 100644 index 000000000..96a56c4cc --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedColumnContradictionRule.java @@ -0,0 +1,50 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +import java.util.Set; +public class UnbalancedColumnContradictionRule extends ContradictionRule { + + private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Column must have a value in each cell"; + + public UnbalancedColumnContradictionRule() { + super("BINA-CONT-0004", + "Unbalanced Column", + "Each column must contain an equal number of zeros and ones ", + "currentlynoimage.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + Set col = binaryBoard.getCol(cell.getLocation().x); + + int size = col.size(); + int numZeros = 0; + int numOnes = 0; + + for (BinaryCell item : col) { + if (item.getType() == BinaryType.ZERO) { + numZeros++; + } + else if(item.getType() == BinaryType.ONE) { + numOnes++; + } + } + if (numZeros + numOnes != size) { + return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; + } + if (numZeros != numOnes) { + return null; + } + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java deleted file mode 100644 index 1bfe72ecb..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java +++ /dev/null @@ -1,26 +0,0 @@ -package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; -import edu.rpi.legup.puzzle.binary.BinaryType; - -public class UnbalancedRowColumnContradictionRule extends ContradictionRule { - - private final String NO_CONTRADICTION_MESSAGE = ""; - private final String INVALID_USE_MESSAGE = "Does not contain a contradiction at this index"; - - public UnbalancedRowColumnContradictionRule() { - super("BINA-CONT-0002", - "Unbalanced Row Column", - "A row or column cannot contain more of more value than the other", - "FILL IN WITH IMAGE"); - } - - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - return null; - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowContradictionRule.java new file mode 100644 index 000000000..a9c3346b0 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowContradictionRule.java @@ -0,0 +1,50 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +import java.util.Set; +public class UnbalancedRowContradictionRule extends ContradictionRule { + + private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Row must have a value in each cell"; + + public UnbalancedRowContradictionRule() { + super("BINA-CONT-0003", + "Unbalanced Row", + "Each row must contain an equal number of zeros and ones ", + "currentlynoimage.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + Set row = binaryBoard.getRow(cell.getLocation().y); + + int size = row.size(); + int numZeros = 0; + int numOnes = 0; + + for (BinaryCell item : row) { + if (item.getType() == BinaryType.ZERO) { + numZeros++; + } + else if(item.getType() == BinaryType.ONE) { + numOnes++; + } + } + if (numZeros + numOnes != size) { + return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; + } + if (numZeros != numOnes) { + return null; + } + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} \ No newline at end of file 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 035/359] 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 036/359] 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 037/359] 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 038/359] 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 039/359] 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 040/359] 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 041/359] 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 042/359] 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 6a3cc9c752567aa54f35f93772cb0f985caa36f9 Mon Sep 17 00:00:00 2001 From: charlestian23 Date: Fri, 23 Feb 2024 16:47:32 -0500 Subject: [PATCH 043/359] Update config --- src/main/resources/edu/rpi/legup/legup/config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index 19e63a2a3..7dc58cdda 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -4,6 +4,10 @@ qualifiedClassName="edu.rpi.legup.puzzle.battleship.Battleship" fileType=".xml" fileCreationDisabled="true"/> + Date: Fri, 23 Feb 2024 16:55:05 -0500 Subject: [PATCH 044/359] 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 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