コード例 #1
0
ファイル: game_manager.py プロジェクト: peopzen/ticher
class GameManager():
    players = None
    lead_player = None
    present_trick = None
    game_over = None

    wish_for_power = None

    def __init__(self, players=None):
        self.game_over = False
        cards = Deck()
        divided_cards = cards.split_equally(4)
        if players is None:
            players = [DumbAI(hand=Hand(cards_list=hand), name='Player %s' % index) for index, hand in enumerate(divided_cards)]
        if all(player.hand is None for player in players):
            for idx, player in enumerate(players):
                player.hand = divided_cards[idx]

        self.players = players
        for player in self.players:
            player.create_players([p.name for p in self.players])

        print(*['%s - %s' % (player.name, player.hand) for player in self.players], sep='\n')

        self.present_trick = Trick(len(self.get_player_still_game()))

    def check_if_game_finished(self):
        number_still_in = sum([1 if not x.is_out() else 0 for x in self.players])
        if number_still_in <= 1:
            self.game_over = False
        else:
            self.game_over = True

    def is_game_over(self):
        if self.game_over:
            return True

        # CHECK if both people from same team are out
        self.game_over = sum([1 if not x.is_out() else 0 for x in self.players]) <= 1
        return self.game_over

    def run_game(self):

        # Until 3 players are out
        # Game
        self.exchange_cards()

        for player in self.players:
            if Mahjong() in player.hand.cards:
                self.lead_player = player
                break

        #TODO How to decide lead player
        else:
            self.lead_player = self.players[0]

        while not self.is_game_over():
            print('',
                  '### new trick with players %s' %
                  (', '.join(['%s (%spts)' % (player.name, player.points) for player in self.get_player_still_game()])),
                  '### leading player is %s' % self.lead_player,
                  '### trick - %s' % self.present_trick,
                  '',
                  sep='\n')
            # Take action from the lead player
            self.get_player_action(self.lead_player)

            # Check if game is over
            if self.is_game_over():
                continue
            self.check_for_bomb()

            # Initialize the rolling cycle of player
            player_iterator = self.next_active_player()

            if self.lead_player.is_out():
                self.lead_player = next(player_iterator)
                continue

            active_player = next(player_iterator)

            # Until somebody wins the hand
            while not self.present_trick.is_over() and not self.is_game_over():
                player_action = self.get_player_action(active_player)

                if not player_action.has_passed():

                    # Check if game is over
                    if self.is_game_over():
                        break

                    self.check_for_bomb()

                    if self.lead_player.is_out():
                        self.lead_player = next(player_iterator)

                self.check_for_bomb()
                active_player = next(player_iterator)

            self.end_of_trick()

    def publish_action_to_players(self, action, starting=False):
        for player in self.players:
            player.publish_action(action, starting)

    def end_of_trick(self):

        last_play = self.present_trick.get_last_play()
        trick_owner = self.present_trick.get_last_player()

        if Dragon() in last_play.combination.cards:
            trick_owner_name = trick_owner.get_player_to_pass_dragon_to()
            print('Trick is given to %s' % trick_owner_name)
        else:
            trick_owner_name = trick_owner.name

        # notify all players
        for player in self.players:
                player.end_of_trick(trick_owner_name=trick_owner_name, trick=self.present_trick)

        self.present_trick.reset()

    # Returns a cyclic iterators on players still n starting after the start player
    def next_active_player(self):
        # should always be 4
        cycle = itertools.cycle(self.players)
        player = next(cycle)

        while player != self.lead_player:
            player = next(cycle)

        while True:
            player = next(cycle)
            if not player.is_out():
                yield player

    def get_player_action(self, player=None, bomb=False):
        if player is None:
            player = self.lead_player

        # Check for any bomb here
        if bomb:
            player_action = player.bomb(self.present_trick)
            # Do not update anything for bomb
            if player_action is None:
                return
        else:
            player_action = player.play(self.present_trick, self.wish_for_power)

        print(player_action)
        # Ensure action is valid
        try:
            self.assert_valid_action(player, player_action)

            if player_action.wish is not None:
                self.wish_for_power = player_action.wish

            # TODO - TEMP
            if player.is_out():
                print('=====> %s is out' % player)

            self.present_trick.update(action=player_action, out=player.is_out())
            self.publish_action_to_players(action=player_action,
                                           starting=self.present_trick.is_first_run())

            if not player_action.has_passed():
                self.lead_player = player

                if Dog() in player_action.combination.cards:
                    self.lead_player = self.get_player(player.get_partner().name)
                    self.end_of_trick()

            return player_action

        except Exception as e:
            print('AI Error - %s' % e)
            # If not valid, publish an error message to the player
            player.misplay(player_action, self.present_trick)
            raise ValueError(e)

    # FOR THE MOMENT ONLY check for bomb in the order
    def check_for_bomb(self):
        for player in self.get_player_still_game():
            # Assuming that people would not terminate on a Bomb
            self.get_player_action(player, bomb=True)

    def assert_valid_action(self, player, action):
        if self.present_trick.is_first_run() and action.has_passed() and not player.is_out():
            raise GameManagerException('Cannot pass on an empty trick')

        if self.present_trick is not None and not action.has_passed():
            # Make sure action is valid
            action.assert_valid()
            last_play = self.present_trick.get_last_play()

            if last_play:
                if last_play.combination >= action.combination:
                    raise GameManagerException('Combination should be greater than last played')

        if self.wish_for_power is not None:
            if self.wish_for_power in [card.power for card in action.combination.cards]:
                self.wish_for_power = None

            # TODO assert for combination not possible
            elif self.wish_for_power in [card.power for card in player.hand.cards]:
                raise GameManagerException('Wish should be fulfilled')

    def get_player_still_game(self):
        return [player for player in self.players if not player.is_out()]

    def exchange_cards(self):
        # Get all cards
        exchanged_cards = {}
        for player in self.players:
            exchanged_cards[player.name] = player.give_cards()

            print('%s gives %s' % (player.name, exchanged_cards[player.name]))

        for from_player, gifts in exchanged_cards.items():
            for player_to_give, card in gifts.items():
                self.get_player(player_to_give).received_card(from_player, card)

    def get_player(self, player_name):
        for player in self.players:
            if player.name == player_name:
                return player