Table of Contents

This month is NaNoGenMo. Entrants must write a program to generate a novel of 50,000 words or more. I am happy to say that I have been succesful, and I like what I have created.

1 woww ooo wobo sos wosw bo?

I started messing around simply by replacing letters by other letters. Here is a translate() function that I wrote:

translate = function(text)
   text = text:lower()

   -- abcdefghijklmnopqrstuvwxyz
   -- obsbosbwoybybbobswssobbsos

   text = text:gsub("[dgkmnpv]", "b")
   text = text:gsub("[aeiuy]", "o")
   text = text:gsub("[cfqstxz]", "s")
   text = text:gsub("[hjlr]", "w")

   return text
end

The function is very simple, you can see the letter swaps in the comment there. Let's see some examples:

$ lua translate.lua "Hello, world!"
wowwo, wowwb!

$ lua translate.lua "My name is Twee ~"
bo bobo os swoo ~

$ lua translate.lua "The quick brown fox jumps over the lazy dog"
swo soosb bwowb sox wobbs obow swo woso bob

$ lua translate.lua "Romance is a drowning tuna"
wobobso os o bwowbobb sobo

Isn't it cute? However, I decided not to use this for the final product.

2 Faking English

The next thing that I decided to do was to invent some English words. First, I searched for the usage frequency of letters in English. I wasn't planning on it, but I also found that the order is completely different for the first letter, so I added that as well. Here are two tables:

local first = {"t", "a", "o", "i", "s", "w", "c", "b", "p", "h", "f", "m", "d", "r", "e", "l", "n", "g", "u", "v", "y", "j", "k", "q", "z", "x"}
local other = {"e", "t", "a", "o", "i", "n", "s", "h", "r", "d", "l", "c", "u", "m", "w", "f", "g", "y", "p", "b", "v", "k", "j", "x", "q", "z"}

… and here is a big loop to make words. There percentages to three decimal places were also provided, but I decided not to care about that. I don't want it too realistic ;)

local word = ""
for i = 1, math.random(3, 8) do
   for i = 1, 26 do
      if math.random() > 0.8 then
         if word == "" then
            word = word .. first[i]
         else
            word = word .. other[i]
         end

         break
      end
   end
end

This outputs things like roita, atosto, tute, and eeeea. Admittedly some of the words are a lot nicer than others, but who am I to judge this peaceful people and their ancient language?

3 Making sentce

I couldn't find as much on average sentence length, so I did it the good old fashioned way by counting sentences in books. It turned out that in my head I had vastly underestimated how many words are in sentences, but the average was about 12 - 20 (I don't know, I didn't actually do a calculation). For the sentence generation, I randomly chose whether a sentence would be short, medium, or long:

local sentenceLength = 12
if math.random() > 0.3 then -- medium sentence
   sentenceLength = math.random(8, 16)
elseif math.random() > 0.7 then -- long sentence
   sentenceLength = math.random (13, 25)
else -- short sentence
   sentenceLength = math.random(1, 8)
end

Then, I had to choose what to separate words with. Would it be a comma, a hyphen, and would the sentence end in a question mark? Once again I just did randomness. It works pretty well.

local separator = " "
for i = 1, sentenceLength do
   if i == sentenceLength then
      if math.random() > 0.4 then
           separator = ".  "
      elseif math.random() > 0.6 then
           separator = "!  "
        elseif math.random() > 0.6 then
           separator = "?  "
      end
   elseif math.random() > 0.2 then
      separator = " "
   elseif math.random() > 0.7 then
      separator = ", "
   elseif math.random() > 0.6 then
      separator = "; "
   elseif math.random() > 0.6 then
      separator = " - "
   elseif math.random() > 0.6 then
      separator = "-"
   end

   sentence = sentence .. word() .. separator
end

4 Paragraphs, sections, chapters, parts, book!

All these functions vere very similar. I just used the unit a bit smaller to build up to the one above. Here is the part() function:

part = function()
   local part = ""

   for i = 1, math.random(6, 13) do
      part = part .. chapter()
   end

   return part
end

