Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MinesweeperUtilities Improvements #749

Merged
merged 3 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ public MinesweeperBoard(int width, int height) {
public MinesweeperBoard(int size) {
super(size);
}

@Override
public MinesweeperCell getCell(int x, int y) {
return (MinesweeperCell) super.getCell(x, y);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ public record MinesweeperTileData(MinesweeperTileType type, int data) {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,78 @@
package edu.rpi.legup.puzzle.minesweeper;

import java.awt.*;
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<MinesweeperCell> 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<MinesweeperTileData> 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);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
package edu.rpi.legup.puzzle.minesweeper.rules;

public class BombOrFilledCaseRule {
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 java.util.List;

public class BombOrFilledCaseRule extends CaseRule {

public BombOrFilledCaseRule() {
super("MINE-CASE-0000", "Bomb Or Filled",
"A cell can either be a bomb or filled.\n",
"");
}

@Override
public CaseBoard getCaseBoard(Board board) {
return null;
}

@Override
public List<Board> getCases(Board board, PuzzleElement puzzleElement) {
return null;
}

@Override
public String checkRuleRaw(TreeTransition transition) {
return null;
}

@Override
public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) {
return null;
}
}
9 changes: 7 additions & 2 deletions src/test/java/legup/TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -91,6 +92,10 @@ public static void main(String[] args) {
printTestResults(result35);
Result result36 = JUnitCore.runClasses(TentOrGrassCaseRuleTest.class);
printTestResults(result36);

// Minesweeper
Result result37 = JUnitCore.runClasses(MinesweeperUtilitiesTest.class);
printTestResults(result37);
}

private static void printTestResults(Result result) {
Expand Down
75 changes: 75 additions & 0 deletions src/test/java/puzzles/minesweeper/MinesweeperUtilitiesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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 legup.MockGameBoardFacade;
import legup.TestUtilities;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.stream.Stream;

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<MinesweeperCell> 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<MinesweeperCell> 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<MinesweeperCell> cells = MinesweeperUtilities.getSurroundingCells(board, cell);

final long count = cells.count();
Assert.assertEquals(count, 5);
}


}
11 changes: 11 additions & 0 deletions src/test/resources/puzzles/minesweeper/utilities/3x3test
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="3" width="3">
<cells>

</cells>
</board>
</puzzle>
<solved isSolved="false" lastSaved="--"/>
</Legup>
Loading