/* * pTicTacToe.java * * Copyright(c) 2001, Particle Corp. */ /* * NOTE: This program intentionally uses only JDK 1.0 libs, * in order to be compatible with all java enabled browsers. * (yes, even Netscape3!) */ import java.lang.Thread; import java.applet.Applet; import java.awt.*; import java.util.Vector; /** * the main game class. represents the board, has * alpha-beta proc, etc. * * @version 0.0.1 * @author Alex S. */ class pTicTacToeBoard { // some useful statics public static final int EMPTY = 0; public static final int X = 1; public static final int O = 2; public static final int DRAW = 3; // helper for toString method public static char[] to_string = {'#','X','O'}; // board private int[][] board; private int best_i; private int best_j; /* * each winning line gets a bit */ private static int[][] board_mask = { {1|8|64,2|8,4|8|128}, {1|16,2|16|64|128,4|16}, {1|32|128,2|32,4|32|64}, }; /* * each winning line gets an unset bit, all else * is set. (makes for easier masking later on) */ static { for(int i=0;i<3;i++) for(int j=0;j<3;j++) board_mask[i][j] = ~board_mask[i][j]; } /** * default constructor; allocate mem, and * init the board to empty */ public pTicTacToeBoard(){ board = new int[3][3]; for(int i=0;i<3;i++) for(int j=0;j<3;j++) board[i][j] = EMPTY; } /** * copy constructor; allocate mem, and * init the board to the passed board. */ public pTicTacToeBoard(pTicTacToeBoard b){ board = new int[3][3]; for(int i=0;i<3;i++) for(int j=0;j<3;j++) board[i][j] = b.board[i][j]; } /** * get number of winning lines for a specific player * * @param l Either pTicTacToeBoard.X or pTicTacToeBoard.O * @return number of possible winning lines for l */ public int getWinLinesForPlayer(int l){ int masked = 0xFF; int otherplayer = l == X ? O:X; for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(board[i][j] == otherplayer) masked &= board_mask[i][j]; //mask out win lines int c=0; for(int i=1;i<0x100;i<<=1) //count win lines left if((masked & i) != 0) c++; return c; } /** * evaluate board to a specific player * (this is just a diff of player's open win lines) */ public int getBoardEval(int l){ return getWinLinesForPlayer(l) - getWinLinesForPlayer(l == X ? O:X); } /** * set value at a specific board location */ public void setValOnBoard(int row,int col,int val){ board[row][col] = val; } /** * get value at a specific board location. */ public int getValOnBoard(int row,int col){ return board[row][col]; } // enable quicker end-game check private static int[][][] wingamechecks = { { {0,0},{0,1},{0,2} }, { {0,0},{1,0},{2,0} }, { {0,0},{1,1},{2,2} }, { {0,1},{1,1},{2,1} }, { {0,2},{1,2},{2,2} }, { {1,0},{1,1},{1,2} }, { {2,0},{2,1},{2,2} }, { {2,0},{1,1},{0,2} } }; /** * see if game is over. returns 0 if not, else returns * winner, or DRAW if game ended in draw. */ public int getGameWinner(){ // check for actual winner for(int i=0;i<8;i++){ int a = board[wingamechecks[i][0][0]][wingamechecks[i][0][1]]; int b = board[wingamechecks[i][1][0]][wingamechecks[i][1][1]]; int c = board[wingamechecks[i][2][0]][wingamechecks[i][2][1]]; if(a != EMPTY && a == b && b == c){ return a; } } // no winner, but maybe a draw for(int i=0;i<3;i++) for(int j=0;j<3;j++) if(board[i][j] == EMPTY) return 0; // draw ... (no spaces left for a move) return DRAW; } /** * alpha beta procedure */ public static int alphaBeta(int l,int ply,int alpha,int beta,pTicTacToeBoard board){ int winner = board.getGameWinner(); if(winner != 0){ if(winner == DRAW) alpha = 0; else alpha = winner == l ? 100:-100; // System.out.println("winner spoted:"+winner+"; alpha:"+alpha+"; depth:"+ply); }else if(ply <= 0){ alpha = board.getBoardEval(l); }else{ board.best_i = -1; board.best_j = -1; // for all children L1: for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ if(board.getValOnBoard(i,j) == EMPTY){ // make move, and recursively eval it pTicTacToeBoard moveboard = new pTicTacToeBoard(board); moveboard.setValOnBoard(i,j,l); // our negated beta becomes the other guy's alpha, etc. int val = alphaBeta(l == X ? O:X,ply-1,-beta,-alpha,moveboard); // make sure that we always have a move... if(board.best_i == -1){ board.best_i = i; board.best_j = j; } // is this a better move? if(val > alpha){ alpha = val; board.best_i = i; board.best_j = j; } if(alpha >= beta) break L1; // cutoff } } } } return -alpha; } public int makeMove(int l,int skill){ int val = alphaBeta(l,skill,-100,100,this); // System.out.println("val: "+val+" ("+best_i+","+best_j+")"); setValOnBoard(best_i,best_j,l); return getGameWinner(); } /** * return str representation of the board */ public String toString(){ StringBuffer s = new StringBuffer(); for(int i=0;i<3;i++){ for(int j=0;j<3;j++) s.append(to_string[board[i][j]]); s.append('\n'); } return s.toString(); } } /** * Applet helper class. doesn't have any real logic in it, * (leaving all the game stuff to the board class). * Provides UI for the game. * *

August 15th, 2001 - Fixed image download problem. Images * would not be donwloaded until they were being displayed. On * a slow connection, there was a noticable delay abetween the * time you clicked a mouse, and the time an image would appear. * Fix involved invoking a MediaTracker to download the images * in a seperate thread. * * @version 0.0.1 * @author Alex S. */ public class pTicTacToe extends Applet implements Runnable { private Image imageX,imageO,imageXWins,imageOWins,imageXODraw; private pTicTacToeBoard board; // track loading of images private MediaTracker tracker; private Thread m_thread; private int skill = 9; public void init(){ // initialize media tracker tracker = new MediaTracker(this); setBackground(Color.white); imageO = getImage(getCodeBase(), "pTicTacToe_O.gif"); imageX = getImage(getCodeBase(), "pTicTacToe_X.gif"); imageXWins = getImage(getCodeBase(),"pTicTacToe_XWins.gif"); imageOWins = getImage(getCodeBase(),"pTicTacToe_OWins.gif"); imageXODraw = getImage(getCodeBase(),"pTicTacToe_Draw.gif"); // track image tracker.addImage(imageO,0); tracker.addImage(imageX,1); tracker.addImage(imageXWins,2); tracker.addImage(imageOWins,3); tracker.addImage(imageXODraw,4); board = new pTicTacToeBoard(); try{ skill = Integer.parseInt(getParameter("skill")); }catch(Throwable e){ System.out.println(e.toString()); skill = 9; } } /** * start image download thread */ public void start(){ m_thread = new Thread(this); m_thread.start(); } public void stop(){ m_thread = null; } /** * download all images while the player makes a move... */ public void run(){ try { tracker.waitForAll(); } catch (InterruptedException e) { } } public void paint(Graphics g) { g.setColor(Color.black); Dimension d = size(); int xoff = d.width / 3; int yoff = d.height / 3; g.drawLine(xoff, 0, xoff, d.height); g.drawLine(2*xoff, 0, 2*xoff, d.height); g.drawLine(0, yoff, d.width, yoff); g.drawLine(0, 2*yoff, d.width, 2*yoff); for (int r = 0 ; r < 3 ; r++) { for (int c = 0 ; c < 3 ; c++) { int t = board.getValOnBoard(r,c); if (t == pTicTacToeBoard.O){ g.drawImage(imageO, c*xoff + 1, r*yoff + 1, this); } else if (t == pTicTacToeBoard.X) { g.drawImage(imageX, c*xoff + 1, r*yoff + 1, this); } } } int winner = board.getGameWinner(); if(winner != 0){ if(winner == pTicTacToeBoard.DRAW){ g.drawImage(imageXODraw,0,30, this); }else if(winner == pTicTacToeBoard.X){ g.drawImage(imageXWins,0,30, this); }else if(winner == pTicTacToeBoard.O){ g.drawImage(imageOWins,0,30, this); } } } public boolean mouseUp(Event evt, int x, int y) { // if there was a winner... if(board.getGameWinner() != 0){ board = new pTicTacToeBoard(); repaint(); }else{ Dimension d = size(); // Figure out the row/column int c = (x * 3) / d.width; int r = (y * 3) / d.height; if(board.getValOnBoard (r,c) == pTicTacToeBoard.EMPTY){ board.setValOnBoard (r,c,pTicTacToeBoard.X); repaint(); int winner = board.getGameWinner(); // if there was a winner... if(winner == 0){ board.makeMove(pTicTacToeBoard.O,skill); repaint(); } } } return true; } }