From 1146e827b0371f79f375bcb3782a6a627e0bad2b Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Mon, 21 Oct 2024 22:36:04 -0400 Subject: [PATCH 01/18] Modified Strange Function Name in LightUp Tests and Added a Few Comments --- src/main/java/edu/rpi/legup/history/History.java | 1 + .../edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java | 4 +--- src/main/java/edu/rpi/legup/utility/Logger.java | 5 +++++ .../lightup/rules/TooFewBulbsContradictionRuleTest.java | 2 +- .../lightup/rules/TooManyBulbsContradictionRuleTest.java | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/edu/rpi/legup/history/History.java b/src/main/java/edu/rpi/legup/history/History.java index b244e8f88..3d49424fa 100644 --- a/src/main/java/edu/rpi/legup/history/History.java +++ b/src/main/java/edu/rpi/legup/history/History.java @@ -11,6 +11,7 @@ * It maintains a list of commands and a current index to track the position in the history stack. */ public class History { + // This object does not refer to edu.rpi.legup.utility's class Logger, but rather apache's interface Logger private static final Logger LOGGER = LogManager.getLogger(History.class.getName()); private final Object lock = new Object(); diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java index 4c9ebf882..700e9c75e 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java @@ -348,9 +348,7 @@ public ImageIcon getIcon() { } /** - * Sets the icon for this panel - * - * @return the ImageIcon associated with this panel + * Sets the ImageIcon associated with this panel */ public void setIcon(ImageIcon icon) { this.icon = icon; diff --git a/src/main/java/edu/rpi/legup/utility/Logger.java b/src/main/java/edu/rpi/legup/utility/Logger.java index 67048e5b4..244161442 100644 --- a/src/main/java/edu/rpi/legup/utility/Logger.java +++ b/src/main/java/edu/rpi/legup/utility/Logger.java @@ -10,6 +10,11 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.layout.PatternLayout; +/** + * {@code Logger} is a class that exists only to initialize the imported logger objects + * from apache, and to initialize the files that record the action history. + */ + public class Logger { private static final String LEGUP_HOME = diff --git a/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java index fe994baa6..06090a796 100644 --- a/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/TooFewBulbsContradictionRuleTest.java @@ -21,7 +21,7 @@ public static void setUp() { } @Test - public void TooFewBulbsContradictionRule() throws InvalidFileFormatException { + public void FullTooFewTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/lightup/rules/TooFewBulbsContradictionRule/FullTooFewTest", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); diff --git a/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java b/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java index e27fa3323..63557d3d1 100644 --- a/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/TooManyBulbsContradictionRuleTest.java @@ -22,7 +22,7 @@ public static void setUp() { @Test // complex extensive toofew test - public void TooFewBulbsContradictionRule() throws InvalidFileFormatException { + public void FullTooManyTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/lightup/rules/TooManyBulbsContradictionRule/FullTooManyTest", lightUp); TreeNode rootNode = lightUp.getTree().getRootNode(); From 368a029d2944e6d5694586d39888088cd25eeefa Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Tue, 22 Oct 2024 17:14:44 -0400 Subject: [PATCH 02/18] Added Comments Relating to Removal and Drawing of TreeNodes/TreeElements. Added TODO related to fixing buggy behavior of not accessible but not deleted Nodes, which occurs when deleting a Node that has child Nodes. --- .../java/edu/rpi/legup/model/tree/Tree.java | 5 +++++ .../treeview/TreeElementView.java | 2 +- .../proofeditorui/treeview/TreeNodeView.java | 19 +++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/model/tree/Tree.java b/src/main/java/edu/rpi/legup/model/tree/Tree.java index 3e68015a1..a4aa3a822 100644 --- a/src/main/java/edu/rpi/legup/model/tree/Tree.java +++ b/src/main/java/edu/rpi/legup/model/tree/Tree.java @@ -97,9 +97,13 @@ public TreeElement addTreeElement(TreeTransition transition, TreeNode treeNode) * @param element the tree element to remove */ public void removeTreeElement(TreeElement element) { + // Currently this function does not delete children elements that extend from this node or transition. + // The children are indirectly removed from view (TreeView?) as they no longer have a connection to the base node. + // TODO: Recursively remove all children elements of this TreeElement if (element.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) element; + // Removes this node from its parent transition node.getParent().removeChild(node); node.getParent().setChildNode(null); } else { @@ -109,6 +113,7 @@ public void removeTreeElement(TreeElement element) { TreeController treeController = new TreeController(); TreeView treeView = new TreeView(treeController); treeView.removeTreeTransition(transition); + // Ensures that the other transition are still correct when this transition gets removed (redundant?) transition.getParents().get(0).getChildren().forEach(TreeTransition::reverify); } } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java index 228e69950..bddc5f876 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java @@ -141,7 +141,7 @@ public void setHover(boolean isHovered) { } /** - * Gets the visibility of the tree puzzleElement. Tells the TreeView whether or not to draw the + * Gets the visibility of the tree puzzleElement. Tells the TreeView whether to draw the * tree puzzleElement * * @return visibility of the tree puzzleElement diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java index 0e2a31bbf..578f08b4d 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java @@ -61,17 +61,20 @@ public TreeNodeView(TreeNode treeNode) { } /** - * Draws the TreeNodeView - * + * Draws the TreeNodeView as long as isVisible() is true + * and there is TreeNode attached to this TreeNodeView * @param graphics2D graphics2D used for drawing */ public void draw(Graphics2D graphics2D) { if (isVisible() && treeElement != null) { + // If the logical statement correctly leads to a proof by contradiction, + // draw the X that marks the end of this logical sequence. if (getTreeElement().getParent() != null && getTreeElement().getParent().isJustified() && getTreeElement().getParent().getRule().getRuleType() == RuleType.CONTRADICTION) { isContradictoryState = true; + // Draw two lines that make up the X with the contradiction color graphics2D.setColor(NODE_COLOR_CONTRADICTION); graphics2D.drawLine( location.x - RADIUS, @@ -84,11 +87,14 @@ && getTreeElement().getParent().getRule().getRuleType() location.x - RADIUS, location.y + RADIUS); } else { + // Else the node being drawn is not a contradiction isContradictoryState = false; graphics2D.setStroke(MAIN_STROKE); boolean isContraBranch = getTreeElement().isContradictoryBranch(); if (isSelected) { + // If the TreeNode is selected, draw it on the TreePanel with specified colors, + // outline, and special outline for selected nodes graphics2D.setColor(SELECTION_COLOR); graphics2D.fillOval( location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); @@ -105,7 +111,10 @@ && getTreeElement().getParent().getRule().getRuleType() DIAMETER + 8, DIAMETER + 8); } else { + // Else the current node is not being selected if (isHover) { + // Checks if the current Node is being hovered over. + // If it is, then draw the Node with specified properties graphics2D.setColor(HOVER_COLOR); graphics2D.fillOval( location.x - RADIUS, location.y - RADIUS, DIAMETER, DIAMETER); @@ -122,6 +131,8 @@ && getTreeElement().getParent().getRule().getRuleType() DIAMETER + 8, DIAMETER + 8); } else { + // Otherwise, this is a normal Node that isn't a contradiction, selected, or hovered Node + // Set color to contradiction color if this Node leads to a contradiction Node, or default color otherwise graphics2D.setColor( isContraBranch ? NODE_COLOR_CONTRADICTION : NODE_COLOR_DEFAULT); graphics2D.fillOval( @@ -215,6 +226,10 @@ public Point getLocation() { /** * Sets the location of the tree node * + * This function is never used; the only call that sets the location for where the Node will be drawn + * is currently implemented in edu.rpi.legup.ui.proofeditorui.treeview.TreeView, which uses the setX() + * and setY() functions to update location. (As of October 22, 2024) + * * @param location location of the tree node */ public void setLocation(Point location) { From f7970b3233a02c3c652e0e757979d66eedca345f Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 1 Nov 2024 17:05:18 -0400 Subject: [PATCH 03/18] Initializing Basic Framework and Resources for a New Puzzle --- .../rpi/legup/puzzle/kakurasu/Kakurasu.java | 28 ++++++++++++++++++ .../legup/puzzle/kakurasu/KakurasuBoard.java | 17 +++++++++++ .../legup/puzzle/kakurasu/KakurasuCell.java | 10 +++++++ .../legup/puzzle/kakurasu/KakurasuType.java | 5 ++++ .../legup/images/kakurasu/tiles/EmptyTile.png | Bin 0 -> 9700 bytes .../images/kakurasu/tiles/FilledTile.png | Bin 0 -> 9543 bytes .../images/kakurasu/tiles/UnknownTile.png | Bin 0 -> 9733 bytes 7 files changed, 60 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java create mode 100644 src/main/resources/edu/rpi/legup/images/kakurasu/tiles/EmptyTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/kakurasu/tiles/FilledTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/kakurasu/tiles/UnknownTile.png diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java new file mode 100644 index 000000000..d9ff2a926 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java @@ -0,0 +1,28 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class Kakurasu extends Puzzle { + public Kakurasu() { + super(); + } + + @Override + public void initializeView() { + } + + @Override + public Board generatePuzzle(int difficulty) { + return null; + } + + @Override + public boolean isBoardComplete(Board board) { + return true; + } + + @Override + public void onBoardChange(Board board) { + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java new file mode 100644 index 000000000..81f57cfa8 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -0,0 +1,17 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.gameboard.GridBoard; + +public class KakurasuBoard extends GridBoard { + public KakurasuBoard(int width, int height) { + super(width, height); + } + public KakurasuBoard(int size) { + super(size); + } + + @Override + public KakurasuCell getCell(int x, int y) { + return (KakurasuCell) super.getCell(x, y); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java new file mode 100644 index 000000000..2c3837a64 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -0,0 +1,10 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.gameboard.GridCell; +import java.awt.*; + +public class KakurasuCell extends GridCell { + KakurasuCell(int value, Point location) { + super(value, location); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java new file mode 100644 index 000000000..94f61b37f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java @@ -0,0 +1,5 @@ +package edu.rpi.legup.puzzle.kakurasu; + +public enum KakurasuType { + UNKNOWN, FILLED, EMPTY; +} diff --git a/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/EmptyTile.png b/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/EmptyTile.png new file mode 100644 index 0000000000000000000000000000000000000000..fc2c683eb7860f47d4322fe0e73b0508099a89de GIT binary patch literal 9700 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/FilledTile.png b/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/FilledTile.png new file mode 100644 index 0000000000000000000000000000000000000000..93e169df8f834511f9ac63d5504a41c1b9a4e917 GIT binary patch literal 9543 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/UnknownTile.png new file mode 100644 index 0000000000000000000000000000000000000000..850fbf127f04020fe83c6a4f6db75078967916ea GIT binary patch literal 9733 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 QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 From c40dd1cd0601e4212d168cf7663ae849f7bf9307 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 1 Nov 2024 17:28:20 -0400 Subject: [PATCH 04/18] Adding more framework code for Kakurasu. For graphics, need to make number labels for each of the rows, and each of the hint numbers. --- .../rpi/legup/puzzle/kakurasu/Kakurasu.java | 2 + .../legup/puzzle/kakurasu/KakurasuCell.java | 16 ++++++- .../puzzle/kakurasu/KakurasuController.java | 35 +++++++++++++++ .../puzzle/kakurasu/KakurasuElementView.java | 43 +++++++++++++++++++ .../legup/puzzle/kakurasu/KakurasuView.java | 24 +++++++++++ 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java index d9ff2a926..125ad9ea4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java @@ -10,6 +10,8 @@ public Kakurasu() { @Override public void initializeView() { + boardView = new KakurasuView((KakurasuBoard) currentBoard); + addBoardListener(boardView); } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java index 2c3837a64..f1c3b9130 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -1,10 +1,22 @@ package edu.rpi.legup.puzzle.kakurasu; import edu.rpi.legup.model.gameboard.GridCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; + import java.awt.*; -public class KakurasuCell extends GridCell { - KakurasuCell(int value, Point location) { +public class KakurasuCell extends GridCell { + + KakurasuCell(KakurasuType value, Point location) { super(value, location); } + + /** + * Gets the type of this KakurasuCell + * + * @return type of KakurasuCell + */ + public KakurasuType getType() { + return data; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java new file mode 100644 index 000000000..0f398873f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java @@ -0,0 +1,35 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.event.MouseEvent; + +public class KakurasuController extends ElementController { + + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + KakurasuCell cell = (KakurasuCell) data; + if (e.getButton() == MouseEvent.BUTTON1) { + if (e.isControlDown()) { + this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); + } else { + if (cell.getData() == KakurasuType.UNKNOWN) { + data.setData(KakurasuType.FILLED); + } else if (cell.getData() == KakurasuType.FILLED) { + data.setData(KakurasuType.EMPTY); + } else { + data.setData(KakurasuType.UNKNOWN); + } + } + } else if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == KakurasuType.UNKNOWN) { + data.setData(KakurasuType.EMPTY); + } else if (cell.getData() == KakurasuType.FILLED) { + data.setData(KakurasuType.UNKNOWN); + } else { + data.setData(KakurasuType.FILLED); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java new file mode 100644 index 000000000..05858146c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java @@ -0,0 +1,43 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.ui.boardview.GridElementView; + +import java.awt.*; + +public class KakurasuElementView extends GridElementView { + + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public KakurasuElementView(KakurasuCell cell) { + super(cell); + } + + @Override + public KakurasuCell getPuzzleElement() { + return (KakurasuCell) super.getPuzzleElement(); + } + + @Override + public void drawElement(Graphics2D graphics2D) { + KakurasuCell cell = (KakurasuCell) puzzleElement; + KakurasuType type = cell.getType(); + if (type == KakurasuType.FILLED) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.BLACK); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + } else if (type == KakurasuType.EMPTY) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.WHITE); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } else if (type == KakurasuType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java new file mode 100644 index 000000000..ec068a188 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java @@ -0,0 +1,24 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.ui.boardview.GridBoardView; + +import java.awt.*; + +public class KakurasuView extends GridBoardView { + + public KakurasuView(KakurasuBoard board) { + super(new BoardController(), new KakurasuController(), board.getDimension()); + + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + KakurasuCell cell = (KakurasuCell) puzzleElement; + Point loc = cell.getLocation(); + KakurasuElementView elementView = new KakurasuElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(elementView); + } + } +} \ No newline at end of file From 1a7d664e810b03157adad8be3804169665c54109 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Tue, 5 Nov 2024 16:41:54 -0500 Subject: [PATCH 05/18] Instantiated remaining templated code listed on wiki for Kakurasu. --- .../rpi/legup/puzzle/kakurasu/Kakurasu.java | 9 ++ .../legup/puzzle/kakurasu/KakurasuCell.java | 11 +- .../puzzle/kakurasu/KakurasuCellFactory.java | 58 +++++++++ .../puzzle/kakurasu/KakurasuElementView.java | 3 - .../puzzle/kakurasu/KakurasuExporter.java | 32 +++++ .../puzzle/kakurasu/KakurasuImporter.java | 115 ++++++++++++++++++ .../legup/puzzle/kakurasu/KakurasuType.java | 6 +- 7 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java index 125ad9ea4..823543e7d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java @@ -2,10 +2,19 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCellFactory; +import edu.rpi.legup.puzzle.nurikabe.NurikabeExporter; +import edu.rpi.legup.puzzle.nurikabe.NurikabeImporter; public class Kakurasu extends Puzzle { public Kakurasu() { super(); + this.name = "Kakurasu"; + + this.importer = new KakurasuImporter(this); + this.exporter = new KakurasuExporter(this); + + this.factory = new KakurasuCellFactory(); } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java index f1c3b9130..753804d3d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -5,9 +5,9 @@ import java.awt.*; -public class KakurasuCell extends GridCell { +public class KakurasuCell extends GridCell { - KakurasuCell(KakurasuType value, Point location) { + KakurasuCell(int value, Point location) { super(value, location); } @@ -17,6 +17,11 @@ public class KakurasuCell extends GridCell { * @return type of KakurasuCell */ public KakurasuType getType() { - return data; + return switch (data) { + case 0 -> KakurasuType.UNKNOWN; + case 1 -> KakurasuType.FILLED; + case 2 -> KakurasuType.EMPTY; + default -> null; + }; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java new file mode 100644 index 000000000..09b8f1b07 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java @@ -0,0 +1,58 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.ElementFactory; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.awt.*; + +public class KakurasuCellFactory extends ElementFactory { + @Override + public KakurasuCell importCell(Node node, Board board) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("cell")) { + throw new InvalidFileFormatException("kakurasu Factory: unknown puzzleElement puzzleElement"); + } + + KakurasuBoard kakurasuBoard = (KakurasuBoard) board; + int width = kakurasuBoard.getWidth(); + int height = kakurasuBoard.getHeight(); + + NamedNodeMap attributeList = node.getAttributes(); + int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); + int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); + int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); + if (x >= width || y >= height) { + throw new InvalidFileFormatException("kakurasu Factory: cell location out of bounds"); + } + if (value < 0 || value > 2) { + throw new InvalidFileFormatException("kakurasu Factory: cell unknown value"); + } + + KakurasuCell cell = new KakurasuCell(value, new Point(x, y)); + cell.setIndex(y * height + x); + return cell; + } catch (NumberFormatException e) { + throw new InvalidFileFormatException("kakurasu Factory: unknown value where integer expected"); + } catch (NullPointerException e) { + throw new InvalidFileFormatException("kakurasu Factory: could not find attribute(s)"); + } + } + + public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { + org.w3c.dom.Element cellElement = document.createElement("cell"); + + KakurasuCell cell = (KakurasuCell) puzzleElement; + Point loc = cell.getLocation(); + + cellElement.setAttribute("value", String.valueOf(cell.getData())); + cellElement.setAttribute("x", String.valueOf(loc.x)); + cellElement.setAttribute("y", String.valueOf(loc.y)); + + return cellElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java index 05858146c..261af8c59 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java @@ -6,9 +6,6 @@ public class KakurasuElementView extends GridElementView { - private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); - private static final Color FONT_COLOR = Color.BLACK; - public KakurasuElementView(KakurasuCell cell) { super(cell); } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java new file mode 100644 index 000000000..23410b47f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java @@ -0,0 +1,32 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.PuzzleExporter; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import org.w3c.dom.Document; + +public class KakurasuExporter extends PuzzleExporter { + public KakurasuExporter(Kakurasu kakurasu) { + super(kakurasu); + } + + @Override + protected org.w3c.dom.Element createBoardElement(Document newDocument) { + KakurasuBoard board = (KakurasuBoard) puzzle.getTree().getRootNode().getBoard(); + + org.w3c.dom.Element boardElement = newDocument.createElement("board"); + boardElement.setAttribute("width", String.valueOf(board.getWidth())); + boardElement.setAttribute("height", String.valueOf(board.getHeight())); + + org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + KakurasuCell cell = (KakurasuCell) puzzleElement; + if (cell.getData() != 0) { + org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + cellsElement.appendChild(cellElement); + } + } + + boardElement.appendChild(cellsElement); + return boardElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java new file mode 100644 index 000000000..d848aa498 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java @@ -0,0 +1,115 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.PuzzleImporter; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.awt.*; + +public class KakurasuImporter extends PuzzleImporter { + public KakurasuImporter(Kakurasu kakurasu) { + super(kakurasu); + } + + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + + /** + * Creates an empty board for building + * + * @param rows the number of rows on the board + * @param columns the number of columns on the board + * @throws RuntimeException if board can not be created + */ + @Override + public void initializeBoard(int rows, int columns) { + KakurasuBoard kakurasuBoard = new KakurasuBoard(columns, rows); + + for (int y = 0; y < rows; y++) { + for (int x = 0; x < columns; x++) { + KakurasuCell cell = + new KakurasuCell(KakurasuType.UNKNOWN.toValue(), new Point(x, y)); + cell.setIndex(y * columns + x); + cell.setModifiable(true); + kakurasuBoard.setCell(x, y, cell); + } + } + puzzle.setCurrentBoard(kakurasuBoard); + } + + /** + * Creates the board for building + * + * @param node xml document node + * @throws InvalidFileFormatException if file is invalid + */ + @Override + public void initializeBoard(Node node) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("board")) { + throw new InvalidFileFormatException("kakurasu Importer: cannot find board puzzleElement"); + } + Element boardElement = (Element) node; + if (boardElement.getElementsByTagName("cells").getLength() == 0) { + throw new InvalidFileFormatException("kakurasu Importer: no puzzleElement found for board"); + } + Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("cell"); + + KakurasuBoard kakurasuBoard = null; + if (!boardElement.getAttribute("size").isEmpty()) { + int size = Integer.valueOf(boardElement.getAttribute("size")); + kakurasuBoard = new KakurasuBoard(size); + } else if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + int width = Integer.valueOf(boardElement.getAttribute("width")); + int height = Integer.valueOf(boardElement.getAttribute("height")); + kakurasuBoard = new KakurasuBoard(width, height); + } + + if (kakurasuBoard == null) { + throw new InvalidFileFormatException("kakurasu Importer: invalid board dimensions"); + } + + int width = kakurasuBoard.getWidth(); + int height = kakurasuBoard.getHeight(); + + for (int i = 0; i < elementDataList.getLength(); i++) { + KakurasuCell cell = (KakurasuCell) puzzle.getFactory().importCell(elementDataList.item(i), kakurasuBoard); + Point loc = cell.getLocation(); + if (cell.getData() != KakurasuType.UNKNOWN.toValue()) { + cell.setModifiable(false); + cell.setGiven(true); + } + kakurasuBoard.setCell(loc.x, loc.y, cell); + } + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (kakurasuBoard.getCell(x, y) == null) { + KakurasuCell cell = new KakurasuCell(KakurasuType.UNKNOWN.toValue(), new Point(x, y)); + cell.setIndex(y * height + x); + cell.setModifiable(true); + kakurasuBoard.setCell(x, y, cell); + } + } + } + puzzle.setCurrentBoard(kakurasuBoard); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException("kakurasu Importer: unknown value where integer expected"); + } + } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Kakurasu cannot accept text input"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java index 94f61b37f..f586cd1a7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java @@ -1,5 +1,9 @@ package edu.rpi.legup.puzzle.kakurasu; public enum KakurasuType { - UNKNOWN, FILLED, EMPTY; + UNKNOWN(0), FILLED(1), EMPTY(2); + + int value; + private KakurasuType(int value) {} + int toValue() {return value;} } From 1e91004abcd52d44f7772867c4554de69ca215ac Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 8 Nov 2024 17:35:45 -0500 Subject: [PATCH 06/18] Skyscraper board implementation can be repurposed for Kakurasu (specifically the clue implementation) --- .../legup/puzzle/kakurasu/KakurasuBoard.java | 4 ++++ .../legup/puzzle/kakurasu/KakurasuCell.java | 1 + .../puzzle/kakurasu/KakurasuCellFactory.java | 1 + .../puzzle/kakurasu/KakurasuController.java | 20 +++++++++---------- .../puzzle/kakurasu/KakurasuExporter.java | 1 + .../puzzle/kakurasu/KakurasuImporter.java | 1 + .../legup/puzzle/kakurasu/KakurasuType.java | 16 ++++++++++++--- .../puzzle/kakurasu/elements/EmptyTile.java | 12 +++++++++++ .../puzzle/kakurasu/elements/FilledTile.java | 12 +++++++++++ .../puzzle/kakurasu/elements/UnknownTile.java | 12 +++++++++++ .../kakurasu_elements_reference_sheet.txt | 3 +++ 11 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/EmptyTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/FilledTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/UnknownTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java index 81f57cfa8..140e25cec 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -2,7 +2,11 @@ import edu.rpi.legup.model.gameboard.GridBoard; +import java.util.ArrayList; + public class KakurasuBoard extends GridBoard { + // TODO: Add the vertical and horizontal clues to the kakurasu board, and also add the row/column labels + public KakurasuBoard(int width, int height) { super(width, height); } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java index 753804d3d..917636760 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -5,6 +5,7 @@ import java.awt.*; +// TODO: Mirror the implementation of SkyscraperCell, in order to have clues on the edge public class KakurasuCell extends GridCell { KakurasuCell(int value, Point location) { diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java index 09b8f1b07..7cb9d49ca 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java @@ -10,6 +10,7 @@ import java.awt.*; +// TODO: Include changes to Kakurasu cell into the cell factory public class KakurasuCellFactory extends ElementFactory { @Override public KakurasuCell importCell(Node node, Board board) throws InvalidFileFormatException { diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java index 0f398873f..d6546b961 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java @@ -14,21 +14,21 @@ public void changeCell(MouseEvent e, PuzzleElement data) { if (e.isControlDown()) { this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); } else { - if (cell.getData() == KakurasuType.UNKNOWN) { - data.setData(KakurasuType.FILLED); - } else if (cell.getData() == KakurasuType.FILLED) { - data.setData(KakurasuType.EMPTY); + if (cell.getType() == KakurasuType.UNKNOWN) { + cell.setData(KakurasuType.FILLED.toValue()); + } else if (cell.getType() == KakurasuType.FILLED) { + cell.setData(KakurasuType.EMPTY.toValue()); } else { - data.setData(KakurasuType.UNKNOWN); + cell.setData(KakurasuType.UNKNOWN.toValue()); } } } else if (e.getButton() == MouseEvent.BUTTON3) { - if (cell.getData() == KakurasuType.UNKNOWN) { - data.setData(KakurasuType.EMPTY); - } else if (cell.getData() == KakurasuType.FILLED) { - data.setData(KakurasuType.UNKNOWN); + if (cell.getType() == KakurasuType.UNKNOWN) { + cell.setData(KakurasuType.EMPTY.toValue()); + } else if (cell.getType() == KakurasuType.FILLED) { + cell.setData(KakurasuType.UNKNOWN.toValue()); } else { - data.setData(KakurasuType.FILLED); + cell.setData(KakurasuType.FILLED.toValue()); } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java index 23410b47f..30edb0c6e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java @@ -4,6 +4,7 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import org.w3c.dom.Document; +// TODO: Include changes to puzzle implementation into the exporter public class KakurasuExporter extends PuzzleExporter { public KakurasuExporter(Kakurasu kakurasu) { super(kakurasu); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java index d848aa498..8529c7033 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java @@ -8,6 +8,7 @@ import java.awt.*; +// TODO: Include changes to puzzle implementation into the importer public class KakurasuImporter extends PuzzleImporter { public KakurasuImporter(Kakurasu kakurasu) { super(kakurasu); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java index f586cd1a7..6ef7665c0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java @@ -1,9 +1,19 @@ package edu.rpi.legup.puzzle.kakurasu; public enum KakurasuType { - UNKNOWN(0), FILLED(1), EMPTY(2); + UNKNOWN(0), FILLED(1), EMPTY(2), + HORIZONTAL_CLUE(-1), VERTICAL_CLUE(-2); - int value; - private KakurasuType(int value) {} + public int value; + KakurasuType(int value) {this.value = value;} int toValue() {return value;} + public static KakurasuType convertToKakuType(int num) { + return switch (num) { + case 1 -> KakurasuType.FILLED; + case 2 -> KakurasuType.EMPTY; + case -1 -> KakurasuType.HORIZONTAL_CLUE; + case -2 -> KakurasuType.VERTICAL_CLUE; + default -> KakurasuType.UNKNOWN; + }; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/EmptyTile.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/EmptyTile.java new file mode 100644 index 000000000..0aaacdcd9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/EmptyTile.java @@ -0,0 +1,12 @@ +package edu.rpi.legup.puzzle.kakurasu.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class EmptyTile extends PlaceableElement { + public EmptyTile() { + super("KAKU-ELEM-0002", + "Empty Tile", + "Kakurasu empty tile", + "edu/rpi/legup/images/kakurasu/tiles/EmptyTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/FilledTile.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/FilledTile.java new file mode 100644 index 000000000..6b0685879 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/FilledTile.java @@ -0,0 +1,12 @@ +package edu.rpi.legup.puzzle.kakurasu.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class FilledTile extends PlaceableElement { + public FilledTile() { + super("KAKU-ELEM-0001", + "Filled Tile", + "Kakurasu filled tile", + "edu/rpi/legup/images/kakurasu/tiles/FilledTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/UnknownTile.java new file mode 100644 index 000000000..dc678aa72 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/UnknownTile.java @@ -0,0 +1,12 @@ +package edu.rpi.legup.puzzle.kakurasu.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class UnknownTile extends PlaceableElement { + public UnknownTile() { + super("KAKU-ELEM-0003", + "Unknown Tile", + "Kakurasu unknown tile", + "edu/rpi/legup/images/kakurasu/tiles/UnknownTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt new file mode 100644 index 000000000..6bb440f46 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt @@ -0,0 +1,3 @@ +KAKU-ELEM-0001 : FilledTile +KAKU-ELEM-0002 : EmptyTile +KAKU-ELEM-0003 : UnknownTile From 6a67cd5766bc934ba8bced35c53bda0562cef455 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Tue, 12 Nov 2024 17:41:41 -0500 Subject: [PATCH 07/18] Made a few changes to Kakurasu Implementation. Need to change the KakurasuType Implementation due to editor functionality. --- .../rpi/legup/puzzle/kakurasu/Kakurasu.java | 13 ++++++ .../legup/puzzle/kakurasu/KakurasuBoard.java | 3 ++ .../legup/puzzle/kakurasu/KakurasuCell.java | 39 +++++++++++++++++- .../legup/puzzle/kakurasu/KakurasuClue.java | 7 ++++ .../puzzle/kakurasu/elements/ClueTile.java | 12 ++++++ .../kakurasu_elements_reference_sheet.txt | 1 + .../legup/images/kakurasu/tiles/ClueTile.png | Bin 0 -> 353 bytes 7 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/ClueTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/kakurasu/tiles/ClueTile.png diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java index 823543e7d..4d5787eb4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java @@ -20,6 +20,7 @@ public Kakurasu() { @Override public void initializeView() { boardView = new KakurasuView((KakurasuBoard) currentBoard); + boardView.setBoard(currentBoard); addBoardListener(boardView); } @@ -28,6 +29,18 @@ public Board generatePuzzle(int difficulty) { return null; } + /** + * Determines if the given dimensions are valid for Kakurasu + * + * @param rows the number of rows + * @param columns the number of columns + * @return true if the given dimensions are valid for Kakurasu, false otherwise + */ + @Override + public boolean isValidDimensions(int rows, int columns) { + return rows >= 3 && rows == columns; + } + @Override public boolean isBoardComplete(Board board) { return true; diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java index 140e25cec..78252ef75 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -6,6 +6,9 @@ public class KakurasuBoard extends GridBoard { // TODO: Add the vertical and horizontal clues to the kakurasu board, and also add the row/column labels + // Clues initialized with the puzzle + private ArrayList horizontalClues; + private ArrayList verticalClues; public KakurasuBoard(int width, int height) { super(width, height); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java index 917636760..39a7727e6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -1,14 +1,17 @@ package edu.rpi.legup.puzzle.kakurasu; +import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import java.awt.*; +import java.awt.event.MouseEvent; // TODO: Mirror the implementation of SkyscraperCell, in order to have clues on the edge public class KakurasuCell extends GridCell { - KakurasuCell(int value, Point location) { + public KakurasuCell(int value, Point location) { super(value, location); } @@ -25,4 +28,38 @@ public KakurasuType getType() { default -> null; }; } + + /** + * For use in constructing Kakurasu puzzles in the puzzle editor + * + * @param e The element that is being placed at the location + * @param m The location being edited to include e element + */ + @Override + public void setType(Element e, MouseEvent m) { + // TODO: Complete this function + switch (e.getElementID()) { + case "KAKU-ELEM-0001": + + break; + case "KAKU-ELEM-0002": + + break; + case "KAKU-ELEM-0003": + this.data = 0; + break; + case "KAKU-ELEM-0004": + this.data = 1; + break; + } + } + + @Override + public KakurasuCell copy() { + KakurasuCell copy = new KakurasuCell(data, (Point) location.clone()); + copy.setIndex(index); + copy.setModifiable(isModifiable); + copy.setGiven(isGiven); + return copy; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java new file mode 100644 index 000000000..9d11488d7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java @@ -0,0 +1,7 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.model.gameboard.PuzzleElement; + +public class KakurasuClue extends PuzzleElement { + +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/ClueTile.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/ClueTile.java new file mode 100644 index 000000000..560c442e1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/ClueTile.java @@ -0,0 +1,12 @@ +package edu.rpi.legup.puzzle.kakurasu.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ClueTile extends PlaceableElement { + public ClueTile() { + super("KAKU-ELEM-0004", + "Clue Tile", + "Kakurasu clue tile", + "edu/rpi/legup/images/kakurasu/tiles/ClueTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt index 6bb440f46..d3ba4ff3c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/elements/kakurasu_elements_reference_sheet.txt @@ -1,3 +1,4 @@ KAKU-ELEM-0001 : FilledTile KAKU-ELEM-0002 : EmptyTile KAKU-ELEM-0003 : UnknownTile +KAKU-ELEM-0004 : ClueTile \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/ClueTile.png b/src/main/resources/edu/rpi/legup/images/kakurasu/tiles/ClueTile.png new file mode 100644 index 0000000000000000000000000000000000000000..7a86827f96c840aca4175288e997c49a205ee7cd GIT binary patch literal 353 zcmV-n0iOPeP)Px$8%ab#R9HvtmOrjVKop06EWn@A7DT1eDCqQhg~~3hfW#6+6k-D^6)I}2*Z{q_ zFC%%)%gak<++44iQ_r09&6jgdMgZ`_gCP)recvBJkYyPhfE(ZjxB+SdbzLLRa}2|P z<2Yd3Hj1KvBuUiEjA;@1zK^P^Ad2EuFp46SWr<~3pePCz1QWn-6-Xk>vd}cma{z)M zVB5Cm05na*JkOs8h~pS(nj%ROgkktufN7fGI1UWMK-YCt#mV7YO*oDtT-SxJ>(@xK z1T#S2_wYOqrfH&WTbA0_9VUQvT`%Q#Uj9wY1fZ%a&hx}OzH!t5q|3=F4uSw#mNBbm zHb98~3R)}F25vw3M* Date: Fri, 15 Nov 2024 17:25:46 -0500 Subject: [PATCH 08/18] Pivoted from modeling off Skyscraper to modeling off TreeTent. Since I don't need line in my puzzle, I tried to remove it from my new code. --- .../legup/puzzle/kakurasu/ClueCommand.java | 185 ++++++++++++++++++ .../rpi/legup/puzzle/kakurasu/Kakurasu.java | 70 ++++++- .../legup/puzzle/kakurasu/KakurasuBoard.java | 179 ++++++++++++++++- .../legup/puzzle/kakurasu/KakurasuCell.java | 55 +++--- .../puzzle/kakurasu/KakurasuCellFactory.java | 61 +++--- .../legup/puzzle/kakurasu/KakurasuClue.java | 48 +++++ .../puzzle/kakurasu/KakurasuClueView.java | 55 ++++++ .../puzzle/kakurasu/KakurasuController.java | 77 ++++++-- .../puzzle/kakurasu/KakurasuElementView.java | 74 +++++-- .../puzzle/kakurasu/KakurasuExporter.java | 50 ++++- .../puzzle/kakurasu/KakurasuImporter.java | 139 +++++++++++-- .../legup/puzzle/kakurasu/KakurasuType.java | 22 +-- .../legup/puzzle/kakurasu/KakurasuView.java | 181 ++++++++++++++++- 13 files changed, 1054 insertions(+), 142 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java new file mode 100644 index 000000000..c5c31dd03 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java @@ -0,0 +1,185 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.history.CommandError; +import edu.rpi.legup.history.PuzzleCommand; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.tree.*; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + +public class ClueCommand extends PuzzleCommand { + private TreeViewSelection selection; + private KakurasuClueView clueView; + private Map addTran; + private List> emptyCells; + + public ClueCommand(TreeViewSelection selection, KakurasuClueView clueView) { + this.selection = selection; + this.clueView = clueView; + this.addTran = new HashMap<>(); + this.emptyCells = new ArrayList<>(); + } + + /** Executes a command */ + @Override + public void executeCommand() { + Puzzle puzzle = getInstance().getPuzzleModule(); + Tree tree = puzzle.getTree(); + TreeView treeView = getInstance().getLegupUI().getTreePanel().getTreeView(); + + final TreeViewSelection newSelection = new TreeViewSelection(); + for (int i = 0; i < selection.getSelectedViews().size(); i++) { + TreeElementView selectedView = selection.getSelectedViews().get(i); + TreeElement treeElement = selectedView.getTreeElement(); + + final TreeTransition finalTran; + KakurasuBoard board = (KakurasuBoard) treeElement.getBoard(); + List tempList = emptyCells.get(i); + if (treeElement.getType() == TreeElementType.NODE) { + TreeNode treeNode = (TreeNode) treeElement; + + TreeTransition transition = addTran.get(treeNode); + if (transition == null) { + transition = tree.addNewTransition(treeNode); + addTran.put(treeNode, transition); + } else { + treeNode.addChild(transition); + } + + finalTran = transition; + puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalTran)); + + newSelection.addToSelection(treeView.getElementView(finalTran)); + board = (KakurasuBoard) finalTran.getBoard(); + } else { + finalTran = (TreeTransition) treeElement; + newSelection.addToSelection(treeView.getElementView(treeElement)); + } + + for (KakurasuCell cell : tempList) { + cell = (KakurasuCell) board.getPuzzleElement(cell); + cell.setData(KakurasuType.GRASS); + board.addModifiedData(cell); + finalTran.propagateChange(cell); + + final KakurasuCell finalCell = cell; + puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(finalCell)); + } + if (i == 0) { + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTran)); + } + } + puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); + } + + /** + * Gets the reason why the command cannot be executed + * + * @return if command cannot be executed, returns reason for why the command cannot be executed, + * otherwise null if command can be executed + */ + @Override + public String getErrorString() { + if (selection.getSelectedViews().isEmpty()) { + return CommandError.NO_SELECTED_VIEWS.toString(); + } + + emptyCells.clear(); + for (TreeElementView view : selection.getSelectedViews()) { + TreeElement treeElement = view.getTreeElement(); + KakurasuBoard board = (KakurasuBoard) treeElement.getBoard(); + if (treeElement.getType() == TreeElementType.NODE) { + TreeNode node = (TreeNode) treeElement; + if (!node.getChildren().isEmpty()) { + return CommandError.UNMODIFIABLE_BOARD.toString(); + } + } else { + if (!board.isModifiable()) { + return CommandError.UNMODIFIABLE_BOARD.toString(); + } + } + + List tempList = new ArrayList<>(); + KakurasuClue clue = clueView.getPuzzleElement(); + if (clue.getType() == KakurasuType.CLUE_NORTH + || clue.getType() == KakurasuType.CLUE_SOUTH) { + int col = + clue.getType() == KakurasuType.CLUE_NORTH + ? clue.getClueIndex() + : clue.getClueIndex() - 1; + for (int i = 0; i < board.getWidth(); i++) { + KakurasuCell cell = board.getCell(col, i); + if (cell.getType() == KakurasuType.UNKNOWN && cell.isModifiable()) { + tempList.add(cell); + } + } + } else { + int row = + clue.getType() == KakurasuType.CLUE_WEST + ? clue.getClueIndex() + : clue.getClueIndex() - 1; + for (int i = 0; i < board.getWidth(); i++) { + KakurasuCell cell = board.getCell(i, row); + if (cell.getType() == KakurasuType.UNKNOWN && cell.isModifiable()) { + tempList.add(cell); + } + } + } + if (tempList.isEmpty()) { + return "There are no modifiable unknown cells in every selected tree element."; + } + emptyCells.add(tempList); + } + return null; + } + + /** Undoes an command */ + @Override + public void undoCommand() { + Puzzle puzzle = getInstance().getPuzzleModule(); + Tree tree = puzzle.getTree(); + + for (int i = 0; i < selection.getSelectedViews().size(); i++) { + TreeElementView selectedView = selection.getSelectedViews().get(i); + TreeElement treeElement = selectedView.getTreeElement(); + + final TreeTransition finalTran; + KakurasuBoard board = (KakurasuBoard) treeElement.getBoard(); + List tempList = emptyCells.get(i); + if (treeElement.getType() == TreeElementType.NODE) { + TreeNode treeNode = (TreeNode) treeElement; + + finalTran = treeNode.getChildren().get(0); + tree.removeTreeElement(finalTran); + puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(finalTran)); + + board = (KakurasuBoard) finalTran.getBoard(); + } else { + finalTran = (TreeTransition) treeElement; + } + + for (KakurasuCell cell : tempList) { + cell = (KakurasuCell) board.getPuzzleElement(cell); + cell.setData(KakurasuType.UNKNOWN); + board.removeModifiedData(cell); + + final KakurasuCell finalCell = cell; + puzzle.notifyBoardListeners(listener -> listener.onBoardDataChanged(finalCell)); + } + + if (i == 0) { + puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTran)); + } + } + final TreeViewSelection newSelection = selection; + puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java index 4d5787eb4..c531e5d33 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java @@ -2,13 +2,16 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.puzzle.nurikabe.NurikabeCellFactory; -import edu.rpi.legup.puzzle.nurikabe.NurikabeExporter; -import edu.rpi.legup.puzzle.nurikabe.NurikabeImporter; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; + +import java.util.List; public class Kakurasu extends Puzzle { + public Kakurasu() { super(); + this.name = "Kakurasu"; this.importer = new KakurasuImporter(this); @@ -17,36 +20,85 @@ public Kakurasu() { this.factory = new KakurasuCellFactory(); } + /** Initializes the game board. Called by the invoker of the class */ @Override public void initializeView() { + KakurasuBoard board = (KakurasuBoard) currentBoard; boardView = new KakurasuView((KakurasuBoard) currentBoard); - boardView.setBoard(currentBoard); - addBoardListener(boardView); + boardView.setBoard(board); } + /** + * Generates a random edu.rpi.legup.puzzle based on the difficulty + * + * @param difficulty level of difficulty (1-10) + * @return board of the random edu.rpi.legup.puzzle + */ @Override public Board generatePuzzle(int difficulty) { return null; } + @Override /** * Determines if the given dimensions are valid for Kakurasu * * @param rows the number of rows * @param columns the number of columns - * @return true if the given dimensions are valid for Kakurasu, false otherwise + * @return true if the given dimensions are valid for Tree Tent, false otherwise */ - @Override public boolean isValidDimensions(int rows, int columns) { - return rows >= 3 && rows == columns; + // This is a placeholder, this method needs to be implemented + return rows > 0 && columns > 0; } + /** + * Determines if the current board is a valid state + * + * @param board board to check for validity + * @return true if board is valid, false otherwise + */ @Override public boolean isBoardComplete(Board board) { + KakurasuBoard kakurasuBoard = (KakurasuBoard) board; + + for (ContradictionRule rule : contradictionRules) { + if (rule.checkContradiction(kakurasuBoard) == null) { + return false; + } + } + for (PuzzleElement data : kakurasuBoard.getPuzzleElements()) { + KakurasuCell cell = (KakurasuCell) data; + if (cell.getType() == KakurasuType.UNKNOWN) { + return false; + } + } return true; } + /** + * Callback for when the board puzzleElement changes + * + * @param board the board that has changed + */ + @Override + public void onBoardChange(Board board) {} + + /** + * @return if it is valid Kakurasu puzzle must have same number of clues as the dimension size + */ @Override - public void onBoardChange(Board board) { + public boolean checkValidity() { + // TODO: Fix this function + KakurasuBoard b = (KakurasuBoard) this.getBoardView().getBoard(); + List elements = b.getPuzzleElements(); + int treeCount = 0; + for (PuzzleElement element : elements) { + KakurasuCell c = (KakurasuCell) element; + if (c.getType() == KakurasuType.TREE) { + treeCount++; + } + } + return treeCount != 0; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java index 78252ef75..10cf85870 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -1,24 +1,193 @@ package edu.rpi.legup.puzzle.kakurasu; +import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.awt.*; import java.util.ArrayList; +import java.util.List; public class KakurasuBoard extends GridBoard { - // TODO: Add the vertical and horizontal clues to the kakurasu board, and also add the row/column labels - // Clues initialized with the puzzle - private ArrayList horizontalClues; - private ArrayList verticalClues; + + private ArrayList rowClues; + private ArrayList colClues; public KakurasuBoard(int width, int height) { super(width, height); + + this.rowClues = new ArrayList<>(); + this.colClues = new ArrayList<>(); + + for (int i = 0; i < height; i++) { + rowClues.add(null); + } + for (int i = 0; i < width; i++) { + colClues.add(null); + } } + public KakurasuBoard(int size) { - super(size); + this(size, size); + } + + public ArrayList getRowClues() { + return rowClues; + } + + public ArrayList getColClues() { + return colClues; } @Override public KakurasuCell getCell(int x, int y) { return (KakurasuCell) super.getCell(x, y); } + + @Override + public PuzzleElement getPuzzleElement(PuzzleElement element) { + return switch (element.getIndex()) { + case -2 -> element; + case -1 -> element; + default -> super.getPuzzleElement(element); + }; + } + + @Override + public void setPuzzleElement(int index, PuzzleElement puzzleElement) { + if (index < puzzleElements.size()) { + puzzleElements.set(index, puzzleElement); + } + // TODO: Throw warning if index is above puzzleElement.size()? + } + + @Override + public void notifyChange(PuzzleElement puzzleElement) { + int index = puzzleElement.getIndex(); + if (index < puzzleElements.size()) { + puzzleElements.set(index, puzzleElement); + } + } + + public KakurasuClue getClue(int x, int y) { + if (x == getWidth() && 0 <= y && y < getHeight()) { + return rowClues.get(y); + } else { + if (y == getHeight() && 0 <= x && x < getWidth()) { + return colClues.get(x); + } + } + return null; + } + + /** + * Get a list of all orthogonally adjacent cells. + * + * @param cell The cell to get adjacent cells from. + * @param type The cell types to get. + * @return List of adjacent cells in the form { up, right, down, left }. If an adjacent cell is + * null, it will not be added to the list. + */ + public List getAdjacent(KakurasuCell cell, KakurasuType type) { + List adj = new ArrayList<>(); + Point loc = cell.getLocation(); + for (int i = -2; i < 2; i++) { + KakurasuCell adjCell = getCell(loc.x + (i % 2), loc.y + ((i + 1) % 2)); + if (adjCell != null && adjCell.getType() == type) { + adj.add(adjCell); + } + } + return adj; + } + + /** + * Gets all cells of a specified type that are diagonals of a specified cell + * + * @param cell the base cell + * @param type the type to look for + * @return a list of TreeTentCells that are diagonals of the given KakurasuCell and are of the + * given KakurasuType + */ + public List getDiagonals(KakurasuCell cell, KakurasuType type) { + List dia = new ArrayList<>(); + Point loc = cell.getLocation(); + KakurasuCell upRight = getCell(loc.x + 1, loc.y - 1); + KakurasuCell downRight = getCell(loc.x + 1, loc.y + 1); + KakurasuCell downLeft = getCell(loc.x - 1, loc.y + 1); + KakurasuCell upLeft = getCell(loc.x - 1, loc.y - 1); + if (upRight != null && upRight.getType() == type) { + dia.add(upRight); + } + if (downLeft != null && downLeft.getType() == type) { + dia.add(downLeft); + } + if (downRight != null && downRight.getType() == type) { + dia.add(downRight); + } + if (upLeft != null && upLeft.getType() == type) { + dia.add(upLeft); + } + return dia; + } + + /** + * Creates and returns a list of TreeTentCells that match the given KakurasuType + * + * @param index the row or column number + * @param type type of Kakurasu element + * @param isRow boolean value based on whether a row of column is being checked + * @return List of TreeTentCells that match the given KakurasuType + */ + public List getRowCol(int index, KakurasuType type, boolean isRow) { + List list = new ArrayList<>(); + if (isRow) { + for (int i = 0; i < dimension.width; i++) { + KakurasuCell cell = getCell(i, index); + if (cell.getType() == type) { + list.add(cell); + } + } + } else { + for (int i = 0; i < dimension.height; i++) { + KakurasuCell cell = getCell(index, i); + if (cell.getType() == type) { + list.add(cell); + } + } + } + return list; + } + + /** + * Determines if this board contains the equivalent puzzle elements as the one specified + * + * @param board board to check equivalence + * @return true if the boards are equivalent, false otherwise + */ + @Override + public boolean equalsBoard(Board board) { + KakurasuBoard kakurasuBoard = (KakurasuBoard) board; + return super.equalsBoard(kakurasuBoard); + } + + /** + * Performs a deep copy of the KakurasuBoard + * + * @return a KakurasuBoard object that is a deep copy of the current KakurasuBoard + */ + @Override + public KakurasuBoard copy() { + KakurasuBoard copy = new KakurasuBoard(dimension.width, dimension.height); + for (int x = 0; x < this.dimension.width; x++) { + for (int y = 0; y < this.dimension.height; y++) { + copy.setCell(x, y, getCell(x, y).copy()); + } + } + for (PuzzleElement e : modifiedData) { + copy.getPuzzleElement(e).setModifiable(false); + } + copy.rowClues = rowClues; + copy.colClues = colClues; + return copy; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java index 39a7727e6..f2641bf22 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -2,54 +2,45 @@ import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; -import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -import edu.rpi.legup.puzzle.skyscrapers.SkyscrapersCell; import java.awt.*; import java.awt.event.MouseEvent; -// TODO: Mirror the implementation of SkyscraperCell, in order to have clues on the edge -public class KakurasuCell extends GridCell { +public class KakurasuCell extends GridCell { - public KakurasuCell(int value, Point location) { + public KakurasuCell(KakurasuType value, Point location) { super(value, location); } - /** - * Gets the type of this KakurasuCell - * - * @return type of KakurasuCell - */ public KakurasuType getType() { - return switch (data) { - case 0 -> KakurasuType.UNKNOWN; - case 1 -> KakurasuType.FILLED; - case 2 -> KakurasuType.EMPTY; - default -> null; - }; + return data; + } + + public int getValue() { + switch (data) { + case FILLED: + return 1; + case EMPTY: + return 2; + default: + return 0; + } } - /** - * For use in constructing Kakurasu puzzles in the puzzle editor - * - * @param e The element that is being placed at the location - * @param m The location being edited to include e element - */ @Override public void setType(Element e, MouseEvent m) { - // TODO: Complete this function - switch (e.getElementID()) { - case "KAKU-ELEM-0001": - + switch (e.getElementName()) { + case "Unknown Tile": + this.data = KakurasuType.UNKNOWN; break; - case "KAKU-ELEM-0002": - + case "Filled Tile": + this.data = KakurasuType.FILLED; break; - case "KAKU-ELEM-0003": - this.data = 0; + case "Empty Tile": + this.data = KakurasuType.EMPTY; break; - case "KAKU-ELEM-0004": - this.data = 1; + default: + System.out.println("KakurasuCell.setType: Unknown element"); break; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java index 7cb9d49ca..b879013ea 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCellFactory.java @@ -10,47 +10,64 @@ import java.awt.*; -// TODO: Include changes to Kakurasu cell into the cell factory public class KakurasuCellFactory extends ElementFactory { + /** + * Creates a puzzleElement based on the xml document Node and adds it to the board + * + * @param node node that represents the puzzleElement + * @param board board to add the newly created cell + * @return newly created cell from the xml document Node + * @throws InvalidFileFormatException if file is invalid + */ @Override - public KakurasuCell importCell(Node node, Board board) throws InvalidFileFormatException { + public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { try { - if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("kakurasu Factory: unknown puzzleElement puzzleElement"); - } - KakurasuBoard kakurasuBoard = (KakurasuBoard) board; int width = kakurasuBoard.getWidth(); int height = kakurasuBoard.getHeight(); - NamedNodeMap attributeList = node.getAttributes(); - int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); - int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); - int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); - if (x >= width || y >= height) { - throw new InvalidFileFormatException("kakurasu Factory: cell location out of bounds"); - } - if (value < 0 || value > 2) { - throw new InvalidFileFormatException("kakurasu Factory: cell unknown value"); - } + if (node.getNodeName().equalsIgnoreCase("cell")) { + + int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); + int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); + int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); + if (x >= width || y >= height) { + throw new InvalidFileFormatException( + "Kakurasu Factory: cell location out of bounds"); + } + if (value < 0 || value > 3) { + throw new InvalidFileFormatException("Kakurasu Factory: cell unknown value"); + } - KakurasuCell cell = new KakurasuCell(value, new Point(x, y)); - cell.setIndex(y * height + x); - return cell; + KakurasuCell cell = new KakurasuCell(KakurasuType.valueOf(value), new Point(x, y)); + cell.setIndex(y * width + x); + return cell; + } else { + throw new InvalidFileFormatException( + "Kakurasu Factory: unknown puzzleElement puzzleElement"); + } } catch (NumberFormatException e) { - throw new InvalidFileFormatException("kakurasu Factory: unknown value where integer expected"); + throw new InvalidFileFormatException( + "Kakurasu Factory: unknown value where integer expected"); } catch (NullPointerException e) { - throw new InvalidFileFormatException("kakurasu Factory: could not find attribute(s)"); + throw new InvalidFileFormatException("Kakurasu Factory: could not find attribute(s)"); } } + /** + * Creates a xml document puzzleElement from a cell for exporting + * + * @param document xml document + * @param puzzleElement PuzzleElement cell + * @return xml PuzzleElement + */ public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { org.w3c.dom.Element cellElement = document.createElement("cell"); KakurasuCell cell = (KakurasuCell) puzzleElement; Point loc = cell.getLocation(); - cellElement.setAttribute("value", String.valueOf(cell.getData())); + cellElement.setAttribute("value", String.valueOf(cell.getValue())); cellElement.setAttribute("x", String.valueOf(loc.x)); cellElement.setAttribute("y", String.valueOf(loc.y)); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java index 9d11488d7..c8d429eda 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java @@ -3,5 +3,53 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; public class KakurasuClue extends PuzzleElement { + private KakurasuType type; + private int clueIndex; + public KakurasuClue(int value, int clueIndex, KakurasuType type) { + super(value); + this.index = -2; + this.clueIndex = clueIndex; + this.type = type; + } + + public static String colNumToString(int col) { + final StringBuilder sb = new StringBuilder(); + col--; + while (col >= 0) { + int numChar = (col % 26) + 65; + sb.append((char) numChar); + col = (col / 26) - 1; + } + return sb.reverse().toString(); + } + + public static int colStringToColNum(String col) { + int result = 0; + for (int i = 0; i < col.length(); i++) { + result *= 26; + result += col.charAt(i) - 'A' + 1; + } + return result; + } + + public int getClueIndex() { + return clueIndex; + } + + public void setClueIndex(int clueIndex) { + this.clueIndex = clueIndex; + } + + public KakurasuType getType() { + return type; + } + + public void setType(KakurasuType type) { + this.type = type; + } + + public KakurasuClue copy() { + return new KakurasuClue(data, clueIndex, type); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java new file mode 100644 index 000000000..ffab2a351 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java @@ -0,0 +1,55 @@ +package edu.rpi.legup.puzzle.kakurasu; + +import edu.rpi.legup.ui.boardview.ElementView; + +import java.awt.*; + +public class KakurasuClueView extends ElementView { + + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public KakurasuClueView(KakurasuClue clue) { + super(clue); + } + + /** + * Gets the PuzzleElement associated with this view + * + * @return PuzzleElement associated with this view + */ + @Override + public KakurasuClue getPuzzleElement() { + return (KakurasuClue) super.getPuzzleElement(); + } + + @Override + public void drawElement(Graphics2D graphics2D) { + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value; + + KakurasuClue clue = getPuzzleElement(); + switch (clue.getType()) { + case CLUE_NORTH: + value = String.valueOf(clue.getData() + 1); + break; + case CLUE_EAST: + value = String.valueOf(clue.getData()); + break; + case CLUE_SOUTH: + value = String.valueOf(clue.getData()); + break; + case CLUE_WEST: + value = KakurasuClue.colNumToString(clue.getData() + 1); + break; + default: + value = ""; + } + + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + graphics2D.drawString(value, xText, yText); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java index d6546b961..e1c93e407 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java @@ -1,35 +1,78 @@ package edu.rpi.legup.puzzle.kakurasu; +import edu.rpi.legup.app.GameBoardFacade; import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.history.AutoCaseRuleCommand; +import edu.rpi.legup.history.EditDataCommand; +import edu.rpi.legup.history.ICommand; +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.ui.boardview.BoardView; +import edu.rpi.legup.ui.boardview.ElementView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; +import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import java.awt.event.MouseEvent; +import static edu.rpi.legup.app.GameBoardFacade.getInstance; + public class KakurasuController extends ElementController { + private ElementView lastCellPressed; + + public KakurasuController() { + super(); + this.lastCellPressed = null; + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.getButton() != MouseEvent.BUTTON2) { + BoardView boardView = getInstance().getLegupUI().getBoardView(); + if (boardView != null) { + lastCellPressed = boardView.getElement(e.getPoint()); + } + } + } + + @Override + public void mouseReleased(MouseEvent e) { + // TODO: Figure out if mouseReleased is still needed, and what it needs to do + if (GameBoardFacade.getInstance().getLegupUI().getTreePanel() != null + && e.getButton() != MouseEvent.BUTTON2) { + lastCellPressed = null; + } else { + super.mouseReleased(e); + } + } + @Override - public void changeCell(MouseEvent e, PuzzleElement data) { - KakurasuCell cell = (KakurasuCell) data; + public void changeCell(MouseEvent e, PuzzleElement element) { + KakurasuCell cell = (KakurasuCell) element; if (e.getButton() == MouseEvent.BUTTON1) { - if (e.isControlDown()) { - this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); + if (cell.getData() == KakurasuType.UNKNOWN) { + element.setData(KakurasuType.FILLED); } else { - if (cell.getType() == KakurasuType.UNKNOWN) { - cell.setData(KakurasuType.FILLED.toValue()); - } else if (cell.getType() == KakurasuType.FILLED) { - cell.setData(KakurasuType.EMPTY.toValue()); + if (cell.getData() == KakurasuType.FILLED) { + element.setData(KakurasuType.EMPTY); } else { - cell.setData(KakurasuType.UNKNOWN.toValue()); + element.setData(KakurasuType.UNKNOWN); } } - } else if (e.getButton() == MouseEvent.BUTTON3) { - if (cell.getType() == KakurasuType.UNKNOWN) { - cell.setData(KakurasuType.EMPTY.toValue()); - } else if (cell.getType() == KakurasuType.FILLED) { - cell.setData(KakurasuType.UNKNOWN.toValue()); - } else { - cell.setData(KakurasuType.FILLED.toValue()); + } else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == KakurasuType.UNKNOWN) { + element.setData(KakurasuType.EMPTY); + } else { + if (cell.getData() == KakurasuType.EMPTY) { + element.setData(KakurasuType.FILLED); + } else { + element.setData(KakurasuType.UNKNOWN); + } + } } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java index 261af8c59..97c74a963 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java @@ -3,38 +3,72 @@ import edu.rpi.legup.ui.boardview.GridElementView; import java.awt.*; +import java.awt.geom.Rectangle2D; public class KakurasuElementView extends GridElementView { - public KakurasuElementView(KakurasuCell cell) { super(cell); } - @Override - public KakurasuCell getPuzzleElement() { - return (KakurasuCell) super.getPuzzleElement(); - } - + /** + * Draws on the given frame based on the type of the cell of the current puzzleElement + * + * @param graphics2D the frame to be drawn on + */ @Override public void drawElement(Graphics2D graphics2D) { KakurasuCell cell = (KakurasuCell) puzzleElement; KakurasuType type = cell.getType(); - if (type == KakurasuType.FILLED) { - graphics2D.setStroke(new BasicStroke(1)); - graphics2D.setColor(Color.BLACK); - graphics2D.fillRect(location.x, location.y, size.width, size.height); - } else if (type == KakurasuType.EMPTY) { - graphics2D.setStroke(new BasicStroke(1)); - graphics2D.setColor(Color.WHITE); - graphics2D.fillRect(location.x, location.y, size.width, size.height); - graphics2D.setColor(Color.BLACK); - graphics2D.drawRect(location.x, location.y, size.width, size.height); - } else if (type == KakurasuType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(0)); + if (type == KakurasuType.UNKNOWN) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.LIGHT_GRAY); - graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.fill( + new Rectangle2D.Double( + location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); graphics2D.setColor(Color.BLACK); - graphics2D.drawRect(location.x, location.y, size.width, size.height); + graphics2D.draw( + new Rectangle2D.Double( + location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); + } else { + if (type == KakurasuType.TREE) { + graphics2D.drawImage( + KakurasuView.TREE, + location.x, + location.y, + size.width, + size.height, + null, + null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } else { + if (type == KakurasuType.GRASS) { + graphics2D.drawImage( + KakurasuView.GRASS, + location.x, + location.y, + size.width, + size.height, + null, + null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } else { + if (type == KakurasuType.TENT) { + graphics2D.drawImage( + KakurasuView.TENT, + location.x, + location.y, + size.width, + size.height, + null, + null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + } + } } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java index 30edb0c6e..f0be179bb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java @@ -4,15 +4,26 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import org.w3c.dom.Document; -// TODO: Include changes to puzzle implementation into the exporter public class KakurasuExporter extends PuzzleExporter { + public KakurasuExporter(Kakurasu kakurasu) { super(kakurasu); } + /** + * Creates and returns a new board element in the XML document specified + * + * @param newDocument the XML document to append to + * @return the new board element + */ @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { - KakurasuBoard board = (KakurasuBoard) puzzle.getTree().getRootNode().getBoard(); + KakurasuBoard board; + if (puzzle.getTree() != null) { + board = (KakurasuBoard) puzzle.getTree().getRootNode().getBoard(); + } else { + board = (KakurasuBoard) puzzle.getBoardView().getBoard(); + } org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("width", String.valueOf(board.getWidth())); @@ -21,13 +32,42 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); for (PuzzleElement puzzleElement : board.getPuzzleElements()) { KakurasuCell cell = (KakurasuCell) puzzleElement; - if (cell.getData() != 0) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + if (cell.getData() != KakurasuType.UNKNOWN) { + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); } } - boardElement.appendChild(cellsElement); + + org.w3c.dom.Element axisEast = newDocument.createElement("axis"); + axisEast.setAttribute("side", "east"); + for (KakurasuClue clue : board.getRowClues()) { + org.w3c.dom.Element clueElement = newDocument.createElement("clue"); + clueElement.setAttribute("value", String.valueOf(clue.getData())); + clueElement.setAttribute("index", KakurasuClue.colNumToString(clue.getClueIndex())); + axisEast.appendChild(clueElement); + } + boardElement.appendChild(axisEast); + + org.w3c.dom.Element axisSouth = newDocument.createElement("axis"); + axisSouth.setAttribute("side", "south"); + for (KakurasuClue clue : board.getColClues()) { + org.w3c.dom.Element clueElement = newDocument.createElement("clue"); + clueElement.setAttribute("value", String.valueOf(clue.getData())); + clueElement.setAttribute("index", String.valueOf(clue.getClueIndex())); + axisSouth.appendChild(clueElement); + } + boardElement.appendChild(axisSouth); + + if (!board.getLines().isEmpty()) { + org.w3c.dom.Element linesElement = newDocument.createElement("lines"); + for (PuzzleElement data : board.getLines()) { + org.w3c.dom.Element lineElement = puzzle.getFactory().exportCell(newDocument, data); + linesElement.appendChild(lineElement); + } + boardElement.appendChild(linesElement); + } return boardElement; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java index 8529c7033..a6f8771cf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java @@ -7,8 +7,9 @@ import org.w3c.dom.NodeList; import java.awt.*; +import java.util.ArrayList; +import java.util.List; -// TODO: Include changes to puzzle implementation into the importer public class KakurasuImporter extends PuzzleImporter { public KakurasuImporter(Kakurasu kakurasu) { super(kakurasu); @@ -37,13 +38,23 @@ public void initializeBoard(int rows, int columns) { for (int y = 0; y < rows; y++) { for (int x = 0; x < columns; x++) { - KakurasuCell cell = - new KakurasuCell(KakurasuType.UNKNOWN.toValue(), new Point(x, y)); - cell.setIndex(y * columns + x); - cell.setModifiable(true); - kakurasuBoard.setCell(x, y, cell); + if (kakurasuBoard.getCell(x, y) == null) { + KakurasuCell cell = new KakurasuCell(KakurasuType.UNKNOWN, new Point(x, y)); + cell.setIndex(y * columns + x); + cell.setModifiable(true); + kakurasuBoard.setCell(x, y, cell); + } } } + + for (int i = 0; i < rows; i++) { + kakurasuBoard.getRowClues().set(i, new KakurasuClue(0, i + 1, KakurasuType.CLUE_EAST)); + } + + for (int i = 0; i < columns; i++) { + kakurasuBoard.getColClues().set(i, new KakurasuClue(0, i + 1, KakurasuType.CLUE_SOUTH)); + } + puzzle.setCurrentBoard(kakurasuBoard); } @@ -57,11 +68,13 @@ public void initializeBoard(int rows, int columns) { public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { - throw new InvalidFileFormatException("kakurasu Importer: cannot find board puzzleElement"); + throw new InvalidFileFormatException( + "Kakurasu Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { - throw new InvalidFileFormatException("kakurasu Importer: no puzzleElement found for board"); + throw new InvalidFileFormatException( + "Kakurasu Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); NodeList elementDataList = dataElement.getElementsByTagName("cell"); @@ -70,23 +83,29 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { if (!boardElement.getAttribute("size").isEmpty()) { int size = Integer.valueOf(boardElement.getAttribute("size")); kakurasuBoard = new KakurasuBoard(size); - } else if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { - int width = Integer.valueOf(boardElement.getAttribute("width")); - int height = Integer.valueOf(boardElement.getAttribute("height")); - kakurasuBoard = new KakurasuBoard(width, height); + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { + int width = Integer.valueOf(boardElement.getAttribute("width")); + int height = Integer.valueOf(boardElement.getAttribute("height")); + kakurasuBoard = new KakurasuBoard(width, height); + } } if (kakurasuBoard == null) { - throw new InvalidFileFormatException("kakurasu Importer: invalid board dimensions"); + throw new InvalidFileFormatException("Kakurasu Importer: invalid board dimensions"); } int width = kakurasuBoard.getWidth(); int height = kakurasuBoard.getHeight(); for (int i = 0; i < elementDataList.getLength(); i++) { - KakurasuCell cell = (KakurasuCell) puzzle.getFactory().importCell(elementDataList.item(i), kakurasuBoard); + KakurasuCell cell = + (KakurasuCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), kakurasuBoard); Point loc = cell.getLocation(); - if (cell.getData() != KakurasuType.UNKNOWN.toValue()) { + if (cell.getData() != KakurasuType.UNKNOWN) { cell.setModifiable(false); cell.setGiven(true); } @@ -96,21 +115,103 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (kakurasuBoard.getCell(x, y) == null) { - KakurasuCell cell = new KakurasuCell(KakurasuType.UNKNOWN.toValue(), new Point(x, y)); - cell.setIndex(y * height + x); + KakurasuCell cell = new KakurasuCell(KakurasuType.UNKNOWN, new Point(x, y)); + cell.setIndex(y * width + x); cell.setModifiable(true); kakurasuBoard.setCell(x, y, cell); } } } + + NodeList axes = boardElement.getElementsByTagName("axis"); + if (axes.getLength() != 2) { + throw new InvalidFileFormatException("Kakurasu Importer: cannot find axes"); + } + + Element axis1 = (Element) axes.item(0); + Element axis2 = (Element) axes.item(1); + + if (!axis1.hasAttribute("side") || !axis1.hasAttribute("side")) { + throw new InvalidFileFormatException( + "Kakurasu Importer: side attribute of axis not specified"); + } + String side1 = axis1.getAttribute("side"); + String side2 = axis2.getAttribute("side"); + if (side1.equalsIgnoreCase(side2) + || !(side1.equalsIgnoreCase("east") || side1.equalsIgnoreCase("south")) + || !(side2.equalsIgnoreCase("east") || side2.equalsIgnoreCase("south"))) { + throw new InvalidFileFormatException( + "Kakurasu Importer: axes must be different and be {east | south}"); + } + NodeList eastClues = + side1.equalsIgnoreCase("east") + ? axis1.getElementsByTagName("clue") + : axis2.getElementsByTagName("clue"); + NodeList southClues = + side1.equalsIgnoreCase("south") + ? axis1.getElementsByTagName("clue") + : axis2.getElementsByTagName("clue"); + + if (eastClues.getLength() != kakurasuBoard.getHeight() + || southClues.getLength() != kakurasuBoard.getWidth()) { + throw new InvalidFileFormatException( + "Kakurasu Importer: there must be same number of clues as the dimension of" + + " the board"); + } + + for (int i = 0; i < eastClues.getLength(); i++) { + Element clue = (Element) eastClues.item(i); + int value = Integer.valueOf(clue.getAttribute("value")); + int index = KakurasuClue.colStringToColNum(clue.getAttribute("index")); + + if (index - 1 < 0 || index - 1 > kakurasuBoard.getHeight()) { + throw new InvalidFileFormatException( + "Kakurasu Importer: clue index out of bounds"); + } + + if (kakurasuBoard.getRowClues().get(index - 1) != null) { + throw new InvalidFileFormatException("Kakurasu Importer: duplicate clue index"); + } + kakurasuBoard + .getRowClues() + .set(index - 1, new KakurasuClue(value, index, KakurasuType.CLUE_EAST)); + } + + for (int i = 0; i < southClues.getLength(); i++) { + Element clue = (Element) southClues.item(i); + int value = Integer.valueOf(clue.getAttribute("value")); + int index = Integer.valueOf(clue.getAttribute("index")); + + if (index - 1 < 0 || index - 1 > kakurasuBoard.getWidth()) { + throw new InvalidFileFormatException( + "Kakurasu Importer: clue index out of bounds"); + } + + if (kakurasuBoard.getColClues().get(index - 1) != null) { + throw new InvalidFileFormatException("Kakurasu Importer: duplicate clue index"); + } + kakurasuBoard + .getColClues() + .set(index - 1, new KakurasuClue(value, index, KakurasuType.CLUE_SOUTH)); + } + puzzle.setCurrentBoard(kakurasuBoard); } catch (NumberFormatException e) { - throw new InvalidFileFormatException("kakurasu Importer: unknown value where integer expected"); + throw new InvalidFileFormatException( + "Kakurasu Importer: unknown value where integer expected"); } } @Override public void initializeBoard(String[] statements) throws UnsupportedOperationException { - throw new UnsupportedOperationException("Kakurasu cannot accept text input"); + throw new UnsupportedOperationException("Tree Tent cannot accept text input"); + } + + @Override + public List getImporterElements() { + List elements = new ArrayList<>(); + elements.add("cell"); + elements.add("line"); + return elements; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java index 6ef7665c0..4773fbcc0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuType.java @@ -1,19 +1,19 @@ package edu.rpi.legup.puzzle.kakurasu; public enum KakurasuType { - UNKNOWN(0), FILLED(1), EMPTY(2), - HORIZONTAL_CLUE(-1), VERTICAL_CLUE(-2); + UNKNOWN, + FILLED, + EMPTY, + CLUE_NORTH, + CLUE_EAST, + CLUE_SOUTH, + CLUE_WEST; - public int value; - KakurasuType(int value) {this.value = value;} - int toValue() {return value;} - public static KakurasuType convertToKakuType(int num) { + public static KakurasuType valueOf(int num) { return switch (num) { - case 1 -> KakurasuType.FILLED; - case 2 -> KakurasuType.EMPTY; - case -1 -> KakurasuType.HORIZONTAL_CLUE; - case -2 -> KakurasuType.VERTICAL_CLUE; - default -> KakurasuType.UNKNOWN; + case 1 -> FILLED; + case 2 -> EMPTY; + default -> UNKNOWN; }; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java index ec068a188..ccd9efd7a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuView.java @@ -1,24 +1,201 @@ package edu.rpi.legup.puzzle.kakurasu; import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.tree.TreeElement; +import edu.rpi.legup.ui.boardview.ElementView; import edu.rpi.legup.ui.boardview.GridBoardView; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import javax.imageio.ImageIO; import java.awt.*; +import java.io.IOException; +import java.util.ArrayList; public class KakurasuView extends GridBoardView { + private static final Logger LOGGER = LogManager.getLogger(KakurasuView.class.getName()); + + static Image FILLED, EMPTY, UNKNOWN; + + static { + try { + FILLED = + ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/kakurasu/tiles/FilledTile.png")); + EMPTY = + ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/kakurasu/tiles/EmptyTile.png")); + UNKNOWN = + ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/kakurasu/tiles/UnknownTile.png")); + } catch (IOException e) { + LOGGER.error("Failed to open Kakurasu images"); + } + } + + private ArrayList northClues; + private ArrayList eastClues; + private ArrayList southClues; + private ArrayList westClues; public KakurasuView(KakurasuBoard board) { super(new BoardController(), new KakurasuController(), board.getDimension()); + this.northClues = new ArrayList<>(); + this.eastClues = new ArrayList<>(); + this.southClues = new ArrayList<>(); + this.westClues = new ArrayList<>(); + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { KakurasuCell cell = (KakurasuCell) puzzleElement; Point loc = cell.getLocation(); KakurasuElementView elementView = new KakurasuElementView(cell); elementView.setIndex(cell.getIndex()); elementView.setSize(elementSize); - elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementView.setLocation( + new Point((loc.x + 1) * elementSize.width, (loc.y + 1) * elementSize.height)); elementViews.add(elementView); } + + for (int i = 0; i < gridSize.height; i++) { + KakurasuClueView row = + new KakurasuClueView(new KakurasuClue(i, i, KakurasuType.CLUE_WEST)); + row.setLocation(new Point(0, (i + 1) * elementSize.height)); + row.setSize(elementSize); + + KakurasuClueView clue = new KakurasuClueView(board.getRowClues().get(i)); + clue.setLocation( + new Point( + (gridSize.width + 1) * elementSize.width, + (i + 1) * elementSize.height)); + clue.setSize(elementSize); + + westClues.add(row); + eastClues.add(clue); + } + + for (int i = 0; i < gridSize.width; i++) { + KakurasuClueView col = + new KakurasuClueView(new KakurasuClue(i, i, KakurasuType.CLUE_NORTH)); + col.setLocation(new Point((i + 1) * elementSize.width, 0)); + col.setSize(elementSize); + + KakurasuClueView clue = new KakurasuClueView(board.getColClues().get(i)); + clue.setLocation( + new Point( + (i + 1) * elementSize.width, + (gridSize.height + 1) * elementSize.height)); + clue.setSize(elementSize); + + northClues.add(col); + southClues.add(clue); + } + } + + /** + * Gets the ElementView from the location specified or null if one does not exists at that + * location + * + * @param point location on the viewport + * @return ElementView at the specified location + */ + @Override + public ElementView getElement(Point point) { + Point scaledPoint = + new Point( + (int) Math.round(point.x / getScale()), + (int) Math.round(point.y / getScale())); + for (ElementView element : elementViews) { + if (element.isWithinBounds(scaledPoint)) { + return element; + } + } + for (KakurasuClueView clueView : northClues) { + if (clueView.isWithinBounds(scaledPoint)) { + return clueView; + } + } + for (KakurasuClueView clueView : eastClues) { + if (clueView.isWithinBounds(scaledPoint)) { + return clueView; + } + } + for (KakurasuClueView clueView : southClues) { + if (clueView.isWithinBounds(scaledPoint)) { + return clueView; + } + } + for (KakurasuClueView clueView : westClues) { + if (clueView.isWithinBounds(scaledPoint)) { + return clueView; + } + } + return null; + } + + public ArrayList getNorthClues() { + return northClues; + } + + public ArrayList getEastClues() { + return eastClues; + } + + public ArrayList getSouthClues() { + return southClues; + } + + public ArrayList getWestClues() { + return westClues; + } + + @Override + protected Dimension getProperSize() { + Dimension boardViewSize = new Dimension(); + boardViewSize.width = (gridSize.width + 2) * elementSize.width; + boardViewSize.height = (gridSize.height + 2) * elementSize.height; + return boardViewSize; + } + + /** + * Called when the tree element has changed. + * + * @param treeElement tree element + */ + @Override + public void onTreeElementChanged(TreeElement treeElement) { + super.onTreeElementChanged(treeElement); + KakurasuBoard kakurasuBoard; + if (board instanceof CaseBoard) { + kakurasuBoard = (KakurasuBoard) ((CaseBoard) board).getBaseBoard(); + } else { + kakurasuBoard = (KakurasuBoard) board; + } + } + + @Override + public void drawBoard(Graphics2D graphics2D) { + super.drawBoard(graphics2D); + + for (KakurasuClueView clueView : northClues) { + clueView.draw(graphics2D); + } + + for (KakurasuClueView clueView : eastClues) { + clueView.draw(graphics2D); + } + + for (KakurasuClueView clueView : southClues) { + clueView.draw(graphics2D); + } + + for (KakurasuClueView clueView : westClues) { + clueView.draw(graphics2D); + } } -} \ No newline at end of file +} From 80e9e0626df1a649afaa6bf8be2030e90e6ab453 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 15 Nov 2024 17:55:50 -0500 Subject: [PATCH 09/18] Removed some unneeded code and some errors --- .../legup/puzzle/kakurasu/KakurasuBoard.java | 3 +- .../legup/puzzle/kakurasu/KakurasuCell.java | 13 +++--- .../legup/puzzle/kakurasu/KakurasuClue.java | 20 --------- .../puzzle/kakurasu/KakurasuClueView.java | 21 +++------- .../puzzle/kakurasu/KakurasuElementView.java | 41 ++++++------------- .../puzzle/kakurasu/KakurasuExporter.java | 10 +---- .../puzzle/kakurasu/KakurasuImporter.java | 2 +- 7 files changed, 25 insertions(+), 85 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java index 10cf85870..d0ac89cb3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -47,8 +47,7 @@ public KakurasuCell getCell(int x, int y) { @Override public PuzzleElement getPuzzleElement(PuzzleElement element) { return switch (element.getIndex()) { - case -2 -> element; - case -1 -> element; + case -2, -1 -> element; default -> super.getPuzzleElement(element); }; } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java index f2641bf22..4aefb5b1f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuCell.java @@ -17,14 +17,11 @@ public KakurasuType getType() { } public int getValue() { - switch (data) { - case FILLED: - return 1; - case EMPTY: - return 2; - default: - return 0; - } + return switch (data) { + case FILLED -> 1; + case EMPTY -> 2; + default -> 0; + }; } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java index c8d429eda..445ef3f20 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClue.java @@ -13,26 +13,6 @@ public KakurasuClue(int value, int clueIndex, KakurasuType type) { this.type = type; } - public static String colNumToString(int col) { - final StringBuilder sb = new StringBuilder(); - col--; - while (col >= 0) { - int numChar = (col % 26) + 65; - sb.append((char) numChar); - col = (col / 26) - 1; - } - return sb.reverse().toString(); - } - - public static int colStringToColNum(String col) { - int result = 0; - for (int i = 0; i < col.length(); i++) { - result *= 26; - result += col.charAt(i) - 'A' + 1; - } - return result; - } - public int getClueIndex() { return clueIndex; } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java index ffab2a351..63e24738c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuClueView.java @@ -31,22 +31,11 @@ public void drawElement(Graphics2D graphics2D) { String value; KakurasuClue clue = getPuzzleElement(); - switch (clue.getType()) { - case CLUE_NORTH: - value = String.valueOf(clue.getData() + 1); - break; - case CLUE_EAST: - value = String.valueOf(clue.getData()); - break; - case CLUE_SOUTH: - value = String.valueOf(clue.getData()); - break; - case CLUE_WEST: - value = KakurasuClue.colNumToString(clue.getData() + 1); - break; - default: - value = ""; - } + value = switch (clue.getType()) { + case CLUE_NORTH, CLUE_WEST -> String.valueOf(clue.getData() + 1); + case CLUE_EAST, CLUE_SOUTH -> String.valueOf(clue.getData()); + default -> ""; + }; int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java index 97c74a963..b179908f3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuElementView.java @@ -31,44 +31,27 @@ public void drawElement(Graphics2D graphics2D) { new Rectangle2D.Double( location.x + 0.5f, location.y + 0.5f, size.width - 1, size.height - 1)); } else { - if (type == KakurasuType.TREE) { + if (type == KakurasuType.FILLED) { graphics2D.drawImage( - KakurasuView.TREE, + KakurasuView.FILLED, location.x, location.y, size.width, size.height, null, null); - graphics2D.setColor(Color.BLACK); - graphics2D.drawRect(location.x, location.y, size.width, size.height); } else { - if (type == KakurasuType.GRASS) { - graphics2D.drawImage( - KakurasuView.GRASS, - location.x, - location.y, - size.width, - size.height, - null, - null); - graphics2D.setColor(Color.BLACK); - graphics2D.drawRect(location.x, location.y, size.width, size.height); - } else { - if (type == KakurasuType.TENT) { - graphics2D.drawImage( - KakurasuView.TENT, - location.x, - location.y, - size.width, - size.height, - null, - null); - graphics2D.setColor(Color.BLACK); - graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - } + graphics2D.drawImage( + KakurasuView.EMPTY, + location.x, + location.y, + size.width, + size.height, + null, + null); } + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java index f0be179bb..e3c671a95 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuExporter.java @@ -45,7 +45,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { for (KakurasuClue clue : board.getRowClues()) { org.w3c.dom.Element clueElement = newDocument.createElement("clue"); clueElement.setAttribute("value", String.valueOf(clue.getData())); - clueElement.setAttribute("index", KakurasuClue.colNumToString(clue.getClueIndex())); + clueElement.setAttribute("index", String.valueOf(clue.getClueIndex())); axisEast.appendChild(clueElement); } boardElement.appendChild(axisEast); @@ -60,14 +60,6 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { } boardElement.appendChild(axisSouth); - if (!board.getLines().isEmpty()) { - org.w3c.dom.Element linesElement = newDocument.createElement("lines"); - for (PuzzleElement data : board.getLines()) { - org.w3c.dom.Element lineElement = puzzle.getFactory().exportCell(newDocument, data); - linesElement.appendChild(lineElement); - } - boardElement.appendChild(linesElement); - } return boardElement; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java index a6f8771cf..d8009c925 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java @@ -162,7 +162,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int i = 0; i < eastClues.getLength(); i++) { Element clue = (Element) eastClues.item(i); int value = Integer.valueOf(clue.getAttribute("value")); - int index = KakurasuClue.colStringToColNum(clue.getAttribute("index")); + int index = Integer.valueOf(clue.getAttribute("index")); if (index - 1 < 0 || index - 1 > kakurasuBoard.getHeight()) { throw new InvalidFileFormatException( From 077d06305c2f45f7c303e320bdda6e4fba35ff10 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Tue, 19 Nov 2024 16:58:32 -0500 Subject: [PATCH 10/18] Made edits that allow for Kakurasu puzzles to be loaded. Rules now need to be implemented. --- puzzles files/kakurasu/test | 43 +++++++++++++++++++ .../legup/puzzle/kakurasu/ClueCommand.java | 4 +- .../rpi/legup/puzzle/kakurasu/Kakurasu.java | 18 -------- .../puzzle/kakurasu/KakurasuController.java | 30 +------------ .../puzzle/kakurasu/KakurasuImporter.java | 10 +---- src/main/resources/edu/rpi/legup/legup/config | 4 ++ 6 files changed, 51 insertions(+), 58 deletions(-) create mode 100644 puzzles files/kakurasu/test diff --git a/puzzles files/kakurasu/test b/puzzles files/kakurasu/test new file mode 100644 index 000000000..d3718ce1b --- /dev/null +++ b/puzzles files/kakurasu/test @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java index c5c31dd03..6beebc0ef 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java @@ -66,7 +66,7 @@ public void executeCommand() { for (KakurasuCell cell : tempList) { cell = (KakurasuCell) board.getPuzzleElement(cell); - cell.setData(KakurasuType.GRASS); + cell.setData(KakurasuType.EMPTY); board.addModifiedData(cell); finalTran.propagateChange(cell); @@ -141,7 +141,7 @@ public String getErrorString() { return null; } - /** Undoes an command */ + /** Undoes a command */ @Override public void undoCommand() { Puzzle puzzle = getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java index c531e5d33..82b3763f0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java @@ -83,22 +83,4 @@ public boolean isBoardComplete(Board board) { */ @Override public void onBoardChange(Board board) {} - - /** - * @return if it is valid Kakurasu puzzle must have same number of clues as the dimension size - */ - @Override - public boolean checkValidity() { - // TODO: Fix this function - KakurasuBoard b = (KakurasuBoard) this.getBoardView().getBoard(); - List elements = b.getPuzzleElements(); - int treeCount = 0; - for (PuzzleElement element : elements) { - KakurasuCell c = (KakurasuCell) element; - if (c.getType() == KakurasuType.TREE) { - treeCount++; - } - } - return treeCount != 0; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java index e1c93e407..cd166081a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuController.java @@ -19,35 +19,7 @@ import static edu.rpi.legup.app.GameBoardFacade.getInstance; public class KakurasuController extends ElementController { - - private ElementView lastCellPressed; - - public KakurasuController() { - super(); - this.lastCellPressed = null; - } - - @Override - public void mousePressed(MouseEvent e) { - if (e.getButton() != MouseEvent.BUTTON2) { - BoardView boardView = getInstance().getLegupUI().getBoardView(); - if (boardView != null) { - lastCellPressed = boardView.getElement(e.getPoint()); - } - } - } - - @Override - public void mouseReleased(MouseEvent e) { - // TODO: Figure out if mouseReleased is still needed, and what it needs to do - if (GameBoardFacade.getInstance().getLegupUI().getTreePanel() != null - && e.getButton() != MouseEvent.BUTTON2) { - lastCellPressed = null; - } else { - super.mouseReleased(e); - } - } - + @Override public void changeCell(MouseEvent e, PuzzleElement element) { KakurasuCell cell = (KakurasuCell) element; diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java index d8009c925..3188343ac 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuImporter.java @@ -204,14 +204,6 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { @Override public void initializeBoard(String[] statements) throws UnsupportedOperationException { - throw new UnsupportedOperationException("Tree Tent cannot accept text input"); - } - - @Override - public List getImporterElements() { - List elements = new ArrayList<>(); - elements.add("cell"); - elements.add("line"); - return elements; + throw new UnsupportedOperationException("Kakurasu cannot accept text input"); } } diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index e01767677..09e27a43f 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -51,5 +51,9 @@ qualifiedClassName="edu.rpi.legup.puzzle.minesweeper.Minesweeper" fileType=".xml" fileCreationDisabled="true"/> + From 7515f433eac85fdbdca96d877a7d227bcf1d3014 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Tue, 19 Nov 2024 17:41:57 -0500 Subject: [PATCH 11/18] Beginning to implement the rules system for Kakurasu. --- .../legup/puzzle/kakurasu/KakurasuBoard.java | 1 - .../rules/FinishWithFilledDirectRule.java | 93 ++++++++++++++++++ .../rules/kakurasu_reference_sheet.txt | 5 + .../edu/rpi/legup/images/kakurasu/temp.png | Bin 0 -> 9733 bytes 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt create mode 100644 src/main/resources/edu/rpi/legup/images/kakurasu/temp.png diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java index d0ac89cb3..9f9a41636 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -57,7 +57,6 @@ public void setPuzzleElement(int index, PuzzleElement puzzleElement) { if (index < puzzleElements.size()) { puzzleElements.set(index, puzzleElement); } - // TODO: Throw warning if index is above puzzleElement.size()? } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java new file mode 100644 index 000000000..00efd6300 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java @@ -0,0 +1,93 @@ +package edu.rpi.legup.puzzle.kakurasu.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; +import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuType; + +import java.awt.*; +import java.util.List; + +public class FinishWithFilledDirectRule extends DirectRule { + public FinishWithFilledDirectRule() { + super( + "KAKU-BASC-0001", + "Finish with Filled", + "The only way to satisfy the clue in a row or column are these tiles.", + "edu/rpi/legup/images/kakurasu/temp.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule + * + * @param transition transition to check + * @param puzzleElement equivalent puzzleElement + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message + */ + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + KakurasuBoard initialBoard = (KakurasuBoard) transition.getParents().get(0).getBoard(); + KakurasuCell initCell = (KakurasuCell) initialBoard.getPuzzleElement(puzzleElement); + KakurasuBoard finalBoard = (KakurasuBoard) transition.getBoard(); + KakurasuCell finalCell = (KakurasuCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(finalCell.getType() == KakurasuType.FILLED + && initCell.getType() == KakurasuType.UNKNOWN)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be filled to apply this rule."; + } + + if (isForced(initialBoard, initCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be grass."; + } + } + + /** + * Is used to determine if the cell being passed in is forced to exist in the + * position it is in, given the board that was passed in + * + * @param board board to check the cell against + * @param cell the cell whose legitimacy is in question + * @return if the cell is forced to be at its position on the board + */ + private boolean isForced(KakurasuBoard board, KakurasuCell cell) { + Point loc = cell.getLocation(); + List filledRow = board.getRowCol(loc.y, KakurasuType.FILLED, true); + List filledCol = board.getRowCol(loc.x, KakurasuType.FILLED, false); + + // TODO: Actually make this helper function return the correct value + // The remaining value in this row or column must be equal to the achievable remaining value + + return true; + } + + /** + * 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) { + KakurasuBoard KakurasuBoard = (KakurasuBoard) node.getBoard().copy(); + for (PuzzleElement element : KakurasuBoard.getPuzzleElements()) { + KakurasuCell cell = (KakurasuCell) element; + if (cell.getType() == KakurasuType.UNKNOWN && isForced(KakurasuBoard, cell)) { + cell.setData(KakurasuType.FILLED); + KakurasuBoard.addModifiedData(cell); + } + } + if (KakurasuBoard.getModifiedData().isEmpty()) { + return null; + } else { + return KakurasuBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt new file mode 100644 index 000000000..e20d1031e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt @@ -0,0 +1,5 @@ +KAKU-BASC-0001 : FinishWithFilledDirectRule + +KAKU-CONT-0001 : A + +KAKU-CASE-0001 : A \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/images/kakurasu/temp.png b/src/main/resources/edu/rpi/legup/images/kakurasu/temp.png new file mode 100644 index 0000000000000000000000000000000000000000..850fbf127f04020fe83c6a4f6db75078967916ea GIT binary patch literal 9733 zcmeHNc{G&m`yaAXmTak)X=KYg%*HS^C}WMXl|9BR3}%Lz(F{qlgir~QEfj5%rPW@R z5)lchi0qNbR+8UCz3*G+_dVx#-gAE6|ID0c=6SCBx<1!^eeUbJ&)nCU7_0pz8-?VA zKp@aYGgF)m@C{zQ1o(hYW>C;92(%$B$j*UfgJ*;N7<4klmjq@7`jNmS4uuQ?ae8jr zQ%Yu)MZVp#9TYgf8*hX(%{CU^9J!;+j0A#MNvKa$Jt+njdWBUWjaiz zWF)szlOj%Q_{4l}jLmFyB&I)FG~53;!hrZZEkL$Czq;uQtVmlB%SPO$w|oiQdrz=U z#_+W<87!wdPFDT+xnzIQ+X`==boP_4ix-N5?teH9&*aqxo4?%C6pgASKeUZ2+3BwTuCnh&U9y{~cr{+wr+PhiQw1i)A`kRWnMuo}kS3dKUx1Pvs z+jYeNqkUclcRHmgqe-o((th%VCnur6L~?n!<74B5xFnLBFpM}O_lY?g<*0aFgQ5n$ ztU+qL8mTCpI_mp?G0+mPo@09-bSYVx>!ztVlH?tRQzH-YC71JTw$u`85t7w9`&1}$ zLV%cYr}?eqF8h<$VR6%!zwsxBTwlC4f7m!$wgpLj$gQ#E6_0HAgXsu4+5Rftm(M^` zb9ZXQ+BZvr# zz6Mt1Dw9>;B2vV(;j$@!%fGoAce8kh*a27nlBfO(Cj>orwEy|>NlRdub1;7b73%CLB?4~x%*w1=ytE@?bobt?62fI7@wAl3qRUwq+r+~OEB+7 zK4d%^xSRxTJZE|L73rd3=Qid}(~BOl>QxcBO6TqJ(oWsT`6_q4)4}%rNjvYm4~Cty z^PzV;5`5mWUb`+}S(C+A!tcbty!TbOlS8mnwl&n;Rz zdq7z5XUQN&WuCCGN|L=r(pNUDEiY05)+W$_hPBm4(o?;Zru3A{CYptndp3`2D3^)9 zQ?ZyeojIC1Dm|uR!P4UZ7MbBe4Y<~nhXEF>sTq!YZkO=*PUW(rIg_cQ%k32>V?Mf- zs4EMN?MnSP+;$>H&vnoq_O|s>t=0EU`Sigy{HS_rRia5$jy@r+Z{z2s{^Q28%$FJ_Nc zjLh?FJG(CFsMbn$P|h=$q?+MW@Gcr_xr)uGkBi z4TM;S;9-#?a?Z_INN<|bV6^PCjK8&XN+NW?8PaJXmG5u#?a;RL20WQN6cRJZy-D4y zE+3*iUAy-Uu0j&DO>J8J^vWsTjBO_tZD?a#9t6`o&V?2>wO>kq)q@!A&6uz|ZQw3h zN6dqL@hx)>a^g!&5w01?)Zb_9;#_w1NhkSkXQ%OS1KCY#yqfmiGUjNx>8&vR1(Dpj zW5vtKZb4X3oTD}MNN{oe6mK>G37T1`?na4ngVn~Jmul=6a$-i5%)n2IknqNq7VTk% zL&HGW_neinPw3f~3vu;h-&IB5W+v81<_nDulohwzzK9GCBJ(BI=WJaLj9&47=XS9c z7Li!f&*bDr`?V$c2gc+c`P}Woh0Zh?nfY$Jc?I8n@wQGE#9B^|%ew%6RxX=+uuCsA zSBy)7X>fbPavS=1u#Q-Dg}}?0`Ufk2j`o#4A`K`drZ4KYKzGEoNX(Qn2B zdF%JYh>1$<{qkJ-D$mZS>p7UKf?wq^ysn{zd&8e&y(H7ZLZW(W1uVj@6(I^Xrq_sc z^UjCYzDxFlRLHf23-?d@Nx6tBgum_={uHpNy_;J<%2Pa4Ok6Jar0jN)x{ZRk+ytJ8 zGX;jH63j2lQ}&ddJv^w42(wU}vXYhLH8RyiiKF=5^&bJ?_9 zV{7Eq16~LxL2Bq-!=-G+wwO_0mUx-O+i04h+ulCoGne|@VX|_@{Dko71|0qz+y|DI zx}18bIj|W_7D2|=dq@@WIq>^}&&No)#}*weeq1zCWZ3OL;k-@!$|>aIz=2%<-7Z{( z5^PDfG&&-^=GptaPfna&!Qia;*7}tGt)E;MxxVRyWPvT8Z`TofB+wWUAF*?eK~=yge83oQ zt!*u*nxgvNPUK+JxS?hKd8_J6^R}Ku5)n$OB{7LU#MURLpNpkwKZ`nVu6`*q`)y8T zitTx3!uu!GM4yDM=h741AX9&~CTO0gCs#vP4xcD|mvc6^$Tr8YKLKKH?FM&%J5PrW zDAkWCbtuj6bJ%w(CBuQ?CU~gbUdY<%63g`30m>mqqgVTM4Fhk@7!6UR(oU6{mzYOB zbxc+}c>DnPq+}cj38bM^Z<{%LwlP?`7}L%snXXq=ZD_*+EMtd&FuV#x6h;IZq6po`poi+xNq>;Q1~z;e2?MnoSWNQi+cUK zqltqaNu&;6l(z+P9<2fU2n)gIQY5G)FD`ctCSFUXT&IMOZ9r~8x|MR5YLxapn|tOs ztTlY^&nMDZ(jTB0=wT;02gaYG4!w7B9e5q6#p8FsdQl0_?$L^~O4@G6l)myrHfJNk zWb@+G?|M1gzn;5$h1&PZ@wMQ@{_bTmop{mXct;51T8m9f`^h}QrtwV{QJ5%klQKfa z`#q1vRkT#{Z7W}F_T1=smwD-PL~Pc+k|ubHOksXuaF=$ME_&C!JG7Cv;%v#&E@n2A1niqQ(3+E=M6~9D&J-CFO z?phdGV)NE;RdJPb%WJ%FYH;op7vMg|&F0k;Y8Q0+W0n9@)=|B$yhw?5Gz~GOt16+i zXT3(#VO>QfMf^2c6yD!>oK*ln)dsRZ*zKJx& z--(*bm~1X|Kj6PjXDB0vCn{nlq7fHW=Ne;cw%;t-?1c-OrnNvSVKb;RF6Qn}`;#I& zqB}ZKE>12zYFQ$hkzI*}=du#@Qt@Z4li(#iw7HKjk-_>Q1m&a$h*Dbvsbz~f!GOs0 z$V-l=9km?krM0C8hD}F4F}^b9nD>VE4+ah&E9bj&@=pFPH|TM~_fe;mI{_cE1ExQe zb@>!%y69Iu^r=mVkBC>>dVD7gs;bGURP(U4&tdeRd{q4GaC&;|j#G<9waydwI%r1J zA=+dgxtKb2_dxx`fR9qXQnHeQxno{e6?`dwJUwz!@bywtLDQy*nu&Ll_e)<;MiLlf zGZiz%u3Y6k>Z4NiQn%QTKUO(W939J}icIc%UVd7Aoq21x_m89cb}9$^3jJ?V-d?#) ze_i!pd1Bd+KST6($IhHwr5cmsib*4O?~QT-n&av9b?QMfJAx{gd*I zlgj1{nm$R$Qclm$zhrLGl-5tif9@zwC7`RAD}|-?SMv&!JDnKYw#mpJpFj3yfxUk* zb5yTU=haMjr|43TDz$#n>L_i=!o=pWsi)n@JGDOBx32T9RDHP>&f@K!u{30f=hGI6 z7U?WkofI~0KDgn_m~f0tLm8o5!6mMOb3^6+m6daSdFm-xO}B}FF)X{j|LNF`BlZVe z2`(S*(~ebEwqCzAsdD3D&)1tvularXF!b);Q8^b3=g1Sv=p)O8^c4D+H)SD5Urj#T z)im|ykJDS^A2e0%3e=qIZCY|%7*X)$M29$hGaa`cBIG@u)Bmvi2IGNckUNguo-!Lv zdgRrq+6GYwMxiTaRZ=2Zxl6+K;&{R6aj8T}yAN zo_mICiK`y0c3G^PO@4Hv_7>EQ-OBe&FC=lE98$P=^U-U?DcPx@K9h=zsjc5bm#yZk zrwCmt)dxgn+NGtx^KMA}{7rkCXghEQl%)V?JqJq*41rG7z!T{nBn=ML4>${gK)dxh zet3cpi3Ro`c~WRt$W(bX1WX}fA@*99FiSr}k{87^h(WRq+HXe)@*(IFA^LhkyEzyD zfJ$QF!5peDjfvr4A#1o8;C@xC2?4LEuzau(2TLokA)P@2qcl(&FsKoSas&a<69VsM z5Xl%DobgWx;0X)yVzK-%nwo4jTZ4_%pffx*;kvrInlOYW0s#dypv*uT3(tYln2M_q zKQM44CV@fmV^Qcd@G2(WgC4-bLLk66_^Q^Z*8dWORf?V=4X$K_vWb?-#)ET?>au&?NbisDLUH z@CyIUrHPrP)!!DY6nIjoerr|$*}rMBDCB?0`fYBjBWvOOIuXG9Z`|Ls|B8K08PKw{ z#Ng{jKp|q*Zs9065rIaLpl~>_6etvo4D}%CAfRXhSyu-JC+O&s z(7!;L(U>eejX+w30>CvW01gqaO(1C@&``8C$^(k>KxskoS~^Im4iT;Gfh6ea=)!b= zfjGdR09lFm{dHEWP(%QV;6c!VBVhz6jD$i#Q3RMa)B^_Kp-~93mbR9*E(s4`gCY_z z#&iZ14=g8ziuWXG`q4bs2385j?6ES#LJ%6Te;mi-oLC3cPB$mf+ysKbFOm!UQw|S9AVn*4vW&e?0vt0$|4gaBt8YOuawub>;H`|p?{7$BpUDwhz%TOP=6Tg{XaVG zrZMZC_VrHtdZ&H8)4twmU+=W9ciPuG?dzTP^-lY5opvR*bO6v!=kwTSf&;CrzOyT@ zBmxpaKT}602qe6D_2L4hWyk_TewLY~5&tm1w1f(eU_#WU)keG-ZjYTq?PM2R90U`o zHORBK8rF|IC|nzhtEmnOH7OP@Do>u?vF)j#7}y{b#Kp}c@Z;u1)ri6_TVDOep)ugE QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 From 37de42cf1000ce06654ec97afdde2a199be86dbe Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 22 Nov 2024 16:42:17 -0500 Subject: [PATCH 12/18] First Kakurasu Rule has been implemented --- .../legup/puzzle/kakurasu/KakurasuBoard.java | 6 ++--- .../rules/FinishWithFilledDirectRule.java | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java index 9f9a41636..36ee8ebfa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/KakurasuBoard.java @@ -70,10 +70,8 @@ public void notifyChange(PuzzleElement puzzleElement) { public KakurasuClue getClue(int x, int y) { if (x == getWidth() && 0 <= y && y < getHeight()) { return rowClues.get(y); - } else { - if (y == getHeight() && 0 <= x && x < getWidth()) { - return colClues.get(x); - } + } else if (y == getHeight() && 0 <= x && x < getWidth()) { + return colClues.get(x); } return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java index 00efd6300..1d27f579f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java @@ -7,9 +7,11 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuClue; import edu.rpi.legup.puzzle.kakurasu.KakurasuType; import java.awt.*; +import java.util.ArrayList; import java.util.List; public class FinishWithFilledDirectRule extends DirectRule { @@ -59,10 +61,28 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem private boolean isForced(KakurasuBoard board, KakurasuCell cell) { Point loc = cell.getLocation(); List filledRow = board.getRowCol(loc.y, KakurasuType.FILLED, true); + List unknownRow = board.getRowCol(loc.y, KakurasuType.UNKNOWN, true); List filledCol = board.getRowCol(loc.x, KakurasuType.FILLED, false); + List unknownCol = board.getRowCol(loc.x, KakurasuType.UNKNOWN, false); - // TODO: Actually make this helper function return the correct value - // The remaining value in this row or column must be equal to the achievable remaining value + // Check if the remaining locations available must be filled to fulfill the clue value + int rowSum = 0; + for(KakurasuCell kc : filledRow) { + rowSum += kc.getLocation().y; + } + for(KakurasuCell kc : unknownRow) { + rowSum += kc.getLocation().y; + } + if(rowSum != board.getClue(board.getWidth(), loc.y).getData()) return false; + + int colSum = 0; + for(KakurasuCell kc : filledCol) { + colSum += kc.getLocation().x; + } + for(KakurasuCell kc : unknownCol) { + colSum += kc.getLocation().x; + } + if(colSum != board.getClue(loc.x, board.getHeight()).getData()) return false; return true; } From c00d61d3b39cd7324b4daa6f5cad00917aa4fa43 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 22 Nov 2024 17:16:43 -0500 Subject: [PATCH 13/18] FillWithRules completed and working --- .../rules/FinishWithEmptyDirectRule.java | 102 ++++++++++++++++++ .../rules/FinishWithFilledDirectRule.java | 19 ++-- 2 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java new file mode 100644 index 000000000..cdaffc082 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java @@ -0,0 +1,102 @@ +package edu.rpi.legup.puzzle.kakurasu.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; +import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuType; + +import java.awt.*; +import java.util.List; + +public class FinishWithEmptyDirectRule extends DirectRule { + public FinishWithEmptyDirectRule() { + super( + "KAKU-BASC-0002", + "Finish with Empty", + "The only way to satisfy the clue in a row or column are these empty tiles.", + "edu/rpi/legup/images/kakurasu/temp.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule + * + * @param transition transition to check + * @param puzzleElement equivalent puzzleElement + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message + */ + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + KakurasuBoard initialBoard = (KakurasuBoard) transition.getParents().get(0).getBoard(); + KakurasuCell initCell = (KakurasuCell) initialBoard.getPuzzleElement(puzzleElement); + KakurasuBoard finalBoard = (KakurasuBoard) transition.getBoard(); + KakurasuCell finalCell = (KakurasuCell) finalBoard.getPuzzleElement(puzzleElement); + if (!(finalCell.getType() == KakurasuType.EMPTY + && initCell.getType() == KakurasuType.UNKNOWN)) { + return super.getInvalidUseOfRuleMessage() + ": This cell must be empty to apply this rule."; + } + + if (isForced(initialBoard, initCell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be empty."; + } + } + + /** + * Is used to determine if the cell being passed in is forced to exist in the + * position it is in, given the board that was passed in + * + * @param board board to check the cell against + * @param cell the cell whose legitimacy is in question + * @return if the cell is forced to be at its position on the board + */ + private boolean isForced(KakurasuBoard board, KakurasuCell cell) { + Point loc = cell.getLocation(); + List filledRow = board.getRowCol(loc.y, KakurasuType.FILLED, true); + List filledCol = board.getRowCol(loc.x, KakurasuType.FILLED, false); + + // Check if the remaining locations available must be filled to fulfill the clue value + int rowSum = 0; + for(KakurasuCell kc : filledRow) { + rowSum += kc.getLocation().x + 1; + } + if(rowSum == board.getClue(board.getWidth(), loc.y).getData()) return true; + + int colSum = 0; + for(KakurasuCell kc : filledCol) { + colSum += kc.getLocation().y + 1; + } + // Return true if the clue is fulfilled, false if it isn't + return (colSum == board.getClue(loc.x, board.getHeight()).getData()); + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + KakurasuBoard KakurasuBoard = (KakurasuBoard) node.getBoard().copy(); + for (PuzzleElement element : KakurasuBoard.getPuzzleElements()) { + KakurasuCell cell = (KakurasuCell) element; + if (cell.getType() == KakurasuType.UNKNOWN && isForced(KakurasuBoard, cell)) { + cell.setData(KakurasuType.EMPTY); + KakurasuBoard.addModifiedData(cell); + } + } + if (KakurasuBoard.getModifiedData().isEmpty()) { + return null; + } else { + return KakurasuBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java index 1d27f579f..3cb6f236f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java @@ -19,7 +19,7 @@ public FinishWithFilledDirectRule() { super( "KAKU-BASC-0001", "Finish with Filled", - "The only way to satisfy the clue in a row or column are these tiles.", + "The only way to satisfy the clue in a row or column are these filled tiles.", "edu/rpi/legup/images/kakurasu/temp.png"); } @@ -46,7 +46,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem if (isForced(initialBoard, initCell)) { return null; } else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be grass."; + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be filled."; } } @@ -68,23 +68,22 @@ private boolean isForced(KakurasuBoard board, KakurasuCell cell) { // Check if the remaining locations available must be filled to fulfill the clue value int rowSum = 0; for(KakurasuCell kc : filledRow) { - rowSum += kc.getLocation().y; + rowSum += kc.getLocation().x + 1; } for(KakurasuCell kc : unknownRow) { - rowSum += kc.getLocation().y; + rowSum += kc.getLocation().x + 1; } - if(rowSum != board.getClue(board.getWidth(), loc.y).getData()) return false; + if(rowSum == board.getClue(board.getWidth(), loc.y).getData()) return true; int colSum = 0; for(KakurasuCell kc : filledCol) { - colSum += kc.getLocation().x; + colSum += kc.getLocation().y + 1; } for(KakurasuCell kc : unknownCol) { - colSum += kc.getLocation().x; + colSum += kc.getLocation().y + 1; } - if(colSum != board.getClue(loc.x, board.getHeight()).getData()) return false; - - return true; + // Return true if the clue is fulfilled, false if it isn't + return (colSum == board.getClue(loc.x, board.getHeight()).getData()); } /** From 80cb176aa0a120f6f8e18857742ced4efbfc2d3c Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 22 Nov 2024 17:36:11 -0500 Subject: [PATCH 14/18] Contradiction Rules written. New contradiction rule thought of: "UnreachableSum". Previous direct rules can be made into/combined with "RequiredFill" and "RequiredEmpty" --- .../rules/ExceededSumContradictionRule.java | 60 +++++++++++++++++ .../rules/IncompleteSumContradictionRule.java | 64 +++++++++++++++++++ .../rules/kakurasu_reference_sheet.txt | 7 +- 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/ExceededSumContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/IncompleteSumContradictionRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/ExceededSumContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/ExceededSumContradictionRule.java new file mode 100644 index 000000000..7dd2f0faa --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/ExceededSumContradictionRule.java @@ -0,0 +1,60 @@ +package edu.rpi.legup.puzzle.kakurasu.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; +import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuType; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentType; + +import java.awt.*; +import java.util.List; + +public class ExceededSumContradictionRule extends ContradictionRule { + public ExceededSumContradictionRule() { + super( + "KAKU-CONT-0001", + "Exceeded Sum", + "The sum of this row or column exceeds one of the clues.", + "edu/rpi/legup/images/kakurasu/temp.png"); + } + + /** + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule + * + * @param board board to check contradiction + * @param puzzleElement equivalent puzzleElement + * @return null if the transition contains a contradiction at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + KakurasuBoard kakurasuBoard = (KakurasuBoard) board; + KakurasuCell cell = (KakurasuCell) puzzleElement; + + Point loc = cell.getLocation(); + List filledRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.FILLED, true); + List filledCol = kakurasuBoard.getRowCol(loc.x, KakurasuType.FILLED, false); + int rowSum = 0; + for(KakurasuCell kc : filledRow) { + rowSum += kc.getLocation().x + 1; + } + int colSum = 0; + for(KakurasuCell kc : filledCol) { + colSum += kc.getLocation().y + 1; + } + + if (rowSum > kakurasuBoard.getClue(kakurasuBoard.getWidth(), loc.y).getData() + || colSum > kakurasuBoard.getClue(loc.x, kakurasuBoard.getHeight()).getData()) { + return null; + } else { + return super.getNoContradictionMessage(); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/IncompleteSumContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/IncompleteSumContradictionRule.java new file mode 100644 index 000000000..eb0911d11 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/IncompleteSumContradictionRule.java @@ -0,0 +1,64 @@ +package edu.rpi.legup.puzzle.kakurasu.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; +import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuType; + +import java.awt.*; +import java.util.List; + +public class IncompleteSumContradictionRule extends ContradictionRule { + public IncompleteSumContradictionRule() { + super( + "KAKU-CONT-0002", + "Incomplete Sum", + "The sum of this row or column cannot fulfill one of the clues.", + "edu/rpi/legup/images/kakurasu/temp.png"); + } + + /** + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule + * + * @param board board to check contradiction + * @param puzzleElement equivalent puzzleElement + * @return null if the transition contains a contradiction at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + KakurasuBoard kakurasuBoard = (KakurasuBoard) board; + KakurasuCell cell = (KakurasuCell) puzzleElement; + + Point loc = cell.getLocation(); + List filledRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.FILLED, true); + List unknownRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.UNKNOWN, true); + List filledCol = kakurasuBoard.getRowCol(loc.x, KakurasuType.FILLED, false); + List unknownCol = kakurasuBoard.getRowCol(loc.x, KakurasuType.UNKNOWN, false); + + int rowSum = 0; + for(KakurasuCell kc : filledRow) { + rowSum += kc.getLocation().x + 1; + } + for(KakurasuCell kc : unknownRow) { + rowSum += kc.getLocation().x + 1; + } + int colSum = 0; + for(KakurasuCell kc : filledCol) { + colSum += kc.getLocation().y + 1; + } + for(KakurasuCell kc : unknownCol) { + colSum += kc.getLocation().y + 1; + } + + if (rowSum < kakurasuBoard.getClue(kakurasuBoard.getWidth(), loc.y).getData() + || colSum < kakurasuBoard.getClue(loc.x, kakurasuBoard.getHeight()).getData()) { + return null; + } else { + return super.getNoContradictionMessage(); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt index e20d1031e..a4a399e2a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt @@ -1,5 +1,10 @@ KAKU-BASC-0001 : FinishWithFilledDirectRule +KAKU-BASC-0002 : FinishWithEmptyDirectRule +KAKU-BASC-0003 : RequiredFilledDirectRule + + +KAKU-CONT-0001 : ExceededSumContradictionRule +KAKU-CONT-0002 : IncompleteSumContradictionRule -KAKU-CONT-0001 : A KAKU-CASE-0001 : A \ No newline at end of file From d7022ba7dcade7cd43242995db922c49d15f69fd Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Tue, 3 Dec 2024 21:35:09 -0500 Subject: [PATCH 15/18] Added a new Contradiction Rule --- .../UnreachableSumContradictionRule.java | 59 +++++++++++++++++++ .../rules/kakurasu_reference_sheet.txt | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java new file mode 100644 index 000000000..5cbea72db --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java @@ -0,0 +1,59 @@ +package edu.rpi.legup.puzzle.kakurasu.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; +import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuType; + +import java.awt.*; +import java.util.List; + +public class UnreachableSumContradictionRule extends ContradictionRule { + public UnreachableSumContradictionRule() { + super( + "KAKU-CONT-0003", + "Unreachable Sum", + "The combination of available values cannot exactly land on the clue's value.", + "edu/rpi/legup/images/kakurasu/temp.png"); + } + + /** + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule + * + * @param board board to check contradiction + * @param puzzleElement equivalent puzzleElement + * @return null if the transition contains a contradiction at the specified puzzleElement, + * otherwise error message + */ + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + KakurasuBoard kakurasuBoard = (KakurasuBoard) board; + KakurasuCell cell = (KakurasuCell) puzzleElement; + + // TODO: Finish this rule + + Point loc = cell.getLocation(); + List filledRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.FILLED, true); + List unknownRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.UNKNOWN, true); + List filledCol = kakurasuBoard.getRowCol(loc.x, KakurasuType.FILLED, false); + List unknownCol = kakurasuBoard.getRowCol(loc.x, KakurasuType.UNKNOWN, false); + + int rowValueRemaining = kakurasuBoard.getClue(kakurasuBoard.getWidth(), loc.y).getData(); + for(KakurasuCell kc : filledRow) { + rowValueRemaining -= kc.getLocation().x + 1; + } + int colValueRemaining = kakurasuBoard.getClue(loc.x, kakurasuBoard.getHeight()).getData(); + for(KakurasuCell kc : filledCol) { + colValueRemaining -= kc.getLocation().y + 1; + } + + if (true) { + return null; + } else { + return super.getNoContradictionMessage(); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt index a4a399e2a..9f7eb0976 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt @@ -5,6 +5,6 @@ KAKU-BASC-0003 : RequiredFilledDirectRule KAKU-CONT-0001 : ExceededSumContradictionRule KAKU-CONT-0002 : IncompleteSumContradictionRule - +KAKU-CONT-0003 : UnreachableSumContradictionRule KAKU-CASE-0001 : A \ No newline at end of file From e8fe7765bf202407ca95cb1718ddfbc8c05ad733 Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Thu, 5 Dec 2024 23:08:35 -0500 Subject: [PATCH 16/18] Updated the implementation of UnreachableSumContradictionRule --- ...Rule.java => RequiredEmptyDirectRule.java} | 5 +- ...ule.java => RequiredFilledDirectRule.java} | 9 ++-- .../UnreachableSumContradictionRule.java | 46 +++++++++++++++++-- .../rules/kakurasu_reference_sheet.txt | 8 ++-- 4 files changed, 53 insertions(+), 15 deletions(-) rename src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/{FinishWithEmptyDirectRule.java => RequiredEmptyDirectRule.java} (96%) rename src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/{FinishWithFilledDirectRule.java => RequiredFilledDirectRule.java} (95%) diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java similarity index 96% rename from src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java rename to src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java index cdaffc082..cf97a1671 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithEmptyDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java @@ -12,8 +12,8 @@ import java.awt.*; import java.util.List; -public class FinishWithEmptyDirectRule extends DirectRule { - public FinishWithEmptyDirectRule() { +public class RequiredEmptyDirectRule extends DirectRule { + public RequiredEmptyDirectRule() { super( "KAKU-BASC-0002", "Finish with Empty", @@ -57,6 +57,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem * @return if the cell is forced to be at its position on the board */ private boolean isForced(KakurasuBoard board, KakurasuCell cell) { + // TODO: Fix this so it doesn't only work if all are empty Point loc = cell.getLocation(); List filledRow = board.getRowCol(loc.y, KakurasuType.FILLED, true); List filledCol = board.getRowCol(loc.x, KakurasuType.FILLED, false); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java similarity index 95% rename from src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java rename to src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java index 3cb6f236f..e34409a6a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FinishWithFilledDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java @@ -7,18 +7,16 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; -import edu.rpi.legup.puzzle.kakurasu.KakurasuClue; import edu.rpi.legup.puzzle.kakurasu.KakurasuType; import java.awt.*; -import java.util.ArrayList; import java.util.List; -public class FinishWithFilledDirectRule extends DirectRule { - public FinishWithFilledDirectRule() { +public class RequiredFilledDirectRule extends DirectRule { + public RequiredFilledDirectRule() { super( "KAKU-BASC-0001", - "Finish with Filled", + "Required Filled", "The only way to satisfy the clue in a row or column are these filled tiles.", "edu/rpi/legup/images/kakurasu/temp.png"); } @@ -59,6 +57,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem * @return if the cell is forced to be at its position on the board */ private boolean isForced(KakurasuBoard board, KakurasuCell cell) { + // TODO: Fix this so it doesn't only work if all are filled Point loc = cell.getLocation(); List filledRow = board.getRowCol(loc.y, KakurasuType.FILLED, true); List unknownRow = board.getRowCol(loc.y, KakurasuType.UNKNOWN, true); diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java index 5cbea72db..4deea2370 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java @@ -8,6 +8,7 @@ import edu.rpi.legup.puzzle.kakurasu.KakurasuType; import java.awt.*; +import java.util.ArrayList; import java.util.List; public class UnreachableSumContradictionRule extends ContradictionRule { @@ -33,8 +34,6 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { KakurasuBoard kakurasuBoard = (KakurasuBoard) board; KakurasuCell cell = (KakurasuCell) puzzleElement; - // TODO: Finish this rule - Point loc = cell.getLocation(); List filledRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.FILLED, true); List unknownRow = kakurasuBoard.getRowCol(loc.y, KakurasuType.UNKNOWN, true); @@ -50,10 +49,51 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { colValueRemaining -= kc.getLocation().y + 1; } - if (true) { + // If the value for either the row or col is already exceeded, this is the wrong rule to call. + if(rowValueRemaining < 0 || colValueRemaining < 0) return super.getNoContradictionMessage(); + + // If either value is already 0, then it is already possible to fulfill + // If it isn't 0, then it's possible for the remaining values to not be able to fulfill it + boolean rowPossible = (rowValueRemaining==0), colPossible = (colValueRemaining==0); + + int rowTotal = 0, colTotal = 0; + // No need to sort the values as the KakurasuCells are given in increasing index order + if(!rowPossible) { + ArrayList rowValues = new ArrayList<>(); + for(KakurasuCell kc : unknownRow) { + rowValues.add(kc.getLocation().x + 1); + rowTotal += kc.getLocation().x + 1; + } + // If the remaining unknown cells' values is less than the remaining value, + // this requires the usage of a different contradiction rule, not this one. + if(rowTotal < rowValueRemaining) return super.getNoContradictionMessage(); + rowPossible = isReachable(rowValueRemaining, 0, rowValues); + } + if(!colPossible) { + ArrayList colValues = new ArrayList<>(); + for(KakurasuCell kc : unknownCol) { + colValues.add(kc.getLocation().y + 1); + colTotal += kc.getLocation().y + 1; + } + // If the remaining unknown cells' values is less than the remaining value, + // this requires the usage of a different contradiction rule, not this one. + if(colTotal < colValueRemaining) return super.getNoContradictionMessage(); + colPossible = isReachable(colValueRemaining, 0, colValues); + } + + if (!rowPossible || !colPossible) { return null; } else { return super.getNoContradictionMessage(); } } + + // Helper function that checks if the target clue is reachable given a list of KakurasuCells + // This function only works if the list of values are given in increasing index order (which it currently is) + private boolean isReachable(int target, int currentIndex, ArrayList values) { + if(target == 0) return true; + if(target < 0 || currentIndex >= values.size()) return false; + return (isReachable(target, currentIndex+1, values) || + isReachable(target - values.get(currentIndex), currentIndex+1, values)); + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt index 9f7eb0976..8dfccc616 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/kakurasu_reference_sheet.txt @@ -1,10 +1,8 @@ -KAKU-BASC-0001 : FinishWithFilledDirectRule -KAKU-BASC-0002 : FinishWithEmptyDirectRule -KAKU-BASC-0003 : RequiredFilledDirectRule - +KAKU-BASC-0001 : RequiredFilledDirectRule +KAKU-BASC-0002 : RequiredEmptyDirectRule KAKU-CONT-0001 : ExceededSumContradictionRule KAKU-CONT-0002 : IncompleteSumContradictionRule KAKU-CONT-0003 : UnreachableSumContradictionRule -KAKU-CASE-0001 : A \ No newline at end of file +KAKU-CASE-0001 : FilledOrEmptyCaseRule \ No newline at end of file From 0f024bbfc79059a032dd850b82f7fb0506f4a86a Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 6 Dec 2024 00:00:31 -0500 Subject: [PATCH 17/18] Allowed Required Direct Rules to work for all general cases instead of needing the remaining unknown cells to all have to be the filled or empty. --- .../rules/RequiredEmptyDirectRule.java | 9 ++-- .../rules/RequiredFilledDirectRule.java | 41 +++++++++++++++---- .../UnreachableSumContradictionRule.java | 15 +++++-- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java index cf97a1671..3218d9023 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java @@ -57,7 +57,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem * @return if the cell is forced to be at its position on the board */ private boolean isForced(KakurasuBoard board, KakurasuCell cell) { - // TODO: Fix this so it doesn't only work if all are empty Point loc = cell.getLocation(); List filledRow = board.getRowCol(loc.y, KakurasuType.FILLED, true); List filledCol = board.getRowCol(loc.x, KakurasuType.FILLED, false); @@ -67,14 +66,16 @@ private boolean isForced(KakurasuBoard board, KakurasuCell cell) { for(KakurasuCell kc : filledRow) { rowSum += kc.getLocation().x + 1; } - if(rowSum == board.getClue(board.getWidth(), loc.y).getData()) return true; + // If setting this cell to filled causes the clue to fail, this cell must be empty + if(rowSum + loc.x + 1 > board.getClue(board.getWidth(), loc.y).getData()) return true; int colSum = 0; for(KakurasuCell kc : filledCol) { colSum += kc.getLocation().y + 1; } - // Return true if the clue is fulfilled, false if it isn't - return (colSum == board.getClue(loc.x, board.getHeight()).getData()); + // Return true if the clue is exceeded if this cell is filled, + // Return false if setting the cell to filled keeps the col total to under the clue value + return (colSum + loc.y + 1> board.getClue(loc.x, board.getHeight()).getData()); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java index e34409a6a..6aa952085 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredFilledDirectRule.java @@ -10,6 +10,7 @@ import edu.rpi.legup.puzzle.kakurasu.KakurasuType; import java.awt.*; +import java.util.ArrayList; import java.util.List; public class RequiredFilledDirectRule extends DirectRule { @@ -65,24 +66,48 @@ private boolean isForced(KakurasuBoard board, KakurasuCell cell) { List unknownCol = board.getRowCol(loc.x, KakurasuType.UNKNOWN, false); // Check if the remaining locations available must be filled to fulfill the clue value - int rowSum = 0; + int rowValueRemaining = board.getClue(board.getWidth(), loc.y).getData(); for(KakurasuCell kc : filledRow) { - rowSum += kc.getLocation().x + 1; + rowValueRemaining -= kc.getLocation().x + 1; } + ArrayList rowValues = new ArrayList<>(); + // Add all the unknown row values to the Arraylist except for the one being checked by the function for(KakurasuCell kc : unknownRow) { - rowSum += kc.getLocation().x + 1; + if(kc.getLocation() != loc) rowValues.add(kc.getLocation().x + 1); } - if(rowSum == board.getClue(board.getWidth(), loc.y).getData()) return true; + // If the clue is not reachable without the current cell being filled, but is possible with it filled, + // then that means the current cell is a required fill on this board + if(!isReachable(rowValueRemaining, 0, rowValues) && + isReachable(rowValueRemaining-(loc.x+1), 0, rowValues)) return true; - int colSum = 0; + int colValueRemaining = board.getClue(loc.x, board.getHeight()).getData(); for(KakurasuCell kc : filledCol) { - colSum += kc.getLocation().y + 1; + colValueRemaining -= kc.getLocation().y + 1; } + ArrayList colValues = new ArrayList<>(); + // Add all the unknown col values to the Arraylist except for the one being checked by the function for(KakurasuCell kc : unknownCol) { - colSum += kc.getLocation().y + 1; + if(kc.getLocation() != loc) colValues.add(kc.getLocation().y + 1); } // Return true if the clue is fulfilled, false if it isn't - return (colSum == board.getClue(loc.x, board.getHeight()).getData()); + return (!isReachable(colValueRemaining, 0, rowValues) && + isReachable(colValueRemaining-(loc.y+1), 0, colValues)); + } + + /** + * Helper function that checks if the target clue is reachable given a list of KakurasuCells + * This function only works if the list of values are given in increasing index order (which it currently is) + * + * @param target The integer that we are trying to add up to, given the values + * @param currentIndex The index of the next value that we are considering + * @param values Values that we are given to try to sum up to the target + * @return If it's possible to sum the values in a way to get the target value + */ + private boolean isReachable(int target, int currentIndex, ArrayList values) { + if(target == 0) return true; + if(target < 0 || currentIndex >= values.size()) return false; + return (isReachable(target, currentIndex+1, values) || + isReachable(target - values.get(currentIndex), currentIndex+1, values)); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java index 4deea2370..9096fc189 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/UnreachableSumContradictionRule.java @@ -64,7 +64,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { rowValues.add(kc.getLocation().x + 1); rowTotal += kc.getLocation().x + 1; } - // If the remaining unknown cells' values is less than the remaining value, + // If the remaining unknown cells' values is less than the remaining clue value, // this requires the usage of a different contradiction rule, not this one. if(rowTotal < rowValueRemaining) return super.getNoContradictionMessage(); rowPossible = isReachable(rowValueRemaining, 0, rowValues); @@ -75,7 +75,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { colValues.add(kc.getLocation().y + 1); colTotal += kc.getLocation().y + 1; } - // If the remaining unknown cells' values is less than the remaining value, + // If the remaining unknown cells' values is less than the remaining clue value, // this requires the usage of a different contradiction rule, not this one. if(colTotal < colValueRemaining) return super.getNoContradictionMessage(); colPossible = isReachable(colValueRemaining, 0, colValues); @@ -88,8 +88,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - // Helper function that checks if the target clue is reachable given a list of KakurasuCells - // This function only works if the list of values are given in increasing index order (which it currently is) + /** + * Helper function that checks if the target clue is reachable given a list of KakurasuCells + * This function only works if the list of values are given in increasing index order (which it currently is) + * + * @param target The integer that we are trying to add up to, given the values + * @param currentIndex The index of the next value that we are considering + * @param values Values that we are given to try to sum up to the target + * @return If it's possible to sum the values in a way to get the target value + */ private boolean isReachable(int target, int currentIndex, ArrayList values) { if(target == 0) return true; if(target < 0 || currentIndex >= values.size()) return false; From 8580ecaf9fec458ab2546ea984acac15d79a835a Mon Sep 17 00:00:00 2001 From: ProAndrewLi Date: Fri, 6 Dec 2024 00:18:25 -0500 Subject: [PATCH 18/18] Updated a Direct Rule, and included the Case Rule. Only thing left is the command to autofill a row or column with empties and update the pngs for the rules, but otherwise, the game is completely function. --- .../kakurasu/rules/FilledOrEmptyCaseRule.java | 114 ++++++++++++++++++ .../rules/RequiredEmptyDirectRule.java | 2 +- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FilledOrEmptyCaseRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FilledOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FilledOrEmptyCaseRule.java new file mode 100644 index 000000000..3780cfd7d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/FilledOrEmptyCaseRule.java @@ -0,0 +1,114 @@ +package edu.rpi.legup.puzzle.kakurasu.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.CaseRule; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.kakurasu.KakurasuBoard; +import edu.rpi.legup.puzzle.kakurasu.KakurasuCell; +import edu.rpi.legup.puzzle.kakurasu.KakurasuType; + +import java.util.ArrayList; +import java.util.List; + +public class FilledOrEmptyCaseRule extends CaseRule { + + public FilledOrEmptyCaseRule() { + super( + "KAKU-CASE-0001", + "Filled or Empty", + "Each blank cell is either filled or empty.", + "edu/rpi/legup/images/kakurasu/temp.png"); + } + + @Override + public CaseBoard getCaseBoard(Board board) { + KakurasuBoard kakurasuBoard = (KakurasuBoard) board.copy(); + kakurasuBoard.setModifiable(false); + CaseBoard caseBoard = new CaseBoard(kakurasuBoard, this); + for (PuzzleElement element : kakurasuBoard.getPuzzleElements()) { + if (((KakurasuCell) element).getType() == KakurasuType.UNKNOWN) { + caseBoard.addPickableElement(element); + } + } + return caseBoard; + } + + /** + * Gets the possible cases at a specific location based on this case rule + * + * @param board the current board state + * @param puzzleElement equivalent puzzleElement + * @return a list of elements the specified could be + */ + @SuppressWarnings("unchecked") + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList<>(); + Board case1 = board.copy(); + PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); + data1.setData(KakurasuType.FILLED); + case1.addModifiedData(data1); + cases.add(case1); + + Board case2 = board.copy(); + PuzzleElement data2 = case2.getPuzzleElement(puzzleElement); + data2.setData(KakurasuType.EMPTY); + case2.addModifiedData(data2); + cases.add(case2); + + return cases; + } + + /** + * Checks whether the transition logically follows from the parent node using this rule + * + * @param transition transition to check + * @return null if the child node logically follow from the parent node, otherwise error message + */ + @Override + public String checkRuleRaw(TreeTransition transition) { + List childTransitions = transition.getParents().get(0).getChildren(); + if (childTransitions.size() != 2) { + return super.getInvalidUseOfRuleMessage() + ": This case rule must have 2 children."; + } + + TreeTransition case1 = childTransitions.get(0); + TreeTransition case2 = childTransitions.get(1); + if (case1.getBoard().getModifiedData().size() != 1 + || case2.getBoard().getModifiedData().size() != 1) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case."; + } + + KakurasuCell mod1 = (KakurasuCell) case1.getBoard().getModifiedData().iterator().next(); + KakurasuCell mod2 = (KakurasuCell) case2.getBoard().getModifiedData().iterator().next(); + if (!mod1.getLocation().equals(mod2.getLocation())) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; + } + + if (!((mod1.getType() == KakurasuType.FILLED && mod2.getType() == KakurasuType.EMPTY) + || (mod2.getType() == KakurasuType.FILLED && mod1.getType() == KakurasuType.EMPTY))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have a filled and an empty cell."; + } + + return null; + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule + * + * @param transition transition to check + * @param puzzleElement equivalent puzzleElement + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message + */ + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return checkRuleRaw(transition); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java index 3218d9023..df6264890 100644 --- a/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/kakurasu/rules/RequiredEmptyDirectRule.java @@ -16,7 +16,7 @@ public class RequiredEmptyDirectRule extends DirectRule { public RequiredEmptyDirectRule() { super( "KAKU-BASC-0002", - "Finish with Empty", + "Required Empty", "The only way to satisfy the clue in a row or column are these empty tiles.", "edu/rpi/legup/images/kakurasu/temp.png"); }