dbx manual

7. The rules engine

I won't lie to you here ... the rule syntax is quite complicated. But this complexity gives rise to powerful functionality in a compact and flexible format. I honestly couldn't think of a way to make it simpler without also making it extremely verbose.

Please note that this page does not document the API event onruletest, which fires whenever a rule is tested; for information about that, please see API Events

Pre-requisites

To use the rules engine, you must have the dbx.rules.js codebase included on your page:

<!-- dbx rules engine -->
<script type="text/javascript" src="dbx.rules.js"></script>

It can go anywhere after the main dbx script, but before your configuration script.

The rules engine only works for two-dimensional orientations, ie. it will not work at all if the orientation is set to "vertical" or "horizontal". To avoid confusion, the setRule() method validates your orientation and will throw an exception if it's invalid. Apart from that, there are no specific limitations on how your implementation is configured.

But there are a couple of design considerations to bear in mind, which are that the rules engine works best on boxes which are all approximately the same size, and which are all roughly square.

Creating rules

Rules are created using the addRule() method of a group, calls to which must obviously come after your original group constructor. A simple rule definition looks like this:

purple.setRule("N, S");

In that example, we've defined the rule "N, S", which means that boxes can only move according to the pattern move north, then move south.

Using a single argument in setRule() creates a global rule, that is, a rule which applies to all boxes. You can only create one global rule, and any subsequent global rule definitions will overwrite the previous one. But you can also create additional rules which only apply to specific boxes, by specifying a box class as the second argument to setRule():

purple.setRule("N, S", "foo");

