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

Hello, and thank you. I have a question about some problems that my perl code was having. First, some background. I had to make some quick code to calculate the score of a FIRST Robotic competition match(using last year's rules) and then create a program that kept track of the average scores of all the teams, along with several other variables -- I thought perl would be perfect for this, and so I started coding. I ran across several problems, and that is why my code looks sloppy. . . I aplogize. But, the problem is with the output -- quite simply, it's incorrect. Here's the code for what it's worth:

(P.S.: above where it says "EXPERIMENTAL" all works correctly; that is merely the rules for scoring, and is non-important for this question, so far as I can tell)

# score.pl $RANKINGS = "rank.txt"; get_info(); process_info(); calc_scr(); #disp_scr(); rank(); sub get_info { print "---Enter the following data---\n", "\tNumber of black balls in goal:\t"; $blk_bls = <stdin>; print "\tNumber of big balls in goal:\t"; $big_bls = <stdin>; print "\tNumber of robots in endzone:\t"; $rbt_end = <stdin>; print "\tNumber of goals on the bridge:\t"; $gls_brg = <stdin>; print "\tNumber of seconds remaining:\t"; $tim_rmn = <stdin>; print "\t\tIs this a qualifying match(1 or 0):\t"; if(<stdin> == 1) { for(1..4) { print "\tDoes team $_ have its large ball in goal(1 or 0):\t"; $ball[$_] = <stdin>; } } for(1..4) { print "\tNumber of rule violations for team $_:\t"; $rl_vlt[$_] = <stdin>; } } sub process_info { $mult = $gls_brg + 1 + ($gls_brg == 2); if(tim_rmn <= 120 && tim_rmn >= 91) { $mult *= 3 } elsif(tim_rmn <= 90 && tim_rmn >= 61) { $mult *= 2.5 } elsif(tim_rmn <= 60 && tim_rmn >= 31) { $mult *= 2 } elsif(tim_rmn <= 30 && tim_rmn >= 15) { $mult *= 1.5 } else { $mult *= 1 } } sub calc_scr { $all_scr = ($blk_bls + $rbt_end * 10 + $big_bls * 10) * $mult; for(1..4) { $tm_scr[$_] = ($all_scr * (1 + $ball[$_] * .1)) * ((10 - $rl_vlt[$_]) * .1); } } sub disp_scr { for(1..4) { print "Team $_ score is: $tm_scr[$_]\n" } } ## ...EXPERIMENTAL... sub get_team_info { for(1..4) { print "\tEnter number of team $_:\t"; $tm_nmb[$_] = <stdin>; if($fst_tm[$_] = first_time($tm_nmb[$_])) { print "\tEnter names of team members, separated by space\n", "\t(e.g., mrawls srawls):\t"; $mbr_nms[$_] = <stdin>; } } } sub first_time { open(RANKINGS) or die "Failed to open $RANKINGS, at"; if(<RANKINGS> =~ /New File/) { $new_file = 1; return 1; } @entries = split /^<>$/, <RANKINGS>; @numbers = map { ($a,$b) = split /:/,$_,2; $a } @entries; for $n(@numbers) { if($n == $_[0]) { return } } return 1; } sub rank { get_team_info(); open(RANKINGS) or die "Failed to open $RANKINGS, at"; @entries = split /^<>$/, <RANKINGS>; for $i(1..4) { if(!$fst_tm[$i]) { $j = -1; for(@numbers) { $j++; $_ == $tm_nmb[$i] && break; } ($tn,$as,$hs,$mn,$nm) = split /:/, $entries[$j]; $as = ($as + $tm_scr[$i]) / 2; $tm_av[$i] = $as; if($tm_scr[$i] > $hs) { $hs = $tm_scr[$i] } ++$nm; $tm_rank[$i] = "\n$tn:$as:$hs:$mn:$nm\n"; } else { $tm_nmb[$i] =~ s/\n//; $mbr_nms[$i] =~ s/\n//; $tm_rank[$i] = "\n$tm_nmb[$i]:$tm_scr[$i]:$tm_scr[$i]:$mbr_nms[$i]:1 +\n" } } if(!$new_file) { @averages = map { ($a,$b,$c) = split /:/, $_, 3; $b } @entries; for $i(1..4) { for(0..$#averages) { if($averages[$_] < $tm_avg[$i]) { @part_1 = (@entries)[0..$_-1]; @part_2 = ($tm_avg[$i],(@entries)[$_..$#entries]); @sorted = (@part_1,@part_2); open(RANKINGS,">$RANKINGS"); print RANKINGS join '<>', @sorted; } } } } else { open(RANKINGS,">$RANKINGS"); @sorted = sort { (split /:/, $b)[1] <=> (split /:/, $a)[1] } @tm +_rank; pop @sorted; print RANKINGS join '<>', @sorted; } }

Replies are listed 'Best First'.
Re: Ranking/Score keeping
by jlongino (Parson) on Oct 19, 2001 at 06:37 UTC
    I've noticed that no one has replied to your post. Here are some things that you could do that would make it easier for someone to help without us having to spend several hours:

    • You didn't  use strict;  so I suspect that you didn't run the program using  perl -w  either. This might generate some meaningful messages from what you think is "correct" code. Using strict will force you to scope your variables with my, our, or local.
    • Your code relies on giving a good deal of input which most people are unfamiliar with. You might consider eliminating the STDIN portions and just use pre-initialized variables (scalars, arrays, hashes).
    • We don't have any idea what the calculations should be, so we can't tell if the precedence of your operators are correct or not. You can remedy this by providing us with input (as explained above) and then showing us what the correct results would be so that we can compare it to what we get when your code is run.

    You might want to modify your code and then reply to your original post. It may be that in following the advice given above that you might solve the problem yourself. Either way it would cut the time needed for any of us to attempt a solution. Good luck,

    Jim

    "If a man does his best, what else is there?" -- General George S. Patton

      Thank you for your comment. I ran the code with 'use strict' and '-w' (including a 'use vars' statement at the top) and it ran and compiled and whatnot successfully. I probably should have clarified, but some problems that I noticed witht the output was this:

      (1)The first output works correctly, except a blank line is added at the top (could it make an error in my logic?)
      (2)THe second(and third and etc.) time it is not correct. Namely, when I enter the data and give the team number, if I have given the team numbers as 1 2 3 and 4, and I enter 1, it does not prompt me for the names(this is correct, because I would already have them) BUT for 2 3 and 4 it incorrectly prompts me for their names; also, the output is erroneous. Namely the last part of it (the number of matches) stays at 1, i.e., does not increase to 2 (3, 4, etc.) as it should. Also it doesn't average the score, or remember the names.

      Sample input for the program would be this, if it helps: 11 . 2 . 3 . 2 . 92 . 0 . 0 . 0 . 0 . 0 . 1 . name1 name2 . 2 . name3 name4 . 3 . name5 name6 . 4 . name7 name8 . <END OF INPUT>

      The score would be 732 (as it correctly displays); but if you enter the SAME data again, it works incorrectly (and if you change the 11, let us say, to a 10, you can see that the scores don't average either).

      As for the "calculations" I don't think you need to know them, becuase (I may not have made it clear) but they DO work. . . it is the actual ranking that does not. I have tested them (in the original C++ they were written in, albeit) and they work fine.

      If any more info is needed, just ask.

        The program died when attempting to open the ranks.txt file (line 69). You should modify your code so that if the file doesn't exist (hence it is a /New File/) you create and initialize it accordingly.

        Once I've entered the required input the program ends and does not print any output to the screen. It writes to the rank.txt file:

        --------------------------------------- 1:244:244:andy ben:1 <> 2:244:244:cathy doug:1 <> 3:244:244:elaine fred:1 <> 4:244:244:gail harry:1 ---------------------------------------
        I'm not sure where you came up with 732, it is not displayed anywhere that I can see. Just because something was coded in C++ doesn't mean that it will work correctly after you convert it to Perl. If your plan is to read in each team's data and then do calculations and write the info back out to the file, you should use logic along these lines:
        if rank.txt exists { if rank.txt is not empty { read each team's info and store in an array/hash } } if user wants to add new team data { loop # once through loop gets only data for one team get* team number get* member names get* team scores/whatever append record to rank.txt endloop # * input/store to array/hash } perform calculations for array/hash print results
        More observations:
        • A common mistake for programmers is to type in a complete program and then try to debug it. Your code should be developed in small chunks. For example, don't attempt to code subs to perform calculations when you haven't even debugged/verified your input routines yet. Your chunk size may vary depending on the complexity of a given code segment.
        • Use print liberally when verifying code correctness. You can always comment them out later.
        • Start by commenting out your all of your subroutine calls. As you successfully debug one chunk, uncomment the next.
        --Jim