Hi,
I am making tic tac toe, and I basically have it all working, except the GUI has a few bugs. Right now, I am not using a layout. I am using "setBounds" instead.
When a player wins, an image saying "you win" should be displayed over the title image and the game board. That works, except when you move the mouse over the buttons, the "you win" image is covered by those buttons.
Also, when the program first runs, the buttons are not visible unless you roll over them with a mouse...
Here is the code:

//import everything:
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.applet.AudioClip;
import java.net.*;

//the class with the JFrame and the ActionListner:
public class TicTacToe extends JFrame implements ActionListener
{
    //this is an array of the buttons (spots).  We use an array, not
    //an arrayList because the number of buttons is constant; it does
    //not change.
    JButton spots[] = new JButton[9];
    
    //this will keep track of turns
    int turn = 1;
    
    
    //this is a JLabel: it will display the text
    JLabel lbl = new JLabel(new ImageIcon("title.png"));
    
    //this JLabel will display whose turn it is
    JLabel turnLbl = new JLabel("X's Turn");
    
    ImageIcon red = new ImageIcon("red.png");
    ImageIcon blue = new ImageIcon("blue.png");
    ImageIcon blank = new ImageIcon("blank.png");
    ImageIcon loseImg = new ImageIcon("lose.png");
    ImageIcon winImg = new ImageIcon("win.png");
    ImageIcon drawImg = new ImageIcon("draw.png");
    
    JLabel lose = new JLabel(loseImg);
    JLabel win = new JLabel(winImg);
    JLabel draw = new JLabel(drawImg);
    
    URL hitURL = null, cheerURL = null,  booURL = null;
    AudioClip hit = null, cheer = null, boo = null;
    
    //the constructor
    public TicTacToe()
    {
        super("TicTacToe: Boxing Style");
        setSize(450,700);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        //this will hold the buttons:
        Container container = getContentPane();
        //this will tell the computer how to display the buttons:
        container.setLayout(null);          
                
        
        //we will add the winning, losing, and draw labels, butt
        //move them off the screen.
        container.add(lose);
        lose.setBounds(-500,-500,331,438);
        container.add(win);
        lose.setBounds(-500,-500,331,438);
        container.add(draw);
        lose.setBounds(-500,-500,331,438);
        
        //add the lbl:
        container.add(lbl);
        lbl.setBounds(20,0,400,288);
        
        try
        {
            hitURL = this.getClass().getResource("hit.wav");
            booURL = this.getClass().getResource("boo.wav");
            cheerURL = this.getClass().getResource("cheer.wav");
            hit = JApplet.newAudioClip(hitURL);
            boo = JApplet.newAudioClip(booURL);
            cheer = JApplet.newAudioClip(cheerURL);
        }
        catch(Exception e){}      
        
        //before we can add the buttons to the container, we must
        //initialize them.  Use a for loop to do it:
        int newLine = 0;
        int lineCount = 0;
        for(int i = 0; i < spots.length; i++)
        {
            //initialize it with a blank image
            spots[i] = new JButton(blank);
            //this checks whether to use a new row
            if(i==3 || i ==6)
            {
                newLine++;
                lineCount = 0;
            }
            
            //set the position of the button
            spots[i].setBounds(75+(lineCount*100),300+(newLine*100),100,100);

            //add it to the container
            container.add(spots[i]);
            
            //and now add the action listener:
            spots[i].addActionListener(this);
            
            lineCount++;
        }
        
        //add the JLabel that describes the turn
        container.add(turnLbl);
        turnLbl.setBounds(200,630,100,30);
        
        container.setComponentZOrder(lose,0);
        container.setComponentZOrder(win,0);
        container.setComponentZOrder(draw,0);
    }
    
