Skip to content

Latest commit

 

History

History
252 lines (178 loc) · 10.3 KB

MESSAGES.md

File metadata and controls

252 lines (178 loc) · 10.3 KB

Additional notes

This file contains notes on some advanced/experimental features of Bracery. It is a supplement to the main README.md file.

Plain text symbol definitions

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).

Import scripts

The import/ directory contains scripts to import various word and phrase sets, notably from Darius Kazemi's corpora. Type cd import; make to run.

Braceplate message sequences

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.

Tags

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
  • 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)

Templates

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)

Messages

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

Special variables

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.

Footers

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.

Accept/reject handlers

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.}

Braceplate syntax

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.

Using braceplates

Web simulation

Bracery message template demo (source in web/markov.html)

Command line simulation

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

Visualization

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

Parser hacks for local scoping

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).

Example

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!