in reply to Creating a random generator

Here is an update to what I posted previously in this thread. With all the changes made, this is hopefully more understandable. Because of the length of the script, I have broken it down into several code blocks. Notes are at the end on areas where work still needs to be done. Comments from within the code will be pointing to those notes.

Head block

This first code block is the head of the script.

#!/usr/bin/perl -w use strict; use warnings; use diagnostics; use CGI; use CGI::Carp qw(fatalsToBrowser); use Games::Dice qw(roll); use HTML::Template; use Lingua::EN::Inflect qw(PL PL_N PL_V PL_ADJ NO NUM PL_eq PL_N_eq PL +_V_eq PL_ADJ_eq A AN PART_PRES ORD NUMWORDS inflect classical def_nou +n def_verb def_adj def_a def_an); use B; =pod =head1 NAME F<rg_mutate.pl> =head1 AUTHOR Lady_Aleena =head1 ACKNOWLEDGEMENTS Perl Monks: Anno, bradenshep, Corion, ikegami, Limbic~Region, Petruchi +o, shmem, Sidhekin, wfsp, ysth and the rest of the community that has + been so kind to me. =head1 SYNOPSIS This script randomly generates mutations of player characters. =cut print "content-type: text/html \n\n"; my %Data; sub random { my ($name) = @_; my $array = $Data{$name}; my $pick = $array->[rand @$array]; if (ref($pick) eq 'CODE') {return $pick->();} else {return $pick;} }
Static block

This next code block is all the static arrays with the arrays in alphabetical order.

#Static# $Data{AbNm} = [qw(strength dexterity constitution intelligence wisdom +charisma)]; $Data{Algn} = [qw(lawful chaotic good evil neutral)]; $Data{BdPt} = ['eye','lip','nail','nose','hair','skin','entire body']; $Data{ClSp} = [qw(Air All Animal Astral Chaos Charm Combat Creation Di +vination Earth Fire Guardian Healing Law Necromatic Numbers Plant Pro +tection Summoning Sun Thought Time Travelers War Wards Water Weather) +]; $Data{Clss} = [qw(Warrior Rogue Priest Wizard Psionisist)]; $Data{Dice} = [qw(1d4 1d6 1d8 1d10 1d12 1d20)]; $Data{Divn} = ['Demi-','Lesser ','Intermediate ','Greater ']; $Data{Eff1} = [qw(Acid Cold Electricity Fire Gas Sonic)]; $Data{Eff2} = ['Acid','Cold','Electricity','Fire','Poison','Energy Dra +in']; $Data{Eff3} = ['Acid','Cold','Electricity','Fire','Energy Drain','Gas' +,'Poison','Sonic']; $Data{Emtn} = [qw(joy sorrow trust fear love hate indifference)]; $Data{Fail} = ['all crtical misses','failed backstabs','failed saving +throws']; $Data{Gaze} = [qw(Paralysis Stone Stun Death)]; $Data{Gems} = [qw(diamond ruby sapphire emerald amethyst garnet spinel + zircon crystal quartz)]; $Data{ImDm} = ['immune to','takes ×2 dmg from']; $Data{LNWp} = ['language','non-weapon proficiency','weapon']; $Data{MITp} = ['Magical Liquids','Scrolls','Rings','Rods','Staves','Wa +nds','Books','Gems & Jewelry','Clothing','Boots & Gloves','Girdles & +Helms','Bags & Bottles','Dust & Stones','Household Items','Musical In +struments','Weird Stuff','Humorous Items','Armor and Shields','Weapon +s','Artifacts']; $Data{Mtls} = [qw(platinum gold silver copper nickel bronze tin iron s +teel lead aluminum)]; $Data{PlMi} = [qw(+ -)]; $Data{Radi} = ["1' or Touch","5'","10'","20'","50'","100'"]; $Data{Siz1} = [qw(taller shorter)]; $Data{Siz2} = [qw(longer shorter)]; $Data{SpFr} = ['common','uncommon','rare','very rare']; $Data{SvTr} = ['Paralyzation','Poison','Death Magic','Rods','Staves',' +Wands','Petrifaction','Polymorph','Breath Weapon','Spells']; $Data{Thng} = ['animals','plants','constructs','dragons','lycenthropes +','undead','inanimate objects']; $Data{Time} = [qw(round turn hour day week month year)]; $Data{Toch} = ['befouls','purifies','nullifies holy water','nullifies +unholy water']; $Data{Vocl} = [qw(Deafen Fear Terror Flight)]; $Data{WpMg} = ['non-','']; $Data{WpMt} = [qw(bone metal stone wooden)]; $Data{WpTp} = [qw(bludgeoning piercing slashing missile)]; $Data{WzSc} = [qw(Abjuration Air Alteration Conjuration/Summoning Divi +nation Earth Enchantment/Charm Fire Illusion/Phantasm Invocation/Evoc +ation Necromancy Water)];
Dynamic block

