Jump to content

Mad Libs

From Rosetta Code
Revision as of 17:09, 8 November 2011 by rosettacode>Glennj (add Ruby)
Mad Libs is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Mad Libs is a phrasal template word game where one player prompts another for a list of words to substitute for blanks in a story, usually with funny results.

Write a program to create a Mad Libs like story. The program should read a multiline story from the input. The story will be terminated with a blank line. Then, find each replacement to be made within the story, ask the user for a word to replace it with, and make all the replacements. Stop when there are none left and print the final story.

The input should be in the form:

<name> went for a walk in the park. <he or she>
found a <noun>. <name> decided to take it home.

It should then ask for a name, a he or she and a noun (<name> gets replaced both times with the same value.)

This page uses content from Wikipedia. The original article was at Mad Libs. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)

C++

<lang cpp>#include <iostream>

  1. include <string>

using namespace std;

int main() {

 string story, input;
 //Loop
 while(true)
 {
   //Get a line from the user
   getline(cin, input);
   //If it's blank, break this loop
   if(input == "\r")
     break;
   //Add the line to the story
   story += input;
 }
 //While there is a '<' in the story
 int begin;
 while((begin = story.find("<")) != string::npos)
 {
   //Get the category from between '<' and '>'
   int end = story.find(">");
   string cat = story.substr(begin + 1, end - begin - 1);
   //Ask the user for a replacement
   cout << "Give me a " << cat << ": ";
   cin >> input;
   //While there's a matching category 
   //in the story
   while((begin = story.find("<" + cat + ">")) != string::npos)
   {
     //Replace it with the user's replacement
     story.replace(begin, cat.length()+2, input);
   }
 }
 //Output the final story
 cout << endl << story;
 return 0;

}</lang>

Go

Variance: The fun of Mad Libs is not knowing the story ahead of time, so instead of asking the player to enter the story template, my program asks the player to enter the file name of a story template (with contents presumably unknown to the player.) <lang go>package main

import (

   "bufio"
   "fmt"
   "io/ioutil"
   "os"
   "regexp"
   "strings"

)

func main() {

   pat := regexp.MustCompile("<.+?>")
   if len(os.Args) != 2 {
       fmt.Println("usage: madlib <story template file>")
       return
   }
   b, err := ioutil.ReadFile(os.Args[1])
   if err != nil {
       fmt.Println(err)
       return
   }
   tmpl := string(b)
   m := make(map[string]string)
   for _, p := range pat.FindAllString(tmpl, -1) {
       m[p] = ""
   }
   fmt.Println("Enter replacements:")
   br := bufio.NewReader(os.Stdin)
   for p := range m {
       for {
           fmt.Printf("%s: ", p[1:len(p)-1])
           r, isPre, err := br.ReadLine()
           if err != nil {
               fmt.Println(err)
               return
           }
           if isPre {
               fmt.Println("you're not playing right")
               return
           }
           s := strings.TrimSpace(string(r))
           if s == "" {
               fmt.Println("  hmm?")
               continue
           }
           m[p] = s
           break
       }
   }
   fmt.Println("\nYour story:\n")
   fmt.Println(pat.ReplaceAllStringFunc(tmpl, func(p string) string {
       return m[p]
   }))

}</lang> Sample run:

Enter replacements:
name: Wonko the Sane
noun: wild weasel
he or she: he

Your story:

Wonko the Sane went for a walk in the park. he
found a wild weasel. Wonko the Sane decided to take it home.


Icon and Unicon

This just runs with the sample. It would be much more fun with a database of randomly selected story templates. <lang Icon>procedure main()

  ml := "<name> went for a walk in the park. There <he or she> _
         found a <noun>. <name> decided to take it home."  # sample
  MadLib(ml)                                               # run it

end

link strings

procedure MadLib(story)

  write("Please provide words for the following:")
  V := []
  story ? while ( tab(upto('<')), put(V,tab(upto('>')+1)) )
  every writes(v := !set(V)," : ") do 
     story := replace(story,v,read())
  write("\nYour MadLib follows:\n",story)

end</lang>

strings.icn provides replace

Sample output:

Please provide words for the following:
<noun> : keys
<he or she> : she
<name> : Goldilocks

Your MadLib follows:
Goldilocks went for a walk in the park. There she found a keys. Goldilocks decided to take it home.

Pike

this solution uses readline to make editing more convenient. <lang Pike>#!/usr/bin/pike

Stdio.Readline readln = Stdio.Readline();

