493 lines
13 KiB
C++
493 lines
13 KiB
C++
#include <iostream>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <windows.h>
|
|
#include <conio.h>
|
|
using namespace std;
|
|
|
|
struct Player
|
|
{
|
|
string name;
|
|
char color; // 'B' یا 'W'
|
|
bool isBot;
|
|
};
|
|
const int MAXN = 10;
|
|
int N = 8;
|
|
char board[MAXN][MAXN];
|
|
//صفحه بازی
|
|
int dr[8] =
|
|
{-1,-1,-1,0,0,1,1,1};
|
|
int dc[8] =
|
|
{-1,0,1,-1,1,-1,0,1};
|
|
bool inBounds(int r, int c)
|
|
{
|
|
return r >= 0 && c >= 0 && r < N && c < N;
|
|
}
|
|
void initBoard()
|
|
{
|
|
for (int r = 0; r < N; ++r)
|
|
for (int c = 0; c < N; ++c)
|
|
board[r][c] = ' ';
|
|
int mid = N / 2;
|
|
board[mid - 1][mid - 1] = 'W';
|
|
board[mid][mid] = 'W';
|
|
board[mid - 1][mid] = 'B';
|
|
board[mid][mid - 1] = 'B';
|
|
}
|
|
|
|
bool hasFlipsInDir(int r, int c, char me, int dirIndex, int& endR, int& endC)
|
|
|
|
{
|
|
char opp;
|
|
if (me == 'B')
|
|
{
|
|
opp = 'W';
|
|
} else
|
|
{
|
|
opp = 'B';
|
|
}
|
|
|
|
int nr = r + dr[dirIndex];
|
|
int nc = c + dc[dirIndex];
|
|
|
|
if (!inBounds(nr, nc))
|
|
{
|
|
return false;
|
|
}
|
|
if (board[nr][nc] != opp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while (inBounds(nr, nc))
|
|
{
|
|
if (board[nr][nc] == opp)
|
|
{
|
|
nr = nr + dr[dirIndex];
|
|
nc = nc + dc[dirIndex];
|
|
}
|
|
else if (board[nr][nc] == me)
|
|
{
|
|
endR = nr;
|
|
endC = nc;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isValidMove(int r, int c, char me)
|
|
{
|
|
if (!inBounds(r, c) || board[r][c] != ' ')
|
|
return false;
|
|
|
|
for (int d = 0; d < 8; ++d)
|
|
{
|
|
int endR, endC;
|
|
if (hasFlipsInDir(r, c, me, d, endR, endC)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void applyMove(int r, int c, char me)
|
|
{
|
|
board[r][c] = me;
|
|
for (int d = 0; d < 8; ++d)
|
|
{
|
|
int endR, endC;
|
|
if (hasFlipsInDir(r, c, me, d, endR, endC))
|
|
{
|
|
int nr = r + dr[d];
|
|
int nc = c + dc[d];
|
|
while (!(nr == endR && nc == endC))
|
|
{
|
|
board[nr][nc] = me;
|
|
nr += dr[d];
|
|
nc += dc[d];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void saveGame(const string& playerBlack, const string& playerWhite, char nextTurn)
|
|
{
|
|
ofstream file("save.txt");
|
|
if (!file)
|
|
{
|
|
cout << "Error saving game!\n";
|
|
return;
|
|
}
|
|
file << N << "\n" << playerBlack << "\n" << playerWhite << "\n" << nextTurn << "\n";
|
|
for (int i = 0; i < N; ++i)
|
|
{
|
|
for (int j = 0; j < N; ++j) file << board[i][j];
|
|
file << "\n";
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
void saveToHistory(const string& pBlack, const string& pWhite, int blackScore, int whiteScore, const string& winner)
|
|
{
|
|
ofstream file("history.txt", ios::app);
|
|
if (!file)
|
|
{
|
|
cout << "Error saving to history!\n";
|
|
return;
|
|
}
|
|
|
|
time_t now = time(0);
|
|
char* dt = ctime(&now);
|
|
dt[strlen(dt)-1] = '\0';
|
|
|
|
file << "Date: " << dt << "\n";
|
|
file << "Players: " << pBlack << " (Black) vs " << pWhite << " (White)\n";
|
|
file << "Score: " << blackScore << " - " << whiteScore << "\n";
|
|
file << "Winner: " << winner << "\n";
|
|
file << "-----------------------------------\n";
|
|
|
|
file.close();
|
|
}
|
|
|
|
void showGameHistory()
|
|
{
|
|
ifstream file("history.txt");
|
|
if (!file) {
|
|
cout << "No game history yet.\n";
|
|
return;
|
|
}
|
|
|
|
cout << "\n=== GAME HISTORY ===\n\n";
|
|
string line;
|
|
while (getline(file, line)) {
|
|
cout << line << "\n";
|
|
}
|
|
cout << "\n";
|
|
file.close();
|
|
}
|
|
|
|
bool hasAnyMove(char me)
|
|
{
|
|
for (int r = 0; r < N; ++r)
|
|
for (int c = 0; c < N; ++c)
|
|
if (isValidMove(r, c, me)) return true;
|
|
return false;
|
|
}
|
|
|
|
int countPieces(char piece)
|
|
{
|
|
int cnt = 0;
|
|
for (int r = 0; r < N; ++r)
|
|
for (int c = 0; c < N; ++c)
|
|
if (board[r][c] == piece) cnt++;
|
|
return cnt;
|
|
}
|
|
|
|
void setColor(int color)
|
|
{
|
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
|
|
}
|
|
|
|
|
|
void printBoardWithHints(char turn, int cursorR = -1, int cursorC = -1)
|
|
{
|
|
int blackCount = countPieces('B');
|
|
int whiteCount = countPieces('W');
|
|
|
|
setColor(14); // رنگ زرد روشن برای امتیاز
|
|
cout << "\nScore -> Black: " << blackCount << " | White: " << whiteCount << "\n";
|
|
setColor(10);
|
|
cout << "\n ";
|
|
for (int c = 0; c < N; ++c)
|
|
cout << c + 1 << " ";
|
|
cout << "\n";
|
|
|
|
cout << " ┌";
|
|
for (int c = 0; c < N; ++c)
|
|
cout << "───" << (c < N - 1 ? "┬" : "");
|
|
cout << "┐\n";
|
|
|
|
for (int r = 0; r < N; ++r)
|
|
{
|
|
cout << r + 1 << " │";
|
|
for (int c = 0; c < N; ++c)
|
|
{
|
|
if (r == cursorR && c == cursorC) setColor(12); // کرسر قرمز
|
|
else if (board[r][c] == ' ' && isValidMove(r, c, turn)) setColor(6);
|
|
else if (board[r][c] == 'B') setColor(8);
|
|
else if (board[r][c] == 'W') setColor(15);
|
|
else setColor(10);
|
|
|
|
if (r == cursorR && c == cursorC && board[r][c] == ' ') cout << "[ ]";
|
|
else if (board[r][c] == 'B') cout << " ● ";
|
|
else if (board[r][c] == 'W') cout << " ○ ";
|
|
else if (board[r][c] == ' ' && isValidMove(r, c, turn)) cout << " * ";
|
|
else cout << " ";
|
|
|
|
setColor(10);
|
|
cout << "│";
|
|
}
|
|
cout << "\n";
|
|
if (r < N - 1)
|
|
{
|
|
cout << " ├";
|
|
for (int c = 0; c < N; ++c)
|
|
cout << "───" << (c < N - 1 ? "┼" : "");
|
|
cout << "┤\n";
|
|
}
|
|
|
|
}
|
|
cout << " └";
|
|
for (int c = 0; c < N; ++c)
|
|
cout << "───" << (c < N - 1 ? "┴" : "");
|
|
cout << "┘\n";
|
|
setColor(7);
|
|
}
|
|
|
|
|
|
void helpScreen()
|
|
{
|
|
setColor(6);
|
|
cout << "\n--- Help ---\n";
|
|
setColor(13);
|
|
cout << "Othello (Reversi) rules summary:\n";
|
|
cout << "- Board size can be 6x6, 8x8, or 10x10. Initial center has 2 Black and 2 White discs in a cross.\n";
|
|
cout << "- A valid move places a disc on an empty cell that brackets opponent discs\n";
|
|
cout << " in at least one direction (horizontal, vertical, or diagonal) between\n";
|
|
cout << " the new disc and an existing disc of the mover.\n";
|
|
cout << "- All bracketed opponent discs in those directions flip to the mover's color.\n";
|
|
cout << "- If a player has no valid moves, they pass. If both cannot move, game ends.\n";
|
|
cout << "- Winner is the player with more discs. Symbols: B=Black, W=White, .=empty, *=hint , ●=Black , ○=White\n";
|
|
cout << "- During the game, enter 0 0 to save and exit.\n";
|
|
setColor(6);
|
|
cout << "------------\n\n";
|
|
setColor(7);
|
|
}
|
|
|
|
void botMove(char me)
|
|
{
|
|
for (int r = 0; r < N; ++r)
|
|
{
|
|
for (int c = 0; c < N; ++c)
|
|
{
|
|
if (isValidMove(r, c, me))
|
|
{
|
|
applyMove(r, c, me);
|
|
cout << "Bot placed at: row " << (r + 1) << ", col " << (c + 1) << "\n";
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool loadGame(string& playerBlack, string& playerWhite, char& turn)
|
|
{
|
|
ifstream file("save.txt");
|
|
if (!file) {
|
|
cout << "No saved game found.\n";
|
|
return false;
|
|
}
|
|
|
|
int size;
|
|
file >> size;
|
|
N = size;
|
|
file >> ws;
|
|
getline(file, playerBlack);
|
|
getline(file, playerWhite);
|
|
file >> turn;
|
|
file.ignore();
|
|
for (int r = 0; r < N; ++r)
|
|
for (int c = 0; c < N; ++c)
|
|
board[r][c] = ' ';
|
|
|
|
|
|
for (int r = 0; r < N; ++r)
|
|
{
|
|
string line;
|
|
getline(file, line);
|
|
for (int c = 0; c < N && c < (int)line.size(); ++c)
|
|
{
|
|
board[r][c] = line[c];
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
cout << "Game loaded successfully!\n";
|
|
return true;
|
|
}
|
|
|
|
void playGame(bool singlePlayer, bool loadExisting = false)
|
|
{
|
|
string playerBlack, playerWhite;
|
|
char turn = 'B';
|
|
int cursorR = 0, cursorC = 0; // موقعیت کرسر
|
|
if (loadExisting)
|
|
{
|
|
if (!loadGame(playerBlack, playerWhite, turn))
|
|
{
|
|
cout << "Starting new game instead.\n";
|
|
loadExisting = false;
|
|
}
|
|
}
|
|
|
|
if (!loadExisting)
|
|
{
|
|
cout << "Choose board size (6 / 8 / 10): ";
|
|
cin >> N;
|
|
while (N != 6 && N != 8 && N != 10)
|
|
{
|
|
cout << "Invalid size! Choose 6, 8 or 10: ";
|
|
cin >> N;
|
|
}
|
|
|
|
cout << "Enter Black player name: ";
|
|
getline(cin >> ws, playerBlack);
|
|
|
|
if (singlePlayer)
|
|
playerWhite = "Bot";
|
|
else
|
|
{
|
|
cout << "Enter White player name: ";
|
|
getline(cin >> ws, playerWhite);
|
|
}
|
|
|
|
initBoard();
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
system("cls");
|
|
printBoardWithHints(turn, cursorR, cursorC);
|
|
cout << "Turn: " << (turn == 'B' ? playerBlack : playerWhite)
|
|
<< " (" << turn << ")"
|
|
<< " | Score: " << countPieces('B') << " - " << countPieces('W') << "\n";
|
|
|
|
if (!hasAnyMove(turn))
|
|
{
|
|
cout << "No valid moves for " << (turn == 'B' ? "Black" : "White") << ". PASS.\n";
|
|
char nextTurn = (turn == 'B') ? 'W' : 'B';
|
|
if (!hasAnyMove(nextTurn))
|
|
{
|
|
cout << "No valid moves for both players. Game over.\n";
|
|
break;
|
|
}
|
|
turn = nextTurn;
|
|
_getch();
|
|
continue;
|
|
}
|
|
|
|
if (singlePlayer && turn == 'W')
|
|
{
|
|
botMove(turn);
|
|
turn = 'B';
|
|
_getch();
|
|
continue;
|
|
}
|
|
|
|
cout << "Use W/A/S/D to move, Enter to place, 0 to save.\n";
|
|
|
|
char key = _getch();
|
|
if (key == 'w' || key == 'W') cursorR = max(0, cursorR - 1);
|
|
else if (key == 's' || key == 'S') cursorR = min(N - 1, cursorR + 1);
|
|
else if (key == 'a' || key == 'A') cursorC = max(0, cursorC - 1);
|
|
else if (key == 'd' || key == 'D') cursorC = min(N - 1, cursorC + 1);
|
|
else if (key == '\r') // Enter
|
|
{
|
|
if (isValidMove(cursorR, cursorC, turn))
|
|
{
|
|
applyMove(cursorR, cursorC, turn);
|
|
turn = (turn == 'B') ? 'W' : 'B';
|
|
}
|
|
else
|
|
{
|
|
cout << "Invalid move!\n";
|
|
_getch();
|
|
}
|
|
}
|
|
else if (key == '0')
|
|
{
|
|
saveGame(playerBlack, playerWhite, turn);
|
|
cout << "Game saved! Returning to main menu.\n";
|
|
_getch();
|
|
return;
|
|
}
|
|
}
|
|
|
|
system("cls");
|
|
printBoardWithHints('B');
|
|
int black = countPieces('B');
|
|
int white = countPieces('W');
|
|
cout << "Final count -> Black: " << black << " | White: " << white << "\n";
|
|
|
|
string winner;
|
|
if (black > white) winner = playerBlack + " (Black)";
|
|
else if (white > black) winner = playerWhite + " (White)";
|
|
else winner = "Draw";
|
|
|
|
cout << "Winner: " << winner << "\n";
|
|
saveToHistory(playerBlack, playerWhite, black, white, winner);
|
|
_getch();
|
|
}
|
|
|
|
|
|
int main()
|
|
{
|
|
SetConsoleOutputCP(CP_UTF8);
|
|
SetConsoleCP(CP_UTF8);
|
|
int choice;
|
|
do
|
|
{
|
|
setColor(2);
|
|
cout << "\n-------------------";
|
|
setColor(14);
|
|
cout << "\n--- Main Menu ---\n";
|
|
setColor(11);
|
|
cout << " 1. Two-player game\n";
|
|
cout << " 2. Single-player (vs bot)\n";
|
|
cout << " 3. Help\n";
|
|
cout << " 4. Game History\n";
|
|
cout << " 5. Load Game\n";
|
|
cout << " 0. Exit\n";
|
|
setColor(15);
|
|
cout << "Select: ";
|
|
setColor(2);
|
|
|
|
cout << "\n-------------------\n";
|
|
setColor(7);
|
|
cin >> choice;
|
|
|
|
if (choice == 1)
|
|
{
|
|
playGame(false);
|
|
} else if (choice == 2)
|
|
{
|
|
playGame(true);
|
|
} else if (choice == 3)
|
|
{
|
|
helpScreen();
|
|
|
|
}else if (choice == 4)
|
|
{
|
|
showGameHistory();
|
|
|
|
}else if (choice == 5)
|
|
{
|
|
playGame(false,true);
|
|
}
|
|
else if (choice == 0)
|
|
{
|
|
cout << "Goodbye!\n";
|
|
} else {
|
|
cout << "Invalid option.\n";
|
|
}
|
|
} while (choice != 0);
|
|
|
|
return 0;
|
|
} |