Breakout, the game the course builds in HiSoft C, running on the Tatung Einstein: coloured brick rows, the bat and the ball on a black court
← Back to Courses
module
32

Score And Speed

Introduction

S31's game can be won and lost, but it is the same game from the first brick to the last. Arcade Breakout speeds up as you go, and when you clear the wall it gives you another, faster. This stage adds both, and a level number, and it is short: every number it changes was put in a #define or a global by an earlier stage, and the changes are where they were always going to be.

A Tick That Changes

TICK was a #define - a constant - because until now the speed was fixed. Now it is a global:

#define STARTTICK 4

int score, remaining, lives, level, tick;

and the loop waits wait_frames(tick). Four frames a tick to begin with, a little slower than S28's three, because the game is longer now and the first level should be winnable. Two things shorten it:

    if (remaining == BRICKROWS * BRICKCOLS / 2 && tick > 1)
        tick--;

in hit_brick, when exactly half the bricks have gone - once, because remaining passes 38 once - and, on a new level:

        if (remaining == 0) {
            level++;
            tick = STARTTICK - level + 1;
            if (tick < 1)
                tick = 1;
            draw_bricks();
            draw_score();
        }

Level 2 starts at three frames, level 3 at two, level 4 at one, and it stays at one: fifty ticks a second, which nobody will reach. The wall comes back with draw_bricks, which S30 wrote to set every brick to 1 and reset remaining; nothing else is needed, because nothing else remembers the bricks.

Where The Level Is Checked

The outer loop of S31 ran while there were lives and bricks. Now it runs while there are lives, and a cleared wall is dealt with at the top of it, before the next ball is placed:

    while (lives > 0) {
        if (remaining == 0) {
            ...
        }
        place_ball(&b, &paddle);
        wait_launch();
        ...

So clearing the wall ends the inner loop as before (while (remaining > 0)), the ball is not lost so no life goes, and the outer loop comes round, finds no bricks, builds the next level and carries on. The Cleared! message has gone; the game ends only on Game over.

Scoring

score += 10 * level; - the same brick is worth more on a later level, which is the arcade's way of saying the later levels are harder. The status row grows a field: Score: %4d Lives: %d Level: %d, still thirty-two characters with its trailing space, so that S31's padding rule holds.

The Code

BREAK5.C with these changes, saved as BREAK6.C:

#define STARTTICK 4

in place of #define TICK 3;

int score, remaining, lives, level, tick;
void draw_score()
{
    curat(0, 22);
    printf("Score: %4d  Lives: %d  Level: %d ", score, lives, level);
}

in hit_brick, after remaining-- and draw_score():

    score += 10 * level;
    remaining--;
    draw_score();
    if (remaining == BRICKROWS * BRICKCOLS / 2 && tick > 1)
        tick--;

and main:

main()
{
    char line[40];
    int c;
    paddle.width = 5;
    paddle.left = 18;
    score = 0;
    lives = LIVES;
    level = 1;
    tick = STARTTICK;
    define_shapes();
    draw_court();
    draw_bricks();
    draw_bat(&paddle);
    draw_score();
    curoff();
    while (lives > 0) {
        if (remaining == 0) {
            level++;
            tick = STARTTICK - level + 1;
            if (tick < 1)
                tick = 1;
            draw_bricks();
            draw_score();
        }
        place_ball(&b, &paddle);
        wait_launch();
        while (remaining > 0) {
            c = kbd();
            if (c == LEFTKEY)
                move_bat(&paddle, -1);
            if (c == RIGHTKEY)
                move_bat(&paddle, 1);
            move_ball(&b, &paddle);
            if (b.y >= BATROW)
                break;
            hit_brick(&b);
            draw_ball(&b);
            wait_frames(tick);
        }
        if (b.y >= BATROW) {
            curat(b.oldx, b.oldy);
            putchar(' ');
            lives--;
            draw_score();
        }
    }
    curon();
    message("Game over.  Press ENTER");
    gets(line);
    cls40();
}

The whole file is BREAK6.C in Appendix III.

Starting from

BREAK5.C, edited as above; HC BREAK6.C; BREAK6.

What you should see

S31's game, a shade slower at first, with Level: 1 at the end of the status row. Clear half the wall and the ball quickens; clear all of it and a new wall appears with Level: 2, faster again, and each brick now worth 20. Three balls lost, Game over. Press ENTER.

Change One Thing

  • Change STARTTICK to 2. Which level runs at one frame a tick?
  • Change the half-way test to remaining % 10 == 0. What happens to tick, and why does the tick > 1 matter more now?
  • Take out draw_bricks() from the new-level code. What does level 2 look like, and what does the game do?

Exercises

32.1 Make the ball faster on every brick row: a tick per row, in an array indexed by the row of the last brick hit.

32.2 Show Level only when it changes, with a message that stays for a second (fifty frames) before the ball is placed.

32.3 Award a bonus of 100 * level for clearing a wall, and print it in a message before the next level.

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
undefined symbol TICK (or tick)The #define went and a use of it remains, or the global is not declared.
Level 2 has no bricks and the ball bounces between the walls for everdraw_bricks() is missing from the new-level code.
The ball never speeds uptick is set once and wait_frames still waits STARTTICK.
A life is lost when the wall is clearedThe outer loop still tests remaining, or the lost-ball test is not b.y >= BATROW.
wait_frames(0) and the ball is unplayably fastThe floor if (tick < 1) tick = 1; is missing.

Summary

tick is a global the game changes: down one at half the wall, and STARTTICK - level + 1 on each new wall, never below 1. A cleared wall is caught at the top of the outer loop and rebuilt with draw_bricks. Bricks score 10 * level.

Next

S33, Finishing Touches - a title screen, a sound, playing again, and a .COM that stands on its own.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.