void print_help() {

   write(#"Write a Story.

Names or objects in the story can be made variable by referencing them as <person> <object>, etc. End the story with an empty line.

Type show to read the story. You will be asked to fill the variables, and the the story will be shown.

Type help to see this message again. Type exit to quit.

"); }

void add_line(string input) {

   array variables = parse_for_variables(input);
   write("Found variables: %{\"%s\" %}\n", variables);
   story += input+"\n";

}

array parse_for_variables(string input) {

   array vars = Array.flatten(array_sscanf(input, "%*[^<>]%{<%[^<>]>%*[^<>]%}%*[^<>]"));
   return Array.uniq(vars);

}

mapping fill_variables(string story) {

   array vars = parse_for_variables(story);
   mapping variables = ([]);
   foreach(vars;; string name)
   {
       string value = readln->read(sprintf("Please name a%s %s: ", (<'a','e','i','o','u'>)[name[1]]?"":"n", name));
       if (value != "")
           variables["<"+name+">"] = value;
   }
   return variables;

}

void show_story(string story) {

   mapping variables = fill_variables(story);
   write("\n"+replace(story, variables));

}

void do_exit() {

   exit(0);

}

mapping functions = ([ "help":print_help,

                      "show":show_story,
                      "exit":do_exit,
                    ]);

string story = "";

void main() {

   Stdio.Readline.History readline_history = Stdio.Readline.History(512);
   readln->enable_history(readline_history);

   string prompt="> ";

   print_help();
   while(1)
   {
       string input=readln->read(prompt);
       if(!input)
           exit(0);
       if(input == "")
           show_story(story);
       else if (functions[input])
           functions[input]();
       else add_line(input);
   }

}</lang>

Sample output:

Write a Story.

Names or objects in the story can be made variable by 
referencing them as <person> <object>, etc.
End the story with an empty line.

Type show to read the story. You will be asked to fill the variables, 
and the the story will be shown.

Type help to see this message again.
Type exit to quit.

> <person> is a programmer.
Found variables: "person" 
> <he or she> created <website> for all of us to enjoy.
Found variables: "he or she" "website" 
> 
Please name a person: Michael
Please name a he or she: he
Please name a website: RosettaCode

Michael is a programmer.
he created RosettaCode for all of us to enjoy.
> 
Please name a person: Guilaumme
Please name a he or she: he
Please name a website: PLEAC

Guilaumme is a programmer.
he created PLEAC for all of us to enjoy.
> exit

Ruby

<lang ruby>puts "Enter a story, terminated by an empty line:" story = "" until (line = STDIN.gets).chomp.empty?

 story << line

end

story.scan(/(?<=[<]).+?(?=[>])/).uniq.each do |var|

 print "Enter a value for '#{var}': "
 story.gsub!(/<#{var}>/, STDIN.gets.chomp)

end

puts "" puts story</lang>

Example

Enter a story, terminated by an empty line:
<name> went for a walk in the park. <he or she>
found a <noun>. <name> decided to take it home.

Enter a value for 'name': FOO
Enter a value for 'he or she': BAR
Enter a value for 'noun': BAZ

FOO went for a walk in the park. BAR
found a BAZ. FOO decided to take it home.


Tcl

<lang tcl>package require Tcl 8.5

  1. Read the template...

puts [string repeat "-" 70] puts "Enter the story template, ending with a blank line" while {[gets stdin line] > 0} {

   append content $line "\n"

}

  1. Read the mapping...

puts [string repeat "-" 70] set mapping {} foreach piece [regexp -all -inline {<[^>]+>} $content] {

   if {[dict exists $mapping $piece]} continue
   puts -nonewline "Give me a $piece: "
   flush stdout
   dict set mapping $piece [gets stdin]

}

  1. Apply the mapping and print...

puts [string repeat "-" 70] puts -nonewline [string map $mapping $content] puts [string repeat "-" 70]</lang> Sample session:

----------------------------------------------------------------------
Enter the story template, ending with a blank line
<name> went for a walk in the park. <he or she>
found a <noun>. <name> decided to take it home.

----------------------------------------------------------------------
Give me a <name>: Wonko the Sane
Give me a <he or she>: He
Give me a <noun>: wild weasel
----------------------------------------------------------------------
Wonko the Sane went for a walk in the park. He
found a wild weasel. Wonko the Sane decided to take it home.
----------------------------------------------------------------------
Cookies help us deliver our services. By using our services, you agree to our use of cookies.