    public void reset()
    {
        for(int i = 0; i < spots.length; i++)
        {
            spots[i].setIcon(blank);
            spots[i].addActionListener(this);
        }
        turn = 1;
    }
    
    
    //the mandatory method:
    public void actionPerformed(ActionEvent e)
    {
        hit.play();
        try
        {
            Thread.sleep(600);
        }
        catch(Exception excep){}
        
        //this will check the button pressed:
        for(int i = 0; i < spots.length; i++)
        {
            if(e.getSource()==spots[i])
            {

                //set the button to X
                spots[i].setIcon(red);

                //now, change the JLabel that describes the player's turn
                turnLbl.setText("X's (Red's) Turn");

                //disable the btn so it can't be re-pressed
                spots[i].removeActionListener(this);
            }
        }
        
        turn++;
        
        //before letting the other player go, check whether a player won:
        checkWin();
                
        //this method lets the computer select its turn
        ai();    
    }
    
    public void ai()
    {
        
        boolean movedYet;
        
        
        //if this is the computer's first turn, then try to go in the top left
        //if already taken, take the middle
        if(turn == 2)
        {
            //if the top left is taken, take the middle
            if(spots[0].getIcon().equals(red))
            {
                spots[4] .setIcon(blue);
                spots[4].removeActionListener(this);
                movedYet = true;
            }
            //else, take the top left
            else
            {
                spots[0] .setIcon(blue);
                spots[0].removeActionListener(this);
                movedYet = true;
            }
        }
        //if this is not the first turn, then check for 2 out of 3 spots
        //taken.  If there are none, go to a random location
        else
        {
            
            //callin this method checks for two in a row of the first String passed in.
            //It then takes the 3rd spot with the 2nd String passed in:
            movedYet = twoInARow(blue);
            
            //if the computer didn't take an offensive spot, take a defensive
            //one.
            if(!movedYet)
            {
                movedYet = twoInARow(red);
                //if there is no defensive move, take the next open one.
                if(!movedYet)
                {
                    //this loop finds the first untaken spot:
                    for(int i = 0; i < spots.length; i++)
                    {
                        //if empty, take it!
                        if(spots[i].getIcon().equals(blank))
                        {
                            spots[i] .setIcon(blue);
                            spots[i].removeActionListener(this);
                            movedYet = true;
                            break;
                        }
                    }
                }
            }
        }
               
        turn++;
        System.out.println(turn);
        checkWin();
        
        if(!movedYet)
        {
             //if no spot was taken, it must be a cat's game:
             draw.setBounds(50,50,331,438);
             reset();
        }
    }
    
    public boolean twoInARow(Icon a)
    {
        
            for(int i = 0; i < 3; i++)
            {
                //this checks for 2 in a row from the top
                if(spots[i].getIcon().equals(a) &&
                        spots[i+3].getIcon().equals(a) &&
                        spots[i+6].getIcon().equals(blank))
                {
                    spots[i+6].setIcon(blue);
                    spots[i+6].removeActionListener(this);
                    return true;
                }
                
                //this checks (from the top and bottom)
                //for a taken spot, then a gap, then a taken one:
                if(spots[i].getIcon().equals(a) &&
                        spots[i+6].getIcon().equals(a) &&
                        spots[i+3].getIcon().equals(blank))
                {
                    spots[i+3].setIcon(blue);
                    spots[i+3].removeActionListener(this);
                    return true;
                }
                
                
                //this checks for 2 in a row from the bottom
                if(spots[i+6].getIcon().equals(a) &&
                        spots[i+3].getIcon().equals(a) &&
                        spots[i].getIcon().equals(blank))
                {
                    spots[i] .setIcon(blue);
                    spots[i].removeActionListener(this);
                    return true;
                }
                
                //this checks for 2 in a row from the left
               if(spots[i*3].getIcon().equals(a) &&
                    spots[(i*3)+1].getIcon().equals(a) &&
                        spots[(i*3)+2].getIcon().equals(blank))
                {
                    spots[(i*3)+2] .setIcon(blue);
                    spots[(i*3)+2].removeActionListener(this);
                    return true;
                }
                
                
                //this checks (from the left and right)
                //for a taken spot, then a gap, then a taken one
                if(spots[i*3].getIcon().equals(a) &&
                    spots[(i*3)+2].getIcon().equals(a) &&
                        spots[(i*3)+1].getIcon().equals(blank))
                {
                    spots[(i*3)+1] .setIcon(blue);
                    spots[(i*3)+1].removeActionListener(this);
                    return true;
                }
                
                
                //this checks for 2 in a row from the right
                if(spots[(i*3)+2].getIcon().equals(a) &&
                    spots[(i*3)+1].getIcon().equals(a) &&
                        spots[i*3].getIcon().equals(blank))
                {
                    spots[i*3] .setIcon(blue);
                    spots[i*3].removeActionListener(this);
                    return true;
                }
                
                //now we will check for a diagnol 2 in a row:
                for(int j = 0; j <= 2; j+=2)
                {
                    //this will check for diagnol X wins
                    if(spots[j].getIcon()==a &&
                        spots[4].getIcon()==a &&
                        spots[8-j].getIcon().equals(blank))
                    {
                        spots[8-j] .setIcon(blue);
                        spots[8-j].removeActionListener(this);
                        return true;
                    }
                    
                    //this checks (from a diagnol)
                   //for a taken spot, then a gap, then a taken one
                    if(spots[j].getIcon()==a &&
                        spots[8-j].getIcon()==a &&
                        spots[4].getIcon().equals(blank))
                    {
                        spots[4] .setIcon(blue);
                        spots[4].removeActionListener(this);
                        return true;
                    }
                    
                    
                    if(spots[8-j].getIcon()==a &&
                        spots[4].getIcon()==a &&
                        spots[j].getIcon().equals(blank))
                    {
                        spots[j] .setIcon(blue);
                        spots[j].removeActionListener(this);
                        return true;
                    }
                }
            }
            return false;
    }
    
