from axelrod.action import Action

from axelrod.player import Player

C, D = Action.C, Action.D

class Capri(Player):
    """
    CAPRI is a memory-3 strategy proposed in [Murase2020]_. Its behavior is
    defined by the following five rules applied to the last 3 moves of the
    player and the opponent:

    - C: Cooperate at mutual cooperation.  This rule prescribes c at (ccc, ccc).
    - A: Accept punishment when you mistakenly defected from mutual cooperation.
      This rule prescribes c at (ccd, ccc), (cdc, ccd), (dcc, cdc), and (ccc,
      dcc).
    - P: Punish your co-player by defecting once when he defected from mutual
      cooperation.  This rule prescribes d at (ccc, ccd), and then c at (ccd,
      cdc), (cdc, dcc), and (dcc, ccc).
    - R: Recover cooperation when you or your co-player cooperated at mutual
      defection.  This rule prescribes c at (ddd, ddc), (ddc, dcc), (dcc, ccc),
      (ddc, ddd), (dcc, ddc), (ccc, dcc), (ddc, ddc), and (dcc, dcc).
    - I: In all the other cases, defect.

    The original implementation used in [Murase2020]_ is available at
    https://github.com/yohm/sim_exhaustive_m3_PDgame

    Names:

    - CAPRI: Original Name by Y. Murase et al. [Murase2020]_
    """

    name = "CAPRI"
    classifier = {
        "memory_depth": 3,
        "stochastic": False,
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def strategy(self, opponent: Player) -> Action:
        # initial history profile is full cooperation
        hist = list(zip(self.history[-3:], opponent.history[-3:]))
        while len(hist) < 3:
            hist.insert(0, (C, C))

        if hist == [(C, C), (C, C), (C, C)]:  # Rule: C
            return C
        if hist == [(C, C), (C, C), (D, C)]:  # Rule: A
            return C
        if hist == [(C, C), (D, C), (C, D)]:
            return C
        if hist == [(D, C), (C, D), (C, C)]:
            return C
        if hist == [(C, D), (C, C), (C, C)]:  # Rule: A & R2
            return C
        if hist == [(C, C), (C, C), (C, D)]:  # Rule: P
            return D
        if hist == [(C, C), (C, D), (D, C)]:
            return C
        if hist == [(C, D), (D, C), (C, C)]:
            return C
        if hist == [(D, C), (C, C), (C, C)]:  # Rule: P & R1
            return C
        if hist == [(D, D), (D, D), (D, C)]:  # Rule: R1
            return C
        if hist == [(D, D), (D, C), (C, C)]:
            return C
        if hist == [(D, D), (D, D), (C, D)]:  # Rule: R2
            return C
        if hist == [(D, D), (C, D), (C, C)]:
            return C
        if hist == [(D, D), (D, D), (C, C)]:  # Rule: R3
            return C
        if hist == [(D, D), (C, C), (C, C)]:
            return C
        return D