|
| 1 | +import java.awt.Color; |
| 2 | +import java.awt.Dimension; |
| 3 | +import java.awt.Font; |
| 4 | +import java.awt.GridLayout; |
| 5 | +import java.awt.Insets; |
| 6 | +import java.awt.event.MouseAdapter; |
| 7 | +import java.awt.event.MouseEvent; |
| 8 | +import java.awt.event.WindowEvent; |
| 9 | +import java.util.Random; |
| 10 | +import javax.swing.AbstractButton; |
| 11 | +import javax.swing.JButton; |
| 12 | +import javax.swing.JFrame; |
| 13 | +import javax.swing.JOptionPane; |
| 14 | +import javax.swing.JPanel; |
| 15 | + |
| 16 | +public class MineSweeper{ |
| 17 | + |
| 18 | + |
| 19 | + static int tiles = 300; |
| 20 | + static int grid[] = new int[tiles]; |
| 21 | + static boolean flags[] = new boolean[tiles]; |
| 22 | + static boolean discovered[] = new boolean[tiles]; |
| 23 | + static JButton[] buttons = new JButton[tiles]; |
| 24 | + static int[] xMod = {0, 1, 1, 1, 0, -1, -1, -1}; |
| 25 | + static int[] yMod = {1, 1, 0, -1, -1, -1, 0, 1}; |
| 26 | + static JFrame frame; |
| 27 | + static JPanel panel; |
| 28 | + static Random ran; |
| 29 | + static boolean gameOver = false; |
| 30 | + static boolean boardGenerated = false; |
| 31 | + static int rows, cols; |
| 32 | + static int buttonSize = 40; |
| 33 | + static int bombAmount = 50; |
| 34 | + |
| 35 | + // possible label colours (1 is blue, 2 is green ect.) |
| 36 | + static Color[] label = {new Color(0x0100FE), new Color(0x017F01), new Color(0x0FE0000), new Color(0x010080), new Color(0x810102), new Color(0x008081), new Color(0x000000), new Color(0x808080)}; |
| 37 | + |
| 38 | + |
| 39 | + public static void initialize(){ |
| 40 | + |
| 41 | + // rows = (int)Math.sqrt(tiles); |
| 42 | + // cols = (int)Math.sqrt(tiles); |
| 43 | + rows = 15; |
| 44 | + cols = 20; |
| 45 | + |
| 46 | + if(rows * cols != tiles){ |
| 47 | + System.out.println("Error Invalid Number of Tiles"); |
| 48 | + } |
| 49 | + |
| 50 | + // initialize objects |
| 51 | + ran = new Random(); |
| 52 | + frame = new JFrame("Tatti Sweeper"); |
| 53 | + panel = new JPanel(); |
| 54 | + |
| 55 | + // create and add buttons to grid |
| 56 | + createButtons(); |
| 57 | + |
| 58 | + // set default values |
| 59 | + clearBoard(); |
| 60 | + |
| 61 | + // setup JFrame |
| 62 | + frame.getContentPane().add(panel); |
| 63 | + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
| 64 | + frame.setResizable(true); |
| 65 | + frame.setSize(buttonSize * cols, buttonSize * rows); |
| 66 | + frame.setLocationRelativeTo(null); |
| 67 | + } |
| 68 | + |
| 69 | + public static void clearBoard(){ |
| 70 | + // populate arrays |
| 71 | + for (int i = 0; i < grid.length; i++){ |
| 72 | + buttons[i].setEnabled(true); |
| 73 | + buttons[i].setText(""); |
| 74 | + grid[i] = 0; |
| 75 | + flags[i] = false; |
| 76 | + discovered[i] = false; |
| 77 | + } |
| 78 | + gameOver = false; |
| 79 | + } |
| 80 | + |
| 81 | + private static void generateBoard(int click){ |
| 82 | + |
| 83 | + plantBombs(bombAmount, click); |
| 84 | + updateButtonLabels(); |
| 85 | + } |
| 86 | + |
| 87 | + // initilize button objects as well as attach mouse handler |
| 88 | + private static void createButtons(){ |
| 89 | + |
| 90 | + // loop through buttons |
| 91 | + for (int i = 0; i < buttons.length; i++){ |
| 92 | + |
| 93 | + buttons[i] = new JButton(); |
| 94 | + buttons[i].setFont(new Font("verdana", Font.BOLD, 18)); |
| 95 | + buttons[i].setMargin(new Insets(0, 0, 0, 0)); |
| 96 | + buttons[i].setPreferredSize(new Dimension(buttonSize, buttonSize)); |
| 97 | + buttons[i].setMaximumSize(new Dimension(buttonSize, buttonSize)); |
| 98 | + buttons[i].setActionCommand(Integer.toString(i)); |
| 99 | + buttons[i].addMouseListener(new MouseAdapter(){ |
| 100 | + |
| 101 | + public void mousePressed(MouseEvent e){ |
| 102 | + |
| 103 | + int button = Integer.parseInt(((AbstractButton)e.getSource()).getActionCommand()); |
| 104 | + |
| 105 | + if(!boardGenerated && e.getButton() != MouseEvent.BUTTON3){ |
| 106 | + generateBoard(button); |
| 107 | + sweep(button); |
| 108 | + } |
| 109 | + else if(!gameOver){ |
| 110 | + // seperate right click flags and left click sweeps |
| 111 | + if(e.getButton() == MouseEvent.BUTTON3){ |
| 112 | + // toggles the flag at requested button |
| 113 | + flag(button); |
| 114 | + } |
| 115 | + else{ |
| 116 | + // normal sweeping |
| 117 | + sweep(button); |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + }); |
| 122 | + |
| 123 | + // add buttons to JFrame |
| 124 | + panel.add(buttons[i]); |
| 125 | + } |
| 126 | + |
| 127 | + // create grid |
| 128 | + panel.setLayout(new GridLayout(rows, cols)); |
| 129 | + |
| 130 | + // display the window |
| 131 | + frame.pack(); |
| 132 | + frame.setVisible(true); |
| 133 | + } |
| 134 | + |
| 135 | + public static void updateButtonLabels(){ |
| 136 | + |
| 137 | + // loop through all tiles |
| 138 | + for (int i = 0; i < grid.length; i++){ |
| 139 | + |
| 140 | + // tiles must be discovered inorder for anything other then flags to be displayed |
| 141 | + if(discovered[i]){ |
| 142 | + if(grid[i] == 0){ // valid range of colours |
| 143 | + // blank tiles get set to disabled for effect |
| 144 | + buttons[i].setText(""); |
| 145 | + buttons[i].setEnabled(false); |
| 146 | + } |
| 147 | + else if(grid[i] < 9 && grid[i] > 0){ |
| 148 | + buttons[i].setForeground(label[grid[i] - 1]); |
| 149 | + buttons[i].setText(Integer.toString(grid[i])); |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + // gameover reveals all of the board |
| 154 | + if(gameOver){ |
| 155 | + if(grid[i] > 0 && grid[i] < 9){ |
| 156 | + buttons[i].setForeground(label[grid[i] - 1]); |
| 157 | + buttons[i].setText(Integer.toString(grid[i])); |
| 158 | + } |
| 159 | + else if(grid[i] == 0){ |
| 160 | + buttons[i].setEnabled(false); |
| 161 | + } |
| 162 | + // show bombs as black 'X' |
| 163 | + else if(grid[i] == -1){ |
| 164 | + buttons[i].setForeground(Color.BLACK); |
| 165 | + buttons[i].setText("X"); |
| 166 | + } |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + // inverts the boolean flag value |
| 172 | + private static void flag(int i){ |
| 173 | + |
| 174 | + // cannot flag already discovered tiles |
| 175 | + if(!discovered[i]){ |
| 176 | + flags[i] = !flags[i]; |
| 177 | + // update board |
| 178 | + updateButtonLabels(); |
| 179 | + } |
| 180 | + |
| 181 | + // if tile is flagged, set as red 'F', otherwise clear button (for blank buttons previously marked) |
| 182 | + if(flags[i]){ |
| 183 | + buttons[i].setForeground(Color.RED); |
| 184 | + buttons[i].setText("F"); |
| 185 | + } |
| 186 | + else{ |
| 187 | + buttons[i].setText(""); |
| 188 | + updateButtonLabels(); |
| 189 | + } |
| 190 | + } |
| 191 | + |
| 192 | + // selects specified amount of tiles to be bombs |
| 193 | + public static void plantBombs(int amount, int click){ |
| 194 | + |
| 195 | + // loop until desired number is achieved |
| 196 | + for (int i = 0; i < amount; i++){ |
| 197 | + int possibleLocation = ran.nextInt(tiles); |
| 198 | + |
| 199 | + // if tile is not already taken, set tile to be bomb, otherwise reloop |
| 200 | + if(grid[possibleLocation] != -1 && possibleLocation != click){ |
| 201 | + grid[possibleLocation] = -1; |
| 202 | + } |
| 203 | + else{ |
| 204 | + i--; |
| 205 | + } |
| 206 | + } |
| 207 | + generateNumbers(); |
| 208 | + boardGenerated = true; |
| 209 | + } |
| 210 | + |
| 211 | + // populates the grid with numbers representing how many bombs are in the surrounding 8 tiles |
| 212 | + public static void generateNumbers(){ |
| 213 | + |
| 214 | + // loop through grid |
| 215 | + for (int i = 0; i < grid.length; i++){ |
| 216 | + |
| 217 | + // no markers are needed on tiles with bombs |
| 218 | + if(grid[i] != -1){ |
| 219 | + |
| 220 | + // counter variable used to keep a running total of bombs |
| 221 | + int counter = 0; |
| 222 | + |
| 223 | + // up to 8 possible tiles around |
| 224 | + for (int k = 0; k < 8; k++){ |
| 225 | + |
| 226 | + // convert 1D array number into rows and cols |
| 227 | + // then a modifier is added on to search in 3 x 3 area |
| 228 | + int x = i % cols + yMod[k]; |
| 229 | + int y = i / cols + xMod[k]; |
| 230 | + |
| 231 | + // make sure the new values are valid on the grid |
| 232 | + if(checkIfValid(x, y)){ |
| 233 | + // if the grid location is a bomb then add to the total |
| 234 | + if(grid[y * cols + x] == -1){ |
| 235 | + counter++; |
| 236 | + } |
| 237 | + } |
| 238 | + } |
| 239 | + // set current tile to total count |
| 240 | + grid[i] = counter; |
| 241 | + } |
| 242 | + } |
| 243 | + } |
| 244 | + |
| 245 | + // discovers blank and number tiles around the given source |
| 246 | + public static void sweep(int current){ |
| 247 | + |
| 248 | + // if current tile is a flag, reveal it (mouse would have clicked on current tile meaning player wanted to reveal flag) |
| 249 | + if(flags[current]){ |
| 250 | + // removes flag and discover |
| 251 | + flags[current] = false; |
| 252 | + discovered[current] = true; |
| 253 | + } |
| 254 | + // action depends on tile type |
| 255 | + switch (grid[current]) { |
| 256 | + // sweeping a bomb loses the game. |
| 257 | + case -1: |
| 258 | + // sets gameover and displays board |
| 259 | + gameOver = true; |
| 260 | + updateButtonLabels(); |
| 261 | + // taunts player |
| 262 | + int n = JOptionPane.showConfirmDialog(frame, "You Suck\nTry Again?", "Lol Loser!", JOptionPane.YES_NO_OPTION); |
| 263 | + if(n == 0){ |
| 264 | + boardGenerated = false; |
| 265 | + clearBoard(); |
| 266 | + } |
| 267 | + break; |
| 268 | + // continues spread of tile is blank |
| 269 | + case 0: |
| 270 | + discovered[current] = true; |
| 271 | + spread(current); |
| 272 | + break; |
| 273 | + // stops spread on anything thats not blank |
| 274 | + default: |
| 275 | + discovered[current] = true; |
| 276 | + // updateboard |
| 277 | + break; |
| 278 | + } |
| 279 | + updateButtonLabels(); |
| 280 | + checkWin(); |
| 281 | + } |
| 282 | + |
| 283 | + // tests of the game has been won |
| 284 | + private static void checkWin(){ |
| 285 | + |
| 286 | + // assumes the player has won until proven otherwise |
| 287 | + boolean win = true; |
| 288 | + |
| 289 | + // loops through grid |
| 290 | + for (int i = 0; i < grid.length && win; i++){ |
| 291 | + // undiscovered nonbomb tiles or unflaged bombs means that the player has not won |
| 292 | + if(!discovered[i] && grid[i] != -1){ |
| 293 | + win = false; |
| 294 | + } |
| 295 | + } |
| 296 | + |
| 297 | + // on winning |
| 298 | + if(win){ |
| 299 | + int n = JOptionPane.showConfirmDialog(frame, "Winner Winner Chicken Dinner!\n Play another?", "Congrats!", JOptionPane.YES_NO_OPTION); |
| 300 | + if(n == 0){ |
| 301 | + boardGenerated = false; |
| 302 | + clearBoard(); |
| 303 | + } |
| 304 | + else{ |
| 305 | + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); |
| 306 | + } |
| 307 | + } |
| 308 | + } |
| 309 | + |
| 310 | + private static void spread(int i){ |
| 311 | + |
| 312 | + // convert values to rows and cols for ease of use; |
| 313 | + int x = i % cols; |
| 314 | + int y = i / cols; |
| 315 | + |
| 316 | + // test 8 tiles around |
| 317 | + for (int k = 0; k < 8; k++){ |
| 318 | + |
| 319 | + // add modifier to cordinates to accese surrounding area |
| 320 | + int currentX = x + xMod[k]; |
| 321 | + int currentY = y + yMod[k]; |
| 322 | + |
| 323 | + // converts 2 coordinates back to single |
| 324 | + int current = currentY * cols + currentX; |
| 325 | + |
| 326 | + // make sure new location is valid the current tile being spread from is a blank space |
| 327 | + if(checkIfValid(currentX, currentY) && grid[i] == 0){ |
| 328 | + |
| 329 | + // handling diffrent grid types |
| 330 | + switch (grid[current]) { |
| 331 | + case -1: |
| 332 | + // do not continue spreading at bomb |
| 333 | + break; |
| 334 | + case 0: |
| 335 | + // at a blank tile that is not discovered and not flaged, discover and spread from there |
| 336 | + if(!discovered[current] && !flags[current]){ |
| 337 | + discovered[current] = true; |
| 338 | + spread(current); |
| 339 | + } |
| 340 | + break; |
| 341 | + default: |
| 342 | + // otherwise discover tile |
| 343 | + discovered[current] = true; |
| 344 | + break; |
| 345 | + } |
| 346 | + } |
| 347 | + } |
| 348 | + } |
| 349 | + |
| 350 | + // make sure x and y are in bounds of 0 to row length |
| 351 | + public static boolean checkIfValid(int x, int y){ |
| 352 | + |
| 353 | + return (x > -1 && x < cols && y > -1 && y < rows); |
| 354 | + } |
| 355 | + |
| 356 | + public static void main(String args[]){ |
| 357 | + |
| 358 | + javax.swing.SwingUtilities.invokeLater(new Runnable(){ |
| 359 | + |
| 360 | + public void run(){ |
| 361 | + |
| 362 | + initialize(); |
| 363 | + } |
| 364 | + }); |
| 365 | + } |
| 366 | +} |
0 commit comments