# contributed by mrfesol (Tomasz Wesolowski)

from easyAI import TwoPlayerGame
from easyAI.Player import Human_Player
from copy import deepcopy
from easyAI.AI.DictTranspositionTable import DictTranspositionTable
from easyAI.AI.Hashes import JSWHashTranspositionTable


class Chopsticks(TwoPlayerGame):
    """
    Simple game you can play with your fingers.
    See the rules on http://en.wikipedia.org/wiki/Chopsticks_(hand_game)
    Here, for simplicity, you can do only taps and splits.

    A move consists of:
        (type - split or tap, touching hand, touched hand, number of sticks 'transferred')
    for instance:
        ('split', 0, 1, 2)
    means that we transfer 2 sticks from hand 0 to hand 1
    and...
        ('tap', 1, 0, 3)
    indicates that we tap opponent's hand 0 with our hand 1 holding 3 sticks

    Type 'show moves' before any move and do a move by "move #XX"
    """

    def __init__(self, players, numhands=2):
        self.players = players
        self.numplayers = len(self.players)
        self.numhands = numhands
        self.current_player = 1  # player 1 starts.

        hand = [1 for hand in range(self.numhands)]
        self.hands = [hand[:] for player in range(self.numplayers)]

    def possible_moves(self):
        moves = []
        # splits
        for h1 in range(self.numhands):
            for h2 in range(self.numhands):
                if h1 == h2:
                    continue
                hand1 = self.hands[self.current_player - 1][h1]
                hand2 = self.hands[self.current_player - 1][h2]
                for i in range(1, 1 + min(hand1, 5 - hand2)):
                    move = ("split", h1, h2, i)
                    if hand1 != hand2 + i and self.back_to_startstate(move) == False:
                        moves.append(move)

        # taps
        for i in range(self.numhands):
            for j in range(self.numhands):
                hand_player = self.hands[self.current_player - 1][i]
                hand_opp = self.hands[self.opponent_index - 1][j]
                if hand_player != 0 and hand_opp != 0:
                    moves.append(("tap", i, j, self.hands[self.current_player - 1][i]))
        return moves

    def make_move(self, move):
        type, one, two, value = move
        if type == "split":
            self.hands[self.current_player - 1][one] -= value
            self.hands[self.current_player - 1][two] += value
        else:
            self.hands[self.opponent_index - 1][two] += value

        for player in range(self.numplayers):
            for hand in range(self.numhands):
                if self.hands[player][hand] >= 5:
                    self.hands[player][hand] = 0

    def lose(self):
        return max(self.hands[self.current_player - 1]) == 0

    def win(self):
        return max(self.hands[self.opponent_index - 1]) == 0

    def is_over(self):
        return self.lose() or self.win()

    def show(self):
        for i in range(self.numplayers):
            print("Player %d: " % (i + 1)),
            for j in range(self.numhands):
                if self.hands[i][j] > 0:
                    print("|" * self.hands[i][j] + "\t"),
                else:
                    print("x\t"),
            print("")

    def scoring(self):
        """
        Very simple heuristic counting 'alive' hands
        """
        if self.lose():
            return -100
        if self.win():
            return 100
        alive = [0] * 2
        for player in range(self.numplayers):
            for hand in range(len(self.hands[player])):
                alive[player] += self.hands[player][hand] > 0
        return alive[self.current_player - 1] - alive[self.opponent_index - 1]

    def ttentry(self):
        """
        Returns game entry
        """
        entry = [
            self.hands[i][j]
            for i in range(self.numplayers)
            for j in range(self.numhands)
        ]
        entry = entry + [self.current_player]
        return tuple(entry)

    def back_to_startstate(self, move):
        """
        Checking if move will cause returning to start state - never-ending loop protection
        """
        nextstate = self.copy()
        nextstate.make_move(move)
        hands_min = min([min(nextstate.hands[i]) for i in range(self.numplayers)])
        hands_max = max([max(nextstate.hands[i]) for i in range(self.numplayers)])
        return hands_min == 1 and hands_max == 1


if __name__ == "__main__":
    from easyAI import Negamax, AI_Player, SSS, DUAL
    from easyAI.AI.TranspositionTable import TranspositionTable

    ai_algo_neg = Negamax(4)
    ai_algo_sss = SSS(4)
    dict_tt = DictTranspositionTable(32, JSWHashTranspositionTable())
    ai_algo_dual = DUAL(4, tt=TranspositionTable(dict_tt))
    Chopsticks(
        [AI_Player(ai_algo_neg), AI_Player(ai_algo_dual)]
    ).play()  # first player never wins

    print("-" * 10)
    print("Statistics of custom dictionary:")
    print("Calls of hash: ", dict_tt.num_calls)
    print("Collisions: ", dict_tt.num_collisions)
