Story Generator
Generate stories from descriptions written in Story Description Language (see below)!
Install
Story Generator is a Ruby Gem, so simply install it with gem install story-gen
. Of course you also need Ruby >= 1.9.3!
Usage
story file.sdl
Read story description from "file.sdl", generate a story and write it to stdout.
story -c -n MyStory -o file.rb file.sdl
Read story description from "file.sdl" and write a class MyStory to "file.rb". The class MyStory subclasses Story (see docs) and can be used like this: MyStory.new.write()
.
story --help
More help on story
.
Story Description Language (SDL)
A simple story:
"Hello, world!"
It just prints "Hello, world!". To print newline just write newline
or nl
:
"Hello, world!" newline
"Another line of helloness!" nl
Or you may insert newline inside the quotes:
"
Chapter I
---------
"
You may use single quotes as well:
'Hello, world!'
Comments:
"Hello, world!" (note: a comment, "literal" style)
/* a comment, C style */
A story is based on Facts. To state a Fact just write it:
"John" loves "Liza"
In the Fact expression you may use english and russian words (except keywords, see below), characters from "#№@$%/-,[]{}", integer numbers (e.g., 18
) and arbitrary double- or single-quoted strings ("..."
or '...'
). Trailing commas are ignored (xxx yyy zzz,
is the same as xxx yyy zzz
). The words are case-insensitive (so loves
and Loves
mean the same), quoted strings are case-sensitive (so "John"
≠"JOHN"
).
Let's state some Facts:
"John" is a boy;
"Sam" is a boy;
"Liza" is a girl;
"Sabrina" is a girl;
"John" loves "Liza";
Statements in the story are separated with a semicolon (";"). The semicolon is optional if its absence does not cause an ambiguity, so the following is valid too:
"John" is a boy;
"Sam" is a boy;
"Liza" is a girl;
"Sabrina" is a girl;
"John" loves "Liza"
What can Facts be used for? You may form conditions with them:
If X is a boy then "Let's meet " X;
Or, with a comma (",") before then
:
If X is a boy, then "Let's meet " X;
Here X
is a variable. Variable names consist of underscores ("_") and capital letters only (e.g., LONG_VARIABLE_NAME
). You may only capture quoted strings or numbers as variables, so the following is invalid:
If "John" A "Liza" then ... /* ERROR! */
You may use the captured variables in the statement after then
keyword.
To print the captured variable you just write it along with some quoted strings and newlines:
"Let's meet " X "." nl;
But wait... We have two boys! What does if
choose as X
then? The answer is: random. If there are several combinations of variables which fit the condition then a random combination is chosen.
You may form complex Fact expressions using and
, or
and not
keywords and parentheses:
If X is a boy and Y is a girl then X" meets "Y"!"
If (X is a boy) and (Y is a girl) then X" meets "Y"!"
If X is a boy and Y is a girl and not X loves Y then
X" meets "Y"!"
The not
keyword may also be written inside the Fact:
If X is a boy and Y is a girl and X not loves Y then
X" meets "Y"!"
Not all combinations of and
, or
and not
are available, though. Use common sense to find out which one are. For example, this is an error:
If X is not a boy then "How can I determine "X"?" /* ERROR! */
You may also compare variables in the condition:
If X is a boy and Y is a boy and X <> Y then
X" and "Y" are two different boys!"
There are limitations on the comparison: the comparison must be after the and
keyword and all variables must be mentioned in the left part of and
.
You may use =
, !=
, <>
, =/=
, <=
, <
, >
and >=
as comparison operators. Take types of the comparands into account, though!
You may use asterisk ("*") instead of the variable to avoid capturing:
If X is a boy and X not loves * then
X" is a lonely boy."
You may use otherwise
keyword:
If X is a boy and X not loves * then
X" is a lonely boy."
otherwise
X" has found someone!"
You may combine several conditions and then
statements using colon (":") and dashes ("-"):
If:
- X is a boy then "We have found a boy "X;
- Y is a girl then "We have found a girl "Y;
- otherwise "We do not know anyone";
This is like a classical if ... else if ... else ...
but if multiple conditions are true then the random one is chosen (instead of the first one, like in the classical if-else
). The last otherwise
is the same as else
in the classical if-else
- it is chosen if all other conditions are false.
Look at the trailing semicolons (";") by the way - they resolve ambiguity!
Another form of if
:
If:
a) X is a boy then "We have found a boy "X;
b) Y is a girl then "We have found a girl "Y;
c) otherwise "We do not know anyone";
You may use captured variables to state a Fact:
If X is a boy and Y is a girl then
X loves Y
You may set the Fact false:
If X loves Y then
X not loves Y
Set multiple Facts false:
If X is a boy then
X not loves *
You may use the captured variables in another conditions by prefixing them with hat ("^"):
If X is a boy then
if ^X loves * then "We found "X" which is in love!"
otherwise "We found "X" which is stll single."
To combine several statements into one use a colon (":") with the final dot ("."):
If X is a boy then:
"Let's meet " X "!";
X " is a boy!".
or parentheses:
If X is a boy then (
"Let's meet " X "!";
X " is a boy!";
)
There are other statements you can use:
- "While":
While <fact expression> <statement>
Here <fact expression> is the same as in if
statement except that you may use not
in top level:
While X not loves *:
If X is a boy and Y is a girl then X loves Y.
The variables from <fact expression> are not available in <statement>.
Note that usually there is no way to delimit <fact expression> from <statement> except by wrapping the <statement> into ":" and "." or into parentheses.
- "Repeat n times":
10 times "Hello!" (note: print "Hello!" 10 times)
10...20 times "Hello!" (note: random number between 10 and 20 is chosen)
X times "Hello!" (note: the value of the captured variable X is used)
X...Y times "Hello!" (note: the value between two captured variables
is used)
- "For all":
For all <fact expression> <statement>
<statement> is executed for all combinations of variables in <fact expression>. The <fact expression> is like in if
statement.
- Ruby code:
<code>puts(x); puts(y)</code>
Inside the code you can access the captured variables by their lowercase names:
If X loves Y then
<code>puts(x); puts(y)</code>
- "Either ... or ...":
Either <statement> [;|,]
or <statement> [;|,]
or <statement> [;|,]
...
or <statement> [;|,]
[;|,]
means optional character which is either semicolon (";") or comma (",").
A random <statement> is chosen and executed.
- "When":
When <fact> <statement>
When you try to state the <fact> then <statement> is executed.
When X loves Y:
""X" marries "Y"!";
.
"John" loves "Liza";
The code above prints "John marries Liza!"
Notes
(note: ...)
comment may have nested parentheses:
(note: this is a comment (with nested parentheses)!)
You may end the story with a dot ("."):
"John" loves "Liza";
"Sam" loves "Sophia".
Keywords if
, either
, or
, for
(in for all
) and while
may start with a capital letter: If
, Either
etc.
You may also use russian keywords! ;)
Examples
See them in "sample" directory (you may get it with story --samples
)! Or in YARD doc's "File list"!