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
27

The Court

Introduction

From here on each section adds a piece to the game, and what you run at the end of the section is the game so far. This one is the court: three walls, a bat at the bottom, nothing moving yet. It is also the skeleton every later stage hangs on, so it is worth getting the shape of it into your fingers - where the numbers go, where the structures go, what main does and does not do.

The Numbers

#define LEFTWALL 0
#define RIGHTWALL 39
#define TOPWALL 0
#define BATROW 21
#define WALL '#'
#define BATCHAR '='

Every number the court is made of, at the top, named. The walls are the screen's edges - column 0, column 39, row 0 - and the bat lives on row 21, one above the bottom row, which is kept for messages. Two of the #defines are characters: what a wall is drawn with, and what the bat is. Change WALL to * and the whole court changes; change BATROW to 20 and the bat moves up a row and everything that checks for the bat moves with it. That is what S19 was for.

The Bat

typedef struct {
    int left, width;
} bat;

bat paddle;

A bat is where it starts and how wide it is. It is a structure (S18) because the ball will need to ask it both questions at once, and the functions that move and draw it want one thing to point at. The members are left and width, not x - S18's rule: the ball will have x, and no two structures may share a member name. Nor may a member share its name with a global, which S30 found out the hard way.

paddle is global. The whole game's state - the bat, the ball, the bricks, the score - is global in every stage, so that any function can see it; the functions still take pointers, so that they say what they touch.

Drawing

void draw_court()
{
    int i;
    cls40();
    for (i = LEFTWALL; i <= RIGHTWALL; i++) {
        curat(i, TOPWALL);
        putchar(WALL);
    }
    for (i = TOPWALL + 1; i <= BATROW; i++) {
        curat(LEFTWALL, i);
        putchar(WALL);
        curat(RIGHTWALL, i);
        putchar(WALL);
    }
}

Forty characters along the top, two on each of twenty-one rows: 82 curats and putchars, about a third of a second (S24). It is drawn once. draw_bat is a curat and width putchars; it will be called whenever the bat moves, so it draws only the bat and nothing round it.

main

main()
{
    char line[40];
    paddle.width = 5;
    paddle.left = 18;
    draw_court();
    draw_bat(&paddle);
    curat(0, 22);
    curoff();
    rawin();
    curon();
    cls40();
    printf("Press ENTER to finish\n");
    gets(line);
}

Set up the state; draw; wait; tidy. The cursor is parked on the message row and turned off so that it does not sit in the court; the gets at the end is S22's habit, unnecessary this time because rawin takes its key, and kept because from S29 the loop reads the keyboard with kbd() and it will be needed. Every stage's main has this shape, and the game loop goes between draw_bat and the tidying.

The Code

#include STDIO.H

#define LEFTWALL 0
#define RIGHTWALL 39
#define TOPWALL 0
#define BATROW 21
#define WALL '#'
#define BATCHAR '='

typedef struct {
    int left, width;
} bat;

bat paddle;

void draw_court()
{
    int i;
    cls40();
    for (i = LEFTWALL; i <= RIGHTWALL; i++) {
        curat(i, TOPWALL);
        putchar(WALL);
    }
    for (i = TOPWALL + 1; i <= BATROW; i++) {
        curat(LEFTWALL, i);
        putchar(WALL);
        curat(RIGHTWALL, i);
        putchar(WALL);
    }
}

void draw_bat(b) bat *b;
{
    int i;
    curat(b->left, BATROW);
    for (i = 0; i < b->width; i++)
        putchar(BATCHAR);
}

main()
{
    char line[40];
    paddle.width = 5;
    paddle.left = 18;
    draw_court();
    draw_bat(&paddle);
    curat(0, 22);
    curoff();
    rawin();
    curon();
    cls40();
    printf("Press ENTER to finish\n");
    gets(line);
}

#include ?EIN.LIB?
#include ?STDIO.LIB?

Starting from

Typed as BREAK1.C; HC BREAK1.C; BREAK1.

What you should see

A row of # across the top, a column of # down each side to row 21, and ===== in the middle of row 21. Any key clears it; ENTER at the message brings the prompt back.

The court

Change One Thing

  • Change BATROW to 15. What moves, and what does not?
  • Change paddle.left to 37. Where is the bat, and is anything wrong with it? (S29 stops this happening.)
  • Take cls40() out of draw_court. Run it twice.

Exercises

27.1 Add a #define BOTTOMROW 22 and a draw_message(s) that prints a string at the left of that row, clearing the row first, and use it for the "Press ENTER" message.

27.2 Make the side walls a different character from the top, with a #define for each.

27.3 Draw the court in colour: tcol before the walls and again before the bat, and back to 0xF4 before the message.

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
duplicate declaration - storage class mismatch at a variableIts name is a structure member's. Rename one.
The bat is drawn but the right wall has a gappaddle.left + width reached column 39 and overwrote it.
The screen is not cleared after the programcls40() after the rawin, or the program stopped before it.
Keys left at the promptThe gets at the end is missing.

Summary

#defines for every number, a bat structure with members named for it alone, a draw_ function for each thing, and a main that sets up, draws, waits and tidies. The game loop goes in the gap.

Next

S28, The Ball Moves - a ball structure with a direction, a tick, bouncing off three walls, and what happens at the bottom.

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.