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

Hello Monks!

I am brand new to Perl, as well as programming, and have been reading the book Beginning Perl by Curtis "Ovid" Poe. I browsed the internet for hours to no avail, so forgive me if this a repost. At the end of Chapter 5 the following excercise is asked:

"You’re writing a game and want to randomly generate a character’s statistics for strength, intelligence, and dexterity. Each statistic is determined by summing the values of two rolls of a six-sided die. For example, if you determine the character’s strength and roll the die twice and get the values 2 and 6, the characters strength is 8 (2 + 6). Write the code to generate a new char- acter. Remember that the code to simulate one roll of a six-sided die is 1 + int(rand(6)) (from Chapter 4). You use a heredoc (see Chapter 3) to print the character’s statistics."

He gives the following code with it :
my %stat_for = ( strength => undef, intelligence => undef, dexterity => undef, ); # add your code here print <<”END_CHARACTER”; Strength: $stat_for{strength} Intelligence: $stat_for{intelligence} Dexterity: $stat_for{dexterity} END_CHARACTER

And this is what I came up with:

#!/usr/bin/perl use strict; use warnings; use diagnostics; my %stat_for = ( strength => undef, intelligence => undef, dexterity => undef, ); while ( my ( $stat_name, $randvalue ) = each %stat_for ) { my $randvalue = ( 1 + int(rand(6)) ); my $randv2 = ( 1 + int(rand(6)) ); my $true = $randvalue + $randv2; print <<"END"; Strength: $true\n Intelligence: $true\n Dexterity: $true\n END }

My problem is that this prints 3 times, and I only want it to print once. So I'm looking for the correct way to write this code. Also, this chapter was about if/elsif, for/foreach, while/until, and given/when; And I'm not sure if I should be using them here. I know mine is off of what he laid out, and more than likely horrid. But, it's the best I could come up with, being all new to this. Any help is greatly appreciated.

Thanks in advance!

Replies are listed 'Best First'.
Re: New to Perl: Hashes and int(rand())
by davido (Cardinal) on Jun 14, 2014 at 06:42 UTC

    The reason it prints three times is that you moved the "print" statement into the loop's body, so on each loop iteration, it prints. Unfortunately, since you modified the HERE doc, you can't just move the print outside of the loop, because the $true variable won't be in scope. Even if it were in scope, it would print the same value for strength, intelligence, and dexterity, since it only contains one value.

    The easiest solution is to revert to the template that the book gave you (namely, the HERE doc should look like this:

    print <<"END"; Strength: $stat_for{strength}

    ...and so on). Once you've made that change, you can change this line:

    $true = $randvalue + $randv2;

    ...to this...

    $stat_for{$stat_name} = $randvalue + $randv2;

    Dave

Re: New to Perl: Hashes and int(rand())
by Athanasius (Archbishop) on Jun 14, 2014 at 06:48 UTC

    Hello Einzig, and welcome to the Monastery!

    You shouldn’t change the output code — that’s what you’re aiming for. Look again at the original:

    print <<"END_CHARACTER"; Strength: $stat_for{strength} Intelligence: $stat_for{intelligence} Dexterity: $stat_for{dexterity} END_CHARACTER

    This shows you that you need to populate (fill) the hash %stat_for with 3 values: 1 for each of the keys “strength”, “intelligence”, and “dexterity”.

    Read that again. ... 3 values: 1 for each of the keys .... That’s a clue! You need a foreach loop here, to apply the code in the loop to each of the keys in the hash.

    How to get the keys in the hash? Use Perl’s keys function. What to do with each key? Assign it a value found by summing two simulated rolls of the die. Note: the 3 values should be calculated independently (your current code calculates only 1 value and assigns this same value to all 3 statistics of the given character).

    One more point: The output code should come outside the loop, not inside it as you have it now. That way, the output will occur only once.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: New to Perl: Hashes and int(rand())
by AnomalousMonk (Archbishop) on Jun 14, 2014 at 10:14 UTC

    The next steps are to abstract the process of rolling a 6-sided die, and then to abstract the process of creating an n-sided die to roll. The latter step is a bit advanced, but just be aware it's possible. Also be aware that the built-in rand function may not be all that random.

    c:\@Work\Perl>perl -wMstrict -le "printf qq{%d }, die6() for 1 .. 10; print ''; ;; sub die6 { return 1 + int rand 6; } ;; ;; sub make_die { my ($sides) = @_; ;; return sub { return 1 + int rand $sides; }; } ;; my $die_20 = make_die(20); my $die_5 = make_die(5); ;; printf qq{20: %d; 5: %d \n}, $die_20->(), $die_5->() for 1 .. 6; " 1 5 4 4 6 5 4 1 1 1 20: 9; 5: 2 20: 13; 5: 1 20: 6; 5: 1 20: 9; 5: 3 20: 9; 5: 1 20: 5; 5: 1
      I also thought about making it in a sub, and it is certainly a good idea, but I figured out that the OP just completed chapter 5 of Ovid's book and subroutines are explained only in chapter 7.

        I'm not sure I'd wait so long to introduce such a basic concept, but I haven't looked at Ovid's book and so I'm not familiar with its basic structure. Also, there's the little problem that I've never written a book of any kind...

Re: New to Perl: Hashes and int(rand())
by Laurent_R (Canon) on Jun 14, 2014 at 08:09 UTC
    This is an (untested) example of the code you might want to insert into the program template provided by Ovid in order to populate the %stat_for hash:
    foreach my $key (keys %stat_for) { $stat_for{$key} = 2 + int (rand (6)) + int (rand (6)); }
    This will populate different random values for each of the three characteristrics (intelligence, strength, dexterity) of your character, because the $key variable will take successively the values of the three caracteristics.