Overview
RISC-V Snake is a simple snake clone written to run in the Linux terminal. It was made over the span of two days as a fun personal challenge. I wanted to gain a deeper understanding of RISC-V and how it is used in real-time applications (like video games!). The project features the complete snake game loop with snappy WASD directional controls, apple spawning, collision detection, and score keeping. My professors still show off this project to new students and use it as an example of going beyond the course material.
Technical Approach
Since this project was written in assembly and runs directly in the terminal, I did not have an engine, framework, or graphics library to lean on. Most of the work came down to building the pieces that a normal game framework would usually hide from you: input, timing, drawing, game state, and cleanup.
The first problem I had to solve was real-time input. By default, terminal input waits for the user to press enter and echoes typed characters back to the screen, which obviously does not work very well for Snake. To get around that, I used Linux syscalls to read and modify the terminal settings. At startup, the game saves the original terminal flags, disables echo and canonical input mode, and sets standard input to nonblocking mode. That lets the game check for a single WASD keypress every frame without stopping the whole program or filling the terminal with typed characters. When the player quits or gets a game over, the game restores the original terminal settings before exiting.
The main game loop runs at a fixed refresh rate by sleeping for 150 milliseconds between frames. Each frame checks for input, updates the current movement direction, shifts the snake body forward, moves the head, and checks for wall collisions, self collisions, and apple collisions. When the snake eats an apple, the game increases the snake length and uses the Linux getrandom syscall to choose a new apple position inside the board.
Rendering is handled entirely as text output. Each frame clears the terminal, then constructs the full board out of ASCII characters: # for the border, O for the snake, @ for the apple, and . for empty spaces. Instead of writing every character individually, the game builds the whole frame into a buffer on the stack and writes the screen out all at once. That made the display cleaner and helped the game feel much closer to a real-time game than I expected from a terminal assembly project.
Lessons Learned
This project forced me to understand how much the terminal and operating system are usually doing for you. Something as simple as "read input every frame" turned into learning about Linux syscalls, terminal flags, nonblocking input, input flushing, and why restoring state matters when your program exits. The core Snake rules were familiar, but implementing them in RISC-V made every small system feel much more deliberate.
It also gave me a much better appreciation for low-level game loops. I had to think about timing, input polling, state updates, rendering, memory layout, and cleanup without relying on any higher-level tools. It was a small project, but it made the connection between computer organization and game programming feel very real. I am glad I took it on, and it remains one of my favorite "I wonder if I can actually do this" projects.