user4276463
user4276463

Reputation:

Restarting minesweeper GUI program

I've made a GUI game that is much like minesweeper, although there is much less logic. Basically a user clicks a space and sees if there is gold underneath and if there isn't, it shows an X. If there is, it shows a gold piece. I want to make so the game restarts and all the data is reset once the user finds 10 gold pieces. However, I'm not having success with my do while loop. I've commented out the part of my code that causes me problems. Each time I run the program with the stuff that I have commented out, it gets stuck and won't continue running when I click the first button. Otherwise, with the stuff commented out, it works how it should.

Question: How can I make my program restart and the data values reset once a user uncovers 10 gold pieces? Also, why is my 12x12 button grid I've set up showing up not proportional to how I set up the 2D button array?

Here's what the game looks like when I have the do while commented out:

public class NuggetPanel extends JPanel {

    // variable declaration
    boolean restartGame = false;
    JButton[][] buttons = new JButton[12][12];
    JButton restart = new JButton("You win! Click to restart game");
    int count = 0;
    int goldFound = 0;
    JLabel clickCount = new JLabel("Number of digs: " + count);
    JLabel goldCount = new JLabel("Gold found: " + goldFound);


    // sets up the panel
    public NuggetPanel() {

        setLayout(new GridLayout(12, 12, 2, 2));
        JPanel LabelPane = new JPanel();


        //creates 2D array of buttons
        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons[i].length; j++) {
                buttons[i][j] = new JButton("");
                buttons[i][j].addActionListener(new buttonListener());
                add(buttons[i][j]);
            }

        }



        //adds components
        LabelPane.add(clickCount);
        LabelPane.add(goldCount);
        LabelPane.add(restart);
        add(LabelPane);
        restart.setVisible(false);
    }

    // button is clicked
    private class buttonListener implements ActionListener {    

        @Override
        public void actionPerformed(ActionEvent e) {

//  do{

            // finds which button was clicked
            JButton buttonClicked = (JButton) e.getSource();

            restartGame = false;
            restart.setVisible(false);


            // sets up gold array
            Boolean[] randomGold = new Boolean[144];

            // fills gold array with 10 true elements
            for (int i = 0; i < randomGold.length; i++) {
                randomGold[i] = false;

                // sets 10 indexes in the array to true
                if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80 || i == 90 || i == 100) {
                    randomGold[i] = true;
                }
            }

            // randomizes gold array
            Collections.shuffle(Arrays.asList(randomGold));

            // iterates through button array
            for (int i = 0; i < buttons.length; i++) {

                for (int j = 0; j < buttons[i].length; j++) {

                    if (buttonClicked == buttons[i][j]) {

                        // if there is a gold under the square, shows gold icon
                        if (randomGold[i] == true) {
                            buttons[i][j].setIcon(new ImageIcon("./src/Gold.jpg"));
                            buttons[i][j].removeActionListener(this);
                            goldFound++;
                            count++;
                            clickCount.setText("Number of digs: " + count);
                            goldCount.setText("Gold found: " + goldFound + "  ");

                        }

                        // if there is no gold under the square, shows X
                        else {
                            buttons[i][j].removeActionListener(this);
                            buttons[i][j].setIcon(new ImageIcon("./src/Missed.png"));
                            count++;
                            clickCount.setText("Number of digs: " + count);
                            goldCount.setText("Gold found: " + goldFound);
                        }

//                      if (goldFound == 10) {
//                          restart.setVisible(true);
//                      }
//
//                      if (buttonClicked == restart) {
//                          count = 0;
//                          goldFound = 0;
//                          clickCount.setText("Number of digs: " + count);
//                          for (int x = 0; i < buttons.length; i++)
//                          {
//                              for(int y = 0; j < buttons[i].length; j++)
//                              {
//                                  buttons[x][y].setIcon(null);
//                              }
//                          }
//                          restartGame = true;
//                      }

                        j = buttons[i].length - 1;
                        i = buttons.length - 1;

                    }

                }
            }
//      } while(restartGame = true);
    }
}
}

Upvotes: 0

Views: 901

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347214

Also, why is my 12x12 button grid I've set up showing up not proportional to how I set up the 2D button array?

This is a two fold issue. JButton has margin properties which aren't normally square. Also, when you add components to a GridLayout, ALL the components get sized the same, so when you add your LabelPane to the layout, it forces all the buttons to have at least it's width.

