Clown College: Day Dot
Have you ever dreamed of learning shallow and insipid beginner level lessons in C programming from a moronic vibe coder passing off Claude Code output as original content? Me neither. Let's begin:
Our very first lesson will comfortably sail past the fundamentals you should be learning, and instead land somewhere beyond the traditional 'hello world' or 'clone photoshop' introductory programming lessons. I don't know the basics anyway, so it's better for your learning this way.
Let's create a game. It will be small, light weight, and able to run @ 60fps in stunning 800px x 450px resolution on anything more powerful than a Casio G-SHOCK watch. It will also be fun.
Whom amongst us has not wasted several hours on the porcelain throne playing this deviously simple yet challenging classic:
Fact: A small C program is an objectively beautiful thing when closely examined! I will show you that the beauty is both in the simplicity and the flexibility of this wonderful, historically important language. To writ; we will start off by building the simplest version of this game we can. Then we will make it slightly less simple. Then less simple again.
What do we gain with each slightly more complicated rebuild? Read on to find out. Just kidding: I'll spoil it now. We're RAM and binary size ricing. Nowadays we need to make every kb count. How small can we get this thing on disk and at runtime - and what can we learn in the process? How much of this stuff can we get through in a single blog post before I call it wraps? Who knows.
First: let's draw up a basic table that servers as a high level inventory of the things we will need to make for our cave chopper game. Since the game is small, this is easy:
Now we think libraries. We're working in a Debian Linux environment, which means talking to old man X11 if we want to push anything to the screen. X11 is Linux's ancient crustacean of a display architecture with its own vast and dramatic history you should read about elsewhere. When we include Raylib as a dependecy in our game, we get access to a variety of easy to use pre-made functions at the expense of a larger binary size and RAM overhead. Raylib gives us some shortcuts we can take, but has many things we don't need in it too, which we have to carry regardless of whether we use/need them.
Of the three version we make, each version will keep the same 100-ish lines of core game code (cave generation, helicopter physics, collision detection, score keeping) while swapping out different ways of handling system reaching function calls for things like drawing rectangles, creating and closing system windows and receiving key presses.
Let's start with a very simple example of how our approach differs between including Raylib and standard C:
Say we want to check something important: is Mouse 1 being pressed? Raylib gives us the function IsMouseButtonDown, allowing a wonderfully simple single line implementation:
IsMouseButtonDown(MOUSE_BUTTON_LEFT);
Without Raylib, we are instead stuck using standard C libraries like Xlib (a library that talks to X11 in Linux) and will have to work a little harder to detect that same input. We will also see (some) of what Raylib was doing under the hood for us since we have to start doing more of it ourselves.
Task: We create a simple input and event handling loop to continuosly check if the user is interacting with the window (a key press, mouse click, attempt to close) and update the corresponding state variable (we will use a boolean) if an event is detected.
First: Declare and initialise boolean state tracker. This exists outside of the main event loop (which is looking for actual inputs) and constantly remembers/resets the state of the variable 'mouse' to be false (no input):
bool mouse = false
We then do three things using functions from the Xlib library:
1. Create a while loop that polls for events waiting in the display Queue (dpy). 2. Declare a generic x11 event structure (e) to hold incoming events. 3. Pull the next event from the queue and store it in the structure (e) we just made:while (XPending(dpy)) {
XEvent e;
XNextEvent(dpy, &e);
We require some other bits and pieces to make it all work. The input handler gets a way to examine what type of event occured (eg mouse click or key press or window close request). We determine whether the event is a press (true).
switch (e.type) {
bool down = e.type == KeyPress;
Now we check specifically for Mouse Button 1 input using standard C functions calls and set the mouse variable to true if pressed (remember, it will remain false while released due to our 'bool mouse = false' declaration above):
case ButtonPress:
case ButtonRelease:
if (e.xbutton.button ==Button1) mouse = e.type == ButtonPress;
You can consider that mouse now handled. It was a bit more work then the single line function call we had available with Raylib - yet didn't exactly relegate us to the role of writing raw machine code with a blindfold on.
Indeed: Included in standard C there are many libraries with simple and accessible functions we can call. It will just be a matter of needing to call more of them and orchestrate their functions with more deliberateness if we choose to go that route.
On Day 1 of Clown College we will return to this topic, hopefully getting to compile our chopper game, but instead getting distracted, chasing tangents, forgetting the point of the lesson, realising it never had a point in the first place, and continuing on in said fashion.
I do hope you'll join me as we seek to learn computers more good on this journey.
Version 1
- Uses Raylib as a dependecyVersion 2
- Ditches Raylib as a dependency, replaces it with a lower level library for handling those same basic functions: x11
Version 3
- Blursed minimalism. Ditches all dependencies, allocating it's own pixel array as an ass-tier desktop environment and creating it's own draw functions to render pixels - essentially: telling the deskptop graphical environment and all it's associated bloat to hit the road. We're hijacking Linux, booting it into a text-only user space so we can draw pixels directly to framebuffer, cutting out greedy RAM hungry middlemen like Mutter and Wayland. Once we have the frame buffer under control we then rewrite a 99% fat free "we have Raylib at home" to do only the basic system stuff we need like detect key inputs and lock the keep player scores.