Confabulator

A recursive syntax for the procedural generation of random sentences in Ruby.

Why do I care?

Perhaps you want to generate emails, text messages, or webpages that are all readable, but have different wordings. Perhaps you're writing a game and want to vary character dialog. Perhaps you're writing a testing language and need a concise way to express linguistic possibilities. Perhaps you don't need a reason.

Install

gem install confabulator

Use

require 'confabulator'

Choice blocks

Choice blocks let the parser make a random choice.

> 5.times { puts Confabulator::Parser.new("{Choice one|Choice two} and stuff").confabulate }
Choice two and stuff
Choice one and stuff
Choice two and stuff
...

Choices inside of choices (ad infinitum) are fine:

> 5.times { puts Confabulator::Parser.new("This is {an example|a {good|great|mediocre} demonstration}").confabulate }
This is a great demonstration
This is a mediocre demonstration
This is an example
This is a good demonstration
This is an example
This is an example
This is a good demonstration
...

You can differentially weight the options: is 5 times more likely|than this

Substitutions

Substitutions let you re-use common templates.

> knowledge = Confabulator::Knowledge.new
> knowledge.add "friend", "{friend|world|there}" # a hash is also acceptable
> Confabulator::Parser.new("Hello, [friend]!", :knowledge => knowledge).confabulate
=> "Hello, there!"
> Confabulator::Parser.new("Hello, [friend]!", :knowledge => knowledge).confabulate
=> "Hello, world!"
...

Equivalently, as a helper on the Knowledge object:

> knowledge.confabulate("Hello, [friend]!")
=> "Hello, there!"

You can ask a substitution to be capitalized:

> knowledge.confabulate("Hello, [friend:c]!")
=> "Hello, World!"

Or pluralized:

> knowledge.add "dude" => "friend"
> knowledge.confabulate("Hello, [dude:p]!")
=> "Hello, friends!"

Substitutions can contain other substitutions inside of choice nodes inside of other substitutions, etc., ad infinitum. Just try to avoid infinite loops!

Escaping

You must escape the special characters [, `, and | with backslashes:

> knowledge.add "dude" => "friend"
> knowledge.confabulate("Hello, \\{friend\\|something\\ \\`\\`stuff\\`\\` \\[dude:p]!")
=> "Hello, friend|something  ``stuff`` [dude:p]!!"

Protected regions

Sometimes you want to insert user generated content without having to escape every {, [, `, and |. For this you use protected regions.

> knowledge.add "dude" => "friend"
> user_content = "protect regions [and stuff] with double backticks (`)!"
> knowledge.confabulate("Hello, ``#{user_content}``")
=> "Hello, protect regions [and stuff] with double backticks (`)!"

At the moment, sequences of more than one backtick are never allowed inside of a protected region.

Enumerating possible confabulations

You can output an array of all possible confabulations using all_confabulations, like so:

> Confabulator::Parser.new("{Hello|Hi} {world|there}").all_confabulations
=> [
     "Hello world", 
     "Hello there", 
     "Hi world", 
     "Hi there"
   ]

Internally, this calls tree, which outputs a simplified confabulation parse tree.

Next Steps

Here are some things that could be added to this library:

  • Depth limits / recursion detection
  • Learning through back propagation of a reward signal and optimization of the choice nodes to make the rewarded or penalized outcome more or less likely.
  • Whatever you want!

Helping out

Fork, write specs, add a feature, write documentation, send me a pull request!