# Path: lcs/Perception.py
# class Perception(collections.abc.Sequence):
#     """
#     Represents current state of the environment at given time instance.
#     By default each environment attribute is represented as `str` type.
#     """
# 
#     __slots__ = ['_items', 'oktypes']
# 
#     def __init__(self, observation, oktypes=(str,)):
#         assert all(type(e) in oktypes for e in observation)
#         self._items = tuple(observation)
# 
#     @classmethod
#     def empty(cls):
#         return cls([], oktypes=(None,))
# 
#     def __hash__(self):
#         return hash(self._items)
# 
#     def __getitem__(self, i):
#         return self._items[i]
# 
#     def __len__(self) -> int:
#         return len(self._items)
# 
#     def __repr__(self):
#         return ' '.join(map(str, self))
# 
#     def __eq__(self, other):
#         return all(p0 == p1 for p0, p1 in zip(self, other))
# 
# Path: lcs/agents/acs2/Configuration.py
# class Configuration(acs.Configuration):
#     def __init__(self, **kwargs):
#         super(Configuration, self).__init__(**kwargs)
# 
#         # RL discount factor
#         self.gamma: float = kwargs.get('gamma', 0.95)
# 
#         # use of Probability-Enhanced-Effects (PEE)
#         self.do_pee: bool = kwargs.get('do_pee', False)
# 
#         # use of genetic generalization algorithm
#         self.do_ga: bool = kwargs.get('do_ga', False)
# 
#         self.theta_ga: int = kwargs.get('theta_ga', 100)
# 
#         # probability of mutating single attribute
#         self.mu: float = kwargs.get('mu', 0.3)
# 
#         # probability of crossover
#         self.chi: float = kwargs.get('chi', 0.8)
# 
#         # use of action planning mechanism
#         self.do_action_planning: bool = kwargs.get('do_action_planning', False)
# 
#         self.action_planning_frequency: int = kwargs.get(
#             'action_planning_frequency', 50)
# 
#         # initial quality assigned to classifiers
#         self.initial_q: float = kwargs.get('initial_q', 0.5)
# 
#         # probability of executing biased exploration phases
#         self.biased_exploration_prob: float = kwargs.get(
#             'biased_exploration_prob', 0.05)
# 
#         self.action_selector = kwargs.get('action_selector', EpsilonGreedy)(
#             all_actions=self.number_of_possible_actions,
#             epsilon=self.epsilon,
#             biased_exploration_prob=self.biased_exploration_prob
#         )
# 
#     def __str__(self) -> str:
#         return str(vars(self))


