// TicTacToe.cpp // (c) Markus Lumpe, CS 229 Spring 2007 #include "TicTacToe.h" //---- MoveException -----------------------------// MoveException::MoveException( string aMessage ) { fMessage = aMessage; } string MoveException::getMessage() { return fMessage; } //---- TicTacToe ----------------------------------// TicTacToe::TicTacToe( TicTacToeView& aView ) : fView(aView) { // register game fView.registerGame( this ); fPlayerId = 1; fComputerId = 0; // setup board for ( int i = 0; i < 3; i++ ) for ( int j = 0; j < 3; j++ ) fBoard[i][j] = -1; fFieldsSet = 0; fPlay = true; } int TicTacToe::countSetFields() { int Result = 0; for ( int i = 0; i < 3; i++ ) for ( int j = 0; j < 3; j++ ) if ( fBoard[i][j] != -1 ) Result++; return Result; } int TicTacToe::getField( int aIndex ) { return fBoard[aIndex/3][aIndex%3]; } void TicTacToe::setField( int aIndex, int aValue ) { // Check for a legal move first! int lRow = aIndex / 3; int lColumn = aIndex % 3; if ( fBoard[lRow][lColumn] == -1 ) { fBoard[lRow][lColumn] = aValue; fFieldsSet = countSetFields(); // Update board on screen fView.printBoard(); } else throw MoveException( "Position already used!" ); } // automatic move logic int TicTacToe::fOptimalMoves[9] = { 4, 0, 2, 6, 8, 1, 3, 5, 7 }; void TicTacToe::setMove( int aMove, int aPlayerId ) { fBoard[aMove / 3][aMove % 3] = aPlayerId; } void TicTacToe::unsetMove( int aMove ) { fBoard[aMove / 3][aMove % 3] = -1; } bool TicTacToe::isFree( int aMove ) { return fBoard[aMove / 3][aMove % 3] == -1; } int TicTacToe::performComputerMove( int aPlayerId, int aComputerId ) { fPlayerId = aPlayerId; fComputerId = aComputerId; int Result = computeComputerMove(); fPlayerId = 1; fComputerId = 0; return Result; } int TicTacToe::computeComputerMove() { // This procedure ist only called, if there is at least one move possible. // Based on JDK1.2 demo TicTacToe.java (optimal moves only) int lAMove = -1; // first check, whether we can find a winning move for ( int i = 0; i < 9; i++ ) { lAMove = fOptimalMoves[i]; if ( isFree( lAMove ) ) { setMove( lAMove, fComputerId ); // computer if ( checkWin() ) { unsetMove( lAMove ); return lAMove; // take it } else unsetMove( lAMove ); } } // Can player win in next move? for ( int i = 0; i < 9; i++ ) { lAMove = fOptimalMoves[i]; if ( isFree( lAMove ) ) { setMove( lAMove, fPlayerId ); // player if ( checkWin() ) { unsetMove( lAMove ); return lAMove; // take it } else unsetMove( lAMove ); } } // Can the player win in two steps? // In this case, we have to block the first move if ( fFieldsSet < 7 ) // not enough moves left { for ( int i = 0; i < 9; i++ ) { lAMove = fOptimalMoves[i]; if ( isFree( lAMove ) ) { setMove( lAMove, fPlayerId ); // player for ( int j = 0; j < 9; j++ ) { int lAMove2 = fOptimalMoves[j]; if ( isFree( lAMove2 ) ) { setMove( lAMove2, fPlayerId ); // player if ( checkWin() ) { unsetMove( lAMove2 ); unsetMove( lAMove ); // Now we need to find the best response return findComputerResponse( lAMove, lAMove2 ); } else unsetMove( lAMove2 ); } // no else needed } unsetMove( lAMove ); } else lAMove = -1; } } // At this point the player cannot win in two moves. // Any move will do. for ( int i = 0; i < 9; i++ ) { lAMove = fOptimalMoves[i]; if ( isFree( lAMove ) ) break; } return lAMove; // take it } int TicTacToe::findComputerResponse( int aMove1, int aMove2 ) { // computer takes 1 setMove( aMove1, fComputerId ); // computer move int lNextMove = -1; // check if computer can win in one move for ( int i = 0; i < 9; i++ ) { lNextMove = fOptimalMoves[i]; if ( isFree( lNextMove ) ) { setMove( lNextMove, fComputerId ); // computer if ( checkWin() ) { unsetMove( lNextMove ); break; } else unsetMove( lNextMove ); } else lNextMove = -1; } int lPlayerMove = -1;; if ( lNextMove != -1 ) { // computer can win with this move. If player plays reasonable, // then this move would be the player's choice. setMove( lNextMove, fPlayerId ); // player // Now, can player win in one move? for ( int i = 0; i < 9; i++ ) { lPlayerMove = fOptimalMoves[i]; if ( isFree( lPlayerMove ) ) { setMove( lPlayerMove, fPlayerId ); // player if ( checkWin() ) { unsetMove( lPlayerMove ); break; } else unsetMove( lPlayerMove ); } else lPlayerMove = -1; } unsetMove( lNextMove ); } // undo first move unsetMove( aMove1 ); // the player wins, so our first choice was bad if ( lPlayerMove != -1 ) return aMove2; // safe move return aMove1; } bool TicTacToe::isActive() { return fPlay; } void TicTacToe::announceGameOver() { fPlay = false; // print message fView.draw(); } void TicTacToe::announcePlayerHasWon( int aPlayer ) { fPlay = false; // print message fView.declareWinner( aPlayer ); } void TicTacToe::isGameOver( int aPlayer ) { // player 1: 0, player 2: 1 if ( checkWin() ) announcePlayerHasWon( aPlayer ); else if ( fFieldsSet == 9 ) announceGameOver(); } bool TicTacToe::checkWin() { return (checkRows() || checkColumns() || checkNWToSE() || checkNEToSW()); } bool TicTacToe::checkRows() { return (checkRow( 0 ) || checkRow( 1 ) || checkRow( 2 )); } bool TicTacToe::checkRow( int aRow ) { return ((fBoard[aRow][0] == fBoard[aRow][1]) && (fBoard[aRow][1] == fBoard[aRow][2]) && (fBoard[aRow][2] != -1)); } bool TicTacToe::checkColumns() { return (checkColumn( 0 ) || checkColumn( 1 ) || checkColumn( 2 )); } bool TicTacToe::checkColumn( int aColumn ) { return ((fBoard[0][aColumn] == fBoard[1][aColumn]) && (fBoard[1][aColumn] == fBoard[2][aColumn]) && (fBoard[2][aColumn] != -1)); } bool TicTacToe::checkNWToSE() { return ((fBoard[0][0] == fBoard[1][1]) && (fBoard[1][1] == fBoard[2][2]) && (fBoard[2][2] != -1)); } bool TicTacToe::checkNEToSW() { return ((fBoard[0][2] == fBoard[1][1]) && (fBoard[1][1] == fBoard[2][0]) && (fBoard[2][0] != -1)); }