Passion projects and ruminations related to IT development.
For the frontend we will turn once again to Elm, expanding our knowledge to include: custom elements, SVG interactivity and URL parsing. Let’s get going.
We assume the read is familiar with chapter 2 of New Kind of Science. This will make reading the rest of the article a lot easier.
We first want to present some definitions to the reader, assuming you read chapter 2 of NKS you will recognise the following concepts:
To illustrate, if we have only dead and live cells with the window size of 3, we get 8 combinations:
If we enumerate them and give them outcomes:
0 dead-dead-dead -> 0
1 dead-dead-live -> 1
2 dead-live-dead -> 1
3 dead-live-live -> 0
4 live-dead-dead -> 0
5 live-dead-live -> 1
6 live-live-dead -> 1
7 live-live-live -> 0
we can construct the rule index as follows:
(0 << 0) + (1 << 1) + (1 << 2) + (0 << 3) + (0 << 4) + (1 << 5) + (1 << 6) + (0 << 7)
= 0 + 2 + 4 + 0 + 0 + 32 + 64 + 0
In our frontend we visualize this rule using our combinations view (white = dead, black = live):
Interactive design for rule index manipulation
By clicking the individual combinations, you can toggle their outcome. It will also update the text box containing the numerical value of the rule index.
The Rust implementation ended up being small enough. We use the same bitvec library as we did for an earlier project for solving Sudoku puzzles. There was the option to use other libraries such as fixedbitset, however I opted for the extra available bitwise functionality offered by bitvec.
We decided to adopt the following assumptions:
The bitvec library offers some nice macros to represent bit vectors, which we made handy use of in our unit testing module. In the end, our implementation stores the rule as the rule index, the integer is sufficient to be able to perform the necessary computations. There is no need to unpack it and come up with elaborate data structures, unlike in later automata. The code to apply the rule is
Since we update the cells in place, we use a single integer as a cache to keep track of old values. You will also notice that the first window constructed will have its rightmost cell be the first in the universe. All others cells will be the last (window - 1) of the universe, yet we are using the outcome and writing it to the first cell of the universe. This of course is incorrect, and we end up course correcting at the very end by shifting the whole universe to the left. Doing it this way means we don’t have to fiddle with indices too much.
Adopting WebAssembly for our project was easy enough with the excellent tutorial (already showcasing Conway’s Game of Life) provided by the creators of rustwasm. On top of that, we made handy use of an already existing template to generate our skeleton to include Elm and Rust.
That said, there are some small caveats to keep in mind:
Let’s look at the Elm code first:
mapCustomAttribute is a helper function to introduce some more structure, but it can be seen that we are talking about a single HTML “cellular-automata” element. Here is what it looks like in HTML, with the two canvas elements as its children:
HTML custom element
As seen above, you must specify the expected attributes in the
observedAttributes method, a change in an attribute not part of this list will not trigger the
We also included a custom method named
state-change, informing Elm that the pausing was done successfully. On Elm’s side, we already displayed the
onStateChange event as a HTML attribute for the node element. All we need now is a decoder:
The rule index text field in our app makes it easy to configure the automata rule, but we also want people to simply share a URL with other people with whatever interesting rule index they encounter. By inputting the URL, the app will automatically load the rule based on the supplied rule index as a query parameter. We solve this by updating the URL whenever the rule index is modified in the app. Since we want URL manipulation, we resort to the
Browser.Navigation module to update the browser address bar whenever the rule index changes with:
And in reverse we need a parser to extract the rule index from the URL and load it when the page loads:
You will notice that aside from the rule index, we also include the window size as a query parameter. Since we are using a third party library for the rule index (represented as an unsigned integer of 64 bits) we used a custom parser. The
init function is modified to use the parser to operate on the supplied URL and provide sensible defaults in case the parameters prove faulty.
We used the
elm/svg library to draw our combinations interface pictured earlier in the Math section. The solution to incorporate interactivity is dead simple, since each combination can either have on (live) or off (dead) for its outcome. With this boolean nature, we do not have to add a lot of events to individial SVG elements and we can attach a message to the entire SVG (each combination is its own SVG):
All that is needed is an
onClick attribute. In later automata, we will need to account for a whole array of effects for a combination and we won’t be able to resort to such a simple solution no more.
Whilst I have a lot of good things to say about Elm, we knew we’d find fault with the language at some point, as we would with any other language. So this is a small rant about third party packages and how they are dealt with. Third party packages can be registered on Elm packages, the site however is not an artifact store and it all it really does aside from displaying documentation is to link to a Github repository containing the actual Elm code.
This poses a huge problem, which we encountered when trying to apply malaire/elm-uint64. Using
elm install will complain about the package source code not being available and a quick look at the Source link provided on the Elm packages website does indeed confirm the repository does not exist. The original author deleted it citing displeasure with the way Elm was being managed. Luckily, another Elm developer happened to have the package code stored locally and pushed it to Github, resubmitting it to Elm packages: MartinSStewart/elm-uint64.
This does make me more wary of any third party Elm libraries and the Elm ecosystem as a whole.
For hosting we opted to leverage our AWS knowledge and simply create a S3 bucket configured to serve as a static website hosting solution using CDK. The code is available here.
In addition, we deploy a ServiceCatalog portfolio and product to easily onboard new Elm+Rust WebAssembly projects. The product will take a Github repo (assuming you set up an OAuth2 Codestar connection to your Github account) and a URL path (S3 path in bucket) under which the web app will be made available. In doing so, we can reuse our deployment strategy much quicker in the future for additional automata web apps.
The simplicity of the model representing a simple automata rule allowed us to focus on these new elements, and passing information from Elm to Rust/WebAssembly became a non-blocker. The resuls can be seen on our Vault.