Module chillow.controller.online_controller

Expand source code
import asyncio
import multiprocessing
from datetime import datetime, timezone
import requests
import websockets
from requests import RequestException

from chillow.controller.controller import Controller
from chillow.model.action import Action
from chillow.model.game import Game
from chillow.service.data_loader import DataLoader
from chillow.service.data_writer import DataWriter
from chillow.view.view import View
from chillow.service.ai import *  # noqa: F403


class OnlineController(Controller):

    def __init__(self, view: View, url: str, key: str, server_time_url: str, data_loader: DataLoader,
                 data_writer: DataWriter, ai_class: str, ai_params):
        """Creates a new online controller.

        Args:
            view: The UI that should be used.
            url: The URL of the spe_ed server.
            key: The API key.
            server_time_url: The URL to request the current time of the server.
            data_loader: Object to load data.
            data_writer: Object to write data.
            ai_class: The name of the AI class to be used.
            ai_params: The parameters of the AI.
        """

        super().__init__(view)
        self.__url = url
        self.__key = key
        self.__server_time_url = server_time_url
        self.__data_loader = data_loader
        self.__data_writer = data_writer
        self.__ai = None
        self.__default_ai = None
        self.__ai_class = ai_class
        self.__ai_params = ai_params

    def play(self):
        """See base class."""
        asyncio.get_event_loop().run_until_complete(self.__play())
        self._view.end()
        self.__ai = None
        self.__default_ai = None

    async def __play(self):
        async with websockets.connect(f"{self.__url}?key={self.__key}") as websocket:
            while True:
                game_data = await websocket.recv()
                game = self.__data_loader.load(game_data)

                self._view.update(game)

                if not game.running:
                    break

                try:
                    time_data = requests.get(self.__server_time_url).text
                    server_time = self.__data_loader.read_server_time(time_data)
                except (RequestException, ValueError):
                    server_time = datetime.now(timezone.utc)
                own_time = datetime.now(server_time.tzinfo)
                game.normalize_deadline(server_time, own_time)

                if self.__ai is None:
                    self.__ai = globals()[self.__ai_class](game.you, *self.__ai_params)
                    self.__default_ai = NotKillingItselfAI(game.you, [AIOptions.max_distance], 1, 0, 3)  # noqa: F405

                if game.you.active:
                    action = self.__choose_action(game, server_time.tzinfo)
                    data_out = self.__data_writer.write(action)
                    await websocket.send(data_out)

    def __choose_action(self, game: Game, time_zone: datetime.tzinfo) -> Action:
        return_value = multiprocessing.Value('i')
        self.__default_ai.create_next_action(game, return_value)

        own_time = datetime.now(time_zone)
        seconds_for_calculation = (game.deadline - own_time).seconds

        process = multiprocessing.Process(target=Controller.call_ai, args=(self.__ai, game, return_value,))
        process.start()
        process.join(seconds_for_calculation - 1)

        if process.is_alive():
            process.terminate()

        return Action.get_by_index(return_value.value)

Classes

class OnlineController (view: View, url: str, key: str, server_time_url: str, data_loader: DataLoader, data_writer: DataWriter, ai_class: str, ai_params)

Connects the services to execute the game logic and controls an UI.

Creates a new online controller.

Args

view
The UI that should be used.
url
The URL of the spe_ed server.
key
The API key.
server_time_url
The URL to request the current time of the server.
data_loader
Object to load data.
data_writer
Object to write data.
ai_class
The name of the AI class to be used.
ai_params
The parameters of the AI.
Expand source code
class OnlineController(Controller):

    def __init__(self, view: View, url: str, key: str, server_time_url: str, data_loader: DataLoader,
                 data_writer: DataWriter, ai_class: str, ai_params):
        """Creates a new online controller.

        Args:
            view: The UI that should be used.
            url: The URL of the spe_ed server.
            key: The API key.
            server_time_url: The URL to request the current time of the server.
            data_loader: Object to load data.
            data_writer: Object to write data.
            ai_class: The name of the AI class to be used.
            ai_params: The parameters of the AI.
        """

        super().__init__(view)
        self.__url = url
        self.__key = key
        self.__server_time_url = server_time_url
        self.__data_loader = data_loader
        self.__data_writer = data_writer
        self.__ai = None
        self.__default_ai = None
        self.__ai_class = ai_class
        self.__ai_params = ai_params

    def play(self):
        """See base class."""
        asyncio.get_event_loop().run_until_complete(self.__play())
        self._view.end()
        self.__ai = None
        self.__default_ai = None

    async def __play(self):
        async with websockets.connect(f"{self.__url}?key={self.__key}") as websocket:
            while True:
                game_data = await websocket.recv()
                game = self.__data_loader.load(game_data)

                self._view.update(game)

                if not game.running:
                    break

                try:
                    time_data = requests.get(self.__server_time_url).text
                    server_time = self.__data_loader.read_server_time(time_data)
                except (RequestException, ValueError):
                    server_time = datetime.now(timezone.utc)
                own_time = datetime.now(server_time.tzinfo)
                game.normalize_deadline(server_time, own_time)

                if self.__ai is None:
                    self.__ai = globals()[self.__ai_class](game.you, *self.__ai_params)
                    self.__default_ai = NotKillingItselfAI(game.you, [AIOptions.max_distance], 1, 0, 3)  # noqa: F405

                if game.you.active:
                    action = self.__choose_action(game, server_time.tzinfo)
                    data_out = self.__data_writer.write(action)
                    await websocket.send(data_out)

    def __choose_action(self, game: Game, time_zone: datetime.tzinfo) -> Action:
        return_value = multiprocessing.Value('i')
        self.__default_ai.create_next_action(game, return_value)

        own_time = datetime.now(time_zone)
        seconds_for_calculation = (game.deadline - own_time).seconds

        process = multiprocessing.Process(target=Controller.call_ai, args=(self.__ai, game, return_value,))
        process.start()
        process.join(seconds_for_calculation - 1)

        if process.is_alive():
            process.terminate()

        return Action.get_by_index(return_value.value)

Ancestors

Methods

def play(self)

See base class.

Expand source code
def play(self):
    """See base class."""
    asyncio.get_event_loop().run_until_complete(self.__play())
    self._view.end()
    self.__ai = None
    self.__default_ai = None