Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Lady Aleena's first working module

by Lady_Aleena (Priest)
on Oct 05, 2009 at 05:29 UTC ( [id://799163]=perlmeditation: print w/replies, xml ) Need Help??

Over the years I have been here, many of you have probably despaired that I would ever get anywhere. I was so afraid of making modules because they looked so complicated, and I complained bitterly about them. I finally made one however, and I wonder why I was so timid about them in the first place. This module is rather simplistic and only usable by a very very small fraction (maybe .01%) of the Perl programmers out there that I have not decided whether or not I will ever upload it to CPAN.

This module started when I finally decided to read and learn how to use both map and grep about 2 weeks ago. It was a long time in coming. I may not be too much farther along, but any progress is better than none, I hope. maping and grepping will save me a lot of time and energy in the future. I am happy with the results of this little module. Sorry it is not tidier.

package Games::Random::Alignment; use strict; use warnings; use diagnostics; use base 'Exporter'; our @EXPORT_OK = qw(random_alignment); =head1 Alignment This module generates random alignments for AD&D 2nd Edition. =head2 Authors Lady Aleena with lots of help from DrForr, whoppix, and rindolf in the + #perlcafe on freenode. =head2 Use To use this module, please enter the following. use Games::Random::Alignment qw(random_alignment); When you want to generate a random alignment with this module, you can + choose from any of the alignment axes. random_alignment("parts") will generate lawful, chaot +ic, good, evil, or neutral. random_alignment("good_vs_evil") will generate good, neutral +, or evil. random_alignment("lawful_vs_chaotic") will generate lawful, neutr +al, or chaotic. random_alignment("evil") will generate lawful evil, +neutral evil, or chaotic evil. random_alignment("good") will generate lawful good, +neutral good, or chaotic good. random_alignment("chaotic") will generate chaotic good, + chaotic neutral, or chaotic evil. random_alignment("lawful") will generate lawful good, +lawful neutral, or lawful evil. random_alignment("neutral_lc") will generate lawful neutra +l, true neutral, or chaotic neutral. random_alignment("neutral_ge") will generate neutral good, + true neutral, or neutral evil. random_alignment("any") will generate any two part +alignment. =cut my @parts = qw(lawful chaotic good evil neutral); my @good_vs_evil = qw(good neutral evil); my @lawful_vs_chaotic = qw(lawful neutral chaotic); my @evil = map($_." evil",@lawful_vs_chaotic); my @good = map($_." good",@lawful_vs_chaotic); my @chaotic = map("chaotic ".$_,@good_vs_evil); my @lawful = map("lawful ".$_,@good_vs_evil); my @neutral_lc = (map($_." neutral",grep {$_ ne "neutral"} @lawful_vs_ +chaotic), "true neutral"); my @neutral_ge = (map("neutral ".$_,grep {$_ ne "neutral"} @good_vs_ev +il), "true neutral"); sub full { my $prefix = $lawful_vs_chaotic[rand @lawful_vs_chaotic]; my $suffix = $good_vs_evil[rand @good_vs_evil]; if ($prefix eq $suffix) { return "true neutral"; } else { return $prefix." ".$suffix; } } sub random_alignment { my $type = shift; if ($type eq 'parts') { return $parts[rand @parts]; } elsif ($type eq 'good_vs_evil') { return $good_vs_evil[rand @good_vs_evil]; } elsif ($type eq 'lawful_vs_chaotic') { return $lawful_vs_chaotic[rand @lawful_vs_chaotic]; } elsif ($type eq 'evil') { return $evil[rand @evil]; } elsif ($type eq 'good') { return $good[rand @good]; } elsif ($type eq 'chaotic') { return $chaotic[rand @chaotic]; } elsif ($type eq 'lawful') { return $lawful[rand @lawful]; } elsif ($type eq 'neutral_lc') { return $neutral_lc[rand @neutral_lc]; } elsif ($type eq 'neutral_ge') { return $neutral_ge[rand @neutral_ge]; } else { return full; } } 1;

My second module is similar to the first, randomly generating more stuff. :)

package Games::Random::Effect; use warnings; use strict; use diagnostics; use base 'Exporter'; our @EXPORT_OK = qw(random_effect); =head1 Effect This module generates random effects for AD&D 2nd Edition. =head2 Author Lady Aleena =head2 Use To use this module, please enter the following. use Games::Random::Effect qw(random_effect); When you want to generate a random effect with this module, you can ch +oose from the following. random_effect("general") will generate any of the effects listed b +elow. random_effect("gaze") will generate paralysis, stone, stun, or +death. random_effect("range") will generate acid, cold, electricity, fi +re, gas, or sonic. random_effect("touch") will generate acid, cold, electricity, fi +re, poison, or energy drain. random_effect("vocal") will generate deafen, fear, terror, or fl +ight. =cut my @part = qw(acid cold electricity fire); my @gaze = qw(paralysis stone stun death); my @range_part = qw(gas sonic); my @range = (@part, @range_part); my @touch_part = qw(poison energy_drain); my @touch = (@part, @touch_part); my @touch_special = qw(befouls purifies nullifies_holy_water nullifies +_unholy_water); my @vocal = qw(deafen fear terror flight); my @general = (@part, @gaze, @range_part, @touch_part, @vocal); my $result; sub random_effect { my $type = shift; if ($type eq 'general') { $result = $general[rand @general]; } elsif ($type eq 'gaze') { $result = $gaze[rand @gaze]; } elsif ($type eq 'range') { $result = $range[rand @range]; } elsif ($type eq 'touch') { $result = $touch[rand @touch]; } elsif ($type eq 'vocal') { $result = $vocal[rand @vocal]; } else { $result = $general[rand @general]; } $result =~ tr/_/ /; return $result; } 1;

I have a third in the works right now, but I overcomplicated it, I think.

Have a nice day!
Lady Aleena

Replies are listed 'Best First'.
Re: Lady Aleena's first working module
by CountZero (Bishop) on Oct 05, 2009 at 06:18 UTC
    Lady Aleena,

    Welcome to the world of module writers! You have made a big step, it is almost as a rite of passage.

    An important part of module writing is choosing a good name for your modules. As you are likely to write many more modules, the naming should both be consistent and clearly convey the use of the module.

    Perhaps another name for your modules could be Games::ADD::Alignment::Random and Games::ADD::Effect::Random.

    And later you could add other modules, all under the Games::ADD::... namespace, such as Games::ADD::Utilities which can contain various subroutines (or methods) useful in all your other modules, such as a general subroutine which does the $attribute[rand @attribute] choosing.

    If you want to avoid the if ... then ... elsif ... elsif ... else ... you can think of another data-structure where you use a hash whose keys are the attribute names and whose values are an array-reference to the list of possible attribute values. It would then become trivially easy to expand the list of attributes, just by adding to the data-structure, without having to edit the if ... then ... elsif ... elsif ... else ... code. One could even consider putting the attribute values into a configuration file, so your code doesn't even have to change one bit when you change the attributes.

    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

Re: Lady Aleena's first working module
by ELISHEVA (Prior) on Oct 05, 2009 at 07:39 UTC

    You've come a long way, baby!

    Egads! White space. Lots of it! strict, warnings, polite use of Exporter (with @EXPORT_OK), consistent use of indenting, documentation. All good!

    Count Zero has made great suggestions. Here's one more. There are some standard conventions about how sections of POD are organized. Here's how I would revise your pod to use more standard headings and organization. However, there are many styles out there and I encourage you to check CPAN for further examples, as well.

    =pod =head1 NAME Games::Random::Alignment - generates random alignments for AD&D 2nd Ed +ition. =head1 USAGE use Games::Random::Alignmnet qw(random_alignment); # generate lawful, chaotic, good, evil or neutral random_alignment("parts"); # generate good, neutral or evil random_alignment("good_vs_evil"); # generate lawful, neutral, or chaotic random_alignment("lawful_vs_chaotic"); # generate lawful evil, neutral evil, or chaotic evil. random_alignment("evil"); # generate lawful good, neutral good, or chaotic good. random_alignment("good"); # generate chaotic good, chaotic neutral, or chaotic evil. random_alignment("chaotic"); # generate lawful good, lawful neutral, or lawful evil. random_alignment("lawful"); # generate lawful neutral, true neutral, or chaotic neutral. random_alignment("neutral_lc"); # generate neutral good, true neutral, or neutral evil. random_alignment("neutral_ge"); # generate any two part alignment. random_alignment("any"); =head1 DESCRIPTION This module generates random alignments for AD&D 2nd Edition. When yo +u want to generate a random alignment with this module, you can choos +e from any of the alignment axes. For examples, see the L</USAGE> se +ction above. =head1 AUTHORS Lady Aleena with lots of help from DrForr, whoppix, and rindolf in the + #perlcafe on freenode. =cut

    As you have already probably figured out indenting pod causes it to be formatted as code, so all of the usage section will look like code samples. L</Some heading name here> lets you create a link to any other heading (a name introduced by =head1, =head2, etc) in your pod. See perlpod for all the gory details.

    Best, beth

Re: Lady Aleena's first working module
by toolic (Bishop) on Oct 05, 2009 at 13:26 UTC
    I prefer interpolation over concatenation where possible because it seems less noisy to me. For example, I would change:
    my @evil = map($_." evil",@lawful_vs_chaotic); return $prefix." ".$suffix;

    to:

    my @evil = map("$_ evil",@lawful_vs_chaotic); return "$prefix $suffix";
    I have not decided whether or not I will ever upload it to CPAN
    Your decision should be based on whether your code provides something that is not currently available on CPAN, according to results of a diligent search on your behalf. Does your code provide more functionality? Or, does it do so more efficiently (speed, memory)? Is it more robust or portable than something comparable? Is it more user-friendly (simpler to use, easier to download/install)?

    If the answer to any of these questions is 'yes', then I would say it is a candidate for CPAN. In that case, your next step is to create a test suite. And to finish things off, I second ELISHEVA's advice regarding more standard POD sections.

      toolic,

      The only reason I go with concatenation is that it is easier to see in my text editor which color codes everything. Everything between "" and '' is in gray. To find scalars and other code faster while debugging, it is just simpler to concatenate. Every once in a while I will use qq{} for really long strings with some scalars and code in it, but that is rare. I am not too partial of the color my text editor has for hashes. I really should change that. :)

      I haven't really looked on CPAN for these, but I doubt that there are any modules that have this level of specificity. They are not too functional outside of their specific topics. I haven't checked speed, but they are small, so it stands to reason that they are fast. Well, at least my reasoning anyway. I am not sure how robust they are, but I tried to cover all the bases under the specific topic. They are pretty portable, I think, as I did not make them specific to one platform such as loading them down with HTML. I will have to look into what a test suite is.

      Thank you for searching CPAN on my behalf, I appreciate it. One day I may make a presence for myself there, but I am not quite that good yet. :)

      Have a nice day!
      Lady Aleena

        It is isn't a question of "that good" but rather "good enough" and "useful". If there are other people that want to build AD&D games using Perl, then they might find your module useful.

        Good enough means using good programming practices (you are), a not-too-unconventional documentation style, and, of course, a test suite that passes. See Test::Simple and Test::More for more information on how you can use Perl to test your software. Testing sounds intimidating but it is really quite easy.

        The only other question is do I have the time to update it as I add new features and to maintain it if someone reports problems? A module is more likely to be used if it looks like it is being well taken care of.

        Best, beth

