My first bot - Tic Tac Toe - Part I
My first bot! - Tic Tac Toe in Java with Swing - Part I
If you are interested in the source code or trying out the program, you can check it out on my GitHub: yuk068 - TicTacToeGame
I, My calling
So lately, I think I’ve discovered what my favorite activity in terms of coding is, and that is making bots. While at first, I was only making or recreating simple games like 2048, Chess, Snake,… And while those are fun, I soon realized that creating bots that can solve those games, play with you or even play against themselves? Just think about how a bot for a particular game can be create gets me going. For 2048 or Snake, the bot will be a solver type, and it will have to analyze the board, and base its moves on both logical thinking and probability calculation. And for more complex games that requires logical thinking like Chess or Tic Tac Toe, the bot is a player type, it will have to have the ability to see in to the future, and calculate the best move from all the possible outcomes with very little luck involved.
II, Tic Tac Toe in Java
1, The motivation
So basically I put a pin on my chess project and started making Tic Tac Toe in Java instead. Tic Tac Toe, a 3x3 game while players take turn marking their symbol (either O or X) on the board until one player win by making 3 of their symbols lined up in a row or the game ends in a draw where no player won and there are no available moves left. All in all, It was a much simpler game than Chess, and because I already planed to make a bot, I decided to start first with a Tic Tac Toe bot. Even though it’s kinda simple and not that complicated, it’d be my first step, to make my first bot.
2, Core implementation
So this time around, I’ve already familiar with the “archetype” of these games already (grid based games). This was probably my third time making something like this, first it was 2048, then it was Chess, and now Tic Tac Toe, they’re all grid based! So while I’m had prior experience, this time I decided to also take performance and optimization into consideration. I won’t go too in depth into how my prior games was “un-optimized” and also Java and Swing isn’t really the best tools to make games in. But that’s what I’m familiar with and there were not really a demand for performance, these are basically just learning experiences and I love them for that. But basically, my prior games made heavy use of OOP (Object Oriented Programming) and while it’s great for abstraction and making things easier to implement, Objects are infamous for hogging up loads of memory. So this time, instead of structuring my Tic Tac Toe with classes like Symbol, Square, Board, Move, Game… I basically implemented the core logic and functionality of the Tic Tac Toe game in one class - TicTacToeGame.
I mean I suppose that only when you’re comfortable with it enough can you really stray from abstraction to make games like these. But looking back at my zipbombs of games I still think they’re pretty garbage… So anyway, my take on Tic Tac Toe, uses an integer matrix as it’s board. where 0 represents an empty square, -1 is player O and 1 is player X:
public boolean turn;
private int[][] board;
public final int width;
public final int height;
public final Random random = new Random();
public final int toWinInARow;
public static final int EMPTY = 0;
public static final int PLAYER_X = 1;
public static final int PLAYER_O = -1;
public static final int DRAW = 0;
public static final int GAME_ONGOING = -2;
public static final int OUTSIDE_BOARD = -3;
public static final int PLAYER_X_WON = Integer.MAX_VALUE;
public static final int PLAYER_O_WON = Integer.MIN_VALUE;
Note that the width, height and toWinInARow fields are only placeholders, in the case of Tic Tac Toe, they’re always set to 3. They are only here because I do plan to expand the game, and the bot to be able to adapt to Caro game (basically a larger board that usually requires more symbol in a row to win), but that’s the story for another time.
So as you can see, board is just an int[][], and everything is also represented by integers in the from of constants. Boolean turn here is used to keep track of what symbol will be place on the next move. Everytime a move is made, turn negate itself and the behavior of the game change accordingly:
public boolean makeMove(int posX, int posY) {
if (withinBoard(posX, posY) && getSquare(posX, posY) == 0) {
setSquare(posX, posY, !turn ? -1 : 1);
turn = !turn;
SFX.playSound("/asset/beep.wav", 5); // Playing audio with volume level 5
return true;
}
return false;
}
Moreover, there are also methods like checkWin(), which just return the constants PLAYER_X_WON, PLAYER_O_WON, DRAW or GAME_ONGOING depending on the state of the game. And another method called isGameOver(), which returns whether checkWin() returns a value different other than GAME_ONGOING or not. And that’s basically all we need for a functional Tic Tac Toe game. I also added the printBoard() method at the early stage of the project to be able to play the game in the terminal by inputting the coordinates and have the board printed to the standard output, this is an example of how that works:
And that is Tic Tac Toe game in Java! Neatly packed into a single class, which I believe will help to keep memory usage low, especially helpful for lower end devices and a major performance increase from my older implementation with heavy use of Objects for similar grid based games.
3, GUI
Of course at this stage, I can’t just stop now. I’m already pretty familiar with Swing at this point already. And who wants to play Tic Tac Toe by typing out coordinates? And so another class called TicTacToeGameGUI was created. It essentially just keep an instance of TicTacToeGame, convert the matrix to a suitable graphical display, and provide mouse control for clicking on squares to make a move.
One of my mistake was how I used pretty high resolution pngs for the sprite of X and O. In my Chess Game project, that was a suitable approach because chess pieces are very complex. But here, I could have just use a graphical library to help me draw out X and O on the spot. I will keep that in mind and implement it in my extended - Caro Game, which will have a much more significant effect.
But basically, TicTacToeGameGUI uses Swing to be able to convert my text display to the standard output to a visually pleasing JFrame, with mouse control for the best gameplay experience.
// Initializing a game
game = new TicTacToeGame();
bot = new TicTacToeBot(game, 5); // More on this in part 2
panel = new JPanel();
panel.setLayout(new GridLayout(game.height, game.width));
for (int i = 0; i < game.height; i++) {
for (int j = 0; j < game.width; j++) {
JPanel square = getSquare();
panel.add(square);
}
}
// Concrete getSquare() method
private JPanel getSquare() {
JPanel square = new JPanel();
square.setBorder(BorderFactory.createLineBorder(Color.GRAY));
square.addMouseListener(new MouseAdapter() {
// Adding mouse control to each child JPanels
@Override
public void mouseReleased(MouseEvent e) {
if (!game.gameIsOver()) {
int index = panel.getComponentZOrder(square);
int x = index / game.width;
int y = index % game.width;
if (game.withinBoard(x, y)) {
boolean moveSuccess = game.makeMove(x, y);
// More on this in part 2
if (moveSuccess) {
update();
if (!game.gameIsOver()) {
Timer timer = new Timer(200, event -> {
bot.makeMove();
update();
TicTacToeBot.reach = 0;
});
timer.setRepeats(false);
timer.start();
}
}
}
}
}
});
return square;
}
// Update method
public void update() {
for (int i = 0; i < game.height; i++) {
for (int j = 0; j < game.width; j++) {
JPanel square = (JPanel) panel.getComponent(i * game.width + j);
int value = game.getSquare(i, j);
JLabel label = new JLabel();
square.removeAll();
square.setLayout(new BorderLayout());
square.add(label, BorderLayout.CENTER);
// I should have use simple graphical library to draw sprites here
try {
if (value == -1) {
label.setIcon(getImageIcon("asset/Artboard 2.png", calculateSizeImage(), calculateSizeImage()));
} else if (value == 1) {
label.setIcon(getImageIcon("asset/Artboard 1.png", calculateSizeImage(), calculateSizeImage()));
} else {
label.setIcon(null);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
square.revalidate();
square.repaint();
}
}
// Check for game over operation, new game operation is implemented elsewhere
if (game.gameIsOver()) {
try {
String msg = ", '1-9' to test different bot depths";
switch (game.checkWinTicTacToe()) {
case -1:
JOptionPane.showMessageDialog(null, "O won!" + msg, "Game Over", JOptionPane.INFORMATION_MESSAGE, getImageIcon("asset/nihahaha.png", 100, 100));
break;
case 1:
JOptionPane.showMessageDialog(null, "X won!" + msg, "Game Over", JOptionPane.INFORMATION_MESSAGE, getImageIcon("asset/nihahaha.png", 100, 100));
break;
case 0:
JOptionPane.showMessageDialog(null, "Draw!" + msg, "Game Over", JOptionPane.INFORMATION_MESSAGE, getImageIcon("asset/nihahaha.png", 100, 100));
break;
}
} catch (IOException e) {
System.out.println("Exception while loading image");
}
}
}
As you can see, there are several noteworthy points:
- TicTacToeGame is kept as an instance to allow for seamless new game operations.
- Update() simply keep track of the internal implementation of the board, which is an int[][]. And display the correct sprite or keep it empty.
- Each one of the squares is attached a MouseAdapter, to allow for precise call to the makeMove(int x, int y) method.
- Update() also keep track of whether the game is over or not through game.isGameOver(), if its true, everything will be suspended until a new game is created.
- Starting a new game is seamless and convenient simply by attaching a new TicTacToeGame to the game field and updating the frame. Which can be extracted to a separate method and envoked as needed.
You might also recognize how I kept the GUI implementation flexible, this is for if I ever want to expand to Caro Game (which I will) then everything will still work perfectly as intended and I will not have to make drastic changes to the GUI class.
Epilogue
And thats about it! A Tic Tac Toe game made in Java with Swing, this is only the part I of this post where I cover the process of making and implementing the game itself. The second part of this post will be released shortly where I cover the process of making the Tic Tac Toe bot. Thank you so much for reading and stay tuned for part II!