    public void checkWin()
    {
        //first, we will use to go through three iterations.  This allows us to
        //check for both horizontal and vertical wins without using too
        //much code:
        for(int i = 0; i < 3; i++)
        {
            //this checks for a vertical X win
            if(spots[i].getIcon().equals(red) &&
                    spots[i+3].getIcon().equals(red) &&
                    spots[i+6].getIcon().equals(red))
            {
                cheer.play();
                try
                {
                    Thread.sleep(600);
                }
                catch(Exception excep){}
        
                win.setBounds(50,50,331,438);
                reset();
                return;
            }
            
            //this checks for a vertical O win
            if(spots[i].getIcon().equals(blue) &&
                    spots[i+3].getIcon().equals(blue) &&
                    spots[i+6].getIcon().equals(blue))
            {
                boo.play();
                try
                {
                    Thread.sleep(600);
                }
                catch(Exception excep){}
                lose.setBounds(50,50,331,438);;
                reset();
                return;
            }
            
            //this checks for a horizontal X win
            if(spots[i*3].getIcon().equals(red) &&
                    spots[(i*3)+1].getIcon().equals(red) &&
                    spots[(i*3)+2].getIcon().equals(red))
            {
                cheer.play();
                try
                {
                    Thread.sleep(600);
                }
                catch(Exception excep){}
                
               win.setBounds(50,50,331,438);
                reset();
                return;
            }
            
            //this checks for a horizontal O win
            if(spots[i*3].getIcon().equals(blue) &&
                    spots[(i*3)+1].getIcon().equals(blue) &&
                    spots[(i*3)+2].getIcon().equals(blue))
            {
                boo.play();
                try
                {
                    Thread.sleep(600);
                }
                catch(Exception excep){}
                lose.setBounds(50,50,331,438);;
                reset();
                return;
            }
        }
        
        //now, this loop will check for diagnol wins
        for(int i = 0; i <= 2; i+=2)
        {
            //this will check for diagnol X wins
            if(spots[i].getIcon().equals(red) &&
                    spots[4].getIcon().equals(red) &&
                    spots[8-i].getIcon().equals(red))
            {
                cheer.play();
                try
                {
                    Thread.sleep(600);
                }
                catch(Exception excep){}
                
               win.setBounds(50,50,331,438);
                reset();
                return;
            }
            
            //this will check for diagnol O wins
            if(spots[i].getIcon().equals(blue) &&
                    spots[4].getIcon().equals(blue) &&
                    spots[8-i].getIcon().equals(blue))
            {
                boo.play();
                try
                {
                    Thread.sleep(600);
                }
                catch(Exception excep){}
                lose.setBounds(50,50,331,438);;
                reset();
                return;
            }
        }
    }
    
