I’ve played NetHack for about as long as I can remember (for the record I’ve had four successful ascensions, and one gut wrenching botched ascension where I offered the amulet of Yendor to the wrong deity). NetHack helped fuel my interest in computer programming. When I started learning some C and C++ in high school, being able to examine all those .c and .h files really helped make everything I was learning feel far less abstract, and more real (Incidentally, the NetHack code base is super interesting. Alexei Pepers gave an amazing talk titled Tech Tourist Mode: A Guided Tour of the NetHack Codebase that I cannot recommend enough).

Fast forward to one week ago. I saw on Twitter Bob Nystrom produce a slick, compressed piece of C code for generating roguelike dungeons (RIP Ginny). What I liked about the dungeons that were generated is they were wonderfully simple: they consisted of a series of interlocking rooms, no connecting passageways or open world areas in sight! I thought this was the perfect sort of source material for a quick game jam idea. Then I checked the seven day roguelike challenge page. It turned out that it was starting that very day! To me it felt like a signal from the universe to go and do this thing. So I went for it.

I have no experience developing games. The closest I ever came is once while learning to do object oriented programming, a created a class of objects for a deck of ordinary playing cards. I created an interface so that you can do all the ordinary things you do with cards, but no enforcement of rules for any actual games, leaving that to the human being interacting with the program I wrote. Because I had no experience making games, this meant that I would just figure out how to do things on my own regardless of whether it made sense or not.

I chose to once again work in Rust. This time I had something specific in mind with my choice of Rust, I wanted to use the Termion crate. Termion offers a really nice interface for writing interactive TTY applications.

First Challenge: Displaying a Dungeon on the Screen

Usually in Rust if you want to display a string in terminal output a println! macro will suffice, or for error messages eprintln!. However for a game interface you need to enter into raw mode in the terminal, and at that point you are generally using the write! and writeln! macros. Failure to track the differences between these two macros, and also that the ANSI escape design is one based, results in a lot of off by one errors, and I spent a lot of time fixing those.

Second Challenge: Introducing Player Movement

Player movement is managed with a hash map that contains tuples with cursor coordinates as the key, and the character at that coordinate as the value. The nice thing about this setup is that you can feed dungeons to the game as a string. However I suspect this approach would have caused me problems down the line as future development occured. As I tried to add more features to the game I really began to understand all the additional information I needed to track.

At any rate, player movement consisted of tracking the player location on the grid, and swapping the player @ symbol with the moved into location.

Third Challenge: Introducing Collision Detection

The initial collision detection that I implemented was simply for walls and doors. I created a hash set with those symbols, if the space the player tries to move into a space with a char contained in the hash set, the move is denied. If I had implemented enemy movement and combat this would have required an extra case to handle, but in principle it would not have been any more complicated, it just would have required more items in the hash set, or even better, to invert the whole thing and have a set of valid spaces, and resolution for types that are not in the set.

Fourth Challenge: Introducing Interactions Between the Player and the Environment

Trying to add player interactions is where I ended up hitting my limit with the 7drl challenge. I created a player struct, and some methods for it. The first method I implemented was to add items to the player’s inventory. However this ended up running afoul of the borrow rules in a way that wasn’t as simple as passing a value as a reference. I look forward to fixing this problem, but it wasn’t something I really had time to address in the allotted time when I encountered it last night before going to bed.

Conclusion

As expected, I didn’t create a finished game, but I learned a lot in the process.

  • I learned how to use a powerful TTY API, and how to create more powerful interfaces for the terminal
  • I developed a greater appreciation for the importance of code organization for a project with the complexity of a roguelike game
  • I pushed myself towards having to work with some of the more complicated parts of Rust. While working on this I was developing robust struct and implementation methods, adding traits to those structs, and using smart pointers for cases where you need to work with data whose size cannot be known at compile time.

From that perspective this exercise was a great success, although if I try this again next year I suspect I will just try to use the libtcod library so I can actually finish something.

Screenshot of prototype