Skip to content

Commit

Permalink
Merge branch 'dev' into thermometer
Browse files Browse the repository at this point in the history
  • Loading branch information
charlestian23 authored May 5, 2024
2 parents 2bf9790 + d68859b commit 1e3821e
Show file tree
Hide file tree
Showing 35 changed files with 1,616 additions and 8 deletions.
11 changes: 11 additions & 0 deletions puzzles files/minesweeper/5x5 Minesweeper Easy/123456
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Legup version="2.0.0">
<puzzle name="Minesweeper">
<board height="5" width="5">
<cells>

</cells>
</board>
</puzzle>
<solved isSolved="false" lastSaved="--"/>
</Legup>
64 changes: 64 additions & 0 deletions src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<MinesweeperTileData> {

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;
}
}
Original file line number Diff line number Diff line change
@@ -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<MinesweeperTileData> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
Loading

0 comments on commit 1e3821e

Please sign in to comment.