Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented new puzzle Kakurasu #874

Open
wants to merge 18 commits into
base: Kakurasu
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1146e82
Modified Strange Function Name in LightUp Tests and Added a Few Comments
ADEPLI Oct 22, 2024
368a029
Added Comments Relating to Removal and Drawing of TreeNodes/TreeEleme…
ADEPLI Oct 22, 2024
f7970b3
Initializing Basic Framework and Resources for a New Puzzle
ADEPLI Nov 1, 2024
c40dd1c
Adding more framework code for Kakurasu. For graphics, need to make n…
ADEPLI Nov 1, 2024
1a7d664
Instantiated remaining templated code listed on wiki for Kakurasu.
ADEPLI Nov 5, 2024
1e91004
Skyscraper board implementation can be repurposed for Kakurasu (speci…
ADEPLI Nov 8, 2024
6a67cd5
Made a few changes to Kakurasu Implementation. Need to change the Kak…
ADEPLI Nov 12, 2024
916ea46
Pivoted from modeling off Skyscraper to modeling off TreeTent. Since …
ADEPLI Nov 15, 2024
80e9e06
Removed some unneeded code and some errors
ADEPLI Nov 15, 2024
077d063
Made edits that allow for Kakurasu puzzles to be loaded. Rules now ne…
ADEPLI Nov 19, 2024
7515f43
Beginning to implement the rules system for Kakurasu.
ADEPLI Nov 19, 2024
37de42c
First Kakurasu Rule has been implemented
ADEPLI Nov 22, 2024
c00d61d
FillWithRules completed and working
ADEPLI Nov 22, 2024
80cb176
Contradiction Rules written. New contradiction rule thought of: "Unre…
ADEPLI Nov 22, 2024
d7022ba
Added a new Contradiction Rule
ADEPLI Dec 4, 2024
e8fe776
Updated the implementation of UnreachableSumContradictionRule
ADEPLI Dec 6, 2024
0f024bb
Allowed Required Direct Rules to work for all general cases instead o…
ADEPLI Dec 6, 2024
8580eca
Updated a Direct Rule, and included the Case Rule. Only thing left is…
ADEPLI Dec 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions puzzles files/kakurasu/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Legup version="3.0.0">
<saved/>
<puzzle name="Kakurasu">
<board height="8" width="8">
<cells>
<cell value="1" x="0" y="1"/>
<cell value="1" x="2" y="1"/>
<cell value="1" x="6" y="1"/>
<cell value="1" x="7" y="2"/>
<cell value="1" x="1" y="3"/>
<cell value="1" x="2" y="3"/>
<cell value="1" x="6" y="3"/>
<cell value="1" x="3" y="4"/>
<cell value="1" x="5" y="5"/>
<cell value="1" x="0" y="7"/>
<cell value="1" x="4" y="7"/>
<cell value="1" x="6" y="7"/>
</cells>
<axis side="east">
<clue index="1" value="3"/>
<clue index="2" value="0"/>
<clue index="3" value="2"/>
<clue index="4" value="1"/>
<clue index="5" value="2"/>
<clue index="6" value="1"/>
<clue index="7" value="1"/>
<clue index="8" value="2"/>
</axis>
<axis side="south">
<clue index="1" value="3"/>
<clue index="2" value="0"/>
<clue index="3" value="2"/>
<clue index="4" value="1"/>
<clue index="5" value="2"/>
<clue index="6" value="1"/>
<clue index="7" value="1"/>
<clue index="8" value="2"/>
</axis>
</board>
</puzzle>
<solved isSolved="false" lastSaved="2024-03-12 17:29:25"/>
</Legup>
1 change: 1 addition & 0 deletions src/main/java/edu/rpi/legup/history/History.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/edu/rpi/legup/model/tree/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
}
}
Expand Down
185 changes: 185 additions & 0 deletions src/main/java/edu/rpi/legup/puzzle/kakurasu/ClueCommand.java
Original file line number Diff line number Diff line change
@@ -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<TreeNode, TreeTransition> addTran;
private List<List<KakurasuCell>> 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<KakurasuCell> 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.EMPTY);
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<KakurasuCell> 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 a 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<KakurasuCell> 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));
}
}
86 changes: 86 additions & 0 deletions src/main/java/edu/rpi/legup/puzzle/kakurasu/Kakurasu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package edu.rpi.legup.puzzle.kakurasu;

import edu.rpi.legup.model.Puzzle;
import edu.rpi.legup.model.gameboard.Board;
import edu.rpi.legup.model.gameboard.PuzzleElement;
import edu.rpi.legup.model.rules.ContradictionRule;

import java.util.List;

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();
}

/** 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(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 Tree Tent, false otherwise
*/
public boolean isValidDimensions(int rows, int 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) {}
}
Loading
Loading