class TestUnoHumanPlayer(unittest.TestCase):

    def setUp(self):
        self.player = HumanUnoPlayer()
        self.other = HumanUnoPlayer()
        self.discard = DiscardPile()

    def test_valid_color_move(self):
        self.player.hand = [UnoCard(0, 'Number', 'Red')]
        self.discard.cards = [UnoCard(7, 'Number', 'Red')]
        self.player.play_card(str(self.player.hand[0]), self.discard)
        self.assertTrue(self.player.hand_is_empty())
        self.assertEqual(str(self.discard.peek()), str(UnoCard(0, 'Number', 'Red')))

    def test_valid_number_move(self):
        self.player.hand = [UnoCard(0, 'Number', 'Red')]
        self.discard.cards = [UnoCard(0, 'Number', 'Yellow')]
        self.player.play_card(str(self.player.hand[0]), self.discard)
        self.assertTrue(self.player.hand_is_empty())
        self.assertEqual(str(self.discard.peek()), str(UnoCard(0, 'Number', 'Red')))

    def test_valid_wild_move(self):
        self.player.hand = [UnoCard(-1, 'Wild', 'Black')]
        self.discard.cards = [UnoCard(0, 'Number', 'Yellow')]
        self.player.play_card(str(self.player.hand[0]), self.discard)
        self.assertTrue(self.player.hand_is_empty())
        self.assertEqual(str(self.discard.peek()), str(UnoCard(-1, 'Wild', 'Black')))

    def test_invalid_move(self):
        self.player.hand = [UnoCard(0, 'Number', 'Yellow')]
        self.discard.cards = [UnoCard(7, 'Number', 'Red')]
        self.player.play_card(str(self.player.hand[0]), self.discard)
        self.assertEqual(str(self.player.hand[0]), str(UnoCard(0, 'Number', 'Yellow')))
        self.assertEqual(str(self.discard.peek()), str(UnoCard(7, 'Number', 'Red')))
 def __init__(self, player_count=2):
     self.players = []
     self.deck = Deck()
     self.pile = DiscardPile()
     self.deck.shuffle()
     for i in range(player_count):
         self.players.append(ERSPlayer())
     self.deal_all()
    def __init__(self, player_count = 2):
        self.players = []
        self.deck = UnoDeck()
        self.deck.shuffle()
        self.discard = DiscardPile()
        self.players.append(HumanUnoPlayer('Human 1'))
        for i in range(player_count - 1):
            self.players.append(AIUnoPlayer(_('Computer {}').format(i + 1)))

        self.deal(7)
        self.current_player = 0
        self.TURN_CONS = 1
class ERSTable(Table):
   
    def __init__(self, player_count=2):
        self.players = []
        self.deck = Deck()
        self.pile = DiscardPile()
        self.deck.shuffle()
        for i in range(player_count):
            self.players.append(ERSPlayer())
        self.deal_all()

    def get_winner(self):
        for i in range(len(self.players)):
            return i

    def play_game(self):
        player = 0
        while not self.winner():
            value = 0
            if len(self.pile.cards) > 0:
                value = self.pile.peek().value
            if value == 1 or value > 10:
                player = self.war(player)
            else:
                if player == 0:
                    raw_input(_("Play a card!"))
                card = self.players[player].flip()
                print(_("Player {} plays the {}!").format(player, card))
                self.pile.add(card)
            self.wait_for_slap(0.25)
            player = ((player + 1) % len(self.players))
        print(_("Player {} wins!").format(self.get_winner()))

    def wait_for_slap(self, t):
        sleep(t)

    def war(self, player):
        value = self.pile.peek().value
        num = 0
        if value == 1:
            num = 4
        else:
            num = value - 10
        for i in range(num):
            card = self.players[player].flip()
            print(_("Player {} plays the {}!").format(player, card))
            self.pile.add(card)
            value = card.value
            self.wait_for_slap(0.25)
            if value == 1 or value > 10:
                return player
        self.players[player].cards = self.pile.cards + self.players[player].hand
        self.pile.cards = []
        return player - 1        

    def winner(self):
        for player in self.players:
            if len(player.hand) == 0:
                return True
        return False
 def setUp(self):
     self.pile = DiscardPile()
