Story Generator (#96)

Boris P. wrote:

Little Red-Cap revisited

Nice one Boris!

Regards,
Jordan

On Oct 1, 2006, at 12:29 PM, Leslie V. wrote:

#!/usr/bin/env ruby -w
“S cries.” ]

puts output.strip.gsub(/(.{1,80}|\S{81,})(?: +|$\n?)/, “\1\n”)

END

I just get:

jim.rb:17: odd number list for Hash
something to do with the email formatting? I’ll have to look later.

See if this version holds the formatting better and works for you:

http://pastie.caboo.se/15699

James Edward G. II

On Sep 29, 2006, at 9:02 AM, Ruby Q. wrote:

The secret of the rabbit’s magick sword will be revealed when my
story generated
is posted.

There’s one problem, as the perpetrator of this quiz, I’m not sure if
I should post my solution. But if I don’t, the secret will be forever
lost :slight_smile: What to do?

Also, I’ve built another story generator using very simple code. I
did this because JEG II pointed out that my original solution was a
bit complex and took me more time than one should spend on a Ruby
Quiz. So I set myself the task of seeing what I could do with code
that I wrote in less than an hour. The results are much more rigid
than what the original story generator produces. Although the code
didn’t take long to write, I have to admit I’ve been working for
three hours on the story template and the phase dictionaries and
still haven’t finished. To give an idea of the flavor of this new
generator I include two (incomplete) stories it generated:

  1. The Three Bears Go To Dwemthy’s Array

One day Papa Bear asked, “Vacation starts next month. Where shall we
go?”

Papa Bear wanted to go to Dwemthy’s Array. Mama Bear wanted to go to
Corporate USA. But Baby Bear got all exited. “I want to go to
Dwemthy’s Array! I want to go to Dwemthy’s Array! I want to go to
Dwemthy’s Array!”

In the end, they agreed to go to Dwemthy’s Array.

Although it seemed nearly forever to Baby Bear, next month eventually
arrived. The B.s piled into their pick-up truck and off they went.
Along the way they stop twice to let the little one use a rest room.

They stayed four days. While they were there they saw a Jabberwocky,
a Vicious Green Fungus, a Bogus Fox, a Rabbit. At the park’s
restaurants they had Dwemthy’s pizza, Mama Dragnon’s roast rabbit,
jabberwocky steak. They enjoyed attractions such as Demon Twister,
Monster-Go-Round, Dragon’s Den. Mama Bear was shocked by the Demon
Twister. Baby Bear especially liked the Bogus Fox.

On the way back they stopped at a ice cream store where they all had
three-scoop sundaes.

Papa Bear thought <park_feature> was best. Mama Bear thought
<park_feature> was best. But Baby Bear was certain that the Bogus Fox
was really the best.

The end.

  1. The Three Droids Go To Corporate USA

One day R2P2 asked, “Vacation starts next week. Where shall we go?”

R2P2 wanted to go to Corporate USA. R2M2 wanted to go to Corporate
USA. But R2B2 got all exited. “I don’t want to go to Corporate USA! I
don’t want to go to Corporate USA! I don’t want to go to Corporate USA!”

In the end, they agreed to go to Corporate USA.

Although it seemed nearly forever to R2B2, next week eventually
arrived. The Droids piled into their SUV and off they went. Along the
way they stopped at a ice cream store where they all had three-scoop
sundaes.

They stayed three days. While they were there they saw a Tax
Accountant, a Marketing Manager, a Chief Executive Officer, a Stock
Broker. At the park’s restaurants they had Board Room Buffet ™,
outsourced curry, pizza. They enjoyed attractions such as Golden
Parachute Drop, Chamber of Outsourcing Horrors, Takeover Museum. R2M2
was shocked by the Chamber of Outsourcing Horrors. R2B2 especially
liked the Marketing Manager.

On the way back they made a wrong turn and got lost.

R2P2 thought <park_feature> was best. R2M2 thought <park_feature> was
best. But R2B2 was certain that the Marketing Manager was really the
best.

The end.

My second question: should I post the new generator?

Regards, Morton

On Oct 1, 2006, at 6:37 PM, James Edward G. II wrote:

forever lost :slight_smile: What to do?

I can’t think of any good reason not to show the code. The whole
point of Ruby Q. is to share and learn.

I take it from your answer that one is only allowed to submit one
solution and that I should submit the first solution but not the
second. Am I reading you right?

Regards, Morton

On Oct 1, 2006, at 4:37 PM, Morton G. wrote:

On Sep 29, 2006, at 9:02 AM, Ruby Q. wrote:

The secret of the rabbit’s magick sword will be revealed when my
story generated
is posted.

There’s one problem, as the perpetrator of this quiz, I’m not sure
if I should post my solution. But if I don’t, the secret will be
forever lost :slight_smile: What to do?

I can’t think of any good reason not to show the code. The whole
point of Ruby Q. is to share and learn.

James Edward G. II

On 10/1/06, Morton G. [email protected] wrote:

There’s one problem, as the perpetrator of this quiz, I’m not sure
if I should post my solution. But if I don’t, the secret will be
forever lost :slight_smile: What to do?

I can’t think of any good reason not to show the code. The whole
point of Ruby Q. is to share and learn.

I take it from your answer that one is only allowed to submit one
solution and that I should submit the first solution but not the
second. Am I reading you right?

Well it’s a mailing list and not just a quiz list. So let’s see all
the code! What’s not part of the quiz can just not go in the quiz,
since the silent period is over, right?

Les

Here is my solution, which is based on Dwemthy’s Array.

#! /usr/bin/ruby # Author: Morton G. # # Date: 2006-09-23 # # Based on the Dwemthy's Array example in Chapter 6 of "why's (poignant) # guide to ruby".

This is the story of a rabbit that went on a quest to slay a dragon.

But before he got to face the dragon, he had to kill a whole bunch

of other montsters, each much stronger than he. How could a tiny, weak

rabbit survive against this terrible arary of monsters? Well, he had

a magick sword, magick lettuce, a few bombs, and – most

importantly –

incredible good luck.

Abstract class providing the basic behavior of all the creatures in

the

story.

class Creature

# Get a metaclass for this class.
def self.metaclass
   class << self; self; end
end

# Advanced metaprogramming code for nice, clean traits.
def self.traits(*arr)
   return @traits if arr.empty?
   # Set up accessors for each variable
   attr_accessor(*arr)
   # Add a new class method to for each trait.
   arr.each do |a|
      metaclass.instance_eval do
         define_method(a) do |val|
            @traits ||= {}
            @traits[a] = val
         end
      end
   end
   # For each monster, the `initialize' method should use the

default
# number for each trait.
class_eval do
define_method(:initialize) do
self.class.traits.each do |k,v|
instance_variable_set("@#{k}", v)
end
end
end
end

# Damage assessment after taking a hit during fight.
def hit(damage)
   bonus = rand(magick)
   if bonus % 9 == 7
      @life += bonus
      puts "Protective spell adds #{bonus} to #{self} life force."
   end
   puts "Fighting lowers #{self} life force by #{damage}."
   case
   when damage < 1
      puts "#{self} wasn't touched!"
   when damage < 0.25 * life
      puts "#{self} suffered a minor wound."
   when damage < 0.5 * life
      puts "#{self} was wounded."
   when damage < 0.75 * life
      puts "#{self} was seriously wounded but carries on."
   when damage < life
      puts "#{self} was gravely wounded but survives."
   else
      puts "#{self} dies."
   end
   @life -= damage
end

# One participant's attack during one fight turn.
def attack
   puts attack_description
   damage = rand(strength * life + weapon_force)
   foe.hit(damage)
end

# One fight (attack + counter-attack) in a battle.
def fight
   if life <= 0
      puts "#{self} is too dead to fight."
   else
      puts "#{self} [#{life}] and #{foe} [#{foe.life}] fight."
      # Attack opponent.
      attack if foe.life > 0
      # Opponent's counter-attack.
      foe.attack if life > 0 && foe.life > 0
   end
   self
end

def to_s
   self.class.name
end

# Description of how an attack was made. Subclasses will often

override
# this.
def attack_description
“#{self} attacks #{foe} with #{weapon}.”
end

# Creature default attributes are read-only.
traits :life, :strength, :magick, :challenge, :weapon, :weapon_force
# This trait is dynaminc -- don't give it a default value.
traits :foe

