Reputation:
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
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();
}
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