<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Fast, Declarative, Character Simulation Using Bottom-Up Logic Programming</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Ian Horswill</string-name>
          <email>ian@northwestern.edu</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Samuel Hill</string-name>
          <email>samuelhill2022@northwestern.edu</email>
          <xref ref-type="aff" rid="aff0">0</xref>
          <xref ref-type="aff" rid="aff1">1</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>AIIDE Workshop on Experimental Artificial Intelligence in Games</institution>
        </aff>
        <aff id="aff1">
          <label>1</label>
          <institution>Northwestern University</institution>
          ,
          <addr-line>2233 Tech Drive, Evanston, IL, 60208</addr-line>
          ,
          <country country="US">USA</country>
        </aff>
      </contrib-group>
      <abstract>
        <p>Declarative programming offers several advantages in terms of compactness and modularity. Logic programming and rule-based systems are often chosen for tasks such as social simulation because their use of declarative rules and predicates map well to rules of social engagement. Unfortunately, declarative programming is often quite slow, making it inappropriate for large systems or highfrequency updates. This is partly because of its use of search algorithms, but also because of its heavy use of pointer chasing, dynamic allocation, garbage collection, and runtime type-checking. In this paper, we discuss how bottom-up execution of logic programs can be implemented without these issues. We argue that character simulation is a “sweet spot” for bottom-up logic programming, allowing character behavior to be specified in terms of declarative rules, while offering performance competitive with Python systems such as Talk of the Town. We present a language, TED, which offers very good performance and has been used both in research and in an unannounced commercial game.</p>
      </abstract>
      <kwd-group>
        <kwd>Social simulation</kwd>
        <kwd>logic programming</kwd>
        <kwd>declarative programming 1</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1. Introduction</title>
      <p>Simulations, including games, involve iterating
through data structures representing the world state,
updating the various components of the world and
looking for pairwise interactions between them.</p>
      <p>
        For example, needs-based AI [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ], such as in The
Sims [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ] involves finding for each character  an object
 in the world that best satisfies its various needs 
using some variant of the one-line formula:
 ( ) = arg max
 ∈
 ∈
 ( ,  ,  )
The naïve implementation of this involves three nested
loops running over the sets of characters  , objects  ,
and needs  . :
      </p>
      <sec id="sec-1-1">
        <title>For each character c in C</title>
      </sec>
      <sec id="sec-1-2">
        <title>If character idle</title>
        <p>Best score = 0</p>
      </sec>
      <sec id="sec-1-3">
        <title>For each object o in O Score = 0</title>
      </sec>
      <sec id="sec-1-4">
        <title>For each need n in N</title>
        <p>Score += S(c, o, n)</p>
      </sec>
      <sec id="sec-1-5">
        <title>If score &gt; best score</title>
        <p>Best object = o
Best score = score</p>
      </sec>
      <sec id="sec-1-6">
        <title>Interact with best object</title>
        <p>This has running time  (  ). In an effort to optimize
it, the programmer might maintain separate, dynamic
lists of just the characters that need to be updated, just
the objects that are available to satisfy a specific need,
etc. This comes at the cost of having to modify other
parts of the program to maintain these lists, increasing
development costs and dependencies between
modules.</p>
        <p>
          Ideally, one would be able to specify the
fundamental computation being performed
(maximization) separately from the choice of data
structures, as one does in relational databases: queries
are expressed in terms of a set of logical data
structures (relational tables). The physical layout of
the data can be adjusted independently to best support
the mix of queries needed. As those queries inevitably
change, the physical data structures can be changed to
support them. Similar arguments have been made in
the context of entity-component systems for massively
online games [
          <xref ref-type="bibr" rid="ref3">3</xref>
          ].
        </p>
        <p>In this paper, we describe a high-performance
