From bcc904a9d3a4077a0e419cb5913923deaa8f8dec Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:23:26 -0500 Subject: [PATCH 01/74] Added a text file File represents all files we will put and use to fully implement the StarBattle puzzle later. --- .../rpi/legup/puzzle/starbattle/allfiles.txt | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt new file mode 100644 index 000000000..7f006191a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -0,0 +1,230 @@ +//StarBattle.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class StarBattle extends Puzzle { + public StarBattle() { + super(); + this.name = "StarBattle"; + + this.importer = new StarBattleImporter(this); + this.exporter = new StarBattleExporter(this); + + this.factory = new StarBattleCellFactory(); + } + + @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) { + } +} + +//StarBattleBoard.java + +package edu.rpi.legup.puzzle.lightup; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +public class StarBattleBoard extends GridBoard { + + private int size; + private vector group_sizes; + + /** + * StarBattleBoard Constructor - create a new Star Battle board + * + * @param size size of one side of the star battle board + */ + + public StarBattleBoard(int size) { + super(size, size); + group_sizes = vector(size); + } + + @Override + public StarBattleCell getCell(int x, int y) { + return (StarBattleCell) super.getCell(x, y); + } + + +} + +//StarBattleCell.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +public class StarBattleCell extends GridCell { + private int groupIndex; + private int max; + + /** + * SudokuCell Constructor - creates a new Sudoku cell to hold the puzzleElement + * + * @param valueInt value of the star battle cell denoting its state + * @param location location of the cell on the board + * @param size size of the sudoku cell + */ + public StarBattleCell(int value, Point location, int groupIndex, int size) { + super(value, location); + this.groupIndex = groupIndex; + this.max = size; + } + + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()) { + case "SBUP-PLAC-0001": + this.data = -4; + break; + case "SBUP-UNPL-0002": + this.data = -1; + break; + case "SBUP-UNPL-0003": + this.data = -2; + break; + case "SBUP-UNPL-0001"://Not sure how button events work + switch (m.getButton()){ + case MouseEvent.BUTTON1: + if (this.data < 0 || this.data > 3) { + this.data = 0; + } + else { + this.data = this.data + 1; + } + break; + case MouseEvent.BUTTON3: + if (this.data > 0) { + this.data = this.data - 1; + } + else { + this.data = 3;//Unsure + } + break; + } + break; + } + } + + public LightUpCellType getType() { + switch (data) { + case -3: + return LightUpCellType.UNKNOWN; + case -2: + return LightUpCellType.STAR; + case -1: + return LightUpCellType.BLACK; + default: + if (data >= 0) { + return StarBattleCellType.WHITE; + } + } + return null; + } + + /** + * Gets the region index of the cell + * + * @return group index of the cell + */ + public int getGroupIndex() { + return groupIndex; + } + + /** + * Gets the size of the cell + * + * @return size of the cell + */ + + public int getMax() { + return max; + } + +} + +//StarBattleCellController.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.event.MouseEvent; + +public class StarBattleCellController extends ElementController { + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + StarBattleCell cell = (StarBattleCell) 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() == 0) { + data.setData(-3); + } + else { + data.setData(cell.getData() + 1); + } + } + } + else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == -3) { + data.setData(0); + } + else { + data.setData(cell.getData() - 1); + } + } + } + } +} + +//StarBattleCellFactory.java + + + +//StarBattleCellType.java +package edu.rpi.legup.puzzle.starbattle; + +public enum StarBattleType { + UNKNOWN(-3), STAR(-2), BLACK(-1), WHITE(0); + + public int value; + + StarBattleCell(int value) { + this.value = value; + } +} + +//StarBattleExporter.java +//StarBattleImporter.java +//StarBattleView.java \ No newline at end of file From 9fa0e0205a7f744b9597135202290e1363339593 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:45:28 -0500 Subject: [PATCH 02/74] Imported the black and star tiles ID probably needs to be changed in the future, and more assets on the way. --- .../puzzle/starbattle/elements/BlackTile.java | 9 +++++++++ .../puzzle/starbattle/elements/StarTile.java | 9 +++++++++ .../edu/rpi/legup/images/starbattle/black.gif | Bin 0 -> 856 bytes .../edu/rpi/legup/images/starbattle/star.gif | Bin 0 -> 545 bytes 4 files changed, 18 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/black.gif create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/star.gif diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java new file mode 100644 index 000000000..fd74774a7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class BlackTile extends NonPlaceableElement { + public BlackTile() { + super("STBL-PLAC-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java new file mode 100644 index 000000000..2510869eb --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class StarTile extends NonPlaceableElement { + public StarTile() { + super("LTUP-PLAC-0001", "Star Tile", "The star tile", "edu/rpi/legup/images/starbattle/star.gif"); + } +} diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/black.gif b/src/main/resources/edu/rpi/legup/images/starbattle/black.gif new file mode 100644 index 0000000000000000000000000000000000000000..13381a7179c2af7ad23ef064b743d3e479b56070 GIT binary patch literal 856 zcmZ?wbhEHbRA5kG_|Cv^;=~DtQ7{?;BQ*pRf3kqRt^*=Ld4hq%l;J<8jK_ur2b(#B zwPH?eSa`TyK-p`K$HqlRyCsaX?wr`T_;|m9bC-Px$+et)0R7gwB)k~;OQ4|O8-%S#Q6f(WuN(@LbxC6?>z|bQh@)#H)BSjf;3uWXn zP!c6GWTuoTBMJi~86e~tQXYG0-#TsIx!*bRl~udbx7OP0zy9m7zdk*dz8=T--vKPZ zNZe}5ye)&{8DLYd0Dk7cjT39^h^+_)Av2;~qv~I+oyO zH;_68qf-gSS5A~<6}DyFAzZ*gyu-&-wh#DP0#*UeVHPIj6985Df-zYUfr3d@-r_ah z<1to;v1D)nd-JVpIkQjs`y%Q0XIp&7Vq88Gu{7JF`N8ys&8EnBIreozzwXyvjSvlDruB;5x!$Xnyj1X!07h$Pn6jcYf6SBM5q!d&djqZoq_nZLnHO!*stxbz$k z^O6OVs+_<8W@9oUnLlJW2AgrJxN#Uiu)EO55S(udFjW7z7Y*E=w)sI0G84zJJpYbF zwzLI!nx2AqGGew**VH(+^=R_>j2PR-CF)OWzawq%uDa$G`gogyQMoI%|H<=lr(y6R jw|ThRX3<*4@9h2yt8`HryKo;Y00000NkvXXu0mjflb`cS literal 0 HcmV?d00001 From 17d26d73444b353a1ea31abde928c754020f754d Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:53:11 -0500 Subject: [PATCH 03/74] Added UnknownTile Also fixed the descriptions of the black and star tile, not sure if we need another asset. --- .../puzzle/starbattle/elements/BlackTile.java | 2 +- .../puzzle/starbattle/elements/StarTile.java | 2 +- .../puzzle/starbattle/elements/UnknownTile.java | 9 +++++++++ .../edu/rpi/legup/images/starbattle/empty.gif | Bin 0 -> 857 bytes 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/empty.gif diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java index fd74774a7..2601bd351 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java @@ -4,6 +4,6 @@ public class BlackTile extends NonPlaceableElement { public BlackTile() { - super("STBL-PLAC-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); + super("STBL-PLAC-0002", "Black Tile", "The black tile that shows where you cannot place a star", "edu/rpi/legup/images/lightup/black.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java index 2510869eb..19ba7baed 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -4,6 +4,6 @@ public class StarTile extends NonPlaceableElement { public StarTile() { - super("LTUP-PLAC-0001", "Star Tile", "The star tile", "edu/rpi/legup/images/starbattle/star.gif"); + super("LTUP-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java new file mode 100644 index 000000000..192a95357 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class UnknownTile extends NonPlaceableElement { + public UnknownTile() { + super("LTUP-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + } +} diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif b/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif new file mode 100644 index 0000000000000000000000000000000000000000..38b91d0a2b9c6599eb19d14466db047a86d2ea97 GIT binary patch literal 857 zcmZ?wbhEHbRA5kG_|Cxa|Nno6Q7{?;BQ*qcKpqF>1qKc~21X7Uj|~eBHggDT#hlo% z@Nm0;vez7sjf;+UOBiR}IkEBaF$M+Z0v^pz$tNbN+pdZ^xoPR?=?2NC=6GICbzrat E0QgHHq5uE@ literal 0 HcmV?d00001 From 8955343d8bbdac96fa80cb31480b1784a58c1db5 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 13 Feb 2024 16:32:48 -0500 Subject: [PATCH 04/74] Create StarBattleCellType.java Created StarBattleCellType.java file --- .../legup/puzzle/starbattle/StarBattleCellType.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java new file mode 100644 index 000000000..0548a6b38 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -0,0 +1,12 @@ +//StarBattleCellType.java +package edu.rpi.legup.puzzle.starbattle; + +public enum StarBattleType { + UNKNOWN(-3), STAR(-2), BLACK(-1); + + public int value; + + StarBattleCell(int value) { + this.value = value; + } +} \ No newline at end of file From 1ad2347247d53afe83646401416f5ee4e3ecd735 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 13 Feb 2024 16:37:59 -0500 Subject: [PATCH 05/74] Update StarBattleCellType.java Fixed StarBattleCellType typos --- .../edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java index 0548a6b38..8fb3338f7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -1,12 +1,12 @@ //StarBattleCellType.java package edu.rpi.legup.puzzle.starbattle; -public enum StarBattleType { +public enum StarBattleCellType { UNKNOWN(-3), STAR(-2), BLACK(-1); public int value; - StarBattleCell(int value) { + StarBattleCellType(int value) { this.value = value; } } \ No newline at end of file From 12b7a4358c042c863ea0a03792cd790207244d18 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:41:30 -0500 Subject: [PATCH 06/74] Added StarBattle.java Also changed the IDs for our assets --- .../legup/puzzle/starbattle/StarBattle.java | 33 +++++++++++++++++++ .../puzzle/starbattle/StarBattleCell.java | 0 .../puzzle/starbattle/elements/StarTile.java | 2 +- .../starbattle/elements/UnknownTile.java | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java new file mode 100644 index 000000000..e9e18b92a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle; +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class StarBattle extends Puzzle { + public StarBattle() { + super(); + //this.name = "StarBattle"; + + //this.importer = new StarBattleImporter(this); + //this.exporter = new StarBattleExporter(this); + + //this.factory = new StarBattleCellFactory(); + } + + @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/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java index 19ba7baed..d42cc0010 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -4,6 +4,6 @@ public class StarTile extends NonPlaceableElement { public StarTile() { - super("LTUP-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); + super("STBL-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java index 192a95357..3e1cbca26 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -4,6 +4,6 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("LTUP-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + super("STBL-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); } } From 24450fd99b358f5dfb06d76933c21ad49e13d0bd Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:31:13 -0500 Subject: [PATCH 07/74] Added White Tile and finished starter code for Cell Only Board needs to be added to get the basic puzzle running. --- .../puzzle/starbattle/StarBattleCell.java | 85 ++++++++++++++++++ .../puzzle/starbattle/StarBattleCellType.java | 2 +- .../starbattle/elements/UnknownTile.java | 2 +- .../puzzle/starbattle/elements/WhiteTile.java | 10 +++ .../edu/rpi/legup/images/starbattle/white.gif | Bin 0 -> 9700 bytes 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/white.gif diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index e69de29bb..22d74dd7f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -0,0 +1,85 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.elements.Element; +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.*; +import java.awt.event.MouseEvent; + +public class StarBattleCell extends GridCell { + private int groupIndex; + private int max; + + /** + * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement + * + * @param valueInt value of the star battle cell denoting its state + * @param location location of the cell on the board + * @param size size of the star battle cell + */ + public StarBattleCell(int value, Point location, int groupIndex, int size) { + super(value, location); + this.groupIndex = groupIndex; + this.max = size; + } + + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()) { + case "STBL-PLAC-0001": + this.data = -3; + break; + case "STBL-PLAC-0002": + this.data = -2; + break; + case "STBL-PLAC-0003": + this.data = -1; + break; + + case "STBL-UNPL-0001"://Not sure how button events work + switch (m.getButton()){ + case MouseEvent.BUTTON1: + if (this.data > 0 || this.data < -3) { + this.data = -3; + } + else { + this.data = this.data + 1; + } + break; + case MouseEvent.BUTTON3: + if (this.data > -4) { + this.data = this.data - 1; + } + else { + this.data = -1;//Unsure + } + break; + } + break; + } + } + + public StarBattleCellType getType() { + switch (data) { + case -3: + return StarBattleCellType.UNKNOWN; + case -2: + return StarBattleCellType.STAR; + case -1: + return StarBattleCellType.BLACK; + default: + if (data >= 0) { + return StarBattleCellType.UNKNOWN; + } + } + return null; + } + + public StarBattleCell copy() { + StarBattleCell copy = new StarBattleCell(data, (Point) location.clone(), groupIndex, max); + copy.setIndex(index); + copy.setModifiable(isModifiable); + copy.setGiven(isGiven); + return copy; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java index 8fb3338f7..f3524034e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -2,7 +2,7 @@ package edu.rpi.legup.puzzle.starbattle; public enum StarBattleCellType { - UNKNOWN(-3), STAR(-2), BLACK(-1); + WHITE(-3), STAR(-2), BLACK(-1), UNKNOWN(0); public int value; diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java index 3e1cbca26..c2459f642 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -4,6 +4,6 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("STBL-PLAC-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + super("STBL-UNPL-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java new file mode 100644 index 000000000..a064c1fad --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java @@ -0,0 +1,10 @@ +package edu.rpi.legup.puzzle.starbattle.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class WhiteTile extends PlaceableElement { + public WhiteTile() { + super("STBL-PLAC-0001", "White Tile", "The white tile", "edu/rpi/legup/images/starbattle/white.gif"); + } +} + diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/white.gif b/src/main/resources/edu/rpi/legup/images/starbattle/white.gif new file mode 100644 index 0000000000000000000000000000000000000000..fc2c683eb7860f47d4322fe0e73b0508099a89de GIT binary patch literal 9700 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 literal 0 HcmV?d00001 From c64481dbd4f964927f53fa0956c67eb3c83057c6 Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 16 Feb 2024 13:36:31 -0500 Subject: [PATCH 08/74] create StarBattleBoard.java --- .../puzzle/starbattle/StarBattleBoard.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java new file mode 100644 index 000000000..300695111 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -0,0 +1,21 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +public class StarBattleBoard extends GridBoard { + public StarBattleBoard(int width, int height) { + super(width, height); + } + + public StarBattleBoard(int size) { + super(size, size); + } + + @Override + public StarBattleCell getCell(int x, int y) { + return (StarBattleCell) super.getCell(x,y); + } +} + + From f3b23833821404742f88b8dd073e01b5fe25ab0d Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:36:02 -0500 Subject: [PATCH 09/74] Added getRow, getCol, and potential group implementation for later. All StarBattle boards should be square, so I deleted the width height constructor. I also added size for returning a row, col, for future rules. --- .../puzzle/starbattle/StarBattleBoard.java | 34 +++++++++++++++++-- .../rpi/legup/puzzle/starbattle/allfiles.txt | 10 +++--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index 300695111..a32197fa7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -1,21 +1,49 @@ package edu.rpi.legup.puzzle.starbattle; +import java.util.*; + import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; public class StarBattleBoard extends GridBoard { - public StarBattleBoard(int width, int height) { - super(width, height); - } + + private int size; + //private ArrayList groupSizes; public StarBattleBoard(int size) { super(size, size); + this.size = size; } @Override public StarBattleCell getCell(int x, int y) { return (StarBattleCell) super.getCell(x,y); } + + /* + public StarBattleCell getCell(int groupIndex, int x, int y) { + return getCell(x + (groupIndex % groupSize) * groupSize, y + (groupIndex / groupSize) * groupSize); + }*/ + + public int getSize() { + return size; + } + + public Set getRow(int rowNum) { + Set row = new HashSet<>(); + for (int i = 0; i < size; i++) { + row.add(getCell(i, rowNum)); + } + return row; + } + + public Set getCol(int colNum) { + Set column = new HashSet<>(); + for (int i = 0; i < size; i++) { + column.add(getCell(colNum, i)); + } + return column; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt index 7f006191a..1b1824f21 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -101,14 +101,14 @@ public class StarBattleCell extends GridCell { public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { case "SBUP-PLAC-0001": - this.data = -4; + this.data = -3; break; - case "SBUP-UNPL-0002": - this.data = -1; - break; - case "SBUP-UNPL-0003": + case "SBUP-PLAC-0002": this.data = -2; break; + case "SBUP-PLAC-0003": + this.data = -1; + break; case "SBUP-UNPL-0001"://Not sure how button events work switch (m.getButton()){ case MouseEvent.BUTTON1: From f6e11dcc1eed36b44a4bae8aead8da03f4e53496 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 23 Feb 2024 11:49:24 -0500 Subject: [PATCH 10/74] Update allfiles.txt Fixed some temporary documentation to reference star battle instead of sudoku. --- src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt index 1b1824f21..7970eadbf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -85,11 +85,11 @@ public class StarBattleCell extends GridCell { private int max; /** - * SudokuCell Constructor - creates a new Sudoku cell to hold the puzzleElement + * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement * * @param valueInt value of the star battle cell denoting its state * @param location location of the cell on the board - * @param size size of the sudoku cell + * @param size size of the star battle cell */ public StarBattleCell(int value, Point location, int groupIndex, int size) { super(value, location); From 9c3daa796877a9ade90c6a3cba0378ad09faca1f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 23 Feb 2024 15:29:26 -0500 Subject: [PATCH 11/74] Direct Rule Classes Created skeletons of classes for direct rules. --- .../starbattle/rules/BlackoutDirectRule.java | 49 +++++++++++++++++++ .../rules/FinishWithStarsDirectRule.java | 47 ++++++++++++++++++ .../RegionsWithinRowsColumnsDirectRule.java | 46 +++++++++++++++++ .../RowsColumnsWithinRegionsDirectRule.java | 46 +++++++++++++++++ .../rules/SurroundStarDirectRule.java | 48 ++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java new file mode 100644 index 000000000..3c89b0800 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -0,0 +1,49 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class BlackoutDirectRule extends DirectRule { + + public BlackoutDirectRule() { + super("STBL-BASC-0001", + "Blackout", + "If a row, column, or region has enough stars, its unknown spaces are black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} + + diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java new file mode 100644 index 000000000..d2711eced --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -0,0 +1,47 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class FinishWithStarsDirectRule extends DirectRule { + + public FinishWithStarsDirectRule() { + super("STBL-BASC-0002", + "Finish With Stars", + "Unknown spaces must be stars if there are just enough in a row, column, or region to satisfy the puzzle number.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java new file mode 100644 index 000000000..81645a2ad --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java @@ -0,0 +1,46 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class RegionsWithinRowsColumnsDirectRule extends DirectRule { + public RegionsWithinRowsColumnsDirectRule() { + super("STBL-BASC-0003", + "Regions Within Rows/Columns", + "If a number of regions is fully contained by an equal number of rows or columns, spaces of other regions in those rows or columns must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java new file mode 100644 index 000000000..53a7b27ee --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java @@ -0,0 +1,46 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class RowsColumnsWithinRegionsDirectRule extends DirectRule { + public RowsColumnsWithinRegionsDirectRule() { + super("STBL-BASC-0004", + "Rows/Columns Within Regions", + "If a number of rows or columns is fully contained by an equal number of regions, spaces of other rows or columns, respectively, in those regions must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java new file mode 100644 index 000000000..932ef7ddd --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -0,0 +1,48 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class SurroundStarDirectRule extends DirectRule { + + public SurroundStarDirectRule() { + super("STBL-BASC-0005", + "Surround Star", + "Any space adjacent to a star must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} + From cda9403b1f9bb82880db422e7e18128b3bd53ba4 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:01:39 -0500 Subject: [PATCH 12/74] Added javadoc to cell --- .../java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index 22d74dd7f..2e89649e2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -15,6 +15,7 @@ public class StarBattleCell extends GridCell { * * @param valueInt value of the star battle cell denoting its state * @param location location of the cell on the board + * @param groupIndex indicates what group # the cell is in. * @param size size of the star battle cell */ public StarBattleCell(int value, Point location, int groupIndex, int size) { From 0fb71deb5b70bd388e02b2eaa3b3658a81f19fb7 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:24:04 -0500 Subject: [PATCH 13/74] Adding barebones Contradiction Rules Files Might have to separate the too few and too many rules into 3 files, for row, column, and region. --- .../rules/ClashingOrbitContradictionRule | 33 +++++++++++++++++++ .../rules/TooFewStarsContradictionRule | 33 +++++++++++++++++++ .../rules/TooManyStarsContradictionRule | 33 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule new file mode 100644 index 000000000..2eb584ea1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class ClashingOrbitContradictionRule extends ContradictionRule { + + public ClashingOrbitContradictionRule() { + super("STBL-CONT-0003", + "Clashing Orbit", + "No two stars can be adjacent to each other.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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) { + return super.getNoContradictionMessage(); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule new file mode 100644 index 000000000..a063b2b6b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class TooFewStarsContradictionRule extends ContradictionRule { + + public TooFewStarsContradictionRule() { + super("STBL-CONT-0002", + "Too Few Stars", + "There are too few stars in this region/row/column and there are not enough places to put the remaining stars.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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) { + return super.getNoContradictionMessage(); + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule new file mode 100644 index 000000000..56cd71ba7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule @@ -0,0 +1,33 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +public class TooManyStarsContradictionRule extends ContradictionRule { + + public TooManyStarsContradictionRule() { + super("STBL-CONT-0001", + "Too Many Stars", + "There are too many stars in this region/row/column.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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) { + return super.getNoContradictionMessage(); + } +} \ No newline at end of file From cef9d476d8ba74a82112c47b1186a5375a12fb02 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:06:34 -0500 Subject: [PATCH 14/74] Added the Element and Board Controller Hopefully we can upload puzzles now that regions are done. --- .../legup/puzzle/starbattle/StarBattle.java | 2 + .../starbattle/StarBattleController.java | 36 +++++++++++++++ .../starbattle/StarBattleElementView.java | 46 +++++++++++++++++++ .../puzzle/starbattle/StarBattleView.java | 0 4 files changed, 84 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java index e9e18b92a..78800916e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java @@ -15,6 +15,8 @@ public StarBattle() { @Override public void initializeView() { + boardView = new StarBattleView((StarBattleBoard) currentBoard); + addBoardListener(boardView); } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java new file mode 100644 index 000000000..f7a094397 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java @@ -0,0 +1,36 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.event.MouseEvent; + +public class StarBattleController extends ElementController { + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + StarBattleCell cell = (StarBattleCell) 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() == 0) { + data.setData(-3); + } + else { + data.setData(cell.getData() + 1); + } + } + } + else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == -3) { + data.setData(0); + } + else { + data.setData(cell.getData() - 1); + } + } + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java new file mode 100644 index 000000000..a88773a06 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java @@ -0,0 +1,46 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.ui.boardview.GridElementView; + +import java.awt.*; + +public class StarBattleElementView extends GridElementView { + + public StarBattleElementView(StarBattleCell cell) { + super(cell); + } + + @Override + public StarBattleCell getPuzzleElement() { + return (StarBattleCell) super.getPuzzleElement(); + } + + @Override + public void drawElement(Graphics2D graphics2D) { + StarBattleCell cell = (StarBattleCell) puzzleElement; + StarBattleCellType type = cell.getType(); + if (type == StarBattleCellType.STAR) { + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.drawImage(StarBattleView.STAR, location.x, location.y, size.width, size.height, Color.WHITE, null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } else if (type == StarBattleCellType.BLACK) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.BLACK); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + } else if (type == StarBattleCellType.WHITE) { + 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 == StarBattleCellType.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); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java new file mode 100644 index 000000000..e69de29bb From 973f041a5ec3fd67d40406a5d6c39c7be7655267 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 23 Feb 2024 20:19:39 -0500 Subject: [PATCH 15/74] Added View file --- .../puzzle/starbattle/StarBattleView.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index e69de29bb..b79b7743a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -0,0 +1,55 @@ +package edu.rpi.legup.puzzle.starbattle; + +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeView; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleController; +import edu.rpi.legup.puzzle.starbattle.StarBattleElementView; +import edu.rpi.legup.puzzle.starbattle.StarBattleView; +import edu.rpi.legup.ui.boardview.GridBoardView; + +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.model.gameboard.CaseBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.ui.boardview.ElementView; +import java.awt.*; +import java.util.ArrayList; + +public class StarBattleView extends GridBoardView { + private final static Logger LOGGER = LogManager.getLogger(StarBattleView.class.getName()); + + static Image STAR; + + static { + try { + STAR = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/starbattle/star.png")); + } + catch (IOException e) { + LOGGER.error("Failed to open StarBattle images"); + } + } + + public StarBattleView(StarBattleBoard board) { + super(new BoardController(), new StarBattleController(), board.getDimension()); + + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + StarBattleCell cell = (StarBattleCell) puzzleElement; + Point loc = cell.getLocation(); + StarBattleElementView elementView = new StarBattleElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + elementView.setLocation(new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(elementView); + } + } + + +} From 3fe6695c1ed01db14c2dcc7ef2a26d8aa3484ff8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 27 Feb 2024 16:50:04 -0500 Subject: [PATCH 16/74] Create StarOrEmptyCaseRule.java Created skeleton of class for Star or Empty case rule. --- .../starbattle/rules/StarOrEmptyCaseRule.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java new file mode 100644 index 000000000..a30823faa --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java @@ -0,0 +1,78 @@ +package edu.rpi.legup.puzzle.starbattle.rules; + +import edu.rpi.legup.model.rules.CaseRule; +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.tree.TreeTransition; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +import java.util.ArrayList; + +public class StarOrEmptyCaseRule extends CaseRule { + + public StarOrEmptyCaseRule() { + super("STBL-CASE-0002", + "Star or Empty", + "Each unknown space is either a star or empty.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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) { + //TODO: implement this + return null; + } + + @Override + public CaseBoard getCaseBoard(Board board) { + StarBattleBoard starBattleBoard = (StarBattleBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(starBattleBoard, this); + starBattleBoard.setModifiable(false); + for (PuzzleElement element : starBattleBoard.getPuzzleElements()) { + if (((StarBattleCell) element).getType() == StarBattleCellType.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 + */ + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList<>(); + Board case1 = board.copy(); + PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); + data1.setData(StarBattleCellType.STAR); + case1.addModifiedData(data1); + cases.add(case1); + + Board case2 = board.copy(); + PuzzleElement data2 = case2.getPuzzleElement(puzzleElement); + data2.setData(StarBattleCellType.BLACK); + case2.addModifiedData(data2); + cases.add(case2); + + return cases; + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } +} From e677a6eec68aa48ebe8fe121923f979b227baf2f Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:50:36 -0500 Subject: [PATCH 17/74] Adding Region files --- .../rpi/legup/model/gameboard/GridRegion.java | 53 +++++++++++++++++++ .../puzzle/starbattle/StarBattleRegion.java | 9 ++++ 2 files changed, 62 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java new file mode 100644 index 000000000..6718ab6f4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java @@ -0,0 +1,53 @@ +package edu.rpi.legup.model.gameboard; + +import java.util.ArrayList; +import java.util.List; + +public abstract class GridRegion { + + protected List regionCells; + + /** + * Region Constructor + */ + public GridRegion() { + this.regionCells = new ArrayList<>(); + } + + /** + * Adds the cell to the region + * @param cell cell to be added to the region + */ + public void addCell(T cell) { + regionCells.add(cell); + } + + /** + * Removes the cell from the region + * @param cell cell to be remove from the region + */ + public void removeCell(T cell) { + regionCells.remove(cell); + } + + /** + * Returns the list of cells in the region + * @return list of cells in region + */ + public List getCells() { + return regionCells; + } + + /** + * Returns the number of cells in the region + * @return number of cells in the region + */ + public int getSize(){ + return regionCells.size(); + } + + /* + public void colorRegion(){} + */ + +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java new file mode 100644 index 000000000..68a25bc3b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java @@ -0,0 +1,9 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.gameboard.GridRegion; + +public class StarBattleRegion extends GridRegion{ + public StarBattleRegion() { + super(); + } +} From 23df2a15c08736e4902b1093a9e6eb92084c2ef7 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:26:50 -0500 Subject: [PATCH 18/74] Added the first XML puzzle file to be read later. --- .../6x6 Star Battle 1star Normal.xml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 puzzles files/starbattle/6x6 Star Battle 1star Normal.xml diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal.xml new file mode 100644 index 000000000..4d9f8b631 --- /dev/null +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 47337b7e5457b5991be30616b38e618ae0ffaa3e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 27 Feb 2024 17:31:09 -0500 Subject: [PATCH 19/74] Implement Surround Star Direct Rule Wrote an implementation for checkRuleRawAt for Surround Star Direct Rule. --- ...le => ClashingOrbitContradictionRule.java} | 0 .../rules/SurroundStarDirectRule.java | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+) rename src/main/java/edu/rpi/legup/puzzle/starbattle/rules/{ClashingOrbitContradictionRule => ClashingOrbitContradictionRule.java} (100%) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java similarity index 100% rename from src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule rename to src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index 932ef7ddd..33f148e94 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -2,9 +2,13 @@ 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.rules.DirectRule; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +import edu.rpi.legup.puzzle.nurikabe.rules.NoNumberContradictionRule; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; @@ -29,7 +33,22 @@ public SurroundStarDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + StarBattleBoard origBoard = (StarBattleBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new ClashingOrbitContradictionRule(); + StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); + + if (cell.getType() != StarBattleCellType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + + StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); + //TODO: please for the love of god make a copy method for star battle board because this isn't actually going to work otherwise + modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR); + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { + return "Black cells must be placed adjacent to a star!"; + } return null; } From 024aa4f94e6b083ed008f7369713e7259519fcdc Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 27 Feb 2024 17:32:33 -0500 Subject: [PATCH 20/74] Changed files to java instead of text. --- ...ewStarsContradictionRule => TooFewStarsContradictionRule.java} | 0 ...yStarsContradictionRule => TooManyStarsContradictionRule.java} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/java/edu/rpi/legup/puzzle/starbattle/rules/{TooFewStarsContradictionRule => TooFewStarsContradictionRule.java} (100%) rename src/main/java/edu/rpi/legup/puzzle/starbattle/rules/{TooManyStarsContradictionRule => TooManyStarsContradictionRule.java} (100%) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java similarity index 100% rename from src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule rename to src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java similarity index 100% rename from src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule rename to src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java From 2d9dc038b9b14e029acf4e48a5d62bcc3ac7bb4b Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 27 Feb 2024 18:47:58 -0500 Subject: [PATCH 21/74] created add star case rule and reference sheet to keep track of the rules --- .../starbattle/rules/AddStarCaseRule.java | 57 +++++++++++++++++++ .../rules/starbattle_reference_sheet.txt | 15 +++++ 2 files changed, 72 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java new file mode 100644 index 000000000..d945f5be9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java @@ -0,0 +1,57 @@ +package edu.rpi.legup.puzzle.starbattle.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.lightup.LightUpBoard; +import edu.rpi.legup.puzzle.lightup.LightUpCell; +import edu.rpi.legup.puzzle.starbattle.*; + +import java.awt.*; +import java.util.*; +import java.util.List; + +public class AddStarCaseRule extends CaseRule { + public AddStarCaseRule() { + super("STBL-CASE-0001", + "Add Star", + "Different ways a region's star number can be satisfied", + ""); + } + + @Override + public CaseBoard getCaseBoard(Board board) { + StarBattleBoard starbattleBoard = (StarBattleBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(starbattleBoard, this); + + return caseBoard; + } + + /** + * Gets the possible cases at a specific location based on this case rule + * + * @param board the current board state + * @param puzzleElement the cell to determine the possible cases for + * @return a list of elements the specified spot could be + */ + @Override + public List getCases(Board board, PuzzleElement puzzleElement) { + StarBattleBoard starbattleBoard = (StarBattleBoard) board; + // take selected spot, check if there are any stars in that row, column, and region + + // do we want all possible cases, that seems like a lot + return null; + } + + @Override + public String checkRuleRaw(TreeTransition transition) { + return null; + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt new file mode 100644 index 000000000..70e81bb73 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt @@ -0,0 +1,15 @@ +Case Rules: +Add Star: STBL-CASE-0001 +Star or Empty: STBL-CASE-0002 + +Basic Rules: +Blackout: STBL-BASC-0001 +Finish With Stars: STBL-BASC-0002 +Regions Within Rows or Columns: STBL-BASC-0003 +Rows or Columns Within Regions: STBL-BASC-0004 +Surround Star: STBL-BASC-0005 + +Contradiction Rules: +Too Many Stars: STBL-CONT-0001 +Too Few Stars: STBL-CONT-0002 +Clashing Orbit: STBL-CONT-0003 \ No newline at end of file From 7f2406c99d01075d5e25afe5279c08d874a0608b Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 12 Mar 2024 16:34:56 -0400 Subject: [PATCH 22/74] New puzzle --- ....xml => 6x6 Star Battle 1star Normal1.xml} | 0 .../6x6 Star Battle 1star Normal2.xml | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+) rename puzzles files/starbattle/{6x6 Star Battle 1star Normal.xml => 6x6 Star Battle 1star Normal1.xml} (100%) create mode 100644 puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml similarity index 100% rename from puzzles files/starbattle/6x6 Star Battle 1star Normal.xml rename to puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml new file mode 100644 index 000000000..cd34805e5 --- /dev/null +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 555890adc30f1f658be9a45e6ae4cc2a2cae362a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 12 Mar 2024 17:02:07 -0400 Subject: [PATCH 23/74] Add Regions to StarBattleBoard --- .../puzzle/starbattle/StarBattleBoard.java | 24 +++++++++++++++++++ .../puzzle/starbattle/StarBattleRegion.java | 8 +++++++ .../rules/SurroundStarDirectRule.java | 3 --- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index a32197fa7..143fbaca6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -8,11 +8,13 @@ public class StarBattleBoard extends GridBoard { private int size; + protected List regions; //private ArrayList groupSizes; public StarBattleBoard(int size) { super(size, size); this.size = size; + this.regions = new ArrayList<>(); } @Override @@ -44,6 +46,28 @@ public Set getCol(int colNum) { } return column; } + + public StarBattleRegion getRegion(int index) { + if (index >= size) { + return null; + } + return regions.get(index); + } + + public StarBattleBoard copy() { + StarBattleBoard copy = new StarBattleBoard(size); + 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()); + } + if (x < this.regions.size()) + copy.regions.add(this.getRegion(x).copy()); + } + for (PuzzleElement e : modifiedData) { + copy.getPuzzleElement(e).setModifiable(false); + } + return copy; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java index 68a25bc3b..ebae5ce86 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java @@ -6,4 +6,12 @@ public class StarBattleRegion extends GridRegion{ public StarBattleRegion() { super(); } + + public StarBattleRegion copy() { + StarBattleRegion copy = new StarBattleRegion(); + for (StarBattleCell c: regionCells) { + copy.addCell(c.copy()); + } + return copy; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index 33f148e94..1565bff9d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -6,9 +6,6 @@ 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.nurikabe.NurikabeBoard; -import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -import edu.rpi.legup.puzzle.nurikabe.rules.NoNumberContradictionRule; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; From 1f72e6ab5d9a9532fc76cbf6fe74b44b5eaca8e1 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:03:49 -0400 Subject: [PATCH 24/74] Changed some elements in puzzles and added importer --- puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml | 2 +- puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml | 2 +- .../edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java | 0 .../java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java | 2 -- 4 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml index 4d9f8b631..ce1330437 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml @@ -1,7 +1,7 @@ - + diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml index cd34805e5..10eb04aa3 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml @@ -1,7 +1,7 @@ - + diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index b79b7743a..fdf0df221 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -7,8 +7,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; -import edu.rpi.legup.puzzle.nurikabe.NurikabeView; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleController; From be30904d9a2e0b9a7b9dce1deb0b8ec65b29bca6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 12 Mar 2024 17:10:49 -0400 Subject: [PATCH 25/74] Blackout rule check method --- .../starbattle/rules/BlackoutDirectRule.java | 16 ++++++++++++++++ .../starbattle/rules/SurroundStarDirectRule.java | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java index 3c89b0800..57a5688f2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -2,6 +2,7 @@ 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.rules.DirectRule; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; @@ -30,6 +31,21 @@ public BlackoutDirectRule() { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + StarBattleBoard origBoard = (StarBattleBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new TooManyStarsContradictionRule(); + + StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); + + if (cell.getType() != StarBattleCellType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + + StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); + modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR); + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { + return "Black cells must be placed in a row, region, or column with enough stars!"; + } return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index 1565bff9d..0c46da09c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -41,7 +41,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); - //TODO: please for the love of god make a copy method for star battle board because this isn't actually going to work otherwise modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR); if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { return "Black cells must be placed adjacent to a star!"; From f37f4ef5d78a8e675272c351b11f899fea97b3db Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 12 Mar 2024 17:16:49 -0400 Subject: [PATCH 26/74] FinishWithStars rule check method --- .../rules/FinishWithStarsDirectRule.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java index d2711eced..53ab8d947 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -2,6 +2,7 @@ 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.rules.DirectRule; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; @@ -30,6 +31,21 @@ public FinishWithStarsDirectRule() { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + StarBattleBoard origBoard = (StarBattleBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new TooFewStarsContradictionRule(); + + StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); + + if (cell.getType() != StarBattleCellType.STAR) { + return "Only star cells are allowed for this rule!"; + } + + StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); + modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.BLACK); + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { + return "Star cells must be placed in a row, region, or column without extra spaces!"; + } return null; } From 9fe76cbb915f25fd098e0a3533d068b26d3662d1 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 12 Mar 2024 17:20:37 -0400 Subject: [PATCH 27/74] Deleted Add Star Case Rule, started on Clashing Orbit --- .../starbattle/rules/AddStarCaseRule.java | 57 ------------------- .../rules/ClashingOrbitContradictionRule.java | 25 ++++++++ .../rules/TooManyStarsContradictionRule.java | 5 ++ 3 files changed, 30 insertions(+), 57 deletions(-) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java deleted file mode 100644 index d945f5be9..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/AddStarCaseRule.java +++ /dev/null @@ -1,57 +0,0 @@ -package edu.rpi.legup.puzzle.starbattle.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.lightup.LightUpBoard; -import edu.rpi.legup.puzzle.lightup.LightUpCell; -import edu.rpi.legup.puzzle.starbattle.*; - -import java.awt.*; -import java.util.*; -import java.util.List; - -public class AddStarCaseRule extends CaseRule { - public AddStarCaseRule() { - super("STBL-CASE-0001", - "Add Star", - "Different ways a region's star number can be satisfied", - ""); - } - - @Override - public CaseBoard getCaseBoard(Board board) { - StarBattleBoard starbattleBoard = (StarBattleBoard) board.copy(); - CaseBoard caseBoard = new CaseBoard(starbattleBoard, this); - - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement the cell to determine the possible cases for - * @return a list of elements the specified spot could be - */ - @Override - public List getCases(Board board, PuzzleElement puzzleElement) { - StarBattleBoard starbattleBoard = (StarBattleBoard) board; - // take selected spot, check if there are any stars in that row, column, and region - - // do we want all possible cases, that seems like a lot - return null; - } - - @Override - public String checkRuleRaw(TreeTransition transition) { - return null; - } - - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java index 2eb584ea1..c15593796 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java @@ -9,6 +9,8 @@ import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; +import java.awt.*; + public class ClashingOrbitContradictionRule extends ContradictionRule { public ClashingOrbitContradictionRule() { @@ -28,6 +30,29 @@ public ClashingOrbitContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + StarBattleBoard starbattleBoard = (StarBattleBoard) board; + StarBattleCell cell = (StarBattleCell) starbattleBoard.getPuzzleElement(puzzleElement); + + if (cell.getType() != StarBattleCellType.STAR) { + return super.getNoContradictionMessage(); + } + + // check neighboring cells for a star + Point location = cell.getLocation(); + + int rowStart = Math.max( location.x - 1, 0 ); + int rowEnd = Math.min( location.x + 1, starbattleBoard.getSize() - 1 ); + int colStart = Math.max( location.y - 1, 0 ); + int colEnd = Math.min( location.y + 1, starbattleBoard.getSize() - 1 ); + + for (int row = rowStart; rowStart <= rowEnd; row++) { + for (int col = colStart; colStart <= colEnd; col++) { + if (starbattleBoard.getCell(row, col).getType() == StarBattleCellType.STAR) { + return null; + } + } + } + return super.getNoContradictionMessage(); } } \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index 56cd71ba7..09f6e8136 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -28,6 +28,11 @@ public TooManyStarsContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + // check row + + // check column + + // check region return super.getNoContradictionMessage(); } } \ No newline at end of file From 78069d4600b81872eb365df557a1d0dac7d2d875 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:26:48 -0400 Subject: [PATCH 28/74] Added importer --- .../puzzle/starbattle/StarBattleImporter.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java index e69de29bb..161b03bf0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java @@ -0,0 +1,110 @@ +package edu.rpi.legup.puzzle.starbattle; + +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.*; +import java.awt.Point; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class StarBattleImporter extends PuzzleImporter{ + + + public StarBattleImporter(StarBattle starbattle) { + super(starbattle); + } + + private Map regionsMap; + + /** + * Puzzle setting to support row and column inputs + */ + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + /** + * Puzzle setting to disable support for text input + */ + @Override + public boolean acceptsTextInput() { + return false; + } + + + /** + * Constructs empty StarBattle gameboard as per the provided dimensions + * @param rows number of rows and columns for the gameboard + */ + @Override + public void initializeBoard(int rows, int columns) { + StarBattleBoard StarBattleBoard = new StarBattleBoard(rows); + puzzle.setCurrentBoard(StarBattleBoard); + } + + + + /** + * Constructs StarBattle gameboard + * @param node xml document node + * @throws InvalidFileFormatException if file is invalid + */ + @Override + public void initializeBoard(Node node) throws InvalidFileFormatException { + Element puzzleElement = (Element) node; + + NodeList regionNodes = puzzleElement.getElementsByTagName("region"); + if (regionNodes.getLength() == 0) { + throw new InvalidFileFormatException("No regions found for the StarBattle puzzle"); + } + + int size = Integer.parseInt(puzzleElement.getAttribute("size")); + + StarBattleBoard StarBattleBoard = new StarBattleBoard(size); // Initialize the board with width and height from XML + + for (int i = 0; i < regionNodes.getLength(); i++) { + Element regionElement = (Element) regionNodes.item(i); + NodeList cellNodes = regionElement.getElementsByTagName("cell"); + + for (int j = 0; j < cellNodes.getLength(); j++) { + Element cellElement = (Element) cellNodes.item(j); + int x = Integer.parseInt(cellElement.getAttribute("x")); + int y = Integer.parseInt(cellElement.getAttribute("y")); + int value = Integer.parseInt(cellElement.getAttribute("value")); + + Point cellPoint = new Point(x, y); + + // Create the StarBattleCell with the cell type and value + StarBattleCell cell = new StarBattleCell(value, cellPoint, i, size); + cell.setIndex(y * size + x); // Calculate the index based on size + cell.setModifiable(true); + + // Add the cell to the board + StarBattleBoard.setCell(x, y, cell); + } + } + + puzzle.setCurrentBoard(StarBattleBoard); + } + + + + /** + * Initialize board via string of statements. + * @throws UnsupportedOperationException since StarBattle does not support text input + */ + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Ripple Effect does not accept text input"); + } +} + + + From 4b75d469117c407b38fed10edcfc090618ecdc3b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 15 Mar 2024 16:18:53 -0400 Subject: [PATCH 29/74] Fix clashing orbit bugs --- .../starbattle/rules/ClashingOrbitContradictionRule.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java index c15593796..79453803b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java @@ -45,9 +45,10 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { int colStart = Math.max( location.y - 1, 0 ); int colEnd = Math.min( location.y + 1, starbattleBoard.getSize() - 1 ); - for (int row = rowStart; rowStart <= rowEnd; row++) { - for (int col = colStart; colStart <= colEnd; col++) { - if (starbattleBoard.getCell(row, col).getType() == StarBattleCellType.STAR) { + for (int row = rowStart; row <= rowEnd; row++) { + for (int col = colStart; col <= colEnd; col++) { + if (starbattleBoard.getCell(row, col).getType() == StarBattleCellType.STAR + && (row != location.x || col != location.y)) { return null; } } From 900854f83aa48368507d4a569b21eb27376cb698 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 15 Mar 2024 16:31:01 -0400 Subject: [PATCH 30/74] Add puzzle number field to board --- .../edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index 143fbaca6..dedcc0a58 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -8,12 +8,14 @@ public class StarBattleBoard extends GridBoard { private int size; + private int puzzleNum; protected List regions; //private ArrayList groupSizes; - public StarBattleBoard(int size) { + public StarBattleBoard(int size, int num) { super(size, size); this.size = size; + this.puzzleNum = num; this.regions = new ArrayList<>(); } @@ -39,6 +41,8 @@ public Set getRow(int rowNum) { return row; } + public int getPuzzleNumber() { return puzzleNum; } + public Set getCol(int colNum) { Set column = new HashSet<>(); for (int i = 0; i < size; i++) { @@ -55,7 +59,7 @@ public StarBattleRegion getRegion(int index) { } public StarBattleBoard copy() { - StarBattleBoard copy = new StarBattleBoard(size); + StarBattleBoard copy = new StarBattleBoard(size, puzzleNum); 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()); From e0db10322ebcd8bbb74678c972453db1e4578498 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 15 Mar 2024 16:57:22 -0400 Subject: [PATCH 31/74] Contradiction check function for too few stars --- .../puzzle/starbattle/StarBattleBoard.java | 4 +++ .../puzzle/starbattle/StarBattleCell.java | 2 ++ .../rules/TooFewStarsContradictionRule.java | 31 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index dedcc0a58..cebe7d2db 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -58,6 +58,10 @@ public StarBattleRegion getRegion(int index) { return regions.get(index); } + public StarBattleRegion getRegion(StarBattleCell cell) { + return getRegion(cell.getGroupIndex()); + } + public StarBattleBoard copy() { StarBattleBoard copy = new StarBattleBoard(size, puzzleNum); for (int x = 0; x < this.dimension.width; x++) { diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index 2e89649e2..4296c8230 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -24,6 +24,8 @@ public StarBattleCell(int value, Point location, int groupIndex, int size) { this.max = size; } + public int getGroupIndex() { return groupIndex; } + @Override public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java index a063b2b6b..af30a5e18 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java @@ -8,6 +8,9 @@ import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; +import edu.rpi.legup.puzzle.starbattle.StarBattleRegion; + +import java.awt.*; public class TooFewStarsContradictionRule extends ContradictionRule { @@ -28,6 +31,34 @@ public TooFewStarsContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + StarBattleBoard sbBoard = (StarBattleBoard) board; + StarBattleCell cell = (StarBattleCell) puzzleElement; + Point location = cell.getLocation(); + int column = location.x; + int row = location.y; + int rowCount = 0; + int columnCount = 0; + for (int i = 0; i < sbBoard.getSize(); ++i) { + if (sbBoard.getCell(row, i).getType() != StarBattleCellType.BLACK) { + ++rowCount; + } + if (sbBoard.getCell(i, column).getType() != StarBattleCellType.BLACK) { + ++columnCount; + } + } + if (rowCount < sbBoard.getPuzzleNumber() || columnCount < sbBoard.getPuzzleNumber()) { + return null; + } + StarBattleRegion region = sbBoard.getRegion(cell); + int regionCount = 0; + for (StarBattleCell c: region.getCells()) { + if (c.getType() != StarBattleCellType.BLACK) { + ++regionCount; + } + } + if (regionCount < sbBoard.getPuzzleNumber()) { + return null; + } return super.getNoContradictionMessage(); } } \ No newline at end of file From e8375dbbe4531fc1589b38bdeb402f013969649b Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:10:58 -0400 Subject: [PATCH 32/74] Changed importer and xml files to include puzzleNum --- .../6x6 Star Battle 1star Normal1.xml | 1 + .../6x6 Star Battle 1star Normal2.xml | 1 + .../puzzle/starbattle/StarBattleBoard.java | 7 ++++++ .../puzzle/starbattle/StarBattleImporter.java | 24 ++++++++++--------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml index ce1330437..4163e15a9 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml @@ -1,6 +1,7 @@ + diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml index 10eb04aa3..29307fb58 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml @@ -1,6 +1,7 @@ + diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index dedcc0a58..e80e49399 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -17,6 +17,9 @@ public StarBattleBoard(int size, int num) { this.size = size; this.puzzleNum = num; this.regions = new ArrayList<>(); + for (int i = 0; i < size; i++) { + regions.add(new StarBattleRegion()); + } } @Override @@ -58,6 +61,10 @@ public StarBattleRegion getRegion(int index) { return regions.get(index); } + public void setRegion(int regionNum, StarBattleRegion region) { + regions.set(regionNum, region); + } + public StarBattleBoard copy() { StarBattleBoard copy = new StarBattleBoard(size, puzzleNum); for (int x = 0; x < this.dimension.width; x++) { diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java index 161b03bf0..32090172e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java @@ -13,7 +13,7 @@ import java.util.Map; -public class StarBattleImporter extends PuzzleImporter{ +public class StarBattleImporter extends PuzzleImporter{ public StarBattleImporter(StarBattle starbattle) { @@ -38,14 +38,14 @@ public boolean acceptsTextInput() { return false; } - - /** + /** * Constructs empty StarBattle gameboard as per the provided dimensions * @param rows number of rows and columns for the gameboard */ @Override public void initializeBoard(int rows, int columns) { - StarBattleBoard StarBattleBoard = new StarBattleBoard(rows); + int puzzle_num = 1; + StarBattleBoard StarBattleBoard = new StarBattleBoard(rows, puzzle_num); puzzle.setCurrentBoard(StarBattleBoard); } @@ -59,19 +59,19 @@ public void initializeBoard(int rows, int columns) { @Override public void initializeBoard(Node node) throws InvalidFileFormatException { Element puzzleElement = (Element) node; - + int puzzle_num = Integer.parseInt(puzzleElement.getAttribute("puzzle_num")); NodeList regionNodes = puzzleElement.getElementsByTagName("region"); - if (regionNodes.getLength() == 0) { - throw new InvalidFileFormatException("No regions found for the StarBattle puzzle"); - } - int size = Integer.parseInt(puzzleElement.getAttribute("size")); + if (regionNodes.getLength() != size) { + throw new InvalidFileFormatException("Not the current amount of regions in the puzzle."); + } - StarBattleBoard StarBattleBoard = new StarBattleBoard(size); // Initialize the board with width and height from XML + StarBattleBoard StarBattleBoard = new StarBattleBoard(size, puzzle_num); // Initialize the board with width and height from XML for (int i = 0; i < regionNodes.getLength(); i++) { Element regionElement = (Element) regionNodes.item(i); NodeList cellNodes = regionElement.getElementsByTagName("cell"); + StarBattleRegion region_i = new StarBattleRegion(); for (int j = 0; j < cellNodes.getLength(); j++) { Element cellElement = (Element) cellNodes.item(j); @@ -88,7 +88,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { // Add the cell to the board StarBattleBoard.setCell(x, y, cell); + region_i.addCell(cell); } + StarBattleBoard.setRegion(i, region_i); } puzzle.setCurrentBoard(StarBattleBoard); @@ -102,7 +104,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { */ @Override public void initializeBoard(String[] statements) throws UnsupportedOperationException { - throw new UnsupportedOperationException("Ripple Effect does not accept text input"); + throw new UnsupportedOperationException("Star Battle does not accept text input"); } } From 52c1aee25da3e80586cbdba537be411fa798fff9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 15 Mar 2024 17:27:54 -0400 Subject: [PATCH 33/74] Rule check method for star or empty case rule --- .../starbattle/rules/StarOrEmptyCaseRule.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java index a30823faa..1bbbe3e92 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java @@ -5,11 +5,14 @@ import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; import java.util.ArrayList; +import java.util.List; public class StarOrEmptyCaseRule extends CaseRule { @@ -29,7 +32,29 @@ public StarOrEmptyCaseRule() { */ @Override public String checkRuleRaw(TreeTransition transition) { - //TODO: implement this + 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."; + } + + StarBattleCell mod1 = (StarBattleCell) case1.getBoard().getModifiedData().iterator().next(); + StarBattleCell mod2 = (StarBattleCell) 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() == StarBattleCellType.STAR && mod2.getType() == StarBattleCellType.BLACK) || + (mod2.getType() == StarBattleCellType.STAR && mod1.getType() == StarBattleCellType.BLACK))) { + return super.getInvalidUseOfRuleMessage() + ": This case rule must create a star cell and a black cell."; + } + return null; } From 08492e146af252e83bc9737cba59ebae996df286 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:40:46 -0400 Subject: [PATCH 34/74] Added exporter --- .../puzzle/starbattle/StarBattleExporter.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java new file mode 100644 index 000000000..dcf149d0f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java @@ -0,0 +1,36 @@ +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.PuzzleExporter; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import org.w3c.dom.Document; + +public class StarBattleExporter extends PuzzleExporter { + public StarBattleExporter(StarBattle starbattle) { + super(starbattle); + } + + @Override + protected org.w3c.dom.Element createBoardElement(Document newDocument) { + StarBattleBoard board = (StarBattleBoard) puzzle.getTree().getRootNode().getBoard(); + org.w3c.dom.Element boardElement = newDocument.createElement("board"); + boardElement.setAttribute("size", String.valueOf(board.getSize())); + boardElement.setAttribute("puzzle_num", String.valueOf(board.getPuzzleNumber())); + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + org.w3c.dom.Element regionsElement = newDocument.createElement("region"); + StarBattleRegion region = (StarBattleRegion) puzzleElement; + org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); + for (PuzzleElement puzzleElement : region.getPuzzleElements()) { + StarBattleCell cell = (StarBattleCell) puzzleElement; + if (cell.getData() != -2) { + org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + cellsElement.appendChild(cellElement); + } + regionsElement.appendChild(cellsElement); + } + boardElement.appendChild(regionsElement); + } + + return boardElement; + } +} From cbe163513b4897bee86a96a209122199b65413be Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 15 Mar 2024 20:17:17 -0400 Subject: [PATCH 35/74] Updated TooManyStarsContradictionRule --- .../rules/ClashingOrbitContradictionRule.java | 1 + .../rules/TooManyStarsContradictionRule.java | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java index 79453803b..28398143e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java @@ -33,6 +33,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { StarBattleBoard starbattleBoard = (StarBattleBoard) board; StarBattleCell cell = (StarBattleCell) starbattleBoard.getPuzzleElement(puzzleElement); + // Contradiction rule can only be applied to cells with a star in it if (cell.getType() != StarBattleCellType.STAR) { return super.getNoContradictionMessage(); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index 09f6e8136..9165a2b1a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -5,11 +5,17 @@ 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.lightup.LightUpCell; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; +import edu.rpi.legup.puzzle.starbattle.StarBattleRegion; + +import java.awt.*; +import java.util.List; public class TooManyStarsContradictionRule extends ContradictionRule { + private final String INVALID_USE_MESSAGE = "Contradiction must be a cell containing a star."; public TooManyStarsContradictionRule() { super("STBL-CONT-0001", @@ -28,11 +34,68 @@ public TooManyStarsContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + StarBattleBoard starbattleBoard = (StarBattleBoard) board; + StarBattleCell cell = (StarBattleCell) starbattleBoard.getPuzzleElement(puzzleElement); + Point location = cell.getLocation(); + int starCount = 0; + int puzzleNum = starbattleBoard.getPuzzleNumber(); + + if (cell.getType() != StarBattleCellType.STAR) { + return super.getNoContradictionMessage(); + } + // check row + for (int i = location.x - 1; i >= 0; i--) { // to the left of selected cell + StarBattleCell check = starbattleBoard.getCell(i, location.y); + if (check.getType() == StarBattleCellType.STAR) { + starCount++; + if (starCount >= puzzleNum) { + break; + } + } + } + + for (int i = location.x + 1; i < starbattleBoard.getWidth(); i++) { // to the right of selected cell + StarBattleCell check = starbattleBoard.getCell(i, location.y); + if (check.getType() == StarBattleCellType.STAR) { + starCount++; + if (starCount >= puzzleNum) { + break; + } + } + } // check column + starCount = 0; + for (int j = location.y - 1; j >= 0; j--) { // above selected cell + StarBattleCell check = starbattleBoard.getCell(location.x, j); + if (check.getType() == StarBattleCellType.STAR) { + starCount++; + if (starCount >= puzzleNum) { + break; + } + } + } + + for (int j = location.y + 1; j < starbattleBoard.getWidth(); j++) { // below selected cell + StarBattleCell check = starbattleBoard.getCell(location.x, j); + if (check.getType() == StarBattleCellType.STAR) { + starCount++; + if (starCount >= puzzleNum) { + break; + } + } + } // check region + starCount = 0; + List reg; + /* + for (go through list of regions) { + check all spots in region + } + */ + return super.getNoContradictionMessage(); } } \ No newline at end of file From dd8b3fef0047ce78fefb691bd5e87885ac47645b Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 19 Mar 2024 16:39:38 -0400 Subject: [PATCH 36/74] added region check to TooManyStarsContradictionRule --- .../rules/TooManyStarsContradictionRule.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index 9165a2b1a..a3044a8cb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -15,7 +15,7 @@ import java.util.List; public class TooManyStarsContradictionRule extends ContradictionRule { - private final String INVALID_USE_MESSAGE = "Contradiction must be a cell containing a star."; + private final String INVALID_USE_MESSAGE = "Contradiction must be applied to a cell containing a star."; public TooManyStarsContradictionRule() { super("STBL-CONT-0001", @@ -39,9 +39,10 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Point location = cell.getLocation(); int starCount = 0; int puzzleNum = starbattleBoard.getPuzzleNumber(); + boolean valid = false; if (cell.getType() != StarBattleCellType.STAR) { - return super.getNoContradictionMessage(); + return INVALID_USE_MESSAGE; } // check row @@ -50,6 +51,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { + valid = true; break; } } @@ -60,6 +62,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { + valid = true; break; } } @@ -72,6 +75,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { + valid = true; break; } } @@ -82,6 +86,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { + valid = true; break; } } @@ -89,13 +94,20 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { // check region starCount = 0; - List reg; - /* - for (go through list of regions) { - check all spots in region + StarBattleRegion reg = starbattleBoard.getRegion(cell); + List cellList = reg.getCells(); // list of cells + for (int k = 0; k < cellList.size(); k++) { + if (cellList.get(k) == cell) { + continue; + } else if (cellList.get(k).getType() == StarBattleCellType.STAR) { + valid = true; + break; + } } - */ - return super.getNoContradictionMessage(); + if (valid == true) { + return super.getNoContradictionMessage(); + } + return null; } } \ No newline at end of file From 2555896041d62b86b86656145cce6fde2106492a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 19 Mar 2024 16:41:27 -0400 Subject: [PATCH 37/74] Split up row/column and region containment rules into separate rules for rows and columns --- .../puzzle/starbattle/StarBattleBoard.java | 2 +- .../rules/ColumnsWithinRegionsDirectRule.java | 43 +++++++++++++++++++ .../rules/FinishWithStarsDirectRule.java | 2 +- ...va => RegionsWithinColumnsDirectRule.java} | 9 ++-- .../rules/RegionsWithinRowsDirectRule.java | 43 +++++++++++++++++++ ....java => RowsWithinRegionsDirectRule.java} | 9 ++-- .../rules/SurroundStarDirectRule.java | 2 +- .../rules/starbattle_reference_sheet.txt | 10 +++-- 8 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java rename src/main/java/edu/rpi/legup/puzzle/starbattle/rules/{RegionsWithinRowsColumnsDirectRule.java => RegionsWithinColumnsDirectRule.java} (81%) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java rename src/main/java/edu/rpi/legup/puzzle/starbattle/rules/{RowsColumnsWithinRegionsDirectRule.java => RowsWithinRegionsDirectRule.java} (82%) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index a2a93ff8e..7accc86e3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -60,7 +60,7 @@ public StarBattleRegion getRegion(int index) { } return regions.get(index); } - + public StarBattleRegion getRegion(StarBattleCell cell) { return getRegion(cell.getGroupIndex()); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java new file mode 100644 index 000000000..05c9a2868 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -0,0 +1,43 @@ +package edu.rpi.legup.puzzle.starbattle.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; + +public class ColumnsWithinRegionsDirectRule extends DirectRule { + public ColumnsWithinRegionsDirectRule() { + super("STBL-BASC-0002", + "Columns Within Regions", + "If a number of columns is fully contained by an equal number of regions, spaces of other columns in those regions must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java index 53ab8d947..13050a5ac 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -13,7 +13,7 @@ public class FinishWithStarsDirectRule extends DirectRule { public FinishWithStarsDirectRule() { - super("STBL-BASC-0002", + super("STBL-BASC-0003", "Finish With Stars", "Unknown spaces must be stars if there are just enough in a row, column, or region to satisfy the puzzle number.", "INSERT IMAGE NAME HERE"); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java similarity index 81% rename from src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java rename to src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java index 81645a2ad..84787e704 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java @@ -5,13 +5,10 @@ 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.starbattle.StarBattleBoard; -import edu.rpi.legup.puzzle.starbattle.StarBattleCell; -import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; -public class RegionsWithinRowsColumnsDirectRule extends DirectRule { - public RegionsWithinRowsColumnsDirectRule() { - super("STBL-BASC-0003", +public class RegionsWithinColumnsDirectRule extends DirectRule { + public RegionsWithinColumnsDirectRule() { + super("STBL-BASC-0004", "Regions Within Rows/Columns", "If a number of regions is fully contained by an equal number of rows or columns, spaces of other regions in those rows or columns must be black.", "INSERT IMAGE NAME HERE"); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java new file mode 100644 index 000000000..e71c97df9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java @@ -0,0 +1,43 @@ +package edu.rpi.legup.puzzle.starbattle.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; + +public class RegionsWithinRowsDirectRule extends DirectRule { + public RegionsWithinRowsDirectRule() { + super("STBL-BASC-0005", + "Regions Within Rows/Columns", + "If a number of regions is fully contained by an equal number of rows or columns, spaces of other regions in those rows or columns must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java similarity index 82% rename from src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java rename to src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java index 53a7b27ee..128c44095 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java @@ -5,13 +5,10 @@ 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.starbattle.StarBattleBoard; -import edu.rpi.legup.puzzle.starbattle.StarBattleCell; -import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; -public class RowsColumnsWithinRegionsDirectRule extends DirectRule { - public RowsColumnsWithinRegionsDirectRule() { - super("STBL-BASC-0004", +public class RowsWithinRegionsDirectRule extends DirectRule { + public RowsWithinRegionsDirectRule() { + super("STBL-BASC-0006", "Rows/Columns Within Regions", "If a number of rows or columns is fully contained by an equal number of regions, spaces of other rows or columns, respectively, in those regions must be black.", "INSERT IMAGE NAME HERE"); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index 0c46da09c..a5533104c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -13,7 +13,7 @@ public class SurroundStarDirectRule extends DirectRule { public SurroundStarDirectRule() { - super("STBL-BASC-0005", + super("STBL-BASC-0007", "Surround Star", "Any space adjacent to a star must be black.", "INSERT IMAGE NAME HERE"); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt index 70e81bb73..958440a71 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt @@ -4,10 +4,12 @@ Star or Empty: STBL-CASE-0002 Basic Rules: Blackout: STBL-BASC-0001 -Finish With Stars: STBL-BASC-0002 -Regions Within Rows or Columns: STBL-BASC-0003 -Rows or Columns Within Regions: STBL-BASC-0004 -Surround Star: STBL-BASC-0005 +Columns Within Regions: STBL-BASC-0002 +Finish With Stars: STBL-BASC-0003 +Regions Within Columns: STBL-BASC-0004 +Regions Within Rows: STBL-BASC-0005 +Rows Within Regions: STBL-BASC-0006 +Surround Star: STBL-BASC-0007 Contradiction Rules: Too Many Stars: STBL-CONT-0001 From 489f31d4c53180d2fe7e0d61396fb13d8e99df51 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:09:49 -0400 Subject: [PATCH 38/74] Create StarBattleCellFactory.java --- .../edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java new file mode 100644 index 000000000..e69de29bb From 7674f2225dc23ba409b56a76f5e7ec939dc6f3e8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 19 Mar 2024 17:19:19 -0400 Subject: [PATCH 39/74] Start implementing columns within regions direct rule --- .../puzzle/starbattle/StarBattleBoard.java | 11 +++++++ .../puzzle/starbattle/StarBattleRegion.java | 9 ++++++ .../rules/ColumnsWithinRegionsDirectRule.java | 29 ++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index 7accc86e3..764e952ea 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -69,6 +69,17 @@ public void setRegion(int regionNum, StarBattleRegion region) { regions.set(regionNum, region); } + public int columnStars(int columnIndex) { + int stars = 0; + if (columnIndex < size) { + for (StarBattleCell c: this.getCol(columnIndex)) { + if (c.getType() == StarBattleCellType.STAR) + ++stars; + } + } + return stars; + } + public StarBattleBoard copy() { StarBattleBoard copy = new StarBattleBoard(size, puzzleNum); for (int x = 0; x < this.dimension.width; x++) { diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java index ebae5ce86..099cabfcd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java @@ -14,4 +14,13 @@ public StarBattleRegion copy() { } return copy; } + + public int numStars() { + int stars = 0; + for (StarBattleCell c: regionCells) { + if (c.getType() == StarBattleCellType.STAR) + ++stars; + } + return stars; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java index 05c9a2868..efd8b47ca 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -1,10 +1,16 @@ package edu.rpi.legup.puzzle.starbattle.rules; import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.GridRegion; 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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +import java.util.HashSet; public class ColumnsWithinRegionsDirectRule extends DirectRule { public ColumnsWithinRegionsDirectRule() { @@ -25,7 +31,28 @@ public ColumnsWithinRegionsDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); + if (cell.getType() != StarBattleCellType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + //which columns are within the region + HashSet columns = new HashSet(); + int columnStars = 0; + for (PuzzleElement r: board.getRegion(cell).getCells()) { + columns.add(((StarBattleCell) r).getLocation().x); + } + //which regions are the columns within + HashSet regions = new HashSet(); + int regionStars = 0; + for (Integer c: columns) { + for (int i = 0; i < board.getSize(); ++i) { + if (regions.add(board.getCell(c,i).getGroupIndex())) { + regionStars += board.getRegion(board.getCell(c,i)).numStars(); + } + } + } + // are the columns and regions missing an equal amount of stars return null; } From 53a5004f00ab5e81a954c89223e98c412559bf87 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 19 Mar 2024 17:28:54 -0400 Subject: [PATCH 40/74] Continue columns within regions --- .../legup/puzzle/starbattle/StarBattleBoard.java | 11 +++++++++++ .../rules/ColumnsWithinRegionsDirectRule.java | 14 +++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index 764e952ea..ce04f31a6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -80,6 +80,17 @@ public int columnStars(int columnIndex) { return stars; } + public int rowStars(int rowIndex) { + int stars = 0; + if (rowIndex < size) { + for (StarBattleCell c: this.getRow(rowIndex)) { + if (c.getType() == StarBattleCellType.STAR) + ++stars; + } + } + return stars; + } + public StarBattleBoard copy() { StarBattleBoard copy = new StarBattleBoard(size, puzzleNum); for (int x = 0; x < this.dimension.width; x++) { diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java index efd8b47ca..086a8f82e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -11,6 +11,7 @@ import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; import java.util.HashSet; +import java.util.Set; public class ColumnsWithinRegionsDirectRule extends DirectRule { public ColumnsWithinRegionsDirectRule() { @@ -31,19 +32,23 @@ public ColumnsWithinRegionsDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + //TODO: fix this StarBattleBoard board = (StarBattleBoard) transition.getBoard(); StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); if (cell.getType() != StarBattleCellType.BLACK) { return "Only black cells are allowed for this rule!"; } //which columns are within the region - HashSet columns = new HashSet(); + Set columns = new HashSet(); int columnStars = 0; for (PuzzleElement r: board.getRegion(cell).getCells()) { - columns.add(((StarBattleCell) r).getLocation().x); + int column = ((StarBattleCell) r).getLocation().x; + if (columns.add(column)) { + columnStars += board.columnStars(column); + } } //which regions are the columns within - HashSet regions = new HashSet(); + Set regions = new HashSet(); int regionStars = 0; for (Integer c: columns) { for (int i = 0; i < board.getSize(); ++i) { @@ -53,6 +58,9 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } } // are the columns and regions missing an equal amount of stars + if (board.getPuzzleNumber() * columns.size() - columnStars != board.getPuzzleNumber() * regions.size() - regionStars) { + return "The number of missing stars in the "; + } return null; } From eee926b4c51e049d07e8ba30618208979bb51b24 Mon Sep 17 00:00:00 2001 From: Sarah Date: Wed, 20 Mar 2024 13:52:28 -0400 Subject: [PATCH 41/74] reworked TooManyStars logic --- .../8x8 Star Battle 1star Normal1.xml | 10 ++++++ .../rules/TooManyStarsContradictionRule.java | 33 ++++++++++--------- 2 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml diff --git a/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml new file mode 100644 index 000000000..ba237102f --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index a3044a8cb..7777ff5a4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -39,30 +39,30 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Point location = cell.getLocation(); int starCount = 0; int puzzleNum = starbattleBoard.getPuzzleNumber(); - boolean valid = false; + boolean valid = true; if (cell.getType() != StarBattleCellType.STAR) { - return INVALID_USE_MESSAGE; + return this.INVALID_USE_MESSAGE; } // check row - for (int i = location.x - 1; i >= 0; i--) { // to the left of selected cell + for (int i = location.x - 1; i >= 0; i--) { StarBattleCell check = starbattleBoard.getCell(i, location.y); if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { - valid = true; + valid = false; break; } } } - for (int i = location.x + 1; i < starbattleBoard.getWidth(); i++) { // to the right of selected cell + for (int i = location.x + 1; i < starbattleBoard.getWidth(); i++) { StarBattleCell check = starbattleBoard.getCell(i, location.y); if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { - valid = true; + valid = false; break; } } @@ -70,23 +70,23 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { // check column starCount = 0; - for (int j = location.y - 1; j >= 0; j--) { // above selected cell + for (int j = location.y - 1; j >= 0; j--) { StarBattleCell check = starbattleBoard.getCell(location.x, j); if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { - valid = true; + valid = false; break; } } } - for (int j = location.y + 1; j < starbattleBoard.getWidth(); j++) { // below selected cell + for (int j = location.y + 1; j < starbattleBoard.getWidth(); j++) { StarBattleCell check = starbattleBoard.getCell(location.x, j); if (check.getType() == StarBattleCellType.STAR) { starCount++; if (starCount >= puzzleNum) { - valid = true; + valid = false; break; } } @@ -97,15 +97,16 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { StarBattleRegion reg = starbattleBoard.getRegion(cell); List cellList = reg.getCells(); // list of cells for (int k = 0; k < cellList.size(); k++) { - if (cellList.get(k) == cell) { - continue; - } else if (cellList.get(k).getType() == StarBattleCellType.STAR) { - valid = true; - break; + if (cellList.get(k).getType() == StarBattleCellType.STAR) { + starCount++; + if (starCount >= puzzleNum) { + valid = false; + break; + } } } - if (valid == true) { + if (valid == false) { return super.getNoContradictionMessage(); } return null; From 59c8d6f9563caff709e20e84a4bc15f8d04a5600 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:10:18 -0400 Subject: [PATCH 42/74] Exporter and Cell Factory started --- .../starbattle/StarBattleCellFactory.java | 66 +++++++++++++++++++ .../puzzle/starbattle/StarBattleExporter.java | 11 ++-- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java index e69de29bb..06c879836 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java @@ -0,0 +1,66 @@ +package edu.rpi.legup.puzzle.starbattle; +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.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.starbattle.starbattleBoard; +import edu.rpi.legup.puzzle.starbattle.starbattleCell; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.awt.*; + +public class StarBattleCellFactory extends ElementFactory { + @Override + public StarBattleCell importCell(Node node, Board board) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("cell")) { + throw new InvalidFileFormatException("starbattle Factory: unknown puzzleElement puzzleElement"); + } + + StarBattleBoard starbattleBoard = (StarBattleBoard) board; + int size = starbattleBoard.getSize(); + + 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()); + int groupIndex = Integer.valueOf(attributeList.getNamedItem("groupIndex").getNodeValue()); + if (x >= size || y >= size) { + throw new InvalidFileFormatException("starbattle Factory: cell location out of bounds"); + } + if (groupIndex >= size || groupIndex < 0) { + throw new InvalidFileFormatException("starbattle Factory: not in a valid region"); + } + if (value != 0) { //ALL INITIAL PUZZLES ARE BLANK, SUBJECT TO CHANGE + throw new InvalidFileFormatException("starbattle Factory: cell unknown value"); + } + + StarBattleCell cell = new StarBattleCell(value, new Point(x, y), groupIndex, size); + cell.setIndex(y * size + x); + return cell; + } + catch (NumberFormatException e1) { + throw new InvalidFileFormatException("starbattle Factory: unknown value where integer expected"); + } catch (NullPointerException e2) { + throw new InvalidFileFormatException("starbattle Factory: could not find attribute(s)"); + } + } + + public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { + org.w3c.dom.Element cellElement = document.createElement("cell"); + + NurikabeCell cell = (NurikabeCell) 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/starbattle/StarBattleExporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java index dcf149d0f..58fb41e63 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.starbattle; import edu.rpi.legup.model.PuzzleExporter; -import edu.rpi.legup.model.gameboard.PuzzleElement; import org.w3c.dom.Document; @@ -16,14 +15,12 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element boardElement = newDocument.createElement("board"); boardElement.setAttribute("size", String.valueOf(board.getSize())); boardElement.setAttribute("puzzle_num", String.valueOf(board.getPuzzleNumber())); - for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + for (StarBattleRegion sb_region : board.regions) { org.w3c.dom.Element regionsElement = newDocument.createElement("region"); - StarBattleRegion region = (StarBattleRegion) puzzleElement; org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); - for (PuzzleElement puzzleElement : region.getPuzzleElements()) { - StarBattleCell cell = (StarBattleCell) puzzleElement; - if (cell.getData() != -2) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); + for (StarBattleCell cell : sb_region.getCells()) { + if (cell.getData() == 0) { + org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, cell); cellsElement.appendChild(cellElement); } regionsElement.appendChild(cellsElement); From 9d5362b94680e212e7c0521e48de106e12ab021b Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:12:50 -0400 Subject: [PATCH 43/74] Update StarBattleCellFactory.java --- .../rpi/legup/puzzle/starbattle/StarBattleCellFactory.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java index 06c879836..827934111 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java @@ -2,9 +2,8 @@ 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.puzzle.nurikabe.NurikabeCell; -import edu.rpi.legup.puzzle.starbattle.starbattleBoard; -import edu.rpi.legup.puzzle.starbattle.starbattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.save.InvalidFileFormatException; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -54,7 +53,7 @@ public StarBattleCell importCell(Node node, Board board) throws InvalidFileForma public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { org.w3c.dom.Element cellElement = document.createElement("cell"); - NurikabeCell cell = (NurikabeCell) puzzleElement; + StarBattleCell cell = (StarBattleCell) puzzleElement; Point loc = cell.getLocation(); cellElement.setAttribute("value", String.valueOf(cell.getData())); From 0deae52edcb4cc5315db11626dc46f4d0d6697fe Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:26:53 -0400 Subject: [PATCH 44/74] Deleted extraneous libraries, and finished going thru guide of implementing puzzles Hopefully we get puzzles up soon. --- .../java/edu/rpi/legup/puzzle/starbattle/StarBattle.java | 8 ++++---- .../legup/puzzle/starbattle/StarBattleCellFactory.java | 4 ---- .../rpi/legup/puzzle/starbattle/StarBattleImporter.java | 9 --------- .../edu/rpi/legup/puzzle/starbattle/StarBattleView.java | 2 +- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java index 78800916e..760fa88b2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java @@ -5,12 +5,12 @@ public class StarBattle extends Puzzle { public StarBattle() { super(); - //this.name = "StarBattle"; + this.name = "StarBattle"; - //this.importer = new StarBattleImporter(this); - //this.exporter = new StarBattleExporter(this); + this.importer = new StarBattleImporter(this); + this.exporter = new StarBattleExporter(this); - //this.factory = new StarBattleCellFactory(); + this.factory = new StarBattleCellFactory(); } @Override diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java index 827934111..92ea8abf6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java @@ -2,14 +2,10 @@ 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.puzzle.starbattle.StarBattleBoard; -import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.save.InvalidFileFormatException; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import java.awt.*; diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java index 32090172e..fa0e065ee 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java @@ -5,12 +5,7 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.*; import java.awt.Point; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; public class StarBattleImporter extends PuzzleImporter{ @@ -20,8 +15,6 @@ public StarBattleImporter(StarBattle starbattle) { super(starbattle); } - private Map regionsMap; - /** * Puzzle setting to support row and column inputs */ @@ -49,8 +42,6 @@ public void initializeBoard(int rows, int columns) { puzzle.setCurrentBoard(StarBattleBoard); } - - /** * Constructs StarBattle gameboard * @param node xml document node diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index fdf0df221..a39770d85 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -31,7 +31,7 @@ public class StarBattleView extends GridBoardView { STAR = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/starbattle/star.png")); } catch (IOException e) { - LOGGER.error("Failed to open StarBattle images"); + LOGGER.error("Failed to open starbattle image."); } } From 2ba4382b9243a73a7bf1b58d91c8d1d79349bc07 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 22 Mar 2024 17:12:02 -0400 Subject: [PATCH 45/74] Implemented rule check function for region/row/column overlap rules --- .../rules/ColumnsWithinRegionsDirectRule.java | 46 ++++++--- .../rules/ColumnsWithinRowsDirectRule.java | 97 +++++++++++++++++++ .../rules/RegionsWithinColumnsDirectRule.java | 8 +- .../rules/RegionsWithinRowsDirectRule.java | 7 +- .../rules/RowsWithinColumnsDirectRule.java | 51 ++++++++++ .../rules/RowsWithinRegionsDirectRule.java | 55 ++++++++++- 6 files changed, 240 insertions(+), 24 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java index 086a8f82e..980f6db19 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -32,34 +32,50 @@ public ColumnsWithinRegionsDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - //TODO: fix this + //assumption: the rule has been applied to its fullest extent and the rows and regions + //are now mutually encompassing StarBattleBoard board = (StarBattleBoard) transition.getBoard(); StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); if (cell.getType() != StarBattleCellType.BLACK) { return "Only black cells are allowed for this rule!"; } - //which columns are within the region + //the columns that are contained Set columns = new HashSet(); - int columnStars = 0; - for (PuzzleElement r: board.getRegion(cell).getCells()) { - int column = ((StarBattleCell) r).getLocation().x; - if (columns.add(column)) { - columnStars += board.columnStars(column); - } - } - //which regions are the columns within + //the regions that contain them Set regions = new HashSet(); + //columns and regions to process + Set columnsToCheck = new HashSet(); + Set regionsToCheck = new HashSet(); + int columnStars = 0; int regionStars = 0; - for (Integer c: columns) { - for (int i = 0; i < board.getSize(); ++i) { - if (regions.add(board.getCell(c,i).getGroupIndex())) { - regionStars += board.getRegion(board.getCell(c,i)).numStars(); + regions.add(cell.getGroupIndex()); + regionsToCheck.add(cell.getGroupIndex()); + + while (!columnsToCheck.isEmpty() || !regionsToCheck.isEmpty()) { + for (int r: regionsToCheck) { + regionStars += board.getRegion(r).numStars(); + for (PuzzleElement c: board.getRegion(r).getCells()) { + int column = ((StarBattleCell) c).getLocation().x; + if (columns.add(column)) { + columnsToCheck.add(column); + } + } + regionsToCheck.remove(r); + } + for (int c: columnsToCheck) { + columnStars += board.columnStars(c); + for (int i = 0; i < board.getSize(); ++i) { + int region = board.getCell(c,i).getGroupIndex(); + if (regions.add(region)) { + regionsToCheck.add(region); + } } + columnsToCheck.remove(c); } } // are the columns and regions missing an equal amount of stars if (board.getPuzzleNumber() * columns.size() - columnStars != board.getPuzzleNumber() * regions.size() - regionStars) { - return "The number of missing stars in the "; + return "The number of missing stars in the columns and regions must be equal and every extraneous cell must be black!"; } return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java new file mode 100644 index 000000000..4c8eb6915 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java @@ -0,0 +1,97 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +import java.util.HashSet; +import java.util.Set; + +public class ColumnsWithinRowsDirectRule extends DirectRule { + + public ColumnsWithinRowsDirectRule() { + super("STBL-BASC-XXXX", + "Columns Within Rows", + "If a number of columns is fully contained by an equal number of rows, spaces of other columns in those rows must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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) { + + //assumption: the rule has been applied to its fullest extent and the rows and columns + //are now mutually encompassing + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); + if (cell.getType() != StarBattleCellType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + + //the columns that are contained + Set columns = new HashSet(); + //the rows that contain them + Set rows = new HashSet(); + //columns and rows to process + Set columnsToCheck = new HashSet(); + Set rowsToCheck = new HashSet(); + int columnStars = 0; + int rowStars = 0; + int firstRow = cell.getLocation().y; + rows.add(firstRow); + rowsToCheck.add(firstRow); + + while (!columnsToCheck.isEmpty() || !rowsToCheck.isEmpty()) { + for (int r: rowsToCheck) { + rowStars += board.rowStars(r); + for (PuzzleElement c: board.getRow(r)) { + int column = ((StarBattleCell) c).getLocation().x; + if (columns.add(column)) { + columnsToCheck.add(column); + } + } + rowsToCheck.remove(r); + } + for (int c: columnsToCheck) { + columnStars += board.columnStars(c); + for (PuzzleElement r: board.getCol(c)) { + int row = ((StarBattleCell) r).getLocation().y; + if (rows.add(row)) { + rowsToCheck.add(row); + } + } + columnsToCheck.remove(c); + } + } + // are the columns and regions missing an equal amount of stars + if (board.getPuzzleNumber() * columns.size() - columnStars != board.getPuzzleNumber() * rows.size() - rowStars) { + return "The number of missing stars in the columns and rows must be equal and every extraneous cell must be black!"; + } + return null; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java index 84787e704..74e296522 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java @@ -9,8 +9,8 @@ public class RegionsWithinColumnsDirectRule extends DirectRule { public RegionsWithinColumnsDirectRule() { super("STBL-BASC-0004", - "Regions Within Rows/Columns", - "If a number of regions is fully contained by an equal number of rows or columns, spaces of other regions in those rows or columns must be black.", + "Regions Within Columns", + "If a number of regions is fully contained by an equal number of columns, spaces of other regions in those columns must be black.", "INSERT IMAGE NAME HERE"); } @@ -25,8 +25,8 @@ public RegionsWithinColumnsDirectRule() { */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - - return null; + ColumnsWithinRegionsDirectRule correspondingRule = new ColumnsWithinRegionsDirectRule(); + return correspondingRule.checkRuleRawAt(transition, puzzleElement); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java index e71c97df9..f89e0d63f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java @@ -9,8 +9,8 @@ public class RegionsWithinRowsDirectRule extends DirectRule { public RegionsWithinRowsDirectRule() { super("STBL-BASC-0005", - "Regions Within Rows/Columns", - "If a number of regions is fully contained by an equal number of rows or columns, spaces of other regions in those rows or columns must be black.", + "Regions Within Rows", + "If a number of regions is fully contained by an equal number of rows, spaces of other regions in those rows must be black.", "INSERT IMAGE NAME HERE"); } @@ -26,7 +26,8 @@ public RegionsWithinRowsDirectRule() { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; + RowsWithinRegionsDirectRule correspondingRule = new RowsWithinRegionsDirectRule(); + return correspondingRule.checkRuleRawAt(transition, puzzleElement); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java new file mode 100644 index 000000000..48fb1cd81 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java @@ -0,0 +1,51 @@ +package edu.rpi.legup.puzzle.starbattle.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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +import java.util.HashSet; +import java.util.Set; + +public class RowsWithinColumnsDirectRule extends DirectRule { + + public RowsWithinColumnsDirectRule() { + super("STBL-BASC-XXXX", + "Rows Withing Columns", + "If a number of rows is fully contained by an equal number of columns, spaces of other rows in those columns must be black.", + "INSERT IMAGE NAME HERE"); + } + + /** + * 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) { + + ColumnsWithinRowsDirectRule correspondingRule = new ColumnsWithinRowsDirectRule(); + return correspondingRule.checkRuleRawAt(transition, puzzleElement); + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java index 128c44095..8cc68d292 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java @@ -5,12 +5,18 @@ 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.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; + +import java.util.HashSet; +import java.util.Set; public class RowsWithinRegionsDirectRule extends DirectRule { public RowsWithinRegionsDirectRule() { super("STBL-BASC-0006", - "Rows/Columns Within Regions", - "If a number of rows or columns is fully contained by an equal number of regions, spaces of other rows or columns, respectively, in those regions must be black.", + "Rows Within Regions", + "If a number of rows is fully contained by an equal number of regions, spaces of other rows in those regions must be black.", "INSERT IMAGE NAME HERE"); } @@ -26,6 +32,51 @@ public RowsWithinRegionsDirectRule() { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + //assumption: the rule has been applied to its fullest extent and the rows and regions + //are now mutually encompassing + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); + if (cell.getType() != StarBattleCellType.BLACK) { + return "Only black cells are allowed for this rule!"; + } + //the rows that are contained + Set rows = new HashSet(); + //the regions that contain them + Set regions = new HashSet(); + //rows and regions to process + Set rowsToCheck = new HashSet(); + Set regionsToCheck = new HashSet(); + int rowStars = 0; + int regionStars = 0; + regions.add(cell.getGroupIndex()); + regionsToCheck.add(cell.getGroupIndex()); + + while (!rowsToCheck.isEmpty() || !regionsToCheck.isEmpty()) { + for (int r: regionsToCheck) { + regionStars += board.getRegion(r).numStars(); + for (PuzzleElement ro: board.getRegion(r).getCells()) { + int row = ((StarBattleCell) ro).getLocation().y; + if (rows.add(row)) { + rowsToCheck.add(row); + } + } + regionsToCheck.remove(r); + } + for (int r: rowsToCheck) { + rowStars += board.rowStars(r); + for (int i = 0; i < board.getSize(); ++i) { + int region = board.getCell(i,r).getGroupIndex(); + if (regions.add(region)) { + regionsToCheck.add(region); + } + } + rowsToCheck.remove(r); + } + } + // are the columns and regions missing an equal amount of stars + if (board.getPuzzleNumber() * rows.size() - rowStars != board.getPuzzleNumber() * regions.size() - regionStars) { + return "The number of missing stars in the rows and regions must be equal and every extraneous cell must be black!"; + } return null; } From 49cd17e88546c0f43a14c9c5a4101c6e5a9bb84c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 22 Mar 2024 17:17:56 -0400 Subject: [PATCH 46/74] Minor changes to direct rules --- .../rules/ColumnsWithinRegionsDirectRule.java | 2 +- .../rules/ColumnsWithinRowsDirectRule.java | 4 ++-- .../starbattle/rules/FinishWithStarsDirectRule.java | 2 +- .../rules/RegionsWithinColumnsDirectRule.java | 4 ++-- .../rules/RegionsWithinRowsDirectRule.java | 4 ++-- .../rules/RowsWithinColumnsDirectRule.java | 4 ++-- .../rules/RowsWithinRegionsDirectRule.java | 4 ++-- .../starbattle/rules/SurroundStarDirectRule.java | 2 +- .../starbattle/rules/starbattle_reference_sheet.txt | 12 +++++++----- 9 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java index 980f6db19..93fb86dc5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -17,7 +17,7 @@ public class ColumnsWithinRegionsDirectRule extends DirectRule { public ColumnsWithinRegionsDirectRule() { super("STBL-BASC-0002", "Columns Within Regions", - "If a number of columns is fully contained by an equal number of regions, spaces of other columns in those regions must be black.", + "If a number of columns is fully contained by a number of regions with an equal number of missing stars, spaces of other columns in those regions must be black.", "INSERT IMAGE NAME HERE"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java index 4c8eb6915..389ba02c2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java @@ -15,9 +15,9 @@ public class ColumnsWithinRowsDirectRule extends DirectRule { public ColumnsWithinRowsDirectRule() { - super("STBL-BASC-XXXX", + super("STBL-BASC-0003", "Columns Within Rows", - "If a number of columns is fully contained by an equal number of rows, spaces of other columns in those rows must be black.", + "If a number of columns is fully contained by a number of rows with an equal number of missing stars, spaces of other columns in those rows must be black.", "INSERT IMAGE NAME HERE"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java index 13050a5ac..ed6a8a986 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -13,7 +13,7 @@ public class FinishWithStarsDirectRule extends DirectRule { public FinishWithStarsDirectRule() { - super("STBL-BASC-0003", + super("STBL-BASC-0004", "Finish With Stars", "Unknown spaces must be stars if there are just enough in a row, column, or region to satisfy the puzzle number.", "INSERT IMAGE NAME HERE"); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java index 74e296522..d2fa4fbe3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java @@ -8,9 +8,9 @@ public class RegionsWithinColumnsDirectRule extends DirectRule { public RegionsWithinColumnsDirectRule() { - super("STBL-BASC-0004", + super("STBL-BASC-0005", "Regions Within Columns", - "If a number of regions is fully contained by an equal number of columns, spaces of other regions in those columns must be black.", + "If a number of regions is fully contained by a number of columns with an equal number of missing stars, spaces of other regions in those columns must be black.", "INSERT IMAGE NAME HERE"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java index f89e0d63f..15993b85d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java @@ -8,9 +8,9 @@ public class RegionsWithinRowsDirectRule extends DirectRule { public RegionsWithinRowsDirectRule() { - super("STBL-BASC-0005", + super("STBL-BASC-0006", "Regions Within Rows", - "If a number of regions is fully contained by an equal number of rows, spaces of other regions in those rows must be black.", + "If a number of regions is fully contained by a number of rows with an equal number of missing stars, spaces of other regions in those rows must be black.", "INSERT IMAGE NAME HERE"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java index 48fb1cd81..2d6c148d0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java @@ -15,9 +15,9 @@ public class RowsWithinColumnsDirectRule extends DirectRule { public RowsWithinColumnsDirectRule() { - super("STBL-BASC-XXXX", + super("STBL-BASC-0007", "Rows Withing Columns", - "If a number of rows is fully contained by an equal number of columns, spaces of other rows in those columns must be black.", + "If a number of rows is fully contained by a number of columns with an equal number of missing stars, spaces of other rows in those columns must be black.", "INSERT IMAGE NAME HERE"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java index 8cc68d292..7dfaae59f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java @@ -14,9 +14,9 @@ public class RowsWithinRegionsDirectRule extends DirectRule { public RowsWithinRegionsDirectRule() { - super("STBL-BASC-0006", + super("STBL-BASC-0008", "Rows Within Regions", - "If a number of rows is fully contained by an equal number of regions, spaces of other rows in those regions must be black.", + "If a number of rows is fully contained by a number of regions with an equal number of missing stars, spaces of other rows in those regions must be black.", "INSERT IMAGE NAME HERE"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index a5533104c..0608d388f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -13,7 +13,7 @@ public class SurroundStarDirectRule extends DirectRule { public SurroundStarDirectRule() { - super("STBL-BASC-0007", + super("STBL-BASC-0009", "Surround Star", "Any space adjacent to a star must be black.", "INSERT IMAGE NAME HERE"); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt index 958440a71..f18965fd6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt @@ -5,11 +5,13 @@ Star or Empty: STBL-CASE-0002 Basic Rules: Blackout: STBL-BASC-0001 Columns Within Regions: STBL-BASC-0002 -Finish With Stars: STBL-BASC-0003 -Regions Within Columns: STBL-BASC-0004 -Regions Within Rows: STBL-BASC-0005 -Rows Within Regions: STBL-BASC-0006 -Surround Star: STBL-BASC-0007 +Columns Within Rows: STBL-BASC-0003 +Finish With Stars: STBL-BASC-0004 +Regions Within Columns: STBL-BASC-0005 +Regions Within Rows: STBL-BASC-0006 +Rows Within Columns: STBL-BASC-0007 +Rows Within Regions: STBL-BASC-0008 +Surround Star: STBL-BASC-0009 Contradiction Rules: Too Many Stars: STBL-CONT-0001 From 096f070b8482cef0942aad5258f9e94f6ee1e238 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:33:28 -0400 Subject: [PATCH 47/74] Added StarBattle to config Showed how to run in allfiles Changed puzzles to be more standardized with each other --- bin/main/edu/rpi/legup/legup/config | 6 ++++- .../6x6 Star Battle 1star Normal1.xml | 3 +-- .../6x6 Star Battle 1star Normal2.xml | 3 +-- .../8x8 Star Battle 1star Normal1.xml | 23 ++++++++++++++++++- .../starbattle/StarBattleCellFactory.java | 7 +++++- .../puzzle/starbattle/StarBattleView.java | 2 +- .../rpi/legup/puzzle/starbattle/allfiles.txt | 7 +++++- src/main/resources/edu/rpi/legup/legup/config | 6 ++++- 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config index 19e63a2a3..05f4e9e40 100644 --- a/bin/main/edu/rpi/legup/legup/config +++ b/bin/main/edu/rpi/legup/legup/config @@ -31,6 +31,10 @@ + - + diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml index 4163e15a9..110dd9abe 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml @@ -1,8 +1,7 @@ - - + diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml index 29307fb58..49efeba59 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml @@ -1,8 +1,7 @@ - - + diff --git a/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml index ba237102f..e429c3ad1 100644 --- a/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml +++ b/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml @@ -1,7 +1,28 @@ - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java index 92ea8abf6..9f7fd0fee 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java @@ -39,9 +39,14 @@ public StarBattleCell importCell(Node node, Board board) throws InvalidFileForma cell.setIndex(y * size + x); return cell; } + catch (NumberFormatException e1) { + e1.printStackTrace(); throw new InvalidFileFormatException("starbattle Factory: unknown value where integer expected"); - } catch (NullPointerException e2) { + } + + catch (NullPointerException e2) { + e2.printStackTrace(); throw new InvalidFileFormatException("starbattle Factory: could not find attribute(s)"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index a39770d85..cc6d39677 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -22,7 +22,7 @@ import java.util.ArrayList; public class StarBattleView extends GridBoardView { - private final static Logger LOGGER = LogManager.getLogger(StarBattleView.class.getName()); + private static final Logger LOGGER = LogManager.getLogger(StarBattleView.class.getName()); static Image STAR; diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt index 7970eadbf..5a9ec0f0a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -227,4 +227,9 @@ public enum StarBattleType { //StarBattleExporter.java //StarBattleImporter.java -//StarBattleView.java \ No newline at end of file +//StarBattleView.java + +How to run Legup: + +./gradlew build +Java -jar build/libs/Legup.jar \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index 19e63a2a3..05f4e9e40 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -31,6 +31,10 @@ + - + From b713212c73cdb07adcf209fcd8c773ba9b856455 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 26 Mar 2024 16:46:20 -0400 Subject: [PATCH 48/74] Reorganized puzzle files (put in subdirectories) --- .../6x6 Star Battle 1star Normal1.xml | 0 .../6x6 Star Battle 1star Normal2.xml | 0 .../rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java | 1 - 3 files changed, 1 deletion(-) rename puzzles files/starbattle/{ => 6x6 Star Battle Normal}/6x6 Star Battle 1star Normal1.xml (100%) rename puzzles files/starbattle/{ => 6x6 Star Battle Normal}/6x6 Star Battle 1star Normal2.xml (100%) diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml b/puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal1.xml similarity index 100% rename from puzzles files/starbattle/6x6 Star Battle 1star Normal1.xml rename to puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal1.xml diff --git a/puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal2.xml similarity index 100% rename from puzzles files/starbattle/6x6 Star Battle 1star Normal2.xml rename to puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal2.xml diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java index 57a5688f2..ceea86164 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -57,7 +57,6 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem */ @Override public Board getDefaultBoard(TreeNode node) { - return null; } } From 52220ca56382cc0552b8660137a20f5f756151bf Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 26 Mar 2024 16:47:05 -0400 Subject: [PATCH 49/74] Created 8x8 1 star puzzle --- .../8x8 Star Battle 1star Normal1.xml | 31 ------ .../8x8 Star Battle 1star Normal1.xml | 104 ++++++++++++++++++ 2 files changed, 104 insertions(+), 31 deletions(-) delete mode 100644 puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml create mode 100644 puzzles files/starbattle/8x8 Star Battle Normal/8x8 Star Battle 1star Normal1.xml diff --git a/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml deleted file mode 100644 index e429c3ad1..000000000 --- a/puzzles files/starbattle/8x8 Star Battle 1star Normal1.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle Normal/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle Normal/8x8 Star Battle 1star Normal1.xml new file mode 100644 index 000000000..977a62e1f --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle Normal/8x8 Star Battle 1star Normal1.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From e2db3f2ff2f11c8ed7e3872473d6a11b406647c5 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 26 Mar 2024 17:44:35 -0400 Subject: [PATCH 50/74] Created more puzzles --- .../6x6 Star Battle 1star Normal1.xml | 0 .../6x6 Star Battle 1star Normal2.xml | 0 .../6x6 StarBattle 1star Normal3.xml | 67 ++++++++++++ .../7x7 puzzle1.xml | 42 +++++++ .../8x8 Star Battle 1star Normal1.xml | 0 .../8x8 Star Battle 1star Normal2.xml | 103 ++++++++++++++++++ .../8x8 Star Battle 1star Normal3.xml | 103 ++++++++++++++++++ 7 files changed, 315 insertions(+) rename puzzles files/starbattle/{6x6 Star Battle Normal => 6x6 Star Battle 1 star Normal}/6x6 Star Battle 1star Normal1.xml (100%) rename puzzles files/starbattle/{6x6 Star Battle Normal => 6x6 Star Battle 1 star Normal}/6x6 Star Battle 1star Normal2.xml (100%) create mode 100644 puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml rename puzzles files/starbattle/{8x8 Star Battle Normal => 8x8 Star Battle 1 star Normal}/8x8 Star Battle 1star Normal1.xml (100%) create mode 100644 puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml create mode 100644 puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml diff --git a/puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal1.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml similarity index 100% rename from puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal1.xml rename to puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml diff --git a/puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml similarity index 100% rename from puzzles files/starbattle/6x6 Star Battle Normal/6x6 Star Battle 1star Normal2.xml rename to puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml diff --git a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml new file mode 100644 index 000000000..80f8658d3 --- /dev/null +++ b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml new file mode 100644 index 000000000..b4c56807a --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle Normal/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml similarity index 100% rename from puzzles files/starbattle/8x8 Star Battle Normal/8x8 Star Battle 1star Normal1.xml rename to puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml new file mode 100644 index 000000000..e9c734546 --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml new file mode 100644 index 000000000..1f050440b --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f747aa8ac836f3a0bba4a4587a3d6ffbe76f381b Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 29 Mar 2024 16:01:43 -0400 Subject: [PATCH 51/74] Update StarBattleView.java --- .../edu/rpi/legup/puzzle/starbattle/StarBattleView.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index cc6d39677..e0914215a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -6,20 +6,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - -import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; -import edu.rpi.legup.puzzle.starbattle.StarBattleCell; -import edu.rpi.legup.puzzle.starbattle.StarBattleController; -import edu.rpi.legup.puzzle.starbattle.StarBattleElementView; import edu.rpi.legup.puzzle.starbattle.StarBattleView; import edu.rpi.legup.ui.boardview.GridBoardView; import edu.rpi.legup.controller.BoardController; -import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.ui.boardview.ElementView; import java.awt.*; -import java.util.ArrayList; public class StarBattleView extends GridBoardView { private static final Logger LOGGER = LogManager.getLogger(StarBattleView.class.getName()); From 23c3d92a9d7ccc7538234f8e80ed856f268df73b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 29 Mar 2024 16:47:14 -0400 Subject: [PATCH 52/74] Wrote 5x5 puzzle file 1 --- .../5x5 Star Battle 1 star Normal 1.xml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml diff --git a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml new file mode 100644 index 000000000..8cd026437 --- /dev/null +++ b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 99e431fd3d452b0edb04f980976b188895264f5d Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 29 Mar 2024 16:51:07 -0400 Subject: [PATCH 53/74] StarBattle Runs --- .../legup/puzzle/starbattle/StarBattleController.java | 6 ++---- .../edu/rpi/legup/puzzle/starbattle/StarBattleView.java | 6 ++---- src/main/java/edu/rpi/legup/utility/LegupUtils.java | 9 ++++++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java index f7a094397..6b2a9352d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java @@ -14,12 +14,10 @@ public void changeCell(MouseEvent e, PuzzleElement data) { this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); } else { - if (cell.getData() == 0) { + data.setData(cell.getData() + 1); + if (cell.getData() >= 0) { data.setData(-3); } - else { - data.setData(cell.getData() + 1); - } } } else { diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index e0914215a..ceb0eec19 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -14,16 +14,14 @@ import java.awt.*; public class StarBattleView extends GridBoardView { - private static final Logger LOGGER = LogManager.getLogger(StarBattleView.class.getName()); - static Image STAR; static { try { - STAR = ImageIO.read(ClassLoader.getSystemResourceAsStream("edu/rpi/legup/images/starbattle/star.png")); + STAR = ImageIO.read(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/starbattle/star.gif")); } catch (IOException e) { - LOGGER.error("Failed to open starbattle image."); + // pass } } diff --git a/src/main/java/edu/rpi/legup/utility/LegupUtils.java b/src/main/java/edu/rpi/legup/utility/LegupUtils.java index dbdd24e7f..317fb787e 100644 --- a/src/main/java/edu/rpi/legup/utility/LegupUtils.java +++ b/src/main/java/edu/rpi/legup/utility/LegupUtils.java @@ -85,7 +85,14 @@ private static List findClassesZip(String path, String packageName) throw for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) { if (!entry.isDirectory() && entry.getName().endsWith(".class") && entry.getName().startsWith(packageName)) { String className = entry.getName().replace('/', '.'); - classes.add(Class.forName(className.substring(0, className.length() - ".class".length()))); + String substr = className.substring(0, className.length() - ".class".length()); + try { + Class c = Class.forName(substr); + classes.add(c); + } + catch (LinkageError | ClassNotFoundException e) { + System.out.println("Failed on " + substr); + } } } return classes; From 63ea4911079b43136c3ea1f272d3663e85cc6d44 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 29 Mar 2024 17:27:36 -0400 Subject: [PATCH 54/74] Fix spelling error --- .../5x5 Star Battle 1 star Normal 1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml index 8cd026437..5fefc844f 100644 --- a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml +++ b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml @@ -1,4 +1,4 @@ - + From 617b0acabfb25b7a3abf4dc0300c56e6c74e4221 Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Fri, 29 Mar 2024 17:35:39 -0400 Subject: [PATCH 55/74] Update StarBattleController.java Fixed the right click issue. --- .../edu/rpi/legup/puzzle/starbattle/StarBattleController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java index 6b2a9352d..9566a3f52 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java @@ -22,12 +22,10 @@ public void changeCell(MouseEvent e, PuzzleElement data) { } else { if (e.getButton() == MouseEvent.BUTTON3) { + data.setData(cell.getData() - 1); if (cell.getData() == -3) { data.setData(0); } - else { - data.setData(cell.getData() - 1); - } } } } From b3452a8262338a721c84ab3fb0058359b1e50d0b Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 29 Mar 2024 18:03:45 -0400 Subject: [PATCH 56/74] Created more puzzles --- .../6x6 StarBattle 1star Normal3.xml | 1 + .../7x7 Star Battle 1star Hard1.xml | 60 +++++++++++++ .../7x7 Star Battle 1star Normal1.xml | 85 +++++++++++++++++++ .../7x7 Star Battle 1star Normal2.xml | 85 +++++++++++++++++++ .../7x7 puzzle1.xml | 42 --------- .../8x8 Star Battle 1star Normal1.xml | 1 + .../8x8 Star Battle 1star Normal2.xml | 1 + .../8x8 Star Battle 1star Normal3.xml | 1 + 8 files changed, 234 insertions(+), 42 deletions(-) create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml delete mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml diff --git a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml index 80f8658d3..855943612 100644 --- a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml +++ b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml @@ -64,4 +64,5 @@ + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml new file mode 100644 index 000000000..bacc00b11 --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml new file mode 100644 index 000000000..70b81e376 --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml new file mode 100644 index 000000000..c541ece06 --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml deleted file mode 100644 index b4c56807a..000000000 --- a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 puzzle1.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml index 977a62e1f..02dd5d6c0 100644 --- a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml @@ -101,4 +101,5 @@ + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml index e9c734546..0df84ef62 100644 --- a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml @@ -100,4 +100,5 @@ + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml index 1f050440b..725c91d7f 100644 --- a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml @@ -100,4 +100,5 @@ + \ No newline at end of file From 85c9b7c9d3e84b9d6e3eeb3374e72103073407f1 Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 2 Apr 2024 16:40:19 -0400 Subject: [PATCH 57/74] created image for star or empty case rule --- .../rpi/legup/images/starbattle/UnknownTile.png | Bin 0 -> 9733 bytes .../starbattle/cases/StarOrEmptyCaseRule.png | Bin 0 -> 4437 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png new file mode 100644 index 0000000000000000000000000000000000000000..850fbf127f04020fe83c6a4f6db75078967916ea GIT binary patch literal 9733 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 QTOg2`(SBUvUiYy70dQO53jhEB literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png new file mode 100644 index 0000000000000000000000000000000000000000..0383711bb7fb4d881c78c0d5c61227ac7c05cb49 GIT binary patch literal 4437 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 literal 0 HcmV?d00001 From 84827707929cb2992d9cbdedd30ec7cac52903fc Mon Sep 17 00:00:00 2001 From: EmilioBejasa <69165764+EmilioBejasa@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:10:42 -0400 Subject: [PATCH 58/74] Deleted WHITE element It was unnecessary, we were switching between unknown star and blank anyways. --- .../legup/puzzle/starbattle/StarBattleCellType.java | 2 +- .../puzzle/starbattle/StarBattleController.java | 12 ++++++++---- .../puzzle/starbattle/StarBattleElementView.java | 7 +------ .../puzzle/starbattle/rules/StarOrEmptyCaseRule.java | 4 ++-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java index f3524034e..565f608d7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -2,7 +2,7 @@ package edu.rpi.legup.puzzle.starbattle; public enum StarBattleCellType { - WHITE(-3), STAR(-2), BLACK(-1), UNKNOWN(0); + STAR(-2), BLACK(-1), UNKNOWN(0); public int value; diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java index 9566a3f52..4ebeb39ba 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java @@ -14,18 +14,22 @@ public void changeCell(MouseEvent e, PuzzleElement data) { this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); } else { - data.setData(cell.getData() + 1); if (cell.getData() >= 0) { - data.setData(-3); + data.setData(-2); + } + else { + data.setData(cell.getData() + 1); } } } else { if (e.getButton() == MouseEvent.BUTTON3) { - data.setData(cell.getData() - 1); - if (cell.getData() == -3) { + if (cell.getData() == -2) { data.setData(0); } + else { + data.setData(cell.getData() - 1); + } } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java index a88773a06..ff5d54d1c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java @@ -29,12 +29,6 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.BLACK); graphics2D.fillRect(location.x, location.y, size.width, size.height); - } else if (type == StarBattleCellType.WHITE) { - 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 == StarBattleCellType.UNKNOWN) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.setColor(Color.LIGHT_GRAY); @@ -42,5 +36,6 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); } + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java index 1bbbe3e92..035e55073 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java @@ -20,7 +20,7 @@ public StarOrEmptyCaseRule() { super("STBL-CASE-0002", "Star or Empty", "Each unknown space is either a star or empty.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png"); } /** @@ -98,6 +98,6 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; + return checkRuleRaw(transition); } } From 09263e681d8e316deea41a3ce920783caad9dead Mon Sep 17 00:00:00 2001 From: Sarah Date: Tue, 2 Apr 2024 17:35:17 -0400 Subject: [PATCH 59/74] More rule images --- .../ClashingOrbitContradictionRule.png | Bin 0 -> 5397 bytes .../TooFewStarsContradictionRule.png | Bin 0 -> 3571 bytes .../TooManyStarsContradictionRule.png | Bin 0 -> 5815 bytes .../starbattle/rules/BlackOutDirectRule.png | Bin 0 -> 5209 bytes .../rules/FinishWithStarDirectRule.png | Bin 0 -> 4399 bytes .../images/starbattle/rules/SurroundStar.png | Bin 0 -> 5102 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..3cdbfd4e1ebb0dc820f59cfd90297d35795e93df GIT binary patch literal 5397 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 Wa{k=8Y32$D_&C}V?XKD$i2e@+u;#@8 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..724f8aa64d9733c30ca7fe5d1d52fe92cb539e8d GIT binary patch literal 5815 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..53fac213291591a0318f46e049ad083a25315527 GIT binary patch literal 5209 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..24eddc3b058a4a390b9e9344d4aa012c85f415be GIT binary patch literal 4399 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png new file mode 100644 index 0000000000000000000000000000000000000000..13287f779e74d6c70489f32013ec3675b07dfe4c GIT binary patch literal 5102 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 literal 0 HcmV?d00001 From 999fa1780ad12f8ecbe0912d3da285b7b7c902ee Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 5 Apr 2024 16:42:27 -0400 Subject: [PATCH 60/74] Made small test board --- .../rules/BlackoutDirectRuleTest.java | 19 ++++++++++ .../BlackoutDirectRule/ColumnBlackout | 36 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java create mode 100644 src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout diff --git a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java new file mode 100644 index 000000000..b87b88de5 --- /dev/null +++ b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java @@ -0,0 +1,19 @@ +package puzzles.starbattle.rules; + +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.puzzle.starbattle.StarBattle; +import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; +import edu.rpi.legup.puzzle.starbattle.rules.BlackoutDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; + +public class BlackoutDirectRuleTest { +} diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout new file mode 100644 index 000000000..f2a5b42d9 --- /dev/null +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 15e19d39a03c41b0807233927bac1b7eb63d454b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 5 Apr 2024 16:52:31 -0400 Subject: [PATCH 61/74] Add image paths to rule constructors --- .../rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java | 2 +- .../puzzle/starbattle/rules/ClashingOrbitContradictionRule.java | 2 +- .../puzzle/starbattle/rules/FinishWithStarsDirectRule.java | 2 +- .../legup/puzzle/starbattle/rules/SurroundStarDirectRule.java | 2 +- .../puzzle/starbattle/rules/TooFewStarsContradictionRule.java | 2 +- .../puzzle/starbattle/rules/TooManyStarsContradictionRule.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java index ceea86164..45c550bde 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -16,7 +16,7 @@ public BlackoutDirectRule() { super("STBL-BASC-0001", "Blackout", "If a row, column, or region has enough stars, its unknown spaces are black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java index 28398143e..0ca27ab4a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java @@ -17,7 +17,7 @@ public ClashingOrbitContradictionRule() { super("STBL-CONT-0003", "Clashing Orbit", "No two stars can be adjacent to each other.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java index ed6a8a986..f6b7eef86 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -16,7 +16,7 @@ public FinishWithStarsDirectRule() { super("STBL-BASC-0004", "Finish With Stars", "Unknown spaces must be stars if there are just enough in a row, column, or region to satisfy the puzzle number.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index 0608d388f..5d42e895e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -16,7 +16,7 @@ public SurroundStarDirectRule() { super("STBL-BASC-0009", "Surround Star", "Any space adjacent to a star must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/SurroundStar.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java index af30a5e18..d1ed62107 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java @@ -18,7 +18,7 @@ public TooFewStarsContradictionRule() { super("STBL-CONT-0002", "Too Few Stars", "There are too few stars in this region/row/column and there are not enough places to put the remaining stars.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index 7777ff5a4..880e3b8ac 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -21,7 +21,7 @@ public TooManyStarsContradictionRule() { super("STBL-CONT-0001", "Too Many Stars", "There are too many stars in this region/row/column.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png"); } /** From 729d6b7fe2908851f8cd9915be63883b9de88fcd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 5 Apr 2024 17:18:02 -0400 Subject: [PATCH 62/74] Write first test for blackout direct rule --- .../rules/BlackoutDirectRuleTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java index b87b88de5..d42f40c87 100644 --- a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java +++ b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java @@ -1,5 +1,9 @@ package puzzles.starbattle.rules; +import edu.rpi.legup.puzzle.nurikabe.Nurikabe; +import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import legup.MockGameBoardFacade; import legup.TestUtilities; import edu.rpi.legup.model.tree.TreeNode; @@ -15,5 +19,51 @@ import edu.rpi.legup.puzzle.starbattle.rules.BlackoutDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; + public class BlackoutDirectRuleTest { + + private static final BlackoutDirectRule RULE = new BlackoutDirectRule(); + private static StarBattle starbattle; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + starbattle = new StarBattle(); + } + + @Test + public void BlackoutDirectRuleTest_ColumnBlackout() throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/starbattle/rules/BlackoutDirectRule/ColumnBlackout", starbattle); + TreeNode rootNode = starbattle.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + StarBattleBoard board = (StarBattleBoard) transition.getBoard(); + + StarBattleCell cell1 = board.getCell(1, 1); + cell1.setData(StarBattleCellType.BLACK.value); + StarBattleCell cell2 = board.getCell(1, 2); + cell2.setData(StarBattleCellType.BLACK.value); + StarBattleCell cell3 = board.getCell(1, 3); + cell3.setData(StarBattleCellType.BLACK.value); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || point.equals(cell3.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } } From ee074b1b63917e8cfea295d72ddb6a87731a2713 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 5 Apr 2024 17:24:06 -0400 Subject: [PATCH 63/74] Test puzzle files --- .../BlackoutDirectRule/RegionBlackout | 36 +++++++++++++++++++ .../BlackoutDirectRule/RowBlackout | 36 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout create mode 100644 src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout new file mode 100644 index 000000000..f2a5b42d9 --- /dev/null +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout new file mode 100644 index 000000000..f2a5b42d9 --- /dev/null +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c789e5b049b31dc793de92016a594cb8d1969f86 Mon Sep 17 00:00:00 2001 From: Sarah Date: Fri, 5 Apr 2024 17:44:05 -0400 Subject: [PATCH 64/74] remade images to be 3x3 --- .../7x7 Star Battle 1star Hard1.xml | 33 ++++++- .../7x7 Star Battle 1star Normal.xml | 85 ++++++++++++++++++ .../ClashingOrbitContradictionRule.png | Bin 5397 -> 5135 bytes .../TooFewStarsContradictionRule.png | Bin 3571 -> 3218 bytes .../TooManyStarsContradictionRule.png | Bin 5815 -> 6199 bytes .../starbattle/rules/BlackOutDirectRule.png | Bin 5209 -> 4717 bytes .../rules/ColumnsWithinRegionsDirectRule.png | Bin 0 -> 3781 bytes .../rules/FinishWithStarDirectRule.png | Bin 4399 -> 4326 bytes .../rules/RowsWithinRegionsDirectRule.png | Bin 0 -> 3761 bytes 9 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml index bacc00b11..c1d7770f6 100644 --- a/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml @@ -36,22 +36,47 @@ - + + + + + + + + + + + + - + + + + - + + + + + + + + + - + + + + diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml new file mode 100644 index 000000000..cab0a0a5e --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png index 3cdbfd4e1ebb0dc820f59cfd90297d35795e93df..d25340b407edf02eebfdbc35764b0527ecea59b7 100644 GIT binary patch delta 4008 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 H!|4A3=DL=1 delta 4272 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 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png index 8d4c7bf3520f49394a88c0a5922779aa589b1674..cc019752f8531a366fd784d6772182e185a8d8df 100644 GIT binary patch delta 2078 zcmb7Fdpy$%8{gJaj{7CX!g!mgOzws=*OE#uFJ~@mvYAW02sv)EABol?%DN!;$fe{~ zOfF}ViatZBjoM_=+{tB|96CGiAMfAqdH#F;d7kI{e8124S%crwXsiZ6ltCkh;AOt|!XNo|eJy(J^ST&#JqBs>Mi||n(#g&+DO^)V|rp#je=#WvPJ)a z^QyHnr~^o<6BF5=I!O3jS)oT=UUY71ou2=*r$id-c0@0ZWwfom!?<7IykoXz8y6~1 zo-D*xD6a0egED$|DcEdVDrpyxidL?^#Z#=L!~qNbGxIKmX@lll%evqM#08B zo56Y@njXA6D_s248u`g`X^sE#`s~j3)}`5Z$V#4gVx>Dpg!1+AX-psz|Ft0$#xgSP z!(Fgg_1kyutV-{Z-(SXJok6*Fxs~5sTwL6CK;b)IdjhAodIcXJ2e!t~|F#rIA|bhq zxT`2!`xmMe=xwQeX;;Dmhp7UZRy#D@*xijOw241dIy^o;@XA_xqZ%57GrXr%?n!NK zX&F<&Hn;`t#dwt1l~-5mFRe9`24^o z%Uq|?#IWfat6Kk-(2@1o_eojpXapLK2Fw$daIQaO6%t7#Qd5GtW)(W_+r>>=wj#Z# zyObcuWDzB_G~ga%N^^m`sIzk%&C5>-!xkZpXVq>LkCTegan(B1_wO9;^GLx{re9DO z8U>0tLjwtlN%U7q_ZaY33FPe|UHz(3XY)L(5@-G-Q*lT)Y8Nvj_=qCF)|TB(1t}pQ zE@^ue!FmqR|2s(n5aFQ9FQRyzZb&4O+$)j7=HC#J(5&+Ghi8f_RN>U2DcvVO;!jFj zD!M2UJXxjo(XB&@c8dFKqIq`0<9QYkK*faj@x@gIwAb!nI<@Jg^s8JQ0g+zGqifqG z{-I0f)|oio4W&+RtVYMjTXZFF(4OG(Q?rh$1a*_&w0L`@sT<>05(Ddhdt7n*FxK1M zT}}}81q$8KJFSz0Ya(QNF?*WpXsh}8wMSqNmYsB6eXGemUa33h-@@wrTe9kayT(?$ zuOJ3D)3Aa&>%IfzWaeISi0TZx$M^z;c!dXkhLD~-E*yly7gylcLZ{-TRSky3BP3( z2;Q`iPebG=j7fD4(o*FMOi46Qj3Y-VFFprTaG*sZ@EIlA|0NsvXVm8@?B=_Sz5yV6 z`Iq_N6SZ>CT_K0~+~|~VQji!h^oe$EAY$ZP1(BzL3BtLcy1`JT>n92wVMKrbG5d@V z!D(yV9<7N083ZF~v{0$KV-koMr3+0gVBVG9sR5eX_exkzU0e{M?ZuR54>98rTvOBf z7&z!!V}B0Zb@+<3ih-{jEf9N6YSrc-yJ<$$LuW6a1o0DUCp__Z?R~1MNB77)XkC{U zx1WrSHI>Lx1KaFAB-3zfrISJnmEk>(~sJc%Ct_kuuzH$sN6tf!(r@gvB4ysD{7;ENdh_#imvH66Ouwdc;E9f#_^@V)l=Gsg z#-4w`Crp_pb4Sb}BGe<@WG{QN_^3|h{tMZLOgJO{HDk*Fnb}p$GLhxXPv1hGvgV#X zSW&oOCZ#h=24qH$ftHJH6Jif(4Hy;gqn*98W7Esb*dsZmpF+Z>f>LM|MFXJ8^+mkw z&eqA`_YI0C)9>%!Hl-YyI|0e5Zw+rt?{g1m{bxM>@6v%~1!xAw6Q>5@3|o&DDU+45 z)jeYT6G;tkFwQFu2*Q7GYWkr5PY|layeUeZHz@YMFTjF~BnD#$wrP+x?~6qE`uhH9 z8sVZYfdtKU&D9?1&6F8?&R`T&>YMP?q<+qfRE~c9czh(B?!oCyGSksP$6lGdr%VnE z2oMAFAm;@R{%f_hq1Flxk7t?m@2#{L3)?%D%7Dy6q$IiGkIGwxv7v_`4;{X5v delta 2434 zcma)-S5(u962=KlP%%_B)WijZ&;lg%k|m%(Ahg{@gcS+Jf*5HEMEKK-bi@S-MFbK- zKuT6n2~8lh4Iro?xO53%)(ssIJ9; zsM5d6Jg@%jC7bF}Knl_=F~dK8h_u(o{|#gG<)mCba%}g+dQz;kI@oF&n^=&}lsk4$ z4=%-_?Mb8Lg9LlK5mlq3lOvau-Us8;q29k zs`JR$gNUPGFr^i^GWOqbTMRbAo$YZf8Vy7N`H37l4c6IY4dZPvBdRRn)#nEBQs~H zj8an!SlQyywMnvm-Xz5(oHE{&tn1t9M9)=W<8*x5^F9N!w2^VOjs+oWA>q&}59!(V ztBt;0xS8i6OU(C;zm5oL$e9ZR0&DLY6#i@vO`4&i$z*cs3Zo1wB_;JiL!hz>7s+NQ z#q4wal%ijG^!N8~ufNsmlvh0r?f=Eo)5FgXR#RO)P9yH^mnI`lSc`8|bmyr18w zWRF#B0e@EytgKB^rfNOT4v&mf)z!hRuE@#Cj&~;rY3k@?CnhC{(_;^|D`#oMOtDL} zn4LvjU|`@xJV>ZrLY%PQ{fuwZ@=naP)$vMXz@Vg*gtA5Pk8CzOoYP+Lb?4LBljg!* ztE*S>!@|S+J7D|$0|PBx zq}~GN%gEpf=ED1G2bs>|thN*d7t5Yq3BT*%f zoy7qQ#APoC>bmULw3L($?IL%3doiH*6_r|8>_HH-8k|C)Z7>+Swp*}eNVh_?*J-p*w{$a5EGkSWZM`MZ6}9o$ z&qYZ{>>~4zFgP+DBoq*F{mbUZZO*h|IZB$xOp4oF7SC3;Wc~io@26jW8PF1E70N3( zCFdxayN{JyPAjnM%gpb*Y4)dIT?Pj8OI4fv=_goi1eL#LX{24w^pZUneaXYlPUOz@ z;X=)b?Nv7$5{_2MH+v%ghw~<}1JwxFM|(%^>v-5M{}$dXyiR-JoWayLIs#T+cDjH2 zFIKZL!|Up(6LzbWdiNPJT>E3Nm8CM6ZR`NIvMMh@1;5kqpX6>0KhQD-w7^4Q7m`A= zs6%g`dQS3disH)ckj4wmi3+c^KXgPJHeCFDxY<}(MO$s~Fq}A4Vj8Ya5p?L!X`XHm z{h@|J+0ve-UX!V4Z7Qe5eJfcRefWA$vff`^P3BXVOpnkf)wy~OPEJm4Vx&m#i|ERf zLLxY9cpwY1bl`DPSM>w%T@`P%HVQWjxKYaCcPI2H?~3A&zR^ zRsOG2CBl``BU(@cqF}LhprhzU!yM+2L;OEx&ff+f&|`qY3q-5pzc2QJ+_R0;^PokS zk#Na5;Nn?<#QRK7dNf7w0y1WOO7rtnff_a{+7>O%E9Nm7NcT))(NKtuvzF||{LR$) zQv|y5#|_sRDstGtS{HjaCLAxPQ&ub;|LF&09FNV=iNKP^Y@>AYtYa;)BBc5GA7_+zwS0XElTBRII7x+z$YF?|hRRUb*zAbGgHD%%zHDdi|YfNH_zws$} zkmzR>iEHR~eMY&Z`J;rw0s7>N>fa7q>6N_=FR2VcV$c*QBSJ|`Ic}E7v0K4E5;FA$ z_idox0)ol^b)^7b#X&_Dued|w)c;o^6K+~Bdr#{N=MnI9$pG#O^~)B!YcE&OE-0^w z!V7kU>)gw!tia=yY6nl=9hT{=>Y6lMr5{*Eu65{n+!7_OYqa*%pqw@!9S@Fo^4cb$&c7B6ih8@s zC-w{1Q%A3xN?EubZ|LYKRUlbJ8=O+ad9Z(M`%JNY z{LL)R-Z`zrT0mv6h14=xJpN(zWgZ8`zmjaV#c2HkXjJq86u#>*B$41*Ao zivE&6N?$G&Q)bFNt*^gP42#cAEsG&#ikjz<>By_S`( zB=86mke6M6zaiTM5m1JDhE!&E>>@0LU+flmGw# diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png index 724f8aa64d9733c30ca7fe5d1d52fe92cb539e8d..b468ae6f6fb70271fd594e8cb6b5148dbd9153c5 100644 GIT binary patch delta 5082 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 YfPTG|sWX}iqkzlMxo@adbI&2>Kgw3Y`v3p{ delta 4095 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 Jt$bo1{eO3p=0E@d diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..3493810b78122654a0f7091cf605ef0cf9dc9c84 GIT binary patch literal 3781 zcmb_fc|4SR7awJd>{+TSgCd$S3kHM9*rFm?3RjrTItw#ni;2iyx0EIORFd9AWPdYC zwo)!FvMWo9G}Mh;%YC15yGnQY<9*-xJkLDOe9!rv-#Op&ea_4z+S%?C6WJyL0)fP= zt<3F#E0K2!3j*%}BWaGnWmABa3j+iaZ{nSNX?w)C1E3Mp!kKACqfkgxCMcASCZf%; z`%K9Mr2c+qYcv7@@k8q)_4MI7a5%~Up{J*Z($~?3Lzrl!4uI(*5qT_pF9^gx=j-Uq zbhbH&CeSEaI3f*C(h8*n02=^Xgrb343WH?20GMOeboaE-1A_VIz|NiFd+q7|a8MWcJe5C#SYFgOy1L_z@%C?kx@#Dzks3{@b~ z`cUR127&Gy!1Se2!MxBoJS~W6tggQ1^?OQ0!rC?gL3Hx!CPV^^L?-cwVZabth;>;K z323G-lT7+9aV(kn9YXfyRe~ntsNTlvp->{p3l~IYs$z zsRi^Ih9mNrfv;oc_f=>tk?~F9yh_G z6s4oBr;mi|ArN@@TCCrf{3((p9tfn1)J39n4G=mAJp*0+uWMF~e_G;53nKXN%EBPm zqJPD{y8lg^*VFY!sq1O|KQ%hwn}>|o<<+N`*I&Q@^jCXbe*p}Q4PyGx=okka9p?kK z@ueE^JpVwytL%T*Auv4FiEu5wP+Y*DD+L#!wbdMh7+wjl~IMtT%EURH4I=&;d;42H!f>TO_ZJ9z=Ej&btNT zDxe{T)e^(0ZD1vjoqHKH&E<$Z9VdW5zV7Zr8M>k)e4z7Pd~I=Pf$3*2?}Fb28%j&d z@SpLo?nmgug|FHt>O1AI%P3{mlC4maBsKBG3+RN1((

H#jbB8k%5>BN}m;F3#a zd}>!Vp>ws^9VjG!EK*Q9^Yr{m>pmqDIb9)qB5{9hmybDtpjuvD&VKH9?HOY8#^hR| ze+g*Z8QrkC*lb?v$_WU&@IjO3!L+1|rs`Wp@0CFf7wQslY9Uzh`-Rjy^ZPe-Ol!8* zJbvh}B*&1jU1ke=yB~|6?GnoAgP#_S48O5WJ?Llla`&}6BXv2pI-E)&+EJgt>Q9>2 zQ05*avAN)v@BF6_fJ&Eq77p&nb+^st+jI_9{ygsWEm$w4oRK^P~#q(48Fvac9yRpV$e7RgEl7 zrQxRA@$c{TzUU^f7u`(;+gJ{5bEn%k9q>46QoHn?bh5l9o_Z0YBB%S+56Pi zgkFW7HX*!#wfpBS-P~~bq&GcLbpdnZ{thce7oL<|FWm9a*{QP%pSXoYTiS8G_mI%D z*xN9V><{~PN?&2NX15*tn9HWr&${?~UwpJ=nyQ2rP1Kp0)J$aw+}4noEMPkRfRS)A z)%17ER1uNgH+e3l0m;@XUd|H=GjP0P;N`YUkph^h^E-&4Y}( zy5GuTybBFvVbGkWaoH~TC5t#FFZgVsUq0IgyVG)ZZe!b?N%{?E+ z3Hv*|TG_hNme$o1aBh&(M{^hTcM$O3!na!!Vo45qOZm_^^YUz{E_Xn_|3qmH zS~$5@eGK&*=Y#i9E$ps`WQ*>?gYd}TLV5&QoSDb0*l0@Ugy`@bH>WgcCZO(jz|`ZGJv`u3od32cT_*+pf`7 z41?TzuHMZ7!$XCL&qF%WMYz#wqS!G^W&F+k65xqUmxvfm;}C;~;w$3iyLWL1QcGFarS0tke=&8&$};eF zfnytO&sUg(HxrQ#Lj!l&34;c2y*V0J-&`2ALciZ~- z$ITK*i!E|;nD(&n%h0LAr!Gb&KrkE2K62vz{gX3R+gJ7QX1si9Q`6Ey&4gz|>j$S% zyPDfIGI#^fe3Vnc)3k^L}wjUj`=c^@=!7NsN zNZQo(r&9LvugY{`qJ0{Xb)UjtX1%{*?~S-TIq6plo}QlWoch%1R|y=^!39-Sa=E#= zMI|MxzatoQx}B@5rVsgN1g*bHOfyXc1cqv8%u+l1`b6s8&lVR$KBtRf&CE8U4Gjg( rP8qyTO-8PDWsA>n7WIe-N!LY|TqdJ){2v{O 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 delta 3273 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 Date: Tue, 9 Apr 2024 16:38:38 -0400 Subject: [PATCH 65/74] Added direct rule images, updated image locations --- .../rules/ColumnsWithinRegionsDirectRule.java | 2 +- .../rules/ColumnsWithinRowsDirectRule.java | 2 +- .../rules/RegionsWithinColumnsDirectRule.java | 2 +- .../rules/RegionsWithinRowsDirectRule.java | 2 +- .../rules/RowsWithinColumnsDirectRule.java | 2 +- .../rules/RowsWithinRegionsDirectRule.java | 2 +- .../rules/ColumnsWithinRegionsDirectRule.png | Bin 3781 -> 6456 bytes .../rules/ColumnsWithinRowsDirectRule.png | Bin 0 -> 5331 bytes .../rules/RegionsWithinColumnsDirectRule.png | Bin 0 -> 6402 bytes .../rules/RegionsWithinRowsDirectRule.png | Bin 0 -> 6269 bytes .../rules/RowsWithinColumnsDirectRule.png | Bin 0 -> 5117 bytes .../rules/RowsWithinRegionsDirectRule.png | Bin 3761 -> 6477 bytes 12 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java index 93fb86dc5..b42bfd1c0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -18,7 +18,7 @@ public ColumnsWithinRegionsDirectRule() { super("STBL-BASC-0002", "Columns Within Regions", "If a number of columns is fully contained by a number of regions with an equal number of missing stars, spaces of other columns in those regions must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java index 389ba02c2..0a78c8868 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java @@ -18,7 +18,7 @@ public ColumnsWithinRowsDirectRule() { super("STBL-BASC-0003", "Columns Within Rows", "If a number of columns is fully contained by a number of rows with an equal number of missing stars, spaces of other columns in those rows must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java index d2fa4fbe3..16951fb2a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java @@ -11,7 +11,7 @@ public RegionsWithinColumnsDirectRule() { super("STBL-BASC-0005", "Regions Within Columns", "If a number of regions is fully contained by a number of columns with an equal number of missing stars, spaces of other regions in those columns must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java index 15993b85d..27dc001a0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java @@ -11,7 +11,7 @@ public RegionsWithinRowsDirectRule() { super("STBL-BASC-0006", "Regions Within Rows", "If a number of regions is fully contained by a number of rows with an equal number of missing stars, spaces of other regions in those rows must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java index 2d6c148d0..4054ec017 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java @@ -18,7 +18,7 @@ public RowsWithinColumnsDirectRule() { super("STBL-BASC-0007", "Rows Withing Columns", "If a number of rows is fully contained by a number of columns with an equal number of missing stars, spaces of other rows in those columns must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png"); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java index 7dfaae59f..7af2c79ed 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java @@ -17,7 +17,7 @@ public RowsWithinRegionsDirectRule() { super("STBL-BASC-0008", "Rows Within Regions", "If a number of rows is fully contained by a number of regions with an equal number of missing stars, spaces of other rows in those regions must be black.", - "INSERT IMAGE NAME HERE"); + "edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png"); } /** diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png index 3493810b78122654a0f7091cf605ef0cf9dc9c84..1a88e9d07c6a0e4a1425d9725be9d1fc130058ee 100644 GIT binary patch delta 5806 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 delta 2978 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..fde683dcde26e2d717999fe759d97d3f3f524dee GIT binary patch literal 6402 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 E0_Y@20RR91 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..bac64a87c9ae9df53b8be031c17306773ccfba7d GIT binary patch literal 5117 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png index a9f22f16f5042b5bd5b9294f9fb465b15f1eeb0a..8907e047521d5310676821ddb141672f2b5e4d7c 100644 GIT binary patch delta 5692 zcma)AWn9!^HV@ zfb6WKIrK#|_%&P=;I?)uer|AGzZXzTKL<+*D-Ky1f&>&>LZ2d%7z>0{#So!`JoWH! zv$K$VYUM6N00My^ZdTTky71RB1jvsVPmq%swCEDZ0}N{U|6Ek^@px_}BPhWy!Y?K+ z!eb>OA;cprC}_dtT4hyL?wIZ4>OmQyL0K_A(%D+46))vDrZC;h%g@}xiGX#?EYwe#_bKnZ7k!OzA9HYoyK7llTOXkc z=qiG4{LuTb#Eiv}aYIRptN8_+Nlt~8MEL^6>e|HsxNq>=n{hdRXQ?Z>$5JEml4>Jn z`=}2d+Qs?MGf6#h`}fxJ&;#GkTD4AuUobmblE4iwbItKh^Ltv8z%iU)L?FKVD`HxS zsM&>Pf3Z8k{GuX-2h7ZIb>br5g~0|D&O(0JANd3J;Q^GU2)-4rv{Lussc}eV3BSIk zyGIGo<0w7+y%8~)Q-|;Gm{O-GWvs>vacQT+_slrv$ENo=GPHI5|c3?eI_IB6#D{5fWK zsqKMkr3|Fn?<%M4<$rv^Ii(c@VF7FJ+CI!lkF87U}S+q7=AjlbO{7>C`Q zRasInkj{4pV|tsd2os&%I7;9ygy$F8H>s>Op-cc@!tmrUOD_%h?iMMm#7!o>4qz$~B$7U(#$jlBXM$@PpN$GTi6+|ReD{n&;+4TeI(D>3 zY{;*&j*Z8SPuX3HDMpTLjx;_8uO#kn?reu4!U6M*DT6EcZ42~B$MmhK4x9Dapr-||iq-9K$43+AaRz!r&v-%YE~6<)T%Fsv+(lP>Pq)NR zqCOHvG8UD)&4T2E+3#3n#XC{8F(DTm`rE-OHs_UgpjWg3;yjoMAwZv;6-bbbHiAeI zhvgpszu^%;-=`xD`d-2y!_XIZ-*gQ2*oHWnW=(aiv_!lEkqQ&$uhUjs6*o6Q@`uRy z%!F9x+COhvuCYLW=qUhvnVsxFwughJKp=h)zW&n1E|mwvMyyL7;c|)(qDal=YsLn1 zJTye&frO9s4tdjB73SddrH&r>$!9^FXLOOijmLf6fIDcGmE@4~wk!WJ=eJC$@Q#kE zI;dgj=mF=+ylz--@BYdYICjfRlWuuE#G8hJ)0itdhQRX=zoTnXDNVY*J*nA$4%qmI z9C1-~-$jj_Q~vU`KHuGsHCHm^!9bQgNK!9JP#;YDla{$Gup~#)m8EERh)t0tP1s#+ zH_q=l<1v&WL%iP@@HS*8@2NdI{myIp zGc~SCfkZ||i&D$B+{MHGRX%>pD*&o)$v{DYQ&vVjf1bM^$L8ZS4{mC?^>_ki7Datq ziWx@u1ctdT(0Q2}0R(}y6m1f8o6u^SsM*g%C27ty#XX9K?s~d+HK*B(=JdK<1I-GI zVV=Jy55G7XDJTedpK)hhtVf)ed(Org)QDm^On&J#jI3r?9KDJ`9i|V{14Fgg0m+x< zH-*F8ftglb&p)Nj$Iy!{Jz4!>?tztcW>3-pFeE0M^@Di)3%IE}h}TzY8gGZ3 zijB%*tY4m6gQxvhm%FPb_}9jX-|NuJU%s|cmk!>32rP@$zF@2 zZ%j9@u4RUXp8a5}SjcpC9ZnZ%(&}lfTRPdgfBfHeC4!YY&rjf1fYr9w^0Q}3A!naQ zNzA}by0<^mwx+9}%%SzIW^iJG9hQr^6%? z@uMi74PW&uXFkdEy?4H{Y=@A57tkD=Q&0&jL*K}Q-h}tKcCFW# zR_V^vzdQAGXY&dw0kyo*ySW{8NBl)gG%KAwnTu|ivfQLbK&_W7_iXj@`KjWDb84#; z_HFURK@PF3u(E?9)4)5tq0il`dJoF0Yu9}tx5gYpKNEs;s%JN0YdM&emUsA~q|c{N zGy`%Ta_rX1X1*ORuK2h3FP&zN{K@0)Hh)l>zAYlqN-Drv0B~;lAGhOK@Uxc^h3n0F z$umu2Ot*UPguXJG*ff!%h`J`AjgSy|Ea!=t39AtZHn)Sb$(dtw$rXnb4Uk3QvGT7_ zv6n3rU7HfBsf9H$$fUm2--`|Yl|_`lMVI(6BtB+CW6UWbt{t+aiH6=)sfP`~o_QA;#%4BQCoF$~;G@Z+!Tk2D8kUsv+8 zt)fXI+O38+?N{24j|wyjN(5yd?`iX3+KG~FQ-1^%7pB^`aC0MACvueU4x#hoG$&>! zB%~ywkny2U&JazJ`M#lv zrxiZ{O!H}D&n=-~1v2x4sCp)r=Gz8gAI=BiZ zyqLbzav%9}#wa|OEV3U>5(SEncmR>75?cW$aDLyUkZPg`us5E@l}C;Ij?hPInwWB1 z6-UT@0P=?Jd%TOPW0uEF#DYVGuGm>%zZ}4X5#JM_YtgQoeP}$QPyAi|vZYUro7CS3 zt&3riBBtf&nh07q>%PY391 zDQvr9iA)Ow1nMea1pD@67Fv2__Z`6ryBXOvqj5eRu$c6*uC zBnNjU#*b=^sc$5|kd&N9dZN78QQ#G}xt;*i$(f*tNyRx*Q@Ju!*cvvMy8|A!#6^MW z##3U85U(oF)y|jg2-u8AJZ2;C(zZqL06GOTqzPN1)#u{V62XlKq9IcX*Ye*#>F4H_ zHXI^Mq)PFj(K23nBtA^1aam~W{@Gh>$1P-feGPZ3ZhwDng8MCJd9-I7yBm$W(l7z!LxQBPU5;hE4fbMGBOX5t``!!nkb?19qRUjY&I@n14J>nxqCP z8tmjK!ca>@K%z0%I?giXyb8;Cx>h?cIMh?oqN0jUqN9mmW;zi^?Aq}QpxrW7MP=n< zQ=S~$aW%MpfR9HIj70wY8D4JwnyDLT(g(|FjLB&;eP{29)00j3#Yf>%vg=lqKQ-yI__QtM>X>6Ue{K&)W55dHBn_XUB z{sZCVeW8Oo!Nf|>SA6408m54C(1AeXwC6VS+AgzF?wyB+GZ57RJda=^^*k zc^=flQPbyjeYRV_d%p!7yZ7%V#%xIC9;@+nMvA-|G0FPi^vv+QY`?;EbdAm5i*Mx6 z|GTrD+G^`Tr5|u=593_vjoZi!keA2UpDv&}rH+k_ojbd|x%O?9A5KmZwYxTW=|?BJ zJICX(U9fnB;l`Erj#M+;47#iu&1DcWhLAOL+@gw>IhK|(?T;INuqN7G@&bv7qUzKn{+bi4?}aN4Y~E_ zpDsljIT{3@vc5Di#ds2(IoF3u10)*VjcK zLBbETQpzpCkQI6|e-}Gr{3c{+n~5*FGy~2tWJksop*f(p$G8X6c&IRV)d};pFp#Yk z(+YgoxadzBFy59u;gUq}UFDwge6HY`F$etMIq-35>ZF>PWi|JdZZ^gv1kj1u2@<2m z^XBcDSNs|ji^Jc=HhcSlql52KAE~8Cb8^j5z1WQUf()_Lnd6Wbx=Yz>V)OSO{_<$m zm_cBq5hfhD`F3B{Pp3IOkF=Au_RU0eE>6}$0}sB_5>*``-vSdarogAStE1%3BB5^f z0>IVmKK&buxqz`wwC(ibTpSUFuUQ@TeQ)-?Sj3M>f7aWIlI^B9;Qmg+{O)2gl*hcS zFy_)iL&0~-M$!otDqwyucz7)x@X7$z;lxY!p5_MDLvSFvU`jJ*tMS01jCWhob;UaB zB@;*KTv%H)>W`$v_A0u41P!OM12a~))iF@;1&k?^5t@>@=v0?Ru+V=DqH!~RNf;c# z%2@&?7BR)CXih#2+hDgtfgijj%14OYTgPZXpiN$(0Zq%~B+<4IXWt7Fw!l7-d=gbK zpIHgmLNN*~eRT0KK$tRB`$-$waeN4b0d&<)C_IugMX|h~g*;6h$Ah74@f`Vmfxm`P zOnadtvqf()TKrqhu~NO^l@*8wS7%X71ZqV8d3%Cv9+rTUbMISt`QSz9d@|Oj9*#J} zMe5;oIEV+$BGx5pDWH~}+>)wHaPVBoY;E)08Y34MH~x1Q_ad~MKRs_OrM`vFWrFgj zr-e-X4diN`2Utja;!gb ze&5*s`UDwzUqn5u0fQ3t#Bwp|)^adrp1Wr_rL^C&#!2S@)^vda?v2EYI2XI5ZO4eF z9;Bk5Q_cQjcJ_^RNu=_ht@JX(u=RIm;dNi8hqF65`LxoE6x>*siW*3~Js)5Qx-4ZA zVvMf4#C5P;*kD=Xx042_IKJI{pow*M;}U8<(Zb07d9Gcb#m+QuE<6=~_4qTZ!RtS! zM5>e+Nn#gQ4IAx(02aprVlmP_Ym#9sgIy(%9u_`O0a+#){6O%u;_KKa`*J&w#VQM> zyn`~Yjt5I5fr_kK0>)26RyP45X-azUJwdeZ*H==TKsRx%zAT)MboNPhp6MEv7Xz-8 z*Mc$a`eCu?JZ0R?q3~~ona(|f1hV?n0d&z50yUmt4kOJMcQot~aHTWIoyWGpn5(p1H%7HLW3PpIa9j)|T~E=(KP`pCjyu!Dnx;MP{zxw$zX zmYR)_kdS}mRkO0Pj9Xe-Hs>3~zDfhP0YNXebby({k@by@@cZ92x5nDDvx76Q-aIA3 za-YK~1C|8N{qA>E9fR)pSJOX3jn9OvhuSLPgBLb0wPX{rv*X@T^RWmECoRsc_9K0> z+6Vnx@?;9Dz9Gd6RUz7#u~b307NqgLZ$OE4@%Gl{X9h E9~7y-K>z>% delta 2945 zcmbVLdpr|*8=p}w(UIns+svi8ZiZpzvfM^FC)Pz*X2{*F#Sn8p?#xtm1HCQ9wh1s8 zP4eb9|tF|F|je6J*YxXx|Ix8s^bdOfaYd$} z{ENSfpI(ze)~0$+FdA1Q{`lXq{Vw(Vn#`(EQ* z_nf=#(&1R1sm6ND@%WG+ry=Zn?J1SqR_B8%;^fCAQhrOJ&uT;#x0QA`{!9cnewre^ zYMQ^(7@jf5S^K#0P_f^#e`Ta;(PAvRc#Jn%d-<}=o-sU04pLgX7f%p90K(2%W zvZSd=Nl#C&pyPtdc^8*G+B!PMnLNvvESBbV77GXvFIW(`Dk6H#&<%&9cT#KF%@1M; zgHAd5c^UJ5;*c_$h^C9?(f<8;A-~L?s%m@9#iTSkobp3! zhDZnkG8!5hiY3%IqQko|RO{;%edVb`4z};s=gzc!xR|!g9BFQj$uKV(E8+s@(n!%O zmaUsfQqfz?tO`5zrihx<EDw&1I#W-TiBm2n7NqX{>9WCHdL2iH$A-7kQnS9Ph=Ft?k2^FJxZ0z9I; zH4r}P0wr|z0vJQyxn&VnvIrADD=uYwE(eTC5PB@Hp2GRRQ^~aY36^B#OiXOMhAP@+ z2)*FzCQfbLo2%NP?z``bunr8yKMf3A1quw$OuFe0_6bl7ME!}3-^tW?< zmfMp#qjVITmAwNXx3atp)Ml|*2Y%fMmXehf1{8EkOW@bU0fgT3Q{L7}7=YoSpiQ+j zpxg?tv}b}n5^(mI%!{m&%Q{6BH&4_n3a*x`^6_*2HqH^M>s4o5ZXb;A8u;XG{$`9K zI!uK{C;bp}iuFd~eP>vu#06>CIQu&p;yL&ey zHpq3g{f-u(tLsGKCG#I3=rq_)>v*UXvoP6sG3KVPyA$%Bf4CBaFbSHXfwd14sP4EL ztq`o_CFrx^BZ1utQtBep^F2(DuMoQhovV>E5~5PTM<(;ccVpwhDzgn@oz@}PXgU}J zF=3Dlhe{t%rZMqCaolVD%)=yifatY-AMD0F|4et#z)DI(Ve7lgPojT~6LPT*3f3Ot zUa?Y75r6oS72g-_h>52XMT|{uAo=YHLNsfT-NqE`lIkl~-u&Fk-o^7HxnL=H$5N|G z04)dAQ-;pxg8G|%IPNJoT1H!g9O8*stYhcFqY~Id3<>aEZeB}tU*v}4?TA-U2%)7j z=uuPM6@b(rqB=5Ll*u$rj9;!$ondiT{|uXyjHz9}keJltPHj9C56#KH95Q{kqH)o!|2N5_e? zl#BISkZ7v7G^(OU%s^RG;aqV>nSJ?lkEKVgI{Fv*4$4Km*+eaZg`6$OKDzwNhaXgx zD_vqx-}bin7jZ` zQ}eFm>gtkpITfXcwC;QN11%FagLRycEqYN^qM4Grr%Kh>z3y3m&SIiY>lO_!VmR4urF6ia19_HjbO6+)wXwqqxp?d@OoMT@0)-cz4h2ek0@PFd^X3nV-9yd{`e>U+w5V>W z5F+44bG|&=kEFe~og-}G6RoLFa;2U{*&;|X6Ev0SpNC9DrPLRm9jW341S=h!Ymt`C zMq_ipIj7!z*z2^gtK`IG|2&!KYhY{8CE0AJ0TGwD0y}rdq~%E$i7lD;3Ap9-=4WQ< zAlYmU#@Mk9%OoM1>QnBK>IRrrSFLGc0mGH?aS{`c1*btQxivo`lc@UjvpA<0pI>=M z*XxNw1PJ0M4yFlM1%!?amF~I%U8UjN(xgv)Rs^RRAjHN#PauNLr4ns|UF4m#!=ip{ zy*ka>;ORh6GTx1&oxPpK<8@c*DB%vdVyJ#XoIH{3v)@Ul+@{;P0mtwAScjkC@Rx`S z*&Ln#6SB5%P!xhHEF zKxM#-XoDwDo_Pfg}zpqYEi-C z?R&>Q?|`t%2oFr&xmWE@N_qWS(%xa4 z*xP%kkC`>V<%%= Date: Tue, 9 Apr 2024 16:47:58 -0400 Subject: [PATCH 66/74] Fix a puzzle file --- .../5x5 Star Battle 1 star Normal 1.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml index 5fefc844f..cccf07c95 100644 --- a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml +++ b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml @@ -1,7 +1,7 @@ - + @@ -9,6 +9,7 @@ + @@ -26,6 +27,7 @@ + @@ -45,9 +47,6 @@ - - - \ No newline at end of file From 1db4a7a426a50b7b7b6947435be2c458d8af778e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 9 Apr 2024 17:46:10 -0400 Subject: [PATCH 67/74] Fixed some bugs with the rules --- .../5x5 Star Battle 1 star Normal 1.xml | 1 + .../edu/rpi/legup/puzzle/starbattle/StarBattleCell.java | 2 +- .../legup/puzzle/starbattle/rules/BlackoutDirectRule.java | 2 +- .../starbattle/rules/FinishWithStarsDirectRule.java | 2 +- .../puzzle/starbattle/rules/StarOrEmptyCaseRule.java | 4 ++-- .../puzzle/starbattle/rules/SurroundStarDirectRule.java | 2 +- .../starbattle/rules/TooManyStarsContradictionRule.java | 4 ++-- .../starbattle.rules/BlackoutDirectRule/ColumnBlackout | 8 ++++++-- 8 files changed, 15 insertions(+), 10 deletions(-) diff --git a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml index cccf07c95..b86e16ff2 100644 --- a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml +++ b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml @@ -49,4 +49,5 @@ + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index 4296c8230..a316872d9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -13,7 +13,7 @@ public class StarBattleCell extends GridCell { /** * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement * - * @param valueInt value of the star battle cell denoting its state + * @param value value of the star battle cell denoting its state * @param location location of the cell on the board * @param groupIndex indicates what group # the cell is in. * @param size size of the star battle cell diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java index 45c550bde..75fbaadb6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -42,7 +42,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); - modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR); + modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR.value); if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { return "Black cells must be placed in a row, region, or column with enough stars!"; } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java index f6b7eef86..36e691e74 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -42,7 +42,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); - modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.BLACK); + modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.BLACK.value); if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { return "Star cells must be placed in a row, region, or column without extra spaces!"; } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java index 035e55073..0aa147c6f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java @@ -83,13 +83,13 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); Board case1 = board.copy(); PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); - data1.setData(StarBattleCellType.STAR); + data1.setData(StarBattleCellType.STAR.value); case1.addModifiedData(data1); cases.add(case1); Board case2 = board.copy(); PuzzleElement data2 = case2.getPuzzleElement(puzzleElement); - data2.setData(StarBattleCellType.BLACK); + data2.setData(StarBattleCellType.BLACK.value); case2.addModifiedData(data2); cases.add(case2); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index 5d42e895e..e1c6f3084 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -41,7 +41,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } StarBattleBoard modified = (StarBattleBoard) origBoard.copy(); - modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR); + modified.getPuzzleElement(puzzleElement).setData(StarBattleCellType.STAR.value); if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { return "Black cells must be placed adjacent to a star!"; } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index 880e3b8ac..2ae424d45 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -99,14 +99,14 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { for (int k = 0; k < cellList.size(); k++) { if (cellList.get(k).getType() == StarBattleCellType.STAR) { starCount++; - if (starCount >= puzzleNum) { + if (starCount > puzzleNum) { valid = false; break; } } } - if (valid == false) { + if (valid) { return super.getNoContradictionMessage(); } return null; diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout index f2a5b42d9..ddcc4dc9a 100644 --- a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout @@ -1,5 +1,6 @@ - - + + + @@ -25,12 +26,15 @@ + + + \ No newline at end of file From 4a502d5330a122c20e2fe01cfff5373831ead9db Mon Sep 17 00:00:00 2001 From: Chase-Grajeda Date: Tue, 16 Apr 2024 17:26:22 -0400 Subject: [PATCH 68/74] Checkstyle update Silly checkstyle change --- .../java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java index 099cabfcd..730f9291f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java @@ -18,8 +18,9 @@ public StarBattleRegion copy() { public int numStars() { int stars = 0; for (StarBattleCell c: regionCells) { - if (c.getType() == StarBattleCellType.STAR) + if (c.getType() == StarBattleCellType.STAR) { ++stars; + } } return stars; } From e7ea94483b15feec13e44a65cb8af7e738aaa4e3 Mon Sep 17 00:00:00 2001 From: Chase-Grajeda Date: Tue, 16 Apr 2024 17:31:04 -0400 Subject: [PATCH 69/74] Update StarBattleBoard.java Another checkstyle fix --- .../edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index ce04f31a6..2c4a20ebc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -73,8 +73,9 @@ public int columnStars(int columnIndex) { int stars = 0; if (columnIndex < size) { for (StarBattleCell c: this.getCol(columnIndex)) { - if (c.getType() == StarBattleCellType.STAR) + if (c.getType() == StarBattleCellType.STAR) { ++stars; + } } } return stars; @@ -84,8 +85,9 @@ public int rowStars(int rowIndex) { int stars = 0; if (rowIndex < size) { for (StarBattleCell c: this.getRow(rowIndex)) { - if (c.getType() == StarBattleCellType.STAR) + if (c.getType() == StarBattleCellType.STAR) { ++stars; + } } } return stars; @@ -97,8 +99,9 @@ public StarBattleBoard copy() { for (int y = 0; y < this.dimension.height; y++) { copy.setCell(x, y, getCell(x, y).copy()); } - if (x < this.regions.size()) + if (x < this.regions.size()) { copy.regions.add(this.getRegion(x).copy()); + } } for (PuzzleElement e : modifiedData) { copy.getPuzzleElement(e).setModifiable(false); From 8b2397ab1abbd3ffe203406d7a6bad0995254913 Mon Sep 17 00:00:00 2001 From: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Date: Sun, 5 May 2024 13:38:18 -0400 Subject: [PATCH 70/74] Tree Tent Test Suite (#775) * TentForTree test cases One valid case of connecting a tree to a tent One invalid case of connecting a tree to a tent when there is another valid tent * Incomplete test for already connected tree * Test for Fill in Row Case Rule Looks like the case rule was implemented incorrectly :/ * Fill in Row Test Cases Actually case generator works properly * Fill in Row Test Cases When the clue is that there are 0 tents, the case returned should be equivalent to FinishWithGrass. * Comments * Fix TreeTent Puzzle Editor * LinkTentCaseRule Four Trees Test Case Expected to create four cases, each one connecting a line between the tree in the center and one of the four surrounding trees. * LinkTentCaseRule Various Test Cases With one tree, one case is created. With no trees, no cases are created. With diagonal trees, no cases are created. * TreeTent Puzzle Editor Fix Removed double-calling setCell due to custom and super mouseReleased * LinkTreeCaseRule Test Cases Similar test cases to LinkTentCaseRule - Make sure diagonal tents have no bearing on cases - Make sure no tents around the tree fails - One tent should connect to the correct tree - Two tents should create two cases - No other cases to check, as there cannot be more than two tents around a tree; otherwise, the tents would touch * Reset puzzle * Improved FillInRowCaseRule Rewrote recursive generate board function to be slightly more optimized * Fix FillInRowCaseRule * Implement Copy Clue function Fixes a bug where you couldn't apply the FillInRow Case Rule on a child node * Allow Importing Puzzle-Specific Puzzle Elements When checking the proof tree, the Puzzle Importer only checks for changed cells. However, for TreeTent, the user can create lines, which cannot be saved as a "cell" object. Now, an importer class can override the getImporterElements function to specify what types of changes the board may experience. * Fix Puzzles Uneven Sides Simple mistake in calculation of index * Check Rule on Columns Adjust all simple cases to also check the central column rule * Change test puzzle names * FillInRowCaseRule Test Checks that the rule works on an empty 5x5 board * TreeForTent tests and TentForTree tests * Minor formatting changes * Fix boards with uneven side lengths * Comments * Standardizing Comments * Standardizing Comments All Direct Rules * Fix Build Issues The Ubuntu and Checkstyle Autocompilers have trouble opening this file for some reason * Comment out problematic file * Uncommenting problematic test * Update file name * Commented out Star Battle test --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 --- .../treetent/8x8 TreeTent Easy/1646651 | 5 +- .../edu/rpi/legup/model/PuzzleImporter.java | 18 +- .../legup/puzzle/treetent/TreeTentBoard.java | 46 +- .../puzzle/treetent/TreeTentCellFactory.java | 2 +- .../legup/puzzle/treetent/TreeTentClue.java | 2 +- .../puzzle/treetent/TreeTentController.java | 8 +- .../puzzle/treetent/TreeTentExporter.java | 2 +- .../puzzle/treetent/TreeTentImporter.java | 13 +- .../treetent/rules/FillinRowCaseRule.java | 106 ++-- .../rules/BlackoutDirectRuleTest.java | 140 +++--- .../rules/EmptyFieldDirectRuleTest.java | 169 ++----- .../treetent/rules/FillinRowCaseRuleTest.java | 464 ++++++++++++++++++ .../rules/FinishWithGrassDirectRuleTest.java | 136 +++-- .../rules/FinishWithTentsDirectRuleTest.java | 143 +++--- .../rules/LastCampingSpotDirectRuleTest.java | 72 +-- .../treetent/rules/LinkTentCaseRuleTest.java | 191 +++++++ .../treetent/rules/LinkTreeCaseRuleTest.java | 192 ++++++++ .../SurroundTentWithGrassDirectRuleTest.java | 54 +- .../rules/TentForTreeDirectRuleTest.java | 171 ++++++- .../TooManyTentsContradictionRuleTest.java | 2 +- .../TouchingTentsContradictionRuleTest.java | 86 +--- .../rules/TreeForTentDirectRuleTest.java | 163 +++++- .../EmptyRow3x3OneTent} | 5 +- .../EmptyRow3x3ThreeTent} | 5 +- .../EmptyRow3x3TwoTent} | 11 +- .../EmptyRow3x3ZeroTent} | 1 - .../FillinRowCaseRule/EmptyRow5x5TwoTent | 23 + .../FillinRowCaseRule/PartialFillOneTent | 29 ++ .../treetent/rules/LinkTentCaseRule/DiagTrees | 26 + .../rules/LinkTentCaseRule/FourTreesOneTent | 26 + .../treetent/rules/LinkTentCaseRule/NoTrees | 22 + .../rules/LinkTentCaseRule/OneTreeOneTent | 23 + .../treetent/rules/LinkTreeCaseRule/DiagTents | 26 + .../treetent/rules/LinkTreeCaseRule/NoTents | 22 + .../treetent/rules/LinkTreeCaseRule/OneTent | 23 + .../treetent/rules/LinkTreeCaseRule/TwoTents | 24 + .../rules/TentForTreeDirectRule/ArbitraryTree | 28 ++ .../rules/TentForTreeDirectRule/ConnectedTent | 31 ++ .../TentForTreeDirectRule/OneTreeOneTent | 28 ++ .../OneTreeTwoAdjacentTent | 28 ++ .../TouchingTentsMixedDownRight | 20 - .../TouchingTentsMixedUpLeft | 20 - .../TouchingTentsMixedUpRight | 20 - .../TouchingTentsTreeDiagonal | 2 +- .../rules/TreeForTentDirectRule/ArbitraryTent | 28 ++ .../rules/TreeForTentDirectRule/ConnectedTree | 31 ++ .../TreeForTentDirectRule/OneTentOneTree | 28 ++ .../OneTentTwoAdjacentTrees | 28 ++ 48 files changed, 2155 insertions(+), 588 deletions(-) create mode 100644 src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java create mode 100644 src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java create mode 100644 src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java rename src/test/resources/puzzles/treetent/rules/{EmptyFieldDirectRule/EmptyFieldFailLeft => FillinRowCaseRule/EmptyRow3x3OneTent} (78%) rename src/test/resources/puzzles/treetent/rules/{EmptyFieldDirectRule/EmptyFieldFailRight => FillinRowCaseRule/EmptyRow3x3ThreeTent} (78%) rename src/test/resources/puzzles/treetent/rules/{TouchingTentsContradictionRule/TouchingTentsMixedDownLeft => FillinRowCaseRule/EmptyRow3x3TwoTent} (57%) rename src/test/resources/puzzles/treetent/rules/{EmptyFieldDirectRule/EmptyFieldFailBottom => FillinRowCaseRule/EmptyRow3x3ZeroTent} (92%) create mode 100644 src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow5x5TwoTent create mode 100644 src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/PartialFillOneTent create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/DiagTrees create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/FourTreesOneTent create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/NoTrees create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/OneTreeOneTent create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/DiagTents create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/NoTents create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/OneTent create mode 100644 src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/TwoTents create mode 100644 src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree create mode 100644 src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ConnectedTent create mode 100644 src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent create mode 100644 src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent delete mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight delete mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft delete mode 100644 src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight create mode 100644 src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent create mode 100644 src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree create mode 100644 src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree create mode 100644 src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees diff --git a/puzzles files/treetent/8x8 TreeTent Easy/1646651 b/puzzles files/treetent/8x8 TreeTent Easy/1646651 index 36dbc7f1e..db70ca164 100644 --- a/puzzles files/treetent/8x8 TreeTent Easy/1646651 +++ b/puzzles files/treetent/8x8 TreeTent Easy/1646651 @@ -1,5 +1,6 @@ - + + @@ -38,5 +39,5 @@ - + diff --git a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java index 0cc163200..c22831c8d 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -6,6 +6,8 @@ import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.save.InvalidFileFormatException; + +import java.lang.reflect.Array; import java.util.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -130,6 +132,19 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { public abstract void initializeBoard(String[] statements) throws UnsupportedOperationException, IllegalArgumentException; + /** + * Used to check that elements in the proof tree are saved properly. + *

Make sure the list elements are lowercase + * + * @return A list of elements that will change when solving the puzzle + * * e.g. {"cell"}, {"cell", "line"} + */ + public List getImporterElements() { + List elements = new ArrayList<>(); + elements.add("cell"); + return elements; + } + /** * Creates the proof for building * @@ -379,7 +394,8 @@ protected void makeTransitionChanges(TreeTransition transition, Node transElemen NodeList cellList = transElement.getChildNodes(); for (int i = 0; i < cellList.getLength(); i++) { Node node = cellList.item(i); - if (node.getNodeName().equalsIgnoreCase("cell")) { + List elements = getImporterElements(); + if (elements.contains(node.getNodeName().toLowerCase())) { Board board = transition.getBoard(); PuzzleElement cell = puzzle.getFactory().importCell(node, board); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java index 09706f92a..6ded23a18 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -3,6 +3,8 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.tree.Tree; + import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -53,21 +55,29 @@ public TreeTentCell getCell(int x, int y) { @Override public PuzzleElement getPuzzleElement(PuzzleElement element) { - switch (element.getIndex()) { - case -2: - return element; - case -1: - TreeTentLine line = (TreeTentLine) element; - TreeTentLine thisLine = null; - for (TreeTentLine l : lines) { - if (line.compare(l)) { - thisLine = l; - break; - } - } - return thisLine; - default: - return super.getPuzzleElement(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 == -1) { + lines.add((TreeTentLine) puzzleElement); + } else if (index < puzzleElements.size()) { + puzzleElements.set(index, puzzleElement); + } + } + + @Override + public void notifyChange(PuzzleElement puzzleElement) { + int index = puzzleElement.getIndex(); + if (index == -1) { + lines.add((TreeTentLine) puzzleElement); + } else if (index < puzzleElements.size()) { + puzzleElements.set(index, puzzleElement); } } @@ -168,20 +178,20 @@ public List getDiagonals(TreeTentCell cell, TreeTentType type) { * * @param index the row or column number * @param type type of TreeTent element - * @param isRow boolean value beased on whether a row of column is being checked + * @param isRow boolean value based on whether a row of column is being checked * @return List of TreeTentCells that match the given TreeTentType */ public List getRowCol(int index, TreeTentType type, boolean isRow) { List list = new ArrayList<>(); if (isRow) { - for (int i = 0; i < dimension.height; i++) { + for (int i = 0; i < dimension.width; i++) { TreeTentCell cell = getCell(i, index); if (cell.getType() == type) { list.add(cell); } } } else { - for (int i = 0; i < dimension.width; i++) { + for (int i = 0; i < dimension.height; i++) { TreeTentCell cell = getCell(index, i); if (cell.getType() == type) { list.add(cell); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java index 1697b8bd5..a3553940d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentCellFactory.java @@ -39,7 +39,7 @@ public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormat } TreeTentCell cell = new TreeTentCell(TreeTentType.valueOf(value), new Point(x, y)); - cell.setIndex(y * height + x); + cell.setIndex(y * width + x); return cell; } else { if (node.getNodeName().equalsIgnoreCase("line")) { diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClue.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClue.java index bcba7dc94..7b93f1301 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClue.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentClue.java @@ -50,6 +50,6 @@ public void setType(TreeTentType type) { } public TreeTentClue copy() { - return null; + return new TreeTentClue(data, clueIndex, type); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java index 79e074657..667c2ba7d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentController.java @@ -32,8 +32,10 @@ public TreeTentController() { public void mousePressed(MouseEvent e) { if (e.getButton() != MouseEvent.BUTTON2) { BoardView boardView = getInstance().getLegupUI().getBoardView(); - dragStart = boardView.getElement(e.getPoint()); - lastCellPressed = boardView.getElement(e.getPoint()); + if (boardView != null) { + dragStart = boardView.getElement(e.getPoint()); + lastCellPressed = boardView.getElement(e.getPoint()); + } } } @@ -105,6 +107,8 @@ public void mouseReleased(MouseEvent e) { } dragStart = null; lastCellPressed = null; + } else { + super.mouseReleased(e); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java index 475aaab1e..82c1b373d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentExporter.java @@ -52,7 +52,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element axisSouth = newDocument.createElement("axis"); axisSouth.setAttribute("side", "south"); - for (TreeTentClue clue : board.getRowClues()) { + for (TreeTentClue 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())); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java index 0117f41ce..c791617ce 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java @@ -3,6 +3,9 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; import java.awt.*; +import java.util.ArrayList; +import java.util.List; + import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -113,7 +116,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { for (int x = 0; x < width; x++) { if (treeTentBoard.getCell(x, y) == null) { TreeTentCell cell = new TreeTentCell(TreeTentType.UNKNOWN, new Point(x, y)); - cell.setIndex(y * height + x); + cell.setIndex(y * width + x); cell.setModifiable(true); treeTentBoard.setCell(x, y, cell); } @@ -216,4 +219,12 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { 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; + } } diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index 0116c0dcd..a796c992a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -1,9 +1,11 @@ package edu.rpi.legup.puzzle.treetent.rules; +import edu.rpi.legup.model.Puzzle; 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.Tree; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; @@ -11,7 +13,9 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import java.awt.*; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; public class FillinRowCaseRule extends CaseRule { @@ -61,7 +65,7 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - ArrayList cases = new ArrayList(); + ArrayList cases; List group; int tentsLeft; TreeTentClue clue = ((TreeTentClue) puzzleElement); @@ -70,7 +74,7 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { if (clue.getType() == TreeTentType.CLUE_SOUTH) { group = tBoard.getRowCol(clueIndex, TreeTentType.UNKNOWN, false); tentsLeft = - tBoard.getRowClues().get(clueIndex).getData() + tBoard.getColClues().get(clueIndex).getData() - tBoard.getRowCol(clueIndex, TreeTentType.TENT, false).size(); cases = genCombinations(tBoard, group, tentsLeft, clueIndex, false); } else { @@ -83,60 +87,100 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { // generate every combination (nCr) // call goodBoard for each generated combination - // alternitive would be to implement collision avoidance while generating instead of after + // alternative would be to implement collision avoidance while generating instead of after if (cases.size() > 0) { return cases; } return null; } + /** + * + * @param iBoard the board to place tents onto + * @param tiles the locations where tents can be placed + * @param target the target number of tents to place + * @param index the index of tiles which is trying to be placed + * @param isRow Used to check validity of board + * @return the list of boards created + */ private ArrayList genCombinations( TreeTentBoard iBoard, List tiles, int target, Integer index, boolean isRow) { - return genCombRecursive( - iBoard, tiles, tiles, target, 0, new ArrayList(), index, isRow); + ArrayList generatedBoards = new ArrayList<>(); + genCombRecursive( + iBoard, tiles, target, 0, new ArrayList(), 0, index, generatedBoards, isRow); + return generatedBoards; } - private ArrayList genCombRecursive( + /** + * + * Recursive function to generate all ways of placing the target number of tents + * from the list of tiles to fill. + * + * @param iBoard The board + * @param tiles Unknown Tiles to fill + * @param target number of tents to place + * @param current number of tents already placed + * @param currentTile index of the next tile to add + * @param selected the cells which have tents + * @param index The index of the clue + * @param isRow Used for checking if the board is good + * + * The generated boards are placed into generatedBoards (passed by reference) + */ + + private void genCombRecursive( TreeTentBoard iBoard, - List original, List tiles, int target, int current, List selected, + int currentTile, Integer index, + ArrayList generatedBoards, boolean isRow) { - ArrayList b = new ArrayList<>(); + // Base Case: Enough tents have been placed if (target == current) { - TreeTentBoard temp = iBoard.copy(); - for (TreeTentCell c : original) { - if (selected.contains(c)) { - PuzzleElement change = temp.getPuzzleElement(c); - change.setData(TreeTentType.TENT); - temp.addModifiedData(change); - } else { - PuzzleElement change = temp.getPuzzleElement(c); - change.setData(TreeTentType.GRASS); - temp.addModifiedData(change); + TreeTentBoard boardCopy = iBoard.copy(); + // Selected Tiles should already be filled + // Fill in other tiles with Grass + for (TreeTentCell tile : tiles) { + if (!selected.contains(tile)) { + PuzzleElement element = boardCopy.getPuzzleElement(tile); + element.setData(TreeTentType.GRASS); + boardCopy.addModifiedData(element); } } - if (goodBoard(temp, index, isRow)) { - b.add(temp); - } - return b; + // board validity is checked after placing every tent + // because the base case doesn't place any tents, the board + // should still be valid + generatedBoards.add(boardCopy); + return; } - for (int i = 0; i < tiles.size(); ++i) { - List sub = tiles.subList(i + 1, tiles.size()); - List next = new ArrayList(selected); - next.add(tiles.get(i)); - b.addAll( - genCombRecursive( - iBoard, original, sub, target, current + 1, next, index, isRow)); + + // Recursive Case: + // Looking at the group of possible tiles, save one of the tiles into selected, + // Place it on the board, + // Check if the board is good and recurse + // + // Backtracking: + // Remove the placed tent from the board and selected + for (int i = currentTile; i < tiles.size(); ++i){ + TreeTentCell tile = tiles.get(i); + selected.add(tile); + PuzzleElement element = iBoard.getPuzzleElement(tile); + element.setData(TreeTentType.TENT); + iBoard.addModifiedData(element); + if (goodBoard(iBoard, index, isRow)) { + genCombRecursive(iBoard, tiles, target, current + 1, selected, i + 1, index, generatedBoards, isRow); + } + element.setData(TreeTentType.UNKNOWN); + iBoard.addModifiedData(element); + selected.remove(tile); } - return b; } // Effectively runs TouchingTents check on all the added tents to make sure that the proposed @@ -153,7 +197,7 @@ private boolean goodBoard(TreeTentBoard board, Integer index, boolean isRow) { for (TreeTentCell t : tents) { List adj = board.getAdjacent(t, TreeTentType.TENT); List diag = board.getDiagonals(t, TreeTentType.TENT); - if (adj.size() > 0 || diag.size() > 0) { + if (!adj.isEmpty() || !diag.isEmpty()) { return false; } } diff --git a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java index d42f40c87..7789b273b 100644 --- a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java +++ b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java @@ -1,69 +1,73 @@ -package puzzles.starbattle.rules; +// This test is for a puzzle that is not fully implemented yet and is causing issues. +// Commenting this out for now, but once Star Battle is fully implemented this should +// be uncommented and finished. -import edu.rpi.legup.puzzle.nurikabe.Nurikabe; -import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; -import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; -import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -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.puzzle.starbattle.StarBattle; -import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; -import edu.rpi.legup.puzzle.starbattle.StarBattleCell; -import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; -import edu.rpi.legup.puzzle.starbattle.rules.BlackoutDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; - -import java.awt.*; - -public class BlackoutDirectRuleTest { - - private static final BlackoutDirectRule RULE = new BlackoutDirectRule(); - private static StarBattle starbattle; - - @BeforeClass - public static void setUp() { - MockGameBoardFacade.getInstance(); - starbattle = new StarBattle(); - } - - @Test - public void BlackoutDirectRuleTest_ColumnBlackout() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/starbattle/rules/BlackoutDirectRule/ColumnBlackout", starbattle); - TreeNode rootNode = starbattle.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - StarBattleBoard board = (StarBattleBoard) transition.getBoard(); - - StarBattleCell cell1 = board.getCell(1, 1); - cell1.setData(StarBattleCellType.BLACK.value); - StarBattleCell cell2 = board.getCell(1, 2); - cell2.setData(StarBattleCellType.BLACK.value); - StarBattleCell cell3 = board.getCell(1, 3); - cell3.setData(StarBattleCellType.BLACK.value); - - board.addModifiedData(cell1); - board.addModifiedData(cell2); - board.addModifiedData(cell3); - - Assert.assertNull(RULE.checkRule(transition)); - - for (int i = 0; i < board.getHeight(); i++) { - for (int k = 0; k < board.getWidth(); k++) { - Point point = new Point(k, i); - if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || point.equals(cell3.getLocation())) { - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - else { - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); - } - } - } - } -} +//package puzzles.starbattle.rules; +// +//import edu.rpi.legup.puzzle.nurikabe.Nurikabe; +//import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +//import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +//import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +//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.puzzle.starbattle.StarBattle; +//import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +//import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +//import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; +//import edu.rpi.legup.puzzle.starbattle.rules.BlackoutDirectRule; +//import edu.rpi.legup.save.InvalidFileFormatException; +// +//import java.awt.*; +// +//public class BlackoutDirectRuleTest { +// +// private static final BlackoutDirectRule RULE = new BlackoutDirectRule(); +// private static StarBattle starbattle; +// +// @BeforeClass +// public static void setUp() { +// MockGameBoardFacade.getInstance(); +// starbattle = new StarBattle(); +// } +// +// @Test +// public void BlackoutDirectRuleTest_ColumnBlackout() throws InvalidFileFormatException { +// TestUtilities.importTestBoard("puzzles/starbattle/rules/BlackoutDirectRule/ColumnBlackout", starbattle); +// TreeNode rootNode = starbattle.getTree().getRootNode(); +// TreeTransition transition = rootNode.getChildren().get(0); +// transition.setRule(RULE); +// +// StarBattleBoard board = (StarBattleBoard) transition.getBoard(); +// +// StarBattleCell cell1 = board.getCell(1, 1); +// cell1.setData(StarBattleCellType.BLACK.value); +// StarBattleCell cell2 = board.getCell(1, 2); +// cell2.setData(StarBattleCellType.BLACK.value); +// StarBattleCell cell3 = board.getCell(1, 3); +// cell3.setData(StarBattleCellType.BLACK.value); +// +// board.addModifiedData(cell1); +// board.addModifiedData(cell2); +// board.addModifiedData(cell3); +// +// Assert.assertNull(RULE.checkRule(transition)); +// +// for (int i = 0; i < board.getHeight(); i++) { +// for (int k = 0; k < board.getWidth(); k++) { +// Point point = new Point(k, i); +// if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || point.equals(cell3.getLocation())) { +// Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); +// } +// else { +// Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); +// } +// } +// } +// } +//} diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java index 38284c410..8ffb2ee4f 100644 --- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java @@ -26,11 +26,17 @@ public static void setUp() { treetent = new TreeTent(); } - // creates a 3x3 puzzle with no trees - // make the (1,1) tile GRASS - // checks if tiles logically follow the EmptyFieldDirectRule - @Test - public void EmptyFieldTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + *

Empty + * XXX + * XGX + * XXX + *

Makes the (1, 1) tile GRASS + * Checks if the rule correctly detects no trees around the grass tile + */ + @Test + public void EmptyFieldTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyField", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -64,12 +70,17 @@ public void EmptyFieldTest() throws InvalidFileFormatException { } } - // creates a 3x3 puzzle with 4 trees - // trees are at (0,0), (2,0), (0,2), and (2,2) - // make the (1,1) tile GRASS. - // checks if tiles logically follow the EmptyFieldDirectRule - @Test - public void DiagonalTreeTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + *

Trees are at (0, 0), (2, 0), (0, 2), and (2, 2) + * RXR + * XGX + * RXR + *

Makes the (1, 1) tile GRASS + * Checks if the rule correctly ignores the trees on the diagonals + */ + @Test + public void DiagonalTreeTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/DiagonalTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -103,12 +114,17 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { } } - // creates a 3x3 puzzle with 4 trees - // trees are at (0,1), (1,0), (1,2), and (2,1) - // make the (1,1) tile GRASS. - // checks if tiles don't logically follow the EmptyFieldDirectRule - @Test - public void EmptyFieldTestFail() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + *

Trees are at (0, 1), (1, 0), (1, 2), and (2, 1) + * XRX + * RGR + * XRX + *

Makes the (1, 1) tile GRASS + * Checks if the rule is not valid when there are adjacent trees + */ + @Test + public void EmptyFieldTestFail() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -137,12 +153,17 @@ public void EmptyFieldTestFail() throws InvalidFileFormatException { } } - // creates a 3x3 puzzle with 1 tree - // tree is at (1,0) - // make the (1,1) tile GRASS. - // checks if tiles don't logically follow the EmptyFieldDirectRule - @Test - public void EmptyFieldTestFailTop() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + *

Tree at (1, 0) + * XRX + * XGX + * XXX + *

Makes the (1, 1) tile GRASS + * Checks if the rule is not valid when there is one adjacent tree + */ + @Test + public void EmptyFieldTestFailTop() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -170,106 +191,4 @@ public void EmptyFieldTestFailTop() throws InvalidFileFormatException { } } } - - // creates a 3x3 puzzle with 1 tree - // tree is at (1,2) - // make the (1,1) tile GRASS. - // checks if tiles don't logically follow the EmptyFieldDirectRule - @Test - public void EmptyFieldTestFailTopBottom() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom", treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // get board state - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - // change the board's cells considering breaking the EmptyField rule - TreeTentCell cell1 = board.getCell(1, 1); - cell1.setData(TreeTentType.GRASS); - board.addModifiedData(cell1); - - // confirm there is not a logical following of the EmptyField rule - Assert.assertNotNull(RULE.checkRule(transition)); - - // the cells should not follow the rule - TreeTentCell c; - for (int i = 0; i < board.getWidth(); i++) { - for (int j = 0; j < board.getHeight(); j++) { - c = board.getCell(j, i); - // does not use the rule to logically follow - Assert.assertNotNull(RULE.checkRuleAt(transition, c)); - } - } - } - - // creates a 3x3 puzzle with 1 tree - // tree is at (0,1) - // make the (1,1) tile GRASS. - // checks if tiles don't logically follow the EmptyFieldDirectRule - @Test - public void EmptyFieldTestFailLeft() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft", treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // get board state - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - // change the board's cells considering breaking the EmptyField rule - TreeTentCell cell1 = board.getCell(1, 1); - cell1.setData(TreeTentType.GRASS); - board.addModifiedData(cell1); - - // confirm there is not a logical following of the EmptyField rule - Assert.assertNotNull(RULE.checkRule(transition)); - - // the cells should not follow the rule - TreeTentCell c; - for (int i = 0; i < board.getWidth(); i++) { - for (int j = 0; j < board.getHeight(); j++) { - c = board.getCell(j, i); - // does not use the rule to logically follow - Assert.assertNotNull(RULE.checkRuleAt(transition, c)); - } - } - } - - // creates a 3x3 puzzle with 1 tree - // tree is at (2,1) - // make the (1,1) tile GRASS. - // checks if tiles don't logically follow the EmptyFieldDirectRule - @Test - public void EmptyFieldTestFailRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight", treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // get board state - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - // change the board's cells considering breaking the EmptyField rule - TreeTentCell cell1 = board.getCell(1, 1); - cell1.setData(TreeTentType.GRASS); - board.addModifiedData(cell1); - - // confirm there is not a logical following of the EmptyField rule - Assert.assertNotNull(RULE.checkRule(transition)); - - // the cells should not follow the rule - TreeTentCell c; - for (int i = 0; i < board.getWidth(); i++) { - for (int j = 0; j < board.getHeight(); j++) { - c = board.getCell(j, i); - // does not use the rule to logically follow - Assert.assertNotNull(RULE.checkRuleAt(transition, c)); - } - } - } } diff --git a/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java new file mode 100644 index 000000000..71b478e7a --- /dev/null +++ b/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java @@ -0,0 +1,464 @@ +package puzzles.treetent.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.*; +import edu.rpi.legup.puzzle.treetent.rules.FillinRowCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Array; +import java.util.ArrayList; + +public class FillinRowCaseRuleTest { + private static final FillinRowCaseRule RULE = new FillinRowCaseRule(); + private static TreeTent treetent; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + treetent = new TreeTent(); + } + + /** + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles + * and a clue of 0 tents in the row. + * + *

checks that 1 case is created and that it is equivalent to FinishWithGrass rule + * May need to change checks due to issue #777 + * + * @throws InvalidFileFormatException + */ + @Test + public void TentOrTreeTestZeroTentClue() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ZeroTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + /* Test the Row */ + TreeTentClue testing_row = board.getClue(3, 1); + ArrayList cases = RULE.getCases(board, testing_row); + + // assert that one case was found + Assert.assertEquals(1, cases.size()); + + // assert the case filled the row with grass + TreeTentBoard testCase = (TreeTentBoard) cases.getFirst(); + Assert.assertEquals(3, testCase.getRowCol(1, TreeTentType.GRASS, true).size()); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (h == 1) { + continue; + } + + original_cell = board.getCell(w, h); + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + + /* Test the Column */ + TreeTentClue testing_col = board.getClue(1, 3); + cases = RULE.getCases(board, testing_col); + + // assert one case was created + Assert.assertEquals(1, cases.size()); + + // assert the case filled the column with grass + testCase = (TreeTentBoard) cases.getFirst(); + Assert.assertEquals(3, testCase.getRowCol(1, TreeTentType.GRASS, false).size()); + + // checks other cells have not been modified + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (w == 1) { + continue; + } + + original_cell = board.getCell(w, h); + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles + * and a clue of 1 tent in the row. The column rules make the board impossible, but + * they are not checked here. + * + *

checks 3 cases are created checks; + * first case is TENT tile at x=0, + * second case is TENT tile at x=1, + * and a third case is TENT tile at x=2. + * The cases can be in any order. + * Then, it checks that other cells have not been modified + * + * @throws InvalidFileFormatException + */ + @Test + public void FillInRowEmptyOneTentClue() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3OneTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + /* Test the Row */ + TreeTentClue testing_row = board.getClue(3, 1); + ArrayList cases = RULE.getCases(board, testing_row); + + // assert correct number of cases created + Assert.assertEquals(3, cases.size()); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + // Each case must have 1 tent in the row + Assert.assertEquals(1, testCase.getRowCol(1, TreeTentType.TENT, true).size()); + + // and they must have 2 grass tiles in the row + Assert.assertEquals(2, testCase.getRowCol(1, TreeTentType.GRASS, true).size()); + } + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (h == 1) { + continue; + } + + original_cell = board.getCell(w, h); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + + } + } + } + + /* Test the Column */ + TreeTentClue testing_col = board.getClue(1, 3); + cases = RULE.getCases(board, testing_col); + + // assert correct number of cases created + Assert.assertEquals(3, cases.size()); + // Only one arrangement is possible when taking into account the + // touching tents contradiction rule. + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + // Each case must have 1 tent in the column + Assert.assertEquals(1, testCase.getRowCol(1, TreeTentType.TENT, false).size()); + + // and they must have 2 grass tiles in the column + Assert.assertEquals(2, testCase.getRowCol(1, TreeTentType.GRASS, false).size()); + } + + // checks other cells have not been modified + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (w == 1) { + continue; + } + + original_cell = board.getCell(w, h); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles + * and a clue of 2 tent in the row. The column rules make the board impossible, but + * they are not checked here. + * + *

checks 1 case is created. Checks that the case is when + * there are TENT tiles at x=0 and x=2. + * Then, it checks that other cells have not been modified + * + * @throws InvalidFileFormatException + */ + @Test + public void FillInRowEmptyTwoTentClue() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3TwoTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + /* Test the Row */ + TreeTentClue testing_row = board.getClue(3, 1); + ArrayList cases = RULE.getCases(board, testing_row); + + // assert correct number of cases created + Assert.assertEquals(1, cases.size()); + // Only one arrangement is possible when taking into account the + // touching tents contradiction rule. + + TreeTentBoard testCase = (TreeTentBoard) cases.getFirst(); + + // The two side tiles are tents, + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(0, 1).getType()); + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(2, 1).getType()); + + // and the center tile is grass. + Assert.assertEquals(TreeTentType.GRASS, testCase.getCell(1, 1).getType()); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (h == 1) { + continue; + } + + original_cell = board.getCell(w, h); + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + + /* Test the Column */ + TreeTentClue testing_col = board.getClue(1, 3); + cases = RULE.getCases(board, testing_col); + + // assert correct number of cases created + Assert.assertEquals(1, cases.size()); + // Only one arrangement is possible when taking into account the + // touching tents contradiction rule. + + testCase = (TreeTentBoard) cases.getFirst(); + + // The two side tiles are tents, + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(1, 0).getType()); + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(1, 2).getType()); + + // and the center tile is grass. + Assert.assertEquals(TreeTentType.GRASS, testCase.getCell(1, 1).getType()); + + // checks other cells have not been modified + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (w == 1) { + continue; + } + + original_cell = board.getCell(w, h); + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles + * and a clue of 3 tent in the row. + * + *

checks that 0 cases are created + * + * @throws InvalidFileFormatException + */ + @Test + public void FillInRowEmptyThreeTentClue() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ThreeTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + /* Test the Row */ + TreeTentClue testing_row = board.getClue(3, 1); + ArrayList cases = RULE.getCases(board, testing_row); + + // assert there were no cases found, as filling in all tiles causes the tents to touch + Assert.assertNull(cases); + + /* Test the Column */ + TreeTentClue testing_col = board.getClue(1, 3); + cases = RULE.getCases(board, testing_row); + + Assert.assertNull(cases); + } + + /** + * empty 5x5 TreeTent puzzle Tests FillinRowCaseRule on row with 5 UNKNOWN tiles + * and a clue of 2 tents in the row. + * + *

checks that 6 cases are created and each case has the right number of tents + * + * @throws InvalidFileFormatException + */ + @Test + public void FillInRowEmpty5x5TwoTentClue() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FillinRowCaseRule/EmptyRow5x5TwoTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + // Test the Row + TreeTentClue testing_row = board.getClue(5, 2); + ArrayList cases = RULE.getCases(board, testing_row); + + // assert correct number of cases created + Assert.assertEquals(6, cases.size()); + // Only one arrangement is possible when taking into account the + // touching tents contradiction rule. + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + // Each case must have 2 tens in the row + Assert.assertEquals(2, testCase.getRowCol(2, TreeTentType.TENT, true).size()); + + // and they must have 3 grass tiles in the row + Assert.assertEquals(3, testCase.getRowCol(2, TreeTentType.GRASS, true).size()); + } + + TreeTentCell original_cell; + TreeTentCell case_cell; + + // checks other cells have not been modified + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (h == 2) { + continue; + } + + original_cell = board.getCell(w, h); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + + // Test the Column + TreeTentClue testing_col = board.getClue(2, 5); + cases = RULE.getCases(board, testing_col); + + // assert correct number of cases created + Assert.assertEquals(6, cases.size()); + // Only one arrangement is possible when taking into account the + // touching tents contradiction rule. + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + // Each case must have 2 tents in the column + Assert.assertEquals(2, testCase.getRowCol(2, TreeTentType.TENT, false).size()); + + // and they must have 4 grass tiles in the column + Assert.assertEquals(3, testCase.getRowCol(2, TreeTentType.GRASS, false).size()); + } + + // checks other cells have not been modified + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + if (w == 2) { + continue; + } + + original_cell = board.getCell(w, h); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; + + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + } + + /** + * 7x3 TreeTent puzzle Tests FillinRowCaseRule on col with 3 UNKNOWN tiles separated by grass + * tiles and a clue of 3 tents in the col. + * + *

checks that 1 case is created and that all three UNKNOWN tiles have become tents + * + * @throws InvalidFileFormatException + */ + @Test + public void FillInRowPartialFillThreeTent() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/FillinRowCaseRule/PartialFillOneTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentClue testing_col = board.getClue(1, 7); + ArrayList cases = RULE.getCases(board, testing_col); + + // assert that one case was found + Assert.assertEquals(1, cases.size()); + + // assert the case has three tents in the column + TreeTentBoard testCase = (TreeTentBoard) cases.getFirst(); + Assert.assertEquals(3, testCase.getRowCol(1, TreeTentType.TENT, false).size()); + + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(1, 1).getType()); + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(1, 3).getType()); + Assert.assertEquals(TreeTentType.TENT, testCase.getCell(1, 5).getType()); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + if (w == 1) { + continue; + } + for (int h = 0; h < board.getHeight(); h++) { + + original_cell = board.getCell(w, h); + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } +} diff --git a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java index 0783ab8b8..f37761e26 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java @@ -28,14 +28,18 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle with a tent at (0,0) Tests FinishWithGrassDirectRule on GRASS tiles - * horizontal of the tent at (1,0) and (2,0) - * - * @throws InvalidFileFormatException - */ - @Test - public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Tent at (1, 1) + * XXX x + * GTG 1 + * XXX x + * xxx + *

Makes (0, 1) and (2, 1) GRASS + * Checks if the rule detects the middle row to be filled in correctly + */ + @Test + public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -74,14 +78,18 @@ public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a tent at (0,0) Tests FinishWithGrassDirectRule on GRASS tiles - * vertical of the tent at (0,1) and (0,2) - * - * @throws InvalidFileFormatException - */ - @Test - public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Tent at (0, 0) + * TXX x + * GXX x + * GXX x + * 1xx + *

Makes (0, 1) and (0, 2) GRASS + * Checks if the rule detects the leftmost column to be filled in correctly + */ + @Test + public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -121,10 +129,14 @@ public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { } /** - * 3x3 TreeTent puzzle with a tent at (0,0) Tests FinishWithGrassDirectRule on GRASS tiles at - * (1,0), (2,0), (0,1), and (0,2) - * - * @throws InvalidFileFormatException + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Tent at (0, 0) + * TGG 1 + * GXX x + * GXX x + * 1xx + *

Makes (0, 1), (0, 2), (1, 0), and (2, 0) GRASS + * Checks if the rule detects the top row and leftmost column to be filled in correctly */ @Test public void FinishWithGrassTest() throws InvalidFileFormatException { @@ -174,14 +186,18 @@ public void FinishWithGrassTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with no tents Tests FinishWithGrassDirectRule on GRASS tiles GRASS tiles - * fill entire board - * - * @throws InvalidFileFormatException - */ - @Test - public void NoTentTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Empty + * GGG 0 + * GGG 0 + * GGG 0 + * 000 + *

Fill Board with GRASS + * Checks if the rule allows all cells to be filled when the clue for all rows and columns is zero. + */ + @Test + public void NoTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -215,14 +231,18 @@ public void NoTentTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a tent at (1,1) Tests FinishWithGrassDirectRule on GRASS tiles - * surrounding the tent at (1,0), (0,1), (2,1), and (1,2) - * - * @throws InvalidFileFormatException - */ - @Test - public void MiddleTentTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Tent at (1, 1) + * XGX x + * GTG 1 + * XGX x + * x1x + *

Makes (1, 0), (0, 1), (2, 1), and (1, 2) GRASS + * Checks if the rule correctly allows the central row and column to be filled with grass. + */ + @Test + public void MiddleTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -270,14 +290,18 @@ public void MiddleTentTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with missing tents Tests FinishWithGrassDirectRule on GRASS tiles filling - * the puzzle all GRASS tiles should fail the FinishWithGrassDirectRule - * - * @throws InvalidFileFormatException - */ - @Test - public void FailTentTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Empty + * GGG 1 + * GGG 1 + * GGG 1 + * 111 + *

Fill Board with GRASS + * Checks if the rule is not valid when a row or column does not have the required number of tents but is filled with grass + */ + @Test + public void FailTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -311,14 +335,22 @@ public void FailTentTest() throws InvalidFileFormatException { } } - /** - * 7x7 TreeTent puzzle with multiple tents spaced out Tests FinishWithGrassDirectRule on GRASS - * tiles between the tents at (0,3), (2,3), (4,3), and (6,3) - * - * @throws InvalidFileFormatException - */ - @Test - public void SpacedOutTentTest() throws InvalidFileFormatException { + /** + * 7x7 TreeTent puzzle Tests FinishWithGrassDirectRule + *

Tents at (1, 3), (3, 3), and (5, 3) + * XXXXXXX x + * XXXXXXX x + * XXXXXXX x + * TGTGTGT 4 + * XXXXXXX x + * XXXXXXX x + * XXXXXXX x + * xxxxxxx + *

Makes (0, 3), (2, 3), (4, 3), and (6, 3) GRASS + * Checks if applying the rule on row 3 is valid + */ + @Test + public void SpacedOutTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java index 652af615f..d82be3f87 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java @@ -27,14 +27,18 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle with a GRASS tile at (0,0) Tests FinishWithTentsDirectRule on TENT tiles - * horizontal of the GRASS tile at (1,0) and (2,0) - * - * @throws InvalidFileFormatException - */ - @Test - public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Grass at (0, 0) + * GTT 2 + * XXX x + * XXX x + * xxx + *

Makes (1, 0) and (2, 0) TENT + * Checks that the rule correctly fills in the first row + */ + @Test + public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents", treetent); @@ -67,14 +71,18 @@ public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a GRASS tile at (0,0) Tests FinishWithTentsDirectRule on TENT tiles - * vertical of the GRASS tile at (0,1) and (0,2) - * - * @throws InvalidFileFormatException - */ - @Test - public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Grass at (0, 0) + * GXX x + * TXX x + * TXX x + * 2xx + *

Makes (0, 1) and (0, 2) TENT + * Checks that the rule correctly fills in the first column + */ + @Test + public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents", treetent); @@ -107,14 +115,18 @@ public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a GRASS tile at (1,1) Tests FinishWithTentsDirectRule on TENT tiles - * around the GRASS tile at (1,0), (1,2), (0,1), and (2,1) - * - * @throws InvalidFileFormatException - */ - @Test - public void FinishWithTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Grass at (0, 0) + * GTT 2 + * TXX x + * TXX x + * 2xx + *

Makes (1, 0), (2, 0), (0, 1) and (0, 2) TENT + * Checks that the rule correctly fills both the first row and first column + */ + @Test + public void FinishWithTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -155,14 +167,18 @@ public void FinishWithTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a TENT tile at (1,1) Tests FinishWithTentsDirectRule on TENT tiles - * around the TENT tile at (1,0), (1,2), (0,1), and (2,1) - * - * @throws InvalidFileFormatException - */ - @Test - public void AdditionalTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Tent at (1, 1) + * XTX x + * TTT 3 + * XTX x + * x3x + *

Makes (1, 0), (0, 1), (2, 1), and (1, 2) TENT + * Checks that the rule correctly fills in the middle row and column when a tent starts at (1, 1) + */ + @Test + public void AdditionalTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -203,14 +219,18 @@ public void AdditionalTentsTest() throws InvalidFileFormatException { } } - /** - * Empty 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule on TENT tiles of entire puzzle all - * TENT tiles should fail FinishWithTentsDirectRule as no TENT tiles should be there - * - * @throws InvalidFileFormatException - */ - @Test - public void FinishWithTentsFailTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Empty + * TTT 0 + * TTT 0 + * TTT 0 + * 000 + *

Fills the board with tents + * Checks that the rule does not allow for more tents in any of the rows or columns + */ + @Test + public void FinishWithTentsFailTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -239,15 +259,18 @@ public void FinishWithTentsFailTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a TENT tile at (1,1) Tests FinishWithTentsDirectRule on TENT tiles - * around the TENT tile at (1,0), (1,2), (0,1), and (2,1) all TENT tiles should fail - * FinishWithTentsDirectRule as there were already sufficient number of TENT tiles - * - * @throws InvalidFileFormatException - */ - @Test - public void TooManyTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Tent at (1, 1) + * XTX x + * TTT 1 + * XTX x + * x1x + *

Makes (1, 0), (0, 1), (2, 1) and (1, 2) Tent + * Checks that the rule does not allow for more tents in the central row or central column + */ + @Test + public void TooManyTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -279,16 +302,18 @@ public void TooManyTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle with a TENT tile at (1,1) Tests FinishWithTentsDirectRule on TENT tiles - * around the TENT tile at (1,0), (1,2), (0,1), and (2,1) all TENT tiles should fail - * FinishWithTentsDirectRule as there are multiple configurations of the placement of the TENT - * tiles - * - * @throws InvalidFileFormatException - */ - @Test - public void AmbiguousTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + *

Tent at (1, 1) + * XTX x + * TTT 2 + * XTX x + * x2x + *

Makes (1, 0), (0, 1), (2, 1) and (1, 2) Tent + * Checks that the rule is not satisfied because there are multiple configurations of tents for the central row and central column + */ + @Test + public void AmbiguousTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java index 92d6e4a59..ad4559922 100644 --- a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java @@ -26,13 +26,17 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * @throws InvalidFileFormatException - *

Checks if a test works for an empty square above a tree which is surrounded on all - * other sides. - */ - @Test - public void EmptyFieldTest_Up() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + *

TREE at (1, 1) and (0, 1); GRASS at (1, 2) and (2, 1) + * XTX + * RRG + * XGX + *

Makes (1, 0) TENT + * Checks that a tent must be placed above the central tree + */ + @Test + public void EmptyFieldTest_Up() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -60,13 +64,17 @@ public void EmptyFieldTest_Up() throws InvalidFileFormatException { } } - /** - * @throws InvalidFileFormatException - *

Checks if a test works for an empty square below a tree which is surrounded on all - * other sides. - */ - @Test - public void EmptyFieldTest_Down() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + *

TREE at (1, 1) and (0, 1); GRASS at (1, 0) and (1, 2) + * XGX + * RRG + * XTX + *

Makes (1, 2) TENT + * Checks that a tent must be placed below the central tree + */ + @Test + public void EmptyFieldTest_Down() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -94,13 +102,17 @@ public void EmptyFieldTest_Down() throws InvalidFileFormatException { } } - /** - * @throws InvalidFileFormatException - *

Checks if a test works for an empty square to the left of a tree which is surrounded - * on all other sides. - */ - @Test - public void EmptyFieldTest_Left() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + *

TREE at (1, 1) and (2, 1); GRASS at (1, 0) and (1, 2) + * XGX + * TRR + * XGX + *

Makes (0, 1) TENT + * Checks that a tent must be placed on the left of the central tree + */ + @Test + public void EmptyFieldTest_Left() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -128,13 +140,17 @@ public void EmptyFieldTest_Left() throws InvalidFileFormatException { } } - /** - * @throws InvalidFileFormatException - *

Checks if a test works for an empty square to the right of a tree which is surrounded - * on all other sides. - */ - @Test - public void EmptyFieldTest_Right() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + *

TREE at (1, 1) and (1, 2); GRASS at (0, 1) and (1, 0) + * XGX + * GRT + * XRX + *

Makes (2, 1) TENT + * Checks that a tent must be placed to the right of the central tree + */ + @Test + public void EmptyFieldTest_Right() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java new file mode 100644 index 000000000..3ed1fd79e --- /dev/null +++ b/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java @@ -0,0 +1,191 @@ +package puzzles.treetent.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.Tree; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.*; +import edu.rpi.legup.puzzle.treetent.rules.LinkTentCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.LinkedList; + +public class LinkTentCaseRuleTest { + private static final LinkTentCaseRule RULE = new LinkTentCaseRule(); + private static TreeTent treetent; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + treetent = new TreeTent(); + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent + * with one tree surrounding it. + * + *

checks that 1 cases is with the line connecting the central tent and the tree + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentOneTreeTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTentCaseRule/OneTreeOneTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that one cases was found + Assert.assertEquals(1, cases.size()); + TreeTentBoard testCase = (TreeTentBoard) cases.getFirst(); + + TreeTentLine expectedLine = new TreeTentLine(board.getCell(1, 1), board.getCell(1, 0)); + + ArrayList lines = testCase.getLines(); + + // One line connecting the tree to the tent + Assert.assertEquals(1, lines.size()); + TreeTentLine line = lines.getFirst(); + + // Expected line + Assert.assertTrue(line.compare(expectedLine)); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + original_cell = board.getCell(w, h); + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent + * with four trees surrounding it. + * + *

checks that 4 cases are created, each of which create a line + * connecting the tent to one of the four trees without repeat. + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentFourTreesTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTentCaseRule/FourTreesOneTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that four cases were found + Assert.assertEquals(4, cases.size()); + + ArrayList expectedLines = new ArrayList<>(); + expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(1, 0))); + expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(0, 1))); + expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(2, 1))); + expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(1, 2))); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard ; + ArrayList lines = testCase.getLines(); + + // Each case should connect one line from the tent to + // one of the four trees next to it + Assert.assertEquals(1, lines.size()); + TreeTentLine line = lines.getFirst(); + + // Check to make sure that cases do not repeat + // and cover all four possibilities + boolean exists = false; + for (TreeTentLine expectedLine : expectedLines) { + if (line.compare(expectedLine)) { + expectedLines.remove(expectedLine); + exists = true; + break; + } + } + + Assert.assertTrue(exists); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + original_cell = board.getCell(w, h); + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent + * with zero trees around it. + * + *

Ensures no cases are created + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentNoTreesTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTentCaseRule/NoTrees", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that no cases were found + Assert.assertEquals(0, cases.size()); + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent + * with trees on a diagonal. + * + *

Ensures no cases are created + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentDiagTreesTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTentCaseRule/DiagTrees", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that no cases were found + Assert.assertEquals(0, cases.size()); + } +} diff --git a/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java new file mode 100644 index 000000000..fffde14b1 --- /dev/null +++ b/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java @@ -0,0 +1,192 @@ +package puzzles.treetent.rules; + +import com.sun.source.doctree.LinkTree; +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTent; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.rules.LinkTreeCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; + +public class LinkTreeCaseRuleTest { + private static final LinkTreeCaseRule RULE = new LinkTreeCaseRule(); + private static TreeTent treetent; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + treetent = new TreeTent(); + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree + * with one tent above + * + *

Ensures one case is created that connects the tree to the tent. + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentOneTentTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTreeCaseRule/OneTent", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that no cases were found + Assert.assertEquals(1, cases.size()); + TreeTentBoard testCase = (TreeTentBoard) cases.getFirst(); + + TreeTentLine expectedLine = new TreeTentLine(board.getCell(1, 1), board.getCell(1, 0)); + + ArrayList lines = testCase.getLines(); + + // One line connecting the tree to the tent + Assert.assertEquals(1, lines.size()); + TreeTentLine line = lines.getFirst(); + + // Expected line + Assert.assertTrue(line.compare(expectedLine)); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + original_cell = board.getCell(w, h); + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree + * with two tents, one on the left and one on the right. + * + *

Ensures two cases are created, one connecting the tree and the + * left tent, and one connecting the tree and the right tent. + * Because tents must be surrounded by grass, there can be at most + * two tents around a given tree. + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentTwoTentsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTreeCaseRule/TwoTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that no cases were found + Assert.assertEquals(2, cases.size()); + + ArrayList expectedLines = new ArrayList<>(); + expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(0, 1))); + expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(2, 1))); + + for (Board testCaseBoard : cases) { + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard ; + ArrayList lines = testCase.getLines(); + + // Each case should connect one line from the tent to + // either the left or right tree + Assert.assertEquals(1, lines.size()); + TreeTentLine line = lines.getFirst(); + + // Check to make sure that cases do not repeat + // and cover both possibilities + boolean exists = false; + for (TreeTentLine expectedLine : expectedLines) { + if (line.compare(expectedLine)) { + expectedLines.remove(expectedLine); + exists = true; + break; + } + } + + Assert.assertTrue(exists); + + // checks other cells have not been modified + TreeTentCell original_cell; + TreeTentCell case_cell; + + for (int w = 0; w < board.getWidth(); w++) { + for (int h = 0; h < board.getHeight(); h++) { + original_cell = board.getCell(w, h); + case_cell = testCase.getCell(w, h); + Assert.assertEquals(original_cell.getType(), case_cell.getType()); + } + } + } + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree + * with zero tents around it. + * + *

Ensures no cases are created + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentNoTreesTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTreeCaseRule/NoTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that no cases were found + Assert.assertEquals(0, cases.size()); + } + + /** + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree + * with tents on a diagonal. + * + *

Ensures no cases are created + * + * @throws InvalidFileFormatException + */ + @Test + public void LinkTentDiagTentsTest() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/LinkTreeCaseRule/NoTents", treetent); + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + TreeTentCell test_location = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, test_location); + + // assert that no cases were found + Assert.assertEquals(0, cases.size()); + } +} diff --git a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java index 7ff57a052..6177bb64c 100644 --- a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java @@ -27,12 +27,17 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * @throws InvalidFileFormatException Test to check if all adjacent and diagonals not filled - * with a tree are filled with grass - */ - @Test - public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule + *

TREE at (0, 0), (2, 0), (0, 1), (2, 1), (1, 2), and (2, 2); TENT at (1, 1) + * RGR + * RTR + * GRR + *

Makes (1, 0) and (0, 2) GRASS + * Checks that the rule detects unknown adjacent and diagonal tiles correctly + */ + @Test + public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrass", treetent); @@ -64,14 +69,17 @@ public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatExcepti } } - /** - * @throws InvalidFileFormatException - *

Test with a 3x3 board with an absolutely empty area aside from a tent in the middle - * While such a situation is an illegal treetent setup, this direct rule doesn't consider - * that aspect, so its ok in this context - */ - @Test - public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule + *

TENT at (1, 1) + * GGG + * GTG + * GGG + *

Makes all cells adjacent and diagonal to the tent GRASS + * Checks that the rule detects all adjacent and diagonal tiles correctly + */ + @Test + public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad", treetent); @@ -129,13 +137,17 @@ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileForm } } - /** - * @throws InvalidFileFormatException - *

Test to see if the rule passes even if no grass was able to be placed due to the - * presence of trees. - */ - @Test - public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule + *

TENT at (1, 1); TREE on all adjacent and diagonal tiles + * RRR + * RTR + * RRR + *

Null + * Checks that the rule correctly detects no missing tiles + */ + @Test + public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees", treetent); diff --git a/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java index 68dbeaf48..e55704ec2 100644 --- a/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java @@ -1,14 +1,167 @@ package puzzles.treetent.rules; -// This feature is no longer supported +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.*; +import edu.rpi.legup.puzzle.treetent.rules.TentForTreeDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; + public class TentForTreeDirectRuleTest { - // private static final TentForTreeBasicRule RULE = new TentForTreeBasicRule(); - // private static TreeTent treetent; - // - // @BeforeClass - // public static void setUp() { - // MockGameBoardFacade.getInstance(); - // treetent = new TreeTent(); - // } + private static final TentForTreeDirectRule RULE = new TentForTreeDirectRule(); + private static TreeTent treetent; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + treetent = new TreeTent(); + } + + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + *

TREE at (1, 0); TENT at (1, 1) + * XRX + * XTX + * XXX + *

Makes a line between (1, 0) and (1, 1) + * Checks that the rule correctly detects the central tent as the only possible connection + */ + @Test + public void TentForTreeTestOneTreeOneTentTest() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + + ArrayList lines = board.getLines(); + for (TreeTentLine l : lines) { + if (l.compare((line))) { + Assert.assertNull(RULE.checkRuleAt(transition, l)); + } else { + Assert.assertNotNull(RULE.checkRuleAt(transition, l)); + } + } + } + + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + *

TREE at (1, 0) and (1, 2); TENT at (1, 1) + * XRX + * XTX + * XRX + *

Makes a line between (1, 0) and (1, 1) + * Checks that the rule works when connecting a line between the tree at (1, 0) and tent at (1, 1) + */ + @Test + public void TentForTreeArbitraryTreeTest() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + *

TREE at (1, 0) and (1, 2); TENT at (1, 1); LINE between (1, 0) and (1, 1) + * XRX + * XTX + * XRX + *

Makes a line between (1, 1) and (1, 2) + * Checks that the rule fails for the tree at (1, 2) because there are no valid tents to connect to + */ + @Test + public void TentForTreeConnectedTent() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 2); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + + ArrayList lines = board.getLines(); + for (TreeTentLine l : lines) { + Assert.assertNotNull(RULE.checkRuleAt(transition, l)); + } + } + + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + *

TREE at (1, 1); TENT at (1, 0) and (1, 2) + * XTX + * XRX + * XTX + *

Makes a line between (1, 1) and (1, 2) + * Checks that the rule fails for the tree at (1, 1) because there are two valid tents to connect to + */ + @Test + public void TentForTreeOneTreeTwoAdjacentTent() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 2); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNotNull(RULE.checkRule(transition)); + } } diff --git a/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java index 2e542b3d2..c1dc6380a 100644 --- a/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TooManyTentsContradictionRuleTest.java @@ -29,7 +29,7 @@ public static void setUp() { TESTING BASIS: All test in this Rule use a 3x3 table. There is a Tree at (1,1) - There are tents at (0,1) and (2,2) + There are tents at (1,0) and (2,2) All Tent Counts are listed left to right or top to bottom */ diff --git a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java index 9f5455a92..e44543ab6 100644 --- a/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TouchingTentsContradictionRuleTest.java @@ -135,90 +135,6 @@ public void TouchingTentsContradictionRule_2By2Square() throws InvalidFileFormat Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } - /** - * @throws InvalidFileFormatException Tests a tent of orientation TT T - */ - @Test - public void TouchingTentsContradictionRule_UpLeft() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft", - treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - Assert.assertNull(RULE.checkContradiction(board)); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); - } - - /** - * @throws InvalidFileFormatException Tests a tent of orientation TT T - */ - @Test - public void TouchingTentsContradictionRule_UpRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight", - treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - Assert.assertNull(RULE.checkContradiction(board)); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); - } - - /** - * @throws InvalidFileFormatException Tests a tent of orientation T TT - */ - @Test - public void TouchingTentsContradictionRule_DownLeft() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft", - treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - Assert.assertNull(RULE.checkContradiction(board)); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); - } - - /** - * @throws InvalidFileFormatException Tests a tent of orientation T TT - */ - @Test - public void TouchingTentsContradictionRule_DownRight() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight", - treetent); - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - Assert.assertNull(RULE.checkContradiction(board)); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(0, 1))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); - Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - } - /** * @throws InvalidFileFormatException Tests if tree adjacent triggers a null */ @@ -254,6 +170,6 @@ public void TouchingTentsContradictionRule_TreeDiagonal() throws InvalidFileForm Assert.assertNotNull(RULE.checkContradiction(board)); Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(0, 0))); - Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(1, 1))); } } diff --git a/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java index f4ea6703b..ba1b49b8c 100644 --- a/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java @@ -1,14 +1,161 @@ package puzzles.treetent.rules; -// This feature is no longer supported +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.treetent.TreeTent; +import edu.rpi.legup.puzzle.treetent.TreeTentBoard; +import edu.rpi.legup.puzzle.treetent.TreeTentCell; +import edu.rpi.legup.puzzle.treetent.TreeTentLine; +import edu.rpi.legup.puzzle.treetent.rules.TreeForTentDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; + public class TreeForTentDirectRuleTest { - // private static final TreeForTentBasicRule RULE = new TreeForTentBasicRule(); - // private static TreeTent treetent; + private static final TreeForTentDirectRule RULE = new TreeForTentDirectRule(); + private static TreeTent treetent; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + treetent = new TreeTent(); + } + + /** + * 3x3 TreeTent puzzle Tests TreeForTentDirectRule + *

TENT at (1, 0); TREE at (1, 1) + * XTX + * XRX + * XXX + *

Makes a line between (1, 0) and (1, 1) + * Checks that the rule correctly detects the central tree as the only possible connection + */ + @Test + public void TreeForTentTestOneTreeOneTentTest() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * 3x3 TreeTent puzzle Tests TreeForTentDirectRule + *

TENT at (1, 0) and (1, 2); TREE at (1, 1) + * XTX + * XRX + * XTX + *

Makes a line between (1, 0) and (1, 1) + * Checks that the rule works when connecting a line between the tent at (1, 0) and the tree at (1, 1) + */ + @Test + public void TentForTreeWithArbitraryTreeTest() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + } + + /** + * 3x3 TreeTent puzzle Tests TreeForTentDirectRule + *

TENT at (1, 0) and (1, 2); TREE at (1, 1); LINE between (1, 0) and (1, 1) + * XTX + * XRX + * XTX + *

Makes a line between (1, 1) and (1, 2) + * Checks that the rule fails for the tent at (1, 2) because there are no valid trees to connect to + */ + @Test + public void TentForTreeConnectedTent() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 2); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + + ArrayList lines = board.getLines(); + for (TreeTentLine l : lines) { + Assert.assertNotNull(RULE.checkRuleAt(transition, l)); + } + } + + /** + * 3x3 TreeTent puzzle Tests TreeForTentDirectRule + *

TENT at (1, 1); TREE at (1, 0) and (1, 2) + * XRX + * XTX + * XRX + *

Makes a line between (1, 1) and (1, 2) + * Checks that the rule fails for the tree at (1, 1) because there are two valid trees to connect to + */ + @Test + public void TentForTreeOneTreeTwoAdjacentTent() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees", + treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 2); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); - // @BeforeClass - // public static void setUp() { - // MockGameBoardFacade.getInstance(); - // treetent = new TreeTent(); - // } + Assert.assertNotNull(RULE.checkRule(transition)); + } } diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3OneTent similarity index 78% rename from src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft rename to src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3OneTent index d19e01daf..1e1259a44 100644 --- a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailLeft +++ b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3OneTent @@ -2,16 +2,15 @@ - - + - + diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ThreeTent similarity index 78% rename from src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight rename to src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ThreeTent index bf3954964..7c352347d 100644 --- a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailRight +++ b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ThreeTent @@ -2,16 +2,15 @@ - - + - + diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3TwoTent similarity index 57% rename from src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft rename to src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3TwoTent index af4ca0831..1f4a907eb 100644 --- a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownLeft +++ b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3TwoTent @@ -1,18 +1,17 @@ - + - - - - + + - + + diff --git a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ZeroTent similarity index 92% rename from src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom rename to src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ZeroTent index 80deadaea..a13c7cc55 100644 --- a/src/test/resources/puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailBottom +++ b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow3x3ZeroTent @@ -2,7 +2,6 @@ - diff --git a/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow5x5TwoTent b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow5x5TwoTent new file mode 100644 index 000000000..ddfcf44db --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/EmptyRow5x5TwoTent @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/PartialFillOneTent b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/PartialFillOneTent new file mode 100644 index 000000000..47d06d5b9 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/FillinRowCaseRule/PartialFillOneTent @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/DiagTrees b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/DiagTrees new file mode 100644 index 000000000..4a10b61f2 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/DiagTrees @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/FourTreesOneTent b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/FourTreesOneTent new file mode 100644 index 000000000..96a06de3b --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/FourTreesOneTent @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/NoTrees b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/NoTrees new file mode 100644 index 000000000..c781cfde9 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/NoTrees @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/OneTreeOneTent b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/OneTreeOneTent new file mode 100644 index 000000000..103c1ce3e --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTentCaseRule/OneTreeOneTent @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/DiagTents b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/DiagTents new file mode 100644 index 000000000..e50bef8c1 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/DiagTents @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/NoTents b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/NoTents new file mode 100644 index 000000000..3bab63f75 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/NoTents @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/OneTent b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/OneTent new file mode 100644 index 000000000..69eb680de --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/OneTent @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/TwoTents b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/TwoTents new file mode 100644 index 000000000..7fb4b6e46 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/LinkTreeCaseRule/TwoTents @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree new file mode 100644 index 000000000..258ebec21 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ConnectedTent b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ConnectedTent new file mode 100644 index 000000000..031bca068 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/ConnectedTent @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent new file mode 100644 index 000000000..dcc428c8b --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent new file mode 100644 index 000000000..508cda0a8 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight deleted file mode 100644 index 05b7a7667..000000000 --- a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedDownRight +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft deleted file mode 100644 index b8b81c7b4..000000000 --- a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpLeft +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight deleted file mode 100644 index af7fb5df2..000000000 --- a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsMixedUpRight +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal index b2f54aef3..b25eb6c4c 100644 --- a/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal +++ b/src/test/resources/puzzles/treetent/rules/TouchingTentsContradictionRule/TouchingTentsTreeDiagonal @@ -3,7 +3,7 @@ - + diff --git a/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent new file mode 100644 index 000000000..ce083cfc0 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree new file mode 100644 index 000000000..6ebecc06f --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree new file mode 100644 index 000000000..fabfe06e2 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees new file mode 100644 index 000000000..8218fb2f1 --- /dev/null +++ b/src/test/resources/puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0e8ae65e05c3385e5e1e3a506645f75b2a80dffc Mon Sep 17 00:00:00 2001 From: Kevin Xu <35581753+kevinkxu@users.noreply.github.com> Date: Sun, 5 May 2024 13:42:20 -0400 Subject: [PATCH 71/74] Issue 784 (#790) * one region color ripple effect * wip * remove irrelevant file * wip * wip2 * save as for proof editor * proof editor fix --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- .../edu/rpi/legup/ui/ProofEditorPanel.java | 34 ++++----- .../edu/rpi/legup/ui/PuzzleEditorPanel.java | 71 +++++++++++-------- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java index 956f83ba4..8401e19f2 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -580,31 +580,31 @@ private void saveProofAs() { if (puzzle == null) { return; } - - fileDialog.setMode(FileDialog.SAVE); - fileDialog.setTitle("Save As"); - String curFileName = GameBoardFacade.getInstance().getCurFileName(); - if (curFileName == null) { - fileDialog.setDirectory( - LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); - } else { - File curFile = new File(curFileName); - fileDialog.setDirectory(curFile.getParent()); + + LegupPreferences preferences = LegupPreferences.getInstance(); + File preferredDirectory = + new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); + if (preferences.getSavedPath() != "") { + preferredDirectory = new File(preferences.getSavedPath()); } - fileDialog.setVisible(true); + folderBrowser = new JFileChooser(preferredDirectory); - String fileName = null; - if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { - fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); - } + folderBrowser.showSaveDialog(this); + folderBrowser.setVisible(true); + folderBrowser.setCurrentDirectory(new File(LegupPreferences.WORK_DIRECTORY)); + folderBrowser.setDialogTitle("Select Directory"); + folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + folderBrowser.setAcceptAllFileFilterUsed(false); - if (fileName != null) { + String path = folderBrowser.getSelectedFile().getAbsolutePath(); + + if (path != null) { try { PuzzleExporter exporter = puzzle.getExporter(); if (exporter == null) { throw new ExportFileException("Puzzle exporter null"); } - exporter.exportPuzzle(fileName); + exporter.exportPuzzle(path); } catch (ExportFileException e) { e.printStackTrace(); } diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java index b3cd30ffb..f50c8d6fc 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -35,6 +35,7 @@ public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { private JMenuItem helpLegup, aboutLegup; private JMenuBar menuBar; private JToolBar toolBar; + private JFileChooser folderBrowser; private JFrame frame; private JButton[] buttons; JSplitPane splitPanel; @@ -380,25 +381,37 @@ public void loadPuzzleFromHome(String game, String[] statements) { public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { - if (noQuit("Opening a new puzzle to edit?")) { + if (noQuit("Opening a new puzzle?")) { return new Object[0]; } } - if (fileDialog == null) { - fileDialog = new FileDialog(this.frame); - } + LegupPreferences preferences = LegupPreferences.getInstance(); String preferredDirectory = preferences.getUserPref(LegupPreferences.WORK_DIRECTORY); + if (preferences.getSavedPath() != "") { + preferredDirectory = preferences.getSavedPath(); + } - fileDialog.setMode(FileDialog.LOAD); - fileDialog.setTitle("Select Puzzle"); - fileDialog.setDirectory(preferredDirectory); - fileDialog.setVisible(true); + File preferredDirectoryFile = new File(preferredDirectory); + JFileChooser fileBrowser = new JFileChooser(preferredDirectoryFile); String fileName = null; File puzzleFile = null; - if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { - fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); - puzzleFile = new File(fileName); + + fileBrowser.showOpenDialog(this); + fileBrowser.setVisible(true); + fileBrowser.setCurrentDirectory(new File(preferredDirectory)); + fileBrowser.setDialogTitle("Select Proof File"); + fileBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + fileBrowser.setAcceptAllFileFilterUsed(false); + + File puzzlePath = fileBrowser.getSelectedFile(); + System.out.println(puzzlePath.getAbsolutePath()); + + if (puzzlePath != null) { + fileName = puzzlePath.getAbsolutePath(); + String lastDirectoryPath = fileName.substring(0, fileName.lastIndexOf(File.separator)); + preferences.setSavedPath(lastDirectoryPath); + puzzleFile = puzzlePath; } else { // The attempt to prompt a puzzle ended gracefully (cancel) return null; @@ -538,39 +551,35 @@ private String savePuzzle() { } } - if (fileDialog == null) { - fileDialog = new FileDialog(this.frame); + LegupPreferences preferences = LegupPreferences.getInstance(); + File preferredDirectory = + new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); + if (preferences.getSavedPath() != "") { + preferredDirectory = new File(preferences.getSavedPath()); } + folderBrowser = new JFileChooser(preferredDirectory); - fileDialog.setMode(FileDialog.SAVE); - fileDialog.setTitle("Save Proof"); - String curFileName = GameBoardFacade.getInstance().getCurFileName(); - if (curFileName == null) { - fileDialog.setDirectory( - LegupPreferences.getInstance().getUserPref(LegupPreferences.WORK_DIRECTORY)); - } else { - File curFile = new File(curFileName); - fileDialog.setDirectory(curFile.getParent()); - } - fileDialog.setVisible(true); + folderBrowser.showSaveDialog(this); + folderBrowser.setVisible(true); + folderBrowser.setCurrentDirectory(new File(LegupPreferences.WORK_DIRECTORY)); + folderBrowser.setDialogTitle("Select Directory"); + folderBrowser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + folderBrowser.setAcceptAllFileFilterUsed(false); - String fileName = null; - if (fileDialog.getDirectory() != null && fileDialog.getFile() != null) { - fileName = fileDialog.getDirectory() + File.separator + fileDialog.getFile(); - } + String path = folderBrowser.getSelectedFile().getAbsolutePath(); - if (fileName != null) { + if (path != null) { try { PuzzleExporter exporter = puzzle.getExporter(); if (exporter == null) { throw new ExportFileException("Puzzle exporter null"); } - exporter.exportPuzzle(fileName); + exporter.exportPuzzle(path); } catch (ExportFileException e) { e.printStackTrace(); } } - return fileName; + return path; } public DynamicView getDynamicBoardView() { From 58eba415dcde10b9b8f26ccfea2ee79af4ba58d3 Mon Sep 17 00:00:00 2001 From: Brandon McCusker Date: Sun, 5 May 2024 13:46:44 -0400 Subject: [PATCH 72/74] Binary (#812) * Test commit * Test commit again * Implementing the constructor and some abstract methods * Implementing the BinaryType file for the grid cell possibilities * initialized puzzle files, began writing the puzzle importer and finished most of binary.java functionality * Finished setting up BinaryImporter completely with all helper files (BinaryView, BinaryCellFactory, BinaryController, BinaryElementView). These files create the grid view of the puzzle and are responsible for the GUI of the puzzle. * Fixed bugs in binary files, wrote binary exporter * Fixed compile errors * Added binary puzzle to config, began writing rules * Fixed minor syntax and import error * Fixed checkstyle error message in BinaryCellFactory * Made slight progress when trying to output puzzle, first edition of Three Adjacent Ones and Zeros Contradiction rules * Fixed checkstyle error for if statements * Added functions to BinaryBoard that returns the rows and columns of a specified index. * Fixed error that causes BinaryImporter to fail to initialize board * First edition of Unbalanced Row and Unbalanced Column Contradiction rules * Update config * Deleted false config file, began rule implementation, began fixing puzzle board visual * Fixed stylecheck errors * First edition of Duplicate Rows and Duplicate Columns Contradiction rules * First edition of One Tile Gap Direct rule * Second edition of One Tile Gap Direct rule, typo in if statement * Initial Board Rendering correct * All user board interactions fiex to be correct * Altered given cells color and fixed rules ref * Added temporary images to rules * Fixed checkStyle error * Changed Direct Rule * Create MyWikiContributions * Rename MyWikiContributions to MyWikiContributions.md * Combined each of the contradiction rules into one file rather than two separate files * Fixed checkStyle error in ThreeAdjacentContradictionRule * Wrote prelim OneZeroCaseRule * Fixed bugs and continued Contradiciton rule work * Added copy in BinaryBoard, fixed getCell with checking out of bounds * Three adjacent CR complete and prelim testing done * Tested UnbalancedRowOrColumnContradictionRule to verify correction Fixed checkStyle errors * Tested UnbalancedRowOrColumnContradictionRule to verify correction Fixed checkStyle errors * Fixed checkStyle error in ThreeAdjacentContradictionRule * Deleted unnecessary file that was auto created * OneZeroCaseRule working version complete, began writing SurroundPairDIrectRule fixed minor bugs * Finished DuplicateRowsOrColumnsContradictionRule for rows but not columns, added method to get all types of cells in row. * fixed stylecheck errors * fixed stylecheck errors * All rules complete and ready for test suite, constructed additional test puzzles * Cleaned rule files, unified formatting, removed print statements from testing * Cleaned rule files, unified formatting, removed print statements from testing, fixed minor bug * Found error in ThreeAdjacentContradictionRule, missing valid contradictions in OneTileGap and SurroundPair Direct Rules * Fixed checkStyle error "if construct must use {}'s" * Fixed buggy three adjacent contradiction rule * Made slight progress in onetilegap direct rule * Slight progress with SurroundPairDirectRule * Completed and tested all rules to verify correctness. * Added final rule images, adjusted nming converntions for rule images, updated wiki with new rule images * Added Wiki Contributions * Added Binary Puzzle Presentation * Added Wiki Contributions * Add files via upload * Made improvement to rule images * Delete LEGUP Binary Puzzle Progress.pptx * Delete LEGUPBinaryPuzzleProgress.pptx * Delete WikiContributions.md * deleted blank file * Automated Java code formatting changes --------- Co-authored-by: zachbonagura Co-authored-by: zachbonagura <157537291+zachbonagura@users.noreply.github.com> Co-authored-by: charlestian23 Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Bram van Heuveln --- bin/main/edu/rpi/legup/legup/config | 47 ------- puzzles files/binary/6x6 easy/089764562 | 22 +++ puzzles files/binary/6x6 easy/128903434 | 21 +++ puzzles files/binary/6x6 easy/876868768 | 25 ++++ puzzles files/binary/6x6 easy/927364891 | 28 ++++ .../rpi/legup/model/gameboard/GridRegion.java | 16 ++- .../edu/rpi/legup/puzzle/binary/Binary.java | 71 ++++++++++ .../rpi/legup/puzzle/binary/BinaryBoard.java | 84 ++++++++++++ .../rpi/legup/puzzle/binary/BinaryCell.java | 35 +++++ .../puzzle/binary/BinaryCellFactory.java | 59 ++++++++ .../legup/puzzle/binary/BinaryController.java | 45 +++++++ .../puzzle/binary/BinaryElementView.java | 120 +++++++++++++++++ .../legup/puzzle/binary/BinaryExporter.java | 39 ++++++ .../legup/puzzle/binary/BinaryImporter.java | 117 ++++++++++++++++ .../rpi/legup/puzzle/binary/BinaryType.java | 11 ++ .../rpi/legup/puzzle/binary/BinaryView.java | 24 ++++ .../puzzle/binary/elements/BlankTile.java | 1 + .../puzzle/binary/elements/NumberTile.java | 1 + .../rules/CompleteRowColumnDirectRule.java | 55 ++++++++ ...plicateRowsOrColumnsContradictionRule.java | 55 ++++++++ .../binary/rules/OneOrZeroCaseRule.java | 90 +++++++++++++ .../binary/rules/OneTileGapDirectRule.java | 64 +++++++++ .../binary/rules/SurroundPairDirectRule.java | 48 +++++++ .../rules/ThreeAdjacentContradictionRule.java | 127 ++++++++++++++++++ ...nbalancedRowOrColumnContradictionRule.java | 68 ++++++++++ .../binary/rules/binary_reference_sheet.txt | 9 ++ .../legup/puzzle/starbattle/StarBattle.java | 4 +- .../puzzle/starbattle/StarBattleBoard.java | 20 +-- .../puzzle/starbattle/StarBattleCell.java | 29 ++-- .../starbattle/StarBattleCellFactory.java | 26 ++-- .../puzzle/starbattle/StarBattleCellType.java | 10 +- .../starbattle/StarBattleController.java | 20 +-- .../starbattle/StarBattleElementView.java | 11 +- .../puzzle/starbattle/StarBattleExporter.java | 6 +- .../puzzle/starbattle/StarBattleImporter.java | 31 ++--- .../puzzle/starbattle/StarBattleRegion.java | 6 +- .../puzzle/starbattle/StarBattleView.java | 25 ++-- .../puzzle/starbattle/elements/BlackTile.java | 6 +- .../puzzle/starbattle/elements/StarTile.java | 6 +- .../starbattle/elements/UnknownTile.java | 6 +- .../puzzle/starbattle/elements/WhiteTile.java | 7 +- .../starbattle/rules/BlackoutDirectRule.java | 18 +-- .../rules/ClashingOrbitContradictionRule.java | 25 ++-- .../rules/ColumnsWithinRegionsDirectRule.java | 39 +++--- .../rules/ColumnsWithinRowsDirectRule.java | 38 +++--- .../rules/FinishWithStarsDirectRule.java | 16 ++- .../rules/RegionsWithinColumnsDirectRule.java | 16 ++- .../rules/RegionsWithinRowsDirectRule.java | 16 ++- .../rules/RowsWithinColumnsDirectRule.java | 22 ++- .../rules/RowsWithinRegionsDirectRule.java | 38 +++--- .../starbattle/rules/StarOrEmptyCaseRule.java | 33 ++--- .../rules/SurroundStarDirectRule.java | 17 +-- .../rules/TooFewStarsContradictionRule.java | 17 ++- .../rules/TooManyStarsContradictionRule.java | 19 ++- .../edu/rpi/legup/utility/LegupUtils.java | 3 +- .../rules/CompleteRowColumnDirectRule.png | Bin 0 -> 2835 bytes .../DuplicateRowOrColumnContradictionRule.png | Bin 0 -> 3455 bytes .../images/binary/rules/OneOrZeroCaseRule.png | Bin 0 -> 5578 bytes .../binary/rules/OneTileGapDirectRule.png | Bin 0 -> 2751 bytes .../binary/rules/SurroundPairDirectRule.png | Bin 0 -> 2982 bytes .../rules/ThreeAdjacentContradictionRule.png | Bin 0 -> 2508 bytes .../UnbalancedRowColumnContradictionRule.png | Bin 0 -> 2793 bytes src/main/resources/edu/rpi/legup/legup/config | 4 + 63 files changed, 1506 insertions(+), 310 deletions(-) delete mode 100644 bin/main/edu/rpi/legup/legup/config create mode 100644 puzzles files/binary/6x6 easy/089764562 create mode 100644 puzzles files/binary/6x6 easy/128903434 create mode 100644 puzzles files/binary/6x6 easy/876868768 create mode 100644 puzzles files/binary/6x6 easy/927364891 create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/Binary.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png diff --git a/bin/main/edu/rpi/legup/legup/config b/bin/main/edu/rpi/legup/legup/config deleted file mode 100644 index 4aef6de60..000000000 --- a/bin/main/edu/rpi/legup/legup/config +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 easy/089764562 b/puzzles files/binary/6x6 easy/089764562 new file mode 100644 index 000000000..7b22ffc10 --- /dev/null +++ b/puzzles files/binary/6x6 easy/089764562 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/binary/6x6 easy/128903434 b/puzzles files/binary/6x6 easy/128903434 new file mode 100644 index 000000000..ea8ef93b0 --- /dev/null +++ b/puzzles files/binary/6x6 easy/128903434 @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/binary/6x6 easy/876868768 b/puzzles files/binary/6x6 easy/876868768 new file mode 100644 index 000000000..0f0ff745e --- /dev/null +++ b/puzzles files/binary/6x6 easy/876868768 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/binary/6x6 easy/927364891 b/puzzles files/binary/6x6 easy/927364891 new file mode 100644 index 000000000..da76d067b --- /dev/null +++ b/puzzles files/binary/6x6 easy/927364891 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java index 6718ab6f4..c41d5e1b2 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java @@ -4,18 +4,17 @@ import java.util.List; public abstract class GridRegion { - + protected List regionCells; - - /** - * Region Constructor - */ + + /** Region Constructor */ public GridRegion() { this.regionCells = new ArrayList<>(); } /** * Adds the cell to the region + * * @param cell cell to be added to the region */ public void addCell(T cell) { @@ -24,6 +23,7 @@ public void addCell(T cell) { /** * Removes the cell from the region + * * @param cell cell to be remove from the region */ public void removeCell(T cell) { @@ -32,6 +32,7 @@ public void removeCell(T cell) { /** * Returns the list of cells in the region + * * @return list of cells in region */ public List getCells() { @@ -40,14 +41,15 @@ public List getCells() { /** * Returns the number of cells in the region + * * @return number of cells in the region */ - public int getSize(){ + public int getSize() { return regionCells.size(); } /* public void colorRegion(){} */ - + } diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java new file mode 100644 index 000000000..773513cda --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -0,0 +1,71 @@ +package edu.rpi.legup.puzzle.binary; + +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; + +public class Binary extends Puzzle { + public Binary() { + super(); + + this.name = "Binary"; + + this.importer = new BinaryImporter(this); + this.exporter = new BinaryExporter(this); + + this.factory = new BinaryCellFactory(); + } + + /** Initializes the game board. Called by the invoker of the class */ + @Override + public void initializeView() { + boardView = new BinaryView((BinaryBoard) currentBoard); + boardView.setBoard(currentBoard); + addBoardListener(boardView); + } + + /** + * 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; + } + + // /** + // * Determines if the given dimensions are valid for Binary + // * + // * @param rows the number of rows + // * @param columns the number of columns + // * @return true if the given dimensions are valid for Binary, false otherwise + // */ + // @Override + // public boolean isValidDimensions(int rows, int columns){ + // return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; + // } + + @Override + public boolean isBoardComplete(Board board) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + for (ContradictionRule rule : contradictionRules) { + if (rule.checkContradiction(binaryBoard) == null) { + return false; + } + } + for (PuzzleElement data : binaryBoard.getPuzzleElements()) { + BinaryCell cell = (BinaryCell) data; + if (cell.getType() == BinaryType.UNKNOWN) { + return false; + } + } + return true; + } + + @Override + public void onBoardChange(Board board) {} +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java new file mode 100644 index 000000000..35c37b1a1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java @@ -0,0 +1,84 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.awt.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class BinaryBoard extends GridBoard { + private int size; + + public BinaryBoard(int width, int height) { + super(width, height); + this.size = width; + } + + public BinaryBoard(int size) { + super(size, size); + this.size = size; + } + + @Override + public BinaryCell getCell(int x, int y) { + if (y * dimension.width + x >= puzzleElements.size() + || x >= dimension.width + || y >= dimension.height + || x < 0 + || y < 0) { + return null; + } + return (BinaryCell) super.getCell(x, y); + } + + public Set getRowCells(int rowNum) { + Set row = new HashSet<>(); + for (int i = 0; i < size; i++) { + BinaryCell cell = getCell(i, rowNum); + row.add(cell); + } + return row; + } + + public ArrayList getRowTypes(int rowNum) { + ArrayList row = new ArrayList(); + for (int i = 0; i < size; i++) { + BinaryCell cell = getCell(i, rowNum); + row.add(cell.getType()); + } + return row; + } + + public ArrayList getColTypes(int colNum) { + ArrayList col = new ArrayList(); + for (int i = 0; i < size; i++) { + BinaryCell cell = getCell(colNum, i); + col.add(cell.getType()); + } + return col; + } + + public Set getCol(int colNum) { + Set col = new HashSet<>(); + for (int i = 0; i < size; i++) { + col.add(getCell(colNum, i)); + } + return col; + } + + @Override + public BinaryBoard copy() { + System.out.println("BinaryBoard copy()"); + BinaryBoard copy = new BinaryBoard(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); + } + return copy; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java new file mode 100644 index 000000000..9007215ad --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java @@ -0,0 +1,35 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.gameboard.GridCell; +import java.awt.Point; + +public class BinaryCell extends GridCell { + public BinaryCell(int valueInt, Point location) { + super(valueInt, location); + } + + public BinaryType getType() { + switch (data) { + case 0: + return BinaryType.ZERO; + case 1: + return BinaryType.ONE; + case 2: + return BinaryType.UNKNOWN; + default: + if (data > 1) { + return BinaryType.UNKNOWN; + } + } + return null; + } + + @Override + public BinaryCell copy() { + BinaryCell copy = new BinaryCell(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/binary/BinaryCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java new file mode 100644 index 000000000..890c26656 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java @@ -0,0 +1,59 @@ +package edu.rpi.legup.puzzle.binary; + +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 java.awt.*; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +public class BinaryCellFactory extends ElementFactory { + + public BinaryCell importCell(Node node, Board board) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("cell")) { + throw new InvalidFileFormatException( + "binary Factory: unknown puzzleElement puzzleElement"); + } + + BinaryBoard binaryBoard = (BinaryBoard) board; + int width = binaryBoard.getWidth(); + int height = binaryBoard.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("binary Factory: cell location out of bounds"); + } + if (value < -2) { + throw new InvalidFileFormatException("binary Factory: cell unknown value"); + } + + BinaryCell cell = new BinaryCell(value, new Point(x, y)); + cell.setIndex(y * height + x); + return cell; + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "binary Factory: unknown value where integer expected"); + } catch (NullPointerException e) { + throw new InvalidFileFormatException("binary Factory: could not find attribute(s)"); + } + } + + public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { + org.w3c.dom.Element cellElement = document.createElement("cell"); + + BinaryCell cell = (BinaryCell) 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/binary/BinaryController.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java new file mode 100644 index 000000000..0bad559d9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java @@ -0,0 +1,45 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.awt.event.MouseEvent; + +public class BinaryController extends ElementController { + + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + BinaryCell cell = (BinaryCell) 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() == 0) { + data.setData(1); + } else { + if (cell.getData() == 1) { + data.setData(2); + } else { + data.setData(0); + } + } + } + } else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == 0) { + data.setData(1); + } else { + if (cell.getData() == 1) { + data.setData(2); + } else { + data.setData(0); + } + } + } + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java new file mode 100644 index 000000000..9ac99c958 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java @@ -0,0 +1,120 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.ui.boardview.GridElementView; +import java.awt.*; + +public class BinaryElementView extends GridElementView { + + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 17); + private static final Color FONT_COLOR = Color.BLACK; + + public BinaryElementView(BinaryCell cell) { + super(cell); + } + + /** + * Gets the PuzzleElement associated with this view + * + * @return PuzzleElement associated with this view + */ + @Override + public BinaryCell getPuzzleElement() { + return (BinaryCell) super.getPuzzleElement(); + } + + @Override + public void drawGiven(Graphics2D graphics2D) { + BinaryCell cell = (BinaryCell) puzzleElement; + BinaryType type = cell.getType(); + if (type == BinaryType.ZERO) { + 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); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + } else { + if (type == BinaryType.ONE) { + 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); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + + ((size.height - metrics.getHeight()) / 2) + + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + + } else { + if (type == BinaryType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(0)); + 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); + } + } + } + } + + @Override + public void drawElement(Graphics2D graphics2D) { + BinaryCell cell = (BinaryCell) puzzleElement; + BinaryType type = cell.getType(); + if (type == BinaryType.ZERO) { + 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); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + } else { + if (type == BinaryType.ONE) { + 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); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + + ((size.height - metrics.getHeight()) / 2) + + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + + } else { + if (type == BinaryType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(0)); + 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); + } + } + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java new file mode 100644 index 000000000..cd58314b6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java @@ -0,0 +1,39 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.PuzzleExporter; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import org.w3c.dom.Document; + +public class BinaryExporter extends PuzzleExporter { + + public BinaryExporter(Binary binary) { + super(binary); + } + + @Override + protected org.w3c.dom.Element createBoardElement(Document newDocument) { + BinaryBoard board; + if (puzzle.getTree() != null) { + board = (BinaryBoard) puzzle.getTree().getRootNode().getBoard(); + } else { + board = (BinaryBoard) puzzle.getBoardView().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()) { + BinaryCell cell = (BinaryCell) puzzleElement; + if (cell.getData() != -2) { + 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/binary/BinaryImporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java new file mode 100644 index 000000000..2fc5b09ef --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java @@ -0,0 +1,117 @@ +package edu.rpi.legup.puzzle.binary; + +import edu.rpi.legup.model.PuzzleImporter; +import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class BinaryImporter extends PuzzleImporter { + public BinaryImporter(Binary binary) { + super(binary); + } + + @Override + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + + @Override + public void initializeBoard(int rows, int columns) { + BinaryBoard binaryBoard = new BinaryBoard(columns, rows); + + for (int y = 0; y < rows; y++) { + for (int x = 0; x < columns; x++) { + BinaryCell cell = new BinaryCell(BinaryType.UNKNOWN.toValue(), new Point(x, y)); + cell.setIndex(y * columns + x); + cell.setModifiable(true); + binaryBoard.setCell(x, y, cell); + } + } + puzzle.setCurrentBoard(binaryBoard); + } + + /** + * 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( + "binary Importer: cannot find board puzzleElement"); + } + Element boardElement = (Element) node; + if (boardElement.getElementsByTagName("cells").getLength() == 0) { + throw new InvalidFileFormatException( + "binary Importer: no puzzleElement found for board"); + } + + Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("cell"); + + BinaryBoard binaryBoard = null; + if (!boardElement.getAttribute("size").isEmpty()) { + int size = Integer.valueOf(boardElement.getAttribute("size")); + binaryBoard = new BinaryBoard(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")); + binaryBoard = new BinaryBoard(width, height); + } + } + + int width = binaryBoard.getWidth(); + int height = binaryBoard.getHeight(); + + if (binaryBoard == null || width % 2 != 0 || height % 2 != 0) { + throw new InvalidFileFormatException("binary Importer: invalid board dimensions"); + } + + for (int i = 0; i < elementDataList.getLength(); i++) { + BinaryCell cell = + (BinaryCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), binaryBoard); + Point loc = cell.getLocation(); + if (cell.getData() != BinaryType.UNKNOWN.toValue()) { + cell.setModifiable(false); + cell.setGiven(true); + } + binaryBoard.setCell(loc.x, loc.y, cell); + } + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (binaryBoard.getCell(x, y) == null) { + BinaryCell cell = + new BinaryCell(BinaryType.UNKNOWN.toValue(), new Point(x, y)); + cell.setIndex(y * height + x); + cell.setModifiable(true); + binaryBoard.setCell(x, y, cell); + } + } + } + puzzle.setCurrentBoard(binaryBoard); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "binary Importer: unknown value where integer expected"); + } + } + + @Override + public void initializeBoard(String[] statements) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Binary cannot accept text input"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java new file mode 100644 index 000000000..6e3413d7a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java @@ -0,0 +1,11 @@ +package edu.rpi.legup.puzzle.binary; + +public enum BinaryType { + ZERO, + ONE, + UNKNOWN; + + public int toValue() { + return this.ordinal(); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java new file mode 100644 index 000000000..b11554f28 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java @@ -0,0 +1,24 @@ +package edu.rpi.legup.puzzle.binary; + +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 BinaryView extends GridBoardView { + + public BinaryView(BinaryBoard board) { + super(new BoardController(), new BinaryController(), board.getDimension()); + + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + BinaryCell cell = (BinaryCell) puzzleElement; + Point loc = cell.getLocation(); + BinaryElementView elementView = new BinaryElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(elementView); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java @@ -0,0 +1 @@ + diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java @@ -0,0 +1 @@ + diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java new file mode 100644 index 000000000..e38c6b78d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java @@ -0,0 +1,55 @@ +package edu.rpi.legup.puzzle.binary.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.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; + +public class CompleteRowColumnDirectRule extends DirectRule { + + public CompleteRowColumnDirectRule() { + super( + "BINA-BASC-0003", + "Complete Row Column", + "If a row/column of length n contains n/2 of a single value, the remaining cells must contain the other value", + "edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.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) { + BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new UnbalancedRowOrColumnContradictionRule(); + BinaryCell binaryCell = (BinaryCell) puzzleElement; + BinaryBoard modified = origBoard.copy(); + BinaryCell c = (BinaryCell) modified.getPuzzleElement(puzzleElement); + + // System.out.println("ORIG" + binaryCell.getData()); + // System.out.println("AFTER" + Math.abs(binaryCell.getData() - 1)); + modified.getPuzzleElement(puzzleElement).setData(binaryCell.getData()); + System.out.println(contraRule.checkContradictionAt(modified, puzzleElement)); + + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { + return null; + } + + return "Grouping of Three Ones or Zeros not found"; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java new file mode 100644 index 000000000..8b0d88ae4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java @@ -0,0 +1,55 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; +import java.util.ArrayList; + +public class DuplicateRowsOrColumnsContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Row or column must have a value in each cell"; + + public DuplicateRowsOrColumnsContradictionRule() { + super( + "BINA-CONT-0003", + "Duplicate Rows Or Columns", + "There must not be two rows or two columns that are duplicates", + "edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + + ArrayList row = binaryBoard.getRowTypes(cell.getLocation().y); + + int size = row.size(); + + for (int i = 0; i < size; i++) { + if (i > cell.getLocation().y) { + ArrayList currRow = binaryBoard.getRowTypes(i); + if (currRow.equals(row)) { + return null; + } + } + } + + ArrayList col = binaryBoard.getColTypes(cell.getLocation().x); + + for (int i = 0; i < size; i++) { + if (i > cell.getLocation().x) { + ArrayList currCol = binaryBoard.getColTypes(i); + if (currCol.equals(col)) { + return null; + } + } + } + + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java new file mode 100644 index 000000000..70549cd72 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java @@ -0,0 +1,90 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; +import java.util.ArrayList; +import java.util.List; + +public class OneOrZeroCaseRule extends CaseRule { + + public OneOrZeroCaseRule() { + super( + "BINA-CASE-0001", + "One or Zero", + "Each blank cell is either a one or a zero.", + "edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png"); + } + + @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."; + } + + BinaryCell mod1 = (BinaryCell) case1.getBoard().getModifiedData().iterator().next(); + BinaryCell mod2 = (BinaryCell) 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() == BinaryType.ZERO && mod2.getType() == BinaryType.ONE) + || (mod2.getType() == BinaryType.ZERO && mod1.getType() == BinaryType.ONE))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must an empty white and black cell."; + } + + return null; + } + + @Override + public CaseBoard getCaseBoard(Board board) { + BinaryBoard binaryBoard = (BinaryBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(binaryBoard, this); + binaryBoard.setModifiable(false); + for (PuzzleElement element : binaryBoard.getPuzzleElements()) { + if (((BinaryCell) element).getType() == BinaryType.UNKNOWN) { + caseBoard.addPickableElement(element); + } + } + return caseBoard; + } + + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList<>(); + Board case1 = board.copy(); + PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); + data1.setData(BinaryType.ZERO.toValue()); + case1.addModifiedData(data1); + cases.add(case1); + + Board case2 = board.copy(); + PuzzleElement data2 = case2.getPuzzleElement(puzzleElement); + data2.setData(BinaryType.ONE.toValue()); + case2.addModifiedData(data2); + cases.add(case2); + + return cases; + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java new file mode 100644 index 000000000..2e1e96fa5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java @@ -0,0 +1,64 @@ +package edu.rpi.legup.puzzle.binary.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.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; + +public class OneTileGapDirectRule extends DirectRule { + private final String INVALID_USE_MESSAGE = "Number at cell is incorrect"; + + public OneTileGapDirectRule() { + super( + "BINA-BASC-0002", + "One Tile Gap", + "If an empty tile is in between the same value, fill the gap with the other value.", + "edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png"); + } + + boolean checkLeftRight(BinaryCell c, BinaryBoard board) { + int x = c.getLocation().x; + int y = c.getLocation().y; + return board.getCell(x - 1, y).getType() != c.getType() + || board.getCell(x + 1, y).getType() != c.getType(); + } + + boolean checkUpDown(BinaryCell c, BinaryBoard board) { + int x = c.getLocation().x; + int y = c.getLocation().y; + return board.getCell(x, y - 1).getType() != c.getType() + || board.getCell(x, y + 1).getType() != c.getType(); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new ThreeAdjacentContradictionRule(); + BinaryCell binaryCell = (BinaryCell) puzzleElement; + BinaryBoard modified = origBoard.copy(); + + // System.out.println("ORIG" + binaryCell.getData()); + // System.out.println("AFTER" + Math.abs(binaryCell.getData() - 1)); + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + PuzzleElement newP = binaryCell; + + System.out.println(contraRule.checkContradictionAt(modified, newP)); + + if (contraRule.checkContradictionAt(modified, newP) == null) { + return null; + } + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + return "Grouping of Three Ones or Zeros not found"; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java new file mode 100644 index 000000000..dc2f07c8b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java @@ -0,0 +1,48 @@ +package edu.rpi.legup.puzzle.binary.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.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; + +public class SurroundPairDirectRule extends DirectRule { + + public SurroundPairDirectRule() { + super( + "BINA-BASC-0001", + "Surround Pair", + "If two adjacent tiles have the same value, surround the tiles with the other value.", + "edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png"); + } + + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new ThreeAdjacentContradictionRule(); + BinaryCell binaryCell = (BinaryCell) puzzleElement; + BinaryBoard modified = origBoard.copy(); + + // System.out.println("ORIG" + binaryCell.getData()); + // System.out.println("AFTER" + Math.abs(binaryCell.getData() - 1)); + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + PuzzleElement newP = binaryCell; + + System.out.println(contraRule.checkContradictionAt(modified, newP)); + + if (contraRule.checkContradictionAt(modified, newP) == null) { + return null; + } + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + return "Grouping of Three Ones or Zeros not found"; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java new file mode 100644 index 000000000..075642246 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java @@ -0,0 +1,127 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +public class ThreeAdjacentContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Contradiction must be a zero or one"; + + public ThreeAdjacentContradictionRule() { + super( + "BINA-CONT-0001", + "Three Adjacent", + "There must not be three adjacent zeros or three adjacent ones in a row or column", + "edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + int height = binaryBoard.getHeight(); + int width = binaryBoard.getWidth(); + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + System.out.println("THE CELL IS : " + cell.getType()); + int cellX = cell.getLocation().x; + int cellY = cell.getLocation().y; + BinaryCell oneUp = null; + BinaryCell oneDown = null; + BinaryCell oneForward = null; + BinaryCell oneBackward = null; + BinaryCell twoUp = null; + BinaryCell twoDown = null; + BinaryCell twoForward = null; + BinaryCell twoBackward = null; + if (binaryBoard.getCell(cellX, cellY + 1) != null) { + oneUp = binaryBoard.getCell(cellX, cellY + 1); + } + if (binaryBoard.getCell(cellX, cellY - 1) != null) { + oneDown = binaryBoard.getCell(cellX, cellY - 1); + } + if (binaryBoard.getCell(cellX + 1, cellY) != null) { + oneForward = binaryBoard.getCell(cellX + 1, cellY); + } + if (binaryBoard.getCell(cellX - 1, cellY) != null) { + oneBackward = binaryBoard.getCell(cellX - 1, cellY); + } + if (binaryBoard.getCell(cellX, cellY + 2) != null) { + twoUp = binaryBoard.getCell(cellX, cellY + 2); + } + if (binaryBoard.getCell(cellX, cellY - 2) != null) { + twoDown = binaryBoard.getCell(cellX, cellY - 2); + } + if (binaryBoard.getCell(cellX + 2, cellY) != null) { + twoForward = binaryBoard.getCell(cellX + 2, cellY); + } + if (binaryBoard.getCell(cellX - 2, cellY) != null) { + twoBackward = binaryBoard.getCell(cellX - 2, cellY); + } + + if (cell.getType() == BinaryType.ONE || cell.getType() == BinaryType.ZERO) { + if (twoBackward != null + && oneBackward != null + && twoBackward.getType() != BinaryType.UNKNOWN + && oneBackward.getType() != BinaryType.UNKNOWN) { + if (twoBackward.getType() == cell.getType() + && oneBackward.getType() == cell.getType()) { + System.out.println("1"); + return null; + } + } + if (twoForward != null + && oneForward != null + && twoForward.getType() != BinaryType.UNKNOWN + && oneForward.getType() != BinaryType.UNKNOWN) { + if (twoForward.getType() == cell.getType() + && oneForward.getType() == cell.getType()) { + System.out.println("2"); + return null; + } + } + if (twoDown != null + && oneDown != null + && twoDown.getType() != BinaryType.UNKNOWN + && oneDown.getType() != BinaryType.UNKNOWN) { + if (twoDown.getType() == cell.getType() && oneDown.getType() == cell.getType()) { + System.out.println("3"); + return null; + } + } + if (twoUp != null + && oneUp != null + && twoUp.getType() != BinaryType.UNKNOWN + && oneUp.getType() != BinaryType.UNKNOWN) { + if (twoUp.getType() == cell.getType() && oneUp.getType() == cell.getType()) { + System.out.println("4"); + return null; + } + } + if (oneBackward != null + && oneForward != null + && oneBackward.getType() != BinaryType.UNKNOWN + && oneForward.getType() != BinaryType.UNKNOWN) { + if (oneBackward.getType() == cell.getType() + && oneForward.getType() == cell.getType()) { + System.out.println("5"); + return null; + } + } + if (oneUp != null + && oneDown != null + && oneUp.getType() != BinaryType.UNKNOWN + && oneDown.getType() != BinaryType.UNKNOWN) { + if (oneUp.getType() == cell.getType() && oneDown.getType() == cell.getType()) { + System.out.println("6"); + return null; + } + } + } + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java new file mode 100644 index 000000000..5089b3b5f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java @@ -0,0 +1,68 @@ +package edu.rpi.legup.puzzle.binary.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.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; +import java.util.Set; + +public class UnbalancedRowOrColumnContradictionRule extends ContradictionRule { + + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Row or column must have a value in each cell"; + + public UnbalancedRowOrColumnContradictionRule() { + super( + "BINA-CONT-0002", + "Unbalanced Row Or Column", + "Each row or column must contain an equal number of zeros and ones", + "edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + Set row = binaryBoard.getRowCells(cell.getLocation().y); + + int size = row.size(); + int rowNumZeros = 0; + int rowNumOnes = 0; + + for (BinaryCell item : row) { + if (item.getType() == BinaryType.ZERO) { + rowNumZeros++; + } else if (item.getType() == BinaryType.ONE) { + rowNumOnes++; + } + } + + if (rowNumZeros == size / 2 && rowNumOnes == size / 2) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } + + Set col = binaryBoard.getCol(cell.getLocation().x); + + size = col.size(); + int colNumZeros = 0; + int colNumOnes = 0; + + for (BinaryCell item : col) { + if (item.getType() == BinaryType.ZERO) { + colNumZeros++; + } else if (item.getType() == BinaryType.ONE) { + colNumOnes++; + } + } + + if (colNumZeros == size / 2 && colNumOnes == size / 2) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt new file mode 100644 index 000000000..c8cb0d1b9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt @@ -0,0 +1,9 @@ +BINA-BASC-0001 : SurroundPairDirectRule +BINA-BASC-0002 : OneTileGapDirectRule +BINA-BASC-0003 : CompleteRowColumnDirectRule + +BINA-CONT-0001 : ThreeAdjacentContradictionRule +BINA-CONT-0002 : UnbalancedRowOrColumnContradictionRule +BINA-CONT-0003 : DuplicateRowsOrColumnsContradictionRule + +BINA-CASE-0001 : OneOrZeroCaseRule \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java index 760fa88b2..39092bbc6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattle.java @@ -1,4 +1,5 @@ package edu.rpi.legup.puzzle.starbattle; + import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; @@ -30,6 +31,5 @@ public boolean isBoardComplete(Board board) { } @Override - public void onBoardChange(Board board) { - } + public void onBoardChange(Board board) {} } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java index 2c4a20ebc..5132f33e4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleBoard.java @@ -1,16 +1,16 @@ package edu.rpi.legup.puzzle.starbattle; -import java.util.*; - import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.util.*; public class StarBattleBoard extends GridBoard { private int size; private int puzzleNum; protected List regions; - //private ArrayList groupSizes; + + // private ArrayList groupSizes; public StarBattleBoard(int size, int num) { super(size, size); @@ -24,10 +24,10 @@ public StarBattleBoard(int size, int num) { @Override public StarBattleCell getCell(int x, int y) { - return (StarBattleCell) super.getCell(x,y); + return (StarBattleCell) super.getCell(x, y); } - /* + /* public StarBattleCell getCell(int groupIndex, int x, int y) { return getCell(x + (groupIndex % groupSize) * groupSize, y + (groupIndex / groupSize) * groupSize); }*/ @@ -44,7 +44,9 @@ public Set getRow(int rowNum) { return row; } - public int getPuzzleNumber() { return puzzleNum; } + public int getPuzzleNumber() { + return puzzleNum; + } public Set getCol(int colNum) { Set column = new HashSet<>(); @@ -72,7 +74,7 @@ public void setRegion(int regionNum, StarBattleRegion region) { public int columnStars(int columnIndex) { int stars = 0; if (columnIndex < size) { - for (StarBattleCell c: this.getCol(columnIndex)) { + for (StarBattleCell c : this.getCol(columnIndex)) { if (c.getType() == StarBattleCellType.STAR) { ++stars; } @@ -84,7 +86,7 @@ public int columnStars(int columnIndex) { public int rowStars(int rowIndex) { int stars = 0; if (rowIndex < size) { - for (StarBattleCell c: this.getRow(rowIndex)) { + for (StarBattleCell c : this.getRow(rowIndex)) { if (c.getType() == StarBattleCellType.STAR) { ++stars; } @@ -109,5 +111,3 @@ public StarBattleBoard copy() { return copy; } } - - diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java index a316872d9..ddae8f882 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCell.java @@ -2,7 +2,6 @@ import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; - import java.awt.*; import java.awt.event.MouseEvent; @@ -13,10 +12,10 @@ public class StarBattleCell extends GridCell { /** * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement * - * @param value value of the star battle cell denoting its state - * @param location location of the cell on the board + * @param value value of the star battle cell denoting its state + * @param location location of the cell on the board * @param groupIndex indicates what group # the cell is in. - * @param size size of the star battle cell + * @param size size of the star battle cell */ public StarBattleCell(int value, Point location, int groupIndex, int size) { super(value, location); @@ -24,7 +23,9 @@ public StarBattleCell(int value, Point location, int groupIndex, int size) { this.max = size; } - public int getGroupIndex() { return groupIndex; } + public int getGroupIndex() { + return groupIndex; + } @Override public void setType(Element e, MouseEvent m) { @@ -38,27 +39,25 @@ public void setType(Element e, MouseEvent m) { case "STBL-PLAC-0003": this.data = -1; break; - - case "STBL-UNPL-0001"://Not sure how button events work - switch (m.getButton()){ + + case "STBL-UNPL-0001": // Not sure how button events work + switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data > 0 || this.data < -3) { this.data = -3; - } - else { + } else { this.data = this.data + 1; } break; case MouseEvent.BUTTON3: if (this.data > -4) { this.data = this.data - 1; - } - else { - this.data = -1;//Unsure + } else { + this.data = -1; // Unsure } break; } - break; + break; } } @@ -85,4 +84,4 @@ public StarBattleCell copy() { copy.setGiven(isGiven); return copy; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java index 9f7fd0fee..eb2a830f6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellFactory.java @@ -1,20 +1,21 @@ package edu.rpi.legup.puzzle.starbattle; + 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 java.awt.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import java.awt.*; - public class StarBattleCellFactory extends ElementFactory { @Override public StarBattleCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { - throw new InvalidFileFormatException("starbattle Factory: unknown puzzleElement puzzleElement"); + throw new InvalidFileFormatException( + "starbattle Factory: unknown puzzleElement puzzleElement"); } StarBattleBoard starbattleBoard = (StarBattleBoard) board; @@ -24,28 +25,27 @@ public StarBattleCell importCell(Node node, Board board) throws InvalidFileForma int value = Integer.valueOf(attributeList.getNamedItem("value").getNodeValue()); int x = Integer.valueOf(attributeList.getNamedItem("x").getNodeValue()); int y = Integer.valueOf(attributeList.getNamedItem("y").getNodeValue()); - int groupIndex = Integer.valueOf(attributeList.getNamedItem("groupIndex").getNodeValue()); + int groupIndex = + Integer.valueOf(attributeList.getNamedItem("groupIndex").getNodeValue()); if (x >= size || y >= size) { - throw new InvalidFileFormatException("starbattle Factory: cell location out of bounds"); + throw new InvalidFileFormatException( + "starbattle Factory: cell location out of bounds"); } if (groupIndex >= size || groupIndex < 0) { throw new InvalidFileFormatException("starbattle Factory: not in a valid region"); } - if (value != 0) { //ALL INITIAL PUZZLES ARE BLANK, SUBJECT TO CHANGE + if (value != 0) { // ALL INITIAL PUZZLES ARE BLANK, SUBJECT TO CHANGE throw new InvalidFileFormatException("starbattle Factory: cell unknown value"); } StarBattleCell cell = new StarBattleCell(value, new Point(x, y), groupIndex, size); cell.setIndex(y * size + x); return cell; - } - - catch (NumberFormatException e1) { + } catch (NumberFormatException e1) { e1.printStackTrace(); - throw new InvalidFileFormatException("starbattle Factory: unknown value where integer expected"); - } - - catch (NullPointerException e2) { + throw new InvalidFileFormatException( + "starbattle Factory: unknown value where integer expected"); + } catch (NullPointerException e2) { e2.printStackTrace(); throw new InvalidFileFormatException("starbattle Factory: could not find attribute(s)"); } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java index 565f608d7..e48e46fcb 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleCellType.java @@ -1,12 +1,14 @@ -//StarBattleCellType.java +// StarBattleCellType.java package edu.rpi.legup.puzzle.starbattle; public enum StarBattleCellType { - STAR(-2), BLACK(-1), UNKNOWN(0); + STAR(-2), + BLACK(-1), + UNKNOWN(0); - public int value; + public int value; StarBattleCellType(int value) { this.value = value; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java index 4ebeb39ba..ba19361c9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleController.java @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.ElementController; import edu.rpi.legup.model.gameboard.PuzzleElement; - import java.awt.event.MouseEvent; public class StarBattleController extends ElementController { @@ -11,23 +10,24 @@ public void changeCell(MouseEvent e, PuzzleElement data) { StarBattleCell cell = (StarBattleCell) 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 { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + e.getX(), + this.boardView.getCanvas().getY() + e.getY()); + } else { if (cell.getData() >= 0) { data.setData(-2); - } - else { + } else { data.setData(cell.getData() + 1); } } - } - else { + } else { if (e.getButton() == MouseEvent.BUTTON3) { if (cell.getData() == -2) { data.setData(0); - } - else { + } else { data.setData(cell.getData() - 1); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java index ff5d54d1c..66d59d364 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleElementView.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.starbattle; import edu.rpi.legup.ui.boardview.GridElementView; - import java.awt.*; public class StarBattleElementView extends GridElementView { @@ -22,7 +21,14 @@ public void drawElement(Graphics2D graphics2D) { if (type == StarBattleCellType.STAR) { graphics2D.setColor(Color.LIGHT_GRAY); graphics2D.fillRect(location.x, location.y, size.width, size.height); - graphics2D.drawImage(StarBattleView.STAR, location.x, location.y, size.width, size.height, Color.WHITE, null); + graphics2D.drawImage( + StarBattleView.STAR, + location.x, + location.y, + size.width, + size.height, + Color.WHITE, + null); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); } else if (type == StarBattleCellType.BLACK) { @@ -36,6 +42,5 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); } - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java index 58fb41e63..f2d2c4d80 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleExporter.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.starbattle; import edu.rpi.legup.model.PuzzleExporter; - import org.w3c.dom.Document; public class StarBattleExporter extends PuzzleExporter { @@ -20,14 +19,15 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); for (StarBattleCell cell : sb_region.getCells()) { if (cell.getData() == 0) { - org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, cell); + org.w3c.dom.Element cellElement = + puzzle.getFactory().exportCell(newDocument, cell); cellsElement.appendChild(cellElement); } regionsElement.appendChild(cellsElement); } boardElement.appendChild(regionsElement); } - + return boardElement; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java index fa0e065ee..2a608c893 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleImporter.java @@ -2,37 +2,32 @@ import edu.rpi.legup.model.PuzzleImporter; import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.Point; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.awt.Point; +public class StarBattleImporter extends PuzzleImporter { -public class StarBattleImporter extends PuzzleImporter{ - - public StarBattleImporter(StarBattle starbattle) { super(starbattle); } - /** - * Puzzle setting to support row and column inputs - */ + /** Puzzle setting to support row and column inputs */ @Override public boolean acceptsRowsAndColumnsInput() { return true; } - /** - * Puzzle setting to disable support for text input - */ + /** Puzzle setting to disable support for text input */ @Override public boolean acceptsTextInput() { return false; } - /** + /** * Constructs empty StarBattle gameboard as per the provided dimensions + * * @param rows number of rows and columns for the gameboard */ @Override @@ -44,6 +39,7 @@ public void initializeBoard(int rows, int columns) { /** * Constructs StarBattle gameboard + * * @param node xml document node * @throws InvalidFileFormatException if file is invalid */ @@ -54,10 +50,13 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { NodeList regionNodes = puzzleElement.getElementsByTagName("region"); int size = Integer.parseInt(puzzleElement.getAttribute("size")); if (regionNodes.getLength() != size) { - throw new InvalidFileFormatException("Not the current amount of regions in the puzzle."); + throw new InvalidFileFormatException( + "Not the current amount of regions in the puzzle."); } - StarBattleBoard StarBattleBoard = new StarBattleBoard(size, puzzle_num); // Initialize the board with width and height from XML + StarBattleBoard StarBattleBoard = + new StarBattleBoard( + size, puzzle_num); // Initialize the board with width and height from XML for (int i = 0; i < regionNodes.getLength(); i++) { Element regionElement = (Element) regionNodes.item(i); @@ -87,10 +86,9 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { puzzle.setCurrentBoard(StarBattleBoard); } - - /** * Initialize board via string of statements. + * * @throws UnsupportedOperationException since StarBattle does not support text input */ @Override @@ -98,6 +96,3 @@ public void initializeBoard(String[] statements) throws UnsupportedOperationExce throw new UnsupportedOperationException("Star Battle does not accept text input"); } } - - - diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java index 730f9291f..b35d80655 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleRegion.java @@ -2,14 +2,14 @@ import edu.rpi.legup.model.gameboard.GridRegion; -public class StarBattleRegion extends GridRegion{ +public class StarBattleRegion extends GridRegion { public StarBattleRegion() { super(); } public StarBattleRegion copy() { StarBattleRegion copy = new StarBattleRegion(); - for (StarBattleCell c: regionCells) { + for (StarBattleCell c : regionCells) { copy.addCell(c.copy()); } return copy; @@ -17,7 +17,7 @@ public StarBattleRegion copy() { public int numStars() { int stars = 0; - for (StarBattleCell c: regionCells) { + for (StarBattleCell c : regionCells) { if (c.getType() == StarBattleCellType.STAR) { ++stars; } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java index ceb0eec19..550b5495d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/StarBattleView.java @@ -1,26 +1,22 @@ package edu.rpi.legup.puzzle.starbattle; -import java.io.IOException; - -import javax.imageio.ImageIO; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import edu.rpi.legup.puzzle.starbattle.StarBattleView; -import edu.rpi.legup.ui.boardview.GridBoardView; - import edu.rpi.legup.controller.BoardController; import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.ui.boardview.GridBoardView; import java.awt.*; +import java.io.IOException; +import javax.imageio.ImageIO; public class StarBattleView extends GridBoardView { static Image STAR; static { try { - STAR = ImageIO.read(ClassLoader.getSystemClassLoader().getResource("edu/rpi/legup/images/starbattle/star.gif")); - } - catch (IOException e) { + STAR = + ImageIO.read( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/starbattle/star.gif")); + } catch (IOException e) { // pass } } @@ -34,10 +30,9 @@ public StarBattleView(StarBattleBoard board) { StarBattleElementView elementView = new StarBattleElementView(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 * elementSize.width, loc.y * elementSize.height)); elementViews.add(elementView); } } - - } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java index 2601bd351..99f42886e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java @@ -4,6 +4,10 @@ public class BlackTile extends NonPlaceableElement { public BlackTile() { - super("STBL-PLAC-0002", "Black Tile", "The black tile that shows where you cannot place a star", "edu/rpi/legup/images/lightup/black.gif"); + super( + "STBL-PLAC-0002", + "Black Tile", + "The black tile that shows where you cannot place a star", + "edu/rpi/legup/images/lightup/black.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java index d42cc0010..13ada3f4d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -4,6 +4,10 @@ public class StarTile extends NonPlaceableElement { public StarTile() { - super("STBL-PLAC-0001", "Star Tile", "The star tile, the token of the game.", "edu/rpi/legup/images/starbattle/star.gif"); + super( + "STBL-PLAC-0001", + "Star Tile", + "The star tile, the token of the game.", + "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java index c2459f642..425fb5d5e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -4,6 +4,10 @@ public class UnknownTile extends NonPlaceableElement { public UnknownTile() { - super("STBL-UNPL-0001", "Unknown Tile", "An empty tile", "edu/rpi/legup/images/starbattle/star.gif"); + super( + "STBL-UNPL-0001", + "Unknown Tile", + "An empty tile", + "edu/rpi/legup/images/starbattle/star.gif"); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java index a064c1fad..2227eb37a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/WhiteTile.java @@ -4,7 +4,10 @@ public class WhiteTile extends PlaceableElement { public WhiteTile() { - super("STBL-PLAC-0001", "White Tile", "The white tile", "edu/rpi/legup/images/starbattle/white.gif"); + super( + "STBL-PLAC-0001", + "White Tile", + "The white tile", + "edu/rpi/legup/images/starbattle/white.gif"); } } - diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java index 75fbaadb6..2ab66cf93 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/BlackoutDirectRule.java @@ -13,20 +13,21 @@ public class BlackoutDirectRule extends DirectRule { public BlackoutDirectRule() { - super("STBL-BASC-0001", + super( + "STBL-BASC-0001", "Blackout", "If a row, column, or region has enough stars, its unknown spaces are black.", "edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -50,7 +51,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 @@ -60,5 +62,3 @@ public Board getDefaultBoard(TreeNode node) { return null; } } - - diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java index 0ca27ab4a..88f0072e5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ClashingOrbitContradictionRule.java @@ -3,30 +3,29 @@ 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.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; - import java.awt.*; public class ClashingOrbitContradictionRule extends ContradictionRule { public ClashingOrbitContradictionRule() { - super("STBL-CONT-0003", + super( + "STBL-CONT-0003", "Clashing Orbit", "No two stars can be adjacent to each other.", "edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @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 + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -41,15 +40,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { // check neighboring cells for a star Point location = cell.getLocation(); - int rowStart = Math.max( location.x - 1, 0 ); - int rowEnd = Math.min( location.x + 1, starbattleBoard.getSize() - 1 ); - int colStart = Math.max( location.y - 1, 0 ); - int colEnd = Math.min( location.y + 1, starbattleBoard.getSize() - 1 ); + int rowStart = Math.max(location.x - 1, 0); + int rowEnd = Math.min(location.x + 1, starbattleBoard.getSize() - 1); + int colStart = Math.max(location.y - 1, 0); + int colEnd = Math.min(location.y + 1, starbattleBoard.getSize() - 1); for (int row = rowStart; row <= rowEnd; row++) { for (int col = colStart; col <= colEnd; col++) { if (starbattleBoard.getCell(row, col).getType() == StarBattleCellType.STAR - && (row != location.x || col != location.y)) { + && (row != location.x || col != location.y)) { return null; } } @@ -57,4 +56,4 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { return super.getNoContradictionMessage(); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java index b42bfd1c0..433567460 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRegionsDirectRule.java @@ -1,7 +1,6 @@ package edu.rpi.legup.puzzle.starbattle.rules; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.GridRegion; import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.DirectRule; import edu.rpi.legup.model.tree.TreeNode; @@ -9,41 +8,41 @@ import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; - import java.util.HashSet; import java.util.Set; public class ColumnsWithinRegionsDirectRule extends DirectRule { public ColumnsWithinRegionsDirectRule() { - super("STBL-BASC-0002", + super( + "STBL-BASC-0002", "Columns Within Regions", "If a number of columns is fully contained by a number of regions with an equal number of missing stars, spaces of other columns in those regions must be black.", "edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { - //assumption: the rule has been applied to its fullest extent and the rows and regions - //are now mutually encompassing + // assumption: the rule has been applied to its fullest extent and the rows and regions + // are now mutually encompassing StarBattleBoard board = (StarBattleBoard) transition.getBoard(); StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); if (cell.getType() != StarBattleCellType.BLACK) { return "Only black cells are allowed for this rule!"; } - //the columns that are contained + // the columns that are contained Set columns = new HashSet(); - //the regions that contain them + // the regions that contain them Set regions = new HashSet(); - //columns and regions to process + // columns and regions to process Set columnsToCheck = new HashSet(); Set regionsToCheck = new HashSet(); int columnStars = 0; @@ -52,9 +51,9 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem regionsToCheck.add(cell.getGroupIndex()); while (!columnsToCheck.isEmpty() || !regionsToCheck.isEmpty()) { - for (int r: regionsToCheck) { + for (int r : regionsToCheck) { regionStars += board.getRegion(r).numStars(); - for (PuzzleElement c: board.getRegion(r).getCells()) { + for (PuzzleElement c : board.getRegion(r).getCells()) { int column = ((StarBattleCell) c).getLocation().x; if (columns.add(column)) { columnsToCheck.add(column); @@ -62,10 +61,10 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } regionsToCheck.remove(r); } - for (int c: columnsToCheck) { + for (int c : columnsToCheck) { columnStars += board.columnStars(c); for (int i = 0; i < board.getSize(); ++i) { - int region = board.getCell(c,i).getGroupIndex(); + int region = board.getCell(c, i).getGroupIndex(); if (regions.add(region)) { regionsToCheck.add(region); } @@ -74,14 +73,16 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } } // are the columns and regions missing an equal amount of stars - if (board.getPuzzleNumber() * columns.size() - columnStars != board.getPuzzleNumber() * regions.size() - regionStars) { + if (board.getPuzzleNumber() * columns.size() - columnStars + != board.getPuzzleNumber() * regions.size() - regionStars) { return "The number of missing stars in the columns and regions must be equal and every extraneous cell must be black!"; } return null; } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java index 0a78c8868..5d108a0cd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/ColumnsWithinRowsDirectRule.java @@ -8,44 +8,44 @@ import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; - import java.util.HashSet; import java.util.Set; public class ColumnsWithinRowsDirectRule extends DirectRule { public ColumnsWithinRowsDirectRule() { - super("STBL-BASC-0003", + super( + "STBL-BASC-0003", "Columns Within Rows", "If a number of columns is fully contained by a number of rows with an equal number of missing stars, spaces of other columns in those rows must be black.", "edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { - //assumption: the rule has been applied to its fullest extent and the rows and columns - //are now mutually encompassing + // assumption: the rule has been applied to its fullest extent and the rows and columns + // are now mutually encompassing StarBattleBoard board = (StarBattleBoard) transition.getBoard(); StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); if (cell.getType() != StarBattleCellType.BLACK) { return "Only black cells are allowed for this rule!"; } - //the columns that are contained + // the columns that are contained Set columns = new HashSet(); - //the rows that contain them + // the rows that contain them Set rows = new HashSet(); - //columns and rows to process + // columns and rows to process Set columnsToCheck = new HashSet(); Set rowsToCheck = new HashSet(); int columnStars = 0; @@ -55,9 +55,9 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem rowsToCheck.add(firstRow); while (!columnsToCheck.isEmpty() || !rowsToCheck.isEmpty()) { - for (int r: rowsToCheck) { + for (int r : rowsToCheck) { rowStars += board.rowStars(r); - for (PuzzleElement c: board.getRow(r)) { + for (PuzzleElement c : board.getRow(r)) { int column = ((StarBattleCell) c).getLocation().x; if (columns.add(column)) { columnsToCheck.add(column); @@ -65,9 +65,9 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } rowsToCheck.remove(r); } - for (int c: columnsToCheck) { + for (int c : columnsToCheck) { columnStars += board.columnStars(c); - for (PuzzleElement r: board.getCol(c)) { + for (PuzzleElement r : board.getCol(c)) { int row = ((StarBattleCell) r).getLocation().y; if (rows.add(row)) { rowsToCheck.add(row); @@ -77,14 +77,16 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } } // are the columns and regions missing an equal amount of stars - if (board.getPuzzleNumber() * columns.size() - columnStars != board.getPuzzleNumber() * rows.size() - rowStars) { + if (board.getPuzzleNumber() * columns.size() - columnStars + != board.getPuzzleNumber() * rows.size() - rowStars) { return "The number of missing stars in the columns and rows must be equal and every extraneous cell must be black!"; } return null; } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java index 36e691e74..80ae9a4c8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/FinishWithStarsDirectRule.java @@ -13,20 +13,21 @@ public class FinishWithStarsDirectRule extends DirectRule { public FinishWithStarsDirectRule() { - super("STBL-BASC-0004", + super( + "STBL-BASC-0004", "Finish With Stars", "Unknown spaces must be stars if there are just enough in a row, column, or region to satisfy the puzzle number.", "edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -50,7 +51,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java index 16951fb2a..7022a06ac 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinColumnsDirectRule.java @@ -8,20 +8,21 @@ public class RegionsWithinColumnsDirectRule extends DirectRule { public RegionsWithinColumnsDirectRule() { - super("STBL-BASC-0005", + super( + "STBL-BASC-0005", "Regions Within Columns", "If a number of regions is fully contained by a number of columns with an equal number of missing stars, spaces of other regions in those columns must be black.", "edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -30,7 +31,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java index 27dc001a0..7ab50d42b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RegionsWithinRowsDirectRule.java @@ -8,20 +8,21 @@ public class RegionsWithinRowsDirectRule extends DirectRule { public RegionsWithinRowsDirectRule() { - super("STBL-BASC-0006", + super( + "STBL-BASC-0006", "Regions Within Rows", "If a number of regions is fully contained by a number of rows with an equal number of missing stars, spaces of other regions in those rows must be black.", "edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -31,7 +32,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java index 4054ec017..2df20e464 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinColumnsDirectRule.java @@ -5,30 +5,25 @@ 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.starbattle.StarBattleBoard; -import edu.rpi.legup.puzzle.starbattle.StarBattleCell; -import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; - -import java.util.HashSet; -import java.util.Set; public class RowsWithinColumnsDirectRule extends DirectRule { public RowsWithinColumnsDirectRule() { - super("STBL-BASC-0007", + super( + "STBL-BASC-0007", "Rows Withing Columns", "If a number of rows is fully contained by a number of columns with an equal number of missing stars, spaces of other rows in those columns must be black.", "edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -38,7 +33,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java index 7af2c79ed..78f8f00e7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/RowsWithinRegionsDirectRule.java @@ -8,42 +8,42 @@ import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; - import java.util.HashSet; import java.util.Set; public class RowsWithinRegionsDirectRule extends DirectRule { public RowsWithinRegionsDirectRule() { - super("STBL-BASC-0008", + super( + "STBL-BASC-0008", "Rows Within Regions", "If a number of rows is fully contained by a number of regions with an equal number of missing stars, spaces of other rows in those regions must be black.", "edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { - //assumption: the rule has been applied to its fullest extent and the rows and regions - //are now mutually encompassing + // assumption: the rule has been applied to its fullest extent and the rows and regions + // are now mutually encompassing StarBattleBoard board = (StarBattleBoard) transition.getBoard(); StarBattleCell cell = (StarBattleCell) board.getPuzzleElement(puzzleElement); if (cell.getType() != StarBattleCellType.BLACK) { return "Only black cells are allowed for this rule!"; } - //the rows that are contained + // the rows that are contained Set rows = new HashSet(); - //the regions that contain them + // the regions that contain them Set regions = new HashSet(); - //rows and regions to process + // rows and regions to process Set rowsToCheck = new HashSet(); Set regionsToCheck = new HashSet(); int rowStars = 0; @@ -52,9 +52,9 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem regionsToCheck.add(cell.getGroupIndex()); while (!rowsToCheck.isEmpty() || !regionsToCheck.isEmpty()) { - for (int r: regionsToCheck) { + for (int r : regionsToCheck) { regionStars += board.getRegion(r).numStars(); - for (PuzzleElement ro: board.getRegion(r).getCells()) { + for (PuzzleElement ro : board.getRegion(r).getCells()) { int row = ((StarBattleCell) ro).getLocation().y; if (rows.add(row)) { rowsToCheck.add(row); @@ -62,10 +62,10 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } regionsToCheck.remove(r); } - for (int r: rowsToCheck) { + for (int r : rowsToCheck) { rowStars += board.rowStars(r); for (int i = 0; i < board.getSize(); ++i) { - int region = board.getCell(i,r).getGroupIndex(); + int region = board.getCell(i, r).getGroupIndex(); if (regions.add(region)) { regionsToCheck.add(region); } @@ -74,14 +74,16 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } } // are the columns and regions missing an equal amount of stars - if (board.getPuzzleNumber() * rows.size() - rowStars != board.getPuzzleNumber() * regions.size() - regionStars) { + if (board.getPuzzleNumber() * rows.size() - rowStars + != board.getPuzzleNumber() * regions.size() - regionStars) { return "The number of missing stars in the rows and regions must be equal and every extraneous cell must be black!"; } return null; } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java index 0aa147c6f..df900dcd5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java @@ -1,31 +1,29 @@ package edu.rpi.legup.puzzle.starbattle.rules; -import edu.rpi.legup.model.rules.CaseRule; 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.NurikabeCell; -import edu.rpi.legup.puzzle.nurikabe.NurikabeType; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; - import java.util.ArrayList; import java.util.List; public class StarOrEmptyCaseRule extends CaseRule { public StarOrEmptyCaseRule() { - super("STBL-CASE-0002", + super( + "STBL-CASE-0002", "Star or Empty", "Each unknown space is either a star or empty.", "edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png"); } /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this rule. This method is - * the one that should overridden in child classes. + * 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 @@ -39,20 +37,25 @@ public String checkRuleRaw(TreeTransition transition) { 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."; + 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."; } StarBattleCell mod1 = (StarBattleCell) case1.getBoard().getModifiedData().iterator().next(); StarBattleCell mod2 = (StarBattleCell) 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."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; } - if (!((mod1.getType() == StarBattleCellType.STAR && mod2.getType() == StarBattleCellType.BLACK) || - (mod2.getType() == StarBattleCellType.STAR && mod1.getType() == StarBattleCellType.BLACK))) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must create a star cell and a black cell."; + if (!((mod1.getType() == StarBattleCellType.STAR + && mod2.getType() == StarBattleCellType.BLACK) + || (mod2.getType() == StarBattleCellType.STAR + && mod1.getType() == StarBattleCellType.BLACK))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must create a star cell and a black cell."; } return null; @@ -74,7 +77,7 @@ public CaseBoard getCaseBoard(Board board) { /** * Gets the possible cases at a specific location based on this case rule * - * @param board the current board state + * @param board the current board state * @param puzzleElement equivalent puzzleElement * @return a list of elements the specified could be */ diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java index e1c6f3084..89857875d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/SurroundStarDirectRule.java @@ -13,20 +13,21 @@ public class SurroundStarDirectRule extends DirectRule { public SurroundStarDirectRule() { - super("STBL-BASC-0009", + super( + "STBL-BASC-0009", "Surround Star", "Any space adjacent to a star must be black.", "edu/rpi/legup/images/starbattle/rules/SurroundStar.png"); } /** - * Checks whether the child node logically follows from the parent node - * at the specific puzzleElement index using this rule + * 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 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 + * @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) { @@ -49,7 +50,8 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem } /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link TreeNode}. + * 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 @@ -60,4 +62,3 @@ public Board getDefaultBoard(TreeNode node) { return null; } } - diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java index d1ed62107..e88b7c6b9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooFewStarsContradictionRule.java @@ -3,31 +3,30 @@ 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.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; import edu.rpi.legup.puzzle.starbattle.StarBattleRegion; - import java.awt.*; public class TooFewStarsContradictionRule extends ContradictionRule { public TooFewStarsContradictionRule() { - super("STBL-CONT-0002", + super( + "STBL-CONT-0002", "Too Few Stars", "There are too few stars in this region/row/column and there are not enough places to put the remaining stars.", "edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @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 + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -51,7 +50,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } StarBattleRegion region = sbBoard.getRegion(cell); int regionCount = 0; - for (StarBattleCell c: region.getCells()) { + for (StarBattleCell c : region.getCells()) { if (c.getType() != StarBattleCellType.BLACK) { ++regionCount; } @@ -61,4 +60,4 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } return super.getNoContradictionMessage(); } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java index 2ae424d45..12603a6ba 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/TooManyStarsContradictionRule.java @@ -3,34 +3,33 @@ 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.lightup.LightUpCell; import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; import edu.rpi.legup.puzzle.starbattle.StarBattleCell; import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; import edu.rpi.legup.puzzle.starbattle.StarBattleRegion; - import java.awt.*; import java.util.List; public class TooManyStarsContradictionRule extends ContradictionRule { - private final String INVALID_USE_MESSAGE = "Contradiction must be applied to a cell containing a star."; + private final String INVALID_USE_MESSAGE = + "Contradiction must be applied to a cell containing a star."; public TooManyStarsContradictionRule() { - super("STBL-CONT-0001", + super( + "STBL-CONT-0001", "Too Many Stars", "There are too many stars in this region/row/column.", "edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png"); } /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using this rule + * Checks whether the transition has a contradiction at the specific puzzleElement index using + * this rule * - * @param board board to check contradiction + * @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 + * otherwise error message */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { @@ -111,4 +110,4 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } return null; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/utility/LegupUtils.java b/src/main/java/edu/rpi/legup/utility/LegupUtils.java index 770ccc6a2..ce21a8d9a 100644 --- a/src/main/java/edu/rpi/legup/utility/LegupUtils.java +++ b/src/main/java/edu/rpi/legup/utility/LegupUtils.java @@ -98,8 +98,7 @@ private static List findClassesZip(String path, String packageName) try { Class c = Class.forName(substr); classes.add(c); - } - catch (LinkageError | ClassNotFoundException e) { + } catch (LinkageError | ClassNotFoundException e) { System.out.println("Failed on " + substr); } } diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..a74654d4301f75defc639727604f3d59f05612a3 GIT binary patch literal 2835 zcma);2~bm67KWn+M3e@ROOr0TBZUM3BWMiY%gtAd9le zBFK^e8XTY_(u5$O0XxVRB%(lY0z`}wLqO)?bWKf9*L2UT_uj4Z?x}O`yY-#_+zgbP zql~nwGzbKeadxtE2gZKj>6MZM-rnLDL|_0DY@9u%fGbhzY&u}?jj{KR@xc8Ub1?{y z1%+O~MPd&TLhxAZ1wt4uhQF=T5^y@S>12z?2E~NqE9=9v6byg*QOVbJjp(;oe$drYp$hN z$rqkLsqS6M_K9i=d7-1LBp+YqgBf6c?QI=O1MTD?cXdNRrK_R*tv@NqDI5>+ep|fH zPOGn%63|Jcq)f09&iWXCPTDY(-N=>jAI(U2%E!9kGV+BOyIPCK!x3s~vb+FQbQa=D zI$Q_-qhl>Cl1+0NZ-5VUk1$Q8%a5Z#5~QtAAaY7MNE$4Gfi1KLT}KCy zSiO7KB{q$pusn60Y8=+FM?r!+Ioy_o(D)>rt6G*UtE;Ox z;8RTSI3C;>-B(F0vyv4RN%c$fX~VG=v{GD?s3fZSCJgIyf+NJY`K=gWbH&7f_6Q?S zU}cIEG6+sBVy~IEPkzTp^Xv8LX>%wJhhqua$yc&q;909eoM8@W`1Cs0(Wjdkx1`|A+(WpsZ zH{JPEhmliMZl3c_w(UUIni=edoEh}H=e^6lq$rz(A*udMk%PCHRv>uQ^325Tayv+? z%!&uy_%@J)D~Mm-m^Tj0vFopXa8_goasLVy(uQ!Xiz zDo_eNk6+D=3b`&JRmKG{hD(!>Jj&}Ps&QXqeRP;@5USkfHY((|ldE~{;Vo4Nht;Q? zC(ji9N1eUA%DycXUq5EZC=8oOidI*j#4|Gx4(;UJcCz+>Q5%NxDqHLQL1Ch$l{x3! zU56~hE6l{rYG5&vh>Nw__kVE3=v^anhj#uCH2#wsCGMql!60YXZ2ZB7LO%ZLgokf_ zP#%6!V`F6Fc`)kRnn+V4e5%dF?Lf^+lRG78K_Dx$bCb*^!0$SdMhnj4yTZ z@_N;%o$NW{`;h0GS#j$rzdG{F@{~&{mt`a!XW)%uSonH*E%a^0GhNN9mX)Wu10Iho zVo2bggu~w{g-jTxo5; zc8~a>2X*#VW;ccVck{$R!7ObwDrnY}gb&Pua?;B8kL9C-2j&HPcIRCQzWNmdo-J?1 zAd3Pdxn=f-vxogwb|Bl!W%gVlEKqQ8Wm()Ip-GbO9ou z9-65K7x29pB7dL!41z&oP{t0`JTkAUuI^rEJio`(F%UyHHA$@|M=VdBdX#zt@vCtC zsf3Qk7)~}jp-~`$a|I&ShKZ{tL&Xgi@XS<~q4J^Kt$`YxmBaf26zY<{uMugxgjnUG z&G|IS`1*bcr!9X}7?3oacvnt;d2Masl#fiD9ZEis?xgDRHH_!=Vk-j?YEq*3Ba){72mtQUNuOWM>L7pUidPhm9qHO?KrC@*^8W=(4>96e)FY zR55`isamkFkRoO8jR~3DSED4C!?rwPu>Cf6d^ohOPI@GClP#;pk6QSI?UQTq&n@Vu z-qH%-16BNlHa~-ef=ABhlXRR=kJaXcd=BN%Oj=*d`H4L>tzkp!^Cq&r|HAFTY3XF6 zjfLcYkah^QdaJY&<-(Ea;nmhC_NutdqwEr;T0<^wG=bDGugO##fTDE!4}1A{H*+`$$d<&uWwWA3gW4bEa! z_V(Qd5ENT_>RH_sV(RKvKVRhf4W~piU4tI-Rv!$W_0frBOV(@82iGu75sc?zaff$A zkBa6f?@rE4D#Kz)l$abx9z)(x38|pLtyYT2JmTV!uG11X<16>0Yi)B+tBPNgmT9;1 z(Lbr|{)^5DlCCGv+;pe%<$Fs@^VHi02T5$QhzF;Phg(WL?2q}y8P189_Z>kLKVZ* zB$gblm=gWBdIUU);~4+Q>*OLcy(M_kDT$-|B&PJ=vKDX1=!p+jaQWUpwFT~HMrX%^ z`hO#>Fl+H(w~YsoUPbm)={WwmeE0Or+bRKLTne)h|>BoT~*jE7)^kzy~H*@RC=( zC($7{7O?tTJX6b|nWM_NM@jf7d-TbmqRSK`CcnSsWHDiSC;M%(*5vzNO{~Yu0ihe6 zN{D9Kua~3}E~p|Hdc61Nr-6EkWFM$!wc#%A-Z8Z_nO$MH6g|il)Vxp5>0}MxK)foI zGvm@#H2HjfaHC>KmY+evyyNXc;J2Xksr$&_A3kjwwuk@xWc9s$dYAC9jWX!oC#s=$ z9{7nX_daSivst`;O=6T6E0=Rtt%emYRIRmWbj-&BkjM(6RIdfeflfvLHypogY}wbi W-$D%jr3*N)f}HK$?8r8Oq<;bxYC}%| literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..214aa53483ccc0f6749bffda004ccfc2f70ded29 GIT binary patch literal 3455 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png new file mode 100644 index 0000000000000000000000000000000000000000..73072f2ce5ea9d189fe55058aa6a4fa09ecc3979 GIT binary patch literal 5578 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..b68f67e441d3519f17daffd48da37f35e9b3bcfb GIT binary patch literal 2751 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..67a4e47f363222af68afe45acacd0783f0f35319 GIT binary patch literal 2982 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..862408b63a9c6d72d815b79064ea587657bc5e5e GIT binary patch literal 2508 zcma)83p|@y7Ee`YLd#NAwMww9R6QD{q}w7D)x;~}Q4+LDWE9aRD2<}VdY5`@Cdjr$ zsd(1An4yAr^?auDb%+7wl`+eu$bI-ZId(Ziw|NW9M_SQ1e zhonIukPPY~5(`K+@b-Ye0={nIfiOTwQqH5C!N3y<_DuxFDj`;GAx^=5Ave9r1Q3xF z97NEg_>c(%66KrVkmcPSW`L32j*%sq;2q*0OoBN32N6Kdp#%u*Gz4*-3^6=uXaqTF zbjHvWXb_AY#0u$rWbU932qbkBg+w@q(*=_V@=TKwOIYe$UWan6V+SG&kZ({(I`Rby zS%`dL_q|fY>b!I3C71W4_u`ar}h@v4pK0QUV^9La|f%4u<^=c~2Ow|#Iq?KfBMVYT^ zjWv5!y=n-0S%27P_PXgKePXWhh#A2UD&F#3mz$+>`l2l_da}RnP)}bGyfofo%ViJTUOuytPATheT2gm8x^1TBlve|e#U@S_ zWJUXthgY>ErAGbW?AuC!E||;w)So{>NKEWi=auSv-#B|+QV}f8u)u)!0H={tw7A0n zNlWg*K%g<5(+GdAYeoGuU17N`PNL|2yxen~jbN{hyZ9b23>T^-WM zbURm&+-$2T4=);SjlrzWyK`t+OC3hW&#zMCC5zWr7y~NCXneJ7mDUpmsCjdR5!i@d9j4j3 zs_f$7Ba!@hwWEOY8!ooIToc+ms#$dF?j;lTbm$CchNpH#H^ISLSt^-gpBBw@ua z<(X54*XF`qvJmfDDx2yX;>~I^0}P7mZU(;npx3;H63Q-kD9X#G<(;8ZMALFUsW-?- ze_BaXj$o!;*X0JFb`6WM=PwPY20e|KsGj@sB^dV zzrjiZvFrB`CxM8N?|Q%)f(v=Xnw-wz>d|9m1&oS`YHCdV+G)Y}xbJ?76RYs3HKT05 zI#k1^nbVz1x^a+EoRHVyex&^ujAaV!iC-Y*K0xzDy4N%W1#6FmzaIMnuvYWrde=%7 zy_DyPwOEZfZknHFaO3#FbV%uIRNL@;CJV3hKCE>%FDVszF=~Hq45MO%zO*T9r@`6Y zPqm;YjEUJ-kP&H&3TOWS+8iJiwb%=q&=cK#+s$A$da!wyU*7z}PEU%x?)~TYUtHj0 zs%BAxM-c91aBXwMj(0X}-VUTTIdRFQ`&_aF!o9}&2|K-LJg<5sQnu)ge6|v0s83iW zm|+F!SYE1xU7%Brqn-9@M1s-D4b<3LvZY(Rlt-v%ipVRi=b?vw-pJfJqk-OvrOhr; ze3b#*D(19U`3;?VYLDqc%s!UFAlU-r1vU|wr`s6(Q8u#lbUisrjXw>(aL17aa9#8{ z&I>-CEXbNZGSm#!R8=(yHC=e$t-(4tGn@FzXt1K018@2lrzPiOj2r$pAO_F=Cgc1H z8XEqXiu&W*5f(v0M)-0!?!q$d@0yce5^OaTv-^OQbe6-?DN_%tck7{__jJYlYTaK*ZfEMipV;Cys58|Gl%#h^@%$aE-m7yH4NQ+JPAkQ6 zvE_h|Z3frqO{nhv>=J+Qu4LB#wR_vOTb@;%;>C3%%NasneS%yv{$2DsHP1u*C_I6p zpA7!G#|DTd^+qC$WeR^_Wal)2`+-Ll@{~PWVHZ+iW&QUFGW8Dk_o2%u$Y&xe4k#?_yKc7UweQ3_)RWjD(}SUUa{2X#QFp@fT z*x%8;JS?^rOR;D0HN28?fgM&TYH#lpt1%JT$ILSA8JA+CReX5#>*=I>Ps-W)Q23}l z#x-!l*7S4~L-dx2?PrF?K2SWr{$VJ0rYGNLsF|Dmc}DgvAO6>!l9bY+4#98YmCO^y z@AKE=L|!eGt%s!pn5wb~6~ujo!$kRwZ90u#54)hBXTyF~NFG8>iQGr*F@=xI%bT{$ zc!v{?#z7W7!dr>j>9bfML=UnR&{`Zv2dz-S#KV7H#lM&3{6O!%pf>Hhf0JpQ9jACH ztY`3-J%6)KkFgHU70AVgFS{dU3<%{mpGX+cv+n;uGC!IZ*`n}x&#E=>rv*Y;*&{2@ HUy1z(|GtR) literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..029bd12ac8782f97428d092ee56187d0aa7d77cf GIT binary patch literal 2793 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index 4aef6de60..07b0b0586 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -4,6 +4,10 @@ qualifiedClassName="edu.rpi.legup.puzzle.battleship.Battleship" fileType=".xml" fileCreationDisabled="true"/> + Date: Sun, 5 May 2024 14:18:16 -0400 Subject: [PATCH 73/74] Minesweeper (#815) * Initial Minesweeper implementation work * Add Unset tile type * Init elements Created initial classes for bomb, flag, utilities, and contradiction rule. * Make clicking tiles cycle between numbers, bomb, and empty tiles. * Fix checkstyle errors * Added * Revert "Added" This reverts commit 89fbe943a4e9ad7ae2fa7daed0b71b6f8357fe30. * Added * Create 123456 * Revert "Create 123456" This reverts commit ace712290986aead21470839844001c3eb9a582d. * Created Bomb or Filled Case Rule * Update BombOrFilledCaseRule.java removed fillapix imports * Various additions Added the unset puzzle element, added the minesweeper board copy function, fixed the bomb or filled case rule * Revert "Create 123456" This reverts commit ace712290986aead21470839844001c3eb9a582d. * Update Minesweeper classes to use newer Java features * Add documentation to Minesweeper classes * Added helper functions to utilities class Added helper functions used for getting cells adjacent to flag, as well as combinations of possible bomb tiles. * Added function to retrieve a tile's number * Create SatisfyFlagCaseRule.java * Fixed "bomb or filled" case rule's picture * Fixed "satisfy flag" case rule's picture * Add some methods to MinesweeperUtilities and created some tests to verify the getSurroundingCells function works as expected. * temp * Added BombOrFilledCaseRule * Revert "temp" This reverts commit b4c8ed9b91fe5ba822df71799c68c017dd42f074. * Update minesweeperUtilities.java * Add bomb image * Added reference sheet for Minesweeper tiles for puzzle editor * Added Minesweeper Utility Functions -Fixed Bomb or Filled case rule picture -Added getTileNumber and setCellType functions -added 5x5 test puzzle * Fixed "satisfy flag" case rule * Automated Java code formatting changes * Revert "Automated Java code formatting changes" This reverts commit f9d52a3eb0c5fe522beb33635816a2a3154c88ed. * Added "More Bombs Than Flag" Contradiction Rule * Delete Unset (replaced by UnsetTile) * Added dot to empty tile image * Fixed "Satisfy Flag" Case Rule Picture * Fixed "satisfy flag" bug fixed bug where "satisfy flag" allowed you to select an empty tile instead of a flag tile * Added "Finish With Bombs" Direct Rule * Update FinishWithBombsDirectRule.java * Automated Java code formatting changes * Automated Java code formatting changes --------- Co-authored-by: Fisher Co-authored-by: FisherLuba <145061313+FisherLuba@users.noreply.github.com> Co-authored-by: philippark Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> --- .../minesweeper/5x5 Minesweeper Easy/123456 | 11 + .../edu/rpi/legup/model/PuzzleImporter.java | 27 +- .../legup/puzzle/minesweeper/Minesweeper.java | 64 +++++ .../puzzle/minesweeper/MinesweeperBoard.java | 36 +++ .../puzzle/minesweeper/MinesweeperCell.java | 73 ++++++ .../minesweeper/MinesweeperCellFactory.java | 101 ++++++++ .../minesweeper/MinesweeperController.java | 57 +++++ .../MinesweeperElementIdentifiers.java | 16 ++ .../minesweeper/MinesweeperElementView.java | 83 +++++++ .../minesweeper/MinesweeperExporter.java | 44 ++++ .../minesweeper/MinesweeperImporter.java | 128 ++++++++++ .../minesweeper/MinesweeperTileData.java | 107 ++++++++ .../minesweeper/MinesweeperTileType.java | 14 ++ .../minesweeper/MinesweeperUtilities.java | 170 +++++++++++++ .../puzzle/minesweeper/MinesweeperView.java | 65 +++++ .../puzzle/minesweeper/elements/BombTile.java | 13 + .../minesweeper/elements/EmptyTile.java | 14 ++ .../puzzle/minesweeper/elements/FlagTile.java | 13 + .../minesweeper/elements/UnsetTile.java | 14 ++ .../minesweeper_elements_reference_sheet.txt | 4 + .../rules/BombOrFilledCaseRule.java | 93 +++++++ .../rules/FinishWithBombsDirectRule.java | 64 +++++ .../LessBombsThanFlagContradictionRule.java | 50 ++++ .../MoreBombsThanFlagContradictionRule.java | 54 ++++ .../rules/SatisfyFlagCaseRule.java | 231 ++++++++++++++++++ .../legup/puzzle/treetent/TreeTentBoard.java | 2 - .../puzzle/treetent/TreeTentImporter.java | 1 - .../treetent/rules/FillinRowCaseRule.java | 37 +-- .../edu/rpi/legup/ui/ProofEditorPanel.java | 2 +- .../images/minesweeper/cases/BombOrFilled.jpg | Bin 0 -> 6037 bytes .../images/minesweeper/cases/SatisfyFlag.jpg | Bin 0 -> 21869 bytes .../images/minesweeper/cases/Satisfy_Flag.png | Bin 0 -> 3496 bytes .../contradictions/Bomb_Surplus.jpg | Bin 0 -> 9833 bytes .../images/minesweeper/direct/Fill_Bombs.jpg | Bin 0 -> 22643 bytes .../legup/images/minesweeper/tiles/Bomb.png | Bin 0 -> 5572 bytes .../legup/images/minesweeper/tiles/Empty.png | Bin 0 -> 176 bytes src/main/resources/edu/rpi/legup/legup/config | 6 +- src/test/java/legup/TestRunner.java | 9 +- .../minesweeper/MinesweeperUtilitiesTest.java | 69 ++++++ .../rules/BlackoutDirectRuleTest.java | 49 ++-- .../rules/EmptyFieldDirectRuleTest.java | 83 +++---- .../treetent/rules/FillinRowCaseRuleTest.java | 49 ++-- .../rules/FinishWithGrassDirectRuleTest.java | 149 +++++------ .../rules/FinishWithTentsDirectRuleTest.java | 152 ++++++------ .../rules/LastCampingSpotDirectRuleTest.java | 80 +++--- .../treetent/rules/LinkTentCaseRuleTest.java | 33 +-- .../treetent/rules/LinkTreeCaseRuleTest.java | 46 ++-- .../SurroundTentWithGrassDirectRuleTest.java | 62 +++-- .../rules/TentForTreeDirectRuleTest.java | 151 ++++++------ .../rules/TreeForTentDirectRuleTest.java | 63 ++--- .../puzzles/minesweeper/utilities/3x3test | 11 + 51 files changed, 2061 insertions(+), 539 deletions(-) create mode 100644 puzzles files/minesweeper/5x5 Minesweeper Easy/123456 create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/EmptyTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/cases/BombOrFilled.jpg create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/cases/SatisfyFlag.jpg create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/cases/Satisfy_Flag.png create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/Bomb_Surplus.jpg create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/direct/Fill_Bombs.jpg create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Bomb.png create mode 100644 src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Empty.png create mode 100644 src/test/java/puzzles/minesweeper/MinesweeperUtilitiesTest.java create mode 100644 src/test/resources/puzzles/minesweeper/utilities/3x3test diff --git a/puzzles files/minesweeper/5x5 Minesweeper Easy/123456 b/puzzles files/minesweeper/5x5 Minesweeper Easy/123456 new file mode 100644 index 000000000..2aa0b46ab --- /dev/null +++ b/puzzles files/minesweeper/5x5 Minesweeper Easy/123456 @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java index c22831c8d..0902478db 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -6,8 +6,6 @@ import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.save.InvalidFileFormatException; - -import java.lang.reflect.Array; import java.util.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -132,18 +130,19 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { public abstract void initializeBoard(String[] statements) throws UnsupportedOperationException, IllegalArgumentException; - /** - * Used to check that elements in the proof tree are saved properly. - *

    Make sure the list elements are lowercase - * - * @return A list of elements that will change when solving the puzzle - * * e.g. {"cell"}, {"cell", "line"} - */ - public List getImporterElements() { - List elements = new ArrayList<>(); - elements.add("cell"); - return elements; - } + /** + * Used to check that elements in the proof tree are saved properly. + * + *

    Make sure the list elements are lowercase + * + * @return A list of elements that will change when solving the puzzle * e.g. {"cell"}, {"cell", + * "line"} + */ + public List getImporterElements() { + List elements = new ArrayList<>(); + elements.add("cell"); + return elements; + } /** * Creates the proof for building diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java new file mode 100644 index 000000000..ed8066f39 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java @@ -0,0 +1,64 @@ +package edu.rpi.legup.puzzle.minesweeper; + +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 org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Minesweeper extends Puzzle { + + public static final String NAME = "Minesweeper"; + + public Minesweeper() { + this.name = NAME; + this.importer = new MinesweeperImporter(this); + this.exporter = new MinesweeperExporter(this); + this.factory = MinesweeperCellFactory.INSTANCE; + } + + @Override + @Contract(pure = false) + public void initializeView() { + this.boardView = new MinesweeperView((MinesweeperBoard) this.currentBoard); + this.boardView.setBoard(this.currentBoard); + addBoardListener(boardView); + } + + @Override + @Contract("_ -> null") + public @Nullable Board generatePuzzle(int difficulty) { + return null; + } + + @Override + @Contract(pure = true) + public boolean isBoardComplete(@NotNull Board board) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board; + + for (ContradictionRule rule : contradictionRules) { + if (rule.checkContradiction(minesweeperBoard) == null) { + return false; + } + } + for (PuzzleElement data : minesweeperBoard.getPuzzleElements()) { + final MinesweeperCell cell = (MinesweeperCell) data; + if (cell.getData().equals(MinesweeperTileData.empty())) { + return false; + } + } + return true; + } + + @Override + @Contract(pure = true) + public void onBoardChange(@NotNull Board board) {} + + @Override + @Contract(pure = true) + public boolean isValidDimensions(int rows, int columns) { + return rows >= 1 && columns >= 1; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java new file mode 100644 index 000000000..bef317ab5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java @@ -0,0 +1,36 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.model.gameboard.GridBoard; + +public class MinesweeperBoard extends GridBoard { + + public MinesweeperBoard(int width, int height) { + super(width, height); + } + + public MinesweeperBoard(int size) { + super(size); + } + + @Override + public MinesweeperCell getCell(int x, int y) { + return (MinesweeperCell) super.getCell(x, y); + } + + /** + * Performs a deep copy of the Board + * + * @return a new copy of the board that is independent of this one + */ + @Override + public MinesweeperBoard copy() { + MinesweeperBoard newMinesweeperBoard = + new MinesweeperBoard(this.dimension.width, this.dimension.height); + for (int x = 0; x < this.dimension.width; x++) { + for (int y = 0; y < this.dimension.height; y++) { + newMinesweeperBoard.setCell(x, y, getCell(x, y).copy()); + } + } + return newMinesweeperBoard; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java new file mode 100644 index 000000000..325e42b7b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java @@ -0,0 +1,73 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.model.elements.Element; +import edu.rpi.legup.model.gameboard.GridCell; +import java.awt.*; +import java.awt.event.MouseEvent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public class MinesweeperCell extends GridCell { + + public MinesweeperCell(@NotNull MinesweeperTileData value, @NotNull Point location) { + super(value, location); + } + + public @NotNull MinesweeperTileType getTileType() { + return super.data.type(); + } + + public @NotNull int getTileNumber() { + return super.data.data(); + } + + @Override + @Contract(pure = false) + /** Sets this cell's data to the value specified by {@link Element#getElementID()} */ + public void setType(@NotNull Element element, @NotNull MouseEvent event) { + switch (element.getElementID()) { + case MinesweeperElementIdentifiers.BOMB -> { + this.data = MinesweeperTileData.bomb(); + break; + } + case MinesweeperElementIdentifiers.FLAG -> { + final int currentData = super.data.data(); + switch (event.getButton()) { + case MouseEvent.BUTTON1 -> { + if (currentData >= 8) { + this.data = MinesweeperTileData.empty(); + return; + } + this.data = MinesweeperTileData.flag(currentData + 1); + return; + } + case MouseEvent.BUTTON2, MouseEvent.BUTTON3 -> { + if (currentData <= 0) { + this.data = MinesweeperTileData.empty(); + return; + } + this.data = MinesweeperTileData.flag(currentData - 1); + return; + } + } + } + default -> { + this.data = MinesweeperTileData.empty(); + } + } + } + + public void setCellType(MinesweeperTileData type) { + this.data = type; + } + + @Override + @Contract(pure = true) + public @NotNull MinesweeperCell copy() { + MinesweeperCell copy = new MinesweeperCell(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/minesweeper/MinesweeperCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java new file mode 100644 index 000000000..5fe6096a9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java @@ -0,0 +1,101 @@ +package edu.rpi.legup.puzzle.minesweeper; + +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 java.awt.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +public class MinesweeperCellFactory extends ElementFactory { + + /** The key of the data used in {@link NamedNodeMap} */ + private static final String DATA_ATTRIBUTE = "data"; + + /** The key of the x position used in {@link NamedNodeMap} */ + private static final String X_ATTRIBUTE = "x"; + + /** The key of the y position used in {@link NamedNodeMap} */ + private static final String Y_ATTRIBUTE = "y"; + + private MinesweeperCellFactory() {} + + public static final MinesweeperCellFactory INSTANCE = new MinesweeperCellFactory(); + + /** + * @param node node that represents the puzzleElement + * @param board Board to use to verify the newly created {@link MinesweeperCell} is valid + * @return a new {@link MinesweeperCell} + * @throws InvalidFileFormatException If the node name is not "cell" + * @throws NumberFormatException If the {@link #X_ATTRIBUTE} or {@link #Y_ATTRIBUTE} is not a + * number + * @throws NullPointerException If one of {@link #DATA_ATTRIBUTE}, {@link #X_ATTRIBUTE} or + * {@link #Y_ATTRIBUTE} does not exist. + */ + @Override + @Contract(pure = false) + public @NotNull PuzzleElement importCell( + @NotNull Node node, @NotNull Board board) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("cell")) { + throw new InvalidFileFormatException( + "Minesweeper Factory: unknown puzzleElement puzzleElement"); + } + + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board; + final int width = minesweeperBoard.getWidth(); + final int height = minesweeperBoard.getHeight(); + + final NamedNodeMap attributeList = node.getAttributes(); + final int value = + Integer.parseInt(attributeList.getNamedItem(DATA_ATTRIBUTE).getNodeValue()); + final int x = Integer.parseInt(attributeList.getNamedItem(X_ATTRIBUTE).getNodeValue()); + final int y = Integer.parseInt(attributeList.getNamedItem(Y_ATTRIBUTE).getNodeValue()); + if (x >= width || y >= height) { + throw new InvalidFileFormatException( + "Minesweeper Factory: cell location out of bounds"); + } + if (value < -2) { + throw new InvalidFileFormatException("Minesweeper Factory: cell unknown value"); + } + final MinesweeperCell cell = + new MinesweeperCell(MinesweeperTileData.fromData(value), new Point(x, y)); + cell.setIndex(y * height + x); + return cell; + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Minesweeper Factory: unknown value where integer expected"); + } catch (NullPointerException e) { + throw new InvalidFileFormatException( + "Minesweeper Factory: could not find attribute(s)"); + } + } + + /** + * @param document Document used to create the element + * @param puzzleElement PuzzleElement cell + * @return a {@link Element} that contains the {@link #DATA_ATTRIBUTE}, {@link #X_ATTRIBUTE}, + * and {@link #Y_ATTRIBUTE} + */ + @Override + @Contract(pure = false) + public @NotNull Element exportCell( + @NotNull Document document, + @SuppressWarnings("rawtypes") @NotNull PuzzleElement puzzleElement) { + org.w3c.dom.Element cellElement = document.createElement("cell"); + + MinesweeperCell cell = (MinesweeperCell) puzzleElement; + Point loc = cell.getLocation(); + + cellElement.setAttribute(DATA_ATTRIBUTE, String.valueOf(cell.getData())); + cellElement.setAttribute(X_ATTRIBUTE, String.valueOf(loc.x)); + cellElement.setAttribute(Y_ATTRIBUTE, String.valueOf(loc.y)); + + return cellElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java new file mode 100644 index 000000000..aaf061704 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java @@ -0,0 +1,57 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.awt.event.MouseEvent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public class MinesweeperController extends ElementController { + + /** + * If the button clicked was button 1, then {@link MinesweeperTileData#fromData(int)} is called + * with a value of {@code current.data() + 1}. If the button clicked was button 2 or 3, then + * {@link MinesweeperTileData#fromData(int)} is called with a value of {@code currentData() - 1} + * Otherwise {@link MinesweeperTileData#empty()} is returned. + * + * @param event The user's click data + * @param current The current data at the cell they clicked on + * @return A different cell data depending on what the current data is + */ + @Contract(pure = true) + public static @NotNull MinesweeperTileData getNewCellDataOnClick( + @NotNull MouseEvent event, @NotNull MinesweeperTileData current) { + final int numberData = current.data(); + return switch (event.getButton()) { + case MouseEvent.BUTTON1 -> MinesweeperTileData.fromData(numberData + 1); + case MouseEvent.BUTTON2, MouseEvent.BUTTON3 -> + MinesweeperTileData.fromData(numberData - 1); + default -> MinesweeperTileData.empty(); + }; + } + + /** + * @see #getNewCellDataOnClick(MouseEvent, MinesweeperTileData) + * @param event The user's click data + * @param data The current data at the cell they clicked on + */ + @Override + @SuppressWarnings("unchecked") + @Contract(pure = false) + public void changeCell( + @NotNull MouseEvent event, @SuppressWarnings("rawtypes") @NotNull PuzzleElement data) { + final MinesweeperCell cell = (MinesweeperCell) data; + if (event.isControlDown()) { + this.boardView + .getSelectionPopupMenu() + .show( + boardView, + this.boardView.getCanvas().getX() + event.getX(), + this.boardView.getCanvas().getY() + event.getY()); + return; + } + + final MinesweeperTileData newData = getNewCellDataOnClick(event, cell.getData()); + data.setData(newData); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java new file mode 100644 index 000000000..77e490f7e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java @@ -0,0 +1,16 @@ +package edu.rpi.legup.puzzle.minesweeper; + +public class MinesweeperElementIdentifiers { + + /** ID for unset Minesweeper elements */ + public static final String UNSET = "MINESWEEPER-UNSET"; + + /** ID for bomb Minesweeper elements */ + public static final String BOMB = "MINESWEEPER-BOMB"; + + /** ID for empty Minesweeper elements */ + public static final String EMPTY = "MINESWEEPER-EMPTY"; + + /** ID for flag Minesweeper elements */ + public static final String FLAG = "MINESWEEPER-FLAG"; +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java new file mode 100644 index 000000000..1bfc0d698 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java @@ -0,0 +1,83 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.ui.boardview.GridElementView; +import java.awt.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public class MinesweeperElementView extends GridElementView { + + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public MinesweeperElementView(@NotNull MinesweeperCell cell) { + super(cell); + } + + @Override + public @NotNull MinesweeperCell getPuzzleElement() { + return (MinesweeperCell) super.getPuzzleElement(); + } + + @Override + @SuppressWarnings("Duplicates") + @Contract(pure = true) + public void drawElement(@NotNull Graphics2D graphics2D) { + final MinesweeperCell cell = (MinesweeperCell) puzzleElement; + final MinesweeperTileType type = cell.getTileType(); + if (type == MinesweeperTileType.FLAG) { + 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); + + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + final FontMetrics metrics = graphics2D.getFontMetrics(FONT); + final String value = String.valueOf(((MinesweeperCell) puzzleElement).getData().data()); + final int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + final int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + graphics2D.drawString(value, xText, yText); + return; + } + if (type == MinesweeperTileType.UNSET) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.DARK_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + return; + } + if (type == MinesweeperTileType.EMPTY) { + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.drawImage( + MinesweeperView.EMPTY_IMAGE, + location.x, + location.y, + size.width, + size.height, + Color.GRAY, + null); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + if (type == MinesweeperTileType.BOMB) { + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.drawImage( + MinesweeperView.BOMB_IMAGE, + location.x, + location.y, + size.width, + size.height, + Color.GRAY, + 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/minesweeper/MinesweeperExporter.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperExporter.java new file mode 100644 index 000000000..8ae9c5a9a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperExporter.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.PuzzleExporter; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class MinesweeperExporter extends PuzzleExporter { + + public MinesweeperExporter(@NotNull Puzzle puzzle) { + super(puzzle); + } + + @Override + @Contract(pure = true) + protected @NotNull Element createBoardElement(@NotNull Document newDocument) { + MinesweeperBoard board; + if (puzzle.getTree() != null) { + board = (MinesweeperBoard) puzzle.getTree().getRootNode().getBoard(); + } else { + board = (MinesweeperBoard) puzzle.getBoardView().getBoard(); + } + + final org.w3c.dom.Element boardElement = newDocument.createElement("board"); + boardElement.setAttribute("width", String.valueOf(board.getWidth())); + boardElement.setAttribute("height", String.valueOf(board.getHeight())); + + final org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + final MinesweeperCell cell = (MinesweeperCell) puzzleElement; + if (!MinesweeperTileData.unset().equals(cell.getData())) { + final 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/minesweeper/MinesweeperImporter.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java new file mode 100644 index 000000000..419a69247 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java @@ -0,0 +1,128 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.model.PuzzleImporter; +import edu.rpi.legup.save.InvalidFileFormatException; +import java.awt.*; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class MinesweeperImporter extends PuzzleImporter { + + public MinesweeperImporter(@NotNull Minesweeper minesweeper) { + super(minesweeper); + } + + @Override + @Contract(pure = true, value = "-> true") + public boolean acceptsRowsAndColumnsInput() { + return true; + } + + @Override + @Contract(pure = true, value = "-> false") + public boolean acceptsTextInput() { + return false; + } + + @Override + @Contract(pure = false) + public void initializeBoard(int rows, int columns) { + MinesweeperBoard minesweeperBoard = new MinesweeperBoard(columns, rows); + + for (int y = 0; y < rows; y++) { + for (int x = 0; x < columns; x++) { + MinesweeperCell cell = + new MinesweeperCell(MinesweeperTileData.unset(), new Point(x, y)); + cell.setIndex(y * columns + x); + cell.setModifiable(true); + minesweeperBoard.setCell(x, y, cell); + } + } + puzzle.setCurrentBoard(minesweeperBoard); + } + + @Override + @Contract(pure = false) + public void initializeBoard(@NotNull Node node) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("board")) { + throw new InvalidFileFormatException( + "Minesweeper Importer: cannot find board puzzleElement"); + } + final Element boardElement = (Element) node; + if (boardElement.getElementsByTagName("cells").getLength() == 0) { + throw new InvalidFileFormatException( + "Minesweeper Importer: no puzzleElement found for board"); + } + final Element dataElement = + (Element) boardElement.getElementsByTagName("cells").item(0); + final NodeList elementDataList = dataElement.getElementsByTagName("cell"); + + final MinesweeperBoard minesweeperBoard = getMinesweeperBoard(boardElement); + + final int width = minesweeperBoard.getWidth(); + final int height = minesweeperBoard.getHeight(); + + for (int i = 0; i < elementDataList.getLength(); i++) { + final MinesweeperCell cell = + (MinesweeperCell) + puzzle.getFactory() + .importCell(elementDataList.item(i), minesweeperBoard); + final Point loc = cell.getLocation(); + if (MinesweeperTileData.unset().equals(cell.getData())) { + cell.setModifiable(false); + cell.setGiven(true); + } + minesweeperBoard.setCell(loc.x, loc.y, cell); + } + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (minesweeperBoard.getCell(x, y) == null) { + final MinesweeperCell cell = + new MinesweeperCell(MinesweeperTileData.unset(), new Point(x, y)); + cell.setIndex(y * height + x); + cell.setModifiable(true); + minesweeperBoard.setCell(x, y, cell); + } + } + } + puzzle.setCurrentBoard(minesweeperBoard); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "Minesweeper Importer: unknown value where integer expected"); + } + } + + @Contract(pure = true) + private static @NotNull MinesweeperBoard getMinesweeperBoard(@NotNull Element boardElement) + throws InvalidFileFormatException { + MinesweeperBoard minesweeperBoard = null; + if (!boardElement.getAttribute("size").isEmpty()) { + final int size = Integer.parseInt(boardElement.getAttribute("size")); + minesweeperBoard = new MinesweeperBoard(size); + } else { + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { + final int width = Integer.parseInt(boardElement.getAttribute("width")); + final int height = Integer.parseInt(boardElement.getAttribute("height")); + minesweeperBoard = new MinesweeperBoard(width, height); + } + } + + if (minesweeperBoard == null) { + throw new InvalidFileFormatException("Minesweeper Importer: invalid board dimensions"); + } + return minesweeperBoard; + } + + @Override + @Contract(value = "_ -> fail", pure = false) + public void initializeBoard(@NotNull String[] statements) + throws UnsupportedOperationException, IllegalArgumentException { + throw new UnsupportedOperationException("Minesweeper does not support text input."); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java new file mode 100644 index 000000000..5296cf057 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java @@ -0,0 +1,107 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import java.util.Objects; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record MinesweeperTileData(MinesweeperTileType type, int data) { + + public static final int UNSET_DATA = -2; + public static final int BOMB_DATA = -1; + public static final int EMPTY_DATA = 0; + + /** + * Always has a type of {@link MinesweeperTileType#UNSET}, and a data value of {@value + * UNSET_DATA} + */ + private static final MinesweeperTileData UNSET = + new MinesweeperTileData(MinesweeperTileType.UNSET, UNSET_DATA); + + /** + * Always has a type of {@link MinesweeperTileType#BOMB}, and a data value of {@value BOMB_DATA} + */ + private static final MinesweeperTileData BOMB = + new MinesweeperTileData(MinesweeperTileType.BOMB, BOMB_DATA); + + /** + * Always has a type of {@link MinesweeperTileType#EMPTY}, and a data value of {@value + * EMPTY_DATA} + */ + private static final MinesweeperTileData EMPTY = + new MinesweeperTileData(MinesweeperTileType.EMPTY, EMPTY_DATA); + + /** + * @param count how many bombs are near the flag + * @return a new {@link MinesweeperTileData} with a {@link MinesweeperTileData#type} of {@link + * MinesweeperTileType#FLAG} and a {@link MinesweeperTileData#data} of {@code count} + */ + @Contract(pure = true) + public static @NotNull MinesweeperTileData flag(int count) { + return new MinesweeperTileData(MinesweeperTileType.FLAG, count); + } + + /** + * @param data Determines what type of {@link MinesweeperTileData} to return. + * @return If {@code data} is one of {@link MinesweeperTileData#UNSET_DATA}, {@link + * MinesweeperTileData#BOMB_DATA}, or {@link MinesweeperTileData#EMPTY_DATA}, it will return + * that data. If {@code data} is less than any of the values, or greater than 8, it will + * return {@link MinesweeperTileData#UNSET_DATA}. Otherwise, it returns {@link + * MinesweeperTileData#flag(int)} and passes {@code data} as the parameter. + */ + @Contract(pure = true) + public static @NotNull MinesweeperTileData fromData(int data) { + return switch (data) { + case UNSET_DATA -> unset(); + case BOMB_DATA -> bomb(); + case EMPTY_DATA -> empty(); + default -> { + if (data <= -2 || data > 8) { + yield unset(); + } + yield flag(data); + } + }; + } + + public static @NotNull MinesweeperTileData unset() { + return UNSET; + } + + public static @NotNull MinesweeperTileData bomb() { + return BOMB; + } + + public static @NotNull MinesweeperTileData empty() { + return EMPTY; + } + + public boolean isUnset() { + return this.data == UNSET_DATA; + } + + public boolean isBomb() { + return this.data == BOMB_DATA; + } + + public boolean isEmpty() { + return this.data == EMPTY_DATA; + } + + public boolean isFlag() { + return this.data > 0 && this.data <= 8; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MinesweeperTileData that = (MinesweeperTileData) o; + return data == that.data && type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(type, data); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java new file mode 100644 index 000000000..a682da5e5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java @@ -0,0 +1,14 @@ +package edu.rpi.legup.puzzle.minesweeper; + +public enum MinesweeperTileType { + + /** A cell with nothing */ + UNSET, + + /** Represents a cell with no bombs in it */ + EMPTY, + /** A flag has values 1-8 representing how many bombs are touching it */ + FLAG, + /** A bomb tile that should be marked by nearby flags */ + BOMB +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java new file mode 100644 index 000000000..d38460ac8 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java @@ -0,0 +1,170 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.puzzle.minesweeper.rules.LessBombsThanFlagContradictionRule; +import java.awt.*; +import java.util.*; +import java.util.Objects; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public final class MinesweeperUtilities { + + private static final int SURROUNDING_CELL_MIN_INDEX = 0; + private static final int SURROUNDING_CELL_MAX_INDEX = 9; + + public static Stream getSurroundingCells( + MinesweeperBoard board, MinesweeperCell cell) { + final Point loc = cell.getLocation(); + final int height = board.getHeight(); + final int width = board.getWidth(); + final int x = (int) loc.getX(); + final int y = (int) loc.getY(); + // IntStream of 0-9 to represent 2D matrix of surrounding elements, + // this maps from 0,0 to 2,2 so everything needs to be shifted + // left 1 and up 1 to become + // -1,1 to 1,1 + // and 5 is skipped because we want to ignore 1,1 + return IntStream.range(SURROUNDING_CELL_MIN_INDEX, SURROUNDING_CELL_MAX_INDEX) + // skip 0,0 element + .filter(i -> i != (SURROUNDING_CELL_MAX_INDEX - SURROUNDING_CELL_MIN_INDEX) / 2) + .mapToObj( + index -> { + final int newX = index / 3 - 1 + x; + final int newY = index % 3 - 1 + y; + // only keep valid locations + if (newX < 0 || newY < 0 || newX >= width || newY >= height) { + return null; + } + return board.getCell(newX, newY); + }) + .filter(Objects::nonNull); + } + + public static int countSurroundingType( + MinesweeperBoard board, MinesweeperCell cell, MinesweeperTileType type) { + final Stream stream = + getSurroundingCells(board, cell).map(MinesweeperCell::getData); + return (int) + (switch (type) { + case UNSET -> stream.filter(MinesweeperTileData::isUnset); + case BOMB -> stream.filter(MinesweeperTileData::isBomb); + case EMPTY -> stream.filter(MinesweeperTileData::isEmpty); + case FLAG -> stream.filter(MinesweeperTileData::isFlag); + }) + .count(); + } + + public static int countSurroundingBombs(MinesweeperBoard board, MinesweeperCell cell) { + return countSurroundingType(board, cell, MinesweeperTileType.BOMB); + } + + public static int countSurroundingUnset(MinesweeperBoard board, MinesweeperCell cell) { + return countSurroundingType(board, cell, MinesweeperTileType.UNSET); + } + + public static int countSurroundingEmpty(MinesweeperBoard board, MinesweeperCell cell) { + return countSurroundingType(board, cell, MinesweeperTileType.EMPTY); + } + + public static int countSurroundingFlags(MinesweeperBoard board, MinesweeperCell cell) { + return countSurroundingType(board, cell, MinesweeperTileType.FLAG); + } + + /** + * @return how many bombs are left that need to be placed around {@code cell} which must be a + * flag + */ + public int countNeededBombsFromFlag(MinesweeperBoard board, MinesweeperCell cell) { + if (!cell.getData().isFlag()) { + throw new IllegalArgumentException("Bombs are only needed surrounding flags"); + } + return cell.getData().data() - countSurroundingBombs(board, cell); + } + + public static boolean hasEmptyAdjacent(MinesweeperBoard board, MinesweeperCell cell) { + ArrayList adjCells = getAdjacentCells(board, cell); + for (MinesweeperCell adjCell : adjCells) { + if (adjCell.getTileType() == MinesweeperTileType.UNSET) { + return true; + } + } + return false; + } + + public static ArrayList getAdjacentCells( + MinesweeperBoard board, MinesweeperCell cell) { + ArrayList adjCells = new ArrayList(); + Point cellLoc = cell.getLocation(); + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (cellLoc.getX() + i < 0 + || cellLoc.y + j < 0 + || cellLoc.x + i >= board.getWidth() + || cellLoc.y + j >= board.getHeight()) { + continue; + } + MinesweeperCell adjCell = + (MinesweeperCell) board.getCell(cellLoc.x + i, cellLoc.y + j); + if (adjCell == null || adjCell == cell) { + continue; + } + adjCells.add(adjCell); + } + } + return adjCells; + } + + public static ArrayList getCombinations(int chosenNumItems, int totalNumItems) { + ArrayList combinations = new ArrayList(); + + // calculate all combinations + boolean[] array = new boolean[totalNumItems]; + recurseCombinations(combinations, 0, chosenNumItems, 0, totalNumItems, array); + + return combinations; + } + + private static void recurseCombinations( + ArrayList result, + int curIndex, + int maxBlack, + int numBlack, + int len, + boolean[] workingArray) { + if (curIndex == len) { + // complete, but not valid solution + if (numBlack != maxBlack) { + return; + } + // complete and valid solution + result.add(workingArray.clone()); + return; + } + // there is no chance of completing the required number of solutions, so quit + if (len - curIndex < maxBlack - numBlack) { + return; + } + + if (numBlack < maxBlack) { + workingArray[curIndex] = true; + recurseCombinations(result, curIndex + 1, maxBlack, numBlack + 1, len, workingArray); + } + workingArray[curIndex] = false; + recurseCombinations(result, curIndex + 1, maxBlack, numBlack, len, workingArray); + } + + public static boolean isForcedBomb(MinesweeperBoard board, MinesweeperCell cell) { + + LessBombsThanFlagContradictionRule tooManyBombs = new LessBombsThanFlagContradictionRule(); + MinesweeperBoard emptyCaseBoard = board.copy(); + MinesweeperCell emptyCell = (MinesweeperCell) emptyCaseBoard.getPuzzleElement(cell); + emptyCell.setCellType(MinesweeperTileData.empty()); + ArrayList adjCells = getAdjacentCells(emptyCaseBoard, emptyCell); + for (MinesweeperCell adjCell : adjCells) { + if (tooManyBombs.checkContradictionAt(emptyCaseBoard, adjCell) == null) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java new file mode 100644 index 000000000..e8ab8dfcb --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java @@ -0,0 +1,65 @@ +package edu.rpi.legup.puzzle.minesweeper; + +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.ui.boardview.GridBoardView; +import java.awt.*; +import java.io.IOException; +import java.util.Objects; +import javax.imageio.ImageIO; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +public class MinesweeperView extends GridBoardView { + + private static final Logger LOGGER = LogManager.getLogger(MinesweeperView.class.getName()); + public static final Image BOMB_IMAGE; + + public static final Image EMPTY_IMAGE; + + static { + Image tempBombImage = null; + try { + tempBombImage = + ImageIO.read( + Objects.requireNonNull( + ClassLoader.getSystemClassLoader() + .getResource( + "edu/rpi/legup/images/minesweeper/tiles/Bomb.png"))); + } catch (IOException e) { + LOGGER.error("Failed to open Minesweeper images"); + } + BOMB_IMAGE = tempBombImage; + } + + static { + Image tempEmptyImage = null; + try { + tempEmptyImage = + ImageIO.read( + Objects.requireNonNull( + ClassLoader.getSystemClassLoader() + .getResource( + "edu/rpi/legup/images/minesweeper/tiles/Empty.png"))); + } catch (IOException e) { + LOGGER.error("Failed to open Minesweeper images"); + } + EMPTY_IMAGE = tempEmptyImage; + } + + public MinesweeperView(@NotNull MinesweeperBoard board) { + super(new BoardController(), new MinesweeperController(), board.getDimension()); + + for (PuzzleElement puzzleElement : board.getPuzzleElements()) { + final MinesweeperCell cell = (MinesweeperCell) puzzleElement; + final Point loc = cell.getLocation(); + final MinesweeperElementView elementView = new MinesweeperElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(elementView); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java new file mode 100644 index 000000000..78a5d320c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.minesweeper.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class BombTile extends NonPlaceableElement { + public BombTile() { + super( + "MINE-UNPL-0001", + "Bomb", + "A bomb", + "edu/rpi/legup/images/minesweeper/tiles/Bomb.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/EmptyTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/EmptyTile.java new file mode 100644 index 000000000..7149bfa6a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/EmptyTile.java @@ -0,0 +1,14 @@ +package edu.rpi.legup.puzzle.minesweeper.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class EmptyTile extends PlaceableElement { + + public EmptyTile() { + super( + "MINE-PLAC-0002", + "Empty", + "An empty tile", + "edu/rpi/legup/images/minesweeper/tiles/Empty.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java new file mode 100644 index 000000000..0bbca81f9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.minesweeper.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class FlagTile extends PlaceableElement { + public FlagTile() { + super( + "MINE-PLAC-0001", + "Flag", + "The flag", + "edu/rpi/legup/images/nurikabe/tiles/BlackTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java new file mode 100644 index 000000000..447e2840c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java @@ -0,0 +1,14 @@ +package edu.rpi.legup.puzzle.minesweeper.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class UnsetTile extends NonPlaceableElement { + + public UnsetTile() { + super( + "MINE-UNPL-0002", + "Unset", + "An unset tile", + "edu/rpi/legup/images/minesweeper/tiles/Unset.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt new file mode 100644 index 000000000..08ce23f59 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt @@ -0,0 +1,4 @@ +MINE-UNPL-0001 : BombTile +MINE-PLAC-0001 : FlagTile +MINE-PLAC-0002 : EmptyTile +MINE-UNPL-0002 : UnsetTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java new file mode 100644 index 000000000..a1ef97928 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java @@ -0,0 +1,93 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileData; +import java.util.ArrayList; +import java.util.List; + +public class BombOrFilledCaseRule extends CaseRule { + + public BombOrFilledCaseRule() { + super( + "MINE-CASE-0001", + "Bomb Or Filled", + "A cell can either be a bomb or filled.\n", + "edu/rpi/legup/images/minesweeper/cases/BombOrFilled.jpg"); + } + + @Override + public CaseBoard getCaseBoard(Board board) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(minesweeperBoard, this); + minesweeperBoard.setModifiable(false); + for (PuzzleElement data : minesweeperBoard.getPuzzleElements()) { + MinesweeperCell cell = (MinesweeperCell) data; + if (cell.getData().isUnset()) { + caseBoard.addPickableElement(data); + } + } + return caseBoard; + } + + @Override + public List getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList<>(); + + Board case1 = board.copy(); + MinesweeperCell cell1 = (MinesweeperCell) case1.getPuzzleElement(puzzleElement); + cell1.setData(MinesweeperTileData.bomb()); + case1.addModifiedData(cell1); + cases.add(case1); + + Board case2 = board.copy(); + MinesweeperCell cell2 = (MinesweeperCell) case2.getPuzzleElement(puzzleElement); + cell2.setData(MinesweeperTileData.empty()); + case2.addModifiedData(cell2); + cases.add(case2); + return cases; + } + + @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."; + } + + MinesweeperCell mod1 = + (MinesweeperCell) case1.getBoard().getModifiedData().iterator().next(); + MinesweeperCell mod2 = + (MinesweeperCell) 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.getData().isBomb() && mod2.getData().isEmpty()) + || (mod2.getData().isBomb() && mod1.getData().isEmpty()))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must an empty cell and a bomb cell."; + } + + return null; + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java new file mode 100644 index 000000000..e85008d23 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java @@ -0,0 +1,64 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.*; + +public class FinishWithBombsDirectRule extends DirectRule { + public FinishWithBombsDirectRule() { + super( + "MINE-BASC-0001", + "Finish with Bombs", + "The remaining unknowns around a flag must be bombs to satisfy the number", + "edu/rpi/legup/images/minesweeper/direct/Fill_Bombs.jpg"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + MinesweeperBoard parentBoard = (MinesweeperBoard) transition.getParents().get(0).getBoard(); + MinesweeperCell cell = (MinesweeperCell) board.getPuzzleElement(puzzleElement); + MinesweeperCell parentCell = (MinesweeperCell) parentBoard.getPuzzleElement(puzzleElement); + + if (!(parentCell.getTileType() == MinesweeperTileType.UNSET + && cell.getTileType() == MinesweeperTileType.BOMB)) { + return super.getInvalidUseOfRuleMessage() + + ": This cell must be black to be applicable with this rule."; + } + + if (MinesweeperUtilities.isForcedBomb(parentBoard, cell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be black"; + } + } + + /** + * 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) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) node.getBoard().copy(); + for (PuzzleElement element : minesweeperBoard.getPuzzleElements()) { + MinesweeperCell cell = (MinesweeperCell) element; + if (cell.getTileType() == MinesweeperTileType.UNSET + && MinesweeperUtilities.isForcedBomb( + (MinesweeperBoard) node.getBoard(), cell)) { + cell.setCellType(MinesweeperTileData.bomb()); + minesweeperBoard.addModifiedData(cell); + } + } + if (minesweeperBoard.getModifiedData().isEmpty()) { + return null; + } else { + return minesweeperBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java new file mode 100644 index 000000000..c9919343f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java @@ -0,0 +1,50 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.*; +import java.util.ArrayList; + +public class LessBombsThanFlagContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Contradiction must be a region"; + + public LessBombsThanFlagContradictionRule() { + super( + "MINE-CONT-0000", + "Less Bombs Than Flag", + "There can not be less then the number of Bombs around a flag then the specified number\n", + "edu/rpi/legup/images/nurikabe/contradictions/NoNumber.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board; + MinesweeperCell cell = (MinesweeperCell) minesweeperBoard.getPuzzleElement(puzzleElement); + + int cellNum = cell.getTileNumber(); + if (cellNum <= 0 || cellNum >= 9) { + return super.getNoContradictionMessage(); + } + int numEmpty = 0; + int numAdj = 0; + ArrayList adjCells = + MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); + for (MinesweeperCell adjCell : adjCells) { + numAdj++; + if (adjCell.getTileType() == MinesweeperTileType.EMPTY && adjCell != cell) { + numEmpty++; + } + } + System.out.println(numEmpty); + System.out.println(numAdj); + System.out.println(cellNum); + if (numEmpty > (numAdj - cellNum)) { + return null; + } + + return super.getNoContradictionMessage(); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java new file mode 100644 index 000000000..ecfdbad66 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java @@ -0,0 +1,54 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileType; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperUtilities; +import java.util.ArrayList; + +public class MoreBombsThanFlagContradictionRule extends ContradictionRule { + + public MoreBombsThanFlagContradictionRule() { + super( + "MINE-CONT-0001", + "More Bombs Than Flag", + "There can not be more Bombs around a flag than the specified number\n", + "edu/rpi/legup/images/minesweeper/contradictions/Bomb_Surplus.jpg"); + } + + /** + * 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) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board; + MinesweeperCell cell = (MinesweeperCell) minesweeperBoard.getPuzzleElement(puzzleElement); + + int cellNum = cell.getTileNumber(); + if (cellNum < 0 || cellNum >= 10) { + return super.getNoContradictionMessage(); + } + int numBlack = 0; + ArrayList adjCells = + MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); + for (MinesweeperCell adjCell : adjCells) { + if (adjCell.getTileType() == MinesweeperTileType.BOMB) { + numBlack++; + } + } + if (numBlack > cellNum) { + return null; + } + + return super.getNoContradictionMessage(); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java new file mode 100644 index 000000000..a59369b7a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java @@ -0,0 +1,231 @@ +package edu.rpi.legup.puzzle.minesweeper.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.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.*; +import java.awt.*; +import java.util.*; +import java.util.List; + +public class SatisfyFlagCaseRule extends CaseRule { + public SatisfyFlagCaseRule() { + super( + "MINE-CASE-0002", + "Satisfy Flag", + "Create a different path for each valid way to mark bombs and filled cells around a flag", + "edu/rpi/legup/images/minesweeper/cases/Satisfy_Flag.png"); + } + + @Override + public CaseBoard getCaseBoard(Board board) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(minesweeperBoard, this); + minesweeperBoard.setModifiable(false); + for (PuzzleElement data : minesweeperBoard.getPuzzleElements()) { + MinesweeperCell cell = (MinesweeperCell) data; + if (cell.getTileNumber() > 0 + && cell.getTileNumber() <= 8 + && MinesweeperUtilities.hasEmptyAdjacent(minesweeperBoard, cell)) { + caseBoard.addPickableElement(data); + } + } + return caseBoard; + } + + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList(); + + // get value of cell + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board.copy(); + MinesweeperCell cell = (MinesweeperCell) minesweeperBoard.getPuzzleElement(puzzleElement); + int cellMaxBlack = cell.getTileNumber(); + if (cellMaxBlack <= 0 || cellMaxBlack > 8) { // cell is not valid cell + return null; + } + + // find number of black & unset squares + int cellNumBomb = 0; + int cellNumUnset = 0; + ArrayList unsetCells = new ArrayList(); + ArrayList adjCells = + MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); + for (MinesweeperCell adjCell : adjCells) { + if (adjCell.getTileType() == MinesweeperTileType.BOMB) { + cellNumBomb++; + } + if (adjCell.getTileType() == MinesweeperTileType.UNSET) { + cellNumUnset++; + unsetCells.add(adjCell); + } + } + // no cases if no empty or if too many black already + if (cellNumBomb >= cellMaxBlack || cellNumUnset == 0) { + return cases; + } + + // generate all cases as boolean expressions + ArrayList combinations; + combinations = + MinesweeperUtilities.getCombinations(cellMaxBlack - cellNumBomb, cellNumUnset); + + for (int i = 0; i < combinations.size(); i++) { + Board case_ = board.copy(); + for (int j = 0; j < combinations.get(i).length; j++) { + cell = (MinesweeperCell) case_.getPuzzleElement(unsetCells.get(j)); + if (combinations.get(i)[j]) { + cell.setCellType(MinesweeperTileData.bomb()); + } else { + cell.setCellType(MinesweeperTileData.empty()); + } + case_.addModifiedData(cell); + } + cases.add(case_); + } + + return cases; + } + + @Override + public String checkRuleRaw(TreeTransition transition) { + TreeNode parent = transition.getParents().get(0); + List childTransitions = parent.getChildren(); + + /* + * In order for the transition to be valid, it can only be applied to + * one cell, thus: + * * there must be modified cells + * * all modified cells must share at least one common adjacent + * cell + * * all modified cells must fit within a 3X3 square + * * the center of one of the possible squares must be a cell + * with a number + * * that cells possible combinations must match the transitions + * If all the above is verified, then the transition is valid + */ + + /* ensure there are modified cells */ + Set modCells = transition.getBoard().getModifiedData(); + if (modCells.size() <= 0) { + return super.getInvalidUseOfRuleMessage(); + } + + /* ensure modified cells occur within a 3X3 square */ + int minVertLoc = Integer.MAX_VALUE, maxVertLoc = Integer.MIN_VALUE; + int minHorzLoc = Integer.MAX_VALUE, maxHorzLoc = Integer.MIN_VALUE; + for (PuzzleElement modCell : modCells) { + Point loc = ((MinesweeperCell) modCell).getLocation(); + if (loc.x < minHorzLoc) { + minHorzLoc = loc.x; + } + if (loc.x > maxHorzLoc) { + maxHorzLoc = loc.x; + } + if (loc.y < minVertLoc) { + minVertLoc = loc.y; + } + if (loc.y > maxVertLoc) { + maxVertLoc = loc.y; + } + } + if (maxVertLoc - minVertLoc > 3 || maxHorzLoc - minHorzLoc > 3) { + return super.getInvalidUseOfRuleMessage(); + } + + /* get the center of all possible 3X3 squares, + * and collect all that have numbers */ + MinesweeperBoard board = (MinesweeperBoard) transition.getParents().get(0).getBoard(); + ArrayList possibleCenters = new ArrayList(); + for (PuzzleElement modCell : modCells) { + ArrayList adjacentCells = + MinesweeperUtilities.getAdjacentCells(board, (MinesweeperCell) modCell); + for (MinesweeperCell cell : adjacentCells) { + possibleCenters.add(cell); + } + } + + // removing all elements without a valid number + possibleCenters.removeIf(x -> x.getTileNumber() <= 0 || x.getTileNumber() >= 9); + if (possibleCenters.isEmpty()) { + return super.getInvalidUseOfRuleMessage(); + } + + /* Now go through the remaining centers, and check if their combinations + * match the transitions */ + for (MinesweeperCell possibleCenter : possibleCenters) { + int numBlack = 0; + int numEmpty = 0; + int maxBlack = possibleCenter.getTileNumber(); + for (MinesweeperCell adjCell : + MinesweeperUtilities.getAdjacentCells(board, possibleCenter)) { + if (adjCell.getTileType() == MinesweeperTileType.BOMB) { + numBlack++; + } + if (adjCell.getTileType() == MinesweeperTileType.UNSET) { + numEmpty++; + } + } + if (numEmpty <= 0 || numBlack > maxBlack) { + // this cell has no cases (no empty) or is already broken (too many black) + continue; + } + + ArrayList combinations = + MinesweeperUtilities.getCombinations(maxBlack - numBlack, numEmpty); + if (combinations.size() != childTransitions.size()) { + // not this center because combinations do not match transitions + continue; + } + boolean quitEarly = false; + for (TreeTransition trans : childTransitions) { + /* convert the transition board into boolean format, so that it + * can be compared to the combinations */ + MinesweeperBoard transBoard = (MinesweeperBoard) trans.getBoard(); + ArrayList transModCells = new ArrayList(); + for (PuzzleElement modCell : modCells) { + transModCells.add((MinesweeperCell) transBoard.getPuzzleElement(modCell)); + } + + boolean[] translatedModCells = new boolean[transModCells.size()]; + for (int i = 0; i < transModCells.size(); i++) { + if (transModCells.get(i).getTileType() == MinesweeperTileType.BOMB) { + translatedModCells[i] = true; + } else { + translatedModCells[i] = false; + } + } + + // try to find the above state in the combinations, remove if found + boolean removed = false; + for (boolean[] combination : combinations) { + if (Arrays.equals(combination, translatedModCells)) { + combinations.remove(combination); + removed = true; + break; + } + } + // if combination not found, no need to check further, just quit + if (!removed) { + quitEarly = true; + break; + } + } + + /* we found a center that is valid */ + if (combinations.isEmpty() && !quitEarly) { + return null; + } + } + + return super.getInvalidUseOfRuleMessage(); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java index 6ded23a18..c8962aa03 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentBoard.java @@ -3,8 +3,6 @@ import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.tree.Tree; - import java.awt.*; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java index c791617ce..56dcca59f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTentImporter.java @@ -5,7 +5,6 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; - import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index a796c992a..aaa1a8fbc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -1,11 +1,9 @@ package edu.rpi.legup.puzzle.treetent.rules; -import edu.rpi.legup.model.Puzzle; 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.Tree; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.treetent.TreeTentBoard; import edu.rpi.legup.puzzle.treetent.TreeTentCell; @@ -13,9 +11,7 @@ import edu.rpi.legup.puzzle.treetent.TreeTentType; import java.awt.*; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; public class FillinRowCaseRule extends CaseRule { @@ -95,7 +91,6 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { } /** - * * @param iBoard the board to place tents onto * @param tiles the locations where tents can be placed * @param target the target number of tents to place @@ -111,14 +106,21 @@ private ArrayList genCombinations( boolean isRow) { ArrayList generatedBoards = new ArrayList<>(); genCombRecursive( - iBoard, tiles, target, 0, new ArrayList(), 0, index, generatedBoards, isRow); + iBoard, + tiles, + target, + 0, + new ArrayList(), + 0, + index, + generatedBoards, + isRow); return generatedBoards; } /** - * - * Recursive function to generate all ways of placing the target number of tents - * from the list of tiles to fill. + * Recursive function to generate all ways of placing the target number of tents from the list + * of tiles to fill. * * @param iBoard The board * @param tiles Unknown Tiles to fill @@ -128,10 +130,8 @@ private ArrayList genCombinations( * @param selected the cells which have tents * @param index The index of the clue * @param isRow Used for checking if the board is good - * - * The generated boards are placed into generatedBoards (passed by reference) + *

    The generated boards are placed into generatedBoards (passed by reference) */ - private void genCombRecursive( TreeTentBoard iBoard, List tiles, @@ -168,14 +168,23 @@ private void genCombRecursive( // // Backtracking: // Remove the placed tent from the board and selected - for (int i = currentTile; i < tiles.size(); ++i){ + for (int i = currentTile; i < tiles.size(); ++i) { TreeTentCell tile = tiles.get(i); selected.add(tile); PuzzleElement element = iBoard.getPuzzleElement(tile); element.setData(TreeTentType.TENT); iBoard.addModifiedData(element); if (goodBoard(iBoard, index, isRow)) { - genCombRecursive(iBoard, tiles, target, current + 1, selected, i + 1, index, generatedBoards, isRow); + genCombRecursive( + iBoard, + tiles, + target, + current + 1, + selected, + i + 1, + index, + generatedBoards, + isRow); } element.setData(TreeTentType.UNKNOWN); iBoard.addModifiedData(element); diff --git a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java index 8401e19f2..645a2c0d7 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -580,7 +580,7 @@ private void saveProofAs() { if (puzzle == null) { return; } - + LegupPreferences preferences = LegupPreferences.getInstance(); File preferredDirectory = new File(preferences.getUserPref(LegupPreferences.WORK_DIRECTORY)); diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/cases/BombOrFilled.jpg b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/BombOrFilled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ce7c7418a76349a8a02177ffd42839c2296973e3 GIT binary patch literal 6037 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 Kx1V6p*M9?uqEs0G literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/cases/SatisfyFlag.jpg b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/SatisfyFlag.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae575bd8c2f0aae43caeabfe6e15fdbb06e24993 GIT binary patch literal 21869 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 T4m5G=xBr(PMLzNj>fQKXPAN_w literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/cases/Satisfy_Flag.png b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/Satisfy_Flag.png new file mode 100644 index 0000000000000000000000000000000000000000..7272bbdb66d79af17513181bc3535c91745e4a92 GIT binary patch literal 3496 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 DmMtQG literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/Bomb_Surplus.jpg b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/Bomb_Surplus.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5e8c36662ffe42161667275d6b4d3848dbb8ec4f GIT binary patch literal 9833 zcmbt(3piBk+xHUX7$uQZlMs=na_WGogs>%wk&UU89MW`T99N|%$DKoViYc=TiAg!^ z#7xeWEh5IDj3H-g#$gPzX07kB_xt_d_g{VA^}gS?X4ZAhTJx-D-G|?O-@oU6unw#j zT4rlyV+Dzci9x@C4}@``4VDyNZwRuthjbwbk^=uJLgL_;82J2sVBL@fv`9>B;rkzl z_+s&eV~K=>_+rT=l9CIjl(ek0l$4B=q@>JJ85vnQa7aqaD=d|hU--Q+$-?6cPl2zT zl%&+ci2vDvy@r-cLHSU=xY!10(Q+~IB?|HWEv+Bh z+F2c)U7WuDF9U-^!y}`-$*JiX{;XhbenBrWNcdMyWfEdoA}kX+D9Y*7$6 z#g|JgR^PTnaj&D~@$)M-Y`-d{wD0!A!b)ik11Ijv6BjHXS^`KpvbHlcsjDUDoH^bAmDwl>F-#I%Il~Pvty-O*W2uSQC3w$sr znmTPx!DEnQARF<(aX(;?0%16eOG4!*n1V8-8jR4V1fem+cqUhwjVwIzU$6bb8N1P| z7&J+_&vfl|m|sUnv})i^Xfy09{M z8tpE@puVxGrEqU-#M;x27$ghcuuU8_3lO44a~?1AG03;^M1NS~#Q=`x{@+W#L93&- zy~>dYT=)3pdDVP(g0NNv7wF_U88lGbo|)aoWl0RIu_n*JhVh{;53Pta7ph*(P8~b8 z-QCcnGLJHMlm>HX=sLK0u9u0d?S^@m;A(5c6oVd4Rl+U09sYR6P_PKI$O2z>0>4A5pq7NGt$@>dO9QSE`=wCz*`Q5GFTmIR?Fdd+ z65-$9e0eS!gXG<~-$Y7uHMm!wDF99d*TWc;p4v}F76a(5j$p6FpgkBgXKFcQCjFor&Jui}V{*53G`#+@9}wE?a`z)^g!~%87!$q*wkfEglXXu)voPO`0Z9 z^Z;h>{EgWbSe`i?!6l*!K-V&47m#ZsV#>v!n;7H`ByjgztsD#Q;Gs{l?Cwc7|a&1%^q^OODA7fC5zY_+1$n%^4%8g@CiU*|?t;0#fqmF;D zu7l*I4u5%fi7i~}N18YCZ%RYreBon9Sjv5DP)<14N5!mD}gA!f!qzk|R?7 zTtJ_Z9l37IFT}feo0&nQHqfF2E9Yu?$*7E7C@;Ey=n8U*Yr>(|-0P0F>h=^B7e&qTJKpc06Sh}1t+4Jq?7KTZB+sm^tRQ3e2u=8PNz=2iZRwi>kJ~zjzs+^} zu+PuY*Xaord6m(cd$KFxwr${jQ)^UuA{1A10I}?k4|OkZGuQHC`DY7a9WvZHC_WbI z+eUcZD<-WnEGFHPE`B8TJ2xTs({2*RkKpFd(}(f4f`A7jxMk#W2meQ%-GWHhC^fsW z;np*(^H!af5$Dc@_M3{`ha{&)E@F_jR-b}!r6hXa|I1X|=4S$mP&6%r#64RjX~@0 zIn2+-f)wOd=scY`C*cOO%1QH!SO&c8el|eOX}pAepTjx|&4QZ{DSO8Mv(w12_< zOb=$~o?p8d6K7z_k8zKj>vX>kd3ZVqk4AJ+6)@-oTe4%cXGhy$>7Q+17e9{w!}V9A zulWV;{HXTM?UN-3R5^P!G*626@BJri)5t{S%Q<)4vNj;}WJ1bTa;d^Jj6l#XiLeZAebTHda= zH|^8QS8G2sWLs@1*{&GpT;%uOems?x956;sH#MkamqgbtHR)5SX&_025qRuslEi1J zVzXoZjqCEmIm&IwV-Zv5rDlb@(YWa33y-blEPi?w~X3 ziLoyScHaK->0n=HodR?YT0?ANa~<)~Zn!F{K%04U+Cknou*^J}oYT>`^3n6u1mk4K zTc57j)VU@nM4BEuPtv&gZj{6Ah-vlBJ{aiMrShtX{Nd}g+oOQZX)`!kLF%2MPma}{ z-tuuH?=yEV8E~Auc;cnI`ouQn*@{Odl$P1YFSWM>j!qrPn`9%)no#@XdK~}IWwI!n z&*%m(W{+P8^DmvOnC1Z)ovbUSt^6_!N?M)+ zcQ{yq#8Mj{kEB!`x>4Cq4ALp%WsBm>6*1@|&4vzA-?hC<2!(;krBUeTk9*9c z8{LUi{M@P*-~|lYbvic_ep~(*b6gK3gvTI}d_&fdL3%vMldNZU;oJmz9FG%yO`N8y z1rfDREdAB@()~6i8veC+uJ2Y|jYZvn_xk%@le0!6q>_WFIX-YPvqos<=<4T!Zf%irz&Lwz^4uQH{{C#qdKdCd9h`fKuf^PtpR^G3UkncPDT9o<#1VqbBh!^dLH zq=us}63smxKNOSRr7e`y+c5Oo^BmJi(won)1)J-q9R0)>gos0vlBZ`dwX-ZO?yH)E zoVjO3kVi8Nm zJdJpAjpT=d!I|fhv6X-Ij6yD=&rX%o&{)|0+W7?Zvb8(duGAF}UYS zXu*HLm0FAq9=;`nuJjv>7aU^Ai>du2zZ449wsYN7)z>p}nRn@L2ojwUom9DLt(Gt| z?oglnjmNbbZ}c~to+6WFUaff%a1?rLn%q-fPArYm%?LU2sU5`6RJG2%H1Mae$%!Yoe;d?d3DZSQ+G+4k4P zz2{Y9Tz|jp;!^2kCPnYQ+E3_yfSXf_&^>uUiucJK{$e{AM(ibbwbrMndyY}1%8x$v zw6fSd%h>oL^N_X0%iq&4)StM=Y@?!!70?55d6C=C7_Hn_5^iPVLw#1OeU@DrMA(Tz z$C!eR#9FdIhg_o;p!a%yX-&w6_qn-(n6DcX?OJHsgF(r(XHcJE!-}g{+nKreP8I$M zE^FLZxJy9fFmHJ2(FC520X6TTJAAJx`|}%jur+VopFb(~dgYwWV}EuDKh|q)e%e)3 ztFxc6<`^-?QZ7n!FoeTj*hI<|TD?`0T;>*{F*j*lk}6P&m`)P9m!BW<+(8G9t8B>fCo z{}cuhFG@TMzF^h!SKRCVO#ivx0g4MifG9CQYO5qLG)KD%qW>7p+$3-aI|`b4-*(4jRU0G!9}~`b9$lQwVbFk^0r&O^ z?*Lj-2o%B^p(4w*Q7PYkeAnT=q38%r`uu%(7mm8O+b|L zz!kg(BonvA?qujh;y7FA<(od&7Jy0$GMC!FG&wIwuk;?HR=hIvf4Z88C_xHy%guGU zTM2!PNU+X_{k&r9^tqrGz6L@6cHQ9dWNxk7!3=qg>RJY)L6O}sYxj*n>XTJEPV{>c z2hkvfJ-dlb1g^8OB!4~6K?5cHoyZtV(09O?SK#JY-mhSJK1>iycneB7)QJx~6^mqR z7DENGY%gla)!2TNP3h^PXq`Iu#@6t<;;Kak%~$-_L};kptUmH^jln@j|GsEv^571E z!gT>bz0aL#O0P-LmNN=*c6VN5-<7?i@llA&E8BNN+3-&iTLegOm0K{#hmeSqMim6D zHeC8RPdh%}i^s?zpJj4%qZ-D8j;Ed~(LMCFgQ#}3vN2pAgO*Z-hu{yiw-|JzkqMU5 zbhmWBO$L!Gos9Hs|5)1^K(`7HwhRO7@b3rGP&ESo0S3{m0q$k*!Kl%ZWB=`;d)D;} zJ>a)e9|_~yGceHs)Lk(U`Dd4m0D&7|_zE+m z6!@*O8FN%!D|{2l7y;%>5#mL+y#QI0)}_7d=Sd>5dGPglJ^mX2i5SFK6lv<*dyjC7 zXwqeVxBu3bVAh+(({Y&uJCocLx}2YoKlM9zIh+QY8t#xjA|&%la8_9m^|p# zHIeTk+C^#>l9!@!JDb*>JNai?be?5KKp(KQnXFb zn~vr%YZc_fj&lR3H_JB_jF@XSUtX*bkmFlwbCJAN!*o}YK|;Mm2(g)SK4qI>o4aWW z?elKxRi49=p)ixUJEY9x+54V`9MlhESy8QuyodgF)T8I|5$u3T`uwmEF4~l;m55x| zXO0!Y^UDg72Oa*xAc3aLCIN{TG=Q6Q3A64-GyTWNfBQNt4Upg~fP|BHq3FA&n;10x zm{C$-Fgs4*(uSGp3_z_e!@8!$=qP$*RL5BDzbJkXF!(0^OH16M%L*y*8waZi3~~f) z-JSmKCoEzQZY^9H3J<6-097eWs|0){)pz9}NIV6}B0lO}q5XdYDAWbk3kz!onYmI9 z8OiR1$76A#9X2EzVk1))9x!jfpp&i`G*V@o^Ob_^1%0nkHpi5O%J0V@usr31_@IkG zOJ~0|X%||BL5mPIUPHfCY&L~eL+UKMT$k(XrJU+7mqhY+vi&0UAxe2w6g$)9*paO= z(x%}n0dd#>12hXk&xeHqPMf7c2LXSQgd2fn3BF7*L8AMg{`R>2EPXP0qigzMC7bn| zD~~9jk$>&Yl;CA$Zt#10E-A$?ROMa&X}tpPq_U8T5=Pn4PU^5qb4%vN2*rL{L9I=v>ptWS2|#+Dfz|LkV=wgU>SX7jAQywDos2ra>* zzIeCy0eqGD{I*ZqpKbekYcSA7o$+<=+7Kdvn_O+aU6hTSK-B~my`O*WTUWRu;Lprv zsY?~RMR~FHHI7$jn$8vvoTKyUU(tt+6PxL0`9fkVJV zzKYa2ec}7FNapOt=@tCLAarj|Z2nT*46-yVT^jlImTOvV56%W}s2BNqsiAwLf96~d zEomWIk~#R=L|MeAx#)9fmHCEd#4)SKW<_=Qqn`T9ve#ZG-j%N8@6c#r8Ut!e5SUzO znY8n`$dBfEQL2VlriN*7W|Nxsm6|SlVOPU){gx3EEU|TvX=AR4=x~{p;j;G4V8~~M zs(JnUw>6D-d87nBdmsnR9g?G7ObYj(*U+2)_KZXt7(yoiv=w@~aD5Ijq)`Wcqr(iY zJKSwP^j9+LPS+%#QNlhvRgX%GfrTrxZ&s8_T@IjVA09x`1l4Fg;Y%9gi9yd_^btgx z==phG~0EqI`ch6AyTW_ z-ONh2*fu$5%&uo(&@sj_vSsYfY-)Q+Ng81XVF6cx@4wpFFW~Ap??zYZf8uKG|Hjqc z^-*hBBMQ~<4RnpbthbvWFF3rfCCuc($f#nsLu-IjSI@E~J1n8oYv0?<)yH|3Ne(1-F?5Dd?7crV%N;6_6y`MWtwGLS!CQ z7w`jBnM~xrOm%pjVKGRWlOFzzL8IHeNO>t@JQA`-|M6#k64wU4X&h)WITBRrHgrS1 zv?5;I?2q`qc+JgE6eM!hub8eORysvg5sH?jVo?9q^6=S20ypvK#ikbKUP5mLb265_ z3hrp=#UPPwQOuMt%(6j+u)!XjI_SSkM>LbBDV}B^ltLO19ngMP6g^-LbIU`)rqN8A zfXF&>Nb(JnTSY2T5v@~6gt?i|LHC`&xz6;#&zs`cVGzgc6uNSzO-XDLk8Dkw0R129 z8~toV`zf=E5nL>~8)4)Fb6qG1AwXzHsUj#7svSz=yU{aSw~K z3B%J2pbN(gaHks9M?>d}28lvEXb+mxFz5<>(j26XAI5#aAh~SeLHNT+n8kwf^%!)E zp5sHi8_oArK*Iq*#7JMlDtl3VnM$gTAA#VrYgL3$6xpKoh~Pwnr|CRX^YO=yS#6 z#DHDw&zc=c6R`>FEZ#c4o*-Gn-#JJZC(tlKZj)n3{38qXnKm{qll;;*%iQt@&w=_O zrzPAAf8bDNL3SC#bLj;+4-{_0-?>id`*-M#J<$;d!o|YRCF>H!C@w5lp320|ew?aL zSwu0dAvnv!r{r06PBuUP_sP!YvyMq>8p2{B=cgTx|4yncMIA%?75+jd9D?CZUbA1Ii4$s1n^~b{4*Jt zP;BsrwheO=!Jj^-zB*cTeVQyXb!Cly{%7R?%3F%3pc<_sNrEVL{)2eW4lQ$-?Vd}Y zrV8+6Bx|+~$KNg>fc>QW4dq>=nHHGA{;OHs2egb9>X+WXDMca?-7 zoh#;ytQZ|=;mq9(0)3v1mSv;Yb$2B-p@e)48hZi{@B62vWB^tc0#MCPIn?0h%^9!h zi6EiHu_rAVo8ZpFJV`W>{_LNbz-;1;Kd2Mo6u}}*?lV#lN)NN=DTVa5GDyHeWv7DW zsetJ|=M~r16jn-@Z{Wry*=Srp@R5>jqjfhC5__wC)l*h`LXZ2UQ5$i`i-|y$y_?YR z5wxY}=DlPo^h8>^e zRh1I<81)0+jIcXTqw(2%7-Rs@1DiC1!Y+ccrNMw$;Z4uD%$-2=? zKy=2Lf{E~Wu)&)lx&se)!5}ITlXPng(g1P)bl*S3@NiKyqFe zH@9k>Sxy+q55Ms<@BANJ_61aAg?{*2Ii4HTchZd-7pC>N;XKd8^UqsbX7XHDyQYUd zjaj$nsOzQUmE-VK)kIE{NC__nN{#g?460u!KdAzMMN8S&u)whs=5>M1^R>H$AUr=5p-o`LNH$DF zt!Y2G2lirM&Z7um><|06I8-T+gaju3|4E3b(+P literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/direct/Fill_Bombs.jpg b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/Fill_Bombs.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8214972ac8bf278e441e719ad9ef32c3feae346e GIT binary patch literal 22643 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 qKNsOIqW7=B3F9s2eTRO!tr)YnHh$2ag@yf literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Bomb.png b/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Bomb.png new file mode 100644 index 0000000000000000000000000000000000000000..dd6bad509542c4ac925986d71315b4d655e952c4 GIT binary patch literal 5572 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 + fileCreationDisabled="false"/> + diff --git a/src/test/java/legup/TestRunner.java b/src/test/java/legup/TestRunner.java index 8661f0e69..5486a0353 100644 --- a/src/test/java/legup/TestRunner.java +++ b/src/test/java/legup/TestRunner.java @@ -3,10 +3,11 @@ import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; -import puzzles.battleship.rules.*; +import puzzles.battleship.rules.AdjacentShipsContradictionRuleTest; +import puzzles.battleship.rules.FinishWithShipsDirectRuleTests; import puzzles.lightup.rules.*; +import puzzles.minesweeper.MinesweeperUtilitiesTest; import puzzles.nurikabe.rules.*; -import puzzles.skyscrapers.rules.*; import puzzles.treetent.rules.*; /** This class runs all of the tests for the project without needing to run build scripts. */ @@ -93,6 +94,10 @@ public static void main(String[] args) { printTestResults(result36); Result result37 = JUnitCore.runClasses(TentOrGrassCaseRuleTest.class); printTestResults(result37); + + // Minesweeper + Result result38 = JUnitCore.runClasses(MinesweeperUtilitiesTest.class); + printTestResults(result38); } private static void printTestResults(Result result) { diff --git a/src/test/java/puzzles/minesweeper/MinesweeperUtilitiesTest.java b/src/test/java/puzzles/minesweeper/MinesweeperUtilitiesTest.java new file mode 100644 index 000000000..0e0112040 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/MinesweeperUtilitiesTest.java @@ -0,0 +1,69 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperUtilities; +import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.stream.Stream; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MinesweeperUtilitiesTest { + + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + @Test + public void getSurroundingCellsSizeThreeByThreeAtOneXOneTest() + throws InvalidFileFormatException { + + TestUtilities.importTestBoard("puzzles/minesweeper/utilities/3x3test", minesweeper); + + final MinesweeperBoard board = (MinesweeperBoard) minesweeper.getCurrentBoard(); + MinesweeperCell cell = board.getCell(1, 1); + + final Stream cells = MinesweeperUtilities.getSurroundingCells(board, cell); + + final long count = cells.count(); + Assert.assertEquals(count, 8); + } + + @Test + public void getSurroundingCellsSizeThreeByThreeAtZeroXZeroTest() + throws InvalidFileFormatException { + + TestUtilities.importTestBoard("puzzles/minesweeper/utilities/3x3test", minesweeper); + + final MinesweeperBoard board = (MinesweeperBoard) minesweeper.getCurrentBoard(); + MinesweeperCell cell = board.getCell(0, 0); + + final Stream cells = MinesweeperUtilities.getSurroundingCells(board, cell); + + final long count = cells.count(); + Assert.assertEquals(count, 3); + } + + @Test + public void getSurroundingCellsSizeThreeByThreeAtZeroXOneTest() + throws InvalidFileFormatException { + + TestUtilities.importTestBoard("puzzles/minesweeper/utilities/3x3test", minesweeper); + + final MinesweeperBoard board = (MinesweeperBoard) minesweeper.getCurrentBoard(); + MinesweeperCell cell = board.getCell(0, 1); + + final Stream cells = MinesweeperUtilities.getSurroundingCells(board, cell); + + final long count = cells.count(); + Assert.assertEquals(count, 5); + } +} diff --git a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java index 7789b273b..59d5b37af 100644 --- a/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java +++ b/src/test/java/puzzles/starbattle/rules/BlackoutDirectRuleTest.java @@ -2,30 +2,30 @@ // Commenting this out for now, but once Star Battle is fully implemented this should // be uncommented and finished. -//package puzzles.starbattle.rules; +// package puzzles.starbattle.rules; // -//import edu.rpi.legup.puzzle.nurikabe.Nurikabe; -//import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; -//import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; -//import edu.rpi.legup.puzzle.nurikabe.NurikabeType; -//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.puzzle.nurikabe.Nurikabe; +// import edu.rpi.legup.puzzle.nurikabe.NurikabeBoard; +// import edu.rpi.legup.puzzle.nurikabe.NurikabeCell; +// import edu.rpi.legup.puzzle.nurikabe.NurikabeType; +// 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.puzzle.starbattle.StarBattle; -//import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; -//import edu.rpi.legup.puzzle.starbattle.StarBattleCell; -//import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; -//import edu.rpi.legup.puzzle.starbattle.rules.BlackoutDirectRule; -//import edu.rpi.legup.save.InvalidFileFormatException; +// import edu.rpi.legup.puzzle.starbattle.StarBattle; +// import edu.rpi.legup.puzzle.starbattle.StarBattleBoard; +// import edu.rpi.legup.puzzle.starbattle.StarBattleCell; +// import edu.rpi.legup.puzzle.starbattle.StarBattleCellType; +// import edu.rpi.legup.puzzle.starbattle.rules.BlackoutDirectRule; +// import edu.rpi.legup.save.InvalidFileFormatException; // -//import java.awt.*; +// import java.awt.*; // -//public class BlackoutDirectRuleTest { +// public class BlackoutDirectRuleTest { // // private static final BlackoutDirectRule RULE = new BlackoutDirectRule(); // private static StarBattle starbattle; @@ -38,7 +38,9 @@ // // @Test // public void BlackoutDirectRuleTest_ColumnBlackout() throws InvalidFileFormatException { -// TestUtilities.importTestBoard("puzzles/starbattle/rules/BlackoutDirectRule/ColumnBlackout", starbattle); +// +// TestUtilities.importTestBoard("puzzles/starbattle/rules/BlackoutDirectRule/ColumnBlackout", +// starbattle); // TreeNode rootNode = starbattle.getTree().getRootNode(); // TreeTransition transition = rootNode.getChildren().get(0); // transition.setRule(RULE); @@ -61,7 +63,8 @@ // for (int i = 0; i < board.getHeight(); i++) { // for (int k = 0; k < board.getWidth(); k++) { // Point point = new Point(k, i); -// if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || point.equals(cell3.getLocation())) { +// if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) || +// point.equals(cell3.getLocation())) { // Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); // } // else { @@ -70,4 +73,4 @@ // } // } // } -//} +// } diff --git a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java index 8ffb2ee4f..c704d59b7 100644 --- a/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/EmptyFieldDirectRuleTest.java @@ -26,17 +26,16 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule - *

    Empty - * XXX - * XGX - * XXX - *

    Makes the (1, 1) tile GRASS - * Checks if the rule correctly detects no trees around the grass tile - */ - @Test - public void EmptyFieldTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + * + *

    Empty XXX XGX XXX + * + *

    Makes the (1, 1) tile GRASS Checks if the rule correctly detects no trees around the grass + * tile + */ + @Test + public void EmptyFieldTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyField", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -70,17 +69,16 @@ public void EmptyFieldTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule - *

    Trees are at (0, 0), (2, 0), (0, 2), and (2, 2) - * RXR - * XGX - * RXR - *

    Makes the (1, 1) tile GRASS - * Checks if the rule correctly ignores the trees on the diagonals - */ - @Test - public void DiagonalTreeTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + * + *

    Trees are at (0, 0), (2, 0), (0, 2), and (2, 2) RXR XGX RXR + * + *

    Makes the (1, 1) tile GRASS Checks if the rule correctly ignores the trees on the + * diagonals + */ + @Test + public void DiagonalTreeTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/DiagonalTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -114,17 +112,15 @@ public void DiagonalTreeTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule - *

    Trees are at (0, 1), (1, 0), (1, 2), and (2, 1) - * XRX - * RGR - * XRX - *

    Makes the (1, 1) tile GRASS - * Checks if the rule is not valid when there are adjacent trees - */ - @Test - public void EmptyFieldTestFail() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + * + *

    Trees are at (0, 1), (1, 0), (1, 2), and (2, 1) XRX RGR XRX + * + *

    Makes the (1, 1) tile GRASS Checks if the rule is not valid when there are adjacent trees + */ + @Test + public void EmptyFieldTestFail() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFail", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -153,17 +149,16 @@ public void EmptyFieldTestFail() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule - *

    Tree at (1, 0) - * XRX - * XGX - * XXX - *

    Makes the (1, 1) tile GRASS - * Checks if the rule is not valid when there is one adjacent tree - */ - @Test - public void EmptyFieldTestFailTop() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests EmptyFieldDirectRule + * + *

    Tree at (1, 0) XRX XGX XXX + * + *

    Makes the (1, 1) tile GRASS Checks if the rule is not valid when there is one adjacent + * tree + */ + @Test + public void EmptyFieldTestFailTop() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/EmptyFieldDirectRule/EmptyFieldFailTop", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java index 71b478e7a..3b8389407 100644 --- a/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FillinRowCaseRuleTest.java @@ -6,17 +6,15 @@ import edu.rpi.legup.puzzle.treetent.*; import edu.rpi.legup.puzzle.treetent.rules.FillinRowCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.lang.reflect.Array; -import java.util.ArrayList; - public class FillinRowCaseRuleTest { - private static final FillinRowCaseRule RULE = new FillinRowCaseRule(); + private static final FillinRowCaseRule RULE = new FillinRowCaseRule(); private static TreeTent treetent; @BeforeClass @@ -26,11 +24,11 @@ public static void setUp() { } /** - * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles - * and a clue of 0 tents in the row. + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles and a clue of 0 + * tents in the row. * - *

    checks that 1 case is created and that it is equivalent to FinishWithGrass rule - * May need to change checks due to issue #777 + *

    checks that 1 case is created and that it is equivalent to FinishWithGrass rule May need + * to change checks due to issue #777 * * @throws InvalidFileFormatException */ @@ -98,16 +96,12 @@ public void TentOrTreeTestZeroTentClue() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles - * and a clue of 1 tent in the row. The column rules make the board impossible, but - * they are not checked here. + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles and a clue of 1 + * tent in the row. The column rules make the board impossible, but they are not checked here. * - *

    checks 3 cases are created checks; - * first case is TENT tile at x=0, - * second case is TENT tile at x=1, - * and a third case is TENT tile at x=2. - * The cases can be in any order. - * Then, it checks that other cells have not been modified + *

    checks 3 cases are created checks; first case is TENT tile at x=0, second case is TENT + * tile at x=1, and a third case is TENT tile at x=2. The cases can be in any order. Then, it + * checks that other cells have not been modified * * @throws InvalidFileFormatException */ @@ -145,7 +139,7 @@ public void FillInRowEmptyOneTentClue() throws InvalidFileFormatException { for (int w = 0; w < board.getWidth(); w++) { for (int h = 0; h < board.getHeight(); h++) { if (h == 1) { - continue; + continue; } original_cell = board.getCell(w, h); @@ -155,7 +149,6 @@ public void FillInRowEmptyOneTentClue() throws InvalidFileFormatException { case_cell = testCase.getCell(w, h); Assert.assertEquals(original_cell.getType(), case_cell.getType()); - } } } @@ -199,13 +192,11 @@ public void FillInRowEmptyOneTentClue() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles - * and a clue of 2 tent in the row. The column rules make the board impossible, but - * they are not checked here. + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles and a clue of 2 + * tent in the row. The column rules make the board impossible, but they are not checked here. * - *

    checks 1 case is created. Checks that the case is when - * there are TENT tiles at x=0 and x=2. - * Then, it checks that other cells have not been modified + *

    checks 1 case is created. Checks that the case is when there are TENT tiles at x=0 and + * x=2. Then, it checks that other cells have not been modified * * @throws InvalidFileFormatException */ @@ -287,8 +278,8 @@ public void FillInRowEmptyTwoTentClue() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles - * and a clue of 3 tent in the row. + * empty 3x3 TreeTent puzzle Tests FillinRowCaseRule on row with 3 UNKNOWN tiles and a clue of 3 + * tent in the row. * *

    checks that 0 cases are created * @@ -319,8 +310,8 @@ public void FillInRowEmptyThreeTentClue() throws InvalidFileFormatException { } /** - * empty 5x5 TreeTent puzzle Tests FillinRowCaseRule on row with 5 UNKNOWN tiles - * and a clue of 2 tents in the row. + * empty 5x5 TreeTent puzzle Tests FillinRowCaseRule on row with 5 UNKNOWN tiles and a clue of 2 + * tents in the row. * *

    checks that 6 cases are created and each case has the right number of tents * diff --git a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java index f37761e26..c89c96dd1 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithGrassDirectRuleTest.java @@ -28,18 +28,16 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Tent at (1, 1) - * XXX x - * GTG 1 - * XXX x - * xxx - *

    Makes (0, 1) and (2, 1) GRASS - * Checks if the rule detects the middle row to be filled in correctly - */ - @Test - public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + * + *

    Tent at (1, 1) XXX x GTG 1 XXX x xxx + * + *

    Makes (0, 1) and (2, 1) GRASS Checks if the rule detects the middle row to be filled in + * correctly + */ + @Test + public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -78,18 +76,16 @@ public void FinishWithGrassHorizontalTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Tent at (0, 0) - * TXX x - * GXX x - * GXX x - * 1xx - *

    Makes (0, 1) and (0, 2) GRASS - * Checks if the rule detects the leftmost column to be filled in correctly - */ - @Test - public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + * + *

    Tent at (0, 0) TXX x GXX x GXX x 1xx + * + *

    Makes (0, 1) and (0, 2) GRASS Checks if the rule detects the leftmost column to be filled + * in correctly + */ + @Test + public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/CornerTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -130,13 +126,11 @@ public void FinishWithGrassVerticalTest() throws InvalidFileFormatException { /** * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Tent at (0, 0) - * TGG 1 - * GXX x - * GXX x - * 1xx - *

    Makes (0, 1), (0, 2), (1, 0), and (2, 0) GRASS - * Checks if the rule detects the top row and leftmost column to be filled in correctly + * + *

    Tent at (0, 0) TGG 1 GXX x GXX x 1xx + * + *

    Makes (0, 1), (0, 2), (1, 0), and (2, 0) GRASS Checks if the rule detects the top row and + * leftmost column to be filled in correctly */ @Test public void FinishWithGrassTest() throws InvalidFileFormatException { @@ -186,18 +180,16 @@ public void FinishWithGrassTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Empty - * GGG 0 - * GGG 0 - * GGG 0 - * 000 - *

    Fill Board with GRASS - * Checks if the rule allows all cells to be filled when the clue for all rows and columns is zero. - */ - @Test - public void NoTentTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + * + *

    Empty GGG 0 GGG 0 GGG 0 000 + * + *

    Fill Board with GRASS Checks if the rule allows all cells to be filled when the clue for + * all rows and columns is zero. + */ + @Test + public void NoTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/NoTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -231,18 +223,16 @@ public void NoTentTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Tent at (1, 1) - * XGX x - * GTG 1 - * XGX x - * x1x - *

    Makes (1, 0), (0, 1), (2, 1), and (1, 2) GRASS - * Checks if the rule correctly allows the central row and column to be filled with grass. - */ - @Test - public void MiddleTentTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + * + *

    Tent at (1, 1) XGX x GTG 1 XGX x x1x + * + *

    Makes (1, 0), (0, 1), (2, 1), and (1, 2) GRASS Checks if the rule correctly allows the + * central row and column to be filled with grass. + */ + @Test + public void MiddleTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/MiddleTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -290,18 +280,16 @@ public void MiddleTentTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Empty - * GGG 1 - * GGG 1 - * GGG 1 - * 111 - *

    Fill Board with GRASS - * Checks if the rule is not valid when a row or column does not have the required number of tents but is filled with grass - */ - @Test - public void FailTentTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithGrassDirectRule + * + *

    Empty GGG 1 GGG 1 GGG 1 111 + * + *

    Fill Board with GRASS Checks if the rule is not valid when a row or column does not have + * the required number of tents but is filled with grass + */ + @Test + public void FailTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/FailTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -335,22 +323,17 @@ public void FailTentTest() throws InvalidFileFormatException { } } - /** - * 7x7 TreeTent puzzle Tests FinishWithGrassDirectRule - *

    Tents at (1, 3), (3, 3), and (5, 3) - * XXXXXXX x - * XXXXXXX x - * XXXXXXX x - * TGTGTGT 4 - * XXXXXXX x - * XXXXXXX x - * XXXXXXX x - * xxxxxxx - *

    Makes (0, 3), (2, 3), (4, 3), and (6, 3) GRASS - * Checks if applying the rule on row 3 is valid - */ - @Test - public void SpacedOutTentTest() throws InvalidFileFormatException { + /** + * 7x7 TreeTent puzzle Tests FinishWithGrassDirectRule + * + *

    Tents at (1, 3), (3, 3), and (5, 3) XXXXXXX x XXXXXXX x XXXXXXX x TGTGTGT 4 XXXXXXX x + * XXXXXXX x XXXXXXX x xxxxxxx + * + *

    Makes (0, 3), (2, 3), (4, 3), and (6, 3) GRASS Checks if applying the rule on row 3 is + * valid + */ + @Test + public void SpacedOutTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithGrassDirectRule/SpacedOutTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java index d82be3f87..b72b0f556 100644 --- a/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/FinishWithTentsDirectRuleTest.java @@ -27,18 +27,15 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Grass at (0, 0) - * GTT 2 - * XXX x - * XXX x - * xxx - *

    Makes (1, 0) and (2, 0) TENT - * Checks that the rule correctly fills in the first row - */ - @Test - public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Grass at (0, 0) GTT 2 XXX x XXX x xxx + * + *

    Makes (1, 0) and (2, 0) TENT Checks that the rule correctly fills in the first row + */ + @Test + public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithHorizontalTents", treetent); @@ -71,18 +68,15 @@ public void FinishWithHorizontalTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Grass at (0, 0) - * GXX x - * TXX x - * TXX x - * 2xx - *

    Makes (0, 1) and (0, 2) TENT - * Checks that the rule correctly fills in the first column - */ - @Test - public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Grass at (0, 0) GXX x TXX x TXX x 2xx + * + *

    Makes (0, 1) and (0, 2) TENT Checks that the rule correctly fills in the first column + */ + @Test + public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithVerticalTents", treetent); @@ -115,18 +109,16 @@ public void FinishWithVerticalTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Grass at (0, 0) - * GTT 2 - * TXX x - * TXX x - * 2xx - *

    Makes (1, 0), (2, 0), (0, 1) and (0, 2) TENT - * Checks that the rule correctly fills both the first row and first column - */ - @Test - public void FinishWithTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Grass at (0, 0) GTT 2 TXX x TXX x 2xx + * + *

    Makes (1, 0), (2, 0), (0, 1) and (0, 2) TENT Checks that the rule correctly fills both the + * first row and first column + */ + @Test + public void FinishWithTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -167,18 +159,16 @@ public void FinishWithTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Tent at (1, 1) - * XTX x - * TTT 3 - * XTX x - * x3x - *

    Makes (1, 0), (0, 1), (2, 1), and (1, 2) TENT - * Checks that the rule correctly fills in the middle row and column when a tent starts at (1, 1) - */ - @Test - public void AdditionalTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Tent at (1, 1) XTX x TTT 3 XTX x x3x + * + *

    Makes (1, 0), (0, 1), (2, 1), and (1, 2) TENT Checks that the rule correctly fills in the + * middle row and column when a tent starts at (1, 1) + */ + @Test + public void AdditionalTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/AdditionalTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -219,18 +209,16 @@ public void AdditionalTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Empty - * TTT 0 - * TTT 0 - * TTT 0 - * 000 - *

    Fills the board with tents - * Checks that the rule does not allow for more tents in any of the rows or columns - */ - @Test - public void FinishWithTentsFailTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Empty TTT 0 TTT 0 TTT 0 000 + * + *

    Fills the board with tents Checks that the rule does not allow for more tents in any of + * the rows or columns + */ + @Test + public void FinishWithTentsFailTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/FinishWithTentsFail", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -259,18 +247,16 @@ public void FinishWithTentsFailTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Tent at (1, 1) - * XTX x - * TTT 1 - * XTX x - * x1x - *

    Makes (1, 0), (0, 1), (2, 1) and (1, 2) Tent - * Checks that the rule does not allow for more tents in the central row or central column - */ - @Test - public void TooManyTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Tent at (1, 1) XTX x TTT 1 XTX x x1x + * + *

    Makes (1, 0), (0, 1), (2, 1) and (1, 2) Tent Checks that the rule does not allow for more + * tents in the central row or central column + */ + @Test + public void TooManyTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/TooManyTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -302,18 +288,16 @@ public void TooManyTentsTest() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule - *

    Tent at (1, 1) - * XTX x - * TTT 2 - * XTX x - * x2x - *

    Makes (1, 0), (0, 1), (2, 1) and (1, 2) Tent - * Checks that the rule is not satisfied because there are multiple configurations of tents for the central row and central column - */ - @Test - public void AmbiguousTentsTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests FinishWithTentsDirectRule + * + *

    Tent at (1, 1) XTX x TTT 2 XTX x x2x + * + *

    Makes (1, 0), (0, 1), (2, 1) and (1, 2) Tent Checks that the rule is not satisfied because + * there are multiple configurations of tents for the central row and central column + */ + @Test + public void AmbiguousTentsTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/FinishWithTentsDirectRule/AmbiguousTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java index ad4559922..fdd55029f 100644 --- a/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/LastCampingSpotDirectRuleTest.java @@ -26,17 +26,15 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule - *

    TREE at (1, 1) and (0, 1); GRASS at (1, 2) and (2, 1) - * XTX - * RRG - * XGX - *

    Makes (1, 0) TENT - * Checks that a tent must be placed above the central tree - */ - @Test - public void EmptyFieldTest_Up() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + * + *

    TREE at (1, 1) and (0, 1); GRASS at (1, 2) and (2, 1) XTX RRG XGX + * + *

    Makes (1, 0) TENT Checks that a tent must be placed above the central tree + */ + @Test + public void EmptyFieldTest_Up() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotUp", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -64,17 +62,15 @@ public void EmptyFieldTest_Up() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule - *

    TREE at (1, 1) and (0, 1); GRASS at (1, 0) and (1, 2) - * XGX - * RRG - * XTX - *

    Makes (1, 2) TENT - * Checks that a tent must be placed below the central tree - */ - @Test - public void EmptyFieldTest_Down() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + * + *

    TREE at (1, 1) and (0, 1); GRASS at (1, 0) and (1, 2) XGX RRG XTX + * + *

    Makes (1, 2) TENT Checks that a tent must be placed below the central tree + */ + @Test + public void EmptyFieldTest_Down() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotDown", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -102,17 +98,15 @@ public void EmptyFieldTest_Down() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule - *

    TREE at (1, 1) and (2, 1); GRASS at (1, 0) and (1, 2) - * XGX - * TRR - * XGX - *

    Makes (0, 1) TENT - * Checks that a tent must be placed on the left of the central tree - */ - @Test - public void EmptyFieldTest_Left() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + * + *

    TREE at (1, 1) and (2, 1); GRASS at (1, 0) and (1, 2) XGX TRR XGX + * + *

    Makes (0, 1) TENT Checks that a tent must be placed on the left of the central tree + */ + @Test + public void EmptyFieldTest_Left() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotLeft", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); @@ -140,17 +134,15 @@ public void EmptyFieldTest_Left() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule - *

    TREE at (1, 1) and (1, 2); GRASS at (0, 1) and (1, 0) - * XGX - * GRT - * XRX - *

    Makes (2, 1) TENT - * Checks that a tent must be placed to the right of the central tree - */ - @Test - public void EmptyFieldTest_Right() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests LastCampingSpotDirectRule + * + *

    TREE at (1, 1) and (1, 2); GRASS at (0, 1) and (1, 0) XGX GRT XRX + * + *

    Makes (2, 1) TENT Checks that a tent must be placed to the right of the central tree + */ + @Test + public void EmptyFieldTest_Right() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/LastCampingSpotDirectRule/LastCampingSpotRight", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); diff --git a/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java index 3ed1fd79e..ed482eba0 100644 --- a/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/LinkTentCaseRuleTest.java @@ -1,22 +1,18 @@ package puzzles.treetent.rules; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.tree.Tree; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.treetent.*; import edu.rpi.legup.puzzle.treetent.rules.LinkTentCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.LinkedList; - public class LinkTentCaseRuleTest { private static final LinkTentCaseRule RULE = new LinkTentCaseRule(); private static TreeTent treetent; @@ -28,8 +24,8 @@ public static void setUp() { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent - * with one tree surrounding it. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent with one tree surrounding + * it. * *

    checks that 1 cases is with the line connecting the central tent and the tree * @@ -76,11 +72,11 @@ public void LinkTentOneTreeTest() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent - * with four trees surrounding it. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent with four trees + * surrounding it. * - *

    checks that 4 cases are created, each of which create a line - * connecting the tent to one of the four trees without repeat. + *

    checks that 4 cases are created, each of which create a line connecting the tent to one of + * the four trees without repeat. * * @throws InvalidFileFormatException */ @@ -106,7 +102,7 @@ public void LinkTentFourTreesTest() throws InvalidFileFormatException { expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(1, 2))); for (Board testCaseBoard : cases) { - TreeTentBoard testCase = (TreeTentBoard) testCaseBoard ; + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; ArrayList lines = testCase.getLines(); // Each case should connect one line from the tent to @@ -142,17 +138,15 @@ public void LinkTentFourTreesTest() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent - * with zero trees around it. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent with zero trees around it. * - *

    Ensures no cases are created + *

    Ensures no cases are created * * @throws InvalidFileFormatException */ @Test public void LinkTentNoTreesTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/LinkTentCaseRule/NoTrees", treetent); + TestUtilities.importTestBoard("puzzles/treetent/rules/LinkTentCaseRule/NoTrees", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -166,10 +160,9 @@ public void LinkTentNoTreesTest() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent - * with trees on a diagonal. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tent with trees on a diagonal. * - *

    Ensures no cases are created + *

    Ensures no cases are created * * @throws InvalidFileFormatException */ diff --git a/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java b/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java index fffde14b1..be237e8d4 100644 --- a/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/LinkTreeCaseRuleTest.java @@ -1,6 +1,5 @@ package puzzles.treetent.rules; -import com.sun.source.doctree.LinkTree; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; @@ -10,16 +9,15 @@ import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.rules.LinkTreeCaseRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.util.ArrayList; - public class LinkTreeCaseRuleTest { - private static final LinkTreeCaseRule RULE = new LinkTreeCaseRule(); + private static final LinkTreeCaseRule RULE = new LinkTreeCaseRule(); private static TreeTent treetent; @BeforeClass @@ -29,17 +27,15 @@ public static void setUp() { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree - * with one tent above + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree with one tent above * - *

    Ensures one case is created that connects the tree to the tent. + *

    Ensures one case is created that connects the tree to the tent. * * @throws InvalidFileFormatException */ @Test public void LinkTentOneTentTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/LinkTreeCaseRule/OneTent", treetent); + TestUtilities.importTestBoard("puzzles/treetent/rules/LinkTreeCaseRule/OneTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -77,20 +73,18 @@ public void LinkTentOneTentTest() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree - * with two tents, one on the left and one on the right. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree with two tents, one on the + * left and one on the right. * - *

    Ensures two cases are created, one connecting the tree and the - * left tent, and one connecting the tree and the right tent. - * Because tents must be surrounded by grass, there can be at most - * two tents around a given tree. + *

    Ensures two cases are created, one connecting the tree and the left tent, and one + * connecting the tree and the right tent. Because tents must be surrounded by grass, there can + * be at most two tents around a given tree. * * @throws InvalidFileFormatException */ @Test public void LinkTentTwoTentsTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/LinkTreeCaseRule/TwoTents", treetent); + TestUtilities.importTestBoard("puzzles/treetent/rules/LinkTreeCaseRule/TwoTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -107,7 +101,7 @@ public void LinkTentTwoTentsTest() throws InvalidFileFormatException { expectedLines.addFirst(new TreeTentLine(board.getCell(1, 1), board.getCell(2, 1))); for (Board testCaseBoard : cases) { - TreeTentBoard testCase = (TreeTentBoard) testCaseBoard ; + TreeTentBoard testCase = (TreeTentBoard) testCaseBoard; ArrayList lines = testCase.getLines(); // Each case should connect one line from the tent to @@ -143,17 +137,15 @@ public void LinkTentTwoTentsTest() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree - * with zero tents around it. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree with zero tents around it. * - *

    Ensures no cases are created + *

    Ensures no cases are created * * @throws InvalidFileFormatException */ @Test public void LinkTentNoTreesTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/LinkTreeCaseRule/NoTents", treetent); + TestUtilities.importTestBoard("puzzles/treetent/rules/LinkTreeCaseRule/NoTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -167,17 +159,15 @@ public void LinkTentNoTreesTest() throws InvalidFileFormatException { } /** - * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree - * with tents on a diagonal. + * empty 3x3 TreeTent puzzle Tests LinkTentCaseRule on a central tree with tents on a diagonal. * - *

    Ensures no cases are created + *

    Ensures no cases are created * * @throws InvalidFileFormatException */ @Test public void LinkTentDiagTentsTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard( - "puzzles/treetent/rules/LinkTreeCaseRule/NoTents", treetent); + TestUtilities.importTestBoard("puzzles/treetent/rules/LinkTreeCaseRule/NoTents", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); diff --git a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java index 6177bb64c..dc61cb863 100644 --- a/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/SurroundTentWithGrassDirectRuleTest.java @@ -27,17 +27,16 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule - *

    TREE at (0, 0), (2, 0), (0, 1), (2, 1), (1, 2), and (2, 2); TENT at (1, 1) - * RGR - * RTR - * GRR - *

    Makes (1, 0) and (0, 2) GRASS - * Checks that the rule detects unknown adjacent and diagonal tiles correctly - */ - @Test - public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule + * + *

    TREE at (0, 0), (2, 0), (0, 1), (2, 1), (1, 2), and (2, 2); TENT at (1, 1) RGR RTR GRR + * + *

    Makes (1, 0) and (0, 2) GRASS Checks that the rule detects unknown adjacent and diagonal + * tiles correctly + */ + @Test + public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrass", treetent); @@ -69,17 +68,16 @@ public void SurroundTentWithGrassBasicRuleTest() throws InvalidFileFormatExcepti } } - /** - * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule - *

    TENT at (1, 1) - * GGG - * GTG - * GGG - *

    Makes all cells adjacent and diagonal to the tent GRASS - * Checks that the rule detects all adjacent and diagonal tiles correctly - */ - @Test - public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule + * + *

    TENT at (1, 1) GGG GTG GGG + * + *

    Makes all cells adjacent and diagonal to the tent GRASS Checks that the rule detects all + * adjacent and diagonal tiles correctly + */ + @Test + public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassBad", treetent); @@ -137,17 +135,15 @@ public void SurroundTentWithGrassBasicRuleTest_BadBoard() throws InvalidFileForm } } - /** - * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule - *

    TENT at (1, 1); TREE on all adjacent and diagonal tiles - * RRR - * RTR - * RRR - *

    Null - * Checks that the rule correctly detects no missing tiles - */ - @Test - public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests SurroundTentWithGrassDirectRule + * + *

    TENT at (1, 1); TREE on all adjacent and diagonal tiles RRR RTR RRR + * + *

    Null Checks that the rule correctly detects no missing tiles + */ + @Test + public void SurroundTentWithGrassBasicRuleTest_FullBoard() throws InvalidFileFormatException { TestUtilities.importTestBoard( "puzzles/treetent/rules/SurroundTentWithGrassDirectRule/SurroundTentWithGrassTrees", treetent); diff --git a/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java index e55704ec2..6bd2db071 100644 --- a/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TentForTreeDirectRuleTest.java @@ -5,14 +5,13 @@ import edu.rpi.legup.puzzle.treetent.*; import edu.rpi.legup.puzzle.treetent.rules.TentForTreeDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.util.ArrayList; - public class TentForTreeDirectRuleTest { private static final TentForTreeDirectRule RULE = new TentForTreeDirectRule(); @@ -24,62 +23,58 @@ public static void setUp() { treetent = new TreeTent(); } - /** - * 3x3 TreeTent puzzle Tests TentForTreeDirectRule - *

    TREE at (1, 0); TENT at (1, 1) - * XRX - * XTX - * XXX - *

    Makes a line between (1, 0) and (1, 1) - * Checks that the rule correctly detects the central tent as the only possible connection - */ - @Test - public void TentForTreeTestOneTreeOneTentTest() throws InvalidFileFormatException { - - TestUtilities.importTestBoard( - "puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent", - treetent); - - TreeNode rootNode = treetent.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - TreeTentBoard board = (TreeTentBoard) transition.getBoard(); - - TreeTentCell cell1 = board.getCell(1, 0); - TreeTentCell cell2 = board.getCell(1, 1); - TreeTentLine line = new TreeTentLine(cell1, cell2); - - board.addModifiedData(line); - board.getLines().add(line); - - Assert.assertNull(RULE.checkRule(transition)); - - ArrayList lines = board.getLines(); - for (TreeTentLine l : lines) { - if (l.compare((line))) { - Assert.assertNull(RULE.checkRuleAt(transition, l)); - } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, l)); + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + * + *

    TREE at (1, 0); TENT at (1, 1) XRX XTX XXX + * + *

    Makes a line between (1, 0) and (1, 1) Checks that the rule correctly detects the central + * tent as the only possible connection + */ + @Test + public void TentForTreeTestOneTreeOneTentTest() throws InvalidFileFormatException { + + TestUtilities.importTestBoard( + "puzzles/treetent/rules/TentForTreeDirectRule/OneTreeOneTent", treetent); + + TreeNode rootNode = treetent.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + TreeTentBoard board = (TreeTentBoard) transition.getBoard(); + + TreeTentCell cell1 = board.getCell(1, 0); + TreeTentCell cell2 = board.getCell(1, 1); + TreeTentLine line = new TreeTentLine(cell1, cell2); + + board.addModifiedData(line); + board.getLines().add(line); + + Assert.assertNull(RULE.checkRule(transition)); + + ArrayList lines = board.getLines(); + for (TreeTentLine l : lines) { + if (l.compare((line))) { + Assert.assertNull(RULE.checkRuleAt(transition, l)); + } else { + Assert.assertNotNull(RULE.checkRuleAt(transition, l)); + } } } - } - - /** - * 3x3 TreeTent puzzle Tests TentForTreeDirectRule - *

    TREE at (1, 0) and (1, 2); TENT at (1, 1) - * XRX - * XTX - * XRX - *

    Makes a line between (1, 0) and (1, 1) - * Checks that the rule works when connecting a line between the tree at (1, 0) and tent at (1, 1) - */ - @Test - public void TentForTreeArbitraryTreeTest() throws InvalidFileFormatException { + + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + * + *

    TREE at (1, 0) and (1, 2); TENT at (1, 1) XRX XTX XRX + * + *

    Makes a line between (1, 0) and (1, 1) Checks that the rule works when connecting a line + * between the tree at (1, 0) and tent at (1, 1) + */ + @Test + public void TentForTreeArbitraryTreeTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree", - treetent); + "puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -97,21 +92,19 @@ public void TentForTreeArbitraryTreeTest() throws InvalidFileFormatException { Assert.assertNull(RULE.checkRule(transition)); } - /** - * 3x3 TreeTent puzzle Tests TentForTreeDirectRule - *

    TREE at (1, 0) and (1, 2); TENT at (1, 1); LINE between (1, 0) and (1, 1) - * XRX - * XTX - * XRX - *

    Makes a line between (1, 1) and (1, 2) - * Checks that the rule fails for the tree at (1, 2) because there are no valid tents to connect to - */ - @Test - public void TentForTreeConnectedTent() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + * + *

    TREE at (1, 0) and (1, 2); TENT at (1, 1); LINE between (1, 0) and (1, 1) XRX XTX XRX + * + *

    Makes a line between (1, 1) and (1, 2) Checks that the rule fails for the tree at (1, 2) + * because there are no valid tents to connect to + */ + @Test + public void TentForTreeConnectedTent() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree", - treetent); + "puzzles/treetent/rules/TentForTreeDirectRule/ArbitraryTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -134,20 +127,18 @@ public void TentForTreeConnectedTent() throws InvalidFileFormatException { } } - /** - * 3x3 TreeTent puzzle Tests TentForTreeDirectRule - *

    TREE at (1, 1); TENT at (1, 0) and (1, 2) - * XTX - * XRX - * XTX - *

    Makes a line between (1, 1) and (1, 2) - * Checks that the rule fails for the tree at (1, 1) because there are two valid tents to connect to - */ - @Test - public void TentForTreeOneTreeTwoAdjacentTent() throws InvalidFileFormatException { + /** + * 3x3 TreeTent puzzle Tests TentForTreeDirectRule + * + *

    TREE at (1, 1); TENT at (1, 0) and (1, 2) XTX XRX XTX + * + *

    Makes a line between (1, 1) and (1, 2) Checks that the rule fails for the tree at (1, 1) + * because there are two valid tents to connect to + */ + @Test + public void TentForTreeOneTreeTwoAdjacentTent() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent", - treetent); + "puzzles/treetent/rules/TentForTreeDirectRule/OneTreeTwoAdjacentTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); diff --git a/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java b/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java index ba1b49b8c..3cccdc417 100644 --- a/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java +++ b/src/test/java/puzzles/treetent/rules/TreeForTentDirectRuleTest.java @@ -8,14 +8,13 @@ import edu.rpi.legup.puzzle.treetent.TreeTentLine; import edu.rpi.legup.puzzle.treetent.rules.TreeForTentDirectRule; import edu.rpi.legup.save.InvalidFileFormatException; +import java.util.ArrayList; import legup.MockGameBoardFacade; import legup.TestUtilities; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import java.util.ArrayList; - public class TreeForTentDirectRuleTest { private static final TreeForTentDirectRule RULE = new TreeForTentDirectRule(); @@ -29,19 +28,17 @@ public static void setUp() { /** * 3x3 TreeTent puzzle Tests TreeForTentDirectRule - *

    TENT at (1, 0); TREE at (1, 1) - * XTX - * XRX - * XXX - *

    Makes a line between (1, 0) and (1, 1) - * Checks that the rule correctly detects the central tree as the only possible connection + * + *

    TENT at (1, 0); TREE at (1, 1) XTX XRX XXX + * + *

    Makes a line between (1, 0) and (1, 1) Checks that the rule correctly detects the central + * tree as the only possible connection */ - @Test - public void TreeForTentTestOneTreeOneTentTest() throws InvalidFileFormatException { + @Test + public void TreeForTentTestOneTreeOneTentTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree", - treetent); + "puzzles/treetent/rules/TreeForTentDirectRule/OneTentOneTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -61,19 +58,17 @@ public void TreeForTentTestOneTreeOneTentTest() throws InvalidFileFormatExceptio /** * 3x3 TreeTent puzzle Tests TreeForTentDirectRule - *

    TENT at (1, 0) and (1, 2); TREE at (1, 1) - * XTX - * XRX - * XTX - *

    Makes a line between (1, 0) and (1, 1) - * Checks that the rule works when connecting a line between the tent at (1, 0) and the tree at (1, 1) + * + *

    TENT at (1, 0) and (1, 2); TREE at (1, 1) XTX XRX XTX + * + *

    Makes a line between (1, 0) and (1, 1) Checks that the rule works when connecting a line + * between the tent at (1, 0) and the tree at (1, 1) */ @Test public void TentForTreeWithArbitraryTreeTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent", - treetent); + "puzzles/treetent/rules/TreeForTentDirectRule/ArbitraryTent", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -93,19 +88,17 @@ public void TentForTreeWithArbitraryTreeTest() throws InvalidFileFormatException /** * 3x3 TreeTent puzzle Tests TreeForTentDirectRule - *

    TENT at (1, 0) and (1, 2); TREE at (1, 1); LINE between (1, 0) and (1, 1) - * XTX - * XRX - * XTX - *

    Makes a line between (1, 1) and (1, 2) - * Checks that the rule fails for the tent at (1, 2) because there are no valid trees to connect to + * + *

    TENT at (1, 0) and (1, 2); TREE at (1, 1); LINE between (1, 0) and (1, 1) XTX XRX XTX + * + *

    Makes a line between (1, 1) and (1, 2) Checks that the rule fails for the tent at (1, 2) + * because there are no valid trees to connect to */ @Test public void TentForTreeConnectedTent() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree", - treetent); + "puzzles/treetent/rules/TreeForTentDirectRule/ConnectedTree", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); @@ -130,18 +123,16 @@ public void TentForTreeConnectedTent() throws InvalidFileFormatException { /** * 3x3 TreeTent puzzle Tests TreeForTentDirectRule - *

    TENT at (1, 1); TREE at (1, 0) and (1, 2) - * XRX - * XTX - * XRX - *

    Makes a line between (1, 1) and (1, 2) - * Checks that the rule fails for the tree at (1, 1) because there are two valid trees to connect to + * + *

    TENT at (1, 1); TREE at (1, 0) and (1, 2) XRX XTX XRX + * + *

    Makes a line between (1, 1) and (1, 2) Checks that the rule fails for the tree at (1, 1) + * because there are two valid trees to connect to */ @Test public void TentForTreeOneTreeTwoAdjacentTent() throws InvalidFileFormatException { TestUtilities.importTestBoard( - "puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees", - treetent); + "puzzles/treetent/rules/TreeForTentDirectRule/OneTentTwoAdjacentTrees", treetent); TreeNode rootNode = treetent.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); diff --git a/src/test/resources/puzzles/minesweeper/utilities/3x3test b/src/test/resources/puzzles/minesweeper/utilities/3x3test new file mode 100644 index 000000000..2bf4f5c3b --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/utilities/3x3test @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 9b46ce7f5301def76e8fd87234f245aaffadbc15 Mon Sep 17 00:00:00 2001 From: Marcus Moreno <148644006+morenomarcus03@users.noreply.github.com> Date: Sun, 5 May 2024 14:28:19 -0400 Subject: [PATCH 74/74] Team Thermometers final merge (#816) * Layout of project * Added Mercury or Blocked case rule and updated ThermometerController and ThermometerFill. (#725) Added the first rule case, no test cases yet. Modified ThermometerController to reflect how the user will interact with the game. Modified ThermometerFill to reflect controller * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Thermometer (#735) * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. * Thermometer (#738) * Update ThermometerBoard.java * Created element files * Element/rule images * Discontinuous Mercury Rule + Misc. behind the scenes changes (#739) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: Fuzzabee <63023750+Fuzzabee@users.noreply.github.com> * Thermometer too many/few mercury (#747) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers Test Suite (#708) * Simplify rule names * Contradiction Test Suite * checkstyle * Update Exporter * Revert "Update Exporter" This reverts commit bae1a1f531e407e3b9dd1d28cc79330aa181f410. * Case Rule Test Suite * Update SkyscrapersExporter.java * allow null transitions * Update TreeTransition.java * Direct Rule Test Suite * added tests pass * Update DirectRule.java Commenting out print statement --------- Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Update ThermometerBoard.java * Skyscrapers puzzle editor (#720) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Implementing Puzzle Editor Removing Extraneous TreeTent Code Allowing setting tile number * Allow for editing clues on all axies * Remove Extraneous Code Removed functionality required for TreeTent but unnecessary for Skyscrapers * Remove Extraneous Code * Clue Tile in Editor Editor now requires selecting the clue tile to edit clues Added images for the tiles * Merge branch 'dev' into skyscrapersPuzzleEditor * Checkstyle Requirement * Necessary Code * Allow for test functionality --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Created element files * Element/rule images * Java Autoformatter (#728) * Create java-autoformat.yml * Setup Java * Run spotless on repository * Adding spotless dependency during build * Adding spotless dependency during build * Give permissions to run spotless * Syntax * Adding Debug Tag * Check for modified files Also removed debug tag * Automated Java code formatting changes * Test command syntax * Correctly Identify modified files * Test autoformatter * Test autoformatter * Test autoformatter * Debugging * Debugging * Change method for detecting changed files * Try building before calling spotless * Update java-autoformat.yml * Update java-autoformat.yml * Update java-autoformat.yml * Purposely bad formatting Purposely adding some bad formatting to see if the auto-formatter triggers * Update build.gradle Disabling Checkstyle in build so the auto-formatter can trigger * Update java-autoformat.yml Have auto-formatter trigger when more commits are added to a pull request * Debugging * Adding more awful formatting * Update java-autoformat.yml * Changing repo URL * Going back to checkout v1 * Trying URL change * Trying out using env * Trying this now...? * Introducing more horrible changes * Spotless formatting using google format v1.19.2 * Manual formatting fix * Automated Java code formatting changes * Different format type Trying to get it to pass checkstyle * Disable checkstyle temporarily * Automated Java code formatting changes * Default google formatting style * Automated Java code formatting changes * Comments and reordering styles * Adding extra newlines * Automated Java code formatting changes * Changing tabs from 2 to 4 spaces Supposedly the only difference that aosp makes is the 2 spaces? Hopefully it doesn't break anything else * Remove solo } requirement and reactivate checkstyle * Automated Java code formatting changes * Update checkstyle.xml Removed problematic LeftCurly and RightCurly requirements * Changing back to tabWidth * Add newline to test formatter + build * Automated Java code formatting changes * Trying some ChatGPT stuff * Getting rid of problematic experimentation --------- Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 * Java21 (#714) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Change Java version to 21 and setup JPackage to bundle Java with the app * Setup gradle action for java 21 * Fix distribution in gradle.yml * Fix java version in tests * Fix java distribution in tests * Add jpackage task * Add Linux to jpackage task * Fix jpackage task * Add java setup to jpackage tasks * Separate build into separate tasks for different operating systems * Fix mac jpackage not working and changed names of artifacts * Potential macos installer build fix * Potential macos installer build fix attempt 2 * Potential macos installer build fix attempt 3 * Add quotes around executable name for macos installer * Add logo and shortcut prompt * Update version in build.gradle * Make installer name different to app (It's a weird way to do it, it renames the file in a gradle task). * Update java-autoformat.yml Added check to make sure the pull request is not from a fork --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: charlestian23 * Update build.gradle (#741) * Update build.gradle Fixing the deprecation issues in the autoformatter * Update build.gradle * Update build.gradle * Update to use Java 21 * Automated Java code formatting changes --------- Co-authored-by: Bram van Heuveln * Updating to version 6.0.0 Changing to 6.0.0 since Java upgrade may break backwards compatibility * Too many mercury 1st draft * Too few mercury contradiction rule * error fixes --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: charlestian23 Co-authored-by: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> * Importer written First draft of the importer written and setData refactored * small bug fixes * Added outlines of Finish...WithEmptyDirectRule and RestIsEmptyDirectRule. Need to figure out best way to incorporate these rules when we have time to meet. * Uploading test xml file for eventual testing * Added Puzzle Factory and Importer Refactored a bunch of code so we now (in theory) import correctly * Thermometer Importer Functionality + Thermometer Puzzle Factory (#756) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. * Importer written First draft of the importer written and setData refactored * small bug fixes * Added Puzzle Factory and Importer Refactored a bunch of code so we now (in theory) import correctly --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: Fuzzabee <63023750+Fuzzabee@users.noreply.github.com> * Started Exported and added to ThermometerBoard access to row and col values * Adding ThermometerExporter.java and therm_test.xml (#759) * Thermometer Importer Functionality + Thermometer Puzzle Factory (#756) (#1) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. * Importer written First draft of the importer written and setData refactored * small bug fixes * Added Puzzle Factory and Importer Refactored a bunch of code so we now (in theory) import correctly --------- Co-authored-by: ZevCe <157339070+ZevCe@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Update ThermometerExporter.java * Git issues, trying to fix --------- Co-authored-by: ZevCe <157339070+ZevCe@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Delete ThermometerExporter.java * Added Thermometer to Config file * Quick Config File change (#761) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers Test Suite (#708) * Simplify rule names * Contradiction Test Suite * checkstyle * Update Exporter * Revert "Update Exporter" This reverts commit bae1a1f531e407e3b9dd1d28cc79330aa181f410. * Case Rule Test Suite * Update SkyscrapersExporter.java * allow null transitions * Update TreeTransition.java * Direct Rule Test Suite * added tests pass * Update DirectRule.java Commenting out print statement --------- Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers puzzle editor (#720) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Implementing Puzzle Editor Removing Extraneous TreeTent Code Allowing setting tile number * Allow for editing clues on all axies * Remove Extraneous Code Removed functionality required for TreeTent but unnecessary for Skyscrapers * Remove Extraneous Code * Clue Tile in Editor Editor now requires selecting the clue tile to edit clues Added images for the tiles * Merge branch 'dev' into skyscrapersPuzzleEditor * Checkstyle Requirement * Necessary Code * Allow for test functionality --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. * Java Autoformatter (#728) * Create java-autoformat.yml * Setup Java * Run spotless on repository * Adding spotless dependency during build * Adding spotless dependency during build * Give permissions to run spotless * Syntax * Adding Debug Tag * Check for modified files Also removed debug tag * Automated Java code formatting changes * Test command syntax * Correctly Identify modified files * Test autoformatter * Test autoformatter * Test autoformatter * Debugging * Debugging * Change method for detecting changed files * Try building before calling spotless * Update java-autoformat.yml * Update java-autoformat.yml * Update java-autoformat.yml * Purposely bad formatting Purposely adding some bad formatting to see if the auto-formatter triggers * Update build.gradle Disabling Checkstyle in build so the auto-formatter can trigger * Update java-autoformat.yml Have auto-formatter trigger when more commits are added to a pull request * Debugging * Adding more awful formatting * Update java-autoformat.yml * Changing repo URL * Going back to checkout v1 * Trying URL change * Trying out using env * Trying this now...? * Introducing more horrible changes * Spotless formatting using google format v1.19.2 * Manual formatting fix * Automated Java code formatting changes * Different format type Trying to get it to pass checkstyle * Disable checkstyle temporarily * Automated Java code formatting changes * Default google formatting style * Automated Java code formatting changes * Comments and reordering styles * Adding extra newlines * Automated Java code formatting changes * Changing tabs from 2 to 4 spaces Supposedly the only difference that aosp makes is the 2 spaces? Hopefully it doesn't break anything else * Remove solo } requirement and reactivate checkstyle * Automated Java code formatting changes * Update checkstyle.xml Removed problematic LeftCurly and RightCurly requirements * Changing back to tabWidth * Add newline to test formatter + build * Automated Java code formatting changes * Trying some ChatGPT stuff * Getting rid of problematic experimentation --------- Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 * Java21 (#714) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Change Java version to 21 and setup JPackage to bundle Java with the app * Setup gradle action for java 21 * Fix distribution in gradle.yml * Fix java version in tests * Fix java distribution in tests * Add jpackage task * Add Linux to jpackage task * Fix jpackage task * Add java setup to jpackage tasks * Separate build into separate tasks for different operating systems * Fix mac jpackage not working and changed names of artifacts * Potential macos installer build fix * Potential macos installer build fix attempt 2 * Potential macos installer build fix attempt 3 * Add quotes around executable name for macos installer * Add logo and shortcut prompt * Update version in build.gradle * Make installer name different to app (It's a weird way to do it, it renames the file in a gradle task). * Update java-autoformat.yml Added check to make sure the pull request is not from a fork --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: charlestian23 * Update build.gradle (#741) * Update build.gradle Fixing the deprecation issues in the autoformatter * Update build.gradle * Update build.gradle * Update to use Java 21 * Automated Java code formatting changes --------- Co-authored-by: Bram van Heuveln * Updating to version 6.0.0 Changing to 6.0.0 since Java upgrade may break backwards compatibility * Importer written First draft of the importer written and setData refactored * small bug fixes * Added Puzzle Factory and Importer Refactored a bunch of code so we now (in theory) import correctly * Delete ThermometerExporter.java * Added Thermometer to Config file --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: charlestian23 Co-authored-by: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> Co-authored-by: Fuzzabee <63023750+Fuzzabee@users.noreply.github.com> * Added necessary code to constructor * Messing with Rule Recognition * Retrofit because I forgot to merge for two weeks (#764) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers Test Suite (#708) * Simplify rule names * Contradiction Test Suite * checkstyle * Update Exporter * Revert "Update Exporter" This reverts commit bae1a1f531e407e3b9dd1d28cc79330aa181f410. * Case Rule Test Suite * Update SkyscrapersExporter.java * allow null transitions * Update TreeTransition.java * Direct Rule Test Suite * added tests pass * Update DirectRule.java Commenting out print statement --------- Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Update ThermometerBoard.java * Skyscrapers puzzle editor (#720) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Implementing Puzzle Editor Removing Extraneous TreeTent Code Allowing setting tile number * Allow for editing clues on all axies * Remove Extraneous Code Removed functionality required for TreeTent but unnecessary for Skyscrapers * Remove Extraneous Code * Clue Tile in Editor Editor now requires selecting the clue tile to edit clues Added images for the tiles * Merge branch 'dev' into skyscrapersPuzzleEditor * Checkstyle Requirement * Necessary Code * Allow for test functionality --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Created element files * Element/rule images * Java Autoformatter (#728) * Create java-autoformat.yml * Setup Java * Run spotless on repository * Adding spotless dependency during build * Adding spotless dependency during build * Give permissions to run spotless * Syntax * Adding Debug Tag * Check for modified files Also removed debug tag * Automated Java code formatting changes * Test command syntax * Correctly Identify modified files * Test autoformatter * Test autoformatter * Test autoformatter * Debugging * Debugging * Change method for detecting changed files * Try building before calling spotless * Update java-autoformat.yml * Update java-autoformat.yml * Update java-autoformat.yml * Purposely bad formatting Purposely adding some bad formatting to see if the auto-formatter triggers * Update build.gradle Disabling Checkstyle in build so the auto-formatter can trigger * Update java-autoformat.yml Have auto-formatter trigger when more commits are added to a pull request * Debugging * Adding more awful formatting * Update java-autoformat.yml * Changing repo URL * Going back to checkout v1 * Trying URL change * Trying out using env * Trying this now...? * Introducing more horrible changes * Spotless formatting using google format v1.19.2 * Manual formatting fix * Automated Java code formatting changes * Different format type Trying to get it to pass checkstyle * Disable checkstyle temporarily * Automated Java code formatting changes * Default google formatting style * Automated Java code formatting changes * Comments and reordering styles * Adding extra newlines * Automated Java code formatting changes * Changing tabs from 2 to 4 spaces Supposedly the only difference that aosp makes is the 2 spaces? Hopefully it doesn't break anything else * Remove solo } requirement and reactivate checkstyle * Automated Java code formatting changes * Update checkstyle.xml Removed problematic LeftCurly and RightCurly requirements * Changing back to tabWidth * Add newline to test formatter + build * Automated Java code formatting changes * Trying some ChatGPT stuff * Getting rid of problematic experimentation --------- Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 * Java21 (#714) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Change Java version to 21 and setup JPackage to bundle Java with the app * Setup gradle action for java 21 * Fix distribution in gradle.yml * Fix java version in tests * Fix java distribution in tests * Add jpackage task * Add Linux to jpackage task * Fix jpackage task * Add java setup to jpackage tasks * Separate build into separate tasks for different operating systems * Fix mac jpackage not working and changed names of artifacts * Potential macos installer build fix * Potential macos installer build fix attempt 2 * Potential macos installer build fix attempt 3 * Add quotes around executable name for macos installer * Add logo and shortcut prompt * Update version in build.gradle * Make installer name different to app (It's a weird way to do it, it renames the file in a gradle task). * Update java-autoformat.yml Added check to make sure the pull request is not from a fork --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: charlestian23 * Update build.gradle (#741) * Update build.gradle Fixing the deprecation issues in the autoformatter * Update build.gradle * Update build.gradle * Update to use Java 21 * Automated Java code formatting changes --------- Co-authored-by: Bram van Heuveln * Updating to version 6.0.0 Changing to 6.0.0 since Java upgrade may break backwards compatibility * Too many mercury 1st draft * Too few mercury contradiction rule * error fixes * misc cleanup * Starting on case rules * Rest is Empty direct rule and getHead for vials * Prior is filled rule and getTail functionality * Prior filled small fix * Retrofit because I forgot to merge for 2 weeks * Rest empty quick fix --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: charlestian23 Co-authored-by: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> * Fixing desyncs in repo * More syncing and organizing files * Thermometer (#766) * Small misc improvements Further work on getting a file to open without errors * Fixed verify vial method * Various thermometer fixes (#767) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers Test Suite (#708) * Simplify rule names * Contradiction Test Suite * checkstyle * Update Exporter * Revert "Update Exporter" This reverts commit bae1a1f531e407e3b9dd1d28cc79330aa181f410. * Case Rule Test Suite * Update SkyscrapersExporter.java * allow null transitions * Update TreeTransition.java * Direct Rule Test Suite * added tests pass * Update DirectRule.java Commenting out print statement --------- Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers puzzle editor (#720) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Implementing Puzzle Editor Removing Extraneous TreeTent Code Allowing setting tile number * Allow for editing clues on all axies * Remove Extraneous Code Removed functionality required for TreeTent but unnecessary for Skyscrapers * Remove Extraneous Code * Clue Tile in Editor Editor now requires selecting the clue tile to edit clues Added images for the tiles * Merge branch 'dev' into skyscrapersPuzzleEditor * Checkstyle Requirement * Necessary Code * Allow for test functionality --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. * Java Autoformatter (#728) * Create java-autoformat.yml * Setup Java * Run spotless on repository * Adding spotless dependency during build * Adding spotless dependency during build * Give permissions to run spotless * Syntax * Adding Debug Tag * Check for modified files Also removed debug tag * Automated Java code formatting changes * Test command syntax * Correctly Identify modified files * Test autoformatter * Test autoformatter * Test autoformatter * Debugging * Debugging * Change method for detecting changed files * Try building before calling spotless * Update java-autoformat.yml * Update java-autoformat.yml * Update java-autoformat.yml * Purposely bad formatting Purposely adding some bad formatting to see if the auto-formatter triggers * Update build.gradle Disabling Checkstyle in build so the auto-formatter can trigger * Update java-autoformat.yml Have auto-formatter trigger when more commits are added to a pull request * Debugging * Adding more awful formatting * Update java-autoformat.yml * Changing repo URL * Going back to checkout v1 * Trying URL change * Trying out using env * Trying this now...? * Introducing more horrible changes * Spotless formatting using google format v1.19.2 * Manual formatting fix * Automated Java code formatting changes * Different format type Trying to get it to pass checkstyle * Disable checkstyle temporarily * Automated Java code formatting changes * Default google formatting style * Automated Java code formatting changes * Comments and reordering styles * Adding extra newlines * Automated Java code formatting changes * Changing tabs from 2 to 4 spaces Supposedly the only difference that aosp makes is the 2 spaces? Hopefully it doesn't break anything else * Remove solo } requirement and reactivate checkstyle * Automated Java code formatting changes * Update checkstyle.xml Removed problematic LeftCurly and RightCurly requirements * Changing back to tabWidth * Add newline to test formatter + build * Automated Java code formatting changes * Trying some ChatGPT stuff * Getting rid of problematic experimentation --------- Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 * Java21 (#714) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Change Java version to 21 and setup JPackage to bundle Java with the app * Setup gradle action for java 21 * Fix distribution in gradle.yml * Fix java version in tests * Fix java distribution in tests * Add jpackage task * Add Linux to jpackage task * Fix jpackage task * Add java setup to jpackage tasks * Separate build into separate tasks for different operating systems * Fix mac jpackage not working and changed names of artifacts * Potential macos installer build fix * Potential macos installer build fix attempt 2 * Potential macos installer build fix attempt 3 * Add quotes around executable name for macos installer * Add logo and shortcut prompt * Update version in build.gradle * Make installer name different to app (It's a weird way to do it, it renames the file in a gradle task). * Update java-autoformat.yml Added check to make sure the pull request is not from a fork --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: charlestian23 * Update build.gradle (#741) * Update build.gradle Fixing the deprecation issues in the autoformatter * Update build.gradle * Update build.gradle * Update to use Java 21 * Automated Java code formatting changes --------- Co-authored-by: Bram van Heuveln * Updating to version 6.0.0 Changing to 6.0.0 since Java upgrade may break backwards compatibility * Importer written First draft of the importer written and setData refactored * small bug fixes * Added Puzzle Factory and Importer Refactored a bunch of code so we now (in theory) import correctly * Delete ThermometerExporter.java * Added Thermometer to Config file * Added necessary code to constructor * Messing with Rule Recognition * Fixing desyncs in repo * More syncing and organizing files * Small misc improvements Further work on getting a file to open without errors * Fixed verify vial method --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: charlestian23 Co-authored-by: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> Co-authored-by: Fuzzabee <63023750+Fuzzabee@users.noreply.github.com> * More work towards visual display we now have semi working tials! * Update ThermometerElementView.java * Unfinished thermometer updates (#772) * Thermometer (#773) Adding images because real rotation is scary * Rotating images!!!!! * Thermometer with rotating images!!!! (#782) * Added in placeable elements * Stabilizing bug fixes * Import silliness * Bug fixes for the Thermometer Bug Fixes (#794) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers Test Suite (#708) * Simplify rule names * Contradiction Test Suite * checkstyle * Update Exporter * Revert "Update Exporter" This reverts commit bae1a1f531e407e3b9dd1d28cc79330aa181f410. * Case Rule Test Suite * Update SkyscrapersExporter.java * allow null transitions * Update TreeTransition.java * Direct Rule Test Suite * added tests pass * Update DirectRule.java Commenting out print statement --------- Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Skyscrapers puzzle editor (#720) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Implementing Puzzle Editor Removing Extraneous TreeTent Code Allowing setting tile number * Allow for editing clues on all axies * Remove Extraneous Code Removed functionality required for TreeTent but unnecessary for Skyscrapers * Remove Extraneous Code * Clue Tile in Editor Editor now requires selecting the clue tile to edit clues Added images for the tiles * Merge branch 'dev' into skyscrapersPuzzleEditor * Checkstyle Requirement * Necessary Code * Allow for test functionality --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Starting work on thermometer class * Added Vial Class Added the vial class which will be helpful for checking rules down the line * Discontinuous Mercury Rule added Added the Discontinuous Mercury contradiciton rule. Added variables to the ThermometerBoard class to keep track of the numbers on the perimiter of the board as well as accessors/setters for these variables. Rewrote a section of the Vials code to make sure cells are added correctly as well as added some accessor functions. * Java Autoformatter (#728) * Create java-autoformat.yml * Setup Java * Run spotless on repository * Adding spotless dependency during build * Adding spotless dependency during build * Give permissions to run spotless * Syntax * Adding Debug Tag * Check for modified files Also removed debug tag * Automated Java code formatting changes * Test command syntax * Correctly Identify modified files * Test autoformatter * Test autoformatter * Test autoformatter * Debugging * Debugging * Change method for detecting changed files * Try building before calling spotless * Update java-autoformat.yml * Update java-autoformat.yml * Update java-autoformat.yml * Purposely bad formatting Purposely adding some bad formatting to see if the auto-formatter triggers * Update build.gradle Disabling Checkstyle in build so the auto-formatter can trigger * Update java-autoformat.yml Have auto-formatter trigger when more commits are added to a pull request * Debugging * Adding more awful formatting * Update java-autoformat.yml * Changing repo URL * Going back to checkout v1 * Trying URL change * Trying out using env * Trying this now...? * Introducing more horrible changes * Spotless formatting using google format v1.19.2 * Manual formatting fix * Automated Java code formatting changes * Different format type Trying to get it to pass checkstyle * Disable checkstyle temporarily * Automated Java code formatting changes * Default google formatting style * Automated Java code formatting changes * Comments and reordering styles * Adding extra newlines * Automated Java code formatting changes * Changing tabs from 2 to 4 spaces Supposedly the only difference that aosp makes is the 2 spaces? Hopefully it doesn't break anything else * Remove solo } requirement and reactivate checkstyle * Automated Java code formatting changes * Update checkstyle.xml Removed problematic LeftCurly and RightCurly requirements * Changing back to tabWidth * Add newline to test formatter + build * Automated Java code formatting changes * Trying some ChatGPT stuff * Getting rid of problematic experimentation --------- Co-authored-by: Bram van Heuveln Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: charlestian23 * Java21 (#714) * Fixed Short Truth Table case rule bug (#707) * Revert "Bugfix 549 (#682)" This reverts commit 5048ee69d958fdde9b1bb35854c56c9920068346. * Case rule test fix (#705) Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Rapid fix for STT case rules Case rules broke at some point from legacy code or merge conflict. Provided is a quick fix in CaseRule and CaseRule_Generic * Revert "Revert "Bugfix 549 (#682)"" (#706) This reverts commit e9fe310378721aa4b4fa358aa57ec44f21d086c1. --------- Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> * Change Java version to 21 and setup JPackage to bundle Java with the app * Setup gradle action for java 21 * Fix distribution in gradle.yml * Fix java version in tests * Fix java distribution in tests * Add jpackage task * Add Linux to jpackage task * Fix jpackage task * Add java setup to jpackage tasks * Separate build into separate tasks for different operating systems * Fix mac jpackage not working and changed names of artifacts * Potential macos installer build fix * Potential macos installer build fix attempt 2 * Potential macos installer build fix attempt 3 * Add quotes around executable name for macos installer * Add logo and shortcut prompt * Update version in build.gradle * Make installer name different to app (It's a weird way to do it, it renames the file in a gradle task). * Update java-autoformat.yml Added check to make sure the pull request is not from a fork --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: charlestian23 * Update build.gradle (#741) * Update build.gradle Fixing the deprecation issues in the autoformatter * Update build.gradle * Update build.gradle * Update to use Java 21 * Automated Java code formatting changes --------- Co-authored-by: Bram van Heuveln * Updating to version 6.0.0 Changing to 6.0.0 since Java upgrade may break backwards compatibility * Importer written First draft of the importer written and setData refactored * small bug fixes * Added Puzzle Factory and Importer Refactored a bunch of code so we now (in theory) import correctly * Delete ThermometerExporter.java * Added Thermometer to Config file * Added necessary code to constructor * Messing with Rule Recognition * Fixing desyncs in repo * More syncing and organizing files * Small misc improvements Further work on getting a file to open without errors * Fixed verify vial method * More work towards visual display we now have semi working tials! * Update ThermometerElementView.java * Rotating images!!!!! * Added in placeable elements * Stabilizing bug fixes * Import silliness --------- Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: charlestian23 Co-authored-by: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> Co-authored-by: Fuzzabee <63023750+Fuzzabee@users.noreply.github.com> * Final Images + Rule file expansion pack (#795) * Thermometer Cleanup and debugging * Major Bug Fix (#798) * Working row/col numbers (#799) * Early Work for displaying nums * Work towards displaying numbers * Working row/col Numbers * Thermometer (#801) * Found bug in name of ThermometerTooLarge.png which was throwing errors and not showing up in LEGUP. * Added documentation comments to rules * Updated controller to have correct mouse buttons as well as added debug info with middle mouse. Cleaned up a few methods in TheremomterCell as well. * Fixed ThermomterExporter to allow saving to write correctly to file. * Thermometer (#807) * Found bug in name of ThermometerTooLarge.png which was throwing errors and not showing up in LEGUP. * Added documentation comments to rules * Updated controller to have correct mouse buttons as well as added debug info with middle mouse. Cleaned up a few methods in TheremomterCell as well. * Fixed ThermomterExporter to allow saving to write correctly to file. * Updated therm_text.xml as row/col was reversed. Also updated some comments on Thermometer and ThermometerExporter * Last minute comments (#810) * Final Marcus pull (#814) * Update ThermometerBoard.java * Created element files * Element/rule images * Too many mercury 1st draft * Too few mercury contradiction rule * error fixes * misc cleanup * Starting on case rules * Rest is Empty direct rule and getHead for vials * Prior is filled rule and getTail functionality * Prior filled small fix * Retrofit because I forgot to merge for 2 weeks * Rest empty quick fix * Images with thicker lines/orientations * New more visible rule images * small addition * Rule Stubs * Final rule images * Major Bug Fix Zevs actually so dumb lol * Right Click works now * Reference Sheet Fill-out * Update therm_test.xml * final final rule image * small comments * comments * Automated Java code formatting changes * Automated Java code formatting changes * Update config --------- Co-authored-by: ZevCe Co-authored-by: Fuzzabee <63023750+Fuzzabee@users.noreply.github.com> Co-authored-by: ZevCe <157339070+ZevCe@users.noreply.github.com> Co-authored-by: Charles Tian <46334090+charlestian23@users.noreply.github.com> Co-authored-by: Chase-Grajeda Co-authored-by: Chase Grajeda <76405306+Chase-Grajeda@users.noreply.github.com> Co-authored-by: ThisMatt Co-authored-by: ThisMatt <98851950+ThisMatt@users.noreply.github.com> Co-authored-by: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Co-authored-by: Bram van Heuveln Co-authored-by: charlestian23 Co-authored-by: Fisher Luba <145061313+FisherLuba@users.noreply.github.com> Co-authored-by: James Baker --- puzzles files/thermometer/therm_test.xml | 27 ++ .../legup/puzzle/thermometer/Thermometer.java | 56 ++++ .../puzzle/thermometer/ThermometerBoard.java | 139 ++++++++ .../puzzle/thermometer/ThermometerCell.java | 67 ++++ .../thermometer/ThermometerController.java | 44 +++ .../thermometer/ThermometerElementView.java | 311 ++++++++++++++++++ .../thermometer/ThermometerExporter.java | 72 ++++ .../puzzle/thermometer/ThermometerFill.java | 8 + .../thermometer/ThermometerImporter.java | 196 +++++++++++ .../thermometer/ThermometerNumberView.java | 37 +++ .../puzzle/thermometer/ThermometerType.java | 8 + .../puzzle/thermometer/ThermometerVial.java | 103 ++++++ .../puzzle/thermometer/ThermometerView.java | 48 +++ .../thermometer/elements/HeadTileBlckE.java | 13 + .../thermometer/elements/HeadTileBlckN.java | 13 + .../thermometer/elements/HeadTileBlckS.java | 13 + .../thermometer/elements/HeadTileBlckW.java | 13 + .../thermometer/elements/HeadTileEmpE.java | 13 + .../thermometer/elements/HeadTileEmpN.java | 13 + .../thermometer/elements/HeadTileEmpS.java | 13 + .../thermometer/elements/HeadTileEmpW.java | 13 + .../thermometer/elements/HeadTileFillE.java | 13 + .../thermometer/elements/HeadTileFillN.java | 13 + .../thermometer/elements/HeadTileFillS.java | 13 + .../thermometer/elements/HeadTileFillW.java | 13 + .../thermometer/elements/ShaftTileBlckE.java | 13 + .../thermometer/elements/ShaftTileBlckN.java | 13 + .../thermometer/elements/ShaftTileBlckS.java | 13 + .../thermometer/elements/ShaftTileBlckW.java | 13 + .../thermometer/elements/ShaftTileEmpE.java | 13 + .../thermometer/elements/ShaftTileEmpN.java | 13 + .../thermometer/elements/ShaftTileEmpS.java | 13 + .../thermometer/elements/ShaftTileEmpW.java | 13 + .../thermometer/elements/ShaftTileFillE.java | 13 + .../thermometer/elements/ShaftTileFillN.java | 13 + .../thermometer/elements/ShaftTileFillS.java | 13 + .../thermometer/elements/ShaftTileFillW.java | 13 + .../thermometer/elements/TipTileBlckE.java | 13 + .../thermometer/elements/TipTileBlckN.java | 13 + .../thermometer/elements/TipTileBlckS.java | 13 + .../thermometer/elements/TipTileBlckW.java | 13 + .../thermometer/elements/TipTileEmpE.java | 13 + .../thermometer/elements/TipTileEmpN.java | 13 + .../thermometer/elements/TipTileEmpS.java | 13 + .../thermometer/elements/TipTileEmpW.java | 13 + .../thermometer/elements/TipTileFillE.java | 13 + .../thermometer/elements/TipTileFillN.java | 13 + .../thermometer/elements/TipTileFillS.java | 13 + .../thermometer/elements/TipTileFillW.java | 13 + ...DiscontinuousMercuryContradictionRule.java | 62 ++++ .../rules/FinishWithBlockedDirectRule.java | 44 +++ .../rules/FinishWithMercuryDirectRule.java | 44 +++ .../rules/MercuryOrBlockedCaseRule.java | 114 +++++++ .../rules/MinimumFillDirectRule.java | 44 +++ .../rules/PriorFilledDirectRule.java | 101 ++++++ .../rules/RestEmptyDirectRule.java | 101 ++++++ .../rules/SatisfyMercuryCaseRule.java | 62 ++++ .../rules/ThermometerTooLargeDirectRule.java | 44 +++ .../rules/TooFewMercuryContradiction.java | 57 ++++ .../rules/TooManyMercuryContradiction.java | 56 ++++ .../rules/thermometer_reference_sheet.txt | 16 + .../images/thermometer/Element Template.png | Bin 0 -> 44790 bytes .../thermometer/Elements/HeadBlockE.png | Bin 0 -> 8613 bytes .../thermometer/Elements/HeadBlockN.png | Bin 0 -> 8499 bytes .../thermometer/Elements/HeadBlockS.png | Bin 0 -> 8519 bytes .../thermometer/Elements/HeadBlockW.png | Bin 0 -> 8542 bytes .../images/thermometer/Elements/HeadEmpE.png | Bin 0 -> 5758 bytes .../images/thermometer/Elements/HeadEmpN.png | Bin 0 -> 5734 bytes .../images/thermometer/Elements/HeadEmpS.png | Bin 0 -> 5747 bytes .../images/thermometer/Elements/HeadEmpW.png | Bin 0 -> 5790 bytes .../images/thermometer/Elements/HeadFillE.png | Bin 0 -> 5992 bytes .../images/thermometer/Elements/HeadFillN.png | Bin 0 -> 5982 bytes .../images/thermometer/Elements/HeadFillS.png | Bin 0 -> 6003 bytes .../images/thermometer/Elements/HeadFillW.png | Bin 0 -> 6077 bytes .../thermometer/Elements/ShaftBlockE.png | Bin 0 -> 6204 bytes .../thermometer/Elements/ShaftBlockN.png | Bin 0 -> 6510 bytes .../thermometer/Elements/ShaftBlockS.png | Bin 0 -> 6539 bytes .../thermometer/Elements/ShaftBlockW.png | Bin 0 -> 6170 bytes .../images/thermometer/Elements/ShaftEmpE.png | Bin 0 -> 1727 bytes .../images/thermometer/Elements/ShaftEmpN.png | Bin 0 -> 1920 bytes .../images/thermometer/Elements/ShaftEmpS.png | Bin 0 -> 1987 bytes .../images/thermometer/Elements/ShaftEmpW.png | Bin 0 -> 1690 bytes .../thermometer/Elements/ShaftFillE.png | Bin 0 -> 1741 bytes .../thermometer/Elements/ShaftFillN.png | Bin 0 -> 1997 bytes .../thermometer/Elements/ShaftFillS.png | Bin 0 -> 2080 bytes .../thermometer/Elements/ShaftFillW.png | Bin 0 -> 1798 bytes .../images/thermometer/Elements/TipBlockE.png | Bin 0 -> 6724 bytes .../images/thermometer/Elements/TipBlockN.png | Bin 0 -> 6949 bytes .../images/thermometer/Elements/TipBlockS.png | Bin 0 -> 6833 bytes .../images/thermometer/Elements/TipBlockW.png | Bin 0 -> 6767 bytes .../images/thermometer/Elements/TipEmpE.png | Bin 0 -> 3233 bytes .../images/thermometer/Elements/TipEmpN.png | Bin 0 -> 3477 bytes .../images/thermometer/Elements/TipEmpS.png | Bin 0 -> 3371 bytes .../images/thermometer/Elements/TipEmpW.png | Bin 0 -> 3219 bytes .../images/thermometer/Elements/TipFillE.png | Bin 0 -> 3740 bytes .../images/thermometer/Elements/TipFillN.png | Bin 0 -> 3978 bytes .../images/thermometer/Elements/TipFillS.png | Bin 0 -> 3903 bytes .../images/thermometer/Elements/TipFillW.png | Bin 0 -> 3733 bytes .../images/thermometer/FinishWithBlocked.png | Bin 0 -> 33412 bytes .../images/thermometer/FinishWithMercury.png | Bin 0 -> 37221 bytes .../images/thermometer/MercOrBlocked.png | Bin 0 -> 27343 bytes .../images/thermometer/MercuryInBody.png | Bin 0 -> 34628 bytes .../legup/images/thermometer/MinimumFill.png | Bin 0 -> 38996 bytes .../images/thermometer/NotEnoughMercury.png | Bin 0 -> 49179 bytes .../images/thermometer/PriorIsFilled.png | Bin 0 -> 26866 bytes .../legup/images/thermometer/RestIsEmpty.png | Bin 0 -> 35958 bytes .../images/thermometer/SatisfyMercury.png | Bin 0 -> 31667 bytes .../thermometer/ThermometerTooLarge.png | Bin 0 -> 33719 bytes .../images/thermometer/TooManyMercury.png | Bin 0 -> 44433 bytes src/main/resources/edu/rpi/legup/legup/config | 20 +- 110 files changed, 2341 insertions(+), 8 deletions(-) create mode 100644 puzzles files/thermometer/therm_test.xml create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithMercuryDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MercuryOrBlockedCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/PriorFilledDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/RestEmptyDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/SatisfyMercuryCaseRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooFewMercuryContradiction.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Element Template.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillE.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillN.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillS.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillW.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/FinishWithBlocked.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/FinishWithMercury.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/MercOrBlocked.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/MercuryInBody.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/MinimumFill.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/NotEnoughMercury.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/PriorIsFilled.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/RestIsEmpty.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/SatisfyMercury.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/ThermometerTooLarge.png create mode 100644 src/main/resources/edu/rpi/legup/images/thermometer/TooManyMercury.png diff --git a/puzzles files/thermometer/therm_test.xml b/puzzles files/thermometer/therm_test.xml new file mode 100644 index 000000000..66e841dc5 --- /dev/null +++ b/puzzles files/thermometer/therm_test.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java new file mode 100644 index 000000000..8138104f5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java @@ -0,0 +1,56 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +// basically just copy-pasted from dev guide on wiki +public class Thermometer extends Puzzle { + public Thermometer() { + super(); + + this.name = "Thermometer"; + + this.importer = new ThermometerImporter(this); + this.exporter = new ThermometerExporter(this); + // we do not have a thermometerCellFactory class as + // thermometerVial has its own thermometerCell factory method + } + + /** Initializes the game board. Called by the invoker of the class */ + @Override + public void initializeView() { + boardView = new ThermometerView((ThermometerBoard) currentBoard); + boardView.setBoard(currentBoard); + addBoardListener(boardView); + } + + /** + * 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; + } + + /** + * 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) { + return true; + } + + /** + * Callback for when the board puzzleElement changes + * + * @param board the board that has changed + */ + @Override + public void onBoardChange(Board board) {} +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java new file mode 100644 index 000000000..95ff7ff83 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java @@ -0,0 +1,139 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.GridBoard; +import java.awt.*; +import java.util.ArrayList; + +public class ThermometerBoard extends GridBoard { + + // an array containing all of our vials on the board + private ArrayList thermometerVials; + + // representations of the number requirements along rows and columns of the board + // we use rotation to store the number + private ArrayList colNumbers; + private ArrayList rowNumbers; + + private ThermometerCell dummyCell; + + // constructors for the boards and variables + public ThermometerBoard(int width, int height) { + super(width, height); + + // initializing the row/col number arrays with zeros, so they can be + // easily updated using the setRow/ColNumber functions + colNumbers = new ArrayList<>(); + for (int i = 0; i < width - 1; i++) { + ThermometerCell cell = + new ThermometerCell( + new Point(i, height - 1), + ThermometerType.UNKNOWN, + ThermometerFill.UNKNOWN, + 0); + cell.setIndex((height - 1) * height + i); + colNumbers.add(cell); + this.setCell(i, height - 1, cell); + } + rowNumbers = new ArrayList<>(); + for (int i = 0; i < height - 1; i++) { + ThermometerCell cell = + new ThermometerCell( + new Point(width - 1, i), + ThermometerType.UNKNOWN, + ThermometerFill.UNKNOWN, + 0); + cell.setIndex(i * height + (width - 1)); + rowNumbers.add(cell); + this.setCell(width - 1, i, cell); + } + + // setting a dummy cell so board doesn't have null cells + dummyCell = + new ThermometerCell( + new Point(width - 1, height - 1), + ThermometerType.UNKNOWN, + ThermometerFill.UNKNOWN, + -1); + dummyCell.setIndex((height - 1) * height + width); + this.setCell(width - 1, height - 1, dummyCell); + + // creating our empty vial of thermometers to add to + thermometerVials = new ArrayList<>(); + } + + // setters and accessors for our array of vials + public void addVial(ThermometerVial v) { + thermometerVials.add(v); + } + + public ArrayList getVials() { + return thermometerVials; + } + + // our setters for row/col numbers with simple input verification + public boolean setRowNumber(int row, int num) { + // first check is to verify we are updating an element in range + // second check is to verify the new number can be achieved by the puzzle + if (row < rowNumbers.size() && num <= colNumbers.size()) { + rowNumbers.get(row).setRotation(num); + return true; + } + return false; + } + + public boolean setColNumber(int col, int num) { + // first check is to verify we are updating an element in range + // second check is to verify the new number can be achieved by the puzzle + if (col < colNumbers.size() && num <= rowNumbers.size()) { + colNumbers.get(col).setRotation(num); + return true; + } + return false; + } + + // basic accessors for row/col numbers + public int getRowNumber(int row) { + if (row < 0 || row >= rowNumbers.size()) return -1; + return rowNumbers.get(row).getRotation(); + } + + public int getColNumber(int col) { + if (col < 0 || col >= rowNumbers.size()) return -1; + return colNumbers.get(col).getRotation(); + } + + // Accessors for saving row/column + public ArrayList getRowNumbers() { + return rowNumbers; + } + + public ArrayList getColNumbers() { + return colNumbers; + } + + // we all suck at programming so instead of using provided array list + // we use our own array lists to keep track of the vials + // marginally useful because it means we are guaranteed to get a + // thermometer cell when calling get cell, but using some type casting + // this override function could very likely be refactored out + @Override + public ThermometerCell getCell(int x, int y) { + for (ThermometerVial vial : this.thermometerVials) { + for (ThermometerCell cell : vial.getCells()) { + if (cell.getLocation().x == x && cell.getLocation().y == y) return cell; + } + } + + for (ThermometerCell cell : rowNumbers) { + if (cell.getLocation().x == x && cell.getLocation().y == y) return cell; + } + + for (ThermometerCell cell : colNumbers) { + if (cell.getLocation().x == x && cell.getLocation().y == y) return cell; + } + + if (x == this.getWidth() - 1 && y == this.getHeight() - 1) return dummyCell; + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java new file mode 100644 index 000000000..175a455b4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java @@ -0,0 +1,67 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.GridCell; +import java.awt.Point; + +public class ThermometerCell extends GridCell { + + // information about the cell needed to display it + private ThermometerType type; + private ThermometerFill fill; + private int rotation; + + public ThermometerCell(Point location, ThermometerType t, ThermometerFill f, int r) { + // since we do not use get/set data value int can be any value + super(1, location); + type = t; + fill = f; + rotation = r; + } + + // Note: setdata does not work for our purposes + public void setType(ThermometerType t) { + type = t; + } + + public ThermometerType getType() { + return type; + } + + public void setFill(ThermometerFill f) { + fill = f; + } + + public ThermometerFill getFill() { + return fill; + } + + public void setRotation(int r) { + rotation = r; + } + + public int getRotation() { + return rotation; + } + + @Override + public ThermometerCell copy() { + ThermometerCell copy = + new ThermometerCell((Point) location.clone(), this.type, this.fill, this.rotation); + copy.setIndex(index); + copy.setModifiable(isModifiable); + copy.setGiven(isGiven); + return copy; + } + + @Override + public String toString() { + return "(" + + location.getX() + + ", " + + location.getY() + + ") TYPE = " + + getType() + + " FILL = " + + getFill(); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java new file mode 100644 index 000000000..cd2135bd7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.awt.event.MouseEvent; + +public class ThermometerController extends ElementController { + + // method for updating thermometer cells since number cells have unknown for + // their fill type we don't need to worry about end user modifying them with this + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + ThermometerCell cell = (ThermometerCell) 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.getFill() == ThermometerFill.EMPTY) { + cell.setFill(ThermometerFill.FILLED); + } else if (cell.getFill() == ThermometerFill.FILLED) { + cell.setFill(ThermometerFill.BLOCKED); + } else { + cell.setFill(ThermometerFill.EMPTY); + } + } + } else if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getFill() == ThermometerFill.EMPTY) { + cell.setFill(ThermometerFill.BLOCKED); + } else if (cell.getFill() == ThermometerFill.BLOCKED) { + cell.setFill(ThermometerFill.FILLED); + } else { + cell.setFill(ThermometerFill.EMPTY); + } + } else if (e.getButton() == MouseEvent.BUTTON2) { + System.out.println("[DEBUG] " + cell); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java new file mode 100644 index 000000000..0657e95b0 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java @@ -0,0 +1,311 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.ui.boardview.GridElementView; +import java.awt.*; +import java.io.IOException; +import javax.imageio.ImageIO; + +public class ThermometerElementView extends GridElementView { + + // mixture of stuff stolen from tree tent and dev guide + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public ThermometerElementView(ThermometerCell cell) { + super(cell); + } + + @Override + public ThermometerCell getPuzzleElement() { + return (ThermometerCell) super.getPuzzleElement(); + } + + // method for drawing a thermometer cell + // basically copy/pasted from tree tent drawing tent images + @Override + public void drawElement(Graphics2D graphics2D) { + + ThermometerCell cell = (ThermometerCell) puzzleElement; + ThermometerType type = cell.getType(); + ThermometerFill fill = cell.getFill(); + int rotation = cell.getRotation(); + + graphics2D.drawImage( + imageSrc(type, fill, rotation), + location.x, + location.y, + size.width, + size.height, + null, + null); + + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + + // modified code from tree trent to display images + private Image imageSrc(ThermometerType t, ThermometerFill f, int r) { + + // will have a 36 switch case at end to determine which image gets opened + int result = 0; + + // 100 = NORTH, 200 = WEST, 300 = SOUTH, 400 = EAST + switch (r) { + case 0 -> result += 100; + case 90 -> result += 400; + case 180 -> result += 300; + case 270 -> result += 200; + default -> { + System.out.println("ThermometerElementView: Invalid Rotation"); + return null; + } + } + + // 10 = EMPTY, 20 = FILLED, 30 = BLOCKED + switch (f) { + case ThermometerFill.EMPTY -> result += 10; + case ThermometerFill.FILLED -> result += 20; + case ThermometerFill.BLOCKED -> result += 30; + default -> { + System.out.println("ThermometerElementView: Invalid Fill"); + return null; + } + } + + // 1 = HEAD, 2 = SHAFT, 3 = TIP + switch (t) { + case ThermometerType.HEAD -> result += 1; + case ThermometerType.SHAFT -> result += 2; + case ThermometerType.TIP -> result += 3; + default -> { + System.out.println("ThermometerElementView: Invalid Type"); + return null; + } + } + + try { + switch (result) { + case 111 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png")); + } + + case 112 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png")); + } + + case 113 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpN.png")); + } + + case 121 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillN.png")); + } + + case 122 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png")); + } + + case 123 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillN.png")); + } + + case 131 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png")); + } + + case 132 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png")); + } + + case 133 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockN.png")); + } + + case 211 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png")); + } + + case 212 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png")); + } + + case 213 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpE.png")); + } + + case 221 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillE.png")); + } + + case 222 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png")); + } + + case 223 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillE.png")); + } + + case 231 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png")); + } + + case 232 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png")); + } + + case 233 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockE.png")); + } + + case 311 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png")); + } + + case 312 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png")); + } + + case 313 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpS.png")); + } + + case 321 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillS.png")); + } + + case 322 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png")); + } + + case 323 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillS.png")); + } + + case 331 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png")); + } + + case 332 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png")); + } + + case 333 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockS.png")); + } + + case 411 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png")); + } + + case 412 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png")); + } + + case 413 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpW.png")); + } + + case 421 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillW.png")); + } + + case 422 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png")); + } + + case 423 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillW.png")); + } + + case 431 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png")); + } + + case 432 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png")); + } + + case 433 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockW.png")); + } + } + } catch (IOException e) { + System.out.println("ThermometerElementView: Unexpected Issue"); + return null; + } + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java new file mode 100644 index 000000000..d4e6dbd39 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java @@ -0,0 +1,72 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.PuzzleExporter; +import java.util.ArrayList; +import org.w3c.dom.Document; + +public class ThermometerExporter extends PuzzleExporter { + + public ThermometerExporter(Thermometer thermometer) { + super(thermometer); + } + + @Override + protected org.w3c.dom.Element createBoardElement(Document newDocument) { + ThermometerBoard board = (ThermometerBoard) puzzle.getTree().getRootNode().getBoard(); + + // Creating the XML section for the board + org.w3c.dom.Element boardElement = newDocument.createElement("board"); + boardElement.setAttribute("width", String.valueOf(board.getWidth() - 1)); + boardElement.setAttribute("height", String.valueOf(board.getHeight() - 1)); + + // Creating the XML section for the vials and appending to the board + org.w3c.dom.Element vialsElement = newDocument.createElement("vials"); + ArrayList vials = board.getVials(); + for (ThermometerVial vial : vials) { + org.w3c.dom.Element vialElement = newDocument.createElement("vial"); + // The way the vials are created are with the head (bulb) position and the final + // position + // This implementation doesn't allow for curved thermometers, but for right now that's + // fine + vialElement.setAttribute( + "headx", String.valueOf((int) vial.getHead().getLocation().getX())); + vialElement.setAttribute( + "heady", String.valueOf((int) vial.getHead().getLocation().getY())); + vialElement.setAttribute( + "tailx", String.valueOf((int) vial.getTail().getLocation().getX())); + vialElement.setAttribute( + "taily", String.valueOf((int) vial.getTail().getLocation().getY())); + vialsElement.appendChild(vialElement); + } + boardElement.appendChild(vialsElement); + + // Creating the XML section for the row numbers and appending to the board + org.w3c.dom.Element rowNumbersElement = newDocument.createElement("rowNumbers"); + ArrayList rowNumbers = board.getRowNumbers(); + // The row numbers are the numbers on the right most column, labeling how many filled + // sections + // are in the row + for (ThermometerCell cell : rowNumbers) { + int number = cell.getRotation(); + org.w3c.dom.Element rowNumberElement = newDocument.createElement("row"); + rowNumberElement.setAttribute("value", String.valueOf(number)); + rowNumbersElement.appendChild(rowNumberElement); + } + boardElement.appendChild(rowNumbersElement); + + // Creating the XML section for the col numbers and appending ot the board + org.w3c.dom.Element colNumbersElement = newDocument.createElement("colNumbers"); + // The col numbers are the numbers on the bottom row, labeling how many filled sections + // are in the column + ArrayList colNumbers = board.getColNumbers(); + for (ThermometerCell cell : colNumbers) { + int number = cell.getRotation(); + org.w3c.dom.Element colNumberElement = newDocument.createElement("col"); + colNumberElement.setAttribute("value", String.valueOf(number)); + colNumbersElement.appendChild(colNumberElement); + } + boardElement.appendChild(colNumbersElement); + + return boardElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java new file mode 100644 index 000000000..34a1ff12e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java @@ -0,0 +1,8 @@ +package edu.rpi.legup.puzzle.thermometer; + +public enum ThermometerFill { + UNKNOWN, + EMPTY, + FILLED, + BLOCKED; +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java new file mode 100644 index 000000000..711418d63 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java @@ -0,0 +1,196 @@ +package edu.rpi.legup.puzzle.thermometer; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +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; + +public class ThermometerImporter extends PuzzleImporter { + + // basic stuff stolen from dev guide/filled in by default + public ThermometerImporter(Thermometer thermometer) { + super(thermometer); + } + + @Override + public boolean acceptsRowsAndColumnsInput() { + return false; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + + @Override + public void initializeBoard(int rows, int columns) {} + + // method for initializing board from an xml file which has + // a provided width/height + @Override + public void initializeBoard(Node node) throws InvalidFileFormatException { + // sticking everything in a try statement because god has forsaken everyone + try { + // checking basic formatting of file + if (!node.getNodeName().equalsIgnoreCase("board")) { + throw new InvalidFileFormatException( + "thermometer Importer: cannot find board puzzleElement"); + } + + // getting the list of vials to turn into real vials + Element boardElement = (Element) node; + if (boardElement.getElementsByTagName("vials").getLength() == 0) { + throw new InvalidFileFormatException( + "thermometer Importer: no puzzleElement found for board"); + } + Element dataElement = (Element) boardElement.getElementsByTagName("vials").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("vial"); + + // checking both a width and height were provided for the board + ThermometerBoard thermometerBoard = null; + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { + + // grabbing the height/width of the board + int width = Integer.parseInt(boardElement.getAttribute("width")); + int height = Integer.parseInt(boardElement.getAttribute("height")); + + // grabbing the lists of rowNumbers/colNumbers + Element rowElement = + (Element) boardElement.getElementsByTagName("rowNumbers").item(0); + NodeList rowNodeList = rowElement.getElementsByTagName("row"); + + Element colElement = + (Element) boardElement.getElementsByTagName("colNumbers").item(0); + NodeList colNodeList = colElement.getElementsByTagName("col"); + + // checking that the number of row and col numbers agrees with height/width of board + if (colNodeList.getLength() != width) { + throw new InvalidFileFormatException( + "Mismatch between width and number of colNums.\n colNodeList.length:" + + colNodeList.getLength() + + " width:" + + width); + } + if (rowNodeList.getLength() != height) { + throw new InvalidFileFormatException( + "thermometer Importer: no rowNumbers found for board"); + } + + // finally creating our thermometer board, we add one to the size since row/col + // numbers + // are considered cells on the grid + thermometerBoard = new ThermometerBoard(width + 1, height + 1); + // adding row and column numbers to our board + importRowColNums(rowNodeList, colNodeList, thermometerBoard); + } else { + throw new InvalidFileFormatException( + "thermometer Importer: invalid board height/width"); + } + + // grabbing height/width from board, need to subtract 1 + // because grids height/width is 1 bigger than number of vials on board + int width = thermometerBoard.getWidth() - 1; + int height = thermometerBoard.getHeight() - 1; + + // adding in the vials + for (int i = 0; i < elementDataList.getLength(); i++) { + importThermometerVial(elementDataList.item(i), thermometerBoard); + } + + // verifying all vial cells were filled by vials + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (thermometerBoard.getCell(x, y) == null) { + throw new InvalidFileFormatException( + "Thermometer importer Undefined tile at (" + x + "," + y + ")"); + } + } + } + + puzzle.setCurrentBoard(thermometerBoard); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "thermometer Importer: unknown value where integer expected"); + } + } + + @Override + public void initializeBoard(String[] statements) + throws UnsupportedOperationException, IllegalArgumentException {} + + private void importRowColNums(NodeList rowNodes, NodeList colNodes, ThermometerBoard board) + throws InvalidFileFormatException { + + // going through our list or row nodes grabbed from the xml file and + // then calling the thermometer boards setRowNumber function to update the value + for (int i = 0; i < rowNodes.getLength(); i++) { + Node node = rowNodes.item(i); + int rowNum = + Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue()); + if (!board.setRowNumber(i, rowNum)) { + throw new InvalidFileFormatException("thermometer Importer: out of bounds rowNum"); + } + } + + // same process but for col numbers + for (int i = 0; i < colNodes.getLength(); i++) { + Node node = colNodes.item(i); + int colNum = + Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue()); + if (!board.setColNumber(i, colNum)) { + throw new InvalidFileFormatException("thermometer Importer: out of bounds colNum"); + } + } + } + + private void importThermometerVial(Node node, ThermometerBoard board) + throws InvalidFileFormatException { + // head is the top of the thermometer and tip is the end of the thermometer + // thermometers in the xml are specified only by their head and tip cells + int headX = Integer.parseInt(node.getAttributes().getNamedItem("headx").getNodeValue()); + int headY = Integer.parseInt(node.getAttributes().getNamedItem("heady").getNodeValue()); + int tipX = Integer.parseInt(node.getAttributes().getNamedItem("tailx").getNodeValue()); + int tipY = Integer.parseInt(node.getAttributes().getNamedItem("taily").getNodeValue()); + + // making sure we can add the vial before doing so + if (verifyVial(headX, headY, tipX, tipY, board)) { + // adding the vial to the board + board.addVial(new ThermometerVial(headX, headY, tipX, tipY, board)); + } else { + throw new InvalidFileFormatException("thermometer Vial Factory: overlapping vials"); + } + } + + private boolean verifyVial(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { + // figuring out which axis the thermometer travels along + if (headX == tipX) { + // finding start and end of Vial + int top = min(headY, tipY); + int bottom = max(headY, tipY); + + // verifying that every cell along path is currently unconstructed + for (int i = top; i <= bottom; i++) { + if (board.getCell(headX, i) != null) return false; + } + } else if (headY == tipY) { + // finding start and end of Vial + // I have words to say to james + int left = min(headX, tipX); + int right = max(headX, tipX); + + // verifying that every cell along path is currently unconstructed + for (int i = left; i <= right; i++) { + if (board.getCell(i, headY) != null) return false; + } + } else { + // thermometer does not line up along a single axis + return false; + } + return true; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java new file mode 100644 index 000000000..4a00b8a18 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java @@ -0,0 +1,37 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.GridCell; +import edu.rpi.legup.ui.boardview.GridElementView; +import java.awt.*; + +public class ThermometerNumberView extends GridElementView { + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public ThermometerNumberView(GridCell cell) { + super(cell); + } + + @Override + public GridCell getPuzzleElement() { + return (GridCell) super.getPuzzleElement(); + } + + @Override + public void drawElement(Graphics2D graphics2D) { + ThermometerCell cell = (ThermometerCell) puzzleElement; + + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + int val; + + if (cell != null) val = cell.getRotation(); + else val = -1; + + int xText = location.x + (size.width - metrics.stringWidth(String.valueOf(val))) / 2; + int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + + graphics2D.drawString(String.valueOf(val), xText, yText); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java new file mode 100644 index 000000000..f482411a5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java @@ -0,0 +1,8 @@ +package edu.rpi.legup.puzzle.thermometer; + +public enum ThermometerType { + UNKNOWN, + HEAD, + SHAFT, + TIP; +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java new file mode 100644 index 000000000..2cba64363 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java @@ -0,0 +1,103 @@ +package edu.rpi.legup.puzzle.thermometer; + +import java.awt.*; +import java.util.ArrayList; + +public class ThermometerVial { + private ArrayList cells; + + public ThermometerVial(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { + // basic constructor, instantiating our members field and then + // calling helper function to do all the heavy lifting + cells = new ArrayList(); + fillData(headX, headY, tipX, tipY, board); + } + + // function called by the constructor which adds in all of the cells to the array + // as well as updates their type on the board + private void fillData(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { + // not totally happy with layout of code but most readable version I can think of atm + // top left coordinate is 0,0 cells are added from head to tip always + // because cells have already been verified by time constructor is called + // we can guarantee that only the x or only the y coordinates wont line up + if (headY < tipY) { + addCell(headX, headY, ThermometerType.HEAD, 0, board); + for (int i = headY + 1; i < tipY; i++) { + addCell(headX, i, ThermometerType.SHAFT, 0, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 0, board); + } else if (tipY < headY) { + addCell(headX, headY, ThermometerType.HEAD, 180, board); + for (int i = headY - 1; i > tipY; i--) { + addCell(headX, i, ThermometerType.SHAFT, 180, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 180, board); + } else if (headX < tipX) { + addCell(headX, headY, ThermometerType.HEAD, 90, board); + for (int i = headX + 1; i < tipX; i++) { + addCell(i, headY, ThermometerType.SHAFT, 90, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 90, board); + } else { + addCell(headX, headY, ThermometerType.HEAD, 270, board); + for (int i = headX - 1; i > tipX; i--) { + addCell(i, headY, ThermometerType.SHAFT, 270, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 270, board); + } + } + + // helper function for adding a single cell + private void addCell(int x, int y, ThermometerType t, int rotation, ThermometerBoard board) { + ThermometerCell cell = + new ThermometerCell(new Point(x, y), t, ThermometerFill.EMPTY, rotation); + cell.setIndex(y * board.getHeight() + x); + this.cells.add(cell); + // still important for element view stuff + board.setCell(x, y, cell); + } + + // TODO (probably) DOES NOT WORK AS INTENDED + // BECAUSE MOST RULES GET A PUZZLE ELEMENT PASSED IN AND WEIRD + // TYPE CASTING STUFF, PAY ATTENTION TO THIS WHEN WE START + // DEBUGGING RULES + // a basic accessor to check if a cell is contained in vial + public boolean containsCell(ThermometerCell cell) { + for (ThermometerCell c : cells) { + if (c.getLocation() == cell.getLocation()) { + return true; + } + } + return false; + } + + // Returns cell containing head of thermometer + public ThermometerCell getHead() { + return cells.getFirst(); + } + + // Returns cell containing tail of thermometer + public ThermometerCell getTail() { + return cells.getLast(); + } + + // Retruns all cells in vial, from head to tip + public ArrayList getCells() { + return cells; + } + + // checking for discontinuous flow inside of vial + public boolean continuousFlow() { + // bool which is true until it runs into an empty/blocked cell in the vial + // if an empty cell in the vial is found while flow is set to false + // we know there is a break in the flow + boolean flow = true; + + for (ThermometerCell c : cells) { + if (c.getFill() != ThermometerFill.FILLED && flow) flow = false; + + if (c.getFill() == ThermometerFill.FILLED && !flow) return false; + } + return true; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java new file mode 100644 index 000000000..444037cfe --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java @@ -0,0 +1,48 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.ui.boardview.GridBoardView; +import java.awt.*; + +public class ThermometerView extends GridBoardView { + + public ThermometerView(ThermometerBoard board) { + super(new BoardController(), new ThermometerController(), board.getDimension()); + + // loop for displaying the vial cells + // stolen largely from dev guide + for (ThermometerVial vial : board.getVials()) { + for (ThermometerCell cell : vial.getCells()) { + Point loc = cell.getLocation(); + ThermometerElementView elementView = new ThermometerElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(elementView); + } + } + + // loop for displaying row numbers, same as above + for (ThermometerCell rowNum : board.getRowNumbers()) { + Point loc = rowNum.getLocation(); + ThermometerNumberView numberView = new ThermometerNumberView(rowNum); + numberView.setIndex(rowNum.getIndex()); + numberView.setSize(elementSize); + numberView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(numberView); + } + + // loop for displaying col numbers, also same as above + for (ThermometerCell colNum : board.getColNumbers()) { + Point loc = colNum.getLocation(); + ThermometerNumberView numberView = new ThermometerNumberView(colNum); + numberView.setIndex(colNum.getIndex()); + numberView.setSize(elementSize); + numberView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(numberView); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java new file mode 100644 index 000000000..8f0507ab5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckE extends PlaceableElement { + public HeadTileBlckE() { + super( + "Therm-PLAC-0001", + "Head Tile Block East", + "The tile corresponding to the blocked head of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java new file mode 100644 index 000000000..f195c299b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckN extends PlaceableElement { + public HeadTileBlckN() { + super( + "Therm-PLAC-0002", + "Head Tile Block North", + "The tile corresponding to the blocked head of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java new file mode 100644 index 000000000..d9e4c4a6a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckS extends PlaceableElement { + public HeadTileBlckS() { + super( + "Therm-PLAC-0003", + "Head Tile Block South", + "The tile corresponding to the blocked head of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java new file mode 100644 index 000000000..2bcbfdf2d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckW extends PlaceableElement { + public HeadTileBlckW() { + super( + "Therm-PLAC-0004", + "Head Tile Block West", + "The tile corresponding to the blocked head of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java new file mode 100644 index 000000000..0b678ed73 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpE extends PlaceableElement { + public HeadTileEmpE() { + super( + "Therm-PLAC-0005", + "Head Tile Empty East", + "The tile corresponding to the empty head of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java new file mode 100644 index 000000000..b865b0cae --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpN extends PlaceableElement { + public HeadTileEmpN() { + super( + "Therm-PLAC-0006", + "Head Tile Empty North", + "The tile corresponding to the empty head of a North thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java new file mode 100644 index 000000000..40989d814 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpS extends PlaceableElement { + public HeadTileEmpS() { + super( + "Therm-PLAC-0007", + "Head Tile Empty South", + "The tile corresponding to the empty head of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java new file mode 100644 index 000000000..ba344ff8a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpW extends PlaceableElement { + public HeadTileEmpW() { + super( + "Therm-PLAC-0008", + "Head Tile Empty West", + "The tile corresponding to the empty head of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java new file mode 100644 index 000000000..e8bfb8f82 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillE extends PlaceableElement { + public HeadTileFillE() { + super( + "Therm-PLAC-0009", + "Head Tile Filled East", + "The tile corresponding to the filled head of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java new file mode 100644 index 000000000..4a835601c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillN extends PlaceableElement { + public HeadTileFillN() { + super( + "Therm-PLAC-0010", + "Head Tile Filled North", + "The tile corresponding to the filled head of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java new file mode 100644 index 000000000..df559ec6f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillS extends PlaceableElement { + public HeadTileFillS() { + super( + "Therm-PLAC-0011", + "Head Tile Filled South", + "The tile corresponding to the filled head of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java new file mode 100644 index 000000000..80194ceaf --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillW extends PlaceableElement { + public HeadTileFillW() { + super( + "Therm-PLAC-0012", + "Head Tile Filled West", + "The tile corresponding to the filled head of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java new file mode 100644 index 000000000..7080b9a47 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckE extends PlaceableElement { + public ShaftTileBlckE() { + super( + "Therm-PLAC-0013", + "Shaft Tile Blocked East", + "The tile corresponding to a Blocked middle segment of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java new file mode 100644 index 000000000..760baf624 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckN extends PlaceableElement { + public ShaftTileBlckN() { + super( + "Therm-PLAC-0014", + "Shaft Tile Blocked North", + "The tile corresponding to a Blocked middle segment of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java new file mode 100644 index 000000000..ec14a669d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckS extends PlaceableElement { + public ShaftTileBlckS() { + super( + "Therm-PLAC-0015", + "Shaft Tile Blocked South", + "The tile corresponding to a Blocked middle segment of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java new file mode 100644 index 000000000..183fd798c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckW extends PlaceableElement { + public ShaftTileBlckW() { + super( + "Therm-PLAC-0016", + "Shaft Tile Blocked West", + "The tile corresponding to a Blocked middle segment of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java new file mode 100644 index 000000000..771c1afc3 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpE extends PlaceableElement { + public ShaftTileEmpE() { + super( + "Therm-PLAC-0017", + "Shaft Tile Empty East", + "The tile corresponding to an empty middle segment of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java new file mode 100644 index 000000000..c2ec4e0ab --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpN extends PlaceableElement { + public ShaftTileEmpN() { + super( + "Therm-PLAC-0018", + "Shaft Tile Empty North", + "The tile corresponding to an empty middle segment of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java new file mode 100644 index 000000000..fc2828ddb --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpS extends PlaceableElement { + public ShaftTileEmpS() { + super( + "Therm-PLAC-0019", + "Shaft Tile Empty South", + "The tile corresponding to an empty middle segment of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java new file mode 100644 index 000000000..ce0e7bce3 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpW extends PlaceableElement { + public ShaftTileEmpW() { + super( + "Therm-PLAC-0020", + "Shaft Tile Empty West", + "The tile corresponding to an empty middle segment of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java new file mode 100644 index 000000000..a8aeb44ed --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillE extends PlaceableElement { + public ShaftTileFillE() { + super( + "Therm-PLAC-0021", + "Shaft Tile Filled East", + "The tile corresponding to a filled middle segment of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java new file mode 100644 index 000000000..0366c3d0b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillN extends PlaceableElement { + public ShaftTileFillN() { + super( + "Therm-PLAC-0022", + "Shaft Tile Filled North", + "The tile corresponding to a filled middle segment of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java new file mode 100644 index 000000000..b55d2a1f9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillS extends PlaceableElement { + public ShaftTileFillS() { + super( + "Therm-PLAC-0023", + "Shaft Tile Filled South", + "The tile corresponding to a filled middle segment of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java new file mode 100644 index 000000000..3b2cd0454 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillW extends PlaceableElement { + public ShaftTileFillW() { + super( + "Therm-PLAC-0024", + "Shaft Tile Filled West", + "The tile corresponding to a filled middle segment of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java new file mode 100644 index 000000000..ea94846c2 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckE extends PlaceableElement { + public TipTileBlckE() { + super( + "Therm-PLAC-0025", + "Tip Tile Block East", + "The tile corresponding to the Blocked tip of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java new file mode 100644 index 000000000..25ae8afda --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckN extends PlaceableElement { + public TipTileBlckN() { + super( + "Therm-PLAC-0026", + "Tip Tile Block North", + "The tile corresponding to the Blocked tip of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java new file mode 100644 index 000000000..e19082162 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckS extends PlaceableElement { + public TipTileBlckS() { + super( + "Therm-PLAC-0027", + "Tip Tile Block South", + "The tile corresponding to the Blocked tip of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java new file mode 100644 index 000000000..a0c49bc77 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckW extends PlaceableElement { + public TipTileBlckW() { + super( + "Therm-PLAC-0028", + "Tip Tile Block West", + "The tile corresponding to the Blocked tip of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java new file mode 100644 index 000000000..6595da855 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpE extends PlaceableElement { + public TipTileEmpE() { + super( + "Therm-PLAC-0029", + "Tip Tile Empty East", + "The tile corresponding to the empty tip of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java new file mode 100644 index 000000000..cacfe5d5d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpN extends PlaceableElement { + public TipTileEmpN() { + super( + "Therm-PLAC-0030", + "Tip Tile Empty North", + "The tile corresponding to the empty tip of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java new file mode 100644 index 000000000..2815b9fa1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpS extends PlaceableElement { + public TipTileEmpS() { + super( + "Therm-PLAC-0031", + "Tip Tile Empty South", + "The tile corresponding to the empty tip of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java new file mode 100644 index 000000000..3bd77495f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpW extends PlaceableElement { + public TipTileEmpW() { + super( + "Therm-PLAC-0032", + "Tip Tile Empty West", + "The tile corresponding to the empty tip of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java new file mode 100644 index 000000000..8c9953dd2 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillE extends PlaceableElement { + public TipTileFillE() { + super( + "Therm-PLAC-0033", + "Tip Tile Fill East", + "The tile corresponding to the filled tip of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java new file mode 100644 index 000000000..f5ce01c7b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillN extends PlaceableElement { + public TipTileFillN() { + super( + "Therm-PLAC-0034", + "Tip Tile Fill North", + "The tile corresponding to the filled tip of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java new file mode 100644 index 000000000..05d68fe81 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillS extends PlaceableElement { + public TipTileFillS() { + super( + "Therm-PLAC-0035", + "Tip Tile Fill South", + "The tile corresponding to the filled tip of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java new file mode 100644 index 000000000..6aa486ba1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillW extends PlaceableElement { + public TipTileFillW() { + super( + "Therm-PLAC-0036", + "Tip Tile Fill West", + "The tile corresponding to the filled tip of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java new file mode 100644 index 000000000..b013b3493 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java @@ -0,0 +1,62 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; +import java.util.ArrayList; + +// TODO: Rule is untested +public class DiscontinuousMercuryContradictionRule extends ContradictionRule { + + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Contradiction must be a vial"; + + public DiscontinuousMercuryContradictionRule() { + super( + "THERM-CONT-0001", + "Discontinuous Mercury", + "A vial has a filled cell after an empty or blocked cell", + "edu/rpi/legup/images/thermometer/MercuryInBody.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 + */ + // User can click on any cell in a vial with a discontinuous flow + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + // useful variables + ThermometerBoard thermometerBoard = (ThermometerBoard) board; + + ThermometerCell cell = (ThermometerCell) thermometerBoard.getPuzzleElement(puzzleElement); + + ArrayList thermometerVials = thermometerBoard.getVials(); + + // finding out which vial contains the specified cell + for (int i = 0; i < thermometerVials.size(); i++) { + ThermometerVial thermometerVial = thermometerVials.get(i); + // if a vial contains the clicked on cell + // checking if the vial has a break in the flow + if (thermometerVial.containsCell(cell)) { + if (thermometerVial.continuousFlow()) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } else { + return null; + } + } + } + + // if none of the vials contain the clicked on cell yell at user + return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java new file mode 100644 index 000000000..d09b98300 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class FinishWithBlockedDirectRule extends DirectRule { + public FinishWithBlockedDirectRule() { + super( + "THERM-BASC-0004", + "Finish With Blocked", + "Remaining tiles must be blocked once requirement is satisfied", + "edu/rpi/legup/images/thermometer/FinishWithBlocked.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) { + return null; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithMercuryDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithMercuryDirectRule.java new file mode 100644 index 000000000..09fb8874d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithMercuryDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class FinishWithMercuryDirectRule extends DirectRule { + public FinishWithMercuryDirectRule() { + super( + "THERM-BASC-0003", + "Finish with Mercury", + "Remaining tiles must be filled to satisfy requirement", + "edu/rpi/legup/images/thermometer/FinishWithMercury.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) { + return null; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MercuryOrBlockedCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MercuryOrBlockedCaseRule.java new file mode 100644 index 000000000..a0644aec0 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MercuryOrBlockedCaseRule.java @@ -0,0 +1,114 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.*; +import java.util.ArrayList; +import java.util.List; + +// TODO: Rule is untested +public class MercuryOrBlockedCaseRule extends CaseRule { + public MercuryOrBlockedCaseRule() { + super( + "THERM-CASE-0001", + "Mercury or Blocked", + "Each unassigned tile must be filled with mercury or blocked.", + "edu/rpi/legup/images/thermometer/MercOrBlocked.png"); + } + + /** + * 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."; + } + + ThermometerCell mod1 = + (ThermometerCell) case1.getBoard().getModifiedData().iterator().next(); + ThermometerCell mod2 = + (ThermometerCell) 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.getFill() == ThermometerFill.BLOCKED + && mod2.getFill() == ThermometerFill.FILLED) + || (mod2.getFill() == ThermometerFill.BLOCKED + && mod1.getFill() == ThermometerFill.FILLED))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have a filled or blocked 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 null; + } + + @Override + public CaseBoard getCaseBoard(Board board) { + ThermometerBoard thermometerBoard = (ThermometerBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(thermometerBoard, this); + thermometerBoard.setModifiable(false); + for (PuzzleElement element : thermometerBoard.getPuzzleElements()) { + if (((ThermometerCell) element).getFill() == ThermometerFill.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 + */ + @Override + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { + ArrayList cases = new ArrayList<>(); + Board case1 = board.copy(); + ThermometerCell data1 = (ThermometerCell) case1.getPuzzleElement(puzzleElement); + data1.setFill(ThermometerFill.FILLED); + case1.addModifiedData(data1); + cases.add(case1); + + Board case2 = board.copy(); + ThermometerCell data2 = (ThermometerCell) case2.getPuzzleElement(puzzleElement); + data2.setFill(ThermometerFill.BLOCKED); + case2.addModifiedData(data2); + cases.add(case2); + + return cases; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java new file mode 100644 index 000000000..ab389d6ff --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class MinimumFillDirectRule extends DirectRule { + public MinimumFillDirectRule() { + super( + "THERM-BASC-0005", + "Minimum Fill", + "Some thermometers must be filled a minimum amount to satisfy requirement", + "edu/rpi/legup/images/thermometer/MinimumFill.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) { + return null; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/PriorFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/PriorFilledDirectRule.java new file mode 100644 index 000000000..61622ddb1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/PriorFilledDirectRule.java @@ -0,0 +1,101 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; +import java.util.ArrayList; + +// TODO: Rule is untested +public class PriorFilledDirectRule extends DirectRule { + + public PriorFilledDirectRule() { + super( + "THERM-BASC-0002", + "Prior is Filled", + "All tiles proceeding a filled tile in a vial must be filled", + "edu/rpi/legup/images/thermometer/PriorIsFilled.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 + */ + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + ThermometerBoard initialBoard = + (ThermometerBoard) transition.getParents().get(0).getBoard(); + ThermometerBoard finalBoard = (ThermometerBoard) transition.getBoard(); + + ThermometerCell cell = (ThermometerCell) finalBoard.getPuzzleElement(puzzleElement); + if (cell.getFill() != ThermometerFill.FILLED) { + return super.getInvalidUseOfRuleMessage() + ": Cell is not filled at this index"; + } + + ArrayList allVials = finalBoard.getVials(); + ThermometerVial host = null; + for (ThermometerVial vials : allVials) { + if (vials.containsCell((cell))) { + host = vials; + } + } + if (host == null) return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 1"; + int x = (int) cell.getLocation().getX(); + int y = (int) cell.getLocation().getX(); + + // Identifies next cell from tail location, checks if it is filled + if (host.getTail() == cell) { + return super.getInvalidUseOfRuleMessage() + ": rule can not apply to tail"; + } else if (host.getTail().getLocation().getX() == x) { + if (host.getTail().getLocation().getY() > y) { + if (initialBoard.getCell(x, y + 1).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getTail().getLocation().getY() < y) { + if (initialBoard.getCell(x, y - 1).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2"; + } else if (host.getTail().getLocation().getY() == y) { + if (host.getTail().getLocation().getX() > x) { + if (initialBoard.getCell(x + 1, y).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getTail().getLocation().getX() < x) { + if (initialBoard.getCell(x - 1, y).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2.1"; + } + return super.getInvalidUseOfRuleMessage() + "Something went wrong - 3"; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/RestEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/RestEmptyDirectRule.java new file mode 100644 index 000000000..486c5c1da --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/RestEmptyDirectRule.java @@ -0,0 +1,101 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; +import java.util.ArrayList; + +// TODO: Rule is untested +public class RestEmptyDirectRule extends DirectRule { + + public RestEmptyDirectRule() { + super( + "THERM-BASC-0001", + "Rest is Empty", + "All tiles following a blocked tile in a vial must be blocked", + "edu/rpi/legup/images/thermometer/RestIsEmpty.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 + */ + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + ThermometerBoard initialBoard = + (ThermometerBoard) transition.getParents().get(0).getBoard(); + ThermometerBoard finalBoard = (ThermometerBoard) transition.getBoard(); + + ThermometerCell cell = (ThermometerCell) finalBoard.getPuzzleElement(puzzleElement); + if (cell.getFill() != ThermometerFill.BLOCKED) { + return super.getInvalidUseOfRuleMessage() + ": Cell is not blocked at this index"; + } + + ArrayList allVials = finalBoard.getVials(); + ThermometerVial host = null; + for (ThermometerVial vials : allVials) { + if (vials.containsCell((cell))) { + host = vials; + } + } + if (host == null) return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 1"; + int x = (int) cell.getLocation().getX(); + int y = (int) cell.getLocation().getX(); + + // Identifies previous cell from head location, checks if it is blocked + if (host.getHead() == cell) { + return super.getInvalidUseOfRuleMessage() + ": rule can not apply to head"; + } else if (host.getHead().getLocation().getX() == x) { + if (host.getHead().getLocation().getY() > y) { + if (initialBoard.getCell(x, y + 1).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getHead().getLocation().getY() < y) { + if (initialBoard.getCell(x, y - 1).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2"; + } else if (host.getHead().getLocation().getY() == y) { + if (host.getHead().getLocation().getX() > x) { + if (initialBoard.getCell(x + 1, y).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getHead().getLocation().getX() < x) { + if (initialBoard.getCell(x - 1, y).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2.1"; + } + return super.getInvalidUseOfRuleMessage() + "Something went wrong - 3"; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/SatisfyMercuryCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/SatisfyMercuryCaseRule.java new file mode 100644 index 000000000..05c861281 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/SatisfyMercuryCaseRule.java @@ -0,0 +1,62 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.*; +import java.util.ArrayList; + +// TODO:This rule is unimplemented +public class SatisfyMercuryCaseRule extends CaseRule { + public SatisfyMercuryCaseRule() { + super( + "THERM-CASE-0002", + "Satisfy Mercury", + "There are multiple ways column/row requirements can be fufilled", + "edu/rpi/legup/images/thermometer/SatisfyMercury.png"); + } + + /** + * 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) { + 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 null; + } + + /** + * 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 getCases(Board board, PuzzleElement puzzleElement) { + return null; + } + + @Override + public CaseBoard getCaseBoard(Board board) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java new file mode 100644 index 000000000..d679781b7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class ThermometerTooLargeDirectRule extends DirectRule { + public ThermometerTooLargeDirectRule() { + super( + "THERM-BASC-0006", + "Thermometer Too Large", + "If thermometer is larger than required mercury, some of it must be blocked", + "edu/rpi/legup/images/thermometer/ThermometerTooLarge.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) { + return null; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooFewMercuryContradiction.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooFewMercuryContradiction.java new file mode 100644 index 000000000..8750eb64d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooFewMercuryContradiction.java @@ -0,0 +1,57 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; + +// TODO: Rule is untested +public class TooFewMercuryContradiction extends ContradictionRule { + + private final String Invalid_Use_Message = "Mercury can still reach limit"; + + public TooFewMercuryContradiction() { + super( + "THERM-CONT-0002", + "Too Few Mercury", + "Not enough mercury in column/row to fufill requirement", + "edu/rpi/legup/images/thermometer/NotEnoughMercury.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 + // Checks if row or column of input element has too many blocked tiles + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + ThermometerBoard grid = (ThermometerBoard) board; + ThermometerCell cell = (ThermometerCell) grid.getPuzzleElement(puzzleElement); + int blocked = 0; + for (int i = 0; i < grid.getHeight(); i++) { + if (grid.getCell((int) cell.getLocation().getX(), i).getFill() + == ThermometerFill.BLOCKED) { + blocked++; + } + } + if (grid.getRowNumber((int) cell.getLocation().getX()) > blocked) return null; + + blocked = 0; + for (int i = 0; i < grid.getWidth(); i++) { + if (grid.getCell(i, (int) cell.getLocation().getY()).getFill() + == ThermometerFill.BLOCKED) { + blocked++; + } + } + if (grid.getColNumber((int) cell.getLocation().getY()) > blocked) return null; + + return Invalid_Use_Message; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java new file mode 100644 index 000000000..06b8a017c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java @@ -0,0 +1,56 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; + +// TODO: Rule is untested +public class TooManyMercuryContradiction extends ContradictionRule { + + private final String Invalid_Use_Message = "Mercury does not exceed limit"; + + public TooManyMercuryContradiction() { + super( + "THERM-CONT-0003", + "Too Many Mercury", + "More mercury in column/row than target", + "edu/rpi/legup/images/thermometer/TooManyMercury.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) { + ThermometerBoard grid = (ThermometerBoard) board; + ThermometerCell cell = (ThermometerCell) grid.getPuzzleElement(puzzleElement); + int filled = 0; + for (int i = 0; i < grid.getHeight(); i++) { + if (grid.getCell((int) cell.getLocation().getX(), i).getFill() + == ThermometerFill.FILLED) { + filled++; + } + } + if (grid.getRowNumber((int) cell.getLocation().getX()) > filled) return null; + + filled = 0; + for (int i = 0; i < grid.getWidth(); i++) { + if (grid.getCell(i, (int) cell.getLocation().getY()).getFill() + == ThermometerFill.FILLED) { + filled++; + } + } + if (grid.getColNumber((int) cell.getLocation().getY()) > filled) return null; + + return Invalid_Use_Message; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt new file mode 100644 index 000000000..546fb89a6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt @@ -0,0 +1,16 @@ +THERM-BASC-0001 : RestEmptyDirectRule +THERM-BASC-0002 : PriorFilledDirectRule +THERM-BASC-0003 : FinishWithMercuryDirectRule +THERM-BASC-0004 : FinishWithBlockedDirectRule +THERM-BASC-0005 : MinimumFillDirectRule +THERM-BASC-0006 : ThermometerTooLargeDirectRule + +THERM-CONT-0001 : DiscontinuousMercuryContradictionRule +THERM-CONT-0002 : TooFewMercuryContradiction +THERM-CONT-0003 : TooManyMercuryContradiction + +THERM-CASE-0001 : MercuryOrBlockedCaseRule +THERM-CASE-0002 : SatisfyMercuryCaseRule + +Images can be found/edited here: +https://docs.google.com/presentation/d/1YHNog2fGvLJEx4kbJZiwwAlP-m2-E1O7hGh0HJ7S0gE/edit?usp=sharing \ No newline at end of file diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Element Template.png b/src/main/resources/edu/rpi/legup/images/thermometer/Element Template.png new file mode 100644 index 0000000000000000000000000000000000000000..52e3e36b7df920f61178ec85b2acd61ca54514b5 GIT binary patch literal 44790 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 pLFD&_fB(CC{?|6951J literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png new file mode 100644 index 0000000000000000000000000000000000000000..238a301af09392ccd9f8967a0fb73a44d1a0be3a GIT binary patch literal 8613 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 Ci^|jh literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png new file mode 100644 index 0000000000000000000000000000000000000000..8f343a3ba7122f08a553c3887d369f14de09029e GIT binary patch literal 8499 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 b4m+&7<2myq`U?1=1BZd0F}_gudc^+$EJ>70 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png new file mode 100644 index 0000000000000000000000000000000000000000..f95536076e2071aff77bd5733e82b52e21556451 GIT binary patch literal 8519 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 R|NOAW@Z5QPp`I<}{{Y6Ti4*_; literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png new file mode 100644 index 0000000000000000000000000000000000000000..b1bbd31bed26fafc57b7f481717e45fe6a2271f6 GIT binary patch literal 8542 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png new file mode 100644 index 0000000000000000000000000000000000000000..78179a63314a018e1a546b6cfae44f6a4bd69be7 GIT binary patch literal 5734 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 M{aeUtgzNMF0NQK!LI3~& literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png new file mode 100644 index 0000000000000000000000000000000000000000..19240e5fca588fe9b33d6105d3d23245bd93f855 GIT binary patch literal 5790 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillN.png new file mode 100644 index 0000000000000000000000000000000000000000..9905da3aa1dbc7a3415e66b091583cf9b9d6a5ec GIT binary patch literal 5982 zcmc(DX*d*K*uIiXN|r1WSx1X~$Pz*ZW5_y;ok_BcLiVV!lqHO1Y%#XRFfn7vAY05Y z*-H#YMfep7S+Xxv}&t*E#1t=X%b$p64w0xf3nTjW{`kI9OO% zIE`Tja2A#mpuYwxcS=Yy)OX@e)AwGaquX|}_y^qqfOJ7A! zhLjGkH8-xlKe8!ISY=mmp+}~>gsjvY{)!^4_)dlYDl8gLw!@BPj3&Fw3iQxt;)CeL@oQ%HF&RA)kn@Q7M*~ZO{R#*?l z(-s$fHg?x${J2J428TrfLQJ=v=YTh&b&u%)36gM0gTw)y1c&?)U8r z=cS7>!E--5VD!wh(lQn6+))071Pju;>#~BdSZfUc;i*GH+SF#XNr0}4BJ6-l)SV8P zsIHM(3ETgA^jHwDoB6O`*v$smR>6PEuEupa^14_Xc567_ag!KIP40`cqS}>B%L|q4 zuWCmfJ_-8X5jTId{8|sl{}0(tQ$G1LQL$;rRZkv$a?`LcYuL`7?$A_hY7F>&)LfBVJ0|^=A)Kf=xT) zHsrpS&UC0K$m7AhakLdOCqV?W`l(P$YgzbLUwGcO&$P4F3q|D0Vc-6|y$dxG{fcw? z-?as5aAmOb*5DoI;>i7l>%WMcAm)?(vmM^f5t$$Ni6o8aE3~zd0NUCa5%rxweDKEH zz)ErF5P)E?V5amz80N21y;w(6DlBV}%IekwUiKU#HxVW5KUR+15b=VbZp2zidRE0g z3i^Um%_`TKIUC0#CZQ}|b;{-Kqgsuym2a5{iDKJ#5t2L^GIx>SIu_HCsvL=g;(!%Gp!mY zk6cpuZe)tu+gdJ2I4h`Ze6L^vM!yu;a>iAYqyyirzh*{ReR)D{Yh!3FJ3WjEo?rSp zXSw6%w_ms&^j+vH-lse(`I998q(5?+=vQqWUaZVEV7E*^oER{x`^O;*VvHC~n@At$ zB*(J)P-UcFogK;rfIuTe4CD)HU{+!Tt>cY{=H62y>Mi6 z#Tz1aRjWetO8T}DP~MoHPLf2LFrU^;9JMF-vMhKdI)g=gr{|&F;+;UeN+{TjlzXNW+~KW<7l%t%U)jpl+cebbrA~eM!buo!z7v7! zR6i_rm5kZckEIpZJiY(0jL)FKRTxutl(|XFY`wAOAv1z z_jtIE`&C0c_(woG3j%_7Vj|9PYUlDnaH-5@=BmVV&sQ_VnX+S3mx?p@_CU-e8o{m* zl-HY1%vf`yqB)KIB~SKt1G6)_dHLbMY?_XWH-rjtbbU2ZhBXC01#X}PFmF3mol_057DpX;U)nNj`9sHm7h`i)Lkxvwp3k4< ze1*Ks$z#GHCBMqx&+ucr(|07@QLZZeJU$n_pKS;b#j-nyYd&!ypUAH7j8)3VM;K?G zM7g^Y_HU6wQAm0^(F-jkUi-pXb$;~?9Z0uq*({hCltiXOWPOC9aQ%C2WxTcZ)3jRv zHmfC1)e*|gb4tEJ{BY#5>;pu+)qHGEyQGr*hY#E%YANbRC+&m8MIIn6QnzDR*>SgD zD1Zh zwuaR-hb}udepcy86(N(A;ea^XPj`!g5&C73Lk{xw2V!-QVNUEJ&_;hkj9f-0#%Nc# z0qfkV#7lH-Qj1;NFD-w4qtyK?m*jWHV(~)P%Scu|o1$EGSiFFx&u=jV4^)3U(Rn6Q zTqe^Am8>_Ss;xF`Hv$56CD78*l3`p{A1}LMEPknbiqpq+W!k+;!<+hDuFl;osBm!E zY%snmX}WcPUJUX1`tXxhF#}$*g#kC+h+ImFtuk8_+e=f(CVm0K9ex+T9qPGZFaCV7 z>{e3kk{GT!uAQSosw*BF^&_|;kp`I6xZc^qRq=-2Ao(7~kDdOLtfW1{G8+$tVQz7l zeTJM>F1x0TIUpNh=+_bYLebF##nJle3}&rUV2F(~FwQ(H1TpL9b87!vl}L__$li`< z_V`By8O#;cst|~Lm%X0e#k;36WI#u(O!|c_yQCmH8z3jG-WC}huS!g;?GPZPnmD=0 zUco?8w0|#LL`#0`Z;4W=$a8^**q8$M>=K92LLXjQ#D;g`s`mz#GDrl;j%zmjg}PDg z6BNB-zy{^K6vF7RX}bsgDG2~NTKpHOms2Lnf|iu)h*uHqppu*T8w4tqy z>qZm1oOj5;V#)Dp50V2@Dd!1a+9Y$<+&mgJl`mNw-r#b{4ay7wc2y>MJHE&L%!P-{ zPKRYNl%w9b&9Y<7Q$uP1uRS%YA#|%x14|Fy_fk3RZ4Jj7*I84E_jBl0^LY)<9v7h| zQ7&xz^ZXKmIdp8)h0>$;UgI3M*_uo9i$lmo=hI}b=K~bYVZ){LGZ+TI*9D8KD+eF$05$DT*<2;i8!$pga7M+ zA>Q*imBIniU9QsUqXmYr=JtRHJ~vemAF4F_WiSV&jijV`emA)s!H#dbj#Bwx4l`Qi zihPMoN3NXkS2T|FGx)+iH8;Nd{3+e4-NXu97n_m_0)&zLbK9IOEFu;m!G3NakTgeC z42-aq&j+txn0ml34`#B)dp6*1d+_B=D-NG%oEPLVfW;v~_!fnK4)=bN#2iFo9cFOS zLcF|W|Ey%Wj%$w8YnigA7ynezx|BY*TM4-Fk&&G;Lh!pGtoN$XgxUAKtAY;bP#&ga zDRH~qYTb=bmcAe;VW_&3lEFn-US9UQ&&8C?lCzzD-rpM?#VyZ^)H9$0q$y7xz8OSf z&8@OWRGtDVlIZIFdh2epbvJr9lhY~oW=o@&MWA@C8u=kbNWa`Z6c0AhPN$GCl_nNM zRJoqtj_`4Ry0rCnWZA($SDg= z%z>2GWzL4@wY?CJQ+N1m&+6zKt6}ci7>sU~EbZub z?Ahn$yK>N)xkCzbU@-z}V{|;_S&;2$`m^zzt+E#I$-qAa@9J`!`$%5*XzKe) z$Ww{u>}~!uqv>|T*;bgEU)r5vH4?05H8hdUP$aTyg>}^bri5_EC15C+2igJm6 zEN@rDIjMw5>^&Z+W-gP}hpr9>^1xs*2=1TbiWzbpu!G=60LYCL&v$9dkteyo0V(x0 z)~d3dW}(~?_|n;yKcf2B)fLR$trUC2`!AJ5Z;ldYhG7O>rAiEDiw4!oU}FsJQ>ZB^ zDSr1Y%>Uom&f+82wupua$Me}d-F)|xa;&K^3J1YHEgp<`Z*T5Qo${48B6}Y)V*0O@ z4B0IAXUk4b1y-JB1E<-Cx%?6^$Up!Ssie8VOhLntSUhuc{pa!RbI3G2WJs}pjkEGb z43MzC8aJYu_LH_h*s-(qrK|S@p@%kRl^w8b9y(9_%Z0qI3;P)i8C>0Pd+4@WzGp~C zL-@gNK;sLAxWVS2UoCBI(JgiQ0HdG_uci? zh7Yv?%l9rqe~1Mmj&~}o@c8MPcFJ;}8ShiW1P?LTU9WagG=H>%I`3#yACO>man7&2 zGkk%49yQ+(9J}_vLBQIG-sqBzo$M4FStyDQO$YsFW)BWO@dwIPpHWLjr zBc;mGK6We1^;ne4^oMIu6&YY&1e7WJj6_HlR1AImfsmJC$XuEm@cR&txoq_hmi!0zl(V&p(Wm0v91z7Zf%DdqF%M`|uf zgoe~sFbr^$!Y}r&Lt(Uk7|m-G`Sr53mt3ZG0zQrm!3j9uMX0_?ogwScz7Fr~Rj8qq zs!A0j=^W@{NuK;7*Xs^j^}?v%I#%;zS-kmU=&pFHa!pG6POsHS2LBt)E_;nkN8Dc+ zdoBH7ufYWQo32XZ^g9K2xyy!a&mAo;${w#@k<~n2;)NqA$C_%5Q*iCOrUM2NHr698 zHM?Lq_v9w-BS*=horuG*HtZDqE1yA*Sk_!@w}-?$jDg3BB@-l(-hY8?B!$<(I-a{+Q?y4pfPOV>R&JVB4D@(CZAV!T5e^Ll z+1vr9wx(UP<(7^;SjdBv&n`I33ww|`Kvvv^H^V6t69jR}e#BDx>V)pb;8bzq&Cc^B z#)7@MGfHe-PujMW&Wbrzf(%9qunP6fRH-3l$mpo0Fbh~OuL z@+Mgd(ifkf(G}6FYB1APj60*p7y25l9iAX^n!0*eR6mokQ@q5b zntonX=dD)RP*29e#=B>8M}Cuwha8?|-235LWs$b^c9y zlCf-!-(HdUxR{#?YnVGOa=U`>a}U;Ayu_75Jmr}sH|iU=L7fD~bJoOHH{BVf(_POw zGZM}vKPemCR2QKWOS?p58@5MFVcxsM9`tA#=#>UM-Q$J^1;(hL%2H5Hdsoap7|c8s zcRc4-&%%~LdTA-HZUWttMFGDXc`Kk^0Xu)>vlOG+V z6;*8|{?VfDBQ~f}yZVh@tGbapt#i{+j0r#g(EVEthw4#H7EaM^|3&AM0kNR|8U^(f|6O{@0q;f6(&&*Ab5 z>e9An&##M|34_;9rpx_rX{1GaUmU)>tIdS}u4=?7QV%G{0_N|}HDOL0dwpU@Un&oo ztCONnri(Ni7`PWYyt|8Z@}%oXfeyMQd}lJORoi}iYRXT+xoPP5WL0cGVID{0j`x$( zYSB{jA8wt0d$|95Qh^0ClcGN5ZL704n_0Ls9Yx(c*cfa@g05^~L~lMCT9!a4w(s5z v+Wqt0kE^z_vQnj{ZQF5Ud%|D+h-+7WB)O(u^vtm)&SGq6Zcq)m^YlLewm@T! literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillS.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6c6d75d4e7e447027fd3bd191bff2b677ed4c9 GIT binary patch literal 6003 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillW.png new file mode 100644 index 0000000000000000000000000000000000000000..94ad0b2b4e770be966da8f5e88f6810de2068e0b GIT binary patch literal 6077 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png new file mode 100644 index 0000000000000000000000000000000000000000..d351674c56eaa9a0b76b38edc2fe2c1dad4bf46f GIT binary patch literal 6204 zcmZ{oc_38l|Hr4>c1oDBPPRH{#6;0tTewoEV`fxjG%XZrl&!19dPV8hQHL4AB@&5| zM9Y_`o5HOOQns?x2;YRPDQmL*p5y-h`2F+yW0>=qbDrmYKA+F?d_J!y(!ri1yHaf> zg+h_F93)Ge?$!>lLTC zPW}{%$~yA1$ktK&8-=3UV2f?z|KU2+7Ls$QX;J0(ze-rFy(?{~Hs$E}8k;3{-rm&2 zV(c%UL#_0UI{f*S&MOU^8jB5?dQv^9%6vTPaA@wEyd-y<4OSjiURWP>r&H>sU^mi# zvPYxiS;%DjHFN(Y|JvDehpSQ-7Y)}~O zFvZwCdsdaAW;8^2O$>gR(4z3&C%?8$xV9uv@41z=|Nj0z&toVz)%xF5M5_-GdyFWH z)?ySj4nZ-Fp)T^$fPbmqj4ru(8i%%ihG^$2Xb2^m0d7bNBfxO(Iy!&aBAkH#K+QgTn&MT{U>!jiT>K1T<6D6mlONDO}BNw z37Jc8nteRAn1wok85kKYuAw!x$@Zec>^;%EMp!Q@*7Q)BSx4AkbeL{Kd#mVKmf)`B zOR%G3w~MG3c#JMDU$?-fy*)4GtqYA@@++Cu)G2lq%=U^uiEG>&4;*M-r!4M8jZfej z0$fL=4(k@tZOGU3-rnmFm;jka#i>wOsAlQh&jClID@#f2g(j%NzK}1~&6!dT?cIZy z&W!A|>{ru8?e$nu2)5S-44>n;m*(e4@V>#h7N{HxRnCoxRSXqfkmI?K{;cNj$5WHt zm3}AsV}MYHOT~NmZ!_7m*5Qh3vT<4z-zry_^i*%6 zYDVkHvbJX<)%rm?8X>IepH}_N2MB=>C|{dzQ?q*{(zFxvboc*d5YGL$S_ys)K;PlW-)X_4cX{o=O$)LkPz! zOhQ4Xs)sJ-|9TEK0ug$_=)-Ai`J#!(sJ#~4FJGkqVf(`5|}QH>R{%}4R95$X!o$m9zd!+;w#fj$jF zy4~IYi;6gh{A`AH5zdVg1oYpzEu|Io?z|MmvzBlz54WDS=rP10TVFW-8U3DfQ50SF z<{sf)24m>FZw#*?j(Xc&Qdk#3h3}Y<^F3iO1dWNRa16feU1|2&`}_CM;-%ZQ%d6IK z*e*7-PHxTtFIqes1s1nf_XO39(BYCKNR}$Vv+_x8EzNXg8{%EQAcM^XO9{9nDlX`s zN96=GC1Z}h{e!^iQ}5*7U!v3C@C*+30z}?&rxN@ugi(cE1M;L(E2-|3^TN8QejCu#=L zGCpM~z>#|7XjapM4}1PxuJs?Wt(^qO-MJoBa3ST&tRv<6(Bhe-o7~=!avepnUWRjD zmEX)xv_9|`zB+PQ(_?Pzw)M4Puv&t$2jVzdEKnJ;G~w4PbW;yE-*s~5xcn5y3^d;} z+HEzSkhZ>MwP$L>)bQk%KdBj?j@tB&)>#i93O@7t>c$`)x%9IWPG5RE2_+1v?@ zmMiW~o}KLIs12CyHVp53!*FvIottj$NU78ceN&t8`GSl=?@ZdGIq?C_v_7Wnl)dGU zvHzzCRVlCKcKSJ=s|GGU?dPU_EfozlU;Fm9N4J1l)aX?8I?2#%^h@32hZPzjU2z^? zYSVh!f;)qu1N>Cy3+ZXFV;H`c41ZNg1`K5?MSLEPZTuxew%0| zUlYV&ck9}5=vBfVqqOk*pzg7pGlg&b>#RIEtKl<0K3yJSx`s{+vcT>$Z((?0 zqI2os^ASN|-InR(kkOtG4W|m)H5C!#1G&R7<}2eSIJ(lu!@_UPrPfQ)jNaQfnHye` zak}DgRhtZ3$KPbwq;%NUlnnM%1&HwR;aSg^Mo30go775D=w`0gv(lIs$z83Z^f$QX(^4l3v2g{QQa;fZ{D*A zhh*7!wvP*u#bi6Ltx8fByJB9O5Mvkp?b;!%x4*z_$%p{Mmod-}&g%c}A)#NNRECYb z(oMTw+PDLcqGg2LlOxwpaqP9yr0iB1!)J#*<02sCckPpHdC__MAD9NW1tw`qy1!Sh zc|19z5&ZGAM?g4+%xS^wRWa{SGr)hKWojZ$FOs3@sls|Rmd|p8 zzXj$0Do8Lz__8Q}ksv__;cKFNnjk?1;crCwupnU>!nZ>CEP;|4!k0(+iv>!$2!9RA zR|FN$0e)f@Oh8&*bQO^85s(z&V^$-*YW3p@6M-a3Ie1X|V zV6+CoWD)j}wP+0klVz}L5cJ4eY|&bF&06%+TI|oY>~Ua$u|+U@99UrNPng{VEC{y9 zQ8cE!IAI{wzZORSM6f^K?q4-F812`a|J%Ubhl5ZCc<9~3$2TOb8z9e<7#aW2GXLpw zI%KIZ)0=NO`o?Go%0Mt$eN4Hxw0A6-ma&aVEMrZi_JYFIW?koG+1kZ7}=j5@qB*?uEY|3isk z|Lz5w_$ABpq?OR$VxLwFXcmZH#F{pB?O0CO*D8V@ahbXlhxT5fM?_MWUI6}Hu$+gQ zfF*)zB7#gRC0=zmtb&%=0S~{GJ2i0cu|meOzWlPGN38_!CB)la_^oL1vkB>Oa7%+J zaCFM|A>Yi>0da$7K>fCVb2SZg9LYt;TT(b$A?3B&N|^_!8UK-JiRniph&>P0 zXc^y5r!=nu>4GT93;5O`fe@uN0=^+gDMZOoKxPt*C|L{0+@c`~aso2j79j~*0y6)8 zMG_1IWJV%L!WIFUqY6l)R(Vbsp0fnYIZN=KKn=-+Lnuu=Ld|4RVXD}VitNT9IhNWQ zcw{SHe}x`>nHo(%usmx7teCC%CWtk%ow$VIn>GlHEl}en2+pI48>pF!2x=Hj{K@X= zA9E#>?Z-Jo1)-BC9=cb`S@t6+b9?*JsV_CxRm*LDUjMP$;7M2fOj&2XPm|v0x0mv* zyik&uJf?>?4;v&?e3>Hz&CSU2`f9xRb_8n@(Y{rwCqY^ChsY zo~HerstZ5ispB0(S7xw3IIl*KuG>sN3Th&{T}v^ER>R4CyL}Zi!a`_o!@>iIhQsAv zx%(oRhA5z&hasP%%?4NfR0g;G>}8AC9Fb0u4u)0L-GpjxSUvS;u+I^^{rV_Su}Tq; z!4!kN5dvL<(GI{DUAtLt2NImGmq-=FS)|Y(Bv#B7>J*u_(#1zfi)i7YX;zqs*8V$SY-X>v_w_4WiyqY{d(9+6FRoUo#RL;CB)>~$_Lj9!igR;3|%?vkJ zeBx`{@kb}xd=>!7C}pXPO7eDjtHw8}=HuqWQ(w}D5A_`pbUy9Ayag27!l}05b7n&; zwzA#^y&A2x9DX1AN&5F`amd#vUj}mwv3$#^uD@#1L7DYdkAak53u6t0em&PbQ);f8 zwXU`X{E|A=+A-(RQN(+o%8VA^`&=2 zWK_H9WXbr|4+Y1BY0(E2NF)h8k^(6oeCQEvKKJAEP)^<(02);t%h!a}M>c@%r_54f zw~@WazNU~FAdEDv?M`yr(rp5@YpTZ}0WVhQf@U8;`J18^*W;l13V(miPxIW5PabW> zfoXY)6K5tqc-AbI|LriRD)U_-poPgjH%^ViRR>QzSyvo8Kj1ds|1otv{8pw(-L%~Q z{1_nX-J!HZNLE)LwXjnkZ)2^Ivd=8fnJB@A3;CN+eC_5pvM`Bf_N<{ z>XsIn?n0OyeHn;)3W(|80@t3|5NkL_H&Fv<-{;;J{^R8uqMlMVH~zqKfA(OaAD;S* z-j~0YEkR>=?r^oRUe@em{gat>BkNJn9~Nr{7Qy7}srrc201u9|Oe}~cSh-d}YzIi@ z-S`>a=Ith8E+XCeuJ3)pvDbq+uWh`VH_SR&a9u_AR^xnu@m7eH`y1(@ z_C7iQRI>U+nP`f4kju8aWRo2prJR;M+rPgI4CehSJ23(b_L=MT8}NL(S5H;Wfjz8T z?S<#R;Gh|aiV+b!mW11JqJB$jUNt>Y3&i3Ilqdv@Qkr}A(S3TN+Ja^8Mfy~p5&IT7 z9s_Mc*mD5M*OLJt9IQa7ECj|uAXL@}|3N0*``}u)CYG8$c1HNX8vU6;)E~xjRs#u- zfQt`_e%@I$dvfxM&s{2Ll3ur8AL|ugQOHA=PeZj3ikg}YeKf_VuK+Eb+Z%N?A3(B z0RWUzFNAc|e0j@+?iW6-f&OLjh+dqFO)Q5k5^Yg85|gV`=~ zb7N>UG%+^@oJz$rQSH5?e7U_6W&~tw496F^j|tsoE3 z$gS% zK5kW1oGEiS2IhyboS*m|`%=|QFk-kCXgo%elJsT?K}Gij91ZAB+@JEq|BLk6{RBk& z73tPSAw)ja@AgtgWdm0ajpq~E`fNEG+B+iXVxqGg&U&r^7EdZU&?R9uLALR*0$2<7 zA1|##37g@RCf`plB7ll`**LG4LuK=GBrRc3LqkfFw8^(N)jY`5&h|32Rpb92NB_U) z)qMv872BRx96Hl?D@~ok?*qHt09menIj?idXW75!+8*E#{7C1W+c$n~eSC8G5aBJj oU}QpR^1Ig%c-7v#TWK`BVuko)QC|0JaN${%l-Ol#QR9C1f zDJd;;fS7D0r9UX(M@FlFo`Q%X82tI;B-@^$l;5B?3>qkZx)WVV>3)LR++k(V#vF${ zPbw+t7$Ltu9N5$`C8gzy9hh{tFz?asbJ^OyhW~!8z42ZaT(msxzgllz+Az$IX7J$_ z^Nf7EK{jsQwP@{|{JVZ^mRb`-ZC}9c;3BNN$Uy*cRqPx{Oip?7&i0bt>mSb1Qn#IK zc>CeS`1V&3HpJB4i6^n$R^P(?8g5>H?cZM&-IZkSnXdGg`PrUxStAp@y6&p`aPNeVak^V_l!(9?dIZIW_Iw!%81GCh7rjGqQ2%18(3dn}oRSHaqU20uDc#%w1W4Zr^}d=bHy+*H z-QCSo9;PyA9+mg#`P*`9U31a(@OU&^qbnsWI^rh^z$_(Mf&NytW;j^KaH#R97r-3 zdE3R1^LxjaviDle)VU3QF9;jXjdN+fwV4EMZL%%@RQrLmt&7}V6%)^W zn`*NYLC5Q&?r7&!W9<@KQ|k$yc2e<-}vknhG5N1&y2Nn@P|yF7nMP77g1 ziyTROJZ8p>!mtOrNe9sE0Lz#k3BLxD4&GD;lJ9&FJ6>G3N>O8*Svsmpy(UL^U_Q44 zq4kOPNb4%+r}uy0yM>99g-7ORr+C#Gs2m=Z8Rt>^Lx=pdn5exXL&D%<;LW^2XrgRPt1bVZcKneDuY} z5~Fj2S3Ft_FV3ZzJUV^)^ss%${fYxoH|uF*|1O>?8qfjG&F(oY(%<_dMejd{E_p{Q zPW+IvKl{5~!7hKvKQ)vl^21YRs_O>qC|aI1kj1rp1}skJyM3P*=jxD~K1rN7sEc~1 zTU@WGN=+vuYf1?2mdMFf7&wfpDt(L7-?q0qY#@#oBQPLm+#wF)GggV}V72Dd@Xt9t z5fe^)#b!*?#4DReuSuue8Qg2c4L=P&ESJCVVZvvC7`S0h5|kN` zE>e0-Qg|77n!>b{@u^)QSLx5Fte1?UW8GUT*uf4VffEj+5u7}Bh!k`{{M(7w`X6-0mGX z)R@L&=$)HLwtWz4q@P^Bb_FTWxw3d;LP%EdYF6m=r^m`iZ)d>~8+~RchXW^vns~}5 zHgi{Ni2SCFcU}9-qD+CO+4XNdlrc)9Y*}m>HgFru;ZW?_%Er|&g_OMgTtO=NTok0hBLm>%(>QXtuK-f$ zAk7VGvbi`eUZ26ux`U1O@}Z(-6>?&|Cy^TT(Ctrb<{m$o`5775+e;!bAUS4z!bUSV zK#Ioaim;gj448S?5|5Xc26dp>L8zRwXxFXz0!V8G<^IR~ay)e>%1ob;7{8N*7uG^p zYB(xq3up$B@R?N$O*1fNZ8bK!`S+MbR^VwKcv=IkS8HyvH@xeC6%p;gpcW!1(SRb= zl7nI6iN(98h`97qUK-NlWER;18qegRa*A_RqJc@au)u1;;#NKrZvJY-e3C=J@VWO^ z(aFr=UCHa~Z(uPWkm)KKBY}Vvja*kV5Pk%g7u2#5IS5va-AVmO!Z*|)cBUyKBL~5P z*9#)1h{$(6a$kfPMKAaeE&&Esil8tY;?kKFR7RpRnuPCJSYjojs_6&|JP_5?wTP-f zi`;i$L6wK7rU{|2<%nw8f+`GD7iP_0Pz{6y?F*`?fX94g-YN}}qlp5G`LeKALN(po z>R8>6Ibj0Rltx=BAC?}`k%KhF`5v)hZf0U91`ou|nBz1-ngBv+A|A}k|0f1`@Dwuj zOGZmc$iWJH)L$HGKcDJiKo7i|jkq>fi;VL^OiVh9$mt%8IFZSq^2JgjG(E1$K6|!= z_q|VqaaF^o-}k8X`{=qw8=)>z?tzuaC>rBka4FL269IRa#xq4tyz3x zN6;n+n4Bwu+{j3?a2YUEgtc4s+r)d7`%=RCqk%pZLf+`9%_QdC17Uqa0hH$^W_n4g zK>TkoV5{AGIPREBqjpe|t^CzVf%Iv(Vf(}`njM(dwTyt7mVBx=Xim3_ozzO8rMKO7 zx=~}`8tGUAQAbG!PE>27a)e0-FKn@dKH-XuNh?5PPbIr+NVAWMQKF;MRA@?cR4+M` zBJvZMdf)o>^Js!WZl{LN$F{q?-1s&7A7n3Exp62!WN&!h4YYQ-z$ZW5ug&#|-@>Yx zpY9sD5mMQd2V40*)7o(O2?{YP-92w_j+A0Q;*itO;LPDxDF^(R9t}hi%F8a+O{*{I zJNJGuw_shd-j$a$d3)n6TShtXT2;1lA@S?&jYqNRldx+fxS=;XQJ4xk%o7o+iO`XD z-=qU9m={ejB0_%~k+u#d?wQmA9>ZlAqw&00YzKw421}LO!JW2ZHjo!$ixUW$Oo>E* z^^GJR8DuJxT9)*{x21il z@((eH@}T_Uub=nnG2z9+ZM)zV-#8f8pRj8@IDu%{4YX?+?Am~q8KPZhVAnr(NfhC4 zMsU*RAm$K^6OUqcxo-^dwtuf_yaINM9?0ZYCq`V;eF1C9?C4lrktPd z2%H*eYAI=Yj+o7>pcsR4cAWmlW;}+ilm+44x;#8YmP#RpSYHx zSi(`D`H30~g$74~K~s9 z6O#XPn4d}()CE`7xsJzDE?&wdveadH)q_@R@ga zB|mZq>rY(`Hjk5N$2VTq3@r=3+fq*ApQTu%iZ4nw3tAhEgr=Xx zOXpaUaXWEmqd7C~mOGt*W?z7%Mhv$DC^N&Vx<#Z#^k)W^BSs8VB+)k(Trd)Y!Uq>D z5l>>^`H9OoiJ)gSD(8GvwlYIdgl31su9^aLg&lZ7XP^Y=2WXi-N6WS{k!Zm@`Jusx z3EA-{8@7EJGUMul}-O%LhGLb@}W_-WG1qpSBNDSLtTR zY2=3F;*-kN%DZxXo~wPmP0tR(ErumTNh??yy-WiHBE9IE*lS%4JDPILC1mxX-n!qBBJ>SUJeWNs39PK|5T;}5GhkLQ1TF&`$Hh0pTu}nK;IJ~ zFXp=e^$Q1)F+gMr4?X7ZOB|WequxV0XYfcTMHG~Vs=om+9J=sEkiDT7!J$(Oj7%5c zRMTRO?K|LoV;c}_Oa#O&h8cdOefK#+Z@ONuEg~49A$yH@g@Hl5@^uT;Py@_YTZ1Un zwiCB32P<9Mf~-{EFjC5>R00n|U(XJ=Kgu0Oc{LoSwBT*d~ zgTUT`+7!~tBx>s{v^q%8a4BBSKs*v>Xj5qqgWNyEFijhHQH?IU%L|{}ol|-KN81x{ z!?`Yu^}(I%X?9d5QZVWK`*5W9*DeGWGd?ZV+q^rtv&g{(*YW-{s7n-w4myGZ=6~}< z)bA|3;N>Vll;XeregAJ1v+6~*6uffcI8EtxfyiZPJ?K*-9=_k_Prtw#&+RE44eom2 z^fGxvEXdoKl73EN*WneUf_8JJXXRGmor8}q1dhFX%!{du9d+8&Esx(m4uN@n^$P$7 z%CX(yUn&G3&Lrydxi0+47gn!^%w%UJ>k?cGvFB$7lfWHCyH10A_-ya_l18HMZyybc zh#jlfLD#FnW>T0l6lgj|m2LdnwI_funcs3o+z){G&T>-Vu_pqIAp4L5{hz_B`&+VI zGbUC0A<6jm=WTn%3Pb$2zH$)06AJy-+`KS59QZ8J41|MpPL;C5u3OeA#A2dN{9YH^68T3dM zBb9(h^V6?aW5@kAkV1E|CC5tpRC-#DNwmlT|K^u}&NT@Ka7tAk2LF?xeQ!Ojwp1B0 z2d(J27oY!GcywEkPI}FT{ntT`FXx^h&4Q9gV8|?vya|%D-SOO0eyxoNNuR23GyewY z4$ubM1#IxQ_=35A37sY-eo{>}RKOBU6HrSHhnW^7*pAW@J_%>L2F*d8tNV7_-The} zPrwEryf|Mx+b1$?8S8sG^-I>nS5|8oAACNgSUVH)>Z1<1V9mF-=FFX~^(MFO)WqxO zrGitL^3x_a><3OW{g1^eLsuXj(yGY|EC;$wBdNYOEmo}wR4H8dCt)#Ngn3XApdB-a z((jCuN{IDK=C*D+I+V&Fq347CoPG_3fC%#pqAwNUojH~%+!kG zNW3so)AcN}9Pr5q#tgC$LRtnC90Op*X(3i_tG=Zw2OG`iP$SgwB>pRaA>Aix^__)K zq7DUAFTzKQle|x;k@$T+l3lRk>^%@u5zs`+7Wt>wKmv4JgsSX0=&J=w`A^i+<^66F zG)}f&jXgi*;TuY0K8Yq^k^nO{I)c+uIrF|bQt=x$EdY3u?v@hkow}Yam8~V=ovWcz zEhVlnqP)PkiT zD`_fN%7y@VRzqAIP%Pl$kO@SJg2Gl(4r@{wD33QNvlgK1L**gO z_DN%i1n}38A~goW`Hq_M-zW;HHm98J$uV|HX2S1^*`aY>TDnxv@}czDz^)(~#&uxX)OpjYkwYLZG(5MYH_ zS6)!t9oL;n7Oj`rPemFYd$OEcH4Bge*wpudgpUq6l=#i*{$i!Sj-;fd=-vLq^wwK; ni4XV+!9!gN6|danJg@OY`D&j2)8szzjYP@8&Y79dIK=-S1_hNH literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png new file mode 100644 index 0000000000000000000000000000000000000000..91356f95efcefd8c11fa3591253b55b07aa70d78 GIT binary patch literal 6539 zcmaJ`eLRzE_;)%}TU5_xGDY@m%TRRIJ6g4Q%uuu;M~2Rr97^O}9T}1IyC3KG`}g<9K0fzz-}m?W-q&+K*L8g_QG1;1mMmVe zSVKc&i9HwLYiRsH1wRUQA$U?4c3%qq{BV+Q$JWSyVK4{=m_w}HEDepj30kwiXo4~B z1lQxFhQ{*M=R{uBF+FmLy_r&o&wDw8}OtD2;<(XZMuHvFxhm_=cz zIk~nhOh|eJx z?mj-<``dG}(yYEbTj@Xh(~moY%d6&Qj-J)gXlQNw`)}(nV-+8VcsaubRo=T(H`+Vb zwdA_xsU}DNRx4FaDI3~)>o2KQ<*zdAhCQX9{|V|I^c?jl3(S0&slwYC++4o)=RfG8 zNCK6>=~s^m9?2V!R6Jt6GNFSfqdR`r|ds z#hF3e^Jj4@v!2Bp=4HLE*QrS|b(v85ITMY}eYu$?E8=Bv+Ez7kO6GJl9BgAXww@>b zaAE;t!CQ`SyMM7 z8>%<_`Z4@_=+9x`v5uZ%Zth%>Z|zcMTO>)jUr4iCnQBA3qVg&fMTJau9BLcyYixUX z_UlBq3QsGgSRl)`fB9oy#hK%y0ki+s8V%n*eN)p^N&+Mf+fZ?@MoYT$x6ce@yjkmh z=fT&p3LN43C8DA~J^bMr&-h$(?+054$iYS#d*>D-G>-wBHS6<82lT<_naeEVu4Zlod?a4q!oFhz-KdO2;Kkf=*Zxra?;$z)fy*@(EZxi7UlLJVpB zzC=CB^!2enQBcusDWvanZaTA_6=($<_on#xwJy22Sn?-PpYpVyY`HRHJ-Y6cnFX@O zJ-^8u;qYat;aP$nQEY=Yz}~;c6j{0X*h`m(Jz}D|qHl4(u$PUpKT`%#o|cd;b*WcS zfcp)Zkp6bf`V>+biSiVaGp)nH614SfL|+Iry0AA;s*P(&pPQhICBF!kCcd+gIV2=5 zgV%Qj_Zx~O5nlinX7FC~_Eyg!0gj);7&iqD6Tfo-+vL}+dW15E7@jKJ+amiTKoVn& zY(yiSWv%t(WI{$MWbRqqG!1-|QyvDOg|fv7JifH}*MD+LzMLn!Ib-4V0xkYc#$psR zH(tgTl@gUtghU}gB0hv{M5P2JTS!EKiTN@xgp{4Wu8{!HbekJCk#|NG7{^Oyi)fmI z_gkG>b;3?`2GqJ4Bb*dggE_}dI}xv^jMUQ0^|*I%5N9zwIGE#wx$OY2-zDp9<^(Uw z8NO&VGyiQ4_wQFvP7c*OCEKV5dM`QeE$Ix5Sz#Hd>{9rhFCWUyQ-RO2?bhyV=-03A zzV9@*B>35jH>sPB&Ig6wg2C3TJj;_G269f1ho`SU;9!mnw->nEtqNAoT`M<}WWQ3{ zUs4$cb>1{~x}II4iqqe~i8I>a?L5heTXFiO&Y2Gk3bM#?af^TWSI>Uo{ov0BnbUa% z18h?a*N$IAr!xQ<6rg_`6XM8+<7{&ymWuH6feoH2Y27w zx~y2b+VdbITIClZ{*gw10$(7>e&`TlK6ec3N& zG2h+YeKf)Z$1iJo7h1Ms1zrK$VshS->c1Ys@{ZED;nR&i6B{MuWct`yB+tFT7}-tZ zjz7)`*8Fb~*dX$+`c}N}VOUw(1xVSmI+E*c{p3XYxtSx!WWD_zQSrXCbu*jf?i~p` z=6++si&ZtjmoZJIBUe1hxdk_S#l`D4I-m_s)4?{c+p;TIZHk zuQoJq%b+7En~$fKt6zJnH_~!{9BmLa54^NlW;huq2&mB=8oAf}T1UZ^jg=4QM|i82 zV@!7hz;eC34ywbM7JGVI*UI)0na$Fi%0t;MhPB*k%_DQO(->3R&KxSnG}IpMsTmRx z6o3C(as02w^&XL25rN8*jo_8DcuJ_PgezM9ze5NN!R(6vh9l_g1+HinHIYb*FI-LK zcEHGFZ?;DwOem`cR_x;o@k$XU=cJ@AO~Q>&)zzmYHmYM+)G$qY19gXA-)_}W)b-h& zo2uwu61pdL@z4tcCLHaIBCr=HezM?DEVx$$f%fJO(taCY zwEQKT_8>MS^|`oUZ7Mmh0;pXng?B$h^3&% zU9*8JG4ax6)5K9I!v-u^>ki()AM>7~ptx?P2&EMDR5NDq(fJ=k947&KZX_3@kKR9f z+1zAFRUZMkDL)z}Jbs2A>e?3n7u!%^HyfIGoU zSf1B``NIc|3BciTeCyZwUXD_J(^ZCz`M&$v<1isP8XUPrQ%~?{6@M|Vg*nyP+On$U zlJoYQnXg~f5|bU@2A$i)npzVZm4HifOyd4!^=X*FuF#KdF86DV!UfFnI%)_zDgE2X zp`lyXeibHUvsU}kJsyc9qZf8F!}PNEAJ7PXcrDY8uox(dz8JrR%qh zvm}+dsPa^eVyHE%MzrtlXZbF+D5iW`>7Ata;^$x1E&!7I#=Wn?Cii zyMQj7_}(MF|LZ?iMF*d+VE%BXDW>gt=sUMAs$V=W^U9Gpxwq^G)poIIf-ajZG|tE9?ECb(Rbg#pM}wLHKPtxvM#R?sFZq`|3~Up&BxW z!HGA}*H?nrc8LrpN(L^h{8g&`YT#9dT5_=3S$Q;cCJF@>TivYQ^8?xCENs?En}EPVUGM= zeEjJX8`-#?p5mX1ARPq(pFueL`S})u$#p*qq|XwJ3PACmMNc|UN{*d^b^8H+{E}B! z8NL&eLWsHuQqI~-KQyNoWWka;!|FObNF0*7Wz}^sNDGoWqv|?_J*&JYdd-AnZ(VaN zG#x>*G_m0Jr-kOHZVgbR$qtTnmd)(FF}tl#uz}rcER-~m3=IA}`*?SC9qa4VM|Yp_ zZ|XZf*VV)Ac{i+shHyt#g$%%Fk4hihy*oXGJMkkUaF;;p9WuCj{<7WFT~{?*81nY~ zA~ffgMUgCNrpQ!x_zp*Ht#uDREntmf7=akLVHs;2?9_l8j97t23^RS$Y$+>nCBrQ7 zL4PEqyT+6&5EcunvuUka&hE9VLf67(EP)Ia81o++F^-hHlRZ(b2Gko%i*}S4|tq5kuf~!xbBz0Z^ zp}3zXj#)Ed$7&N_dpJ6zII^N=QrP!&3ywc21Imbn6esXXA$rplpU@PZ zu@tWuB6FuH978hF1}=+D>{{@I!ks2_mSgc)bo(_dUR#`GE>taKOAca8t*f(bCD;Hg zf3uXQg~hi($`|+yq6B-J!o2}=a9I2sTTmWkkR;e*tlUsYge{PjTYjY>hjA=FBZi-x zs0jZy>=Uw}1+Sb0Q9^?CxI_kW@hm1svzkjq>KgM39y-YO?TkGK=_$bF&`}UwvYgzs z^ekLA9Bg{&3E7Hij>QKtN4R~yN%!sUhp7FSaKpr4YgtY>%4!b+j^J1^v;6Zz==63%X1f*4v;`i1!NYPJD6*r z-0zD>M{Wc*n4&48VIvR=4(@GaO?{v+ZhMx1qC^IvrH~(}g3;v2FE?9gT1r|SdH8wi zCXj3>Kn5vKxoPqDfGT@38Wm41DP+T~CwBVhXfe2ts|UV*tfC{A8&JvCVlNvj>rZw?H9)(gJ*6ihJE+L7-2cX9{c9 zUqjEoeg6DxoLmp(4HOh$7YP9w3A_;r0VSIGnaOpkl|mg_sDJ{MkL)^XP;cJgi*~Xl z>LK;i6u4IKMbwvJ+VYb?86+ZyN8c0y&zMQ(pK8fn7!HdQ*ET~1KpkQER_Ws2U4T!e z**t?VoLmQGkZXY2+`y7|7jY~!b0$!ZlEYY*1D!SVIsN;{F_{Bu-cEXf${p-F_x1Rp z2-kh+vDm}}a^}fR+{|jg6%>uYf?kB{Z}U?sD{<0`BC_rZ;F|=f2rL9L2yFtm#y$~( zg901CT>@F}No zE3^5_(E*07CSKa$I4&2b};#Tb?^WYrVDG1+-i~;&|sl?eM~~ ze*nj$ao6nq^JS~-Pq7N9A%n}=Of)zQ<9MQk!><&!AuHHYkm;0p`Q_0}CDFf9eHrMk z={rni_R{-nTJ`SKbXA77*%_S$-{LO`*d>83oY$Z$(r{ zQn!Ra3!a^us`}pMIfRwxYMOk6qIUT_x=h{_e$s6jUmz9fznr}pV&WbY6f|0MRJ;*c zaQ|3GVW$mkeO#h+#(1|Z9_Y==%oTF>{ebtJN=*xW5{b-B#l1>6H$kn;v`B!WntW-X z();b-o(7Dm88Y6b&FnbmzUGKlsbY^cB!2az$H0m%anevrctKvpFz> zaC4EVIr2nJs!!g!#lP!Mdiag-x#_(Od9CkzeNAHyWrCc1*G^@SApk`!+qmWsLG6=9{6y)a2Wd)idich2{ zNDMk$mYKgpk>(PzVL&zBDUzE+0J2!=o50-bAK=25_9l@e&HZH1#1m||!lpUIpn?Ll z^2UzIK<+h)5V2KZ#F7yxitkHPoWriKAkjc72K_4_U0Qr;ppgi(4UELSAySDBgJ1!a zBplbs8vjpB>H)d*(o8R+*HJ2Q0keSJO)m@0`YZ_7FQf^;Uh!=u`CE@(6=<7?@-eO+l!9{u;fQ+eTL|Z^lfj0aEE|45l>u7pGExZmxm&C4*Dz*D7Rhk z+WXgP#0r19UVA#sKZ<`j-TPXQG<6+Zc%;>M)5#CJm_<$&733@T>h`^#`mn*$u=axu zsl>lK|JJS)MOMT*xFxEzF=5-6w-H0r{psy4{Y$M!emdRm6%?#PDNA=KOXFpYFXiJy zk^EW{V&HB{Fg?6tJUX$?noFQqwvP;CB)kUgI8=%xwl?19@EkW=TAu^`X2W$IKc~X} z!4@51+B~AakT#BO0Wyj!-6P#5COSwZ?hJy*LVNb9zmJ#TDMaH&awL)R&MOvK&RP%q zJ)DGKc2^;me;6isKjWSPl2TH5nxKS$kc~^cg9F}Mb4*P1lS319%6d&w zxBc1|wF%7p;Y(|}Xa9Nn(C^E7bCbuvB-3BN)HI@Mocq77`Tx^aPm`a#{3Uu$oL&xe z<)ZGhQ(jE7)8EwXdG?o#2XP*tCFeHk;<#jbqsO$0U&Bpg@?WBHVBqrbEsdQWxAU9_ly4r D`sSN- literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png new file mode 100644 index 0000000000000000000000000000000000000000..6013fca198e818b07bb63bfd855f22d67f4f5960 GIT binary patch literal 6170 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png new file mode 100644 index 0000000000000000000000000000000000000000..c59c1a9301921a002956f2fb79fdc8beb059712e GIT binary patch literal 1920 zcmeAS@N?(olHy`uVBq!ia0y~yU{nTS4mO}jr9!$ckYX$ja(7}_cTVOdki(Mh=Adf|uLXG@p6LJm zI`6!CW=)+M-?Fper$78jpEiBE|MuvM8)u7sXCbm&n1@fBCIrapfBcvnW$_S?4q5B$IV?%lilT^E7iXZPu|XV1>9pH_c0 zvV*PJF;@J41W)_nT^X0oo%4J7{deiqORvAy_|6VnzI=IH{l_G$PC)_3o(7I278e0! zg(e9`!6O_U3T_T8hD=HwWJy&ldSjaPs&kWm{JuZ?E9ak^ck9-cvfXbxH$Bv=UbIFc zcJZ>LFOQCPZ`!&w^!k^mm>3;xZS7K1_nuFcdrSqqdzM`|dD7FiaOV5_`>zXMb|KDb zz8C)dsj;2-;NRchUyrElaC##3x2E;P_Wb*&-n_|)4gdb<=jTr!A0OYf?Dk}J|5qz5 zx98o}GS9!a=Jl(4`|IQ1-PsvDUE8Hj{kx=ZM6jWig~bQEzx+v4&eys*=#U)_a{8cP0Emx|mIrgZ%INZ+v)ujm-h}VQC6O|$o zpB;bwCulL3u8^z-hMmnV?|83=I-6QL8-d5boT7oPrtstUh2DkclrBiQ>RYd zHS2QT_W##RwHIGp!Nbu+7`(l1rEhP(WHk7ZO>?_T$E z>y8~7ZvO+OA3bs1grwAZP*myqij~?f_x4l<@2mM4biD-Vgg9H%8LXIk{^`&mYm4|GTFlf_hnkPLWDMrp4jHmYMSFFLx(vt_@o)BQJm5_42N}d6NCd zOQ&wyy*t`!u3v0;`M2kNeSNQH<$eDA`RkQAWxH$tMg9vV7@bEmiiE-76+8 ze)`lYuh_GKi)+Y?*OS^VaeJ$P#fjDL-MgKh%s*M{CVVZV=bT=cwT;c1?)ja8cHbo# zH&R-%;Rq!1i%Ot@&^(ivo4a;i#;sq!s&3xDzkcqVZ`)zzuV$&1$oiHV8*lTmJXeGOE!Gppt2<*oa(anGI@ z^#dt3b$@o8srmKgWl47S>OIxp*ZniwfBEKl)qkI#yQNtDuDtm6_V(A0%4g1;smU*I gr_(QItM#Ahk8QO{_$|icz*>{R)78&qol`;+00Uy@xc~qF literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png new file mode 100644 index 0000000000000000000000000000000000000000..f70a25cf178c49a5887debfa833ef87a68c0032a GIT binary patch literal 1987 zcmeAS@N?(olHy`uVBq!ia0y~yU{nTS4mO}jr9!$ckYX$ja(7}_cTVOdki(Mh=`2pH(`4;@;oiuixI@UOF+Wv~=s5_3N)6eP8nW z+S*S)K0XebXIEPlrqF-<@yy$oUw`$R{_gJX@?Wp6u3j&lP&duK=0`w3!{^OcUY7Xr z^YOjXNzKk)y=Lv&t4DR-zI*rY?cYUt+kel$H~q)!_4{9S)_;C_dV19_)ARFeS1-?h zFP)p8fBop1w{PBDxoqzJw(NI_iPY7|Qw0_}@ywy(o40J-7|7P_7`yb#@4ufF{;oQi z64ck<|N2q-*|g1T)~vZ=x_7g@$l9>g>x%w0C)vmWO){10{e1mX`RT9z4LgOD6`CX% z1&?raD7ZPW7&0k!2nsm%G;k!bxCjiCRLjz`-L+Oyy})3ykeTMUJQ&rJm)@4Wo^>z9 zNb~sdh{rh*9Sor$u!i!ly6!-j@#YU1(cQ`#^S{kodR8kV+-<_M9`ZPbh(dV?N zlKAObx8f;si|t)E-?ZVsv|ea&_ftD%g-L@ga0eic6Ib5No8RZQ*veayuYKv|msgJJ zH^f~qMV`1#(&(F@zu8PVDUl*fU{_f7o>?&Y}TY5F? zYUrt7zp5nrkCz%|_qsj)v-QthiP!~SB;LM#I~5k8+OucRF7@^A(Ytou@Kd%FN$L0j z*F^3u9N`^%YJYEAceM2TyV&p!9ft3cX9KHx{)xb{lC9ychO~)Z-0G#{q*Ey^-|yWPo6A^*;li3UC_1b z*H2g2tm}@?t-888wZ4A;x}#rTTwMHLWU>EzyQyiLBd1IJ&9gs# zVtr=u7obs9N(V1q474@mC$3n_V2s^WvU1)J_0y;0u63U%=j{L`k1wA^m0-!WyzK2E zNOFCAKX>BXSl^Ey9v^gy;ckiT|xW6>~6+Gvz=ax;Ig06o#@*-*H zd5#Vh>Xx$^j=H+KmH(b58tRr>&Ar^*+-&82^VTh`?Ck7PL+{E+h=ZH_vrnrS6Mgj-2(>Y?VC4OF3{_$GIXNf;=ZO#75VPJonU(RNQd&AEc dU#p(hPc7tEJG3yR8CdZ$c)I$ztaD0e0sxhC2lM~{ literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png new file mode 100644 index 0000000000000000000000000000000000000000..48cde1b37ebde1afa61bbcaf5e01626833a17073 GIT binary patch literal 1690 zcmeAS@N?(olHy`uVBq!ia0y~yU{nTS4mO}jr9!$ckYX$ja(7}_cTVOdki(Mh=2N!L`MG(_ z_a%Mh)~s36BYoiFjFPm{($YI+w;#`9Q|S<#py1}vB*Dm;#4^eOhJnvttJ}LD8=N_J z?ww4-;xF&+?w-3jy|S{hzK5aj@TE(WvZV_CsLh{rgu~-Tx$o@Rvv2R*eRZyVechZJ zo40N?-BbI!?9PH*VEEp+b4O--;*9z8@5?W4ZEa1gt*yPo(0A<7qemMyZZyo>ZCK>X zECe(~Kv}`Dr-8+g2?wWzS^tz>-Jgm(|K`lMukRCXFMV{Rv#@FZ@wL(0+qQ1qTKHQ& z|E+z+hlCsVzCG?gaB*kx^K*?7JC{)yp@%m9Rrvqx*|QH154RUu?+IS+$9sK!yu5kc z@yY7`$6kJq*;7$yQT^@Bj>3rfcC}J*`)Vxn_CG(?EB*LO`Okd&6ZLBhtAJ^BhwQyO zcjid^_5S|;zWlxY_4aw~<-fn>8vYVjGu&1F{vJ^N%$YL<-{0LW{Js0fdj*GSgEb`r zb6d{)zQY?fY%s8?`jT;{@7n6{^=7r(`JY`?RW*n2{msqk zkH1vw#q0p3q(15PuOA;De|UDb`QvZZ>tc2q`PENfx^!t`Zf@?Kw#2%+e+fO;u3taS z`yfL{PmfPpTKe%eY31uC_)`Wn&D{9D=*^uwcj7-TdIJi|Pft!3CO^8pJ^%3Y^Yf4M z-UbP0l+XkK Dr(cF; literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png new file mode 100644 index 0000000000000000000000000000000000000000..06a507b745e77d3d69b36e5b16e6b8efeecb75d7 GIT binary patch literal 1741 zcmeAS@N?(olHy`uVBq!ia0y~yU{nTS4mO}jr9!$ckYX$ja(7}_cTVOdki(Mh=}lXgVsQ~rR%nu76g0tq z(e6^~IJfll(-&T3+^yRF@kQXxn>SWS@BQ`c&CQ}`*VgX(npa)BH%CW*y4&KwTW`zG z$D7Z$Uw{AQ|JS>B?)>!j>+90jci*3%H+60J`d8QD?e|ufzi_?jY&a`Aa^>xhpB}&C zFMsvt=dQ2s-nMc-eSItH?u)A*epFcgf8+|Z?CYo0$lWzN^JI?lb8|oaUH$#lwcYXW z@9Xcktq9ou&(hK;NK8$A?^ga4p+TCaOv;w;t=D;a?n=l^uz!o59qX;Knzi=&`qS69 zZrycp^}~-Ji?VXo+e7P z=+UFVoP7MunV$2$%toEWWY!l|f!|(TUA^>X&Nbdmy}i9th1LJq{oVZi{r&5~>wf+7|?uWerNIM~-{_0BJ?yZb}OE1j2yxf0!_Vsmd<1Wv7%71#j W@Mr6vmyQ9eAO=rYKbLh*2~7ZHxQ5aI literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png new file mode 100644 index 0000000000000000000000000000000000000000..26c177a91417b00897cbad101685eb05e32d615f GIT binary patch literal 1997 zcmeAS@N?(olHy`uVBq!ia0y~yU{nTS4mO}jr9!$ckYX$ja(7}_cTVOdki(Mh=y=bK1P z-Me@1u8FIzzn=Q}`T6U;=1VWXoU%H6{j}`s>!zN2^ytyUySvL@_cQq2UmLyM>-o>` zhZl<`7_E!h8FX)@@qr^Ym7kuRS6@7J>Qt+XTefc9YF`s)VsHQdce-#&#mCBf2?i@} zUwrQ7?d@H<*wfnDI<>ZTZ{E@~=g$2zbi13kz32GjT^F~^oH_IU{|En*EoA=18&+-E z6;=1^$KzdvckK85+chUTV#V!~dpRy1@?Ky4@qAKo@#Z{}%B0YJ|27PhQQ*2^<5Ls_cU-MvA75*D>O+k3LfF;P;hf#F=SHe5EO7EOX{Gr zjDKJ-f8n36xARZm+gn|lJ-_+x-A_MOuYYyThQIL4^Uh7e*Ft)3<^4Fwp7dqfXO~EY zX%dE|-|BxX^xM)&oEv5>;1<`r(#bMiKYpF?H!O>Ay%E_!xm=B~MS^uNEG zTEF*8^{r1RMT({`>v( z_P>;3keioajG9TeDG1D0Q2DeS{F4KN?er8_%z5kX-L;or7aCO=zq`LbUR}+xC+ft>j!YFd-6ttW zOoSGT6&WQ-bp~=J8A7#TVMRnz66D77zm@IhN1Dm$JKg56E z-~T3Wd-So#g?BS{Xc8{`=yKFJA$ru!_fR5__M< zi7Mw`CM}VNcg$I|Y@I;mpQqEGUVJ|P`r5hQ_W$4Kcec3I!w!;UKzUD2Pyc%F<0BgL zfA=&*P&E(HCihZk?0f;&oY|9#ZG>F-JP((L8;>;7!?JG*@L64>51-hyTzQ@T5kPe;inHjeY&e~j>OaA`GNx8WR-u|LWZIO z71cni^ZWVwk11TufM)_%FUZM?T>Rj*z)yvEih-Cw66Y^Q<+{^xN#lBVT-u@2`y{2dW?7XgByau_pRweV{f3*eiLV~Gbg0VtHQk=z zpuU9rlj;}SmB(XMw;O%?Y&^d|<)fWbBE!eC`f_p8cbC8K^U{n10^{o3{QQ2gygNG# ztA2cVc$00$+@s?9aaWWNywNGKntSo$#lX2s-@JJ<kq3`ufxF-o3kP#+JW-|5p9FdFa@&V=sRE z*l{k1t=Z9%J0#X8X!~v5zjgc91y$|6_gdj^*!lDO-hI3l7Q5%=tAma^H*F8U{^Q*K zz%94m?mG24UqoG9z0@#sZP@BL{~k%dPWmgFu+jbDhkw@D`@b$SDsp9Z&Eba|K%VxBjE~nx_d^eSdfL z>${3>m%pa<{F%WOR7c#naYI5@UjBOIsRRR!xqi#_)K=bpJFRYi ze7NS`xb-=CdDl$$Jx`DSUXi_e-O1fQF1mlc;uCK7e^1Q6FPC>6Gne7>-hFr7>M38p zel0303KE}uEos`mx-7Y`1Q+{^u&><(= ziz-P+?nw5!eRY}KAy|BTa-eZ1xzt6T*!^{T=gnAh|98rkO`C%J&ooJ#z3^z!{(sva zez%j1-BYn~-VE0N(le~@8E2^(W-X}y_a)f&%gf9lq)sMyE9kC)${y1)KsYV73lpOgJh&Ck2D zVs+)e9sA<`6}=9NeO&$ZRcK^X)GA;C+HC*)$&)2|YHP1%O}%{i@~$Hpt7eHT{>E>A z{he0KRtCXF7~nT;+IA91qD2*TTBN!|4ppC`8qt#D%}3uV1GB|NQY9!{!YY zFE1^vVgXitv(59bDIdtm$M5+J) literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png new file mode 100644 index 0000000000000000000000000000000000000000..3688bbe0aa166897f2553cbf88c130bb53288ec5 GIT binary patch literal 1798 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 J1&Dpb{{b;Q1N#5~ literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockN.png new file mode 100644 index 0000000000000000000000000000000000000000..eca516d8938f1d4f31e423da393b6d69a73ab70f GIT binary patch literal 6949 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 X%6L?xbAT)O*Or)r-FCQy<{S5aB!C2j literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockS.png new file mode 100644 index 0000000000000000000000000000000000000000..a207de1c4589538f56027afb90288d895cfd2cec GIT binary patch literal 6833 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 ON!VEKVdT->qy7(m-WbyW literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpE.png new file mode 100644 index 0000000000000000000000000000000000000000..660506aa735d2d35aae36686c7196c6017934db4 GIT binary patch literal 3233 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpN.png new file mode 100644 index 0000000000000000000000000000000000000000..ae3ca519d958e04e9b84a866ef7e43c33149d4a2 GIT binary patch literal 3477 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 AzW@LL literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpW.png new file mode 100644 index 0000000000000000000000000000000000000000..d67801d76ee66efc218e97ad4b0712d6d4b6808e GIT binary patch literal 3219 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillE.png new file mode 100644 index 0000000000000000000000000000000000000000..1f24029e58bad20ffd03f715da94e33017fae935 GIT binary patch literal 3740 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 s4rgBFv2KGN>1{Ad>3)6z0oi-WBw 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 R@uwGX-qyjU@~qFj{{p=YwiEyW literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillW.png new file mode 100644 index 0000000000000000000000000000000000000000..e931a755586bc26b7aeee16fa650a4b8301f8816 GIT binary patch literal 3733 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 ax1Di$xPFAIKLy^p<+7fMZt=z25C0dFf7cZN literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithMercury.png new file mode 100644 index 0000000000000000000000000000000000000000..6693e2d7b9eef029726fb6fac21c84143cdfbb0d GIT binary patch literal 37221 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/MercOrBlocked.png b/src/main/resources/edu/rpi/legup/images/thermometer/MercOrBlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..66c994de0b755a49235ee1b47b9b62ac18f116bc GIT binary patch literal 27343 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 F{{-1EpcnuE literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/MercuryInBody.png b/src/main/resources/edu/rpi/legup/images/thermometer/MercuryInBody.png new file mode 100644 index 0000000000000000000000000000000000000000..06cb87e42ae44a67520a5ba0f5dc9f730eea03c9 GIT binary patch literal 34628 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/MinimumFill.png b/src/main/resources/edu/rpi/legup/images/thermometer/MinimumFill.png new file mode 100644 index 0000000000000000000000000000000000000000..28719cf53fd7e1e3d5d777761ffd316cac19af62 GIT binary patch literal 38996 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 CntEyg literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/NotEnoughMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/NotEnoughMercury.png new file mode 100644 index 0000000000000000000000000000000000000000..d2277e8ef315270bff0ed47e8431eb2957e9cfca GIT binary patch literal 49179 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/PriorIsFilled.png b/src/main/resources/edu/rpi/legup/images/thermometer/PriorIsFilled.png new file mode 100644 index 0000000000000000000000000000000000000000..9bda3a60be87b7b012c824e878c39acc17b998df GIT binary patch literal 26866 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

    |wbU{@C4Q{PDp}4c$!Q1eeIa0FFl9jLU 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 Kc)?MJ@V@~+6wcxR literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/RestIsEmpty.png b/src/main/resources/edu/rpi/legup/images/thermometer/RestIsEmpty.png new file mode 100644 index 0000000000000000000000000000000000000000..91374e0623b8c24293b8d3013b8dc92f379933dc GIT binary patch literal 35958 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/SatisfyMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/SatisfyMercury.png new file mode 100644 index 0000000000000000000000000000000000000000..f450a205e2d196e47cdb7a489dfecb4b30ff9083 GIT binary patch literal 31667 zcmeFZXHZmYw=Rk(x&*TzOO&7@NLCOegNo#wgMdl~$uv2dP(VO(Msm(MV?csrXizee zX>!ioF}qpc{`NlS?t4zv{c wQ5x_y62p48t)j-c*gV253v&@$m4@iirxz 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