Some of the functions have little extra bits, for example to give the chapter a name. All that it does is grabs a sentence and sticks "###" in front of it, for markdown formatting.

local chapter = "### " .. chapterName .. "\n\n"

5 No no abowo

At this point, I decided to pipe the book() output through translate(), but the results were just very repetitive and not that fun to read. I guess that's what happens when you distill the alphabet to 5 letters.

6 Prolific Novelist

In 30 seconds, I have written two novels, called "teaetito ddoureoe uaoodoer sdeiett ien; oee tatio, faottnn bit mioeca." and "aswrcya attio artpt bta ioi tfe taaeit teitaato". They really do like long book titles in this language. You can read them here and here.

7 Source

Here's all the code. It's a bit of a mess, and quite inefficient at the moment, but doesn't take too long to run. I might update it someday.

math.randomseed(os.time())

translate = function(text)
   text = text:lower()

   -- abcdefghijklmnopqrstuvwxyz
   -- obsbosbwoybybbobswssobbsos

   text = text:gsub("[dgkmnpv]", "b")
   text = text:gsub("[aeiuy]", "o")
   text = text:gsub("[cfqstxz]", "s")
   text = text:gsub("[hjlr]", "w")

   return text
end

word = function()
   local first = {"t", "a", "o", "i", "s", "w", "c", "b", "p", "h", "f", "m", "d", "r", "e", "l", "n", "g", "u", "v", "y", "j", "k", "q", "z", "x"}
   local other = {"e", "t", "a", "o", "i", "n", "s", "h", "r", "d", "l", "c", "u", "m", "w", "f", "g", "y", "p", "b", "v", "k", "j", "x", "q", "z"}

   local word = ""

   for i = 1, math.random(3, 8) do
      for i = 1, 26 do
         if math.random() > 0.8 then
            if word == "" then
               word = word .. first[i]
            else
               word = word .. other[i]
            end

            break
         end
      end
   end

   return word
end

sentence = function()
   local sentence = ""

   local sentenceLength = 12
   if math.random() > 0.3 then -- medium sentence
      sentenceLength = math.random(8, 16)
   elseif math.random() > 0.7 then -- long sentence
      sentenceLength = math.random (13, 25)
   else -- short sentence
      sentenceLength = math.random(1, 8)
   end

   local separator = " "
   for i = 1, sentenceLength do
      if i == sentenceLength then
         if math.random() > 0.4 then
            separator = ".  "
         elseif math.random() > 0.6 then
            separator = "!  "
         elseif math.random() > 0.6 then
            separator = "?  "
         end
      elseif math.random() > 0.2 then
         separator = " "
      elseif math.random() > 0.7 then
         separator = ", "
      elseif math.random() > 0.6 then
         separator = "; "
      elseif math.random() > 0.6 then
         separator = " - "
      elseif math.random() > 0.6 then
         separator = "-"
      end

      sentence = sentence .. word() .. separator
   end

   return sentence
end

paragraph = function()
   local paragraphLength = math.random(1, 10)

   local paragraph = ""
   for i = 1, paragraphLength do
      paragraph = paragraph .. sentence()
   end

   paragraph = paragraph .. "\n\n"

   return paragraph
end

section = function()
   local sectionLength = math.random(3, 8)

   local section = ""
   for i = 1, sectionLength do
      section = section .. paragraph()
   end
   section = section .. "---\n\n"

   return section
end

chapter = function()
   local chapterName = sentence()
   local chapter = "### " .. chapterName .. "\n\n"

   local chapterLength = math.random(5, 12)
   for i = 1, chapterLength do
      chapter = chapter .. section()
   end

   chapter = chapter .. paragraph()

   return chapter
end

part = function()
   local part = ""

   for i = 1, math.random(6, 13) do
      part = part .. chapter()
   end

   return part
end

book = function()
   local bookTitle = sentence()
   local book = bookTitle .. "\n"

   for i = 1, bookTitle:len() do
      book = book .. "="
   end

   book = book .. "\n\n"

   for i = 2, 4 do
      book = book .. "\n\n" .. i - 1 .. "\n--\n\n\n\n" .. part()
   end

   return book
end

print(book())
  • twee xxx