Files
2026-04-15 12:38:50 +00:00

463 lines
13 KiB
C++

#include <iostream>
#include <vector>
#include <cstdlib>
#include <chrono>
#include <thread>
#include <fstream>
#include <string>
#include <ctime>
#include <algorithm>
#include <limits>
#ifdef _WIN32
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#ifndef _WIN32
// پیاده‌سازی توابع _kbhit و _getch برای سیستم‌های غیر ویندوز
int _kbhit() {
struct termios oldt, newt;
int ch;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if (ch != EOF) {
ungetc(ch, stdin);
return 1;
}
return 0;
}
int _getch() {
struct termios oldt, newt;
int ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
#endif
using namespace std;
// constant ثابت های بازی
const int BOARD_WIDTH = 40;
const int BOARD_HEIGHT = 20;
const int INITIAL_LIVES = 3;
const int BRICK_ROWS = 4;
const int BRICK_COLUMNS = 10;
// struct ساختار های بازی
struct Paddle {
int x;
int width;
};
struct Ball {
int x, y;
int vx, vy;
};
struct Brick {
int x, y;
bool active;
};
struct GameData {
// ذخیره تاریخچه بازی
string playerName;
int score;
int lives;
bool won;
string datetime;
};
struct GameState {
// وضعیت بازی در حال حاضر
Paddle paddle;
Ball ball;
vector<Brick> bricks;
int score;
int lives;
bool ballLaunched;
};
// پاک کردن صفحه بازی
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
// ذخیره و خواندن تاریخچه game history
void saveMatch(const GameData& data) {
ofstream fout("game_history.txt", ios::app); //ios::app برای اینکه تمام تاریخچه بازی‌ها ذخیره بشه
if (!fout) return;
fout << data.playerName << ","
<< data.score << ","
<< data.lives << ","
<< data.won << ","
<< data.datetime << "\n";
fout.close();
}
vector<GameData> getHistory() {
vector<GameData> history;
ifstream fin("game_history.txt");
if (!fin) return history;
string line;
while (getline(fin, line)) {
GameData gd;
size_t pos = 0;
vector<string> tokens;
string temp = line;
while ((pos = temp.find(',')) != string::npos) {
tokens.push_back(temp.substr(0, pos));
temp.erase(0, pos + 1);
}
tokens.push_back(temp);
if (tokens.size() == 5) {
gd.playerName = tokens[0];
gd.score = atoi(tokens[1].c_str());
gd.lives = atoi(tokens[2].c_str());
gd.won = (tokens[3] == "1");
gd.datetime = tokens[4];
history.push_back(gd);
}
}
fin.close();
return history;
}
// رسم صفحه ی بازی rendering
void printBoard(const GameState& game) {
clearScreen();
// سقف
for (int i = 0; i < BOARD_WIDTH + 2; ++i) cout << '#';
cout << '\n';
// درون صفحه
for (int y = 0; y < BOARD_HEIGHT; ++y) {
cout << '#';
for (int x = 0; x < BOARD_WIDTH; ++x) {
bool printed = false;
// توپ
if (x == game.ball.x && y == game.ball.y) {
cout << 'O';
printed = true;
}
// راکت
else if (y == BOARD_HEIGHT - 1 &&
x >= game.paddle.x &&
x < game.paddle.x + game.paddle.width) {
cout << '=';
printed = true;
}
// آجر ها
else {
for (size_t i = 0; i < game.bricks.size(); ++i) {
if (game.bricks[i].active &&
game.bricks[i].x == x &&
game.bricks[i].y == y) {
cout << '*';
printed = true;
break;
}
}
}
if (!printed) cout << ' ';
}
cout << "#\n";
}
// کف
for (int i = 0; i < BOARD_WIDTH + 2; ++i) cout << '#';
cout << '\n';
// HUD نمایش اطلاعات بازی
int bricksLeft = 0;
for (size_t i = 0; i < game.bricks.size(); ++i)
if (game.bricks[i].active) bricksLeft++;
int bricksDestroyed = game.score;
cout << "Lives: " << game.lives
<< " | Score: " << game.score
<< " | Bricks Left: " << bricksLeft
<< " | Bricks Destroyed: " << bricksDestroyed << "\n";
}
// گرفتن ورودی از کاربر برای حرکت و کنترل
void checkInput(GameState& game) {
if (_kbhit()) {
char key = (char)_getch();
// A و D کلید برای حرکت
if ((key == 'a' || key == 'A') && game.paddle.x > 0) game.paddle.x--;
else if ((key == 'd' || key == 'D') && game.paddle.x + game.paddle.width < BOARD_WIDTH) game.paddle.x++;
// متوقف شدن
else if (key == 'p' || key == 'P') {
cout << "\nGame Paused. Press C to continue or Q to quit.\n";
while (true) {
char c = (char)_getch();
if (c == 'c' || c == 'C') break;
if (c == 'q' || c == 'Q') { game.lives = 0; break; }
}
}
// پرتاب توپ با زدن کلید space
else if (!game.ballLaunched && key == ' ') {
game.ballLaunched = true;
}
// خروج
else if (key == 'q' || key == 'Q') {
game.lives = 0;
}
}
}
// آپدیت موقعیت و برخورد با دیواره و کم کردن جان بازیکن
void moveTheBall(GameState& game) {
if (!game.ballLaunched) return;
game.ball.x += game.ball.vx;
game.ball.y += game.ball.vy;
// برخورد با چپ و راست
if (game.ball.x <= 0 || game.ball.x >= BOARD_WIDTH - 1) game.ball.vx *= -1;
// برخورد با سقف
if (game.ball.y <= 0) game.ball.vy *= -1;
// رد شدن و کم شدن جان و ریست جای توپ
if (game.ball.y >= BOARD_HEIGHT) {
game.lives--;
game.ballLaunched = false;
game.ball.x = game.paddle.x + game.paddle.width / 2;
game.ball.y = BOARD_HEIGHT - 2;
}
}
// برخورد توپ با راکت با زاویه بر اساس محل برخورد
void checkPaddleHit(GameState& game) {
if (game.ball.y == BOARD_HEIGHT - 2 &&
game.ball.x >= game.paddle.x &&
game.ball.x < game.paddle.x + game.paddle.width) {
// برگشت عمودی
game.ball.vy = -game.ball.vy;
// براساس این که کجا برخورد کرده vx تغییر میکند
double rel = (game.ball.x - (game.paddle.x + game.paddle.width / 2.0))
/ (game.paddle.width / 2.0);
game.ball.vx = (int)(rel * 2.0);
if (game.ball.vx == 0) game.ball.vx = (rel < 0 ? -1 : 1);
}
}
// برخورد با آجر و حذف آجر و امتیاز گرفتن و برگشت عمودی
void checkBrickHit(GameState& game) {
for (size_t i = 0; i < game.bricks.size(); ++i) {
Brick &brick = game.bricks[i];
if (brick.active &&
game.ball.x == brick.x &&
game.ball.y == brick.y) {
brick.active = false;
game.score++;
game.ball.vy = -game.ball.vy;
break;
}
}
}
// مقادیر اولیه بازی
GameState initializeGame() {
GameState game;
game.lives = INITIAL_LIVES;
game.score = 0;
game.ballLaunched = false;
// راکت پایین وسط
game.paddle.x = BOARD_WIDTH / 2 - 3;
game.paddle.width = 6;
// توپ بالای راکت
game.ball.x = game.paddle.x + 3;
game.ball.y = BOARD_HEIGHT - 2;
// حرکت اولیه توپ
game.ball.vx = 1;
game.ball.vy = -1;
// ساختن آجر ها
game.bricks.clear();
for (int row = 0; row < BRICK_ROWS; ++row) {
for (int col = 0; col < BRICK_COLUMNS; ++col) {
Brick brick;
brick.x = col * (BOARD_WIDTH / BRICK_COLUMNS);
brick.y = row + 2;
brick.active = true;
game.bricks.push_back(brick);
}
}
return game;
}
// حلقه بازی
void gameLoop(GameState& game, const string& playerName) {
const int FPS = 30;
chrono::milliseconds frameTime(1000 / FPS);
while (game.lives > 0) {
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
checkInput(game);
moveTheBall(game);
checkPaddleHit(game);
checkBrickHit(game);
printBoard(game);
// هیچ آجر فعالی برای بردن نمانده
bool anyBricks = false;
for (size_t i = 0; i < game.bricks.size(); ++i) {
if (game.bricks[i].active) { anyBricks = true; break; }
}
if (!anyBricks) break;
chrono::high_resolution_clock::time_point end = chrono::high_resolution_clock::now();
chrono::duration<double, milli> elapsed = end - start;
if (elapsed < frameTime) this_thread::sleep_for(frameTime - elapsed);
}
// پایان و ذخیره بازی
clearScreen();
GameData gd;
gd.playerName = playerName;
gd.score = game.score;
gd.lives = game.lives;
gd.won = (game.lives > 0);
time_t now = time(0);
gd.datetime = ctime(&now);
saveMatch(gd);
if (game.lives <= 0) cout << "Game Over!\n";
else cout << "Victory! All bricks destroyed!\n";
cout << "Final Score: " << game.score << "\n";
cout << "\nPress Enter to return to menu...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cin.get();
}
// help توضیحات
void showHelp() {
clearScreen();
cout << "=== HELP ===\n";
cout << "A / Left : Move paddle left\n";
cout << "D / Right : Move paddle right\n";
cout << "Space : Launch ball\n";
cout << "P : Pause game\n";
cout << "Q : Quit game\n";
cout << "\nPress Enter to return to menu...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cin.get();
}
// history تاریخچه
bool higherScore(const GameData& a, const GameData& b) {
return a.score > b.score;
}
void showGameHistory() {
clearScreen();
vector<GameData> history = getHistory();
sort(history.begin(), history.end(), higherScore);
cout << "=== GAME HISTORY ===\n";
for (size_t i = 0; i < history.size(); ++i) {
cout << history[i].playerName
<< " | Score: " << history[i].score
<< " | Lives: " << history[i].lives
<< " | " << (history[i].won ? "Won" : "Lost")
<< " | " << history[i].datetime
<< '\n';
}
cout << "\nPress Enter to return to menu...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cin.get();
}
// منو بازی
void showMainMenu() {
while (true) {
clearScreen();
cout << "=== BREAKOUT GAME ===\n";
cout << "1. New Game\n";
cout << "2. Help\n";
cout << "3. Game History\n";
cout << "4. Exit\n";
cout << "Select an option: ";
char choice = (char)_getch();
if (choice == '1') {
clearScreen();
cout << "Enter Player Name: ";
string name;
cin >> name;
GameState game = initializeGame();
gameLoop(game, name);
}
else if (choice == '2') {
showHelp();
}
else if (choice == '3') {
showGameHistory();
}
else if (choice == '4') {
break;
}
else {
// کاربر ورودی نامعتبر زد این پیام رو بده
cout << "\nInvalid option! Press any key...";
(void)_getch();
}
}
}
int main() {
showMainMenu();
return 0;
}