import javafx.geometry.Pos; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; public class Cage { private ArrayList<Cell> cells = new ArrayList<>(); private HashMap<Integer, Cell> cellMap = new HashMap<>(); private int target; private char operator; private GameManager manager; public Text idOverlay; Cage(GameManager manager, CageConfig cageConfig) { this.manager = manager; this.target = cageConfig.target; this.operator = cageConfig.operator; // adds each cell to it's list of cages from the config for (int cellID : cageConfig.cellIDs) { this.cells.add(new Cell(manager, cellID, this)); } // puts them into a map so they can be found by id for (Cell cell : cells) { cellMap.put(cell.getCellID(), cell); } // operator s stands for singleton // constructs the target/operator label for the cage if (operator != 's') { idOverlay = new Text(Integer.toString(target) + operator); } else { idOverlay = new Text(Integer.toString(target)); } idOverlay.setFont(Font.font("Courier New", FontWeight.BOLD, 15)); StackPane.setAlignment(idOverlay, Pos.TOP_LEFT); Cell left = getTopLeft(); left.getChildren().add(idOverlay); } // sets borders to be thinner between cells in the same cage by iterating over all cells twice void setBorders() { for (Cell cell : cells) { double top = 1.5, bottom = 1.5, left = 1.5, right = 1.5; for (Cell adjCell : cells) { if (adjCell.getCellID() == cell.getCellID() + 1) { right = 0.1; } else if (adjCell.getCellID() == cell.getCellID() - 1) { left = 0.1; } else if (adjCell.getCellID() == (cell.getCellID() + this.manager.getN())) { bottom = 0.1; } else if (adjCell.getCellID() == (cell.getCellID() - this.manager.getN())) { top = 0.1; } } BorderStroke borderStroke = new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, new CornerRadii(0.1), new BorderWidths(top, right, bottom, left)); cell.setBorder(new Border(borderStroke)); } } // gets the top left cell in a cage to put the label on Cell getTopLeft() { Cell topLeft = null; int highestID = Integer.MAX_VALUE; for (Cell cell : cells) { if (cell.getCellID() < highestID) { highestID = cell.getCellID(); topLeft = cell; } } return topLeft; } // checks if itself is filled correctly // maths checks return max value because it was a number that is unlikely to appear otherwise given its hard capped at 8*8 // because i wanted to use the same function for mistake and victory checking but mistake checking // needed to ignore empty cells public boolean isFilledCorrectly(boolean victoryCheck) { if (this.operator == 'x' || this.operator == '*') { return multiplyCells(victoryCheck).equals(this.target) || (!victoryCheck && multiplyCells(false) == Integer.MAX_VALUE); } else if (this.operator == '+') { return addCells(victoryCheck).equals(this.target) || (!victoryCheck && addCells(false) == Integer.MAX_VALUE); } else if (this.operator == '-') { return subtractCells(victoryCheck).equals(this.target) || (!victoryCheck && subtractCells(false) == Integer.MAX_VALUE); } else if (this.operator == '/' || this.operator == '÷') { Double total = divideCells(victoryCheck); if (total % 1 == 0) { return total.intValue() == this.target || (!victoryCheck && divideCells(false) == Double.MAX_VALUE); } return false; } else { return cells.get(0).getCurrentValue().equals(this.target); } } public Integer subtractCells(boolean victoryCheck) { ArrayList<Integer> cellValues = new ArrayList<>(); for (int i = 0; i < cells.size(); i++) { if (cells.get(i).getCurrentValue() == 0 && !victoryCheck) { return Integer.MAX_VALUE; } cellValues.add(cells.get(i).getCurrentValue()); } Collections.sort(cellValues); Collections.reverse(cellValues); int total = cellValues.get(0); for (int i : cellValues.subList(1, cellValues.size())) { total -= i; } return total; } public Integer multiplyCells(boolean victoryCheck) { Integer flag = 1; for (Cell cell : cells) { if (cell.getCurrentValue() == 0 && !victoryCheck) { return Integer.MAX_VALUE; } flag = flag * cell.getCurrentValue(); } return flag; } public Integer addCells(boolean victoryCheck) { Integer flag = 0; for (Cell cell : cells) { if (cell.getCurrentValue() == 0 && !victoryCheck) { return Integer.MAX_VALUE; } flag += cell.getCurrentValue(); } return flag; } public Double divideCells(boolean victoryCheck) { ArrayList<Integer> cellValues = new ArrayList<>(); for (int i = 0; i < cells.size(); i++) { if (cells.get(i).getCurrentValue() == 0 && !victoryCheck) { return Double.MAX_VALUE; } cellValues.add(cells.get(i).getCurrentValue()); } Collections.sort(cellValues); Collections.reverse(cellValues); double total = cellValues.get(0); for (int i : cellValues.subList(1, cellValues.size())) { total = total / i; } return total; } public ArrayList<Cell> getCells() { return this.cells; } public HashMap<Integer, Cell> getCellMap() { return this.cellMap; } public char getOperator() { return this.operator; } public int getTarget() { return this.target; } }