import java.applet.*;import java.awt.*;import java.awt.event.*; public class Tictactoe2 extends Applet implements ActionListener { Button squares[]; int squaresRemaining; Button resetBtn; Button easyBtn; Button hardBtn; Button expertBtn; Button perfectBtn; int strategy; static final int RANDOM = 0; static final int HARD = 1; static final int EXPERT = 2; static final int PERFECT = 3; boolean[] levelsEnabled = new boolean[4]; static final int O = 1; static final int X = -1; int caddyCorner = -1; int borderedCorner = -1; public void init() { strategy = RANDOM; // set layout and size Panel controlPanel = new Panel(); Panel buttonPanel1 = new Panel(); Panel buttonPanel2 = new Panel(); Panel buttonPanel3 = new Panel(); controlPanel.setLayout(new GridLayout(1,5)); buttonPanel1.setLayout(new GridLayout(1,3)); buttonPanel2.setLayout(new GridLayout(1,3)); buttonPanel3.setLayout(new GridLayout(1,3)); setLayout(new GridLayout(5,1)); setSize(350,350); easyBtn = new Button(); easyBtn.addActionListener(this); easyBtn.setFont(new Font("Serif",Font.BOLD,20)); easyBtn.setLabel("Easy"); hardBtn = new Button(); hardBtn.addActionListener(this); hardBtn.setFont(new Font("Serif",Font.PLAIN,10)); hardBtn.setLabel("Hard"); expertBtn = new Button(); expertBtn.addActionListener(this); expertBtn.setFont(new Font("Serif",Font.PLAIN,10)); expertBtn.setLabel("Expert"); perfectBtn = new Button(); perfectBtn.addActionListener(this); perfectBtn.setFont(new Font("Serif",Font.PLAIN,10)); perfectBtn.setLabel("Perfect"); // disable non-random strats at first. they need to be unlocked levelsEnabled[RANDOM] = true; hardBtn.setEnabled(false); expertBtn.setEnabled(false); perfectBtn.setEnabled(false); resetBtn = new Button(); resetBtn.addActionListener(this); resetBtn.setFont(new Font("Serif",Font.BOLD,13)); resetBtn.setLabel("Reset"); controlPanel.add(resetBtn); controlPanel.add(easyBtn); controlPanel.add(hardBtn); controlPanel.add(expertBtn); controlPanel.add(perfectBtn); Label myLbl = new Label("A Tic-Tac-Toe Game", Label.CENTER); add(myLbl); add(controlPanel); squaresRemaining = 9; // add buttons squares = new Button[9]; for ( int i = 0; i < 9; i++ ) { squares[i] = new Button(); squares[i].addActionListener(this); squares[i].setFont(new Font("Serif",Font.BOLD,20)); squares[i].setBackground(Color.gray); if (i== 0 || i== 1 || i== 2) buttonPanel1.add(squares[i]); if (i== 3 || i== 4 || i== 5) buttonPanel2.add(squares[i]); if (i== 6 || i== 7 || i== 8) buttonPanel3.add(squares[i]); } add(controlPanel); add(buttonPanel1); add(buttonPanel2); add(buttonPanel3); } // end init public void actionPerformed(ActionEvent e) { // main logic is here when user clicks a square if (e.getSource() == resetBtn){ reset(); return; }else if (e.getSource() == easyBtn){ setStrat(RANDOM); return; } else if (e.getSource() == hardBtn){ setStrat(HARD); return; } else if (e.getSource() == expertBtn){ setStrat(EXPERT); return; } else if (e.getSource() == perfectBtn){ setStrat(PERFECT); return; } else { // traverse each square to see which was clicked for ( int i=0; i<9; i++ ) { if ( e.getSource() == squares[i] ) { // main logic is here when user clicks a square // if square is already occupied, do nothing if ( squares[i].getLabel().equals("X") || squares[i].getLabel().equals("O") ) { return; } // user takes a square squares[i].setLabel("X"); squaresRemaining--; // detect for win String winner = ""; winner = detectWin(); if ( winner.equals("X") || winner.equals("O")) { endGame(); } else { if ( squaresRemaining <= 0 ) { tieGame(); // Tie Game endGame(); // Tie Game } else { // computer makes a move computerMove(); squaresRemaining--; winner = detectWin(); if ( winner.equals("X") || winner.equals("O") || squaresRemaining <= 0 ) { endGame(); // if winner or tie } } } } } } } public String detectWin(){ // returns "X", "O", or "NO WINNER FOUND!" if ( !(squares[1].getLabel().equals("")) && squares[0].getLabel().equals(squares[1].getLabel()) && squares[1].getLabel().equals(squares[2].getLabel()) ) { markWinner(0,1,2); return squares[1].getLabel(); // top row } if ( !(squares[4].getLabel().equals("")) && squares[3].getLabel().equals(squares[4].getLabel()) && squares[4].getLabel().equals(squares[5].getLabel()) ) { markWinner(3,4,5); return squares[4].getLabel(); // mid row } if ( !(squares[7].getLabel().equals("")) && squares[6].getLabel().equals(squares[7].getLabel()) && squares[7].getLabel().equals(squares[8].getLabel()) ) { markWinner(6,7,8); return squares[7].getLabel(); // bottom row } if ( !(squares[3].getLabel().equals("")) && squares[0].getLabel().equals(squares[3].getLabel()) && squares[3].getLabel().equals(squares[6].getLabel()) ) { markWinner(0,3,6); return squares[3].getLabel(); // left col } if ( !(squares[4].getLabel().equals("")) && squares[1].getLabel().equals(squares[4].getLabel()) && squares[4].getLabel().equals(squares[7].getLabel()) ) { markWinner(1,4,7); return squares[4].getLabel(); // mid col } if ( !(squares[5].getLabel().equals("")) && squares[2].getLabel().equals(squares[5].getLabel()) && squares[5].getLabel().equals(squares[8].getLabel()) ) { markWinner(2,5,8); return squares[5].getLabel(); // right col } if ( !(squares[4].getLabel().equals("")) && squares[0].getLabel().equals(squares[4].getLabel()) && squares[4].getLabel().equals(squares[8].getLabel()) ) { markWinner(0,4,8); return squares[4].getLabel(); // top left to bot right } if ( !(squares[4].getLabel().equals("")) && squares[2].getLabel().equals(squares[4].getLabel()) && squares[4].getLabel().equals(squares[6].getLabel()) ) { markWinner(2,4,6); return squares[4].getLabel(); // top right to bot left } return "NO WINNER FOUND!"; } public void markWinner(int a, int b, int c){ // mark line red if user (X) wins // mark line blue if computer (O) wins if ( squares[a].getLabel().equals("X") ) { squares[a].setBackground(Color.red); squares[b].setBackground(Color.red); squares[c].setBackground(Color.red); // if X (the user) wins, enable next level of difficulty // unless already perfect if (strategy == RANDOM){ levelsEnabled[strategy+1] = true; } else if (strategy == HARD){ levelsEnabled[strategy+1] = true; } else if (strategy == EXPERT){ levelsEnabled[strategy+1] = true; } } else { squares[a].setBackground(Color.blue); squares[b].setBackground(Color.blue); squares[c].setBackground(Color.blue); } } public void tieGame(){ //color black x String tempLbl; for ( int i=0; i<9; i+=2) { tempLbl = squares[i].getLabel(); squares[i].setBackground(Color.black); squares[i].setForeground(Color.red); squares[i].setLabel(tempLbl); squares[i].setForeground(Color.black); } } public void endGame(){ // disable all squares for ( int i=0; i<9; i++) squares[i].setEnabled(false); //enable strategy buttons if (levelsEnabled[RANDOM]) easyBtn.setEnabled(true); if (levelsEnabled[HARD]) hardBtn.setEnabled(true); if (levelsEnabled[EXPERT]) expertBtn.setEnabled(true); if (levelsEnabled[PERFECT]) perfectBtn.setEnabled(true); } public void computerMove(){ if (strategy == RANDOM) moveRandom(); else if (strategy == HARD) moveHard(); else if (strategy == EXPERT) moveExpert(); else if (strategy == PERFECT) movePerfect(); } public void moveRandom(){ boolean foundMove = false; int pickedSquare; while (!foundMove){ pickedSquare = (int) (Math.random() * 9 ); // 0 to 8 if (squares[pickedSquare].getLabel().equals("")){ squares[pickedSquare].setLabel("O"); foundMove = true; } } } public void moveHard(){ boolean foundMove = false; foundMove = findMove(O); // that is, find a win for O if (!foundMove) foundMove = findMove(X); // that is, find a win for X, or block for O if (!foundMove) moveRandom(); } // opposite corner exploit discovered by Brian Ziebart public void moveExpert(){ boolean foundMove = false; foundMove = findMove(O); // that is, find a win for O if (!foundMove) foundMove = findMove(X); // that is, find a win for X, or block for O if (!foundMove) foundMove = moveCenter(); if (!foundMove) foundMove = moveUnborderedCorner(); if (!foundMove) foundMove = moveCorner(); if (!foundMove) moveRandom(); } // strategy derived from: // http://www.chessandpoker.com/tic_tac_toe_strategy.html public void movePerfect(){ boolean foundMove = false; foundMove = findMove(O); // that is, find a win for O if (!foundMove) foundMove = findMove(X); // that is, find a win for X, or block for O if (!foundMove) foundMove = moveCenter(); // if we can't win, block, or go center, check these conditions: // condition 1: if X is on a corner and an edge: if (!foundMove) if (condition1()) //move to the oppossite corner foundMove = moveCaddy(); // condition 2a: if both X's are on edges and border a corner if (!foundMove) if (condition2a()) //move to the bordered corner foundMove = moveBorderd(); // condition 2b: if both X's are on edges and don't border a corner if (!foundMove) if (condition2b()) //move to any edge foundMove = moveEdge(); // condition 3: if both X's are on corners if (!foundMove) if (condition3()) //move to any edge foundMove = moveEdge(); if (!foundMove) foundMove = moveUnborderedCorner(); if (!foundMove) foundMove = moveCorner(); if (!foundMove) moveRandom(); } public boolean moveCenter(){ if (squares[4].getLabel().equals("")){ squares[4].setLabel("O"); return true; } else return false; } public boolean moveCorner(){ if (squares[0].getLabel().equals("")){ squares[0].setLabel("O"); return true; } else if (squares[2].getLabel().equals("")){ squares[2].setLabel("O"); return true; } else if (squares[6].getLabel().equals("")){ squares[6].setLabel("O"); return true; } else if (squares[8].getLabel().equals("")){ squares[8].setLabel("O"); return true; } else return false; } public boolean moveUnborderedCorner(){ if (squares[1].getLabel().equals("X") && squares[3].getLabel().equals("X")){ if (squares[2].getLabel().equals("")){ squares[2].setLabel("O"); return true; } if (squares[6].getLabel().equals("")){ squares[6].setLabel("O"); return true; } if (squares[8].getLabel().equals("")){ squares[8].setLabel("O"); return true; } } if (squares[1].getLabel().equals("X") && squares[5].getLabel().equals("X")){ if (squares[0].getLabel().equals("")){ squares[0].setLabel("O"); return true; } if (squares[6].getLabel().equals("")){ squares[6].setLabel("O"); return true; } if (squares[8].getLabel().equals("")){ squares[8].setLabel("O"); return true; } } if (squares[3].getLabel().equals("X") && squares[7].getLabel().equals("X")){ if (squares[2].getLabel().equals("")){ squares[2].setLabel("O"); return true; } if (squares[0].getLabel().equals("")){ squares[0].setLabel("O"); return true; } if (squares[8].getLabel().equals("")){ squares[8].setLabel("O"); return true; } } if (squares[5].getLabel().equals("X") && squares[7].getLabel().equals("X")){ if (squares[2].getLabel().equals("")){ squares[2].setLabel("O"); return true; } if (squares[6].getLabel().equals("")){ squares[6].setLabel("O"); return true; } if (squares[0].getLabel().equals("")){ squares[0].setLabel("O"); return true; } } return false; } public boolean moveCaddy(){ if (squares[caddyCorner].getLabel().equals("")){ squares[caddyCorner].setLabel("O"); return true; } else return false; } public boolean moveBorderd(){ if (squares[borderedCorner].getLabel().equals("")){ squares[borderedCorner].setLabel("O"); return true; } else return false; } public boolean moveEdge(){ if (squares[1].getLabel().equals("")){ squares[1].setLabel("O"); return true; } else if (squares[3].getLabel().equals("")){ squares[3].setLabel("O"); return true; } else if (squares[5].getLabel().equals("")){ squares[5].setLabel("O"); return true; } else if (squares[7].getLabel().equals("")){ squares[7].setLabel("O"); return true; } else return false; } // condition 1: if X is on a corner and an edge: public boolean condition1(){ int corners = 0; int edges = 0; int hits = 0; for (int i = 0; i <9; i++){ if(squares[i].getLabel().equals("X")){ hits++; if (i == 0 || i == 2 || i == 6 || i == 8 ){ corners++; if (i == 0) caddyCorner = 8; if (i == 2) caddyCorner = 6; if (i == 8) caddyCorner = 0; if (i == 6) caddyCorner = 2; } if (i == 1 || i == 3 || i == 5 || i == 7 ){ edges++; } } } // only use strat if x has played twice and on an edge and a corner if (corners == 1 && edges == 1 && hits == 2){ return true; } else { return false; } } // condition 2b: if both X's are on edges and don't border a corner public boolean condition2b(){ int edges = 0; int hits = 0; for (int i = 0; i <9; i++){ if(squares[i].getLabel().equals("X")){ hits++; if (i == 1 || i == 3 || i == 5 || i == 7 ){ edges++; } } } // only use strat if x has played twice and on 2 edges if (edges == 2 && hits == 2){ // opposite edges if ((squares[1].getLabel().equals("X") && squares[7].getLabel().equals("X")) || (squares[3].getLabel().equals("X") && squares[5].getLabel().equals("X")) ) return true; } return false; } // condition 2a: if both X's are on edges and border a corner public boolean condition2a(){ int edges = 0; int hits = 0; for (int i = 0; i <9; i++){ if(squares[i].getLabel().equals("X")){ hits++; if (i == 1 || i == 3 || i == 5 || i == 7 ){ edges++; } } } // only use strat if x has played twice and on 2 edges if (edges == 2 && hits == 2){ // adjacent edges if (squares[1].getLabel().equals("X") && squares[3].getLabel().equals("X")){ borderedCorner = 0; return true; } if (squares[1].getLabel().equals("X") && squares[5].getLabel().equals("X")){ borderedCorner = 2; return true; } if (squares[3].getLabel().equals("X") && squares[7].getLabel().equals("X")){ borderedCorner = 6; return true; } if (squares[5].getLabel().equals("X") && squares[7].getLabel().equals("X")){ borderedCorner = 8; return true; } } return false; } // condition 3: if both X's are on corners public boolean condition3(){ int corners = 0; int hits = 0; for (int i = 0; i <9; i++){ if(squares[i].getLabel().equals("X")){ hits++; if (i == 0 || i == 2 || i == 6 || i == 8 ){ corners++; } } } // only use strat if x has played twice and on 2 corners if (corners == 2 && hits == 2){ return true; } else { return false; } } public boolean findMove(int team){ // look for rows with two entires. // we will use the summing trick, with // x = -1 // o = 1 // and empty = 0 //thus, a sum of 2 indicates win for o // and a sum of -2 indicates a win for x int bits[] = new int[9]; int target = -999; int move = -999; boolean foundMove = false; for (int i = 0; i <9; i++){ if (squares[i].getLabel().equals("X")){ bits[i] = -1; //squares[i].setLabel("-1"); } else if (squares[i].getLabel().equals("O")){ bits[i] = 1; //squares[i].setLabel("1"); }else { bits[i] = 0; //squares[i].setLabel("0"); } } // we search based on the param, team. if (team == X) target = -2; if (team == O) target = 2; if (bits[0] + bits [1] + bits[2] == target) { if (bits[0] == 0) move = 0; if (bits[1] == 0) move = 1; if (bits[2] == 0) move = 2; foundMove = true; } else if (bits[3] + bits [4] + bits[5] == target) { if (bits[3] == 0) move = 3; if (bits[4] == 0) move = 4; if (bits[5] == 0) move = 5; foundMove = true; } else if (bits[6] + bits [7] + bits[8] == target) { if (bits[6] == 0) move = 6; if (bits[7] == 0) move = 7; if (bits[8] == 0) move = 8; foundMove = true; } else if (bits[0] + bits [3] + bits[6] == target) { if (bits[0] == 0) move = 0; if (bits[3] == 0) move = 3; if (bits[6] == 0) move = 6; foundMove = true; } else if (bits[1] + bits [4] + bits[7] == target) { if (bits[1] == 0) move = 1; if (bits[4] == 0) move = 4; if (bits[7] == 0) move = 7; foundMove = true; } else if (bits[2] + bits [5] + bits[8] == target) { if (bits[2] == 0) move = 2; if (bits[5] == 0) move = 5; if (bits[8] == 0) move = 8; foundMove = true; } else if (bits[0] + bits [4] + bits[8] == target) { if (bits[0] == 0) move = 0; if (bits[4] == 0) move = 4; if (bits[8] == 0) move = 8; foundMove = true; } else if (bits[2] + bits [4] + bits[6] == target) { if (bits[2] == 0) move = 2; if (bits[4] == 0) move = 4; if (bits[6] == 0) move = 6; foundMove = true; } if (foundMove){ squares[move].setLabel("O"); return true; } else return false; } public void setStrat(int strat){ strategy = strat; if (strategy == HARD){ hardBtn.setFont(new Font("Serif",Font.BOLD,20)); hardBtn.setLabel("Hard"); easyBtn.setFont(new Font("Serif",Font.PLAIN,10)); easyBtn.setLabel("Easy"); expertBtn.setFont(new Font("Serif",Font.PLAIN,10)); expertBtn.setLabel("Expert"); perfectBtn.setFont(new Font("Serif",Font.PLAIN,10)); perfectBtn.setLabel("Perfect"); } else if (strategy == RANDOM){ easyBtn.setFont(new Font("Serif",Font.BOLD,20)); easyBtn.setLabel("Easy"); hardBtn.setFont(new Font("Serif",Font.PLAIN,10)); hardBtn.setLabel("Hard"); expertBtn.setFont(new Font("Serif",Font.PLAIN,10)); expertBtn.setLabel("Expert"); perfectBtn.setFont(new Font("Serif",Font.PLAIN,10)); perfectBtn.setLabel("Perfect"); } else if (strategy == EXPERT){ expertBtn.setFont(new Font("Serif",Font.BOLD,20)); expertBtn.setLabel("Expert"); easyBtn.setFont(new Font("Serif",Font.PLAIN,10)); easyBtn.setLabel("Easy"); hardBtn.setFont(new Font("Serif",Font.PLAIN,10)); hardBtn.setLabel("Hard"); perfectBtn.setFont(new Font("Serif",Font.PLAIN,10)); perfectBtn.setLabel("Perfect"); } else if (strategy == PERFECT){ expertBtn.setFont(new Font("Serif",Font.PLAIN,10)); expertBtn.setLabel("Expert"); easyBtn.setFont(new Font("Serif",Font.PLAIN,10)); easyBtn.setLabel("Easy"); hardBtn.setFont(new Font("Serif",Font.PLAIN,10)); hardBtn.setLabel("Hard"); perfectBtn.setFont(new Font("Serif",Font.BOLD,20)); perfectBtn.setLabel("Perfect"); } } public void reset(){ squaresRemaining = 9; // reset buttons for ( int i = 0; i < 9; i++ ) { squares[i].setBackground(Color.gray); squares[i].setFont(new Font("Serif",Font.BOLD,20)); squares[i].setLabel(""); squares[i].setEnabled(true); } // disable difficulty buttons easyBtn.setEnabled(false); hardBtn.setEnabled(false); expertBtn.setEnabled(false); perfectBtn.setEnabled(false); } }