-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Finish room case rule attempt (#717)
* Update SurroundRegionDirectRule.java * Update SurroundRegionDirectRule.java * Update SurroundRegionDirectRule.java * Create FinishRoomCaseRule.java * more work done to finish room case rule. Also since there currently is no case rules that alter 2 separate puzzzle tiles I added the basis for those in the case rule class * Finish Room Case rule work Remove unecessary changes from caseRule.java. Show all work before deleting uneccesary bits from finishRoom case rule attempt. * FinishRoomCaseRule initial completed attempt First clean attempt at rule. * get draft pull need help * Changed the ID * Update FinishRoomCaseRule.java * Finish Room Case Rule Basically Done Rooms seem to be finished correctly including all edge cases. Arrow color is still red but may be fixed when updating my branch. * Cleaned up code Removed print statements and unnecessary lines * Finish Room green arrows and test case start Green arrows now show when rule is ran correctly. Red arrow when incorrect and larger than 5 paths. Started the test case for it. * Tests and rule rework Work on test class and start of work within Rule and CaseRule to correctly display error message when too many cases are generated * Generalized max cases Max cases is generalized for each case rule. Can be altered from the original 10 to whichever you choose. I input 5 for finish room and 2 for black or white case rule in Nurikabe but can be done for case rules in other puzzles. AutoCaseRuleCommand was changed as well to display message when too many cases of the case rule is generated. * FinishRoom Image Created and added new image to display the finish room case rule * Added argument for minimum number of case rules * Test Case initial Initial passing cases, may need additional work but currently passes. * Update CaseRule.java * False justification bug fix Fixes green arrow when using case rule after altering 1 random tile. Also sets minimum cases number to 2. --------- Co-authored-by: Ivan Ho <[email protected]> Co-authored-by: Charles Tian <[email protected]> Co-authored-by: charlestian23 <[email protected]>
- Loading branch information
1 parent
9270d0e
commit 2ad0fd7
Showing
8 changed files
with
331 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FinishRoomCaseRule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package edu.rpi.legup.puzzle.nurikabe.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.nurikabe.NurikabeBoard; | ||
import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; | ||
import edu.rpi.legup.puzzle.nurikabe.NurikabeType; | ||
import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities; | ||
import edu.rpi.legup.utility.DisjointSets; | ||
|
||
import java.awt.*; | ||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class FinishRoomCaseRule extends CaseRule { | ||
public FinishRoomCaseRule() { | ||
super("NURI-CASE-0002", | ||
"Finish Room", | ||
"Room can be finished in up to five ways", | ||
"edu/rpi/legup/images/nurikabe/cases/FinishRoom.png"); | ||
this.MAX_CASES = 5; | ||
this.MIN_CASES = 2; | ||
} | ||
|
||
/** | ||
* Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is | ||
* the one that should overridden in child classes. | ||
* | ||
* @param transition transition to check | ||
* @return null if the child node logically follow from the parent node, otherwise error message | ||
*/ | ||
@Override | ||
public String checkRuleRaw(TreeTransition transition) { | ||
NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); | ||
List<TreeTransition> childTransitions = transition.getParents().get(0).getChildren(); | ||
if (childTransitions.size() > 5) { | ||
return super.getInvalidUseOfRuleMessage() + ": This case rule must have 5 or less children."; | ||
} | ||
if(childTransitions.size() < 2) { | ||
return super.getInvalidUseOfRuleMessage() + ": This case rule must have 2 or more children."; | ||
} | ||
Set<Point> locations = new HashSet<>(); | ||
for (TreeTransition t1 : childTransitions) { | ||
locations.add(((NurikabeCell) t1.getBoard().getModifiedData().iterator().next()).getLocation()); //loop see if matches | ||
if (t1.getBoard().getModifiedData().size() != 1) { | ||
return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; | ||
} | ||
for (Point loc : locations) { | ||
for (Point loc2 : locations) { | ||
if (!(loc.equals(loc2)) && (loc.x == loc2.x) && (loc.y == loc2.y)) { | ||
return super.getInvalidUseOfRuleMessage() + ": This case rule must alter a different cell for each case."; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
public CaseBoard getCaseBoard(Board board) { | ||
NurikabeBoard nurikabeBoard = (NurikabeBoard) board.copy(); | ||
CaseBoard caseBoard = new CaseBoard(nurikabeBoard, this); | ||
DisjointSets<NurikabeCell> regions = NurikabeUtilities.getNurikabeRegions(nurikabeBoard); | ||
nurikabeBoard.setModifiable(false); | ||
|
||
|
||
for (PuzzleElement element : nurikabeBoard.getPuzzleElements()) { //loops all puzzle elements | ||
if (((NurikabeCell) element).getType() == NurikabeType.NUMBER) { //if the tile is a white number block | ||
Set<NurikabeCell> disRow = regions.getSet(((NurikabeCell) element)); //store the row of the white region | ||
boolean only = true; //placeholder boolean of if the element being tested is the only number block in the room or not | ||
for(NurikabeCell d : disRow) { //loops through tiles in the room | ||
if((d.getType() == NurikabeType.NUMBER) && !(d.getData().equals(((NurikabeCell) element).getData()))) { //if found another number tile and it's data is different than the element we're working with | ||
only = false; //set only to false | ||
} | ||
} | ||
if (disRow.size() + 1 == ((NurikabeCell) element).getData() && only) { //if size of region is 1 less than the number block and the number block is only number block in the region | ||
caseBoard.addPickableElement(element); //add that room as a pickable 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 | ||
*/ | ||
@Override | ||
public ArrayList<Board> getCases(Board board, PuzzleElement puzzleElement) {//throws IllegalStateException { | ||
ArrayList<Board> cases = new ArrayList<>(); //makes array list of cases | ||
NurikabeBoard nuriBoard = (NurikabeBoard) board.copy(); //nurikabe board to edit | ||
NurikabeCell numbaCell = nuriBoard.getCell(((NurikabeCell) puzzleElement).getLocation().x, | ||
((NurikabeCell) puzzleElement).getLocation().y); //number cell whose room we want to fill | ||
int filledRoomSize = numbaCell.getData(); //size of room we want afterward | ||
Set<Point> locations = new HashSet<>(); //locations where white space is added to finish room | ||
Point left = new Point(-1,0); | ||
Point right = new Point(1, 0); | ||
Point bot = new Point(0, -1); | ||
Point top = new Point(0, 1); | ||
Set<Point> directions = new HashSet<>(); | ||
directions.add(left); | ||
directions.add(right); | ||
directions.add(top); | ||
directions.add(bot); | ||
Set<Point> checkedPoints = new HashSet<>(); //add all into checked points and continue at start of loop if inside | ||
DisjointSets<NurikabeCell> regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); //gathers regions | ||
Set<NurikabeCell> disRow = regions.getSet(numbaCell); //set of white spaces | ||
for (NurikabeCell d : disRow) { //loops through white spaces | ||
if(cases.size() >= 6) { //no need to check this many cases | ||
//throw new IllegalStateException("Too many cases"); | ||
continue; //crash/runtime protection | ||
} | ||
for(Point direction : directions) { | ||
if(cases.size() >= 6) { //no need to check this many cases | ||
//throw new IllegalStateException("Too many cases"); | ||
continue; //crash/runtime protection | ||
} | ||
if(!((nuriBoard.getWidth() > (d.getLocation().x + direction.x) && (nuriBoard.getHeight() > d.getLocation().y + direction.y) && | ||
(d.getLocation().x + direction.x >= 0) && (d.getLocation().y + direction.y >= 0)))) { | ||
continue; //if next location check would be outside of grid then continue | ||
} | ||
NurikabeCell curr = nuriBoard.getCell(d.getLocation().x + direction.x, d.getLocation().y + direction.y); | ||
if(checkedPoints.contains(curr.getLocation())) { | ||
continue; //if we already checked whether or not making this tile white would complete the room then continue | ||
} | ||
checkedPoints.add(curr.getLocation()); //adds location to checkedPoints so we don't check it again and accidentally add | ||
if (curr.getType() == NurikabeType.UNKNOWN) { //found adjacent space to region that is currently unknown | ||
curr.setData(NurikabeType.WHITE.toValue()); //changes adjacent cell color to white | ||
nuriBoard.addModifiedData(curr); // adds modified before check | ||
regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); //update regions | ||
Set<NurikabeCell> disCreatedRow = regions.getSet(curr); //gets set of created row with new white cell added | ||
if (disCreatedRow.size() == filledRoomSize) { //If adding white fills the room to exact size of number block and doesn't connect with another room | ||
Point here = curr.getLocation(); //gets current location of new white tile that fills room | ||
boolean alreadyIn = false; //sets whether or not the tile has already been added to false | ||
for (Point p : locations) { //loops through locations of previously added tiles | ||
if (p == here) { //if point is already in | ||
alreadyIn = true; //change already in to true | ||
break; | ||
} | ||
} | ||
if (!alreadyIn) { //if point wasn't already in | ||
Board casey = nuriBoard.copy(); //copy the current board with white tile changed | ||
PuzzleElement datacasey = curr; //gets changed white tile as a puzzle element | ||
datacasey.setData(NurikabeType.WHITE.toValue()); //ensure set to white, probably redundant | ||
casey.addModifiedData(datacasey); //ensure confirmed white change | ||
regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); //update regions | ||
cases.add(casey); //add this case to list of cases | ||
locations.add(here); //add location of new white tile to list of locations so that we don't accidentally add it again later | ||
} | ||
} | ||
curr.setData(NurikabeType.UNKNOWN.toValue()); //set cell type back to unknown | ||
nuriBoard.addModifiedData(curr); // confirms change back to unknown | ||
regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); //updates regions | ||
} | ||
} | ||
} | ||
return cases; | ||
} | ||
|
||
/** | ||
* Checks whether the child node logically follows from the parent node | ||
* at the specific puzzleElement index using this rule | ||
* | ||
* @param transition transition to check | ||
* @param puzzleElement equivalent puzzleElement | ||
* @return null if the child node logically follow from the parent node at the specified puzzleElement, | ||
* otherwise error message | ||
*/ | ||
@Override | ||
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+2.97 KB
src/main/resources/edu/rpi/legup/images/nurikabe/cases/FinishRoom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 114 additions & 0 deletions
114
src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package puzzles.nurikabe.rules; | ||
|
||
import edu.rpi.legup.history.AutoCaseRuleCommand; | ||
import edu.rpi.legup.model.gameboard.Board; | ||
import edu.rpi.legup.puzzle.nurikabe.*; | ||
import edu.rpi.legup.puzzle.nurikabe.rules.FinishRoomCaseRule; | ||
import edu.rpi.legup.utility.DisjointSets; | ||
import legup.MockGameBoardFacade; | ||
import legup.TestUtilities; | ||
import edu.rpi.legup.model.tree.TreeNode; | ||
import edu.rpi.legup.model.tree.TreeTransition; | ||
import org.junit.Assert; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
import edu.rpi.legup.save.InvalidFileFormatException; | ||
|
||
import java.util.ArrayList; | ||
|
||
import java.awt.*; | ||
import java.util.Set; | ||
|
||
public class FinishRoomCaseRuleTest { | ||
|
||
private static final FinishRoomCaseRule RULE = new FinishRoomCaseRule(); | ||
private static Nurikabe nurikabe; | ||
|
||
@BeforeClass | ||
public static void setUp() { | ||
MockGameBoardFacade.getInstance(); | ||
nurikabe = new Nurikabe(); | ||
} | ||
|
||
/** | ||
* Tests the Finish Room case rule by ensuring that it results in 5 or less children, that contain a modified cell that is white | ||
*/ | ||
@Test | ||
public void FinishRoomCaseRule_FinishRoomCaseRuleBaseTest() throws InvalidFileFormatException { | ||
TestUtilities.importTestBoard("puzzles/nurikabe/rules/FinishRoomCaseRule/FinishRoomCaseRuleBase", nurikabe); | ||
TreeNode rootNode = nurikabe.getTree().getRootNode(); | ||
TreeTransition transition = rootNode.getChildren().get(0); | ||
transition.setRule(RULE); | ||
|
||
NurikabeBoard board = (NurikabeBoard) transition.getBoard(); | ||
NurikabeCell cell = board.getCell(6,4); | ||
ArrayList<Board> cases = RULE.getCases(board,cell); | ||
Assert.assertEquals(2,cases.size()); | ||
|
||
NurikabeBoard caseBoard = (NurikabeBoard) cases.get(0); | ||
NurikabeBoard caseBoard2 = (NurikabeBoard) cases.get(1); | ||
|
||
NurikabeType board1Type = caseBoard.getCell(5,5).getType(); | ||
NurikabeType board2Type = caseBoard2.getCell(6,6).getType(); | ||
|
||
Assert.assertTrue((board1Type.equals(NurikabeType.WHITE) && board2Type.equals(NurikabeType.WHITE))); | ||
|
||
Assert.assertEquals(caseBoard.getHeight(),caseBoard2.getHeight(), board.getHeight()); | ||
Assert.assertEquals(caseBoard.getWidth(),caseBoard2.getWidth(), board.getWidth()); | ||
|
||
DisjointSets<NurikabeCell> regions = NurikabeUtilities.getNurikabeRegions(caseBoard); //gathers regions | ||
Set<NurikabeCell> disRow = regions.getSet(caseBoard.getCell(5, 5)); | ||
Assert.assertEquals(disRow.size(), 3); | ||
|
||
DisjointSets<NurikabeCell> regions2 = NurikabeUtilities.getNurikabeRegions(caseBoard2); //gathers regions | ||
Set<NurikabeCell> disRow2 = regions2.getSet(caseBoard2.getCell(6, 6)); | ||
Assert.assertEquals(disRow2.size(), 3); | ||
|
||
for(int i=0; i<caseBoard.getHeight(); i++){ | ||
for(int k=0; k<caseBoard.getWidth(); k++){ | ||
Point point = new Point(k,i); | ||
if(point.equals(caseBoard.getCell(k,i).getLocation())){ | ||
continue; | ||
} | ||
Assert.assertTrue(caseBoard.getCell(k,i).equals(caseBoard2.getCell(k,i)) | ||
|| caseBoard2.getCell(k,i).equals(caseBoard2.getCell(k,i))); | ||
} | ||
} | ||
|
||
|
||
|
||
|
||
NurikabeCell cell2 = board.getCell(4,2); | ||
ArrayList<Board> cases2 = RULE.getCases(board,cell2); | ||
|
||
Assert.assertEquals(6,cases2.size()); //correctly stops generating possible cases after | ||
// more than 5 (the max) is found. Would have generated 8 cases | ||
// FinishRoomCaseRule finny = new FinishRoomCaseRule(); | ||
// finny.checkRuleRaw(); | ||
//"Invalid use of the case rule FinishRoom: This case rule must have 5 or less children." | ||
|
||
//getErrorString in auto case rule | ||
//should display "The selection can produce a max of 5 cases." | ||
//AutoCaseRuleCommand autoCaseRuleCommand = new AutoCaseRuleCommand(elementView, selection, caseBoard.getCaseRule(), caseBoard, e); | ||
|
||
// NurikabeBoard caseyBoard = (NurikabeBoard) cases2.get(0); | ||
// NurikabeBoard caseyBoard2 = (NurikabeBoard) cases2.get(1); | ||
// NurikabeBoard caseyBoard3 = (NurikabeBoard) cases2.get(2); | ||
// NurikabeBoard caseyBoard4 = (NurikabeBoard) cases2.get(3); | ||
// NurikabeBoard caseyBoard5 = (NurikabeBoard) cases2.get(4); | ||
// NurikabeBoard caseyBoard6 = (NurikabeBoard) cases2.get(5); | ||
// NurikabeBoard caseyBoard7 = (NurikabeBoard) cases2.get(6); | ||
// NurikabeBoard caseyBoard8 = (NurikabeBoard) cases2.get(7); | ||
// | ||
// NurikabeType boardy1Type = caseyBoard.getCell(5,5).getType(); | ||
// NurikabeType boardy2Type = caseyBoard2.getCell(6,6).getType(); | ||
// NurikabeType boardy3Type = caseyBoard.getCell(5,5).getType(); | ||
// NurikabeType boardy4Type = caseyBoard2.getCell(6,6).getType(); | ||
// NurikabeType boardy5Type = caseyBoard.getCell(5,5).getType(); | ||
// NurikabeType boardy6Type = caseyBoard2.getCell(6,6).getType(); | ||
// NurikabeType boardy7Type = caseyBoard.getCell(5,5).getType(); | ||
// NurikabeType boardy8Type = caseyBoard2.getCell(6,6).getType(); | ||
|
||
|
||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/test/resources/puzzles/nurikabe/rules/FinishRoomCaseRule/FinishRoomCaseRuleBase
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<Legup> | ||
<puzzle name="Nurikabe"> | ||
<board width="7" height="7"> | ||
<cells> | ||
<cell value="1" x="3" y="0"/> | ||
<cell value="6" x="4" y="2"/> | ||
<cell value="2" x="6" y="2"/> | ||
<cell value="3" x="6" y="4"/> | ||
<cell value="3" x="3" y="6"/> | ||
<cell value="-1" x="5" y="2"/> | ||
<cell value="-1" x="5" y="4"/> | ||
<cell value="-1" x="6" y="3"/> | ||
<cell value="0" x="6" y="5"/> | ||
<cell value="0" x="1" y="2"/> | ||
<cell value="0" x="1" y="3"/> | ||
<cell value="0" x="2" y="2"/> | ||
<cell value="0" x="3" y="2"/> | ||
</cells> | ||
</board> | ||
</puzzle> | ||
</Legup> |