When I tell people I'm building a game in Ruby, I get looks.
"Ruby? For a game? Isn't that... slow?"
Fair question. Most game developers reach for C++, C#, or Unity. Ruby is for web apps, not games. Everyone knows this.
Except I've been building a roguelike – a procedurally generated dungeon crawler inspired by the 1980s classic Rogue – in Ruby for six months now, and it's been brilliant.
What I'm Building
Think classic dungeon crawler: ASCII graphics, procedurally generated mazes, turn-based movement, permadeath. Your character (@) navigates randomly generated dungeons, fighting monsters, collecting items, trying not to die.
It runs entirely in the terminal. No fancy graphics. Just pure game logic and procedural generation.
The Unconventional Choice
I'm not a game developer by training. I build web applications and APIs. So choosing Ruby for a game felt both natural and slightly mad.
But here's the thing: I wasn't building a AAA title. I was building a turn-based game that runs in the terminal. My bottleneck wasn't performance. It was understanding game architecture.
For that, Ruby's clarity was perfect.
Rapid Iteration Changed Everything
No compilation step. No waiting. Just change the code and run it.
I implemented four different maze generation algorithms: Binary Tree, Aldous-Broder, Recursive Backtracker, and Recursive Division. Here's how simple the Binary Tree algorithm looks:
class BinaryTree < AbstractAlgorithm
def self.on(grid)
grid.each_cell do |cell|
has_north = !cell.north.nil?
has_east = !cell.east.nil?
if has_north && has_east
cell.link(cell: rand(2) == 0 ? cell.north : cell.east, bidirectional: true)
elsif has_north
cell.link(cell: cell.north, bidirectional: true)
elsif has_east
cell.link(cell: cell.east, bidirectional: true)
end
end
grid
end
end
Look at that. grid.each_cell reads like English. The logic is clear: randomly connect each cell either north or east. I could tweak this, run it, see results immediately. No fuss.
When you're learning game architecture – which I was – this feedback loop is invaluable.
Components Stay Simple
The core of my game uses Entity-Component-System (ECS) architecture. Components should be pure data containers, and Ruby makes this trivial:
class PositionComponent < Component
attr_reader :row, :column
def initialize(row:, column:)
super()
@row = row
@column = column
end
def set_position(row, column)
@row = row
@column = column
end
end
That's my entire PositionComponent. No boilerplate. No getters and setters cluttering the code. Ruby's attr_reader handles it. Named parameters make it obvious what you're passing:
position = PositionComponent.new(row: 5, column: 10)
Compare this to languages where you need builder patterns just to maintain readability. Ruby gets out of your way.
What About Performance?
Let's be honest: Ruby isn't fast.
For my use case? Didn't matter. The game is turn-based. It waits for player input. The bottleneck is human reaction time, not Ruby's execution speed.
Maze generation on an 80×40 grid? Milliseconds. Entity queries with dozens of entities? Trivial. If I were building a real-time action game with hundreds of entities updating 60 times per second, Ruby would be the wrong choice.
But I wasn't. Context matters.
Developer Joy Matters Too
Here's what I didn't expect: Ruby made me happy while coding.
When I write entity.has_component?(:position), it reads like a question I'd ask a colleague. The code communicates intent clearly. I could focus on understanding ECS, event systems, and procedural generation rather than fighting with syntax.
When you're building something complex in your spare time, enjoyment matters. If I'd chosen a language I found frustrating, I might have abandoned the project.
The Result
The game now has procedurally generated mazes, an Entity-Component-System architecture, event-driven logging, command-based input handling, and 48 spec files of test coverage.
Not bad for a language supposedly "not meant for games."
Want the full story? I wrote a comprehensive article on my blog covering the complete architecture, testing strategy, and lessons learned: Why I Chose Ruby to Build a Roguelike Game
The code is open source on GitHub if you want to see how it all fits together.
P.S. – I documented this entire journey in a book: Building Your Own Roguelike: A Practical Guide. It walks through building this from scratch – the ECS pattern, event systems, maze generation algorithms, and everything you see in Vanilla Roguelike.
Thank you for reading,
David Silva
Top comments (0)