This short document describes the concept, usage and implementation of Rules in Alan.

Concepts

Location

A location is simply a place in an Alan game where things and actors can “be”. In the following locations are important because every Alan statement has to be executed “somewhere” since the concept of here, nearby and so on can be used. It is also important since printouts in Alan are only shown to the player if that printout is made at the same location as the hero (the players alter ego) is, otherwise the player would see messages not within his view. (Read on to see the common solution to conveying information from distant locations to the player.)

Events

Events are named sequences of statements executed at the end of a player round. Events are executed at some precise location. That location may be defined at scheduling-time (Schedule explosion At house…) or be dynamically determined when the event is about to be executed (Schedule hunger At hero…) allowing events to “follow” an instance. A common use of the “following event” is to make the hero aware of distant events by scheduling a “messaging” event at the hero (Schedule distant_explosion_sounds At hero After 0.).

Note that it is possible to schedule an event after zero rounds. The semantics of this is that the scheduled event should be run within the current round, i.e. before the player gets another chance to move.

Events are always executed in a context with a particular location specified. If no location is specified the special location nowhere is used, allowing all location referencing expressions and statements to be used without errors.

Rules

The intended use of Rules is to trigger actions, events and status changes when a particular condition occurs. In practice they consist of expressions that, when evaluated to true, execute some statements. But since it would be expensive to monitor all data all the time, the rules are evaluated at a few select moments during a player interaction instead.

Rules are in general tricky because various forms of implementation gives different semantics. In Alan, rules are intended to be “flank triggered”, meaning that it is only when an expression goes from false to true that the rule should fire its statements. (Earlier implementations fired the statements every time the rule evaluated to true, resulting in multiple executions of the statements even within the same round.)

Rules are executed in a context without location, expressions and statements referring to the current location are prohibited by the compiler.

A Round

During a player round the following happens:

  • Player executes a command

  • Every other actor executes one step in their script

  • All pending events are run

In addition to this, rules need to be monitored, which is the subject of this document.

The Problem

Rules should be evaluated so that common usecases works as most would expect. Since rules are a part of the game world, much like doors that might be open or closed, each actor needs to react to what has happened before. Actors are quasi-parallel in nature, meaning that they mostly act in sequence after each other. This means that rules must be evaluated after each actor to ensure that rules triggered by an actors actions are catered for as quickly as possible.

Triggering Rules

Since every step in a round may affect the expressions triggering rules, the rule expressions have to be evaluated multiple times during a round. There are multiple options for this:

  • After each actor and after each event

  • After all actors and after each event

  • After all actors and after all events

  • After all actors and events

Rule Looping

The statements executed by a rule that is triggered might change the world state so that further rules are triggered.

Events and Rule Looping

An event might set an attribute so that a rule is triggered. If that rule schedules the same event a non-terminating loop might be the result.

Design Solution

Flank Triggering

The “flank triggering” property of the rules means only execute the rule statements when the expression changes from false to true. This implies that we need to remember what value a rule expression previously was.

Simulation of Continuous Evaluation

The remembered value of a rule expressions should always reflect the actual value. For example once a value goes high it should trigger the statements, but we must continue to monitor the value so that we can detect another flank whenever that happens. So each rule expression should always be evaluated and and its new value saved.

Execution of Statements

The statements of a rule are only executed on a flank, i.e. when the expression goes from false to high. In addition to this, there are rules about how often the same rule should trigger. The details are TBD, but for example the same rule should not run its statements within the same running of rules. Rules are run until stable, see below, which might imply that the value of a single rule expression might go from false to true and back, and then to true again. The second flank should not trigger execution.

Rules Loops Until Stable

Execution of rules might change the game world so that more rules are triggered. Rules must therefore be evaluated until there are no more rules triggering.

  • Repeat until no rule statements are run:

    • For each rule

      • Evaluate its expression

      • If this new value indicated a flank (previously false, now true)

        • If the statements had not previously been executed

          • Execute its statements

      • Store the new value

This will always terminate because one rule that is executed will not be executed again and the set of possible rules to execute is decreased every loop.

When to Execute Rules

A possible scenario is that there are multiple rules that each trigger its own event. Imagine the following

When <trap triggered> Schedule <trap>.
When <stone falls> Schedule <stone on trap>.
Event <trap> "You are trapped."
Event <stone on trap> "The stone lands on the trap." Make trap triggered.

The expected sequence of events is that whatever makes the stone to land on the trap, the trap is triggered. Actually, whatever makes the trap triggered should run the trap event. This makes good cause-effect separation possible.

To enable this, rules must run after each event.

Avoiding rule/event looping

Since rules are reset between runs, and events may trigger rules which in turn might schedule events that trigger new (or same) rules, it is necessary to check for these loops.

When <exp1> Schedule <event1>.
Event <event1> Make Not <exp1>. Make <exp2>.
When <exp2> Schedule <event2>.
Event <event2> Make Not <exp2>. Make <exp1>.