3

I am writing a simple text based adventure game in Python. I would like to have certain processes occur periodically regardless of what the user does, approximately every 2 minutes. For instance: Have NPC's move around the rooms, have people get hungry and thirsty, have people heal, and during combat, have the battle proceed. Right now, I'm using 'raw_input' to get commands from the user, but this essentially pauses the code. How can I make the game proceed even if the user just sits there and doesn't type anything?

  • Sounds like you'll need at least two threads — one for IO and the other for real-time game actions. – Waleed Khan Mar 29 '13 at 00:59
  • Does anything happen "asynchronously" (can something happen while waiting for the user to input), or is it just changes that can happen and the only opportunity to witness them is upon entering a new command? – Brian Cain Mar 29 '13 at 00:59
  • Yes to Waleed, I think that's it. Though I don't know how to do that... Yes to Brian as well. I would prefer things to continue to happen even if the user doesn't type anything. – user2218093 Mar 29 '13 at 01:23

5 Answers5

1

I think typically in this situation you wouldn't have a background process or thread doing calculations. Instead, when the user types in some response do a time delta and based off the elapsed time between inputs calculate how much a player would have healed and what the battle events would have been etc.. That is if you don't want console updates while game is waiting for the user to respond.

Edit: or try something like this:

import time
import sys

win32 = True
try:
    from msvcrt import kbhit, getch
    print "[+] Running on windows, using msvcrt."
except ImportError:
    print "[+] Not running on windows, attempting unix-like."
    win32 = False

    import termios, fcntl, sys, os
    import select
    fd = sys.stdin.fileno()

    oldterm = termios.tcgetattr(fd)
    newattr = termios.tcgetattr(fd)
    newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
    termios.tcsetattr(fd, termios.TCSANOW, newattr)

    oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)


POLLTIME = 5
done = False
command = ""
while not done:

    sys.stdout.write("\r")
    print("Something happened (polling)%s" % (" " * command.__len__() ))
    sys.stdout.write("Enter command: %s" % command)
    sys.stdout.flush()

    t = time.time()
    if win32:
        while time.time() - t < POLLTIME:
            if kbhit():
                c = getch()
                if ord(c) < 127 and ord(c) > 31:
                    command += c
                    message = "\rEnter command: " + command
                    sys.stdout.write("\r%s" % message)
                if "\r" == c:
                    if "quit\r" == command:
                        done = True
                        break
                    sys.stdout.write("\rThe command was: %s\n" % command)
                    command = ""
                    sys.stdout.write("\rEnter command: %s \b" %command)
                elif "\b" == c:
                    command = command[:-1]
                    sys.stdout.write("\rEnter command: %s \b" %command)
                sys.stdout.flush()
    else:
        while time.time() - t < POLLTIME:
            try:
                c = '\0'                
                if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
                    c = sys.stdin.readline(1)
                    if ord(c) < 127 and ord(c) > 31:
                        command += c
                        message = "\rEnter command: " + command
                        sys.stdout.write("\r%s" % message)
                if c == "\n":
                    if "quit" == command:
                        done = True
                        break
                    print("\rThe command was: %s" % command)
                    command = ""
                    message = "\rEnter command: " + command
                    sys.stdout.write("\r%s" % message)
                if 127 == ord(c):
                    command = command[:-1]
                    sys.stdout.write("\rEnter command: %s \b" % command)
                sys.stdout.flush()

            except IOError:
                    pass
Alden
  • 2,229
  • 1
  • 15
  • 21
  • I would actually prefer that the console _does_ update while waiting for the user to respond. Otherwise, there would be a large build up of occurrences. Say the user gets up and has lunch then comes back 30 minutes later. As soon as they enter a command there would be a rush of things that have happened over 30 minutes. Also, I'd like the user to be aware that they may die if they leave the game unattended. – user2218093 Mar 29 '13 at 01:30
  • I'd love to check out this code, but when I run on my machine it doesn't recognize msvcrt. I'm running python 3.3 on a mac. What do I need to do to make this work? – user2218093 Mar 29 '13 at 12:11
  • Turns out msvcrt is windows only... should have known. Well I updated the code to work for linux. Hopefully it works with mac as well. I don't have access to a osx machine so I cant test it myself. Also this was tested with python 2.7 so if you have problems try putting it through the 2 to 3 code translator. – Alden Mar 29 '13 at 23:25
0

The answer is -- don't write real time for a console! If you want to do this text-based, you may wish to switch to Tkinter. This will allow you to do these things separately -- and also display text during these periodic events, and use a simple .after() call to execute them.

pydsigner
  • 2,779
  • 1
  • 20
  • 33
0
  1. Sample the time after each input (up to you whether to do it only for successful commands or optionally include invalid ones).

  2. Compare this time to the prior sample and divide by some world tick interval.

  3. Iterate through the list of activities that happen per tick (for npc in npcs: npc.move_to_adjacent_posn(), e.g.).

Brian Cain
  • 14,403
  • 3
  • 50
  • 88
0

There are ways to read user input without pausing the code. It's called "asynchronous I/O" or "non-blocking I/O". One way to do it is to create a separate thread to listen to the user's requests and queue them to process inside your game loop.

This question and its answers explain how to do non-blocking I/O in Python: Non-blocking read on a subprocess.PIPE in python

Community
  • 1
  • 1
Eser Aygün
  • 7,794
  • 1
  • 20
  • 30
0

I am not sure how you can do this without using a separate thread (and it is easy to use a separate thread).

But my point here will be: look like your text-based function is a event/command based application? i.e. the client state won't change if there is no further command/event from the user? Not sure what you are trying to monitor with a timed function, but if your application is not already event-based, i.e. aggregate the state from the set of event the user perform/send, then you might want to make your application to be event-based, then you can get rid of the timed function. hope that help.

John
  • 2,107
  • 3
  • 22
  • 39