673 lines
22 KiB
C++
673 lines
22 KiB
C++
#include <iostream>
|
|
#include <string>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <conio.h>
|
|
#include <fstream>
|
|
using namespace std;
|
|
|
|
// کامنت ها برای اینه که تو صورت سوال گفته شده کد باید کامنت گذاری شده باشه
|
|
struct game
|
|
{
|
|
string name1;
|
|
string name2;
|
|
int countB;
|
|
int countW;
|
|
string result;
|
|
string date;
|
|
string time;
|
|
};
|
|
string board[8][8];
|
|
int moves[64][2];
|
|
int countPass = 0;
|
|
int countW;
|
|
int countB;
|
|
//تابعی که بررسی میکنه یک جهت دارای حداقل یک مهره حریف و بعد از اون حداقل یک خودیه یا نه
|
|
bool checkDirection(string board[8][8], int row, int col, string color,int drow,int dcol)
|
|
{
|
|
string opponent;
|
|
if (color == "●")
|
|
{
|
|
opponent = "○";
|
|
}
|
|
else
|
|
{
|
|
opponent = "●";
|
|
}
|
|
int currentRow = row + drow;
|
|
int currentCol = col + dcol;
|
|
if (currentRow < 0 || currentRow >= 8 || currentCol < 0 || currentCol >= 8)
|
|
{
|
|
return false;
|
|
}
|
|
//چک اینکه مهره کناری حتما حریف باشه
|
|
if (board[currentRow][currentCol] != opponent)
|
|
{
|
|
return false;
|
|
}
|
|
while (currentRow >= 0 && currentRow < 8 && currentCol >= 0 && currentCol < 8)
|
|
{
|
|
if (board[currentRow][currentCol] == opponent)
|
|
{
|
|
currentRow += drow;
|
|
currentCol += dcol;
|
|
}
|
|
else if (board[currentRow][currentCol] == color)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
//بررسی میکنه حداقل یه جهت با شرایطی که تو کامنت بالایی گفتم وجود داره یا نه
|
|
bool existsValidDirection(string board[8][8], int row, int col, string color)
|
|
{
|
|
int d[] = {-1, 0, 1};
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
if (i == 1 && j == 1)
|
|
{
|
|
continue;
|
|
}
|
|
if (checkDirection(board, row, col, color, d[i], d[j]) == true)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
//بررسی میکنه شرایط برای گذاشتن مهره توی اون خونه انتخاب شده هست یا نه
|
|
bool isValidMove(string board[8][8], int row, int col, string color)
|
|
{
|
|
if (board[row][col] == " " && existsValidDirection(board, row, col, color) == true)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//تمام خونه هایی که میشه توش با توجه به شرایط مهره گذاشت رو پیدا و شمارش میکنه
|
|
void findValidMoves(string board[8][8], string color, int moves[][2], int& count)
|
|
{
|
|
//int& برای اینه که وقتی تابع رو صدا زدیم و کارش رو انجام داد مقدار count بیرون از تابع تغییر نکنه
|
|
//راحتتر این بود که int عادی بذاریم و بعد count رو برگردونیم ولی خب.
|
|
count = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
if (isValidMove(board, i, j, color) == true )
|
|
{
|
|
moves[count][0] = i;
|
|
moves[count][1] = j;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//به طور رندوم یکی از خونه های معتبر رو انتخاب میکنه
|
|
int chooseMove(int count)
|
|
{
|
|
int randomNum = rand() % count;
|
|
return randomNum;
|
|
}
|
|
//توی خونه انتخاب شده مهره میذاره و مهره های محاصره شده حریف رو تبدیل میکنه به مهره های خودی
|
|
void applyMove(string board[8][8], int row, int col, string color)
|
|
{
|
|
int d[] = {-1, 0, 1};
|
|
string opponent = (color == "●") ? "○" : "●";
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
if (i == 1 && j == 1)
|
|
{
|
|
continue;
|
|
}
|
|
if (checkDirection(board, row, col, color, d[i], d[j]))
|
|
{
|
|
int currentRow = row + d[i];
|
|
int currentCol = col + d[j];
|
|
|
|
while (board[currentRow][currentCol] == opponent)
|
|
{
|
|
board[currentRow][currentCol] = color;
|
|
currentRow += d[i];
|
|
currentCol += d[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
board[row][col] = color;
|
|
}
|
|
//صفحه بازی رو توی یه جدول چاپ میکنه
|
|
void printBoard(string board[8][8], int cursorRow, int cursorCol)
|
|
{
|
|
|
|
cout << "┌";
|
|
for (int col = 0; col < 8; col++)
|
|
{
|
|
cout << "─";
|
|
if (col != 7)
|
|
{
|
|
cout << "┬";
|
|
}
|
|
else
|
|
{
|
|
cout << "┐";
|
|
}
|
|
}
|
|
cout << endl;
|
|
for (int row = 0; row < 8; row++)
|
|
{
|
|
cout << "│";
|
|
for (int col = 0; col < 8; col++)
|
|
{
|
|
//به خاطر شرط پایین وقتی به کرسرها -1 بدیم صفحه بدون کرسر چاپ میشه
|
|
if (row == cursorRow && col == cursorCol)
|
|
{
|
|
if (board[row][col] == " ")
|
|
{
|
|
cout << "*";
|
|
}
|
|
else
|
|
{
|
|
cout << board[row][col];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cout << board[row][col];
|
|
}
|
|
cout << "│";
|
|
}
|
|
cout << endl;
|
|
if (row != 7)
|
|
{
|
|
cout << "├";
|
|
for (int col = 0; col < 8; col++)
|
|
{
|
|
cout << "─";
|
|
if (col != 7)
|
|
{
|
|
cout << "┼";
|
|
}
|
|
else
|
|
{
|
|
cout << "┤";
|
|
}
|
|
}
|
|
cout << endl;
|
|
}
|
|
}
|
|
cout << "└";
|
|
for (int col = 0; col < 8; col++)
|
|
{
|
|
cout << "─";
|
|
if (col != 7)
|
|
{
|
|
cout << "┴";
|
|
}
|
|
else
|
|
{
|
|
cout << "┘";
|
|
}
|
|
}
|
|
cout << endl;
|
|
}
|
|
//با استفاده از تابع های تعریف و پیاده سازی شده، تمام کارایی که ربات باید توی نوبتش انجام بده رو انجام میده
|
|
bool robotTurn(string board[8][8], string color)
|
|
{
|
|
int count = 0;
|
|
findValidMoves(board, color, moves, count);
|
|
system("cls");
|
|
if (count == 0)
|
|
{
|
|
//برای اینکه صفحه بدون کرسر چاپ بشه
|
|
printBoard(board, -1, -1);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int chosenMove = chooseMove(count);
|
|
int row = moves[chosenMove][0];
|
|
int col = moves[chosenMove][1];
|
|
applyMove(board, row, col, color);
|
|
printBoard(board, -1, -1);
|
|
return true;
|
|
}
|
|
}
|
|
//کلیدهایی که کاربر برای حرکت روی صفحه بازی میزنه رو شناسایی میکنه و کرسر رو نشون میده.
|
|
//با استفاده از تابع های تعریف و پیاده سازی شده، وقتی بازیکن خونه موردنظرش رو انتخاب کرد
|
|
//اگر خونه دارای شرایط لازم وجود نداشت پیام پاس میده یا اگر خونه نامعتبر بود اخطار میده
|
|
//اگر معتبر بود مهره های محاصره شده حریف رو تبدیل میکنه به مهره های خودی و مهره رو توی خونه انتخاب شده میذاره
|
|
bool humanTurn(string board[8][8],string name, string turn)
|
|
{
|
|
int count = 0;
|
|
bool isSelected = false;
|
|
int row;
|
|
int col;
|
|
int cursorRow = 0;
|
|
int cursorCol = 0;
|
|
findValidMoves(board, turn, moves, count);
|
|
if (count == 0)
|
|
{
|
|
system("cls");
|
|
cout << "Pass" << endl << "Press any key to continue." << endl;
|
|
getch();
|
|
return false;
|
|
}
|
|
while (!isSelected)
|
|
{
|
|
system("cls");
|
|
printBoard(board, cursorRow, cursorCol);
|
|
cout << "Turn: " << turn << endl;
|
|
char key = getch();
|
|
if (key == 'w')
|
|
{
|
|
cursorRow--;
|
|
if (cursorRow < 0) cursorRow = 0;
|
|
}
|
|
else if (key == 'a')
|
|
{
|
|
cursorCol--;
|
|
if (cursorCol < 0) cursorCol = 0;
|
|
}
|
|
else if (key == 's')
|
|
{
|
|
cursorRow++;
|
|
if (cursorRow > 7) cursorRow = 7;
|
|
}
|
|
else if (key == 'd')
|
|
{
|
|
cursorCol++;
|
|
if (cursorCol > 7) cursorCol = 7;
|
|
}
|
|
if (key == '\r')
|
|
{
|
|
if (isValidMove(board, cursorRow, cursorCol, turn))
|
|
{
|
|
row = cursorRow;
|
|
col = cursorCol;
|
|
isSelected = true;
|
|
}
|
|
else
|
|
{
|
|
cout << "Invalid move!" << endl << "Press any key to choose again." << endl;
|
|
getch();
|
|
}
|
|
}
|
|
}
|
|
applyMove(board, row, col, turn);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
{
|
|
srand(static_cast<unsigned int>(time(nullptr)));
|
|
bool running = true;
|
|
string color = "●";
|
|
int choice = 0;
|
|
int capacity = 10;
|
|
int size;
|
|
game* history;
|
|
history = new game[capacity];
|
|
ifstream file("history.txt");
|
|
size = 0;
|
|
game temp;
|
|
//تا وقتی به ته فایل نرسیده
|
|
while (!file.eof())
|
|
{
|
|
file >> temp.name1 >> temp.name2 >> temp.countB >> temp.countW >> temp.result >> temp.date >> temp.time;
|
|
if (file.eof()) break;
|
|
if (size == capacity)
|
|
{
|
|
int newCapacity = capacity * 2;
|
|
game* newHistory = new game[newCapacity];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
newHistory[i] = history[i];
|
|
}
|
|
delete[] history;
|
|
history = newHistory;
|
|
capacity = newCapacity;
|
|
}
|
|
history[size] = temp; // بازی رو که خوند میریزه تو آرایه
|
|
size++;
|
|
}
|
|
file.close();
|
|
while (running)
|
|
{
|
|
cout << "1) New Game" << endl;
|
|
cout << "2) Help" << endl;
|
|
cout << "3) Game History" << endl;
|
|
cout << "4) Exit" << endl;
|
|
cin >> choice;
|
|
//بازی جدید
|
|
if (choice == 1)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
board[i][j]= " ";
|
|
}
|
|
}
|
|
board[3][3] = "○";
|
|
board[4][4] = "○";
|
|
board[3][4] = "●";
|
|
board[4][3] = "●";
|
|
int players;
|
|
cout << "1) Single Player" << endl;
|
|
cout << "2) Two Player" << endl;
|
|
cin >> players;
|
|
cin.ignore();
|
|
if (players == 1)
|
|
{
|
|
//بازی یک نفره. حریف بازیکن ربات است
|
|
string name;
|
|
cout << "Enter your name:";
|
|
cin >> name;
|
|
cout << "The game starts with " << name << " (●)." << endl;
|
|
cout << "Use keys w/a/s/d to move and Enter to apply." << endl;
|
|
cout << "Press any key to start." << endl;
|
|
getch();
|
|
string playerColor = "●";
|
|
string robotColor = "○";
|
|
string turn = playerColor;
|
|
countPass = 0;
|
|
int cursorRow = 0;
|
|
int cursorCol = 0;
|
|
|
|
while (countPass < 2)
|
|
{
|
|
//نوبت بازیکن
|
|
if (turn == "●")
|
|
{
|
|
|
|
|
|
if (humanTurn(board, name, playerColor) == true)
|
|
{
|
|
countPass = 0;
|
|
}
|
|
else
|
|
{
|
|
countPass++;
|
|
}
|
|
turn = robotColor;
|
|
}
|
|
//نوبت ربات
|
|
else if (turn == "○")
|
|
{
|
|
if (robotTurn(board, robotColor) == true)
|
|
{
|
|
countPass = 0;
|
|
}
|
|
else
|
|
{
|
|
countPass++;
|
|
}
|
|
turn = playerColor;
|
|
}
|
|
}
|
|
//شمارش مهره ها
|
|
countW = 0;
|
|
countB = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
if (board[i][j] == "○")
|
|
{
|
|
countW++;
|
|
}
|
|
else if (board[i][j] == "●")
|
|
{
|
|
countB++;
|
|
}
|
|
}
|
|
}
|
|
system("cls");
|
|
cout << "Game over." << endl;
|
|
cout << "Black: " << countB << endl;
|
|
cout << "White: " << countW << endl;
|
|
if (countW > countB)
|
|
{
|
|
cout << "White won!" << endl;
|
|
}
|
|
else if (countB > countW)
|
|
{
|
|
cout << "Black won!" << endl;
|
|
}
|
|
if (countW == countB)
|
|
{
|
|
cout << "The game is drawn" << endl;
|
|
}
|
|
//فرایند ذخیره تاریخچه
|
|
game currentGame;
|
|
currentGame.name1 = name;
|
|
currentGame.name2 = "computer";
|
|
currentGame.countB = countB;
|
|
currentGame.countW = countW;
|
|
if (countB > countW)
|
|
currentGame.result = "Black";
|
|
else if (countW > countB)
|
|
currentGame.result = "White";
|
|
else
|
|
currentGame.result = "Draw";
|
|
//کمک از ai:
|
|
time_t now = time(nullptr);
|
|
tm* localtm = localtime(&now);
|
|
string currentDate = to_string(localtm->tm_mday) + "/"
|
|
+ to_string(localtm->tm_mon + 1) + "/"
|
|
+ to_string(localtm->tm_year + 1900);
|
|
string currentTime = to_string(localtm->tm_hour) + ":"
|
|
+ to_string(localtm->tm_min) + ":"
|
|
+ to_string(localtm->tm_sec);
|
|
currentGame.date = currentDate;
|
|
currentGame.time = currentTime;
|
|
if (size == capacity)
|
|
{
|
|
int newCapacity = capacity * 2;
|
|
game* newHistory = new game[newCapacity];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
newHistory[i] = history[i];
|
|
}
|
|
delete[] history;
|
|
history = newHistory;
|
|
capacity = newCapacity;
|
|
}
|
|
history[size] = currentGame;
|
|
size++;
|
|
}
|
|
else if (players == 2)
|
|
{
|
|
//بازی دونفره
|
|
string name1, name2;
|
|
cout << "Enter players' names:";
|
|
cin.ignore();
|
|
getline(cin, name1);
|
|
getline(cin, name2);
|
|
cout << "The game starts with Black (●)." << endl;
|
|
cout << "Use keys w/a/s/d to move and Enter to apply." << endl;
|
|
cout << "Press any key to start.";
|
|
getch();
|
|
string playerColorb = "●";
|
|
string playerColorw = "○";
|
|
string turn = playerColorb;
|
|
int countb = 0;
|
|
int countw = 0;
|
|
int cursorRow = 0;
|
|
int cursorCol = 0;
|
|
countPass = 0;
|
|
bool gameOver = false;
|
|
while (countPass < 2)
|
|
{
|
|
|
|
if (turn == "●")
|
|
{
|
|
if (humanTurn(board, name1, playerColorb) == true)
|
|
{
|
|
countPass = 0;
|
|
}
|
|
else
|
|
{
|
|
countPass++;
|
|
}
|
|
turn = playerColorw;
|
|
}
|
|
|
|
else if (turn == "○")
|
|
{
|
|
if (humanTurn(board, name2, playerColorw) == true)
|
|
{
|
|
countPass = 0;
|
|
}
|
|
else
|
|
{
|
|
countPass++;
|
|
}
|
|
turn = playerColorb;
|
|
}
|
|
}
|
|
countW = 0;
|
|
countB = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
if (board[i][j] == "○")
|
|
{
|
|
countW++;
|
|
}
|
|
else if (board[i][j] == "●")
|
|
{
|
|
countB++;
|
|
}
|
|
}
|
|
}
|
|
gameOver = true;
|
|
system("cls");
|
|
cout << "Game over." << endl;
|
|
cout << "Black: " << countB << endl;
|
|
cout << "White: " << countW << endl;
|
|
if (countW > countB)
|
|
{
|
|
cout << "White won!" << endl;
|
|
}
|
|
else if (countB > countW)
|
|
{
|
|
cout << "Black won!" << endl;
|
|
}
|
|
if (countW == countB)
|
|
{
|
|
cout << "The game is drawn" << endl;
|
|
}
|
|
game currentGame;
|
|
currentGame.name1 = name1;
|
|
currentGame.name2 = name2;
|
|
currentGame.countB = countB;
|
|
currentGame.countW = countW;
|
|
if (countB > countW)
|
|
currentGame.result = "Black";
|
|
else if (countW > countB)
|
|
currentGame.result = "White";
|
|
else
|
|
currentGame.result = "Draw";
|
|
//کمک از ai:
|
|
time_t now = time(nullptr);
|
|
tm* localtm = localtime(&now);
|
|
string currentDate = to_string(localtm->tm_mday) + "/"
|
|
+ to_string(localtm->tm_mon + 1) + "/"
|
|
+ to_string(localtm->tm_year + 1900);
|
|
string currentTime = to_string(localtm->tm_hour) + ":"
|
|
+ to_string(localtm->tm_min) + ":"
|
|
+ to_string(localtm->tm_sec);
|
|
currentGame.date = currentDate;
|
|
currentGame.time = currentTime;
|
|
if (size == capacity)
|
|
{
|
|
int newCapacity = capacity * 2;
|
|
game* newHistory = new game[newCapacity];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
newHistory[i] = history[i];
|
|
}
|
|
delete[] history;
|
|
history = newHistory;
|
|
capacity = newCapacity;
|
|
}
|
|
history[size] = currentGame;
|
|
size++;
|
|
}
|
|
}
|
|
//راهنما
|
|
else if (choice == 2)
|
|
{
|
|
cout << "keys: " << endl;
|
|
cout << "w: up" << endl << "a: left" << endl << "s: down" << endl << "d: right" << endl << "Enter: to place" << endl;
|
|
cout << "In single-player mode, after you move, the robot plays immediately, then it's your turn again. Don't get confused!" << endl;
|
|
cout << "Othello is a two-player board game played on an 8*8 grid. " <<
|
|
"Players take turns placing discs on the board, with one side black and the other white." <<
|
|
" A move is valid if it traps one or more of the opponent's discs between the new disc and another disc of the same color, " <<
|
|
" causing the trapped discs to flip." <<
|
|
" The game ends when no valid moves remain, " <<
|
|
"and the player with the most discs on the board wins." << endl;
|
|
}
|
|
//نمایش تاریخچه
|
|
else if (choice == 3)
|
|
{
|
|
if (size == 0)
|
|
{
|
|
cout << "No game history found." << endl;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
cout << i + 1 << "." << history[i].name1 << " vs " << history[i].name2
|
|
<< " / score: " << history[i].countB << "-" << history[i].countW
|
|
<< " / winner: " << history[i].result << " / date: " << history[i].date << " / time: " << history[i].time<< endl;
|
|
}
|
|
}
|
|
}
|
|
//خروج از بازی
|
|
else if (choice == 4)
|
|
{
|
|
running = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
cout << "invalid choice." << endl;
|
|
}
|
|
}
|
|
//نوشتن تاریخچه در فایل
|
|
ofstream outfile("history.txt");
|
|
if (!outfile.is_open())
|
|
{
|
|
cout << "Cannot open history.txt for writing!" << endl;
|
|
}
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
outfile << history[i].name1 << " " << history[i].name2 << " "
|
|
<< history[i].countB << " " << history[i].countW << " "
|
|
<< history[i].result << " " << history[i].date << " "
|
|
<< history[i].time << endl;
|
|
}
|
|
outfile.close();
|
|
delete[] history;
|
|
return 0;
|
|
}
|
|
|