Exemple #1
0
    def __init__(self, playersName, maxScore=100):

        self.maxScore = maxScore

        self.roundNum = 0
        self.deck = Deck()
        self.trickNum = 0  # initialization value such that first round is round 0
        self.dealer = -1  # so that first dealer is 0
        self.passes = [1, -1, 2, 0]  # left, right, across, no pass
        self.currentTrick = Trick()
        self.trickWinner = -1

        # Make four players
        self.players = [
            Player(playersName[0]),
            Player(playersName[1]),
            Player(playersName[2]),
            Player(playersName[3])
        ]

        # Make two teams
        self.teams = [
            Team(0, self.players[0], self.players[2]),
            Team(1, self.players[1], self.players[3])
        ]

        # Linking players to a team
        self.players[0].team = self.teams[0]
        self.players[2].team = self.teams[0]
        self.players[1].team = self.teams[1]
        self.players[3].team = self.teams[1]

        # Linking players to their teammate
        self.players[0].teammate = self.players[2]
        self.players[2].teammate = self.players[0]
        self.players[1].teammate = self.players[3]
        self.players[3].teammate = self.players[1]
        '''
        Player physical locations:
        Game runs clockwise

             p3
        p2        p4
             p1

        '''

        self.event = None
        self.round = 0

        self.renderInfo = {'printFlag': False, 'Msg': ""}
Exemple #2
0
    def play_reset(self):
        """
        Function called in the reset function. Called at each new round. Once all the important values are reset, and
        given the trick id (between 0 and 7) we define the first player of the first trick using np.roll.

        :return:
        """
        # Select color and value
        self.round_number += 1

        self.atout_suit = self.suits_order[random.randint(
            0, 3)]  # select randomly the suit
        self.value = random.randint(
            0, 1)  # Can only announce 80 or 90 to begin with
        self.attacker_team = random.randint(
            0,
            1)  # 0 if it is team 0 (player 0 and player 2) else 1 for team 1
        self.suits_order = self._define_suits_order(self.suits_order)

        # We rebuild the deck based on previous trick won by each players
        self._rebuild_deck()
        self.played_tricks = []

        for p in self.players:
            if self.attacker_team == 0:
                if p.index % 2 == 0:
                    p.attacker = 1
                else:
                    p.attacker = 0
            elif self.attacker_team == 1:
                if p.index % 2 == 1:
                    p.attacker = 1
                else:
                    p.attacker = 0
        #  -----
        for p in self.players:
            p.resetRound()
            p.discardTricks()

        # -----
        self._deal_cards()

        self.trick = Trick(self.atout_suit, len(self.played_tricks) + 1)
        self.current_trick_rotation = self._create_trick_rotation(
            self.round_number % 4)
        self._play_until_end_of_rotation_or_ai_play()
Exemple #3
0
    def _event_ShowTrickEnd(self):

        cards = []
        for card in self.currentTrick.trick:
            cards += [str(card)]

        self.event_data_for_client \
            = {"event_name": self.event,
               "broadcast": True,
               "data": {
                   'trickNum': self.trickNum + 1,
                   'trickWinner': self.players[self.trickWinner].name,
                   'cards': cards,
                   'contrat': self.contrat,
                   'suit': self.atout_suit
               }
               }

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Trick {0} ***\n'.format(self.trickNum +
                                                                1)
        self.renderInfo['Msg'] += 'Winner: {0}\n'.format(
            self.players[self.trickWinner].name)
        self.renderInfo['Msg'] += 'ValueTrick: {0}\n'.format(
            self._countTrickValue())

        for i in range(2):
            self.renderInfo['Msg'] += '{}:{}\n'.format(self.teams[i].name,
                                                       self.teams[i].score)

        self.renderInfo['Msg'] += 'Winning team: {0}\n'.format(
            self.players[self.trickWinner].team.name)
        self.renderInfo['Msg'] += 'cards: {0}\n'.format(cards)

        self.renderInfo['Msg'] += 'AtoutIs: {0}\n'.format(
            Suit(self.atout_suit).string)

        self.currentTrick = Trick()

        self.trickNum += 1
        if self.trickNum < 8:
            self.event = 'PlayTrick'
            self.event_data_for_server = {'shift': 0}
        else:
            self.event = 'RoundEnd'
            self.event_data_for_server = {}
