Python 3 неблокирующее синхронное поведение

IMHO, alloca считается плохой практикой, потому что все боятся исчерпать ограничение размера стека.

Я многому научился, прочитав этот поток и некоторые другие ссылки:

Я использую alloca в основном для того, чтобы сделать мои простые файлы C компилируемыми на msvc и gcc без каких-либо изменений, C89 style, no # ifdef _MSC_VER и т. д.

Спасибо! Эта нить заставила меня зарегистрироваться на этом сайте:)

3
задан thestateofmay 16 January 2019 в 02:32
поделиться

1 ответ

Вы сказали:

Я хочу создать интерфейс для ИИ, чтобы взять под контроль змею. Было бы несправедливо, если бы состояние игры просто передавалось ИИ на каждой итерации, так как это могло бы занять столько времени, сколько потребуется для вычисления следующего хода. Следовательно, почему он должен быть синхронным и неблокирующим.

Итак, чтобы получить то, что вы хотите, вам нужна абстракция. В приведенном ниже примере я создал класс Controller, который это делает. KeyboardController обрабатывает ввод с клавиатуры, в то время как AsyncController запускает поток и использует класс Queue для передачи состояния игры и решения «ИИ». Обратите внимание, что вы должны получать события pygame в основном потоке, поэтому я делаю это в основном цикле и просто передаю события в контроллер.

Ваш AI должен вызываться функцией worker. Как вы можете видеть, в настоящее время «ИИ» в рабочей функции действует только каждые 0,5 секунды, а частота кадров равна 120. Для игры не имеет значения, что ИИ принимает решение так долго.

Вот код:

import pygame
import time
import random
from queue import Queue, Empty
from threading import Thread

class Controller():
    def __init__(self, color, message, actor):
        self.color = color
        self.message = message
        if actor: self.attach(actor)

    def attach(self, actor):
        self.actor = actor
        self.actor.controller = self
        self.actor.image.fill(self.color)

class AsyncController(Controller):
    def __init__(self, actor=None):
        super().__init__(pygame.Color('orange'), "AI is in control.", actor)
        self.out_queue = Queue()
        self.in_queue  = Queue()
        t = Thread(target=self.worker)
        t.daemon = True
        t.start()

    def update(self, events, dt):
        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE: self.actor.controller = KeyboardController(self.actor)

        self.out_queue.put_nowait((self.actor, events, dt))
        try: return self.in_queue.get_nowait()
        except Empty: pass

    def worker(self):
        while True:
            try:
                actor, events, dt = self.out_queue.get_nowait()
                if actor.rect.x < 100: self.in_queue.put_nowait(pygame.Vector2(1, 0))
                if actor.rect.x > 600: self.in_queue.put_nowait(pygame.Vector2(-1, 0))
                if actor.rect.y < 100: self.in_queue.put_nowait(pygame.Vector2(0, 1))
                if actor.rect.y > 400: self.in_queue.put_nowait(pygame.Vector2(0, -1))
                if random.randrange(1, 100) < 15:
                    self.in_queue.put_nowait(random.choice([
                        pygame.Vector2(1, 0),
                        pygame.Vector2(-1, 0),
                        pygame.Vector2(0, -1), 
                        pygame.Vector2(0, 1)]))

                time.sleep(0.5)
            except Empty:
                pass

class KeyboardController(Controller):
    def __init__(self, actor=None):
        super().__init__(pygame.Color('dodgerblue'), "You're in control.", actor)

    def update(self, events, dt):
        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE: self.actor.controller = AsyncController(self.actor)
                if e.key == pygame.K_UP: return pygame.Vector2(0, -1)
                if e.key == pygame.K_DOWN: return pygame.Vector2(0, 1)
                if e.key == pygame.K_LEFT: return pygame.Vector2(-1, 0)
                if e.key == pygame.K_RIGHT: return pygame.Vector2(1, 0)

class Actor(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color('dodgerblue'))
        self.rect = self.image.get_rect(center=(100, 100))
        self.direction = pygame.Vector2(1, 0)
        self.pos = self.rect.center

    def update(self, events, dt):
        new_direction = self.controller.update(events, dt)
        if new_direction:
            self.direction = new_direction
        self.pos += (self.direction * dt * 0.2)
        self.rect.center = self.pos

def main():
    pygame.init()

    actor   = Actor()
    sprites = pygame.sprite.Group(actor)
    screen  = pygame.display.set_mode([800,600])
    clock   = pygame.time.Clock()
    font    = pygame.font.SysFont("consolas", 20, True)
    dt      = 0
    KeyboardController(actor)

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        sprites.update(events, dt)
        screen.fill(pygame.Color('grey12'))
        screen.blit(font.render(actor.controller.message + ' [SPACE] to change to keyboard control.', True, pygame.Color('white')), (10, 10))
        sprites.draw(screen)
        dt = clock.tick(120)
        pygame.display.update()

if __name__ == '__main__':
    main()

enter image description here

Обратите внимание, что эта реализация использует бесконечные очереди. Вы хотите добавить некоторую логику для очистки очередей, чтобы ваши игры не занимали огромное количество памяти.

0
ответ дан sloth 16 January 2019 в 02:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: