From 68fa815ef8682d2a30a285fc17838e11eb40b309 Mon Sep 17 00:00:00 2001 From: ZevCe <157339070+ZevCe@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:37:21 -0400 Subject: [PATCH] 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> --- .../puzzle/thermometer/ThermometerBoard.java | 22 ++--- .../puzzle/thermometer/ThermometerCell.java | 60 +++++++----- .../thermometer/ThermometerController.java | 20 ++-- .../thermometer/ThermometerExporter.java | 4 + .../thermometer/ThermometerImporter.java | 98 +++++++++++++++++++ .../Vial.java => ThermometerVial.java} | 32 +++--- .../thermometer/ThermometerVialFactory.java | 71 ++++++++++++++ ...DiscontinuousMercuryContradictionRule.java | 12 +-- 8 files changed, 243 insertions(+), 76 deletions(-) create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java rename src/main/java/edu/rpi/legup/puzzle/thermometer/{elements/Vial.java => ThermometerVial.java} (79%) create mode 100644 src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVialFactory.java diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java index 01a6ac918..7ced15f52 100644 --- a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java @@ -1,17 +1,15 @@ package edu.rpi.legup.puzzle.thermometer; import edu.rpi.legup.model.gameboard.GridBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.puzzle.thermometer.elements.Vial; import java.util.ArrayList; -import static edu.rpi.legup.puzzle.thermometer.elements.Vial.verifyVial; +import static edu.rpi.legup.puzzle.thermometer.ThermometerVial.verifyVial; public class ThermometerBoard extends GridBoard{ //an array containing all of our vials on the board - private ArrayList vials; + private ArrayList thermometerVials; //representations of the number requirements along rows and columns of the board private ArrayList colNumbers; @@ -31,7 +29,7 @@ public ThermometerBoard(int width, int height){ rowNumbers.add(0); } - vials = new ArrayList<>(); + thermometerVials = new ArrayList<>(); } public ThermometerBoard(int size){ super(size, size); @@ -44,19 +42,15 @@ public ThermometerBoard(int size){ rowNumbers.add(0); } - vials = new ArrayList<>(); + thermometerVials = new ArrayList<>(); } //setters and accessors for our array of vials - public boolean addVial(ThermometerCell headCell, ThermometerCell tipCell) { - if(verifyVial(headCell, tipCell, this)) { - vials.add(new Vial(headCell, tipCell, this)); - return true; - } - return false; + public void addVial(ThermometerVial v) { + thermometerVials.add(v); } - public ArrayList getVials() { - return vials; + public ArrayList getVials() { + return thermometerVials; } diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java index 771a08b80..6792bb53a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java @@ -5,41 +5,51 @@ import java.awt.Point; public class ThermometerCell extends GridCell { - public ThermometerCell(int valueInt, Point location) { - super(valueInt, location); + private ThermometerType type; + private ThermometerFill fill; + public ThermometerCell(Point location) { + //since we do not use get/set data value int can be any value + super(0, location); + type = ThermometerType.UNKNOWN; + fill = ThermometerFill.UNKNOWN; } + + + + //Note: setdata does not work for our purposes + public void setType(ThermometerType t){ + type = t; + } + + public ThermometerType getType() { - switch (data) { - case 0: - return ThermometerType.UNKNOWN; - case 1: - return ThermometerType.HEAD; - case 2: - return ThermometerType.SHAFT; - case 3: - return ThermometerType.TIP; - } - return null; + return switch (type.ordinal()) { + case 0 -> ThermometerType.UNKNOWN; + case 1 -> ThermometerType.HEAD; + case 2 -> ThermometerType.SHAFT; + case 3 -> ThermometerType.TIP; + default -> null; + }; + } + + public void setFill(ThermometerFill f){ + fill = f; } public ThermometerFill getFill() { - switch (data) { - case 0: - return ThermometerFill.UNKNOWN; - case 1: - return ThermometerFill.FILLED; - case 2: - return ThermometerFill.EMPTY; - case 3: - return ThermometerFill.BLOCKED; - } - return null; + return switch (fill.ordinal()) { + case 0 -> ThermometerFill.UNKNOWN; + case 1 -> ThermometerFill.FILLED; + case 2 -> ThermometerFill.EMPTY; + case 3 -> ThermometerFill.BLOCKED; + default -> null; + }; } @Override public ThermometerCell copy() { - ThermometerCell copy = new ThermometerCell(data, (Point) location.clone()); + ThermometerCell copy = new ThermometerCell((Point) location.clone()); copy.setIndex(index); copy.setModifiable(isModifiable); copy.setGiven(isGiven); diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java index 253cf8706..8d12f00ea 100644 --- a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java @@ -15,26 +15,26 @@ 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() == ThermometerFill.EMPTY.ordinal()) { - data.setData(ThermometerFill.FILLED.ordinal()); + if (cell.getFill() == ThermometerFill.EMPTY) { + cell.setFill(ThermometerFill.FILLED); } - else if (cell.getData() == ThermometerFill.FILLED.ordinal()) { - data.setData(ThermometerFill.BLOCKED.ordinal()); + else if (cell.getFill() == ThermometerFill.FILLED) { + cell.setFill(ThermometerFill.BLOCKED); } else { - data.setData(ThermometerFill.EMPTY.ordinal()); + cell.setFill(ThermometerFill.EMPTY); } } } else if (e.getButton() == MouseEvent.BUTTON2) { - if (cell.getData() == ThermometerFill.EMPTY.ordinal()) { - data.setData(ThermometerFill.BLOCKED.ordinal()); + if (cell.getFill() == ThermometerFill.EMPTY) { + cell.setFill(ThermometerFill.BLOCKED); } - else if (cell.getData() == ThermometerFill.BLOCKED.ordinal()) { - data.setData(ThermometerFill.FILLED.ordinal()); + else if (cell.getFill() == ThermometerFill.BLOCKED) { + cell.setFill(ThermometerFill.FILLED); } else { - data.setData(ThermometerFill.EMPTY.ordinal()); + cell.setFill(ThermometerFill.EMPTY); } } } 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..4a6764702 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java @@ -0,0 +1,4 @@ +package edu.rpi.legup.puzzle.thermometer; + +public class ThermometerExporter { +} 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..5af4f478b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java @@ -0,0 +1,98 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.PuzzleImporter; +import edu.rpi.legup.puzzle.thermometer.ThermometerVialFactory; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.awt.*; + +public class ThermometerImporter extends PuzzleImporter { + 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) { + + } + + @Override + public void initializeBoard(Node node) throws InvalidFileFormatException { + try { + if (!node.getNodeName().equalsIgnoreCase("board")) { + throw new InvalidFileFormatException("thermometer Importer: cannot find board puzzleElement"); + } + 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("cells").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("cell"); + + ThermometerBoard thermometerBoard = null; + if (!boardElement.getAttribute("size").isEmpty()) { + int size = Integer.parseInt(boardElement.getAttribute("size")); + thermometerBoard = new ThermometerBoard(size); + if (boardElement.getElementsByTagName("rowNumbers").getLength() != size) { + throw new InvalidFileFormatException("thermometer Importer: no rowNumbers found for board"); + } + if (boardElement.getElementsByTagName("colNumbers").getLength() != size) { + throw new InvalidFileFormatException("thermometer Importer: no colNumbers found for board"); + } + } else if (!boardElement.getAttribute("width").isEmpty() && !boardElement.getAttribute("height").isEmpty()) { + int width = Integer.parseInt(boardElement.getAttribute("width")); + int height = Integer.parseInt(boardElement.getAttribute("height")); + if (boardElement.getElementsByTagName("colNumbers").getLength() != width) { + throw new InvalidFileFormatException("thermometer Importer: no colNumbers found for board"); + } + if (boardElement.getElementsByTagName("rowNumbers").getLength() != height) { + throw new InvalidFileFormatException("thermometer Importer: no rowNumbers found for board"); + } + //TODO: potentially have to deal with size issues and non interactable cells + thermometerBoard = new ThermometerBoard(width, height); + } + + if (thermometerBoard == null) { + throw new InvalidFileFormatException("thermometer Importer: invalid board dimensions"); + } + + int width = thermometerBoard.getWidth(); + int height = thermometerBoard.getHeight(); + + for (int i = 0; i < elementDataList.getLength(); i++) { + ThermometerVialFactory.importThermometerVial(elementDataList.item(i), thermometerBoard); + } + + //verifying all vials were used + 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: invalid puzzle, unused tiles"); + } + } + } + + 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 { + + } +} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/Vial.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java similarity index 79% rename from src/main/java/edu/rpi/legup/puzzle/thermometer/elements/Vial.java rename to src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java index 93b7fe491..19f6c4ce2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/Vial.java +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java @@ -1,33 +1,21 @@ -package edu.rpi.legup.puzzle.thermometer.elements; - -import edu.rpi.legup.puzzle.thermometer.ThermometerCell; -import edu.rpi.legup.puzzle.thermometer.ThermometerBoard; -import edu.rpi.legup.puzzle.thermometer.ThermometerFill; -import edu.rpi.legup.puzzle.thermometer.ThermometerType; -import edu.rpi.legup.model.elements.PlaceableElement; +package edu.rpi.legup.puzzle.thermometer; import java.awt.*; import java.util.ArrayList; import static java.lang.Math.*; -public class Vial { +public class ThermometerVial { private ArrayList cells; - public Vial(ThermometerCell headCell, ThermometerCell tipCell, ThermometerBoard board) { + public ThermometerVial(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { cells = new ArrayList<>(); - fillData(headCell, tipCell, board); + 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(ThermometerCell headCell, ThermometerCell tipCell, ThermometerBoard board) { - //shorthand for useful variables - int headX = (int) headCell.getLocation().getX(); - int headY = (int) headCell.getLocation().getY(); - int tipX = (int) tipCell.getLocation().getX(); - int tipY = (int) tipCell.getLocation().getY(); - + 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 @@ -65,9 +53,11 @@ else if (headX < tipX){ //helper function for adding a single cell private void addCell(int x, int y, ThermometerType t, ThermometerBoard board){ - - //this very likely is not updating the data in the way we want it to - board.getCell(x, y).setData(t.ordinal()); + ThermometerCell cell = new ThermometerCell(new Point(x, y)); + cell.setIndex(y * board.getHeight() + x); + cell.setModifiable(true); + board.setCell(x, y, cell); + board.getCell(x, y).setType(t); cells.add(board.getCell(x, y)); } @@ -114,7 +104,7 @@ public static boolean verifyVial(ThermometerCell headCell, ThermometerCell tipCe //verifying that every cell along path is currently unclaimed for (int i = top; i < bottom; i++) { - if(board.getCell(headX, i).getType() != ThermometerType.UNKNOWN) return false; + if(board.getCell(headX, i) != null || board.getCell(headX, i).getType() != ThermometerType.UNKNOWN) return false; } } else if (headY == tipY) { diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVialFactory.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVialFactory.java new file mode 100644 index 000000000..d1bef94d8 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVialFactory.java @@ -0,0 +1,71 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.ElementFactory; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import java.awt.*; + +import static edu.rpi.legup.puzzle.thermometer.ThermometerVial.verifyVial; +import static java.lang.Math.max; +import static java.lang.Math.min; + +public class ThermometerVialFactory extends ElementFactory{ + //We do not import single cells at a time, instead we are + //importing/exporting entire vials hence these functions + //remain empty + @Override + public PuzzleElement importCell(Node node, Board board) throws InvalidFileFormatException { + return null; + } + @Override + public Element exportCell(Document document, PuzzleElement puzzleElement) { + return null; + } + + public static void importThermometerVial(Node node, ThermometerBoard board) throws InvalidFileFormatException{ + 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()); + + + if(verifyVial(headX, headY, tipX, tipY, board)) { + board.addVial(new ThermometerVial(headX, headY, tipX, tipY, board)); + } + throw new InvalidFileFormatException("thermometer Importer: overlapping vials"); + } + + private static 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 + int top = min(headX, tipX); + int bottom = max(headX, tipX); + + //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{ + //thermometer does not line up along a single axis + return false; + } + return true; + } +} 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 index a24e2b403..8a8ef00f6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java @@ -5,7 +5,7 @@ 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.elements.Vial; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; import java.util.ArrayList; @@ -30,15 +30,15 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { ThermometerCell cell = (ThermometerCell) thermometerBoard.getPuzzleElement(puzzleElement); - ArrayList vials = thermometerBoard.getVials(); + ArrayList thermometerVials = thermometerBoard.getVials(); //finding out which vial contains the specified cell - for (int i = 0; i < vials.size(); i++) { - Vial vial = vials.get(i); + 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(vial.containsCell(cell)){ - if(vial.continuousFlow()){ + if(thermometerVial.containsCell(cell)){ + if(thermometerVial.continuousFlow()){ return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } else{