Как запустить цикл while в фоновом режиме

#python-3.x #while-loop

#python-3.x #цикл while

Вопрос:

Я пытаюсь запрограммировать пошаговую игру, в которой для выполнения некоторых действий требуется более одного хода. Я выяснил, что включение цикла while внутри функции, которая выполняет действие, которое принимает несколько оборотов, может сработать. Это мой тест:

 #This function is supposed to 'work' after 3 turns
def action(current_turn):
    while global_turn - current_turn != 3:
        pass
    test = "works"

test = "doesn't work"
game =True
global_turn = 0
while game:
    global_turn  = 1
    print(f'nThis is turn #{global_turn}n')
    user_input = input('Do [1]yes [2]no')
    if user_input == '2':
        pass
    elif user_input == '1':
        action(global_turn)
  

Я думал, что это продолжало бы печатать «This is turn #{global_turn}» вечно, даже если я вызвал функцию, но она просто сидит там. Есть ли какой-либо способ заставить цикл while продолжаться, одновременно продолжая работу с внешним циклом?

Ответ №1:

Попробуйте этот код:

 #This function is supposed to 'work' after 3 turns
def action(current_turn, action_turn):
    if global_turn != action_turn:
        return
    print('action!')

game = True
global_turn = 0
action_turn = None
while game:
    global_turn  = 1
    print(f'nThis is turn #{global_turn}n')
    user_input = input('Do [1]yes [2]no')
    if user_input == '2':
        pass
    elif user_input == '1':
        action_turn = global_turn   3
    action(global_turn, action_turn)
  

Примечание: это фактически не запускает цикл while в фоновом режиме. В вашем примере это не кажется необходимым. Но если ваша игра становится более сложной, вы можете захотеть изучить

для запуска чего-либо в фоновом режиме.


Редактировать

asyncio Версия:

 import asyncio


class Game:
    def __init__(self):
        self.game_running = True
        self.global_turn = 0
        self.turn_started = asyncio.Event()
        self.turn_ended = asyncio.Event()

    async def action(self, wait_turns):
        while wait_turns > 0:
            await self.turn_ended.wait()
            await self.turn_started.wait()
            wait_turns -= 1
        print('action!')


    async def main(self):
        while self.game_running:
            self.global_turn  = 1
            print(f'nThis is turn #{self.global_turn}n')
            self.turn_ended.clear()
            self.turn_started.set()
            await asyncio.sleep(0)  # yield control to other tasks
            user_input = input('Do [1]yes [2]no')
            if user_input == '2':
                pass
            elif user_input == '1':
                asyncio.ensure_future(self.action(3))
            self.turn_started.clear()
            self.turn_ended.set()
            await asyncio.sleep(0)  # yield control to other tasks


if __name__ == '__main__':
    game = Game();
    loop = asyncio.get_event_loop()
    loop.run_until_complete(game.main())
  

Объяснение:

  • Когда пользователь вводит данные, 1 сопрограмма action запланирована в «фоновом режиме» с использованием asyncio.ensure_future
  • При каждом повороте задаются два asyncio.Event сигнала. Один для начала хода, один для конца хода.
  • action Сопрограмма ожидает начала и завершения определенного числа оборотов, прежде чем напечатает «действие»
  • action Сопрограмма может выполняться несколько раз в фоновом режиме, каждый раз отсчитывая количество оборотов, пока не станет активным.
  • await asyncio.sleep(0) требуется для того, чтобы action сопрограммы продолжали выполняться.
  • Если вам нужно, чтобы действия были активны, пока main функция ожидает ввода пользователем, вы можете использовать aioconsole.ainput :

     from aioconsole import ainput
    
    async def main(self):
        [...]
        user_input = await ainput("Do [1]yes [2]no")
        [...]
      

Комментарии:

1. Дело в том, что в моей игре 3 игрока, и вы можете совершать несколько действий за ход.

2. @bajotupie Я добавил другое решение к своему ответу