Exemple #4
0
    def _event_NewRound(self):

        self.round += 1
        if self.roundNum == 0:
            self.deck.shuffle()
        self.roundNum += 1
        self.trickNum = 0
        self.trickWinner = -1
        self.dealer = (self.dealer + 1) % len(self.players)
        for p in self.players:
            p.resetRound()
            p.discardTricks()

        self._dealCards(self.dealer)
        self.currentTrick = Trick()

        self.atout_suit = -1
        self.contrat = 0
        self.leadingTeam = None

        for t in self.teams:
            t.score = 0

        self.event_data_for_client \
            = {'event_name': self.event
            , 'broadcast': True
            , 'data': {
                "Teams": [
                    {'teamName': self.teams[0].name,
                     'score': self.teams[0].score},
                    {'teamName': self.teams[1].name,
                     'score': self.teams[1].score},
                ]
            }
               }

        self.event = 'ShowPlayerHand'
        self.event_data_for_server = {'now_player_index': 0}

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Start Round {0} ***\n'.format(
            self.round)
        for p in self.players:
            self.renderInfo['Msg'] += '{0}: {1}\n'.format(p.name, p.score)
            self.renderInfo['Msg'] += 'Leading team: {0}\n'.format(
                self.leadingTeam)
Exemple #5
0
    def play_step(self, action):
        # Play until end of trick
        self._play_until_end_of_rotation_or_ai_play()

        # Handle end of trick
        winner = self.trick.winner
        # add score to teams
        self.played_tricks.append(self.trick)

        # should stop if trick_number==8
        if len(self.played_tricks) == 8:
            return True

        self.trick = Trick(self.atout_suit, len(self.played_tricks) + 1)
        # Choose next starter
        self.current_trick_rotation = self._create_trick_rotation(winner)
        # Play until AI
        self._play_until_end_of_rotation_or_ai_play()
        return False
