This was a really fun project in Harvard's CS50x course.
Tools Utilized: C, Graphical User Interface

This was the first GUI problem set in Harvard's CS50x course. It's written in C and uses Stanford's Portable Library API.

``````// standard libraries
#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// Stanford Portable Library
#include <spl/gevents.h>
#include <spl/gobjects.h>
#include <spl/gwindow.h>

// height and width of game's window in pixels
#define HEIGHT 800
#define WIDTH 600

// number of rows of bricks
#define ROWS 5

// number of columns of bricks
#define COLS 10

// lives
#define LIVES 3

// bricks
#define BRICKMARGIN 4
#define BRICKHEIGHT 10
#define BRICKLENGTH ((WIDTH / COLS) - BRICKMARGIN)

#define PADDLE_Y HEIGHT - (BRICKHEIGHT * 2)
#define PADDLE_X WIDTH / 2 - BRICKLENGTH

// ball
#define BALL_Y HEIGHT - (BRICKHEIGHT * 2 + RADIUS)
#define BALL_X (WIDTH / 2) - (RADIUS / 2)

// prototypes
void initBricks(GWindow window);
GOval initBall(GWindow window);
GLabel initScoreboard(GWindow window);
void updateScoreboard(GWindow window, GLabel label, int points, int lives);
GObject detectCollision(GWindow window, GOval ball);

int main(void)
{
// seed pseudorandom number generator
srand48(time(NULL));

// instantiate window
GWindow window = newGWindow(WIDTH, HEIGHT);

// instantiate bricks
initBricks(window);

// instantiate ball, centered in middle of window
GOval ball = initBall(window);

// instantiate paddle, centered at bottom of window

// instantiate scoreboard, centered in middle of window, just above ball
GLabel label = initScoreboard(window);

// number of bricks initially
int bricks = COLS * ROWS;

// number of lives initially
int lives = LIVES;

// number of points initially
int points = 0;

// speed of ball
double velocityX = drand48() + 1;
double velocityY = -3.0;

// keep playing until game over
while (lives > 0 && bricks > 0)
{
updateScoreboard(window, label, points, lives);

// listen for mouse event
GEvent event = getNextEvent(MOUSE_EVENT);
// if we heard any event
if (event != NULL)
{
// if the event was movement
if (getEventType(event) == MOUSE_MOVED)
{
// ensure paddle follows the cursor along the x-axis
double x = getX(event) - getWidth(paddle) / 2;
}
}

// move ball
move(ball, velocityX, velocityY);

// bounce off right edge of window
if (getX(ball) + RADIUS >= WIDTH)
{
velocityX = -velocityX;
}
// bounce off left edge of window
else if (getX(ball) <= 0)
{
velocityX = -velocityX;
}
// bounce off the top
if (getY(ball) <= 0)
{
velocityY = -velocityY;
}
// if the paddle misses the ball, remove a life and reset
if (getY(ball) >= PADDLE_Y)
{
lives--;
updateScoreboard(window, label, points, lives);
setLocation(ball, BALL_X, BALL_Y);
if (lives > 0 && bricks > 0)
waitForClick();
}
pause(10);
// detect collision
GObject object = detectCollision(window, ball);

if (object != NULL)
{
if (object == paddle)
{
velocityY = -velocityY - .1;
velocityX = velocityX - .5;
}
else if (strcmp(getType(object), "GRect") == 0)
{
removeGWindow(window, object);
velocityY = -velocityY;
points++;
bricks--;
updateScoreboard(window, label, points, lives);
}
}
if (bricks > 0 && lives == 0)
{
char s;
sprintf(s, "You lose!");
setLabel(label, s);
setLocation(label, (WIDTH - getWidth(label)) / 2, (HEIGHT - getHeight(label)) / 2);
}
else if (bricks <= 0 && lives > 0)
{
char s;
sprintf(s, "You win!");
setLabel(label, s);
setLocation(label, (WIDTH - getWidth(label)) / 2, (HEIGHT - getHeight(label)) / 2);
}

}

// wait for click before exiting
waitForClick();

// game over
closeGWindow(window);
return 0;
}

/**
* Initializes window with a grid of bricks.
*/
void initBricks(GWindow window)
{
int brickY = BRICKLENGTH + BRICKMARGIN;

for (int i = 0; i < ROWS; i++)
{
int brickX = BRICKMARGIN - (BRICKMARGIN / 2);
for (int j = 0; j < COLS; j++)
{
GRect brick = newGRect(brickX, brickY, BRICKLENGTH, BRICKHEIGHT);

if (i == 0)
setColor(brick, "ffff9d");

if (i == 1)
setColor(brick, "beeb9f");

if (i == 2)
setColor(brick, "79bd8f");

if (i == 3)
setColor(brick, "00a388");

if (i == 4)
setColor(brick, "ff6138");

setFilled(brick, true);
brickX+=(BRICKLENGTH + BRICKMARGIN);
}
brickY+=(BRICKHEIGHT + BRICKMARGIN);
}
}

/**
* Instantiates ball in center of window.  Returns ball.
*/
GOval initBall(GWindow window)
{
GOval ball = newGOval(BALL_X, BALL_Y, RADIUS, RADIUS);
setColor(ball, "83d1e8");
setFilled(ball, true);
return ball;
}

/**
* Instantiates paddle in bottom-middle of window.
*/
{
}

/**
* Instantiates, configures, and returns label for scoreboard.
*/
GLabel initScoreboard(GWindow window)
{
GLabel label = newGLabel("Start");
setFont(label, "SansSerif-28");
setColor(label, "83d1e8");
return label;
}

/**
* Updates scoreboard's label, keeping it centered in window.
*/
void updateScoreboard(GWindow window, GLabel label, int points, int lives)
{
// update label
char s;
sprintf(s, "Points: %i, Lives: %i", points, lives);
setLabel(label, s);

// center label in window
setLocation(label, (WIDTH - getWidth(label)) / 2, (HEIGHT - getHeight(label)) / 2);
}

/**
* Detects whether ball has collided with some object in window
* by checking the four corners of its bounding box (which are
* outside the ball's GOval, and so the ball can't collide with
* itself).  Returns object if so, else NULL.
*/
GObject detectCollision(GWindow window, GOval ball)
{
// ball's location
double x = getX(ball);
double y = getY(ball);

// for checking for collisions
GObject object;

// check for collision at ball's top-left corner
object = getGObjectAt(window, x, y);
if (object != NULL)
{
return object;
}

// check for collision at ball's top-right corner
object = getGObjectAt(window, x + RADIUS, y);
if (object != NULL)
{
return object;
}

// check for collision at ball's bottom-left corner
object = getGObjectAt(window, x, y + RADIUS);
if (object != NULL)
{
return object;
}

// check for collision at ball's bottom-right corner
object = getGObjectAt(window, x + RADIUS, y + RADIUS);
if (object != NULL)
{
return object;
}

// no collision
return NULL;
}``````