COE 312 Project 3, Fall 2012

Lebanese American University · Data Structures · 7 pages · Dr. Wissam Fawaz

All COE 312 files
COE 312 Project 3, Fall 2012, page 1

Stuck on a question? A tutor can walk you through it.

COE 312 Project 3, Fall 2012, page 2 COE 312 Project 3, Fall 2012, page 3 COE 312 Project 3, Fall 2012, page 4 COE 312 Project 3, Fall 2012, page 5 COE 312 Project 3, Fall 2012, page 6 COE 312 Project 3, Fall 2012, page 7

More COE 312 files

Show the text of this project
COE 312 (Data Structures) Fall 2012 W. FAWAZ Project III Project III (Due date: Thursday January 17, 2013) I. Objective This project is about writing a Java application that allows two players to play Tic-Tac-Toe. The Tic-Tac-Toe game is played on a two-dimensional three-by-three board. Two players alternate placing their symbols on squares. The first to get three squares in a row, column, or a long diagonal wins. In the context of this project, the computer makes the moves for one of the players and consequently you will be designing a single player Tic-Tac-Toe game where your end user will be challenging the computer. II. Brute-force implementation As the title of this section suggests, we will first opt for an implementation where the computer does not make optimal moves. In particular, during the course of the game, the computer will be inserting his symbols into randomly selected cells. The skeleton for a TicTacToe class is given below. TicTacToe has a data object called board that represents the current game position. A host of trivial self-descriptive methods are also defined within the TicTacToe class, including methods to clear the board, test whether a square is occupied, place a symbol on a square, and to test whether a win has been achieved. Kindly go over the provided implementation before proceeding to accomplishing your first assigned task. public class TicTacToe { public static final int HUMAN = 0; public static final int COMPUTER = 1; public static final int EMPTY = 2; private int[][] board = new int[3][3]; // Constructor public TicTacToe() { clearBoard( ); } public int [] [] getBoard() { return board; } // Play move, including checking legality public boolean playMove(int side, int row, int column) { if( row < 0 || row >= 3 || column < 0 || column >= 3 L E B A N E S E A M E R I C A N U N I V E R S I T Y Electrical and Computer Engineering Dept || board[ row ][ column ] != EMPTY ) return false; board[ row ][ column ] = side; return true; } // Simple supporting routines public void clearBoard() { for( int i = 0; i < 3; i++ ) for( int j = 0; j < 3; j++ ) board[ i ][ j ] = EMPTY; } public boolean boardIsFull( ) { for( int row = 0; row < 3; row++ ) for( int column = 0; column < 3; column++ ) if( board[ row ][ column ] == EMPTY ) return false; return true; } public boolean isAWin( int side ) { int row, column; /* Look for all in a row */ for( row = 0; row < 3; row++ ) { for( column = 0; column < 3; column++ ) if( board[ row ][ column ] != side ) break; if( column >= 3 ) return true; } /* Look for all in a column */ for( column = 0; column < 3; column++ ) { for( row = 0; row < 3; row++ ) if( board[ row ][ column ] != side ) break; if( row >= 3 ) return true; } /* Look on diagonals */ if( board[ 1 ][ 1 ] == side && board[ 2 ][ 2 ] == side && board[ 0 ][ 0 ] == side ) return true; if( board[ 0 ][ 2 ] == side && board[ 1 ][ 1 ] == side && board[ 2 ][ 0 ] == side ) return true; return false; } // Play a move, possibly clearing a square private void place( int row, int column, int piece ) { board[ row ][ column ] = piece; } private boolean squareIsEmpty( int row, int column ) { return board[ row ][ column ] == EMPTY; } } Your first task, if you choose to accept it , is to supplement the TicTacToe class presented earlier with another class called TicTacToeMain whose responsibility is to define a user interface for your application. More specifically, TicTacToeMain should present the user with a two-dimensional three-by-three grid of nine JButton objects. Each of the players involved in the game will be expected to place his/her symbol onto one of the enabled buttons. After a player has made his/her move, the button receiving the symbol gets disabled. Whenever the human player makes a move, make sure to place the letter X in the specified square and the letter O whenever the second player (i.e., computer) moves. It is needless to say that each move must target an empty square. After each move, use the methods of the TicTacToe class to determine whether the game has been won and whether it is a draw. When the game is over, display a dialog box indicating who won in case there was a winner and enabling the human player to play the game again. The main limitation of this first implementation of the TicTacToe game is that your opponent is not following an optimal strategy when making his moves. In view of this, you may have noticed that beating the computer was within your reach, to say the least! It would be nice if we can come up with a more optimal implementation of the game in which the computer would resort to a move selection algorithm to make his moves. This is what we will try to achieve through the next task. III. Backtracking-based implementation The objective of this section is to have the computer select an optimal move during the game and then to have you incorporate his strategy into your previously developed user interface. The challenge is to decide, for any position, what the best move is. The class Best, shown below, will be used to store the optimal move that is returned by the move selection algorithm employed by the computer. public class Best { int row; int column; int val; public Best( int v ) { this( v, 0, 0 ); } public Best( int v, int r, int c ) { val = v; row = r; column = c; } } The general strategy involves the use of a backtracking algorithm. A backtracking algorithm uses recursion to try all the possibilities. The basis for making the optimal decision will be the positionValue method that is included below as part of a modified version of the TicTacToe class. Go over the amended version of TicTacToe carefully before moving on. public class TicTacToe { private int [ ] [ ] board = new int[ 3 ][ 3 ]; public static final int HUMAN = 0; public static final int COMPUTER = 1; public static final int EMPTY = 2; public static final int HUMAN_WIN = 0; public static final int DRAW = 1; public static final int UNCLEAR = 2; public static final int COMPUTER_WIN = 3; public TicTacToe() {// Same implementation as before} public int [] [] getBoard() {// Same implementation as before} // Find optimal move public Best chooseMove(int side) { int opp; // The other side Best reply; // Opponent's best reply int simpleEval; // Result of an immediate evaluation int bestRow = 0; int bestColumn = 0; int value; if( ( simpleEval = positionValue( ) ) != UNCLEAR ) return new Best( simpleEval ); if( side == COMPUTER ) { opp = HUMAN; value = HUMAN_WIN; } else { opp = COMPUTER; value = COMPUTER_WIN; } for( int row = 0; row < 3; row++ ) for( int column = 0; column < 3; column++ ) if( squareIsEmpty(row, column)) { place( row, column, side ); reply = chooseMove( opp ); place( row, column, EMPTY ); // Update if side gets better position if( side == COMPUTER && reply.val > value || side == HUMAN && reply.val < value ) { value = reply.val; bestRow = row; bestColumn = column; } } return new Best( value, bestRow, bestColumn ); } // Compute static value of current position (win, draw, etc.) private int positionValue( ) { return isAWin( COMPUTER ) ? COMPUTER_WIN : isAWin( HUMAN ) ? HUMAN_WIN : boardIsFull( ) ? DRAW : UNCLEAR; } public boolean playMove(int side, int row, int column) { // Implementation provided earlier } public void clearBoard() { // Implementation given earlier } public boolean boardIsFull( ) { // Same implementation as before } public boolean isAWin( int side ) { // Refer to previously presented implementation } private void place( int row, int column, int piece ) { // Same implementation as before } private boolean squareIsEmpty( int row, int column ) { // Same implementation as before }} As indicated earlier, at the heart of the optimal decision made by the computer are the newly-introduced methods called chooseMove and positionValue. The method positionValue returns HUMAN_WIN, DRAW, COMPUTER_WIN, or UNCLEAR, depending on what the board represents. As a matter of fact, the strategy used is the minimax strategy, which is based on the assumption of optimal play by both players. The value of a position is a COMPUTER_WIN if optimal play implies that the computer can force a win. If the computer can force a draw but not a win, the value is DRAW. If the human player on the other hand can force a win, the value is HUMAN_WIN. Given that the strategy utilized by the computer should favor him over the human player, it makes sense to have HUMAN_WIN < DRAW < COMPUTER_WIN. Your second task as you might have expected is to update your previous implementation of the user interface by integrating the minimax strategy described above into the decision-making process of your computer. Your job is not only to add both the Best and the modified TicTacToe class to your project but also to make use of the chooseMove method offered by the latter. Before approaching this task, it is strongly recommended that you read through the following paragraphs that aim at explaining in more details the main idea behind the minimax strategy. I would like though to attract your attention to the fact that failing to understand the discussion provided below would not deter you from completing this second task, so there is no need to worry or panic if you don’t grasp the concept! For the computer, the value of the position is the maximum of all the values corresponding to the positions that can result from making a move. For example, suppose that one move leads to a winning position, two moves lead to a drawing position, and six moves lead to a losing position. Then, the move that leads to the winning position is the move to make. In contrast, for the human player, we use the minimum instead of the maximum. This approach suggests a recursive algorithm to determine the value of a position. If the position is a terminal position (game is over, i.e., we can see right away that Tic-Tac-Toe has been achieved or the board is full without Tic-Tac-Toe), the position’s value is immediate. Otherwise, the chooseMove method recursively tries all moves, computing the value for each resulting position, and chooses the maximum value. In summary, if we have an immediate answer, we can return. This possibility is handled by the base case of the chooseMove method. Otherwise, the chooseMove method looks for the best position value and returns it in a Best object. Although this approach optimally solves Tic-Tac-Toe. It performs a lot of searching. Particularly, to choose the first move on an empty board, it makes 549,946 recursive calls. Therefore, you are encouraged to make the first computer move on an empty board in a random way. Moreover, by using some algorithmic tricks, you can compute the same information with fewer searches. One such technique is known as alpha-beta-pruning, which is an improvement over the minimax algorithm. You will get bonus marks if you go the extra mile and introduce the alpha-beta-pruning strategy to your project for the purpose of achieving the highest level of optimality. Note that Google is a good starting point if you are interested in knowing more about alpha- beta-pruning. What to turn in? This project is due at the beginning of class on the due date. You have to turn in the following material in both hard and soft copies. Criteria Percentage HTML Documentation of your code. In addition, provide explanations and illustrations in one or two pages along with a short write-up of questions and/or problems that you encountered while doing this project. 2 pts (10%) Source code that contains an appropriate amount of comments. Well- organized and correct code receives 18 pts, messy yet working code receives 12 pts, code with bugs receives 6 pts, and incomplete code receives 3 pts. 18 pts (90 %) Total 20pts (100%) Good Luck!