I am creating a battle game for telegram groups as my beginner's project, one player challenges the other, and then a battle happens in the chat. Now I want to implement a "timeout" function for it.
I've managed to make it work using _thread:
import _thread
class Battle:
def __init__(self, chat_id):
self.chat = chat
print('Instance of battle started on chat %s' % self.chat)
self.pcount = 0
self.p1score = 0
self.p2score = 0
def reset(self):
self.pcount = 0
self.p1score = 0
self.p2score = 0
def timeout(chat_id):
time.sleep(90)
battle = battledic[chat_id]
if battledic[chat_id].timer == 1:
sendmessage(chat_id, 'no one accepted') # first parameter is the chat address and the second one is the message
battle.reset()
else:
return
battle = Battle(chat_id)
if message == "challenge":
if battle.pcount == 0:
_thread.start_new_thread(timeout, (chat_id,))
...
So basically what I'm doing is calling the timeout function, that is going to sleep in a different thread for 90 seconds, and then it checks if the battle still with only one player (pcount == 1), if it is the case, it sends the chat a message telling them the fight did not start, and then resets the battle instance.
The problem here is that I'm raising exceptions very often, even though it works fine most of the time, also sometimes it doesn't have the desired effect, closing ongoing battles. Researching I've found out that this is not the desired way of doing this butI could not find better solution, and maybe I want to run other battles from other chats in parallel too. What should be used in this case? cooperative multitasking? multiprocessing? the trheading module? thanks for any tips on this task!
I've got you started. I'm not sure how you are going to chat or keep record of who's playing.
My first thoughts were to run threads, but the while loop is easier.
# from threading import Thread
from time import sleep
no_one = True
battledic = {} # your dict here. It may be better to use a list of objects (classes) for your opponents
class Battle(object):
def __init__(self, chat_id):
self.chat_id = chat_id
self.pcount = 0
self.p1score = 0
self.p2score = 0
print('Instance of battle started on chat %s' % self.chat_id)
def reset(self):
self.pcount = 0
self.p1score = 0
self.p2score = 0
def wheres_waldo():
x = 0; no_one = True
while x < 90: # waits 90 seconds unless found
for player in battledic.keys():
if player.is_requesting: # found someone
no_one = False
break # quit looking
else:
print('No one yet')
sleep(1); x+= 1
if no_one:
sendmessage(chat_id, 'no one accepted') # first parameter is the chat address and the second one is the message
battle.reset()
else:
sendmessage(chat_id, 'Found someone')
def sendmessage(chat_id,message):
print('%s on chat %s' % (message,chat_id))
battle = Battle(chat_id)
while True:
message = raw_input('Command? ')
if message == "challenge":
if battle.pcount == 0:
wheres_waldo()
# first thoughts were to run threads but the while loop is easier
# wait = Thread(target=timeout, args=(chat_id))
# where = Thread(target=wheres_waldo)
# wait.start();where.start()
# wait.join();where.join()
Related
I am coding a discord bot with python, I am running into difficulties with the next feature. When the message: ('user' joined) is read the bot should start looping the function every x seconds until the message: ('user' left) is read.
I have been testing different code in scratch files. It succesfully starts looping at 'user' joined. But when the ('user' left) message should come through it won't take the new argument and continues the loop forever.
These two versions look most promising:
import re
import time
def message(servermessage):
a = re.sub("[^\S]", " ", servermessage).split()
if a[1] == 'joined':
online = False
elif a[1] == 'left':
online = True
while True:
mcusername = a[0]
print(mcusername, 0)
if online:
break
time.sleep(2)
message('user joined')
time.sleep(10)
message('user left')
and
import re
import sched, time
s = sched.scheduler(time.time, time.sleep)
def message(servermessage):
a = re.sub("[^\S]", " ", servermessage).split()
def rewardplayer():
s.enter(1, 1, rewardplayer)
mcusername = a[0]
print(mcusername, 0)
if a[1] == 'joined':
s.enter(1, 1, rewardplayer)
s.run()
elif a[1] == 'left':
s.cancel()
message('user joined')
time.sleep(10)
print('done')
message('user left')
Another requirement that I have is that it should be able to run the same loop for a different user when a ('newuser' joined) message is given before the ('previoususer' left) message from a previous user.
I apologise for the vague explanation. But I hope you can help. Thanks in advance!
About your first code:
there you have while which should be broken when online is True. But its never changing inside that loop. So its similar to deadlock.
About your second code:
there you are using "scheduler". Usually it means you want to run some events periodically. But your aim is to react when event was happened. In any time. Not schedulled.
So here I can recommend you to use async python way.
(please, read about asyncio)
import re
import asyncio
async def process_msgs(queue):
while True:
msg = await queue.get()
online, usr_name = parse_msg(msg)
print(usr_name, 0)
queue.task_done()
def parse_msg(msg):
result = re.sub("[^\S]", " ", msg).split()
action = result[1]
usr_name = result[0]
if action == 'joined':
online = False
elif action == 'left':
online = True
return online, usr_name
async def main():
queue = asyncio.Queue()
queue.put_nowait('usr joined')
task = asyncio.create_task(process_msgs(queue))
queue.put_nowait('usr left')
await queue.join()
task.cancel()
asyncio.run(main())
You can use it like that.
There we have a queue, where we put our events. You can do that through infinite way too (listen socket or smthg - here you will get msg about different users).
And we have task (worker), which work while our "main" function is alive.
Background
Recently I simplified a text-only game I was making:
Here is the code:
import time
#
# Declaration of classes -- game logic
#
class Action:
def __init__(self, description, newLocation):
self.description = description
self.newLocation = newLocation
class Location:
def __init__(self, name, description):
self.name = name
self.description = description
self.actions = []
def addAction(self, action):
self.actions.append(action)
def arrive(self):
print("----------------------------------------")
print('You arrived to: {}'.format(self.name))
print(self.description)
i = 0
print(" (0. Exit game)")
for action in self.actions:
i = i + 1
print(" {}. {}".format(i, action.description))
validChoice = False
while not validChoice:
try:
index = int(input("? : "))
if index == 0:
print("Abandoning your quest... :(")
return
if 0 < index and index <= len(self.actions):
validChoice = True
except ValueError:
pass
self.actions[index - 1].newLocation.arrive() # indexing is 0-based, but we get 1-based index from the user
#
# Declare your locations here -- data
#initial rooms
house = Location("house", "Inside the house there is a sweet aroma of rolls being baked.")
field = Location("field", "Grass swishes as you walk through it. a solitary horse is grazing in the field.")
stable = Location("stable", "You are greeted by the sound of various animal noises and the smell of various animal odors")
#secondary rooms-house
#declare location actions here
spawnPosition = Location("Spawn position", "You wake up in a sunny morning. After you get out of your bad, you decide to:")
spawnPosition.addAction(Action("for house", house))
spawnPosition.addAction(Action("for field", field))
spawnPosition.addAction(Action("for stable", stable))
#house options
house.addAction(Action("leave the house",field))
#
# The game starts here
#
print ("Cool Cousins Studios Presents...")
time.sleep(2) # Sleep for 2 seconds
print ("With use of Python Code...")
time.sleep(2) # Sleep for 2 seconds
print ("Adventure In Saldica")
time.sleep(2) # Sleep for 2 seconds
print ("(cue dramatic music)")
time.sleep(3) # Sleep for 3 seconds
spawnPosition.arrive() # This is all you need to do to initiate the game. When this function returns, your journey has concluded.
print("Bye-bye")
However, I don't know how I can make it change variable values based on the options/locations the user chooses. I've tried putting if statements in the Location.arrive function, but I'm wondering if there is a simpler way.
What I need to know:
How do I make and edit variables based on my location, and what is the simplest way to do it?
You should define a class for your character, where you would store their current location and you can present choices based on the current location or history of actions that the character has taken.
The Location class should contain the available actions you can make from that location and a function to present them when needed.
This way, you will only need to define each room once.
import time
#
# Declaration of classes -- game logic
#
class Character:
def __init__(self, name, location):
self.name: str = name
self.location: Location = location # initialize with some starting location
self.actions = [] # Store all the actions this player has taken
# Here we will have a never ending loop to get inputs from the user
while True:
newLocation = self.location.arrive()
self.actions.append(newLocation)
self.location = newLocation
if sum([1 for loc in self.actions if loc == house]) >= 2:
house.description = "Why are you back home? Go somewhere else."
if self.location == exitLocation:
break
print("\n\nYou visited:")
for i in self.actions:
print(i.name)
class Location:
def __init__(self, name, description, actions:list = []):
self.name = name
self.description = description
self.actions = actions # this should be a list of locations that can be visited from here
def arrive(self):
print("----------------------------------------")
print('You arrived to: {}'.format(self.name))
print(self.description)
i = 0
print(" (0. Exit game)")
for action in self.actions:
i = i + 1
print(" {}. for {}".format(i, action.name))
validChoice = False
while not validChoice:
try:
index = int(input("? : "))
if index == 0:
validChoice = True
print("Abandoning your quest... :(")
return exitLocation
if 0 < index and index <= len(self.actions):
validChoice = True
return self.actions[index - 1] # indexing is 0-based, but we get 1-based index from the user
except ValueError:
pass
#
# Declare your locations here -- data
#initial rooms
field = Location("field", "Grass swishes as you walk through it. a solitary horse is grazing in the field.")
stable = Location("stable", "You are greeted by the sound of various animal noises and the smell of various animal odors")
house = Location("house", "Inside the house there is a sweet aroma of rolls being baked.")
spawnPosition = Location("Spawn position", "You wake up in a sunny morning. After you get out of your bed, you decide to:", [field, stable, house])
exitLocation = Location("Exit Game", "Bye-bye")
#secondary rooms-house
# Declare location actions here
field.actions = [stable, house, spawnPosition]
stable.actions = [field, house, spawnPosition]
house.actions = [field, stable, spawnPosition]
#
# The game starts here
#
print ("Cool Cousins Studios Presents...")
time.sleep(2) # Sleep for 2 seconds
print ("With use of Python Code...")
time.sleep(2) # Sleep for 2 seconds
print ("Adventure In Saldica")
time.sleep(2) # Sleep for 2 seconds
print ("(cue dramatic music)")
time.sleep(3) # Sleep for 3 seconds
# Create a character and initialize with spawn position
character = Character("John", spawnPosition) # This is all you need to do to initiate the game. When this function returns, your journey has concluded.
print("----------------------------------------")
print("Bye-bye")
If you want to change the description of the room based on events that have occurred, you can read self.actions within the character object and modify the descriptions of the rooms there. Same goes for changing the available actions from a room. You would simply modify house.actions in that while loop, if you want to change which rooms you can visit from there after the user has completed some set of actions.
A straightforward application of:
Prompt for user input.
Start countdown (or count up) timer.
Wait on user input (as timer counts down/up).
If user inputs a correct response, conditional statement 1
Else, conditional statement 2
If user exceeds a preset time, timer expires and user is directed accordingly.
I've tried some of the solutions offered on this web site. However, in all cases, the count up/down timer seems to stop once the user input prompt is generated. In other words, the timer does not seem to run as a separate (background) thread.
import threading
import time
class TimerClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
self.count = 10
def run(self):
while self.count > 0 and not self.event.is_set():
print (self.count)
int_answer = (int(input('Enter your age: '), base = 10)
str_answer = str(int_answer)
while str_answer == '':
self.count -= 1
self.event.wait(10)
if (int_answer > 50) :
<do something>
else:
<do somethingelse>
def stop(self):
self.event.set()
tmr = TimerClass()
tmr.start()
time.sleep(1)
tmr.stop()
The program should go to condition 1 if a response of > 50 is provided; else, go to condition 2 if a response of <= 50 is entered. The timer should expire after a period of 10 secs. if the user has not provided a response (with user notification).
I've adapted the code from this answer to your needs:
import threading
import queue
import time
def read_kbd_input(inputQueue):
print('Ready for keyboard input:')
while (True):
input_str = input()
inputQueue.put(input_str)
def main():
inputQueue = queue.Queue()
inputThread = threading.Thread(target=read_kbd_input, args=(inputQueue,), daemon=True)
inputThread.start()
start_time = time.time()
while True:
if (inputQueue.qsize() > 0):
input_str = inputQueue.get()
# Insert your code here to do whatever you want with the input_str.
print("input_str = {}".format(input_str))
break
time.sleep(0.1) # poll each 100ms
if time.time() - start_time > 2: # timeout after 2 sec
break
print("End.")
if (__name__ == '__main__'):
main()
I'm trying to design a control interface for my system which sends and receives some data through serial link. My searches related to GUI design took me to understand the "multi-threading" issue and code below shows the latest position I arrived.
This indicates similar parts (e.g try, run) with the ones I've seen on example GUIs. I planned to convert this to a GUI, once I understand how it exactly works.
So the problem is after I start, stop the code below I can't restart it again. Because, as I understand, multi-threading features only one cycle: start, stop and quit. I mean it doesn't accept start command after stop.
My question is how I can make this code to accept start after stopping?
Best wishes
import threading, random, time
class process(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
self.leave = 0
print("\n it's running ...\n\n")
while self.leave != 1:
print "Done!"
time.sleep(1)
operate = process()
while True:
inputt = input(" START : 1 \n STOP\t : 0 \n QUIT\t : 2 \n")
try:
if int(inputt) == 1:
operate.start()
elif int(inputt) == 0:
operate.leave = 1
elif int(inputt) == 2:
break
except:
print(" Wrong input, try egain...\n")
Create process inside while True loop
if int(inputt) == 1:
operate = process()
operate.start()
It should work.
... but your code may need other changes to make it safer - you will have to check if process exists before you try to stop it. You could use operate = None to control it.
import threading
import random
import time
class Process(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
self.leave = False
print("\n it's running ...\n\n")
while self.leave == False:
print("Done!")
time.sleep(1)
operate = None
while True:
inputt = input(" START : 1 \n STOP\t : 0 \n QUIT\t : 2 \n")
try:
if int(inputt) == 1:
if operate is None:
operate = Process()
operate.start()
elif int(inputt) == 0:
if operate is not None:
operate.leave = True
operate.join() # wait on process end
operate = None
elif int(inputt) == 2:
if operate is not None:
operate.leave = True
operate.join() # wait on process end
break
except:
print(" Wrong input, try egain...\n")
Other method is not to leave run() when you set leave = True but keep running thead. You would need two loops.
def run(self):
self.leave = False
self.stoped = False
print("\n it's running ...\n\n")
while self.leave == False:
while self.stoped == False:
print("Done!")
time.sleep(1)
I am trying to make a text based game in which the user is a pilot in space. I want to create a movement system but am unsure how to do it. I want the user to be able to put in the desired grid coordinates, and his vehicle will begin to change its grid coords to get closer and closer to the ones he inputted.
Now, to do this I will probably need multithreading and a time element. But I am unsure how I can use a time element. Any advice is greatly appreciate, i'm just trying to learn here. Thanks guys!
from Gundam2 import Mobilesuits
#Main Variable/Object declarations:
Leo1=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[100,100,100])
Leo2=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[300,100,100])
Leo3=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[100,150,100])
currentmobilesuit=Leo1
#Main Function declarations
def commands(user_input,currentmobilesuit):
if user_input == "radar":
currentmobilesuit.radar()
elif user_input == "commands":
print("Command list:\nradar")
else:
print("Invalid command\nType 'commands' for a list of valid commands")
#Main execution
while True:
commands(raw_input(),currentmobilesuit)
class Mobilesuits:
#class global variables/methods here
instances = [] #grid cords here
def __init__(self,armor,speed,name,description,cockpit_description,\
radar_range, coordinates):
Mobilesuits.instances.append(self)
self.armor=armor
self.speed=speed
self.name=name
self.description=description
self.cockpit_description=cockpit_description
self.radar_range=radar_range
self.coordinates=coordinates
def can_detect(self, other):
for own_coord, other_coord in zip(self.coordinates, other.coordinates):
if abs(own_coord - other_coord) > self.radar_range:
return False
return True
def radar(self):
for other in Mobilesuits.instances:
if other is not self and self.can_detect(other):
print "%s detected at %s" % (other.description, other.coordinates)
Games typically have a "master loop" of some kind; yours does here:
#Main execution
while True:
commands(raw_input(),currentmobilesuit)
The simplest thing to do is to count in the loop:
#Main execution
turn_count = 0
while True:
commands(raw_input(),currentmobilesuit)
turn_count += 1
If you wanted the real time taken to have some impact on the counter, or be the counter, you can get the current time from the time module calling time.time().
#Main execution
import time
time_start = time.time()
time_elapsed = 0
while True:
commands(raw_input(),currentmobilesuit)
time_elapsed = time.time() - time_start
A couple other thoughts:
Make a Game class, and put the turn counter and game loop in that.
Have the commands function return a number that is the number of time units that took place during the command; for example, entering an invalid command might take 0 turns, while repairing a robot might take 5.
#Main execution
turn_count = 0
while True:
turns_taken = commands(raw_input(),currentmobilesuit)
turn_count += turns_taken
You can use non-blocking I/O. This will help you avoid the complications of threading. Here's your sample code implemented with a non-blocking read of stdin:
#!/usr/bin/python
import sys
import select
call_count = 0
#Main Function declarations
def commands(user_input):
global call_count
if len(user_input) > 0:
print('call count: ' + str(call_count) + ' user entered: ' + user_input)
def raw_input_no_block():
global call_count
call_count = call_count + 1
input_avail = select.select([sys.stdin], [], [], 0.1)[0] #wait for 0.1 seconds
if input_avail:
return sys.stdin.readline()
else:
return ''
#Main execution
while True:
commands(raw_input_no_block())