pwmarcz.pl

Writing a roguelike in Lua


(adapted from a post on Reddit)

I recently wrote a seven-day roguelike in Lua. It was my first project in Lua, and second roguelike (after coding one in Python a year before). I don't have much to base this on, but believe dynamic languages like this are the fastest and best way to write a roguelike game. You write relatively little boilerplate and have much freedom with how to design the engine, compared to, say, endless classes bureaucracy in Java or manual memory management in C.

Coming from Python, several things irritated me. The language feels lower-level. There are less primitives available from the start. Everything takes more code to write. While the tables are a nice concept, often I missed working with real lists, tuples and arrays. Also list comprehensions, everything being a sequence. Ternary if. Multiple return values, and unpacking, instead of simple tuples. Optional arguments… the list goes on.

Most of the things I wanted I could actually do in Lua, but it seemed more verbose and more prone to failure. Of course, in part this is probably due to my inexperience. And I was probably trying to mimic the previous (Python) engine's architecture too closely.

I loved being "close to the metal" - actually understanding how the tables/methods I use actually work. Classes in Python feel too heavy sometimes (and definitely more magical, harder to understood, and uglier). In Lua, the whole thing seems very malleable. This is a language you can make every object system you can think of, after all.

As for having to define all the OOP-style mechanisms from scratch - this is, IMO, a non-issue. After writing an object system, using it was as easy as everywhere else. I even managed to hack some field-imitating class properties.

It frightens me how easy it is to make an error in Lua. All it takes is to forget a local before a variable, or mix up foo:bar() with foo.bar() - and the error may not surface at all until much later (unless you bother to make tests… I probably should have): the system doesn't complain if you do .bar() instead of :bar(). It doesn't complain if you give it wrong number of arguments. Reading an uninitialized variable gives you a silent nil. Bugs not failing early enough are a problem in most dynamically-typed languages, but somehow in Lua it seemed especially bad.

(Some of these are fixable, for example you can easily forbid making global variables, ensuring that leaving out a local will blow up).

There is no built-in module/namespace system, just loading files. Well, actually there is the module() function, but from what I've read, it's considered deprecated and harmful…

Numbering arrays starting from one is an ugly wart. Of course, everyone tells you that you get used to it, and they're probably right… but I haven't seen any argument about it being superior, just about not being important. Still looks ugly. Especially when if you interface with a library that counts from zero. But this is nitpicking, the language seems very well designed apart from that.

Keep in mind that I'm a beginner and I don't know what I'm talking about. If I stayed with Lua for some more projects, I probably would've stopped whining about it not being Python and learned to appreciate it more. Also, the experience wasn't as bad as my negative comments here would make it seem.

tl;dr Try it.