Using SWT:
package com.apps;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* The Tic Tac Toe Game.
*
* @author Ashok Kumar P S
*/
public class TicTacToe {
/** Representing an empty cell. **/
private final static int EMPTY = 0;
/** Representing an user filled cell. **/
private final static int USER = 1;
/** Representing system cell. **/
private final static int SYSTEM = 2;
/** Icon for user cell. **/
private final static String USER_ICON = "resources/images/User.png";
/** Icon for system cell. **/
private final static String SYSTEM_ICON = "resources/images/System.png";
/** Icon for Shell. **/
private final static String SHELL_ICON = "resources/images/TicTacToe.png";
/** Board data storage. **/
private int[][] board;
/** Buttons for cells. **/
private Button[][] btnCells;
/** Used to specify the game is finished or not. **/
private boolean gameFinished;
/** Represents the winner. **/
private int winner;
/** Stores the cell position which is currently selected by user. **/
private Position posUserSelected;
private Label lblMessage;
private Image imgSystem, imgUser, imgEmpty, imgTicTacToe;
private Font fntMsgText;
private Shell shell;
private Display display;
public TicTacToe() {
init();
btnCells = new Button[3][3];
}
/**
* Initializes/resets the board and other required fields.
*/
private void init() {
board = new int[3][3];
for (int i = 0; i < 3; i++) {
Arrays.fill(board[i], EMPTY);
}
gameFinished = false;
posUserSelected = null;
winner = EMPTY;
if (lblMessage != null) {
lblMessage.setText("");
}
}
/**
* Fill the board cells with images based on the board array.
*/
private void fillBoard() {
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
final int col = board[i][j];
// Set the image when need to reset the game or after selecting
// the cell.
if (col == EMPTY || btnCells[i][j].getImage() == imgEmpty) {
btnCells[i][j].setImage(col == USER ? imgUser
: (col == SYSTEM ? imgSystem : imgEmpty));
}
}
}
}
/**
* Populate the given statistics for the board, row and column data.
*
* @param stat
* - Statistics to be populated.
* @param board
* - 2D array represents the board data.
* @param row
* - Row index.
* @param col
* - Column index.
*/
private void populateStat(Statistics stat, int[][] board, int row, int col) {
if (board[row][col] == USER) {
stat.userCells = stat.userCells + 1;
} else if (board[row][col] == SYSTEM) {
stat.systemCells = stat.systemCells + 1;
} else {
stat.listAvailPos.add(new Position(row, col));
}
}
/**
* Prepare the rows/columns statistics for the given board.
*
* @param board
* @return List of statistics.
*/
private List<Statistics> prepareStatistics(int[][] board) {
// Horizontal.
List<Statistics> stats = new ArrayList<Statistics>(8);
for (int i = 0; i < 3; i++) {
Statistics stat = new Statistics();
for (int j = 0; j < 3; j++) {
populateStat(stat, board, i, j);
}
stats.add(stat);
}
// Vertical.
for (int i = 0; i < 3; i++) {
Statistics stat = new Statistics();
for (int j = 0; j < 3; j++) {
populateStat(stat, board, j, i);
}
stats.add(stat);
}
// Diagonal.
for (int diag = 1; diag <= 2; diag++) {
Statistics stat = new Statistics();
if (diag == 1) {
// Forward.
for (int j = 0; j < 3; j++) {
populateStat(stat, board, j, j);
}
} else {
// Backward.
for (int i = 0, j = 2; i < 3; i++, j--) {
populateStat(stat, board, i, j);
}
}
stats.add(stat);
}
return stats;
}
/**
* Checks whether the board is over - User/System won the game or no more
* space to play.
*
* @param stats
* - The statistics of the board.
* @return
*/
private boolean isBoardOver(List<Statistics> stats) {
gameFinished = true;
// Trying to find out the winner.
for (Statistics stat : stats) {
if (stat.userCells == 3 || stat.systemCells == 3) {
winner = stat.userCells == 3 ? USER
: (stat.systemCells == 3 ? SYSTEM : EMPTY);
break;
}
}
// Trying to find whether the board is full or not.
if (winner == EMPTY) {
for (int[] row : board) {
for (int col : row) {
if (col == EMPTY) {
gameFinished = false;
}
}
}
}
return gameFinished;
}
/**
* Finds all available empty cells around the given position in the board.
*
* @param pos
* - Around this cell all available/empty cells will be found.
* @return List of available cells that can be filled.
*/
private List<Position> findAvailPosAround(Position pos) {
List<Position> listPos = new ArrayList<Position>();
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
int row = pos.row + i, col = pos.col + j;
if (row >= 0 && row <= 2 && col >= 0 && col <= 2
&& (row == pos.row && col == pos.col) == false
&& board[row][col] == EMPTY) {
listPos.add(new Position(row, col));
}
}
}
return listPos;
}
/**
* Finds the best position to place the system icon.
*
* @param listPos
* @return
*/
private Position findBestPosToPlace(List<Position> listPos) {
int[][] boardCopy = new int[3][];
// The temporary board used for keeping the temporary cells to find
// statistics.
for (int i = 0; i < board.length; i++) {
boardCopy[i] = board[i];
}
// Each empty cell (listPos) with its best statistics.
Map<Statistics, Position> mapStats = new LinkedHashMap<Statistics, Position>();
StatisticsComparator statComparator = new StatisticsComparator();
for (Position pos : listPos) {
boardCopy[pos.row][pos.col] = SYSTEM;
List<Statistics> listStats = prepareStatistics(boardCopy);
// Sorting statistics based on the number of system cells in the row
// and available empty space in that row.
Collections.sort(listStats, statComparator);
// Taking the obvious statistics.
mapStats.put(listStats.get(0), pos);
// Resetting the board.
boardCopy[pos.row][pos.col] = EMPTY;
}
List<Statistics> listStats = new ArrayList<Statistics>(mapStats
.keySet());
// Again sorting and taking the obvious statistics.
Collections.sort(listStats, statComparator);
// Returning the relevant position the found statistics.
return mapStats.get(listStats.get(0));
}
/**
* Place the system icon based on the current board statistics and the
* user's current cell selection.
*
* @param stats
*/
private void placeSystemIcon(List<Statistics> stats) {
Position pos = null;
// For defending the opponent from winning or finish the game by keeping
// the winning system cell.
for (Statistics stat : stats) {
if (stat.systemCells == 2 || stat.userCells == 2) {
if (stat.listAvailPos.size() > 0) {
pos = stat.listAvailPos.get(0);
// On placing the third system cell, the game finishes. So
// breaking the loop.
if (stat.systemCells == 2) {
break;
}
}
}
}
// Trying to position the system cell for winning line.
if (pos == null) {
// Finding available positions around the current selection.
List<Position> listPos = findAvailPosAround(posUserSelected);
// Find if found position will be also useful for system to win or
// form the sequence.
pos = findBestPosToPlace(listPos);
}
board[pos.row][pos.col] = SYSTEM;
}
/**
* Fills the board with the user selection & system selection and validate
* the board status.
*/
private void fillAndValidateBoard() {
board[posUserSelected.row][posUserSelected.col] = USER;
fillBoard();
List<Statistics> stats = prepareStatistics(board);
if (isBoardOver(stats)) {
return;
}
// Place system cell.
placeSystemIcon(stats);
stats = prepareStatistics(board);
fillBoard();
if (isBoardOver(stats)) {
return;
}
}
/**
* When the user selects a cell this method populates the image and find a
* system cell as next step.
*
* @param pos
* - User selected cell.
*/
private void onCellSelected(Position pos) {
if (board[pos.row][pos.col] != EMPTY || gameFinished) {
return;
}
posUserSelected = pos;
fillAndValidateBoard();
// Print the message with game status.
if (gameFinished) {
if (winner == EMPTY) {
lblMessage.setText("The game is Tied.");
} else {
lblMessage.setText(winner == SYSTEM ? "System won the game."
: "You won the game.");
}
}
}
private void startGame() {
shell = new Shell(SWT.MIN | SWT.CLOSE);
display = shell.getDisplay();
try {
shell.setText("Tic Tac Toe - The Game");
GridLayout layout = new GridLayout(1, true);
shell.setLayout(layout);
imgTicTacToe = new Image(display, SHELL_ICON);
shell.setImage(imgTicTacToe);
Composite compTable = new Composite(shell, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
GridData gridData = new GridData(GridData.CENTER, GridData.CENTER,
true, true);
gridData.widthHint = 100;
gridData.heightHint = 100;
compTable.setLayout(gridLayout);
fntMsgText = new Font(display, new FontData("Arial", 10, SWT.BOLD));
imgSystem = new Image(display, SYSTEM_ICON);
imgUser = new Image(display, USER_ICON);
imgEmpty = new Image(display, 1, 1);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
btnCells[i][j] = new Button(compTable, SWT.PUSH);
btnCells[i][j].setLayoutData(gridData);
btnCells[i][j].setAlignment(SWT.CENTER);
btnCells[i][j].setData(new Position(i, j));
btnCells[i][j].setImage(imgEmpty);
btnCells[i][j].addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Button btn = (Button) e.widget;
onCellSelected((Position) btn.getData());
}
});
}
}
lblMessage = new Label(shell, SWT.WRAP);
lblMessage.setFont(fntMsgText);
lblMessage.setForeground(display.getSystemColor(SWT.COLOR_RED));
lblMessage.setText("");
lblMessage.setAlignment(SWT.CENTER);
gridData = new GridData(GridData.CENTER, GridData.CENTER, true,
true);
gridData.minimumWidth = 200;
lblMessage.setLayoutData(gridData);
Composite compButtons = new Composite(shell, SWT.NONE);
compButtons.setLayout(new GridLayout(2, true));
gridData = new GridData(GridData.CENTER, GridData.CENTER, true,
true);
compButtons.setLayoutData(gridData);
Button btnPlayAgain = new Button(compButtons, SWT.NONE);
btnPlayAgain.setText("Play Again!");
btnPlayAgain.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
init();
fillBoard();
}
});
Button btnExit = new Button(compButtons, SWT.NONE);
btnExit.setText("Exit");
btnExit.setLayoutData(gridData);
btnExit.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
shell.close();
}
});
shell.pack();
Rectangle clientArea = display.getClientArea();
Rectangle shellBounds = shell.getBounds();
shell.setBounds(clientArea.width / 2 - shellBounds.width / 2,
clientArea.height / 2 - shellBounds.height / 2,
shellBounds.width, shellBounds.height);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
} finally {
fntMsgText.dispose();
imgSystem.dispose();
imgUser.dispose();
imgEmpty.dispose();
imgTicTacToe.dispose();
}
}
public static void main(String[] args) {
TicTacToe ticTacToe = new TicTacToe();
ticTacToe.startGame();
}
/**
* Stores the row and column number - cell position.
*/
class Position {
public int row = 0, col = 0;
public Position() {
}
public Position(int row, int col) {
this.row = row;
this.col = col;
}
public String toString() {
return "(" + row + ", " + col + ")";
}
}
/**
* To store the statistics of each sequence in the board.
*/
class Statistics {
// Number of existing user/system entries in a row of cells.
public int userCells = 0, systemCells = 0;
// List of empty positions available in this row of cells.
public List<Position> listAvailPos = new ArrayList<Position>(3);
public String toString() {
return "\n[listAvailPos=" + listAvailPos + ", systemCells="
+ systemCells + ", userCells=" + userCells + "]";
}
}
/**
* Comparator for sorting Statistics in ascending order based on its best
* fit.
*/
class StatisticsComparator implements Comparator<Statistics> {
public int compare(Statistics stat1, Statistics stat2) {
if (stat2.systemCells > stat1.systemCells
&& stat2.listAvailPos.size() > 0) {
return 1;
} else if (stat2.systemCells == stat1.systemCells
&& stat2.listAvailPos.size() > stat1.listAvailPos.size()) {
return 1;
}
return 0;
}
}
}
Read more...