http://www.perlmonks.org?node_id=11104382

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

Hi Guys, Im writing a "Simple" text game in perl for learning purpose.

I have this problem. I want to create a timer that can do some background stuff like adding player hp / mana / anything but im not sure how to create it at this moment and don't know if this is the right part of code to execute subroutine for that. And main problem is that Heartbeat is execute only once.

This is the code snipped for reading player input.

while (1) { Engine::doHeatbeat; chomp (my $input = <STDIN>); # Trim right / left white character $input =~ s/^\s+|\s+$//g; given ($input) { Engine::Parser::Command($input); } }
The Heartbeat subroutine is at this moment dummy.. Its my first time here so sorry if i write the question wrong ;)

Replies are listed 'Best First'.
Re: Infinite LOOP and reading from STDIN
by hippo (Bishop) on Aug 13, 2019 at 12:55 UTC

    Welcome to the Monastery!

    It sounds like you are looking for an event loop. The AnyEvent intro is probably as good a place to start as any. Be prepared for a steep learning curve.

    BTW, you might want to consider not using given in your code. It is still experimental.

      Yes, i know switch is experimental, but doing this in if's is messy, any other way for that ? like in C way of CASE ?

        Glivter:   Further to haukex's reply:   In certain cases, I find  ? : ternary chains (update: I've also seen them referred to as "ladders") very neat, clear, readable and maintainable, and useful:

        my $result = CONDITION1 ? EXPRESSION1 : # "if" case CONDITION2 ? EXPRESSION2 : # "elsif" case CONDITION3 ? EXPRESSION3 : # ditto ... # and so on... die "no condition met... " # "else" default case ;
        And, of course, you don't have to die or take any other drastic action in the default case, but simply return a default value. Your EXPRESSION can also be a side-effect-possessing  do { ... } expression or subroutine call that returns a meaningful value. (A do-block or subroutine call always returns a value, but if you don't design it right, that value may be surprising.)


        Give a man a fish:  <%-{-{-{-<

        Yes, i know switch is experimental, but doing this in if's is messy, any other way for that ? like in C way of CASE ?

        I know what you mean, and I have been dreaming of a good switch implementation for Perl, but after doing a lot of research and trying a lot of different modules, I have come to accept that if-elsif-else chains are just "the" way to do it in core Perl. They also give more power in their conditions, that many of the switch implementations can't provide. To match a single value against a list of other values, there are modules such as List::Util's any and several other options. for ($value) can be used as a topicalizer. The only thing I miss is that given can return a value from its block.

Re: Infinite LOOP and reading from STDIN
by tybalt89 (Monsignor) on Aug 13, 2019 at 14:33 UTC
    #!/usr/bin/perl # https://perlmonks.org/?node_id=11104382 use strict; use warnings; use IO::Select; use Time::HiRes qw(time); use List::Util qw( max ); sub Engine::doHeatbeat { print "in Engine::doHeatbeat\n"; } sub Engine::Parser::Command { print "in Engine::Parser::Command( @_ )\n"; } my $interval = 10; # seconds my $sel = IO::Select->new(*STDIN); my $trigger = time + $interval; # unix time to call heartbeat while(1) { my $wait = max 0, $trigger - time; for my $fh ( $sel->can_read( $wait ) ) { chomp( my $input = <$fh> ); Engine::Parser::Command($input); } if( $trigger <= time ) { Engine::doHeatbeat; $trigger = time + $interval; } }

    Consider this an example of why you should use an Async package as recommended by Hippo here Re: Infinite LOOP and reading from STDIN

Re: Infinite LOOP and reading from STDIN
by BillKSmith (Monsignor) on Aug 13, 2019 at 13:43 UTC
    Event-based programming is not "Simple" in Perl (or any other language). I recommend that you either change your requirements or postpone this project until you have learned more Perl. When you are ready, try to write your game as a GUI application using a module such as Tk. (I am sure that you will be much happier with the finished game).
    Bill
Re: Infinite LOOP and reading from STDIN
by Theodore (Friar) on Aug 14, 2019 at 07:55 UTC
    Hi,

    I did that for a MUD some years ago — the original was written in REXX back in the 80's, out of nostalgia I recreated it in Perl using the old data files. I used IO::Async::Loop for the main event loop.

    Code snippets:

    use IO::Async::Loop; ..... our $loop = IO::Async::Loop->new; $loop->add( IO::Async::Timer::Periodic->new( # This will call the main operational code of Muda: interval => 1, notifier_name => 'MAIN', on_tick => \&mainMudaTick )->start ); ..... $loop->run;

    The full code can be found here: https://www.grcrun11.gr/files/

Re: Infinite LOOP and reading from STDIN
by jcb (Parson) on Aug 14, 2019 at 01:46 UTC

    Your program blocks waiting for user input, and we can take advantage of that for a simpler answer to timed events.

    All you need is to store a timestamp for the last time doHeartbeat has been run and compare it to the current time before updating it. You now know how many seconds have elapsed since you last "looked at" the player's status, so you handle healing and mana recharge by "catching up" to the events that "transpired" while the program was waiting for input.

    As an example, suppose that a player normally gains 1 mana every minute. The doHeartbeat procedure would then subtract the stored timestamp from time, divide by 60 seconds to a minute, multiply by 1 mana per minute, and add the result to the player's mana pool. (You have to track fractional mana, but you do not have to display it.)

    With the code you have shown, doHeartbeat is executed once for each command the user gives. You might also want to fix the "doHeatbeat" typo. :-)

    Lastly, using $_ and making the loop while(<STDIN>) will cause the program to exit if STDIN reaches EOF, rather than needing Control-C.