<commit_before>import logging
from enum import IntEnum
from .dsl import LazyNum, Picker, Selector
from .enums import CardType, PowSubType, Zone
from .entity import Entity


def _eval_card(source, card):
	"""
	Return a Card instance from \a card
	The card argument can be:
	- A Card instance (nothing is done)
	- The string ID of the card (the card is created)
	- A Picker instance (a card is dynamically picked)
	"""
	if isinstance(card, Picker):
		card = card.pick(source)

	if not isinstance(card, list):
		cards = [card]
	else:
		cards = card

	ret = []
	for card in cards:
		if isinstance(card, str):
			ret.append(source.game.card(card))
		else:
			ret.append(card)

	return ret


class EventListener:
	ON = 1
	AFTER = 2

	def __init__(self, trigger, actions, at, zone=Zone.PLAY, once=False):
		self.trigger = trigger
		self.actions = actions
		self.at = at
		self.once = once
		self.in_hand = zone == Zone.HAND

	def __repr__(self):
		return "<EventListener %r>" % (self.trigger)


class Action:  # Lawsuit
	type = PowSubType.TRIGGER

	class Args(IntEnum):
		"""
		Arguments given to Actions.
		Works like an IntEnum, with the value representing the argument index.
		"""
		pass

	def __init__(self, *args, **kwargs):
		self._args = args
		self._argnames = []
		for e, arg in zip(self.Args, self._args):
			self._argnames.append(e.name)

	def __repr__(self):
		args = ["%s=%r" % (k, v) for k, v in zip(self._argnames, self._args)]
		return "<Action: %s(%s)>" % (self.__class__.__name__, ", ".join(args))

	def after(self, *actions, zone=Zone.PLAY):
		return EventListener(self, actions, EventListener.AFTER, zone=zone)

	def on(self, *actions, zone=Zone.PLAY):
		return EventListener(self, actions, EventListener.ON, zone=zone)

	def once(self, *actions, zone=Zone.PLAY):
		return EventListener(self, actions, EventListener.ON, zone=zone, once=True)

	def _broadcast(self, entity, source, at, *args):
		for event in entity.events:
			if entity.zone == Zone.HAND and not event.in_hand:
				continue
			if event.at != at:
				continue
			if isinstance(event.trigger, self.__class__) and event.trigger.matches(entity, args):
				logging.info("%r triggers off %r from %r", entity, self, source)
				entity.trigger_event(source, event, args)

	def broadcast(self, source, at, *args):
		for entity in source.game.hands:
			self._broadcast(entity, source, at, *args)

		for entity in source.game.entities:
			zone = getattr(entity, "zone", Zone.INVALID)
			if zone not in (Zone.PLAY, Zone.SECRET):
				continue
			self._broadcast(entity, source, at, *args)

	def get_args(self, source):
		return self._args

	def matches(self, source, args):
		for arg, match in zip(args, self._args):
			# this stuff is stupidslow
			res = match.eval([arg], source)
			if not res or res[0] is not arg:
				return False<commit_msg>Allow wildcard matching events with None<commit_after>import logging
from enum import IntEnum
from .dsl import LazyNum, Picker, Selector
from .enums import CardType, PowSubType, Zone
from .entity import Entity


def _eval_card(source, card):
	"""
	Return a Card instance from \a card
	The card argument can be:
	- A Card instance (nothing is done)
	- The string ID of the card (the card is created)
	- A Picker instance (a card is dynamically picked)
	"""
	if isinstance(card, Picker):
		card = card.pick(source)

	if not isinstance(card, list):
		cards = [card]
	else:
		cards = card

	ret = []
	for card in cards:
		if isinstance(card, str):
			ret.append(source.game.card(card))
		else:
			ret.append(card)

	return ret


class EventListener:
	ON = 1
	AFTER = 2

	def __init__(self, trigger, actions, at, zone=Zone.PLAY, once=False):
		self.trigger = trigger
		self.actions = actions
		self.at = at
		self.once = once
		self.in_hand = zone == Zone.HAND

	def __repr__(self):
		return "<EventListener %r>" % (self.trigger)


class Action:  # Lawsuit
	type = PowSubType.TRIGGER

	class Args(IntEnum):
		"""
		Arguments given to Actions.
		Works like an IntEnum, with the value representing the argument index.
		"""
		pass

	def __init__(self, *args, **kwargs):
		self._args = args
		self._argnames = []
		for e, arg in zip(self.Args, self._args):
			self._argnames.append(e.name)

	def __repr__(self):
		args = ["%s=%r" % (k, v) for k, v in zip(self._argnames, self._args)]
		return "<Action: %s(%s)>" % (self.__class__.__name__, ", ".join(args))

	def after(self, *actions, zone=Zone.PLAY):
		return EventListener(self, actions, EventListener.AFTER, zone=zone)

	def on(self, *actions, zone=Zone.PLAY):
		return EventListener(self, actions, EventListener.ON, zone=zone)

	def once(self, *actions, zone=Zone.PLAY):
		return EventListener(self, actions, EventListener.ON, zone=zone, once=True)

	def _broadcast(self, entity, source, at, *args):
		for event in entity.events:
			if entity.zone == Zone.HAND and not event.in_hand:
				continue
			if event.at != at:
				continue
			if isinstance(event.trigger, self.__class__) and event.trigger.matches(entity, args):
				logging.info("%r triggers off %r from %r", entity, self, source)
				entity.trigger_event(source, event, args)

	def broadcast(self, source, at, *args):
		for entity in source.game.hands:
			self._broadcast(entity, source, at, *args)

		for entity in source.game.entities:
			zone = getattr(entity, "zone", Zone.INVALID)
			if zone not in (Zone.PLAY, Zone.SECRET):
				continue
			self._broadcast(entity, source, at, *args)

	def get_args(self, source):
		return self._args

	def matches(self, source, args):
		for arg, match in zip(args, self._args):
			if match is None:
				# Allow matching Action(None, None, z) to Action(x, y, z)
				continue
			# this stuff is stupidslow
			res = match.eval([arg], source)
			if not res or res[0] is not arg:
				return False
{'fraction_non_alphanumeric': 0.12419551226300227, 'fraction_numerical': 0.0010436597669159853, 'mean_word_length': 3.8811544991511036, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5790543', 'n_tokens_mistral': 2109, 'n_tokens_neox': 1906, 'n_words': 624}