Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Creating a random generator

by Lady_Aleena (Priest)
on Sep 06, 2007 at 13:36 UTC ( [id://637422] : perlquestion . print w/replies, xml ) Need Help??

Lady_Aleena has asked for the wisdom of the Perl Monks concerning the following question:

Please bear with the as I am a complete novice in programming languages. The only languages that I know are HTML and CSS.

I would like to create a random generator for my website based on several charts, so that the users of my site will not have to manually roll for the results (manually meaning getting out a their dice).

Could someone please help me by showing me a basic generator, such as maybe how to randomly select a color (red, yellow, green, cyan, blue, magenta, white, gray, black)? From there, hopefully, it may be easier for me to understand how this language works.

Any help would be appreciated.

LA


Update

From the tutorials and replies below; here is a sample of some arrays that I have created for randomization.

print "content-type: text/html \n\n"; @AbilityName = qw(strength dexterity constitution intelligence wisdom +charisma ); @Class = qw(Warrior Rogue Priest Wizard Psionisist); @WeapType = qw(bludgeoning piercing slashing missile); @WeapMat = qw(bone metal stone wooden); @NWPlearn = ( "$AbilityName[rand @AbilityName] based", "$Class[rand @Class] specific" ); @Mutation = ( "unable to learn $NWPlearn[rand @NWPlearn] non-weapon proficiencies", "unable to learn $WeapType[rand @WeapType] weapons", "can not use $WeapMat[rand @WeapMat] weapons" ); print "Character mutation: $Mutation[rand @Mutation]", "\n";

This is only the beginning, some parts of the random generator that I would like to create are extremely complex, and these are just a small part of it. I just hope that I got it right so far.

Update 2

Learned a very valuable lesson, the order in which the arrays was listed is important. Also, qw only works on single word items, it doesn't seem to work on phrases. I may be wrong on that, but I will keep researching it. Updated the code above. So far, it works!

Replies are listed 'Best First'.
Re: Creating a random generator
by dsheroh (Monsignor) on Sep 06, 2007 at 13:55 UTC
    The key statement for generating random numbers is rand. By itself, it generates a fraction which is at least 0 and less than 1. Given a number, as in rand(6), it generates an integer which is at least 0 and less than the number it was given (0 to 5 in this case).

    For the example you've given with colors, the code would be:

    my @colors = qw(red yellow green cyan blue magenta white gray black); print $colors[rand @colors], "\n";
    The first line makes a list of available choices and the second creates a number from 0 to (the number of options - 1), then prints the item at that position in the list. (List elements are numbered starting at 0 rather than 1, so this can produce any element in the list.)
      Given a number, as in rand(6), it generates an integer which is at least 0 and less than the number it was given (0 to 5 in this case)

      Almost, but not quite.

      % perl -le 'print rand(6)' 4.45325434943245

      This will take the number between 0 and less than 1 and multiply it by 6, nearly always leaving stuff behind the decimal point.

      If you really don't want the fractional part you have to shave it off with either int($num) or sprintf('%.0f', $num). In the present case it'll work just fine, since Perl will Do The Right Thing and treat the number as an integer anyway, in order to use it as an index into the array. But were the OP to print the number itself, they may be surprised by the result.

      • another intruder with the mooring in the heart of the Perl

Re: Creating a random generator
by injunjoel (Priest) on Sep 06, 2007 at 17:48 UTC
    Welcome Lady_Aleena,
    I think you will find perl to be a great first language to learn. I too pretty much backed my way into consistent programming from an HTML (and JavaScript) background. I am not sure if you mean W3C or the W3schools site but thats not important. The important part is getting some grokable material to foster your nascent perl career.

    The Beginning Perl Wiki would be my suggestion.

    As Fletch mentioned, disregarding tone, learning the basics (for-loops, if-elses, etc) first will shallow out the learning curve a great deal. Of course the Monastery is always open. So long as you show what you tried (distilled case, don't just paste every line :), what you found and what you would like to have happen, you will get an answer.
    So again, Welcome and good luck.

    -InjunJoel
    "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
Re: Creating a random generator
by andreas1234567 (Vicar) on Sep 06, 2007 at 13:58 UTC
    Hi, and welcome to perlmonks.

    Perl has a built-in random number generator, rand. Follow the link to its documentation.

    use strict; use warnings; my @colors = qw (red yellow green cyan blue magenta white gray black); for (1 .. 10) { print $colors[rand(scalar(@colors))]; } __END__ $ perl -wl 637422.pl blue white gray cyan red green red black red red
    --
    Andreas
Re: Creating a random generator
by CountZero (Bishop) on Sep 06, 2007 at 15:23 UTC
    Hello Lady Aleena! Welcome to the Monastery, may your stay with us be long and full of learning.

    One thing which everyone seems to have forgotten to ask, is: "Are you sure the web-server your program is going to run on has Perl installed?"

    If not, you are really out-of-luck and will have to look elsewhere for your program.

    Perl is not something you can just "embed" in your HTML-code and have it magically run and produce the wanted results.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      CountZero...I am pretty sure that my server has Perl installed since I have forms on my site which have form handlers which I think are written in Perl. The file extensions are .pl. A very long time ago I had to do some weird stuff with those files, which I can't remember now, to make them work. All I remember was that it was weird.

Re: Creating a random generator
by Fletch (Bishop) on Sep 06, 2007 at 14:01 UTC

    To put it bluntly, you don't know any programming languages. HTML and CSS are to programming as knowing how to drive is to building a car from the ground up; yes it's a useful skill and roughly related but verrrrry little knowledge will be of use across the problem domains. Your best bet is to get an introductory book (say Learning Perl from ORA) and absorb that. Then you'll be better equipped to proceed; and you've already got a ready made problem to do your own experimentation.

    Sure someone could give you a code snippet showing you how to use rand and you could cargo cult that into something that produces some results, but you'll be back again asking how to print it out. Or how to read the list of options from a file. Or . . . ad infinitum. Learn the basics first and you'll save yourself and the people you pester for help much frustration.

      I know that markup and programming languages are very different and have tried to learn the basics from what I have found on the web. I am still looking for a site that has instructions for Perl that is like the HTML and CSS specifications on the W3C site.

      The best way for me to learn something is to learn how to apply it to my current project. If it would cause to much trouble here, I will try to find somewhere else to get the guidance I will need to complete it.

        I don't know if W3C has tutorials (and I'm having problems accessing the site from here), but if you're looking similar to the HTML and CSS specs, then you can read through the Perl documentation. Don't forget to check out the modules that come with Perl too. (If you're using ActivePerl, it installs this documentation locally in HTML format.)
Re: Creating a random generator
by dwm042 (Priest) on Sep 06, 2007 at 14:01 UTC
    A simple approach is to make a list, then scale the internal random number generator to the length of the list, and use the number generated to pick an element of the list. This is a simple example:

    #!/usr/bin/perl # use warnings; use strict; my @colors = qw(red yellow green cyan blue magenta white gray black); my $col = int(rand(scalar @colors)); print "Color = $colors[$col]\n";
    Typical output is:

    C:\Code>perl choose_color_1.pl Color = magenta C:\Code>perl choose_color_1.pl Color = red C:\Code>perl choose_color_1.pl Color = black

      esper, andreas1234567, and dwm042;

      There are tiny variations between the code that you gave me. Will that make a difference? I would like to have as little white space as possible. So are all the carriage returns needed between the lines?

      Now that I have the .pl file, how would I integrate this onto a web page? I do not have Perl installed on my computer at home so the only way to see it working is using on a web page.

      Update 1

      dwm042...On the following line, does "int" mean interger?

      my $col = int(rand(scalar @colors));

      To all

      Can the items in the array(?) be put on seperate lines? In the actual generator I would like to create each item will be a phrase, a sentence, or a table and most will consist of a lot of html code.

      Update 2

      What does the qw mean on the following line? Found it in a search.

      my @colors = qw(red yellow green cyan blue magenta white gray black);

      Update 3

      The above scripts are all causing 500 Internal Service Errors when I try to run them. I CHMOD 755'd them, was that wrong? Found the fix. I needed to add the following code.

      print "content-type: text/html \n\n";

        The first step will be to install Perl on your computer. If you have a machine running Microsoft Windows, download and install ActiveState Perl. If you have an Apple computer, chances are good that Perl already is installed. If you have any other brand of computer, the chance that it already has Perl installed isn't that bad either, but ask your machine administrator on how to access it.

        Then, you can test the .pl files directly by saving them from a text editor and then running them. On Windows, notepad.exe is an ugly but preinstalled editor. Word and Wordpad are unsuitable for editing programs. On the Apple products I don't know what built-in editors there are except vi and vi is an editor you want to avoid when starting out.

        So, if you have saved your Perl program as myprogram.pl, you can give it a local test run by opening a command prompt in the directory you saved the file to and typing

        perl -w myprogram.pl

        If there were any errors, Perl will tell you so. If not, look if the output matches what you expect.

        After that, you can upload the Perl file myprogram.pl to your web account, possibly into the cgi-bin directory, make it executable and then link to that.

Re: Creating a random generator
by elsiddik (Scribe) on Sep 07, 2007 at 12:42 UTC
    Hi there, My suggestion will be Learning perl by Randal Schwartz and Tom phoenix, it a great book to start with , try even to look around on the net there's plenty tutorials out there.
    http://perldoc.perl.org/perldoc.tar.gz http://elsiddik.googlepages.com/pickingUpPerl.pdf http://www.comp.leeds.ac.uk/Perl/start.html
    besides this amazing monastery. cheers.

      I found another site that could be added...

      http://www.tizag.com/perlT/index.php

        First of all you may have rendered that url with [http://www.tizag.com/perlT/index.php] or [http://www.tizag.com/perlT/index.php|site], which render respectively like http://www.tizag.com/perlT/index.php and site respectively.

        Then, having given a peek at that page and reading the very introduction:

        "This tutorial will be covering the PERL syntax and should provide you with a very solid foundation of PERL for you to build upon. It is recommended that before you start walking through our tutorial that you have a general understanding of Web Development as well as some background knowledge of HTML and CSS as our tutorial is directed toward Web programming."

        I can tell you in advance that it is definitely a site to be avoided like plague.

Re: Creating a random generator
by Lady_Aleena (Priest) on Sep 08, 2007 at 06:14 UTC
    Here is the code of another small part of the full generator which will be in a seperate file of which there will be over a dozen. The first 3 scalars have default information in them. I would like to be able to change those values in the array (if necessary). I thought I had it right, but I don't get the expected result when I run on my computer.
    #!/usr/bin/perl $b = beholder; $bk = "Monstrous Manual"; #default book $pg = 0; #defalt page number @gen = ( "beholder $b='' $pg=21", "death kiss $pg=21", "eye of the deep $pg=21", "gauth $pg=21", "spectator $pg=21", "undead $pg=21", "hive mother $pg=25", "director $pg=25", "examiner $pg=25", "lensman $pg=25", "overseer $pg=25", "watcher $pg=25" ); print "$gen[rand@gen] $b (<i>$bk</i>, pg $pg)", "\n";
    The result I get is...
    lensman 0=25 beholder (<i>Monstrous Manual</i>, pg 0)
    instead of...
    lensman beholder (<i>Monstrous Manual</i>, pg 25)
    I tried moving the end quote to before the variables, but that just got me a lot of errors instead.
    List of errors (condensed):
    Scalar found where operator expected at C:\***\beholder.pl line 7, near ""beholder" $b" (Missing operator before $b?) line 7, near "'' $pg" (Missing operator before $pg?) line 8, near ""death kiss" $pg" (Missing operator before $pg?) line 9, near ""eye of the deep" $pg" (Missing operator before $pg?) line 10, near ""gauth" $pg" (Missing operator before $pg?) line 11, near ""spectator" $pg" (Missing operator before $pg?) line 12, near ""undead" $pg" (Missing operator before $pg?) line 13, near ""hive mother" $pg" (Missing operator before $pg?) line 14, near ""director" $pg" (Missing operator before $pg?) line 15, near ""examiner" $pg" (Missing operator before $pg?) line 16, near ""lensman" $pg" (Missing operator before $pg?) line 17, near ""overseer" $pg" (Missing operator before $pg?) line 18, near ""watcher" $pg" (Missing operator before $pg?) syntax error at C:\***\beholder.pl line 7, near ""beholder" $b" Execution of C:\***\beholder.pl aborted due to compilation errors.
      I'm not clear on what you're trying to do. Perhaps putting the page numbers in a lookup table (a hash) would help get us along the road a bit.

      #!/usr/bin/perl use strict; use warnings; my $beholder = 'beholder'; my $book = 'Monstrous Manual'; #default book my @gen = ( 'beholder', 'death kiss', 'eye of the deep', 'gauth', 'spectator', 'undead', 'hive mother', 'director', 'examiner', 'lensman', 'overseer', 'watcher', 'Monstrous Manual', ); my %pg = ( 'beholder' => 21, 'death kiss' => 21, 'eye of the deep' => 21, 'gauth' => 21, 'spectator' => 21, 'undead' => 21, 'hive mother' => 25, 'director' => 25, 'examiner' => 25, 'lensman' => 25, 'overseer' => 25, 'watcher' => 25, ); my $section = $gen[rand @gen]; my $page = $pg{$section}; print "$section $beholder (<i>$book</i>, pg $page) \n";

        The first 3 scalars are default values. If they are not declared differently in the array, then they don't change. If they are declared in the array, then they change to the new value.

        Also, all variables have to be as small as possible. I try to conserve space as much as possible, hence the one and two letter variable names.

        In the array, the name of the monster will be chosen randomly, but the other information such as which book it comes from and what page it is on must be declared, hopefully, within the array. If I have to do separate hashes(?) for each value, it will be way to big. Other random generators with similar variables have up to 10 different variables changed all on one line.

        Hopefully that made sense.

        There is a random generation program that I use for what I am trying to recreate in Perl called TableSmith. If you know of it, that will show you where I am coming from.

      I was so excited, I found out about exists so I was going to apply it here. Now I can't figure out how to use it, or even if it will work with this.

      #!/usr/bin/perl -w use strict; use warnings; use diagnostics; use CGI; use CGI::Carp qw(fatalsToBrowser); my %Data; #This sub will eventually go into a module. sub random { my ($name) = @_; my $array = $Data{$name}; my $pick = $array->[rand @$array]; if (ref($pick) eq 'CODE') {return $pick->();} else {return $pick;} } $Data{gen} = [ 'beholder' => {b => 1,pg => 21}, 'death kiss' => {pg => 21}, 'eye of the deep' => {pg => 21}, 'gauth' => {pg => 21}, 'spectator' => {pg => 21}, 'undead' => {pg => 21}, 'hive mother' => {pg => 25}, 'director' => {pg => 25}, 'examiner' => {pg => 25}, 'lensman' => {pg => 25}, 'overseer' => {pg => 25}, 'watcher' => {pg => 25}, ]; print random("gen") exists ($gen{$b}) ? '' : ' beholder') exists ($gen{$bk})||exists ($gen{$pg}) ? ( ' ('. exists ($gen{$bk}) ? ($gen{$bk}) : 'Monstrous Manual') exists ($gen{$pg}) ? ', page '.($gen{$pg})) .')' ) ) keys %gen(@_)

      First, the item from the hash needs to be randomly selected.

      Then if the variable b is in the key's hash, then print nothing. If the variable b is not in the hash, print ' beholder'.

      Then if either bk or pg is in the key's hash, then print ' (' and check the following.

      If bk is in the hash, print it's value. If bk is not in the hash, print 'Monstrous Manual'.

      If pg is in the hash, print ', page ' and it's value. If pg is not in the hash, print nothing.

      After those two are done, print a final ')'.

      If neither bk nor pg are in the key's hash, then print nothing after the key.

        I was so excited, I found out about exists so I was going to apply it here. Now I can't figure out how to use it, or even if it will work with this.

        Your problem is not with "exists()" per-se. At the surface, your problem involves punctuation that is incomplete or incorrect, along with insufficient use of parens, when trying to glom together all the steps of your algorithm into a single concatenated string for a "print". Slow down.

        Also, you are getting mixed up about how your data structure is organized. You initialize %Data{gen} with square brackets, which creates a reference to an anonymous array, and the "random" function is set up to get an array ref from this hash element -- that part of the code says "%Data is a HoA structure".

        But the elements of data being assigned to %Data{gen} are laid out as a hash, with the value of each element being another hash, which says "%Data is a HoHoH structure" (e.g.  $Data{gen}{beholder}{pg} == 21). If you want HoHoH, the initialization would use curlies instead of square brackets:

        $Data{gen} = { 'beholder' => {b => 1, pg => 21}, 'death kiss' => {pg => 21}, ... };
        And you would need a different kind of "random()" function in order to select one of those sub-keys ("beholder, "death kiss", etc) at random -- maybe something like:
        sub random { my ( $key ) = @_; my @choices = keys %{$Data{$key}}; my $chosen = $choices[rand @choices]; return $Data{$key}{$chosen}; # or maybe just return $chosen ? }
        So the first thing to do is decide what sort of data structure you really want, and stick with that.

        But let's look at the overall task in terms of the way you describe your algorithm:

        First, the item from the hash needs to be randomly selected.
        my $output = my $randomkey = random( "gen" );
        Then if the variable b is in the key's hash, then print nothing. If the variable b is not in the hash, print ' beholder'.
        $output .= " beholder" unless ( exists( $Data{gen}{$randomkey}{b} ));
        Then if either bk or pg is in the key's hash, then print ' (' and check the following.

        If bk is in the hash, print it's value. If bk is not in the hash, print 'Monstrous Manual'.

        If pg is in the hash, print ', page ' and it's value. If pg is not in the hash, print nothing.

        After those two are done, print a final ')'.

        If neither bk nor pg are in the key's hash, then print nothing after the key.

        That's a bit of tough going, and your sample data doesn't seem to exercise all the contingencies. Still, let's see if I can rephrase it without damaging the intent:

        If either "bk" or "pg" exists in the key's hash, add a parenthesized string containing a book name, with or without a page number; for a "pg" with no "bk", the default book name is "Monstrous Manual":

        my $addon = ''; if ( exists( $Data{gen}{$randomkey}{bk} ) or exists( $Data{gen}{$randomkey}{pg} )) { $addon = $Data{gen}{$randomkey}{bk} || 'Monstrous Manual'; $addon .= ", page $Data{gen}{$randomkey}{pg}" if ( exists( $Data{gen}{$randomkey}{pg} )); } $output .= " ($addon)" if ( $addon );
        The rules for use of brackets (square and/or curly) when working with data structures require careful attention to detail, and a clear understanding of what your particular data structure is. Data::Dumper can help with that, but you need to need to start with a structure that makes sense to you, and organizes the data nicely for the things you need to do with it. When in doubt, go back to perlreftut, perlref and perldsc (Data Structures Cookbook) -- I've done that quite often, and it helps.

        (This thread is getting rather long in the tooth (and just long)... if you continue hitting walls with this pursuit, you might consider focusing on some relatively self-contained issue that is causing trouble, isolate it as a stand-alone problem, and post it as a new thread.)

Re: Creating a random generator
by Lady_Aleena (Priest) on Oct 01, 2007 at 05:41 UTC

    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.

    Lady Aleena
    • Updates
      • 1 October 2007 (4:10 pm) - Corrected a lot of syntax errors in the VLA and End blocks.
      • 2 October 2007 (4:42 pm) - Added a sub in the Data section to make random selections a bit easier and updated all the affected code blocks. Also, as far as I know, the script is functioning.
      • 3 October 2007 (4:03 am) - Found out that Lingua::En::Inflect will do plurals too, so 3 and 4 may be taken care of when I get that installed.
      • 3 October 2007 (11:42 am) - 1 sub-script is complete. See the body of the notes section.
      • 4 October 2007 (1:12 pm) - All of the subs added up to this point. Cleaned up a few things too.
      • 4 October 2007 (4:19 pm) - To the recommendation on ikegami's scratchpad.
      • 5 October 2007 (3:11 am)
        • Added all subs to the lines that are dynamic and need better randomization.
        • The insanity script is done, it just needs tweaking.
      • 8 October 2007 (1:02 am)
      • 8 October 2007 (5:30 am) - added the rg_ins.pl file code. The main body of the rg_ins.pl script has been trimmed for space.
      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."

      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."

      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."

Re: Creating a random generator
by UnstoppableDrew (Sexton) on Oct 04, 2007 at 17:59 UTC
    From what you shown here, and your list of programming goals, you would be well served by looking into using Template::Toolkit. It's perfect for generating html output from perl scripts, and you can talk to databases from it, which would be good for your attributes pages. I haven't looked at all your projects, but from what I've seen so far I would use a mysql back end and DBI, Template::Toolkit to generate pages, and CGI.pm to handle the form submission. I do realize you're fairly new to perl and that's a daunting list, but it's not bad if you take it one piece at a time. I highly recommend the O'Reilly book 'Perl Template Toolkit'.

      I have looked into an gotten installed HTML::Template, which may do the same thing as Template::Toolkit. I still don't know how to make or use databases just yet, but I am going to get to it very soon, since I will more than likely need it before this is all over. I do not have MySQL and a lot of other things that everyone recommends on my server. I am already using use CGI in my scripts.

      I am learning Perl by using it to do those projects. It is the best way for me to learn, since the subject matter holds my interest more than print "Hello World!\n\n"; amongst other things. I build scripts to have them torn down by my betters and shown what I did poorly or wrong. I am glad that there are people here who understand that some of us can't learn from the abstract, we need to put into a familiar subject right away to learn.

      Thanks for dropping me a line.

      Lady Aleena

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

        So I had some free time, and I haven't written any code for fun in a while, so I decided to put my money where my mouth is. Here's the skeleton of a solution for your proficiency tables. I created a mysql database named 'player', and create one table called 'proficiencies':
        mysql> describe proficiencies; +-------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------+------+-----+---------+-------+ | proficiency | varchar(50) | NO | | | | | s | int(11) | YES | | NULL | | | ra | varchar(20) | NO | | | | | cm | int(11) | YES | | NULL | | | class | varchar(30) | YES | | NULL | | | source | varchar(30) | YES | | NULL | | +-------------+-------------+------+-----+---------+-------+
        I scraped your a-c table and turned it into a text file delimited by pipe chars (since there are commas in the data):
        |Acting|1|Cha|0|Bd, Hu| |Adaptation|2|Int|0|Wa 10+, R 11+|HL| |Administration|1|Int|1|Pr|SM|
        I wrote a script to stuff the data into the db:
        #!/usr/bin/perl use strict; use DBI; use DBD::mysql; my $dsn = "DBI:mysql:database=player;"; my $dbh = DBI->connect($dsn, "root", "secretpassword"); open IN, "<table.txt" or die "Error - failed to open file for reading: + $!\n"; while ( <IN> ) { chomp; my ($null,$prof,$s,$ra,$cm,$class,$source) = split /\|/; my $sql =<<SQLDONE ; INSERT INTO proficiencies VALUES ( '$prof', $s, '$ra', $cm, '$class', +'$source' ); SQLDONE print "$sql\n"; my $sth = $dbh->prepare($sql); $sth->execute; } print "finished loading table\n";
        That was probably not the most efficient way to do it, but my dbi book is at work and all this is off the top of my head.

        Script to read the database and dump data into template toolkit template:

        #!/usr/bin/perl use strict; use DBI; use DBD::mysql; use Template; my $tt = Template->new(); my $dsn = "DBI:mysql:database=player;"; my $dbh = DBI->connect($dsn, "root", "secretpassword"); my @items; my $sql = qq|SELECT * from proficiencies ORDER BY proficiency ASC;|; my $sth = $dbh->prepare($sql); $sth->execute; while ( my @row = $sth->fetchrow_array() ) { push @items, \@row; } my $vars = { items => \@items }; print "Content-type: text/html\n\n"; $tt->process("prof.tmpl", $vars) or die $tt->error();
        Template toolkit template to display the data:
        <html> <head><title>Proficiencies</title> <body> <table border="1" cellspacing="2" cellpadding="2"> <tr> <th>Proficiency</th> <th>S</th> <th>RA</th> <th>CM</th> <th>Class(es)</th> <th>Source(s)</th> </tr> [% FOREACH row IN items %] <tr> <td align="left">[% row.0 %]</td> <td align="center">[% row.1 %]</td> <td align="center">[% row.2 %]</td> <td align="center">[% row.3 %]</td> <td align="left">[% row.4 ? row.4 : "&nbsp;" %]</td> <td align="left">[% row.5 %]</td> </tr> [% END %] </table> </body> </html>
        Live script can be run by hitting this url: http://marold.org/cgi-bin/showtable.cgi
        Hmm...I just looked at HTML::Template. You'd be way better off learning Template Toolkit over that.
Re: Creating a random generator
by Lady_Aleena (Priest) on Sep 25, 2007 at 18:53 UTC
    Here is the code for the random generator to date, but first some notes.
    • Anywhere you see (AorAn) is where I hope the perl has a nice little way of looking at the first character of the following word and adding "a" or "an".
    • Here are links to the modules I am using
    • I am still confused about loops, I don't know how to write them yet.
    • I have commented out lines that I am still working on.
    • ## Shows where I am confused about something.
    • I left a lot of TableSmith code in there, because I don't know how to translate it yet.
    I hope that I did most of this right. Update 1: Some of the smaller syntax errors fixed. Update 2: Some of the arrays have been altered.

        Thank you for the fabulous breakdown of issues you see. Let me see if I understand them.

        The first item is that you suggested that I add -T to the shebang line. I was already told to add -w (after reading perlrun, I may possibly go to -W instead). Can both be added to the shebang?

        Do the following two items duplicate the same thing? If they do, I would choose to keep the first.

        use CGI; use CGI::Carp qw(fatalsToBrowser);
        print "content-type: text/html \n\n";

        I now only have the following in my code...

        my @Dice = qw(1d4 1d6 1d8 1d10 1d12 1d20);

        @Dice2 became redundant.

        You expressed concern for my variable names, with the example being @Radius and $radius. I have been using the capitalized version for the array in the original TableSmith version, and the lowercase version for the phrase fragment for a while now. I can tell the difference.

        All of the data in the script is static. As far as I know the arrays will only change if I think of something to add to them and write it into the code manually. The only thing that will change is the amount of times the user wants to run the script and the amount of times each random element will be called. The script as it is now is only a fragment of what is yet to come. I have a lot of markup to add to make this into a web page. I also have several other sub scripts to write that will be used by this one (but each of those must stand alone as well).

        The original way I wrote the array dealing with the colors was...

        my @Color = ( "<span class=\"mut\" style=\"color:#f00;\">red</span>", "<span class=\"mut\" style=\"color:#ff0;\">yellow</span>", "<span class=\"mut\" style=\"color:#0f0;\">green</span>", "<span class=\"mut\" style=\"color:#0ff;\">cyan</span>", "<span class=\"mut\" style=\"color:#00f;\">blue</span>", "<span class=\"mut\" style=\"color:#f0f;\">magenta</span>", "<span class=\"mut\" style=\"color:#fff;\">white</span>", "<span class=\"mut\" style=\"color:#000;\">black</span>" );
        I thought that this would save me a few bytes...
        my @Colors = ( "f00;\">red", "ff0;\">yellow", "0f0;\">green", "0ff;\">cyan", "00f;\">blue", "f0f;\">magenta", "fff;\">white", "000;\">black" ); my $Color = "<span class=\"mut\" style=\"color:#$Colors[rand @Colors]< +/span>";
        But you think a hash is better?
        my %Colors = ( red =>f00, yellow =>ff0, green =>0f0, cyan =>0ff, blue =>00f, magenta =>f0f, white =>fff, black =>000 );
        How would you make that randomly selected and inserted into the following?
        <span class="mut" style="color:(color code)">(color name)</span>
        As soon as I get the entire script working, I will be getting rid of all unnecessary whitespace from it. I just hope that this doesn't go above the amount of bytes I have alloted for it.
      First things first. You say

      Anywhere you see (AorAn) is where I hope the perl has a nice little way of looking at the first character of the following word and adding "a" or "an".

      If you don't expect too much, this substitution should do that. It changes th word "a" to "an" if the following word begins with a vowel character.

      s/\ba(?=\s+[AEIOUaeiou])/an/g
      It doesn't deal with "a unicorn" or "an hour" and other vagaries of pronunciation.

      As to your code, there's a lot of it and I haven't looked at every line by far. One thing that stands out is that there is little separation of code and data. You have a lot of global hashes and arrays, and later ones seem to rely on earlier ones in uncomfortable ways. Ultimately, these would belong in a single (or a few) configurable hashes, with the configuration data in an external file.

      Otherwise, your code has a massive amount of syntax errors, too many to correct on the fly. It will be nearly impossible to debug remotely, relying only on fatalsToBrowser. You must find a way to run your script locally from a shell so that you can eliminate syntax errors before commiting it to a web server (even a local web server), or indeed, for discussion among perl monks.

      Anno

      Anywhere you see (AorAn) is where I hope the perl has a nice little way of looking at the first character of the following word and adding "a" or "an".
      Lingua::EN::Inflect