views:

258

answers:

3

Hi, After getting good help previously, I have completed my Minesweeper game. However, there is one problem that I can't figure out. The game itself works fine, however, I have an option for the user to change difficulty setting (beginner, intermediate, advanced) which is giving me grief. The user selects "Game" from the JMenuBar and then selects "Change Difficulty" from the drop down menu. This calls the method selectDifficulty() which opens a JOptionMessageBox which uses JRadioButtons to get the user to select one of the 4 options (the 4th being Custom - however I have not implemented that as of yet). The dialog box seems to work fine the first time the user changes difficulty mode. However, when the user attempts to change difficulty a 2nd time or more, the game implements the setting and then the dialogue box reappears and promts the user to select the difficulty again. This sometimes repeats twice before going away, and at other times up to 4 or 5 times. I would appreciate any help as I do not know why this is happening.

Also, I would also appreciate some advice on how I could improve how the difficulty settings are change. As it stands, the code removes the JPanel and the minefield(made of Buttons) and re instantiates the buttons/Japanels with the new difficulty setting. Is there a way to make this more efficient?

There is a logic class and a GUI glass. Both of the above issues are present in the GUI class.

private void selectDifficulty() {

    group.add(b1);
    group.add(b2);
    group.add(b3);
    group.add(b4);
    b1.setSelected(true);

    Object[] array = {new JLabel("Select Difficulty"),b1,b2,b3,b4};
    JOptionPane.showMessageDialog(null, array);

        if (b1.isSelected()) {
            if (mode == 1){
                rw = 9;
                rh = 9;
                mode = 1;    
            }else if(mode == 2){
                rw = 15;
                rh = 15;
                mode = 1;                    
            }else {
                rw = 20;
                rh = 20;
                mode = 1;
            }
            start(9,9);

        }else if (b2.isSelected()) {
            if (mode == 1){
                rw = 9;
                rh = 9;
                mode = 2;    
            }else if(mode == 2){
                rw = 15;
                rh = 15;
                mode = 2;                    
            }else {
                rw = 20;
                rh = 20;
                mode = 2;
            }
            start(15,15);
        }else if (b3.isSelected()) {
            if (mode == 1){
                rw = 9;
                rh = 9;
                mode = 3;    
            }else if(mode == 2){
                rw = 15;
                rh = 15;
                mode = 3;                    
            }else {
                rw = 20;
                rh = 20;
                mode = 3;
            }     
            start(20,20);
        }             
}    

COMPLETE CODE: GUI CLASS

import java.awt.* ;
import java.awt.event.* ;
import javax.swing.* ;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;


public class MineSweeperGUI extends JFrame implements MouseListener {          

MinesweeperLogic logicClass = new MinesweeperLogic();
JButton newGameButton = new JButton ("New Game");
JMenuItem optionsButton = new JMenuItem ("Change Difficulty");
JRadioButton b1 = new JRadioButton ("Beginner: 9 X 9 Grid (10 Mines)");
JRadioButton b2 = new JRadioButton ("Intermediate: 15 X 15 Grid (36 Mines)");
JRadioButton b3 = new JRadioButton ("Advanced:  20 X 20 Grid (80 Mines)");
JRadioButton b4 = new JRadioButton ("Custom");
ButtonGroup group = new ButtonGroup();
JMenuItem aboutButton = new JMenuItem ("About Minesweeper");
JPanel p = new JPanel();
JPanel p2 = new JPanel();
JPanel p3 = new JPanel();
int w, h, rw = 0, rh = 0, mode = 1;


public void MineSweeper(int width, int height) {
    //setupI(); 
    w = width;
    h = height;    
    logicClass.startNewGame(w, h);
    GridLayout layout = new GridLayout (w, h);
    p2.setLayout(new BorderLayout());
    p.setLayout(layout);
    p2.add(p, BorderLayout.CENTER);
    for(int x = 0 ; x < w ; x++) {
        for(int y = 0 ; y < h ; y++) {
            logicClass.label[x][y] = new Button();
            logicClass.label[x][y].setPreferredSize(new Dimension(20,20));
            logicClass.label[x][y].setBackground(new Color(33,58,156));
            logicClass.label[x][y].addMouseListener (this);
            p.add(logicClass.label[x][y]);
        }
    }       
    JMenuBar mb = new JMenuBar();
    JMenu m = new JMenu("Game");
    JMenu m2 = new JMenu("Help");
    m.add(optionsButton);
    m2.add(aboutButton);
    mb.add(m);
    mb.add(m2);

    p2.add(p3, BorderLayout.PAGE_START);
    newGameButton.setPreferredSize (new Dimension(87, 20));
    newGameButton.setFont(new Font("sansserif",Font.BOLD,11));
    newGameButton.setForeground(Color.black);
    newGameButton.setBackground(new Color(235,52,52));
    Border thickBorder = new LineBorder(Color.black, 2);
    newGameButton.setBorder(thickBorder);


    p3.add(newGameButton);
    newGameButton.addMouseListener(this);

    optionsButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            selectDifficulty();
        }
    });

    this.setJMenuBar(mb);
    this.add(p2);
    this.pack();
    this.setVisible(true);

}


private void start(int width, int height){
    p2.remove(p);
    for(int x = 0 ; x < rw ; x++) {
        for(int y = 0 ; y < rh ; y++) {
            p.remove(logicClass.label[x][y]);
        }
    }
    p2.remove(p3);
    p3.remove(newGameButton);
    this.remove(p2);
    MineSweeper(width, height);
}        

private void lose() {
  JOptionPane.showMessageDialog(this, "GAME OVER - YOU LOSE!  Starting New Game", "Game Over", JOptionPane.INFORMATION_MESSAGE);
  newGame();
}

private void win() {
  JOptionPane.showMessageDialog(this, "YOU WIN! Starting New Game", "CONGRATULATIONS", JOptionPane.INFORMATION_MESSAGE);
  newGame();
}


private void newGame() {
   if(mode==1){
       rw = 9;
       rh = 9;
       start(9,9);
    }else if(mode==2){
       rw = 15;
       rh = 15;
       start(15,15);
    }else{
       rw = 20;
       rh = 20;
       start(20,20);
    }
}


public void mouseClicked(MouseEvent e) {
   if(e.getSource() == newGameButton) {    
       if(e.getButton() == e.BUTTON1) {
           newGame();
        }
    }

   for (int x = 0 ; x < w; x++) {
       for (int y = 0 ; y < h; y++) {
           if(e.getSource() == logicClass.label[x][y]) {    
                if(e.getButton() == e.BUTTON1) {
                    if(logicClass.openCell(x, y) == false) {
                        lose();
                    } else {
                        for (int q = 0; q < w; q++) {
                            for (int j = 0; j < h; j++) {
                                if (logicClass.label[q][j].getBackground()==Color.green) {
                                    win();
                                }
                            }
                        }
                    }
               }else if(e.getButton() == e.BUTTON3) {
                   logicClass.markCell(x, y);
                    }
                }       
            }  
        }
     }

 private void selectDifficulty() {

    group.add(b1);
    group.add(b2);
    group.add(b3);
    group.add(b4);
    b1.setSelected(true);

    Object[] array = {new JLabel("Select Difficulty"),b1,b2,b3,b4};
    JOptionPane.showMessageDialog(null, array);

        if (b1.isSelected()) {
            if (mode == 1){
                rw = 9;
                rh = 9;
                mode = 1;    
            }else if(mode == 2){
                rw = 15;
                rh = 15;
                mode = 1;                    
            }else {
                rw = 20;
                rh = 20;
                mode = 1;
            }
            start(9,9);

        }else if (b2.isSelected()) {
            if (mode == 1){
                rw = 9;
                rh = 9;
                mode = 2;    
            }else if(mode == 2){
                rw = 15;
                rh = 15;
                mode = 2;                    
            }else {
                rw = 20;
                rh = 20;
                mode = 2;
            }
            start(15,15);
        }else if (b3.isSelected()) {
            if (mode == 1){
                rw = 9;
                rh = 9;
                mode = 3;    
            }else if(mode == 2){
                rw = 15;
                rh = 15;
                mode = 3;                    
            }else {
                rw = 20;
                rh = 20;
                mode = 3;
            }     
            start(20,20);
        }             
}    

public static void main(String[]args) {
    MineSweeperGUI guiClass = new MineSweeperGUI();
    guiClass.MineSweeper(9,9);
}

public void mouseEntered(MouseEvent e) {}

public void mousePressed(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}
}    

LOGIC CLASS: import java.awt.* ;

public class MinesweeperLogic {

public int width, height;
private int w, h, maxBombs;
private boolean mine[][]; 
private boolean flag[][];
private boolean isClicked[][];
private boolean isZero[][];
private boolean marked;
MineSweeperGUI guiClass;
Button[][] label;
private int surBombs;
private String temp;
private double mineRatio;


public void startNewGame(int width, int height) {
    w = width;
    h = height;
    label = new Button[w][h];
    mine = new boolean[w][h];
    flag = new boolean[w][h];
    isClicked = new boolean[w][h];
    isZero = new boolean[w][h];
    if ( (w*h) <= 81) {
        mineRatio = 0.13;
    }else if ( (w*h) <= 255) {
        mineRatio = 0.16;
    }else {
        mineRatio = 0.2;
    }
    maxBombs = (int) Math.floor (w * h * mineRatio);
    for (int i = 0; i < maxBombs; i++) {
        int x = (int) (Math.random() * w);
        int y = (int) (Math.random() * h);
        if (mine[x][y] == false) {
            mine[x][y] = true;
            isClicked[x][y] = false;
            flag[x][y] = false;
            isZero[x][y] = false;
        } else {
            i--;
        }
    }       
}


int getWidth() {
    return w;
}


int getHeight() {
    return h;
}


boolean openCell(int x, int y) {                
    isClicked[x][y] = true;
    if (mine[x][y] == true   &&   flag[x][y] == false) {
        lose();
        return false;
    } else if (getValue(x, y) > 0   &&   flag[x][y] == false) {
        temp = Integer.toString (getValue(x, y));
        label[x][y].setLabel (temp);
        label[x][y].setBackground (new Color (111,184,252));
        checkWin();
        return true;
    } else if (getValue(x, y) == 0   &&   flag[x][y] == false) {
        for (int q = x-1; q <= x+1; q++) {                       
            if(q < 0   ||   q >= w) {                                        
            continue;
            }
            for (int i = y-1; i <= y+1; i++) {
                if(i < 0   ||   i >= h   ||   flag[q][i] == true) { // makes sure that it wont have an error for buttons next to the wall
                continue;
                }
                label[q][i].setBackground(new Color (111,184,252));
                if (getValue(q, i) != 0) { // opens surrounding cells that have mines around them (max 8 cells)
                temp = Integer.toString (getValue(q, i));
                label[q][i].setLabel (temp);
                isClicked[q][i] = true;
                } else {
                    for (int k = x-1; k <= x+1; k++) {
                        if(k < 0   ||   k >= w) {
                        continue;
                        }
                            for (int m = y-1; m <= y+1; m++) {
                                if (m < 0   ||   m >= h   ||   flag[k][m] == true) { // makes sure that it wont have an error for buttons next to the wall
                                continue;
                                }
                                if (isClicked[k][m] == false   &&   getValue(k, m) == 0) { // recursively continues to open all surrounding cells with no mines around them
                                    openCell(k, m);
                                }
                            }
                        }
                    }
                }
            }
            checkWin(); 
            return true;
            } else {
                return true;
            }
      }           


boolean markCell(int x, int y) {
    if (flag[x][y] == true) {
        flag[x][y] = false;
        label[x][y].setLabel ("");
        label[x][y].setForeground (Color.black);
        label[x][y].setFont (new Font (null, Font.PLAIN, 12));
        return false;
    }
    if (isClicked[x][y] == false && flag[x][y] == false) {
        flag[x][y] = true;
        label[x][y].setFont (new Font ("sansserif", Font.BOLD, 14));
        label[x][y].setLabel ("<|");
        label[x][y].setForeground (Color.red);
    }
    if (mine[x][y] == true) {
        return true;
    } else {
        return false;
    }
}


boolean isOpen(int x, int y) {
    if (isClicked[x][y] == false) {
        return false;
       } else {
           return true;
       }
}


boolean isMarked(int x, int y) {
    if (flag[x][y] == true) {
        return true;
       } else {
           return false;
       }
}


int getValue(int x, int y) {
     if (mine[x][y] == true) {
         return -1;
       } else {
           return neighborBombs(x, y);
       }
   }


private int neighborBombs(int x, int y) {  // checks surrounding 8 squares for number of bombs (it does include itself, but has already been checked for a bomb so it won't matter)
      surBombs = 0;
          for (int q = x-1; q <= x+1; q++) {
              if (q < 0   ||   q >= w) {
                  continue;
              }
                  for (int i = y-1; i <= y+1; i++) {
                      if (i < 0   ||   i >= h) { // makes sure that it wont have an error for buttons next to the wall
                          continue;
                      }
                      if (mine[q][i] == true) {
                         surBombs++;
                      }
                  }   
              }          
       return surBombs;
      }


private void lose() {
    for (int q = 0; q < w; q++) {
        for (int j = 0; j < h; j++) {
            if (mine[q][j] == true) {
                if (label[q][j].getLabel().equals ("<|")) {
                    label[q][j].setBackground (Color.green);
                } else {
                    label[q][j].setBackground (Color.red);
                    label[q][j].setLabel ("*");
                    label[q][j].setForeground (Color.black);
                }


            }
        }
    }
 }


private void checkWin() {
    int count = 0;
    int count2 = 0;
    for (int i = 0; i < w; i++){
        for (int j = 0; j < h; j++) {
            if (isClicked[i][j] == true){
                count++;    
            }
            if (mine[i][j] == true){
                count2++;
            }
        }
    }
    if ( (count + count2) == (w * h) ){
        win();
    }
 }       


private void win() {
    for (int q = 0; q < w; q++) {
        for (int j = 0; j < h; j++) {
            if (mine[q][j] == true) {
                label[q][j].setBackground (Color.green);
                label[q][j].setForeground (Color.red);
                label[q][j].setFont (new Font ("sansserif", Font.BOLD, 14));
                label[q][j].setLabel ("<|");
            }
        }
    }
}
}
+1  A: 

