import random from typing import Optional from swiftstory.exception import WrongAction, JoinError from swiftstory.player import Player from swiftstory.board import Board from swiftstory.status import error, success class Game: WAITING_NEW_JUDGE = 0 WAITING_COLLECTION = 1 WAITING_DESIGNATION = 2 def __init__(self, white_desc, black_desc): white_pick = list(enumerate(white_desc)) black_pick = list(enumerate(black_desc)) self.state = self.WAITING_NEW_JUDGE self.players = [] self.judge: Optional[Player] = None self.board = Board() self.board.white_pick = white_pick self.board.black_pick = black_pick random.shuffle(self.board.white_pick) random.shuffle(self.board.black_pick) def try_join(self, client): if len(self.players) >= 10: raise JoinError('too many players in this game') cards = [] try: for _ in range(10): cards.append(self.board.pick_white_card()) except IndexError: raise JoinError('not enough white cards for player') player = Player() for card in cards: player.receive_card(card) client.player = player client.game = self client.monitor_player() self.players.append(player) for p in self.players: if p is not player: p.register_notification({'op': 'player_joined_game'}) cards = [(idx, desc) for idx, (_, desc) in player.cards.items()] if self.state is self.WAITING_NEW_JUDGE: state = 'waiting_judge' elif self.state is self.WAITING_COLLECTION: state = 'waiting_collection' else: state = 'waiting_designation' return success({'cards': cards, 'game_state': state}) def try_become_judge(self, player): if self.state is not self.WAITING_NEW_JUDGE: # TODO what if the judge has quit ? raise WrongAction('Someone is judge already') self.judge = player self.board.reveal_next_black_card() self.state = self.WAITING_COLLECTION for p in self.players: if p is not player: p.register_notification({'op': 'judge_designed'}) return self.try_view_black_card(player) def try_play_card(self, player, card_id): if self.state is not self.WAITING_COLLECTION: raise WrongAction('Who asked you to play now ?!') if self.judge is player: raise WrongAction('You\'re the judge, you silly') elif player.has_played: raise WrongAction('You already played, you dumb ass') try: card = player.pop_card(card_id) except IndexError: raise WrongAction('Invalid card id') player.has_played = True self.board.play_card(player, card) if self.judge is None: raise ValueError("There is no judge") self.judge.register_notification({'op': 'card_played'}) return success({'card_id': card_id}) def try_collect_cards(self, player): if self.state is not self.WAITING_COLLECTION: raise WrongAction("Do you think it's the moment for collection !?") if self.judge is not player: raise WrongAction('You\'re not the judge, you fool!') self.board.shuffle_played_cards() # we prevent the others to play self.state = self.WAITING_DESIGNATION for p in self.players: if p is not player: p.register_notification({'op': 'cards_collected'}) return self.try_view_played_cards(player) def try_designate_card(self, player, card_id): if self.state is not self.WAITING_DESIGNATION: raise WrongAction('Not now, moron !') if self.judge is not player: raise WrongAction('Who do you think you are !?') if card_id is None and len(self.board.played_cards) > 0: raise WrongAction('There are cards on the board, pick one !') if card_id is not None or len(self.board.played_cards) > 0: # if there are cards on the board # TODO check exception try: card, winner = self.board.played_cards[card_id] except IndexError: return error('Invalid card') winner.inc_score() # put the cards back in the deck self.board.recycle_played_cards() # reset the state of the players for p in self.players: if p.has_played: idx = p.receive_card(self.board.pick_white_card()) p.register_notification({ 'op': 'received_card', 'content': { 'card': { 'id': idx, 'desc': p.cards[idx][1], }, }, }) p.has_played = False self.judge = None for p in self.players: if p is not player: p.register_notification({'op': 'judge_needed'}) self.state = self.WAITING_NEW_JUDGE return success(None) def try_view_player_cards(self, player): return success([(idx, desc) for idx, (_, desc) in player.cards.items()]) def try_view_played_cards(self, player): if self.state is not self.WAITING_DESIGNATION: raise WrongAction('Not now, moron !') return success([desc for (_, desc), _ in self.board.played_cards]) def try_view_black_card(self, player): card = self.board.current_black_card if card is not None: return success(card[1]) raise WrongAction('The black card has not been revealed yet') def disconnect(self, player): if self.judge is player: self.judge = None for p in self.players: p.register_notification({'op': 'judge_needed'}) for card, p in self.board.played_cards: p.receive_card(card) p.register_notification({ 'op': 'received_card', 'content': { 'card': { 'id': card[0], 'desc': card[1], }, }, }) p.has_played = False self.board.played_cards = [] self.state = self.WAITING_NEW_JUDGE