Friday 10 November 2017

Web Tutorial: Ruby Mad Libs

Hey, y'all.

Ever heard of this game called Mad Libs? You supply a list of words and the players make a story out of them. Most of the time, the results are so whacky, it's funny.

If you enjoyed playing that, you're in luck because we're going to make our very own Mad Libs game. For that, we'll be using Ruby. QBasic will do, too, in a pinch, but Ruby makes it easier by having hash features and string substitution similar to C. So if you've got Ruby installed, create your program file and let's get cracking!

First, we do this. As you may have surmised, this prints a nice introductory text on the screen.
puts "Welcome to TeochewThunder's Mad Libs!"


Next, we're going to introduce a Do-While loop. In Ruby, it's expressed as such. Here, we have a variable, continue, which is set to an empty string. And the program continues for eternity until a certain condition is met. In this case, that's when continue has a value of "Y" or "y". Y, you ask? (heh heh) Well, read on...
puts "Welcome to TeochewThunder's Mad Libs!"

continue = ""
begin

end while continue == "Y" or continue == "y"


Because, within the loop, we print a string asking if the user wishes to continue, then use a gets statement to assign user input to the variable continue. So if the user enters anything else but "Y" or "y", the program ends.
puts "Welcome to TeochewThunder's Mad Libs!"

continue = ""
begin
    puts "Do you wish to continue? (Y/N)"
    continue = gets
end while continue == "Y" or continue == "y"


Try it. Does it work? I bet the program exits no matter what!


That's because if you typed in "Y" or "y" as input and then pressed the Enter key, the input gets captured as "Y\n" or "y\n". Yes, even the Enter key counts as input! Seems silly, doesn't it? But fret not, there's an easy way out. Just chomp the end off, and your input will be correct.
begin
    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp
end while continue == "Y" or continue == "y"


Try it again. "Y" or "y" should cause the program to keep running!


Now, within the loop, let's set the variable content by calling a function, getMadLibContent()... and create that function right at the beginning of your code.
def getMadLibContent()

end

puts "Welcome to TeochewThunder's Mad Libs!"

continue = ""
begin
    content = getMadLibContent()

    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp
end while continue == "Y" or continue == "y"


This function should return a hash object. First, let's define an array, contentTypes.
def getMadLibContent()
    contentTypes = [ ]
end


Each array element is a hash object, containing they key story, which points to a string, and words, which points to another array.
def getMadLibContent()
    contentTypes = [
            {
                "story" => "",
                "words" => [ ]
            },
    ]
end


Here, we define the story, using "%s" where we expect words to be substituted.
def getMadLibContent()
    contentTypes = [
            {
                "story" => "While eating %s, I %s took up my %s and placed it under my %s. It %s %s.",
                "words" => [ ]
            },
    ]
end


Next, we make an array of the kind of words we want to substitute into the placeholders provided by "%s". Bear in mind that they have to be in order.
def getMadLibContent()
    contentTypes = [
            {
                "story" => "While eating %s, I %s took up my %s and placed it under my %s. It %s %s.",
                "words" => [
                    "food (e.g, pie, noodles, shortcake)",
                    "adverb (e.g, happily, carefully, quickly)",
                    "noun (e.g, hammer, balloon, cigar)",
                    "item of clothing (e.g, hat, shoe, coat)",
                    "past action (e.g, shouted, ran, floated)",
                    "adverb (e.g, happily, carefully, quickly)"
                ]
            },
    ]
end


Make two more! Get creative!
def getMadLibContent()
    contentTypes = [
            {
                "story" => "While eating %s, I %s took up my %s and placed it under my %s. It %s %s.",
                "words" => [
                    "food (e.g, pie, noodles, shortcake)",
                    "adverb (e.g, happily, carefully, quickly)",
                    "noun (e.g, hammer, balloon, cigar)",
                    "item of clothing (e.g, hat, shoe, coat)",
                    "past action (e.g, shouted, ran, floated)",
                    "adverb (e.g, happily, carefully, quickly)"
                ]
            },
            {
                "story" => "She bought a %s %s and started %s it as it %s. It felt %s to do so, and she continued %s.",
                "words" => [
                    "descriptive (e.g, blue, glad, large)",
                    "furniture (e.g, table, stool, cupboard)",
                    "active action (e.g, running, eating, fighting)",
                    "past action (e.g, shouted, ran, floated)",
                    "descriptive (e.g, blue, glad, large)",
                    "active action (e.g, running, eating, fighting)"
                ]   
            },
            {
                "story" => "His %s was a %s %s and %s %s like a %s. He would try to %s, but this would result in %s %s.",
                "words" => [
                    "relative (e.g, father, son, cousin)",
                    "descriptive (e.g, blue, glad, large)",
                    "noun (e.g, hammer, balloon, cigar)",
                    "adverb (e.g, happily, carefully, quickly)",
                    "past action (e.g, shouted, ran, floated)",
                    "animal (e.g, goldfish, mouse, lion)",
                    "action (e.g, think, stand, kick)",
                    "people (e.g, men, gardeners, mechanics)",
                    "active action (e.g, running, eating, fighting)"
                ]   
            },
    ]
