Hello! This tutorial will show you how to write a roguelike in the Rust programming language and the libtcod library.
It is based on the Python libtcod tutorial. Getting familiar with Rust is not necessarily as easy as with Python so if you’ve never programmed before, you may want to check out the original instead. That said, Rust is a fine language to start with.
This tutorial does assume some prior programming knowledge and at least basic familiarity with Rust. However, we do talk about potentially foreign concepts when we introduce them, so you should still be able to follow along even if you don’t have a lot of programming experience.
There are two great resources to get started with Rust: The Rust Programming Language book and Rust by Example.
You can find the source of this text as well as the code samples here:
Rust is a systems programming language targeting roughly the same space as C++. That means it’s fast and is generally used for things where you need a fair bit of control over how are things structured in memory, etc. Rust is a good candidate for writing a browser, database, operating system, web server and… games.
What attracted me personally to Rust was the fact that it was reasonably low-level (i.e. I could use it for things where I’d normally go for C or C++), with good support for calling into libraries written in C/C++ but feeling much more modern.
It has a real module system, closures, powerful macros and it protects you from segfaults.
As a wonderful cherry on top, it doesn’t have null pointers or references.
libtcod is a library specifically designed for writing roguelikes.
It deals with rendering ASCII characters in a grid, mouse and keyboard
input and provides useful utilities for path finding and field of view,
a noise toolkit and a name generator.
In other words, it provides a good chunk of what a typical roguelike would need.
Some notes about this port
Python and Rust are very different languages. That means what is sometimes straightforward in one may not be as easy in the other. The tutorial follows the structure of the original, but changes things around where it makes sense.
- No global variables
Changing a global variable in Rust is not safe and it’s rather cumbersome, too. We do use global constants for things like the world size, but instead of global variables we just pass values as function parameters.
- No garbage collection and backreferences
Every value in Python is behind a reference-counted pointer. The original tutorial contains a list of objects that are then moved around at will (e.g. by adding them into the inventory) without affecting the original array. They contain other objects (components) with backreferences that can manipulate the original object.
Such a thing is in general not safe in Rust and while you can use mutable reference-counted pointers, the code becomes cumbersome and even then has limitations the Python version doesn’t.
To deal with this, we are using a contiguous block of memory (
Vec) which holds the game objects directly and we use references into that vector where necessary.
- All in one file
The original Python tutorial is all one file. That’s not something you want to end up with — especially when you’re collaborating on the project — but the final result is still not that big and it makes writing and following the tutorial easier when you don’t have to worry about where each code snippet goes.
This isn’t trying to show the best architecture for roguelikes. It’s an architecture that works. It’s not the most performant or extensible one. But it’s easy enough to follow and use to make more stuff.
The original Python tutorial is a bit infamous because games that people do after going through it all look samey. That’s not a reason to shun the tutorial, though. Just don’t get caught in the game design of it. The purpose of this is to teach you how to write a game using libtcod. There’s little of game design here. What I’d recommend is: go through this, absorb the concepts but then start from scratch when actually writing your game, and think about how to make it unique.
Start the tutorial
- Part 1: Graphics
Start your game right away by setting up the screen, printing the stereotypical @ character and moving it around with the arrow keys.
- Part 2: The object and the map
This introduces two new concepts: the generic object system that will be the basis for the whole game and a general map object that you’ll use to hold your dungeon.
- Part 3: The dungeon
Learn how to code up a neat little dungeon generator.
- Part 4: Field-of-view and exploration
Display the player’s field-of-view (FOV) and explore the dungeon gradually (also known as fog-of-war).
- Part 5: Preparing for combat
Place some orcs and trolls around the dungeon (they won’t stay there for long!). Also, deal with blocking objects and game states, which are important before coding the next part.
- Part 6: Going Berserk!
Stalking monsters, fights, splatter — need we say more?
- Part 7: The GUI
A juicy Graphical User Interface with status bars and a colored message log for maximum eye-candy. Also, the infamous "look" command, with a twist: you can use the mouse.
- Part 8: Items and Inventory
The player gets to collect ("borrow") items from the dungeon and use them, with a neat inventory screen. More items added in the next part.
- Part 9: Spells and ranged combat
The player’s strategic choices increase exponentially as we add a few magic scrolls to the mix. Covers damage and mind spells, as well as ranged combat.
- Part 10: Main menu and saving
A main menu complete with a background image and the ability to save and load the game.
- Part 11: Dungeon levels and character progression
Let the player venture deeper into the dungeon and grow stronger, including experience gain, levels and raising stats!
- Part 12: Monster and item progression
Deeper dungeon levels become increasingly more difficult! Here we create tools for dealing with chances and making them vary with level.
- Part 13: Adventure gear
Swords, shields and other equipment can now help the player by granting hefty bonuses. The bonus system can also be used for all kinds of magics and buffs!