#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <vector>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <random>
#include <fstream>
#include <string>
using namespace std;
const int SCREEN_W = 800;
const int SCREEN_H = 600;
float PADDLE_W = 100;
const float PADDLE_H = 20;
const float BALL_RADIUS = 10;
const int BLOCK_W = 60;
const int BLOCK_H = 20;
const int LEVEL_ROWS = 5;
const int LEVEL_COLS = 10;
int MAX_LIVES = 5;
struct Ball
{
float x, y;
float dx, dy;
};
struct Trail
{
float x, y;
int life; };
struct Spark
{
float x, y;
float dx, dy;
int life;
ALLEGRO_COLOR color;
};
enum class PowerUpType
{
Life,
PaddleExtend,
PaddleShrink,
Points
};
struct PowerUp
{
float x, y;
float speed = 2.0f;
PowerUpType type;
bool active = true;
};
struct HighScore
{
int score;
};
vector < HighScore > highScores;
void load_scores()
{
highScores.clear();
ifstream file( "score.txt" );
if( file.is_open() )
{
int s;
while( file >> s )
{
highScores.push_back( { s } );
}
file.close();
}
while( highScores.size() < 10 )
{
highScores.push_back( { 0 } );
}
}
void save_scores()
{
ofstream file( "score.txt", ios::trunc );
if( file.is_open() )
{
for( auto & hs: highScores )
{
file << hs.score << "\n";
}
file.close();
}
}
void update_high_scores( int newScore )
{
highScores.push_back( { newScore } );
sort( highScores.begin(), highScores.end(),[ ]( HighScore a, HighScore b )
{
return a.score > b.score;
} );
if( highScores.size() > 10 )
highScores.resize( 10 );
save_scores();
}
float get_random( float Min, float Max )
{
static default_random_engine e;
uniform_real_distribution < > dis( Min, Max ); return dis( e );
}
int get_random( int Min, int Max )
{
static random_device rd;
static mt19937 gen( rd() );
uniform_int_distribution < std::mt19937::result_type > dis( Min, Max );
return dis( gen );
}
std::vector < Spark > sparks;
std::vector < Trail > trails;
std::vector < PowerUp > powerups;
struct Paddle
{
float x, y;
float speed;
};
struct Block
{
float x, y;
int hits; bool alive = true;
bool indestructible = false; int hit_animation = 0;
};
const std::vector < std::vector < std::vector < int >> > levels =
{
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
},
{
{ 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 2, 2, 0, 0, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }
},
{
{ 2, 1, 0, 0, 1, 1, 0, 0, 1, 2 },
{ 1, 1, 1, 2, 1, 1, 2, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 2, 1, 1, 2, 1, 1, 1 },
{ 2, 1, 0, 0, 1, 1, 0, 0, 1, 2 }
},
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 2, 2, 2, 2, 2, 2, 2, 2, 1 },
{ 1, 2, 2, 3, 3, 3, 3, 2, 2, 1 },
{ 1, 2, 2, 2, 2, 2, 2, 2, 2, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
},
{
{ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
{ 1, 9, 2, 2, 1, 1, 2, 2, 9, 1 },
{ 9, 1, 1, 9, 3, 3, 9, 1, 1, 9 },
{ 1, 9, 2, 2, 1, 1, 2, 2, 9, 1 },
{ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 } },
{
{ 1, 1, 3, 3, 1, 1, 3, 3, 1, 1 },
{ 5, 5, 1, 1, 4, 4, 1, 1, 5, 5 },
{ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 9, 9, 9, 9, 0, 0, 9, 9, 9, 9 } },
{
{ 1, 1, 3, 3, 1, 1, 3, 3, 1, 1 },
{ 5, 5, 1, 1, 4, 4, 1, 1, 5, 5 },
{ 4, 4, 2, 2, 4, 4, 2, 2, 4, 4 },
{ 1, 1, 3, 3, 1, 1, 3, 3, 1, 1 },
{ 5, 5, 5, 5, 9, 9, 5, 5, 5, 5 } }
};
int current_level = 0;
int lives = 2;
bool ball_active = false;
bool draw = true;
bool win = false;
int score = 0;
bool check_collision( float x1, float y1, float w1, float h1,
float x2, float y2, float w2, float h2 )
{
return x1 < x2 + w2 && x1 + w1 > x2 &&
y1 < y2 + h2 && y1 + h1 > y2;
}
void reset_ball_and_paddle( Ball & ball, Paddle & paddle )
{
ball.x = SCREEN_W / 2.0f;
ball.y = SCREEN_H / 2.0f;
ball.dx = 4;
ball.dy = - 4;
paddle.x = SCREEN_W / 2.0f - PADDLE_W / 2;
ball_active = false;
}
void load_level( std::vector < Block > & blocks, int level_index )
{
blocks.clear();
const auto & level = levels[ level_index ];
for( int i = 0; i < LEVEL_ROWS; ++i )
{
for( int j = 0; j < LEVEL_COLS; ++j )
{
int type = level[ i ][ j ];
if( type > 0 )
{
float x = 60 + j *( BLOCK_W + 10 );
float y = 50 + i *( BLOCK_H + 10 );
bool indestructible =( type == 9 ); int hits = indestructible ? 1: type;
blocks.push_back( { x, y, hits, true, indestructible } );
}
}
}
}
int main()
{
if( !al_init() )
{
std::cerr << "Failed to initialize allegro!\n";
return - 1;
}
if( !al_install_keyboard() )
{
std::cerr << "Failed to initialize keyboard!\n";
return - 1;
}
if( !al_install_mouse() )
{
std::cerr << "Failed to initialize mouse!\n";
return - 1;
}
if( !al_init_primitives_addon() )
{
std::cerr << "Failed to initialize primitives!\n";
return - 1;
}
if( !al_init_font_addon() )
{
std::cerr << "Failed to initialize font!\n";
return - 1;
}
if( !al_init_ttf_addon() )
{
std::cerr << "Failed to initialize ttf!\n";
return - 1;
}
ALLEGRO_FONT * font = al_load_font( "arial.ttf", 20, 0 );
if( !font )
{
std::cerr << "Failed to initialize font!\n";
return - 1;
}
ALLEGRO_FONT * font1 = al_load_font( "arial.ttf", 32, 0 );
if( !font1 )
{
std::cerr << "Failed to initialize font1!\n";
return - 1;
}
al_set_new_display_flags( ALLEGRO_FULLSCREEN );
ALLEGRO_DISPLAY * display = NULL;
display = al_create_display( SCREEN_W, SCREEN_H );
al_set_window_title( display, "Arkanoid" );
al_set_window_position( display, 0, 0 );
if( !display )
{
std::cerr << "Failed to initialize display!\n";
return - 1;
}
ALLEGRO_TIMER * timer = al_create_timer( 1.0 / 60 );
ALLEGRO_EVENT_QUEUE * queue = al_create_event_queue();
al_register_event_source( queue, al_get_display_event_source( display ) );
al_register_event_source( queue, al_get_timer_event_source( timer ) );
al_register_event_source( queue, al_get_keyboard_event_source() );
bool running = true;
bool redraw = true;
Ball ball;
Paddle paddle = { SCREEN_W / 2.0f - PADDLE_W / 2, SCREEN_H - 40, 7.0f };
std::vector < Block > blocks;
load_level( blocks, current_level );
reset_ball_and_paddle( ball, paddle );
bool keys[ ALLEGRO_KEY_MAX ] = { false };
al_start_timer( timer );
load_scores();
while( running )
{
ALLEGRO_EVENT ev;
al_wait_for_event( queue, & ev );
if( ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE )
running = false;
else if( ev.type == ALLEGRO_EVENT_KEY_DOWN )
{
keys[ ev.keyboard.keycode ] = true;
if( ev.keyboard.keycode == ALLEGRO_KEY_SPACE )
{
ball_active = true;
}
}
else if( ev.type == ALLEGRO_EVENT_KEY_UP )
keys[ ev.keyboard.keycode ] = false;
else if( ev.type == ALLEGRO_EVENT_TIMER )
{
if( ball_active )
{
trails.push_back( { ball.x, ball.y, 20 } ); for( auto & t: trails )
{
t.life--;
}
trails.erase( std::remove_if( trails.begin(), trails.end(),
[ ]( Trail & t )
{
return t.life <= 0;
} ), trails.end() );
}
if( keys[ ALLEGRO_KEY_ESCAPE ] )
running = false;
if( keys[ ALLEGRO_KEY_LEFT ] && paddle.x > 0 )
paddle.x -= paddle.speed;
if( keys[ ALLEGRO_KEY_RIGHT ] && paddle.x + PADDLE_W < SCREEN_W )
paddle.x += paddle.speed;
if( !ball_active )
{
ball.x = paddle.x + PADDLE_W / 2;
ball.y = paddle.y - BALL_RADIUS - 1;
}
else
{
ball.x += ball.dx;
ball.y += ball.dy;
if( ball.x < 0 || ball.x > SCREEN_W - BALL_RADIUS )
{
ball.dx *= - 1;
for( int i = 0; i < 10; ++i )
{
float scatter = 0.4f; float speed =( float ) get_random( 0, RAND_MAX ) /( RAND_MAX ) * 2.0f + 1.0f;
float impactX = ball.x;
float impactY = ball.y;
float dx = ball.x - impactX;
float dy = ball.y - impactY;
dx +=(( float ) get_random( 0, RAND_MAX ) / RAND_MAX * 2 - 1 ) * scatter;
dy +=(( float ) get_random( 0, RAND_MAX ) / RAND_MAX * 2 - 1 ) * scatter;
float len = sqrt( dx * dx + dy * dy );
if( len != 0 )
{
dx =( dx / len ) * speed;
dy =( dy / len ) * speed;
}
sparks.push_back(
{
impactX,
impactY,
dx,
dy,
20,
al_map_rgb( 255, 255, 0 )
} );
}
}
if( ball.y < 0 )
{
ball.dy *= - 1;
for( int i = 0; i < 10; ++i )
{
float scatter = 0.4f; float speed =( float ) get_random( 0, RAND_MAX ) /( RAND_MAX ) * 2.0f + 1.0f;
float impactX = ball.x;
float impactY = ball.y;
float dx = ball.x - impactX;
float dy = ball.y - impactY;
dx +=(( float ) get_random( 0, RAND_MAX ) / RAND_MAX * 2 - 1 ) * scatter;
dy +=(( float ) get_random( 0, RAND_MAX ) / RAND_MAX * 2 - 1 ) * scatter;
float len = sqrt( dx * dx + dy * dy );
if( len != 0 )
{
dx =( dx / len ) * speed;
dy =( dy / len ) * speed;
}
sparks.push_back(
{
impactX,
impactY,
dx,
dy,
20,
al_map_rgb( 255, 255, 0 )
} );
}
}
if( ball.y > SCREEN_H )
{
lives--;
trails.clear();
sparks.clear();
powerups.clear();
PADDLE_W = 100;
if( lives > 0 )
{
std::cout << "Stracono życie! Pozostało: " << lives << "\n";
reset_ball_and_paddle( ball, paddle );
}
else
{
std::cout << "KONIEC GRY – brak żyć!\n";
update_high_scores( score );
draw = false;
}
}
if( check_collision( ball.x - BALL_RADIUS, ball.y - BALL_RADIUS, BALL_RADIUS * 2, BALL_RADIUS * 2, paddle.x, paddle.y, PADDLE_W, PADDLE_H ) )
{
ball.dy *= - 1;
ball.y = paddle.y - BALL_RADIUS;
for( int i = 0; i < 10; ++i )
{
float scatter = 0.4f; float speed =( float ) get_random( 0, RAND_MAX ) /( RAND_MAX ) * 2.0f + 1.0f;
float impactX = ball.x;
float impactY = ball.y;
float dx = ball.x - impactX;
float dy = ball.y - impactY;
dx +=(( float ) get_random( 0, RAND_MAX ) / RAND_MAX * 2 - 1 ) * scatter;
dy +=(( float ) get_random( 0, RAND_MAX ) / RAND_MAX * 2 - 1 ) * scatter;
float len = sqrt( dx * dx + dy * dy );
if( len != 0 )
{
dx =( dx / len ) * speed;
dy =( dy / len ) * speed;
}
sparks.push_back(
{
impactX,
impactY,
dx,
dy,
20,
al_map_rgb( 255, 255, 0 )
} );
}
}
for( auto & block: blocks )
{
if( !block.alive ) continue;
if( check_collision( ball.x - BALL_RADIUS, ball.y - BALL_RADIUS, BALL_RADIUS * 2, BALL_RADIUS * 2,
block.x, block.y, BLOCK_W, BLOCK_H ) )
{
if( !block.indestructible )
{
score +=( block.hits * 50 );
std::cout << score << std::endl;
block.hits--;
block.hit_animation = 10;
if( block.hits <= 0 )
block.alive = false;
if( rand() % 8 == 0 )
{
PowerUpType type = static_cast < PowerUpType >( rand() % 4 );
powerups.push_back( { block.x + BLOCK_W / 2, block.y + BLOCK_H / 2, 2.0f, type } );
}
}
else
{
block.hit_animation = 10; }
ball.dy *= - 1;
break;
}
}
for( auto & pu: powerups )
{
if( !pu.active ) continue;
pu.y += pu.speed;
if( check_collision( pu.x - 10, pu.y - 10, 20, 20, paddle.x, paddle.y, PADDLE_W, PADDLE_H ) )
{
pu.active = false;
switch( pu.type )
{
case PowerUpType::Life:
if( lives < MAX_LIVES ) lives++;
score += 100;
break;
case PowerUpType::PaddleExtend:
if( PADDLE_W < SCREEN_W / 5 )
{
paddle.speed = 6.0f;
paddle.x -= 20;
const_cast < float & >( PADDLE_W ) += 40;
score += 50;
}
break;
case PowerUpType::PaddleShrink:
if( PADDLE_W > 60 )
{
const_cast < float & >( PADDLE_W ) -= 20;
paddle.x += 10;
score += 150;
}
break;
case PowerUpType::Points:
score += 250;
break;
}
}
if( pu.y > SCREEN_H )
pu.active = false;
}
powerups.erase( std::remove_if( powerups.begin(), powerups.end(),
[ ]( PowerUp & p )
{
return !p.active;
} ), powerups.end() );
for( auto & s: sparks )
{
s.x += s.dx;
s.y += s.dy;
s.life--;
}
sparks.erase( std::remove_if( sparks.begin(), sparks.end(),
[ ]( Spark & s )
{
return s.life <= 0;
} ), sparks.end() );
for( auto & block: blocks )
if( block.hit_animation > 0 )
block.hit_animation--;
bool all_destroyed = true;
for( auto & block: blocks )
{
if( block.alive && !block.indestructible )
{
all_destroyed = false;
break;
}
}
if( all_destroyed )
{
powerups.clear();
current_level++;
if( current_level <( int ) levels.size() )
{
std::cout << "Poziom " << current_level << " ukończony!\n";
load_level( blocks, current_level );
reset_ball_and_paddle( ball, paddle );
trails.clear();
sparks.clear();
}
else
{
std::cout << " WYGRAŁEŚ!\n";
update_high_scores( score );
draw = false;
win = true;
}
}
}
redraw = true;
}
if( redraw && al_is_event_queue_empty( queue ) )
{
al_clear_to_color( al_map_rgb( 0, 0, 0 ) );
if( draw )
{
for( auto & s: sparks )
{
float alpha = s.life / 20.0f;
al_draw_line( s.x, s.y, s.x - s.dx * 2, s.y - s.dy * 2,
al_map_rgba_f( 1.0, 0.8, 0.0, alpha ), 1.0 );
}
for( auto & t: trails )
{
float alpha = t.life / 20.0f;
float radius = BALL_RADIUS *( alpha ); al_draw_filled_circle( t.x, t.y, radius, al_map_rgba_f( 0.09, 0.09, 0.09, alpha * 0.001 ) );
}
al_draw_filled_circle( ball.x, ball.y, BALL_RADIUS, al_map_rgb( 200, 200, 200 ) );
al_draw_filled_rectangle( paddle.x, paddle.y, paddle.x + PADDLE_W, paddle.y + PADDLE_H, al_map_rgb( 128, 128, 128 ) );
for( auto & block: blocks )
{
if( block.alive )
{
ALLEGRO_COLOR color;
if( block.indestructible )
{
color = al_map_rgb( 100, 100, 100 ); }
else if( block.hit_animation > 0 )
{
color = al_map_rgb( 255, 255, 255 );
}
else if( block.hits == 1 )
color = al_map_rgb( 255, 0, 0 );
else if( block.hits == 2 )
color = al_map_rgb( 0, 255, 0 );
else if( block.hits == 3 )
color = al_map_rgb( 0, 0, 255 );
else if( block.hits == 4 )
color = al_map_rgb( 255, 255, 0 );
else if( block.hits == 5 )
color = al_map_rgb( 0, 255, 255 );
else if( block.hits == 6 )
color = al_map_rgb( 255, 0, 255 );
else if( block.hits == 7 )
color = al_map_rgb( 64, 0, 196 );
else if( block.hits == 8 )
color = al_map_rgb( 196, 0, 64 );
al_draw_filled_rectangle( block.x, block.y, block.x + BLOCK_W, block.y + BLOCK_H, color );
}
}
for( auto & pu: powerups )
{
ALLEGRO_COLOR color;
const char * symbol = "?";
switch( pu.type )
{
case PowerUpType::Life:
color = al_map_rgb( 255, 0, 0 );
symbol = "L";
break;
case PowerUpType::PaddleExtend:
color = al_map_rgb( 0, 255, 0 );
symbol = "+";
break;
case PowerUpType::PaddleShrink:
color = al_map_rgb( 255, 255, 0 );
symbol = "-";
break;
case PowerUpType::Points:
color = al_map_rgb( 0, 255, 255 );
symbol = "$";
break;
}
al_draw_filled_circle( pu.x, pu.y, 10, color );
al_draw_text( font, al_map_rgb( 0, 0, 0 ), pu.x, pu.y - 10, ALLEGRO_ALIGN_CENTER, symbol );
}
for( int i = 0; i < lives; ++i )
{
al_draw_filled_circle( 20 + i * 25, SCREEN_H - 20, 10, al_map_rgb( 200, 200, 200 ) );
}
al_draw_textf( font, al_map_rgb( 255, 255, 255 ), SCREEN_W - 150, SCREEN_H - 30, 0, "Wynik: %d", score );
}
else
{
al_draw_text( font1, al_map_rgb( 255, 255, 255 ), 0, 0, 0, "------------------------------ TOP 10 WYNIKÓW ------------------------------" );
for( int i = 0; i < highScores.size(); ++i )
{
string s = to_string( i + 1 ) + ". " + to_string( highScores[ i ].score );
cout << s.c_str() << endl;
al_draw_text( font1, al_map_rgb( 255, 255, 255 ), 0, al_get_font_line_height( font1 ) *( i + 1 ), 0, s.c_str() );
}
al_draw_textf( font1, al_map_rgb( 255, 0, 0 ), 0, al_get_font_line_height( font1 ) * 12, 0, "Twój wynik: %d", score );
if( win )
al_draw_text( font1, al_map_rgb( 255, 255, 255 ), 0, SCREEN_H - al_get_font_line_height( font1 ), 0, "------------------------------ WYGRANA ------------------------------" );
}
al_flip_display();
redraw = false;
}
}
al_destroy_display( display );
al_destroy_timer( timer );
al_destroy_font( font );
al_destroy_font( font1 );
al_destroy_event_queue( queue );
return 0;
}