From 198d857d7c0e9b0f164c7474149f0b31909904fb Mon Sep 17 00:00:00 2001 From: "amirM.t" Date: Mon, 13 Apr 2026 20:05:04 +0330 Subject: [PATCH] Initial commit on develop branch --- game_history.txt | 185 ++++++ main.cpp | 1499 ++++++++++++++++++++++++++++++++++++++++++++++ main.exe | Bin 0 -> 166191 bytes saved_game.txt | 0 stats.txt | 8 + 5 files changed, 1692 insertions(+) create mode 100644 game_history.txt create mode 100644 main.cpp create mode 100644 main.exe create mode 100644 saved_game.txt create mode 100644 stats.txt diff --git a/game_history.txt b/game_history.txt new file mode 100644 index 0000000..0e0cc95 --- /dev/null +++ b/game_history.txt @@ -0,0 +1,185 @@ +Date: Tue Dec 30 21:48:12 2025 +Player 1: amir | Score: 11 +Player 2: BOT | Score: 1 +Winner: amir +------------------------ +Date: Wed Dec 31 18:18:01 2025 +Player 1: amir | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Wed Dec 31 19:30:39 2025 +Player 1: lucas | Score: 6 +Player 2: dustin | Score: 10 +Winner: dustin +------------------------ +Date: Wed Dec 31 19:39:55 2025 +Player 1: amir | Score: 5 +Player 2: BOT | Score: 11 +Winner: BOT +------------------------ +Date: Wed Dec 31 19:54:14 2025 +Player 1: max | Score: 12 +Player 2: holly | Score: 4 +Winner: max +------------------------ +Date: Wed Dec 31 19:55:06 2025 +Player 1: vecna | Score: 6 +Player 2: will | Score: 10 +Winner: will +------------------------ +Date: Wed Dec 31 20:03:41 2025 +Player 1: amir | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Wed Dec 31 20:06:02 2025 +Player 1: amir | Score: 9 +Player 2: BOT | Score: 7 +Winner: amir +------------------------ +Date: Wed Dec 31 20:07:12 2025 +Player 1: amir | Score: 8 +Player 2: BOT | Score: 8 +Winner: Draw +------------------------ +Date: Wed Dec 31 20:08:29 2025 +Player 1: amir | Score: 13 +Player 2: BOT | Score: 3 +Winner: amir +------------------------ +Date: Wed Dec 31 20:11:09 2025 +Player 1: amir | Score: 8 +Player 2: BOT | Score: 8 +Winner: Draw +------------------------ +Date: Wed Dec 31 20:13:04 2025 +Player 1: amir | Score: 11 +Player 2: BOT | Score: 5 +Winner: amir +------------------------ +Date: Wed Dec 31 20:13:57 2025 +Player 1: amir | Score: 12 +Player 2: BOT | Score: 4 +Winner: amir +------------------------ +Date: Wed Dec 31 20:16:57 2025 +Player 1: vecna | Score: 18 +Player 2: BOT | Score: 18 +Winner: Draw +------------------------ +Date: Wed Dec 31 20:17:48 2025 +Player 1: amir | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Wed Dec 31 20:25:18 2025 +Player 1: amir | Score: 3 +Player 2: BOT | Score: 13 +Winner: BOT +------------------------ +Date: Wed Dec 31 20:26:34 2025 +Player 1: 2pac | Score: 6 +Player 2: eminem | Score: 10 +Winner: eminem +------------------------ +Date: Wed Dec 31 22:19:49 2025 +Player 1: amir | Score: 2 +Player 2: BOT | Score: 11 +Winner: BOT +------------------------ +Date: Wed Dec 31 22:20:38 2025 +Player 1: amir | Score: 1 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Wed Dec 31 22:21:37 2025 +Player 1: amir | Score: 4 +Player 2: BOT | Score: 12 +Winner: BOT +------------------------ +Date: Fri Jan 2 16:54:41 2026 +Player 1: amir | Score: 5 +Player 2: BOT | Score: 11 +Winner: BOT +------------------------ +Date: Fri Jan 2 18:03:30 2026 +Player 1: amir | Score: 15 +Player 2: BOT | Score: 1 +Winner: amir +------------------------ +Date: Fri Jan 2 20:39:47 2026 +Player 1: amir | Score: 17 +Player 2: momad | Score: 19 +Winner: momad +------------------------ +Date: Fri Jan 2 21:06:02 2026 +Player 1: amir | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Fri Jan 2 22:08:39 2026 +Player 1: amir | Score: 10 +Player 2: BOT | Score: 6 +Winner: amir +------------------------ +Date: Sat Jan 3 11:48:17 2026 +Player 1: steve | Score: 6 +Player 2: dustin | Score: 10 +Winner: dustin +------------------------ +Date: Sat Jan 3 15:33:15 2026 +Player 1: amir | Score: 10 +Player 2: BOT | Score: 6 +Winner: amir +------------------------ +Date: Sat Jan 3 15:34:36 2026 +Player 1: liam | Score: 6 +Player 2: harry | Score: 10 +Winner: harry +------------------------ +Date: Sat Jan 3 18:47:04 2026 +Player 1: amir | Score: 26 +Player 2: BOT | Score: 38 +Winner: BOT +------------------------ +Date: Sun Jan 4 09:25:38 2026 +Player 1: amir | Score: 11 +Player 2: BOT | Score: 25 +Winner: BOT +------------------------ +Date: Sun Jan 4 14:20:09 2026 +Player 1: amir | Score: 37 +Player 2: BOT | Score: 27 +Winner: amir +------------------------ +Date: Mon Jan 5 11:27:18 2026 +Player 1: steve | Score: 10 +Player 2: BOT | Score: 26 +Winner: BOT +------------------------ +Date: Mon Jan 5 11:29:59 2026 +Player 1: harry | Score: 18 +Player 2: voldi | Score: 46 +Winner: voldi +------------------------ +Date: Mon Jan 5 11:30:52 2026 +Player 1: chester | Score: 5 +Player 2: mike | Score: 11 +Winner: mike +------------------------ +Date: Mon Jan 5 11:31:31 2026 +Player 1: chester | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Mon Jan 5 11:32:27 2026 +Player 1: amir | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ +Date: Mon Jan 5 11:59:01 2026 +Player 1: amir | Score: 6 +Player 2: BOT | Score: 10 +Winner: BOT +------------------------ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..69156bb --- /dev/null +++ b/main.cpp @@ -0,0 +1,1499 @@ +/* +============================================================ + OTHELLO – Console Game (C++) +============================================================ + + Author: Amirmohammad Tayefe + Course: Basic Programming + Language: C++ + Platform: Windows Console + + ------------------------------------------------------------ + DESCRIPTION + ------------------------------------------------------------ + This project is a complete console-based implementation of + the Othello board game. + + Features: + - Single-player (Human vs Bot) + - Two-player (Human vs Human) + - Multiple bot difficulty levels + - Cursor-based menu navigation (arrow keys / WASD) + - Non-flickering ANSI-rendered UI + - Game save & load support + - Game history with pagination + - Persistent player statistics with ranking + - Contextual in-game help + - Input validation and error handling + - Colored and styled text using ANSI escape codes + + ------------------------------------------------------------ + DESIGN NOTES + ------------------------------------------------------------ + - Rendering is done using ANSI escape sequences. + - Cursor-based menus avoid numeric input. + - Player statistics are stored persistently in text files. + - Bots are excluded from statistics tracking. + - Screen flickering is avoided by cursor repositioning. + +============================================================ +*/ + +// ANSI escape codes for text styling and colors +// Used to enhance visual clarity in the console UI + +#define RESET "\033[0m" +#define BLACK_P "\033[30;1m" +#define WHITE_P "\033[37;1m" +#define BOARD_C "\033[36m" +#define HINT_C "\033[33m" +#define CURSOR_BG "\033[47m" +#define GOLD_C "\033[38;5;220m" +#define SILVER_C "\033[38;5;250m" +#define BRONZE_C "\033[38;5;172m" + +// Includes and namespaces + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/* ========= STRUCTS ========= */ +struct Player +{ + char name[32]; + bool isBot; +}; + +struct GameConfig +{ + int boardSize; // 4 to 10, even only + bool hintsEnabled; +}; + +struct Game +{ + int size; + char board[10][10]; + Player players[2]; + int currentPlayer; + bool hintsEnabled; + int botLevel; +}; + +struct PlayerStats +{ + char name[30]; + int totalScore; + int wins; + int losses; + int draws; + int games; +}; + +// used for MiniMax algorithm +int positionWeight[10][10] = + {{100, -20, 10, 5, 5, 10, -20, 100}, + {-20, -50, -2, -2, -2, -2, -50, -20}, + {10, -2, 5, 1, 1, 5, -2, 10}, + {5, -2, 1, 1, 1, 1, -2, 5}, + {5, -2, 1, 1, 1, 1, -2, 5}, + {10, -2, 5, 1, 1, 5, -2, 10}, + {-20, -50, -2, -2, -2, -2, -50, -20}, + {100, -20, 10, 5, 5, 10, -20, 100}}; + +const int MAX_PLAYERS = 100; +PlayerStats players[MAX_PLAYERS]; +int playerCount = 0; + +// Functions Declarations +void clearScreen(); +void waitForKey(); +void seedRandom(); +void initializeGame(Game &game, const GameConfig &config, const Player &p1, const Player &p2); +bool isOnBoard(int row, int col, int size); +char opponentDisk(char disk); +bool isValidMove(const Game &game, int row, int col, char playerDisk); +bool hasAnyValidMove(const Game &game, char playerDisk); +void applyMove(Game &game, int row, int col, char playerDisk); +int countDisks(const Game &game, char playerDisk); +void showHints(Game &game, char playerDisk); +void gameLoop(Game &game); +void newGame(); +void gameHistory(); +void saveCurrentGame(const Game &game); +bool loadSavedGame(Game &game); +bool askYesNo(const char *message); +void loadGame(); +void moveCursor(int &row, int &col, int dRow, int dCol, int size); +void drawBoard(const Game &game, int cursorRow, int cursorCol); +void clearLinesBelow(int startRow, int n); +void showHelp(); +void saveGameHistory(const Game &game, int blackScore, int whiteScore); +void drawHelpPage(int page); +int chooseBotDifficulty(); +void showMenu(); +void drawTitle(); +void showMenuOptions(); +int findPlayer(const char *name); +void saveStatistics(); +void loadStatistics(); +void sortPlayers(); +void showStatistics(); +void updateStatistics(const Game &game, int blackScore, int whiteScore); +void printCentered(const char *text, int width); +void printCenteredInt(int value, int width); +void printCenteredFloat(float value, int width); + +/* ========= Utility Functions ========= */ + +// Clears the console screen. +// NOTE: Uses Windows-specific command. +void clearScreen() +{ + system("cls"); +} + +// Waits until the user presses a key. +// Used to pause screens such as Help or Statistics. +void waitForKey() +{ + getch(); +} + +// Seeds the random number generator. +// Must be called once at program startup. +void seedRandom() +{ + srand(static_cast(time(0))); +} + +/* ========= GAME LOGIC ========= */ + +// Initializes a new game with the given configuration and players. +// Sets up the board, initial pieces, and game state. +void initializeGame(Game &game, const GameConfig &config, const Player &p1, const Player &p2) +{ + game.size = config.boardSize; + game.players[0] = p1; + game.players[1] = p2; + game.currentPlayer = 0; + game.hintsEnabled = config.hintsEnabled; + + // Empty board + for (int r = 0; r < game.size; r++) + for (int c = 0; c < game.size; c++) + game.board[r][c] = ' '; + + // Initial placement + int mid = game.size / 2; + game.board[mid - 1][mid - 1] = 'W'; + game.board[mid][mid] = 'W'; + game.board[mid - 1][mid] = 'B'; + game.board[mid][mid - 1] = 'B'; +} + +// Checks if the given (row, col) is within the board boundaries. +bool isOnBoard(int row, int col, int size) +{ + return row >= 0 && row < size && col >= 0 && col < size; +} + +// Returns the opponent's disk character. +char opponentDisk(char disk) +{ + return (disk == 'B') ? 'W' : 'B'; +} + +// Checks whether a move at (row, col) is valid for the given player. +// A move is valid if it flips at least one opponent disk. +bool isValidMove(const Game &game, int row, int col, char playerDisk) +{ + if (!isOnBoard(row, col, game.size) || game.board[row][col] != ' ') + return false; + + char opp = opponentDisk(playerDisk); + int dx[] = {-1, -1, 0, 1, 1, 1, 0, -1}; + int dy[] = {0, 1, 1, 1, 0, -1, -1, -1}; + + for (int dir = 0; dir < 8; dir++) + { + int r = row + dx[dir], c = col + dy[dir]; + bool foundOpp = false; + while (isOnBoard(r, c, game.size) && game.board[r][c] == opp) + { + r += dx[dir]; + c += dy[dir]; + foundOpp = true; + } + if (foundOpp && isOnBoard(r, c, game.size) && game.board[r][c] == playerDisk) + return true; + } + return false; +} + +// Checks if the specified player has any valid moves remaining. +bool hasAnyValidMove(const Game &game, char playerDisk) +{ + for (int r = 0; r < game.size; r++) + for (int c = 0; c < game.size; c++) + if (isValidMove(game, r, c, playerDisk)) + return true; + + return false; +} + +// Applies a valid move to the board. +// Places the disk and flips all captured opponent disks. +void applyMove(Game &game, int row, int col, char playerDisk) +{ + game.board[row][col] = playerDisk; + char opp = opponentDisk(playerDisk); + + // Check all 8 directions for flips + // dx represents row change, dy represents column change + int dx[] = {-1, -1, 0, 1, 1, 1, 0, -1}; + int dy[] = {0, 1, 1, 1, 0, -1, -1, -1}; + + for (int dir = 0; dir < 8; dir++) + { + int r = row + dx[dir], c = col + dy[dir]; + bool foundOpp = false; + int flipR[10], flipC[10], count = 0; + + while (isOnBoard(r, c, game.size) && game.board[r][c] == opp) + { + flipR[count] = r; + flipC[count] = c; + count++; + r += dx[dir]; + c += dy[dir]; + foundOpp = true; + } + if (foundOpp && isOnBoard(r, c, game.size) && game.board[r][c] == playerDisk) + { + for (int i = 0; i < count; i++) + game.board[flipR[i]][flipC[i]] = playerDisk; + } + } +} + +// Counts the number of disks on the board for the specified player. +int countDisks(const Game &game, char playerDisk) +{ + int cnt = 0; + for (int r = 0; r < game.size; r++) + for (int c = 0; c < game.size; c++) + if (game.board[r][c] == playerDisk) + cnt++; + return cnt; +} + +// Executes a bot move based on the selected difficulty level. +// Easy: random +// Normal: greedy heuristic +// Hard: limited look-ahead evaluation using MiniMax principles +void botMove(Game &game, int &row, int &col) +{ + char botDisk = (game.currentPlayer == 0) ? 'B' : 'W'; + char oppDisk = opponentDisk(botDisk); + + int bestScore = -100000; + bool found = false; + + for (int r = 0; r < game.size; r++) + { + for (int c = 0; c < game.size; c++) + { + if (!isValidMove(game, r, c, botDisk)) + continue; + + found = true; + + // easy: random movement + if (game.botLevel == 1) + { + if (rand() % 2 == 0) + { + row = r; + col = c; + return; + } + } + + Game temp = game; + applyMove(temp, r, c, botDisk); + + int score = 0; + + // normal: greedy algorithm and position analysis + if (game.botLevel == 2) + { + score = countDisks(temp, botDisk) - countDisks(temp, oppDisk); + score += positionWeight[r][c]; + } + + // hard: minimax algorithm (simple version) + else if (game.botLevel == 3) + { + score = positionWeight[r][c] * 2; + + for (int rr = 0; rr < game.size; rr++) + for (int cc = 0; cc < game.size; cc++) + if (isValidMove(temp, rr, cc, oppDisk)) + score -= positionWeight[rr][cc]; + } + + if (score > bestScore) + { + bestScore = score; + row = r; + col = c; + } + } + } + + // fallback safety + // should not happen if there are valid moves + if (!found) + { + row = 0; + col = 0; + } +} + +// Main game execution loop. +// Handles turns, input, rendering, skipping, and end-game detection. +void gameLoop(Game &game) +{ + cout << "\033[?25l"; // hide terminal cursor + int msgRow = game.size * 2 + 4; // first line below the board + + while (true) + { + char playerDisk = (game.currentPlayer == 0) ? 'B' : 'W'; + Player &player = game.players[game.currentPlayer]; + + if (!hasAnyValidMove(game, playerDisk)) + { + drawBoard(game, -1, -1); + clearLinesBelow(msgRow, 2); + + cout << "\033[" << msgRow << ";0H"; + cout << player.name << " has no valid moves. Turn skipped.\n"; + + game.currentPlayer = 1 - game.currentPlayer; + + if (!hasAnyValidMove(game, opponentDisk(playerDisk))) + break; + + this_thread::sleep_for(chrono::seconds(2)); + continue; + } + + int row, col; + + if (player.isBot) + { + drawBoard(game, -1, -1); + + clearLinesBelow(msgRow, 2); + cout << "\033[" << msgRow << ";0H" << "Robot is thinking...\n"; + cout.flush(); + + // Simulate thinking time + this_thread::sleep_for(chrono::seconds(1)); + + botMove(game, row, col); + + // Robot move message display + clearLinesBelow(msgRow, 2); + cout << "\033[" << msgRow << ";0H" << "Robot moved at: " << row << ", " << col << "\n"; + cout.flush(); + + // Pause before applying move for better UX + this_thread::sleep_for(chrono::seconds(1)); + } + else + { + int cursorRow = 0, cursorCol = 0; + + while (true) + { + drawBoard(game, cursorRow, cursorCol); + + clearLinesBelow(msgRow, 3); // clear previous player prompts + cout << "\033[" << msgRow << ";0H"; + cout << player.name << "'s turn: " << ((playerDisk == 'B') ? "Black ●" : "White ○") << "\n"; + + int ch = getch(); + + // Arrow keys + if (ch == 0 || ch == 224) + { + int arrow = getch(); + + switch (arrow) + { + case 72: + moveCursor(cursorRow, cursorCol, -1, 0, game.size); + break; + case 80: + moveCursor(cursorRow, cursorCol, 1, 0, game.size); + break; + case 75: + moveCursor(cursorRow, cursorCol, 0, -1, game.size); + break; + case 77: + moveCursor(cursorRow, cursorCol, 0, 1, game.size); + break; + } + } + // WASD keys and others + else if (ch == 'w' || ch == 'W') + moveCursor(cursorRow, cursorCol, -1, 0, game.size); + else if (ch == 's' || ch == 'S') + moveCursor(cursorRow, cursorCol, 1, 0, game.size); + else if (ch == 'a' || ch == 'A') + moveCursor(cursorRow, cursorCol, 0, -1, game.size); + else if (ch == 'd' || ch == 'D') + moveCursor(cursorRow, cursorCol, 0, 1, game.size); + else if (ch == 13 && isValidMove(game, cursorRow, cursorCol, playerDisk)) + { + row = cursorRow; + col = cursorCol; + break; + } + else if (ch == 'q' || ch == 'Q') + { + bool quitGame = askYesNo("Do you wanna quit the game?"); + if (!quitGame) + continue; + + bool saveGame = askYesNo("Do you wanna save the game?"); + if (saveGame) + saveCurrentGame(game); + return; + } + } + } + + applyMove(game, row, col, playerDisk); + game.currentPlayer = 1 - game.currentPlayer; + + int total = countDisks(game, 'B') + countDisks(game, 'W'); + if (total == game.size * game.size) + break; + } + + drawBoard(game, -1, -1); + clearLinesBelow(msgRow, 3); + + int blackCount = countDisks(game, 'B'); + int whiteCount = countDisks(game, 'W'); + + cout << "\033[" << msgRow << ";0H"; + + cout << "Game Over!\n"; + cout << "Black ●: " << blackCount << " | White ○: " << whiteCount << "\n"; + + if (blackCount > whiteCount) + cout << game.players[0].name << " wins!\n"; + else if (whiteCount > blackCount) + cout << game.players[1].name << " wins!\n"; + else + cout << "It's a draw!\n"; + + saveGameHistory(game, blackCount, whiteCount); + updateStatistics(game, blackCount, whiteCount); + + cout.flush(); + + cout << "Press any key to return to menu..." << endl; + waitForKey(); +} + +void newGame() +{ + bool inNewGameMenu = true; + Player p1, p2; + GameConfig config; + Game game; + + while (inNewGameMenu) + { + clearScreen(); + cout << "*************************" << endl; + cout << "* NEW GAME *" << endl; + cout << "*************************" << endl; + cout << "1. Single Player" << endl; + cout << "2. Two Player" << endl; + cout << "3. Back" << endl; + + char choice = getch(); + + switch (choice) + { + case '1': + clearScreen(); + cout << "[Single Player]\n\n"; + cout << "Enter your name: "; + cin >> p1.name; + p1.isBot = false; + + game.botLevel = chooseBotDifficulty(); + + strcpy(p2.name, "BOT"); + p2.isBot = true; + inNewGameMenu = false; + break; + + case '2': + clearScreen(); + cout << "[Two Player]\n\n"; + cout << "Enter Player 1 name: "; + cin >> p1.name; + cout << endl; + cout << "Enter Player 2 name: "; + cin >> p2.name; + p1.isBot = false; + p2.isBot = false; + inNewGameMenu = false; + break; + case '3': + return; + default: + continue; + } + } + + while (true) + { + clearScreen(); + cout << "Enter board size (even numbers from 4 to 10): "; + cin >> config.boardSize; + + if (config.boardSize >= 4 && config.boardSize <= 10 && config.boardSize % 2 == 0) + break; + + cout << "Invalid board size.\n"; + waitForKey(); + } + + while (true) + { + clearScreen(); + + cout << "Do you want to Enable the move hints? (Y/N)" << endl; + + char h = getch(); + + if (h == 'y' || h == 'Y') + { + config.hintsEnabled = true; + break; + } + if (h == 'n' || h == 'N') + { + config.hintsEnabled = false; + break; + } + } + + clearScreen(); + initializeGame(game, config, p1, p2); + drawBoard(game, -1, -1); + gameLoop(game); +} + +// Saves a completed game data to the history file. +void saveGameHistory(const Game &game, int blackScore, int whiteScore) +{ + ofstream file("game_history.txt", ios::app); // append mode + + if (!file) + return; + + time_t now = time(0); + char *dt = ctime(&now); // get current date/time as string + dt[strlen(dt) - 1] = '\0'; // remove trailing newline + + char winner[32]; + if (blackScore > whiteScore) + strcpy(winner, game.players[0].name); + else if (whiteScore > blackScore) + strcpy(winner, game.players[1].name); + else + strcpy(winner, "Draw"); + + file << "Date: " << dt << "\n"; + file << "Player 1: " << game.players[0].name << " | Score: " << blackScore << "\n"; + file << "Player 2: " << game.players[1].name << " | Score: " << whiteScore << "\n"; + file << "Winner: " << winner << "\n"; + file << "------------------------\n"; + + file.close(); +} + +// Displays paginated game history records. +void gameHistory() +{ + clearScreen(); + ifstream file("game_history.txt"); + if (!file) + { + cout << "No game history found.\n"; + waitForKey(); + return; + } + + const int MAX_ENTRIES = 1000; + const int MAX_LINES = 5; + const int RECORDS_PER_PAGE = 5; + + string history[MAX_ENTRIES][MAX_LINES]; + int entryCount = 0; + int lineCount = 0; + string line; + + // Read file into records + while (getline(file, line)) + { + history[entryCount][lineCount++] = line; + + if (line == "------------------------") + { + entryCount++; + lineCount = 0; + if (entryCount >= MAX_ENTRIES) + break; + } + } + file.close(); + + if (entryCount == 0) + { + cout << "No game history available.\n"; + waitForKey(); + return; + } + + int totalPages = (entryCount + RECORDS_PER_PAGE - 1) / RECORDS_PER_PAGE; + int page = 0; + + while (true) + { + clearScreen(); + + cout << "===== GAME HISTORY =====\n"; + cout << "Total Records: " << entryCount << "\n"; + cout << "Page " << (page + 1) << " / " << totalPages << "\n\n"; + + int start = page * RECORDS_PER_PAGE; + int end = start + RECORDS_PER_PAGE; + if (end > entryCount) + end = entryCount; + + // Display records (latest first) + for (int i = start; i < end; i++) + { + int recordIndex = entryCount - 1 - i; + for (int j = 0; j < MAX_LINES; j++) + { + if (!history[recordIndex][j].empty()) + cout << history[recordIndex][j] << "\n"; + } + cout << "\n"; + } + + cout << "---------------------------------\n"; + cout << "← Prev | → Next | Q Back\n"; + + int ch = getch(); + + if (ch == 0 || ch == 224) + { + int arrow = getch(); + if (arrow == 75 && page > 0) + page--; // Left + else if (arrow == 77 && page < totalPages - 1) + page++; // Right + } + else if (ch == 'q' || ch == 'Q') + return; + } +} + +// Saves the current game state to a file. +// In case of saving more than one game, only the last played game will be saved +void saveCurrentGame(const Game &game) +{ + ofstream file("saved_game.txt", ios::out); + if (!file) + return; + + file << game.size << "\n"; + file << game.hintsEnabled << "\n"; + file << game.currentPlayer << "\n"; + + // Save player info + for (int i = 0; i < 2; i++) + { + file << game.players[i].name << "\n"; + file << game.players[i].isBot << "\n"; + } + + // Save board + for (int r = 0; r < game.size; r++) + { + for (int c = 0; c < game.size; c++) + file << game.board[r][c]; + file << "\n"; + } + + file.close(); +} + +// Loads a saved game from file. +bool loadSavedGame(Game &game) +{ + ifstream file("saved_game.txt"); + + if (!file) + return false; + + // Read basic config + file >> game.size; + file >> game.hintsEnabled; + file >> game.currentPlayer; + file.ignore(1024, '\n'); // flush rest of line + + // Validate board size + if (game.size < 4 || game.size > 10 || game.size % 2 != 0) + return false; + + // Validate current player + if (game.currentPlayer < 0 || game.currentPlayer > 1) + game.currentPlayer = 0; + + for (int i = 0; i < 2; i++) + { + file.getline(game.players[i].name, 32); + + int botFlag = 0; + file >> botFlag; + game.players[i].isBot = (botFlag != 0); + file.ignore(1024, '\n'); + } + + for (int r = 0; r < game.size; r++) + { + char line[32]; + file.getline(line, 32); + + for (int c = 0; c < game.size; c++) + { + char ch = line[c]; + if (ch == 'B' || ch == 'W' || ch == ' ') + game.board[r][c] = ch; + else + game.board[r][c] = ' '; + } + } + + file.close(); + return true; +} + +// Used for Yes/No dialogs. +bool askYesNo(const char *message) +{ + char choice; + while (true) + { + cout << message << " (Y/N): "; + cin >> choice; + + if (choice == 'Y' || choice == 'y') + return true; + if (choice == 'N' || choice == 'n') + return false; + + cout << "Invalid input. Please enter Y or N." << endl; + } +} + +// Loads a previously saved game from file. +// Validates data before restoring state. +void loadGame() +{ + Game game; + + if (!loadSavedGame(game)) + { + clearScreen(); + cout << "No saved game found or save file is corrupted." << endl; + waitForKey(); + return; + } + + clearScreen(); + drawBoard(game, -1, -1); + + int msgRow = game.size * 2 + 4; + clearLinesBelow(msgRow, 2); + + cout << "\033[" << msgRow << ";0H"; + + cout << "Saved game loaded successfully." << endl; + cout << "Press any key to resume the game..."; + cout.flush(); + + waitForKey(); + + gameLoop(game); +} + +// Moves the cursor within the board boundaries. +void moveCursor(int &row, int &col, int dRow, int dCol, int size) +{ + row += dRow; + col += dCol; + + if (row < 0) + row = 0; + if (row >= size) + row = size - 1; + if (col < 0) + col = 0; + if (col >= size) + col = size - 1; +} + +// Used to set bot difficulty level. +int chooseBotDifficulty() +{ + clearScreen(); + + cout << "Choose difficulty level:" << endl; + cout << "1. Easy" << endl; + cout << "2. Normal" << endl; + cout << "3. Hard" << endl; + + while (true) + { + char ch = getch(); + if (ch >= '1' && ch <= '3') + return ch - '0'; + } +} + +/* ========= Graphical Functions ========= */ + +// Displays the main menu and handles user navigation. +void showMenu() +{ + while (true) + { + clearScreen(); + showMenuOptions(); + } +} + +// Draws the game title in 3D style with colors. +void drawTitle() +{ + // ANSI color codes + const char *BLUE = "\033[1;34m"; // elegant blue + + // 3D title + const char *lines[] = + { + " ██████╗ ████████╗██╗ ██╗███████╗██╗ ██╗ ██████╗ ", + "██╔═══██╗╚══██╔══╝██║ ██║██╔════╝██║ ██║ ██╔═══██╗", + "██║ ██║ ██║ ███████║█████╗ ██║ ██║ ██║ ██║", + "██║ ██║ ██║ ██╔══██║██╔══╝ ██║ ██║ ██║ ██║", + "╚██████╔╝ ██║ ██║ ██║███████╗███████╗███████╗╚██████╔╝", + " ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ ╚═════╝ "}; + + // Print each line with color + for (int i = 0; i < 6; i++) + cout << BLUE << lines[i] << RESET << endl; + + cout << endl; +} + +// Displays the interactive menu options with cursor navigation. +void showMenuOptions() +{ + cout << "\033[?25l"; // hide terminal cursor + + const char *SELECT_BG = "\033[47m"; // white background + const char *SELECT_FG = "\033[30;1m"; // black bold text + const char *NORMAL = "\033[1;34m"; // elegant blue + + const char *items[] = { + "New Game", + "Load Game", + "Help", + "Game History", + "Statistics", + "Exit"}; + + const int itemCount = 6; + const int menuRow = 12; + const int menuCol = 3; + int selected = 0; + + clearScreen(); + drawTitle(); + + // Initial draw + for (int i = 0; i < itemCount; i++) + { + cout << "\033[" << (menuRow + i) << ";0H"; + cout << "\033[2K"; // clear line + + cout << "\033[" << (menuRow + i) << ";" << menuCol << "H"; + if (i == selected) + cout << SELECT_BG << SELECT_FG; + else + cout << NORMAL; + + cout << " " << items[i] << " " << RESET; + } + + while (true) + { + int ch = getch(); + int prev = selected; + + if (ch == 0 || ch == 224) + { + int arrow = getch(); + if (arrow == 72) + selected--; + else if (arrow == 80) + selected++; + } + else if (ch == 'w' || ch == 'W') + selected--; + else if (ch == 's' || ch == 'S') + selected++; + else if (ch == 13) + { + clearScreen(); + switch (selected) + { + case 0: + newGame(); + break; + case 1: + loadGame(); + break; + case 2: + showHelp(); + break; + case 3: + gameHistory(); + break; + case 4: + showStatistics(); + break; + case 5: + exit(0); + } + + clearScreen(); + drawTitle(); + selected = 0; + prev = -1; // force redraw to update menu + + for (int i = 0; i < itemCount; i++) + { + cout << "\033[" << (menuRow + i) << ";0H"; + cout << "\033[2K"; + + cout << "\033[" << (menuRow + i) << ";" << menuCol << "H"; + if (i == selected) + cout << SELECT_BG << SELECT_FG; + else + cout << NORMAL; + + cout << " " << items[i] << " " << RESET; + } + + continue; + } + + if (selected < 0) + selected = itemCount - 1; + if (selected >= itemCount) + selected = 0; + + // redraw when moving cursor + if (prev != selected) + { + cout << "\033[" << (menuRow + prev) << ";0H"; + cout << "\033[2K"; + cout << "\033[" << (menuRow + prev) << ";" << menuCol << "H"; + cout << NORMAL << " " << items[prev] << " " << RESET; + + cout << "\033[" << (menuRow + selected) << ";0H"; + cout << "\033[2K"; + cout << "\033[" << (menuRow + selected) << ";" << menuCol << "H"; + cout << SELECT_BG << SELECT_FG << " " << items[selected] << " " << RESET; + } + } +} + +// Displays the game board with hints for valid moves using the asterisk symbol (*). +void showHints(Game &game, char playerDisk) +{ + clearScreen(); + cout << BOARD_C << " "; + for (int c = 0; c < game.size; c++) + cout << c << " "; + cout << RESET << "\n"; + + cout << BOARD_C << " ┌"; + for (int c = 0; c < game.size; c++) + { + cout << "───"; + if (c < game.size - 1) + cout << "┬"; + } + cout << "┐\n" + << RESET; + + for (int r = 0; r < game.size; r++) + { + cout << BOARD_C << r << " │" << RESET; + for (int c = 0; c < game.size; c++) + { + cout << " "; + if (game.board[r][c] == 'B') + cout << BLACK_P << "●" << RESET; + else if (game.board[r][c] == 'W') + cout << WHITE_P << "○" << RESET; + else if (game.hintsEnabled && isValidMove(game, r, c, playerDisk)) + cout << HINT_C << "*" << RESET; + else + cout << " "; + cout << " "; + if (c < game.size - 1) + cout << BOARD_C << "│" << RESET; + } + cout << BOARD_C << "│\n" + << RESET; + if (r < game.size - 1) + { + cout << BOARD_C << " ├"; + for (int c = 0; c < game.size; c++) + { + cout << "───"; + if (c < game.size - 1) + cout << "┼"; + } + cout << "┤\n" + << RESET; + } + } + cout << BOARD_C << " └"; + for (int c = 0; c < game.size; c++) + { + cout << "───"; + if (c < game.size - 1) + cout << "┴"; + } + cout << "┘\n" + << RESET; +} + +// Draws the current game board with ANSI colors. +// Highlights cursor position and valid moves if enabled. +void drawBoard(const Game &game, int cursorRow, int cursorCol) +{ + char playerDisk = (game.currentPlayer == 0) ? 'B' : 'W'; + + // Move to top-left before redrawing + cout << "\033[H"; + + // Column numbers + cout << BOARD_C << " "; + for (int c = 0; c < game.size; c++) + cout << c << " "; + cout << RESET << "\n"; + + // Top border + cout << BOARD_C << " ┌"; + for (int c = 0; c < game.size; c++) + { + cout << "───"; + if (c < game.size - 1) + cout << "┬"; + } + cout << "┐\n" + << RESET; + + // Board rows + for (int r = 0; r < game.size; r++) + { + cout << BOARD_C << r << " │" << RESET; + for (int c = 0; c < game.size; c++) + { + bool isCursor = (r == cursorRow && c == cursorCol); + + if (isCursor) + cout << CURSOR_BG; + + cout << " "; + if (game.board[r][c] == 'B') + cout << BLACK_P << "●" << RESET; + else if (game.board[r][c] == 'W') + cout << WHITE_P << "○" << RESET; + else if (game.hintsEnabled && isValidMove(game, r, c, playerDisk)) + cout << HINT_C << "*" << RESET; + else + cout << " "; + + if (isCursor) + cout << RESET; + + cout << " "; + if (c < game.size - 1) + cout << BOARD_C << "│" << RESET; + } + cout << BOARD_C << "│\n" + << RESET; + + if (r < game.size - 1) + { + cout << BOARD_C << " ├"; + for (int c = 0; c < game.size; c++) + { + cout << "───"; + if (c < game.size - 1) + cout << "┼"; + } + cout << "┤\n" + << RESET; + } + } + + // Bottom border + cout << BOARD_C << " └"; + for (int c = 0; c < game.size; c++) + { + cout << "───"; + if (c < game.size - 1) + cout << "┴"; + } + cout << "┘\n" + << RESET; + + cout.flush(); +} + +// Clears n lines starting from the given row +void clearLinesBelow(int startRow, int n) +{ + for (int i = 0; i < n; i++) + { + cout << "\033[" << (startRow + i) << ";0H"; // move to line + cout << "\033[2K"; // clear entire line + } + cout.flush(); +} + +// Displays the help section with multiple pages and navigation. +void showHelp() +{ + int page = 0; + const int maxPages = 5; + + while (true) + { + clearScreen(); + drawHelpPage(page); + + int ch = getch(); + + if (ch == 0 || ch == 224) + { + int arrow = getch(); + if (arrow == 75 && page > 0) + page--; // Left + else if (arrow == 77 && page < maxPages - 1) + page++; // Right + } + else if (ch == 'q' || ch == 'Q' || ch == 27) + break; + } +} + +// Draws a specific help page based on the page number. +void drawHelpPage(int page) +{ + cout << "========== OTHELLO HELP ==========\n\n"; + + switch (page) + { + case 0: + cout << "WHAT IS OTHELLO?\n\n"; + cout << "Othello is a two-player strategy board game.\n"; + cout << "Players take turns placing disks on the board.\n"; + cout << "Any opponent disks trapped between yours flip.\n"; + cout << "\nYour Goal: finish the game with more disks than your opponent.\n"; + break; + + case 1: + cout << "GAME RULES\n\n"; + cout << "- A move is legal only if it flips at least one disk.\n"; + cout << "- Disks flip in straight lines (horizontal, vertical, diagonal).\n"; + cout << "- If a player has no legal move, the turn is skipped.\n"; + cout << "- Game ends when no player can move.\n"; + break; + + case 2: + cout << "GAME MODES & FEATURES\n\n"; + cout << "- Single Player: play against the bot (in different difficulties).\n"; + cout << "- Two Player: human vs human.\n"; + cout << "- Board sizes: 4x4 to 10x10 (even numbers only).\n"; + cout << "- Hints: highlight legal moves by asterisk (*).\n"; + cout << "- Save & Load supported.\n"; + break; + + case 3: + cout << "CONTROLS\n\n"; + cout << "Move Cursor:\n"; + cout << " W / A / S / D or Arrow Keys\n\n"; + cout << "Place Disk: ENTER\n"; + cout << "Quit Game: Q\n"; + break; + + case 4: + cout << "VISUAL GUIDE & TIPS\n\n"; + cout << "● Black Disk ○ White Disk\n"; + cout << "Highlighted white cell = cursor position\n\n"; + cout << "Tips:\n"; + cout << "- Corners are powerful.\n"; + cout << "- Edges are safer than center.\n"; + cout << "- Mobility matters more than disk count early on.\n"; + break; + } + + cout << "\n---------------------------------\n"; + cout << " ← Prev | → Next | q Exit\n"; +} + +// ========= Statistics Functions ========= + +// Reads player statistics from file into memory. +void loadStatistics() +{ + ifstream in("stats.txt"); + playerCount = 0; + + if (!in) + return; + + while (playerCount < MAX_PLAYERS && + in >> players[playerCount].name >> players[playerCount].totalScore >> players[playerCount].wins >> players[playerCount].losses >> players[playerCount].draws >> players[playerCount].games) + { + playerCount++; + } + + in.close(); +} + +// Saves player statistics from memory to file. +void saveStatistics() +{ + ofstream out("stats.txt"); + + for (int i = 0; i < playerCount; i++) + { + out << players[i].name << " " + << players[i].totalScore << " " + << players[i].wins << " " + << players[i].losses << " " + << players[i].draws << " " + << players[i].games << "\n"; + } + + out.close(); +} + +// Finds a player by name. If not found, creates a new entry in the file. +int findPlayer(const char *name) +{ + for (int i = 0; i < playerCount; i++) + { + if (strcmp(players[i].name, name) == 0) + return i; + } + + if (playerCount < MAX_PLAYERS) + { + strcpy(players[playerCount].name, name); + players[playerCount].totalScore = 0; + players[playerCount].wins = 0; + players[playerCount].losses = 0; + players[playerCount].draws = 0; + players[playerCount].games = 0; + return playerCount++; + } + + return -1; +} + +// Sorts players by ranking in the order: (score, wins, losses). +void sortPlayers() +{ + for (int i = 0; i < playerCount - 1; i++) + { + for (int j = i + 1; j < playerCount; j++) + { + if ( + players[j].totalScore > players[i].totalScore || + (players[j].totalScore == players[i].totalScore && + players[j].wins > players[i].wins) || + (players[j].totalScore == players[i].totalScore && + players[j].wins == players[i].wins && + players[j].losses < players[i].losses)) + { + PlayerStats temp = players[i]; + players[i] = players[j]; + players[j] = temp; + } + } + } +} + +// Displays the statistics table. +// Top 3 players are highlighted with colors for better visibility. +void showStatistics() +{ + system("cls"); + sortPlayers(); + + cout << "RANK NAME SCORE W L D GAMES WIN Rate\n"; + cout << "--------------------------------------------------------------------\n"; + + for (int i = 0; i < playerCount; i++) + { + float winRate = 0.0f; + if (players[i].games > 0) + winRate = (players[i].wins * 100.0f) / players[i].games; + + const char *rowColor = RESET; + + if (i == 0) + rowColor = GOLD_C; + else if (i == 1) + rowColor = SILVER_C; + else if (i == 2) + rowColor = BRONZE_C; + + cout << rowColor; + + printCenteredInt(i + 1, 6); + printCentered(players[i].name, 16); + printCenteredInt(players[i].totalScore, 10); + printCenteredInt(players[i].wins, 6); + printCenteredInt(players[i].losses, 6); + printCenteredInt(players[i].draws, 6); + printCenteredInt(players[i].games, 10); + printCenteredFloat(winRate, 8); + + cout << RESET << '\n'; + } + + cout << "\nPress any key to return..."; + waitForKey(); +} + +// Updates persistent player statistics after a completed game. +// Bot players are excluded from statistics tracking. +void updateStatistics(const Game &game, int blackScore, int whiteScore) +{ + // Player 0 → Black, Player 1 → White + for (int i = 0; i < 2; i++) + { + // Skips bots + if (game.players[i].isBot) + continue; + + int idx = findPlayer(game.players[i].name); + if (idx == -1) + continue; + + players[idx].games++; + + int myScore = (i == 0) ? blackScore : whiteScore; + int oppScore = (i == 0) ? whiteScore : blackScore; + + players[idx].totalScore += myScore; + + if (myScore > oppScore) + players[idx].wins++; + else if (myScore < oppScore) + players[idx].losses++; + else + players[idx].draws++; + } + + saveStatistics(); +} + +// These three are utility functions to print centered text within a given width (used in statistics table). +void printCentered(const char *text, int width) +{ + int len = strlen(text); + if (len >= width) + { + cout << text; + return; + } + + int left = (width - len) / 2; + int right = width - len - left; + + for (int i = 0; i < left; i++) + cout << ' '; + cout << text; + for (int i = 0; i < right; i++) + cout << ' '; +} + +void printCenteredInt(int value, int width) +{ + char buf[20]; + sprintf(buf, "%d", value); + printCentered(buf, width); +} + +void printCenteredFloat(float value, int width) +{ + char buf[20]; + sprintf(buf, "%.1f%%", value); + printCentered(buf, width); +} + +// Program entry point +int main() +{ + seedRandom(); + loadStatistics(); + showMenu(); +} + + diff --git a/main.exe b/main.exe new file mode 100644 index 0000000000000000000000000000000000000000..405b28b845f17799e32953a274ec45338835346f GIT binary patch literal 166191 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 literal 0 HcmV?d00001 diff --git a/saved_game.txt b/saved_game.txt new file mode 100644 index 0000000..e69de29 diff --git a/stats.txt b/stats.txt new file mode 100644 index 0000000..ad68c43 --- /dev/null +++ b/stats.txt @@ -0,0 +1,8 @@ +amir 106 3 4 0 7 +dustin 10 1 0 0 1 +steve 16 0 2 0 2 +liam 6 0 1 0 1 +harry 28 1 1 0 2 +voldi 46 1 0 0 1 +chester 11 0 2 0 2 +mike 11 1 0 0 1