# 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/yacs/yacs.py
# class Condition(ImmutableSequence):
#     WILDCARD = DontCare()
#     OK_TYPES = (str, DontCare)
# 
#     def __init__(self, observation):
#         obs = [DontCare() if str(o) == DontCare.symbol else o for o in
#                observation]
#         super().__init__(obs)
# 
#     @staticmethod
#     def random_matching(p0: Perception) -> Generator[Condition]:
#         max_val = len(p0) - 1
#         while True:
#             c = Condition(p0)
#             num_dont_cares = random.randint(0, max_val)
#             for i in random.sample(range(0, max_val), num_dont_cares):
#                 c[i] = DontCare()
#             yield c
# 
#     @property
#     def expected_improvements(self) -> List[float]:
#         return [f.eis if type(f) is DontCare else None for f in self]
# 
#     @property
#     def generality(self) -> int:
#         return sum(1 for f in self if type(f) is DontCare)
# 
#     def is_more_specialized(self, other: Condition) -> bool:
#         """Checks if other condition is more specialized"""
# 
#         def is_less_general(s, o):
#             if type(s) is DontCare:
#                 return True
# 
#             return s == o
# 
#         def is_different(s, o):
#             if type(s) is DontCare and type(o) is DontCare:
#                 return False
#             else:
#                 return s != o
# 
#         other_less_general = all(
#             is_less_general(s, o) for s, o in zip(self, other))
#         different_tokens = sum(
#             1 for s, o in zip(self, other) if is_different(s, o))
# 
#         return other_less_general and different_tokens > 0
# 
#     def is_more_general(self, other: Condition) -> bool:
#         """Checks if other condition is more general"""
# 
#         def is_more_general(s, o):
#             if type(o) is DontCare:
#                 return True
# 
#             return s == o
# 
#         def is_different(s, o):
#             if type(s) is DontCare and type(o) is DontCare:
#                 return False
#             else:
#                 return s != o
# 
#         other_more_general = all(
#             is_more_general(s, o) for s, o in zip(self, other))
#         different_tokens = sum(
#             1 for s, o in zip(self, other) if is_different(s, o))
# 
#         return other_more_general and different_tokens > 0
# 
#     @property
#     def specificity(self) -> int:
#         return len(self) - self.generality
# 
#     def does_match(self, p: Perception) -> bool:
#         for ci, oi in zip(self, p):
#             if type(ci) is not DontCare and ci != oi:
#                 return False
# 
#         return True
# 
#     def increase_eis(self, idx, beta):
#         t = self[idx]
#         if type(t) is DontCare:
#             t.eis = (1 - beta) * t.eis + beta
# 
#     def decrease_eis(self, idx, beta):
#         t = self[idx]
#         if type(t) is DontCare:
#             t.eis = (1 - beta) * t.eis
# 
#     def subsumes(self, other) -> bool:
#         raise NotImplementedError('YACS has no subsume operator')
# 
# class DontCare:
#     symbol: str = '#'
#     eis: float = 0.0  # expected improvement by specialization
# 
#     def __repr__(self):
#         return self.symbol


import random
import pytest
from lcs import Perception
from lcs.agents.yacs.yacs import Condition, DontCare
        ('####', '###1', False),
        ('###1', '###1', False),
        ('##11', '###1', True),
        ('##11', '####', True),
        ('1#11', '###2', False),
        ('1#11', '###1', True),
    ])
    def test_should_determine_more_general(self, _self, _other, _res):
        # given
        cond = Condition(_self)
        other = Condition(_other)

        # randomly insert some eis values
        for t in cond:
            if type(t) is DontCare and random.random() < 0.5:
                t.eis = random.random()

        for t in other:
            if type(t) is DontCare and random.random() < 0.5:
                t.eis = random.random()

        # then
        assert cond.is_more_general(other) == _res

    @pytest.mark.parametrize('_c, _p, _res', [
        ("#000", "0000", True),
        ("#000", "1000", True),
        ("#000", "1001", False),
    ])
    def test_should_match_perception(self, _c, _p, _res):

