added files

This commit is contained in:
2026-04-12 20:44:08 -07:00
commit 4588950b31
34 changed files with 1342 additions and 0 deletions
@@ -0,0 +1,25 @@
name: CMake Build
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release
+43
View File
@@ -0,0 +1,43 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Linker files
*.ilk
# Debugger Files
*.pdb
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# debug information files
*.dwo
includes/temp.cpp
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.10)
project(breakoutpp)
file(GLOB SOURCES
main.cpp
includes/*.cpp
)
add_executable(breakoutpp ${SOURCES})
target_include_directories(breakoutpp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/includes)
+43
View File
@@ -0,0 +1,43 @@
# Breakout++
![Game Screenshot](src/screenshots/macOS/game.png)
An implementation of the game "Athari breakout" in cpp
this is me and my friend [Matin](https://github.com/Matin-Ardestani) freshman year first semester project.
### How to play
there is a help menu that describes how you should play the game and how it works but basically :
you can move your paddle with "a" and "d" to catch the ball
you can press "p" to pause the game.
### Build Instruction
to build using cmake :
```shell
git clone https://github.com/farnam-jhn/breakoutpp.git
cd breakout
mkdir build
cd build
mkdir src
touch src/history.txt
cmake ..
cmake --build .
./breakoutpp
```
or you can use normal gcc compiler :
```shell
git clone https://github.com/farnam-jhn/breakoutpp.git
cd breakout
g++ -o breakoutpp main.cpp includes/*.cpp
./breakoutpp
```
### Note
currently only supports unix-like operating systems (e.g Linux, macOS, BSD)
you can compile and play the game in windows using [WSL](https://learn.microsoft.com/en-us/windows/wsl/)
+25
View File
@@ -0,0 +1,25 @@
you should cout this in order to get colored output on terminal:
\033[<COLORCODE>m<YOUR TEXT>\033[0m
color codes :
30-37: Foreground colors (30=black, 31=red, 32=green, 33=yellow, 34=blue, 35=magenta, 36=cyan, 37=white)
40-47: Background colors (same color order as foreground)
0: Reset to default
1: Bold/bright
4: Underline
in order to use color + bold/underline you should use ; coloumn
e.g.
std::cout << "\033[31mThis is red text\033[0m" << std::endl;
std::cout << "\033[32mThis is green text\033[0m" << std::endl;
std::cout << "\033[1;34mThis is bold blue text\033[0m" << std::endl;
+21
View File
@@ -0,0 +1,21 @@
// Headers
// Structures
// Global variables
// Functions prototype
// Main function
// Functions
BIN
View File
Binary file not shown.
+244
View File
@@ -0,0 +1,244 @@
// Headers
#include <iostream>
#include <thread>
#include <string>
#include <unistd.h>
#include <vector>
#include <algorithm>
#include <random>
#include <atomic>
#include "getchar.h"
#include "setcursor.h"
#include "structs.h"
using namespace std;
// Global varialbes
extern string board[30][80];
extern string hud[30][20];
extern int board_width;
extern int board_lenght;
extern int bricks_idx[36], bricks_idy[36];
extern int hudLength;
extern int hudWidth;
extern int bricksCount;
extern std::atomic<bool> running;
extern std::atomic<bool> paused;
extern string ballChar ;
extern string block ;
extern string blockRed ;
extern string blockGreen;
extern string blockBlue ;
extern string blockYellow ;
extern string trCorner ;
extern string tlCorner ;
extern string brCorner ;
extern string blCorner ;
extern string paddeleLine ;
extern string horizontalLine ;
extern string verticalLine ;
extern Player player;
// Function Prototypes
void hudCalculation();
string bCountInString();
string scoreInString();
bool isBrick(int, int);
// Functions
void drawBoard(){
hudCalculation();
gotoxy(0, 0);
string currentChar;
cout << endl << endl;
for(int i = 0; i < board_width; i++){
cout << " ";
for(int j = 0; j < board_lenght + hudLength ; j++){
if (j < hudLength){
cout << hud[i][j];
}
else {
int boardCol = j - hudLength;
currentChar = board[i][boardCol];
if (currentChar == horizontalLine ||
currentChar == verticalLine ||
currentChar == tlCorner ||
currentChar == trCorner ||
currentChar == blCorner ||
currentChar == brCorner ||
currentChar == ballChar){
cout << currentChar;
}
else if (isBrick(i, boardCol)) {
int nameSeed1 = (int)player.name[0];
int nameSeed2 = (int)player.name[1];
std::mt19937 gen(i * nameSeed1 + j * nameSeed2); // generates a random based on seed (a combination of i and j with player's name)
std::vector<std::string> colors = {blockRed, blockBlue, blockGreen, blockYellow, block}; // Creates a dynamic array containing the colored blocks characters
std::shuffle(colors.begin(), colors.end(), gen); // shuffles the colors dynamic array
for(int k = 0; k < 5; k++) {
std::cout << colors[0];
}
j += 4;
}
else if (currentChar == paddeleLine){
for (int k = 0 ; k < 10 ; k++){
cout << paddeleLine;
}
j += 9;
}
else {
cout << " ";
}
}
}
cout << endl;
}
}
// checking if a certain location contains a brick or not
bool isBrick(int x, int y){
for(int i = 0, j = 0; i < 36; i++, j++){
if(bricks_idx[i] == x){
if(bricks_idy[i] == y){
return true;
}
}
}
return false;
}
// Head up display
void hudCalculation(){
// setting up borders
for(int i = 1; i < hudLength - 1; i++){
hud[0][i] = horizontalLine; // top
hud[hudWidth - 1][i] = horizontalLine; // bottom
}
for(int i = 1; i < hudWidth - 1; i++){
hud[i][0] = verticalLine; // right
hud[i][hudLength - 1] = verticalLine; // left
}
// setting up corners
hud[0][0] = tlCorner;
hud[0][hudLength - 1] = trCorner;
hud[hudWidth - 1][0] = blCorner;
hud[hudWidth - 1][hudLength - 1] = brCorner;
for (int i = 1; i < hudWidth - 1; i++){
for (int j = 1; j < hudLength - 1; j++){
hud[i][j] = " ";
}
}
string score = " Score : " + scoreInString();
for (int i = 0; i < 15; i++){
hud[2][i + 2] = score[i];
}
string health = " Health : " + to_string(player.health);
for (int i = 0 ; i < 11 ; i++){
hud[4][i + 2] = health[i];
}
string bricksLine = " Bricks : " + bCountInString();
for (int i = 0 ; i < 12 ; i++){
hud[6][i + 2] = bricksLine[i];
}
if (paused) {
string pauseText = " PAUSED ";
for (int i = 0; i < 8; i++){
hud[8][i + 2] = pauseText[i];
}
}
}
// Proccessing input
int inputProccessing(Paddle &paddle){
using namespace std::chrono_literals; // for sleep function
char inputChar = getch();
bool moved = false;
int temp = paddle.start_loc.x;
switch (inputChar) {
case 'p':
case 'P':
paused = !paused;
return 1;
break;
case 'a' :
case 'A' :
if (!paused && paddle.start_loc.x > 1) {
paddle.start_loc.x -= 3;
if (paddle.start_loc.x < 1) {
paddle.start_loc.x = 1;
}
moved = true;
}
break;
case 'd' :
case 'D' :
if (!paused && paddle.start_loc.x < board_lenght - 11) {
paddle.start_loc.x += 3;
if (paddle.start_loc.x > board_lenght - 11) {
paddle.start_loc.x = board_lenght - 11;
}
moved = true;
}
break;
case 'q':
return 0;
break;
case 'Q':
return 0;
break;
default:
return 1;
}
// Changing paddle location
if (!paused && moved){
board[board_width - 2][paddle.start_loc.x] = paddeleLine;
board[board_width - 2][temp] = " ";
}
return 1;
}
string scoreInString(){
string result = to_string(player.score);
result.insert(0, 6 - result.length(), '0');
return result;
}
string bCountInString(){
string result = to_string(bricksCount);
result.insert(0, 2 - result.length(), '0');
return result;
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#ifndef BOARD_H
#define BOARD_H
#include "getchar.h"
#include "structs.h"
#include <iostream>
void drawBoard();
bool isBrick(int, int);
int inputProccessing(Paddle &paddle);
#endif
+16
View File
@@ -0,0 +1,16 @@
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#endif
void show_console_cursor(bool show) {
#if defined(_WIN32)
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(handle, &cci);
cci.bVisible = show;
SetConsoleCursorInfo(handle, &cci);
#else
std::cout << (show ? "\033[?25h" : "\033[?25l");
#endif
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#endif
void show_console_cursor(bool show);
+25
View File
@@ -0,0 +1,25 @@
#pragma once // prevents multiple inclusion
#include <iostream>
#if defined(_WIN32) || defined(_WIN64)
#include <conio.h> // Windows
inline char mygetch() {
return _getch(); // Windows getch
}
#else
#include <termios.h>
#include <unistd.h>
inline char getch() {
char buf = 0;
struct termios old = {0};
tcgetattr(0, &old);
old.c_lflag &= ~ICANON; // disable line buffering
old.c_lflag &= ~ECHO; // disable echo
tcsetattr(0, TCSANOW, &old);
read(0, &buf, 1);
old.c_lflag |= ICANON;
old.c_lflag |= ECHO;
tcsetattr(0, TCSANOW, &old);
return buf;
}
#endif
+155
View File
@@ -0,0 +1,155 @@
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <ctime>
#include "getchar.h"
using namespace std;
extern string trCorner ;
extern string tlCorner ;
extern string brCorner ;
extern string blCorner ;
extern string paddeleLine ;
extern string horizontalLine ;
extern string verticalLine ;
/*
history format:
PlayerName, Best socre, Best socre time, Last score, Last score time
*/
void saveData(string name, int score){
ifstream history_reader("src/history.txt");
if(!history_reader.is_open()){
cout << "ERROR OPENNING HISTORY!";
return;
}
time_t now = time(nullptr);
string date = ctime(&now);
date.pop_back(); // delete \n at the end
string output = "";
bool new_player = true;
string line;
while(getline(history_reader, line)){
int splitter[4];
splitter[0] = line.find(",");
splitter[1] = line.find(",", splitter[0] + 1);
splitter[2] = line.find(",", splitter[1] + 1);
splitter[3] = line.find(",", splitter[2] + 1);
string player_name = line.substr(0, splitter[0]);
int best_score = stoi(line.substr(splitter[0] + 1, splitter[1] - splitter[0] - 1));
string best_score_date = line.substr(splitter[1] + 1, splitter[2] - splitter[1] - 1);
int last_score = stoi(line.substr(splitter[2] + 1, splitter[3] - splitter[2] - 1));
string last_score_date = line.substr(splitter[3] + 1);
if(player_name == name){
new_player = false;
if(score >= best_score){
best_score = score;
best_score_date = date;
}
last_score = score;
last_score_date = date;
}
output += player_name + "," +
to_string(best_score) + "," +
best_score_date + "," +
to_string(last_score) + "," +
last_score_date + "\n";
}
history_reader.close();
if(new_player){
output += name + "," +
to_string(score) + "," +
date + "," +
to_string(score) + "," +
date + "\n";
}
ofstream history_write("src/history.txt", ios::trunc);
history_write << output;
history_write.close();
}
void showHistory(){
ifstream history("src/history.txt");
if(!history.is_open()){
cout << "ERROR OPENNING HISTORY!";
return;
}
system("clear");
const int tableWidth = 100;
// Top border
cout << endl << endl;
cout << tlCorner;
for(int i = 0; i < tableWidth; i++) cout << horizontalLine;
cout << trCorner << endl;
// Title
cout << verticalLine
<< "\033[33m" // yellow
<< setw(tableWidth) << left << " GAME HISTORY"
<< "\033[0m" // reset
<< verticalLine << endl;
// Separator
cout << verticalLine;
for(int i = 0; i < tableWidth; i++) cout << horizontalLine;
cout << verticalLine << endl;
// Header
cout << verticalLine << " "
<< "\033[36m" // cyan
<< setw(15) << left << "Player"
<< setw(12) << "Best Score"
<< setw(25) << "Best Date"
<< setw(14) << "Last Score"
<< setw(32) << "Last Date"
<< "\033[0m" // reset
<< " " << verticalLine << endl;
// Header separator
cout << verticalLine;
for(int i = 0; i < tableWidth; i++) cout << horizontalLine;
cout << verticalLine << endl;
string line;
while(getline(history, line)){
int splitter[4];
splitter[0] = line.find(",");
splitter[1] = line.find(",", splitter[0] + 1);
splitter[2] = line.find(",", splitter[1] + 1);
splitter[3] = line.find(",", splitter[2] + 1);
string player_name = line.substr(0, splitter[0]);
string best_score = line.substr(splitter[0] + 1, splitter[1] - splitter[0] - 1);
string best_score_date = line.substr(splitter[1] + 1, splitter[2] - splitter[1] - 1);
string last_score = line.substr(splitter[2] + 1, splitter[3] - splitter[2] - 1);
string last_score_date = line.substr(splitter[3] + 1);
cout << verticalLine << " "
<< "\033[32m" << setw(15) << left << player_name << "\033[0m" // green - reset
<< "\033[35m" << setw(12) << best_score << "\033[0m" // purple - reset
<< setw(16) << best_score_date << " "
<< "\033[35m" << setw(14) << last_score << "\033[0m" // purple - reset
<< setw(32) << last_score_date
<< " " << verticalLine << endl;
}
// Bottom border
cout << blCorner;
for(int i = 0; i < tableWidth; i++) cout << horizontalLine;
cout << brCorner << endl;
// escape
getch();
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <ctime>
#include "getchar.h"
using namespace std;
void saveData(string name, int score);
void showHistory();
+216
View File
@@ -0,0 +1,216 @@
#include "structs.h"
#include "board.h"
#include <string>
#include <thread>
#include <atomic>
using namespace std;
extern Ball ball;
extern Player player;
extern int board_lenght;
extern int board_width;
extern string board[30][80];
extern string ballChar;
extern int bricks_idx[36];
extern int bricks_idy[36];
extern string paddeleLine ;
extern Paddle paddle;
extern int bricksCount;
extern std::atomic<bool> paused;
extern Brick bricks[36];
void ballmover(Ball &ball){
if (player.health == 0){
player.gameover = true;
}
else if (bricksCount == 0){
player.won = true;
}
using namespace std::chrono_literals;
// Calculate new position
int tempNewX = ball.loc.x + ball.v.vX;
int tempNewY = ball.loc.y + ball.v.vY;
// Checking collision for current position
for(int i = 0; i < 36; i++){
if(bricks_idx[i] != -1 && bricks_idy[i] != -1){
int brick_row = bricks_idx[i];
int brick_col_start = bricks_idy[i];
// Check if ball is stuck in a brick
if(ball.loc.y == brick_row &&
ball.loc.x >= brick_col_start &&
ball.loc.x <= brick_col_start + 4){
// reverse both velocities
ball.v.vX = -ball.v.vX;
ball.v.vY = -ball.v.vY;
tempNewX = ball.loc.x + ball.v.vX;
tempNewY = ball.loc.y + ball.v.vY;
// delete brick and add score
player.score += bricks[i].score;
bricksCount--;
bricks_idx[i] = -1;
bricks_idy[i] = -1;
goto skip_normal_collision; // Skip rest of collision checks
}
}
}
// Checking brick collison for new position
for(int i = 0; i < 36; i++){
if(bricks_idx[i] != -1 && bricks_idy[i] != -1){
int brick_row = bricks_idx[i];
int brick_col_start = bricks_idy[i];
int brick_col_end = brick_col_start + 4;
// checking if there is a brick there, Works like (isBrick( , )) function
if(tempNewY == brick_row &&
tempNewX >= brick_col_start &&
tempNewX <= brick_col_end){
bool bounceX = false;
bool bounceY = false;
// X direction
if(ball.v.vX > 0 && ball.loc.x < brick_col_start){ // heading right
bounceX = true;
}
else if(ball.v.vX < 0 && ball.loc.x > brick_col_end){ // heading left
bounceX = true;
}
// Y direction
if(ball.v.vY > 0 && ball.loc.y < brick_row){ // heading down
bounceY = true;
}
else if(ball.v.vY < 0 && ball.loc.y > brick_row){ // heading up
bounceY = true;
}
if(bounceX && bounceY){ // Corner hit
// fixing the special case problem. in this case two bricks collapse at once
bool special_case_handled = false; // flag
if(i < 23 && i % 12 != 0 && ball.v.vX > 0){
if(bricks_idx[i - 1] != -1 && bricks_idx[i + 12] != -1){
// delete brick and add score ( two bricks )
player.score += (bricks[i - 1].score + bricks[i + 12].score);
bricksCount -= 2;
bricks_idx[i - 1] = -1;
bricks_idy[i - 1] = -1;
bricks_idx[i + 12] = -1;
bricks_idy[i + 12] = -1;
special_case_handled = true;
}
}
else if(i < 23 && i % 12 != 11 && ball.v.vX < 0){
if(bricks_idx[i + 1] != -1 && bricks_idx[i + 12] != -1){
// delete brick and add score ( two bricks )
player.score += (bricks[i + 1].score + bricks[i + 12].score);
bricksCount -= 2;
bricks_idx[i + 1] = -1;
bricks_idy[i + 1] = -1;
bricks_idx[i + 12] = -1;
bricks_idy[i + 12] = -1;
special_case_handled = true;
}
}
if(!special_case_handled){ // no special case accured
// delete brick and add score
player.score += bricks[i].score;
bricksCount--;
bricks_idx[i] = -1;
bricks_idy[i] = -1;
}
ball.v.vX = -ball.v.vX;
ball.v.vY = -ball.v.vY;
}
else if(bounceX){ // Side hit
ball.v.vX = -ball.v.vX;
// delete brick and add score
player.score += bricks[i].score;
bricksCount--;
bricks_idx[i] = -1;
bricks_idy[i] = -1;
}
else { // Top/bottom hit
ball.v.vY = -ball.v.vY;
// delete brick and add score
player.score += bricks[i].score;
bricksCount--;
bricks_idx[i] = -1;
bricks_idy[i] = -1;
}
// Recalculate position after bounce
tempNewX = ball.loc.x + ball.v.vX;
tempNewY = ball.loc.y + ball.v.vY;
break; // Only handle one brick per frame
}
}
}
skip_normal_collision:
// Wall collision
if (tempNewX <= 0 || tempNewX >= board_lenght - 1){
ball.v.vX = -ball.v.vX;
tempNewX = ball.loc.x + ball.v.vX;
}
if (tempNewY == board_width - 1){
player.health--;
ball.v.vY = -ball.v.vY;
tempNewY = ball.loc.y + ball.v.vY;
}
else if (tempNewY <= 0 || tempNewY >= board_width - 1){
ball.v.vY = -ball.v.vY;
tempNewY = ball.loc.y + ball.v.vY;
}
if (tempNewY == board_width - 2) {
// Check if the ball's X position is inside the paddle (width 10)
if (tempNewX >= paddle.start_loc.x && tempNewX < paddle.start_loc.x + 10) {
int distanceFromPS = tempNewX - paddle.start_loc.x;
ball.v.vY = -1; // Force ball to move UP
int velocityMagnitude[10] = {3,2,2,1,1,1,1,2,2,3}; // an array that gives any x axis distance between the ball and paddle start a velocity
if (ball.v.vX > 0){
ball.v.vX = velocityMagnitude[distanceFromPS];
}
else {
ball.v.vX = -velocityMagnitude[distanceFromPS];
}
tempNewY = ball.loc.y + ball.v.vY; // Update next Y immediately
}
}
// Clear old position
if (ball.loc.x != tempNewX || ball.loc.y != tempNewY) {
board[ball.loc.y][ball.loc.x] = " ";
}
// Update position
ball.loc.x = tempNewX;
ball.loc.y = tempNewY;
// Draw ball at new position
board[ball.loc.y][ball.loc.x] = ballChar;
}
+3
View File
@@ -0,0 +1,3 @@
#include "structs.h"
void ballmover(Ball &ball);
+96
View File
@@ -0,0 +1,96 @@
#include <iostream>
#include "getchar.h"
char optionChoosenByUser(){
std::cout << "\n\n\033[33m <-------Select option!------->\033[0m" << std::endl
<< "\033[34m ╔══════════════╗ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<<" 1. New Game "
<<"\033[34m║ \033[0m" << std::endl
<< "\033[34m ╚══════════════╝ \033[0m" << std::endl << std::endl
<< "\033[34m ╔══════════════╗ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<<" 2. Help "
<<"\033[34m║ \033[0m" << std::endl
<< "\033[34m ╚══════════════╝ \033[0m" << std::endl << std::endl
<< "\033[34m ╔══════════════╗ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<<" 3. History "
<<"\033[34m║ \033[0m" << std::endl
<< "\033[34m ╚══════════════╝ \033[0m" << std::endl << std::endl
<< "\033[34m ╔══════════════╗ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<<" 4. Exit "
<<"\033[34m║ \033[0m" << std::endl
<< "\033[34m ╚══════════════╝ \033[0m" << std::endl;
char chosenOpt = getch();
return chosenOpt;
}
void helpMenu(){
system("clear");
std::cout << std::endl << std::endl
<< "\033[34m ╔══════════════════════════════════════════════════════════════════════╗ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " The game consist of few elements : "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " 1. The ball, 2. Paddel, 3. Blocks "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " The ball starts moving in a line; whenever the ball hits the wall, "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " a block or the paddel it reflects like a beam and whenever it hits "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " a block, the block gets destroyed. "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " The goal is to destroy all of the blocks "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " If your ball fall down and don't touch the paddel you lose a "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " heart; you have 3 hearts, if you lose them all you lose. "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " You can move the paddel using \"A\" & \"D\" to move left & right. "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " Press \"P\" or \"p\" to pause the game. "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " Press any button to exit. "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ║\033[0m"
<< " "
<< "\033[34m║ \033[0m" << std::endl
<< "\033[34m ╚══════════════════════════════════════════════════════════════════════╝ \033[0m" << std::endl;
getch();
}
+5
View File
@@ -0,0 +1,5 @@
#include "getchar.h"
#include <iostream>
char optionChoosenByUser();
void helpMenu();
+14
View File
@@ -0,0 +1,14 @@
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#endif
void gotoxy(int x, int y) {
#ifdef _WIN32
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {(SHORT)x, (SHORT)y};
SetConsoleCursorPosition(hConsole, pos);
#else
printf("\033[%d;%dH", y, x);
#endif
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#endif
void gotoxy(int x, int y);
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <string>
struct Player{
int score = 0;
std::string name;
bool initialInput = false;
int health = 3;
bool gameover = false;
bool won = false;
};
struct Location{
int x;
int y;
};
struct Brick{
Location loc;
int score;
};
struct Paddle{
Location start_loc;
// int lenght; // possible feature, right now considered 10
};
struct Velocity{
int vX;
int vY;
};
struct Ball{
Location loc;
Velocity v;
};
+328
View File
@@ -0,0 +1,328 @@
// Headers
#include "includes/menu.h"
#include "includes/getchar.h"
#include "includes/board.h"
#include "includes/cursorhide.h"
#include "includes/structs.h"
#include "includes/mechanic.h"
#include "includes/history.h"
#include <cstdlib>
#include <string>
#include <chrono>
#include <thread>
#include <atomic>
using namespace std::chrono;
// ----- Global variables -----
// Game components
int board_lenght = 80;
int board_width = 30;
std::string board[30][80];
std::string hud[30][20];
int hudLength = 20;
int hudWidth = 30;
int velocitySpeed[3] = {60,70,80};
int bricksCount = 36;
Paddle paddle;
Ball ball;
Player player;
Brick bricks[36];
int bricks_idx[36], bricks_idy[36];
// Characters
std::string ballChar = "\033[32m●\033[0m";
std::string block = "";
std::string blockRed = "\033[31m█\033[0m";
std::string blockGreen= "\033[32m█\033[0m";
std::string blockBlue = "\033[34m█\033[0m";
std::string blockYellow = "\033[33m█\033[0m";
std::string trCorner = "\033[34m╗\033[0m";
std::string tlCorner = "\033[34m╔\033[0m";
std::string brCorner = "\033[34m╝\033[0m";
std::string blCorner = "\033[34m╚\033[0m";
std::string paddeleLine = "\033[32m╍\033[0m";
std::string horizontalLine = "\033[34m═\033[0m";
std::string verticalLine = "\033[34m║\033[0m";
// ----- Functions prototype -----
void setup();
void deallocation();
void locatePaddle(int x);
void boardRender();
void ballMoveTask();
void endGame();
void inputThread();
// ----- Main function -----
std::atomic<bool> running(true); /* used in order to manage the thread
and prevent threads racing (racing : a thread reading
a variable created by another when it's not fully written) */
std::atomic<bool> paused(false);
int main(){
using namespace std::chrono;
show_console_cursor(false); // hides the cursor
// Unicode settings
system("chcp 65001"); // for showing the unicode characters in terminal
// Menu
while (true) {
system("clear");
char opt = optionChoosenByUser();
switch (opt) {
case '1':
{ // written in scope to maintain the threads
setup(); // sets up the board
running.store(true);
system("clear");
std::cout << "\n\n Enter your name : ";
std::cin >> player.name;
system("clear");
std::cout << "\n\n PRESS ANY KEY TO START THE GAME. \n";
getch();
running = true;
std::thread threadOne(boardRender); // creates a thread for board rendering
std::thread threadTwo(ballMoveTask); // created a thread for ball movement
std::thread threadThree(inputThread);
system("clear");
while (running && !player.gameover && !player.won) {
// small sleep for threads to finish their jobs
std::this_thread::sleep_for(milliseconds(100));
}
running = false;
threadOne.join();
threadTwo.join();
threadThree.detach();
if (player.gameover || player.won) {
system("clear");
endGame();
std::cout << "\n\n Press enter twice to return to main menu\n";
getch();
saveData(player.name, player.score);
}
}
break;
case '2':
helpMenu();
break;
case '3':
showHistory();
break;
case '4':
system("clear");
std::cout << std::endl;
std::cout << " Press \"c\" to confirm\n";
char confirmChar = getch();
if (confirmChar == 'c'){
system("clear");
return 0;
}
system("clear");
break;
}
}
}
// ----- Functions -----
void locatePaddle(int x){ // receives starting point x because y stays the same
board[board_width - 2][x] = paddeleLine;
}
// setup the board when starting new game
void setup(){
/*
only the starting char of bricks and the paddle change. because it would be easier to layout them.
*/
// clearing board
for (int i = 0; i < board_width; i++){
for (int j = 0; j < board_lenght; j++) {
board[i][j] = " ";
}
}
// setting up variables
player.gameover = false;
player.won = false;
player.score = 0;
player.health = 3;
bricksCount = 36;
// setting up bricks
/*
How it works : it goes in a row and when ever it reaches the start of a brick it places that into the bricks_idx and does the same thing for the y.
Note : x and y in this function are swapped compared to cartesian system.
*/
int counterX = 1, counterY = 10;
for(int i = 0; i < 36; i++){ // saving the location of each brick
bricks[i].loc.x = counterX;
bricks[i].loc.y = counterY;
bricks_idx[i] = counterX;
bricks_idy[i] = counterY;
counterY += 5;
if(i == 11 || i == 23){ // 11 is where firsto row of bricks end and 23 is end of the second row
counterY = 10;
counterX++;
}
}
// setting up brick scores
/*
first row(index 0 - 11) : 1000
second row(index 12 - 23) : 500
third row(index 24 - 35) : 200
*/
for(int i = 0; i < 12; i++){
bricks[i].score = 1000;
}
for(int i = 12; i < 24; i++){
bricks[i].score = 500;
}
for(int i = 24; i < 36; i++){
bricks[i].score = 200;
}
// setting up borders
for(int i = 1; i < board_lenght - 1; i++){
board[0][i] = horizontalLine; // top
board[board_width - 1][i] = horizontalLine; // bottom
}
for(int i = 1; i < board_width - 1; i++){
board[i][0] = verticalLine; // right
board[i][board_lenght - 1] = verticalLine; // left
}
// setting up corners
board[0][0] = tlCorner;
board[0][board_lenght - 1] = trCorner;
board[board_width - 1][0] = blCorner;
board[board_width - 1][board_lenght - 1] = brCorner;
// setting up bricks
for(int i = 1; i <= 3; i++){
for(int j = 1; j < board_lenght - 1; j++){
if(isBrick(i, j)){
board[i][j] = block;
j += 4;
}
}
}
// setup paddle
paddle.start_loc.x = (board_lenght - 10) / 2 - 1;
paddle.start_loc.y = board_width - 2;
locatePaddle(paddle.start_loc.x);
// setup ball : locations are chosen such that the ball hits the paddle initially
ball.loc.x = 22;
ball.loc.y = 15;
/* Note : the velocity below is not suitable for configuring the speed
in order to change the speed, change the value of interval in ballMoveTask
changing the velocity below would affect the ball collision angle.*/
ball.v.vX = 1;
ball.v.vY = 1;
board[ball.loc.y][ball.loc.x] = ballChar;
}
// Board rendering
void boardRender() {
auto interval = milliseconds(16);
auto next_time = steady_clock::now();
while (running) {
next_time += interval;
drawBoard();
std::this_thread::sleep_until(next_time);
}
}
// Input processing thread
void inputThread() {
while (running && !player.gameover && !player.won) {
int q = inputProccessing(paddle);
if (q == 0) {
running = false;
break;
}
}
}
// Ball rendering
void ballMoveTask(){
auto next_time = steady_clock::now();
while (running) {
auto interval = milliseconds(velocitySpeed[abs(ball.v.vX) - 1]);
next_time += interval;
if (!paused){
ballmover(ball);
if (player.gameover || player.won){
running = false; // closes the threads
break;
}
}
std::this_thread::sleep_until(next_time);
}
}
void endGame(){
// Reporting
if (player.gameover){
std::cout << "\n\n Game over.\n";
}else {
std::cout << "\n\n Game finished. \n Congrats! You Won!";
}
std::cout << "\n Your score : ";
std::cout << player.score;
}
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB