from typing import List, Optional

from axelrod.action import Action

from axelrod.player import Player

Vector = List[float]

C, D = Action.C, Action.D

class NaiveProber(Player):
    """
    Like tit-for-tat, but it occasionally defects with a small probability.

    Names:

    - Naive Prober: [Li2011]_
    """

    name = "Naive Prober"
    classifier = {
        "memory_depth": 1,
        "stochastic": True,
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def __init__(self, p: float = 0.1) -> None:
        """
        Parameters
        ----------
        p, float
            The probability to defect randomly
        """
        super().__init__()
        self.p = p
        if (self.p == 0) or (self.p == 1):
            self.classifier["stochastic"] = False

    def strategy(self, opponent: Player) -> Action:
        """Actual strategy definition that determines player's action."""
        # First move
        if len(self.history) == 0:
            return C
        # React to the opponent's last move
        if opponent.history[-1] == D:
            return D
        # Otherwise cooperate, defect with probability 1 - self.p
        if self.p == 0:
            return C
        if self.p == 1:
            return D
        choice = self._random.random_choice(1 - self.p)
        return choice

class RemorsefulProber(NaiveProber):
    """
    Like Naive Prober, but it remembers if the opponent responds to a random
    defection with a defection by being remorseful and cooperating.

    For reference see: [Li2011]_. A more complete description is given in "The
    Selfish Gene" (https://books.google.co.uk/books?id=ekonDAAAQBAJ):

    "Remorseful Prober remembers whether it has just spontaneously defected, and
    whether the result was prompt retaliation. If so, it 'remorsefully' allows
    its opponent 'one free hit' without retaliating."

    Names:

    - Remorseful Prober: [Li2011]_
    """

    name = "Remorseful Prober"
    classifier = {
        "memory_depth": 2,  # It remembers if its previous move was random
        "stochastic": True,
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def __init__(self, p: float = 0.1) -> None:
        super().__init__(p)
        self.probing = False

    def strategy(self, opponent: Player) -> Action:
        """Actual strategy definition that determines player's action."""
        # First move
        if len(self.history) == 0:
            return C
        # React to the opponent's last move
        if opponent.history[-1] == D:
            if self.probing:
                self.probing = False
                return C
            return D

        # Otherwise cooperate with probability 1 - self.p
        if self.p == 1:
            self.probing = True
            return D
        elif self.p == 0 or self._random.random() < 1 - self.p:
            self.probing = False
            return C

        self.probing = True
        return D