Instead, put your buttons onto a separate panel, using a GridLayout, and then put this and the LabelPanel onto your NuggetPanel using a BorderLayout, for example...

    public NuggetPanel() {

        setLayout(new BorderLayout());

        JPanel buttonPane = new JPanel(new GridLayout(12, 12, 2, 2));
        //creates 2D array of buttons
        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons[i].length; j++) {
                // nb: I'd use a "blank" icon which was the same
                // size as the other icons and set it as the 
                // buttons icon, this will ensure that the 
                // buttons are always the right size...
                buttons[i][j] = new JButton("");
                buttons[i][j].setMargin(new Insets(5, 5, 5, 5));
                buttons[i][j].addActionListener(new buttonListener());
                buttonPane.add(buttons[i][j]);
            }

        }

        //adds components
        JPanel LabelPane = new JPanel();
        LabelPane.add(clickCount);
        LabelPane.add(goldCount);
        LabelPane.add(restart);

        add(buttonPane);
        add(LabelPane, BorderLayout.SOUTH);

        restart.setVisible(false);
    }

How can I make my program restart and the data values reset once a user uncovers 10 gold pieces?

This is a more complex issue and will require you to change the way you are thinking.

actionPerfomed will be called when every ANY button is clicked, but what you are doing is randomizing the location of the gold as well...this will make it almost impossible for the user to win. Why? Because it's possible to place a piece of gold under a button which as already been checked...

    @Override
    public void actionPerformed(ActionEvent e) {

        // finds which button was clicked
        JButton buttonClicked = (JButton) e.getSource();

        restartGame = false;
        restart.setVisible(false);

        // sets up gold array
        Boolean[] randomGold = new Boolean[144];

        // fills gold array with 10 true elements
        for (int i = 0; i < randomGold.length; i++) {
            randomGold[i] = false;

            // sets 10 indexes in the array to true
            if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80 || i == 90 || i == 100) {
                randomGold[i] = true;
            }
        }

        // randomizes gold array
        Collections.shuffle(Arrays.asList(randomGold));

Instead, create a method which can be used to reset or initialise the state of the game...

    protected void reset() {
        // sets up gold array
        randomGold = new Boolean[144];

        // fills gold array with 10 true elements
        for (int i = 0; i < randomGold.length; i++) {
            randomGold[i] = false;

            // sets 10 indexes in the array to true
            if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80 || i == 90 || i == 100) {
                randomGold[i] = true;
            }
        }

        // randomizes gold array
        Collections.shuffle(Arrays.asList(randomGold));

        for (int i = 0; i < buttons.length; i++) {
            for (int j = 0; j < buttons[i].length; j++) {
                // Reset the image icon with a blank image...
                buttons[i][j].setText("");
                buttons[i][j].setEnabled(true);
            }

        }
    }

This should be called at the end of your constructor as well. This will ensure that the game is a ready state to be played...

Instead of using buttons[i][j].removeActionListener(this);, simple set the button as disabled using buttons[i][j].setEnabled(false);, this makes it easier to reset the button, as you don't need to care if the button already has an ActionListener associated with it or not (which could lead to your actionPerformed method been called multiple times for a single button :P)

At the end of your actionPerformed method, you simply need to make a check for the number of goldFound and call reset to reset the game...

if (goldFound == 10) {
    JOptionPane.showMessageDialog(NuggetPanel.this, "You win");
    reset();
}

Additional...

Don't load resources from your src directory using something like...

 buttons[i][j].setIcon(new ImageIcon("./src/Missed.png"));

This will fail once the code is built. Instead, you need to reference the resource as an embedded resource and use Class#getResource

 buttons[i][j].setIcon(new ImageIcon(getClass().getResource("/Missed.png")));

But frankly, this is a little wasteful, you could, instead, define the icons as constants....

public static final ImageIcon MISSED_ICON = new ImageIcon(getClass().getResource("/Missed.png")));

And then simply reference it when ever you need to...

 buttons[i][j].setIcon(MISSED_ICON);

Personally, I prefer ImageIO.read as it throws an IOException, but it makes it more complicated to set up constants...

Also, I would probably consider using a List of some kind to manage the buttons and "gold" locations as it's simply to use List#indexOf(Object) then having to run through your double loop each time...

Upvotes: 1

Related Questions