/* Bradley O'Farrell 05-18-05 Displays a Tic Tac Toe game in an applet. */ import java.util.*; import javax.swing.*; import BreezySwing.*; public class JTic extends GBApplet { private int gridHeight; private int gridWidth; private int percentAI; private JButton[][] box; private char[][] mark; private GBApplet applet = new GBApplet(); private Random randomGenerator = new Random(); private JLabel gameInfo = new JLabel(); private int par = 1000; private char user; private char comp; private int marks; private int userScore; private int compScore; private int draws; private int turns; private int games; //------------------------------------------------------------------------------------------------- // Interface //------------------------------------------------------------------------------------------------- public void init() { //Get parameters String importParamStr; importParamStr = getParameter("Grid_Height"); gridHeight = (new Integer(importParamStr)).intValue(); importParamStr = getParameter("Grid_Width"); gridWidth = (new Integer(importParamStr)).intValue(); importParamStr = getParameter("AI_%"); percentAI = (new Integer(importParamStr)).intValue(); while((gridHeight * gridWidth) > 25) { if(gridHeight > gridWidth) gridHeight--; else gridWidth--; } if(gridWidth < 3) gridWidth = 3; if(gridHeight < 3) gridHeight = 3; if(percentAI > 100) percentAI = 100; if(percentAI < 0) percentAI = 0; box = new JButton[gridHeight][gridWidth]; mark = new char[gridHeight][gridWidth]; //Set up buttons for(int row = 0; row < box.length; row++) { for(int col = 0; col < box[0].length; col++) { box[row][col] = addButton (null,(row+2),(col+1),1,1); } } gameInfo = addLabel (null,1,1,gridWidth,gridHeight); //Set starting values resetGame(); } public void buttonClicked(JButton buttonObj) { //Try-catch is for debugging only try { //Get the button's number int row = 0; int col = 0; for(int btnRow = 0; btnRow < box.length; btnRow++) { for(int btnCol = 0; btnCol < box[0].length; btnCol++) { if(buttonObj == box[btnRow][btnCol]) { row = btnRow; col = btnCol; } } } //Respond to the button clicked turns++; if(mark[row][col] == ' ') { mark[row][col] = user; marks++; updateScreen(); if(checkForWin(user, mark)) userWins(); else if (checkForDraw(mark)) draw(); else runAI(); } else if(mark[row][col] == comp) this.messageBox("This space is already taken."); else if(mark[row][col] == user) this.messageBox("You've already marked here."); } catch (Exception e) { this.messageBox(e+""); } } public void updateScreen() { gameInfo.setText("JTic - Computer ("+comp+") score: "+compScore+" User ("+user+") score: "+userScore); for(int row = 0; row < box.length; row++) { for(int col = 0; col < box[0].length; col++) { box[row][col].setText(mark[row][col]+""); } } } public void endOfGameMessage(String msg) { String tempPar = Format.justify('l',par, 3); if(tempPar.equals("***")) tempPar = " "; this.messageBox(msg+ "\nTurns: "+Format.justify('l',turns, 3)+" Games: "+Format.justify('l', games, 3)+ " Wins: "+Format.justify('l', userScore, 3)+"\nPar: "+Format.justify('l',tempPar, 3)+ " Draws: "+Format.justify('l', draws, 3)+" Loses: "+Format.justify('l', compScore, 3)); resetGame(); } //------------------------------------------------------------------------------------------------- // Methods that deal with winning and losing. //------------------------------------------------------------------------------------------------- public boolean checkForWin(char who, char[][] board) { boolean winStillPossible = true; //Check for a horizontal (-) win for(int row = 0; row < board.length; row++) { winStillPossible = true; for(int col = 0; col < board[0].length && winStillPossible; col++) { if(board[row][col] == who) { if(col == (board[0].length - 1)) return true; } else { winStillPossible = false; } } } //Check for a vertical (|) win for(int col = 0; col < board[0].length; col++) { winStillPossible = true; for(int row = 0; row < board.length && winStillPossible; row++) { if(board[row][col] == who) { if(row == (board.length - 1)) return true; } else { winStillPossible = false; } } } //Check for a downward diagonal (\) win for(int diagonal = 0; diagonal < (1 + Math.abs(board.length - board[0].length)); diagonal++) { winStillPossible = true; for(int currentBox = 0; (currentBox < Math.min(board.length, board[0].length)) && winStillPossible ; currentBox++) { if(board.length <= board[0].length && board[currentBox][currentBox + diagonal] == who) { if(currentBox == (Math.min(board.length, board[0].length) - 1)) return true; } else if(board.length > board[0].length && board[currentBox + diagonal][currentBox] == who) { if(currentBox == (Math.min(board.length, board[0].length) - 1)) return true; } else { winStillPossible = false; } } } //Check for a upward diagonal (/) win for(int diagonal = 0; diagonal < (1 + Math.abs(board.length - board[0].length)); diagonal++) { winStillPossible = true; int row = board.length - 1; int col = 0; if(board.length <= board[0].length) { while(row >= 0 && winStillPossible) { if(board[row][col + diagonal] == who) { if(row == 0) return true; } else { winStillPossible = false; } col++; row--; } } else if(board.length > board[0].length) { while(col < (board[0].length) && winStillPossible) { if(board[row - diagonal][col] == who) { if(col == (board[0].length - 1)) { return true; } } else { winStillPossible = false; } //this.messageBox("R:"+row+"C:"+col+"WSP:"+winStillPossible); col++; row--; } } } return false; } public boolean checkForDraw(char[][] board) { boolean draw = true; for(int row = 0; row < board.length; row++) { for(int col = 0; col < board[0].length; col++) { if(board[row][col] == ' ') draw = false; } } return draw; } public void userWins() { if(turns < par) par = turns; games++; userScore++; endOfGameMessage("You win!"); resetGame(); } public void compWins() { games++; compScore++; endOfGameMessage("The computer wins!"); resetGame(); } public void draw() { games++; draws++; endOfGameMessage("It's a draw!"); resetGame(); } public void resetGame() { //Give a random mark to the computer and player int tempC; int tempU; do { tempC = randomGenerator.nextInt(218) + 32; tempU = randomGenerator.nextInt(218) + 32; comp = (char)tempC; user = (char)tempU; } while(comp == user || comp == ' ' || user == ' ' || (tempC >= 127 && tempC <= 160) || (tempU >= 127 && tempU <= 160)); //Clear board for(int row = 0; row < mark.length; row++) { for(int col = 0; col < mark[0].length; col++) { mark[row][col] = ' '; } } turns = 0; marks = 0; updateScreen(); } //------------------------------------------------------------------------------------------------- // Helper methods //------------------------------------------------------------------------------------------------- public char[][] copyBoard(char[][] oldBoard) { char[][] newBoard = new char[oldBoard.length][oldBoard[0].length]; for(int row = 0; row < oldBoard.length; row++) { for(int col = 0; col < oldBoard[0].length; col++) { newBoard[row][col] = oldBoard[row][col]; } } return newBoard; } public boolean doesSpaceBlockWin(char who, char[][] board, int row, int col) { char[][] nextBoard = copyBoard(board); nextBoard[row][col] = who; return (checkForWin(who, nextBoard)); } //------------------------------------------------------------------------------------------------- // AI methods //------------------------------------------------------------------------------------------------- public void makeSafeRandomMove() { int safeRow = -1; int safeCol = -1; for(int row = 0; row < mark.length; row++) { for(int col = 0; col < mark[0].length; col++) { if(mark[row][col] == ' ' && doesSpaceBlockWin(user, mark, row, col)) { safeRow = row; safeCol = col; } } } if(safeRow >= 0) mark[safeRow][safeCol] = comp; else makeRandomMove(); } public void makeRandomMove() { int row; int col; do { row = randomGenerator.nextInt(box.length); col = randomGenerator.nextInt(box[0].length); } while(mark[row][col] != ' '); mark[row][col] = comp; } public void makeBestMove(char who, char[][] board, int layer, int moveRank) { if(layer < (Math.max(board.length, board[0].length) * 2)) { char[][] nextBoard; int nextMoveRank = moveRank; int nextLayer = layer; char nextMark = who; int lowestMoveRank = 10000; int bestRow = 0; int bestCol = 0; if(who == comp) nextMark = user; else if(who == user) nextMark = comp; for(int row = 0; row < board.length; row++) { for(int col = 0; col < board[0].length; col++) { if(board[row][col] == ' ') { nextMoveRank = moveRank; nextBoard = copyBoard(board); nextBoard[row][col] = who; //Evaluates and ranks the ultimate outcome of each move. if(checkForWin(who, nextBoard)) nextMoveRank -= 2000; else if(doesSpaceBlockWin(nextMark, board, row, col)) nextMoveRank -= 1000; else if(checkForDraw(nextBoard)) nextMoveRank += 1000; else if(checkForWin(nextMark, nextBoard)) nextMoveRank += 2000; else makeBestMove(nextMark, nextBoard, nextLayer++, nextMoveRank++); //If the move is lower than the best move, it becomes the new best move. if(nextMoveRank < lowestMoveRank) { lowestMoveRank = nextMoveRank; bestRow = row; bestCol = col; } nextMoveRank = 1000; } } } //The best move is played board[bestRow][bestCol] = who; moveRank = lowestMoveRank; } } public void runAI() { int compareAI = randomGenerator.nextInt(percentAI); int lagLimit = (gridWidth * gridHeight); int spacesLeft = ((gridWidth * gridHeight) - marks); if(lagLimit > 10) lagLimit = 10; if(percentAI < 50) { if(compareAI > 25) makeRandomMove(); else makeSafeRandomMove(); } else { if(compareAI < 25 || spacesLeft > lagLimit) makeSafeRandomMove(); else { makeBestMove(comp, mark, 0, 0); } } marks++; updateScreen(); if(checkForWin(comp, mark)) compWins(); else if(checkForDraw(mark)) draw(); } }