setokinto/slack-shogi

View on GitHub
app/modules/shogi_input.py

Summary

Maintainability
B
4 hrs
Test Coverage

import uuid
import random

from app.slack_utils.user import User as UserFinder
from app.modules.shogi import Shogi as ShogiModule
from app.modules.parse_input import ParseInput
from app.validator import BasicUserValidator, AllPassUserValidator
from app.kifu import Kifu


class UserDifferentException(Exception):
    pass


class KomaCannotMoveException(Exception):
    pass


class ShogiManager:

    def __init__(self):
        self.shogi = {}

    def is_creatable(self, channel_id):
        if channel_id in self.shogi:
            return False
        else:
            return True

    def create(self, channel_id, users):
        if self.is_creatable(channel_id):
            shogi = Shogi(channel_id, users)
            self.shogi[channel_id] = shogi
            return shogi
        else:
            raise Exception()

    def get_shogi(self, channel_id):
        if channel_id in self.shogi:
            return self.shogi[channel_id]
        else:
            return None

    def is_exists(self, channel_id):
        return channel_id in self.shogi

    def clear(self, channel_id):
        if channel_id in self.shogi:
            del self.shogi[channel_id]


class ShogiInput:
    manager = ShogiManager()

    @staticmethod
    def init(channel_id, users):
        if ShogiInput.creatable_new_shogi(channel_id, users):
            shogi = ShogiInput.manager.create(channel_id, users)
            return shogi
        else:
            return None

    @staticmethod
    def creatable_new_shogi(channel_id, users):
        for user in users:
            if user["id"] is None:
                return False
        if ShogiInput.manager.is_creatable(channel_id):
            return True
        else:
            return False

    @staticmethod
    def exists(channel_id):
        return ShogiInput.manager.is_exists(channel_id)

    @staticmethod
    def koma_is_movable(channel_id, user_id, position, koma, sub_position, promote):
        # TODO: check can move with shogi module
        return True

    @staticmethod
    def clear(channel_id):
        ShogiInput.manager.clear(channel_id)

    @staticmethod
    def move(movement_str, channel_id, user_id):
        shogi = ShogiInput.manager.get_shogi(channel_id)
        if not shogi.validate(shogi, user_id):
            raise UserDifferentException()

        movement = ParseInput.parse(movement_str, shogi)
        if not movement:
            raise KomaCannotMoveException()

        from_x, from_y, to_x, to_y, promote, koma = movement

        if from_x == -1 and from_y == -1 and shogi.droppable(koma, to_x, to_y):
            shogi.drop(koma, to_x, to_y)
        elif shogi.movable(from_x, from_y, to_x, to_y, promote):
            shogi.move(from_x, from_y, to_x, to_y, promote)
        else:
            raise KomaCannotMoveException()

    @staticmethod
    def setAllMode(channel_id):
        # TODO: is it need validation?
        shogi = ShogiInput.manager.get_shogi(channel_id)
        shogi.set_validator(AllPassUserValidator())

    @staticmethod
    def basic_move(channel_id, from_x, from_y, to_x, to_y, promote):
        shogi = ShogiInput.manager.get_shogi(channel_id)
        shogi.move(from_x, from_y, to_x, to_y, promote)

    @staticmethod
    def get_shogi_board(channel_id):
        shogi = ShogiInput.manager.get_shogi(channel_id)
        if shogi is None:
            return None
        return {
            "first": shogi.first_tegoma,
            "second": shogi.second_tegoma,
            "board": shogi.board,
            "info": {
                "first": {
                    "id": shogi.first_user.id,
                    "name": shogi.first_user.name,
                },
                "second": {
                    "id": shogi.second_user.id,
                    "name": shogi.second_user.name,
                }
            },
            "turn": shogi.first,
            "_shogi": shogi,
        }

    @staticmethod
    def matta(channel_id, user_id):
        shogi = ShogiInput.manager.get_shogi(channel_id)
        if not shogi.validate(shogi, user_id):
            raise UserDifferentException()
        shogi.matta()


class ShogiUser:

    def __init__(self, user_id, user_name):
        self.id = user_id
        self.name = user_name


class Shogi:

    def __init__(self, channel_id, users, validator=BasicUserValidator()):
        self._shogi = ShogiModule()
        self.channel_id = channel_id
        self.user_ids = [x["id"] for x in users]
        random.shuffle(users)
        self.first_user = ShogiUser(users[0]["id"], users[0]["name"])
        self.second_user = ShogiUser(users[1]["id"], users[1]["name"])
        self.id = uuid.uuid4().hex
        self._validator = validator
        self.kifu = Kifu()

    def move(self, from_x, from_y, to_x, to_y, promote):
        self._shogi.move(from_x, from_y, to_x, to_y, promote)
        self.kifu.add(from_x, from_y, to_x, to_y, promote, None)

    def drop(self, koma, to_x, to_y):
        self._shogi.drop(koma, to_x, to_y)
        self.kifu.add(-1, -1, to_x, to_y, False, koma)

    def movable(self, from_x, from_y, to_x, to_y, promote):
        return self._shogi.movable(from_x, from_y, to_x, to_y, promote)

    def droppable(self, koma, to_x, to_y):
        return self._shogi.droppable(koma, to_x, to_y)

    def find_koma(self, koma):
        return self._shogi.find_koma(koma)

    def validate(self, shogi, user_id):
        return self._validator.validate(shogi, user_id)

    def set_validator(self, validator):
        self._validator = validator

    def matta(self):
        if len(self.kifu.kifu) == 0:
            raise KomaCannotMoveException
        self.kifu.pop()
        self._shogi = ShogiModule()
        for kifu in self.kifu.kifu:
            from_x, from_y, to_x, to_y, promote, koma = kifu
            if koma is None:
                self._shogi.move(from_x, from_y, to_x, to_y, promote)
            else:
                self._shogi.drop(koma, to_x, to_y)

    @property
    def first(self):
        return self._shogi.first

    @property
    def board(self):
        return self._shogi.board

    @property
    def last_move_x(self):
        return self._shogi.last_move_x

    @property
    def last_move_y(self):
        return self._shogi.last_move_y

    @property
    def first_tegoma(self):
        return self._shogi.first_tegoma

    @property
    def second_tegoma(self):
        return self._shogi.second_tegoma