Skip to main content

Build a Tetris-Style Game in C with Gemini CLI

·3078 words·15 mins
Emiliano Fernández Cervantes
Author
Emiliano Fernández Cervantes
I build things where hardware meets software: Verilog architectures, biomedical instrumentation, and a home lab that keeps growing.

What if you could describe a game in plain language, press enter, and watch an AI agent write and debug C code until something playable appeared in your terminal?

You can, and an afternoon is enough. With a CLI coding agent, a C compiler, and the ncurses library, you can build a Tetris-inspired falling-blocks game that runs natively in your terminal, in a single session. You do not need to be a C programmer to begin, and I was not one either: my own hand-written tetris.c never made it past the compiler, and that failure is where this project actually started. By the end you will have three things worth keeping: a binary you compiled yourself, a readable C codebase you can pick apart line by line, and a workflow you can reuse on the next idea you have.

That is exactly what this guide walks you through.

Tetris-style game running in a terminal window, with a 10 by 20 playfield drawn in ASCII, a stack of colored blocks at the bottom and a magenta T piece falling above it
The finished game mid-play: the 10 by 20 playfield bordered with | and -, empty cells as dots, placed pieces as colored brackets, and the score beside the board, all drawn by ncurses.

Update, August 2026. This post documents a session I ran in February 2026 with Gemini CLI. At Google I/O on May 19, 2026, Google announced that it is consolidating its developer tooling under the Antigravity brand and retiring the standalone Gemini CLI, and on June 18, 2026 Gemini CLI stopped serving requests for free users and for Google AI Pro and Ultra subscribers. Organizations on a Gemini Code Assist Standard or Enterprise license are unaffected. The successor is Antigravity CLI, and Step 1 below gives its current install command. I have left the rest of the post exactly as it happened, because what transfers to the new tool is the workflow, not the specific binary.


A game that shaped history
#

In 1984, a Soviet computer scientist named Alexey Pajitnov sat down at an Electronika 60 terminal and wrote a program in Pascal. The concept was elegant: seven geometric pieces fall from the top of the screen, you rotate and place them to fill rows, and completed rows disappear. He called it Tetris.

That program became one of the most widely played games in history, sold across every platform imaginable for four decades. Although the idea is simple to describe, building even a working clone back then meant handling careful timing loops, terminal rendering, rotation math, and hours of patient debugging. It was real, respectable engineering work.

Today you can prompt an AI agent to produce equivalent C code in a single session. That contrast is the actual subject of this project: forty years of engineering progress, compressed into one conversation with an agent.

You are allowed to build this. The mechanics of falling-block games (the piece shapes, the gravity, the line-clear logic) are not copyrightable. What makes Tetris Tetris as a brand (the trademarked name, the logo, the Korobeiniki music) belongs to The Tetris Company. What you are building here is an independent, Tetris-inspired clone, and the educational and engineering value is identical.


Where this project actually started
#

Before any of this was a guide, it was a file that would not compile.

I was learning C at the time, so I wrote my own tetris.c by hand. It did not build, and after a while of staring at the errors I still could not see why. My first instinct was not to delete it and let an agent start over, it was to ask for a diagnosis, so I opened Gemini CLI in that directory and typed exactly this:

hi, I was trying to do the tetris game but coul not compile it. Could you please check the code for any errors? @tetris.c

Typos and all. The @tetris.c at the end is how Gemini CLI pulls a file into the conversation, so the agent read my actual code rather than a description of it.

The review was honest, and it was not the answer I was hoping for: the file was a mess. I agreed with that assessment in my very next message. Fourteen minutes after asking for a code review, I deleted my own attempt and asked the agent to start from zero.

That decision is worth naming, because it is a judgment call engineers make constantly and rarely talk about: knowing when to keep repairing a draft and when a clean rewrite is simply the faster path. If you are starting this project with a broken attempt of your own, you are not behind. You are exactly where I was.


What you will need
#

  • A Linux terminal or WSL2 on Windows 11
  • GCC (the C compiler): sudo apt install gcc
  • The ncurses development library: sudo apt install libncurses-dev
  • A CLI coding agent: Antigravity CLI today, Gemini CLI in the session this post describes
  • An account to authenticate the agent on first launch (Gemini CLI asked for a free Google account; follow whatever sign-in flow your tool prompts for)

ncurses is what makes a terminal behave like a screen instead of a scrolling log. It gives you cursor positioning, keyboard input without waiting for the enter key, and color pairs, which is everything a falling-blocks game needs and nothing more.

If you are setting up WSL2 for the first time, the first steps of my post on deploying a private AI with Ollama cover the whole installation process.


Step 1: Install a CLI coding agent
#

The current tool is Antigravity CLI, Google’s successor to Gemini CLI. It is a rewrite in Go, and it can run several agents in parallel in the background. It installs from a single script.

On macOS, Linux, or WSL:

curl -fsSL https://antigravity.google/cli/install.sh | bash

On Windows, from PowerShell:

irm https://antigravity.google/cli/install.ps1 | iex

