From 38ea3d44603218b36e4352d7017e28623c108695 Mon Sep 17 00:00:00 2001 From: Rob Dennis Date: Sat, 27 Apr 2013 20:34:17 -0400 Subject: [PATCH] Fixes #19; removed a lot of cruft for heuristics in general --- cuesbey_main/cube_diff/heuristics.py | 196 ++++++++++++++++----- cuesbey_main/cube_diff/tests/test_cards.py | 33 +++- cuesbey_main/cuesbey/settings/__init__.py | 7 +- 3 files changed, 186 insertions(+), 50 deletions(-) diff --git a/cuesbey_main/cube_diff/heuristics.py b/cuesbey_main/cube_diff/heuristics.py index 5d0384a..bed568e 100644 --- a/cuesbey_main/cube_diff/heuristics.py +++ b/cuesbey_main/cube_diff/heuristics.py @@ -10,13 +10,6 @@ estimate_colors_from_lands) from cuesbey_main.cube_diff.autolog import log -colors = ( - 'White', - 'Blue', - 'Black', - 'Red', - 'Green', -) class HeuristicsHandler(object): """ @@ -27,6 +20,14 @@ class HeuristicsHandler(object): checked = True + lands = ('Plains', 'Island', 'Swamp', 'Mountain', 'Forest') + plural_lands = ('Plains', 'Islands', 'Swamps', 'Mountains', 'Forests') + all_lands = lands + plural_lands + land_names = "(%s)" % '|'.join(lands) + plural_land_names = "(%s)" % '|'.join(plural_lands) + all_land_names = "(%s)" % '|'.join(all_lands) + color_names = '(white|blue|black|red|green)' + @abc.abstractproperty def key(self): """ @@ -47,6 +48,103 @@ def get(self, card): def as_dict(cls): return dict(key=cls.key, checked=cls.checked) + @classmethod + def find_all(cls, group, text): + + found = re.findall(group, text, re.I) + log.debug('found %r in %r using %r', found, text, group) + return found + + @classmethod + def find_all_in_multiple(cls, groups, text): + + return set(chain(cls.find_all(group, text) for group in groups)) + + @classmethod + def get_colors_from_found_lands(cls, search_string, text): + """ + return the colors associated with lands found in the text + + :param search_string: the regex match that you expect has one or more + instances of a basic land types + :param text: the text to search from + :return: the associated colors + :rtype: set + """ + + match = re.search(search_string, text, re.I | re.DOTALL) + if not match: + return set() + + log.debug('going to search for lands in %r', match.group()) + + return estimate_colors_from_lands( + cls.find_all(cls.all_land_names, match.group()) + ) + + @classmethod + def get_colors_found(cls, search_string, text): + """ + return the colors we found found in the text + + :param search_string: the regex match that you expect has one or more + references to colors + :param text: the text to search from + :return: the associated colors + :rtype: set + """ + + match = re.search(search_string, text, re.I | re.DOTALL) + + if not match: + return set() + + return { + found.title() + for found in cls.find_all(cls.color_names, match.group()) + } + + @classmethod + def get_all_colors_found_from_multiple(cls, search_strings, text): + """ + get all the colors associated with any of the provided search strings + + :param search_strings: multiple regex matches that you has one or more + references to colors + :param text: the text to search from + :return: the associated colors + :rtype: set + """ + + log.debug('looking for colors with %r in %r', search_strings, text) + + return { + found_color + for search in search_strings + for found_color in cls.get_colors_found(search, text) + } + + @classmethod + def get_all_colors_found_from_lands_in_multiple(cls, search_strings, + text): + """ + get all the colors (as estimated from lands) associated with any of + the provided search strings + + :param search_strings: multiple regex matches that you has one or more + references to colors + :param text: the text to search from + :return: the associated colors + :rtype: set + """ + + return { + found_color + for search in search_strings + for found_color in cls.get_colors_from_found_lands(search, text) + } + + class _handle_x_spells_are_infinite_mana(HeuristicsHandler): key = 'x_spells_are_infinite' @@ -181,28 +279,24 @@ def get(cls, card): boost, it's fair to say that only decks in both colors will want this card """ - land_names = "(Plains|Island|Swamp|Mountain|Forest)" - plural_land_names = "(Plains|Islands|Swamps|Mountains|Forests)" - controlled_lands = ( - re.findall("you control a %s" % land_names, card.text), - re.findall("%s you control" % plural_land_names, card.text) - ) - if any(controlled_lands): - _land_colors = estimate_colors_from_lands(chain(*controlled_lands)) + _land_colors = cls.get_all_colors_found_from_lands_in_multiple( + ["you control a %s.*$" % cls.land_names, + "%s you control.*$" % cls.plural_land_names], card.text + ) - if _land_colors == card.colors: - return + if _land_colors and _land_colors == card.colors: + return - modified_colors = card.colors | _land_colors + modified_colors = card.colors | _land_colors - return { - cls.key: dict(colors=modified_colors) - } + return { + cls.key: dict(colors=modified_colors) + } class _handle_caring_about_permanent_colors(HeuristicsHandler): - key = 'caring_about_controlling_colored_permanents_affect_color' + key = 'caring_about_controlling_colored_permanents_affects_color' @classmethod def get(cls, card): @@ -212,32 +306,45 @@ def get(cls, card): considered a multicolor card """ - color_names = '({})'.format('|'.join(colors)) - - controlled_colors = ( - re.findall('you control a {0}(?: or {0})?'.format(color_names), - card.text, re.I), - re.findall('you control a {0} \w+' - '(?: and a {0})?'.format(color_names), - card.text, re.I), + _found_colors = cls.get_all_colors_found_from_multiple( + ['you control a %s.*$' % cls.color_names, + '%s.*?you control.*$' % cls.color_names], card.text ) - if any(controlled_colors): - controlled_colors = set([ - # each findall returns a 2 tuple, and up to 4 total, thus the - # double unwrapping - color.title() for color in chain(*chain(*controlled_colors)) - if color - ]) + if _found_colors and _found_colors == card.colors: + return - if controlled_colors == card.colors: - return + modified_colors = card.colors | _found_colors - modified_colors = card.colors | controlled_colors + return { + cls.key: dict(colors=modified_colors) + } - return { - cls.key: dict(colors=modified_colors) - } + +class _handle_caring_about_spell_colors(HeuristicsHandler): + key = 'caring_about_spell_colors_affects_color' + + @classmethod + def get(cls, card): + """ + If a card is one color, but needs gets a bonus from controlling + permanents or creatures of a different color or colors, that is often + considered a multicolor card + """ + + _found_colors = cls.get_all_colors_found_from_multiple( + ['%s.* spells you cast.*$' % cls.color_names, + 'you cast a %s.*$' % cls.color_names], card.text + ) + + if _found_colors and _found_colors == card.colors: + return + + modified_colors = card.colors | _found_colors + + return { + cls.key: dict(colors=modified_colors) + } class _handle_phyrexian(HeuristicsHandler): @@ -513,6 +620,7 @@ def get(cls, card): _handle_activated_abilities, _handle_phyrexian_abilities, _handle_suspend, + _handle_caring_about_spell_colors, _handle_caring_about_permanent_colors, ] @@ -527,7 +635,7 @@ def get_heuristics(card): for handler in _all_handlers: h.update(handler.get(card) or {}) except: - log.error('error getting heuristics for card: %r', card) + log.exception('error getting heuristics for card: %r', card) raise return h diff --git a/cuesbey_main/cube_diff/tests/test_cards.py b/cuesbey_main/cube_diff/tests/test_cards.py index 31313c9..bb244cc 100755 --- a/cuesbey_main/cube_diff/tests/test_cards.py +++ b/cuesbey_main/cube_diff/tests/test_cards.py @@ -221,25 +221,50 @@ def test_handle_phyrexian(self): ) )) - def test_controller_permanents_of_a_color_means_something(self): + def test_controlling_permanents_of_a_color_means_something(self): self.assertHeuristicsArePresent('Bloodhall Ooze', dict( - caring_about_controlling_colored_permanents_affect_color=dict( + caring_about_controlling_colored_permanents_affects_color=dict( colors={'Black', 'Red', 'Green'} ) )) self.assertHeuristicsArePresent('Thornwatch Scarecrow', dict( - caring_about_controlling_colored_permanents_affect_color=dict( + caring_about_controlling_colored_permanents_affects_color=dict( colors={'White', 'Green'} ) )) self.assertHeuristicsArePresent('Necra Sanctuary', dict( - caring_about_controlling_colored_permanents_affect_color=dict( + caring_about_controlling_colored_permanents_affects_color=dict( colors={'White', 'Black', 'Green'} ) )) + def test_handle_color_affected_by_spells(self): + self.assertHeuristicsArePresent('Shrine of Burning Rage', dict( + caring_about_spell_colors_affects_color=dict( + colors={'Red'} + ) + )) + + self.assertHeuristicsArePresent('Nightscape Familiar', dict( + caring_about_spell_colors_affects_color=dict( + colors={'Blue', 'Black', 'Red'} + ) + )) + + self.assertHeuristicsArePresent('Sapphire Medallion', dict( + caring_about_spell_colors_affects_color=dict( + colors={'Blue'} + ) + )) + + self.assertHeuristicsArePresent('Quirion Dryad', dict( + caring_about_spell_colors_affects_color=dict( + colors={'White', 'Blue', 'Black', 'Red', 'Green'} + ) + )) + def test_handle_off_color_flashback(self): self.assertHeuristicsArePresent('Lingering Souls', dict( off_color_flashback_is_gold=dict( diff --git a/cuesbey_main/cuesbey/settings/__init__.py b/cuesbey_main/cuesbey/settings/__init__.py index 16d4707..5e9ff2c 100755 --- a/cuesbey_main/cuesbey/settings/__init__.py +++ b/cuesbey_main/cuesbey/settings/__init__.py @@ -179,8 +179,11 @@ 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', + }, + '': { + 'handlers': ['console'], + 'propagate': True, + 'level': 'DEBUG', } - - }, }