RosettaCodeData/Task/Minesweeper-game/Java/minesweeper-game.java

633 lines
20 KiB
Java

--------------------------------- START of Main.java ---------------------------------
//By xykmz. Enjoy!
import java.util.Scanner;
public class Main {
static int intErrorTrap (int x, int y){
int max, min;
if (x < y) {
min = x;
max = y;
} else {
min = y;
max = x;
}
int input;
boolean loopEnd;
do {
System.out.println("Please enter an integer between " + min + " to " + max + ".");
Scanner userInput = new Scanner(System.in); //Player inputs a guess
try
{
input = userInput.nextInt();
if(input > max) //Input is too high
{
loopEnd = false;
System.out.println("Input is invalid.");
return -1;
}
else if(input < min) //Input is too low
{
loopEnd = false;
System.out.println("Input is invalid.");
return -1;
}
else //Input is within acceptable range
{
loopEnd = true;
System.out.println(input + " is a valid input.");
return input;
}
}
catch (Exception e)
{
loopEnd = false;
userInput.next();
System.out.println("Input is invalid.");
return 0;
}
} while (loopEnd == false);
}
public static void main(String[] args) {
System.out.println ("Enter width.");
int x = intErrorTrap (0,60);
System.out.println ("Enter height.");
int y = intErrorTrap (0,30);
System.out.println ("Enter difficulty.");
int d = intErrorTrap (0,100);
new Minesweeper(x, y, d); //Suggested: (60, 30, 15)
}
}
//--------------------------------- END of Main.java ---------------------------------
//--------------------------------- START of Cell.java ---------------------------------
public class Cell{ //This entire class is quite self-explanatory. All we're really doing is setting booleans.
private boolean isMine, isFlagged, isCovered;
private int number;
public Cell(){
isMine = false;
isFlagged = false;
isCovered = true;
number = 0;
}
public void flag(){
isFlagged = true;
}
public void unflag(){
isFlagged = false;
}
public void setMine(){
isMine = true;
}
public boolean isMine(){
return isMine;
}
public void reveal(){
isCovered = false;
}
public void setNumber(int i){ //Set the number of the cell
number = i;
}
public int getNumber(){ //Request the program for the number of the cell
return number;
}
public boolean isFlagged(){
return isFlagged;
}
public boolean isCovered(){
return isCovered;
}
}
//--------------------------------- END of Cell.java ---------------------------------
//--------------------------------- START of Board.java ---------------------------------
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Board extends JPanel{
private static final long serialVersionUID = 1L; //Guarantees consistent serialVersionUID value across different java compiler implementations,
//Auto-generated value might screw things up
private Minesweeper mine;
private Cell[][] cells;
public void paintComponent(Graphics g){
cells = mine.getCells();
for (int i = 0; i < mine.getx(); i++){
for (int j = 0; j < mine.gety(); j++){
Cell current = cells[i][j];
//Flagged cells
if (current.isFlagged()){
if (current.isMine() && mine.isFinished()){
g.setColor(Color.ORANGE); //Let's the player know which mines they got right when the game is finished.
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
g.drawLine(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.drawLine(i * 20, j * 20 + 20, i * 20 + 20, j * 20);
}
else if (mine.isFinished()){ //Shows cells that the player incorrectly identified as mines.
g.setColor(Color.YELLOW);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
else{
g.setColor(Color.GREEN); //Flagging a mine.
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
}
//Unflagged cells
else if (current.isCovered()){ //Covered cells
g.setColor(Color.DARK_GRAY);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
else if (current.isMine()){ //Incorrect cells are shown when the game is over.=
g.setColor(Color.RED);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
g.drawLine(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.drawLine(i * 20, j * 20 + 20, i * 20 + 20, j * 20);
}
else{ //Empty cells or numbered cells
g.setColor(Color.LIGHT_GRAY);
g.fillRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
g.setColor(Color.BLACK);
}
//The following part is very self explanatory - drawing the numbers.
//Not very interesting work.
//Not a fun time.
//Rating: 0/10. Would not recommend.
if (!current.isCovered()){
if (current.getNumber() == 1){
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
}
else if (current.getNumber() == 2){
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 7, j * 20 + 11, i * 20 + 7, j * 20 + 15); //5
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 3){
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 4){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
}
else if (current.getNumber() == 5){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 6){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 7, j * 20 + 11, i * 20 + 7, j * 20 + 15); //5
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
else if (current.getNumber() == 7){
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
}
else if (current.getNumber() == 8){
g.drawLine(i * 20 + 7, j * 20 + 5, i * 20 + 7, j * 20 + 9); //1
g.drawLine(i * 20 + 8, j * 20 + 4, i * 20 + 12, j * 20 + 4); //2
g.drawLine(i * 20 + 13, j * 20 + 5, i * 20 + 13, j * 20 + 9); //3
g.drawLine(i * 20 + 8, j * 20 + 10, i * 20 + 12, j * 20 + 10); //4
g.drawLine(i * 20 + 7, j * 20 + 11, i * 20 + 7, j * 20 + 15); //5
g.drawLine(i * 20 + 13, j * 20 + 11, i * 20 + 13, j * 20 + 15); //6
g.drawLine(i * 20 + 8, j * 20 + 16, i * 20 + 12, j * 20 + 16); //7
}
}
g.setColor(Color.BLACK);
g.drawRect(i * 20, j * 20, i * 20 + 20, j * 20 + 20);
}
}
}
public Board(Minesweeper m){ //Creating a new game so we can draw a board for it
mine = m;
cells = mine.getCells();
addMouseListener(new Actions(mine));
setPreferredSize(new Dimension(mine.getx() * 20, mine.gety() * 20));
}
}
//--------------------------------- END of Board.java ---------------------------------
//--------------------------------- START of Actions.java ---------------------------------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Actions implements ActionListener, MouseListener{
private Minesweeper mine;
//These following four are not important. We simply tell the machine to pay no mind when mouse is pressed, released, etc.
//If these weren't here the computer would freak out and panic over what to do because no instructions were given.
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public void mousePressed(MouseEvent e){
}
public void mouseReleased(MouseEvent e){
}
//Where the fun begins
public Actions(Minesweeper m){
mine = m;
}
//Any time an action is performed, redraw the board and keep it up to date.
public void actionPerformed(ActionEvent e){
mine.reset();
mine.refresh();
}
//Mouse clicky clicky
public void mouseClicked(MouseEvent e){
//Left click - opens mine
if (e.getButton() == 1)
{
int x = e.getX() / 20;
int y = e.getY() / 20;
mine.select(x, y);
}
//Right click - marks mine
if (e.getButton() == 3)
{
int x = e.getX() / 20;
int y = e.getY() / 20;
mine.mark(x, y);
}
mine.refresh(); //Gotta keep it fresh
}
}
//--------------------------------- END of Actions.java ---------------------------------
//--------------------------------- START of Minesweeper.java ---------------------------------
import java.awt.BorderLayout;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class Minesweeper extends JFrame
{
private static final long serialVersionUID = 1L;
private int width, height;
private int difficulty;
private Cell[][] cells;
private Board board;
private JButton reset;
private boolean finished;
public void select(int x, int y){ //Select a mine on the board
if (cells[x][y].isFlagged()) //Is the mine flagged?
return;
cells[x][y].reveal(); //Reveal the cell
resetMarks(); //Reset marks and redraw board
refresh();
if (cells[x][y].isMine()) //If a mine is revealed, you lose.
{
lose();
}
else if (won()) //If the game has been won, you win. Hahahahahaha.
{
win();
}
}
private void setNumbers(){ //For each cell, count the amount of mines in surrounding squares.
//Because the board is modeled as a 2D array, this is relatively easy - simply check the surrounding addresses for mines.
//If there's a mine, add to the count.
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
int count = 0;
if (i > 0 && j > 0 && cells[i - 1][j - 1].isMine())
count++;
if (j > 0 && cells[i][j - 1].isMine())
count++;
if (i < width - 1 && j > 0 && cells[i + 1][j - 1].isMine())
count++;
if (i > 0 && cells[i - 1][j].isMine())
count++;
if (i < width - 1 && cells[i + 1][j].isMine())
count++;
if (i > 0 && j < height - 1 && cells[i - 1][j + 1].isMine())
count++;
if (j < height - 1 && cells[i] [j + 1].isMine())
count++;
if (i < width - 1 && j < height - 1 && cells[i + 1][j + 1].isMine())
count++;
cells[i][j].setNumber(count);
if (cells[i][j].isMine())
cells[i][j].setNumber(-1);
if (cells[i][j].getNumber() == 0)
cells[i][j].reveal();
}
}
for (int i = 0; i < width; i++){ //This is for the beginning of the game.
//If the 8 cells around certain cell have no mines beside them (hence getNumber() = 0) beside them, this cell is empty.
for (int j = 0; j < height; j++){
if (i > 0 && j > 0 && cells[i - 1][j - 1].getNumber() == 0)
cells[i][j].reveal();
if (j > 0 && cells[i][j - 1].getNumber() == 0)
cells[i][j].reveal();
if (i < width - 1 && j > 0 && cells[i + 1][j - 1].getNumber() == 0)
cells[i][j].reveal();
if (i > 0 && cells[i - 1][j].getNumber() == 0)
cells[i][j].reveal();
if (i < width - 1 && cells[i + 1][j].getNumber() == 0)
cells[i][j].reveal();
if (i > 0 && j < height - 1 && cells[i - 1][j + 1].getNumber() == 0)
cells[i][j].reveal();
if (j < height - 1 && cells[i][j + 1].getNumber() == 0)
cells[i][j].reveal();
if (i < width - 1 && j < height - 1 && cells[i + 1][j + 1].getNumber() == 0)
cells[i][j].reveal();
}
}
}
public void mark(int x, int y){ //When a player wants to flag/unflag a cell
if (cells[x][y].isFlagged()) //If the cell is already flagged, unflag it.
cells[x][y].unflag();
else if (cells[x][y].isCovered()) //If the cell has nothing on it and is covered, flag it.
cells[x][y].flag();
resetMarks();
}
private void resetMarks(){ //If a cell is not covered, then it cannot be flagged.
//Every time a player does something, this should be called to redraw the board.
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
if (!cells[i][j].isCovered())
cells[i][j].unflag();
}
}
}
public void reset(){ //Reset the positions of the mines on the board
//If randomly generated number from 0 to 100 is less than the chosen difficulty, a mine is placed down.
//This will somewhat accurately reflect the mine percentage that the user requested - the more cells, the more accurate.
Random random = new Random();
finished = false;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
Cell c = new Cell();
cells[i][j] = c;
int r = random.nextInt(100);
if (r < difficulty)
{
cells[i][j].setMine(); //Put down a mine.
}
}
}
setNumbers(); //Set the numbers after mines have been put down.
}
public int getx() {
return width;
}
public int gety() {
return height;
}
public Cell[][] getCells() {
return cells;
}
public void refresh(){ //Refresh the drawing of the board
board.repaint();
}
private void win(){ //Winning a game
finished = true;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
cells[i][j].reveal();//Reveal all cells
if (!cells[i][j].isMine())
cells[i][j].unflag();
}
}
refresh();
JOptionPane.showMessageDialog(null, "Congratulations! You won!"); //Tell the user they won. They probably deserve to know.
reset();
}
private void lose(){ //Losing a game...basically the same as above.
finished = true;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (!cells[i][j].isCovered())
cells[i][j].unflag();
cells[i][j].reveal();
}
}
refresh();
JOptionPane.showMessageDialog(null, "GAME OVER."); //Dialogue window letting the user know that they lost.
reset();
}
private boolean won(){ //Check if the game has been won
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
if (cells[i][j].isCovered() && !cells[i][j].isMine())
{
return false;
}
}
}
return true;
}
public boolean isFinished(){ //Extremely self-explanatory.
return finished;
}
public Minesweeper(int x, int y, int d){
width = x; //Width of the board
height = y; //Height of the board
difficulty = d; //Percentage of mines in the board
cells = new Cell[width][height];
reset(); //Set mines on the board
board = new Board(this); //Create new board
reset = new JButton("Reset"); //Reset button
add(board, BorderLayout.CENTER); //Put board in the center
add(reset, BorderLayout.SOUTH); //IT'S A BUTTON! AND IT WORKS! VERY COOL
reset.addActionListener(new Actions(this)); //ActionListener to watch for mouse actions
//GUI window settings
setTitle("Minesweeper");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
pack();
setVisible(true);
}
}
//--------------------------------- END of Minesweeper.java ---------------------------------