Re: Lady Aleena's first working module
by Herkum (Parson) on Oct 05, 2009 at 21:29 UTC

    A simplification of your code. You are repeating the same action using different data. So rather than have lots of code lets focus on a data structure that uses the same code.

    my %effects = ( part => [qw(acid cold electricity fire)], gaze => [qw(paralysis stone stun death)], range_part => [qw(gas sonic)], touch_part => ['poison', 'energy drain'], touch_special => ['befouls', 'purifies', 'nullifies holy water', 'nu +llifies unholy water'], vocal => [qw(deafen fear terror flight)], ); $effects{'range'} = [@{$effects{'part'}}, @{$effects{'range_part'}}] +; $effects{'touch'} = [@{$effects{'part'}}, @{$effects{'touch_part'}}] +; $effects{'general'} = [@{$effects{'part'}}, @{$effects{'touch_part'}}, @{$effects{'gaze'}}, @{$effects{'range_part'}}, @{$effects{'vocal'}}]; sub random_effect { my $type = shift; return 'unknown' if not exists $effects{ $type }; return $effects{ $type }[ rand @{$effects{ $type }} ]; } print random_effect('misspelled') . "\n"; print random_effect( 'touch' ) . "\n"; print random_effect( 'touch' ) . "\n"; print random_effect( 'general' ) . "\n"; print random_effect( 'general' ) . "\n";

    Now if you want to add a effect type, you only need to add an entry into %effects. You could probably do something similar for the alignments.

      Herkum,

      Thanks for simplifying my code. While I didn't do exactly what you did above, I did use the hash.

      For Alignment.pm...

      For Effect.pm...

      If there are any further improvements you can think of, please let me know. Thanks again!

      Have a nice day!
      Lady Aleena
