Skip to content

Instantly share code, notes, and snippets.

@bethropolis
Last active December 10, 2024 04:55
Show Gist options
  • Save bethropolis/45af65dcd907d9c59896db121d2ed265 to your computer and use it in GitHub Desktop.
Save bethropolis/45af65dcd907d9c59896db121d2ed265 to your computer and use it in GitHub Desktop.
tik tak toe game
/*
Tic-Tac-Toe Game
This file contains the implementation of a simple Tic-Tac-Toe game.
The game is played between a human player (Player X) and a bot (Player O).
The main functions include:
- displayBoard: To display the current state of the game board.
- checkWin: To check if a player has won the game.
- checkDraw: To check if the game is a draw.
- getPlayerMove: To get and validate the human player's move.
- botMove: To let the bot make a random move.
- playGame: To handle the main gameplay loop.
The game starts with the human player and alternates between the player and the bot.
*/
// -----------------------------------------------------------------------------------------------
// STANDARD LIBRARIES
// -----------------------------------------------------------------------------------------------
#include <iostream> // For input/output
#include <vector> // For vector container class (game board)
#include <cstdlib> // For random number generation (bot move selection)
#include <ctime> // To seed random number generator
#include <limits> // For clearing input buffer (cin)
using namespace std;
/**
* @brief Displays the current state of the Tic-Tac-Toe board.
*
* This function takes a vector of characters representing the Tic-Tac-Toe board
* and prints it in a 3x3 grid format. Each cell of the board is separated by
* vertical and horizontal lines to visually represent the board.
*
* @param board A vector of characters representing the Tic-Tac-Toe board.
* The vector should have exactly 9 elements.
*/
void displayBoard(const vector<char>& board) {
cout << "\n";
cout << "Current Board:\n";
for (int i = 0; i < 9; i += 3) { // loop through each row
cout << " " << board[i] << " | " << board[i+1] << " | " << board[i+2] << "\n";
if (i < 6) cout << "---|---|---\n";
}
cout << "\n";
}
/**
* @brief Checks if the given player has won the game.
*
* This function checks all possible winning conditions in a Tic-Tac-Toe game:
* - Three in a row horizontally
* - Three in a row vertically
* - Three in a row diagonally
*
* @param board A vector representing the Tic-Tac-Toe board.
* @param player The player character to check for a win ('X' or 'O').
* @return true if the player has won, false otherwise.
*
* Example of winning states:
*
* Horizontal win:
* X | X | X
* - | - | -
* - | - | -
*
* Vertical win:
* X | - | -
* X | - | -
* X | - | -
*
* Diagonal win:
* X | - | -
* - | X | -
* - | - | X
* */
bool checkWin(const vector<char>& board, char player) {
// Check rows
for (int i = 0; i < 9; i += 3) {
if (board[i] == player && board[i+1] == player && board[i+2] == player) {
// first run: 1, 2, 3
// second run: 4, 5, 6
// third run: 7, 8, 9
return true;
}
}
// Check columns
for (int i = 0; i < 3; ++i) {
if (board[i] == player && board[i+3] == player && board[i+6] == player) {
// first run: 1, 4, 7
// second run: 2, 5, 8
// third run: 3, 6, 9
return true;
}
}
// Check diagonals (top-left to bottom-right and top-right to bottom-left)
if ((board[0] == player && board[4] == player && board[8] == player) ||
(board[2] == player && board[4] == player && board[6] == player)) {
return true;
}
return false;
}
/**
* @brief Checks if the game is a draw.
*
* This function iterates through the Tic-Tac-Toe board to determine if there are any empty cells left.
* If all cells are filled and no player has won, the game is considered a draw.
*
* @param board A vector representing the Tic-Tac-Toe board.
* @return true if the game is a draw, false otherwise.
*
* Example of a draw state:
*
* X | O | X
* O | X | X
* O | X | O
*/
bool checkDraw(const vector<char>& board) {
for (const char& cell : board) {
if (cell == ' ') return false; // If there's an empty cell, it's not a draw
}
return true; // No empty cells, it's a draw
}
/**
* @brief Prompts the player to enter their move and validates the input.
*
* This function continuously prompts the player (Player X) to enter their move
* until a valid move is provided. A valid move is an integer between 1 and 9
* that corresponds to an empty spot on the board. The function checks if the
* input is within the valid range and if the selected cell is empty. If the
* input is invalid, an error message is displayed and the player is prompted
* to enter their move again.
*
* @param board A constant reference to a vector of characters representing the game board.
* @return int The valid move entered by the player, adjusted for 0-indexing.
*/
int getPlayerMove(const vector<char>& board) {
int move;
while (true) {
cout << "Your turn (Player X). Enter your move (1-9): ";
cin >> move;
// Check if input is valid and within range
// Also check if the selected cell is empty
// we are subtracting 1 from the move because the board is 0-indexed
if (cin.fail() || move < 1 || move > 9 || board[move - 1] != ' ') {
cout << "Invalid input! Please enter a number between 1 and 9 for an empty spot.\n";
// Clear invalid input
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
} else {
return move; // Return the valid move
}
}
}
/**
* @brief Lets the bot make a random move.
*
* This function allows the bot (Player O) to make a move by selecting a random empty spot on the board.
* It ensures that the bot's move is valid by checking if the selected cell is empty.
*
* @param board A vector representing the Tic-Tac-Toe board.
*
*
* Example:
* Initial board state (empty):
* 1 | 2 | 3
* ---|---|---
* 4 | 5 | 6
* ---|---|---
* 7 | 8 | 9
*
* Suppose the board is in the following state:
* X | O | X
* ---|---|---
* | X |
* ---|---|---
* O | |
*
* The bot will randomly select from the remaining empty spots (4, 6, 8, 9)
* If it selects spot 6, the board will be updated to:
* X | O | X
* ---|---|---
* | X | O
* ---|---|---
* O | |
* */
void botMove(vector<char>& board) {
int move;
// Generate a random move until a valid move is found
// Ensure the selected cell is empty
do {
move = rand() % 9; // Generate a random move between 0 and 8
} while (board[move] != ' '); // Ensure the move is valid
board[move] = 'O'; // Place the bot's mark
cout << "Bot (O) has made its move!\n";
}
// Function to handle the gameplay loop
void playGame() {
vector<char> board(9, ' '); // Initialize the game board
char currentPlayer = 'X'; // Player X always starts
srand(static_cast<unsigned>(time(0))); // Seed random generator (without this, bot will make the same moves)
cout << "Welcome to Tic-Tac-Toe!\n";
cout << "You are Player X. The bot is Player O.\n";
cout << "To make a move, enter a number between 1 and 9 corresponding to the board position:\n";
cout << " 1 | 2 | 3\n";
cout << "---|---|---\n";
cout << " 4 | 5 | 6\n";
cout << "---|---|---\n";
cout << " 7 | 8 | 9\n";
// Main game loop
while (true) {
displayBoard(board);
if (currentPlayer == 'X') {
// Player's turn
int move = getPlayerMove(board);
board[move - 1] = 'X';
} else {
// Bot's turn
botMove(board);
}
// Check if the current player wins
if (checkWin(board, currentPlayer)) {
displayBoard(board);
if (currentPlayer == 'X') {
cout << "Congratulations! You (Player X) win!\n";
} else {
cout << "The bot (Player O) wins! Better luck next time.\n";
}
break;
}
// Check if the game is a draw
if (checkDraw(board)) {
displayBoard(board);
cout << "It's a draw! Nobody wins this time.\n";
break;
}
// Switch to the next player
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
}
// -----------------------------------------------------------------------------------------------
// MAIN FUNCTION
// -----------------------------------------------------------------------------------------------
int main() {
playGame(); // Start the game
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment