├── .gitignore ├── Makefile ├── src ├── Util.h ├── main.cpp ├── Game.h ├── App.h ├── Bot.h ├── Board.h ├── Util.cpp ├── App.cpp ├── Game.cpp ├── Board.cpp └── Bot.cpp ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | tictactoe 2 | *~ 3 | *.out 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJS = src/main.cpp src/App.cpp src/Board.cpp src/Bot.cpp src/Game.cpp src/Util.cpp 2 | CC = g++ 3 | COMPILER_FLAGS = -w 4 | LINKER_FLAGS = `sdl2-config --libs` 5 | EXECUTABLE = tictactoe 6 | 7 | all : $(OBJS) 8 | $(CC) $(OBJS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(EXECUTABLE) 9 | -------------------------------------------------------------------------------- /src/Util.h: -------------------------------------------------------------------------------- 1 | // Util.h 2 | // Util class definition. Member-functions defined in Board.cpp 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #ifndef _UTIL_ 7 | #define _UTIL_ 8 | 9 | class Util 10 | { 11 | public: 12 | static void drawCross(int row, int col); 13 | static void drawCircle(int row, int col); 14 | }; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // main.cpp 2 | // app entry point. 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #include "App.h" 7 | #include 8 | 9 | int main(int argc, char *args[]) 10 | { 11 | App app; 12 | 13 | if(!app.init()) 14 | { 15 | printf("Error initializing... bye!\n"); 16 | return 666; 17 | } 18 | 19 | app.run(); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /src/Game.h: -------------------------------------------------------------------------------- 1 | // Game.h 2 | // Game class definition. Member-functions defined in Game.cpp 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #ifndef _GAME_ 7 | #define _GAME_ 8 | 9 | #include 10 | #include "Board.h" 11 | #include "Bot.h" 12 | 13 | class Game 14 | { 15 | public: 16 | enum GAME_STATE {STATE_RUNNING, STATE_STOPPED}; 17 | 18 | Game(); 19 | 20 | void update(SDL_Event *event); 21 | void render() const; 22 | void checkWin(); 23 | 24 | private: 25 | GAME_STATE state; 26 | Bot bot; 27 | Board board; 28 | bool humanTurn; 29 | 30 | void restartGame(); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/App.h: -------------------------------------------------------------------------------- 1 | // App.h 2 | // App class definition. Member-functions defined in App.cpp 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #ifndef _APP_ 7 | #define _APP_ 8 | 9 | #include 10 | #include "Game.h" 11 | 12 | class App 13 | { 14 | public: 15 | static const int SCREEN_WIDTH = 600; 16 | static const int SCREEN_HEIGHT = 600; 17 | 18 | // member functions 19 | ~App(); 20 | 21 | bool init(); 22 | void run(); 23 | 24 | static SDL_Window *getWindow(); 25 | static SDL_Renderer *getRenderer(); 26 | 27 | private: 28 | Game game; 29 | 30 | // SDL components 31 | static SDL_Window *gWindow; 32 | static SDL_Renderer *gRenderer; 33 | SDL_Event event; 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/Bot.h: -------------------------------------------------------------------------------- 1 | // Board.h 2 | // Board class definition. Member-functions defined in Board.cpp 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #ifndef _BOT_ 7 | #define _BOT_ 8 | 9 | #include "Board.h" 10 | 11 | class Bot 12 | { 13 | public: 14 | enum BOT_LEVEL 15 | { 16 | LEVEL_EASY, // random 17 | LEVEL_MEDIUM, // defend + random attack 18 | LEVEL_HARD, // defend + win attack 19 | LEVEL_COUNT 20 | }; 21 | 22 | Bot(); 23 | 24 | void play(Board &board) const; 25 | void restart(); 26 | 27 | private: 28 | BOT_LEVEL level; 29 | 30 | void easy(Board &board) const; 31 | bool medium(Board &board) const; 32 | bool hard(Board &board) const; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/Board.h: -------------------------------------------------------------------------------- 1 | // Board.h 2 | // Board class definition. Member-functions defined in Board.cpp 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #ifndef _BOARD_ 7 | #define _BOARD_ 8 | 9 | class Board 10 | { 11 | public: 12 | enum BOARD_PIECE {PIECE_EMPTY, PIECE_X, PIECE_O}; 13 | 14 | Board(); 15 | 16 | void render() const; 17 | void restart(); 18 | bool hasWinner() const; 19 | bool isDraw() const; 20 | void place(int row, int col, bool humanPiece=true); 21 | bool isFieldEmpty(int row, int col) const; 22 | bool isFieldCross(int row, int col) const; 23 | bool isFieldCircle(int row, int col) const; 24 | 25 | private: 26 | BOARD_PIECE m[3][3]; 27 | int occupiedPlaces; 28 | 29 | // helpers 30 | void renderBoard() const; 31 | void renderPieces() const; 32 | bool checkRows() const; 33 | bool checkColumns() const; 34 | bool checkDiagonals() const; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sdl2-TicTacToe 2 | The simple Tic-Tac-Toe game created in C++ with SDL2. 3 | 4 | This game was developed for **learning purposes**. 5 | The current only supported mode is single player and the bot you'll be facing randomly chooses its AI level (easy, medium or hard) at every new game. 6 | All board shapes were drawn using hardware accelerated primitive rendering, so no image resources were needed in the project. 7 | 8 | ## License 9 | 10 | SDL2-TicTacToe is licensed under the [Do What The Fuck You Want License](https://github.com/davidgomes/tower-engineer/blob/master/LICENSE) 11 | 12 | ## Dependencies 13 | You'll need SDL2 to be able to compile the program. To install it, use the following commands: 14 | 15 | **Debian** 16 | ``` 17 | $ sudo apt-get install libsdl2-dev 18 | ``` 19 | **Arch** 20 | ``` 21 | $ sudo pacman -S sdl2 22 | ``` 23 | 24 | **Red Hat** 25 | ``` 26 | $ sudo yum install SDL2-devel 27 | ``` 28 | 29 | ## Usage 30 | 31 | From the project source folder, run: 32 | ``` 33 | $ make 34 | $ ./tictactoe 35 | ``` 36 | -------------------------------------------------------------------------------- /src/Util.cpp: -------------------------------------------------------------------------------- 1 | // Util.cpp 2 | // Util class member-functions definition. 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #include "Util.h" 7 | #include "App.h" 8 | #include 9 | 10 | // render cross on board field (row, col) 11 | void Util::drawCross(int row, int col) 12 | { 13 | int fieldW = App::SCREEN_WIDTH/3; 14 | int fieldH = App::SCREEN_HEIGHT/3; 15 | 16 | // top left to bottom right 17 | SDL_RenderDrawLine(App::getRenderer(), 18 | col*fieldW, row*fieldH, 19 | col*fieldW+fieldW, row*fieldH+fieldH); 20 | 21 | // top right to bottom left 22 | SDL_RenderDrawLine(App::getRenderer(), 23 | col*fieldW, row*fieldH+fieldH, 24 | col*fieldW+fieldW, row*fieldH); 25 | } 26 | 27 | // render circle on board field (boardX, boardY) 28 | void Util::drawCircle(int row, int col) 29 | { 30 | int fieldW = App::SCREEN_WIDTH/3; 31 | int fieldH = App::SCREEN_HEIGHT/3; 32 | 33 | // calculate circle center 34 | int centerX = col*fieldW+(fieldW/2); 35 | int centerY = row*fieldH+(fieldH/2); 36 | 37 | int r = fieldW/2; 38 | 39 | double step = 2*M_PI/30; 40 | int endX = centerX + r; 41 | int endY = centerY; 42 | 43 | // draw circle.. Multiple straight lines between consecutive 44 | // points of the desired circle 45 | for(double angle=0; angle<2*M_PI; angle+=step) 46 | { 47 | int startX = endX; 48 | int startY = endY; 49 | endX = r * cos(angle) + centerX; 50 | endY = r * sin(angle) + centerY; 51 | SDL_RenderDrawLine(App::getRenderer(), startX, startY, endX, endY); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/App.cpp: -------------------------------------------------------------------------------- 1 | // App.cpp 2 | // App class member-functions definition. 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #include "App.h" 7 | #include 8 | 9 | // static data-members initialization 10 | SDL_Window* App::gWindow = NULL; 11 | SDL_Renderer* App::gRenderer = NULL; 12 | 13 | // free game window 14 | App::~App() 15 | { 16 | if(gRenderer != NULL) 17 | { 18 | SDL_DestroyRenderer(gRenderer); 19 | gRenderer = NULL; 20 | } 21 | 22 | if(gWindow != NULL) 23 | { 24 | SDL_DestroyWindow(gWindow); 25 | gWindow = NULL; 26 | } 27 | } 28 | 29 | // starts SDL and initializes window 30 | bool App::init() 31 | { 32 | if(SDL_Init(SDL_INIT_VIDEO) < 0) 33 | { 34 | printf("Couldn't start SDL! SDL error: %s\n", SDL_GetError()); 35 | return false; 36 | } 37 | 38 | gWindow = SDL_CreateWindow("Tic Tac Toe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); 39 | if(gWindow == NULL) 40 | { 41 | printf("Couldn't create Window! SDL error: %s\n", SDL_GetError()); 42 | return false; 43 | } 44 | 45 | gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 46 | if(gRenderer == NULL) 47 | { 48 | printf("Couldn't create Renderer! SDL error: %s\n", SDL_GetError()); 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | 55 | // starts the game lifecycle 56 | void App::run() 57 | { 58 | bool quit = false; 59 | while(!quit) 60 | { 61 | // handle exit events 62 | while(SDL_PollEvent(&event)) 63 | { 64 | if(event.type == SDL_QUIT) 65 | quit = true; 66 | } 67 | 68 | // update and render the game 69 | game.update(&event); 70 | game.render(); 71 | game.checkWin(); 72 | } 73 | } 74 | 75 | SDL_Window* App::getWindow() 76 | { 77 | return gWindow; 78 | } 79 | 80 | SDL_Renderer* App::getRenderer() 81 | { 82 | return gRenderer; 83 | } 84 | -------------------------------------------------------------------------------- /src/Game.cpp: -------------------------------------------------------------------------------- 1 | // Game.cpp 2 | // Game class member-functions definition. 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #include "Game.h" 7 | #include "App.h" 8 | #include 9 | #include 10 | #include 11 | 12 | Game::Game() 13 | { 14 | srand(time(NULL)); // generate new seed 15 | restartGame(); 16 | } 17 | 18 | // update current game's state 19 | void Game::update(SDL_Event *event) 20 | { 21 | // have we reach the current game's end? 22 | if(state == STATE_STOPPED) 23 | restartGame(); 24 | else 25 | { 26 | bool successfulPlay = false; // to handle change of player turn 27 | 28 | // process human or pc turn 29 | if(!humanTurn) 30 | { 31 | bot.play(board); 32 | successfulPlay = true; 33 | } 34 | else 35 | { 36 | // did the human play? 37 | if(event->type == SDL_MOUSEBUTTONDOWN) 38 | { 39 | // get mouse coordinates and normalize to row/col 40 | int x, y; 41 | SDL_GetMouseState(&x, &y); 42 | int row = y/(App::SCREEN_HEIGHT/3); 43 | int col = x/(App::SCREEN_WIDTH/3); 44 | 45 | // try to validate the move 46 | if(board.isFieldEmpty(row,col)) 47 | { 48 | board.place(row,col); 49 | successfulPlay = true; 50 | } 51 | } 52 | } 53 | 54 | // change turns 55 | if(successfulPlay) 56 | { 57 | humanTurn = !humanTurn; 58 | SDL_Delay(100); // delay between turns 59 | } 60 | } 61 | } 62 | 63 | // render the game to the window 64 | void Game::render() const 65 | { 66 | // bg 67 | SDL_SetRenderDrawColor(App::getRenderer(), 0xFF, 0xFF, 0xFF, 0xFF); 68 | SDL_RenderClear(App::getRenderer()); 69 | 70 | // game objects 71 | board.render(); 72 | 73 | // render 74 | SDL_RenderPresent(App::getRenderer()); 75 | } 76 | 77 | // check board win conditions 78 | void Game::checkWin() 79 | { 80 | if(board.hasWinner() || board.isDraw()) 81 | state = STATE_STOPPED; // so the game loop knows the end of the current game 82 | } 83 | 84 | void Game::restartGame() 85 | { 86 | bot.restart(); 87 | board.restart(); 88 | 89 | // randomly choose first player 90 | // 0=pc, 1=human 91 | humanTurn = (rand()&1) ? true : false; 92 | 93 | // corrects the game state 94 | state = STATE_RUNNING; 95 | } 96 | -------------------------------------------------------------------------------- /src/Board.cpp: -------------------------------------------------------------------------------- 1 | // Board.cpp 2 | // Board class member-functions definition. 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #include "Board.h" 7 | #include "App.h" 8 | #include "Util.h" 9 | 10 | Board::Board() { restart(); } 11 | 12 | // render the board and game pieces to the window 13 | void Board::render() const 14 | { 15 | // set the drawing color to black 16 | SDL_SetRenderDrawColor(App::getRenderer(), 0x00, 0x00, 0x00, 0xFF); 17 | 18 | renderBoard(); 19 | renderPieces(); 20 | } 21 | 22 | // clear the game board 23 | void Board::restart() 24 | { 25 | for(int i=0; i<3; i++) 26 | for(int j=0; j<3; j++) 27 | m[i][j] = PIECE_EMPTY; 28 | 29 | occupiedPlaces = 0; 30 | } 31 | 32 | // check for win conditions 33 | bool Board::hasWinner() const 34 | { 35 | return (checkRows() || checkColumns() || checkDiagonals()); 36 | } 37 | 38 | bool Board::isDraw() const 39 | { 40 | return occupiedPlaces == 9; 41 | } 42 | 43 | // place a piece (human/pc) at (x,y) spot in the board 44 | void Board::place(int row, int col, bool humanPiece) 45 | { 46 | if(row>=0 && row<3 && col>=0 && col<3 && occupiedPlaces<9) 47 | { 48 | m[row][col] = humanPiece ? PIECE_X : PIECE_O; 49 | occupiedPlaces++; 50 | } 51 | } 52 | 53 | // return whether the (x,y) spot is empty 54 | bool Board::isFieldEmpty(int row, int col) const 55 | { 56 | if(row>=0 && row<3 && col>=0 && col<3) 57 | return m[row][col] == PIECE_EMPTY; 58 | 59 | return false; 60 | } 61 | 62 | // return whether the (x,y) spot is filled with piece X 63 | bool Board::isFieldCross(int row, int col) const 64 | { 65 | if(row>=0 && row<3 && col>=0 && col<3) 66 | return m[row][col] == PIECE_X; 67 | 68 | return false; 69 | } 70 | 71 | // return whether the (x,y) spot is filled with piece O 72 | bool Board::isFieldCircle(int row, int col) const 73 | { 74 | if(row>=0 && row<3 && col>=0 && col<3) 75 | return m[row][col] == PIECE_O; 76 | 77 | return false; 78 | } 79 | 80 | // render board lines 81 | void Board::renderBoard() const 82 | { 83 | // vertical lines 84 | SDL_RenderDrawLine(App::getRenderer(), 85 | App::SCREEN_WIDTH/3, 0, 86 | App::SCREEN_WIDTH/3, App::SCREEN_HEIGHT); 87 | 88 | SDL_RenderDrawLine(App::getRenderer(), 89 | App::SCREEN_WIDTH/3*2, 0, 90 | App::SCREEN_WIDTH/3*2, App::SCREEN_HEIGHT); 91 | 92 | // horizontal lines 93 | SDL_RenderDrawLine(App::getRenderer(), 94 | 0, App::SCREEN_HEIGHT/3, 95 | App::SCREEN_WIDTH, App::SCREEN_HEIGHT/3); 96 | 97 | SDL_RenderDrawLine(App::getRenderer(), 98 | 0, App::SCREEN_HEIGHT/3*2, 99 | App::SCREEN_WIDTH, App::SCREEN_HEIGHT/3*2); 100 | } 101 | 102 | // render player pieces 103 | void Board::renderPieces() const 104 | { 105 | for(int i=0; i<3; i++) 106 | for(int j=0; j<3; j++) 107 | { 108 | switch(m[i][j]) 109 | { 110 | case PIECE_X: 111 | Util::drawCross(i, j); break; 112 | case PIECE_O: 113 | Util::drawCircle(i, j); break; 114 | default: 115 | break; 116 | } 117 | } 118 | } 119 | 120 | // check row win conditions 121 | bool Board::checkRows() const 122 | { 123 | if(m[0][0] == m[0][1] && m[0][1] == m[0][2] && !isFieldEmpty(0,2)) 124 | return true; 125 | else if(m[1][0] == m[1][1] && m[1][1] == m[1][2] && !isFieldEmpty(1,2)) 126 | return true; 127 | else if(m[2][0] == m[2][1] && m[2][1] == m[2][2] && !isFieldEmpty(2,2)) 128 | return true; 129 | 130 | return false; 131 | } 132 | 133 | // check column win conditions 134 | bool Board::checkColumns() const 135 | { 136 | if(m[0][0] == m[1][0] && m[1][0] == m[2][0] && !isFieldEmpty(2,0)) 137 | return true; 138 | else if(m[0][1] == m[1][1] && m[1][1] == m[2][1] && !isFieldEmpty(2,1)) 139 | return true; 140 | else if(m[0][2] == m[1][2] && m[1][2] == m[2][2] && !isFieldEmpty(2,2)) 141 | return true; 142 | 143 | return false; 144 | } 145 | 146 | // check diagonal win conditions 147 | bool Board::checkDiagonals() const 148 | { 149 | if(m[0][0] == m[1][1] && m[1][1] == m[2][2] && !isFieldEmpty(2,2)) 150 | return true; 151 | else if(m[0][2] == m[1][1] && m[1][1] == m[2][0] && !isFieldEmpty(2,0)) 152 | return true; 153 | 154 | return false; 155 | } 156 | -------------------------------------------------------------------------------- /src/Bot.cpp: -------------------------------------------------------------------------------- 1 | // Bot.cpp 2 | // Bot class member-functions definition. 3 | // 4 | // Author: yat0 - https://github.com/yat0 5 | 6 | #include "Bot.h" 7 | #include 8 | 9 | Bot::Bot() { restart(); } 10 | 11 | // simulates the bot turn 12 | void Bot::play(Board &board) const 13 | { 14 | switch(level) 15 | { 16 | case LEVEL_EASY: 17 | easy(board); break; 18 | case LEVEL_MEDIUM: 19 | if(!medium(board)) 20 | easy(board); 21 | break; 22 | case LEVEL_HARD: 23 | if(!hard(board) && !medium(board)) 24 | easy(board); 25 | break; 26 | default: 27 | break; 28 | } 29 | } 30 | 31 | // randomly chooses the new bot AI level 32 | void Bot::restart() { level = static_cast(rand()%LEVEL_COUNT); } 33 | 34 | // simulates an easy AI level play 35 | void Bot::Bot::easy(Board &board) const 36 | { 37 | int x = rand()%3; 38 | int y = rand()%3; 39 | 40 | while(!board.isFieldEmpty(x,y)) 41 | { 42 | x = rand()%3; 43 | y = rand()%3; 44 | } 45 | 46 | board.place(x, y, false); 47 | } 48 | 49 | // simulates a medium AI level play 50 | bool Bot::medium(Board &board) const 51 | { 52 | // can block a row? 53 | for(int i=0; i<3; i++) 54 | { 55 | if(board.isFieldCross(i,0) && board.isFieldCross(i,1) && board.isFieldEmpty(i,2)) 56 | { 57 | board.place(i,2,false); 58 | return true; 59 | } 60 | 61 | if(board.isFieldCross(i,0) && board.isFieldCross(i,2) && board.isFieldEmpty(i,1)) 62 | { 63 | board.place(i,1,false); 64 | return true; 65 | } 66 | 67 | if(board.isFieldCross(i,1) && board.isFieldCross(i,2) && board.isFieldEmpty(i,0)) 68 | { 69 | board.place(i,0,false); 70 | return true; 71 | } 72 | } 73 | 74 | // can block a column? 75 | for(int j=0; j<3; j++) 76 | { 77 | if(board.isFieldCross(0,j) && board.isFieldCross(1,j) && board.isFieldEmpty(2,j)) 78 | { 79 | board.place(2,j,false); 80 | return true; 81 | } 82 | else if(board.isFieldCross(0,j) && board.isFieldCross(2,j) && board.isFieldEmpty(1,j)) 83 | { 84 | board.place(1,j,false); 85 | return true; 86 | } 87 | else if(board.isFieldCross(1,j) && board.isFieldCross(2,j) && board.isFieldEmpty(0,j)) 88 | { 89 | board.place(0,j,false); 90 | return true; 91 | } 92 | } 93 | 94 | // can block a diagonal? 95 | if(board.isFieldCross(0,0) && board.isFieldCross(1,1) && board.isFieldEmpty(2,2)) 96 | { 97 | board.place(2,2,false); 98 | return true; 99 | } 100 | else if(board.isFieldCross(0,0) && board.isFieldCross(2,2) && board.isFieldEmpty(1,1)) 101 | { 102 | board.place(1,1,false); 103 | return true; 104 | } 105 | else if(board.isFieldCross(1,1) && board.isFieldCross(2,2) && board.isFieldEmpty(0,0)) 106 | { 107 | board.place(0,0,false); 108 | return true; 109 | } 110 | else if(board.isFieldCross(0,2) && board.isFieldCross(1,1) && board.isFieldEmpty(2,0)) 111 | { 112 | board.place(2,0,false); 113 | return true; 114 | } 115 | else if(board.isFieldCross(0,2) && board.isFieldCross(2,0) && board.isFieldEmpty(1,1)) 116 | { 117 | board.place(1,1,false); 118 | return true; 119 | } 120 | else if(board.isFieldCross(1,1) && board.isFieldCross(2,0) && board.isFieldEmpty(0,2)) 121 | { 122 | board.place(0,2,false); 123 | return true; 124 | } 125 | 126 | return false; // nothing to block 127 | } 128 | 129 | // simulates a hard AI level play 130 | bool Bot::hard(Board &board) const 131 | { 132 | // can win on a row? 133 | for(int i=0; i<3; i++) 134 | { 135 | if(board.isFieldCircle(i,0) && board.isFieldCircle(i,1) && board.isFieldEmpty(i,2)) 136 | { 137 | board.place(i,2,false); 138 | return true; 139 | } 140 | 141 | if(board.isFieldCircle(i,0) && board.isFieldCircle(i,2) && board.isFieldEmpty(i,1)) 142 | { 143 | board.place(i,1,false); 144 | return true; 145 | } 146 | 147 | if(board.isFieldCircle(i,1) && board.isFieldCircle(i,2) && board.isFieldEmpty(i,0)) 148 | { 149 | board.place(i,0,false); 150 | return true; 151 | } 152 | } 153 | 154 | // can win on a column? 155 | for(int j=0; j<3; j++) 156 | { 157 | if(board.isFieldCircle(0,j) && board.isFieldCircle(1,j) && board.isFieldEmpty(2,j)) 158 | { 159 | board.place(2,j,false); 160 | return true; 161 | } 162 | else if(board.isFieldCircle(0,j) && board.isFieldCircle(2,j) && board.isFieldEmpty(1,j)) 163 | { 164 | board.place(1,j,false); 165 | return true; 166 | } 167 | else if(board.isFieldCircle(1,j) && board.isFieldCircle(2,j) && board.isFieldEmpty(0,j)) 168 | { 169 | board.place(0,j,false); 170 | return true; 171 | } 172 | } 173 | 174 | // can win on a diagonal? 175 | if(board.isFieldCircle(0,0) && board.isFieldCircle(1,1) && board.isFieldEmpty(2,2)) 176 | { 177 | board.place(2,2,false); 178 | return true; 179 | } 180 | else if(board.isFieldCircle(0,0) && board.isFieldCircle(2,2) && board.isFieldEmpty(1,1)) 181 | { 182 | board.place(1,1,false); 183 | return true; 184 | } 185 | else if(board.isFieldCircle(1,1) && board.isFieldCircle(2,2) && board.isFieldEmpty(0,0)) 186 | { 187 | board.place(0,0,false); 188 | return true; 189 | } 190 | else if(board.isFieldCircle(0,2) && board.isFieldCircle(1,1) && board.isFieldEmpty(2,0)) 191 | { 192 | board.place(2,0,false); 193 | return true; 194 | } 195 | else if(board.isFieldCircle(0,2) && board.isFieldCircle(2,0) && board.isFieldEmpty(1,1)) 196 | { 197 | board.place(1,1,false); 198 | return true; 199 | } 200 | else if(board.isFieldCircle(1,1) && board.isFieldCircle(2,2) && board.isFieldEmpty(0,2)) 201 | { 202 | board.place(0,2,false); 203 | return true; 204 | } 205 | 206 | return false; // no possible way to win 207 | } 208 | --------------------------------------------------------------------------------