You call MineSweeper(width, height) (should be lowercase as it is a method) at the end of start, which you call in selectDifficulty.

MineSweeper creates all your components new. optionsButton is not created new, so you add another ActionListener to it. Every ActionListener is called when you press the button, so selectDifficulty is called more and more times.

The easiest workaround is to put adding this ActionListener into the constructor of MineSweeperGUI, but you should refactor your source to create all Components only once where possible. At least the whole menu should go to the constructor.

Peter Lang
Yup, it worked. Thank you!!
newBie01
A: 

Try setting your menu component (JMenuBar and children) up once instead of each time MineSweeper is being called. I thought that was a constructor, btw, so it may confuse other Java developers too.

This is because the actionPerformed may be getting invoked more than once.

Kristopher Ives
A: 

You are making it much complicated, Java is an object oriented language, try to separate your work into objects based on properties/actions of those objects.

Anyway, two suggestion for your difficulty dialog.

  • Either you put the 3 difficulties in the menu itself. So the drop down menu, has Beginner,Medium or Hard modes, with a custom mode that opens a JPanel dialog.
  • Or You keep on your method but will need to do some changes. Mainly split the message dialog off of they main GUI and create a new class, lets say OptionsDialog which extends JDialog by itself. In the constructor of the OptionsDialog send a reference to the parent frame. After setting the difficulty call a method on the parent to set the difficulty there.
medopal
thanks for the reply. I don't want to create another class as the instruction were to create 2 classes only.As for adding the 3 difficulties in the menu itself - I had originally attempted to do that but the drop down menu would hide behind the minefield after the game had been started. I tried to figure out the issue for a bit but then decided to add the dialog box. Any idea how I could get around that issue?
newBie01