Select Page

InRule for JavaScript Life

by | Last updated on Aug 13, 2024

Overview

I’ve always been fascinated by the idea of emergent complexity – a concept that describes how with a small number of simple (In)Rules, a system develops which is orders of magnitude more complex than the initial state. Examples of this abound all around us, with one of the best-known example being that of the basic blueprint of life: DNA. With just 4 base pairs (or “letters”) and a few simple rules to govern how they can combine, the blueprints for all organized life as we know it can be described in full fidelity and complete detail. In this post, I won’t be trying to go all Jurassic Park by hacking dino-DNA, but I will be showing off how you can use InRule® for JavaScript to create your own sandbox of emergent complexity by implementing a basic version of the classic Conway’s Game of Life. I’ll go over the rules of the system along with the entity schema. Then, I’ll go over the integration code needed in order to make use of InRule for JavaScript. Finally, we’ll wrap things up with a hit-list of the considerations involved in creating something like this as well as provide some links for further reading and resources.

Harvesting the Rules

One of the great things about the Game of Life is the sheer simplicity of its rules. The idea is that in a grid of arbitrary rows and columns, a single given cell can have one of two states: Alive or Dead. The initial state of the board becomes the “seed” conditions, and once those are specified, it’s no longer necessary to provide any external input into the system. The current state of the system is used as the inputs into the next state of the system following a “tick”, or processing turn. Whether or not a Cell “IsAlive” or not is decided by the present state of the 8 neighbors of the given Cell. Each “birth” or “death” happens simultaneously during the “tick”

Right away, we can derive a number of useful Rules and Rule artifacts from this description. Here’s what comes out of our initial analysis:

  • Our universe consists of a grid
  • A grid has a collection of cells
  • Each cell has a state, IsAlive, that can be either true or false along with its X and Y coordinates on the grid
  • The state of each cell is computed any time the universe “ticks”
  • The determination of a cell’s fate depends on the current states of the individual cells surrounding it
  • The computed (future) state of a cell should not affect the computation of the state of surrounding cells – state changes are simultaneous

Here’s what the resultant entity structure looks like along with the Rule Sets we’ve identified from our requirements. You can see that I added a calculated field, Index, which is a convenient way to avoid needing to compute it from scratch each time I need to know the value of the expression X + (Y*Grid.GridSize). NextState and PreviousState are temporary fields used to hold past and future state values – they will be used to fulfill the requirement of simultaneous evaluation.

Now we can start to look at how we’re going to structure execution of the rules. The first thing to do is to identify the root entity that will serve as the basis for rule execution. This is easy for us in this case – it’s the Conway entity (the small blue plus sign in the Entity icon signifies that the entity has no parent referencing it). Once we’ve done that, we need some way to compute the next state of each cell. By creating an explicit rule set “OnTick”, I can use the ExecuteMemberRule Set action to iterate over all of the grid’s cells, calling the rule set on each.

The OnTick rule set can’t directly set the IsAlive property because it would violate the need to compute future states simultaneously. Here, simultaneous doesn’t necessarily mean that it actually occurs at the same time, it just means that mutation of the IsAlive state flag should never occur until every cell has had its future state computed. We accomplish this by storing the results of the state computation in that NextState temporary field. Once we’ve gone through all the cells and set that value, we can iterate over the collection once more to copy the value in the temporary field to the actual field. Once again, ExecuteMemberRule Set is our friend here, allowing us to do this in a clean fashion, as the screenshot below demonstrates.

This is all well and good, but what about the heart of these rules — the actual state computation? I’ll let the Business Language of those rules tell the story:

The slightly awkward phrasing of the “number of neighbors having an IsAlive of…” comes courtesy of a vocabulary template that I threw together to abstract away the semantics of calculating the number of alive or dead “neighbors” a cell has. Though vocabulary templates may seem like syntactic sugar, they’re actually one of the more potent weapons in the rule integration and authoring tool chest. Here’s what

the GetNeighbors vocabulary template looks like. Note the use of the Current(Field) function to ensure proper resolution of instances within the aggregate Count() expression

Prepare to Integrate

Once we’ve put these pieces into place and tested them using irVerify®, it’s time to get the rules packaged up into JavaScript and integrated into an application! For this example, I’ve chosen a dead-simple web page to serve as the host for my JavaScript-based rules execution. I want to make things as self-contained as possible for this sample, so I’m going to place my CSS and JS in the same file as my HTML. The only external dependencies are jQuery and the JavaScript-packaged rules themselves.

