# Path: mau_mau/constants.py
# class DECK:
#     SEVEN = 7
#     EIGHT = 8
#     JACK = "Jack"
#     VALUES = [SEVEN, EIGHT, 9, 10, JACK, "Queen", "King", "Ace"]
#     SUITS = ["♦", "♥", "♠", "♣"]
# 
# Path: mau_mau/objects.py
# class Stock(_CardCollection):
#     """technically not necessary but then it's clear what this represents"""
# 
# class Waste(_CardCollection):
#     """technically not necessary but then it's clear what this represents"""
# 
# class Hand(_CardCollection):
#     """technically not necessary but then it's clear what this represents"""
# 
# class Card:
#     def __init__(self, value, suit):
#         self.value = value
#         self.suit = suit
# 
#     def __repr__(self):
#         name = self.__class__.__name__
#         return f"{name}('{self.value}', '{self.suit}')"
# 
# Path: mau_mau/strategies.py
# class HumanStrategy(BasicStrategy):
#     @classmethod
#     def choose_antidote(cls, find_antidotes, _, cards):
#         allowedAntidotes = find_antidotes(cards)
#         if not allowedAntidotes:
#             log.info("no antidotes. Just take the punishment ...")
#             return
# 
#         return cls.get_valid_choice(allowedAntidotes, "choose antidote")
# 
#     @classmethod
#     def choose_best_candidate(cls, cards, *args, **kwargs):
#         return cls.get_valid_choice(cards, "choose card to play")
# 
#     # fixme player is asked repeatedly if others can't play
#     # add a buffer that saves the choice until the card is played
#     @property
#     def wantedSuit(self):
#         return self.get_valid_choice(DECK.SUITS, "choose wanted suit")
# 
#     @classmethod
#     def get_valid_choice(cls, choices, msg):
#         def visualize(elems):
#             c = [f"{i} -> {c}" for i, c in enumerate(elems, 1)]
#             return " | ".join(c)
# 
#         while True:
#             idx = input(
#                 f"{msg}.\n{visualize(choices)}\n(choose 1 - {len(choices)}): "
#             )
#             try:
#                 return choices[int(idx) - 1]
# 
#             except (IndexError, ValueError):
#                 log.warning(f"'{idx}' is not a valid choice")
# 
# class BasicStrategy:
#     def __init__(self, player):
#         self.player = player
# 
#     def play(self, table):
#         rule = table.rule
#         log.debug("encountered rule %s", rule)
#         if rule.skipPlayer:
#             rule.skipPlayer = False
#             log.debug("Damn. I can't play!")
#             return
# 
#         card = None
#         if rule.find_antidotes:
#             card = self.before_play(table)
#         self._play(table, card)
# 
#     def before_play(self, table):
#         antidote = self.choose_antidote(
#             table.rule.find_antidotes, self.antidote_strategy, self.player.hand
#         )
# 
#         if not antidote:
#             while table.rule.punishments:
#                 table.rule.punishments.pop()(self.player, table)
#         return antidote
# 
#     @classmethod
#     def choose_antidote(cls, find_antidotes, antidotes_strategy, cards):
#         log.debug("find antidote")
#         antidotes = find_antidotes(cards)
#         if antidotes:
#             antidote = antidotes_strategy(antidotes)
#             log.debug("found antidote %s", antidote)
#             return antidote
# 
#     # noinspection PyUnusedLocal
#     @staticmethod
#     def antidote_strategy(antidotes, *args, **kwargs):
#         return random.choice(antidotes)
# 
#     def _play(self, table, card=None):
#         rule = table.rule
#         candidates = rule.find_playable_cards(self.player.hand)
#         if not card:
#             log.debug("find card to play")
#             if candidates:
#                 card = self.choose_best_candidate(candidates)
# 
#         if card:
#             assert card in candidates, (card, candidates)
#             self.player.put(table, card, self)
#             return
# 
#         log.debug("nothing to play")
#         rule.no_play_action(self.player, table)
# 
#     # noinspection PyUnusedLocal
#     @classmethod
#     def choose_best_candidate(cls, cards, *args, **kwargs):
#         """Not much of a strategy here :)"""
#         return cards[0]
# 
#     @property
#     def wantedSuit(self):
#         """The suit the Player wants as upcard, if he could choose"""
#         counter = collections.Counter([c.suit for c in self.player.hand])
#         # returns list of tuples [(value, count), (value, count), ...]
#         # this slice simply picks the most common value
#         return counter.most_common(1)[0][0]


import logging
from collections.abc import Iterable
from mau_mau.constants import DECK
from mau_mau.objects import Stock, Waste, Hand, Card
from mau_mau.strategies import HumanStrategy, BasicStrategy
        ]

    def check_setup(self, players, cardsPerPlayer):
        numPlayers = len(players)
        assert numPlayers > 1, "not enough players (need at least two)"
        neededCards = numPlayers * cardsPerPlayer
        assert (
            neededCards <= self._deckSize
        ), f"too many players ({numPlayers})"

    def check_table(self, table):
        assert table.upcard
        tableCardsLen = len(table.stock) + len(table.waste) + 1
        handsLen = sum(len(p.hand) for p in table.players if p.hand)
        assert tableCardsLen + handsLen == self._deckSize, (
            tableCardsLen,
            handsLen,
            self._deckSize,
        )

    def set_table(self, table, rules):
        table.rules = rules
        table.stock = Stock(self._deck)
        table.stock.shuffle()
        table.waste = Waste()
        table.upcard = table.stock.fetch()
        table.rule = rules.get_rule(table.upcard)


class Player:

# Path: mau_mau/constants.py
# class DECK:
#     SEVEN = 7
#     EIGHT = 8
#     JACK = "Jack"
#     VALUES = [SEVEN, EIGHT, 9, 10, JACK, "Queen", "King", "Ace"]
#     SUITS = ["♦", "♥", "♠", "♣"]
# 
# Path: mau_mau/objects.py
# class Stock(_CardCollection):
#     """technically not necessary but then it's clear what this represents"""
# 
# class Waste(_CardCollection):
#     """technically not necessary but then it's clear what this represents"""
# 
# class Hand(_CardCollection):
#     """technically not necessary but then it's clear what this represents"""
# 
# class Card:
#     def __init__(self, value, suit):
#         self.value = value
#         self.suit = suit
# 
#     def __repr__(self):
#         name = self.__class__.__name__
#         return f"{name}('{self.value}', '{self.suit}')"
# 
# Path: mau_mau/strategies.py
# class HumanStrategy(BasicStrategy):
#     @classmethod
#     def choose_antidote(cls, find_antidotes, _, cards):
#         allowedAntidotes = find_antidotes(cards)
#         if not allowedAntidotes:
#             log.info("no antidotes. Just take the punishment ...")
#             return
# 
#         return cls.get_valid_choice(allowedAntidotes, "choose antidote")
# 
#     @classmethod
#     def choose_best_candidate(cls, cards, *args, **kwargs):
#         return cls.get_valid_choice(cards, "choose card to play")
# 
#     # fixme player is asked repeatedly if others can't play
#     # add a buffer that saves the choice until the card is played
#     @property
#     def wantedSuit(self):
#         return self.get_valid_choice(DECK.SUITS, "choose wanted suit")
# 
#     @classmethod
#     def get_valid_choice(cls, choices, msg):
#         def visualize(elems):
#             c = [f"{i} -> {c}" for i, c in enumerate(elems, 1)]
#             return " | ".join(c)
# 
#         while True:
#             idx = input(
#                 f"{msg}.\n{visualize(choices)}\n(choose 1 - {len(choices)}): "
#             )
#             try:
#                 return choices[int(idx) - 1]
# 
#             except (IndexError, ValueError):
#                 log.warning(f"'{idx}' is not a valid choice")
# 
# class BasicStrategy:
#     def __init__(self, player):
#         self.player = player
# 
#     def play(self, table):
#         rule = table.rule
#         log.debug("encountered rule %s", rule)
#         if rule.skipPlayer:
#             rule.skipPlayer = False
#             log.debug("Damn. I can't play!")
#             return
# 
#         card = None
#         if rule.find_antidotes:
#             card = self.before_play(table)
#         self._play(table, card)
# 
#     def before_play(self, table):
#         antidote = self.choose_antidote(
#             table.rule.find_antidotes, self.antidote_strategy, self.player.hand
#         )
# 
#         if not antidote:
#             while table.rule.punishments:
#                 table.rule.punishments.pop()(self.player, table)
#         return antidote
# 
#     @classmethod
#     def choose_antidote(cls, find_antidotes, antidotes_strategy, cards):
#         log.debug("find antidote")
#         antidotes = find_antidotes(cards)
#         if antidotes:
#             antidote = antidotes_strategy(antidotes)
#             log.debug("found antidote %s", antidote)
#             return antidote
# 
#     # noinspection PyUnusedLocal
#     @staticmethod
#     def antidote_strategy(antidotes, *args, **kwargs):
#         return random.choice(antidotes)
# 
#     def _play(self, table, card=None):
#         rule = table.rule
#         candidates = rule.find_playable_cards(self.player.hand)
#         if not card:
#             log.debug("find card to play")
#             if candidates:
#                 card = self.choose_best_candidate(candidates)
# 
#         if card:
#             assert card in candidates, (card, candidates)
#             self.player.put(table, card, self)
#             return
# 
#         log.debug("nothing to play")
#         rule.no_play_action(self.player, table)
# 
#     # noinspection PyUnusedLocal
#     @classmethod
#     def choose_best_candidate(cls, cards, *args, **kwargs):
#         """Not much of a strategy here :)"""
#         return cards[0]
# 
#     @property
#     def wantedSuit(self):
#         """The suit the Player wants as upcard, if he could choose"""
#         counter = collections.Counter([c.suit for c in self.player.hand])
#         # returns list of tuples [(value, count), (value, count), ...]
#         # this slice simply picks the most common value
#         return counter.most_common(1)[0][0]


# Path: mau_mau/subjects.py
import logging
from collections.abc import Iterable
from mau_mau.constants import DECK
from mau_mau.objects import Stock, Waste, Hand, Card
from mau_mau.strategies import HumanStrategy, BasicStrategy

        ]

    def check_setup(self, players, cardsPerPlayer):
        numPlayers = len(players)
        assert numPlayers > 1, "not enough players (need at least two)"
        neededCards = numPlayers * cardsPerPlayer
        assert (
            neededCards <= self._deckSize
        ), f"too many players ({numPlayers})"

    def check_table(self, table):
        assert table.upcard
        tableCardsLen = len(table.stock) + len(table.waste) + 1
        handsLen = sum(len(p.hand) for p in table.players if p.hand)
        assert tableCardsLen + handsLen == self._deckSize, (
            tableCardsLen,
            handsLen,
            self._deckSize,
        )

    def set_table(self, table, rules):
        table.rules = rules
        table.stock = Stock(self._deck)
        table.stock.shuffle()
        table.waste = Waste()
        table.upcard = table.stock.fetch()
        table.rule = rules.get_rule(table.upcard)


class Player:

    def __init__(self, name, strategyClass=BasicStrategy):