This code block is all of the arrays that do things with the above and amongst themselves.

#Dynamic# sub radius {"in a ".random("Radi")." radius"} sub tdice { my $tdice = roll(random("Dice")); return "$tdice ".PL_N("time",$tdice)." ".A(random("Time")); } $Data{SpellsT} = [ sub {random("WzSc")." school"}, sub {random("ClSp")." sphere"} ]; $Data{Resist} = [ "immune", sub {"is ".roll('1d100')."% magic resistant"}, "has no resistance" ]; $Data{Spells} = [ "all spells", "all Wizard spells", "all Priest spells", sub {"the ".random("WzSc")." school of wizard spells"}, sub {"the ".random("ClSp")." sphere of priest spells"}, sub {random("Eff3")." spells and spell-like effects"} ]; $Data{MIGp} = [ sub {"magic items in the ".random("MITp")." group"}, "all magic items" ]; $Data{MagicItems} = [ "can use all magic items", "can not use any magic items", sub {"can only use magic items in the ".random("MITp")." group"}, sub {"can not use magic items in the ".random("MITp")." group"}, sub {"causes ".random("MIGp")." to dysfunction ".radius}, sub {"causes ".random("MIGp")." to loose their power ".radius}, sub {"attracts ".random("MIGp")." ".radius}, sub {"repels ".random("MIGp")." ".radius}, sub {"destroys ".random("MIGp")." ".radius} ]; $Data{OtherMagic} = [ "magic dead", sub {"a ".random("Radi")." radius Wild Magic zone"}, "a magic attracter", "addicted to magic energies" ]; $Data{Weapon} = [ sub {random("WpTp")}, sub {random("WpMt")}, sub {random("WpMg")."magical"}, "all" ]; $Data{NWPlearn} = [ sub {random("AbNm")." based"}, sub {random("Clss")." specific"} ]; $Data{SATS} = [ sub {"to saving throws vs. ".random("SvTr")}, "Armor Class", "THAC0 modifier", "to Surprise" ]; $Data{Attack} = [ sub {"Breath Weapon - ".random("Eff1")." for ".random("Dice")." damage +"}, sub {"Gaze Attack - ".random("Gaze")}, sub {"Touch Attack - ".random("Eff2")." for ".random("Dice")." damage" +}, sub {"Vocal Attack - ".random("Vocl")} ]; my %color_info = (red=>'f00',yellow=>'ff0',green=>'0f0',cyan=>'0ff',bl +ue=>'00f',magenta=>'f0f',white=>'fff',black=>'000'); $Data{Color} = [map {"<span class=\"bo\" style=\"color:#$color_info{$_ +};\">$_</span>"} keys %color_info]; $Data{Aura} = [ sub {"visible ".random("Color")}, sub {random("Emtn")}, sub {random("Algn")} ];
Body block - Another dynamic block

This block belongs to the last array in this block, @Body.

my %mammal_info = (bovine=>'cattle', canine =>'dog', caprine =>'goat', + equine=>'horse', feline =>'cat', leporine=>'rabbit', lupine=>'wolf', + ovine =>'sheep', porcine =>'pig', ursine=>'bear', vulpine=>'fox') +; $Data{Mammal} = [map {"<a title=\"$mammal_info{$_}\">$_</a>"} keys %ma +mmal_info]; $Data{Animal} = [ "amphibian", "insetoid", sub {random("Mammal")}, "reptilian" ]; $Data{Animal2} = [qw(amphibian insetoid mammallian reptilian)]; $Data{Head} = [ "an enlarged head", "a shrunken head", "horns" ]; $Data{Eyes} = [ "glowing eyes", "the appearance of no eyes", sub {random("Color")." eyes"}, "eyes in an odd position", "only one eye", sub {roll('1d4+2')." eyes"}, "infravision<p>&#0134; ultravision if infravision is already present</ +p>", sub {my $clrbld = (roll('1d100')>80?'green':'red'); "$clrbld-weakness +color blindness"}, "photo-sensitivity" ]; $Data{Ears} = [ "lobeless ears", "only ear holes", "no ears", "extra-sensitive hearing", sub {roll('1d10*10')."% deafness"}, sub {roll('1d4+2')." ears"} ]; $Data{Nose} = [ "an oversized nose", "only nose holes", "no nose", sub {"a nose with an ".random("Animal")." appearance"}, "an extra-sensitive nose", "no sense of smell" ]; $Data{Mouth} = [ sub {random("Color")." lips"}, "no lips", "an oversized mouth", sub {roll('1d2+1')." mouths"}, ]; $Data{Arms} = [ sub {"arms that are ".roll('1d50')."% ".random("Siz2")." than average" +}, "no elbow joint", "no wrist joint", sub {roll('1d4+2')." arms (all arms above triple the normal amount are + vestigial)"}, sub {random("Animal")."-like arms"} ]; $Data{Hands} = [ "elongated fingers", "one less finger on each hand", "double jointed fingers", "animal claws", "snake fingers", "webbed fingers" ]; $Data{Legs} = [ sub {"legs that are ".roll('1d50')."% ".random("Siz2")." than average" +}, "no knee joint", "no ankle joint", sub {roll('1d4+2')." legs (all legs above double the normal amount are + vestigial)"}, sub {random("Animal")." legs"} ]; $Data{Feet} = [ "elongated toes", "one less toe on each foot", "no toes", "animal paws", "claws for toes", "webbed toes" ]; $Data{Skin} = [ "fur covered skin", sub {random("Animal")." skin"}, "fish scales", "feathers", "invisible skin", sub {random("Color")." skin"}, "ultra-sensitive skin" ]; $Data{Bones} = [ "wooden bones", sub {random("Gems")." bones"}, sub {random("Mtls")." bones"}, "fragile bones", "unbreakable bones" ]; $Data{Teeth} = [ "enlarged teeth", "fangs", "fangs with venom", sub {random("Gems")." teeth"}, sub {random("Mtls")." teeth"}, "two sets of teeth" ]; $Data{Hair} = [ sub {random("Color")." hair"}, "odd textured hair", "uncuttable hair", "no hair anywhere", "invisible hair", sub {random("Mtls")." hair"} ]; $Data{Nails} = [ sub {random("Gems")." nails"}, sub {random("Mtls")." nails"}, "no nails", "unbreakable nails" ]; $Data{Blood} = [ "icor for blood", "poisonous blood", sub {random("Color")." blood"} ]; $Data{Body} = [ sub {random("Skin")}, sub {random("Bones")}, sub {random("Blood")}, sub {random("Head")}, sub {random("Hair")}, sub {random("Eyes")}, sub {random("Ears")}, sub {random("Nose")}, sub {random("Mouth")}, sub {random("Teeth")}, sub {random("Arms")}, sub {random("Hands")}, sub {random("Legs")}, sub {random("Feet")}, sub {random("Nails")}, sub {random("Animal2")." wings"}, sub {random("Animal")." tail"}, "gills" ];
SED block - And another dynamic block

This block belongs to the last array in this block, @SED, which is short for SleepEatDrink.