declarative programming language, TED that can
compactly express the algorithm above in two lines of
code (see figure 1). Moreover, it can be optimized
declaratively as in a relational database, by providing
annotations about how to index the data. TED is highly
performant, running with minimal dynamic allocation,
type checking, or pointer chasing. It also supports
parallel execution. We also briefly describe a city
simulator built using TED.
// Assume C[x], O[x], N[x] mean x is a character, object, or need, respectively
// Score[c, o, t] means t is the total score for object o and character c
var Score = Definition("Score", c, o, t).If(O[o], t==Sum(s, And[N[n], s==S[c,o,n]));
// B[c,o] means c is a character whose best object is o.
var B = Predicate("B", c, o).If(C[c], Maximal(o, t, Score[c, o, t]));</p>
      </sec>
    </sec>
    <sec id="sec-2">
      <title>2. Logic programming</title>
      <p>Logic programming is a family of declarative
programming techniques that involve describing a
program in terms of a set of predicates (relations) and
rules for computing them.</p>
      <p>A rule gives a set of conditions implying the truth
of a predicate. For example, siblinghood could be
defined in terms of shared parentage:</p>
      <sec id="sec-2-1">
        <title>Sibling[x,y].If(Parent[x,p], Parent[y,p]);</title>
        <p>This states that for any  ,  , and  , the sibling
relationship holds if both parent relationships hold.</p>
        <p>
          Although not always expressed in the form of logic
programming, symbolic rules have frequently been
used to describe character behavior and social physics
in systems such as Inform 7 [
          <xref ref-type="bibr" rid="ref4">4</xref>
          ], Comme Il Faut [
          <xref ref-type="bibr" rid="ref5">5</xref>
          ],
Versu [
          <xref ref-type="bibr" rid="ref6">6</xref>
          ], MKULTRA [
          <xref ref-type="bibr" rid="ref7">7</xref>
          ], and City of Gangsters [
          <xref ref-type="bibr" rid="ref8">8</xref>
          ].
        </p>
        <sec id="sec-2-1-1">
          <title>1.1. Top-down execution</title>
          <p>
            Classical logic-programming languages such as Prolog
[
            <xref ref-type="bibr" rid="ref6">6</xref>
            ][
            <xref ref-type="bibr" rid="ref7">7</xref>
            ] execute queries “top-down” using SLD
resolution [
            <xref ref-type="bibr" rid="ref11">11</xref>
            ]. The user “calls” a predicate such as
Sibling, with argument values, and the system tries
to prove the predicate true of those arguments using
one of the predicate’s rules.
          </p>
          <p>Using a rule involves matching the rule’s variables
to the arguments specified in the call. For example, the
call Sibling["Bill", s], i.e. “who is a sibling  of
Bill?”, matches the rule above to yield the substitution:</p>
        </sec>
      </sec>
      <sec id="sec-2-2">
        <title>Sibling["Bill",s] .If(Parent["Bill",p], Parent[s,p]);</title>
        <p>The system then recursively executes the call
Parent["Bill",p]. If there was another rule that
stated Jenny was one of Bill’s parents, then executing
this query would set the variable  to “Jenny”, meaning
that the second call is really Parent[s,"Jenny"]. If
there is a rule that says Jenny is a parent of Christine,
then this call would set  to Christine. The rule proves
Sibling[“Bill”, s] is true and provides Christine
as the value of  . Further solutions (further siblings)
can be found by backtracking the proof process.</p>
        <p>
          Top-down execution computes one solution at a
time, which is an advantage when only one is needed.
On the other hand, if the same call is performed
2 For database queries, it is possible to transform a query into an
equivalent query that, when executed bottom-up is as efficient as
topdown execution using the so-called magic sets algorithm [
          <xref ref-type="bibr" rid="ref12">12</xref>
          ].
•
•
•
•
•
•
repeatedly, e.g. by different subgoals of a call, then the
entire work of that call is repeated. Moreover, the
matching process (unification) is somewhat expensive.
For example, just looking up the value of a matched
variable requires a loop chasing forwarding pointers.
        </p>
        <sec id="sec-2-2-1">
          <title>1.2. Bottom-up execution</title>
          <p>Suppose we had already computed the full extension
(all the child/parent pairs) of the Parent relation and
stored them in an array. In that case, the Sibling rule
above could be computed with the following loop:
for each (x,p) in Parent
for each (y, p2) in Parent
if p == p2</p>
          <p>return (x,y)
Indeed, we could compute an array of all the Siblings,
with a small modification:
for each (x,p) in Parent
for each (y, p2) in Parent
if p == p2</p>
        </sec>
      </sec>
      <sec id="sec-2-3">
        <title>Sibling.Add( (x,y) )</title>
        <p>After execution of this loop, Sibling contains all the
sibling pairs. This has the obvious disadvantages that:</p>
        <p>There may be quite a lot of sibling pairs
You may only care about the siblings of Bill, in
which case the effort to compute the other
families is wasted.2
However, it also has several advantages:</p>
        <p>Unification can be replaced with if’s and
assignments to C-like variables.</p>
        <p>Subsequent calls to Sibling can simply check the
array; rules are executed only once.</p>
        <p>Indexing can be used to speed access to the array.</p>
        <p>What to index can be decided after the rules are
written and evolve during the life cycle of the
game.</p>
        <p>
          This suggests an alternative execution strategy:
compute the complete extensions of each predicate,
and place them in arrays, ensuring before executing a
rule, we first make sure the predicates it references
have been computed. This is known as bottom-up
execution, and is the strategy used in Datalog [
          <xref ref-type="bibr" rid="ref13">13</xref>
          ]. We
However, it’s unclear what this would look like in a game engine
usecase.
// Base table: holds the state of the grid
var Grid = Predicate("Grid", loc.Key, occupied.Indexed);
// Derived table: the number of cells neighboring a given location
var NeighborCells = Predicate("Neighbors", loc.Key, count.Indexed)
        </p>
        <p>.If(Grid[loc, __], count==Count(And[Neighbor[loc, neighbor], Grid[neighbor, true]]));
// Update table: cell dies if over/underpopulated neighborhood
Grid.Set(loc,occupied,false).If(Grid[loc,true],Neighbors[loc,count],(count&lt;2|count&gt;3));
// Update table: cell born if empty and 3 neighbors
Grid.Set(loc, occupied, true).If(Neighbors[loc, 3]);
believe character simulation is a “sweet spot” for
bottom-up logic programming: it is often defined in
terms of rules, and the engine generally does have to
compute complete extensions anyway.</p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>3. Declarative simulation</title>
      <p>There are many reasons why it’s appealing to be able
to treat a simulation as a database that one can query
using a query language. One reason is it makes the
state of the simulation easily inspectable, and so
hopefully debuggable, because everything is already
stored in tables that can be queried by the user.</p>
      <p>
        The motivating example for this work is Ryan’s
[
        <xref ref-type="bibr" rid="ref14">14</xref>
        ] argument for generating stories by running a
cityscale character simulation and then searching
(“sifting”) its output to find interesting stories. Story
sifting effectively requires a query language that can be
run against the simulation. Indeed, Datalog has been
used for story sifting in the past [
        <xref ref-type="bibr" rid="ref15">15</xref>
        ]. However, it
involved logging everything in the simulation to a file
and then reading the file into a separate application for
sifting.
      </p>
      <p>
        This paper began from the question: what if the
query language could also be the simulation language?
That is, can we write the character simulation logic for
games like The Sims [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ], Bad News [
        <xref ref-type="bibr" rid="ref16">16</xref>
        ], [
        <xref ref-type="bibr" rid="ref17">17</xref>
        ], or Prom
Week [
        <xref ref-type="bibr" rid="ref5">5</xref>
        ], [
        <xref ref-type="bibr" rid="ref18">18</xref>
        ] declaratively in some language akin to
Datalog? And if so, how performant would it be?
      </p>
      <p>The basic structure of such a simulation is as
follows. In the foregoing we will generally use the
terms predicate and table interchangeably, since most
predicates are represented at runtime as tables.
Simulations use three main types of tables:
•
•
•</p>
      <p>Base tables store the state of the simulation.
They retain their data from one simulation
step to the next, except insofar as they’re
modified by update tables, below.</p>
      <p>Derived tables are defined in terms of other
tables (base or derived) using rules. Derived
tables are recomputed on every step of the
simulation.</p>
      <p>Update tables list modifications to be made
to different base tables at the end of the
current simulation step. They are themselves
a kind of derived table defined by rules.</p>
      <p>As with relational database systems, tables can
optionally be indexed by different columns (predicate
arguments), making it faster to perform lookups.</p>
    </sec>
    <sec id="sec-4">
      <title>4. The TED language</title>
      <p>TED is a high-performance, bottom-up logic
programming language intended for character
simulation in AI-heavy games. It is strongly-typed,
supports higher-order predicates, metaprogramming,
and parallel execution. It includes an optional
runtime parser-evaluator that allows users to make live
queries against an executing simulator.</p>
      <p>TED is embedded in C#, meaning that TED code is
C# code that builds the TED program in memory. This
has several advantages:
•
•
•</p>
      <p>Good interoperability between TED and C#.
TED code can easily call into C#, and C# code
can easily access the contents of TED tables.
IDE support for C#, such as type and syntax
checking, colorization, and refactoring
automatically extends to TED.</p>
      <p>C# can be used as a macro language for
metaprogramming; higher-level abstractions
can be written as C# code that builds the
necessary TED tables and rules.</p>
      <p>TED by itself is strictly less expressive than Prolog:
it does not allow recursion (see section 5.3) and it does
not allow Prolog “functors” (composite objects are
opaque to TED’s pattern matcher). Unlike datalog,
TED can call arbitrary C# code, which is
Turingcomplete. But it still would not be a natural choice to
use to implement algorithms such as symbolic
integration or natural language generation, that
require manipulating tree structures representing
symbolic expressions. In exchange for this limitation,
we get high performance and parallelizability.</p>
      <sec id="sec-4-1">
        <title>4.1. Trivial example</title>
        <p>
          Figure 2 shows Conway’s Game of Life [
          <xref ref-type="bibr" rid="ref19">19</xref>
          ]
implemented as a short TED program. The program
consists of a series of C# statements that build the
parse tree of the TED program to be executed.
        </p>
        <p>The first statement creates a new predicate object
(table) and stores it in the C# variable Grid. It holds
the state of the board. The predicate has two
arguments, location and whether the location is
occupied by a cell, and its table representation has two
corresponding columns. It’s a base table; it retains its
state from tick to tick except as specified by the Set()
methods at the end. We will talk about .Set() and the
the .Key and .Indexed annotations shortly.</p>
        <p>The second statement also defines a table and
stores it in a C# variable, NeighborCells. This table
maps locations in the grid to the number of cells
surrounding them. Unlike Grid, this statement calls
the predicate’s .If() method to add a rule to the
predicate: NeighborCells[loc,count] is true if:
•
•</p>
        <p>Grid[loc, __], i.e. loc is a location on the
board (__ignores that argument), and
count==Count(And[…]), i.e. count is the
number of solutions to the And[…] query,
which finds neighbors of loc that have cells.
Since NeighborCells has a rule, it’s a derived table;
its table is erased and recomputed on each tick.</p>
        <p>The last two statements call the .Set() method of
the Grid predicate. The Set() method creates and
returns a new predicate, which is a table of rows to be
updated. The two calls create two such tables. The
.If() method called at the ends of the statements
adds rules to those tables, causing it to recompute
those tables, and hence the grid locations to modify, on
each tick, based on their respective rules. The first rule
says to set the occupied column of any location with
less than 2 or more than 3 neighbors to false; it “kills”
the cell. The second rule says to set the occupied
column for locations with exactly 3 neighbors to true;
it spawns cells.</p>
      </sec>
      <sec id="sec-4-2">
        <title>4.1.1. Naïve execution</title>
        <p>The control flow of the overall program is as follows:
repeat forever
recompute NeighborCells based on Grid
recompute table of cells to destroy
recompute table of cells to create
update Grid based on update tables
Let’s look in detail at the rule in the third
statement, which says to kill a cell at location loc if:
•
•
•</p>
        <sec id="sec-4-2-1">
          <title>Grid[loc,true]</title>
          <p>There’s a cell at loc,</p>
        </sec>
        <sec id="sec-4-2-2">
          <title>Neighbors[loc,count]</title>
          <p>It’s neighbor count is count,
(count&lt;2|count&gt;3)</p>
          <p>And the count is outside the desired range</p>
          <p>Remember this is making a table of cells to kill that
will be rebuilt each tick. The naïve execution algorithm
for this would be:
clear the table
for each (loc1, occupied) in Grid
if occupied == true
for each (loc2, count)</p>
          <p>in NeighborCells
if loc1==loc2
if (count&lt;2||count&gt;3)</p>
          <p>add loc1 to the table
On the positive side, the .If() rule is considerably
more compact than the equivalent code above, which
is generally a good thing. However, it exhaustively
searches the NeighborCells table each time it tries
to look up the count for a specific location; for  grid
cells, the algorithm is  ( 2). So on the face of it, the
logic program is a terrible idea.</p>
        </sec>
      </sec>
      <sec id="sec-4-3">
        <title>4.1.2. Declarative optimization</title>
        <p>
          We can speed this rule up by indexing the tables so
they don’t need to be scanned. The .Key and
.Indexed annotations in the declarations of Grid an
NeighborCells tell the system to index the tables by
the specified columns. In the .Key case, the annotation
also promises that the values in that column will be
unique; no two rows can have the same key. Key
indices map column values to single rows. Non-key
indices map values to sets of rows. Both rows list
location as a key; given a location, we can find its row
in  (1) time. They also index their second column; we
can get the sets of rows with/without cells or rows
with a given number of neighbors, in  (1) time. Using
indexing, the rule above effectively executes as:
clear the table
foreach (loc, _) in Grid.Index[
          <xref ref-type="bibr" rid="ref1">1</xref>
          ][true]
(_, c) = NeighborCells.Index[0][loc]
if (c&lt;2||c&gt;3)
        </p>
        <p>add loc to the table</p>
        <p>
          Here, predicate.Index[columnNumber] is the
index for the specified column. It’s a hash table
mapping column values to rows (key index) or linked
lists of rows (non-key). So Grid.Index[
          <xref ref-type="bibr" rid="ref1">1</xref>
          ][true] is
the list of all rows whose second column is true, and
NeighborCells.Index[0][loc] is the unique row
that has loc as its first column. Whereas, the previous
version ran in  ( 2) time, this runs in  ( ) time where
 is the number of cells, a dramatic speedup.
        </p>
        <p>While still not the preferred way to implement Life,
it’s efficient enough to run at 120Hz on a single core of
an i9-9900K, including Unity’s graphics code.</p>
        <p>Logic programming lets us write loops
declaratively: the iteration structure is implicit in the
conditions listed in a rule. By precomputing results
and storing them in tables, bottom-up logic
programming lets us optimize declaratively. Indices
can be added to tables incrementally during
development, as new access patterns are introduced.
Crucially, adding indices requires only adding an
annotation to the predicate declaration; no other
action is required. Rules need no modification.</p>
      </sec>
      <sec id="sec-4-4">
        <title>4.2. Structure of a TED program</title>
        <p>As discussed, a TED program is a C# program builds
the run-time representation of the TED program, then
calls into it as necessary. Declarations create syntax
trees representing the code, then preprocess them to
create the run-time representation used by the
interpreter. Predicates, calls, rules, etc. are all
represented at run-time as typed C# objects.</p>
        <p>TED relies on liberal use of operator overloading to
make TED code look as natural as possible, even
though it is “really” a series of constructor calls for
syntax trees. Square brackets denote calls to
predicates (C# allows the bracket operator to be
overloaded, but not the call operator).</p>
      </sec>
      <sec id="sec-4-5">
        <title>4.2.1. Terms and variables</title>
        <p>Following the terminology used in logic and logic
programming, the expressions used as arguments to
predicates are known as terms.</p>
        <p>Since, again, TED code is really C# code that builds
the syntax tree for the TED code to be executed, a term
such as x+1 is represented as a data structure such as:
var x = new Var&lt;int&gt;("x");
new FunctionalExpression&lt;int&gt;(Add, x,
new Constant&lt;int&gt;(1))
However, overloading allows the programmer to type
x+1 and have it converted to the constructor call
above. The programmer can largely ignore the
internal representation.</p>
        <p>The one place the programmer does need to be
aware of terms and their data types is with variables.
As shown above, a TED variable is represented as a C#
object of type Var&lt;T&gt; where T is the type of the
variable’s value. Before using a variable in a rule:</p>
        <p>P[x].If(Q[x]);
which states that P is true of a value x if Q is true of it,
the programmer needs to first define x as a C# variable
containing a TED variable, as with the declaration
above. Since this is somewhat cumbersome, it can be
shortened to:</p>
        <p>var x = (Var&lt;int&gt;)"x";
The declaration specifies both the name of the variable
and its type. Once a variable is defined, it can be used
in multiple rules, but is treated as a separate local
variable for each rule. While the declaration syntax is
annoying, one can keep the number of variable
declarations to a manageable level.</p>
        <p>Note that the program fragment shown in figure 2,
uses the variables loc, neighbor, occupied, and
count. The declarations for these were withheld until
now and are as follows (Vector2Int is Unity’s
standard data type for grid locations):
var loc = (Var&lt;Vector2Int&gt;)"loc";
var neighbor = (Var&lt;Vector2Int&gt;)"…";
var occupied = (Var&lt;bool&gt;)"occupied";
var count = (Var&lt;int&gt;)"count";</p>
      </sec>
      <sec id="sec-4-6">
        <title>4.2.2. Predicates</title>
        <p>Predicates are C# objects of type Predicate&lt;T1, T2,
…, Tn&gt;, where  is the number of arguments to the
predicate and the   are their respective types. The
predicates we’ve discussed so far are table
predicates. They are represented as tables, and
queries to them are implemented as searches on the
table and its indices. Table predicates have a .If()
method to add rules to them. Table predicates are
created using the Predicate method:</p>
        <p>Predicate(name, arg1, arg2, …, argn)
where name is a string used for identifying the
predicate in error messages, and the argi are variables
with the desired types for each argument (these jointly
define the type of the predicate). Thus the declaration:
var Grid=Predicate("Grid",loc,occupied);
sets Grid to a Predicate&lt;Vector2Int, bool&gt;, i.e. a
predicate with Vector2Int and a bool arguments.</p>
        <p>In addition to table predicates, there are primitive
predicates, which are directly implemented as C#
methods. TED includes many built-in primitives, such
as the &lt; operator, used in figure 2.</p>
        <p>Finally, TED allows definitions, predicates defined
by rules that are inlined into any calls. For example,
var CellAt = Definition(“CellAt”, loc)
.If(Grid[loc, true])
states that CellAt is a predicate over a Vector2Int,
but queries of the form CellAt[ ] should be replaced
by the query Grid[ , true].</p>
      </sec>
      <sec id="sec-4-7">
        <title>4.2.3. Goals and rules</title>
        <p>Procedure calls in Prolog and TED are called goals.
Applying a predicate to a set of terms of the correct
types returns a Goal object: the syntax tree for a call.
Goals for table predicates include an If(Goal…)
method that takes a series of other Goals as
arguments, constructs a rule from them, and adds the
rule to the original Goal’s predicate. Our example
declaration:</p>
        <p>P[x].If(Q[x]);
creates a rule object stating that ∀ .  ( ) →  ( ), and
adds it to the list of P’s rules. For convenience, table
predicates also have their own If methods, allowing
rules to be combined with predicate definition. Thus:
var P = Predicate("P", x).If(Q[x]);
is equivalent to:
var P = Predicate("P", x);</p>
        <p>P[x].If(Q[x]);</p>
      </sec>
      <sec id="sec-4-8">
        <title>4.2.4. Higher-order predicates</title>
        <p>Higher-order predicates are predicates parameterized
by goals or other predicates. TED includes a number
of these, as well as facilities for defining one’s own.
Table predicates cannot be higher order.</p>
        <p>Definitions and primitive predicates can be made
higher order, simply by having one of their arguments
be of type Goal. TED includes a number of built-in
higher-order primitives:
•
•
•
•</p>
        <p>Logical connectives: And[], Or[], and Not[]
Optimization primitives: Maximal and
Minimal, as used in Figure 1.</p>
        <p>Flow-control predicates that execute the goal
in some modified manner, such as Once[].</p>
        <p>Aggregation functions: Count, Sum, and
Aggregate as used in Figure 2.</p>
        <p>User-defined higher-order primitive predicates
are allowed but are currently more involved to write
than other user-defined primitives.</p>
      </sec>
      <sec id="sec-4-9">
        <title>4.2.5. Table operators</title>
        <p>Operators map tables to tables. They encapsulate
algorithms that execute over a table as a whole,
returning a new table as a result. One example is
CountsBy, which makes pivot tables. If the table
Population is a table of characters in the game and it
has a column called sex, then the declaration:
var Demographics =</p>
        <sec id="sec-4-9-1">
          <title>CountsBy("Demographics",</title>
        </sec>
        <sec id="sec-4-9-2">
          <title>Population, sex, count);</title>
          <p>defines a new Predicate&lt;sex,int&gt;, listing the
number of characters with each sex in the current step
of the simulation.</p>
          <p>Most other operators encapsulate graph
algorithms. For example, if R is a table predicate of
type Predicate&lt;T,T&gt; for some T, for example,
representing the edges in a graph whose vertices are
objects of type T, then:</p>
          <p>var RStar = Closure("R*", R);
makes a new predicate, RStar, also of type
Predicate&lt;T,T&gt;, such that RStar[ , ] holds iff  is
reachable from  via edges in R.</p>
          <p>A number of operators implement different forms
of graph matching. If Interested is a
Predicate&lt;Person,Person&gt; describing who is
interested in dating whom, then:</p>
          <p>var D = MatchRandomly("D", Interested);
makes a new table, D, that on any step of the simulation
contains a subset of the rows of Interested such that
no person is listed in two different rows. If we add
another column to Interested specifying a level of
interest, then:</p>
          <p>var D = MatchGreedily("D", Interested);
will attempt to choose a matching with the highest
interest levels possible (although it not necessarily
globally optimal).</p>
          <p>If Interested is a relation not between people,
but between people and jobs (so it is of type
Predicate&lt;Person,Job,float&gt;), and if Capacity
is a Predicate&lt;Job,int&gt; listing how many openings
there are for each Job, then
var E = AssignGreedily("A",</p>
        </sec>
        <sec id="sec-4-9-3">
          <title>Interested, Capacities);</title>
          <p>makes a new table, E, matching people to jobs with the
highest possible interest level, without assigning more
to a job than there are openings.</p>
        </sec>
      </sec>
      <sec id="sec-4-10">
        <title>4.2.6. Table update</title>
        <p>Base tables can be updated by providing tables of
changes to perform.</p>
        <p>If  is a base table of type Predicate&lt;T1, …, Tn&gt;,
then  .Add is a table of the same type whose rows are
appended to  at the end of each simulation step.
Thus,  .Add.If(…), which adds a rule to  .Add,
effectively specifies a rule for when to add a row to  .
There is not presently a  .Delete, but there are plans
to add it.</p>
        <p>Individual columns can be changed by providing
tables of changes to make.  .Set(key,
updateColumn), where key and updateColumn are
columns of  , i.e. variables specified in  ’s definition,
returns a table with the columns key and
updateColumn. At the end of each update step, the
system will iterate through the rows of the  .Set
table, and for each row, use  ’s index to find the row in
 with the specified key, then change the value of that
row’s updateColumn to the value listed in the  .Set
table. Thus, the rule:</p>
        <p>Population.Set(who, status)</p>
        <p>.If(Died(who), status=Status.Dead)
Would update the status column of the Population
table for someone who dies to Status.Dead.</p>
      </sec>
      <sec id="sec-4-11">
        <title>4.2.7. Invariant checking</title>
        <p>Every TED program has two built-in base tables.
Exceptions lists all the exceptions that have been
thrown while running rules, together with the tables
and rules that threw them. Problems lists invariants
and other assertions that have been violated. To
declare an invariant, simply write a rule of the form:</p>
        <sec id="sec-4-11-1">
          <title>Table.Problem.If(…);</title>
          <p>If the rule ever succeeds, it will add a line to the
Problems table listing Table, the rule, and the values
of all variables in the rule. Problem rules are like
assertions in other languages in that checking of them
can be enabled and disabled and they impose no
runtime penalty when disabled. Unlike most languages,
however, TED problem rules can be enabled and
disabled at run-time without recompilation.</p>
        </sec>
      </sec>
    </sec>
    <sec id="sec-5">
      <title>5. Implementation</title>
      <p>TED is highly optimized. Most code can run
without run-time type checks or storage allocation,
apart from the initial allocation or reallocation of the
tables themselves.</p>
      <sec id="sec-5-1">
        <title>5.1. Table representation</title>
        <p>Table data for a Predicate&lt;T1, …, Tn&gt; is stored in
a packed array of tuples of type (T1, …, Tn), one per
table row. Tuples are value types; they are stored
inline in the array, rather than separately represented in
the heap. This means table data is stored as a
contiguous sequence of bytes, without boxing.
Moreover, tables are almost always scanned in order,
so table operations have best-case cache locality.</p>
        <p>Table operations are optimized to avoid copying of
value types. Generics are used for most table
operations, to avoid the need for boxing or run-time
type checking. Compiled code therefore looks largely
like what one would get with hand-written C code,
apart from the use of out-of-line calls for things like
equality comparison.</p>
        <p>For derived tables, which are recomputed on each
simulation step, the array is reused from step to step.
When table data overflows the array, the array size is
doubled, guaranteeing amortized  (1) performance.</p>
        <p>Indices are implemented as custom hash tables
mapping keys to row numbers. To maximize cache
performance, the current implementation uses direct
addressing with linear probing. To avoid clustering,
the hash tables keep their load factors below 0.5,
making clustering unlikely. Indices use 8 bytes per
row per index, for key indices, 12 for general indices.</p>
        <p>Tables can also be declared to have unique rows
(i.e. they are sets rather than multisets). They
maintain a hash set of all rows and ignore duplicate
additions.</p>
      </sec>
      <sec id="sec-5-2">
        <title>5.2. Rule execution</title>
        <p>A rule, created using the If() method, is specified
by a Goal object, known as its head, that forms its
conclusion, and a sequence of Goals, known as its
body, that form its conditions. The actual internal
representation of a rule consists of a series of iterators
for each goal in the body. Rules are transformed into
their iterators through a preprocessing mechanism.</p>
      </sec>
      <sec id="sec-5-3">
        <title>5.2.1. Unification: read-mode and write-mode</title>
        <p>
          Goals, such as P[x, y, 7], are matched against tuples
in tables using unification [
          <xref ref-type="bibr" rid="ref20">20</xref>
          ], which computes the
solution to a set of simultaneous equations over terms.
In its usual form in languages such as Prolog,
unification can equate variables to other variables.
Thus, Robinson’s original unification algorithm [
          <xref ref-type="bibr" rid="ref21">21</xref>
          ]
dynamically computes an equivalence relation over
variables using the disjoint set partition algorithm
[
          <xref ref-type="bibr" rid="ref22">22</xref>
          ]. This requires variables to store forwarding
pointers when they are equated to some other
variable. Looking up the value of a variable involves
looping over forwarding pointers until an unaliased
variable is found. Moreover, equations between
variables must be backtrackable, requiring a
mechanism for undoing the aliasing.
        </p>
        <p>In Datalog-like languages, including TED,
unification is only performed between goals and table
tuples that cannot contain variables. Variables can
therefore only be unified with data values, not other
variables, removing the need to track aliases.</p>
        <p>Variables therefore behave much like C variables;
they are simply typed locations in memory. The first
time a variable is unified with a value, that value is
stored in the location. This is referred to as write
mode. Subsequent uses of the variable later in the
rule, where the variable is unified with other data
values, is implemented by testing equality between the
previously stored value and the new value. This is
referred to as read mode.</p>
        <p>For example, the goal P[x, x, 1] can be unified
with a tuple ( ,  ,  ) from P’s table by first storing  in
x, then testing if that stored value is equal to  , and
finally testing whether  = 1. It’s essentially
equivalent to the C# code:
bool CanUnifyXX1(int a, int b, int c) {
x = a;
return x == b &amp;&amp; c == 1;
}
Running this against a particular  ,  ,  , tests if they’re
unifiable, while updating x to its new value. It requires
no binding lists, pointer chasing, or type checking. It’s
much faster than the general unification algorithm.</p>
        <p>This is the general approach used for unification in
TED. The body of a rule is scanned and the first
(leftmost) occurrence of each variable is found and
marked write mode. All other variable occurrences, as
well as constants, are read mode. A goal unifies with a
tuple if each goal argument unifies with its respective
tuple element. A write-mode variable aways
successfully unifies, and updates the variable. A
readmode variable or constant unifies with a value only if
they are equal.</p>
      </sec>
      <sec id="sec-5-4">
        <title>5.2.2. Body normalization</title>
        <p>The first step of preprocessing is to reduce the rule
body to a normal form in which:
•
•
•</p>
        <p>Functional expressions are hoisted out of
calls to predicates other than the built-in
primitive predicate Eval[]. Thus, P[x+1] is
transformed to: And[Eval[t,x+1],P[t]]
Calls to And[] and Or[] are flattened; that is,
And[a, And[b, c]] is simplified to And[a,
b, c].</p>
        <p>Any goals that can be partially evaluated are
reduced to simpler goals. If their truth values
are known at preprocessing time, they are
replaced with true or false. This includes
simplifying And, Or, and Not. It is possible for
a body to simplify down to just false, in
which case an warning is printed.</p>
      </sec>
      <sec id="sec-5-5">
        <title>5.2.3. Iterator selection</title>
        <p>Finally, the Goals of the body, which again are just
syntax trees, are mapped to iterators to be executed at
runtime. For goals involving primitive predicates, the
primitive implements its own custom iterator. The
preprocessor leaves it to the primitive to choose it.</p>
        <p>For table predicates, the iterator must iterate
through the rows of the table, unify them with the
arguments in the goal, and generate only the ones that
match. The preprocessor attempts to choose iterators
that use indices when possible. In decreasing order of
preference, these are:
•
•
•
•</p>
        <p>If the table is declared to have unique rows and
all goal arguments are read-mode, then the goal is
implemented as a single test against the table’s
hash-set of rows, with no iteration or unification.
If the table has a key index for a read-mode
argument in the goal, the goal is implemented as a
lookup of this row and a single test of whether it
unifies with the arguments.</p>
        <p>If the table has a non-key index for a read-mode
argument, the goal is implemented as lookup of
the linked list of rows with the specified column
value, followed by an iteration over those rows,
unifying them with the arguments.</p>
        <p>If no index is available, the goal is implemented
as an iteration over all rows of the table,
attempting to unify each with the goal arguments.
There is no hinting mechanism to allow the
programmer to advise the system on how to choose
between multiple non-key indices at present. Nor is
there a way to make combined indices over multiple
columns. However, these are planned additions.</p>
      </sec>
      <sec id="sec-5-6">
        <title>5.2.4. Iterator sequencing</title>
        <p>Execution consists of running the iterators in order.
When an iterator succeeds, it updates any write-mode
variables it unified with the selected table row, and
execution proceeds to the next iterator. When an
iterator fails, execution backs up to the previous
iterator which generates its next match, if any.</p>
        <p>When the last iterator succeeds, all variables have
been matched to values during the unification process.
The system forms a tuple from the arguments in the
head goal, fills it in with the values of the relevant
variables, and appends it to the table. It then returns
to the last goal’s iterator to generate the next tuple,
which may involve the last iterator failing, and asking
for the next solution from the previous iterator, which
may fail, etc.</p>
        <p>When the first iterator fails, the rule has generated
all its tuples for the predicate and execution is
complete.</p>
      </sec>
      <sec id="sec-5-7">
        <title>5.3. Simulation control flow</title>
        <p>Simulation proceeds by repeatedly recomputing
derived predicates, then updating base predicates.
Recomputation of a derived predicate works by
recursively updating the predicates used its rules, if
they haven’t already been updated, then recomputing
the table for the derived predicate. Updates of base
tables are specified by update tables, which are
derived tables defined by rules, as discussed
previously.</p>
        <p>
          Bottom-up logic programming can support
recursive rules at the cost of a significantly more
complicated algorithm (a fixed-point iteration [
          <xref ref-type="bibr" rid="ref23">23</xref>
          ]).
The classical use case for recursion in Datalog is
translative closure a binary relation (e.g. reachability
in a graph). However, TED can compute reachability in
an undirected graph in  ( +  ) time and  ( ) space
using its EquivalenceClass operator, whereas the
classical recursive solution in Datalog requires at least
 ( 3) time and  ( 2) space, depending on the
algorithm used and the ability of the system to index
the relevant tables. We have deferred supporting
recursion until we have a compelling use case.
        </p>
      </sec>
      <sec id="sec-5-8">
        <title>5.4. Parallel execution</title>
        <p>Alternatively, TED programs can be executed in
parallel. Each predicate is updated in a separate task.
Once a table is updated, all further access are
readonly, so parallel update requires no locking or other
mutual exclusion. The “continuation” feature of the C#
task parallelism library is sufficient to guarantee tasks
are not scheduled until the tasks they depend on have
been completed, so there is no explicit blocking.</p>
        <p>Since each predicate is updated by a single task, the
level of parallelism depends on the number of
predicates in the program and the length of their serial
dependencies.</p>
      </sec>
    </sec>
    <sec id="sec-6">
      <title>6. Performance</title>
      <p>
        TED is currently used in two projects. It is being used
for consistency-checking the asset database of an
unannounced commercial game. It was chosen over
Unity Prolog [
        <xref ref-type="bibr" rid="ref24">24</xref>
        ] because of its strong typing, and
because its embedded design made interoperability
with C# easier. However, it is not being used for
character simulation thus far.
      </p>
      <p>
        The main project using TED is Voix de la Ville, a
declarative reimplementation of a subset of Ryan’s
Talk of the Town city simulator [
        <xref ref-type="bibr" rid="ref14">14</xref>
        ]. The current
system supports small hundreds of characters
interacting with one another in a city with 44 different
kinds of buildings and 62 different kinds of
professions. Single-core execution times on a single
core of an i7-7700K running at 4.8GHz are show in
figure 3. These are per capita execution times, i.e. the
execution time of a simulation tick, divided by the
population. They represent a combination of
character updates, which are fixed cost per character,
and per-relationship updates, which are inherently
quadratic. The core simulation is ~500 lines of code,
including comments.
      </p>
    </sec>
    <sec id="sec-7">
      <title>7. Related work</title>
      <p>
        Many AI-based games have used symbolic rules for
character control. MKULTRA [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ] was written primarily
in Prolog, save for the graphics and UI code. City of
Gangsters [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ] used another top-down logic
programming language, albeit with an exotic
implementation. Many other games have used some
kind of rule engine. Façade [
        <xref ref-type="bibr" rid="ref25">25</xref>
        ] was implemented
primarily in a reactive planning language, ABL [
        <xref ref-type="bibr" rid="ref26">26</xref>
        ],
however its internal working memory included a
forward-chaining production system. Similarly, Prom
Week [
        <xref ref-type="bibr" rid="ref18">18</xref>
        ] used a forward-chaining production system
implemented in Javascript. The Sims 3 [
        <xref ref-type="bibr" rid="ref27">27</xref>
        ] also used a
rule-based system to script the interactions between
situations, personality traits, and actions available to a
given character [
        <xref ref-type="bibr" rid="ref28">28</xref>
        ].
      </p>
      <p>
        Several game development frameworks and social
simulation middleware have used symbolic rules,
particular for interactive narrative. One of the earliest
and most influential such systems is the Nelson’s
Inform 7 language [
        <xref ref-type="bibr" rid="ref4">4</xref>
        ], [
        <xref ref-type="bibr" rid="ref29">29</xref>
        ], which allows designers to
build interact narrative systems, particularly
simulationist systems, using declarative statements.
The Versu simulationist narrative system [
        <xref ref-type="bibr" rid="ref30">30</xref>
        ], [
        <xref ref-type="bibr" rid="ref31">31</xref>
        ]
used a custom logic programming language, Praxis,
which was based on an exotic modal logic called
eremic logic (aka exclusion logic) [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ]. More recently,
the Lume system [
        <xref ref-type="bibr" rid="ref32">32</xref>
        ] made extensive use of Prolog’s
definite clause grammars [
        <xref ref-type="bibr" rid="ref33">33</xref>
        ], [
        <xref ref-type="bibr" rid="ref34">34</xref>
        ] for text generation.
Lapeyrade has also used Prolog for better character
decision making [
        <xref ref-type="bibr" rid="ref35">35</xref>
        ]
      </p>
      <p>
        Several systems have used forward-chaining
rulebased systems, including ABL, Comme Il Faut [
        <xref ref-type="bibr" rid="ref5">5</xref>
        ], the
3 Tarn Adams, personal communication.
social simulation engine upon which Prom Week was
built, and the Ensemble Engine [
        <xref ref-type="bibr" rid="ref36">36</xref>
        ], CiF’s successor.
      </p>
      <p>
        To our knowledge, bottom-up logic programming
has not previously been used to implement social
simulations. However, Datalog has been used for
storysifting [
        <xref ref-type="bibr" rid="ref15">15</xref>
        ], the process of searching the output of a
social simulator for interesting narrative content.
      </p>
      <p>
        Bottom-up logic programming, and Datalog in
particular, has received the most attention in the
database community [
        <xref ref-type="bibr" rid="ref13">13</xref>
        ], [
        <xref ref-type="bibr" rid="ref37">37</xref>
        ], [
        <xref ref-type="bibr" rid="ref38">38</xref>
        ], where its appeal
came partly from the ability to compile it into
relational algebra operators for efficient execution on
classical database architectures, and because it can be
extended to recursive rules using a fixed-point
evaluation algorithm. This allows it to compute
transitive closure (e.g. reachability in a graph), which
standard relational algebra cannot. This is where most
of the original research on the language and its
implementation was done. More recently, it has seen
extensive use for the semantic web [
        <xref ref-type="bibr" rid="ref39">39</xref>
        ].
      </p>
      <p>
        Games involving large-scale social simulation are
relatively rare. The best known is Dwarf Fortress [
        <xref ref-type="bibr" rid="ref40">40</xref>
        ],
which supports real-time simulator of small hundreds
of characters. Achieving this level of performance
requires implementation in C++ and significant
programmer effort to optimize cache locality and
minimize the number of pointer indirections.3
RimWorld is very similar game that also involves social
simulation for the purpose of storytelling [
        <xref ref-type="bibr" rid="ref41">41</xref>
        ]. In the
research literature, the best known system is Ryan’s
Talk of the Town, [
        <xref ref-type="bibr" rid="ref14">14</xref>
        ], which was used in the
awardwinning game Bad News [
        <xref ref-type="bibr" rid="ref16">16</xref>
        ], [
        <xref ref-type="bibr" rid="ref17">17</xref>
        ]. TotT was a batch
simulation of the growth of a small American town
over the course of 140 years, ending with population
around 400 people using a time-varying level of detail.
It was implemented in Python and required many
minutes to simulate a city. More recently, Johnson-Bey
has developed Neighborly [
        <xref ref-type="bibr" rid="ref42">42</xref>
        ], a more modular and
modifiable implementation based on an
entitycomponent-system architecture [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ]. With the possible
exception of RimWorld, these systems run the
simulation in a single thread.
      </p>
      <p>
        Kismet [
        <xref ref-type="bibr" rid="ref43">43</xref>
        ] is a rapid-prototyping system for social
simulations intended for casual users. It used
answerset programming (a type of logic programming)
internally. However, its focus was on allowing casual
users to build social simulations, rather than on trying
to maximize performance.
      </p>
    </sec>
    <sec id="sec-8">
      <title>8. Conclusion</title>
      <p>TED is a high-performance, embedded, parallelizable,
logic programming system that allows game designers
to quickly and conveniently implement large-scale
social simulations and run them quickly on modern,
multi-core architectures. It allows story-sifting and
simulation to be written in the same language. The use
of tables to store all intermediate results aids
debugging by making all intermediate results
inspectable and queryable at run-time.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>R.</given-names>
            <surname>Zubek</surname>
          </string-name>
          , “
          <string-name>
            <surname>Needs-Based</surname>
            <given-names>AI</given-names>
          </string-name>
          ,”
          <article-title>in Game Programming Gems 8</article-title>
          ,
          <string-name>
            <given-names>Cengage</given-names>
            <surname>Learning</surname>
          </string-name>
          <string-name>
            <surname>PTR</surname>
          </string-name>
          ,
          <year>2010</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>W.</given-names>
            <surname>Wright</surname>
          </string-name>
          , “The Sims.” MAXIS/Electronic Arts,
          <year>2000</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          <article-title>[3] “Entity Systems are the future of MMOG development - Part 1 - T-machine</article-title>
          .org,” Jul.
          <volume>31</volume>
          ,
          <year>2013</year>
          . https://new.tmachine.org/index.php/
          <year>2007</year>
          /09/03/entitysystems
          <article-title>-are-the-future-of-mmog-developmentpart-1/ (accessed Jul</article-title>
          .
          <volume>18</volume>
          ,
          <year>2023</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>G.</given-names>
            <surname>Nelson</surname>
          </string-name>
          , “Inform 7.”
          <year>2006</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>J.</given-names>
            <surname>McCoy</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Treanor</surname>
          </string-name>
          ,
          <string-name>
            <given-names>B.</given-names>
            <surname>Samuel</surname>
          </string-name>
          , N. WardripFruin, and M. Mateas, “
          <article-title>Comme il Faut: A System for Authoring Playable Social Models,” in Proceedings of the 7th AI</article-title>
          and
          <string-name>
            <surname>Interactive Digital Entertainment</surname>
            ,
            <given-names>V.</given-names>
          </string-name>
          <string-name>
            <surname>Bulitko</surname>
            and
            <given-names>M. O.</given-names>
          </string-name>
          <string-name>
            <surname>Riedl</surname>
          </string-name>
          , Eds., Stanford, CA: AAAI Press,
          <year>2011</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>R.</given-names>
            <surname>Evans</surname>
          </string-name>
          , “
          <article-title>Introducing Exclusion Logic as a Deontic Logic,” in Deontic Logic in Computer Science</article-title>
          ,
          <source>Proceedings of the 10th International Conference, DEON 2010, Lecture Notes in Computer Science</source>
          Volume
          <volume>6181</volume>
          ,
          <string-name>
            <surname>Fiesole</surname>
          </string-name>
          , Italy: Springer,
          <year>2010</year>
          , pp.
          <fpage>179</fpage>
          -
          <lpage>195</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <surname>I. Horswill</surname>
          </string-name>
          , “Postmortem:
          <string-name>
            <given-names>MKULTRA</given-names>
            ,
            <surname>An Experimental AI-Based</surname>
          </string-name>
          <string-name>
            <surname>Game</surname>
          </string-name>
          ,” AIIDE, vol.
          <volume>14</volume>
          , no.
          <issue>1</issue>
          , pp.
          <fpage>45</fpage>
          -
          <lpage>51</lpage>
          , Sep.
          <year>2018</year>
          , doi: 10.1609/aiide.v14i1.
          <fpage>13027</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8] SomaSim, “City of Gangsters.” Chicago,
          <year>2021</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          [9]
          <string-name>
            <given-names>D. H. D.</given-names>
            <surname>Warren</surname>
          </string-name>
          ,
          <string-name>
            <given-names>L. M.</given-names>
            <surname>Pereira</surname>
          </string-name>
          , and
          <string-name>
            <given-names>F.</given-names>
            <surname>Pereira</surname>
          </string-name>
          , “
          <article-title>PROLOG - The Language and its implementation compared with LISP,” in Symposium on AI and Programming Languages</article-title>
          ,
          <string-name>
            <surname>ACM</surname>
          </string-name>
          ,
          <year>1977</year>
          , pp.
          <fpage>109</fpage>
          -
          <lpage>115</lpage>
          . doi:
          <volume>10</volume>
          .1145/800228.806939.
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          [10]
          <string-name>
            <given-names>W. F.</given-names>
            <surname>Clocksin</surname>
          </string-name>
          and
          <string-name>
            <given-names>C. S.</given-names>
            <surname>Mellish</surname>
          </string-name>
          ,
          <article-title>Programming in Prolog: Using the ISO Standard</article-title>
          . New York, NY: Springer,
          <year>2003</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref11">
        <mixed-citation>
          [11]
          <string-name>
            <surname>M. H. Van Emden</surname>
            and
            <given-names>R.</given-names>
          </string-name>
          <article-title>a</article-title>
          . Kowalski, “
          <article-title>The Semantics of Predicate Logic as a Programming Language,”</article-title>
          <source>Journal of the ACM</source>
          , vol.
          <volume>23</volume>
          , no.
          <issue>4</issue>
          , pp.
          <fpage>733</fpage>
          -
          <lpage>742</lpage>
          ,
          <year>1976</year>
          , doi: 10.1145/321978.321991.
        </mixed-citation>
      </ref>
      <ref id="ref12">
        <mixed-citation>
          [12] “
          <article-title>Bottom-up beats top-down for datalog | Proceedings of the eighth ACM SIGACT-SIGMODSIGART symposium on Principles of database systems</article-title>
          .” https://dl.acm.org/doi/10.1145/73721.73736 (
          <issue>accessed</issue>
          <year>Jul</year>
          .
          <volume>21</volume>
          ,
          <year>2023</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref13">
        <mixed-citation>
          [13]
          <string-name>
            <given-names>S.</given-names>
            <surname>Ceri</surname>
          </string-name>
          , G. Gottlob, and L. Tanca, “
          <article-title>What you always wanted to know about Datalog (and never dared to ask</article-title>
          ),
          <source>” IEEE Transactions on Knowledge and Data Engineering</source>
          , vol.
          <volume>1</volume>
          , no.
          <issue>1</issue>
          , pp.
          <fpage>146</fpage>
          -
          <lpage>166</lpage>
          , Mar.
          <year>1989</year>
          , doi: 10.1109/69.43410.
        </mixed-citation>
      </ref>
      <ref id="ref14">
        <mixed-citation>
          [14]
          <string-name>
            <given-names>J.</given-names>
            <surname>Ryan</surname>
          </string-name>
          , “Curating Simulated Storyworlds,” University of California Santa Crus,
          <year>2018</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref15">
        <mixed-citation>
          [15]
          <string-name>
            <given-names>M.</given-names>
            <surname>Kreminski</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Dickinson</surname>
          </string-name>
          , and N. WardripFruin, “Felt:
          <string-name>
            <given-names>A Simple</given-names>
            <surname>Story</surname>
          </string-name>
          <string-name>
            <surname>Sifter</surname>
          </string-name>
          ,” R. E. CardonaRivera,
          <string-name>
            <given-names>A.</given-names>
            <surname>Sullivan</surname>
          </string-name>
          , and
          <string-name>
            <given-names>R. M.</given-names>
            <surname>Young</surname>
          </string-name>
          , Eds.,
          <source>in Lecture Notes in Computer Science</source>
          , vol.
          <volume>11869</volume>
          . Cham: Springer International Publishing,
          <year>2019</year>
          , pp.
          <fpage>267</fpage>
          -
          <lpage>281</lpage>
          . doi:
          <volume>10</volume>
          .1007/978-3-
          <fpage>030</fpage>
          -33894- 7_
          <fpage>27</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref16">
        <mixed-citation>
          [16]
          <string-name>
            <given-names>J. O.</given-names>
            <surname>Ryan</surname>
          </string-name>
          ,
          <string-name>
            <given-names>B.</given-names>
            <surname>Samuel</surname>
          </string-name>
          ,
          <article-title>and</article-title>
          <string-name>
            <given-names>A.</given-names>
            <surname>Summerville</surname>
          </string-name>
          , “Bad News 
          <article-title>: A Game Of Death And Communication</article-title>
          ,” pp.
          <fpage>160</fpage>
          -
          <lpage>163</lpage>
          ,
          <year>2016</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref17">
        <mixed-citation>
          [17]
          <string-name>
            <given-names>B.</given-names>
            <surname>Samuel</surname>
          </string-name>
          ,
          <string-name>
            <given-names>J.</given-names>
            <surname>Ryan</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A. J.</given-names>
            <surname>Summerville</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Mateas</surname>
          </string-name>
          , and
          <string-name>
            <given-names>N. W.</given-names>
            <surname>Fruin</surname>
          </string-name>
          , “
          <article-title>Bad news: An experiment in computationally assisted performance</article-title>
          ,
          <source>” in Lecture Notes in Computer Science (including subseries Lecture Notes in Artificial Intelligence and Lecture Notes in Bioinformatics)</source>
          ,
          <year>2016</year>
          . doi:
          <volume>10</volume>
          .1007/978-3-
          <fpage>319</fpage>
          -48279-8_
          <fpage>10</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref18">
        <mixed-citation>
          [18]
          <string-name>
            <surname>J. McCoy</surname>
            ,
            <given-names>M.</given-names>
          </string-name>
          <string-name>
            <surname>Treanor</surname>
            ,
            <given-names>B.</given-names>
          </string-name>
          <string-name>
            <surname>Samuel</surname>
          </string-name>
          ,
          <article-title>and</article-title>
          <string-name>
            <given-names>A. A.</given-names>
            <surname>Reed</surname>
          </string-name>
          , “Prom Week.” Expressive Inteligence Studio at UC Santa Cruz, Santa Cruz, California,
          <year>2012</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref19">
        <mixed-citation>
          [19]
          <string-name>
            <given-names>M.</given-names>
            <surname>Gardner</surname>
          </string-name>
          , “
          <article-title>Mathematical Games - The fantastic combinations of John Conway's new solitaire game 'life</article-title>
          ,'” Scientific American, no.
          <issue>223</issue>
          ,
          <string-name>
            <surname>Oct</surname>
          </string-name>
          .
          <year>1970</year>
          , doi: doi:10.1038/scientificamerican1070-
          <fpage>120</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref20">
        <mixed-citation>
          [20]
          <string-name>
            <given-names>S.</given-names>
            <surname>Russell</surname>
          </string-name>
          and
          <string-name>
            <given-names>P.</given-names>
            <surname>Norvig</surname>
          </string-name>
          , Artificial Intelligence:
          <string-name>
            <given-names>A Modern</given-names>
            <surname>Approach</surname>
          </string-name>
          . Prentice Hall,
          <year>2009</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref21">
        <mixed-citation>
          [21]
          <string-name>
            <given-names>J. A.</given-names>
            <surname>Robinson</surname>
          </string-name>
          , “
          <article-title>Computational logic: The unification computation</article-title>
          ,
          <source>” in Machine Intelligence</source>
          <volume>6</volume>
          , Edinburgh University Press,
          <year>1971</year>
          , pp.
          <fpage>63</fpage>
          -
          <lpage>72</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref22">
        <mixed-citation>
          [22]
          <string-name>
            <given-names>T. H.</given-names>
            <surname>Cormen</surname>
          </string-name>
          ,
          <string-name>
            <given-names>C. E.</given-names>
            <surname>Leiserson</surname>
          </string-name>
          , and
          <string-name>
            <surname>R. R. L.</surname>
          </string-name>
          ,
          <article-title>Introduction to Algorithms</article-title>
          . MIT Press,
          <year>1990</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref23">
        <mixed-citation>
          [23]
          <string-name>
            <given-names>M.</given-names>
            <surname>Alvarez-Picallo</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Eyers-Taylor</surname>
          </string-name>
          , M. Peyton
          <string-name>
            <surname>Jones</surname>
          </string-name>
          , and C.
          <string-name>
            <surname>-H. L. Ong</surname>
          </string-name>
          , “
          <article-title>Fixing Incremental Computation,” in Programming Languages and Systems</article-title>
          , L. Caires, Ed.,
          <source>in Lecture Notes in Computer Science</source>
          . Cham: Springer International Publishing,
          <year>2019</year>
          , pp.
          <fpage>525</fpage>
          -
          <lpage>552</lpage>
          . doi:
          <volume>10</volume>
          .1007/978-3-
          <fpage>030</fpage>
          -17184-1_
          <fpage>19</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref24">
        <mixed-citation>
          [24]
          <string-name>
            <surname>I. Horswill</surname>
          </string-name>
          , “Unity Prolog.”
          <source>Dec. 20</source>
          ,
          <year>2022</year>
          . Accessed: Jul.
          <volume>21</volume>
          ,
          <year>2023</year>
          . [Online]. Available: https://github.com/ianhorswill/UnityProlog
        </mixed-citation>
      </ref>
      <ref id="ref25">
        <mixed-citation>
          [25]
          <string-name>
            <given-names>M.</given-names>
            <surname>Mateas</surname>
          </string-name>
          and
          <string-name>
            <given-names>A.</given-names>
            <surname>Stern</surname>
          </string-name>
          , “Façade.”
          <year>2005</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref26">
        <mixed-citation>
          [26]
          <string-name>
            <given-names>M.</given-names>
            <surname>Mateas</surname>
          </string-name>
          and
          <string-name>
            <given-names>A.</given-names>
            <surname>Stern</surname>
          </string-name>
          , “
          <article-title>A Behavior Language for Story-Based Agents,” IEEE Intelligent Systems</article-title>
          , vol.
          <volume>17</volume>
          , no.
          <issue>4</issue>
          , pp.
          <fpage>39</fpage>
          -
          <lpage>47</lpage>
          ,
          <year>2002</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref27">
        <mixed-citation>
          [27]
          <string-name>
            <surname>Maxis</surname>
          </string-name>
          , “
          <article-title>The Sims 3</article-title>
          .”
          <year>2009</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref28">
        <mixed-citation>
          [28]
          <string-name>
            <given-names>R.</given-names>
            <surname>Evans</surname>
          </string-name>
          , “
          <source>AI Challenges in Sims 3,” in Artificial Intelligence and Interactive Digital Entertainment</source>
          , Stanford, CA: AAAI Press,
          <year>2009</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref29">
        <mixed-citation>
          [29]
          <string-name>
            <given-names>G.</given-names>
            <surname>Nelson</surname>
          </string-name>
          , “Natural Language,
          <string-name>
            <surname>Semantic Analysis</surname>
          </string-name>
          , and Interactive Fiction.” Unpublished white paper, Cambridge, UK,
          <year>2006</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref30">
        <mixed-citation>
          [30]
          <string-name>
            <given-names>R.</given-names>
            <surname>Evans</surname>
          </string-name>
          and
          <string-name>
            <given-names>E.</given-names>
            <surname>Short</surname>
          </string-name>
          , “Versu.” Linden Lab, San Francisco, CA,
          <year>2013</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref31">
        <mixed-citation>
          [31]
          <string-name>
            <given-names>R.</given-names>
            <surname>Evans</surname>
          </string-name>
          and
          <string-name>
            <given-names>E.</given-names>
            <surname>Short</surname>
          </string-name>
          , “
          <article-title>Versu - A Simulationist Storytelling System,” IEEE Transactions on Computational Intelligence and</article-title>
          AI in Games, vol.
          <volume>6</volume>
          , no.
          <issue>2</issue>
          , pp.
          <fpage>113</fpage>
          -
          <lpage>130</lpage>
          ,
          <year>2014</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref32">
        <mixed-citation>
          [32]
          <string-name>
            <given-names>S.</given-names>
            <surname>Mason</surname>
          </string-name>
          ,
          <string-name>
            <given-names>C.</given-names>
            <surname>Stagg</surname>
          </string-name>
          ,
          <string-name>
            <surname>N.</surname>
          </string-name>
          <article-title>Wardrip-fruin, and</article-title>
          <string-name>
            <given-names>M.</given-names>
            <surname>Mateas</surname>
          </string-name>
          , “
          <article-title>Lume: A System for Procedural Story Generation</article-title>
          ,” in
          <source>The Fourteenth International Conference on the Foundations of Digital Games (FDG '19)</source>
          , San Luis Obispo, CA, USA,
          <year>2019</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref33">
        <mixed-citation>
          [33]
          <string-name>
            <given-names>F. C. N.</given-names>
            <surname>Pereira</surname>
          </string-name>
          and
          <string-name>
            <given-names>D. H. D.</given-names>
            <surname>Warren</surname>
          </string-name>
          , “
          <article-title>Definite Clause Grammars for Language Analysis - A Survey of the Formalism and a Comparison with Augmented Transition Networks</article-title>
          ,
          <source>” Artificial Intelligence</source>
          , vol.
          <volume>13</volume>
          , no.
          <fpage>231</fpage>
          -
          <lpage>278</lpage>
          ,
          <year>1980</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref34">
        <mixed-citation>
          [34]
          <string-name>
            <given-names>F. C. N.</given-names>
            <surname>Pereira</surname>
          </string-name>
          and
          <string-name>
            <given-names>S.</given-names>
            <surname>Shieber</surname>
          </string-name>
          ,
          <article-title>Prolog and Natural Language Analysis</article-title>
          . Brookline, MA: Microtome Publishing,
          <year>1987</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref35">
        <mixed-citation>
          [35]
          <string-name>
            <given-names>S.</given-names>
            <surname>Lapeyrade</surname>
          </string-name>
          , “
          <article-title>Reasoning with Ontologies for Non-player Character's Decision-Making in Games,”</article-title>
          <source>Proceedings of the AAAI Conference on Artificial Intelligence and Interactive Digital Entertainment</source>
          , vol.
          <volume>18</volume>
          , no.
          <issue>1</issue>
          ,
          <string-name>
            <surname>Art</surname>
          </string-name>
          . no.
          <issue>1</issue>
          ,
          <string-name>
            <surname>Oct</surname>
          </string-name>
          .
          <year>2022</year>
          , doi: 10.1609/aiide.v18i1.
          <fpage>21980</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref36">
        <mixed-citation>
          [36]
          <string-name>
            <given-names>B.</given-names>
            <surname>Samuel</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A. A.</given-names>
            <surname>Reed</surname>
          </string-name>
          ,
          <string-name>
            <given-names>P.</given-names>
            <surname>Maddaloni</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Mateas</surname>
          </string-name>
          , and
          <string-name>
            <surname>N.</surname>
          </string-name>
          <article-title>Wardrip-Fruin, “The Ensemble Engine: Next-Generation Social Physics”</article-title>
          .
        </mixed-citation>
      </ref>
      <ref id="ref37">
        <mixed-citation>
          [37]
          <string-name>
            <given-names>J. D.</given-names>
            <surname>Ullman</surname>
          </string-name>
          , Ullman: Principles of Database and
          <string-name>
            <surname>Knowledge-Base</surname>
            <given-names>Systems</given-names>
          </string-name>
          , Volume I. Computer Science Press,
          <year>1988</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref38">
        <mixed-citation>
          [38]
          <string-name>
            <given-names>J. D.</given-names>
            <surname>Ullman</surname>
          </string-name>
          , Ullman: Principles of Database and
          <string-name>
            <surname>Knowledge-Base</surname>
            <given-names>Systems</given-names>
          </string-name>
          , Volume II:
          <article-title>The New Technologies</article-title>
          . Computer Science Press,
          <year>1989</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref39">
        <mixed-citation>
          [39]
          <string-name>
            <given-names>G.</given-names>
            <surname>Gottlob</surname>
          </string-name>
          ,
          <string-name>
            <given-names>G.</given-names>
            <surname>Orsi</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Pieris</surname>
          </string-name>
          , and
          <string-name>
            <given-names>M.</given-names>
            <surname>Šimkus</surname>
          </string-name>
          , “
          <article-title>Datalog and Its Extensions for Semantic Web Databases,” in Reasoning Web</article-title>
          .
          <source>Semantic Technologies for Advanced Query Answering: 8th International Summer School</source>
          <year>2012</year>
          , Vienna, Austria, September 3-
          <issue>8</issue>
          ,
          <year>2012</year>
          . Proceedings, T. Eiter and T. Krennwallner, Eds.,
          <source>in Lecture Notes in Computer Science</source>
          . Berlin, Heidelberg: Springer,
          <year>2012</year>
          , pp.
          <fpage>54</fpage>
          -
          <lpage>77</lpage>
          . doi:
          <volume>10</volume>
          .1007/978-3-
          <fpage>642</fpage>
          -33158-
          <issue>9</issue>
          _
          <fpage>2</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref40">
        <mixed-citation>
          [40]
          <string-name>
            <given-names>T.</given-names>
            <surname>Adams</surname>
          </string-name>
          and
          <string-name>
            <given-names>Z.</given-names>
            <surname>Adams</surname>
          </string-name>
          , “
          <article-title>Slaves to Armok: God of Blood Chapter II: Dwarf Fortress</article-title>
          .
          <source>” Bay 12 Games</source>
          ,
          <year>2006</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref41">
        <mixed-citation>
          [41]
          <string-name>
            <given-names>T.</given-names>
            <surname>Sylvester</surname>
          </string-name>
          , “RimWorld.” Ludeon Studios, Oct.
          <year>2018</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref42">
        <mixed-citation>
          [42]
          <string-name>
            <given-names>S.</given-names>
            <surname>Johnson-Bey</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M. J.</given-names>
            <surname>Nelson</surname>
          </string-name>
          , and
          <string-name>
            <given-names>M.</given-names>
            <surname>Mateas</surname>
          </string-name>
          , “
          <article-title>Neighborly: A Sandbox for Simulation-based Emergent Narrative</article-title>
          ,” in
          <source>2022 IEEE Conference on Games (CoG)</source>
          , Beijing, China: IEEE, Aug.
          <year>2022</year>
          , pp.
          <fpage>425</fpage>
          -
          <lpage>432</lpage>
          . doi:
          <volume>10</volume>
          .1109/CoG51982.
          <year>2022</year>
          .
          <volume>9893631</volume>
          .
        </mixed-citation>
      </ref>
      <ref id="ref43">
        <mixed-citation>
          [43]
          <string-name>
            <given-names>B. S.</given-names>
            <surname>Samuel</surname>
          </string-name>
          , “Kismet:
          <string-name>
            <given-names>A Small</given-names>
            <surname>Social Simulation Language</surname>
          </string-name>
          ,” Jan.
          <year>2021</year>
          , Accessed: Jul.
          <volume>24</volume>
          ,
          <year>2023</year>
          . [Online]. Available: https://www.academia.edu/101779750/Kismet _A_Small_Social_Simulation_Language
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>