Then follow the tool’s own first-run instructions to authenticate.

For the record, and not as a step to follow, the agent I used in February 2026 was Gemini CLI, running on Gemini 2.5 Pro. That session’s own log does not record a model, but every Gemini CLI session on my machine from that period ran on Gemini 2.5, and four other sessions from that same day logged 2.5 Pro. The CLI did occasionally fall back to Gemini 2.5 Flash back then, so take that as the model the tool was serving me at the time rather than a field I can point at in the record.

Gemini CLI shipped as an npm package and needed Node.js 18 or later:

npm install -g @google/gemini-cli   # retired on June 18, 2026
gemini

Signing in with a free Google account was all it took back then. That is the path that stopped working, so if you have an older tutorial open in another tab, this is why nothing comes back.

Either way, what you land in is an interactive terminal session with the agent. It works like a chat interface with hands: you describe what you want, and the agent reasons, writes code, and runs commands on your behalf.


Step 2: Ask for the game
#

Here is the prompt that produced the entire game. It is one sentence, and I am quoting it exactly as I typed it:

thank you but you are right, this code was a mess. Could you create the tetris game in c instead? I deleted the previous file so please start over

That is the whole specification. I did not name a library, a board size, a control scheme, a scoring rule, or a compile command. What came back was a single C file of 234 lines, written and compiled by the agent itself, and every design decision in the list below belongs to the agent rather than to me:

  • ncurses as the rendering layer, a single file with #include <ncurses.h>, linked with -lncurses.
  • A 10 by 20 playfield (BOARD_WIDTH 10, BOARD_HEIGHT 20) with borders drawn from | and -.
  • All seven tetrominoes with four rotation states each, held in one const int PIECES[7][4][4][4] lookup table instead of computed rotation math.
  • Arrow-key controls: left and right to move, up to rotate clockwise through (rotation + 1) % 4, down to soft-drop, plus q or Q to quit.
  • Seven init_pair() color pairs, one per piece: cyan, yellow, magenta, green, red, blue, and white.
  • Gravity on a fixed interval: the main loop sleeps 20 ms per pass and drops the piece once its counter passes 20, so roughly 400 ms per row, and it never accelerates.
  • Quadratic scoring: score += lines_cleared * lines_cleared * 100, so four rows cleared in one placement are worth 4 × 4 × 100 = 1,600 points, against the 4 × 100 = 400 you would collect clearing them one at a time.

Those are good defaults, and that is the genuinely interesting part. ncurses is the natural choice for a terminal game, 10 by 20 is the standard playfield, arrow keys are what a player will reach for first, and rewarding multi-line clears is what gives the game its risk-and-reward tension. A one-line request landed on conventions it would have taken me a while to research.

The trade-off still runs in one direction, though: whatever you leave unsaid, the agent decides for you, and it decides quietly. My own game is the proof. Gravity never speeds up, so there is no difficulty curve, and there is no next-piece preview and no hold slot. None of that is a bug. They are simply features I never asked for.

So the practical rule is not “be specific or it will fail,” because vagueness clearly did not fail here. It is this: be specific about the things you actually care about. If you want a wider board, WASD controls, wall kicks on rotation, or a speed that ramps with the score, name them in the prompt. Adding the compile command and a line like “make sure it builds and runs” is worth it too, because it turns a text-generation task into a verifiable one. Everything else you can happily delegate.

Whatever you choose to spell out, submit the prompt and let the agent work.


Step 3: Watch the debugging loop
#

This is the part worth paying attention to. Gemini CLI does not hand you a block of text to paste somewhere. It wrote tetris.c straight into my project directory and compiled it there, and when the session ended the built binary was sitting next to the source. The unit of work is a program that runs, not a snippet you still have to assemble.

The loop I can document in detail is the first one, the one that ran on my broken file. I handed the agent code that would not compile, it read the real file, it explained what was wrong with it, and the conclusion we arrived at was that a rewrite would get me to a working game faster than a repair would. That is the same cycle any developer runs: read the failure, reason about the cause, weigh a fix against a redesign, and then act. The difference is that it played out in minutes instead of over an evening.

How many attempts the agent needed on its own rewrite, I honestly cannot tell you, because my log preserves only my side of the conversation. What I can tell you is what ended up on disk: one self-contained C file that builds cleanly with gcc tetris.c -o tetris -lncurses and a playable game.

What I can measure exactly is the clock, and it is the number most people want when they ask what an agent is worth. From my first prompt to a compiled, playable binary the whole thing took 24 minutes and 44 seconds. The rewrite on its own, from the “start over” message to a program that ran, took 11 minutes and 19 seconds, and the binary appeared fifteen seconds after the source file did. Those figures come from evidence rather than memory: the two prompt timestamps are in the Gemini CLI log at ~/.gemini/tmp/<hash>/logs.json (06:35:59 and 06:49:24 UTC), and the two file timestamps are the modification times of tetris.c (07:00:28) and of the binary next to it (07:00:43). The log runs on UTC and my machine was eight hours behind it, which is why the log says February 2 and this post is dated the evening of February 1. So I can tell you precisely how long it took without being able to tell you how many tries it took, and both halves of that are worth saying out loud.

Moreover, reading the code that comes back is one of the most direct ways to pick up C and ncurses concepts. Tracing how is_valid_position() guards every move, how place_piece() shifts rows down after a clear, and how the color pairs are attached to piece types teaches more than a chapter on syntax would, and the learning happens as a natural side effect of building the project. If you want to read the whole file before you write a line of your own, that session’s output is public at EmilianFC20/tetris-in-c under an MIT license, committed exactly as the agent produced it and left unpolished on purpose. The compiled binary is deliberately not in there, because compiling it is your part.

Syntax-colored listing of lines 7 to 31 of tetris.c, showing the board defines and the opening entries of the PIECES lookup table
Lines 7 to 31 of the generated tetris.c: the BOARD_WIDTH, BOARD_HEIGHT and PIECE_SIZE defines, and the start of the PIECES table with the I, O, T and S tetrominoes and their four rotation states each.

Although the loop is fast, you are still the engineer in the room. The agent’s job is to produce a candidate. Yours is to read it, run it, and decide whether it actually does what you asked.


Step 4: Compile and play
#

In my session the agent had already written tetris.c and compiled it before it handed the work back, so there was nothing left to build. If your agent stops at the source file, or if you are working through this by hand, this is the command:

gcc tetris.c -o tetris -lncurses

The -lncurses flag is the one people forget. It tells the linker to link your program against the ncurses library, and without it the code compiles fine and then fails at the link stage with undefined references to functions like initscr.

If the compiler cannot find ncurses.h, install the development headers first:

sudo apt install libncurses-dev

Then run the game:

./tetris
Terminal transcript showing gcc compiling tetris.c without output, an ls listing with the tetris binary next to its source, and the game printing its Game Over line
The whole build and run cycle: gcc returns silently, ls -l shows the 17320-byte tetris binary next to the 7089-byte tetris.c, and ./tetris ends by printing the final score.

Controls:

KeyAction
← / →Move piece left or right
Rotate piece clockwise
Soft-drop (faster descent)
qQuit

Score is calculated by lines cleared per placement. Clearing multiple lines in one move scores significantly more than clearing them one at a time, so it pays to build the board up deliberately for multi-line clears rather than dropping pieces wherever they happen to fit.


What you get out of building it
#

When that game launches in your terminal for the first time, there is a real moment of satisfaction. You described something, and working software appeared.

Underneath that moment, you walk away with three concrete things. First, a program you built and compiled yourself, which is a different feeling from downloading someone else’s binary. Second, a complete, self-contained C codebase you can read as a study text: game state, an input loop, timing, rotation logic, and terminal rendering, all in one file small enough to hold in your head. Third, a repeatable workflow, because nothing about the process was specific to Tetris.

Mine is published at EmilianFC20/tetris-in-c if you would like something to compare yours against, or simply to read the 234 lines and see how far one sentence of English got.

That last point is what makes AI CLI agents a genuine force multiplier. You do not need to master C memory management, terminal rendering APIs, or rotation matrices before you can build something that works. Instead of studying for months and then building, you can start with the result you want, read the generated code to understand what it does, and build a mental model of the language from the inside out. The distance between “I have an idea” and “I have a running program” has never been shorter.

This is exactly what I found when I built this project. I was learning C and exploring Gemini CLI at the same time, and the intersection turned out to be a good one: a concrete goal, a language I was still very much learning, and an agent that could take the first pass at the implementation while I focused on understanding what it produced. Although the finished game is the part you can play, the failed attempt is the part I learned the most from. Writing a file that would not compile, hearing an honest assessment of why, and choosing a clean rewrite over a rescue told me more about where my C actually stood than a working program ever would have. Each setback was simply another layer to look at, and another opportunity to keep improving.

The contrast with 1984 is worth keeping in mind, though. Pajitnov’s work represents a level of dedication and craftsmanship that deserves real respect, and what AI agents offer is not a replacement for that depth. It is a faster on-ramp to the point where you can start building genuine understanding of your own.


What’s next
#

Once your game is running, several directions are worth exploring:

  • Increasing difficulty over time: raise the gravity speed as the score grows, so the game challenges the player to keep improving.
  • Next-piece preview: add a small panel showing the upcoming piece so the player can plan ahead.
  • Hold piece mechanic: let the player park one piece and swap back to it later.
  • Persistent high score: write the best score to a file and display it at startup.
  • Graphical rendering with SDL2: replace ncurses with SDL2 for a proper windowed game with pixel graphics and sound.

Each of these is also a good second exercise in the same workflow: describe the change precisely, let the agent draft it, then read what came back before you accept it.

If you build your own version of this, I would love to hear what you extended or changed.


Tetris® is a registered trademark of The Tetris Company, LLC. This project is an independent educational reimplementation of the falling-blocks game mechanic and is not affiliated with or endorsed by The Tetris Company.