Speaking of packaged rules, I’ve packaged up my rules using the irX® for JavaScript extension to irAuthor® with the box checked to provide Execution log entries. ProTip: if you don’t see a JAVASCRIPT tab in your irAuthor command ribbon, you need to run the InRule installer and select the option to install irX® for JavaScript from the list of features (licensing for the extension is separate from irAuthor). Placing the resultant file in the same directory as the HTML file that will host it keeps things simple.

Integrated Markup

There’s not a lot of HTML that needs to be written here, just enough to render a simple grid along with a button to initiate a “Tick” and a place for feedback from the rules engine to be displayed to the user (for troubleshooting purposes):

We don’t want to paint ourselves into a corner by mixing the display of the grid with the logic governing its behavior, so we’ll instead define the ROW_COUNT and COLUMN_COUNT values as constants in our JS code. It’s a trivial exercise to turn that into a parameter that can be passed in via, say the query string of the URL, but for the purposes of keeping things simple, we’ll make do with constants.

The creation of the Rule Session and the registration of the “Conway” entity with our JS Conway entity are two critical steps for integration. The first prepares the runtime for execution while the second specifies the data state that will be used by the engine during execution. We could actually execute the rules at this point, but there wouldn’t be much to see since we haven’t populated our entity state, nor have we written any integration code to display that state in the UI.

To start, we need to generate the HTML Table cells that will comprise the UI of our grid, and at the same time populate the initial entity state that we’re going to use to execute rules. This is accomplished via a simple set of nested for (…) loops and some jQuery. The relevant part of this is shown below:

This gets us to the point where we can see a table of cells, and that table of cells matches the entity state that we’re going to be applying the rules against. A jQuery-based click handler provides the functionality to allow users to click a cell to toggle its initial state, which fulfills the rest of the requirements needed to prepare for rules execution, while another handler hooks up the “Tick” button to a handler we’ll get to in a bit. Before we can discuss the Tick logic however, we need to address two fundamental needs:

  1. Before each “Tick”, we need to update entity state in response to a user having clicked a cell to toggle its state in the view
  2. Conversely, after every “Tick” we need to update the UI to match the current entity state so the user can see what’s happened

There are tons of different ways to accomplish these goals, but I’m going to go with the naïve approach of simply looping through our cells, looking up the matching HTML cell, and updating either entity state or the CSS class as appropriate. Thus, we come up with the following implementation pattern:

The “Tick” rule set is an explicit rule set, which means I use a slightly different invocation pattern from automatic rule sets. Instead of invoking off of the session, I invoke off of the entity to which the explicit rule set belongs. I pass in the name of the rule set, an empty array to signify that I’m not passing in any parameters to this rule set, and lastly, a callback to be fired upon completion of rule execution.

Because of the way that the inRule for JavaScript engine uses the entity state, I can interrogate and change that state at any time simply by referencing the original JavaScript object passed into session.CreateEntity. That allows me to define functions like updateTableFromEntityState like this:

Wrapping up

Integration tasks completed, it’s time to see how emergent our complexity really can be! Loading up the page in a web browser and setting the initial state for a repeating pattern known as “R-Pentomino” (see https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life#Examples_of_patterns for more examples!), I spam the “Tick” button and watch as my sandbox takes on a life of its own.

The astute reader may notice that this isn’t the first time that I’ve played around with implementing the Game of Life using a framework or tool that wasn’t originally designed for the task – indeed, I demonstrated an alternative implementation of Life as part of a talk I gave on SpecFlow a few years back. I’ve found that Life makes for an eminently suitable reference implementation in many different milieus because it’s just complicated enough to be both interesting and non-trivial, while still remaining quite simple enough to not detract from the main point and allow irrelevant details to intrude upon the topic at hand. Though not many developers or businesses have a need to run simulations of this particular variety, there are several lessons to take away from this type of application:

  • Effective rule harvesting and design can and should be performed outside of the tooling that will be used to implement the system
  • Relationships between pieces of data may have a temporal component in addition to any other (e.g. referential) requirements. This means that…
  • It is important to understand as early in the process as possible what shape the initial and the final (expected) states of the data will take as this will become the boundary conditions for your system
  • Simplicity can lead to some wonderfully complex and rich interactions. Design components as simple as possible (KISS never goes out of fashion!) and allow the interactions between components to be the primary place for the expression of complexity.

What sort of lessons does Conway’s Game of Life teach you?

Resources and further information:

Conway’s Game of Life:

BLOG POSTS BY TOPIC:

FEATURED ARTICLES:

STAY UPDATED ON THE LATEST FROM INRULE

We'd love to send you monthly updates! Learn about our webinars and newly published content by subscribing to our emails. We'll never share your email address and you can easily unsubscribe at any time.