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

I'm making a Dungeons & Dragons die-rolling program in which the user is asked for input over and over until Ctrl-C. The source code is here:

#!/usr/bin/perl print "Input the dice and modifier you want to roll like this: 2d9+100 +. To quit, press Ctrl-C.\n"; while (1) { my $dice = <>; my @arr = split (/d/, $dice); my @arr2 = split (/\+/, $arr[1]); for my $i (1 .. $arr[0]){ print ((int(rand($arr2[0] - 1)) + 1 + $arr2[1]), "\n");}}

I want to add input history to this program; that is, when a user presses the up-arrow, the program provides the last input, just like in interactive ksh/csh/bash. How would I go about this? $thanks++, zsl

Replies are listed 'Best First'.
Re: How to add input history
by PerlSufi (Friar) on Apr 06, 2015 at 17:31 UTC
Re: How to add input history
by stevieb (Canon) on Apr 06, 2015 at 17:45 UTC

    Here's a way without the external module. It's very basic, but could give you some guidance to make it more complex. Uses a dispatch table to run commands based on what the user enters, and will run the last command typed if user hits the up arrow.

    Essentially, all you have to do is keep history in an array outside of your main game loop, then push to it every round. When the up arrow is seen, you need to pull the last entry from the history array, and use it.

    #!/usr/bin/perl use strict; use warnings; my $up_arrow = "\e[A"; # up arrow my @history = []; my %dispatch = ( 'fight' => \&fight, 'run' => \&run, ); while(1){ print "Enter command...\n"; my $in = <STDIN>; chomp $in; if ($in eq $up_arrow){ print "$history[-1]"; push @history, $history[-1]; $dispatch{$history[-1]}->(); } else { push @history, $in; $dispatch{$in}->(); } } sub fight(){ print "Everybody was kung-fu fighting!\n"; } sub run(){ print "I'm running the hell away!\n"; }

    Input/output:

    steve02@steve02-ws:~$ ./arrow.pl Enter command... run I'm running the hell away! Enter command... ^[[A runI'm running the hell away! Enter command... fight Everybody was kung-fu fighting! Enter command... ^[[A fightEverybody was kung-fu fighting! Enter command...

    EDIT: I often like to make code examples a little more complex than necessary to give requesters things to think about, but in this case, I may have gone a bit over the top. This is likely because now coding in Python, I like to exercise my Perl when I can :)

    Here's a more basic example of how one could do what OP asked. Note that if *only* the most recent history is ever needed, it would be advisable to shift off of the history array each iteration as to not eat up all memory if the plan is to do many, many iterations.

    The following code asks the user for a number, and prints the result of that number multiplied by 10. Hitting the up arrow will perform the same calculation with the same number as last round.

    #!/usr/bin/perl use warnings; # always use this use strict; # always use this my $up_arrow = "\e[A"; # up arrow control character my @history = []; my $number = 10; while(1){ print "Enter a number...\n"; my $input = <STDIN>; chomp $input; if ($input eq $up_arrow){ $input = $history[-1]; } push @history, $input; my $result = $number * $input; print "\nResult of $input x $number is: $result\n"; }

    Cheers,

    -stevieb

      Thanks, stevieb! I've updated my program to account for warnings raised when input lacks a modifier, but there's a problem. Am I using your technique right?

      #!/usr/bin/perl use warnings; use strict; my $up = "\e[A"; my @history = []; print "Input the dice and modifier you want to roll like this: 2d9+100 +. To quit, press Ctrl-C.\n"; while (1) { my $dice = <>; chomp $dice; my @arr = split (/d/, $dice); if ($dice =~ /\+/) { my @arr2 = split (/\+/, $arr[1]); $arr2[0]++; for my $i (1 .. $arr[0]) { print ((int(rand($arr2[0] - 1)) + 1 + $arr2[1]), "\n"); } } else { $arr[1]++; for my $i (1 .. $arr[0]) { print ((int(rand($arr[1] - 1)) + 1), "\n"); } } push @history, $dice; }

      Whenever I use this code, I get this problem:

      zsl@pc:~/codefolder $ perl dieroller.pl Input the dice and modifier you want to roll like this: 2d9+100. To qu +it, press Ctrl-C. 2d9+100 101 101 ^[[A Argument "^[[A" isn't numeric in foreach loop entry at DDDR.pl line 19 +, <> line 2. ^C zsl@pc:~/codefolder $
      $thanks++, zsl

        Wait, oops. I forgot to add an extra if clause. The new code looks like:

        #!/usr/bin/perl use warnings; use strict; my $up = "\e[A"; my @history = []; my $number = 10; print "Input the dice and modifier you want to roll like this: 2d9+100 +. To quit, press Ctrl-C.\n"; while (1) { my $dice = <>; chomp $dice; my @arr = split (/d/, $dice); if ($dice eq $up) { $dice = $history[-1]; } elsif ($dice =~ /\+/) { my @arr2 = split (/\+/, $arr[1]); $arr2[0]++; for my $i (1 .. $arr[0]) { print ((int(rand($arr2[0] - 1)) + 1 + $arr2[1]), "\n"); } } else { $arr[1]++; for my $i (1 .. $arr[0]) { print ((int(rand($arr[1] - 1)) + 1), "\n"); } } push @history, $dice; }

        When I run it, the up arrow doesn't cause any error messages, but it doesn't do anything either:

        Input the dice and modifier you want to roll like this: 2d9+100. To qu +it, press Ctrl-C. 2d9+100 109 101 ^[[A ^C
        $thanks++, zsl
Re: How to add input history
by pme (Monsignor) on Apr 06, 2015 at 17:33 UTC
Re: How to add input history
by jeffa (Bishop) on Apr 06, 2015 at 21:26 UTC

    This is not an answer to your question, but you can utilize regular expressions and the matching operation modifier e to evaluate the expression for you:

    for my $input (<DATA>) { chomp $input; (my $eval = $input) =~ s/(\d+)d(\d+)(.*)/$1*(int(rand$2)+1)+$3/e; printf "%10s = %i\n", $input, $eval; } __DATA__ 2d9+100 1d20+10 1d3+100 50d10-5

    Good luck, and perhaps the information found here can help: Make a Text Based RPG

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: How to add input history
by Anonymous Monk on Apr 06, 2015 at 20:02 UTC
    #!/usr/bin/perl # http://perlmonks.org/?node_id=1122584 use Term::ReadLine; use strict; (my $term = new Term::ReadLine 'dierolling')->ornaments(",,,"); print "Input the dice and modifier you want to roll like this: 2d9+100 +. To quit, press Ctrl-C.\n"; while (1) { defined(my $dice = $term->readline('? ')) or last; my @arr = split (/d/, $dice); my @arr2 = split (/\+/, $arr[1]); for my $i (1 .. $arr[0]){ print ((int(rand($arr2[0] - 1)) + 1 + $arr2[1]), "\n");}}

    This is only during a single run. If you want history to be persistent from one execution to the next, please say so.

      Your code doesn't seem to do anything when I use the up-arrow...

      steve@spek ~ $ ./test.pl Input the dice and modifier you want to roll like this: 2d9+100 +. To quit, press Ctrl-C. ? 2d9+100 104 105 ? ^[[A ?

      -stevieb

        See the perldoc page DESCRIPTION for Term::ReadLine . You will need one of the Term::ReadLine::* packages.

        I have Term::ReadLine::Perl

        That's probably why it worked for me.