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

This is my first perl program. It is a skiing program. I wrote it in basic decades ago. Thought it would be a fun way to get aquainted with perl and introduce my son to programing.

The first part randomly fills the screen with Ts that represent trees. Then it goes into a loop that puts a V on the screen that represents the skier.

This is where I am having a problem. When the j key is pressed I want the V to move to the left. When the k key is pressed I want the V to move to the right. Instead it breaks out of the loop and the text stops scrolling.

How can I fix this so the V moves and the scrolling continues. Code below.

#!/usr/bin/perl use Win32::Console::ANSI; use Term::ANSIScreen qw/:color :cursor :screen/; use Term::ReadKey; ReadMode('noecho'); cls; #fill the screen with Ts $y = 1; while ($y < 100) { $col = int(rand(80)); printf ("%${col}s\n", T); $y = $y + 1; } $wait = 1; $skierx = 40; $skiery = 0; $y = $y + 5; while (1 < 2) { while (not defined ($key = ReadKey(-1))) { # No key pressed $col = int(rand(79)); $y = $y + 1; locate $y,$col; print "T\n"; $skiery = $y - 50; locate $skiery,$skierx; print "V"; #locate $y,1; print "$skiery $skierx $key"; select(undef, undef, undef, $wait); #pauses here $wait = $wait - .003; } # here when key pressed if ($key == "j") {$skierx = $skierx - 1;} if ($key == "k") {$skierx = $skierx + 1;} if ($key == "x") {exit;} }

