Search code examples
javaswinggraphicsmouselistenertic-tac-toe

Simulate a mouse click within Board (JPanel)


I am writing a simple TicTacToe game in java. The basic implementation is done. Player vs Player is working flawlessly.

But now, when I am implementing the AI, I've a problem. I have calculated the move for the computer, but I cannot make the computer click that location [with (x, y) coordinates] on the JPanel.

I've seen the Robot class, but it doesn't click on the required place, and seems to click outside the window.

Here is the code, which draws the board on a JPanel :

private class Board extends JPanel{
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public Board(){
            this.setSize(new Dimension(board_width, board_height));
            this.setPreferredSize(new Dimension(board_width, board_height));
            this.setBackground(new Color(0x84ACFF));            
            this.setOpaque(true);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            //g.setColor(new Color(0xf9d1af));
            g.setColor(Color.white);
            g.drawLine((board_width/3), 0, (board_width/3), board_height);
            g.drawLine((board_width/3)*2, 0, (board_width/3)*2, board_height);
            g.drawLine(0, (board_height/3), (board_width), (board_height/3));
            g.drawLine(0, (board_height/3)*2, (board_width), (board_height/3)*2);
            g.finalize();
        }
    }

And here is the mouseListener I am using with the JPanel:

@Override
public void mousePressed(MouseEvent e) {
    Graphics g = this.getGraphics();

    int X = e.getX();
    int Y = e.getY();

    //JOptionPane.showMessageDialog(null, e.getXOnScreen()+":"+e.getYOnScreen());

    int block_x = board_width/3;
    int block_y = board_height/3;

    X = X/block_x;
    Y = Y/block_y;

    //For array Assignment
    Click_X = Y;
    Click_Y = X;

    //Keep copy of the originals
    Orig_X = X;
    Orig_Y = Y;

    //------------------------------------------
    //          Assign to array & Draw
    //------------------------------------------
    if (computer){
        if (start.equals("Computer")){
            if (game_board[Click_X][Click_Y] != ' ') return;
            moves++;
            game_board[Click_X][Click_Y] = Cross;
            drawCross(((X+1)*block_x)+25, ((Y+1)*block_y)+25, 50, 50, g);
            dcsn = new Decision(game_board, moves);
        }
        else{
            if (game_board[Click_X][Click_Y] != ' ') return;
            moves++;
            game_board[Click_X][Click_Y] = Nought;
            drawNought(((X+1)*block_x), ((Y+1)*block_y), 50, 50, g);
            dcsn = new Decision(game_board, moves);
        }
    }
    else{
        if (start.equals("Human")){
            if (game_board[Click_X][Click_Y] != ' ') return;
            moves++;
            game_board[Click_X][Click_Y] = Cross;
            drawCross(((X+1)*block_x)+25, ((Y+1)*block_y)+25, 50, 50, g);
            dcsn = new Decision(game_board, moves);
        }
        else{
            if (game_board[Click_X][Click_Y] != ' ') return;
            moves++;
            game_board[Click_X][Click_Y] = Nought;
            drawNought(((X+1)*block_x), ((Y+1)*block_y), 50, 50, g);
            dcsn = new Decision(game_board, moves);
        }
    }

    switch (dcsn.gameOver()){
    case "X":
        JOptionPane.showMessageDialog(null,"X has won!");
        break;
    case "O":
        JOptionPane.showMessageDialog(null,"O has won!");
        break;
    case "draw":
        JOptionPane.showMessageDialog(null,"Draw!");
        break;
    case " ":
        break;
    }

    switchTurn();
}

Now how do I make the computer click at the calculated location ? Any ideas ?

Here is the link to full source code : Download Source

It is otherwise difficult to reproduce the situation.


Solution

  • I'd like to give you an example on how to do it like I suggested in the comments. ("Why don't you just add a MouseListener for every cell", "You don't need a robot class") Of course you don't have to do it this way, but I think it is just easier to code & to understand. Right now the computer just picks random cells and there's no restart/win/lose, I just wanted to show you the concept. I wouldn't use the robot class in this case, the program shouldn't be able to take control over the cursor ;)

    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.util.Random;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.border.LineBorder;
    
    public class TicTacToe {
    
        Cell[][] cells = new Cell[3][3];
        boolean humanTurn = true;
    
        public TicTacToe() {
    
            JFrame frame = new JFrame("T I C - T A C - T O E");
    
            JPanel panel = new JPanel(new GridLayout(3, 3));
            addCells(panel);
    
            frame.add(panel);
    
            frame.setSize(600, 600);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
    
            ActionListener actionListener = new ActionListener() {
    
                Random r = new Random();
    
                @Override
                public void actionPerformed(ActionEvent e) {
    
                    if (!humanTurn) {
                        // Random cell
                        int x = r.nextInt(3);
                        int y = r.nextInt(3);
                        Cell cell = cells[x][y];
    
                        if (!cell.clicked) {
    
                            // Only the computer runs the gameloop, so false
                            cell.human = false;
                            cell.cross = false;
    
                            cell.repaint();
    
                            // Make sure the panel repaints before setting next
                            // booleans
                            EventQueue.invokeLater(new Runnable() {
                                public void run() {
                                    humanTurn = true;
                                    cell.clicked = true;
                                }
                            });
                        }
                    }
    
                }
            };
            Timer gameloop = new Timer(10, actionListener);
            gameloop.start();
    
        }
    
        private void addCells(JPanel panel) {
    
            for (int y = 0; y < 3; y++) {
                for (int x = 0; x < 3; x++) {
    
                    Cell cell = new Cell();
                    cell.setBorder(new LineBorder(Color.BLACK));
    
                    cells[x][y] = cell;
    
                    panel.add(cell);
    
                }
            }
        }
    
        public static void main(String[] args) {
    
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new TicTacToe();
                }
            });
    
        }
    
        class Cell extends JPanel implements MouseListener {
    
            boolean clicked = false;
            boolean human = true;
            boolean cross = false;
    
            public Cell() {
    
                addMouseListener(this);
    
            }
    
            @Override
            public void paintComponent(Graphics g) {
    
                super.paintComponent(g);
    
                Graphics2D gg = (Graphics2D) g;
    
                // Makes drawing smooth
                gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
                // Drawing blue cross (player)
                if (human && cross) {
                    gg.setColor(Color.BLUE);
                    gg.drawLine(20, 20, getWidth() - 20, getHeight() - 20);
                    gg.drawLine(getWidth() - 20, 20, 20, getHeight() - 20);
                }
    
                // Drawing red circle (computer)
                else if (!human && !cross) {
                    gg.setColor(Color.RED);
                    gg.drawOval(10, 10, getWidth() - 20, getHeight() - 20);
                }
    
            }
    
            @Override
            public void mouseClicked(MouseEvent arg0) {
    
                if (humanTurn && !clicked) {
    
                    // Only human can click on the panel, so true
                    human = true;
                    cross = true;
    
                    repaint();
    
                    // Make sure the panel repaints before setting next booleans
                    EventQueue.invokeLater(new Runnable() {
                        public void run() {
                            humanTurn = false;
                            clicked = true;
                        }
                    });
    
                }
    
            }
    
            @Override
            public void mouseEntered(MouseEvent arg0) {
    
            }
    
            @Override
            public void mouseExited(MouseEvent arg0) {
    
            }
    
            @Override
            public void mousePressed(MouseEvent arg0) {
    
            }
    
            @Override
            public void mouseReleased(MouseEvent arg0) {
    
            }
    
        }
    
    }