Node.JS Automatic Poem Generator As a simple programming exercise, I wrote a piece of Node.JS code which generates random poems. It is based on a list of articles (kind of), nouns, linking verbs, and adjectives. It then combines them into lines, and eventually stanzas.

var articles = "The,All of the,Most of the,Some of the,My,Your,His,Her,Their,Our,Everybody's,Almost all of the,That,I knew that the,We knew that the,She knew that the,He knew that the,They knew that the".split(",");
var nouns = "darkness,morning,light,feeling,beauty,love,hatred,expression,message,happiness,sadness,anger,frustration".split(",");
var lv = "was,had been.will be,could be,might be,should have been,would have been,could have been".split(",");
var adjectives = "abstract,mysterious,permanent,unfortunate,intricate,confusing,true,false,fake,a lie,a stranger,a friend,serene,confusing,an enemy,terrible,enchanting,mine,yours,his,hers,theirs,ours,fortunate,understood,mine,interesting,mutual,artistic,musical".split(",");
var x;

var ta = articles[Math.floor(Math.random() * articles.length)];
var tb = nouns[Math.floor(Math.random() * nouns.length)];
var final = ta + " " + tb + "\nby Julian Lachniet\n------------------\n"

for (x = 0; x &lt; 16; x++) {
  var a = articles[Math.floor(Math.random() * articles.length)];
  var b = nouns[Math.floor(Math.random() * nouns.length)];
  var c = lv[Math.floor(Math.random() * lv.length)];
  var d = adjectives[Math.floor(Math.random() * adjectives.length)];
  final += a + " " + b + " " + c + " " + d + ".\n";
  if (x % 4 == 3) {
    final += "\n";
  }
}

cr = new Date
final += "(c) " + cr.getFullYear();

console.log(final);


The poems are really quite silly, but any individual line out of context might seem normal.

He knew that the darkness
by Julian Lachniet
------------------
They knew that the darkness was mutual.
All of the sadness would have been serene.
Most of the feeling could have been artistic.
We knew that the sadness was intricate.

We knew that the feeling could be mine.
Your light had been.will be a lie.
They knew that the beauty would have been understood.
Their love would have been intricate.

They knew that the happiness should have been mine.
His frustration should have been mysterious.
Some of the darkness should have been musical.
That message might be unfortunate.

Your light might be enchanting.
Everybody's hatred should have been enchanting.
His expression could be interesting.
Their hatred would have been mine.

(c) 2017


I have several issues with my code already, which I don't really know how to change:


What is a better way to name the "dictionary" arrays (articles, nouns, lv, adjectives)?
Is there a more effective way to represent arrays than CSVs? I decided against using a standard array due to how much space it takes up.


Any tips/pointers would be appreciated.
I am not well versed in coding node.js as I'd like to be (I can read and debug, but learning to program in it is still ongoing) and I did not see anything that said I could not present an answer using a compatible language, so I converted your code into SmartBASIC.

If you were expecting node.js code, I will understand. I needed a quick way to show you several areas where you can improve on your own program, and suggestions that will smarten your program's AI. For yes, SIRI seems to be human like, it is not perfect. And it is harder to have a computer write something in one of the arts, whether a poem, lyrics or even a short story.

I did notice one slight error. In the statement below, you have a period instead of a comma, which will get you strange errors (had been.will be):

var lv = "was,had been.will be,could be,might be,should have been,would have ... 


I experimented with several ideas, and several show promise. I incorporated them in my SmartBASIC program, which I commented thoroughly.

Your dictionary is limited, but I found out that this is not always a bad thing. I experimented with nouns, verbs, adjectives and articles where I created several hundred in each, and while it was still hit or miss, it was more miss than yours hit. Quantity does not always equate to quality.

One of the problems I noticed is when you want to use a color. Rarely does a color stand alone, nor does it always work with every word. A "white beard" is fine, but what is a "white crash?" 

But a white crash can be poetic if the theme is right. That later.

So with colors, I created in my table colors with modifier tags. These are color words with a tag at the end so the program knows that another word is needed after a color word is used. 

Here is a sample DATA statement that you should be able to convert to a "VAR[" Table:

DATA "*beard","fool","white*","black*","green*","blue*","yellow*","red*","light beams","*river","*garment","*ship","*snake","*snakes","*sails","voice+","ship+","ocean blue"


What you want to look at are the words with an "" in front of or at the end. The "" is my token that color is involved, and a modifier is needed.

If the "" is before the word, then I need to add a color before it. If the "" is after the word, then it is a color word and it needs a modifier.

