package org.deeplearning4j.examples.tictactoe;
import org.datavec.api.util.ClassPathResource;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
/**
* <b>Developed by KIT Solutions Pvt. Ltd.</b> (www.kitsol.com) on 24-Aug-16
* This is a GUI to play game using trained network.
*/
public class PlayTicTacToe extends JFrame {
private String playerInformation = "FirstPlayer:X";
private JFrame frame = new JFrame("TicTacToe");
private JButton[] gridMoveButton = new JButton[9];
private JButton startButton = new JButton("Start");
private JButton switchButton = new JButton("Switch Player");
private JLabel infoLabel = new JLabel(playerInformation);
private boolean isAIFirstPlayer = true;
private int xWon = 0;
private int oWon = 0;
private int draw = 0;
private TicTacToePlayer ticTacToePlayer;
private Thread aiLoad;
/**
* Constructor that loads trained data and initializes TicTacToePlayer object to play the game.
* Also, initializes the GUI and display it.
*/
public PlayTicTacToe() throws HeadlessException {
String filePath = "";
try {
filePath = new ClassPathResource("TicTacToe").getFile().getAbsolutePath() + File.separator + "AllMoveWithReward.txt";
} catch (Exception e) {
System.out.println("FilePathException" + e.toString());
}
ticTacToePlayer = new TicTacToePlayer();
ticTacToePlayer.setFilePath(filePath);
aiLoad = new Thread(ticTacToePlayer);
aiLoad.start();
ticTacToePlayer.setUpdateLimit(10);
ticTacToePlayer.setAutoUpdate(true);
frame.setSize(350, 450);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
}
public static void main(String[] args) {
PlayTicTacToe game = new PlayTicTacToe();
game.renderGUI();
}
/**
* Create the GUI for tictactoe game with 9 move button,two utility button.
*/
private void renderGUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel menu = new JPanel(new BorderLayout());
JPanel tital = new JPanel(new BorderLayout());
JPanel game = new JPanel(new GridLayout(3, 3));
frame.add(mainPanel);
mainPanel.setPreferredSize(new Dimension(325, 425));
menu.setPreferredSize(new Dimension(300, 50));
tital.setPreferredSize(new Dimension(300, 50));
game.setPreferredSize(new Dimension(300, 300));
//Create the basic layout for game
mainPanel.add(menu, BorderLayout.NORTH);
mainPanel.add(tital, BorderLayout.AFTER_LINE_ENDS);
mainPanel.add(game, BorderLayout.SOUTH);
tital.add(infoLabel, BorderLayout.CENTER);
menu.add(startButton, BorderLayout.WEST);
menu.add(switchButton, BorderLayout.EAST);
//Create the 9 Grid button on UI
for (int i = 0; i < 9; i++) {
gridMoveButton[i] = new JButton();
gridMoveButton[i].setText(" ");
gridMoveButton[i].setVisible(true);
gridMoveButton[i].setEnabled(false);
gridMoveButton[i].addActionListener(new PlayTicTacToe.MyActionListener(i));
game.add(gridMoveButton[i]);
}
game.setVisible(true);
startButton.setEnabled(false);
switchButton.setEnabled(false);
//Start Button Click Listener.
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
reset(); //Reset GUI
changeGridButtonAccessibility(true);
switchButton.setEnabled(true);
if (isAIFirstPlayer == true) {
INDArray firstMove = Nd4j.zeros(1, 9);
INDArray nextMove = ticTacToePlayer.getNextBestMove(firstMove, 1);
updateMoveOnBoard(nextMove);
}
}
});
//switch Button Click Listener
switchButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (isAIFirstPlayer == true) {
playerInformation = "FirstPlayer:O";
isAIFirstPlayer = false;
} else {
playerInformation = "FirstPlayer:X";
isAIFirstPlayer = true;
}
reset(); //Reset GUI
updateInformation();
}
});
while (true) {
if (ticTacToePlayer.isAILoad() == true) {
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
startButton.setEnabled(true);
switchButton.setEnabled(true);
}
/**
* Update the GUI depending upon the move provided by TicTacToePlayer or by manual player
*/
private void updateMoveOnBoard(INDArray board) {
if (board == null) {
return;
}
if (isAIFirstPlayer == true) {
for (int i = 0; i < 9; i++) {
if (((int) board.getDouble(i)) == 1) {
gridMoveButton[i].setText("X");
} else if (((int) board.getDouble(i)) == 2) {
gridMoveButton[i].setText("O");
}
}
} else {
for (int i = 0; i < 9; i++) {
if (((int) board.getDouble(i)) == 1) {
gridMoveButton[i].setText("O");
} else if (((int) board.getDouble(i)) == 2) {
gridMoveButton[i].setText("X");
}
}
}
}
/**
* Reset the button for and also reset the TicTacToe player object after game is finished.
*/
private void reset() {
for (int i = 0; i < 9; i++) {
gridMoveButton[i].setText(" ");
gridMoveButton[i].setEnabled(false);
}
ticTacToePlayer.reset();
}
/**
* Enable or disable the game by disabling all buttons.
*/
private void changeGridButtonAccessibility(boolean enable) {
for (int i = 0; i < 9; i++) {
gridMoveButton[i].setEnabled(enable);
}
}
/**
* This function gives the current state board in INDArray
*/
private INDArray getCurrentStateOfBoard() {
INDArray positionArray = Nd4j.zeros(1, 9);
for (int i = 0; i < 9; i++) {
String gridMoveButtonValue = gridMoveButton[i].getText();
if (isAIFirstPlayer == true) {
if (gridMoveButtonValue.equals("X")) {
positionArray.putScalar(new int[]{0, i}, 1);
} else if (gridMoveButtonValue.equals("O")) {
positionArray.putScalar(new int[]{0, i}, 2);
}
} else {
if (gridMoveButtonValue.equals("O")) {
positionArray.putScalar(new int[]{0, i}, 1);
} else if (gridMoveButtonValue.equals("X")) {
positionArray.putScalar(new int[]{0, i}, 2);
}
}
}
return positionArray;
}
/**
* This method update the UI(Board) for click event on any button on the board and
* immediately calls automatic user to play the next move.
*/
private void userNextMove(int indexPosition) {
String gridMoveButtonText = gridMoveButton[indexPosition].getText();
if (gridMoveButtonText.equals(" ")) {
gridMoveButton[indexPosition].setText("O");
playUsingAI();
}
}
/**
* This method is used for playing next move by machine itself.
*/
private void playUsingAI() {
INDArray currentBoard = getCurrentStateOfBoard();
INDArray nextMove = null;
boolean gameFinish = false;
int gameState = 0;
if (isAIFirstPlayer == true) {
ticTacToePlayer.addBoardToList(currentBoard, 2);
if (isGameFinish() == true) {
gameFinish = true;
} else {
nextMove = ticTacToePlayer.getNextBestMove(currentBoard, 1);
gameState = ticTacToePlayer.getGameDecision();
}
} else {
ticTacToePlayer.addBoardToList(currentBoard, 1);
if (isGameFinish() == true) {
gameFinish = true;
} else {
nextMove = ticTacToePlayer.getNextBestMove(currentBoard, 2);
gameState = ticTacToePlayer.getGameDecision();
}
}
if (gameFinish == true) {
updateInformation();
} else {
if (nextMove != null) {
updateMoveOnBoard(nextMove);
}
if (gameState != 0) {
if (isAIFirstPlayer == true) {
if (gameState == 1) {
xWon++;
} else if (gameState == 2) {
oWon++;
} else {
draw++;
}
} else {
if (gameState == 1) {
oWon++;
} else if (gameState == 2) {
xWon++;
} else {
draw++;
}
}
updateInformation();
}
}
}
/**
* Updates statisitical information about each user in terms of how many games both user won or lost and drawn also.
*/
private void updateInformation() {
String updateInformation = playerInformation + " X:" + String.valueOf(xWon) + " O:" + String.valueOf(oWon) + " Draw:" + String.valueOf(draw);
infoLabel.setText(updateInformation);
changeGridButtonAccessibility(false);
}
/**
* checks is game is finished. It checks it from TicTacToePlayer object.
*/
private boolean isGameFinish() {
int result = ticTacToePlayer.getGameDecision();
if (result != 0) {
if (result == 1) {
if (isAIFirstPlayer == true) {
xWon++;
} else {
oWon++;
}
} else if (result == 2) {
if (isAIFirstPlayer == false) {
xWon++;
} else {
oWon++;
}
} else {
draw++;
}
return true;
}
return false;
}
/**
* This is Action listener for move buttons
*/
private class MyActionListener implements ActionListener {
private int index;
public MyActionListener(int index) {
this.index = index;
}
@Override
public void actionPerformed(ActionEvent e) {
userNextMove(index);
}
}
}