Files
breakoutpp/main.cpp
T
farnam_jhn 4588950b31
CMake Build / build (ubuntu-latest) (push) Has been cancelled
added files
2026-04-12 20:44:08 -07:00

329 lines
8.3 KiB
C++

// 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;
}