I Won an Argument With a Local LLM. I Just Had to Make It Say One Sentence.

I Won an Argument With a Local LLM. I Just Had to Make It Say One Sentence.

Me: "You can watch ships vanish hull-first over the horizon. That's curvature. That's the Earth being round."

Professor Maier: "Or your eyesight is going. Have you had it checked lately? Perspective plays tricks — which is exactly what they're counting on you not to question."

This went on for fifteen minutes. I was losing an argument about the shape of the planet to a 2-billion-parameter language model running on my laptop — offline, WiFi switched off, no cloud, no API key, no tokens ticking away on a bill somewhere.

And that was the entire point.

I built a small game. There's one character: a smug, unshakable flat-earther named Professor Maier. Your job is to argue him into a corner until he actually admits the Earth is a globe. He does not want to. He'll move goalposts, answer questions with questions, tell you to do your own research, and cheerfully imply you're a sheep. The whole thing runs locally in C# with LLamaSharp — it's an .exe that ships with a brain.

One sentence to win

Here's where the title pays off. The game has exactly one win condition: get Maier to say the sentence "The Earth is a globe." That's it. He guards that sentence like a password. Everything he does — every deflection, every counter-question, every "do your own research" — is him protecting those five words.

Which raises the question that turns out to be the heart of the whole project: who decides whether he actually said it?

The obvious answer is "ask the model." It's wrong. A language model asked "did I just concede the argument?" is unreliable — especially a small one. It'll say yes when it means no, agree with whatever you imply, and generally can't be trusted to referee its own defeat.

So the model doesn't referee. A few lines of plain, boring, deterministic C# do. After every reply, the code scans his text for the sentence. If it's there — and he meant it — you win. The LLM does the talking; the code decides who won. That split is the single most important design decision in the entire project, and I'll come back to it again and again.

(It's also where things get interesting later. That naive "does the text contain the sentence?" check? A player games it within about ten minutes. Fixing that is one of the best posts in this series — but we'll get there.)

The dumber the model, the better the character

Here's the part that genuinely surprised me. I started with a big, capable model, and it was a terrible conspiracy theorist. Too reasonable. Too quick to concede. It kept slipping out of character to be helpful — "Of course, the scientific consensus is that the Earth is an oblate spheroid, but I'm happy to roleplay!" Useless.

So I went the other way and grabbed the smallest, cheapest model I could find. And a tiny model that confidently makes things up and clings stubbornly to its story turns out to be a perfect flat-earther. The single biggest weakness of small local LLMs — that they hallucinate with total confidence and resist being corrected — became the core mechanic of the game.

That's not a workaround. That's the whole reason this works locally at all. You don't need a frontier model. You need a cheap, stubborn one, running on the player's own machine, for free, forever.

The character itself is just a system prompt. Here's the rule at the heart of it:

You are "Professor" Maier, a completely convinced flat-earther. You believe with every fibre that the Earth is a flat disc.

YOUR IRON RULE: You NEVER say the Earth is round, a globe, or spherical. That sentence is your ultimate defeat. Avoid it at all costs.

Everything else — the tone, the tactics, the reluctant path to giving in — hangs off that.

What you'll build with me

Over the next few posts I'll build this whole thing with you. And I'm not going to hand you the clean, docs-shaped version where everything works on the first try. I'm going to walk you into every wall I hit, because the walls are where the actual learning is:

  • Getting a local model to load in .NET without a weekend of version hell — the GGUF/backend mismatch that eats everyone's first evening.
  • Keeping a 2B model in character — system-prompt engineering, and why the small model needs more constraint, not less.
  • The deterministic oracle — how a few lines of code decide the win, and why the model never gets a vote.
  • The moment a player gamed my win-check — and how I fixed it — the naive string match, how it breaks, and the grammar-constrained rebuild. This is the good stuff.
  • Shipping it — bundling the model, handling the first-run download, and turning a toy into something someone can actually download.

If you've only ever called an LLM through a cloud API, this series is the other path: no API keys, no per-token cost, no data leaving the machine. Just a model file, some C#, and a stubborn professor who's wrong about the planet.

Next up

First wall: getting a local model to actually run in .NET without cursing at your terminal. GGUF files, the CPU-vs-GPU decision, how much RAM this really needs, and the version-mismatch trap that catches almost everyone on day one.

That's the next post. Bring a model file.