tci_printf is functionally identical to printf — the output is
the same. The difference is that it writes to a file descriptor
directly, without the standard I/O library.
This page uses it exclusively: the prize ladder, the highlighted
current level, the question text, the option letters, the audience bar
chart, the win screen — all of it goes through tci_printf. Not because
printf would fail, but because this is what we built.
This page implements the three display functions and the three exit screens.
ANSI escape codes[1]
The terminal interprets sequences starting with \033[ as formatting
instructions rather than printable text. \033 is the escape character
(ASCII 27); the [ that follows opens the sequence. The terminal acts
on it silently — the sequences themselves never appear on screen.
Three sequences are used in this game:
| Sequence | Effect |
|---|---|
\033[33m | Foreground yellow |
\033[32m | Foreground green |
\033[0m | Reset all formatting |
A string wrapped between \033[33m and \033[0m appears yellow in
the terminal. Everything after \033[0m returns to the default colour.
display_ladder
The ladder renders from level 15 at the top to level 1 at the bottom.
The current level is highlighted in yellow. Safe levels get a *
prefix in green:
void display_ladder(int level, int safe_level)
{
int i;
tci_printf("\n");
for (i = LEVELS - 1; i >= 0; i--) {
if (i == level)
tci_printf("\033[33m");
if (SAFE[i])
tci_printf("\033[32m *\033[0m");
else
tci_printf(" ");
if (i == safe_level && i != level) /* yellow takes priority when both match */
tci_printf("\033[32m");
tci_printf(" %2d: %s", i + 1, PRIZES[i]); /* i + 1: PRIZES is 0-indexed, display is not */
if (i == level || (i == safe_level && i != level))
tci_printf("\033[0m");
tci_printf("\n");
}
tci_printf("\n");
}The colour is opened before the row content and reset after, so each
row is self-contained. When safe_level == -1, the condition
i == safe_level is never true — no valid level index is -1 — so
no green tint is applied and the argument needs no special handling.
display_question
The question block shows the question text, then each option letter and string. Options eliminated by the 50:50 lifeline are skipped:
void display_question(question_t *q, int hidden[4])
{
int i;
const char letters[] = "ABCD"; /* indexed by i to produce the option letter */
tci_printf("%s\n\n", q->text);
for (i = 0; i < 4; i++) {
if (!hidden[i])
tci_printf(" %c. %s\n", letters[i], q->opts[i]);
}
tci_printf("\n");
}letters[0] is 'A', letters[1] is 'B', and so on — a string
literal used as a lookup table. The same technique appears in
display_audience below with "ABCD"[i].
hidden[i] = 1 means option i is not displayed. The correct answer
is never hidden; handle_lifeline in the next page ensures this.
display_audience
The audience lifeline shows a fake percentage bar chart weighted
toward the correct answer. The percentages are generated with rand()
and scaled to sum close to 100:
void display_audience(question_t *q)
{
int pcts[4];
int total;
int scaled;
int i;
int j;
for (i = 0; i < 4; i++)
pcts[i] = 2 + rand() % 8; /* 2–9 raw points for wrong answers */
pcts[q->answer] = 58 + rand() % 16; /* 58–73 raw points for the correct answer */
total = 0;
for (i = 0; i < 4; i++)
total += pcts[i];
tci_printf("\nAsk the Audience:\n\n");
for (i = 0; i < 4; i++) {
scaled = (pcts[i] * 100) / total; /* normalise raw score to percentage */
tci_printf(" %c: ", "ABCD"[i]);
for (j = 0; j < scaled / 3; j++) /* one '#' per 3 percentage points */
tci_printf("#");
tci_printf(" %d%%\n", scaled); /* %% prints a literal % */
}
tci_printf("\n");
}The raw scores do not need to sum to 100 — the scaling step handles that. Each raw score is divided by the total and multiplied by 100 to produce the displayed percentage.
Integer division means the displayed values will not always sum to exactly 100. That is acceptable for fake audience data.
Exit screens
Three outcomes end the game: a win, a loss, and a walk away. Each gets a dedicated function.
display_win uses green to mark the top prize:
void display_win(void)
{
tci_printf("\n\033[32m");
tci_printf(" Congratulations! You have won £1,000,000!\n");
tci_printf("\033[0m\n");
}display_loss shows what the player leaves with — the banked safe
level amount, or nothing:
void display_loss(int safe_level)
{
tci_printf("\nWrong answer.\n");
if (safe_level >= 0)
tci_printf("You leave with %s.\n\n", PRIZES[safe_level]);
else
tci_printf("You leave with £0.\n\n");
}display_walkaway confirms the banked prize. The player walks before
answering the current level, so the banked amount is the prize from
the previous level — PRIZES[level - 1]:
void display_walkaway(int level)
{
tci_printf("\nYou walk away with %s.\n\n",
level > 0 ? PRIZES[level - 1] : "£0"); /* level not yet answered */
}level > 0 guards the edge case where the player walks before
answering question 1. At that point level is 0 and PRIZES[-1]
does not exist, so the result is "£0".
Test the display
Add a temporary test to main.c after loading questions to verify
the ladder and question render correctly:
int hidden[4] = {0, 0, 0, 0};
display_ladder(0, -1);
display_question(questions[0], hidden);
free_questions(questions, count);
return (0);Run with a two-question questions.txt:
make re
./game questions.txtThe terminal should show the prize ladder with level 1 highlighted in
yellow, safe levels marked with a green *, and the first question
with all four options visible.
Once the output looks right, remove the test code and restore the stub. The display functions are complete.