# 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/yacs/yacs.py
# class Condition(ImmutableSequence):
#     WILDCARD = DontCare()
#     OK_TYPES = (str, DontCare)
# 
#     def __init__(self, observation):
#         obs = [DontCare() if str(o) == DontCare.symbol else o for o in
#                observation]
#         super().__init__(obs)
# 
#     @staticmethod
#     def random_matching(p0: Perception) -> Generator[Condition]:
#         max_val = len(p0) - 1
#         while True:
#             c = Condition(p0)
#             num_dont_cares = random.randint(0, max_val)
#             for i in random.sample(range(0, max_val), num_dont_cares):
#                 c[i] = DontCare()
#             yield c
# 
#     @property
#     def expected_improvements(self) -> List[float]:
#         return [f.eis if type(f) is DontCare else None for f in self]
# 
#     @property
#     def generality(self) -> int:
#         return sum(1 for f in self if type(f) is DontCare)
# 
#     def is_more_specialized(self, other: Condition) -> bool:
#         """Checks if other condition is more specialized"""
# 
#         def is_less_general(s, o):
#             if type(s) is DontCare:
#                 return True
# 
#             return s == o
# 
#         def is_different(s, o):
#             if type(s) is DontCare and type(o) is DontCare:
#                 return False
#             else:
#                 return s != o
# 
#         other_less_general = all(
#             is_less_general(s, o) for s, o in zip(self, other))
#         different_tokens = sum(
#             1 for s, o in zip(self, other) if is_different(s, o))
# 
#         return other_less_general and different_tokens > 0
# 
#     def is_more_general(self, other: Condition) -> bool:
#         """Checks if other condition is more general"""
# 
#         def is_more_general(s, o):
#             if type(o) is DontCare:
#                 return True
# 
#             return s == o
# 
#         def is_different(s, o):
#             if type(s) is DontCare and type(o) is DontCare:
#                 return False
#             else:
#                 return s != o
# 
#         other_more_general = all(
#             is_more_general(s, o) for s, o in zip(self, other))
#         different_tokens = sum(
#             1 for s, o in zip(self, other) if is_different(s, o))
# 
#         return other_more_general and different_tokens > 0
# 
#     @property
#     def specificity(self) -> int:
#         return len(self) - self.generality
# 
#     def does_match(self, p: Perception) -> bool:
#         for ci, oi in zip(self, p):
#             if type(ci) is not DontCare and ci != oi:
#                 return False
# 
#         return True
# 
#     def increase_eis(self, idx, beta):
#         t = self[idx]
#         if type(t) is DontCare:
#             t.eis = (1 - beta) * t.eis + beta
# 
#     def decrease_eis(self, idx, beta):
#         t = self[idx]
#         if type(t) is DontCare:
#             t.eis = (1 - beta) * t.eis
# 
#     def subsumes(self, other) -> bool:
#         raise NotImplementedError('YACS has no subsume operator')
# 
# class DontCare:
#     symbol: str = '#'
#     eis: float = 0.0  # expected improvement by specialization
# 
#     def __repr__(self):
#         return self.symbol


# Path: tests/lcs/agents/yacs/test_Condition.py
import random
import pytest
from lcs import Perception
from lcs.agents.yacs.yacs import Condition, DontCare

        ('####', '###1', False),
        ('###1', '###1', False),
        ('##11', '###1', True),
        ('##11', '####', True),
        ('1#11', '###2', False),
        ('1#11', '###1', True),
    ])
    def test_should_determine_more_general(self, _self, _other, _res):
        # given
        cond = Condition(_self)
        other = Condition(_other)

        # randomly insert some eis values
        for t in cond:
            if type(t) is DontCare and random.random() < 0.5:
                t.eis = random.random()

        for t in other:
            if type(t) is DontCare and random.random() < 0.5:
                t.eis = random.random()

        # then
        assert cond.is_more_general(other) == _res

    @pytest.mark.parametrize('_c, _p, _res', [
        ("#000", "0000", True),
        ("#000", "1000", True),
        ("#000", "1001", False),
    ])
    def test_should_match_perception(self, _c, _p, _res):

        assert Condition(_c).does_match(Perception(_p)) is _res