#!/bin/python
import roomai.common
from roomai.sevenking import SevenKingPublicState
from roomai.sevenking import SevenKingPrivateState
from roomai.sevenking import SevenKingPersonState
from roomai.sevenking import SevenKingAction
from roomai.sevenking import SevenKingPokerCard
from roomai.sevenking import AllSevenKingPokerCards
import random

import roomai.sevenking


AllPatterns = dict()
###
###numCards
AllPatterns["p_0"] = ("p_0",0) ## check
AllPatterns["p_1"] = ("p_1",1)
AllPatterns["p_2"] = ("p_2",2)
AllPatterns["p_3"] = ("p_3",3)
AllPatterns["p_4"] = ("p_4",4)

class SevenKingEnv(roomai.common.AbstractEnv):
    num_players = 2

    def init(self):
        self.public_state  = SevenKingPublicState()
        self.private_state = SevenKingPrivateState()
        self.person_states = [SevenKingPersonState() for i in range(self.num_players)]

        self.public_state_history  = []
        self.private_state_history = []
        self.person_states_history = []

        ## private_state
        self.private_state.keep_cards = [c.__deepcopy__() for c in AllSevenKingPokerCards]
        random.shuffle(self.private_state.keep_cards)
        self.private_state.hand_cards = [[] for i in range(self.num_players)]
        for i in range(self.num_players):
            for j in range(5):
                c = self.private_state.keep_cards[-1]
                self.private_state.keep_cards.pop()
                self.private_state.hand_cards[i].append(c)

        ## public_state
        self.public_state.turn            = self.choose_player_with_lowest_card()
        self.public_state.is_terminal     = False
        self.public_state.scores          = []
        self.public_state.previous_id     = None
        self.public_state.previous_action = None
        self.public_state.stage           = 0

        self.public_state.num_players     = self.num_players
        self.public_state.num_keep_cards  = len(self.private_state.keep_cards)
        self.public_state.num_hand_cards  = [len(cards) for cards in self.private_state.hand_cards]

        ## person_state
        for i in range(self.num_players):
            self.person_states[i].id         = i
            self.person_states[i].hand_card  = [c.__deepcopy__() for c in self.private_state.hand_cards[i]]
            if i == self.public_state.turn:
                self.person_states[i].available_actions = SevenKingEnv.available_actions(self.public_state, self.person_states[i])

        self.__gen_history__()
        infos = self.__gen_infos__()
        return infos, self.public_state, self.person_states, self.private_state

    def forward(self, action):
        pu   = self.public_state
        pr   = self.private_state
        pes  = self.person_states
        turn = pu.turn

        if SevenKingEnv.is_action_valid(action, pu, pes[turn]) == False:
            raise  ValueError("The %s is an invalid action " % (action.key()))

        ## the action plays its role
        if action.pattern[0] == "p_0":
            pu.is_check[turn]           = True
            pu.num_check               -= 1
            pes[turn].available_actions = dict()
        else:
            action_key_tmp  = dict([(c.key(), None) for c in action.cards])
            cards_tmp       = pr.hand_cards[turn]

            pr.hand_cards[turn] = []
            for c in cards_tmp:
                if c.key() not in action_key_tmp:
                    pr.hand_cards[turn].append(c)

            if pu.stage == 0:
                for i in range(5 - len(pr.hand_cards[turn])):
                    c = pr.keep_cards[-1]
                    pr.keep_cards = pr.keep_cards.pop()
                    pr.hand_cards[turn].append(c)

            pes[turn].hand_card         = [c.__deepcopy__() for c in pr.hand_cards[turn]]
            pes[turn].available_actions = dict()

        pu.previous_id     = turn
        pu.previous_action = action.__deepcopy__()


        ## termminal
        if self.public_state.stage == 1 and len(self.private_state.hand_cards[turn]) == 0:
            pu.is_terminal = True
            pu.scores      = self.compute_scores()
            new_turn       = None
            pu.turn        = new_turn

        ## stage 0 to 1
        elif len(self.private_state.keep_cards) < 5:
            new_turn                        = self.choose_player_with_lowest_card()
            pu.turn                         = new_turn
            pu.num_check                    = 0
            pu.is_check                     = [False for i in range(pu.num_players)]
            pes[new_turn].available_actions = SevenKingEnv.available_actions(pu, pes[new_turn])
            pu.stage                        = 1

        ## round next
        elif self.public_state.num_check + 1 == pu.num_players:
            new_turn                        = self.choose_player_with_lowest_card()
            pu.turn                         = new_turn
            pu.num_check                    = 0
            pu.is_check                     = [False for i in range(pu.num_players)]
            pes[new_turn].available_actions = SevenKingEnv.available_actions(pu, pes[new_turn])

        else:
            new_turn                        = (turn + 1) % pu.num_players
            pes[new_turn].available_actions = SevenKingEnv.available_actions(self.public_state, self.person_states[new_turn])


        self.__gen_history__()
        infos = self.__gen_infos__()
        return infos, self.public_state, self.person_states, self.private_state

    def compute_scores(self):
        scores                         = [-1 for i in range(self.num_players)]
        scores[self.public_state.turn] = self.num_players -1
        return scores

    def choose_player_with_lowest_card(self):
        min_card    = self.private_state.hand_cards[0][0]
        min_playerid = 0
        for playerid in range(self.num_players):
            for c in self.private_state.hand_cards[playerid]:
                if SevenKingPokerCard.compare(min_card, c) < 0:
                    min_card     = c
                    min_playerid = playerid
        return min_playerid

    ######################## Utils function ###################
    @classmethod
    def compete(cls, env, players):
        env.num_players = len(players)
        infos, public_state, person_states, private_state = env.init()
        for i in range(env.num_players):
            players[i].receive_info(infos[i])

        while public_state.is_terminal == False:
            turn   = public_state.turn
            action = players[turn].take_action()
            infos, public_state, person_states, private_state = env.forward(action)
            for i in range(env.num_players):
                players[i].receive_info(infos[i])

        return public_state.scores

    @classmethod
    def action2pattern(cls, action):
        ###numCards
        num_cards  = len(action.cards)
        return AllPatterns["p_%d"%(num_cards)]


    @classmethod
    def is_action_valid(self, action, public_state, person_state):
        ### is action from hand_cards
        hand_keys = dict()
        for c in person_state.hand_card:
            key = c.key
            if c not in hand_keys:  hand_keys[key] = 1
            else:  hand_keys[c] += 1
        action_keys = dict()
        for c in action.cards:
            key = c.key
            if c not in action_keys: action_keys[key] = 1
            else:action_keys[key] +=1

        for k in action_keys:
            if k not in hand_keys or hand_keys[k] < action_keys[k]:
                return False

        ## pattern
        previous_action = public_state.previous_action
        if previous_action is None:
            previous_action = SevenKingAction("")
        if previous_action.pattern[0] != "p_0" and previous_action.pattern[0] != action.pattern[0]:
            return False

        ## large
        if previous_action.pattern[0] != "p_0":
            max_action_card = action.cards[0]
            for c in action.cards:
                if SevenKingPokerCard.compare(max_action_card,c) < 0:
                    max_action_card = c
            max_previous_card = previous_action.cards[0]
            for c in previous_action.cards:
                if SevenKingPokerCard.compare(max_previous_card,c) < 0:
                    max_previous_card = c
            if SevenKingPokerCard.compare(max_action_card, max_previous_card) < 0 :
                return False

        return True



    ########################### about gen_available_actions ########################
    @classmethod
    def __gen_available_actions_with_pattern(cls, hand_card, pattern):
        res = []

        if len(hand_card) < pattern[1]:
            return res
        if pattern[0] == "p_0":
            return res

        point2cards = dict()
        for c in hand_card:
            point = c.get_point_rank()
            if point not in point2cards:
                point2cards[point] = []
            point2cards[point].append(c.__deepcopy__())

        if pattern[0] == "p_1":
            for c in hand_card:
                res.append(SevenKingAction(c.key))
        elif pattern[0] == "p_2":
            for p in point2cards:
                if len(point2cards[p]) >= 2:
                    pass
        elif pattern[0] == "p_3":
            for p in point2cards:
                if len(point2cards[p]) >= 3:
                    pass
        elif pattern[0] == "p_4":
            for p in point2cards:
                if len(point2cards[p]) >= 4:
                    point2cards[p].sort(cmp = SevenKingPokerCard.compare())
                    str = "%s,%s,%s,%s"%(
                        point2cards[p][0].key(),
                        point2cards[p][1].key(),
                        point2cards[p][2].key(),
                        point2cards[p][3].key()
                    )
                    res.append(SevenKingAction(str))

        else:
            raise ValueError("The %s pattern is invalid"%(pattern[0]))

        return res

    @classmethod
    def available_actions(cls, public_state, person_state):
        available_actions      = dict()
        available_actions[""]  = SevenKingAction("")

        previous_action = public_state.previous_action
        if previous_action is None:
            previous_action = SevenKingAction("")
        hand_cards = person_state.hand_card

        if previous_action.pattern[0] == "p_0":
            for pattern in AllPatterns.values():
                actions = cls.__gen_available_actions_with_pattern(hand_cards, pattern)
                for action in actions:
                    if cls.is_action_valid(action, public_state, person_state) == True:
                        available_actions[action.key] = action
        else:
            actions = cls.__gen_available_actions_with_pattern(hand_cards, AllPatterns["p_0_0_0"])
            for action in actions:
                if cls.is_action_valid(action, public_state, person_state) == True:
                    available_actions[action.key] = action


        return available_actions


