smograth
smograth

Reputation: 63

Code only works when I use breakpoints - probably a thread issue

I know this is probably a timing issue but i'm honestly not experienced enough to pin the specific thing down and figure out a fix. Basically, it's a Connect Four game, and I'm just now learning the basics of event listeners and such.

package siena;
import java.awt.*;

import javax.imageio.ImageIO;
import javax.swing.*;

public class ConnectFour {
    public static int buttonPushed = -1;

    public static void main (String[] args) throws java.lang.Exception {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gs = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gs.getDefaultConfiguration();
        JFrame board = new JFrame("Connect Four", gc);
        board.setResizable(false);
        board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GridBagLayout gridbag = new GridBagLayout();
        board.setLayout(gridbag);
        GridBagConstraints c = new GridBagConstraints();
        c.gridwidth = 3;
        JButton ng = new JButton("New Game");
        c.anchor = c.NORTH;
        c.fill = c.HORIZONTAL;
        c.insets = new Insets(5, 5, 5, 5);
        board.add(ng, c);
        c.gridx = 1;
        JButton eg = new JButton("Close");
        c.gridy = 1;
        c.gridwidth = 1;
        final JButton add[] = new JButton[7];
        ButtonClicker clickers[] = new ButtonClicker[9];
        for (int i = 0; i < clickers.length; i++) {
            clickers[i] = new ButtonClicker(i);
        }
        ng.addActionListener(clickers[0]);
        eg.addActionListener(clickers[8]);
        for(int i = 0; i < 7; i++) {
            add[i] = new JButton("Drop token");
            add[i].addActionListener(clickers[i + 1]);
            c.gridx = i;
            board.add(add[i], c);
        }
        JLabel slot[][] = new JLabel[7][6];
        int slotStat[][] = new int[7][6];
        c.ipadx = 0;
        c.ipady = 0;
        c.gridx = 0;
        c.gridy = 2;
        ImageIcon[] img = {new ImageIcon(ImageIO.read(new java.io.File("src/siena/gray.png"))), new ImageIcon(ImageIO.read(new java.io.File("src/siena/red.png"))), new ImageIcon(ImageIO.read(new java.io.File("src/siena/yellow.png")))};
        JLabel n = new JLabel(img[0]);
        for (int x = 0; x < 7; x++) {
            for (int y = 0; y < 6; y++) {
                slot[x][y] = new JLabel(n.getIcon());
                slotStat[x][y] = 0;
                board.add(slot[x][y], c);
                c.gridy++;
            }
            c.gridx++;
            c.gridy = 2;
        }
        c.gridx = 0;
        c.gridy = 8;
        c.gridwidth = 7;
        c.anchor = c.CENTER;
        Label info = new Label("It is the RED player's turn.");
        info.setAlignment(info.CENTER);
        board.add(info, c);
        board.pack();
        board.setVisible(true);
        boolean isRedTurn = true;
        int redCheck = 0;
        int yellowCheck = 0;
        while(buttonPushed != 9){
            while(buttonPushed == -1);
                switch(buttonPushed) {
                    case 0: 
                        for (int x = 0; x < 7; x++) {
                            for (int y = 0; y < 6; y++) {
                                slotStat[x][y] = 0;
                                slot[x][y].setIcon(img[0]);
                                isRedTurn = true;
                            }
                        }
                        buttonPushed = -1;
                        break;
                    case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                        if (slotStat[buttonPushed - 1][0] != 0) {
                            info.setText("That column is full!");
                            info.wait(100, 50000);
                            buttonPushed = -1;
                            break;
                        } else {
                            for (int y = 0; y < 6; y++) {
                                if (y != 5 && slotStat[buttonPushed - 1][y + 1] == 0) {
                                    //if we're not at the bottom and the slot below this one is empty
                                    if (isRedTurn) slot[buttonPushed - 1][y].setIcon(img[1]);
                                    else slot[buttonPushed - 1][y].setIcon(img[2]);
                                    Thread.sleep(200);
                                    slot[buttonPushed - 1][y].setIcon(img[0]);
                                } else {
                                    //if we're at the bottom or the slot below this one is full
                                if (isRedTurn) {
                                    slotStat[buttonPushed][y] = 1;
                                    slot[buttonPushed][y].setIcon(img[1]);
                                    redCheck++;
                                } else {
                                    slotStat[buttonPushed][y] = 2;
                                    slot[buttonPushed][y].setIcon(img[2]);
                                    yellowCheck++;
                                }
                                isRedTurn = !isRedTurn;                 
                            }
                        }
                    }
                    buttonPushed = -1;
                break;
            }
        }       
    }
}

The ButtonPushed class is just something that extends ActionListener so i can find out what button was pressed, it has an "id" int attribute that, on button pushed gets passed to the buttonPushed int in the connect four thing

Upvotes: 1

Views: 117

Answers (1)

smograth
smograth

Reputation: 63

So, to summarize the comments, I was using the kind of structure that would be used in a linear (not event-based) program. While loops don't work when you're waiting for input, and instead of using one big static main method and a slightly modified ActionListener class, I should have used input events to call internal methods to modify internal data, then update the external GUI (in its own class and with its own class methods) based on that internal data.

If anyone needs to see an example, this is my work so far. I still need to edit some of it (namely how the program ends, and I need to add a win detector), but the GUI is fully functional.

ConnectFour class: http://pastebin.com/yeGALQn7

ButtonClicker class: http://pastebin.com/Y2y0SfW9

CFBoard class: http://pastebin.com/g9qqBmry

Oh, and, for the curious reading this, I'm reasonably sure that the reason it only works in debug mode is that the breakpoints stopped the input from conflicting with main() running through the while() loop over and over, so making it fully event-driven meant that those conflicts didn't exist anymore.

Thanks to everyone for the help, especially @HovercraftFullOfEels!

Upvotes: 1

Related Questions