import logging
import random
import lcs.agents.acs as acs
import lcs.agents.acs2.alp as alp_acs2
import lcs.strategies.anticipatory_learning_process as alp
import lcs.strategies.genetic_algorithms as ga
import lcs.strategies.reinforcement_learning as rl
from itertools import chain
from typing import Optional, List
from lcs import Perception
from lcs.agents.acs2 import Configuration
from . import Classifier
                      if classifier.is_enhanceable()]

        logging.debug(
            "Applying enhanced effect part; number of candidates={}; " +
            "previous situation: {}".format(
                len(candidates), previous_situation))

        # If there are less than 2 candidates, don't do it
        if len(candidates) < 2:
            return

        for candidate in candidates:
            candidates2 = [classifier for classifier in candidates
                           if candidate != classifier]
            if len(candidates2) > 0:
                merger = random.choice(candidates2)
                new_classifier = candidate.merge_with(merger,
                                                      previous_situation,
                                                      time)
                if new_classifier is not None:
                    candidate.reverse_increase_quality()
                    alp.add_classifier(new_classifier, action_set, new_list,
                                       cfg.theta_exp)

        return new_list

    @staticmethod
    def apply_alp(population: ClassifiersList,
                  match_set: ClassifiersList,
                  action_set: ClassifiersList,

# Path: lcs/Perception.py
# class Perception(collections.abc.Sequence):
#     """
#     Represents current state of the environment at given time instance.
#     By default each environment attribute is represented as `str` type.
#     """
# 
#     __slots__ = ['_items', 'oktypes']
# 
#     def __init__(self, observation, oktypes=(str,)):
#         assert all(type(e) in oktypes for e in observation)
#         self._items = tuple(observation)
# 
#     @classmethod
#     def empty(cls):
#         return cls([], oktypes=(None,))
# 
#     def __hash__(self):
#         return hash(self._items)
# 
#     def __getitem__(self, i):
#         return self._items[i]
# 
#     def __len__(self) -> int:
#         return len(self._items)
# 
#     def __repr__(self):
#         return ' '.join(map(str, self))
# 
#     def __eq__(self, other):
#         return all(p0 == p1 for p0, p1 in zip(self, other))
# 
# Path: lcs/agents/acs2/Configuration.py
# class Configuration(acs.Configuration):
#     def __init__(self, **kwargs):
#         super(Configuration, self).__init__(**kwargs)
# 
#         # RL discount factor
#         self.gamma: float = kwargs.get('gamma', 0.95)
# 
#         # use of Probability-Enhanced-Effects (PEE)
#         self.do_pee: bool = kwargs.get('do_pee', False)
# 
#         # use of genetic generalization algorithm
#         self.do_ga: bool = kwargs.get('do_ga', False)
# 
#         self.theta_ga: int = kwargs.get('theta_ga', 100)
# 
#         # probability of mutating single attribute
#         self.mu: float = kwargs.get('mu', 0.3)
# 
#         # probability of crossover
#         self.chi: float = kwargs.get('chi', 0.8)
# 
#         # use of action planning mechanism
#         self.do_action_planning: bool = kwargs.get('do_action_planning', False)
# 
#         self.action_planning_frequency: int = kwargs.get(
#             'action_planning_frequency', 50)
# 
#         # initial quality assigned to classifiers
#         self.initial_q: float = kwargs.get('initial_q', 0.5)
# 
#         # probability of executing biased exploration phases
#         self.biased_exploration_prob: float = kwargs.get(
#             'biased_exploration_prob', 0.05)
# 
#         self.action_selector = kwargs.get('action_selector', EpsilonGreedy)(
#             all_actions=self.number_of_possible_actions,
#             epsilon=self.epsilon,
#             biased_exploration_prob=self.biased_exploration_prob
#         )
# 
#     def __str__(self) -> str:
#         return str(vars(self))


# Path: lcs/agents/acs2/ClassifiersList.py
import logging
import random
import lcs.agents.acs as acs
import lcs.agents.acs2.alp as alp_acs2
import lcs.strategies.anticipatory_learning_process as alp
import lcs.strategies.genetic_algorithms as ga
import lcs.strategies.reinforcement_learning as rl
from itertools import chain
from typing import Optional, List
from lcs import Perception
from lcs.agents.acs2 import Configuration
from . import Classifier

                      if classifier.is_enhanceable()]

        logging.debug(
            "Applying enhanced effect part; number of candidates={}; " +
            "previous situation: {}".format(
                len(candidates), previous_situation))

        # If there are less than 2 candidates, don't do it
        if len(candidates) < 2:
            return

        for candidate in candidates:
            candidates2 = [classifier for classifier in candidates
                           if candidate != classifier]
            if len(candidates2) > 0:
                merger = random.choice(candidates2)
                new_classifier = candidate.merge_with(merger,
                                                      previous_situation,
                                                      time)
                if new_classifier is not None:
                    candidate.reverse_increase_quality()
                    alp.add_classifier(new_classifier, action_set, new_list,
                                       cfg.theta_exp)

        return new_list

    @staticmethod
    def apply_alp(population: ClassifiersList,
                  match_set: ClassifiersList,
                  action_set: ClassifiersList,

                  p0: Perception,