$Data{Sleep} = [ 'hardly ever wakes up', 'needs twice as much sleep', 'needs half as much sleep', 'does not need sleep', 'is an insomniac', 'is a narcoleptic', 'is unable to remember the past after sleeping' ]; $Data{Eat} = [ 'can not stop eating', 'needs twice as much food', 'needs half as much food', 'does not need to eat', 'is carnivorous', 'is herbivorous', 'is lethargic after eating any amount of food' ]; $Data{Drink} = [ 'can not stop drinking', 'needs twice as much liquid', 'needs half as much liquid', 'does not need liquids', 'is an alcoholic', 'can not get drunk', 'is intoxicated after injesting any liquid' ]; $Data{SED} = [ sub {random("Sleep")}, sub {random("Eat")}, sub {random("Drink")}, sub {random("Sleep")." and ".random("Eat")}, sub {random("Sleep")." and ".random("Drink")}, sub {random("Eat")." and ".random("Drink")}, sub {random("Sleep").", ".random("Eat").", and ".random("Drink")} ];
VLA block

This is where everything comes together, in this vla.

$Data{Mutation} = [ "no unusual effect", "General ability", #See note 1 "Sub-ability", #See note 2 sub {"casts up to ".random("SpFr")." ".random("SpellsT")." spells as a + natural ability ".tdice." (See the spell progession for maximum spel +l level.)"}, sub {random("Resist")." to ".random("Spells")}, sub {random("MagicItems")}, sub {"is ".random("OtherMagic")}, sub {random("ImDm")." ".random("Weapon")." weapons"}, "takes only magical damage", sub {"knows one ".random("LNWp")." that parent knows"}, sub {"knows all ".PL_N(random("LNWp"))." that parent knows"}, sub {"unable to learn ".PL_N(random("LNWp"))." that parent knows"}, "unable to learn any language other than racial language", sub {random("PlMi").roll('1d4')." on all non-weapon proficiency checks +"}, sub {"unable to learn ".random("NWPlearn")." non-weapon proficiencies" +}, sub {"unable to learn ".random("WpTp")." weapons"}, sub {"can not use ".random("WpMt")." weapons"}, sub {"born already at ".ORD(roll(random("Dice")))." level"}, sub {"unable to advance past ".ORD(roll('5d6'))." level"}, sub {random("PlMi").roll('1d10')." ".random("SATS")}, sub {"a Wild Magic Surge accompanies ".random("Fail")}, "attacks as a warrior of the same level. If already a warrior, attacks + at 1 level higher.", "can not make a Critical Hit", "attracts these followers: <ul>(RangerFollowers.pl)</ul>", # See note +5 sub {"repels all animals ".radius}, "backstabs as a thief of the same level. If already a thief, backstabs + at 1 level higher.", "turns undead as a priest of the same level. If already a priest, turn +s at 1 level higher.", "Wild Psionic Talent: (Psionics.pl)", ## See note 5 "unable to use Psionics", sub {"Special Attack: ".random("Attack")." ".tdice}, sub {"is ".roll('1d50')."% ".random("Siz1")." than average"}, sub {"has ".random("Body")}, sub {"can change ".random("BdPt")." color at will to ".random("Color") +}, sub {$Data{BdPt}[roll('1d3-1')]."s turn ".random("Color")." when magic + is used ".radius}, sub {$Data{BdPt}[roll('3d2+1')]." turns ".random("Color")." when magic + is used ".radius}, sub {"has ".A(random("Aura"))." aura"}, sub {"communicates with ".random("Thng")." ".radius}, sub {"knows history of ".random("Thng")." ".radius}, "is blessed", "is cursed", sub {"touch ".random("Toch")}, sub {random("SED")}, sub {do 'rg_ins.pl'}, ####****This is where rg_ins.pl goes.****#### "has characteristics of (AAn-AllMonsters.pl)", # See note 5 "has lycanthropy as a (Lycanthrope.pl)", #See note 5 sub {random("SED")." after seeing (AAn-AllMonsters.pl) for a ".$Data{T +ime}[roll('1d4+2')]}, # See note 5 sub {"is ".random("Divn")."power - character should be retired"} ];
End block

After all of that, it has to do something, so here is the beginnings of what I would like to see happen.

print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http: +//www.w3.org/TR/2002/REC-xhtml1-20020801/DTD/xhtml1-strict.dtd">'; print '<html><head><title>Character Mutations</title><link rel="styles +heet" type="text/css" href="../../main.css"/></head><body><h1>Charact +er Mutations Generator</h1><p>All effects are cumulative, and one can + be cancelled out by another.</p><ol>'; for(1..roll(random("Dice"))){print "<li>".random("Mutation")."</li>\n" +}; print "</ol></body></html>";
rg_ins.pl

This is the file that is called by the 5th to the last line in the VLA.

#!/usr/bin/perl -w use strict; use warnings; use diagnostics; use CGI; use CGI::Carp qw(fatalsToBrowser); use HTML::Template; sub Disorder { my @Disorder = ('amnesia','catatonia','dementia','hallucinatory insani +ty','manic despression','paranoia','schizophrenia'); return $Disorder[rand @Disorder] } my %InsDesc = ( abluto=>'washing or bathing', acaro=>'ticks and mites, and the diseases they transmit', aero=>'air, atmosphere, weather, or gas', agro=>'agricultural soils, or open spaces', ailuro=>'cats', xeno=>'foreigners, strangers, foreign things, or outside causes', xero=>'dry conditions or dryness', xylo=>'wood, wooden objects, or forests', zoo=>'animals', zym=>'fermentation' ); sub Ins { my @InsKeys = keys %InsDesc; my $InsRand = $InsKeys[rand @InsKeys]; my @InsType = ( $InsRand."phobia (fear of ".$InsDesc{$InsRand}.")", $InsRand."mania (obsession of or with ".$InsDesc{$InsRand}.")", $InsRand."philia (insatiable desire for ".$InsDesc{$InsRand}.")" ); } sub Insanity { my @InsGen = (Disorder,Ins); return $InsGen[rand @InsGen]; } print Insanity; if (!caller) { print "This will only show up when run directly" };
Notes
  1. VLA [1]
    • 1st: Will there be an addition or subtraction. - $PM[rand @PM]
    • 2nd: Roll the dice. - roll('$Dice[rand @Dice]') - this may be wrong
    • 3rd: What ability will it be modifying? - This is what I don't know. How do I get it to chose a random ability from another file?
  2. VLA [2]
    • 1st: Will there be an addition or subtraction. - $PM[rand @PM]
    • 2nd: Roll the dice. - roll('$Dice[rand @Dice]') - this may be wrong
    • 3rd: What ability's sub-ability will it be modifying? - This is what I don't know. How do I get it to chose a random ability's random sub-ability from another file?
  3. I need this to not only roll the dice, I need it to look at what it rolled. If the roll is 1, then the word "time" will print. If it is greater than 1, the word "times" will print.
  4. I need something that will make any item in @LNW plural, see below.
  5. Other generators to be written, names are preliminary.
    • AllMonsters.pl - with its accompanying sub-scripts
    • rg_ins.pl - This file is nearly complete, it just needs tweaking.
    • Lycanthrope.pl
    • RangerFollowers.pl
  6. Modules I need to look into on CPAN
    • File::Slurp
    • Lingua::EN::Inflect - all of those (AorAn)s and plurals needed.
    • Math::TrulyRandom
    • Find a module to make things plural
The End

I hope that this is little clearer than the last time I posted this script. I know that it is still a mess.

Lady Aleena

Replies are listed 'Best First'.
Re^2: Creating a random generator
by shmem (Chancellor) on Oct 03, 2007 at 17:11 UTC
    Hm. You construct your arrays all fine, but after they are constructed, the random() function isn't called when you reference an array item, but you absolutely need runtime randomness for your layout to work.

    You have to add a bit of magic to your arrays with tie.

    use Tie::Array; @ISA = qw(Tie::StdArray); sub FETCH { my ($self,$index) = @_; if (ref($self->[$index]) =~ /CODE/) { return $self->[$index]->(); } $self->[$index]; }

    Now convert all your array elements which are dynamically constructed (i.e via concatenation of some strings and calls to e.g. random()) into anonymous subs, and tie those arrays:

    my @MagicItems; tie @MagicItems, main; # main, or the package of your +code @MagicItems = ( "can use all magic items", "can not use any magic items", sub { "can only use magic items in the ".random("MITp")." group"}, sub { "can not use magic items in the ".random("MITp")." group"}, sub { "causes $MagicItemGroup[rand @MagicItemGroup] to dysfunction + $radius"}, sub { "causes $MagicItemGroup[rand @MagicItemGroup] to loose their + power $radius"}, sub { "attracts $MagicItemGroup[rand @MagicItemGroup] $radius"}, sub { "repels $MagicItemGroup[rand @MagicItemGroup] $radius"}, sub { "destroys $MagicItemGroup[rand @MagicItemGroup] $radius"}, );

    Do this with every array which has randomly constructed items. Then

    for (1..10) { print $MagicItems[rand(@MagicItems,)],"\n"; };

    will yield e.g.

    can not use any magic items can not use magic items in the Magical Liquids group can not use magic items in the Musical Instruments group destroys magic items in the Scrolls group in a 20' radius repels magic items in the Bags & Bottles group in a 20' radius causes all magic items to loose their power in a 20' radius can not use magic items in the Rods group causes magic items in the Humorous Items group to loose their power in + a 20' radius can only use magic items in the Weird Stuff group can only use magic items in the Humorous Items group

    Solving the static radius problem is left as an exercise to the reader.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      I think you, ikegami, and Petruchio did great work for me during that Chatterbox conversation yesterday. It pulled what you did here together with their ideas.

      Many thanks for what you three did!

      Lady Aleena

      "An it harm none, do as ye will."

Re^2: Creating a random generator
by graff (Chancellor) on Oct 01, 2007 at 13:24 UTC
    An interesting layout, with interesting challenges. Here are some ideas that came to mind as I looked at it -- you might consider:
    • Instead of all those distinct arrays, how about a hash-of-arrays -- what you have as array names could just be hash keys.

    • You could put all the string values for loading the arrays into a plain-text data file, instead of having them in the code. A simple file format would do -- one array per line, like this "Arrayname: value | value | ..."

    • For all those places where an array value includes a randomly chosen value from another array, you just need some sort of "template symbol" to mark the position of the value and the name of the array it comes from -- e.g.
      SpellsT: @_WizardSchool school | @_WizardSchool sphere
    • For places where an array value is the result of a function call, use a slightly different template symbol, where you can include function parameters where necessary:
      Aura: visible &_color | @_Emotion | @_Align Resist: immune | is &_roll:1d100 % magic resistant | has no resistance
    Keeping all the initialization data in its own file, separate from the code, is usually helpful for maintenance -- fixing data is different from fixing code. Here's what the code might look like for loading such data:
    my %game_data; open( IN, "<", "game_data.txt" ) or die "game_data.txt: $!"; while (<IN>) { chomp; my ( $key, @values ) = split( /\s*[:|]\s*/ ); $game_data{$key} = [ @values ]; } close IN; my %init_func = { color = sub { my $val=some_simple_op(); return $val}, roll = \&roll, ... } for my $key ( keys %game_data ) { my @replace_vals = (); for my $val ( @{$game_data{$key}} ) { if ( $val =~ /[@&]_\S+/ ) { # need to split and process my @words = split ' ', $val; $val = ''; for my $wrd ( @wrds ) { if ( $wrd =~ /\@_(\S+)/ ) { my $refkey = $1; if ( exists( $game_data{$refkey} )) { my $n = @{$game_data{$refkey}}; $wrd = $game_data{$refkey}[rand $n]; } else { warn "No $refkey in game_data for $key\n"; } } elsif ( $wrd =~ /\&_(\S+)/ ) { my ( $refkey, @args ) = split /[:,]/, $1; if ( exists( $init_func{$refkey} )) { $wrd = $init_func{$refkey}->(@args); } else { warn "No $refkey function for $key\n"; } } $val .= "$wrd "; } } push @replace_vals, $val; } $game_data{$key} = [ @replace_vals ]; }
    You'll probably want to tweak that to suite your tastes for notations and additional flexibility (and I haven't tried testing it, so it may need some fixing as well).

    There is one likely (possibly vexing) downside to this approach: the sequence in which %game_data elements are initialized (replacement values put in as needed) will matter, e.g. if a key/array "foo" depends on values from key/array "bar", which depends on values from "glub", etc. If "foo" elements get processed first, they will contain "unprocessed" strings from the "bar" list.

    To get around that, the data file would need to be ordered according to "primitive sets first", "first-order dependencies next", etc, so that any line with "template symbol" references to arrays is guaranteed to come after the arrays that it references.

    In that case, you'll need to move the logic for parsing and replacement of values into the "while" loop that reads the file. The for ( keys %game_data ) loop that I suggested above would do them in random order, which would probably do the wrong thing.

      An HoA would probably add a bit more confusion right now, though I don't really know.

      I will look into a plain text file.

      That large bit of code is confusing, could you possibly break it down for me? I know there is a hash in there somewhere, but not where.

      Lady Aleena

      "An it harm none, do as ye will."

      graff;

      Every time I look at this I am loving it more, but just for the static data. Since the dynamic data might only be used here, let's not transfer them to the text file right now. That is one headache we don't need.

      The first section of the code you gave me seems a bit straight forward. I altered it to fit the current naming scheme for the HoA.

      my %Data; open( IN, "<", "data.txt" ) or die "data.txt: $!"; while (<IN>) { chomp; my ( $key, @values ) = split( /\s*[:|]\s*/ ); $Data{$key} = [ @values ]; } close IN;

      Since we are not moving any dynamic content over, is the second bit of code necessary?

      Now, the third section is the one that could get me into real trouble. I removed a lot of the spacing, but still indented a bit here and there, I hope you don't mind. I changed all that I could find to the current setup as well.

      for my $key ( keys %Data ) { my @replace_vals = (); for my $val ( @{$Data{$key}} ) { if ( $val =~ /[@&]_\S+/ ) { # need to split and process my @words = split ' ', $val; $val = ''; for my $wrd ( @wrds ) { if ( $wrd =~ /\@_(\S+)/ ) { my $refkey = $1; if ( exists( $Data{$refkey} )) { my $n = @{$Data{$refkey}}; $wrd = $Data{$refkey}[rand $n]; } else { warn "No $refkey in Data for $key\n"; } } elsif ( $wrd =~ /\&_(\S+)/ ) { my ( $refkey, @args ) = split /[:,]/, $1; if ( exists( $init_func{$refkey} )) { $wrd = $init_func{$refkey}->(@args); } else { warn "No $refkey function for $key\n"; } } $val .= "$wrd "; } } push @replace_vals, $val; } $Data{$key} = [ @replace_vals ]; }

      Now, what will get me into the most trouble is all of the regular expressions used. At least that is what I think all of those strings of punctuation are. I rarely use them in the find/replace in my text editor. When I do use them, it is with extreme caution. I will read up on them, but the docs are hard going for me. If you could explain a few of them in the code above, that would help me get a much better grasp on them.

      Leaving the dynamic portions of the script in it, there shouldn't be any problems with dependency as far as I can tell.

      Lady Aleena

      "An it harm none, do as ye will."

        Since we are not moving any dynamic content over, is the second bit of code necessary?

        If you just want to stick with nothing but static data in the data file, the first little loop that reads from the data file is all you need. The rest of it is for applying dynamics to the file content, and would only be needed if you want the file to include specs for the dynamic settings.

        The second loop loads a separate hash of code refs, keyed by the names used in the data file in order to invoke one or another function to fill a given data field (i.e. "roll()", "color()", etc). The third loop goes back over the data loaded from the file, and does replacements of "special" field values with suitable strings, based either on a random selection from a given array, or on the output of a given function (from the hash of code refs).

        Leaving the dynamic portions of the script in it, there shouldn't be any problems with dependency as far as I can tell.

        If you mean "leaving all the dynamic assignments to be done individually, in proper sequence, in the script", then you're right, there are no dependencies involving the data file -- just make sure all the "primary" (static) arrays are set, and make sure the script puts the dynamic assignments in proper order.

        But it is still possible for the data file to do more work, and for the perl script to be shorter and simpler, making the system easier to cope with. I've tried studying the other replies (esp. this one by shmem, which taught me a lot), in order to understand how the finished system ought to behave, and here's an approach that simply loads the data, with its various directives for dynamic (randomized) results at run time, and leaves all the random selections to be done at the end, via a simple recursive function that operates on those directives:

        That has just a small sample of data to demonstrate how it works -- you can add more data as you see fit. If you actually want to have two data files -- one for static strings and one for "cross-referenced dynamic" strings, that would be fine -- just read both files the same way, into the same %Data hash.

        Note that the file format is pretty flexible as to white-space formatting: the essential feature is that all arrays start with "Name:", have pipes "|" between elements, and are separated from other arrays by at least one blank line (no blank line within an array), because the "paragraph mode" input will read multi-line records up to and including 2 or more line-feeds. Comment "paragraphs" are allowed (ignored).

        You'll probably find that it's pretty easy to make mistakes in the data file (misspellings, inconsistent capitalization, missing names, wrong sigils for function vs. array, missing pipe symbols between array elements), but a data file tends to be easier to fix than code, and it wouldn't be very hard to come up with a reasonable "validator" script, to check for common mistakes in the data, and/or add some sensible error checking to the above script.

        (UPDATE: One mistake you must be very careful to avoid is a circular reference among two or more arrays -- i.e. an element in List1 uses a reference to @_List2, which in turn points back to that same element in List1. Chains like List1 - List2 - List3 - ... - List1 are nastier in the sense of being harder to track down, but the script will blow up in either type of case.)

        Regarding the "%field_func" hash, note that the "radius" function doesn't really need to be there -- it can be defined in the data file as well -- but you can/should put a code ref in there for your "insanity" function (make sure the function returns the string you want, rather than just printing the string to stdout); then include suitable references to it in the data file (" &_insanity ").

        The core of the approach is the DataString function, which has two important features: (1) it uses a while loop over a given string to make sure all "sigil" replacements are done in turn, and (2) it uses recursion (calls itself) each time it has to do an embedded Random selection. This makes it possible to lay out the data file without worrying about the relative ordering of dependent arrays; so long as all the data are loaded into %Data before going to work, everything should work.

Re^2: Creating a random generator
by Lady_Aleena (Priest) on Oct 08, 2007 at 10:23 UTC

    Addendum to Re: Creating a random generator

    There are several scripts that will go along with the main Mutation script. These scripts will be part of this random generator and will also need to be independent of it. The scripts below will become part of this ever increasing randomness. I am not sure how I am going to go about getting these to work within the main body of the script.

    • rg_ab.pl - Random abilities - this is started
    • rg_ins.pl - Random insanities - this is nearly complete
    • rg_ff.pl - Random fighter's followers
    • rg_fr.pl - Random ranger's followers
    • rg_wp.pl - Random wild psionic powers
    • rg_m.pl - Random monsters
      • rg_mb.pl - Random beholders - this is started
      • rg_mc.pl - Random constructs
      • rg_md.pl - Random dragons
      • rg_mf.pl - Random faerie
      • rg_mgi.pl - Random giants
      • rg_mgo.pl - Random goblinoids
      • rg_ml.pl - Random lycanthropes
      • rg_mm.pl - Random mixed monsters
      • rg_mp.pl - Random planar creatures
      • rg_ms.pl - Random scaly creatures
      • rg_mud.pl - Random undead
      • rg_muw.pl - Random underwater creatures
Lady Aleena

"An it harm none, do as ye will."