Exemple #6
0
class GymCoinche(Env):
    def __init__(self):
        # observation_space
        # 32 played cards + 32 player cards + 32 cards of current trick + contract_value + attacker
        # 8 atouts + 8 suit 1 + 8 suit 2 + 8 suit 3
        # RL Coach observation_space has to be a Box
        self.observation_space = spaces.Box(low=0, high=1, shape=(98, ))
        # 32 cards
        # 8 atouts + 8 suit 1 + 8 suit 2 + 8 suit 3
        self.action_space = spaces.Box(low=0, high=1, shape=(32, ))

        self.players = [
            RandomPlayer(0, "N"),
            RandomPlayer(1, "E"),
            AIPlayer(2, "S"),
            RandomPlayer(3, "W")
        ]
        self.current_trick_rotation = []
        self.deck = Deck()
        self.round_number = 0
        self.played_tricks = []
        self.trick = None
        self.atout_suit = None
        self.value = None
        # TODO: select randomly the attacker_team in {0,1}
        self.attacker_team = 0

        # TODO: refacto
        self.suits_order = [HEART, SPADE, DIAMOND, CLUB]

    def reset(self):
        """
        reset is mandatory to use gym framework. Reset is called at the end of each round (8 tricks)
        :return: observation
        """
        self.play_reset()
        observation = self._get_trick_observation()
        return observation

    def step(self, action):
        """
        step is mandatory to use gym framework
        In each step, every player play exactly one, especially the AIPlayers.
        :param action:
        :return: observation, reward, done, info
        """
        done = self.play_step(action)
        if not done:
            observation = self._get_trick_observation()
            current_player = self.current_trick_rotation[0]
            last_trick = self.played_tricks[-1]
            reward = self._get_trick_reward(last_trick, current_player)
            winning_team = 0 if last_trick.winner % 2 == 0 else 1
            info = {'winner': last_trick.winner, 'winning_team': winning_team}
            return observation, reward, done, info
        else:
            observation = self._get_round_observation()
            # TODO: define round reward
            reward = self._get_round_reward()
            info = {}
            return observation, reward, done, info

    def _get_trick_reward(self, trick, player):
        score = trick.score()
        score_factor = player.index % 2 == trick.winner % 2
        return score * score_factor

    def _get_round_reward(self):
        return 1

    def play_reset(self):
        """
        Function called in the reset function. Called at each new round. Once all the important values are reset, and
        given the trick id (between 0 and 7) we define the first player of the first trick using np.roll.

        :return:
        """
        # Select color and value
        self.round_number += 1

        self.atout_suit = self.suits_order[random.randint(
            0, 3)]  # select randomly the suit
        self.value = random.randint(
            0, 1)  # Can only announce 80 or 90 to begin with
        self.attacker_team = random.randint(
            0,
            1)  # 0 if it is team 0 (player 0 and player 2) else 1 for team 1
        self.suits_order = self._define_suits_order(self.suits_order)

        # We rebuild the deck based on previous trick won by each players
        self._rebuild_deck()
        self.played_tricks = []

        for p in self.players:
            if self.attacker_team == 0:
                if p.index % 2 == 0:
                    p.attacker = 1
                else:
                    p.attacker = 0
            elif self.attacker_team == 1:
                if p.index % 2 == 1:
                    p.attacker = 1
                else:
                    p.attacker = 0
        #  -----
        for p in self.players:
            p.resetRound()
            p.discardTricks()

        # -----
        self._deal_cards()

        self.trick = Trick(self.atout_suit, len(self.played_tricks) + 1)
        self.current_trick_rotation = self._create_trick_rotation(
            self.round_number % 4)
        self._play_until_end_of_rotation_or_ai_play()

    def _rebuild_deck(self):
        # Ordering previous played tricks by players

        # Check if duplicated cards
        for p in self.players:
            for trick in self.played_tricks:
                if p.index == trick.winner:
                    self.deck.addTrick(trick)
        self.deck.cut_deck()
        self.deck = Deck()

    def _deal_cards(self):
        """
        This function deals the deck
        """
        players_round = np.roll(self.players, self.round_number)
        legalDealingSequences = [[3, 3, 2],
                                 [3, 2,
                                  3]]  # Defining academic dealing sequences
        dealingSequence = legalDealingSequences[random.randint(
            0, 1)]  # Choose the Dealing Sequence
        for cardsToDeal in dealingSequence:
            for p in players_round:  # Stopping condition on one round
                p.addCards(self.deck.deal(cardsToDeal))

    def play_step(self, action):
        # Play until end of trick
        self._play_until_end_of_rotation_or_ai_play()

        # Handle end of trick
        winner = self.trick.winner
        # add score to teams
        self.played_tricks.append(self.trick)

        # should stop if trick_number==8
        if len(self.played_tricks) == 8:
            return True

        self.trick = Trick(self.atout_suit, len(self.played_tricks) + 1)
        # Choose next starter
        self.current_trick_rotation = self._create_trick_rotation(winner)
        # Play until AI
        self._play_until_end_of_rotation_or_ai_play()
        return False

    def play_ai(self, action):
        current_player = self.current_trick_rotation[0]
        player_cards = current_player.hand.all_cards()
        player_cards_observation = self._create_cards_observation(player_cards)
        player_action_masked = player_cards_observation * action

        # Play cards in probability order
        if np.max(player_action_masked) > 0:
            cards_index = np.argsort(-player_action_masked)
        else:
            cards_index = np.argsort(-player_cards_observation)
        for card_index in cards_index:
            try:
                card_rank = (card_index % 8) + 7
                card_suit = int(card_index / 8)
                card = Card(card_rank, self.suits_order[card_suit].iden)
                # TODO: play_turn
                self.trick.addCard(card, current_player.hand,
                                   current_player.index)
                # print(card_index, card_suit,self.suits_order[card_suit].string, card_rank, self.atout_suit)
                #
                # print([(x.rank.rank, x.suit.string) for x in player_cards])
                current_player.removeCard(card)
                self.current_trick_rotation.pop(0)
                return True
            except PlayException:
                continue
        return False

    def _create_trick_rotation(self, starting_player_index):
        rotation = np.array(self.players)
        while rotation[0].index != starting_player_index:
            rotation = np.roll(rotation, 1)
        return rotation.tolist()

    def _get_trick_observation(self):
        # self.observation_space = [spaces.Discrete(2)] * (32 + 32 + 32) + [spaces.Discrete(10), spaces.Discrete(2)]
        played_cards = [
            card for trick in self.played_tricks for card in trick.trick
        ]
        played_cards_observation = self._create_cards_observation(played_cards)
        current_player = self.current_trick_rotation[0]
        player_cards = [
            card for suits in current_player.hand.hand for card in suits
        ]
        player_cards_observation = self._create_cards_observation(player_cards)
        trick_cards = self.trick.trick
        trick_cards_observation = self._create_cards_observation(trick_cards)
        attacker = current_player.attacker
        contract_value = self.value / 9
        observation = np.concatenate(
            (played_cards_observation, player_cards_observation,
             trick_cards_observation, [contract_value, attacker]))
        return observation

    def _get_round_observation(self):
        # self.observation_space = [spaces.Discrete(2)] * (32 + 32 + 32) + [spaces.Discrete(10), spaces.Discrete(2)]
        played_cards_observation = np.ones(32)
        player_cards_observation = np.zeros(32)
        trick_cards = self.trick.trick
        trick_cards_observation = self._create_cards_observation(trick_cards)
        # TODO: to do
        attacker = 1
        contract_value = self.value / 9
        observation = np.concatenate(
            (played_cards_observation, player_cards_observation,
             trick_cards_observation, [contract_value, attacker]))
        return observation

    def _create_cards_observation(self, cards):
        cards_observation = np.zeros(32)

        for card in cards:
            if card.suit.iden == -1:
                continue
            suit_position = self.suits_order.index(card.suit)
            rank_position = card.rank.rank
            card_position = (rank_position - 7) + (suit_position * 8)
            np.put(cards_observation, card_position, 1)
        return cards_observation

    def _play_until_end_of_rotation_or_ai_play(self):
        """
        When this method is called it is either:
        - An AIPlayer's turn: therefore we break and ask the AIPlayer to give prediction
        - Not an AIPlayer: therefore the current player just play given is deterministic (or random) policy

        :return:
        """
        while len(self.current_trick_rotation) > 0:
            current_player = self.current_trick_rotation[0]
            if isinstance(current_player, AIPlayer):
                break

            # Ask not ai player to player (could be random)
            if not isinstance(current_player, AIPlayer):
                while True:
                    # TODO: dev a deterministic player
                    try:
                        # TODO: play_turn
                        card = current_player.getRandom()
                        self.trick.addCard(card, current_player.hand,
                                           current_player.index)
                        current_player.removeCard(card)
                        self.current_trick_rotation.pop(0)
                        break
                    except PlayException as e:

                        continue

    def _define_suits_order(self, suits_order):
        tmp_suits_order = np.array(suits_order)
        while True:
            if tmp_suits_order[0] == self.atout_suit:
                break
            tmp_suits_order = np.roll(tmp_suits_order, 1)
        return tmp_suits_order.tolist()
