Learning Tracery
Tracery is a “story-grammar generation library” written in JavaScript. It was created by Kate Compton and has risen in popularity for its easy-to-follow functions for taking a set of options and creating a grammar that can produce different texts.
Grammars
As covered in Part 1, a grammar is a set of symbols and some rules to follow. When using Tracery, these are feed to the createGrammar() function to tell it what symbols to use and their possible options.
Symbols
In Tracery, symbols and their choices are defined as an object literal. Each property within the object is an array where the property is the symbol name and the array is all of its possible choices.
createGrammar()
These symbols are passed to the createGrammar() function as part of the Tracery object. This takes the properties of the object passed to it to create associations where the property names can be substituted for one of their possible values in the next step.
The createGrammar() function also returns a grammar object that can be feed some text with certain values to substitute for the property names of the object passed to it.
flatten()
The flatten() function of the object returned by createGrammar() has a function, flatten(), that “flattens” all of the possible choices for each symbol into one randomly chosen from its choices.
This function returns a text representing any textual substitutions made based on what was passed to it. For any symbols wrapped in hashes (#animals# in this example) it is switched for one of its choices.
Play with the example on Repl.it!
And More Grammars!
Through adding more symbols, more complicated output can be achieved. Beyond using #animals#, a second symbol, #locations# could be added.
Adding new symbols with new rules does not change the basic output of the example. However, Tracery also understands symbols within symbols.
Complex rules can be created that will take symbols and expand them based on other, existing symbols.
Interaction-based Grammars
Knowing that flatten() will give one of the choices of a symbol can be helpful in using it to set the initial values of an interaction between parties. Through using conditional statements, multiple uses of flatten() can be used to set and test values to create a very basic interaction.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create two Tracery objects | |
var tracery = require("tracery-grammar"); | |
// Create a Person class | |
function Person(id) { | |
this.id = id; | |
this.say = function(words) { | |
console.log(id + ": " + words); | |
} | |
} | |
// Create two simple people objects | |
var personA = new Person("A"); | |
var personB = new Person("B"); | |
var symbols = { | |
"like": ["benches", "trees", "grass"] | |
}; | |
// Create a grammar | |
var grammar = tracery.createGrammar(symbols); | |
// Set what they like | |
personA.like = grammar.flatten("#like#"); | |
personB.like = grammar.flatten("#like#"); | |
personA.say("I like " + personA.like + "."); | |
if(personA.like == personB.like) { | |
personB.say("I also like " + personA.like); | |
} else { | |
personB.say("I don't like that. I like " + personB.like + "."); | |
} |
As a more expanded example, the symbols could be used as keywords and then “flattened” to queue up different responses based on the history of the conversation and the rules established by the grammars. While this example uses the same grammar, different ones could be created and run against each other, prompting repeating usage of flatten() to choose the response based on the symbol each time.