"""Utilities used by various strategies"""
import functools
import collections
import itertools

from axelrod import RoundRobin, update_history
from axelrod import Actions

from axelrod.strategies.cycler import Cycler

C, D = Actions.C, Actions.D

def detect_cycle(history, min_size=1, offset=0):
    """Detects cycles in the sequence history.

    Mainly used by hunter strategies.

    Parameters

    history: sequence of C and D
        The sequence to look for cycles within
    min_size: int, 1
        The minimum length of the cycle
    offset: int, 0
        The amount of history to skip initially
    """
    history_tail = history[-offset:]
    for i in range(min_size, len(history_tail) // 2):
        cycle = tuple(history_tail[:i])
        for j,  elem in enumerate(history_tail):
            if elem != cycle[j % len(cycle)]:
                break
        if j == len(history_tail) - 1:
            # We made it to the end, is the cycle itself a cycle?
            # I.E. CCC is not ok as cycle if min_size is really 2
            # Since this is the same as C
            return cycle
    return None


def limited_simulate_play(player_1, player_2, h1):
    """Here we want to replay player_1's history to player_2, allowing
    player_2's strategy method to set any internal variables as needed. If you
    need a more complete simulation, see `simulate_play` in player.py. This
    function is specifically designed for the needs of MindReader."""
    h2 = player_2.strategy(player_1)
    update_history(player_1, h1)
    update_history(player_2, h2)

def simulate_match(player_1, player_2, strategy, rounds=10):
    """Simulates a number of matches."""
    for match in range(rounds):
        limited_simulate_play(player_1, player_2, strategy)

def look_ahead(player_1, player_2, game, rounds=10):
    """Looks ahead for `rounds` and selects the next strategy appropriately."""
    results = []

    # Simulate plays for `rounds` rounds
    strategies = [C, D]
    for strategy in strategies:
        # Instead of a deepcopy, create a new opponent and play out the history
        opponent_ = player_2.clone()
        player_ = Cycler(strategy) # Either cooperator or defector
        for h1 in player_1.history:
            limited_simulate_play(player_, opponent_, h1)

        round_robin = RoundRobin(players=[player_, opponent_], game=game,
                                 turns=rounds)
        simulate_match(player_, opponent_, strategy, rounds)
        results.append(round_robin._calculate_scores(player_, opponent_)[0])

    return strategies[results.index(max(results))]


class Memoized(object):
   """Decorator. Caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned
   (not reevaluated). From:
   https://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
   """
   def __init__(self, func):
        self.func = func
        self.cache = {}

   def __call__(self, *args):
        if not isinstance(args, collections.Hashable):
            # uncacheable. a list, for instance.
            # better to not cache than blow up.
            return self.func(*args)
        if args in self.cache:
            return self.cache[args]
        else:
            value = self.func(*args)
            self.cache[args] = value
            return value

   def __repr__(self):
        """Return the function's docstring."""
        return self.func.__doc__

   def __get__(self, obj, objtype):
        """Support instance methods."""
        return functools.partial(self.__call__, obj)


@Memoized
def recursive_thue_morse(n):
    """The recursive definition of the Thue-Morse sequence. The first few terms
    of the Thue-Morse sequence are:
    0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0 . . ."""

    if n == 0:
        return 0
    if n % 2 == 0:
        return recursive_thue_morse(n / 2)
    if n % 2 == 1:
        return 1 - recursive_thue_morse((n - 1) / 2)


def thue_morse_generator(start=0):
    """A generator for the Thue-Morse sequence."""

    for n in itertools.count(start):
        yield recursive_thue_morse(n)

@@ -241,16 +241,17 @@
 ions.D%0A%0A
+%0A
 def dete
@@ -1565,16 +1565,17 @@
 2, h2)%0A%0A
+%0A
 def simu
@@ -1761,16 +1761,17 @@
 ategy)%0A%0A
+%0A
 def look
@@ -2161,16 +2161,17 @@
 trategy)
+ 
  # Eithe
@@ -2630,16 +2630,17 @@
 ct):%0A   
+ 
 %22%22%22Decor
@@ -2702,16 +2702,17 @@
 called.%0A
+ 
    If ca
@@ -2779,16 +2779,17 @@
 rned%0A   
+ 
 (not ree
@@ -2805,16 +2805,17 @@
 . From:%0A
+ 
    https
@@ -2872,20 +2872,23 @@
 oize%0A   
+ 
 %22%22%22%0A
+%0A 
    def _
@@ -2958,16 +2958,17 @@
 e = %7B%7D%0A%0A
+ 
    def _
@@ -2985,24 +2985,24 @@
 lf, *args):%0A
-
         if n
@@ -3358,16 +3358,17 @@
  value%0A%0A
+ 
    def _
@@ -3382,16 +3382,16 @@
 (self):%0A
-
         
@@ -3463,16 +3463,17 @@
 _doc__%0A%0A
+ 
    def _

Fix PEP8 errors