A famous casino is suddenly faced with a sharp decline of their revenues. They decide to offer Texas hold'em also online. Can you help them by writing an algorithm that can rank poker hands? 

## Task

Create a poker hand that has a method to compare itself to another poker hand:

```python
compare_with(self, other_hand)
```

A poker hand has a constructor that accepts a string containing 5 cards:
```python
PokerHand("KS 2H 5C JD TD")
```

The characteristics of the string of cards are:
* Each card consists of two characters, where
 * The first character is the value of the card: `2, 3, 4, 5, 6, 7, 8, 9, T(en), J(ack), Q(ueen), K(ing), A(ce)`
 * The second character represents the suit: `S(pades), H(earts), D(iamonds), C(lubs)`
* A space is used as card separator between cards

The result of your poker hand compare can be one of these 3 options:

```python
[ "Win", "Tie", "Loss" ]
```

## Notes
* Apply the [Texas Hold'em](https://en.wikipedia.org/wiki/Texas_hold_%27em) rules for ranking the cards.
* Low aces are **NOT** valid in this kata.
* There is no ranking for the suits.

If you finished this kata, you might want to continue with [Sortable Poker Hands](https://www.codewars.com/kata/sortable-poker-hands)

Difficulty: interview

Starter code:

def __init__(self, hand):
	

Solutions:
class PokerHand(object):
    CARD = "23456789TJQKA"
    RESULT = ["Loss", "Tie", "Win"]

    def __init__(self, hand):
        values = ''.join(sorted(hand[::3], key=self.CARD.index))
        suits = set(hand[1::3])
        is_straight = values in self.CARD
        is_flush = len(suits) == 1
        self.score = (2 * sum(values.count(card) for card in values)
                      + 13 * is_straight + 15 * is_flush,
                      [self.CARD.index(card) for card in values[::-1]])
        
    def compare_with(self, other):
        return self.RESULT[(self.score > other.score) - (self.score < other.score) + 1]

from collections import Counter


class Card(object):

    def __init__(self, card):
        if card[0] in '23456789':
            self.rank = int(card[0])
        else:
            self.rank = {'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}.get(card[0])
        self.suit = card[1]


class PokerHand(object):

    def __init__(self, hand):
        self.hand = [Card(card) for card in hand.split()]
        self.rank_counts = Counter(card.rank for card in self.hand)

    def compare_with(self, other):
        return "Win" if self.score > other.score else "Loss" if self.score < other.score else "Tie"

    @property
    def score(self):

        def is_flush():
            return len({card.suit for card in self.hand}) == 1

        def is_straight():
            def is_sequential(arr):
                return all(a + 1 == b for a, b in zip(arr, arr[1:]))
            arr = sorted(card.rank for card in self.hand)
            return is_sequential(arr) or (is_sequential(arr[:4]) and arr[0] == 2 and arr[-1] == 14)

        def is_straight_flush():
            return is_flush() and is_straight()

        def is_n_of_kind(n):
            return n in list(self.rank_counts.values())

        def is_full_house():
            return is_n_of_kind(3) and is_n_of_kind(2)

        def is_2_pair():
            return list(self.rank_counts.values()).count(2) == 2

        def get_by_count(n):
            return tuple(rank for rank, count in sorted(list(self.rank_counts.items()), key=lambda x: (x[1], -x[0])) if count == n)

        if is_straight_flush():
            return (9, get_by_count(1))
        elif is_n_of_kind(4):
            return (8, get_by_count(4), get_by_count(1))
        elif is_full_house():
            return (7, get_by_count(3), get_by_count(2))
        elif is_flush():
            return (6, get_by_count(1))
        elif is_straight():
            return (5, get_by_count(1))
        elif is_n_of_kind(3):
            return (4, get_by_count(3), get_by_count(1))
        elif is_2_pair():
            return (3, get_by_count(2), get_by_count(1))
        elif is_n_of_kind(2):
            return (2, get_by_count(2), get_by_count(1))
        else:
            return (1, get_by_count(1))


class PokerHand():
    
    def __init__(self, hand):
        hand = hand.split()
        # get card values
        values = sorted('__23456789TJQKA'.index(card[0]) for card in hand)[::-1]
        # check if same suit
        same_suit = len({card[1] for card in hand}) == 1
        # check if straight
        straight = [values[0]-i for i in range(5)] == values
        # check n of a kind
        kinds = {i: [] for i in range(1, 5)}
        for value in sorted(set(values))[::-1]:
            how_many = values.count(value)
            kinds[how_many].append(value)
        
        # calculate rank
        if same_suit and straight:    self.rank = 8, values                # royal/straight flush
        elif kinds[4]:                self.rank = 7, kinds[4], kinds[1]    # four of a kind
        elif kinds[3] and kinds[2]:   self.rank = 6, kinds[3], kinds[2]    # full house
        elif same_suit:               self.rank = 5, values                # flush
        elif straight:                self.rank = 4, values                # straight
        elif kinds[3]:                self.rank = 3, kinds[3], kinds[1]    # three of a kind
        elif len(kinds[2]) == 2:      self.rank = 2, kinds[2], kinds[1]    # two pairs
        elif kinds[2]:                self.rank = 1, kinds[2], kinds[1]    # one pair
        else:                         self.rank = 0, values                # high card
    
    def compare_with(self, other):
        return ( "Win"  if self.rank > other.rank else
                 "Loss" if self.rank < other.rank else
                 "Tie" )

from collections import Counter

RESULT      = ["Loss", "Tie", "Win"]
HEX_CARDS   = "23456789ABCDE"
CARD_TO_HEX = str.maketrans("TJQKA", "ABCDE")
MAIN_RANK   = [lambda colC, valC, isS:  5 in colC.values() and isS,                    # Straight Flush
               lambda colC, valC, isS:  4 in valC.values(),                            # Square
               lambda colC, valC, isS:  3 in valC.values() and 2 in valC.values(),     # Full house
               lambda colC, valC, isS:  5 in colC.values(),                            # Flush
               lambda colC, valC, isS:  isS,                                           # Straight
               lambda colC, valC, isS:  3 in valC.values(),                            # Three of a kind
               lambda colC, valC, isS:  2 in valC.values() and len(valC.keys()) == 3,  # 2 pairs
               lambda colC, valC, isS:  2 in valC.values() and len(valC.keys()) == 4,  # 1 pair,
               lambda colC, valC, isS:  True]                                          # 1 card


class PokerHand(object):

    def __init__(self, hand):
        hand         = hand.translate(CARD_TO_HEX)
        valCount     = Counter( hand[n] for n in range(0, len(hand), 3) )
        colorCount   = Counter( hand[n] for n in range(1, len(hand), 3) )
        
        minCardIdx   = int(min(valCount.keys()), 16) - 2
        isStraight   = valCount.keys() == set(HEX_CARDS[minCardIdx:minCardIdx+5])
        hashSameRank = int(''.join(sorted(valCount.keys(), key = lambda k: (valCount[k], k), reverse = True)), 16)
        
        for i,func in enumerate(MAIN_RANK):
            if func(colorCount, valCount, isStraight):
                self.handValue = (len(MAIN_RANK) - i) * 16**5 + hashSameRank
                break
                
        
    def compare_with(self, other):
        return RESULT[ (self.handValue >= other.handValue) + (self.handValue > other.handValue) ]

class PokerHand(object):
    FACE_VALUE = {"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"T":10, "J":11, "Q":12,"K":13, "A": 14}

    def __init__(self, hand):
        self.suits = {"S":0, "H":0, "D":0, "C":0}
        self.values = {}
        self.flush = False
        for card in hand.split():
            card_val = self.FACE_VALUE.get(card[0])
            self.values[card_val] = self.values.get(card_val,0) + 1
            self.suits[card[1]] += 1
            if self.suits[card[1]] == 5: self.flush = True
        self.high_low = self.high_low()
        self.straight = self.look_for_straight()
        self.rank_high_low = [self.rank()] + self.high_low[:]
        
    def high_low(self):
        high_low = []
        for i in range(1,5):
            high_low += sorted([k for k,v in self.values.items() if v == i])
        return high_low[::-1]
        
    def look_for_straight(self):
        val_array = self.high_low[:]
        if 14 in val_array: val_array[val_array.index(14)] = 1 if 2 in val_array else 14
        return val_array == list(range(val_array[0],val_array[0] - 5,-1))
        
    def rank(self):
        num_vals = list(self.values.values())
        if self.flush: return 9 if self.straight else 6
        if num_vals.count(4) == 1: return 8
        if num_vals.count(3) == 1: return 7 if num_vals.count(2) == 1 else 4
        if self.straight: return 5
        if num_vals.count(2) > 0: return 3 if num_vals.count(2) == 2 else 2
        return 1
        
    def compare_with(self, other):
        for i in range(len(self.rank_high_low)):
            if self.rank_high_low[i] > other.rank_high_low[i]: return "Win"
            if self.rank_high_low[i] < other.rank_high_low[i]: return "Loss"   
        return "Tie"

from collections import Counter


class CardValue(str):
    ORDER = "23456789TJQKA"

    def __lt__(self, other):
        return self.ORDER.index(self) < self.ORDER.index(other)

    def __gt__(self, other):
        return self.ORDER.index(self) > self.ORDER.index(other)


class Card():
    ORDER = CardValue.ORDER

    def __init__(self, card_str):
        self.value = CardValue(card_str[0])
        self.color = card_str[1]

    def __lt__(self, other):
        return self.value < other.value

    def __str__(self):
        return "".join((self.value, self.color))


class HandValue():
    def __init__(self, poker_hand):
        self.poker_hand = poker_hand

    def compare(self, other):
        return 'Tie'

    def __str__(self):
        return self.__class__.__name__

    @property
    def pretty_string(self):
        return "{} is {}".format(self.poker_hand, self)


class RoyalFlush(HandValue):
    @staticmethod
    def is_valid(poker_hand):
        if poker_hand.is_same_color:
            if {card.value for card in poker_hand.cards} == {'A', 'K', 'Q', 'J', 'T'}:
                return True
        return False


class HighCard(HandValue):
    def compare(self, other):
        self_value_remaining = sorted(self.poker_hand.card_values, reverse=True)
        other_value_remaining = sorted(other.poker_hand.card_values, reverse=True)
        for i in range(5):
            if self_value_remaining[i] > other_value_remaining[i]:
                return 'Win'
            if self_value_remaining[i] < other_value_remaining[i]:
                return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        return True


class Pairs(HandValue):
    def compare(self, other):
        counter_self_values = Counter(self.poker_hand.card_values)
        counter_other_values = Counter(other.poker_hand.card_values)

        # Check the pair
        self_value_2 = next(value for value, nb_repet in counter_self_values.items() if nb_repet == 2)
        other_value_2 = next(value for value, nb_repet in counter_other_values.items() if nb_repet == 2)
        if self_value_2 > other_value_2:
            return 'Win'
        if self_value_2 < other_value_2:
            return 'Loss'

        # Check the remaining solo cards
        self_value_remaining = sorted((value for value, nb_repet in counter_self_values.items() if nb_repet == 1), reverse=True)
        other_value_remaining = sorted((value for value, nb_repet in counter_other_values.items() if nb_repet == 1), reverse=True)
        for i in range(3):
            if self_value_remaining[i] > other_value_remaining[i]:
                return 'Win'
            if self_value_remaining[i] < other_value_remaining[i]:
                return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        counter_values = Counter(poker_hand.card_values)
        return 2 in counter_values.values()


class TwoPairs(HandValue):
    def compare(self, other):
        counter_self_values = Counter(self.poker_hand.card_values)
        counter_other_values = Counter(other.poker_hand.card_values)

        # Check the highest pair
        max_self_value_2 = max([value for value, nb_repet in counter_self_values.items() if nb_repet == 2])
        max_other_value_2 = max([value for value, nb_repet in counter_other_values.items() if nb_repet == 2])
        if max_self_value_2 > max_other_value_2:
            return 'Win'
        if max_self_value_2 < max_other_value_2:
            return 'Loss'

        # Check the lowest pair
        min_self_value_2 = min([value for value, nb_repet in counter_self_values.items() if nb_repet == 2])
        min_other_value_2 = min([value for value, nb_repet in counter_other_values.items() if nb_repet == 2])
        if min_self_value_2 > min_other_value_2:
            return 'Win'
        if min_self_value_2 < min_other_value_2:
            return 'Loss'

        # Otherwise check the lowest remaining value
        self_value_1 = next(value for value, nb_repet in counter_self_values.items() if nb_repet == 1)
        other_value_1 = next(value for value, nb_repet in counter_other_values.items() if nb_repet == 1)
        if self_value_1 > other_value_1:
            return 'Win'
        if self_value_1 < other_value_1:
            return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        counter_values = Counter(poker_hand.card_values)
        return list(counter_values.values()).count(2) == 2


class ThreeOfAKind(HandValue):
    def compare(self, other):
        counter_self_values = Counter(self.poker_hand.card_values)
        counter_other_values = Counter(other.poker_hand.card_values)

        # Check the value of the 3 of a kind
        max_self_value_3 = [value for value, nb_repet in counter_self_values.items() if nb_repet == 3][0]
        max_other_value_3 = [value for value, nb_repet in counter_other_values.items() if nb_repet == 3][0]
        if max_self_value_3 > max_other_value_3:
            return 'Win'
        if max_self_value_3 < max_other_value_3:
            return 'Loss'

        # Check the highest remaining value
        max_self_value_2 = max(value for value, nb_repet in counter_self_values.items() if nb_repet == 1)
        max_other_value_2 = max(value for value, nb_repet in counter_other_values.items() if nb_repet == 1)
        if max_self_value_2 > max_other_value_2:
            return 'Win'
        if max_self_value_2 < max_other_value_2:
            return 'Loss'

        # Otherwise check the lowest remaining value
        min_self_value_2 = min(value for value, nb_repet in counter_self_values.items() if nb_repet == 1)
        min_other_value_2 = min(value for value, nb_repet in counter_other_values.items() if nb_repet == 1)
        if min_self_value_2 > min_other_value_2:
            return 'Win'
        if min_self_value_2 < min_other_value_2:
            return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        counter_self_values = Counter(poker_hand.card_values)
        return 3 in counter_self_values.values()


class FullHouse(HandValue):
    def compare(self, other):
        counter_self_values = Counter(self.poker_hand.card_values)
        counter_other_values = Counter(other.poker_hand.card_values)

        # Check the value of the 3 of a kind
        max_self_value_3 = [value for value, nb_repet in counter_self_values.items() if nb_repet == 3][0]
        max_other_value_3 = [value for value, nb_repet in counter_other_values.items() if nb_repet == 3][0]
        if max_self_value_3 > max_other_value_3:
            return 'Win'
        if max_self_value_3 < max_other_value_3:
            return 'Loss'

        # Case 3 of a kind value is the same, check pair value
        max_self_value_2 = [value for value, nb_repet in counter_self_values.items() if nb_repet == 2][0]
        max_other_value_2 = [value for value, nb_repet in counter_other_values.items() if nb_repet == 2][0]
        if max_self_value_2 > max_other_value_2:
            return 'Win'
        if max_self_value_2 < max_other_value_2:
            return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        counter_self_values = Counter(poker_hand.card_values)
        return set(counter_self_values.values()) == {2, 3}


class FourOfAKind(HandValue):
    def compare(self, other):
        counter_self_values = Counter(self.poker_hand.card_values)
        counter_other_values = Counter(other.poker_hand.card_values)
        max_self_value = [value for value, nb_repet in counter_self_values.items() if nb_repet == 4][0]
        max_other_value = [value for value, nb_repet in counter_other_values.items() if nb_repet == 4][0]
        if max_self_value > max_other_value:
            return 'Win'
        if max_self_value < max_other_value:
            return 'Loss'

        max_self_value = [value for value, nb_repet in counter_self_values.items() if nb_repet == 1][0]
        max_other_value = [value for value, nb_repet in counter_other_values.items() if nb_repet == 1][0]
        if max_self_value > max_other_value:
            return 'Win'
        if max_self_value < max_other_value:
            return 'Loss'
        return 'Tie'


    @staticmethod
    def is_valid(poker_hand):
        counter_self_values = Counter(poker_hand.card_values)
        return 4 in counter_self_values.values()


class Straight(HandValue):
    def compare(self, other):
        max_self_value = max(self.poker_hand.card_values)
        max_other_value = max(other.poker_hand.card_values)
        if max_self_value > max_other_value:
            return 'Win'
        if max_self_value < max_other_value:
            return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        sorted_values = list(sorted(Card.ORDER.index(card.value) for card in poker_hand.cards))
        return sorted_values == list(range(sorted_values[0], sorted_values[0] + 5))


class Flush(HandValue):
    def compare(self, other):
        return HighCard.compare(self, other)


    @staticmethod
    def is_valid(poker_hand):
        return poker_hand.is_same_color


class StraightFlush(HandValue):
    def compare(self, other):
        max_self_value = max(self.poker_hand.card_values)
        max_other_value = max(other.poker_hand.card_values)
        if max_self_value > max_other_value:
            return 'Win'
        if max_self_value < max_other_value:
            return 'Loss'
        return 'Tie'

    @staticmethod
    def is_valid(poker_hand):
        if poker_hand.is_same_color:
            sorted_values = list(sorted(Card.ORDER.index(card.value) for card in poker_hand.cards))
            return sorted_values == list(range(sorted_values[0], sorted_values[0] + 5))
        return False


class PokerHand(object):
    RESULT = ['Loss', 'Tie', 'Win']
    HV_ORDER = [HighCard,
                Pairs,
                TwoPairs,
                ThreeOfAKind,
                Straight,
                Flush,
                FullHouse,
                FourOfAKind,
                StraightFlush,
                RoyalFlush]

    def __init__(self, hand):
        self.cards = tuple(Card(card_str) for card_str in hand.split())
        self.is_same_color = (len(set(self.card_colors)) == 1)
        self.hand_value = None
        for hand_value_cls in reversed(self.HV_ORDER):
            if hand_value_cls.is_valid(self):
                # print("{} is {}".format(hand, hand_value_cls.__name__))
                self.hand_value = hand_value_cls(self)
                break

    def __str__(self):
        return " ".join(map(str, self.cards))

    def compare_with(self, other):
        print(self.hand_value.pretty_string)
        print(other.hand_value.pretty_string)
        if self.HV_ORDER.index(type(self.hand_value)) < self.HV_ORDER.index(type(other.hand_value)):
            return 'Loss'
        if self.HV_ORDER.index(type(self.hand_value)) > self.HV_ORDER.index(type(other.hand_value)):
            return 'Win'
        return self.hand_value.compare(other.hand_value)

    @property
    def card_values(self):
        return tuple(card.value for card in self.cards)

    @property
    def card_colors(self):
        return tuple(card.color for card in self.cards)

class PokerHand(object):
    def __init__(self, hand):
        self.hand = hand
        self.lst = ["10" if "T" in x else x[0].replace('J', "11").replace('Q', "12").replace('K', "13").replace('A', "14") for x in self.hand.split()]
        
    def straight(self):
        return 0 if not ''.join(sorted(self.lst, key=int)) in "234567891011121314" else max(list(map(int, self.lst)))
    
    def four(self):
        return 0 if not max([self.lst.count(x) for x in self.lst]) == 4 else [x for x in self.lst if self.lst.count(x) == 4][0]   
    
    def full_house(self):
        return 0 if not len(set(self.lst)) == 2 or self.four() != 0 else [x for x in self.lst if self.lst.count(x) == 3][0]
    
    def flush(self):
        return 0 if not len({x[-1] for x in self.hand.split()}) == 1 or self.full_house() or self.four() else 6
    
    def three(self):
        return 0 if not max([self.lst.count(x) for x in self.lst]) == 3 or self.full_house() else [x for x in self.lst if self.lst.count(x) == 3][0]
    
    def two_pair(self):
        return 0 if not len(set(self.lst)) == 3 or self.three() else max({x for x in self.lst if self.lst.count(x) == 2}, key=int)
    
    def pair(self):
        return 0 if not len(set(self.lst)) == 4 else max({x for x in self.lst if self.lst.count(x) == 2}, key=int)
     
    def high(self):
        return sum(map(int,self.lst))
    
    def score(self):
        out = list(map(int, [min(self.straight(), self.flush()), self.four(), self.full_house(), self.flush(), self.straight(), self.three(), self.two_pair(), self.pair(), self.high()]))
        b = 14
        return out[0]*b**8 + out[1]*b**7 + out[2]*b**6 + out[3]*b**5 + out[4]*b**4 + out[5]*b**3 + out[6]*b**2 + out[7]*b**1 + out[8]
    
    def final_check(self, other):
        one, two = [sorted(x, key=int, reverse=True) for x in (self.lst, other.lst)]
        for i in range(5):
            if int(one[i])>int(two[i]):
                return 1
            if int(two[i])>int(one[i]):
                return -1
        return 0
    
    
    def compare_with(self, other):
        RESULT = ["Loss", "Win"]
        if self.score() == other.score() and not self.final_check(other):
            return "Tie"
        if not self.straight() and not self.flush() and not self.four() and not self.full_house() and not self.flush() and not self.straight() and not self.three() and not self.two_pair() and not self.pair() and not other.straight() and not other.flush() and not other.four() and not other.full_house() and not other.flush() and not other.straight() and not other.three() and not other.two_pair() and not other.pair():
            return RESULT[self.final_check(other) > 0]
        return RESULT[self.score() > other.score()]


from collections import Counter

class PokerHand(object):

    def __init__(self, hand):
        self.hand = ''.join(hand.split())    

    def evaluate(self):

        HIERARCHY = {'highcard':1, 'pair':2, 'two_pairs':3, 'three_of_aKind':4,
                    'straight':5, 'flush':6, 'full_house':7, 'four_of_aKind':8,
                    'straight_flush':9, 'royal_flush':10}
    
        PRECEDENCE = {'1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7,
                    '8':8, '9':9, 'T':10,'J':11, 'Q':12, 'K':13, 'A':14}
        
        value = sorted(PRECEDENCE[x] for x in self.hand[::2])
        shape = self.hand[1::2]
        

        #royal flush
        if len(set(shape)) == 1 and value == [10,11,12,13,14]:
            return HIERARCHY['royal_flush'], value, 'royal_flush'
        
        #straight_flush
        elif len(set(shape)) == 1 and all(abs(value[i] - value[i+1])==1 for i in range(len(value)-1)):
            return HIERARCHY['straight_flush'], value, 'straight_flush'

        #four of a kind
        elif any(value.count(x)==4 for x in set(value)):
            return HIERARCHY['four_of_aKind'], value, 'four_of_aKind'
        
        #full house
        elif any(value.count(x)==3 for x in set(value)) \
                            and                         \
            any(value.count(x)==2 for x in set(value)):
            return HIERARCHY['full_house'], value, 'full_house'
        
        #flush
        elif len(set(shape))==1:
            return HIERARCHY['flush'], value, 'flush'
        

        #straight
        elif all(abs(value[i] - value[i+1])==1 for i in range(len(value)-1)):
            return HIERARCHY['straight'], value, 'straight'
        
        #three of a kind
        elif any(value.count(x)==3 for x in set(value)):
            return HIERARCHY['three_of_aKind'], value, 'three_of_aKind'
        
        #two pairs
        elif Counter(value).most_common(2)[-1][-1] == 2:
            return HIERARCHY['two_pairs'], value, 'two_pairs'
        
        #pair
        elif any(value.count(x)==2 for x in set(value)):
            return HIERARCHY['pair'], value, 'pair'
        
        #high Card
        else:
            return HIERARCHY['highcard'], value, 'highcard'


        
    def compare_with(self, other):
        rank, cards, combo = self.evaluate()
        other_rank, other_cards, other_combo = other.evaluate()

        if rank > other_rank:
            return 'Win'
        
        
        elif rank == other_rank:
            if cards == other_cards:
                return 'Tie'
            

            elif combo == 'straight_flush' or combo == 'straight':
                if cards.pop() > other_cards.pop():
                    return 'Win'
                else:
                    return 'Loss'

            
            elif combo == 'four_of_aKind' or combo == 'full_house':
                c1, c2 = Counter(cards), Counter(other_cards)

                if c1.most_common()[0][0] > c2.most_common()[0][0]:
                    return 'Win'
                elif c1.most_common()[0][0] < c2.most_common()[0][0]:
                    return 'Loss'
                else:
                    if c1.most_common()[-1][0] > c2.most_common()[-1][0]:
                        return 'Win'
                    else:
                        return 'Loss'

            

            elif combo == 'flush':
                for i in range(4, -1, -1):
                    if cards[i] < other_cards[i]:
                        return 'Loss'
                    else:
                        if cards[i] != other_cards[i]:
                            return 'Win'
            

            elif combo == 'three_of_aKind':
                for i in range(1,-1,-1):
                    if cards[i] < other_cards[i]:
                        return 'Loss'
                    else:
                        if cards[i] != other_cards[i]:
                            return 'Win'
            
            elif combo == 'two_pairs' or combo == 'pair':
                c1,c2 = Counter(cards), Counter(other_cards)
                sorted_c1 = sorted(c1.most_common(),key=lambda x:(x[1],x))
                sorted_c2 = sorted(c2.most_common(),key=lambda x:(x[1],x))

                for i in range(len(sorted_c1)-1,-1,-1):
                    if sorted_c1[i][0] < sorted_c2[i][0]:
                        return 'Loss'
                    else:
                        if sorted_c1[i][0] != sorted_c2[i][0]:
                            return 'Win'
            
            else:
                for i in range(4,-1,-1):
                    if cards[i] < other_cards[i]:
                        return 'Loss'
                    else:
                        if cards[i] != other_cards[i]:
                            return 'Win'

        else:
            return 'Loss'

class PokerHand(object):

    def __init__(self, hand):
        self.hand = hand
        pass
    
    def handProcess(self, handList):
        suit = handList[0][1]
        isHandFlush = True
        
        seenCards = []
        seenPairs = []
        seenTriple = []
        seenFour = []
        
        for card in handList:
            # Detect flush hand
            if suit not in card:
                isHandFlush = False
            
            # Replace cards denomination by real value
            if card[0] == "T":
                handList[handList.index(card)], card = 10, 10
            elif card[0] == "J":
                handList[handList.index(card)], card = 11, 11
            elif card[0] == "Q":
                handList[handList.index(card)], card = 12, 12
            elif card[0] == "K":
                handList[handList.index(card)], card = 13, 13
            elif card[0] == "A":
                handList[handList.index(card)], card = 14, 14
            else:
                 handList[handList.index(card)], card = int(card[0]), int(card[0])
            
            # Check pairs, triple and four of a kind
            if card in seenCards:
                if card in seenTriple:
                    seenFour.append(card)
                    del seenTriple[seenTriple.index(card)]
                elif card in seenPairs:
                    seenTriple.append(card)
                    del seenPairs[seenPairs.index(card)]
                else:
                    seenPairs.append(card)
            seenCards.append(card)
            
            
        # Check if hand is straight
        handList = sorted(handList)
        if sum(handList) == (handList[-1]*(handList[-1]+1))/2-((handList[0]-1)*(handList[0])/2):
            isHandStraight = True
        else:
            isHandStraight = False
                
        # Store highest card
        highestCard = handList[-1]
        
        # Store highest hand value
        highestValue = 0
        
        # Store highest value after a match
        cardsOutsideCombination = []
        
        # Calculate hand score
        handScore = 0
        if highestCard == 14 and isHandStraight and isHandFlush:
            handScore = 9 # Royal Flush
        elif isHandStraight and isHandFlush:
            handScore = 8 # Straight Flush
            highestValue = highestCard
        elif len(seenFour) == 1:
            handScore = 7 # Four of a kind
            highestValue = seenFour[0]
            for card in handList:
                if card not in seenFour:
                    cardsOutsideCombination.append(card)
        elif len(seenTriple) == 1 and len(seenPairs) == 1:
            handScore = 6 # Full House
            highestValue = seenTriple[0]
        elif isHandFlush:
            handScore = 5 # Flush
        elif isHandStraight:
            handScore = 4 # Straight
        elif len(seenTriple) == 1:
            handScore = 3 # Three of a kind
            highestValue = seenTriple[0]
            for card in handList:
                if card not in seenTriple:
                    cardsOutsideCombination.append(card)
        elif len(seenPairs) == 2:
            handScore = 2 # Two pairs
            highestValue = sorted(seenPairs)[-1]
            for card in handList:
                if card not in seenPairs:
                    cardsOutsideCombination.append(card)
        elif len(seenPairs) == 1:
            handScore = 1 # Pair
            highestValue = seenPairs[0]
            for card in handList:
                if card not in seenPairs:
                    cardsOutsideCombination.append(card)
        
        
        return (handScore, highestValue, highestCard, cardsOutsideCombination, handList)
        
        
    def compare_with(self, other):
        hand1 = self.handProcess(self.hand.split())
        hand2 = self.handProcess(other.hand.split())
        
        # Check winner by hand combination score
        # Then by the value of the card of the combination
        # Then by the highest card of the hand
        # Then, iteratively, by the value of the highest card outside the combination
        # Then, iteratively, by the highest card of the hand when not entirely equal
        # Else, it's a tie !
        if hand1[0] > hand2[0]:
            return "Win"
        elif hand1[0] < hand2[0]:
            return "Loss"
        elif hand1[1] > hand2[1]:
            return "Win"
        elif hand1[1] < hand2[1]:
            return "Loss"
        else:
            if hand1[2] > hand2[2]:
                return "Win"
            elif hand1[2] < hand2[2]:
                return "Loss"
            for card in range(0, len(hand1[3])):
                if hand1[3][card] > hand2[3][card]:
                    return "Win"
                elif hand1[3][card] < hand2[3][card]:
                    return "Loss"
            for card in range(len(hand1[4])-1, -1, -1):
                if hand1[4][card] > hand2[4][card]:
                    return "Win"
                elif hand1[4][card] < hand2[4][card]:
                    return "Loss"
            return "Tie"

class PokerHand(object):

    def __init__(self, hand):
        self.st = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']
        self.h = sorted(hand.split(), key=lambda x: self.st.index(x[0]))
        self.v=lambda i,j :"Win" if self.st.index(i) > self.st.index(j) else 'Loss'
    
    def compare_with(self, o):
        o = sorted(o.h, key=lambda x: self.st.index(x[0]))
        if o == self.h : return 'Tie'
        s,s1 = [i[0] for i in o],[i[0] for i in self.h]
        a = [self.straight_flush, self.four_of_kind, self.full_house, self.flush, self.straight, self.three_of_kind,self.two_pair, self.one_pair]
        b = [0, 1, 2, self.flush_, self.straight_,5,6, 7]
        for u, j in enumerate(zip(a, b)):
            k,l = j[0],j[1]
            i,j = k(self.h),k(o)
            if i != j : return 'Win' if i else 'Loss'
            elif i == j == 1 : 
                if u in [3,4] : return l(s1,s) 
                return self.common(s1, s)
        for i, j in zip(s1[::-1], s[::-1]):
            if i != j : return self.v(i,j)
        return 'Tie'
    
    def straight_flush(self, p):
        return len(set([i[1] for i in p])) == 1 and all(abs(self.st.index(p[i][0]) - self.st.index(p[i + 1][0])) == 1 for i in range(len(p) - 1))

    def four_of_kind(self, p):
        p = [i[0] for i in p]
        return any(p.count(i) == 4 for i in p)

    def full_house(self, p):
        p = [i[0] for i in p]
        return any(p.count(i) == 3 for i in p) and any(p.count(i) == 2 for i in p)

    def flush(self, p):
        return len(set([i[1] for i in p])) == 1

    def straight(self, p):
        return all(abs(self.st.index(p[i][0]) - self.st.index(p[i + 1][0])) == 1 for i in range(len(p) - 1))

    def three_of_kind(self, p):
        p = [i[0] for i in p]
        return any(p.count(i) == 3 for i in p)

    def two_pair(self, p):
        p = [i[0] for i in p]
        return [p.count(i) for i in set(p)].count(2) == 2

    def one_pair(self, p):
        p = [i[0] for i in p]
        return [p.count(i) for i in set(p)].count(2) == 1

    def high_card(self, p):
        p = [i[0] for i in p]
        return max(p, key=lambda x: self.st.index(x))

    def straight_flush_(self, i, j):
        return 'Win' if self.v(i[-1],j[-1]) else 'Loss'

    def common(self,i,j):
        findi = sorted([k for k in set(i)], key=lambda x: (-i.count(x), -self.st.index(x)))
        findj = sorted([k for k in set(j)], key=lambda x: (-j.count(x), -self.st.index(x)))
        for k, l in zip(findi, findj):
            if k != l : return 'Win' if self.st.index(k) > self.st.index(l) else 'Loss'
                
    def flush_(self, i, j):
        for k, l in zip(i[::-1], j[::-1]):
            if k != l : return 'Win' if self.st.index(k) > self.st.index(l) else 'Loss'

    def straight_(self, i, j):
        return 'Tie' if self.st.index(i[-1]) == self.st.index(j[-1]) else 'Win' if self.st.index(i[-1]) > self.st.index(j[-1]) else 'Loss'