    //starter (main) method:
    public static void main(String[] args) {
        TicTacToe ttt = new TicTacToe();
    }
    
}

Thanks!

Right now, I am not using a layout.

Bad! And wrong too! You're implicitly using a NullLayout, which you should never, ever use.

I want to use setBounds, ao what layout should I use? SetBounds doesn't seem to work with a FlowLayout.

Thanks!

And why not use a NullLayout? It's an official Java class, so what is wrong with it? Is it just personal preference?

Also, I have seen many, many, many projects/games use this style.

What should I do instead?

Thanks

Use GridLayout or GridBagLayout.

Those do not use pixel locations. They simply use a grid that the user defines. I need to use pixel locations.

Using a NullLayout is not Platform independent as the varying underlying graphics systems display things differently, so the bounds are not always (and usually are not) the same and so your layout will not always work.

As far as the buttons showing up under the image, remove everything from the panel before displaying the image, then after displaying the image, remove it and redisplay/redraw the original item.

You may wish to create one JPanel with everything needed for the game/whatever and one Panel for the Image, then you can swap the JPanels.

If you do not wish to do this (or find it to hard), Google Java SplashScreen to find out how make on of those, and use that to display the image.


And, BTW, you do not have to use "pixel" locations. The GridBagLayout, once you understand it, is a very powerful layout manager where you can lay things out exactly how you want them with creative use of anchors, widths, heights, fills, and weights that is completely platform independent, without having to try and fiddle with pixel sizes and bounds.

As far as the buttons not immediately displaying, try moving the setVisible(true) to the end of the constructor and adding a validate() and/or pack() right before it.

Ahh... now this makes sense :) Thank you.

I want to use pixel locations because my next project is a game where I draw images and they get moved by the keyboard. Therefore, I think it is easier to use a NullLayout and use pixels.

I still have one more question, however. When I run the game, the buttons do not appear. They do not appear unless my mouse moves over them. Why?

Using a NullLayout is not Platform independent as the varying underlying graphics systems display things differently, so the bounds are not always (and usually are not) the same and so your layout will not always work.

As far as the buttons showing up under the image, remove everything from the panel before displaying the image, then after displaying the image, remove it and redisplay/redraw the original item.

You may wish to create one JPanel with everything needed for the game/whatever and one Panel for the Image, then you can swap the JPanels.

If you do not wish to do this (or find it to hard), Google Java SplashScreen to find out how make on of those, and use that to display the image.


And, BTW, you do not have to use "pixel" locations. The GridBagLayout, once you understand it, is a very powerful layout manager where you can lay things out exactly how you want them with creative use of anchors, widths, heights, fills, and weights that is completely platform independent, without having to try and fiddle with pixel sizes and bounds.

As far as the buttons not immediately displaying, try moving the setVisible(true) to the end of the constructor and adding a validate() and/or pack() right before it.

Ahh... now this makes sense :) Thank you.

I want to use pixel locations because my next project is a game where I draw images and they get moved by the keyboard. Therefore, I think it is easier to use a NullLayout and use pixels.

I still have one more question, however. When I run the game, the buttons do not appear. They do not appear unless my mouse moves over them. Why?

Try adding a call to validate() or repaint() after you have added all components. Since you are using Null Layout, the mechanisms for notifying the container to redraw itself may not behave in an expected manner. When you mouse over something, the window redraws the portion behind the mouse cursor, so this is probably what is causing the buttons to become visible.

Try adding a call to validate() or repaint() after you have added all components. Since you are using Null Layout, the mechanisms for notifying the container to redraw itself may not behave in an expected manner. When you mouse over something, the window redraws the portion behind the mouse cursor, so this is probably what is causing the buttons to become visible.

As I mentioned in the last sentence of my post (as well). ;-)

Yep =)

I don't know how I missed that :-O

Sorry!!! :)

But thanks for helping because it worked!

This article has been dead for over six months. Start a new discussion instead.