end

The monsters.

class BogusFox < Creature

life 50
strength 0.6
magick 100
weapon 'axe'
weapon_force 20
challenge "Hail, %s, prepare to die!"

def attack_description
   "BogusFox " + ["swings", "strikes with"][rand(2)] + " his axe."
end

end

class Jabberwocky < Creature

life 100
strength 0.8
magick 100
weapon 'teeth and claws'
weapon_force 20
challenge "Ah, a tasty %s!"

end

class DemonAngel < Creature

life 540
strength 0.2
magick 200
weapon 'black sword'
weapon_force 20
challenge "%s, I will eat your soul!"

def attack_description
   "DemonAngel thrusts " + ["high", "low"][rand(2)] +
   " with her black sword."
end

end

class ViciousGreenFungus < Creature

life 320
strength 0.8
magick 300
weapon 'acid spray'
weapon_force 100
challenge "No %s has ever left my presence alive."

end

class Dragon < Creature

life 1340               # really tough hide
strength 1.0            # big muscles
magick 1066             # studied with Merlin
weapon 'blast of flame' # fiery breath
weapon_force 940
challenge "A brave %s burns just as well as a timid one."

end

MONSTERS = [
BogusFox.new,
Jabberwocky.new,
DemonAngel.new,
ViciousGreenFungus.new,
Dragon.new
].freeze

The hero.

class Rabbit < Creature

traits :bombs

life 25              # no armor or shield
strength 0.4         # not in great shape
magick 50            # more than you might expect
weapon 'boomerang'
weapon_force 4       # can't handle heavy stuff
challenge "I fear you not, %s!"
bombs 3              # quantity

# Little boomerang. Nearly useless.
def ^
   self.weapon = Rabbit.weapon
   self.weapon_force = Rabbit.weapon_force
   fight
end

# Potent magick sword. Rabbit's Vorpal blade is powered by

opponent’s
# life force – a healthier opponent suffers more damage. The
rabbit’s
# only chance? Well, he has some bombs.
def /
self.weapon = ‘magick sword’
self.weapon_force = magick + foe.life
fight
end

# Bombs. Powerful, but rabbits don't have very many.
def *
   if @bombs.zero?
      puts "Rabbit is out of bombs!"
      return
   end
   @bombs -= 1
   self.weapon = 'bomb'
   self.weapon_force = 1600
   fight
end

# Eating magick lettuce casts a spell that improves health.
def %
   gain = 5 + rand(magick)
   puts "Eating magick lettuce adds #{gain} to Rabbit life force."
   @life += gain
end

end

QUOTE = ‘"’

The quest.