Replies are listed 'Best First'.
Re: newbie learning perl
by davido (Cardinal) on Feb 18, 2015 at 22:08 UTC

    Use a ReadMode of 'cbreak', not 'noecho'. The mode you've been using ('noecho') is buffered, which is not what you want. Also, be sure to set the ReadMode to 'restore' prior to terminating, or you'll end up with a wonky terminal! :)

    Finally, your $key relational tests need to use eq instead of ==, since it needs to be a stringwise comparison. With these changes it should work for you. ...at least the keyboard input part works. There's also a user-experience issue: The skier has no view of what's ahead, only what is behind or to the side. That would make for a terrible skiing experience! ;)

    See below:

    use Term::ANSIScreen qw/:color :cursor :screen/; use Term::ReadKey; ReadMode('cbreak'); cls; #fill the screen with Ts $y = 1; while ($y < 100) { $col = int(rand(80)); printf ("%${col}s\n", T); $y = $y + 1; } $wait = .5; $skierx = 40; $skiery = 0; $y = $y + 5; while (1 < 2) { while (not defined ($key = ReadKey(-1))) { # No key pressed $col = int(rand(79)); $y = $y + 1; locate $y,$col; print "T\n"; $skiery = $y - 50; locate $skiery,$skierx; print "V"; #locate $y,1; print "$skiery $skierx $key"; select(undef, undef, undef, $wait); #pauses here $wait = $wait - .003; } # here when key pressed if ($key eq "j") {$skierx = $skierx - 1;} if ($key eq "k") {$skierx = $skierx + 1;} if ($key eq "x") { exit;} } END{ ReadMode('restore'); # Don't leave the terminal wonky. print "\n"; # Don't leave the prompt in a weird place. }

    Also, on my Linux system the use of the Win32 related module was not necessary (and of course, not possible). I don't see anything in the script that actually makes use of that module. Are you sure it's necessary? (It may be; I don't do much with Windows these days, so I'm only speculating that it's not.)


    Dave

      Your changes worked! Blessed are you among monks. At work I use Linux but use c shell for all my scripting. Virtually all of my peers use perl so thought I should see what everybody likes so much. I wanted to introduce my 9 year old to programming and thought this would be a simple program that he could play with. Thus I'm creating it with windows since my son is in a windows world. When I wrote this in basic many moons ago I used peek to return a value that would tell me if the V had hit a location with a T thus a collision. Any thoughts on how I could accomplish this with perl? The goal of the game is to move the V and avoid hitting the Ts as the scrolling slowly speeds up. I need a way to tell if a location has a character in it before the V is put in the location. If no way to do this I'll need to create a list of x,y locations that have a character. Prefer a peek type solution if anybody has one.

        The skiers view: notice the V (skier) is placed 50 lines up the screen ($skiery = $y - 50;) while the Ts show up at the bottom and scroll upward so the skier can see whats coming with time to avoid. Until other obstacles show up like "YELLOW SNOW" which is a wall of characters to avoid or "ROCK" which is only 4 characters to avoid. This to come after I work out how to tell if the V hits an existing character. :P

        I think my 9 year old and his henchmen will be amused. (For about 5 minutes)

Re: newbie learning perl
by GrandFather (Saint) on Feb 19, 2015 at 02:27 UTC

    I've reworked your code somewhat. The most important change is to add strictures (use strict; use warnings; - see The strictures, according to Seuss), but I've also added the start of collision detection (which may have an off by 1 bug in it), allowed the number of display rows to be set and refactored the code somewhat (in part to show off some Perl stuff like join and arrays).

    This code is not tested because I can't quickly find a working ANSI Terminal emulation for Windows 8.

    #!/usr/bin/perl use strict; use warnings; use Term::ANSIScreen qw/:color :cursor :screen/; use Term::ReadKey; my $rows = 50; ReadMode('noecho'); cls; setscroll (1, $rows); locate 1, 1; my @screenRows; # Create the initial screen push @screenRows, newRow() for 1 .. $rows; print join "\n", @screenRows, ''; my $wait = 1; my $skierx = 40; while (1) { if (defined(my $key = ReadKey(-1))) { if ($key eq "j") { --$skierx; } elsif ($key eq "k") { ++$skierx; } elsif ($key eq "x") { exit; } } # Generate and show a new line shift @screenRows; push @screenRows, newRow(); print $screenRows[-1]; # Collision detection if (' ' ne substr $screenRows[$rows / 2], $skierx - 1, 1) { locate $rows, 1; print "Crash!\n"; exit; } # Show skier's updated position locate $rows / 2, $skierx; print "V"; # wait select(undef, undef, undef, $wait); #pauses here $wait -= 0.003; } sub newRow { my $col = int(rand(80)); return ' ' x $col . 'T' . (' ' x (78 - $col)); }
    Perl is the programming world's equivalent of English

      Ouch! So much I dont understand. What is the difference between use strict; & use warnings; and the default settings? Is there an advantage to using my when assigning a value?

        If you don't use "strict" and "my" Perl makes any variables you use "global" (it's a little more subtle that that, but the details aren't important for now). As a general thing global variables are bad news because it's hard to tell where their value might change. "my" declares a variable in the local scope and strict enforces that. strict picks up errors that can be detected at compile time before the program runs.

        warnings tell you about dodgy stuff that happens as the program runs, like variables that are used before they have been given a value or using == where eq should be used.

        In Perl scope is important. It controls where a lexical variable (one declared with my) can be used. For example:

        use strict; use warnings; $undeclaredVar = 0; # strict gets grumpy about this because the variab +le isn't declared my $globalVar = 0; # This variable can be used by any following part o +f the code for my $loopVar (1 .. 10) { # In the scope of the for loop. $loopVar can only be used within t +his loop # Loop variables are somewhat magical my $localVar = $loopVar * 3; # This variable is also local to the +loop. It # is a different variable each time through the loop! my $globalVar = $localVar; # Bad! This $globalVar hides the $glo +balVar # declared outside the loop. Reusing variable names like this +is very # bad style! if ($localVar == 9) { my $nestedVar = rand(10); # This variable is only available in +side this # if block. It's not available in the else part or after t +he if } else { my $nestedVar = rand($localVar); # Reusing the name here is ok +, but note # that this is a different variable than the $nestedVar in + the if # block. Note that we can use variables from the encompass +ing # scope in a nested scope. } my @trailingVar; # This array variable isn't available to any earl +ier part # of the program. In general declare variables in as small a s +cope as # you can and initialize them to the first value they should h +ave. # Arrays and hashes are created empty so they don't generally +need an # explicit initial value. } my @anotherGlobal = ('Hello', 'World'); # This global array variable i +sn't # available to the loop code above because it's declared after the + loop. # Note that we can initialize an array variable using a list
        Perl is the programming world's equivalent of English