import itertools

from typing import Any, Dict, List, Optional, Sequence, Text, Tuple

from axelrod.action import Action

from axelrod.evolvable_player import (
    EvolvablePlayer,
    InsufficientParametersError,
    copy_lists,
)

from axelrod.player import Player

C, D = Action.C, Action.D

actions = (C, D)

Transition = Tuple[int, Action, int, Action]

class FSMPlayer(Player):
    """Abstract base class for finite state machine players."""

    name = "FSM Player"

    classifier: Dict[Text, Any] = {
        "memory_depth": 1,
        "stochastic": False,
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def __init__(
        self,
        transitions: Tuple[Transition, ...] = ((1, C, 1, C), (1, D, 1, D)),
        initial_state: int = 1,
        initial_action: Action = C,
    ) -> None:
        Player.__init__(self)
        self.initial_state = initial_state
        self.initial_action = initial_action
        self.fsm = SimpleFSM(transitions, initial_state)

    def strategy(self, opponent: Player) -> Action:
        """Actual strategy definition that determines player's action."""
        if len(self.history) == 0:
            return self.initial_action
        else:
            return self.fsm.move(opponent.history[-1])

class EvolvedFSM6(FSMPlayer):
    """
    An 6 state FSM player trained with an evolutionary algorithm.

    Evolved using axelrod-dojo version 0.0.8 and axelrod version 4.10.0, trained to maximize score against
    the short_run_time_strategies with 10 machine states for 500 generations, population size of 40,
    mutation rate at 0.1, bottleneck at 10, 200 turns, and 0 noise. The resulting strategy had only 6
    states in its accessible component.

    Names:

        - Evolved FSM 6: Original name by Frederick Vincent & Dashiell Fryer
    """

    name = "Evolved FSM 6"
    classifier = {
        "memory_depth": float("inf"),
        "stochastic": False,
        "long_run_time": False,
        "inspects_source": False,
        "manipulates_source": False,
        "manipulates_state": False,
    }

    def __init__(self) -> None:
        transitions = (
            (2, C, 2, D),
            (2, D, 7, D),
            (3, C, 6, D),
            (3, D, 4, C),
            (4, C, 4, C),
            (4, D, 6, D),
            (5, C, 2, D),
            (5, D, 7, D),
            (6, C, 3, C),
            (6, D, 5, D),
            (7, C, 2, C),
            (7, D, 3, D),
        )

        super().__init__(
            transitions=transitions, initial_state=4, initial_action=C
        )