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.
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) {
}
}
}