();
+ Point cellLoc = cell.getLocation();
+ for (int i=-1; i <= 1; i++) {
+ for (int j=-1; j <= 1; j++) {
+ if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) {
+ continue;
+ }
+ FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j);
+ if (adjCell == null) {
+ continue;
+ }
+ adjCells.add(adjCell);
+ }
+ }
+ return adjCells;
+ }
+
+ /**
+ * Gets all cells that are contained in the square defined as having 'distance'
+ * cells between the center and the outer wall. For example, distance = 1:
+ * |X|X|X|X|X|
+ * |X| | | |X|
+ * |X| |O| |X|
+ * |X| | | |X|
+ * |X|X|X|X|X|
+ * O is 'cell', and all 'X' will be returned in the ArrayList
+ */
+ public static ArrayList getCellsAtDistance(FillapixBoard board, FillapixCell cell, int distance) {
+ ArrayList adjCells = new ArrayList();
+ Point cellLoc = cell.getLocation();
+ int i = 0, j = 0;
+ // top line
+ for (i = cellLoc.x - (distance), j = cellLoc.y - (distance+1); i <= cellLoc.x + (distance+1); i++) {
+ if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) {
+ continue;
+ }
+ FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j);
+ if (adjCell == null) {
+ continue;
+ }
+ adjCells.add(adjCell);
+ }
+ // right line
+ for (i = cellLoc.x + (distance+1), j = cellLoc.y - (distance); j <= cellLoc.y + (distance+1); j++) {
+ if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) {
+ continue;
+ }
+ FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j);
+ if (adjCell == null) {
+ continue;
+ }
+ adjCells.add(adjCell);
+ }
+ // bottom line
+ for (i = cellLoc.x + (distance), j = cellLoc.y + (distance+1); i <= cellLoc.x - (distance+1); i--) {
+ if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) {
+ continue;
+ }
+ FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j);
+ if (adjCell == null) {
+ continue;
+ }
+ adjCells.add(adjCell);
+ }
+ // left line
+ for (i = cellLoc.x - (distance+1), j = cellLoc.y + (distance); j <= cellLoc.y - (distance+1); j--) {
+ if (cellLoc.getX() + i < 0 || cellLoc.y + j < 0 || cellLoc.x + i >= board.getWidth() || cellLoc.y + j >= board.getHeight()) {
+ continue;
+ }
+ FillapixCell adjCell = board.getCell(cellLoc.x + i, cellLoc.y + j);
+ if (adjCell == null) {
+ continue;
+ }
+ adjCells.add(adjCell);
+ }
+
+ return adjCells;
+ }
+
+ /**
+ * Finds all possible combinations of chosenNumObj
items can be
+ * chosen from totalNumObj
total items.
+ * For example, if 1 item is chosen from 2 possible items, the combinations
+ * are:
+ * [ [true,false], [false,true] ]
+ *
+ * @param totalNumItems the total number of items that can possibly be chosen
+ * @param chosenNumItems the number of items to be chosen
+ *
+ * @return an ArrayList of Boolean arrays. Each index in the ArrayList represents
+ * a distinct combination. Each Boolean array will be totalNumItems
+ * long and each index will be true
if the corresponding item is
+ * included in that combination, and false
otherwise.
+ */
+ public static ArrayList getCombinations(int chosenNumItems, int totalNumItems) {
+ ArrayList combinations = new ArrayList();
+
+ // calculate all combinations
+ boolean[] array = new boolean[totalNumItems];
+ recurseCombinations(combinations, 0, chosenNumItems, 0, totalNumItems, array);
+
+ return combinations;
+ }
+
+ private static void recurseCombinations(ArrayList result, int curIndex, int maxBlack, int numBlack, int len, boolean[] workingArray) {
+ if (curIndex == len) {
+ // complete, but not valid solution
+ if (numBlack != maxBlack) {
+ return;
+ }
+ // complete and valid solution
+ result.add(workingArray.clone());
+ return;
+ }
+ // there is no chance of completing the required number of solutions, so quit
+ if (len - curIndex < maxBlack - numBlack) {
+ return;
+ }
+
+ if (numBlack < maxBlack) {
+ workingArray[curIndex] = true;
+ recurseCombinations(result, curIndex+1, maxBlack, numBlack+1, len, workingArray);
+ }
+ workingArray[curIndex] = false;
+ recurseCombinations(result, curIndex+1, maxBlack, numBlack, len, workingArray);
+ }
+
+ public static boolean checkBoardForContradiction(FillapixBoard board) {
+ ContradictionRule tooManyBlack = new TooManyBlackCellsContradictionRule();
+ ContradictionRule tooManyWhite = new TooFewBlackCellsContradictionRule();
+ for (int i= 0; i < board.getWidth(); i++) {
+ for (int j=0; j < board.getHeight(); j++) {
+ if (tooManyBlack.checkContradictionAt(board, board.getCell(i, j)) == null ||
+ tooManyWhite.checkContradictionAt(board, board.getCell(i, j)) == null) {
+ return true;
+ }
+ }
+ }
+ return false;
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java
new file mode 100644
index 000000000..7e43fc6b4
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java
@@ -0,0 +1,9 @@
+package edu.rpi.legup.puzzle.fillapix.elements;
+
+import edu.rpi.legup.model.elements.PlaceableElement;
+
+public class BlackTile extends PlaceableElement {
+ public BlackTile() {
+ super("FPIX-PLAC-0001", "Black Tile", "The black tile", "edu/rpi/legup/images/fillapix/tiles/BlackTile.png");
+ }
+}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java
new file mode 100644
index 000000000..beee70e21
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java
@@ -0,0 +1,27 @@
+package edu.rpi.legup.puzzle.fillapix.elements;
+
+import edu.rpi.legup.model.elements.NonPlaceableElement;
+
+public class NumberTile extends NonPlaceableElement {
+ private int object_num;
+
+ public NumberTile() {
+ super("FPIX-UNPL-0001", "Number Tile", "A numbered tile", "edu/rpi/legup/images/fillapix/tiles/NumberTile.png");
+ object_num = 0;
+ }
+
+ /**
+ * @return this object's tile number...
+ */
+ public int getTileNumber() {
+ return object_num;
+ }
+
+ /**
+ * @param num Amount to set tile object to.
+ */
+ public void setTileNumber(int num) {
+ object_num = num;
+ }
+
+}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java
new file mode 100644
index 000000000..ef754782f
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java
@@ -0,0 +1,9 @@
+package edu.rpi.legup.puzzle.fillapix.elements;
+
+import edu.rpi.legup.model.elements.NonPlaceableElement;
+
+public class UnknownTile extends NonPlaceableElement {
+ public UnknownTile() {
+ super("FPIX-UNPL-0002", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/fillapix/tiles/UnknownTile.png");
+ }
+}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java
new file mode 100644
index 000000000..dd27d2834
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java
@@ -0,0 +1,9 @@
+package edu.rpi.legup.puzzle.fillapix.elements;
+
+import edu.rpi.legup.model.elements.PlaceableElement;
+
+public class WhiteTile extends PlaceableElement {
+ public WhiteTile() {
+ super("FPIX-PLAC-0002", "White Tile", "The white tile", "edu/rpi/legup/images/fillapix/tiles/WhiteTile.png");
+ }
+}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt
new file mode 100644
index 000000000..0409fa800
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt
@@ -0,0 +1,5 @@
+FPIX-PLAC-0001 : BlackTile
+FPIX-PLAC-0002 : WhiteTile
+
+FPIX-UNPL-0001 : NumberTile
+FPIX-UNPL-0002 : UnknownTile
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java
index fd6b7c022..1e5151c48 100644
--- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java
@@ -40,13 +40,13 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) {
Board case1 = board.copy();
FillapixCell cell1 = (FillapixCell) case1.getPuzzleElement(puzzleElement);
- cell1.setType(FillapixCellType.BLACK);
+ cell1.setCellType(FillapixCellType.BLACK);
case1.addModifiedData(cell1);
cases.add(case1);
Board case2 = board.copy();
FillapixCell cell2 = (FillapixCell) case2.getPuzzleElement(puzzleElement);
- cell2.setType(FillapixCellType.WHITE);
+ cell2.setCellType(FillapixCellType.WHITE);
case2.addModifiedData(cell2);
cases.add(case2);
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java
index afd226693..06a8045ed 100644
--- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithBlackDirectRule.java
@@ -8,6 +8,7 @@
import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
import edu.rpi.legup.puzzle.fillapix.FillapixCell;
import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
public class FinishWithBlackDirectRule extends DirectRule {
public FinishWithBlackDirectRule() {
@@ -28,7 +29,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem
return super.getInvalidUseOfRuleMessage() + ": This cell must be black to be applicable with this rule.";
}
- if (isForcedBlack(parentBoard, cell)) {
+ if (FillapixUtilities.isForcedBlack(parentBoard, cell)) {
return null;
}
else {
@@ -36,14 +37,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem
}
}
- private boolean isForcedBlack(FillapixBoard board, FillapixCell cell) {
- TooFewBlackCellsContradictionRule tooManyBlackCells = new TooFewBlackCellsContradictionRule();
- FillapixBoard whiteCaseBoard = board.copy();
- FillapixCell whiteCell = (FillapixCell) whiteCaseBoard.getPuzzleElement(cell);
- whiteCell.setType(FillapixCellType.WHITE);
- return tooManyBlackCells.checkContradictionAt(whiteCaseBoard, cell) == null;
- }
-
/**
* Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}.
*
@@ -55,8 +48,8 @@ public Board getDefaultBoard(TreeNode node) {
FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy();
for (PuzzleElement element : fillapixBoard.getPuzzleElements()) {
FillapixCell cell = (FillapixCell) element;
- if (cell.getType() == FillapixCellType.UNKNOWN && isForcedBlack((FillapixBoard) node.getBoard(), cell)) {
- cell.setType(FillapixCellType.BLACK);
+ if (cell.getType() == FillapixCellType.UNKNOWN && FillapixUtilities.isForcedBlack((FillapixBoard) node.getBoard(), cell)) {
+ cell.setCellType(FillapixCellType.BLACK);
fillapixBoard.addModifiedData(cell);
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java
index ec482d5f7..7e213a59c 100644
--- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/FinishWithWhiteDirectRule.java
@@ -8,10 +8,11 @@
import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
import edu.rpi.legup.puzzle.fillapix.FillapixCell;
import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
public class FinishWithWhiteDirectRule extends DirectRule {
public FinishWithWhiteDirectRule() {
- super("FFIX-BASC-0002",
+ super("FPIX-BASC-0002",
"Finish with White",
"The remaining unknowns around and on a cell must be white to satisfy the number",
"edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png");
@@ -28,7 +29,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem
return super.getInvalidUseOfRuleMessage() + ": This cell must be white to be applicable with this rule";
}
- if (isForcedWhite(parentBoard, cell)) {
+ if (FillapixUtilities.isForcedWhite(parentBoard, cell)) {
return null;
}
else {
@@ -36,14 +37,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem
}
}
- private boolean isForcedWhite(FillapixBoard board, FillapixCell cell) {
- TooManyBlackCellsContradictionRule tooManyBlackCells = new TooManyBlackCellsContradictionRule();
- FillapixBoard blackCaseBoard = board.copy();
- FillapixCell blackCell = (FillapixCell) blackCaseBoard.getPuzzleElement(cell);
- blackCell.setType(FillapixCellType.BLACK);
- return tooManyBlackCells.checkContradictionAt(blackCaseBoard, cell) == null;
- }
-
/**
* Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}.
*
@@ -55,8 +48,8 @@ public Board getDefaultBoard(TreeNode node) {
FillapixBoard fillapixBoard = (FillapixBoard) node.getBoard().copy();
for (PuzzleElement element : fillapixBoard.getPuzzleElements()) {
FillapixCell cell = (FillapixCell) element;
- if (cell.getType() == FillapixCellType.UNKNOWN && isForcedWhite((FillapixBoard) node.getBoard(), cell)) {
- cell.setType(FillapixCellType.WHITE);
+ if (cell.getType() == FillapixCellType.UNKNOWN && FillapixUtilities.isForcedWhite((FillapixBoard) node.getBoard(), cell)) {
+ cell.setCellType(FillapixCellType.WHITE);
fillapixBoard.addModifiedData(cell);
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java
new file mode 100644
index 000000000..656cedb3f
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/MirrorDirectRule.java
@@ -0,0 +1,103 @@
+package edu.rpi.legup.puzzle.fillapix.rules;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import edu.rpi.legup.model.gameboard.Board;
+import edu.rpi.legup.model.gameboard.PuzzleElement;
+import edu.rpi.legup.model.rules.CaseRule;
+import edu.rpi.legup.model.rules.DirectRule;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
+import edu.rpi.legup.puzzle.fillapix.FillapixCell;
+import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
+
+public class MirrorDirectRule extends DirectRule {
+ public MirrorDirectRule() {
+ super("FPIX-BASC-0003",
+ "Mirror",
+ "Two adjacent clues with the same value must have the same number of black squares in their unshared regions",
+ "edu/rpi/legup/images/fillapix/rules/Mirror.png");
+ }
+
+ @Override
+ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
+ FillapixBoard board = (FillapixBoard) transition.getBoard();
+ FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy();
+ FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement);
+ FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement);
+
+ // cell has to have been empty before
+ if (parentCell.getType() != FillapixCellType.UNKNOWN) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // parentBoard cannot have any contradictions
+ if (FillapixUtilities.checkBoardForContradiction(parentBoard)) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // find all cells adjacent to cell that are numbered
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell);
+ ArrayList adjNums = new ArrayList();
+ for (int i=0; i < adjCells.size(); i++) {
+ if ((adjCells.get(i)).getNumber() >= 0 && adjCells.get(i).getNumber() < 10) {
+ adjNums.add(adjCells.get(i));
+ }
+ }
+ // the numbered cells must be next to another numbered cell of the same value
+ Iterator itr = adjNums.iterator();
+ while (itr.hasNext()) {
+ FillapixCell adjNum = itr.next();
+ adjCells = FillapixUtilities.getAdjacentCells(parentBoard, adjNum);
+ boolean found = false;
+ for (FillapixCell adjCell : adjCells) {
+ if (adjCell.getNumber() == adjNum.getNumber() && adjCell.getIndex() != adjNum.getIndex()) {
+ found = true;
+ }
+ }
+ if (!found) {
+ itr.remove();
+ }
+ }
+
+ // change the color of the parentCell, and check if there exists a valid board
+ if (cell.getType() == FillapixCellType.BLACK) {
+ parentCell.setCellType(FillapixCellType.WHITE);
+ }
+ else {
+ parentCell.setCellType(FillapixCellType.BLACK);
+ }
+ parentBoard.addModifiedData(parentCell);
+ CaseRule completeClue = new SatisfyClueCaseRule();
+ List caseBoards;
+ for (FillapixCell adjNum : adjNums) {
+ caseBoards = completeClue.getCases(parentBoard, adjNum);
+ boolean found = true;
+ for (Board b : caseBoards) {
+ if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) {
+ found = false;
+ }
+ }
+ if (found) {
+ return null;
+ }
+ }
+
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ /**
+ * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}.
+ *
+ * @param node tree node used to create default transition board
+ * @return default board or null if this rule cannot be applied to this tree node
+ */
+ @Override
+ public Board getDefaultBoard(TreeNode node) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java
new file mode 100644
index 000000000..fe94dbcb4
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/NonTouchingSharedDirectRule.java
@@ -0,0 +1,99 @@
+package edu.rpi.legup.puzzle.fillapix.rules;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import edu.rpi.legup.model.gameboard.Board;
+import edu.rpi.legup.model.gameboard.PuzzleElement;
+import edu.rpi.legup.model.rules.CaseRule;
+import edu.rpi.legup.model.rules.DirectRule;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
+import edu.rpi.legup.puzzle.fillapix.FillapixCell;
+import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
+
+public class NonTouchingSharedDirectRule extends DirectRule {
+ public NonTouchingSharedDirectRule() {
+ super("FPIX-BASC-0005",
+ "NonTouching Shared",
+ "Clues with shared cells have the same difference in black cells in their unshared regions as the difference in their numbers",
+ "edu/rpi/legup/images/fillapix/rules/TouchingSides.png");
+ }
+
+ @Override
+ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
+ FillapixBoard board = (FillapixBoard) transition.getBoard();
+ FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy();
+ FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement);
+ FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement);
+
+ // cell has to have been empty before
+ if (parentCell.getType() != FillapixCellType.UNKNOWN) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // parentBoard cannot have any contradictions
+ if (FillapixUtilities.checkBoardForContradiction(parentBoard)) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // get all adjCells that have a number
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell);
+ adjCells.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10);
+ /* remove any number cell that does not have another number cell not
+ * touching, but sharing cells */
+ Iterator itr = adjCells.iterator();
+ while (itr.hasNext()) {
+ ArrayList sharingCells = FillapixUtilities.getCellsAtDistance(parentBoard, parentCell, 1);
+ boolean found = false;
+ for (FillapixCell sharingCell : sharingCells) {
+ if (sharingCell.getNumber() >= 0 && sharingCell.getNumber() < 10) {
+ found = true;
+ }
+ }
+ if (!found) {
+ itr.remove();
+ }
+ }
+
+ // change the cell to the opposite color
+ if (cell.getType() == FillapixCellType.BLACK) {
+ parentCell.setCellType(FillapixCellType.WHITE);
+ }
+ else {
+ parentCell.setCellType(FillapixCellType.BLACK);
+ }
+ // check for some contradiction in all cases
+ parentBoard.addModifiedData(parentCell);
+ CaseRule completeClue = new SatisfyClueCaseRule();
+ List caseBoards;
+ for (FillapixCell adjCell : adjCells) {
+ caseBoards = completeClue.getCases(parentBoard, adjCell);
+ boolean found = true;
+ for (Board b : caseBoards) {
+ if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) {
+ found = false;
+ }
+ }
+ if (found) {
+ return null;
+ }
+ }
+
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ /**
+ * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}.
+ *
+ * @param node tree node used to create default transition board
+ * @return default board or null if this rule cannot be applied to this tree node
+ */
+ @Override
+ public Board getDefaultBoard(TreeNode node) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java
new file mode 100644
index 000000000..4520add31
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java
@@ -0,0 +1,234 @@
+package edu.rpi.legup.puzzle.fillapix.rules;
+
+import java.awt.Point;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import edu.rpi.legup.model.gameboard.Board;
+import edu.rpi.legup.model.gameboard.CaseBoard;
+import edu.rpi.legup.model.gameboard.PuzzleElement;
+import edu.rpi.legup.model.rules.CaseRule;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
+import edu.rpi.legup.puzzle.fillapix.FillapixCell;
+import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
+
+public class SatisfyClueCaseRule extends CaseRule {
+ public SatisfyClueCaseRule() {
+ super("FPIX-CASE-0002",
+ "Satisfy Clue",
+ "Each clue must touch that number of squares.",
+ "edu/rpi/legup/images/fillapix/cases/SatisfyClue.png");
+ }
+
+ @Override
+ public CaseBoard getCaseBoard(Board board) {
+ FillapixBoard fillapixBoard = (FillapixBoard) board.copy();
+ CaseBoard caseBoard = new CaseBoard(fillapixBoard, this);
+ fillapixBoard.setModifiable(false);
+ for (PuzzleElement data : fillapixBoard.getPuzzleElements()) {
+ FillapixCell cell = (FillapixCell) data;
+ if (cell.getNumber() >= 0 && cell.getNumber() <= 9 && FillapixUtilities.hasEmptyAdjacent(fillapixBoard, cell)) {
+ caseBoard.addPickableElement(data);
+ }
+ }
+ return caseBoard;
+ }
+
+ @Override
+ public ArrayList getCases(Board board, PuzzleElement puzzleElement) {
+ ArrayList cases = new ArrayList();
+
+ // get value of cell
+ FillapixBoard fillapixBoard = (FillapixBoard) board.copy();
+ FillapixCell cell = (FillapixCell) fillapixBoard.getPuzzleElement(puzzleElement);
+ int cellMaxBlack = cell.getNumber();
+ if (cellMaxBlack < 0 || cellMaxBlack > 9) { // cell is not valid cell
+ return null;
+ }
+
+ // find number of black & empty squares
+ int cellNumBlack = 0;
+ int cellNumEmpty = 0;
+ ArrayList emptyCells = new ArrayList();
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(fillapixBoard, cell);
+ for (FillapixCell adjCell : adjCells) {
+ if (adjCell.getType() == FillapixCellType.BLACK) {
+ cellNumBlack++;
+ }
+ if (adjCell.getType() == FillapixCellType.UNKNOWN) {
+ cellNumEmpty++;
+ emptyCells.add(adjCell);
+ }
+ }
+ // no cases if no empty or if too many black already
+ if (cellNumBlack > cellMaxBlack || cellNumEmpty == 0) {
+ return cases;
+ }
+
+ // generate all cases as boolean expressions
+ ArrayList combinations;
+ combinations = FillapixUtilities.getCombinations(cellMaxBlack - cellNumBlack, cellNumEmpty);
+
+ for (int i=0; i < combinations.size(); i++) {
+ Board case_ = board.copy();
+ for (int j=0; j < combinations.get(i).length; j++) {
+ cell = (FillapixCell) case_.getPuzzleElement(emptyCells.get(j));
+ if (combinations.get(i)[j]) {
+ cell.setCellType(FillapixCellType.BLACK);
+ }
+ else {
+ cell.setCellType(FillapixCellType.WHITE);
+ }
+ case_.addModifiedData(cell);
+ }
+ cases.add(case_);
+ }
+
+ return cases;
+ }
+
+ @Override
+ public String checkRuleRaw(TreeTransition transition) {
+ TreeNode parent = transition.getParents().get(0);
+ List childTransitions = parent.getChildren();
+
+ /*
+ * In order for the transition to be valid, it can only be applied to
+ * one cell, thus:
+ * * there must be modified cells
+ * * all modified cells must share at least one common adjacent
+ * cell
+ * * all modified cells must fit within a 3X3 square
+ * * the center of one of the possible squaress must be a cell
+ * with a number
+ * * that cells possible combinations must match the transitions
+ * If all the above is verified, then the transition is valid
+ */
+
+
+ /* ensure there are modified cells */
+ Set modCells = transition.getBoard().getModifiedData();
+ if (modCells.size() <= 0) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+
+ /* ensure modified cells occur within a 3X3 square */
+ int minVertLoc = Integer.MAX_VALUE, maxVertLoc = Integer.MIN_VALUE;
+ int minHorzLoc = Integer.MAX_VALUE, maxHorzLoc = Integer.MIN_VALUE;
+ for (PuzzleElement modCell : modCells) {
+ Point loc = ((FillapixCell) modCell).getLocation();
+ if (loc.x < minHorzLoc) {
+ minHorzLoc = loc.x;
+ }
+ if (loc.x > maxHorzLoc) {
+ maxHorzLoc = loc.x;
+ }
+ if (loc.y < minVertLoc) {
+ minVertLoc = loc.y;
+ }
+ if (loc.y > maxVertLoc) {
+ maxVertLoc = loc.y;
+ }
+ }
+ if (maxVertLoc - minVertLoc > 3 || maxHorzLoc - minHorzLoc > 3) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+
+ /* get the center of all possible 3X3 squares,
+ * and collect all that have numbers */
+ FillapixBoard board = (FillapixBoard) transition.getParents().get(0).getBoard();
+ Set possibleCenters = new TreeSet();
+ possibleCenters.addAll(FillapixUtilities.getAdjacentCells(board, (FillapixCell) modCells.iterator().next()));
+ for (PuzzleElement modCell : modCells) {
+ possibleCenters.retainAll((FillapixUtilities.getAdjacentCells(board, (FillapixCell) modCell)));
+ }
+ // removing all elements without a valid number
+ possibleCenters.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10);
+ if (possibleCenters.isEmpty()) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+
+ /* Now go through the remaining centers, and check if their combinations
+ * match the transitions */
+ for (FillapixCell possibleCenter : possibleCenters) {
+ int numBlack = 0;
+ int numEmpty = 0;
+ int maxBlack = possibleCenter.getNumber();
+ for (FillapixCell adjCell : FillapixUtilities.getAdjacentCells(board, possibleCenter)) {
+ if (adjCell.getType() == FillapixCellType.BLACK) {
+ numBlack++;
+ }
+ if (adjCell.getType() == FillapixCellType.UNKNOWN) {
+ numEmpty++;
+ }
+ }
+ if (numEmpty <= 0 || numBlack > maxBlack) {
+ // this cell has no cases (no empty) or is already broken (too many black)
+ continue;
+ }
+
+ ArrayList combinations = FillapixUtilities.getCombinations(maxBlack - numBlack, numEmpty);
+ if (combinations.size() != childTransitions.size()) {
+ // not this center because combinations do not match transitions
+ continue;
+ }
+ boolean quitEarly = false;
+ for (TreeTransition trans : childTransitions) {
+ /* convert the transition board into boolean format, so that it
+ * can be compared to the combinations */
+ FillapixBoard transBoard = (FillapixBoard) trans.getBoard();
+ ArrayList transModCells = new ArrayList();
+ for (PuzzleElement modCell : modCells) {
+ transModCells.add((FillapixCell) transBoard.getPuzzleElement(modCell));
+ }
+
+ boolean[] translatedModCells = new boolean[transModCells.size()];
+ for (int i=0; i < transModCells.size(); i++) {
+ if (transModCells.get(i).getType() == FillapixCellType.BLACK) {
+ translatedModCells[i] = true;
+ }
+ else {
+ translatedModCells[i] = false;
+ }
+ }
+
+ // try to find the above state in the combinations, remove if found
+ boolean removed = false;
+ for (boolean[] combination : combinations) {
+ if (Arrays.equals(combination, translatedModCells)) {
+ combinations.remove(combination);
+ removed = true;
+ break;
+ }
+ }
+ // if combination not found, no need to check further, just quit
+ if (!removed) {
+ quitEarly = true;
+ break;
+ }
+ }
+
+ /* we found a center that is valid */
+ if (combinations.isEmpty() && !quitEarly) {
+ return null;
+ }
+ }
+
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+
+ @Override
+ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java
index e8dd32860..c37050978 100644
--- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooFewBlackCellsContradictionRule.java
@@ -6,8 +6,9 @@
import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
import edu.rpi.legup.puzzle.fillapix.FillapixCell;
import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
-import java.awt.*;
+import java.util.ArrayList;
public class TooFewBlackCellsContradictionRule extends ContradictionRule {
@@ -31,22 +32,24 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) {
FillapixBoard fillapixBoard = (FillapixBoard) board;
FillapixCell cell = (FillapixCell) fillapixBoard.getPuzzleElement(puzzleElement);
- Point loc = cell.getLocation();
- for (int i = -1; i < 2; i++) {
- for (int j = -1; j < 2; j++) {
- FillapixCell adjCell = fillapixBoard.getCell(loc.x + i, loc.y + j);
- if (adjCell != null) {
- int cellNum = adjCell.getNumber();
- if (cellNum >= 0) {
- int numBlackCells = fillapixBoard.getNumCells(adjCell, FillapixCellType.BLACK);
- int numUnknownCells = fillapixBoard.getNumCells(adjCell, FillapixCellType.UNKNOWN);
- if (numBlackCells + numUnknownCells < cellNum) {
- return null;
- }
- }
- }
+ int cellNum = cell.getNumber();
+ if (cellNum < 0 || cellNum >= 10) {
+ return super.getNoContradictionMessage();
+ }
+ int numBlack = 0, numEmpty = 0;
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(fillapixBoard, cell);
+ for (FillapixCell adjCell : adjCells) {
+ if (adjCell.getType() == FillapixCellType.BLACK) {
+ numBlack++;
+ }
+ if (adjCell.getType() == FillapixCellType.UNKNOWN) {
+ numEmpty++;
}
}
+ if (numBlack + numEmpty < cellNum) {
+ return null;
+ }
+
return super.getNoContradictionMessage();
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java
index 5a1192cc9..68395ce7f 100644
--- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TooManyBlackCellsContradictionRule.java
@@ -6,8 +6,9 @@
import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
import edu.rpi.legup.puzzle.fillapix.FillapixCell;
import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
-import java.awt.*;
+import java.util.ArrayList;
public class TooManyBlackCellsContradictionRule extends ContradictionRule {
@@ -31,21 +32,21 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) {
FillapixBoard fillapixBoard = (FillapixBoard) board;
FillapixCell cell = (FillapixCell) fillapixBoard.getPuzzleElement(puzzleElement);
- Point loc = cell.getLocation();
- for (int i = -1; i < 2; i++) {
- for (int j = -1; j < 2; j++) {
- FillapixCell adjCell = fillapixBoard.getCell(loc.x + i, loc.y + j);
- if (adjCell != null) {
- int cellNum = adjCell.getNumber();
- if (cellNum >= 0) {
- int numBlackCells = fillapixBoard.getNumCells(adjCell, FillapixCellType.BLACK);
- if (numBlackCells > cellNum) {
- return null;
- }
- }
- }
+ int cellNum = cell.getNumber();
+ if (cellNum < 0 || cellNum >= 10) {
+ return super.getNoContradictionMessage();
+ }
+ int numBlack = 0;
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(fillapixBoard, cell);
+ for (FillapixCell adjCell : adjCells) {
+ if (adjCell.getType() == FillapixCellType.BLACK) {
+ numBlack++;
}
}
+ if (numBlack > cellNum) {
+ return null;
+ }
+
return super.getNoContradictionMessage();
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java
new file mode 100644
index 000000000..6f4be7842
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingCornersDirectRule.java
@@ -0,0 +1,108 @@
+package edu.rpi.legup.puzzle.fillapix.rules;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import edu.rpi.legup.model.gameboard.Board;
+import edu.rpi.legup.model.gameboard.PuzzleElement;
+import edu.rpi.legup.model.rules.CaseRule;
+import edu.rpi.legup.model.rules.DirectRule;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
+import edu.rpi.legup.puzzle.fillapix.FillapixCell;
+import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
+
+public class TouchingCornersDirectRule extends DirectRule {
+ public TouchingCornersDirectRule() {
+ super("FPIX-BASC-0005",
+ "Touching Corners",
+ "Clues with touching corners have the same difference in black cells in their unshared regions as the difference in their numbers",
+ "edu/rpi/legup/images/fillapix/rules/TouchingCorners.png");
+ }
+
+ @Override
+ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
+ FillapixBoard board = (FillapixBoard) transition.getBoard();
+ FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy();
+ FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement);
+ FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement);
+
+ // cell has to have been empty before
+ if (parentCell.getType() != FillapixCellType.UNKNOWN) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // parentBoard cannot have any contradictions
+ if (FillapixUtilities.checkBoardForContradiction(parentBoard)) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // get all adjCells that have a number
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell);
+ adjCells.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10);
+ /* remove any number cell that does not have another number cell diagonally
+ * adjacent to it on the opposite side of the modified cell */
+ Iterator itr = adjCells.iterator();
+ while (itr.hasNext()) {
+ FillapixCell adjCell = itr.next();
+
+ boolean found = false;
+ ArrayList adjAdjCells = FillapixUtilities.getAdjacentCells(parentBoard, adjCell);
+ for (FillapixCell adjAdjCell : adjAdjCells) {
+ if (adjAdjCell.getLocation().x != adjCell.getLocation().x &&
+ adjAdjCell.getLocation().y != adjCell.getLocation().y &&
+ adjAdjCell.getNumber() >= 0 && adjAdjCell.getNumber() < 10 &&
+ adjAdjCell.getIndex() != parentCell.getIndex()) {
+ // adjAdjCell is diagonally adjacent to adjCell && it has a
+ // number && it is not parentCell
+ found = true;
+ }
+ }
+
+ // does not qualify for this rule
+ if (!found) {
+ itr.remove();
+ }
+ }
+
+ // change the cell to the opposite color
+ if (cell.getType() == FillapixCellType.BLACK) {
+ parentCell.setCellType(FillapixCellType.WHITE);
+ }
+ else {
+ parentCell.setCellType(FillapixCellType.BLACK);
+ }
+ // check for some contradiction in all cases
+ parentBoard.addModifiedData(parentCell);
+ CaseRule completeClue = new SatisfyClueCaseRule();
+ List caseBoards;
+ for (FillapixCell adjCell : adjCells) {
+ caseBoards = completeClue.getCases(parentBoard, adjCell);
+ boolean found = true;
+ for (Board b : caseBoards) {
+ if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) {
+ found = false;
+ }
+ }
+ if (found) {
+ return null;
+ }
+ }
+
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ /**
+ * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}.
+ *
+ * @param node tree node used to create default transition board
+ * @return default board or null if this rule cannot be applied to this tree node
+ */
+ @Override
+ public Board getDefaultBoard(TreeNode node) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java
new file mode 100644
index 000000000..bd6dd0169
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/TouchingSidesDirectRule.java
@@ -0,0 +1,115 @@
+package edu.rpi.legup.puzzle.fillapix.rules;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import edu.rpi.legup.model.gameboard.Board;
+import edu.rpi.legup.model.gameboard.PuzzleElement;
+import edu.rpi.legup.model.rules.CaseRule;
+import edu.rpi.legup.model.rules.DirectRule;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.fillapix.FillapixBoard;
+import edu.rpi.legup.puzzle.fillapix.FillapixCell;
+import edu.rpi.legup.puzzle.fillapix.FillapixCellType;
+import edu.rpi.legup.puzzle.fillapix.FillapixUtilities;
+
+public class TouchingSidesDirectRule extends DirectRule {
+ public TouchingSidesDirectRule() {
+ super("FPIX-BASC-0004",
+ "Touching Sides",
+ "Clues with touching sides have the same difference in black cells in their unshared regions as the difference in their numbers",
+ "edu/rpi/legup/images/fillapix/rules/TouchingSides.png");
+ }
+
+ @Override
+ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
+ FillapixBoard board = (FillapixBoard) transition.getBoard();
+ FillapixBoard parentBoard = (FillapixBoard) transition.getParents().get(0).getBoard().copy();
+ FillapixCell cell = (FillapixCell) board.getPuzzleElement(puzzleElement);
+ FillapixCell parentCell = (FillapixCell) parentBoard.getPuzzleElement(puzzleElement);
+
+ // cell has to have been empty before
+ if (parentCell.getType() != FillapixCellType.UNKNOWN) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // parentBoard cannot have any contradictions
+ if (FillapixUtilities.checkBoardForContradiction(parentBoard)) {
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ // get all adjCells that have a number
+ ArrayList adjCells = FillapixUtilities.getAdjacentCells(parentBoard, parentCell);
+ adjCells.removeIf(x -> x.getNumber() < 0 || x.getNumber() >= 10);
+ /* remove any number cell that does not have another number cell adjacent
+ * to it on the opposite side of the modified cell */
+ Iterator itr = adjCells.iterator();
+ while (itr.hasNext()) {
+ // calculate x and y offset of adjCell from cell
+ FillapixCell adjCell = itr.next();
+ int xOffset = adjCell.getLocation().x - cell.getLocation().x;
+ int yOffset = adjCell.getLocation().y - cell.getLocation().y;
+
+ boolean found = false;
+ // check vertically for numbered cell in opposite direction of cell
+ if (adjCell.getLocation().x + xOffset >= 0 && adjCell.getLocation().x < parentBoard.getWidth()) {
+ int adjNum = parentBoard.getCell(adjCell.getLocation().x + xOffset, adjCell.getLocation().y).getNumber();
+ if (adjNum >= 0 && adjNum < 10) {
+ found = true;
+ }
+ }
+ // check horizontally for numbered cell in opposite direction of cell
+ if (adjCell.getLocation().y + yOffset >= 0 && adjCell.getLocation().y < parentBoard.getHeight()) {
+ int adjNum = parentBoard.getCell(adjCell.getLocation().x, adjCell.getLocation().y + yOffset).getNumber();
+ if (adjNum >= 0 && adjNum < 10) {
+ found = true;
+ }
+ }
+
+ // if no horizontally or vertically adjacent cell on opposite side of 'cell' has number,
+ // then adjCell is not valid, so should be removed
+ if (!found) {
+ itr.remove();
+ }
+ }
+
+ // change the cell to the opposite color
+ if (cell.getType() == FillapixCellType.BLACK) {
+ parentCell.setCellType(FillapixCellType.WHITE);
+ }
+ else {
+ parentCell.setCellType(FillapixCellType.BLACK);
+ }
+ // check for some contradiction in all cases
+ parentBoard.addModifiedData(parentCell);
+ CaseRule completeClue = new SatisfyClueCaseRule();
+ List caseBoards;
+ for (FillapixCell adjCell : adjCells) {
+ caseBoards = completeClue.getCases(parentBoard, adjCell);
+ boolean found = true;
+ for (Board b : caseBoards) {
+ if (!FillapixUtilities.checkBoardForContradiction((FillapixBoard) b)) {
+ found = false;
+ }
+ }
+ if (found) {
+ return null;
+ }
+ }
+
+ return super.getInvalidUseOfRuleMessage();
+ }
+
+ /**
+ * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}.
+ *
+ * @param node tree node used to create default transition board
+ * @return default board or null if this rule cannot be applied to this tree node
+ */
+ @Override
+ public Board getDefaultBoard(TreeNode node) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt
index b6172e7fb..2e3c96a86 100644
--- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt
+++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/fillapix_reference_sheet.txt
@@ -1,7 +1,11 @@
FPIX-BASC-0001 : FinishWithBlackDirectRule
FPIX-BASC-0002 : FinishWithWhiteDirectRule
+FPIX-BASC-0003 : MirrorDirectRule
+FPIX-BASC-0004 : TouchingSidesDirectRule
+FPIX-BASC-0005 : TouchingCornersDirectRule
FPIX-CONT-0001 : TooFewBlackCellsContradictionRule
FPIX-CONT-0002 : TooManyBlackCellsContradictionRule
-FPIX-CASE-0001 : BlackOrWhiteCaseRule
\ No newline at end of file
+FPIX-CASE-0001 : BlackOrWhiteCaseRule
+FPIX-CASE-0002 : CompleteClueCaseRule
\ No newline at end of file
diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java
index b77f8a79f..d992fd22c 100644
--- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/SurroundRegionDirectRule.java
@@ -3,12 +3,20 @@
import edu.rpi.legup.model.gameboard.Board;
import edu.rpi.legup.model.gameboard.PuzzleElement;
import edu.rpi.legup.model.rules.DirectRule;
-import edu.rpi.legup.model.rules.ContradictionRule;
import edu.rpi.legup.model.tree.TreeNode;
import edu.rpi.legup.model.tree.TreeTransition;
import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard;
import edu.rpi.legup.puzzle.nurikabe.NurikabeCell;
import edu.rpi.legup.puzzle.nurikabe.NurikabeType;
+import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities;
+import edu.rpi.legup.utility.DisjointSets;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.awt.*;
public class SurroundRegionDirectRule extends DirectRule {
@@ -29,7 +37,6 @@ public SurroundRegionDirectRule() {
*/
@Override
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
- ContradictionRule contraRule = new TooManySpacesContradictionRule();
NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard();
NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard();
@@ -44,12 +51,35 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem
NurikabeCell modCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement);
modCell.setData(NurikabeType.WHITE.toValue());
- if (contraRule.checkContradiction(modified) == null) {
- return null;
- }
- else {
- return "Does not follow from this rule at this index";
+ if(cell.getType() == NurikabeType.BLACK) {
+ DisjointSets regions = NurikabeUtilities.getNurikabeRegions(destBoardState);
+ Set adj = new HashSet<>(); //set to hold adjacent cells
+ Point loc = cell.getLocation(); //position of placed cell
+ List directions = Arrays.asList(new Point(-1, 0), new Point(1, 0), new Point(0, -1), new Point(0, 1));
+ for(Point direction : directions) {
+ NurikabeCell curr = destBoardState.getCell(loc.x + direction.x, loc.y + direction.y);
+ if(curr != null) {
+ if(curr.getType() == NurikabeType.WHITE || curr.getType() == NurikabeType.NUMBER) {
+ adj.add(curr); //adds cells to adj only if they are white or number blocks
+ }
+ }
+ }
+ List numberedCells = new ArrayList<>(); //number value of number cells
+ for (NurikabeCell c : adj) { //loops through adjacent cells
+ Set disRow = regions.getSet(c); //set of white spaces
+ for (NurikabeCell d : disRow) { //loops through white spaces
+ if (d.getType() == NurikabeType.NUMBER) { //if the white space is a number
+ numberedCells.add(d); //add that number to numberedCells
+ }
+ }
+ }
+ for (NurikabeCell number : numberedCells) { //loops through numberedCells
+ if (regions.getSet(number).size() == number.getData()) { //if that cells white area is the exact
+ return null; //size of the number of one of the number cells within that set
+ }
+ }
}
+ return "Does not follow from this rule at this index";
}
/**
diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java
index fa08972ed..ecf59146d 100644
--- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java
+++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java
@@ -1,784 +1,756 @@
-package edu.rpi.legup.ui.proofeditorui.treeview;
-
-import edu.rpi.legup.app.GameBoardFacade;
-import edu.rpi.legup.controller.TreeController;
-import edu.rpi.legup.model.observer.ITreeListener;
-import edu.rpi.legup.model.tree.Tree;
-import edu.rpi.legup.model.tree.TreeElement;
-import edu.rpi.legup.model.tree.TreeNode;
-import edu.rpi.legup.model.tree.TreeTransition;
-import edu.rpi.legup.ui.ScrollView;
-import edu.rpi.legup.utility.DisjointSets;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.util.*;
-import java.util.List;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import static edu.rpi.legup.model.tree.TreeElementType.NODE;
-import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION;
-import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER;
-import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS;
-
-public class TreeView extends ScrollView implements ITreeListener {
- private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName());
-
- private static final int TRANS_GAP = 5;
-
- private static final int NODE_GAP_WIDTH = 70;
- private static final int NODE_GAP_HEIGHT = 15;
-
- private static final int BORDER_GAP_HEIGHT = 20;
- private static final int BORDER_GAP_WIDTH = 20;
-
- private static final int BORDER_SPACING = 100;
-
- private TreeNodeView nodeHover;
-
- private ArrayList currentStateBoxes;
- private Rectangle bounds = new Rectangle(0, 0, 0, 0);
-
- private Tree tree;
- private TreeNodeView rootNodeView;
- private Map viewMap;
- private Dimension dimension;
-
- private TreeViewSelection selection;
-
- public TreeView(TreeController treeController) {
- super(treeController);
- currentStateBoxes = new ArrayList<>();
- setSize(dimension = new Dimension(100, 200));
- setPreferredSize(new Dimension(640, 160));
-
- viewMap = new HashMap<>();
-
- selection = new TreeViewSelection();
- }
-
- public TreeViewSelection getSelection() {
- return selection;
- }
-
- /**
- * Gets the tree node puzzleElement that the mouse is hovering over
- *
- * @return tree node puzzleElement that the mouse is hovering over
- */
- public TreeNodeView getNodeHover() {
- return nodeHover;
- }
-
- /**
- * Sets the tree node puzzleElement that the mouse is hovering over
- *
- * @param nodeHover tree node puzzleElement the mouse is hovering over
- */
- public void setNodeHover(TreeNodeView nodeHover) {
- this.nodeHover = nodeHover;
- }
-
- /**
- * Gets the TreeElementView by the specified point or null if no view exists at the specified point
- *
- * @param point location to query for a view
- * @return TreeElementView at the point specified, otherwise null
- */
- public TreeElementView getTreeElementView(Point point) {
- return getTreeElementView(point, rootNodeView);
- }
-
- /**
- * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or
- * the view specified is null
- *
- * @param point location to query for a view
- * @param elementView view to determine if the point is contained within it
- * @return TreeElementView at the point specified, otherwise null
- */
- private TreeElementView getTreeElementView(Point point, TreeElementView elementView) {
- if (elementView == null) {
- return null;
- }
- else {
- if (elementView.contains(point) && elementView.isVisible()) {
- if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) {
- return null;
- }
- return elementView;
- }
- else {
- if (elementView.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) elementView;
- for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
- TreeElementView view = getTreeElementView(point, transitionView);
- if (view != null) {
- return view;
- }
- }
- }
- else {
- TreeTransitionView transitionView = (TreeTransitionView) elementView;
- return getTreeElementView(point, transitionView.getChildView());
- }
- }
- }
- return null;
- }
-
- public void updateTreeView(Tree tree) {
- this.tree = tree;
- if (selection.getSelectedViews().size() == 0) {
- selection.newSelection(new TreeNodeView(tree.getRootNode()));
- }
- repaint();
- }
-
- /**
- * Sets the tree associated with this TreeView
- *
- * @param tree tree
- */
- public void setTree(Tree tree) {
- this.tree = tree;
- }
-
- public void updateTreeSize() {
- if (GameBoardFacade.getInstance().getTree() == null) {
- return;
- }
- setSize(bounds.getSize());
- }
-
- public void reset() {
- if (bounds.x != 0 || bounds.y != 0) {
- updateTreeSize();
- }
- }
-
- public void zoomFit() {
- double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200);
- double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120);
- zoomTo(Math.min(fitWidth, fitHeight));
- viewport.setViewPosition(new Point(0, viewport.getHeight() / 2));
- }
-
- /**
- * Creates a customized viewport for the scroll pane
- *
- * @return viewport for the scroll pane
- */
- @Override
- protected JViewport createViewport() {
- return new JViewport() {
- @Override
- protected LayoutManager createLayoutManager() {
- return new ViewportLayout() {
- @Override
- public void layoutContainer(Container parent) {
- Point point = viewport.getViewPosition();
- // determine the maximum x and y view positions
- int mx = getCanvas().getWidth() - viewport.getWidth();
- int my = getCanvas().getHeight() - viewport.getHeight();
- // obey edge boundaries
- if (point.x < 0) {
- point.x = 0;
- }
- if (point.x > mx) {
- point.x = mx;
- }
- if (point.y < 0) {
- point.y = 0;
- }
- if (point.y > my) {
- point.y = my;
- }
- // center margins
- if (mx < 0) {
- point.x = 0;
- }
- if (my < 0) {
- point.y = my / 2;
- }
- viewport.setViewPosition(point);
- }
- };
- }
- };
- }
-
- public void draw(Graphics2D graphics2D) {
- currentStateBoxes.clear();
- Tree tree = GameBoardFacade.getInstance().getTree();
- if (tree != null) {
- //setSize(bounds.getDimension());
- graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-
- drawTree(graphics2D);
-
- dimension.width += BORDER_SPACING;
- setSize(dimension);
-// graphics2D.drawRect(0,0, dimension.width, dimension.height);
-
- if (selection.getHover() != null) {
- drawMouseOver(graphics2D);
- }
- }
- }
-
- public void zoomReset() {
- zoomTo(1.0);
- viewport.setViewPosition(new Point(0, 0));
- }
-
- private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) {
- if (nodeView != null) {
- nodeView.draw(graphics2D);
- for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
- transitionView.draw(graphics2D);
- redrawTree(graphics2D, transitionView.getChildView());
- }
- }
- }
-
- public void removeTreeElement(TreeElementView view) {
- if (view.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) view;
- nodeView.getParentView().setChildView(null);
- }
- else {
- TreeTransitionView transitionView = (TreeTransitionView) view;
- transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView));
- }
- }
-
- /**
- * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image
- *
- * @param g the graphics to use to draw
- */
- public void drawMouseOver(Graphics2D g) {
- if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) {
- TreeTransition transition = (TreeTransition) selection.getHover().treeElement;
- int imgWidth = 100;
- int imgHeight = 100;
-
- BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
- image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null);
- Point mousePoint = selection.getMousePoint();
- g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null);
- }
- }
-
- public void resetView() {
- this.tree = null;
- this.rootNodeView = null;
- this.selection.clearSelection();
- this.selection.clearHover();
- }
-
- /**
- * Called when a tree puzzleElement is added to the tree
- *
- * @param treeElement TreeElement that was added to the tree
- */
- @Override
- public void onTreeElementAdded(TreeElement treeElement) {
- if (treeElement.getType() == NODE) {
- addTreeNode((TreeNode) treeElement);
- }
- else {
- addTreeTransition((TreeTransition) treeElement);
- }
- repaint();
- }
-
- /**
- * Called when a tree puzzleElement is removed from the tree
- *
- * @param element TreeElement that was removed to the tree
- */
- @Override
- public void onTreeElementRemoved(TreeElement element) {
- if (element.getType() == NODE) {
- TreeNode node = (TreeNode) element;
- TreeNodeView nodeView = (TreeNodeView) viewMap.get(node);
-
- nodeView.getParentView().setChildView(null);
- removeTreeNode(node);
- }
- else {
- TreeTransition trans = (TreeTransition) element;
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
-
- transView.getParentViews().forEach(n -> n.removeChildrenView(transView));
- removeTreeTransition(trans);
- }
- repaint();
- }
-
- /**
- * Called when the tree selection was changed
- *
- * @param selection tree selection that was changed
- */
- @Override
- public void onTreeSelectionChanged(TreeViewSelection selection) {
- this.selection.getSelectedViews().forEach(v -> v.setSelected(false));
- selection.getSelectedViews().forEach(v -> v.setSelected(true));
-
- this.selection = selection;
-
-// List selectedViews = selection.getSelectedViews();
-// Point avg = new Point();
-// for(TreeElementView elementView : selectedViews) {
-// if(elementView.getType() == NODE) {
-// TreeNodeView nodeView = (TreeNodeView)elementView;
-// avg.x += nodeView.getX();
-// avg.y += nodeView.getY();
-// } else {
-// TreeTransitionView transitionView = (TreeTransitionView) elementView;
-// avg.x += transitionView.getEndX();
-// avg.y += transitionView.getEndY();
-// }
-// }
-// avg.x /= selectedViews.size();
-// avg.y /= selectedViews.size();
-//
-// Point pos = viewport.getViewPosition();
-// if(pos.x < avg.x) {
-// pos.x = avg.x - pos.x;
-// }
-// if(pos.y < avg.y) {
-// pos.y = avg.y - pos.y;
-// }
-//
-// viewport.setViewPosition(pos);
-
- repaint();
- }
-
- /**
- * Called when the model has finished updating the tree.
- */
- @Override
- public void onUpdateTree() {
- repaint();
- }
-
- /**
- * Gets the TreeElementView by the corresponding TreeElement associated with it
- *
- * @param element TreeElement of the view
- * @return TreeElementView of the TreeElement associated with it
- */
- public TreeElementView getElementView(TreeElement element) {
- return viewMap.get(element);
- }
-
- private void removeTreeNode(TreeNode node) {
- viewMap.remove(node);
- node.getChildren().forEach(t -> removeTreeTransition(t));
- }
-
- private void removeTreeTransition(TreeTransition trans) {
- viewMap.remove(trans);
- if (trans.getChildNode() != null) {
- removeTreeNode(trans.getChildNode());
- }
- }
-
- private void addTreeNode(TreeNode node) {
- TreeTransition parent = node.getParent();
-
- TreeNodeView nodeView = new TreeNodeView(node);
- TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent);
-
- nodeView.setParentView(parentView);
- parentView.setChildView(nodeView);
-
- viewMap.put(node, nodeView);
-
- if (!node.getChildren().isEmpty()) {
- node.getChildren().forEach(t -> addTreeTransition(t));
- }
- }
-
- private void addTreeTransition(TreeTransition trans) {
- List parents = trans.getParents();
-
- TreeTransitionView transView = new TreeTransitionView(trans);
- for (TreeNode parent : parents) {
- TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent);
- transView.addParentView(parentNodeView);
- parentNodeView.addChildrenView(transView);
- }
-
- viewMap.put(trans, transView);
-
- if (trans.getChildNode() != null) {
- addTreeNode(trans.getChildNode());
- }
- }
-
- ///New Draw Methods
-
- public void drawTree(Graphics2D graphics2D) {
- if (tree == null) {
- LOGGER.error("Unable to draw tree.");
- }
- else {
- if (rootNodeView == null) {
- rootNodeView = new TreeNodeView(tree.getRootNode());
-
- LOGGER.debug("Creating new views for tree view.");
- createViews(rootNodeView);
-
- selection.newSelection(rootNodeView);
- }
-
- dimension = new Dimension(0, 0);
- calcSpan(rootNodeView);
- rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING);
-
- calculateViewLocations(rootNodeView, 0);
- dimension.height = (int) rootNodeView.getSpan();
-
- redrawTree(graphics2D, rootNodeView);
- LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height);
- }
- }
-
- public void createViews(TreeNodeView nodeView) {
- if (nodeView != null) {
- viewMap.put(nodeView.getTreeElement(), nodeView);
-
- TreeNode node = nodeView.getTreeElement();
- for (TreeTransition trans : node.getChildren()) {
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
- if (transView != null) {
- nodeView.addChildrenView(transView);
- transView.addParentView(nodeView);
- break;
- }
- transView = new TreeTransitionView(trans);
-
- viewMap.put(transView.getTreeElement(), transView);
-
- transView.addParentView(nodeView);
- nodeView.addChildrenView(transView);
-
- TreeNode childNode = trans.getChildNode();
- if (childNode != null) {
- TreeNodeView childNodeView = new TreeNodeView(childNode);
- viewMap.put(childNodeView.getTreeElement(), childNodeView);
-
- childNodeView.setParentView(transView);
- transView.setChildView(childNodeView);
-
- createViews(childNodeView);
- }
- }
- }
- }
-
- public void calculateViewLocations(TreeNodeView nodeView, int depth) {
- nodeView.setDepth(depth);
- int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER;
- nodeView.setX(xLoc);
- dimension.width = Math.max(dimension.width, xLoc);
-
- TreeTransitionView parentTransView = nodeView.getParentView();
- int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY();
- nodeView.setY(yLoc);
-
- ArrayList children = nodeView.getChildrenViews();
- switch (children.size()) {
- case 0:
- break;
- case 1: {
- TreeTransitionView childView = children.get(0);
-
- List parentsViews = childView.getParentViews();
- if (parentsViews.size() == 1) {
- childView.setEndY(yLoc);
-
- childView.setDepth(depth);
-
- Point lineStartPoint = childView.getLineStartPoint(0);
- lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
- lineStartPoint.y = yLoc;
- childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
-
- dimension.width = Math.max(dimension.width, childView.getEndX());
-
- TreeNodeView childNodeView = childView.getChildView();
- if (childNodeView != null) {
- calculateViewLocations(childNodeView, depth + 1);
- }
- }
- else {
- if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) {
- int yAvg = 0;
- for (int i = 0; i < parentsViews.size(); i++) {
- TreeNodeView parentNodeView = parentsViews.get(i);
- depth = Math.max(depth, parentNodeView.getDepth());
- yAvg += parentNodeView.getY();
-
- Point lineStartPoint = childView.getLineStartPoint(i);
- lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2;
- lineStartPoint.y = parentNodeView.getY();
- }
- yAvg /= parentsViews.size();
- childView.setEndY(yAvg);
-
- childView.setDepth(depth);
-
- childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
-
- dimension.width = Math.max(dimension.width, childView.getEndX());
-
- TreeNodeView childNodeView = childView.getChildView();
- if (childNodeView != null) {
- calculateViewLocations(childNodeView, depth + 1);
- }
- }
- }
- break;
- }
- default: {
- int span = 0;
- for (TreeTransitionView childView : children) {
- span += childView.getSpan();
- }
-
- span = (int) ((nodeView.getSpan() - span) / 2);
- for (int i = 0; i < children.size(); i++) {
- TreeTransitionView childView = children.get(i);
-
- childView.setDepth(depth);
-
- Point lineStartPoint = childView.getLineStartPoint(0);
- lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
- lineStartPoint.y = yLoc;
- childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
- childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2));
-
- span += childView.getSpan();
- TreeNodeView childNodeView = childView.getChildView();
- if (childNodeView != null) {
- calculateViewLocations(childNodeView, depth + 1);
- }
- }
- break;
- }
- }
- }
-
- public void calcSpan(TreeElementView view) {
- if (view.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) view;
- TreeNode node = nodeView.getTreeElement();
- if (nodeView.getChildrenViews().size() == 0) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- if (nodeView.getChildrenViews().size() == 1) {
- TreeTransitionView childView = nodeView.getChildrenViews().get(0);
- calcSpan(childView);
- if (childView.getParentViews().size() > 1) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- nodeView.setSpan(childView.getSpan());
- }
- }
- else {
- DisjointSets branches = node.findMergingBranches();
- List children = node.getChildren();
-
- if (node == children.get(0).getParents().get(0)) {
- reorderBranches(node, branches);
- ArrayList newChildrenViews = new ArrayList<>();
- for (TreeTransition trans : node.getChildren()) {
- newChildrenViews.add((TreeTransitionView) viewMap.get(trans));
- }
- nodeView.setChildrenViews(newChildrenViews);
- }
-
- List> mergingSets = branches.getAllSets();
-
- double span = 0.0;
- for (Set mergeSet : mergingSets) {
- if (mergeSet.size() > 1) {
- TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
- TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
- double subSpan = 0.0;
- for (TreeTransition branch : mergeSet) {
- TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
- subCalcSpan(branchView, mergePointView);
- subSpan += branchView.getSpan();
- }
- calcSpan(mergePointView);
- span += Math.max(mergePointView.getSpan(), subSpan);
- }
- else {
- TreeTransition trans = mergeSet.iterator().next();
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
- calcSpan(transView);
- span += transView.getSpan();
- }
- }
- nodeView.setSpan(span);
- }
- }
- }
- else {
- TreeTransitionView transView = (TreeTransitionView) view;
- TreeNodeView nodeView = transView.getChildView();
- if (nodeView == null) {
- transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- calcSpan(nodeView);
- transView.setSpan(nodeView.getSpan());
- }
- }
- }
-
- /**
- * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view
- * specified as stop. Stop tree puzzleElement is NOT included in the span calculation
- *
- * @param view
- * @param stop
- */
- private void subCalcSpan(TreeElementView view, TreeElementView stop) {
- //safe-guard for infinite loop
- if (view == stop) {
- return;
- }
-
- if (view.getType() == NODE) {
- TreeNodeView nodeView = (TreeNodeView) view;
- TreeNode node = nodeView.getTreeElement();
- if (nodeView.getChildrenViews().size() == 0) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- if (nodeView.getChildrenViews().size() == 1) {
- TreeTransitionView childView = nodeView.getChildrenViews().get(0);
- if (childView == stop) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- subCalcSpan(childView, stop);
- if (childView.getParentViews().size() > 1) {
- nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- nodeView.setSpan(childView.getSpan());
- }
- }
- }
- else {
- DisjointSets branches = node.findMergingBranches();
- List children = node.getChildren();
-
- if (node == children.get(0).getParents().get(0)) {
- reorderBranches(node, branches);
- }
-
- List> mergingSets = branches.getAllSets();
-
- double span = 0.0;
- for (Set mergeSet : mergingSets) {
- if (mergeSet.size() > 1) {
- TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
- TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
- double subSpan = 0.0;
- for (TreeTransition branch : mergeSet) {
- TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
- subCalcSpan(branchView, mergePointView);
- subSpan += branchView.getSpan();
- }
- subCalcSpan(mergePointView, stop);
- span += Math.max(mergePointView.getSpan(), subSpan);
- }
- else {
- TreeTransition trans = mergeSet.iterator().next();
- TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
- subCalcSpan(transView, stop);
- span += transView.getSpan();
- }
- }
-
- nodeView.setSpan(span);
- }
- }
- }
- else {
- TreeTransitionView transView = (TreeTransitionView) view;
- TreeNodeView nodeView = transView.getChildView();
- if (nodeView == null || nodeView == stop) {
- transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
- }
- else {
- calcSpan(nodeView);
- transView.setSpan(nodeView.getSpan());
- }
- }
- }
-
- /**
- * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in
- * relative order in the list of child transitions of the specified node
- *
- * @param node root node of the branches
- * @param branches DisjointSets of the child branches of the specified node which determine which branches merge
- */
- private void reorderBranches(TreeNode node, DisjointSets branches) {
- List children = node.getChildren();
- List> mergingSets = branches.getAllSets();
-
- List> newOrder = new ArrayList<>();
- for (Set set : mergingSets) {
- List mergeBranch = new ArrayList<>();
- newOrder.add(mergeBranch);
- children.forEach(t -> {
- if (set.contains(t)) {
- mergeBranch.add(t);
- }
- });
- mergeBranch.sort((TreeTransition t1, TreeTransition t2) ->
- children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1);
- }
-
- newOrder.sort((List b1, List b2) -> {
- int low1 = -1;
- int low2 = -1;
- for (TreeTransition t1 : b1) {
- int curIndex = children.indexOf(t1);
- if (low1 == -1 || curIndex < low1) {
- low1 = curIndex;
- }
- }
- for (TreeTransition t1 : b2) {
- int curIndex = children.indexOf(t1);
- if (low1 == -1 || curIndex < low1) {
- low1 = curIndex;
- }
- }
- return low1 < low2 ? -1 : 1;
- });
-
- List newChildren = new ArrayList<>();
- newOrder.forEach(l -> newChildren.addAll(l));
- node.setChildren(newChildren);
- }
+package edu.rpi.legup.ui.proofeditorui.treeview;
+
+import edu.rpi.legup.app.GameBoardFacade;
+import edu.rpi.legup.controller.TreeController;
+import edu.rpi.legup.model.observer.ITreeListener;
+import edu.rpi.legup.model.tree.Tree;
+import edu.rpi.legup.model.tree.TreeElement;
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.ui.ScrollView;
+import edu.rpi.legup.utility.DisjointSets;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.*;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static edu.rpi.legup.model.tree.TreeElementType.NODE;
+import static edu.rpi.legup.model.tree.TreeElementType.TRANSITION;
+import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.DIAMETER;
+import static edu.rpi.legup.ui.proofeditorui.treeview.TreeNodeView.RADIUS;
+
+public class TreeView extends ScrollView implements ITreeListener {
+ private final static Logger LOGGER = LogManager.getLogger(TreeView.class.getName());
+
+ private static final int TRANS_GAP = 5;
+
+ private static final int NODE_GAP_WIDTH = 70;
+ private static final int NODE_GAP_HEIGHT = 15;
+
+ private static final int BORDER_GAP_HEIGHT = 20;
+ private static final int BORDER_GAP_WIDTH = 20;
+
+ private static final int BORDER_SPACING = 100;
+
+ private TreeNodeView nodeHover;
+
+ private ArrayList currentStateBoxes;
+ private Rectangle bounds = new Rectangle(0, 0, 0, 0);
+
+ private Tree tree;
+ private TreeNodeView rootNodeView;
+ private Map viewMap;
+ private Dimension dimension;
+
+ private TreeViewSelection selection;
+
+ public TreeView(TreeController treeController) {
+ super(treeController);
+ currentStateBoxes = new ArrayList<>();
+ setSize(dimension = new Dimension(100, 200));
+ setPreferredSize(new Dimension(640, 160));
+
+ viewMap = new HashMap<>();
+
+ selection = new TreeViewSelection();
+ }
+
+ public TreeViewSelection getSelection() {
+ return selection;
+ }
+
+ /**
+ * Gets the tree node puzzleElement that the mouse is hovering over
+ *
+ * @return tree node puzzleElement that the mouse is hovering over
+ */
+ public TreeNodeView getNodeHover() {
+ return nodeHover;
+ }
+
+ /**
+ * Sets the tree node puzzleElement that the mouse is hovering over
+ *
+ * @param nodeHover tree node puzzleElement the mouse is hovering over
+ */
+ public void setNodeHover(TreeNodeView nodeHover) {
+ this.nodeHover = nodeHover;
+ }
+
+ /**
+ * Gets the TreeElementView by the specified point or null if no view exists at the specified point
+ *
+ * @param point location to query for a view
+ * @return TreeElementView at the point specified, otherwise null
+ */
+ public TreeElementView getTreeElementView(Point point) {
+ return getTreeElementView(point, rootNodeView);
+ }
+
+ /**
+ * Recursively gets the TreeElementView by the specified point or null if no view exists at the specified point or
+ * the view specified is null
+ *
+ * @param point location to query for a view
+ * @param elementView view to determine if the point is contained within it
+ * @return TreeElementView at the point specified, otherwise null
+ */
+ private TreeElementView getTreeElementView(Point point, TreeElementView elementView) {
+ if (elementView == null) {
+ return null;
+ }
+ else {
+ if (elementView.contains(point) && elementView.isVisible()) {
+ if (elementView.getType() == NODE && ((TreeNodeView) elementView).isContradictoryState()) {
+ return null;
+ }
+ return elementView;
+ }
+ else {
+ if (elementView.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) elementView;
+ for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
+ TreeElementView view = getTreeElementView(point, transitionView);
+ if (view != null) {
+ return view;
+ }
+ }
+ }
+ else {
+ TreeTransitionView transitionView = (TreeTransitionView) elementView;
+ return getTreeElementView(point, transitionView.getChildView());
+ }
+ }
+ }
+ return null;
+ }
+
+ public void updateTreeView(Tree tree) {
+ this.tree = tree;
+ if (selection.getSelectedViews().size() == 0) {
+ selection.newSelection(new TreeNodeView(tree.getRootNode()));
+ }
+ repaint();
+ }
+
+ /**
+ * Sets the tree associated with this TreeView
+ *
+ * @param tree tree
+ */
+ public void setTree(Tree tree) {
+ this.tree = tree;
+ }
+
+ public void updateTreeSize() {
+ if (GameBoardFacade.getInstance().getTree() == null) {
+ return;
+ }
+ setSize(bounds.getSize());
+ }
+
+ public void reset() {
+ if (bounds.x != 0 || bounds.y != 0) {
+ updateTreeSize();
+ }
+ }
+
+ public void zoomFit() {
+ double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200);
+ double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120);
+ zoomTo(Math.min(fitWidth, fitHeight));
+ viewport.setViewPosition(new Point(0, viewport.getHeight() / 2));
+ }
+
+ /**
+ * Creates a customized viewport for the scroll pane
+ *
+ * @return viewport for the scroll pane
+ */
+ @Override
+ protected JViewport createViewport() {
+ return new JViewport() {
+ @Override
+ protected LayoutManager createLayoutManager() {
+ return new ViewportLayout() {
+ @Override
+ public void layoutContainer(Container parent) {
+ Point point = viewport.getViewPosition();
+ // determine the maximum x and y view positions
+ int mx = getCanvas().getWidth() - viewport.getWidth();
+ int my = getCanvas().getHeight() - viewport.getHeight();
+ // obey edge boundaries
+ if (point.x < 0) {
+ point.x = 0;
+ }
+ if (point.x > mx) {
+ point.x = mx;
+ }
+ if (point.y < 0) {
+ point.y = 0;
+ }
+ if (point.y > my) {
+ point.y = my;
+ }
+ // center margins
+ if (mx < 0) {
+ point.x = 0;
+ }
+ if (my < 0) {
+ point.y = my / 2;
+ }
+ viewport.setViewPosition(point);
+ }
+ };
+ }
+ };
+ }
+
+ public void draw(Graphics2D graphics2D) {
+ currentStateBoxes.clear();
+ Tree tree = GameBoardFacade.getInstance().getTree();
+ if (tree != null) {
+ //setSize(bounds.getDimension());
+ graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+ drawTree(graphics2D);
+
+ dimension.width += BORDER_SPACING;
+ setSize(dimension);
+// graphics2D.drawRect(0,0, dimension.width, dimension.height);
+
+ if (selection.getHover() != null) {
+ drawMouseOver(graphics2D);
+ }
+ }
+ }
+
+ public void zoomReset() {
+ zoomTo(1.0);
+ viewport.setViewPosition(new Point(0, 0));
+ }
+
+ private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) {
+ if (nodeView != null) {
+ nodeView.draw(graphics2D);
+ for (TreeTransitionView transitionView : nodeView.getChildrenViews()) {
+ transitionView.draw(graphics2D);
+ redrawTree(graphics2D, transitionView.getChildView());
+ }
+ }
+ }
+
+ public void removeTreeElement(TreeElementView view) {
+ if (view.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) view;
+ nodeView.getParentView().setChildView(null);
+ }
+ else {
+ TreeTransitionView transitionView = (TreeTransitionView) view;
+ transitionView.getParentViews().forEach((TreeNodeView n) -> n.removeChildrenView(transitionView));
+ }
+ }
+
+ /**
+ * When the edu.rpi.legup.user hovers over the transition, draws the corresponding rules image
+ *
+ * @param g the graphics to use to draw
+ */
+ public void drawMouseOver(Graphics2D g) {
+ if (selection.getHover().getType() == TRANSITION && ((TreeTransitionView) selection.getHover()).getTreeElement().isJustified()) {
+ TreeTransition transition = (TreeTransition) selection.getHover().treeElement;
+ int imgWidth = 100;
+ int imgHeight = 100;
+
+ BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
+ image.createGraphics().drawImage(transition.getRule().getImageIcon().getImage(), 0, 0, null);
+ Point mousePoint = selection.getMousePoint();
+ g.drawImage(image, mousePoint.x, mousePoint.y - 50, imgWidth, imgHeight, null);
+ }
+ }
+
+ public void resetView() {
+ this.tree = null;
+ this.rootNodeView = null;
+ this.selection.clearSelection();
+ this.selection.clearHover();
+ }
+
+ /**
+ * Called when a tree puzzleElement is added to the tree
+ *
+ * @param treeElement TreeElement that was added to the tree
+ */
+ @Override
+ public void onTreeElementAdded(TreeElement treeElement) {
+ if (treeElement.getType() == NODE) {
+ addTreeNode((TreeNode) treeElement);
+ }
+ else {
+ addTreeTransition((TreeTransition) treeElement);
+ }
+ repaint();
+ }
+
+ /**
+ * Called when a tree puzzleElement is removed from the tree
+ *
+ * @param element TreeElement that was removed to the tree
+ */
+ @Override
+ public void onTreeElementRemoved(TreeElement element) {
+ if (element.getType() == NODE) {
+ TreeNode node = (TreeNode) element;
+ TreeNodeView nodeView = (TreeNodeView) viewMap.get(node);
+
+ nodeView.getParentView().setChildView(null);
+ removeTreeNode(node);
+ }
+ else {
+ TreeTransition trans = (TreeTransition) element;
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+
+ transView.getParentViews().forEach(n -> n.removeChildrenView(transView));
+ removeTreeTransition(trans);
+ }
+ repaint();
+ }
+
+ /**
+ * Called when the tree selection was changed
+ *
+ * @param selection tree selection that was changed
+ */
+ @Override
+ public void onTreeSelectionChanged(TreeViewSelection selection) {
+ this.selection.getSelectedViews().forEach(v -> v.setSelected(false));
+ selection.getSelectedViews().forEach(v -> v.setSelected(true));
+ this.selection = selection;
+ repaint();
+ }
+
+ /**
+ * Called when the model has finished updating the tree.
+ */
+ @Override
+ public void onUpdateTree() {
+ repaint();
+ }
+
+ /**
+ * Gets the TreeElementView by the corresponding TreeElement associated with it
+ *
+ * @param element TreeElement of the view
+ * @return TreeElementView of the TreeElement associated with it
+ */
+ public TreeElementView getElementView(TreeElement element) {
+ return viewMap.get(element);
+ }
+
+ private void removeTreeNode(TreeNode node) {
+ viewMap.remove(node);
+ node.getChildren().forEach(t -> removeTreeTransition(t));
+ }
+
+ private void removeTreeTransition(TreeTransition trans) {
+ viewMap.remove(trans);
+ if (trans.getChildNode() != null) {
+ removeTreeNode(trans.getChildNode());
+ }
+ }
+
+ private void addTreeNode(TreeNode node) {
+ TreeTransition parent = node.getParent();
+
+ TreeNodeView nodeView = new TreeNodeView(node);
+ TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent);
+
+ nodeView.setParentView(parentView);
+ parentView.setChildView(nodeView);
+
+ viewMap.put(node, nodeView);
+
+ if (!node.getChildren().isEmpty()) {
+ node.getChildren().forEach(t -> addTreeTransition(t));
+ }
+ }
+
+ private void addTreeTransition(TreeTransition trans) {
+ List parents = trans.getParents();
+
+ TreeTransitionView transView = new TreeTransitionView(trans);
+ for (TreeNode parent : parents) {
+ TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent);
+ transView.addParentView(parentNodeView);
+ parentNodeView.addChildrenView(transView);
+ }
+
+ viewMap.put(trans, transView);
+
+ if (trans.getChildNode() != null) {
+ addTreeNode(trans.getChildNode());
+ }
+ }
+
+ ///New Draw Methods
+
+ public void drawTree(Graphics2D graphics2D) {
+ if (tree == null) {
+ LOGGER.error("Unable to draw tree.");
+ }
+ else {
+ if (rootNodeView == null) {
+ rootNodeView = new TreeNodeView(tree.getRootNode());
+
+ LOGGER.debug("Creating new views for tree view.");
+ createViews(rootNodeView);
+
+ selection.newSelection(rootNodeView);
+ }
+
+ dimension = new Dimension(0, 0);
+ calcSpan(rootNodeView);
+ rootNodeView.setSpan(rootNodeView.getSpan() + DIAMETER + BORDER_SPACING);
+
+ calculateViewLocations(rootNodeView, 0);
+ dimension.height = (int) rootNodeView.getSpan();
+
+ redrawTree(graphics2D, rootNodeView);
+ LOGGER.debug("DrawTree: dimensions - " + dimension.width + "x" + dimension.height);
+ }
+ }
+
+ public void createViews(TreeNodeView nodeView) {
+ if (nodeView != null) {
+ viewMap.put(nodeView.getTreeElement(), nodeView);
+
+ TreeNode node = nodeView.getTreeElement();
+ for (TreeTransition trans : node.getChildren()) {
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+ if (transView != null) {
+ nodeView.addChildrenView(transView);
+ transView.addParentView(nodeView);
+ break;
+ }
+ transView = new TreeTransitionView(trans);
+
+ viewMap.put(transView.getTreeElement(), transView);
+
+ transView.addParentView(nodeView);
+ nodeView.addChildrenView(transView);
+
+ TreeNode childNode = trans.getChildNode();
+ if (childNode != null) {
+ TreeNodeView childNodeView = new TreeNodeView(childNode);
+ viewMap.put(childNodeView.getTreeElement(), childNodeView);
+
+ childNodeView.setParentView(transView);
+ transView.setChildView(childNodeView);
+
+ createViews(childNodeView);
+ }
+ }
+ }
+ }
+
+ public void calculateViewLocations(TreeNodeView nodeView, int depth) {
+ nodeView.setDepth(depth);
+ int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER;
+ nodeView.setX(xLoc);
+ dimension.width = Math.max(dimension.width, xLoc);
+
+ TreeTransitionView parentTransView = nodeView.getParentView();
+ int yLoc = parentTransView == null ? (int) nodeView.getSpan() / 2 : parentTransView.getEndY();
+ nodeView.setY(yLoc);
+
+ ArrayList children = nodeView.getChildrenViews();
+ switch (children.size()) {
+ case 0:
+ break;
+ case 1: {
+ TreeTransitionView childView = children.get(0);
+
+ List parentsViews = childView.getParentViews();
+ if (parentsViews.size() == 1) {
+ childView.setEndY(yLoc);
+
+ childView.setDepth(depth);
+
+ Point lineStartPoint = childView.getLineStartPoint(0);
+ lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
+ lineStartPoint.y = yLoc;
+ childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
+
+ dimension.width = Math.max(dimension.width, childView.getEndX());
+
+ TreeNodeView childNodeView = childView.getChildView();
+ if (childNodeView != null) {
+ calculateViewLocations(childNodeView, depth + 1);
+ }
+ }
+ else {
+ if (parentsViews.size() > 1 && parentsViews.get(parentsViews.size() - 1) == nodeView) {
+ int yAvg = 0;
+ for (int i = 0; i < parentsViews.size(); i++) {
+ TreeNodeView parentNodeView = parentsViews.get(i);
+ depth = Math.max(depth, parentNodeView.getDepth());
+ yAvg += parentNodeView.getY();
+
+ Point lineStartPoint = childView.getLineStartPoint(i);
+ lineStartPoint.x = parentNodeView.getX() + RADIUS + TRANS_GAP / 2;
+ lineStartPoint.y = parentNodeView.getY();
+ }
+ yAvg /= parentsViews.size();
+ childView.setEndY(yAvg);
+
+ childView.setDepth(depth);
+
+ childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
+
+ dimension.width = Math.max(dimension.width, childView.getEndX());
+
+ TreeNodeView childNodeView = childView.getChildView();
+ if (childNodeView != null) {
+ calculateViewLocations(childNodeView, depth + 1);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ int span = 0;
+ for (TreeTransitionView childView : children) {
+ span += childView.getSpan();
+ }
+
+ span = (int) ((nodeView.getSpan() - span) / 2);
+ for (int i = 0; i < children.size(); i++) {
+ TreeTransitionView childView = children.get(i);
+
+ childView.setDepth(depth);
+
+ Point lineStartPoint = childView.getLineStartPoint(0);
+ lineStartPoint.x = xLoc + RADIUS + TRANS_GAP / 2;
+ lineStartPoint.y = yLoc;
+ childView.setEndX((NODE_GAP_WIDTH + DIAMETER) * (depth + 1) + RADIUS - TRANS_GAP / 2);
+ childView.setEndY(yLoc - (int) (nodeView.getSpan() / 2) + span + (int) (childView.getSpan() / 2));
+
+ span += childView.getSpan();
+ TreeNodeView childNodeView = childView.getChildView();
+ if (childNodeView != null) {
+ calculateViewLocations(childNodeView, depth + 1);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ public void calcSpan(TreeElementView view) {
+ if (view.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) view;
+ TreeNode node = nodeView.getTreeElement();
+ if (nodeView.getChildrenViews().size() == 0) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ if (nodeView.getChildrenViews().size() == 1) {
+ TreeTransitionView childView = nodeView.getChildrenViews().get(0);
+ calcSpan(childView);
+ if (childView.getParentViews().size() > 1) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ nodeView.setSpan(childView.getSpan());
+ }
+ }
+ else {
+ DisjointSets branches = node.findMergingBranches();
+ List children = node.getChildren();
+
+ if (node == children.get(0).getParents().get(0)) {
+ reorderBranches(node, branches);
+ ArrayList newChildrenViews = new ArrayList<>();
+ for (TreeTransition trans : node.getChildren()) {
+ newChildrenViews.add((TreeTransitionView) viewMap.get(trans));
+ }
+ nodeView.setChildrenViews(newChildrenViews);
+ }
+
+ List> mergingSets = branches.getAllSets();
+
+ double span = 0.0;
+ for (Set mergeSet : mergingSets) {
+ if (mergeSet.size() > 1) {
+ TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
+ TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
+ double subSpan = 0.0;
+ for (TreeTransition branch : mergeSet) {
+ TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
+ subCalcSpan(branchView, mergePointView);
+ subSpan += branchView.getSpan();
+ }
+ calcSpan(mergePointView);
+ span += Math.max(mergePointView.getSpan(), subSpan);
+ }
+ else {
+ TreeTransition trans = mergeSet.iterator().next();
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+ calcSpan(transView);
+ span += transView.getSpan();
+ }
+ }
+ nodeView.setSpan(span);
+ }
+ }
+ }
+ else {
+ TreeTransitionView transView = (TreeTransitionView) view;
+ TreeNodeView nodeView = transView.getChildView();
+ if (nodeView == null) {
+ transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ calcSpan(nodeView);
+ transView.setSpan(nodeView.getSpan());
+ }
+ }
+ }
+
+ /**
+ * Calculates the sub span of a given sub tree rooted at the specified view and stops at the tree puzzleElement view
+ * specified as stop. Stop tree puzzleElement is NOT included in the span calculation
+ *
+ * @param view
+ * @param stop
+ */
+ private void subCalcSpan(TreeElementView view, TreeElementView stop) {
+ //safe-guard for infinite loop
+ if (view == stop) {
+ return;
+ }
+
+ if (view.getType() == NODE) {
+ TreeNodeView nodeView = (TreeNodeView) view;
+ TreeNode node = nodeView.getTreeElement();
+ if (nodeView.getChildrenViews().size() == 0) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ if (nodeView.getChildrenViews().size() == 1) {
+ TreeTransitionView childView = nodeView.getChildrenViews().get(0);
+ if (childView == stop) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ subCalcSpan(childView, stop);
+ if (childView.getParentViews().size() > 1) {
+ nodeView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ nodeView.setSpan(childView.getSpan());
+ }
+ }
+ }
+ else {
+ DisjointSets branches = node.findMergingBranches();
+ List children = node.getChildren();
+
+ if (node == children.get(0).getParents().get(0)) {
+ reorderBranches(node, branches);
+ }
+
+ List> mergingSets = branches.getAllSets();
+
+ double span = 0.0;
+ for (Set mergeSet : mergingSets) {
+ if (mergeSet.size() > 1) {
+ TreeTransition mergePoint = TreeNode.findMergingPoint(mergeSet);
+ TreeTransitionView mergePointView = (TreeTransitionView) viewMap.get(mergePoint);
+ double subSpan = 0.0;
+ for (TreeTransition branch : mergeSet) {
+ TreeTransitionView branchView = (TreeTransitionView) viewMap.get(branch);
+ subCalcSpan(branchView, mergePointView);
+ subSpan += branchView.getSpan();
+ }
+ subCalcSpan(mergePointView, stop);
+ span += Math.max(mergePointView.getSpan(), subSpan);
+ }
+ else {
+ TreeTransition trans = mergeSet.iterator().next();
+ TreeTransitionView transView = (TreeTransitionView) viewMap.get(trans);
+ subCalcSpan(transView, stop);
+ span += transView.getSpan();
+ }
+ }
+
+ nodeView.setSpan(span);
+ }
+ }
+ }
+ else {
+ TreeTransitionView transView = (TreeTransitionView) view;
+ TreeNodeView nodeView = transView.getChildView();
+ if (nodeView == null || nodeView == stop) {
+ transView.setSpan(DIAMETER + NODE_GAP_HEIGHT);
+ }
+ else {
+ calcSpan(nodeView);
+ transView.setSpan(nodeView.getSpan());
+ }
+ }
+ }
+
+ /**
+ * Reorders branches such that merging branches are sequentially grouped together and transitions are kept in
+ * relative order in the list of child transitions of the specified node
+ *
+ * @param node root node of the branches
+ * @param branches DisjointSets of the child branches of the specified node which determine which branches merge
+ */
+ private void reorderBranches(TreeNode node, DisjointSets branches) {
+ List children = node.getChildren();
+ List> mergingSets = branches.getAllSets();
+
+ List> newOrder = new ArrayList<>();
+ for (Set set : mergingSets) {
+ List mergeBranch = new ArrayList<>();
+ newOrder.add(mergeBranch);
+ children.forEach(t -> {
+ if (set.contains(t)) {
+ mergeBranch.add(t);
+ }
+ });
+ mergeBranch.sort((TreeTransition t1, TreeTransition t2) ->
+ children.indexOf(t1) <= children.indexOf(t2) ? -1 : 1);
+ }
+
+ newOrder.sort((List b1, List b2) -> {
+ int low1 = -1;
+ int low2 = -1;
+ for (TreeTransition t1 : b1) {
+ int curIndex = children.indexOf(t1);
+ if (low1 == -1 || curIndex < low1) {
+ low1 = curIndex;
+ }
+ }
+ for (TreeTransition t1 : b2) {
+ int curIndex = children.indexOf(t1);
+ if (low1 == -1 || curIndex < low1) {
+ low1 = curIndex;
+ }
+ }
+ return low1 < low2 ? -1 : 1;
+ });
+
+ List newChildren = new ArrayList<>();
+ newOrder.forEach(l -> newChildren.addAll(l));
+ node.setChildren(newChildren);
+ }
}
\ No newline at end of file
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png b/src/main/resources/edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png
index ff234c17f..17df92f1a 100644
Binary files a/src/main/resources/edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png and b/src/main/resources/edu/rpi/legup/images/fillapix/cases/BlackOrWhite.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/cases/SatisfyClue.png b/src/main/resources/edu/rpi/legup/images/fillapix/cases/SatisfyClue.png
new file mode 100644
index 000000000..8d84efed9
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/cases/SatisfyClue.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png b/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png
index b017078a2..b93d81e67 100644
Binary files a/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png and b/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooFewBlackCells.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png b/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png
index a0b27424d..393a8c544 100644
Binary files a/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png and b/src/main/resources/edu/rpi/legup/images/fillapix/contradictions/TooManyBlackCells.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png
index 41e540ad1..d098bce41 100644
Binary files a/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png and b/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithBlack.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png
index 330f705f1..758ca7591 100644
Binary files a/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png and b/src/main/resources/edu/rpi/legup/images/fillapix/rules/FinishWithWhite.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/Mirror.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/Mirror.png
new file mode 100644
index 000000000..991408d9c
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/rules/Mirror.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/NontouchingShared.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/NontouchingShared.png
new file mode 100644
index 000000000..1be04f3ce
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/rules/NontouchingShared.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingCorners.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingCorners.png
new file mode 100644
index 000000000..f3eba46b6
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingCorners.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingSides.png b/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingSides.png
new file mode 100644
index 000000000..6d3b12dfd
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/rules/TouchingSides.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/BlackTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/BlackTile.png
new file mode 100644
index 000000000..93e169df8
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/BlackTile.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/NumberTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/NumberTile.png
new file mode 100644
index 000000000..5a8540d02
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/NumberTile.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/UnknownTile.png
new file mode 100644
index 000000000..850fbf127
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/UnknownTile.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/fillapix/tiles/WhiteTile.png b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/WhiteTile.png
new file mode 100644
index 000000000..fc2c683eb
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/fillapix/tiles/WhiteTile.png differ
diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config
index 24fdcf365..19e63a2a3 100644
--- a/src/main/resources/edu/rpi/legup/legup/config
+++ b/src/main/resources/edu/rpi/legup/legup/config
@@ -7,7 +7,7 @@
+ fileCreationDisabled="false"/>
-
\ No newline at end of file
+
diff --git a/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java
new file mode 100644
index 000000000..0d94eb672
--- /dev/null
+++ b/src/test/java/puzzles/shorttruthtable/rules/AndEliminationDirectRuleTest.java
@@ -0,0 +1,204 @@
+package puzzles.shorttruthtable.rules;
+
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType;
+import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.elimination.DirectRuleAndElimination;
+import edu.rpi.legup.save.InvalidFileFormatException;
+import legup.MockGameBoardFacade;
+import legup.TestUtilities;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AndEliminationDirectRuleTest {
+ private static final DirectRuleAndElimination RULE = new DirectRuleAndElimination();
+ private static ShortTruthTable stt;
+
+ @BeforeClass
+ public static void setup() {
+ MockGameBoardFacade.getInstance();
+ stt = new ShortTruthTable();
+ }
+
+ /**
+ * Given one statement: B^C where ^ is true
+ *
+ * Checks all possible combinations of true, false, and unknown for B and C
+ * except for where both B and C are true and asserts that each one of them
+ * is not a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void trueAndTest1() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN};
+
+ for (ShortTruthTableCellType cellType1 : cellTypes) {
+ for (ShortTruthTableCellType cellType2 : cellTypes) {
+ if (cellType1 == cellType2 && cellType1 == ShortTruthTableCellType.TRUE) {
+ continue;
+ }
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+ ShortTruthTableCell bonnie = board.getCell(0, 0);
+ ShortTruthTableCell clyde = board.getCell(2, 0);
+
+ if (cellType1 != ShortTruthTableCellType.UNKNOWN) {
+ bonnie.setData(cellType1);
+ board.addModifiedData(bonnie);
+ }
+
+ if (cellType2 != ShortTruthTableCellType.UNKNOWN) {
+ clyde.setData(cellType2);
+ board.addModifiedData(clyde);
+ }
+
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+ }
+ }
+
+ /**
+ * Given one statement: B^C where ^ is true
+ *
+ * Checks all possible combinations of true and unknown for B and C
+ * except for where both B and C are unknown and asserts that each one
+ * of them is a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void trueAndTest2() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.UNKNOWN};
+
+ for (ShortTruthTableCellType cellType1 : cellTypes) {
+ for (ShortTruthTableCellType cellType2 : cellTypes) {
+ if (cellType1 == cellType2 && cellType1 == ShortTruthTableCellType.UNKNOWN) {
+ continue;
+ }
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+ ShortTruthTableCell bonnie = board.getCell(0, 0);
+ ShortTruthTableCell clyde = board.getCell(2, 0);
+
+ if (cellType1 != ShortTruthTableCellType.UNKNOWN) {
+ bonnie.setData(cellType1);
+ board.addModifiedData(bonnie);
+ }
+
+ if (cellType2 != ShortTruthTableCellType.UNKNOWN) {
+ clyde.setData(cellType2);
+ board.addModifiedData(clyde);
+ }
+
+ Assert.assertNull(RULE.checkRule(transition));
+ }
+ }
+ }
+
+ /**
+ * Given one statement: B^C where ^ is false
+ *
+ * Checks all possible combinations of true, false, and unknown for B and C
+ * and asserts that each one of them is not a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void falseAndWithUnknownsTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableCellType[] cellTypes = {ShortTruthTableCellType.TRUE, ShortTruthTableCellType.FALSE, ShortTruthTableCellType.UNKNOWN};
+
+ for (ShortTruthTableCellType cellType1 : cellTypes) {
+ for (ShortTruthTableCellType cellType2 : cellTypes) {
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+ ShortTruthTableCell bonnie = board.getCell(0, 0);
+ ShortTruthTableCell clyde = board.getCell(2, 0);
+
+ if (cellType1 != ShortTruthTableCellType.UNKNOWN) {
+ bonnie.setData(cellType1);
+ board.addModifiedData(bonnie);
+ }
+
+ if (cellType2 != ShortTruthTableCellType.UNKNOWN) {
+ clyde.setData(cellType2);
+ board.addModifiedData(clyde);
+ }
+
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+ }
+ }
+
+ /**
+ * Given one statement: B^C where both B and ^ are false
+ *
+ * Asserts that this is not a valid application of the rule if C is set to
+ * either true or false.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void falseAndWithKnownFalseTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell clyde = board.getCell(2, 0);
+ clyde.setData(ShortTruthTableCellType.TRUE);
+ board.addModifiedData(clyde);
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ clyde.setData(ShortTruthTableCellType.FALSE);
+ board.addModifiedData(clyde);
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+
+ /**
+ * Given one statement: B^C where B is true and ^ is false
+ *
+ * Asserts that this is a valid application of the rule if and only if C is
+ * set to false.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void falseAndWithKnownTrueTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell clyde = board.getCell(2, 0);
+ clyde.setData(ShortTruthTableCellType.TRUE);
+ board.addModifiedData(clyde);
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ clyde.setData(ShortTruthTableCellType.FALSE);
+ board.addModifiedData(clyde);
+ Assert.assertNull(RULE.checkRule(transition));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java b/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java
new file mode 100644
index 000000000..51aa213c6
--- /dev/null
+++ b/src/test/java/puzzles/shorttruthtable/rules/AtomicDirectRuleTest.java
@@ -0,0 +1,188 @@
+package puzzles.shorttruthtable.rules;
+
+import edu.rpi.legup.model.tree.TreeNode;
+import edu.rpi.legup.model.tree.TreeTransition;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTable;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableBoard;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCell;
+import edu.rpi.legup.puzzle.shorttruthtable.ShortTruthTableCellType;
+import edu.rpi.legup.puzzle.shorttruthtable.rules.basic.DirectRuleAtomic;
+import edu.rpi.legup.save.InvalidFileFormatException;
+import legup.MockGameBoardFacade;
+import legup.TestUtilities;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class AtomicDirectRuleTest {
+ private static final DirectRuleAtomic RULE = new DirectRuleAtomic();
+ private static ShortTruthTable stt;
+
+ @BeforeClass
+ public static void setup() {
+ MockGameBoardFacade.getInstance();
+ stt = new ShortTruthTable();
+ }
+
+ /**
+ * Given two statements:
+ * A
+ * A
+ * where the first A is set to false.
+ *
+ * This test sets the second A to false and then asserts that this
+ * is a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void MatchingFalseTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell cell = board.getCell(0, 2);
+ cell.setData(ShortTruthTableCellType.FALSE);
+ board.addModifiedData(cell);
+
+ Assert.assertNull(RULE.checkRule(transition));
+ }
+
+ /**
+ * Given two statements:
+ * A
+ * A
+ * where the first A is set to false.
+ *
+ * This test sets the second A to true and then asserts that this
+ * is not a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void MismatchingFalseTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell cell = board.getCell(0, 2);
+ cell.setData(ShortTruthTableCellType.TRUE);
+ board.addModifiedData(cell);
+
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+
+ /**
+ * Given two statements:
+ * B
+ * B
+ * where the first B is set to true.
+ *
+ * This test sets the second B to true and then asserts that this
+ * is a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void MatchingTrueTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell cell = board.getCell(0, 2);
+ cell.setData(ShortTruthTableCellType.TRUE);
+ board.addModifiedData(cell);
+
+ Assert.assertNull(RULE.checkRule(transition));
+ }
+
+ /**
+ * Given two statements:
+ * B
+ * B
+ * where the first B is set to true.
+ *
+ * This test sets the second B to false and then asserts that this
+ * is not a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void MismatchingTrueTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell cell = board.getCell(0, 2);
+ cell.setData(ShortTruthTableCellType.FALSE);
+ board.addModifiedData(cell);
+
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+
+ /**
+ * Given two statements:
+ * C
+ * C
+ * where neither statement is set to anything.
+ *
+ * This test sets the second C to false and then asserts that this
+ * is not a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void NothingPreviouslyMarkedTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/Empty", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell cell = board.getCell(0, 2);
+ cell.setData(ShortTruthTableCellType.FALSE);
+ board.addModifiedData(cell);
+
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+
+ /**
+ * Given two statements:
+ * C
+ * C
+ * where neither statement is set to anything.
+ *
+ * This test sets the second C to true and then asserts that this
+ * is not a valid application of the rule.
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void NothingPreviouslyMarkedTest2() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/shorttruthtable/rules/AtomicDirectRule/Empty", stt);
+ TreeNode rootNode = stt.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ ShortTruthTableBoard board = (ShortTruthTableBoard) transition.getBoard();
+
+ ShortTruthTableCell cell = board.getCell(0, 2);
+ cell.setData(ShortTruthTableCellType.TRUE);
+ board.addModifiedData(cell);
+
+ Assert.assertNotNull(RULE.checkRule(transition));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java b/src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java
deleted file mode 100644
index 81991fa46..000000000
--- a/src/test/java/puzzles/shorttruthtable/rules/DirectRuleAtomicTest.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package puzzles.shorttruthtable.rules;
-
-class DirectRuleAtomicTest {
-
-
-}
\ No newline at end of file
diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java
index 50e2bb970..b7ec8eb02 100644
--- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java
+++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java
@@ -27,6 +27,9 @@ public static void setUp() {
treetent = new TreeTent();
}
+ // creates a 3x3 puzzle with no trees
+ // make the (1,1) tile GRASS
+ // checks if tiles logically follow the EmptyFieldDirectRule
@Test
public void EmptyFieldTest() throws InvalidFileFormatException {
TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyField", treetent);
@@ -34,28 +37,38 @@ public void EmptyFieldTest() throws InvalidFileFormatException {
TreeTransition transition = rootNode.getChildren().get(0);
transition.setRule(RULE);
+ // get board state
TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+ // change the board's cells considering the EmptyField rule
TreeTentCell cell1 = board.getCell(1, 1);
cell1.setData(TreeTentType.GRASS);
-
board.addModifiedData(cell1);
+ // confirm there is a logical following of the EmptyField rule
Assert.assertNull(RULE.checkRule(transition));
+ // only the cell above should change following the rule
+ TreeTentCell c;
for (int i = 0; i < board.getHeight(); i++) {
for (int k = 0; k < board.getWidth(); k++) {
- Point point = new Point(k, i);
- if (point.equals(cell1.getLocation())) {
- Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
}
else {
- Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
}
}
}
}
+ // creates a 3x3 puzzle with 4 trees
+ // trees are at (0,0), (2,0), (0,2), and (2,2)
+ // make the (1,1) tile GRASS.
+ // checks if tiles logically follow the EmptyFieldDirectRule
@Test
public void DiagonalTreeTest() throws InvalidFileFormatException {
TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/DiagonalTree", treetent);
@@ -63,26 +76,197 @@ public void DiagonalTreeTest() throws InvalidFileFormatException {
TreeTransition transition = rootNode.getChildren().get(0);
transition.setRule(RULE);
+ // get board state
TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+ //change the board's cells considering the EmptyField rule
TreeTentCell cell1 = board.getCell(1, 1);
cell1.setData(TreeTentType.GRASS);
-
board.addModifiedData(cell1);
+ // confirm there is a logical following of the EmptyField rule
Assert.assertNull(RULE.checkRule(transition));
+ // only the cell above should change following the rule
+ TreeTentCell c;
for (int i = 0; i < board.getHeight(); i++) {
for (int k = 0; k < board.getWidth(); k++) {
- Point point = new Point(k, i);
- if (point.equals(cell1.getLocation())) {
- Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
}
else {
- Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
}
}
}
}
+
+ // creates a 3x3 puzzle with 4 trees
+ // trees are at (0,1), (1,0), (1,2), and (2,1)
+ // make the (1,1) tile GRASS.
+ // checks if tiles don't logically follow the EmptyFieldDirectRule
+ @Test
+ public void EmptyFieldTestFail() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ //change the board's cells considering breaking the EmptyField rule
+ TreeTentCell cell1 = board.getCell(1, 1);
+ cell1.setData(TreeTentType.GRASS);
+ board.addModifiedData(cell1);
+
+ // confirm there is not a logical following of the EmptyField rule
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ // the cells should not follow the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int j = 0; j < board.getHeight(); j++) {
+ c = board.getCell(j, i);
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+
+ // creates a 3x3 puzzle with 1 tree
+ // tree is at (1,0)
+ // make the (1,1) tile GRASS.
+ // checks if tiles don't logically follow the EmptyFieldDirectRule
+ @Test
+ public void EmptyFieldTestFailTop() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ //change the board's cells considering breaking the EmptyField rule
+ TreeTentCell cell1 = board.getCell(1, 1);
+ cell1.setData(TreeTentType.GRASS);
+ board.addModifiedData(cell1);
+
+ // confirm there is not a logical following of the EmptyField rule
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ // the cells should not follow the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int j = 0; j < board.getHeight(); j++) {
+ c = board.getCell(j, i);
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+
+ // creates a 3x3 puzzle with 1 tree
+ // tree is at (1,2)
+ // make the (1,1) tile GRASS.
+ // checks if tiles don't logically follow the EmptyFieldDirectRule
+ @Test
+ public void EmptyFieldTestFailTopBottom() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ //change the board's cells considering breaking the EmptyField rule
+ TreeTentCell cell1 = board.getCell(1, 1);
+ cell1.setData(TreeTentType.GRASS);
+ board.addModifiedData(cell1);
+
+ // confirm there is not a logical following of the EmptyField rule
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ // the cells should not follow the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int j = 0; j < board.getHeight(); j++) {
+ c = board.getCell(j, i);
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+
+ // creates a 3x3 puzzle with 1 tree
+ // tree is at (0,1)
+ // make the (1,1) tile GRASS.
+ // checks if tiles don't logically follow the EmptyFieldDirectRule
+ @Test
+ public void EmptyFieldTestFailLeft() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ //change the board's cells considering breaking the EmptyField rule
+ TreeTentCell cell1 = board.getCell(1, 1);
+ cell1.setData(TreeTentType.GRASS);
+ board.addModifiedData(cell1);
+
+ // confirm there is not a logical following of the EmptyField rule
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ // the cells should not follow the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int j = 0; j < board.getHeight(); j++) {
+ c = board.getCell(j, i);
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+
+ // creates a 3x3 puzzle with 1 tree
+ // tree is at (2,1)
+ // make the (1,1) tile GRASS.
+ // checks if tiles don't logically follow the EmptyFieldDirectRule
+ @Test
+ public void EmptyFieldTestFailRight() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ //change the board's cells considering breaking the EmptyField rule
+ TreeTentCell cell1 = board.getCell(1, 1);
+ cell1.setData(TreeTentType.GRASS);
+ board.addModifiedData(cell1);
+
+ // confirm there is not a logical following of the EmptyField rule
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ // the cells should not follow the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int j = 0; j < board.getHeight(); j++) {
+ c = board.getCell(j, i);
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
}
diff --git a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java
index 8dbec657a..2517df563 100644
--- a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java
+++ b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java
@@ -15,6 +15,8 @@
import org.junit.Test;
import java.awt.*;
+import java.util.List;
+import java.util.ArrayList;
public class FinishWithGrassDirectRuleTest {
@@ -27,15 +29,24 @@ public static void setUp() {
treetent = new TreeTent();
}
+ /**
+ * 3x3 TreeTent puzzle with a tent at (0,0)
+ * Tests FinishWithGrassDirectRule on GRASS tiles horizontal of the tent
+ * at (1,0) and (2,0)
+ *
+ * @throws InvalidFileFormatException
+ */
@Test
- public void EmptyFieldTest() throws InvalidFileFormatException {
- TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass", treetent);
+ public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent);
TreeNode rootNode = treetent.getTree().getRootNode();
TreeTransition transition = rootNode.getChildren().get(0);
transition.setRule(RULE);
+ // get board state
TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+ // change the board's cells considering the FinishWithGrass rule
TreeTentCell cell1 = board.getCell(1, 0);
cell1.setData(TreeTentType.GRASS);
TreeTentCell cell2 = board.getCell(2, 0);
@@ -44,21 +55,316 @@ public void EmptyFieldTest() throws InvalidFileFormatException {
board.addModifiedData(cell1);
board.addModifiedData(cell2);
+ // confirm there is a logical following of the EmptyField rule
Assert.assertNull(RULE.checkRule(transition));
+ // only the cell above should change following the rule
+ TreeTentCell c;
for (int i = 0; i < board.getHeight(); i++) {
for (int k = 0; k < board.getWidth(); k++) {
- Point point = new Point(k, i);
- if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) {
- Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation()) || c.getLocation().equals(cell2.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
}
else {
- Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
}
}
}
}
-}
+ /**
+ * 3x3 TreeTent puzzle with a tent at (0,0)
+ * Tests FinishWithGrassDirectRule on GRASS tiles vertical of the tent
+ * at (0,1) and (0,2)
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void FinishWithGrassVerticalTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+ // change the board's cells considering the FinishWithGrass rule
+ TreeTentCell cell1 = board.getCell(0, 1);
+ cell1.setData(TreeTentType.GRASS);
+ TreeTentCell cell2 = board.getCell(0, 2);
+ cell2.setData(TreeTentType.GRASS);
+
+ board.addModifiedData(cell1);
+ board.addModifiedData(cell2);
+
+ // confirm there is a logical following of the EmptyField rule
+ Assert.assertNull(RULE.checkRule(transition));
+
+ // only the cell above should change following the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getHeight(); i++) {
+ for (int k = 0; k < board.getWidth(); k++) {
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation()) || c.getLocation().equals(cell2.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
+ }
+ else {
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+ }
+
+ /**
+ * 3x3 TreeTent puzzle with a tent at (0,0)
+ * Tests FinishWithGrassDirectRule on GRASS tiles
+ * at (1,0), (2,0), (0,1), and (0,2)
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void FinishWithGrassTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ // change the board's cells considering the FinishWithGrass rule
+ TreeTentCell cell1 = board.getCell(1, 0);
+ cell1.setData(TreeTentType.GRASS);
+ TreeTentCell cell2 = board.getCell(2, 0);
+ cell2.setData(TreeTentType.GRASS);
+ TreeTentCell cell3 = board.getCell(0, 1);
+ cell3.setData(TreeTentType.GRASS);
+ TreeTentCell cell4 = board.getCell(0, 2);
+ cell4.setData(TreeTentType.GRASS);
+
+ board.addModifiedData(cell1);
+ board.addModifiedData(cell2);
+ board.addModifiedData(cell3);
+ board.addModifiedData(cell4);
+
+ // confirm there is a logical following of the EmptyField rule
+ Assert.assertNull(RULE.checkRule(transition));
+
+ // only the cell above should change following the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getHeight(); i++) {
+ for (int k = 0; k < board.getWidth(); k++) {
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation()) ||
+ c.getLocation().equals(cell2.getLocation()) ||
+ c.getLocation().equals(cell3.getLocation()) ||
+ c.getLocation().equals(cell4.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
+ }
+ else {
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+ }
+
+ /**
+ * 3x3 TreeTent puzzle with no tents
+ * Tests FinishWithGrassDirectRule on GRASS tiles
+ * GRASS tiles fill entire board
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void NoTentTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ // change the board's cells considering the FinishWithGrass rule
+ List cells = new ArrayList();
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int k = 0; k < board.getHeight(); k++) {
+ TreeTentCell c = board.getCell(i, k);
+ c.setData(TreeTentType.GRASS);
+ cells.add(c);
+ }
+ }
+
+ for (TreeTentCell c : cells) {
+ board.addModifiedData(c);
+ }
+
+ // confirm there is a logical following of the EmptyField rule
+ Assert.assertNull(RULE.checkRule(transition));
+
+ // all cells should change following the rule
+ for (TreeTentCell c : cells) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ /**
+ * 3x3 TreeTent puzzle with a tent at (1,1)
+ * Tests FinishWithGrassDirectRule on GRASS tiles surrounding the tent
+ * at (1,0), (0,1), (2,1), and (1,2)
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void MiddleTentTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ // change the board's cells considering the FinishWithGrass rule
+ TreeTentCell cell1 = board.getCell(1, 0);
+ TreeTentCell cell2 = board.getCell(0, 1);
+ TreeTentCell cell3 = board.getCell(2, 1);
+ TreeTentCell cell4 = board.getCell(1, 2);
+
+ cell1.setData(TreeTentType.GRASS);
+ cell2.setData(TreeTentType.GRASS);
+ cell3.setData(TreeTentType.GRASS);
+ cell4.setData(TreeTentType.GRASS);
+
+ board.addModifiedData(cell1);
+ board.addModifiedData(cell2);
+ board.addModifiedData(cell3);
+ board.addModifiedData(cell4);
+
+ // confirm there is a logical following of the EmptyField rule
+ Assert.assertNull(RULE.checkRule(transition));
+
+ // only the cell above should change following the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getHeight(); i++) {
+ for (int k = 0; k < board.getWidth(); k++) {
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation()) ||
+ c.getLocation().equals(cell2.getLocation()) ||
+ c.getLocation().equals(cell3.getLocation()) ||
+ c.getLocation().equals(cell4.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
+ }
+ else {
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+ }
+
+ /**
+ * 3x3 TreeTent puzzle with missing tents
+ * Tests FinishWithGrassDirectRule on GRASS tiles filling the puzzle
+ * all GRASS tiles should fail the FinishWithGrassDirectRule
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void FailTentTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ // change the board's cells not following the FinishWithGrass rule
+ List cells = new ArrayList();
+ for (int i = 0; i < board.getWidth(); i++) {
+ for (int k = 0; k < board.getHeight(); k++) {
+ TreeTentCell c = board.getCell(i, k);
+ c.setData(TreeTentType.GRASS);
+ cells.add(c);
+ }
+ }
+
+ for (TreeTentCell c : cells) {
+ board.addModifiedData(c);
+ }
+
+ // confirm there is a logical following of the EmptyField rule
+ Assert.assertNotNull(RULE.checkRule(transition));
+
+ // all cells should fail the rule test
+ for (TreeTentCell c : cells) {
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ /**
+ * 7x7 TreeTent puzzle with multiple tents spaced out
+ * Tests FinishWithGrassDirectRule on GRASS tiles between the tents
+ * at (0,3), (2,3), (4,3), and (6,3)
+ *
+ * @throws InvalidFileFormatException
+ */
+ @Test
+ public void SpacedOutTentTest() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ // get board state
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ // change the board's cells considering the FinishWithGrass rule
+ TreeTentCell cell1 = board.getCell(0, 3);
+ TreeTentCell cell2 = board.getCell(2, 3);
+ TreeTentCell cell3 = board.getCell(4, 3);
+ TreeTentCell cell4 = board.getCell(6, 3);
+
+ cell1.setData(TreeTentType.GRASS);
+ cell2.setData(TreeTentType.GRASS);
+ cell3.setData(TreeTentType.GRASS);
+ cell4.setData(TreeTentType.GRASS);
+
+ board.addModifiedData(cell1);
+ board.addModifiedData(cell2);
+ board.addModifiedData(cell3);
+ board.addModifiedData(cell4);
+
+ // confirm there is a logical following of the EmptyField rule
+ Assert.assertNull(RULE.checkRule(transition));
+
+ // only the cell above should change following the rule
+ TreeTentCell c;
+ for (int i = 0; i < board.getHeight(); i++) {
+ for (int k = 0; k < board.getWidth(); k++) {
+ c = board.getCell(k, i);
+ if (c.getLocation().equals(cell1.getLocation()) ||
+ c.getLocation().equals(cell2.getLocation()) ||
+ c.getLocation().equals(cell3.getLocation()) ||
+ c.getLocation().equals(cell4.getLocation())) {
+ // logically follows
+ Assert.assertNull(RULE.checkRuleAt(transition, c));
+ }
+ else {
+ // does not use the rule to logically follow
+ Assert.assertNotNull(RULE.checkRuleAt(transition, c));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java
index b2d47e222..52aea28c0 100644
--- a/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java
+++ b/src/test/java/puzzles/treetent/rules/NoTentForTreeContradictionRuleTest.java
@@ -28,8 +28,12 @@ public static void setUp() {
treetent = new TreeTent();
}
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if a tree is next to only grass in a 2x2 grid triggers the contradiction
+ */
@Test
- public void NoTentForTreeContradictionRule_() throws InvalidFileFormatException {
+ public void NoTentForTreeContradictionRule_Basic() throws InvalidFileFormatException {
TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTree", treetent);
TreeNode rootNode = treetent.getTree().getRootNode();
TreeTransition transition = rootNode.getChildren().get(0);
@@ -43,5 +47,65 @@ public void NoTentForTreeContradictionRule_() throws InvalidFileFormatException
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
}
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests similarly to above, but now with a tent diagonally next to the tree, which should still contradict
+ */
+ @Test
+ public void NoTentForTreeContradictionRule_Diagonal() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests that adjacent trees do not allow a pass
+ */
+ @Test
+ public void NoTentForTreeContradictionRule_TwoTrees() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests similarly to above, but now with a tent diagonally next to two trees, which should still contradict on one.
+ */
+ @Test
+ public void NoTentForTreeContradictionRule_TwoTreesDiagonal() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
}
diff --git a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java
index c3003cef1..2ad2ac90e 100644
--- a/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java
+++ b/src/test/java/puzzles/treetent/rules/NoTreeForTentContradictionRuleTest.java
@@ -10,13 +10,9 @@
import edu.rpi.legup.puzzle.treetent.TreeTent;
import edu.rpi.legup.puzzle.treetent.TreeTentBoard;
-import edu.rpi.legup.puzzle.treetent.TreeTentCell;
-import edu.rpi.legup.puzzle.treetent.TreeTentType;
import edu.rpi.legup.puzzle.treetent.rules.NoTreeForTentContradictionRule;
import edu.rpi.legup.save.InvalidFileFormatException;
-import java.awt.*;
-
public class NoTreeForTentContradictionRuleTest {
private static final NoTreeForTentContradictionRule RULE = new NoTreeForTentContradictionRule();
@@ -28,9 +24,13 @@ public static void setUp() {
treetent = new TreeTent();
}
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 2x2 Grid, a Tent in the NW corner has no adjacent trees
+ */
@Test
- public void NoTreeForTentContradictionRule_() throws InvalidFileFormatException {
- TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent", treetent);
+ public void NoTreeForTentContradictionRule_NW() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW", treetent);
TreeNode rootNode = treetent.getTree().getRootNode();
TreeTransition transition = rootNode.getChildren().get(0);
transition.setRule(RULE);
@@ -43,5 +43,165 @@ public void NoTreeForTentContradictionRule_() throws InvalidFileFormatException
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
}
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 2x2 Grid, a Tent in the NE corner has no adjacent trees
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_NE() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 2x2 Grid, a Tent in the NW corner has no adjacent trees
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_SW() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 2x2 Grid, a Tent in the SE corner has no adjacent trees
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_SE() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 3x3 Grid with no trees, a Tent in the center cell has no adjacent trees
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_3x3() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 3x3 Grid with diagonal trees, a Tent in the center cell has no adjacent trees
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_3x3WithDiagonalTrees() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 3x3 Grid with an adjacent tree, test does not assert null.
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_YesTree() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNotNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2)));
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if, in a 3x3 Grid with touching tents, a Tent in the center cell has no adjacent trees
+ */
+ @Test
+ public void NoTreeForTentContradictionRule_JustTent() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 2)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(2, 2)));
+ }
}
diff --git a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java
index 14afb4dc0..999405747 100644
--- a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java
+++ b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java
@@ -27,6 +27,10 @@ public static void setUp() {
treetent = new TreeTent();
}
+ /**
+ * @throws InvalidFileFormatException
+ * Test to check if all adjacent and diagonals not filled with a tree are filled with grass
+ */
@Test
public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatException {
TestUtilities.importTestBoard("puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrass", treetent);
@@ -58,6 +62,90 @@ public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatExcepti
}
}
}
+
+ /**
+ * @throws InvalidFileFormatException
+ *
+ * Test with a 3x3 board with an absolutely empty area aside from a tent in the middle
+ * While such a situation is an illegal treetent setup, this direct rule doesn't consider that aspect, so its ok in this context
+ */
+ @Test
+ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ TreeTentCell cell1 = board.getCell(0, 0);
+ cell1.setData(TreeTentType.GRASS);
+ TreeTentCell cell2 = board.getCell(1, 0);
+ cell2.setData(TreeTentType.GRASS);
+ TreeTentCell cell3 = board.getCell(2, 0);
+ cell3.setData(TreeTentType.GRASS);
+ TreeTentCell cell4 = board.getCell(0, 1);
+ cell4.setData(TreeTentType.GRASS);
+ //Skip (1,1) due to being the Tent
+ TreeTentCell cell5 = board.getCell(2, 1);
+ cell5.setData(TreeTentType.GRASS);
+ TreeTentCell cell6 = board.getCell(0, 2);
+ cell6.setData(TreeTentType.GRASS);
+ TreeTentCell cell7 = board.getCell(1, 2);
+ cell7.setData(TreeTentType.GRASS);
+ TreeTentCell cell8 = board.getCell(2, 2);
+ cell8.setData(TreeTentType.GRASS);
+
+ board.addModifiedData(cell1);
+ board.addModifiedData(cell2);
+ board.addModifiedData(cell3);
+ //board.addModifiedData(cell4);
+ board.addModifiedData(cell5);
+ board.addModifiedData(cell6);
+ board.addModifiedData(cell7);
+ board.addModifiedData(cell8);
+
+ Assert.assertNull(RULE.checkRule(transition));
+
+ for (int i = 0; i < board.getHeight(); i++) {
+ for (int k = 0; k < board.getWidth(); k++) {
+ Point point = new Point(k, i);
+ if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) ||
+ point.equals(cell3.getLocation()) || //point.equals(cell4.getLocation()) ||
+ point.equals(cell5.getLocation()) || point.equals(cell6.getLocation()) ||
+ point.equals(cell7.getLocation()) || point.equals(cell8.getLocation())
+ ) {
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ }
+ else {
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ }
+ }
+ }
+ }
+
+ /**
+ * @throws InvalidFileFormatException
+ *
+ * Test to see if the rule passes even if no grass was able to be placed due to the presence of trees.
+ */
+ @Test
+ public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkRule(transition));
+
+ for (int i = 0; i < board.getHeight(); i++){
+ for (int k = 0; k < board.getWidth(); k++){
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i)));
+ }
+ }
+ }
}
diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java
index 79fc70118..f48afe5d7 100644
--- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java
+++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java
@@ -10,13 +10,9 @@
import edu.rpi.legup.puzzle.treetent.TreeTent;
import edu.rpi.legup.puzzle.treetent.TreeTentBoard;
-import edu.rpi.legup.puzzle.treetent.TreeTentCell;
-import edu.rpi.legup.puzzle.treetent.TreeTentType;
import edu.rpi.legup.puzzle.treetent.rules.TouchingTentsContradictionRule;
import edu.rpi.legup.save.InvalidFileFormatException;
-import java.awt.*;
-
public class TouchingTentsContradictionRuleTest {
private static final TouchingTentsContradictionRule RULE = new TouchingTentsContradictionRule();
@@ -28,8 +24,14 @@ public static void setUp() {
treetent = new TreeTent();
}
+ //DIAGONAL TESTS
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent diagonal of orientation T
+ * T
+ **/
@Test
- public void TouchingTentsContradictionRule_Diagonal() throws InvalidFileFormatException {
+ public void TouchingTentsContradictionRule_DiagonalUpLeftToDownRight() throws InvalidFileFormatException {
TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonal", treetent);
TreeNode rootNode = treetent.getTree().getRootNode();
TreeTransition transition = rootNode.getChildren().get(0);
@@ -44,8 +46,35 @@ public void TouchingTentsContradictionRule_Diagonal() throws InvalidFileFormatEx
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
}
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent diagonal of orientation T
+ * T
+ **/
@Test
- public void TouchingTentsContradictionRule_Adjacent() throws InvalidFileFormatException {
+ public void TouchingTentsContradictionRule_DiagonalDownLeftToUpRight() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition,board.getCell(1,0)));
+ Assert.assertNull(RULE.checkRuleAt(transition,board.getCell(0,1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+
+ //ADJACENT TESTS
+ /**
+ * @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);
TreeNode rootNode = treetent.getTree().getRootNode();
TreeTransition transition = rootNode.getChildren().get(0);
@@ -59,6 +88,162 @@ public void TouchingTentsContradictionRule_Adjacent() throws InvalidFileFormatEx
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
}
+
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent adjacent of orientation TT
+ **/
+ @Test
+ public void TouchingTentsContradictionRule_AdjacentHorizontal() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt", treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+ //MIXED TESTS
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent of orientation TT
+ * TT
+ **/
+ @Test
+ public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent of orientation TT
+ * T
+ **/
+ @Test
+ public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ }
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent of orientation TT
+ * T
+ **/
+ @Test
+ public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ }
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent of orientation T
+ * TT
+ **/
+ @Test
+ public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ }
+ /**
+ * @throws InvalidFileFormatException
+ * Tests a tent of orientation T
+ * TT
+ **/
+ @Test
+ public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatException {
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNull(RULE.checkContradiction(board));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ }
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if tree adjacent triggers a null
+ */
+ @Test
+ public void TouchingTentsContradictionRule_TreeAdjacent() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNotNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1)));
+ }
+ /**
+ * @throws InvalidFileFormatException
+ * Tests if tree diagonal triggers a null
+ */
+ @Test
+ public void TouchingTentsContradictionRule_TreeDiagonal() throws InvalidFileFormatException{
+ TestUtilities.importTestBoard("puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal",treetent);
+ TreeNode rootNode = treetent.getTree().getRootNode();
+ TreeTransition transition = rootNode.getChildren().get(0);
+ transition.setRule(RULE);
+
+ TreeTentBoard board = (TreeTentBoard) transition.getBoard();
+
+ Assert.assertNotNull(RULE.checkContradiction(board));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0)));
+ Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0)));
+ }
+
}
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd
new file mode 100644
index 000000000..f6f60abc3
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAnd
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse
new file mode 100644
index 000000000..d8edf4a76
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownFalse
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ |
+ |
+
+
+
+
+
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue
new file mode 100644
index 000000000..364d8faf6
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/FalseAndWithKnownTrue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ |
+ |
+
+
+
+
+
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd
new file mode 100644
index 000000000..307f6d14a
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AndEliminationDirectRule/TrueAnd
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty
new file mode 100644
index 000000000..6a6effadf
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/Empty
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA
new file mode 100644
index 000000000..8c5ddc7b9
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/FalseA
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB
new file mode 100644
index 000000000..46a1beb2d
--- /dev/null
+++ b/src/test/resources/puzzles/shorttruthtable/rules/AtomicDirectRule/TrueB
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail
new file mode 100644
index 000000000..f826667e3
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail
@@ -0,0 +1,23 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom
new file mode 100644
index 000000000..80deadaea
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft
new file mode 100644
index 000000000..d19e01daf
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight
new file mode 100644
index 000000000..bf3954964
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop
new file mode 100644
index 000000000..8eaa974ea
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent
similarity index 92%
rename from src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass
rename to src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent
index 3e293d22f..daba3648e 100644
--- a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FinishWithGrass
+++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent
@@ -10,7 +10,7 @@
-
+
diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent
new file mode 100644
index 000000000..9fa14ebe4
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent
new file mode 100644
index 000000000..8f71a57f9
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent
new file mode 100644
index 000000000..a13c7cc55
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent
new file mode 100644
index 000000000..f7b523b0a
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent
@@ -0,0 +1,30 @@
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal
new file mode 100644
index 000000000..af0b49e31
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeDiagonal
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees
new file mode 100644
index 000000000..5111e0585
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTrees
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal
new file mode 100644
index 000000000..10391b85c
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTentForTreeContradictionRule/NoTentForTreeTwoTreesDiagonal
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3 b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3
new file mode 100644
index 000000000..9c5e81936
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent3x3
@@ -0,0 +1,28 @@
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals
new file mode 100644
index 000000000..ad086bbc1
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentDiagonals
@@ -0,0 +1,28 @@
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent
new file mode 100644
index 000000000..93aaf451f
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentJustTent
@@ -0,0 +1,28 @@
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE
new file mode 100644
index 000000000..80ffb9525
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNE
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW
similarity index 100%
rename from src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTent
rename to src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentNW
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE
new file mode 100644
index 000000000..c2fc5206d
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSE
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW
new file mode 100644
index 000000000..8e8249b27
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentSW
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree
new file mode 100644
index 000000000..f46e4b5a5
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/NoTreeForTentContradictionRule/NoTreeForTentYesTree
@@ -0,0 +1,28 @@
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad
new file mode 100644
index 000000000..6065d42cd
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees
new file mode 100644
index 000000000..e8fef64dc
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees
@@ -0,0 +1,28 @@
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt
new file mode 100644
index 000000000..902fb3ee7
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsAdjacentAlt
@@ -0,0 +1,19 @@
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt
new file mode 100644
index 000000000..9b35fb998
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsDiagonalAlt
@@ -0,0 +1,19 @@
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2 b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2
new file mode 100644
index 000000000..59805ca35
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsFull2By2
@@ -0,0 +1,21 @@
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft
new file mode 100644
index 000000000..af4ca0831
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight
new file mode 100644
index 000000000..05b7a7667
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft
new file mode 100644
index 000000000..b8b81c7b4
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight
new file mode 100644
index 000000000..af7fb5df2
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight
@@ -0,0 +1,20 @@
+
+
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent
new file mode 100644
index 000000000..4c63558c2
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeAdjacent
@@ -0,0 +1,19 @@
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal
new file mode 100644
index 000000000..b2f54aef3
--- /dev/null
+++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal
@@ -0,0 +1,19 @@
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file