class TestDiscardPile(unittest.TestCase):

    def setUp(self):
        self.pile = DiscardPile()

    def test_add_to_discard_pile(self):
        size = len(self.pile.cards)
        c = Card(0, "Jokers")
        self.pile.add(c)
        self.assertEqual(len(self.pile.cards), size + 1)
        self.assertEqual(str(self.pile.cards[0]), "Joker")

    def test_peek(self):
        self.pile.add(Card(1, "Spades"))
        self.assertTrue(self.pile.peek().same_as(Card(1, "Spades")))

    def test_peek_multiple_cards(self):
        self.pile.add(Card(1, "Clubs"))
        self.pile.add(Card(1, "Diamonds"))
        self.pile.add(Card(1, "Hearts"))
        self.pile.add(Card(1, "Spades"))
        self.assertTrue(self.pile.peek().same_as(Card(1, "Spades")))

    def test_peek_no_cards(self):
        self.assertEqual(self.pile.peek(), None)

    def test_has_double(self):
        self.pile.add(Card(1, "Clubs"))
        self.pile.add(Card(1, "Diamonds"))
        self.assertTrue(self.pile.has_double())

    def test_hasnt_double(self):
        self.pile.add(Card(1, "Clubs"))
        self.pile.add(Card(2, "Diamonds"))
        self.assertFalse(self.pile.has_double())

    def test_hasnt_double_empty(self):
        self.assertFalse(self.pile.has_double())

    def test_hasnt_double_too_few_cards(self):
        self.pile.add(Card(6, "Spades"))
        self.assertFalse(self.pile.has_double())

    def test_has_sandwich(self):
        self.pile.add(Card(1, "Clubs"))
        self.pile.add(Card(2, "Hearts"))
        self.pile.add(Card(1, "Diamonds"))
        self.assertTrue(self.pile.has_sandwich())

    def test_hasnt_sandwich(self):
        self.pile.add(Card(2, "Clubs"))
        self.pile.add(Card(1, "Hearts"))
        self.pile.add(Card(1, "Diamonds"))
        self.assertFalse(self.pile.has_sandwich())

    def test_hasnt_sandwich_empty(self):
        self.assertFalse(self.pile.has_sandwich())

    def test_hasnt_sandwich_too_few_cards(self):
        self.pile.add(Card(1, "Clubs"))
        self.assertFalse(self.pile.has_sandwich())
        self.pile.add(Card(1, "Hearts"))
        self.assertFalse(self.pile.has_sandwich())
class UnoTable(Table):

    def __init__(self, player_count = 2):
        self.players = []
        self.deck = UnoDeck()
        self.deck.shuffle()
        self.discard = DiscardPile()
        self.players.append(HumanUnoPlayer('Human 1'))
        for i in range(player_count - 1):
            self.players.append(AIUnoPlayer(_('Computer {}').format(i + 1)))

        self.deal(7)
        self.current_player = 0
        self.TURN_CONS = 1

    def get_winner(self):
        return self.winner()

    def play_game(self):
        self.shuffle_and_turn()
        while not self.winner():
            result = None
            if len(self.deck.cards) == 0:
                self.shuffle_and_turn()
            print _("\n~~~~~{}'s Turn~~~~~\n").format(self.players[self.current_player])
            if self.players[self.current_player].player_type() == 1: #is AI
                move = self.players[self.current_player].find_best_move(self.players[(self.current_player + self.TURN_CONS) % (len(self.players))], self.discard)
                if move == _('Draw'):
                    print _('{} draws a card...').format(self.players[self.current_player])
                    self.players[self.current_player].draw_from(self.deck)
                    result = _('draw')
                else:
                    print _('{} plays a {}').format(self.players[self.current_player], move)
                    result = self.players[self.current_player].play_card(move, self.discard)
            else: #is Human
                self.print_output_for_human()
                move = raw_input(_('Please input your move, or draw:\n'))
                if move.lower().strip() == _('draw'): #draw a card
                    self.players[self.current_player].draw_from(self.deck)
                    print _('You draw a {}').format(self.players[self.current_player].hand[0])
                    result = _('draw')
                else: #play a card
                    result = self.players[self.current_player].play_card(move, self.discard)
                    if not result:
                        print _('That is not a valid card/move!\n')
                    else:
                        print _('\n{} plays a {}').format(self.players[self.current_player], move)
            if result:
                if len(self.players[self.current_player].hand) == 1:
                    print _('^^^^^ {} says UNO! ^^^^^').format(self.players[self.current_player])
                self.determine_next_turn(move)
        print _('{} wins!').format(self.get_winner())

    def winner(self):
        for i, player in enumerate(self.players):
            if len(player.hand) == 0:
                return player

    #gameplay helper functions
    def determine_next_turn(self, move):
        if _('Skip') in move:
            self.current_player = (self.current_player + 2 * self.TURN_CONS) % (len(self.players))
        elif _('Draw Two') in move:
            self.current_player = (self.current_player + self.TURN_CONS) % (len(self.players))
            self.players[self.current_player].draw_from(self.deck)
            self.players[self.current_player].draw_from(self.deck)
        elif _('Reverse') in move:
            self.TURN_CONS *= -1
            self.current_player = (self.current_player + self.TURN_CONS) % (len(self.players))
        elif _('Wild') in move and self.players[self.current_player].player_type() == 0:
            valid_color = False
            while not valid_color:
                color = raw_input(_('Please input the color for the wild card.\n'))
                if color in [_('Red'), _('Blue'), _('Green'), _('Yellow')]:
                    valid_color = True
                    self.discard.peek().set_color_of_wild(color)
                    self.current_player = (self.current_player + self.TURN_CONS) % (len(self.players))
        else:
            self.current_player = (self.current_player + self.TURN_CONS) % (len(self.players))

    def shuffle_and_turn(self):
        self.deck.cards.extend(self.discard.cards)
        self.discard.cards = []
        self.deck.shuffle()
        self.discard.add(self.deck.draw())

    def print_hand_count(self):
        s = ''
        for p in self.players:
            s += _('| {} has {} cards in hand.\n').format(p, len(p.hand))
        return s.strip()

    def print_output_for_human(self):
        print _('+==================================+\n| Cards in deck: {}').format(len(self.deck.cards))
        print _('| Top of the discard pile: {}\n|').format(self.discard.peek())
        print '{}'.format(self.print_hand_count())
        print '+==================================+\n'
        print _('***Your hand contains***\n{}\n').format(self.players[self.current_player].hand)
 def setUp(self):
     self.player = HumanUnoPlayer()
     self.other = HumanUnoPlayer()
     self.discard = DiscardPile()