if $0 == FILE
hero = Rabbit.new
MONSTERS.each do |a_foe|
hero.foe = a_foe
a_foe.foe = hero
cry = a_foe.challenge % hero
puts %Q[A #{a_foe} emerges from the gloom and cries out,"#{cry}"]
puts QUOTE + (hero.challenge % a_foe) + QUOTE
# Combat! Hero attacks, foe retaliates. Over and over to the
death,
# but whose death?
while hero.life > 0
if a_foe.life <= 0
# Victory – hero munches on magic lettuce.
hero.%
break
end
# Strike foe with magick sword.
hero./
# Throw bomb if foe still major treat.
hero.* if a_foe.life > 250
end
break if hero.life <= 0
end
puts “It’s over. It’s all over.”
end

Regards, Morton

On Oct 1, 2006, at 5:47 PM, Morton G. wrote:

There’s one problem, as the perpetrator of this quiz, I’m not
sure if I should post my solution. But if I don’t, the secret
will be forever lost :slight_smile: What to do?

I can’t think of any good reason not to show the code. The whole
point of Ruby Q. is to share and learn.

I take it from your answer that one is only allowed to submit one
solution and that I should submit the first solution but not the
second. Am I reading you right?

Nope. People submit multiple solutions all the time.

We’re a low rules kind of crowd.

James Edward G. II

On 10/1/06, Boris P. [email protected] wrote:

Little Red-Cap revisited

Once upon a time little red-cap opened the stomach of the huntsman.

Gruesome I got a couple of really strange situations with my generator
too!

Les

On Oct 1, 2006, at 10:02 PM, James Edward G. II wrote:

On Oct 1, 2006, at 5:47 PM, Morton G. wrote:

I take it from your answer that one is only allowed to submit one
solution and that I should submit the first solution but not the
second. Am I reading you right?

Nope. People submit multiple solutions all the time.

We’re a low rules kind of crowd.

OK, here’s my second solution. It applies the KISS principle with a
vengeance. Since I had some C and regex experience before I took up
Ruby, I could have written this when I was two weeks into my Ruby
life. I had to fall back to such a simple and inelegant style to meet
my self-imposed deadline of one hour or less to write the code. Then,
of course, I blew it by spending almost three hours working on the
story template, word lists, and phrase dictionaries, to make the
output read smoothly. But that’s not coding, right? :slight_smile:

I really like my first solution a lot better, but this one has some
charm if I do say so myself.

The Three Bears Go To Corporate USA

One day Papa Bear asked, “Vacation starts next month. Where shall we
go?”

Papa Bear wanted to go to Corporate USA. Mama Bear wanted to go to
Jurassic Park. But Baby Bear got all exited. “I want to go to
Corporate USA! I want to go to Corporate USA! I want to go to
Corporate USA!”

In the end, they agreed to go to Corporate USA.

Although it seemed nearly forever to Baby Bear, next month eventually
arrived. The B.s piled into their mini-van and off they went. Along
the way they made a wrong turn and got lost.

They stayed two days. While they were there they saw a Venture
Capitalist, a Marketing Manager, a Stock Broker, and a Tax
Accountant. At the park’s restaurants they had pizza, CEO’s platter,
and outsourced curry. They enjoyed attractions such as the Chamber of
Outsourcing Horrors, the Cubicle Maze, and the Golden Parachute Drop.
Mama Bear was shocked by the Cubicle Maze. Baby Bear especially liked
the Tax Accountant.

On the way back they stopped at an ice cream store where they all had
three-scoop sundaes.

Papa Bear thought the Venture Capitalist was best. Mama Bear thought
the Marketing Manager was best. But Baby Bear was certain that the
Tax Accountant was really the best.

The end.
</sample story>

#! /usr/bin/env ruby -w # # Created by Morton G. on 2006-10-01. # # quiz_96.rb -- Story Generator

class Array

def pick(n=1)
   sample = self.dup
   result = []
   until n <= 0 || sample.empty?
      result << sample.delete_at(rand(sample.size))
      n -= 1
   end
   result
end

def pick1
   self.pick.first
end

def pick!(n=1)
   result = []
   until n <= 0 || self.empty?
      result << self.delete_at(rand(self.size))
      n -= 1
   end
   result
end

end

PARKS = [ ‘Jurassic Park’, “Dwemthy’s Array”, ‘Corporate USA’ ]

FAMILIES = %w[ Armadillo Artichoke Bear Droid ]

EXHIBITS = {
‘Jurassic Park’ =>
%w[
tyrannosaur
raptor
sauropod
triceratops
maiasaur
styracosaur
],
“Dwemthy’s Array” =>
[
‘Rabbit’,
‘Bogus Fox’,
‘Jabberwocky’,
‘Demon Angel’,
‘Vicious Green Fungus’,
‘Dragon’
],
‘Corporate USA’ =>
[
‘Tax Accountant’,
‘Commodities Trader’,
‘Venture Capitalist’,
‘Stock Broker’,
‘Chief Executive Officer’,
‘Marketing Manager’
]
}

FOODS = {
‘Jurassic Park’ =>
[
‘Dinoburgers’,
‘sauropod steak’,
‘softshell trilobite’,
‘Kentucky fried pterodon’,
‘Jurassic pizza’,
‘dinosaur kebabs’
],
“Dwemthy’s Array” =>
[
‘jabberwocky steak’,
‘Green fungus omelet’,
‘magick lettuce’,
“Mama Dragnon’s roast rabbit”,
“Dwemthy’s pizza”
],
‘Corporate USA’ =>
[
“CEO’s platter”,
‘NGO salad’,
“Venture Capitalist’s delight”,
‘Board Room Buffet ™’,
‘outsourced curry’,
‘pizza’]
}

ATTRACTIONS = {
‘Jurassic Park’ =>
[
‘DinoCoaster ™’,
‘Dismal Swamp Flatboat’,
‘Raptor Rodeo’,

   ],
"Dwemthy's Array" =>
   [
      'Monster-Go-Round',
      'Fungus Garden',
      'Bogus Fox Bowling',
      'Demon Twister',
      "Dragon's Den"
   ],
'Corporate USA' =>
   [
      'Golden Parachute Drop',
      'Cubicle Maze',
      'Takeover Museum',
      'Chamber of Outsourcing Horrors'
  ]

}

EVENTS = [
“stopped at an ice cream store where they all had three-scoop
sundaes”,
“made a wrong turn and got lost”,
“had to stop twice to let the little one use a rest room”,
“had to swerve violently to avoid a <?> crossing the road”
]

def choose_park(*parks)
choices = parks.uniq
return choices.first if choices.size == 1 # unanimous
return choices.pick1 if choices.size == 3 # all different
# two out three
parks.pop == parks.first ? parks.first : parks.last
end

$park = choose_park($p1=PARKS.pick1, $p2=PARKS.pick1, $p3=PARKS.pick1)
$family = FAMILIES.pick1
$do_not = [ $p1, $p2, $p3 ].uniq.size == 1 ? “don’t " : “”
$papa = case $family
when ‘Droid’
‘R2P2’
else
'Papa ’ + $family
end
$mama = case $family
when ‘Droid’
‘R2M2’
else
'Mama ’ + $family
end
$baby = case $family
when ‘Droid’
‘R2B2’
when ‘Artichoke’
‘Sprout ’ + $family
else
‘Baby ’ + $family
end
$time = %w[ week month ].pick1
$duration = %w[ two three four ].pick1
$exhibits = EXHIBITS[$park].pick(4)
$baby_favorite = $exhibits.pick1
$attractions = ATTRACTIONS[$park].pick(3)
$shocker = $attractions.pick1
favorites = $exhibits + $attractions - [ $baby_favorite ]
$papa_favorite = favorites.pick!.first
$mama_favorite = (favorites - [ $shocker ]).pick1
$exhibits = ‘a ’ + $exhibits.join(’, a ‘)
k = $exhibits.rindex(’,’)
$exhibits.insert(k + 1, ’ and’)
$foods = FOODS[$park].pick(3).join(”, “)
k = $foods.rindex(’,’)
$foods.insert(k + 1, ’ and’)
$attractions = 'the ’ + $attractions.join(”, the ")
k = $attractions.rindex(’,’)
$attractions.insert(k + 1, ’ and’)
$vehicle = [ ‘SUV’, ‘pick-up truck’, ‘car’, ‘mini-van’ ].pick1
events = EVENTS.dup
$trip_event = events.pick!.first.sub(/<?>/, EXHIBITS[$park].pick1)
$return_event = events.pick1.sub(/<?>/, EXHIBITS[$park].pick1)

TEMPLATE = <<TXT
The Three #{$family}s Go To #{$park}

One day #{$papa} asked, “Vacation starts next #{$time}. Where shall
we go?”

#{$papa} wanted to go to #{$p1}. #{$mama} wanted to go to #{$p2}. But
#{$baby} got all exited. “I #{$do_not}want to go to #{$p3}! I #
{$do_not}want to go to #{$p3}! I #{$do_not}want to go to #{$p3}!”

In the end, they agreed to go to #{$park}.

Although it seemed nearly forever to #{$baby}, next #{$time}
eventually arrived. The #{$family}s piled into their #{$vehicle} and
off they went. Along the way they #{$trip_event}.

They stayed #{$duration} days. While they were there they saw #
{$exhibits}. At the park’s restaurants they had #{$foods}. They
enjoyed attractions such as #{$attractions}. #{$mama} was shocked by
the #{$shocker}. #{$baby} especially liked the #{$baby_favorite}.

On the way back they #{$return_event}.

#{$papa} thought the #{$papa_favorite} was best. #{$mama} thought the
#{$mama_favorite} was best. But #{$baby} was certain that the #
{$baby_favorite} was really the best.

The end.
TXT

puts TEMPLATE

Regards, Morton