Let's code with the Roguelike tutorial - Part 0 - Introduction and setup


One common complaint I often hear from late beginner to intermediate programmers is: I don't know where to begin when working on a programming project.

This usually takes the form of comments or questions like:

With this "Let's code" series, I want to go into some explanations about how I plan and consider trade offs when I make decisions.

This is not a classic tutorial, in the sense that I didn't implement a project, go back and clean up the design, and then give a list of step by step instructions of how to code.

Instead, think of this as a coding livestream, but in text form. I'm going to be moving back and forth between different parts of the project. Sometimes I'll put in known suboptimal code as placeholders, or when I have too little information to make a good decision. I'll try to explain things more at the design and decision making level, and relatively less explanation about the raw code itself.

Sometimes I'll be learning the technologies myself, so you can see how I go about learning how to use a new library. I'll try to go into some detail about what I search for, how I read documentation, etc.

My hope is that this will help other programmers learn how to take that next step from intermediate to advanced - and that I also learn something as well from the process.

In no way do I claim to be an expert on these technologies, so if you see something that should be implemented a different way, go ahead and tweet me.

Roguelike tutorial

For this series, I'm going to be following the roguelike tutorial over at: https://www.reddit.com/r/roguelikedev/comments/8ql895/roguelikedev_does_the_complete_roguelike_tutorial/

I'm going to be implementing it in Rust, a language I don't have much experience with.

Why Rust? I've heard many things about how its type system and ownership requirements enable the ability to write low level, safe, and performant code. It's now been used in some moderate sized projects, and so I think it's mature enough to investigate and see what it's like. It's also a different enough language, that even if I end up not using it, I may be able to use some of its concepts to improve my general programming.

Why roguelike? Games are fun. Besides, with other people also doing the tutorial at the same time, it will be interesting to see different variations.

The following will be the main resources I'll use:

Setting up the environment

The first thing I needed to do is get the tcod library. Since I'm running on Windows, I went ahead and downloaded the prebuilt binary. Even though I'm not doing my own build, I still cloned down the source. Being able to look at the exact source for API details, implementation, and debugging is an invaluable resource. Some people like to treat the library as a black box, trying to figure out how it works by varying inputs, order of calls, etc. I find being able to drill into the source and see what it's doing greatly accelerates debugging. Just be careful, of course, not to rely on any implementation details.

After unpacking, I tried out the samples executables in the package, and it worked. So I think the library is set up correctly. Easy enough.

Setting up Python bindings

Why set up Python bindings if I'm going to be coding in Rust?

Well, for one thing, I already have Python installed on my machine, so it's not a ton of extra work to set up some libraries.

Also, I'll have a language I know well, that I can use to test with. Since Python has an interactive terminal, I can quickly do any kind of exploration I might like of the library, and if my Rust code has bugs, this can help narrow down whether the bug is in my code or the tcod library.

The directions are straightforward and I'm able to import libtcodpy and print hello world. So what have I tested here? In my Python script, I haven't made any calls to libtcod, so all I've really tested is that the library can be found, directories and environment are set up correctly.

Setting up Rust bindings

As part of learning Rust, I'm going to be adding the FFI bindings myself. Therefore I won't be using any external crates, but will be linking to libtcod directly. Let's try to do a minimal test similar to the Python test above.

I took a hello world program and added:

#[link(name) = "libtcod"]

I compiled and ran, and it worked.

For some reason, this felt a little too simple... which made me a bit suspicious... what if I linked against a random library?

#[link(name) = "foobar"]

This worked also... clearly I wasn't testing what I thought I was testing. In fact, what I needed to do was add an additional fn to force the link:

#[link(name) = "foobar"]
fn { }

This failed, and changing it back to libtcod passes. In this case, it didn't really impact things too much, since I happened to have the right code in the first place, but for something else I could have wasted a lot of time. This is one of the reasons test driven development requires making sure that your test fails first. Even if you don't follow TDD (and I'm not a TDD fanatic), I've sometimes accidentally built the wrong branch, or deployed incorrectly, etc. so in the right situations it's important to make sure your code is failing properly.

And with that, my environment looked properly set up.

In this series: Contents, next

by Yosen
I'm a software developer for both mobile and web programming. Also: tech consultant for fiction, writer, pop culture aficionado, STEM education, music and digital arts.