Introduction
In our last lesson, you laid the complete architectural and structural groundwork for your capstone project. By designing the data structures and creating a modular file system managed by a professional Makefile, you've prepared the construction site. Today, we'll install the engine that will drive the entire experience: the main game loop.
This lesson will focus on implementing the "heartbeat" of your game. We'll build a continuous cycle that reads user input, processes it, and updates the world accordingly. This is the central mechanism of not just text adventures, but virtually all interactive software. You will learn how to safely capture player commands, parse them into meaningful parts, and call placeholder functions—or "command handlers"—that will eventually contain your core game logic.
The Anatomy of a Game Loop
At its core, a game loop is a simple, repeating sequence of operations that creates the illusion of a dynamic, responsive world. For most games, this cycle can be broken down into three fundamental phases.

- Process Input: The game gathers all external input that has occurred since the last cycle. In a graphical game, this could be mouse clicks, joystick movements, or key presses. For our text adventure, it's simpler: reading a line of text typed by the player.
- Update Game: Based on the input, the game's internal state is modified. If the player typed "go north," the game's data structures are updated to change the player's current location. If they typed "take sword," the sword item is moved from the room's inventory to the player's inventory.
- Render: The results of the update are displayed to the player. For a 3D game, this means drawing complex scenes to the screen. For us, it means printing text to the console, such as "You are now in the dark forest," or "You picked up the rusty sword."
This cycle repeats indefinitely until the player triggers a condition to exit the loop, such as typing "quit".
To see this concept explained with a code-oriented perspective, watch the first part of the following video.
Game Loops Explained in 5 Minutes (With Code)
This video, "Game Loops Explained in 5 Minutes," provides a concise and clear overview of the loop's structure.
Watch from the beginning to the end of the analogy to solidify your high-level understanding of the input-update-render cycle. Then, jump to the C code implementation section from here to see how this translates into a simple while loop.
Implementing the Loop in C
With the conceptual model in mind, let's translate it into C code for our text adventure. We will be working primarily in your main.c and game.c files. The guide "How to Program a Text Adventure in C" provides an excellent, practical implementation that we can adapt.
This chapter from a text adventure tutorial provides the exact structure we need for our main loop and a simple command parser.
First, read the introduction and study the code provided for main.c. Pay close attention to how the while loop is constructed and how it calls both getInput() and parseAndExecute(). Next, examine the code for parsexec.c. This is the core of our command handling. Focus on how it uses strtok() to split the input into a verb and a noun, and then uses strcmp() to decide which action to take.
Let's break down the implementation into two key parts: getting input and parsing it.
Part 1: Getting Safe User Input with fgets
As the resource demonstrates, fgets() is the function of choice for reading player commands. Unlike scanf(), it handles spaces gracefully (allowing for commands like "look at chest") and, most importantly, it's buffer-safe. It requires you to specify the maximum number of characters to read, which prevents the user from crashing your program by typing an unexpectedly long command.
However, fgets() has one important quirk: it stores the newline character (\n) in the buffer when the user presses Enter. This can cause problems. For example, if the user types quit and presses Enter, the buffer will contain "quit\n". A subsequent call to strcmp(buffer, "quit") would fail.
We need a reliable way to remove this trailing newline.
Remove Trailing Newline Character From fgets() Input | C Programming Example
The video "Remove Trailing Newline Character From fgets() Input" offers a detailed explanation of this exact problem and a common solution.
Watch from the introduction of fgets to see a visual demonstration of how the newline character ends up in your string. Then, review the technique presented from here for removing it by overwriting it with a null terminator.
The video proposes one way to solve this: string[strlen(string) - 1] = '\0';. This works, but can fail if the user enters an empty string. A more robust method that doesn't have this issue is to use the strcspn function from <string.h>. It finds the first occurrence of any character from a given set.
The line input[strcspn(input, "\n")] = '\0'; will find the index of the first newline character in input and replace it with a null terminator, effectively chopping it off. We will use this method in our implementation.
Part 2: A Simple Parser with strtok and strcmp
Once you have a clean input string, you need to parse it. As shown in the parsexec.c example from the reading, strtok() is a standard C function for tokenizing a string—breaking it into smaller pieces based on a delimiter.
Calling strtok(input, " \n") the first time will find the first "word" (the verb) before a space or newline. Subsequent calls with NULL as the first argument, like strtok(NULL, " \n"), will continue searching the same original string to find subsequent tokens (the noun).
Once you have the verb as a token, you can use strcmp() to compare it against a list of known commands. This forms a simple but effective command processing system.
Your Implementation Task
Now, let's bring these pieces together in your project files.
-
In
game.h, declare the function that will handle parsing:#ifndef GAME_H #define GAME_H #include <stdbool.h> bool parseAndExecute(char* input); #endif // GAME_H -
In
game.c, create the implementation forparseAndExecute. For now, it will just print messages. This function will returnfalseonly when the "quit" command is issued, signaling to the main loop that it's time to terminate.#include <stdio.h> #include <stdbool.h> #include <string.h> #include "game.h" bool parseAndExecute(char* input) { char* verb = strtok(input, " \n"); char* noun = strtok(NULL, " \n"); if (verb != NULL) { if (strcmp(verb, "quit") == 0) { return false; } else if (strcmp(verb, "look") == 0) { printf("You are looking around.\n"); } else if (strcmp(verb, "go") == 0) { if (noun != NULL) { printf("You want to go %s.\n", noun); } else { printf("Go where?\n"); } } else { printf("I don't know how to '%s'.\n", verb); } } return true; } -
In
main.c, implement the main loop that ties everything together.#include <stdio.h> #include <stdbool.h> #include <string.h> #include "game.h" int main() { char input[100]; printf("Welcome to Little Cave Adventure.\n"); while (true) { printf("\n--> "); if (fgets(input, sizeof(input), stdin) == NULL) { // Handle EOF (Ctrl+D on Unix, Ctrl+Z on Windows) break; } // Remove trailing newline character input[strcspn(input, "\n")] = 0; // Parse and execute the command if (!parseAndExecute(input)) { // 'quit' command was issued break; } } printf("\nBye!\n"); return 0; }
After adding this code, navigate to your project directory in the terminal and run make. Your Makefile from the previous lesson will compile everything. Then, run your executable (./adventure). You should be able to type commands like look, go north, and quit and see the appropriate responses.
Conclusion
You have now implemented the beating heart of your game. This main loop provides the fundamental structure upon which all future logic will be built.
Key Takeaways:
- The game loop consists of three phases: Process Input, Update Game, and Render.
- In a text adventure, this translates to reading a command, modifying game data, and printing a response.
fgets()is the preferred function for safely reading a full line of user input.- The trailing newline character from
fgets()must be removed before string comparisons. Theinput[strcspn(input, "\n")] = 0;pattern is a robust way to do this. strtok()can be used to tokenize user input into a verb and noun.- A series of
if-else ifstatements withstrcmp()forms a basic but functional command parser.
Our current command handlers are simple printf statements. They acknowledge the command but don't affect any underlying game state. In our next lesson, we will begin creating that state. We'll implement the data structures for the game world, linking rooms together to form a map, so that a command like go east can actually move the player to a new location.