This file contains notes on some advanced/experimental features of Bracery. It is a supplement to the main README.md file.
Like Tracery, Bracery allows you to specify symbol definitions in JSON. However, for convenience, Bracery also allows an (optional) plaintext format for symbol definitions. This lets you avoid typing so much distracting punctuation.
In the plaintext format, a symbol definition block begins with a greater-than symbol >
, followed by the name of the symbol, then the end of the line.
Each subsequent line represents an alternate definition for that symbol. The block is terminated by a blank line.
For example, the following consists of two blocks:
>body_part
head
leg
arm
foot
nose
>sentence
"Hey! Look at my #body_part#!"
My #body_part# feels [funny|odd|great].
The #body_part#-bone's connected to the #body_part#-bone.
This is exactly equivalent to the following JSON definitions file.
Note how much less punctuation is needed for the plaintext version,
especially for the first expansion of sentence
where JSON requires that the quotation marks be backslash-escaped:
{
"body_part": [
"head",
"leg",
"arm",
"foot",
"nose"
],
"sentence": [
"\"Hey! Look at my #body_part#!\"",
"My #body_part# feels [funny|odd|great].",
"The #body_part#-bone's connected to the #body_part#-bone."
]
}
Backslash-escaping works in the plaintext format, too; so you can use \n
if you need a newline within a definition line.
The file examples/travel.txt contains the #hero# traveled with...
example in this plaintext format
(examples/travel.json contains the same definitions in JSON).
The import/ directory contains scripts to import various word and phrase sets,
notably from Darius Kazemi's corpora.
Type cd import; make
to run.
Braceplates (Bracery message templates) are a lightweight scheme for sequencing a series of Bracery messages in a Markov chain, allowing for limited contextual continuity between successive messages.
Each template has a set of past-tags and a set of future-tags. Tags are arbitrary strings, excluding spaces. Matches between tags determine the connectivity of the Markov chain.
The past and future-tags are interpreted as follows:
- For template B to be considered as a possible successor (i.e. reply) to a message generated from template A, at least one of A's future-tags must also be one of B's past-tags
- If any of A's future-tags appear in B's past-tags with an exclamation point in front (e.g. A has future-tag
tag
and B has past-tag!tag
), then B is disallowed as a successor to A (these tags are referred to as B's excluded-past-tags)- The minus sign is an alias for the exclamation point in past-tags, so
-tag
means the same as!tag
- The minus sign is an alias for the exclamation point in past-tags, so
- If any of B's past-tags have a plus sign in front (e.g. B has past-tags
+tag
), then A must have that tag in its future-tags. - The special past-tag
root
is used to denote root templates that can be used at the top of a thread (or the past-tags can be left empty for the same effect)
Each Bracery message template has the following fields:
- the past-tags
- the future-tags
- the title
- the Bracery source text that is used to generate individual messages from this template
- (optional) the name of the sender (or, more generally, a user or NPC associated with the message)
- (optional, defaults to 1) the integer weight of the template (used by the recommendation engine)
An individual Braceplate message, generated from one of the above templates, exists in the context of a thread of messages. The first message in the thread must be generated from a root template, as described above. Successive messages are generated by matching tags between consecutive templates.
Each message has the following fields:
- an associated template
- an expansion tree that is a parse tree generated from the Bracery grammar defined by the template's source text
- a set of future-tags which by default are the same as the template's future-tags, but can be overridden by the
$tags
variable, if that variable is assigned a value in the expansion tree - (if not the first message in the thread) a predecessor message, with appropriate overlap between the predecessor's future-tags and the template's past-tags
The root node of the expansion tree "inherits" any variable assignments from the predecessor, with two special variables overridden as follows:
- the
$tags
variable, at the beginning of the expansion, is set to the template's future-tags (joined by whitespace into a single string) - the
$prevtags
variable, at the beginning of the expansion, is set to the predecessor message's future-tags (joined by whitespace into a single string)
The value of the tags
variable by the end of the expansion is used to find the message's future-tags (it is considered to be a whitespace-separated list).
Thus, the template's default future-tags can be "overridden" by variable assignments from the Bracery source text.
The special variable $footer
also has significance, in that the Bracery code &eval{$footer}
is implicitly appended to every message.
A common pattern is to set (or manipulate) $tags
from within $footer
.
The variables $accept
and $reject
are reset to the empty string at the beginning of every message.
If, during the message, $reject
is defined truthy (i.e. nonempty and non-whitespace), then the UI will interpret the player "reject" action
as an "accept" with &eval{$reject}
appended to the message.
Similarly, if $accept
is defined truthy, then the player "accept" action will append &eval{$accept}
to the message.
This allows forced binary choice semantics, similar to a game like Reigns. For example:
The peasants riot for food. $accept={You distribute grain.} $reject={You crush the rebellion.}
Templates can be specified in JSON or in the following plaintext shorthand
100@template_author>Template title#past_tag1 past_tag2#future_tag1 future_tag2 future_tag3
The template itself, featuring $variables, ~nonterminals, [alternations|etc.]
If split over multiple lines, each line is treated as an alternation option, as with symbols.
This defines a template with weight 100
by @template_author
, with the title "Template title", and the specified past tags (past_tag1
and past_tag2
) and future tags (future_tag1
, future_tag2
, and future_tag3
).
The weight (100
), author (@template_author
), and past/future tags (everything from #
onwards) can be omitted.
For an example, see examples/markov/good_news_bad_news.txt.
Bracery message template demo (source in web/markov.html)
You can test template sequencing from the command line using bracery's -m
option
(short for --markov
, because it samples a trajectory through the Markov chain).
Make sure to also load any required symbol definitions.
For example:
bracery -m examples/markov/good_news_bad_news.txt
Or for an interactive experience that allows you to keep re-randomizing the next message in the thread until you're happy with it,
use bracery with the -q
option (short for --quiz
) instead of -m
:
bracery -q examples/markov/good_news_bad_news.txt
You can also use the templates2dot.js
script to get a visualization of the Markov chain as a GraphViz dot file
(the -o
option will create and open the PDF automatically, but only on a Mac with GraphViz installed)
bin/templates2dot.js -o examples/markov/good_news_bad_news.txt
The parsers for both the plaintext symbol definitions and the message definitions include a few hacks that allow you to approximate the effect of local scoping in a file.
The parser has several built-in configuration parameters with names like PREV
, TAGS
, PREFIX
, and so on.
These can be modified by placing a line in the file that looks like this:
## PREV (new value of PREV)
The parameter value will then apply throughout the remainder of the file, after this line. It can be reset to the empty string like so:
## RESET PREV
If the parameter name is omitted, then all parameters will be reset:
## RESET
The parameters have the following meanings:
Parameter | Meaning |
---|---|
PREV |
Will be used as default past-tags for all subsequent messages (along with any other past-tags specified for that message) |
TAGS |
Will be used as default future-tags for all subsequent messages (along with any other future-tags specified for that message) |
TITLE |
Will be prepended to the title of all subsequent messages |
WEIGHT |
Will be used as the default weight of all subsequent messages for which no weight is explicitly specified |
AUTHOR |
Will be used as the default author field of all subsequent messages for which no author is explicitly specified |
PREFIX |
Will be prepended to every subsequent past- or future-tag that starts with an asterisk |
SUFFIX |
Will be appended to every subsequent past- or future-tag that starts with an asterisk |
In addition, any string of the form ~~symbol
will be replaced with ~PREFIXsymbolSUFFIX
where PREFIX
and SUFFIX
are the appropriate parameter values.
You can also write ~*symbol
as an alternate syntax for ~~symbol
(to be consistent with the convention that all tags of the form *tag
will be expanded to PREFIXtagSUFFIX
).
The following two template definitions files are equivalent.
Using the local-scoping parser hacks:
## PREFIX my_scope_
## TITLE Flip-flop
> # root *flop # *flip
Flip!
> (response) # *flip # *flop
Flop!
## RESET
Without the local-scoping parser hacks:
>Flip-flop # root my_scope_flop # my_scope_flip
Flip!
>Flip-flop (response) # my_scope_flip # my_scope_flop
Flop!