That rule will apply to any box which has the value "foo" as part of its class name (for example, a box with the overall class attribute "dbx-box foo". As with the global rule, if you create a rule using a class name that already has a rule defined for it, the new rule will overwrite the previous one.

Deleting rules

The companion method, for deleting rules, is called removeRule(). If no argument is specified then it will delete the global rule; if a class is specified then it will delete any rule that was defined for that class:

purple.removeRule("foo");

Basic rule syntax

The basic direction tokens are points on a compass; these tokens must be uppercase:

N the box can move north
E the box can move east
S the box can move south
W the box can move west

You can also define diagonal directions; these tokens must be an uppercase letter then a lowercase letter:

Ne the box can move north-east
Se the box can move south-east
Sw the box can move south-west
Nw the box can move north-west

And there's one more directional token, which is a wildcard:

* the box can move in any direction

You can string any number of directional tokens together for multiple choices. So for example, this rule definition:

"NESW"

Means that a box can move north, east, south or west; in other words, the box can move in a straight line, but not a diagonal.

Here we also see an example of why case is important in defining compass directions — otherwise how is the rules engine supposed to distinguish "SE" (meaning south or east) from "Se" (meaning south-east). In fact case is enforced — if it's not used properly the rules engine will throw an exception.

Note that diagonal directions do not need to be the exact compass angles they're described as, for example, north-east does not need to be 45°. Any line which is more than nominally off a perfect straight line will be considered a diagonal. A further implication of this is that, if you expect a rule to match a perfect straight line, you must ensure that the blocks sit in a perfect straight line; only a few pixels' discrepancy will be considered a diagonal.

A single rule can define a sequence of moves, not just one; multiple definitions within a single rule are referred to as patterns. Wherever a rule defines more than one pattern, these are separated with commas:

, seperate multiple patterns within a single rule

The purpose of patterns is to define how a box can move over time, allowing you to describe a path that takes several moves to traverse. So for example, the following rule definition:

"N, EW, S"

Means that a box can move: north; then east or west; then south — a rule which defines three moves in total, because it has three separate patterns.

Okay.

So far we've looked at rules which only describe direction, they don't limit the distance — every rule we've defined so far allows for movement in a specified direction for any number of blocks. A block is the distance covered by a single box, so if we had 16 boxes laid out in a square, we would have four blocks distance along each side.

You can limit the number of blocks that can be covered in a single move, by defining that number in braces after the direction. This is referred to as the range of a move:

{n} the range of a single move

So for example, defining a rule like this:

"NS{2}"

Means that a box can move north or south within a range of two blocks. This means up to two blocks, not exactly two; in other words, the range token defines a maximum. The exception to this is the value {1}, which by its nature means exactly one move, since less than that is no move at all!

Combining the blocks syntax with the directional wildcard, we can then make rules that define only distance and not direction. So this rule:

"*{1}"

Means that a box can move in any direction but only for one block (otherwise known as the Blake's 7 Rule  (big grin)).

So then, let's briefly recap, and examine a sample rule. You should be able to work this out before reading the description below it:

"NeSw{2}, *, NS{1}, SwNw{4}"

Got it figured out? That rule means: north-east or south-west within a range of two blocks; then any direction as far as you like; then north or south by one block only; then south-west or north-west within a range of four blocks.

There are two more tokens we need to look at before we've finished the basic syntax, which provide more flexibility in defining patterns and directions. The first of these is the dollar symbol, which means that a move must repeat the previous move:

$ repeat the previous move

Note that repeating the previous move means that the box must move in exactly the same direction, it does not mean that the box has the same choices as the previous move. For example, if a rule is defined like this:

"NS, $"

This means: north or south, then the same move again — so if a user chooses to move the box north, on her next move she must move it north again, she doesn't have the option to move it north or south again.

However the range of a movement does not have to be exactly copied in a repeating rule. You can specify that a move is in the same direction but with a different range, for example:

"NS{4}, ${2}"

That rule means: north or south within a range of four blocks, then the same direction again but only within a range of two blocks.

Finally, there's an "or" token that can describe multiple choices in a single pattern; this uses the pipe symbol, and is useful for situations where you want to describe multiple choices that are more complex than single directions:

| separate multiple choices within a single pattern

So if we define a rule like this:

"NS{2} | EW{3}"

That means: north or south within a range of two blocks, OR, east or west within a range of three blocks. You can separate any combination of tokens using the or symbol, allowing you define choices that are as distinct as you need them to be. For example:

"N{2} | S{3} | *{1}, $"

Which means: north within a range of two blocks, OR, south within a range of three blocks, OR, any direction for just one block; THEN the same move again (which, remember, means exactly the same move again, not the same choices again).

Good.

One more thing to note is that whitespace is ignored within rules and patterns. I've separated them with a space in these examples, but that's just to make them easier to read.

Extended rule syntax

The extended syntax allows you to specify shapes that a move should describe. Currently the only shapes that are available are triangles.

Let's begin with an overview of all the available triangle syntax, then we'll look at each one in turn:

T:n/n describe a right-angled triangle of an exact size
T:n/{n} describe a right-angled triangle where one side is an exact size and the other side is a range
T:{n}/{n} describe a right-angled triangle where both sides are ranges
T:n describe a right-angled triangle where both sides are the same size
T:{n} describe a right-angled triangle where both sides are the same range

So, a basic triangle definition might look this:

"T:2/1"

Where the "T:" is what declares a triangle definition, and the two numbers refer to the length (in blocks) of the a and b sides of a right-angled triangle (ie. the two sides that are not the hypotonuse). A triangle movement effectively traverses the hypotonuse line.

So for example, the triangle definition just above means: to a point that's two blocks in one direction and one block in another direction; this describes the movement of a knight in chess.

The rules engine is not strict about the orientation of the triangle, so this definition for example:

"T:2/3"

Will be treated exactly the same as this definition:

"T:3/2"

Unlike the range tokens in the basic syntax, the numbers in a triangle definition are precise — the example above means exactly two blocks in one direction then exactly three in another direction. However you can also specify ranges by using the same range syntax:

"T:{2}/{3}"

That definition means up to two blocks in one direction, then up to three in another direction. You can also combine a precise number and a range within a single triangle, for example:

"T:1/{2}"

Which means exactly one block in one direction then up to two blocks in another direction.

In all these examples, of course, the two directions must be at right angles to one another, so that overall a right-angled triangle is always described. You cannot use the definition above to mean, for example, north by exactly one block then south by up to two blocks; it would be have be something like north and then east, or west and then north.

You can, however, use the triangle syntax to effectively define straight lines, by specifying zero as one of the values. A triangle definition like this:

"T:2/0"

Would mean exactly two blocks in a straight line (in any direction).

Finally, if you want to define a triangle where both sides are the same size, you can do so by only defining a single number. This for example:

"T:{3}"

Means a move that's up to three blocks in one direction then the same number of blocks in another direction. That differs from this definition:

"T:{3}/{3}"

Because in the former example, both sides must be the same length, whatever that length is chosen to be.

Too easy!

One final final note is that you can mix basic and extended syntax freely within a single pattern or rule. You could send your boxes on epic journeys by coming up with beasts like this:

"NE, $ | T:1/3, NeSeSwNw, EW | NeSw{2}, ${1}, NS, T:2/2, NeSw | NeSw{3}"

Overall then, there's a huge range of movement that can be described using the rules engine — enough, I should think, for even the most complicated applications. Rules can even be defined on the fly, allowing you to describe movement that's conditional upon where a box is at the moment.

Syntax reference

For reference, here's a complete table of all the available syntax:

N the box can move north
E the box can move east
S the box can move south
W the box can move west
Ne the box can move north-east
Se the box can move south-east
Sw the box can move south-west
Nw the box can move north-west
* the box can move in any direction
, separate multiple patterns within a single rule
{n} the range of a single move
$ repeat the previous move
| separate multiple choices within a single pattern
T:n/n describe a right-angled triangle of an exact size
T:n/{n} describe a right-angled triangle where one side is an exact size and the other side is a range
T:{n}/{n} describe a right-angled triangle where both sides are ranges
T:n describe a right-angled triangle where both sides are the same size
T:{n} describe a right-angled triangle where both sides are the same range

Remote controls | ← dbx main page

Categories...

Website gadgets

Bits of site functionality:

Usability widgets

Local network apps

Web-applications for your home or office network:

Game and novelties

Our internal search engine is currently offline, undergoing some configuration changes in preparation for a major site overhaul. In the meantime, you can still search this site using Google Custom Search.


In this area

Main areas


[brothercake] a round peg in a square hole, that still fits