I also created a table of colors only for the times I need to add a color to the front of a word. The word can be a noun or a verb. "White lightning," depending on how it is used, can be a noun or a verb. If you said "White lighning struck a tree," it is a noun. "Dear ole Fred, lay dead, when met by white lightning" could be considered a verb/adverb use.

The second thing is the "+" modifier. In the same DATA statement, the word "voice+" means it needs a modifier after it. I created a specific table for this type of modifier.

You will see in my code that since I used a random generator to start either with an article or verb, every word is in or converted to lowercase. I had to create another modifier, "@" that is placed in front of proper nouns or words that must be capitalized, no matter where in the sentence they appear. @moon and @sun are two examples in my DATA statements.

The third thing that shows promise is to create files that you can read into your VAR tables, but with a twist. I am currently creating theme-based poems. So I created files with words that are used in a particular theme. 

For example, "hope" is one of the themes I am developing, so I am using everything from other poets to a thesaurus to even the Bible to gather as many words, Articles, Nouns, Verbs, Adjectives, and Modifiers that fit the theme of "hope" along with some generic words that would work.

This is where you asked if it would be better to have .CSV instead of tables. It will add flexibility to what your computer program writes about. You would have a VAR table with just themes, and .CSV file names for each theme that holds your vocabulary for that theme. You would first randomly pick a theme, load your tables, then can randomly pick your words.

By moving your tables out to .CSV files, you allow for many different types of dictionaries. And you can even get as complex as having a main theme and a sub-theme and load the appropriate dictionaries.

Another improvement I demonstrated in my code is that sentence structure does not always have to be article-noun-verb-adjective. It doesn't even have to always be subject-noun-verb or noun-verb-subject. Try starting one of your lines with a verb instead of an article. Then you can use verb-noun-subject or even article-verb-noun-adjective.

For example, "Stepping on Mike's foot felt great" shows how a verb can start a sentence.

I mentioned subject. For now, you can use nouns, but you may want to eventually create another table with just items that are considered "subjects." "Science" is a subject. "A Dear John letter" can be a subject rather than a noun. A "Divorce" can also be a subject.

I tried to keep to the same logic you used, but I wrote as vanilla a BASIC program as I possibly could so those with other dialects need to change very little. Like statement separator (!) or some of the statements for lower and upper-case. With minimal changes, I had this working in techBASIC.

But Javascript, being a C-based language, there are enough similarities to BASIC that you should have no problems adapting the routines in my code below into your program.

Not a plug, but incase there are those who never heard of SmartBASIC, it is an app (less than $5 in iTunes) that runs a pretty robust BASIC dialect on iPad and iPhones, and comes with the ability to create Xcode apps for distribution on iTunes. You also do not need access to the internet to run and test your programs.

This code works when run in SmartBASIC and is not broken code.

Here is it with the changes I made (commented):

/*
Poem Generator
By George McGinn (Poems by SmartBASIC)

Program uses very little AI in the beginning as once I have all the 
word pieces in place, the AI will then determine if a word needs a 
qualifier before or after it. 

As this program grows, so will its AI, and the next phase will have it 
learning words, for example, if it chooses a "A" and the next word 
begins with a vowel, it will correct the "a" to an "an", thus its start 
of learning grammar.

UPGRADES/BUG FIXES:
v1.0 - March 1, 2017
    Translation from simple NODE.js program
    Initial logic with limited vocabulary

v1.5 - March 4, 2017 (Minor upgrade - only because this was also v1 updates)
    Improved vocabulary by adding:
       17+ Articles
       31+ Nouns/Subjects
       35+ Verbs/Adverbs
       15+ Adjectives 
       28+ Color Modifiers (new)  
    Randomize start (Article or Verb)
    Improved AI to now include:
       – Capitalization at start of line
       – Created word capitalization modifier tag 
       – Added Color Table for words needing a color before it
       – Added additional word and new color modifier table
       – Added Noun modifier table for before and after tagged noun
    The AI can now do rudimentary word enhancement by addind modifiers
    from specialized tables, including colors and added nouns, verbs, 
    and adjectives before or after a noun. It is capable of recognizing
    words that require capitalization. However, the title still does 
    not work right
*/

OPTION BASE 1
RANDOMIZE

REM Initialize variables/arrays (comments to prevent confusion)

