No new tools are needed for this chapter. The toolchain from
f05/00
covers everything: gcc, make, valgrind.
Project structure
g01a is the first chapter that is not a library — it is a program that
depends on one. The project has a libtci/ subdirectory that carries
the full libtci and libtciutil source:
g01a-practice/
├── libtci/
│ ├── Makefile
│ ├── libtci.h
│ ├── libtciutil.h
│ ├── tci_bzero.c
│ └── ... (all source files)
├── Makefile
├── game.h
├── main.c
├── load.c
├── display.c
└── game.cThe top-level Makefile drives the build. $(MAKE) -C libtci descends
into the subdirectory and runs its own Makefile to produce libtci.a
and libtciutil.a. The game then links against both.
This is the standard pattern for a C project with a local library dependency — the same mechanism used by larger projects that vendor a third-party library alongside their own code.
Clone the companion repo to get everything in place:
git clone https://github.com/thecodingidiot-com/g01a-the-developer.git g01a-practice
cd g01a-practiceBuild the libraries and confirm there are no warnings:
make -C libtci relibtci.a and libtciutil.a should appear inside libtci/ with no
warnings. You only need to do this once — subsequent make re calls
at the top level build the library only if its sources have changed.
Create the game files
Create four empty source files and the shared header:
touch main.c load.c display.c game.c game.hCreate a questions.txt placeholder — the real questions come in the
final page:
touch questions.txtWrite game.h
game.h is the shared header for all four source files. It defines
question_t and declares every function across the modules:
#ifndef GAME_H
#define GAME_H
#include "libtci.h"
#include "libtciutil.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#define LEVELS 15
typedef struct {
char *text;
char *opts[4];
int answer;
char *hint;
} question_t;
/* load.c */
question_t **load_questions(const char *path, int *count);
void free_questions(question_t **questions, int count);
/* display.c */
void display_ladder(int level, int safe_level);
void display_question(question_t *q, int hidden[4]);
void display_audience(question_t *q);
void display_win(void);
void display_loss(int safe_level);
void display_walkaway(int level);
/* game.c */
void game_loop(question_t **questions, int count);
#endifWrite the Makefile
The game compiles four source files and links the two libraries:
NAME = game
CC = gcc
CFLAGS = -Wall -Wextra -g -std=c99 -I libtci
SRCS = main.c load.c display.c game.c
OBJS = $(SRCS:.c=.o)
LIBS = libtci/libtci.a libtci/libtciutil.a
.PHONY: all clean fclean re
all: $(NAME)
$(NAME): $(OBJS) $(LIBS)
$(CC) $(CFLAGS) $(OBJS) $(LIBS) -o $(NAME)
$(LIBS):
$(MAKE) -C libtci
%.o: %.c game.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
$(MAKE) -C libtci clean
rm -f $(OBJS)
fclean: clean
$(MAKE) -C libtci fclean
rm -f $(NAME)
re: fclean all-I libtci tells the compiler to search libtci/ for headers, so
#include "libtci.h" in game.h resolves to libtci/libtci.h
without changing the include directive. $(LIBS) lists the archives by
path; Make builds them by descending into libtci/ when they do not
yet exist. clean and fclean propagate into the subdirectory so
make fclean leaves the tree completely empty.
The %.o: %.c game.h rule rebuilds every object if game.h changes.
Add stubs
Paste a minimal stub into each source file so the project compiles from the start.
main.c:
#include "game.h"
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
return (0);
}load.c:
#include "game.h"
question_t **load_questions(const char *path, int *count)
{
(void)path;
*count = 0;
return (NULL);
}
void free_questions(question_t **questions, int count)
{
(void)questions;
(void)count;
}display.c:
#include "game.h"
void display_ladder(int level, int safe_level)
{
(void)level;
(void)safe_level;
}
void display_question(question_t *q, int hidden[4])
{
(void)q;
(void)hidden;
}
void display_audience(question_t *q)
{
(void)q;
}
void display_win(void)
{
}
void display_loss(int safe_level)
{
(void)safe_level;
}
void display_walkaway(int level)
{
(void)level;
}game.c:
#include "game.h"
void game_loop(question_t **questions, int count)
{
(void)questions;
(void)count;
}Compile:
make reZero warnings. The binary does nothing yet — that changes page by page.
Get the tester
git clone https://github.com/thecodingidiot-com/g01a-the-developer.git
cp g01a-the-developer/test.sh ~/g01a-practice/Leave the clone in place. You will run bash test.sh after the final
page.
What the finished game looks like
When the implementation is complete, a run looks like this:
15: £1,000,000
14: £500,000
13: £250,000
12: £125,000
11: £64,000
* 10: £32,000
9: £16,000
8: £8,000
7: £4,000
6: £2,000
* 5: £1,000
4: £500
3: £300
2: £200
>> 1: £100
Which command lists files and directories in a Unix terminal?
A. ls
B. dir
C. list
D. show
[1] 50:50 [2] Phone [3] Audience [W] Walk away
Your answer (A-D): The >> marker and yellow highlight are the same level. The * marks
safe levels. The game renders this with tci_printf — no other output
function is used anywhere in the display code.