Ruby Fnord Generator - Part One
I recently came across the Steve Jackson Games Fnords program and thought that there should be a really cool and easy way to generate this type of sentence using Ruby. The basic priciple is that you take arrays of NOUNS, PLACES, VERBS, and other parts of speech and generate syntacically correct nonsensical sentences. A little bit like a truncated form of madlibs.
Ruby embedded code in strings was something that I’d played around with, and I thought that it should be relatively easy to base sentence construction using methods embedded in the strings. So basically
"#{name} is a #{adjective} #{noun}."
could be run through the generator and create “Mac is a brown dog”, “Sigmund Freud is a coherent lightbulb”, type of sentences. Fairly basic stuff with NAME, ADJECTIVE and NOUN being arrays of appropriate words and the method, for example, of self.name being defined as
def self.name NAME[rand(NAME.length)] end
If you’ve taken a look at the other Fnords programs at SJ Games (or even my early one which is now downloadable from there), you will have noticed that the sentences themselves have a random chance of having some parts of speech included. It was this random element that first attracted me to solving the problem using Ruby.
Most code on the site uses logic of the following form.
msg="The" # The
if rand(2) == 0 # adjective - (50% chance)
msg+=adjecive
end
msg+=noun # noun
if rand(5) == 0 # in place - (20% chance)
msg+="in #{place}"
end
msg+="is #{adjective}." # is adjective.
That wasn’t DRY enough for me, and too much code when a little would do the same thing. Moving the (rand()) into the actual function that defined the part of speech and making the return from the call optional would do almost the same thing. Worst case I would have to fix up spaces at the end. (I actually decided to do space fixup as a final pass, since it meant I could just type the sentences spaced normally, which makes it easier to maintain.)
Ok so with the method for noun now as
def self.noun(chance=0)
if (rand(chance)<1) then
NOUN[rand(NOUN.length)]
else
""
end
end
The way the Ruby rand works, passing it 0 gives a random number between 0 and 1 and passing a positive integer greater than 0 generates a random integer, so with this code rand(0)<1 is always true, so the next iteration changed this to
if (chance==0 or rand(chance)<1)
Finally to DRY it up even more, I removed the randomness into two separate methods and switched to the “? :” form of “if then”.
def self.true?(chance)
(chance==0 or rand(chance)<1)
end
def self.build(string_array,chance=0)
true?(chance) ? string_array[rand(string_array.length)] : ""
end
which meant that my noun method could now simplify again to
def self.noun(chance=0)
build(NOUNS, chance)
end
One special case was required to allow for “in place” as optional rather than just “place”.
def self.in_place(chance=0)
true?(chance) ? "in #{build(PLACES)}" : ""
end
Now I could create strings using my desired form.
msg="The #{adjective(2)} #{noun} #{in_place(5)} is #{adjective}."
Part 2 will examine how to take this and create the fully functional fnords program.
Trackbacks
Use this link to trackback from your own site.
Comments
You must be logged in to leave a response.