x=0                                            REM Index to poem length (# lines)
i=0                                            REM Index to loading Arrays
cr$=CHR$(10)                                   REM Add a Carriage-Return character
Author$ = "By George McGinn"                   REM Name of Programmer running code
DIM article$(35),noun$(60),verb$(46),adjective$(45)
DIM colormodifier$(28),colortable$(7),nounmodifiers$(14)


REM Load Arrays from Data Statements

GOSUB LoadWordArrays

REM Pick a 2-word title

ta$ = article$(RND (35)+1)
b$=LOWSTR$(noun$(RND(60)+1))
b1$=LOWSTR$(noun$(RND(60)+1))
IF LEFT$(b$,1)="@"  OR LEFT$(b1$,1)="@" THEN GOSUB CheckForCapitalization
IF RIGHT$(b$,1)="*" OR RIGHT$(b$,1)="+" THEN GOSUB CheckForModifierAfter
IF LEFT$(b$,1)="*"  OR LEFT$(b$,1)="+"  THEN GOSUB CheckForModifierBefore
tb$=b$

Title$ = ta$ &amp; " " &amp; tb$

REM Create the poem, 16 lines in 4 4-line stanzas

PRINT Title$
PRINT Author$
PRINT!PRINT

REM NOTE: b1 &amp; c1 placeholders for future use as modifiers, or
REM       to initially use b1 &amp; c1 as modifiers themselves.
FOR X=1 TO 16
    a$=LOWSTR$(article$(RND(35)+1))             REM Pick an Article
    b$=LOWSTR$(noun$(RND(60)+1))                REM Pick a primary noun
    b1$=LOWSTR$(noun$(RND(60)+1))               REM Pick a noun-color modifier
    c$=LOWSTR$(verb$(RND(46)+1))                REM Pick a primary verb
    c1$=LOWSTR$(verb$(RND(46)+1))               REM Pick a verb modifier 
    d$=LOWSTR$(adjective$(RND(45)+1))           REM Pick an adjective
    bc$=LOWSTR$(colormodifier$(RND(28)+1))      REM Pick a modifier for a color
    color$=LOWSTR$(colortable$(RND(7)+1))       REM Pick a color as a modifier
    nounmod$=LOWSTR$(nounmodifiers$(RND(14)+1)) REM Pick a non-color noun modifier

REM Check for modifiers to nouns
    IF LEFT$(b$,1)="@"  OR LEFT$(b1$,1)="@" OR LEFT$(bc$,1)="@" THEN GOSUB CheckForCapitalization
    IF LEFT$(b$,1)="*"  OR LEFT$(b$,1)="+"  THEN GOSUB CheckForModifierBefore
    IF RIGHT$(b$,1)="*" OR RIGHT$(b$,1)="+" THEN GOSUB CheckForModifierAfter

 REM Capitalize the first word of each sentence or stanza (whether noun or verb)
REM r=whether or not the sentence will start with a verb or article
    r=RND(2)+1
    IF r=1 THEN
       z=LEN(a$)
       a$=CAPSTR$(LEFT$(a$,1)) &amp; RIGHT$(a$,z-1)
       Line$=a$ &amp; " " &amp; b$ &amp; " " &amp; c$ &amp; " " &amp; d$
    ELSE
       z=LEN(c$)
       c$=CAPSTR$(LEFT$(c$,1)) &amp; RIGHT$(c$,z-1)
       Line$=c$ &amp; " " &amp; b$ &amp; " " &amp; d$
    ENDIF
    IF X%4=0 THEN Line$=Line$ &amp; "." &amp; cr$
    PRINT Line$
NEXT X

endPROG:
   PRINT!PRINT!PRINT "© 2017 "&amp;Author$&amp;" (if it is good), All Rights Reserved"
   PRINT "Date/Time Stamp: "&amp;CURRENT_MONTH ()&amp;"/"&amp;CURRENT_DATE ()&amp;"/"&amp;CURRENT_YEAR ()&amp;" at "&amp;CURRENT_HOUR()&amp;":"&amp;CURRENT_MINUTE()&amp;":"&amp;CURRENT_SECOND()
   END

REM FUNCTIONS, SUBROUTINES, LABELED DATA STATEMENTS
REM ***********************************************

LoadWordArrays:
REM ***********************************************
REM Load Arrays from Data Statements

REM Load Articles
    For i=1 TO 35!READ article$(i)!NEXT i
REM Load Nouns
    FOR i=1 TO 59!READ noun$(i)!NEXT i
REM Load Verbs
    FOR i=1 TO 46!READ verb$(i)!NEXT i
REM Load Adjectives
    FOR i=1 TO 45!READ adjective$(i)!NEXT i
REM Load Color Modifiers
    FOR i=1 TO 28!READ colormodifier$(i)!NEXT i
REM Load Color Table
    FOR i=1 to 7!READ colortable$(i)!NEXT i
REM Load Noun Modifiers
    FOR i=1 to 14!READ nounmodifiers$(i)!NEXT i
RETURN

CheckForCapitalization:
REM Check for modifiers (@=Capitalize Word)
IF LEFT$(b$,1)="@" THEN
   z=LEN(b$)
   b$=RIGHT$(b$,z-1)
   b$=CAPSTR$(LEFT$(b$,1)) &amp; RIGHT$(b$,z-2)
ENDIF
IF LEFT$(b1$,1)="@" THEN
   z=LEN(b1$)
   b1$=RIGHT$(b1$,z-1)
   b1$=CAPSTR$(LEFT$(b1$,1)) &amp; RIGHT$(b1$,z-2)
ENDIF
IF LEFT$(bc$,1)="@" THEN
   z=LEN(bc$)
   bc$=RIGHT$(bc$,z-1)
   bc$=CAPSTR$(LEFT$(bc$,1)) &amp; RIGHT$(bc$,z-2)
ENDIF

RETURN


CheckForModifierBefore:
REM Check for modifiers Before

REM "*" the noun needs a color before it
IF LEFT$(b$,1)="*" THEN
   z=LEN(b$)
   b$=RIGHT$(b$,z-1)
   b$=color$ &amp; " " &amp; b$
ENDIF

REM "+" the noun needs another noun to follow it (selection from noun modifier)
IF RIGHT$(b$,1)="+" THEN
   z=LEN(b$)
   b$=LEFT$(b$,z-1)
   b$=nounmod$ &amp; " " &amp; b$
ENDIF

RETURN


CheckForModifierAfter:
REM Check for modifiers after

REM "*" the noun is a color only color-friendly nouns considered
IF RIGHT$(b$,1)="*" THEN
   z=LEN(b$)
   b$=LEFT$(b$,z-1)
   b$=b$ &amp; " " &amp; bc$
ENDIF

REM "+" the noun needs another noun to follow it (selection from noun modifier)
IF RIGHT$(b$,1)="+" THEN
   z=LEN(b$)
   b$=LEFT$(b$,z-1)
   b$=b$ &amp; " " &amp; nounmod$
ENDIF

RETURN

REM DATA Statements for Poem Generator

REM 35 Articles
articles:
DATA "The","All of the","Most of the","Some of the" 
DATA "My","Your","His","Her","Their","Our","Everybody's","Almost all of the"
DATA "That","I knew that the","We knew that the","She knew that the","He knew that the","They knew that the","And the coming","Oh, the","A spring of","Beyond the","Within the","And the","Alone, alone,","I fear","I looked upon","A","But where the","Like","A still and","Alone","All alone, a","The moving","It is"  

REM 60 Nouns
nouns:
DATA "darkness","morning","morning+","light","feeling","feeling+","beauty","love","hatred","happiness","sadness","anger","frustration","expression","message","ship","lips","mouth","voice","garment","saint","snake","snakes","water","fire","lead","dreams","air","ghost","sails","sleep","river","cloud","@moon","@sun","waters","life","stars","the stars","lightning","beams","beams+","*beard","fool","white*","black*","green*","blue*","yellow*","red*","light beams","*river","*garment","*ship","*snake","*snakes","*sails","voice+","ship+","ocean blue"

REM 46 Verbs (lv)
verbs:
DATA "was","had been","will be","could be","might be","should have been","would have been","could have been","drunk","drank","lite","heavy","blessed","glossy","velvet","flash","kind","coiled","swarm","swarmed","fire","pity","filled","fill","moved","wind","danced","steep","wide","steep and wide","thick","light","roar","loud","more loud","lightning","struck","fell","more horrible","horrible","awful","hit","huge","holds","long","surrounds"

REM 45 Adjectives
adjectives:
DATA "abstract","mysterious","permanent","unfortunate","was unfortunate"
DATA "intricate","confusing","serene","confusing"
DATA "true","false","fake","a lie"
DATA "a stranger","a friend","an enemy"
DATA "terrible","enchanting","is mine","was yours","is his","is hers","was theirs","was ours"
DATA "fortunate","was understood","mine","interesting","is mutual","with an artistic flair","was musical"
DATA "golden pond","glossy lake","blessed","moment","unaware","no","yes","sure"
DATA "like","dreams","between","inbetween","alone","than that"

REM 28 Color modifiers
colormodifiers:
DATA "morning","light","beauty","love","ship","lips","garment","knight","saint","snake","snakes","water","fire","ghost","sails","river","cloud","@moon","@sun","waters","stars","lightning","beams","beard","velvet","flash","grass","book"

REM 7 Color table entries
colortable:
DATA "red","yellow","blue","green","white","black","orange"

REM 14 Noun Modifier Table (Nouns, Verbs/Adverbs, Adjectives)
nounmodifier:
DATA "sunshine","light","glow","fog","of peace","of love","blue","great","unloved","sad","happy","loved","of light","of love"