Skip to content

Instantly share code, notes, and snippets.

@pgoodman
Created February 6, 2009 00:35
Show Gist options
  • Select an option

  • Save pgoodman/59132 to your computer and use it in GitHub Desktop.

Select an option

Save pgoodman/59132 to your computer and use it in GitHub Desktop.
package minesweeper;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.util.Random;
import java.util.Stack;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import minesweeper.functional.*;
import static minesweeper.functional.ArrayUtils.*;
/**
* The Mine Sweeper program.
*
* @author Peter Goodman
*/
public class MineSweeper extends SimpleGUI {
protected static MineArea[][] areas;
protected static boolean[][] area_clear;
protected static int width, num_bombs;
// exists for the sake of readability
private interface MenuItemDelegate extends Delegate<JMenuItem> { }
/**
* Represents an area that might have a mine under it.
*/
protected static class MineArea extends JPanel {
private static final long serialVersionUID = 1L;
//protected JButton button;
protected MouseInputAdapter mouse_listener;
protected int num_mines = 0,
grid_x,
grid_y;
protected boolean is_bomb = false;
/**
* Create a mine-able area and make sure it knows its (X,Y) coordinates.
*/
public MineArea(int x, int y) {
super(new BorderLayout());
grid_x = x;
grid_y = y;
// add in a button
final MineArea self = this;
MouseInputAdapter mouse_listener = new MouseInputAdapter() {
public void mouseClicked(MouseEvent e) {
self.reveal();
}
};
this.addMouseListener(mouse_listener);
// set the border
setBorder(BorderFactory.createLineBorder(Color.black));
}
/**
* Recursively reveal mines.
*/
public void reveal() {
// exit condition for recursive revealing of mine areas
if(area_clear[grid_x][grid_y])
return;
area_clear[grid_x][grid_y] = true;
// they've clicked a bomb, game over
if(is_bomb) {
System.out.println("Bomb clicked, game over.");
return;
}
String str = " ";
// an area is clear of mines, reveal all surrounding cells
if(num_mines == 0) {
map(getSurroundingAreas(), new Delegate<MineArea>() {
public void call(MineArea area) {
area.reveal();
}
});
// this area has at least one nearby mine
} else if(num_mines > 0) {
str = Integer.toString(num_mines);
}
// add in either a space (for zero mines) or the number of nearby mines
this.add(label(str), BorderLayout.CENTER);
this.removeMouseListener(mouse_listener);
this.repaint();
}
/**
* Get the surrounding mine areas. There are at most 8 surrounding areas.
* @return
*/
public MineArea[] getSurroundingAreas() {
Stack<MineArea> found_areas = new Stack<MineArea>();
int curr_row,
curr_col;
for(curr_col = grid_x-1; curr_col <= grid_x+1; ++curr_col) {
for(curr_row = grid_y-1; curr_row <= grid_y+1; ++curr_row) {
// bounds checking for incrementing
if(curr_col < 0 || width <= curr_col || curr_row < 0 || width <= curr_row)
continue;
// ignore the current area
if(curr_col == grid_x && curr_row == grid_y)
continue;
found_areas.push(areas[curr_col][curr_row]);
}
}
return found_areas.toArray(new MineArea[found_areas.size()]);
}
/**
* Set this area to be a bomb.
*/
public void setAsBomb() {
is_bomb = true;
}
/**
* Check if this area is a bomb on not.
*/
public boolean isBomb() {
return is_bomb;
}
/**
* Increment the bomb counter.
*/
public void incrementCounter() {
num_mines += 1;
}
/**
* Get the x and y coordinates.
*/
public int getGridX() {
return grid_x;
}
public int getGridY() {
return grid_y;
}
public Dimension getPreferredSize() {
return new Dimension(20,20);
}
}
/**
* Constructor, initialize the arrays needed.
* @param width
*/
public MineSweeper(int w, int nb) {
width = w;
num_bombs = nb;
areas = new MineArea[width][width];
area_clear = new boolean[width][width];
}
/**
* Create the GUI.
*/
public void create() {
frame("Mine Sweeper", new Delegate<JFrame>() {
public void call(final JFrame f) {
int row = -1,
col = 0,
num_cells = width*width;
Random rand = new Random();
MineArea bomb;
// create the menu bar, all menus, and menu items
menu_bar(f,
menu("File",
menu_item("Exit", new MenuItemDelegate() {
public void call(JMenuItem item) {
System.exit(0);
}
})
),
menu("Theme",
menu_item("System Default", new MenuItemDelegate() {
public void call(JMenuItem item) {
set_look_and_feel(f,
UIManager.getSystemLookAndFeelClassName()
);
}
}),
menu_item("Motif", new MenuItemDelegate() {
public void call(JMenuItem item) {
set_look_and_feel(f,
"com.sun.java.swing.plaf.motif.MotifLookAndFeel"
);
}
}),
menu_item("OMG Ponies")
)
);
// initialize the area of mines and the bomb counter array
for(int i = 0; i < num_cells; ++i, col = (i % width)) {
if(col == 0)
++row;
areas[col][row] = new MineArea(col, row);
area_clear[col][row] = false;
};
// figure out where the bombs are and increment the bomb counter
// array where necessary for each choice of bombs. If we choose
// somewhere that already is a bomb then we ignore the choice
while(num_bombs > 0) {
bomb = areas[rand.nextInt(width)][rand.nextInt(width)];
if(bomb.isBomb())
continue;
--num_bombs;
// increment the bomb counters
map(bomb.getSurroundingAreas(), new Delegate<MineArea>() {
public void call(MineArea a) {
a.incrementCounter();
}
});
// identify this cell as a bomb
bomb.setAsBomb();
}
// create the main layout using a grid bag, this flattens the areas and then
// turns them into component-grid-constraint's.
add_content(f, grid(
map(flatten(areas, new MineArea[num_cells]),
new Function<ComponentGridConstraint, MineArea>() {
public ComponentGridConstraint call(MineArea a) {
return grid_cell(a).pos(a.getGridX(), a.getGridY());
}
},
new ComponentGridConstraint[num_cells]
)
));
}
});
}
/**
* Create and run the GUI.
* @param args
*/
public static void main(final String[] args) {
run(new MineSweeper(10, 15));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment