Skip to content

Commit

Permalink
Use 'C' for colorless identity. Closes #91
Browse files Browse the repository at this point in the history
  • Loading branch information
ldeluigi committed Mar 24, 2023
1 parent 635b82c commit 35e0a91
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.1.5 on 2023-03-24 19:41

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('spellbook', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='card',
name='identity',
field=models.CharField(default='C', help_text='Card mana identity', max_length=5, validators=[django.core.validators.RegexValidator(message='Can be any combination of one or more letters in [W,U,B,R,G], in order, otherwise C for colorless.', regex='^(?:W?U?B?R?G?|C)$'), django.core.validators.MinLengthValidator(1)], verbose_name='mana identity of card'),
),
migrations.AlterField(
model_name='variant',
name='identity',
field=models.CharField(editable=False, help_text='Mana identity', max_length=5, validators=[django.core.validators.RegexValidator(message='Can be any combination of one or more letters in [W,U,B,R,G], in order, otherwise C for colorless.', regex='^(?:W?U?B?R?G?|C)$'), django.core.validators.MinLengthValidator(1)], verbose_name='mana identity'),
),
]
4 changes: 2 additions & 2 deletions backend/spellbook/models/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .mixins import ScryfallLinkMixin
from .feature import Feature
from ..variants.list_utils import merge_identities
from .validators import IDENTITY_VALIDATOR
from .validators import IDENTITY_VALIDATORS


class Card(models.Model, ScryfallLinkMixin):
Expand All @@ -16,7 +16,7 @@ class Card(models.Model, ScryfallLinkMixin):
related_name='cards',
help_text='Features provided by this single card effects or characteristics',
blank=True)
identity = models.CharField(max_length=5, blank=True, help_text='Card mana identity', verbose_name='mana identity of card', validators=[IDENTITY_VALIDATOR])
identity = models.CharField(max_length=5, blank=False, null=False, default='C', help_text='Card mana identity', verbose_name='mana identity of card', validators=IDENTITY_VALIDATORS)
legal = models.BooleanField(default=True, help_text='Is this card legal in Commander?', verbose_name='is legal')
spoiler = models.BooleanField(default=False, help_text='Is this card from an upcoming set?', verbose_name='is spoiler')
added = models.DateTimeField(auto_now_add=True, editable=False)
Expand Down
6 changes: 3 additions & 3 deletions backend/spellbook/models/validators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.core.validators import RegexValidator
from django.core.validators import RegexValidator, MinLengthValidator

MANA_SYMBOL = r'(?:[0-9WUBRGCPXYZS∞]|[1-9][0-9]{1,2}|(?:2\/[WUBRG]|W\/U|W\/B|B\/R|B\/G|U\/B|U\/R|R\/G|R\/W|G\/W|G\/U)(?:\/P)?)'
MANA_REGEX = r'^(\{' + MANA_SYMBOL + r'\} *)*$'
Expand All @@ -13,8 +13,8 @@

TEXT_VALIDATORS = [DOUBLE_SQUARE_BRACKET_TEXT_VALIDATOR, SYMBOLS_TEXT_VALIDATOR]

IDENTITY_REGEX = r'^W?U?B?R?G?$'
IDENTITY_VALIDATOR = RegexValidator(regex=IDENTITY_REGEX, message='Can be any combination of zero or more letters in [W,U,B,R,G], in order.')
IDENTITY_REGEX = r'^(?:W?U?B?R?G?|C)$'
IDENTITY_VALIDATORS = [RegexValidator(regex=IDENTITY_REGEX, message='Can be any combination of one or more letters in [W,U,B,R,G], in order, otherwise C for colorless.'), MinLengthValidator(1)]

# Scryfall query syntax: https://scryfall.com/docs/syntax
COMPARISON_OPERATORS = r'(?::|[<>]=?|!=|=)'
Expand Down
4 changes: 2 additions & 2 deletions backend/spellbook/models/variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .ingredient import IngredientInCombination
from .combo import Combo
from .job import Job
from .validators import TEXT_VALIDATORS, MANA_VALIDATOR, IDENTITY_VALIDATOR
from .validators import TEXT_VALIDATORS, MANA_VALIDATOR, IDENTITY_VALIDATORS


class Variant(models.Model, ScryfallLinkMixin):
Expand Down Expand Up @@ -54,7 +54,7 @@ class Status(models.TextChoices):
created = models.DateTimeField(auto_now_add=True, editable=False)
updated = models.DateTimeField(auto_now=True, editable=False)
frozen = models.BooleanField(default=False, blank=False, help_text='Is this variant undeletable?', verbose_name='is frozen')
identity = models.CharField(max_length=5, blank=True, help_text='Mana identity', verbose_name='mana identity', editable=False, validators=[IDENTITY_VALIDATOR])
identity = models.CharField(max_length=5, blank=False, null=False, help_text='Mana identity', verbose_name='mana identity', editable=False, validators=IDENTITY_VALIDATORS)
generated_by = models.ForeignKey(Job, on_delete=models.SET_NULL, null=True, blank=True, editable=False, help_text='Job that generated this variant', related_name='variants')
legal = models.BooleanField(blank=False, help_text='Is this variant legal in Commander?', verbose_name='is legal')
spoiler = models.BooleanField(blank=False, help_text='Is this variant a spoiler?', verbose_name='is spoiler')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
identity_result += c;
}
}
django.jQuery('#id_identity').val(identity_result);
django.jQuery('#id_identity').val(identity_result || 'C');
django.jQuery('#id_legal').prop('checked', Object.hasOwn(data.legalities, 'commander') && data.legalities.commander !== 'banned');
card_info.update_card_oracle_image(
document.getElementById('id_oracle_id').value,
Expand Down
5 changes: 5 additions & 0 deletions backend/spellbook/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ def populate_db():
c5 = Card.objects.create(name='E', oracle_id=uuid.UUID('00000000-0000-0000-0000-000000000005'), identity='G', legal=False, spoiler=True)
c6 = Card.objects.create(name='F', oracle_id=uuid.UUID('00000000-0000-0000-0000-000000000006'), identity='WU', legal=True, spoiler=False)
c7 = Card.objects.create(name='G', oracle_id=uuid.UUID('00000000-0000-0000-0000-000000000007'), identity='WB', legal=True, spoiler=False)
c8 = Card.objects.create(name='H', oracle_id=uuid.UUID('00000000-0000-0000-0000-000000000008'), identity='C', legal=True, spoiler=False)
f1 = Feature.objects.create(name='FA', description='Feature A', utility=False)
f2 = Feature.objects.create(name='FB', description='Feature B', utility=True)
f3 = Feature.objects.create(name='FC', description='Feature C', utility=False)
f4 = Feature.objects.create(name='FD', description='Feature D', utility=True)
b1 = Combo.objects.create(mana_needed='{W}{W}', other_prerequisites='Some requisites.', description='Some description.', generator=False)
b2 = Combo.objects.create(mana_needed='{U}{U}', other_prerequisites='Some requisites.', description='Some description.', generator=True)
b3 = Combo.objects.create(mana_needed='{B}{B}', other_prerequisites='Some requisites.', description='Some description.', generator=False)
b4 = Combo.objects.create(mana_needed='{R}{R}', other_prerequisites='Some requisites.', description='Some description.', generator=True)
t1 = Template.objects.create(name='TA', scryfall_query='tou>5')
c1.features.add(f1)
b1.needs.add(f1)
Expand All @@ -33,6 +35,9 @@ def populate_db():
CardInCombo.objects.create(card=c5, combo=b3, order=2, zone_location=IngredientInCombination.ZoneLocation.BATTLEFIELD, card_state='Some state.')
CardInCombo.objects.create(card=c6, combo=b3, order=3, zone_location=IngredientInCombination.ZoneLocation.COMMAND_ZONE, card_state='Some state.')
CardInCombo.objects.create(card=c7, combo=b3, order=4, zone_location=IngredientInCombination.ZoneLocation.LIBRARY, card_state='Some state.')
b4.produces.add(f1)
CardInCombo.objects.create(card=c8, combo=b4, order=1, zone_location=IngredientInCombination.ZoneLocation.HAND, card_state='Some state.')
CardInCombo.objects.create(card=c1, combo=b4, order=2, zone_location=IngredientInCombination.ZoneLocation.BATTLEFIELD, card_state='Some state.')


class CardTests(TestCase):
Expand Down
3 changes: 2 additions & 1 deletion backend/spellbook/variants/list_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def merge_sort_unique(left: list[T], right: list[T]) -> list[T]:

def merge_identities(identities: Iterable[str]):
i = set(''.join(identities).upper())
return ''.join([color for color in 'WUBRG' if color in i])
i.discard('C')
return ''.join([color for color in 'WUBRG' if color in i]) or 'C'


def includes_any(v: set[int], others: Iterable[set[int]]) -> bool:
Expand Down
10 changes: 5 additions & 5 deletions backend/spellbook/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rest_framework.response import Response
from rest_framework.request import Request
from spellbook.models import Card, Template, Feature, Combo, Variant, CardInVariant
from spellbook.variants.list_utils import merge_identities
from spellbook.serializers import CardDetailSerializer, FeatureSerializer, ComboDetailSerializer, TemplateSerializer, VariantSerializer


Expand Down Expand Up @@ -80,20 +81,19 @@ def find_my_combos(request: Request) -> Response:
almost_included_variants = []
almost_included_variants_by_adding_colors = []

identity = set()
for card in cards:
identity.update(list(card.identity))
identity = merge_identities(c.identity for c in cards)
identity_set = set(identity)

for variant in variant_to_cards:
if variant_to_cards[variant].issubset(cards):
included_variants.append(VariantSerializer(variant).data)
elif variant_to_cards[variant].intersection(cards):
if set(variant.identity).issubset(identity):
if set(variant.identity).issubset(identity_set):
almost_included_variants.append(VariantSerializer(variant).data)
else:
almost_included_variants_by_adding_colors.append(VariantSerializer(variant).data)
return Response({
'identity': ''.join(i for i in 'WUBRG' if i in identity),
'identity': identity,
'included': included_variants,
'almost_included': almost_included_variants,
'almost_included_by_adding_colors': almost_included_variants_by_adding_colors,
Expand Down

0 comments on commit 35e0a91

Please sign in to comment.