Exemple #7
0
class CoincheEnv(Env):
    def __init__(self, playersName, maxScore=100):

        self.maxScore = maxScore

        self.roundNum = 0
        self.deck = Deck()
        self.trickNum = 0  # initialization value such that first round is round 0
        self.dealer = -1  # so that first dealer is 0
        self.passes = [1, -1, 2, 0]  # left, right, across, no pass
        self.currentTrick = Trick()
        self.trickWinner = -1

        # Make four players
        self.players = [
            Player(playersName[0]),
            Player(playersName[1]),
            Player(playersName[2]),
            Player(playersName[3])
        ]

        # Make two teams
        self.teams = [
            Team(0, self.players[0], self.players[2]),
            Team(1, self.players[1], self.players[3])
        ]

        # Linking players to a team
        self.players[0].team = self.teams[0]
        self.players[2].team = self.teams[0]
        self.players[1].team = self.teams[1]
        self.players[3].team = self.teams[1]

        # Linking players to their teammate
        self.players[0].teammate = self.players[2]
        self.players[2].teammate = self.players[0]
        self.players[1].teammate = self.players[3]
        self.players[3].teammate = self.players[1]
        '''
        Player physical locations:
        Game runs clockwise

             p3
        p2        p4
             p1

        '''

        self.event = None
        self.round = 0

        self.renderInfo = {'printFlag': False, 'Msg': ""}

    def _countTrickValue(self):
        """
        This function computes the value of the trick given if the cards are atout cards or not
        ex:
        if atout_suit is heart and the trick is [Ad, Td, 9h, 7d]:
        --> the trickValue is 11(Ad) + 10(Td) + 14(9h) + 0(7d) = 35 for player "Sud"

        There is also the 10 de der that gives ten extra points if you win last trick (7th trick)
        """
        trickValue = 0
        for card in self.currentTrick.trick:

            if card != Card(0, -1):
                if card.suit.iden == self.atout_suit:
                    trickValue += atout_values[card.rank.rank]
                else:

                    trickValue += generic_values[card.rank.rank]
        # 10 de der
        if self.trickNum == 7:
            trickValue += 10
        return trickValue

    @classmethod
    def _handsToStrList(self, hands):
        """
        This function helps to print a hand of a player
        """
        output = []
        for card in hands:
            output += [str(card)]
        return output

    def _dealCards(self, dealer):
        """
        This function deals the deck
        TODO: deal card the coinche way !!!
        """
        i = dealer + 1  # Dealer doesn't deal himself first
        #         while(self.deck.size() > 0):
        #             self.players[i % len(self.players)].addCard(self.deck.deal())
        #             i += 1
        '''3-3-2 Dealing'''
        legalDealingSequences = [[3, 3, 2],
                                 [3, 2,
                                  3]]  # Defining academic dealing sequences
        dealingSequence = legalDealingSequences[random.randint(
            0, 1)]  # Flipping a coin to choose the Dealing Sequence
        for cardsToDeal in dealingSequence:
            print("Taille du paquet: ", self.deck.size())
            deck_size = self.deck.size(
            )  # Size of the deck before i-th round of dealing
            while (self.deck.size() > deck_size -
                   4 * cardsToDeal):  # Stopping condition on one round
                self.players[i % len(self.players)].addCards(
                    self.deck.deal(cardsToDeal))
                i += 1

    def _evaluateTrick(self):
        self.trickWinner = self.currentTrick.winner
        p = self.players[self.trickWinner]
        p.trickWon(self.currentTrick.trick)

    # print player's hand
    def _printPlayer(self, i):
        p = self.players[i]
        print(p.name + "'s hand: " + str(p.hand))

    # print all players' hands
    def _printPlayers(self):
        for p in self.players:
            print(p.name + ": " + str(p.hand))

    # show cards played in current trick
    def _printCurrentTrick(self):
        trickStr = '\nCurrent table:\n'
        trickStr += "Atout suit of the round: " + Suit(
            self.atout_suit).__str__() + "\n"
        trickStr += "Trick suit: " + self.currentTrick.suit.__str__() + "\n"
        for i, card in enumerate(self.currentTrick.trick):
            if self.currentTrick.trick[i] is not 0:
                trickStr += self.players[i].name + ": " + str(card) + "\n"
            else:
                trickStr += self.players[i].name + ": None\n"

        return trickStr

    def _getCurrentTrickStrList(self):
        trick_list = []
        for i, card in enumerate(self.currentTrick.trick):
            if self.currentTrick.trick[i] is not 0:
                trick_list += [{
                    'playerName': self.players[i].name,
                    'card': str(card)
                }]

        return trick_list

    def _event_GameStart(self):
        self.event_data_for_server = {}
        self.event_data_for_client \
            = {'event_name': self.event
            , 'broadcast': True
            , 'data': {
                "players": [
                    {'playerName': self.players[0].name},
                    {'playerName': self.players[1].name},
                    {'playerName': self.players[2].name},
                    {'playerName': self.players[3].name}
                ]
            }
               }

        for p in self.players:
            p.score = 0
        self.round = 0

        self.renderInfo = {'printFlag': False, 'Msg': ""}
        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Hearts Start ***\n'

    def _event_NewRound(self):

        self.round += 1
        if self.roundNum == 0:
            self.deck.shuffle()
        self.roundNum += 1
        self.trickNum = 0
        self.trickWinner = -1
        self.dealer = (self.dealer + 1) % len(self.players)
        for p in self.players:
            p.resetRound()
            p.discardTricks()

        self._dealCards(self.dealer)
        self.currentTrick = Trick()

        self.atout_suit = -1
        self.contrat = 0
        self.leadingTeam = None

        for t in self.teams:
            t.score = 0

        self.event_data_for_client \
            = {'event_name': self.event
            , 'broadcast': True
            , 'data': {
                "Teams": [
                    {'teamName': self.teams[0].name,
                     'score': self.teams[0].score},
                    {'teamName': self.teams[1].name,
                     'score': self.teams[1].score},
                ]
            }
               }

        self.event = 'ShowPlayerHand'
        self.event_data_for_server = {'now_player_index': 0}

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Start Round {0} ***\n'.format(
            self.round)
        for p in self.players:
            self.renderInfo['Msg'] += '{0}: {1}\n'.format(p.name, p.score)
            self.renderInfo['Msg'] += 'Leading team: {0}\n'.format(
                self.leadingTeam)

    def _event_ShowPlayerHand(self):

        if self.event_data_for_server['now_player_index'] < 4:
            now_player_index = self.event_data_for_server['now_player_index']
            self.event_data_for_client \
                = {"event_name": self.event,
                   "broadcast": False,
                   "data": {
                       'playerName': self.players[now_player_index].name,
                       'hand': self._handsToStrList(sum(self.players[now_player_index].hand.hand, []))
                   }
                   }
            self.event_data_for_server['now_player_index'] += 1

        else:
            self.event = 'ChooseContrat'
            self.event_data_for_server = {'shift': 0}
            self.numTurnOfAnnounce = 0

    def _event_ChooseContrat(self):
        shift = self.event_data_for_server['shift']

        if shift == 0 and self.numTurnOfAnnounce == 0:
            self.playerToStartAnnounce = (self.dealer + 1) % 4
            self.current_player = self.players[self.playerToStartAnnounce]
        else:
            self.current_player_i = (self.playerToStartAnnounce + shift) % 4
            self.current_player = self.players[self.current_player_i]

        self.event_data_for_client \
            = {"event_name": self.event,
               "broadcast": False,
               "data": {
                   'playerName': self.current_player.name,
                   'hand': self._handsToStrList(sum(self.current_player.hand.hand, [])),
                   'contrat': self.contrat,
                   'suit': self.atout_suit,
                   'shift': shift}
               }

    def _assert_new_contrat(self, actionData):
        proposed_contrat = actionData["data"]["action"]["value"]
        is_bidding = actionData["data"]["action"]["newContrat"]
        if not is_bidding:
            # The player is not making any announce
            return True
        else:
            if proposed_contrat % 10:
                self.announce_error = "Contrat value must be a multiple of 10"
                return False
            if proposed_contrat < 80:
                self.announce_error = "Contrat value must be greater than 80"
                return False
            if proposed_contrat <= self.contrat:
                self.announce_error = "Contrat must be greater than current contrat"
                return False
            # 180 if there is "Belote&Rebelote"
            if proposed_contrat > 180 and proposed_contrat < 250:
                self.announce_error = "Current contrat value doesn't exist. Announce capo (250) or max 180 !"
                return False
            else:
                return True

    def _event_ChooseContratAction(self, actionData):

        shift = self.event_data_for_server['shift']
        player_must_announce_again = False
        if shift == 0 and self.numTurnOfAnnounce == 0:
            self.playerToStartAnnounce = (self.dealer + 1) % 4
            self.current_player_i = self.playerToStartAnnounce
            self.current_player = self.players[self.playerToStartAnnounce]

        else:
            self.current_player_i = (self.playerToStartAnnounce + shift) % 4
            self.current_player = self.players[self.current_player_i]

        # If new contrat proposed isn't valid
        if not self._assert_new_contrat(actionData):
            print(self.announce_error)
            player_must_announce_again = True
            actionData["data"]["action"]["value"] = None
            actionData["data"]["action"]["suit"] = None
            actionData["newContrat"] = False
            self.event = "ChooseContrat"
            self._event_ChooseContrat()

        else:
            self.contrat = actionData["data"]["action"]["value"]
            self.atout_suit = actionData["data"]["action"]["suit"]
        # If the player as passed or made a valid announce
        if not player_must_announce_again:
            # if the player made a valid announce
            if actionData["data"]["action"]["newContrat"]:
                # if there is a modification of the previous contrat, new turn of announce starting after the current player
                self.playerToStartAnnounce = self.current_player_i
                self.numTurnOfAnnounce = 1
                self.event_data_for_server['shift'] = 0
                self.leadingTeam = self.current_player.team.teamNumber

            if self.event_data_for_server['shift'] < 3:

                self.event_data_for_server['shift'] += 1
                self.event = "ChooseContrat"
                self._event_ChooseContrat()

            else:

                self.event_data_for_server['shift'] += 1
                if self.contrat == 0 and self.atout_suit == -1:
                    self.renderInfo['printFlag'] = True

                    self.renderInfo[
                        'Msg'] += "everybody passed - New round is comming"

                    self.event = 'RoundEnd'
                    self._event_ChooseContrat()
                    self.event_data_for_server = {}

                # if everyone had the opportunity to announce, let's play !
                else:
                    self.event = 'PlayTrick'
                    self.renderInfo['Msg'] += 'Leading team: {0}\n'.format(
                        self.leadingTeam)
                    self._event_PlayTrick()

    def _event_PlayTrick(self):

        shift = self.event_data_for_server['shift']
        print("shift", shift, "trickNum", self.trickNum)
        if self.trickNum == 0 and shift == 0:

            current_player = self.players[(self.dealer + 1) % 4]

        else:
            current_player_i = (self.trickWinner + shift) % 4
            current_player = self.players[current_player_i]

        self.event_data_for_client = {
            "event_name": self.event,
            "broadcast": False,
            "data": {
                'playerName': current_player.name,
                'hand': self._handsToStrList(sum(current_player.hand.hand,
                                                 [])),
                'trickNum': self.trickNum + 1,
                'trickSuit': self.currentTrick.suit.__str__(),
                'currentTrick': self._getCurrentTrickStrList(),
                'contrat': self.contrat,
                'suit': self.atout_suit
            }
        }

    def _assert_valid_play(self, add_card, current_player):

        # If suit is atout, you must go higher if you can
        if (add_card is not None and add_card.suit == Suit(self.atout_suit)
                and self.currentTrick.suit == Suit(self.atout_suit)):

            if (current_player.hasHigherAtout(self.atout_suit,
                                              self.currentTrick.highest_rank)
                    and atout_rank[add_card.rank.rank] <
                    self.currentTrick.highest_rank):
                print("Must put a higher atout")
                add_card = None

            # player tries to play off suit but has trick suit
        if add_card is not None and add_card.suit != self.currentTrick.suit:
            if current_player.hasSuit(self.currentTrick.suit):
                print("Must play the suit of the current trick.")
                add_card = None
            elif current_player.hasAtout(Suit(
                    self.atout_suit)) and add_card.suit != Suit(
                        self.atout_suit):
                print("Must play Atout.")
                add_card = None
            elif (current_player.hasAtout(Suit(self.atout_suit))
                  and add_card.suit == Suit(self.atout_suit)):
                # Player can play a higher atout but doesn't do so --> forced to play a higher atout
                if (self.currentTrick.highest_is_atout
                        and current_player.hasHigherAtout(
                            self.atout_suit, self.currentTrick.highest_rank)
                        and atout_rank[add_card.rank.rank] <
                        self.currentTrick.highest_rank):
                    print("Must put a higher atout")
                    add_card = None
        return add_card

    def _event_PlayTrick_Action(self, action_data):

        shift = self.event_data_for_server['shift']
        current_player_i = (self.trickWinner + shift) % 4
        current_player = self.players[current_player_i]
        add_card = current_player.play(action_data['data']['action']['card'])

        if shift == 0:
            self.currentTrick.setTrickSuit(add_card)

        add_card = self._assert_valid_play(add_card=add_card,
                                           current_player=current_player)

        if add_card is not None:
            current_player.removeCard(add_card)
            self.currentTrick.addCard(add_card, current_player_i,
                                      Suit(self.atout_suit))
            self.event_data_for_server['shift'] += 1
            self.event = 'ShowTrickAction'
            self._event_ShowTrickAction()
        else:
            self.event = 'PlayTrick'
            self._event_PlayTrick()

    def _event_ShowTrickAction(self):

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = "\n" + self._printCurrentTrick()
        self.renderInfo['Msg'] += 'Leading team: {0}\n'.format(
            self.leadingTeam)
        self.event_data_for_client \
            = {"event_name": self.event,
               "broadcast": True,
               "data": {
                   'trickNum': self.trickNum + 1,
                   'trickSuit': self.currentTrick.suit.__str__(),
                   'currentTrick': self._getCurrentTrickStrList(),
                   'trickValue': self._countTrickValue(),
                   'contrat': self.contrat,
                   'suit': self.atout_suit
               }
               }

        if self.currentTrick.cardsInTrick < 4:
            self.event = 'PlayTrick'
        else:

            self.event = 'ShowTrickEnd'
            self._evaluateTrick()

            self.players[self.trickWinner].score += self._countTrickValue()
            [t.updateRoundScore() for t in self.teams]

    def _event_ShowTrickEnd(self):

        cards = []
        for card in self.currentTrick.trick:
            cards += [str(card)]

        self.event_data_for_client \
            = {"event_name": self.event,
               "broadcast": True,
               "data": {
                   'trickNum': self.trickNum + 1,
                   'trickWinner': self.players[self.trickWinner].name,
                   'cards': cards,
                   'contrat': self.contrat,
                   'suit': self.atout_suit
               }
               }

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Trick {0} ***\n'.format(self.trickNum +
                                                                1)
        self.renderInfo['Msg'] += 'Winner: {0}\n'.format(
            self.players[self.trickWinner].name)
        self.renderInfo['Msg'] += 'ValueTrick: {0}\n'.format(
            self._countTrickValue())

        for i in range(2):
            self.renderInfo['Msg'] += '{}:{}\n'.format(self.teams[i].name,
                                                       self.teams[i].score)

        self.renderInfo['Msg'] += 'Winning team: {0}\n'.format(
            self.players[self.trickWinner].team.name)
        self.renderInfo['Msg'] += 'cards: {0}\n'.format(cards)

        self.renderInfo['Msg'] += 'AtoutIs: {0}\n'.format(
            Suit(self.atout_suit).string)

        self.currentTrick = Trick()

        self.trickNum += 1
        if self.trickNum < 8:
            self.event = 'PlayTrick'
            self.event_data_for_server = {'shift': 0}
        else:
            self.event = 'RoundEnd'
            self.event_data_for_server = {}

    def _event_RoundEnd(self):
        print(self.teams[1].cardsInRound)
        self.event_data_for_client \
            = {"event_name": self.event,
               "broadcast": True,
               "data": {
                   "teams": [
                       {'TeamName': self.teams[0].name,
                        'score': self.teams[0].score},
                       {'TeamName': self.teams[1].name,
                        'score': self.teams[1].score},
                   ],
                   'Round': self.round,
                   'contrat': self.contrat,
                   'suit': self.atout_suit
               }
               }

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Round {0} End ***\n'.format(self.round)
        for t in self.teams:
            self.renderInfo['Msg'] += 'round Score {0}: {1}\n'.format(
                t.name, t.score)
            self.renderInfo['Msg'] += 'global score {0}: {1}\n'.format(
                t.name, t.globalScore)

        roundScore_list_for_teams = [0, 0]
        if self.contrat == 0 and self.atout_suit == -1:
            self.renderInfo['Msg'] += 'Everybody passed'

        elif self.teams[self.leadingTeam].score >= self.contrat:
            self.teams[self.leadingTeam].globalScore += self.contrat
            roundScore_list_for_teams[self.leadingTeam] = self.contrat

        else:
            self.teams[self.leadingTeam - 1].globalScore += self.contrat
            roundScore_list_for_teams[self.leadingTeam - 1] = self.contrat

        roundScore_list_for_players = roundScore_list_for_teams * 2
        max_score_team = max(self.teams, key=lambda x: x.globalScore)
        # new round if no one has lost
        if max_score_team.globalScore < self.maxScore:
            self.event = 'NewRound'
            self.event_data_for_server = {}
        else:
            self.event = 'GameOver'
            self.event_data_for_server = {}

        reward = {}
        for current_player_i in range(len(self.players)):
            reward[self.players[current_player_i].
                   name] = roundScore_list_for_players[current_player_i]
        '''Rebuild Deck from tricks collected by each player'''
        # Gathering Each Team's tricks
        for t in self.teams:
            t.joinCards()
            t.joinHands()

        if (self.teams[0].cardsInRound !=
            []) | (self.teams[1].cardsInRound !=
                   []):  # If round was played (not everybody passed)
            self.deck.joinTeamsSubDecks(self.teams[0], self.teams[1])

        else:  # Else, Gather back hands
            self.deck.joinTeamsHands(self.teams[0], self.teams[1])
        print("taille du paquet maintenant", self.deck.size())
        self.deck.cut_deck()  # Then cut
        return reward

    def _event_GameOver(self):

        winner = min(self.teams, key=lambda x: x.globalScore)

        self.event_data_for_client \
            = {"event_name": self.event,
               "broadcast": True,
               "data": {
                   "teams": [
                       {'TeamName': self.teams[0].name,
                        'score': self.teams[0].globalScore},
                       {'TeamName': self.teams[1].name,
                        'score': self.teams[1].globalScore},
                   ],
                   'Round': self.round,
                   'Winner': winner.name
               }
               }

        self.renderInfo['printFlag'] = True
        self.renderInfo['Msg'] = '\n*** Game Over ***\n'
        for p in self.teams:
            self.renderInfo['Msg'] += 'round score {0}: {1}\n'.format(
                p.name, p.score)
            self.renderInfo['Msg'] += '{0}: {1}\n'.format(
                p.name, p.globalScore)

        self.renderInfo['Msg'] += '\nRound: {0}\n'.format(self.round)
        self.renderInfo['Msg'] += 'Winner: {0}\n'.format(winner.name)

        self.event = None

    def reset(self):

        # Generate a full deck of cards and shuffle it
        self.event = 'GameStart'
        self._event_GameStart()
        observation = self.event_data_for_client
        self.event = 'NewRound'
        self.event_data_for_server = {}

        return observation

    def render(self):

        if self.renderInfo['printFlag']:
            print(self.renderInfo['Msg'])
            self.renderInfo['printFlag'] = False
            self.renderInfo['Msg'] = ""

    def step(self, action_data):
        observation, reward, done, info = None, None, None, None
        print("\n \ntype of event in env.step: ", self.event)
        if self.event == 'NewRound':
            self._event_NewRound()

        elif self.event == 'ShowPlayerHand':
            self._event_ShowPlayerHand()

        elif self.event == 'ChooseContrat' or self.event == 'ChooseContratAction':
            print("clubs = 0")
            print("diamonds = 1")
            print("spades = 2")
            print("hearts = 3")
            if action_data != None:
                self._event_ChooseContratAction(action_data)
            else:
                if self.event == 'ChooseContrat':
                    self._event_ChooseContrat()

        elif self.event == 'PlayTrick' or self.event == 'ShowTrickAction' or self.event == 'ShowTrickEnd':
            print(self.event)
            if action_data != None and action_data[
                    'event_name'] == "PlayTrick_Action":

                self._event_PlayTrick_Action(action_data)
            else:
                if self.event == 'PlayTrick':
                    self._event_PlayTrick()
                elif self.event == 'ShowTrickEnd':
                    reward = self._countTrickValue()
                    self._event_ShowTrickEnd()

        elif self.event == 'RoundEnd':
            reward = self._event_RoundEnd()

        elif self.event == 'GameOver':
            self._event_GameOver()

        elif self.event == None:
            self.event_data_for_client = None
            done = True

        print(reward)
        observation = self.event_data_for_client
        return observation, reward, done, info