#!/bin/python
import roomai.common
from roomai.sevenking import SevenKingPublicState
from roomai.sevenking import SevenKingPrivateState
from roomai.sevenking import SevenKingPersonState
from roomai.sevenking import SevenKingAction
from roomai.sevenking import SevenKingPokerCard
from roomai.sevenking import AllSevenKingPokerCards
import random

import roomai.sevenking


AllPatterns = dict()
###
###numCards
AllPatterns["p_0"] = ("p_0",0) ## check
AllPatterns["p_1"] = ("p_1",1)
AllPatterns["p_2"] = ("p_2",2)
AllPatterns["p_3"] = ("p_3",3)
AllPatterns["p_4"] = ("p_4",4)

class SevenKingEnv(roomai.common.AbstractEnv):
    num_players = 2

    def init(self):
        self.public_state  = SevenKingPublicState()
        self.private_state = SevenKingPrivateState()
        self.person_states = [SevenKingPersonState() for i in range(self.num_players)]

        self.public_state_history  = []
        self.private_state_history = []
        self.person_states_history = []

        ## private_state
        self.private_state.keep_cards = [c.__deepcopy__() for c in AllSevenKingPokerCards]
        random.shuffle(self.private_state.keep_cards)
        self.private_state.hand_cards = [[] for i in range(self.num_players)]
        for i in range(self.num_players):
            for j in range(5):
                c = self.private_state.keep_cards[-1]
                self.private_state.keep_cards.pop()
                self.private_state.hand_cards[i].append(c)

        ## public_state
        self.public_state.turn            = self.choose_player_with_lowest_card()
        self.public_state.is_terminal     = False
        self.public_state.scores          = []
        self.public_state.previous_id     = None
        self.public_state.previous_action = None
        self.public_state.stage           = 0

        self.public_state.num_players     = self.num_players
        self.public_state.num_keep_cards  = len(self.private_state.keep_cards)
        self.public_state.num_hand_cards  = [len(cards) for cards in self.private_state.hand_cards]

        ## person_state
        for i in range(self.num_players):
            self.person_states[i].id         = i
            self.person_states[i].hand_card  = [c.__deepcopy__() for c in self.private_state.hand_cards[i]]
            if i == self.public_state.turn:
                self.person_states[i].available_actions = SevenKingEnv.available_actions(self.public_state, self.person_states[i])

        self.__gen_history__()
        infos = self.__gen_infos__()
        return infos, self.public_state, self.person_states, self.private_state

    def forward(self, action):
        pu   = self.public_state
        pr   = self.private_state
        pes  = self.person_states
        turn = pu.turn

        if SevenKingEnv.is_action_valid(action, pu, pes[turn]) == False:
            raise  ValueError("The %s is an invalid action " % (action.key()))

        ## the action plays its role
        if action.pattern[0] == "p_0":
            pu.is_check[turn]           = True
            pu.num_check               -= 1
            pes[turn].available_actions = dict()
        else:
            action_key_tmp  = dict([(c.key(), None) for c in action.cards])
            cards_tmp       = pr.hand_cards[turn]

            pr.hand_cards[turn] = []
            for c in cards_tmp:
                if c.key() not in action_key_tmp:
                    pr.hand_cards[turn].append(c)

            if pu.stage == 0:
                for i in range(5 - len(pr.hand_cards[turn])):
                    c = pr.keep_cards[-1]
                    pr.keep_cards = pr.keep_cards.pop()
                    pr.hand_cards[turn].append(c)

            pes[turn].hand_card         = [c.__deepcopy__() for c in pr.hand_cards[turn]]
            pes[turn].available_actions = dict()

        pu.previous_id     = turn
        pu.previous_action = action.__deepcopy__()


        ## termminal
        if self.public_state.stage == 1 and len(self.private_state.hand_cards[turn]) == 0:
            pu.is_terminal = True
            pu.scores      = self.compute_scores()
            new_turn       = None
            pu.turn        = new_turn

        ## stage 0 to 1
        elif len(self.private_state.keep_cards) < 5:
            new_turn                        = self.choose_player_with_lowest_card()
            pu.turn                         = new_turn
            pu.num_check                    = 0
            pu.is_check                     = [False for i in range(pu.num_players)]
            pes[new_turn].available_actions = SevenKingEnv.available_actions(pu, pes[new_turn])
            pu.stage                        = 1

        ## round next
        elif self.public_state.num_check + 1 == pu.num_players:
            new_turn                        = self.choose_player_with_lowest_card()
            pu.turn                         = new_turn
            pu.num_check                    = 0
            pu.is_check                     = [False for i in range(pu.num_players)]
            pes[new_turn].available_actions = SevenKingEnv.available_actions(pu, pes[new_turn])

        else:
            new_turn                        = (turn + 1) % pu.num_players
            pes[new_turn].available_actions = SevenKingEnv.available_actions(self.public_state, self.person_states[new_turn])


        self.__gen_history__()
        infos = self.__gen_infos__()
        return infos, self.public_state, self.person_states, self.private_state

    def compute_scores(self):
        scores                         = [-1 for i in range(self.num_players)]
        scores[self.public_state.turn] = self.num_players -1
        return scores

    def choose_player_with_lowest_card(self):
        min_card    = self.private_state.hand_cards[0][0]
        min_playerid = 0
        for playerid in range(self.num_players):
            for c in self.private_state.hand_cards[playerid]:
                if SevenKingPokerCard.compare(min_card, c) < 0:
                    min_card     = c
                    min_playerid = playerid
        return min_playerid

    ######################## Utils function ###################
    @classmethod
    def compete(cls, env, players):
        env.num_players = len(players)
        infos, public_state, person_states, private_state = env.init()
        for i in range(env.num_players):
            players[i].receive_info(infos[i])

        while public_state.is_terminal == False:
            turn   = public_state.turn
            action = players[turn].take_action()
            infos, public_state, person_states, private_state = env.forward(action)
            for i in range(env.num_players):
                players[i].receive_info(infos[i])

        return public_state.scores

    @classmethod
    def action2pattern(cls, action):
        ###numCards
        num_cards  = len(action.cards)
        return AllPatterns["p_%d"%(num_cards)]


    @classmethod
    def is_action_valid(self, action, public_state, person_state):
        ### is action from hand_cards
        hand_keys = dict()
        for c in person_state.hand_card:
            key = c.key
            if c not in hand_keys:  hand_keys[key] = 1
            else:  hand_keys[c] += 1
        action_keys = dict()
        for c in action.cards:
            key = c.key
            if c not in action_keys: action_keys[key] = 1
            else:action_keys[key] +=1

        for k in action_keys:
            if k not in hand_keys or hand_keys[k] < action_keys[k]:
                return False

        ## pattern
        previous_action = public_state.previous_action
        if previous_action is None:
            previous_action = SevenKingAction("")
        if previous_action.pattern[0] != "p_0" and previous_action.pattern[0] != action.pattern[0]:
            return False

        ## large
        if previous_action.pattern[0] != "p_0":
            max_action_card = action.cards[0]
            for c in action.cards:
                if SevenKingPokerCard.compare(max_action_card,c) < 0:
                    max_action_card = c
            max_previous_card = previous_action.cards[0]
            for c in previous_action.cards:
                if SevenKingPokerCard.compare(max_previous_card,c) < 0:
                    max_previous_card = c
            if SevenKingPokerCard.compare(max_action_card, max_previous_card) < 0 :
                return False

        return True



    ########################### about gen_available_actions ########################
    @classmethod
    def __gen_available_actions_with_pattern(cls, hand_card, pattern):
        res = []

        if len(hand_card) < pattern[1]:
            return res
        if pattern[0] == "p_0":
            return res

        point2cards = dict()
        for c in hand_card:
            point = c.get_point_rank()
            if point not in point2cards:
                point2cards[point] = []
            point2cards[point].append(c.__deepcopy__())

        if pattern[0] == "p_1":
            for c in hand_card:
                res.append(SevenKingAction(c.key))
        elif pattern[0] == "p_2":
            for p in point2cards:
                if len(point2cards[p]) >= 2:
                    pass
        elif pattern[0] == "p_3":
            for p in point2cards:
                if len(point2cards[p]) >= 3:
                    pass
        elif pattern[0] == "p_4":
            for p in point2cards:
                if len(point2cards[p]) >= 4:
                    point2cards[p].sort(cmp = SevenKingPokerCard.compare())
                    str = "%s,%s,%s,%s"%(
                        point2cards[p][0].key(),
                        point2cards[p][1].key(),
                        point2cards[p][2].key(),
                        point2cards[p][3].key()
                    )
                    res.append(SevenKingAction(str))

        else:
            raise ValueError("The %s pattern is invalid"%(pattern[0]))

        return res

    @classmethod
    def available_actions(cls, public_state, person_state):
        available_actions      = dict()
        available_actions[""]  = SevenKingAction("")

        previous_action = public_state.previous_action
        if previous_action is None:
            previous_action = SevenKingAction("")
        hand_cards = person_state.hand_card

        if previous_action.pattern[0] == "p_0":
            for pattern in AllPatterns.values():
                actions = cls.__gen_available_actions_with_pattern(hand_cards, pattern)
                for action in actions:
                    if cls.is_action_valid(action, public_state, person_state) == True:
                        available_actions[action.key] = action
        else:
            actions = cls.__gen_available_actions_with_pattern(hand_cards, AllPatterns["p_0_0_0"])
            for action in actions:
                if cls.is_action_valid(action, public_state, person_state) == True:
                    available_actions[action.key] = action


        return available_actions


#!/bin/python ### ###numCards ## check ## private_state ## public_state ## person_state ## the action plays its role ## termminal ## stage 0 to 1 ## round next ######################## Utils function ################### ###numCards ### is action from hand_cards ## pattern ## large ########################### about gen_available_actions ########################