Nuking the codebase
Although it doesn’t feel like it, I’ve been working on the software adaptation of GMT’s “Fire in the Lake” board game for 21 days. I wanted to do so mainly to finally learn properly the programming language I’ve ever been interested in the most: Rust, a blazing fast, memory safe language that for me clearly will replace C++ for anything that demands speed, such as simulation, games, developing artificial intelligences, etc. In a comparison I’ve read quite a few times, in a taxing task related to AI, C++ managed to finish it in around 2.5 seconds. Rust did it in around 3.5. Python, the language popular amongst data scientists and people who in general aren’t mainly programmers, did it in around 730 seconds. Because I liked many of the upgrades they had done to Python in these last ten years, a few years ago I chose Python to develop some programs on neuroevolution, as well as three dimensional pathfinding. With both of them Python’s limitations were obvious: producing each generation for the neuroevolutionary experiments took what felt like way too long for what they were, and the multithreaded code for the three dimensional pathfinding often locked the main thread by just passing data to other threads in order for them to run the pathfinding algorithm. It could barely handle ten agents at once.
Rust forces you to handle programming constructs that in some cases are even absent in other languages: for example, you need to establish who owns each variable, and in order to prevent some of the hardest to solve bugs in other languages, only a single “structure” at a time can be loaned a mutable variable. That limitation pushes you towards functional programming: basing as much of the program as possible in “closures” that don’t produce side effects: immutable stuff goes in, immutable stuff goes out, and inside you don’t write to any file, you don’t manipulate any database. Obviously you cannot program realistic software like that in its entirety, as at some point you need to track permanent changes to variables at least in memory. However, basing your program on a functional architecture seems like the best possible choice in a language that can handle the performance overhead (because some stuff might need to be copied or cloned).
A couple of weeks ago I had hit a dead end programming my software: external, non-deterministic data (such as player input) entered through a mouth, so to speak, and the changes were done in the depths of the program. That meant that many mutable references were diving deeper and deeper into the code, and at some point the necessities of the code demanded I held on to some of them, which I couldn’t do because of the limitations to borrowing mutable references. In addition, the points of mutability to the data were scattered in the depths, which meant that I couldn’t test nor predict how they affected each other. It was obvious that I needed to refactor most of the program to a functional architecture, but in the end it was easier to learn the lesson and start from scratch. Now the program has gatekeepers (called controllers in a functional architecture) that hold mutable references, and that do as little of the thinking as possible. The decisions are made by structures further into the program, but they don’t get ideally any mutable reference: they just return back to the controllers whatever they decided. That means that the controllers receive a batch of mutations that they, or other controllers, can process sequentially in the outskirts of the program.

Deciding what will change and how, and when and how those changes are implemented, is completely separated in a functional architecture. You end up with abstractions such as ForcesMutation that holds a troop having been eliminated from a space, for example, but the board doesn’t change until those changes are persisted.
Beyond that, despite being a command line program, I made it look reasonably nice as per the following image:

I’m deep into implementing one of the factions’ bots. There is a suffocating amount of different stuff they can do; just the card events require around 120 individual functions to process their effects (and those are the base functions for the events, not counting all the functions involved in implementing each quantum of their effects). But now that I’ve gained fluidity with the language, it’s just a matter of putting many hours into it.
As usual, the code is available in its github page.
Rust forces you to handle programming constructs that in some cases are even absent in other languages: for example, you need to establish who owns each variable, and in order to prevent some of the hardest to solve bugs in other languages, only a single “structure” at a time can be loaned a mutable variable. That limitation pushes you towards functional programming: basing as much of the program as possible in “closures” that don’t produce side effects: immutable stuff goes in, immutable stuff goes out, and inside you don’t write to any file, you don’t manipulate any database. Obviously you cannot program realistic software like that in its entirety, as at some point you need to track permanent changes to variables at least in memory. However, basing your program on a functional architecture seems like the best possible choice in a language that can handle the performance overhead (because some stuff might need to be copied or cloned).
A couple of weeks ago I had hit a dead end programming my software: external, non-deterministic data (such as player input) entered through a mouth, so to speak, and the changes were done in the depths of the program. That meant that many mutable references were diving deeper and deeper into the code, and at some point the necessities of the code demanded I held on to some of them, which I couldn’t do because of the limitations to borrowing mutable references. In addition, the points of mutability to the data were scattered in the depths, which meant that I couldn’t test nor predict how they affected each other. It was obvious that I needed to refactor most of the program to a functional architecture, but in the end it was easier to learn the lesson and start from scratch. Now the program has gatekeepers (called controllers in a functional architecture) that hold mutable references, and that do as little of the thinking as possible. The decisions are made by structures further into the program, but they don’t get ideally any mutable reference: they just return back to the controllers whatever they decided. That means that the controllers receive a batch of mutations that they, or other controllers, can process sequentially in the outskirts of the program.

Deciding what will change and how, and when and how those changes are implemented, is completely separated in a functional architecture. You end up with abstractions such as ForcesMutation that holds a troop having been eliminated from a space, for example, but the board doesn’t change until those changes are persisted.
Beyond that, despite being a command line program, I made it look reasonably nice as per the following image:

I’m deep into implementing one of the factions’ bots. There is a suffocating amount of different stuff they can do; just the card events require around 120 individual functions to process their effects (and those are the base functions for the events, not counting all the functions involved in implementing each quantum of their effects). But now that I’ve gained fluidity with the language, it’s just a matter of putting many hours into it.
As usual, the code is available in its github page.
Published on August 24, 2020 05:42
•
Tags:
board-games, programming, rust
No comments have been added yet.