[QUIZ] Story Generator (#96)

My submission might have been scrubbed because I included a ZIP file,
so here it is again...

Wow, I can't wait to see that generator!

James Edward Gray II

Ok!

It's a bit long and not particularly smart, but here it is. The idea
was to make a sort of Ruby Choose-Your-Own adventure book, where the
computer chooses the adventure. By using variable names which made
sense in the context of the story, I could get the generator to
substitute random alternatives for nouns etc. while leaving the story
failrly easy to follow in code.

Reopening the Array class made multiple decisions very easy to program
- man I love Ruby! In the interim testing I see my unique-item-from-array
helper is not working because the arrays are not static. This makes for
some stories with duplicate names, which isn't nice.

I had some trouble coming up with good names for individual paragraphs
in the story tree, if you look you may see what I mean.

You have to run "story_generator.rb".

Les

------------------ Another run --------------------------------

A long time ago in the land of Middle-Earth there was a loyal Knight named
Sweetcheeks. Sweetcheeks lived there with her interesting Eyebrow
Derick, and the two were pals.

Middle-Earth was in uproar because the Necronomicon, the book of the
dead, had been stolen
by the smelly witch Smythe. Smythe had hidden the book in the Batpoo
Cave and it's presence there
would raise the dastardly Armies of Darkness at midnight!

Sweetcheeks's plan was to recover the book of the dead and outwit the
Armies of Darkness, with Derick serving as her navigator.
While Derick memorized the layout of the Batpoo Cave, Sweetcheeks
memorized the magical words needed to safely
retrieve the Necronomicon.

Sweetcheeks and Derick spent days exploring the Batpoo Cave, stumbling
through the damp undergrowth and biting insects.
When they finally found the Necronomicon, Sweetcheeks prepared to say
the secret phrase - but could not remember it!
'Flitzen!' she said, pretending to remember. With a shudder the earth
split open and the Armies of Darkness rose!

Sweetcheeks and Derick beat a hasty retreat through the Batpoo Cave,
but the elders of Middle-Earth were not interested in their
excuses and drove them out to face the enemy!
In a final desperate standoff the friends remembered the magic words
and used the secret powers of the Necronomicon to defeat the Armies of
Darkness!

THE END.

--------------------------- THE CODE ----------------------------------

<code>
<story_generator.rb>
require 'helper'
require 'phrase_generator'
require 'character'

class StoryGenerator
  def initialize(phraseGenerator, mainCharacter, subCharacter)
    @pg = phraseGenerator
    @mc = mainCharacter
    @sc = subCharacter

    getWords
  end

  def introPhrase
    ["A long time ago", "In a parallel universe", "On the planet of
Albanara"].any
  end

  def getWords
      @once = introPhrase
      @nod = @pg.landName
      @brave = @pg.goodAdjective
      @loyal = @pg.goodAdjective
      @knight = @pg.title
      @friends = @pg.bondingPhrase

      @john = @mc.name
      @his = @mc.hisHer
      @him = @mc.himHer
      @he = @mc.heShe

      @dog = @sc.species
      @skip = @sc.name

      @consternation = @pg.consternation
      @evil = @pg.evilAdjective
      @scary = @pg.evilAdjective

      @dragon = @pg.evilSpecies
      @smarg = @pg.evilName

      @clever = @pg.clever
      @rent, @ribbons = @pg.outcome
      @sword = @pg.cuttingWeapon
      @whirling = @pg.distraction

      @armies = @pg.evilArmy
      @forest = @pg.hidingPlace
      @necronomicon, @magicalBook = @pg.mysteriousArtifact
      @abracadabra = @pg.magicWord

      @scardyPants = [@john, @skip].any
      @braveSirRobin = ([@john, @skip] - [@scardyPants]).to_s

      @mummydesc, @mummy = @pg.magicalRescuer
  end

  def story
    "#{@once} in the land of #{@nod} there was a #{@brave} #{@knight} named\n" +
    "#{@john}. #{@john} lived there with #{@his} #{@loyal} #{@dog} #{@skip}, " +
    "and the two were #{@friends}.\n\n" +
    [premiseBattle, premiseNecro].any
  end

  def premiseBattle
    "The land of #{@nod} was in #{@consternation} because of the #{@evil}\n" +
    "influence of a #{@scary} #{@dragon} named #{@smarg}.\n\n" +
    [distactPlanBattle, catapultPlanBattle].any
  end

  def distactPlanBattle
    "#{@john} and #{@skip} came up with a #{@clever} plan - #{@skip} would\n" +
    "distract the #{@dragon}, giving #{@john} the opportunity to
attack unseen.\n\n" +
    executionBattle
  end

  def catapultPlanBattle
    "#{@john} and #{@skip} came up with a #{@clever} plan - #{@skip}
would lure the\n" +
    "#{@dragon} from it's lair while #{@john} would use one of
#{@nod}'s catapults to blast\n" +
    "it from a safe distance.\n\n" +
    catapultPlanOutcome
  end

  def catapultPlanOutcome
    "#{@skip} bravely taunted the #{@dragon} and when it was in range,
#{@john} fired the catapult.\n" +
    ["The ball of flaming pitch struck the side of the #{@dragon} and
enveloped it in flames! Before\n" +
     "long, nothing was left og the #{@scary} #{@dragon} was a pile of
cinders. " + returnToNod1,

     "The ball of flaming pitch struck #{@skip} squarely between the
eyes! Before anyone could react,\n" +
     "#{@skip} had disappeared in a ball of flame. #{@john} stared in
stunned silence, but the #{@dragon},\n" +
     "sensing an opportunity, blasted #{@john} with it's magic.\n\n" +
failedCatapult,
    ].any
  end

  def failedCatapult
    "#{@john} was crumpled by the blast, and that was the tragic end
of the two friends. The #{@dragon}'s\n" +
    "reign of terror continued unabated over the unfortunate land of
#{@nod} for many years thereafter.\n\n"
  end

  def executionBattle
    "They rode bravely into battle and #{@john} #{@rent} the #{@scary}
#{@dragon} in\n" +
    "#{@ribbons} with #{@his} #{@sword} while #{@skip} created a
diversion by #{@whirling}!\n" +
    resultBattle
  end

  def resultBattle
    ["#{@john} and #{@skip} became the heroes of #{@nod} and lived
happily ever after.\n\n",
     "#{@john} and #{@skip} became well-reknowned in #{@nod} and lived
to be old and wise.\n\n",
     "The wise friends were the heroes of the day but were soon
forgotten by the ungrateful citizens of #{@nod}.\n\n",
     "#{@john} and #{@skip} left the land of #{@nod} to seek fame and
fortune and great pizza!\n\n"].any
  end

  def premiseNecro
    "#{@nod} was in #{@consternation} because the #{@necronomicon},
the #{@magicalBook}, had been stolen\n" +
    "by the #{@scary} #{@dragon} #{@smarg}. #{@smarg} had hidden the
book in the #{@forest} and it's presence there\n" +
    "would raise the #{@evil} #{@armies} at midnight!\n\n" +
    planNecro
  end

  def planNecro
    "#{@john}'s plan was to recover the #{@magicalBook} and outwit the
#{@armies}, with #{@skip} serving as #{@his} navigator.\n" +
    "While #{@skip} memorized the layout of the #{@forest}, #{@john}
memorized the magical words needed to safely\n" +
    "retrieve the #{@necronomicon}.\n\n" +
    [executionNecro1, executionNecro2].any
  end

  def executionNecro1
    "The two friends set off to the #{@forest} to find the
#{@necronomicon}. They searched for many hours, fighting\n" +
    "through the tangled vegetation and being scratched by long thorns
and briars. Deep in the darkest part of #{@forest}\n" +
    "they finally found the #{@necronomicon}. #{@john} spoke the magic
phrase perfectly and the #{@magicalBook} flashed\n" +
    "into #{@his} hands... \n\n" +
    [resultNecro1, resultNecro2].any
  end

  def resultNecro1
    "#{@john} was instantly subverted by the dark power of the magical
device! #{@he.capitalize} rose as the leader of the #{@armies}\n" +
    "in siege against the land of #{@nod}, burning it to the ground.\n\n"
  end

  def resultNecro2
    "#{@john}'s personality was instantly joined with the magical
device, moulding it's power for good. #{@he.capitalize} rose in great
power\n" +
    "and with trusty #{@skip} by #{@his} side, #{@he} singlehandedly
destroyed the #{@armies}!\n\n" +
    [returnToNod1, returnToNod2].any
  end

  def returnToNod1
    "Returning to #{@nod}, #{@john} and #{@skip} were hailed as heroes
and crowned rulers of the land. They ruled justly\n" +
    "for many years in the future.\n\n"
  end

  def returnToNod2
    "Returning to #{@nod} however, #{@john} was subverted by #{@his}
newfound power. When the city elders would not crown #{@him} ruler,\n"

ยทยทยท

On 10/1/06, James Edward Gray II <james@grayproductions.net> wrote:
+
    "#{@he} tried to take power from them. After a bitter war, #{@he}
was overcome and killed by the people of #{@nod}, led by #{@his}\n" +
    "old friend #{@skip}.\n\n"
  end

  def executionNecro2
    "#{@john} and #{@skip} spent hours exploring the #{@forest},
stumbling through the damp undergrowth and biting insects (sic).\n" +
    "When they finally found the #{@necronomicon}, #{@john} prepared
to say the secret phrase - but could not remember it!\n" +
    "'#{@abracadabra}!' #{@he} said, pretending to remember. With a
shudder the earth split open and the #{@armies} rose!\n\n" +
    resultNecro3
  end

  def resultNecro3
    ["#{@john} and #{@skip} beat a hasty retreat through the
#{@forest}, but the elders of #{@nod} were not interested in their\n"
+
      "excuses and drove them out to face the enemy!\n" +
      "In a final desperate standoff the friends " + outcomeNecro,

      "#{@john} and #{@skip} ran themselves ragged through the
brambles but the #{@armies} were gaining on them. Eventually\n" +
      "they tumbled down an embankment and stumbled on " + salvationNecro
    ].any
  end

  def salvationNecro
    "a #{@mummydesc}!\n" +
    "#{@scardyPants} cowered in fear, but #{@braveSirRobin} quickly
started flattering their potential ally.\n\n" +
    salvationOutcomeNecro
  end

  def salvationOutcomeNecro
    [
      "The #{@mummy} was so grateful to have found friends after so
many lonely years in the #{@forest} that he accepted their request\n"
+
       "and rose in their defense. He singlehandedly crushed the
#{@armies} with powerful magic. For years the friends lived on
together\n" +
       "in the #{@forest}, the #{@mummy} learning to adapt to #{@skip}
and #{@john} and them learning to love the #{@forest}.\n\n",

      "The #{@mummy} was touched but not fooled. Annoyed by their
pleading, he blasted the two friends with his magic and then\n" +
       "joined the evil #{@armies} to bring devastation to the whole
land of #{@nod}.\n\n"
    ].any
  end

  def outcomeNecro
    ["were crushed by the evil minions as they marched unopposed over
the whole land of #{@nod}.\n\n",
     "remembered the magic words and used the secret powers of the
#{@necronomicon} to defeat the #{@armies}!\n\n",
     "turned tail and ran.\n" +
     "Skirting the land of #{@nod} they escaped to the kingdom of
Darrel, leaving #{@nod} to fend for itself.\n\n"
    ].any
  end

  def generate
    response = story
    response << "THE END.\n\n"
  end
end

pg = PhraseGenerator.new

characters =
2.times do
  name, hisHer, heShe, himHer = pg.goodName
  characters << Character.new(name, hisHer, heShe, himHer,
pg.goodAdjective, pg.goodSpecies)
end

sg = StoryGenerator.new(pg, characters[0], characters[1])
puts sg.generate

</story_generator.rb>

<character.rb>
class Character
  attr_reader :name, :hisHer, :heShe, :himHer, :adjective, :species

  def initialize(name, hisHer, heShe, himHer, adjective, species)
    @name = name
    @hisHer = hisHer
    @heShe = heShe
    @himHer = himHer
    @adjective = adjective
    @species = species
  end
end
</character.rb>

<helper.rb>
class Array
  #Try to get a unique response
  def any
    @anylist = if !@anylist
    response = nil
    (self.length*3).times do
      response = self[rand(length)]
      break if !(@anylist.include? response)
    end
    @anylist << response
    response
  end
end
</helper.rb>

<phrase_generator.rb>
require 'helper'

class PhraseGenerator
    def goodName
      response =
      if (rand(2)==1)
        response << %W[Skip Derick Brian Kevin Brightbritches Lightleaves].any
        response << "his"
        response << "he"
        response << "him"
      else
        response << %W[Goldilocks Cindy Sweetcheeks Barbara Liselle].any
        response << "her"
        response << "she"
        response << "her"
      end
      response
    end

    def landName
      %W[Dwenthym Narnia Atlantis Middle-Earth].any
    end

    def goodAdjective
      %W[brave hardcore dedicated loyal honest tireless cool interesting].any
    end

    def title
      %W[Knight Accountant Duke Stable-keep Botanist].any
    end

    def goodSpecies
      %W[Aunt Eyebrow Donkey Armadillo Jellyfish Dog Page Friend].any
    end

    def consternation
      (%W[consternation uproar mayhem] + ["a bind"]).any
    end

    def evilName
      %W[Smarg Argonagas Bel Smythe Zoot].any
    end

    def evilAdjective
      %W[dastardly despicable evil smelly repulsive scary terrifying
horrific].any
    end

    def evilSpecies
      %W[dragon morgawr witch troll].any
    end

    def clever
      %W[clever daring brave smile smart diabolical].any
    end

    def outcome
      [
        %W[rent ribbons],
        %W[sliced strips],
        ["stabbed", "the knees"],
      ].any
    end

    def cuttingWeapon
      ["powerful magic sword", "portable angle-grinder"].any
    end

    def distraction
      ["whirling like a Dervish", "prancing like a poodle",
        "rolling in the dirt", "screaming synonyms"].any
    end

    def princessName
      %W[Leia Arabella Liselle Diana Linda Jenna].any
    end

    def hidingPlace
      ["Dark Forest", "Stinky Swamp", "Batpoo Cave"].any
    end

    def evilArmy
      ["Armies of Darkness", "Hordes of Hell"].any
    end

    def mysteriousArtifact
      [
        ["Necronomicon", "book of the dead"],
        ["Sceptara", "magical Sceptre of Ra"]
      ].any
    end

    def magicWord
      %W[Flitzen Bratwurst Alacazam Avrocadavra].any
    end

    def magicalRescuer
      [
        ["massive stone Giant", "giant"],
        ["powerful magical Unicorn", "unicorn"],
        ["Mummy Lord", "mummy"]
      ].any
    end

    def bondingPhrase
      ["thick as thieves", "never apart", "always together", "secretly
lovers", "pals"].any
    end
end
</phrase_generator.rb>

<test_helper.rb>
require 'test/unit' unless defined? $ZENTEST and $ZENTEST
require 'helper'

class TestArray < Test::Unit::TestCase
  def test_any
    a = [1, 2, 3, 4, 5]

    outOfBounds = false
    100.times do
      any = a.any
      outOfBounds = true if !(a.include? any)
    end
    assert(!outOfBounds, "Item returned is not in the array")
  end

  def test_any_coverage
    a = [1, 2, 3, 4, 5]

    coverage = {}
    count = 0
    loop do
      any = a.any
      coverage[any] = 0
      break if coverage.keys.length == 5

      count += 1
      if count > 100
        assert(false, message="Possible random number problem,
coverage not achieved after 100 iterations")
        return
      end
    end

    assert(coverage.keys.length == 5)
    assert(coverage.keys.inject{|a, b| a > b ? a : b} == 5) #max = 5
    assert(coverage.keys.inject{|a, b| a < b ? a : b} == 1) #min = 1
  end

  def test_any_variance
    a = [1, 2]
    any1 = a.any
    any2 = a.any

    assert(any2 == 2) if any1 == 1
    assert(any2 == 1) if any1 == 2

    assert(a.include?(a.any))
  end
end
</test_helper.rb>

</code>