end


OK, that's enough content to start with. You want there to be an element of surprise, so generate a random number and assign the value to the variable scenario, using the rand() function and passing in the length of the contentTypes array as an argument, which is 3. This will net you a random number from 0 to 2. Then create a variable, madlibcontent, and set it to the value of the array element in contentTypes pointed to by scenario. You don't have to explicitly type a return statement at this point, because Ruby functions automatically return the last expression in the function.
def getMadLibContent()
    contentTypes = [
            {
                "story" => "While eating %s, I %s took up my %s and placed it under my %s. It %s %s.",
                "words" => [
                    "food (e.g, pie, noodles, shortcake)",
                    "adverb (e.g, happily, carefully, quickly)",
                    "noun (e.g, hammer, balloon, cigar)",
                    "item of clothing (e.g, hat, shoe, coat)",
                    "past action (e.g, shouted, ran, floated)",
                    "adverb (e.g, happily, carefully, quickly)"
                ]
            },
            {
                "story" => "She bought a %s %s and started %s it as it %s. It felt %s to do so, and she continued %s.",
                "words" => [
                    "descriptive (e.g, blue, glad, large)",
                    "furniture (e.g, table, stool, cupboard)",
                    "active action (e.g, running, eating, fighting)",
                    "past action (e.g, shouted, ran, floated)",
                    "descriptive (e.g, blue, glad, large)",
                    "active action (e.g, running, eating, fighting)"
                ]   
            },
            {
                "story" => "His %s was a %s %s and %s %s like a %s. He would try to %s, but this would result in %s %s.",
                "words" => [
                    "relative (e.g, father, son, cousin)",
                    "descriptive (e.g, blue, glad, large)",
                    "noun (e.g, hammer, balloon, cigar)",
                    "adverb (e.g, happily, carefully, quickly)",
                    "past action (e.g, shouted, ran, floated)",
                    "animal (e.g, goldfish, mouse, lion)",
                    "action (e.g, think, stand, kick)",
                    "people (e.g, men, gardeners, mechanics)",
                    "active action (e.g, running, eating, fighting)"
                ]   
            },
    ]

    scenario = rand(contentTypes.length)
    madlibcontent = contentTypes[scenario]
end


Now, in your loop, content should be the hash object you got from the function getMadLibContent(). Create a variable, userProvided and set it to store an array that has the same length as the words array in the content hash object.
continue = ""
begin
    content = getMadLibContent()
    userProvided = Array.new(content['words'].length)

    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp
end while continue == "Y" or continue == "y"


Then iterate through the words array using a For loop.
continue = ""
begin
    content = getMadLibContent()
    userProvided = Array.new(content['words'].length)
    for i in 0..content['words'].length-1

    end

    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp
end while continue == "Y" or continue == "y"


In the For loop, print a statement asking the user to enter a word or phrase, then print the clue provided by the appropriate element in the words array. And obtain user input using gets again. Don't forget to chomp! Assign the input to the corresponding element in the userProvided array.
continue = ""
begin
    content = getMadLibContent()
    userProvided = Array.new(content['words'].length)
    for i in 0..content['words'].length-1
        puts "Please enter a word or phrase:"
        puts content['words'][i]

        userProvided[i] = gets.chomp   
    end

    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp
end while continue == "Y" or continue == "y"


So far so good, yeah?


Now it's time to tell the story! Use the puts statement to print out the story string from the content hash object.
continue = ""
begin
    content = getMadLibContent()
    userProvided = Array.new(content['words'].length)
    for i in 0..content['words'].length-1
        puts "Please enter a word or phrase:"
        puts content['words'][i]

        userProvided[i] = gets.chomp   
    end

    puts "Here is your story:"
    puts content['story']

    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp
end while continue == "Y" or continue == "y"


Try it! Oh dear, not really what you wanted!


Use this statement instead, followed by a "%" and a test array.
puts content['story'] % ["test1","test2"]


See? You get an error because there are more instances of "%s" than there are elements in the array you provided. But how would we know how many instances of "%s" to replace, and what to replace them with?


Use the userProvided array instead. This will substitute all instances of "%s" with the appropriate element from the userProvided array!
puts content['story'] % userProvided


Try it again. There's your story!




This is really just a simple programming exercise that I'm using as an initial foray into Ruby. I'm really digging it so far.

I'll leave you to %s your %s. Have %s fun!
T___T

No comments:

Post a Comment