Re: Lady Aleena's first working module
by Bloodnok (Vicar) on Oct 05, 2009 at 10:58 UTC
    Congratulations - as the parents curse goes: May they grow up to be a burden to you:-D

    However, it appears that there's sufficient in common between the 2 modules to warrant a single abstraction (or base class) utilising 2 config files (or sub-classes) ... or maybe even both as an extended learning exercise ?

    Just a thought from 1st impressions ...

    A user level that continues to overstate my experience :-))
      Mr. Bloodnok,

      What exactly is the assignment? :)

      Have a nice day!
      Lady Aleena
Re: Lady Aleena's first working module
by Zen (Deacon) on Oct 05, 2009 at 21:09 UTC
    " use Games::Random::Alignmnet qw(random_alignment);" Spelling error here ---^ Cheers.
      Zen,

      Spelling error fixed. Thanks for catching that!

      Have a nice day!
      Lady Aleena
Re: Lady Aleena's first working module
by Tux (Canon) on Oct 07, 2009 at 07:39 UTC

    You're using one of my pet-peeve style errors:

    sub random_alignment { my $type = shift; if ($type eq 'parts') { return $parts[rand @parts]; } elsif ($type eq 'good_vs_evil') { return $good_vs_evil[rand @good_vs_evil]; } elsif ($type eq 'lawful_vs_chaotic') { return $lawful_vs_chaotic[rand @lawful_vs_chaotic]; }

    After a return there can never be an else. the sub returns, so this much better reads like this:

    sub random_alignment { my $type = shift; if ($type eq 'parts') { return $parts[rand @parts]; } if ($type eq 'good_vs_evil') { return $good_vs_evil[rand @good_vs_evil]; } if ($type eq 'lawful_vs_chaotic') { return $lawful_vs_chaotic[rand @lawful_vs_chaotic]; }

    And personally, I'd go even further:

    sub random_alignment { my $type = shift; $type eq "parts" and return $parts[rand @parts]; $type eq "good_vs_evil" and return $good_vs_evil[rand @good_vs_ +evil]; $type eq "lawful_vs_chaotic" and return $lawful_vs_chaotic[rand @law +ful_vs_chaotic];

    Clean, short code. Beautiful, right?


    Enjoy, Have FUN! H.Merijn

      Now you are doing one of my pet peeves, using to much syntax which is not clarifying the code at all. This is better,

      my $type = shift; return $type eq 'parts' ? $parts[ rand @parts] : $type eq 'good_vs_evil' ? $good_vs_evil[ rand @good_v +s_eval] : $type eq 'lawful_vs_chaotic' ? $lawful_vs_chaotic[ rand @lawful +_vs_chaotic] ? q{}

        That kind of code will only work of there is just one return, and "" (or the hideous q{} from PBP) is the only possible alternative.
        My example just simplifies the pre-emptive returns, and lives in the assumption that the default (after all the returns were done) still needs some real coding.

        Personally, I like brevity, and both my code and yours are terse. I do not however think that your code is any clearer than mine. Both are clearer than the code of the OP though.


        Enjoy, Have FUN! H.Merijn
      Tux,

      With what Herkum suggested in Re: Lady Aleena's first working module, using a hash instead of a lot of arrays, I was able to cut down on the redundant code a bit. You can see my changes in Re^2: Lady Aleena's first working module. I am a bit confused as to why you would put the closing bracket of the if on the next level down from it. I am of the mind that the closing bracket should be on the same level as where it was opened. I am also not a big fan of condensing the if, but that is because I don't want it to get lost. That is the same reason I concatenate over interpolate.

      Have a nice day!
      Lady Aleena

        In my humble but very honest opinion, indenting the closing brace to that level is the only logical thing to do. I have tried to explain that (and some other style issues) in here. I know many won't agree, but you have to admit I at least gave it a lot of thought and I'm not one of those people that just blindly follow what their favourite editor thinks to be the best style today.


        Enjoy, Have FUN! H.Merijn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://799163]
Approved by ELISHEVA
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-03-19 02:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found