I took last week off from my day job and spent some time working on HEXROLL|2. One of the things I managed to (sort-of) complete is Scroll, a new random generation language for HEXROLL that will replace the current set of YAML model files.
Scroll offers an efficient syntax for coding text generation models and it enables most of the new capabilities planned for HEXROLL|2.
Some the capabilities Scroll has are:
- Rolling random values from lists.
- Rolling random values using dice notations.
- Using entity types to reuse generation rules.
- Defining global constants to reuse lists or static values.
- Creating a directed-acyclic-graph of entities that represent complex constructs (such as a complete sandbox).
- Sharing data between entities using entity collections and data injection.
Basic Scroll Tutorial
If you are a patron of Hexroll, you can follow along using the REPL editor linked in our Patreon page.
Hello Scroll
Let’s begin with a simple example:
main {
greeting! @ [
* Hello
* Hey
* Hi
]
<html%
<p> {{greeting}}, Scroll. </p>
%html>
}
The basic construct in Scroll are classes. Classes are used to define entity types. In the above example we define a class named main
that has an attribute named greeting
and an HTML
renderer.
Let’s examine the greeting
attribute definition:
greeting! @ [
* Hello
* Hey
* Hi
]
This expression means: “Define an attribute named greeting
and make it visible to any renderer (using the !
symbol), then roll (@
) a random value from a list of possible values indicated by *
bulleted items inside matching [
square brackets ]
”
The @
operator is a key construct of Scroll and we will use it frequently to generate random values and entities moving forward.
The !
operator must be used any time we wish to use an attribute to output text from a renderer or use an attribute from another entity (more on this later).
Now let’s examine the HTML
renderer:
<html%
<p> {{greeting}}, World. </p>
%html>
Any entity type we define can have a <html%
%html>
block that can hold HTML template code. This code is a mixture of standard HTML tags together with template code that can reference attribute values or even execute custom logic.
In our example we render a paragraph using the <p>
tag and then use the double curly braces notation {{greeting}}
to output the random value rolled for greeting
.
Finally, both greeting
and the HTML
renderer are placed inside the {
curly braces }
of the main
class. Every Scroll script must have exactly one main
class definition as the entry point for the generator.
Classes
Classes in Scroll are defined using an identifier, followed by a curly braced block containing the class content:
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
}
Classes can extend other classes:
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
Let put this together:
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
}
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
main {
realm! @ Kingdom
<html%
Our realm is the
<strong>{{realm.title}}</strong>.
%html>
}
In the above example we’ve defined a base Realm
type with a random name. We then extended this class by defining a new class named Kingdom
and indicating that it will be extending Realm
using the round parentheses notation Kingdom (Realm) {...}
.
In the main
class, you can see how we now use the @
operator to roll an entity by using a class name.
Finally, note how we do not use the !
symbol for the name
attribute since it is not directly used in the HTML rendering code. It is important to use !
selectively, especially when generating a large number of entities.
Let’s take this a step further:
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
}
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
Duchy (Realm) {
title! = "Duchy of {{name}}"
}
main {
realm! @@ [
* Kingdom
* Duchy
]
<html%
Our realm is the
<strong>{{realm.title}}</strong>.
%html>
}
We’ve added yet another realm type named Duchy
and used the double roll operator @@
to roll a value from a list, and then used that value as the class name to roll an entity from.
Attributes
We’ve already seen how we can define attributes in classes. We’ve used the =
operator to assign a value to an attribute and we’ve used the @
operator (the @@
variant) to roll a random value into an attribute.
There are two additional assignment operators that can be used to choose entities from entity collections. I will cover these in a later post.
Scroll attributes can contain the following data types:
- Strings
- Numbers
- Dice roll values
- Boolean values (true or false)
- Entities
- List of entities
Strings
Simple strings can be simply assigned into attributes using the =
operator:
attribute_name = A simple string
Strings that has special symbols require surrounding quotes:
attribute_name = "a string with {{template}} code"
Or surrounding <
>
symbols if they span across multiple lines:
attribute_name = <a longer
multi-line string>
Or surrounding <%
%>
symbols if they span across multiple lines and contain special symbols:
attribute_name = <% a longer multi-line string
with {{template}} code and other
<symbols> %>
Rolling random values from lists is done using the @
operator. List items can span across multiple lines as well.
attribute_name @ [
* lists can have multi-line strings
specified without the < > notation.
* < Unless the text contains the * symbol >
]
Numbers
attribute_name = 42
attribute_name = 42.0
Dice roll values
Scroll allows rolling dice values using the @
operator, followed with a dice roll notation:
attribute_name @ 3d6
attribute_name @ 1d20+2
attribute_name @ 8d7x2
Booleans
attribute_name = true
attribute_name = false
Entities
As demonstrated in the examples above, entities can also be rolled using the @
or @@
operators:
attribute_name @ EntityClassName
attribute_name @@ [
* OneEntityClassName
* AnotherEntityClassName
]
List of Entities
Scroll has a special notation for rolling lists of entities:
[3..10 attribute_name] @ EntityClassName
[5..5 attribute_name] @@ [
* OneEntityClassName
* AnotherEntityClassName
]
Attributes that represent lists are placed inside [
square brackets ]
and prefixed with a cardinality notation of min_value..max_value
where min_value
is the minimum number of items to roll and max_value
is the maximum number of items to roll.
List items can be rendered using {% for item in list %} {{item.value} {% endfor %}
template syntax.
Putting everything together
Let’s conclude this short introduction with the following example:
Npc {
first_name @ [
* Walatus
* Ajutor
* Sieggo
* Milia
* Petesia
* Lefsy
* Latilde
]
last_name @ [
* Bossinia
* Brightmer
* Ermenbald
* Hildeward
* Siclebald
* Zawissius
]
age @ 7d12
description! = < {{first_name}} {{last_name}}
({{age}} years old) >
}
Settlement {
name @ [
* Akkis
* Bazuul
* Devilville
* Ecrean
* Frostfyord
* Khezal
]
<html%
{{title}} is home for: <ul>
{% for npc in npcs %}
<li> {{ npc.description }} </li>
{% endfor %} </ul>
%html>
}
Village (Settlement) {
title! = "The Village of {{name}}"
[6..12 npcs!] @ Npc
}
Town (Settlement) {
title! = "The Town of {{name}}"
[12..33 npcs!] @ Npc
}
Realm {
name @ [
* Ishabor
* Zeratha
* Inaba
]
[3..6 settlements!] @@ [
* Town
* Village
]
}
Kingdom (Realm) {
title! = "Kingdom of {{name}}"
}
Duchy (Realm) {
title! = "Duchy of {{name}}"
}
main {
realm! @@ [
* Kingdom
* Duchy
]
<html%
<p>
Our realm is the
<strong>{{realm.title}}</strong>.
</p>
<p>
The realm has the following settlements:
{% for s in realm.settlements %}
<p> {{html(s)}} </p>
{% endfor %}
</p>
%html>
}
This is it for this basic introduction. I will cover more advanced capabilities of Scroll in a future post, but in the meantime, Enjoy the crawl,
Ithai