Skip to content
31 changes: 19 additions & 12 deletions src/main/java/edu/rpi/legup/controller/RuleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,26 @@ public void buttonPressed(Rule rule) {
if (LegupPreferences.getInstance()
.getUserPref(LegupPreferences.AUTO_GENERATE_CASES)
.equalsIgnoreCase(Boolean.toString(true))) {
try { // added try catch for scenarios where rules are cancelled by user ie.
// Skyscraper cellForNumber
CaseBoard caseBoard = caseRule.getCaseBoard(element.getBoard());
if (caseBoard != null && caseBoard.getCount() > 0) {
puzzle.notifyBoardListeners(
listener -> listener.onCaseBoardAdded(caseBoard));
} else {
updateErrorString =
"This board cannot be applied with this case rule.";
TreeNode treeNode = (TreeNode) element;
if (!treeNode.getChildren().isEmpty()) {
updateErrorString =
"Cases cannot be generated from a node with children.";
} else {
try { // added try catch for scenarios where rules are cancelled by user
// ie.
// Skyscraper cellForNumber
CaseBoard caseBoard = caseRule.getCaseBoard(element.getBoard());
if (caseBoard != null && caseBoard.getCount() > 0) {
puzzle.notifyBoardListeners(
listener -> listener.onCaseBoardAdded(caseBoard));
} else {
updateErrorString =
"This board cannot be applied with this case rule.";
}
} // catch rule was cancelled exception
catch (Exception e) {
updateErrorString = e.getMessage();
}
} // catch rule was cancelled exception
catch (Exception e) {
updateErrorString = e.getMessage();
}
} else {
updateErrorString =
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/edu/rpi/legup/history/CommandError.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ public enum CommandError {
NO_CHILDREN("The selection contains a tree node that has children."),
SELECTION_CONTAINS_NODE("The selection contains a tree node."),
SELECTION_CONTAINS_TRANSITION("The selection contains a tree transition."),
EMPTY(""),
DEFAULT_APPLICATION("[Apply Default Rule Application]");

private String value;
private final String value;

/**
* Constructs a CommandError with the specified error message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.*;

/**
* The ValidateContradictionRuleCommand class represents a command for validating and applying a
Expand Down Expand Up @@ -123,6 +124,40 @@ public String getErrorString() {
return CommandError.NO_SELECTED_VIEWS.toString();
}

// Add confirmation if multiple nodes that have changes will be removed
// A contradiction is typically applied to a given state, which is represented
// by a TreeNode. All further transitions and nodes do not matter if there
// is a contradiction at this node, and so this function disconnects the tree,
// potentially causing the user to lose progress.
boolean removingChildren = false;
for (TreeElementView view : selectedViews) {
TreeElement treeElement = view.getTreeElement();
TreeNode treeNode;
if (treeElement instanceof TreeNode) {
treeNode = (TreeNode) treeElement;
} else {
TreeTransition treeTransition = (TreeTransition) treeElement;
treeNode = treeTransition.getParents().getFirst();
}
// If any selected view has children, prompt for confirmation
if (!treeNode.getChildren().isEmpty()) {
removingChildren = true;
break;
}
}

if (removingChildren) {
int confirmReset =
JOptionPane.showConfirmDialog(
null,
"Applying a Contradiction Rule here may cause a large section of the tree to be removed. Continue?",
"Confirm Contradiction Rule",
JOptionPane.YES_NO_OPTION);
if (confirmReset == JOptionPane.NO_OPTION) {
return CommandError.EMPTY.toString();
}
}

for (TreeElementView view : selectedViews) {
if (view.getType() == TreeElementType.TRANSITION) {
TreeTransitionView transView = (TreeTransitionView) view;
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ public GridCell getCell(int x, int y) {
|| y >= dimension.height
|| x < 0
|| y < 0) {
System.err.printf(
"not in bounds, bounds are %dx%d\n", dimension.width, dimension.height);
return null;
}
return (GridCell) puzzleElements.get(y * dimension.width + x);
Expand Down
92 changes: 81 additions & 11 deletions src/main/java/edu/rpi/legup/model/rules/CaseRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import edu.rpi.legup.model.gameboard.PuzzleElement;
import edu.rpi.legup.model.tree.TreeNode;
import edu.rpi.legup.model.tree.TreeTransition;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.*;

/**
* CaseRule is an abstract class representing a rule that can be applied with multiple cases in a
Expand Down Expand Up @@ -55,11 +53,11 @@ public CaseRule(String ruleID, String ruleName, String description, String image
* @param puzzleElement equivalent puzzleElement
* @return a list of elements the specified could be
*/
public abstract List<Board> getCases(Board board, PuzzleElement puzzleElement);
public abstract ArrayList<Board> getCases(Board board, PuzzleElement puzzleElement);

/**
* Checks whether the {@link TreeTransition} logically follows from the parent node using this
* rule.
* rule. In the case rule implementation, sibling transitions will also be updated.
*
* @param transition transition to check
* @return null if the child node logically follow from the parent node, otherwise error message
Expand All @@ -71,7 +69,7 @@ public String checkRule(TreeTransition transition) {
return "Must not have multiple parent nodes";
}

for (TreeTransition childTrans : parentNodes.get(0).getChildren()) {
for (TreeTransition childTrans : parentNodes.getFirst().getChildren()) {
if (childTrans.getRule() == null
|| !childTrans.getRule().getClass().equals(this.getClass())) {
return "All children nodes must be justified with the same case rule.";
Expand All @@ -81,7 +79,7 @@ public String checkRule(TreeTransition transition) {

// Mark transition and new data as valid or not
boolean isCorrect = (check == null);
for (TreeTransition childTrans : parentNodes.get(0).getChildren()) {
for (TreeTransition childTrans : parentNodes.getFirst().getChildren()) {
childTrans.setCorrect(isCorrect);
for (PuzzleElement element : childTrans.getBoard().getModifiedData()) {
element.setValid(isCorrect);
Expand All @@ -93,13 +91,85 @@ public String checkRule(TreeTransition transition) {

/**
* Checks whether the {@link TreeTransition} logically follows from the parent node using this
* rule. This method is the one that should be overridden in child classes.
* rule. This method does not need to be overridden by child case rules.
*
* @param transition transition to check
* @return null if the child node logically follow from the parent node, otherwise error message
*/
@Override
public abstract String checkRuleRaw(TreeTransition transition);
public String checkRuleRaw(TreeTransition transition) {
// The default functionality of checkRuleRaw for case rules
// should work as follows:
// Generate all possible iterations of boards using getCases
// Attempt to match the set of boards with the children found in the tre
// Return true if one such match exists, false otherwise
TreeNode parent = transition.getParents().getFirst();

// If there are no modified cells, then return true if this is the only
// child. If it has siblings, then it should not be true
if (transition.getBoard().getModifiedData().isEmpty()) {
return parent.getChildren().size() == 1 ? null : INVALID_USE_MESSAGE;
}

Board board = parent.getBoard();
List<TreeTransition> childTransitions = parent.getChildren();
List<Board> childBoards = new ArrayList<>();
childTransitions.forEach(t -> childBoards.add(t.getBoard()));

CaseBoard possibleCasesBoard = getCaseBoard(parent.getBoard());

// Locate a cell where a case rule can be applied
Set<PuzzleElement<?>> applicableLocations = new HashSet<>();
for (PuzzleElement<?> element : possibleCasesBoard.getBaseBoard().getPuzzleElements()) {
if (possibleCasesBoard.isPickable(element, null)) {
applicableLocations.add(element);
}
}

// For each cell where a case rule can be applied, generate the cases
for (PuzzleElement<?> element : applicableLocations) {
ArrayList<Board> boards = getCases(board, element);
if (boards.size() != childBoards.size()) {
continue;
}

// For each board in the case, map the current boards to
// respective cases.
Map<Integer, Integer> indexMap = new HashMap<>();
int n = boards.size();
for (int i = 0; i < n; i++) {
Board caseBoard = boards.get(i);

// Attempt to match some board i with the caseBoard j
for (int j = 0; j < n; j++) {
Board childBoard = childBoards.get(j);

// If they match, and another child is already matching,
// the case rule was applied incorrectly. Otherwise,
// move to the next caseBoard/childBoard pairing
if (caseBoard.equalsBoard(childBoard)) {
if (indexMap.containsKey(j)) {
break;
}
indexMap.put(j, i);
break;
}
}

// If we couldn't find a match for board i, then this doesn't work
if (indexMap.size() <= i) {
break;
}
}

// If we have found a mapping that maps the n boards to each other,
// this is a valid application of the case rule
if (indexMap.size() == n) {
return null;
}
}
return INVALID_USE_MESSAGE;
}

/**
* Checks whether the child node logically follows from the parent node at the specific
Expand All @@ -117,8 +187,8 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement

/**
* Checks whether the child node logically follows from the parent node at the specific
* puzzleElement index using this rule. This method is the one that should be overridden in child
* classes.
* puzzleElement index using this rule. This method is the one that should be overridden in
* child classes.
*
* @param transition transition to check
* @param puzzleElement equivalent puzzleElement
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/edu/rpi/legup/model/rules/DirectRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public String checkRule(TreeTransition transition) {
LOGGER.debug(finalBoard.getModifiedData().size());
}
if (transition.getParents().size() != 1
|| transition.getParents().get(0).getChildren().size() != 1) {
|| transition.getParents().getFirst().getChildren().size() != 1) {
transition.getParents().getFirst().getChildren().forEach(t -> t.setCorrect(false));
return "State must have only 1 parent and 1 child";
} else if (finalBoard.getModifiedData().isEmpty()) {
// null transition
Expand All @@ -63,8 +64,8 @@ public String checkRuleRaw(TreeTransition transition) {
Board finalBoard = transition.getBoard();
String checkStr = null;

// Go directly to specific direct rule's judgement if no cells are edited
if (finalBoard.getModifiedData().size() == 0) {
// Go directly to specific direct rule's judgement if no cell's are edited
if (finalBoard.getModifiedData().isEmpty()) {
checkStr = checkRuleRawAt(transition, null);
}
for (PuzzleElement puzzleElement : finalBoard.getModifiedData()) {
Expand Down Expand Up @@ -93,7 +94,7 @@ public String checkRuleAt(TreeTransition transition, PuzzleElement puzzleElement
checkStr = "PuzzleElement must be modified";
} else {
if (transition.getParents().size() != 1
|| transition.getParents().get(0).getChildren().size() != 1) {
|| transition.getParents().getFirst().getChildren().size() != 1) {
checkStr = "State must have only 1 parent and 1 child";
} else {
checkStr = checkRuleRawAt(transition, puzzleElement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import edu.rpi.legup.ui.boardview.GridElementView;
import java.awt.*;

/**
* The view for a Battleship cell
*/
/** The view for a Battleship cell */
public class BattleshipElementView extends GridElementView {
private static final Stroke OUTLINE_STROKE = new BasicStroke(1);
private static final Color OUTLINE_COLOR = new Color(0x212121);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
import edu.rpi.legup.model.PuzzleExporter;
import edu.rpi.legup.model.gameboard.PuzzleElement;
import org.w3c.dom.Document;
/**
* Exports a Battleship puzzle to an XML file
*/

/** Exports a Battleship puzzle to an XML file */
public class BattleshipExporter extends PuzzleExporter {
/**
* Creates a new BattleshipExporter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Imports a Battleship puzzle from an XML file
*/

/** Imports a Battleship puzzle from an XML file */
public class BattleshipImporter extends PuzzleImporter {
public BattleshipImporter(Battleship battleShip) {
super(battleShip);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import edu.rpi.legup.model.gameboard.PuzzleElement;
import edu.rpi.legup.model.rules.CaseRule;
import edu.rpi.legup.model.tree.TreeTransition;
import java.util.List;
import java.util.ArrayList;

public class SegmentTypeCaseRule extends CaseRule {
public SegmentTypeCaseRule() {
Expand All @@ -16,18 +16,6 @@ public SegmentTypeCaseRule() {
"edu/rpi/legup/images/battleship/cases/SegmentType.png");
}

/**
* Checks whether the {@link TreeTransition} logically follows from the parent node using this
* rule. This method is the one that should overridden in child classes.
*
* @param transition transition to check
* @return null if the child node logically follow from the parent node, otherwise error message
*/
@Override
public String checkRuleRaw(TreeTransition transition) {
return null;
}

/**
* Checks whether the child node logically follows from the parent node at the specific
* puzzleElement index using this rule. This method is the one that should overridden in child
Expand Down Expand Up @@ -64,7 +52,7 @@ public CaseBoard getCaseBoard(Board board) {
* @return a list of elements the specified could be
*/
@Override
public List<Board> getCases(Board board, PuzzleElement puzzleElement) {
public ArrayList<Board> getCases(Board board, PuzzleElement puzzleElement) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import edu.rpi.legup.model.gameboard.PuzzleElement;
import edu.rpi.legup.model.rules.CaseRule;
import edu.rpi.legup.model.tree.TreeTransition;
import java.util.List;
import java.util.ArrayList;

public class ShipLocationCaseRule extends CaseRule {

Expand All @@ -17,18 +17,6 @@ public ShipLocationCaseRule() {
"edu/rpi/legup/images/battleship/cases/ShipLocations.png");
}

/**
* Checks whether the {@link TreeTransition} logically follows from the parent node using this
* rule. This method is the one that should overridden in child classes.
*
* @param transition transition to check
* @return null if the child node logically follow from the parent node, otherwise error message
*/
@Override
public String checkRuleRaw(TreeTransition transition) {
return null;
}

/**
* Checks whether the child node logically follows from the parent node at the specific
* puzzleElement index using this rule. This method is the one that should overridden in child
Expand Down Expand Up @@ -65,7 +53,7 @@ public CaseBoard getCaseBoard(Board board) {
* @return a list of elements the specified could be
*/
@Override
public List<Board> getCases(Board board, PuzzleElement puzzleElement) {
public ArrayList<Board> getCases(Board board, PuzzleElement puzzleElement) {
return null;
}
}
Loading