I wrote a basic Minesweeper game for practice. It consist 3 classes:
- Main - only as launcher
- Cell - controling behavior of single cell
- Board - coordinating behavior of cells.
The hardest part for me, was a determining the value of single cells (how many mines is around it), and firing a chain reaction, if some cell was empty(value = 0) - it check closest cells, if they have value, it just reveal it, if they are empty it repeats process on them. So
I wonder if my solution is reasonable (it works, but it look scary), and I would be great, if someone would review it.
These are two methods in Board Class: setCellValues()
and scanForEmptyCells()
. However I will be gratefull for any commants and advices, also concenring OOP, code structure and style.
I post basic working GUI version, if someone would like to run it.
Main Class:
public class Main {
public static void main(String[] args){
Board board = new Board();
board.setBoard();
}
}
Board Class:
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Board {
private Cell[][] cells;
private int cellID = 0;
private int side = 8;
private int limit = side-2;
public void setBoard(){
JFrame frame = new JFrame();
frame.add(addCells());
plantMines();
setCellValues();
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public JPanel addCells(){
JPanel panel = new JPanel(new GridLayout(side,side));
cells = new Cell[side][side];
for(int i = 0; i< side; i++){
for(int j = 0; j<side; j++){
cells[i][j] = new Cell(this);
cells[i][j].setId(getID());
panel.add(cells[i][j].getButton());
}
}
return panel;
}
public void plantMines(){
ArrayList<Integer> loc = generateMinesLocation(10);
for(int i : loc){
getCell(i).setValue(-1);
}
}
/*Choose rendom places for mines*/
public ArrayList<Integer> generateMinesLocation(int q){
ArrayList<Integer> loc = new ArrayList<Integer>();
int random;
for(int i = 0; i<q;){
random = (int)(Math.random()* (side*side));
if(!loc.contains(random)){
loc.add(random);
i++;
}
}
return loc;
}
// MOST IMPORTANT PART/////////////////////////////////////////////////////
/*This method count number of mines around particular cell and set its value*/
public void setCellValues(){
for(int i = 0; i<side; i++){
for(int j = 0; j<side; j++){
if(cells[i][j].getValue() != -1){
if(j>=1 && cells[i][j-1].getValue() == -1) cells[i][j].incrementValue();
if(j<= limit && cells[i][j+1].getValue() == -1) cells[i][j].incrementValue();
if(i>=1 && cells[i-1][j].getValue() == -1) cells[i][j].incrementValue();
if(i<= limit && cells[i+1][j].getValue() == -1) cells[i][j].incrementValue();
if(i>=1 && j>= 1 && cells[i-1][j-1].getValue() == -1) cells[i][j].incrementValue();
if(i<= limit && j<= limit && cells[i+1][j+1].getValue() == -1) cells[i][j].incrementValue();
if(i>=1 && j<= limit && cells[i-1][j+1].getValue() == -1) cells[i][j].incrementValue();
if(i<= limit && j>= 1 && cells[i+1][j-1].getValue() == -1) cells[i][j].incrementValue();
}
}
}
}
/*This method starts chain reaction. When user click on particular cell, if cell is empty (value = 0) this
method look for other empty cells next to activated one. If finds one, it call checkCell and in effect,
start next scan on its closest area.
*/
public void scanForEmptyCells(){
for(int i = 0; i<side; i++){
for(int j = 0; j<side; j++){
if(!cells[i][j].isNotChecked()){
if(j>=1 && cells[i][j-1].isEmpty()) cells[i][j-1].checkCell();
if(j<= limit && cells[i][j+1].isEmpty()) cells[i][j+1].checkCell();
if(i>=1 && cells[i-1][j].isEmpty()) cells[i-1][j].checkCell();
if(i<= limit && cells[i+1][j].isEmpty()) cells[i+1][j].checkCell();
if(i>=1 && j>= 1 && cells[i-1][j-1].isEmpty()) cells[i-1][j-1].checkCell();
if(i<= limit && j<= limit && cells[i+1][j+1].isEmpty()) cells[i+1][j+1].checkCell();
if(i>=1 && j<= limit && cells[i-1][j+1].isEmpty()) cells[i-1][j+1].checkCell();
if(i<= limit && j>= 1 && cells[i+1][j-1].isEmpty()) cells[i+1][j-1].checkCell();
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////
public int getID(){
int id = cellID;
cellID++;
return id;
}
public Cell getCell(int id){
for(Cell[] a : cells){
for(Cell b : a){
if(b.getId() == id) return b;
}
}
return null;
}
public void fail(){
for(Cell[] a : cells){
for(Cell b : a){
b.reveal();
}
}
}
}
Cell Clas:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Cell implements ActionListener{
private JButton button;
private Board board;
private int value;
private int id;
private boolean notChecked;
public Cell(Board board){
button = new JButton();
button.addActionListener(this);
button.setPreferredSize(new Dimension(20,20));
button.setMargin(new Insets(0,0,0,0));
this.board = board;
notChecked = true;
}
public JButton getButton() {
return button;
}
public int getValue() {
return value;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setValue(int value) {
this.value = value;
}
public void displayValue(){
if(value==-1){
button.setText("\u2600");
button.setBackground(Color.RED);
}else if(value!=0){
button.setText(String.valueOf(value));
}
}
public void checkCell(){
button.setEnabled(false);
displayValue();
notChecked = false;
if(value == 0) board.scanForEmptyCells();
if(value == -1) board.fail();
}
public void incrementValue(){
value++;
}
public boolean isNotChecked(){
return notChecked;
}
public boolean isEmpty(){
return isNotChecked() && value==0;
}
public void reveal(){
displayValue();
button.setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
checkCell();
}
}