Created
February 6, 2009 00:35
-
-
Save pgoodman/59132 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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