def cube_scars_use(cube_id): cube = Cube.query.get(cube_id) cube_wrapper = CubeWrapper(cube) form = UseScarForm() form.card_name.choices = [x.name() for x in cube_wrapper.cards()] if not form.validate_on_submit(): flash('Scar use failed') return redirect(url_for('cube_scars', cube_id=cube_id)) card = cube.get_card_by_name(form.card_name.data) scar = Scar.query.get(form.scar_id.data) if not card: flash(f'Invalid card name: {form.card_name.data}') return redirect(url_for('cube_scars', cube_id=cube_id)) if not scar: flash(f'Invalid scar id: {form.card_id.data}') return redirect(url_for('cube_scars', cube_id=cube_id)) scar.applied_timestamp = datetime.utcnow() scar.applied_by_id = current_user.id scar.applied_to_id = card.id db.session.add(scar) db.session.commit() flash(f'Scar applied to {card.name()}') return redirect(url_for('cube_scars', cube_id=cube_id))
def _make_commander_packs(cube, unused, num_packs, num_players): cube_wrapper = CubeWrapper(cube) card_breakdown = { 'all': cube_wrapper.cards(), 'rare': [], # mythics get shuffled in here 'uncommon': [], 'common': [], 'legendary': [], # All the legendary creatures } distribution = [ ('rare', 1), ('uncommon', 3), ('legendary', 2), ('common', 13), ('all', 1), # Foil ] for card in card_breakdown['all']: if 'legendary creature' in card.type_line().lower(): card_breakdown['legendary'].append(card) elif card.rarity() in ('rare', 'mythic'): card_breakdown['rare'].append(card) elif card.rarity() == 'uncommon': card_breakdown['uncommon'].append(card) elif card.rarity() == 'common': card_breakdown['common'].append(card) else: raise ValueError(f"Unknown rarity '{card.rarity()}' on '{card.name()}'") return _make_set_packs(card_breakdown, distribution, num_packs * num_players)
def cube_scars(cube_id): cube = Cube.query.get(cube_id) form = NewScarForm() form.update_as.choices = User.all_names() form.update_as.data = current_user.name rsform = RandomScarsForm() if rsform.validate_on_submit(): random_scar_count = int(rsform.count.data) random_scars = Scar.random_scars(cube_id, random_scar_count) else: rsform.count.data = '2' random_scars = [] cube_wrapper = CubeWrapper(cube) use_scar_form = UseScarForm() use_scar_form.card_name.choices = [''] + [ x.name() for x in cube_wrapper.cards() ] return render_template( 'cube_scars.html', cube=cube, cw=cube_wrapper, form=form, rsform=rsform, usform=use_scar_form, random_scars=random_scars, )
def __init__(self, draft_id, user_id): draft = Draft.query.get(draft_id) if draft.parent_id: self.draft = Draft.query.get(draft.parent_id) else: self.draft = draft self.draft_ids = [self.draft.id ] + [x.id for x in self.draft.children()] self.cube = self.draft.cube self.cube_wrapper = CubeWrapper(self.cube) self.user = User.query.get(user_id) self.seats = Seat.query.filter( Seat.draft_id.in_(self.draft_ids), Seat.user_id == self.user.id, ).all() self.deck = self._load_deck() self.card_set = CardSet() for card in self.deck.maindeck(): self.card_set.add_card(card, maindeck=True) for card in self.deck.sideboard(): self.card_set.add_card(card, sideboard=True) for card in self.deck.command(): self.card_set.add_card(card, command=True)
def cube_packs(setup: dict, user_data: list): # Create a separate 'more_setup' dict here and merge it into the setup dict at the end. # Creating it here makes it easy to see what keys are added. more_setup = { 'packs': [], # pack id to pack mapping 'picks': [], # { 'user_id': id, 'pack_id': pack_id, 'card_id': card_id } } cube = Cube.query.get(setup['cube_id']) wrapper = CubeWrapper(cube) card_data = wrapper.card_data(include_removed=False) num_packs = setup['num_packs'] pack_size = setup['pack_size'] pack_count = len(user_data) * num_packs card_count = pack_count * pack_size if len(card_data) < card_count: raise ValueError( f"Not enough cards for {num_users} users * {num_packs} packs * {pack_size} cards" ) included_card_data = {} card_ids = [str(x) for x in card_data.keys()] random.shuffle(card_ids) for i in range(pack_count): start = i * pack_size end = start + pack_size user_id = user_data[i // num_packs]['id'] pack = { 'pack_id': i, 'card_ids': card_ids[start:end], 'user_id': user_id, # User who opens pack 'pack_num': i % num_packs, # Order of pack opening 'waiting_id': user_id, # User who needs to pick from this pack 'waiting_picks': 1, 'picked_ids': [], 'events': [], 'opened': False, } more_setup['packs'].append(pack) for card_id in pack['card_ids']: included_card_data[card_id] = card_data[int(card_id)] setup.update(more_setup) return included_card_data
def rotisserie(setup: dict, user_data: list): cube = Cube.query.get(setup['cube_id']) wrapper = CubeWrapper(cube) card_data = wrapper.card_data(include_removed=False) first_pick_id = random.choice(user_data)['id'] setup.update({ 'card_ids': [str(x) for x in card_data.keys()], 'waiting_id': first_pick_id, 'picked_ids': [], 'events': [], }) return card_data
def _make_cube_packs(cube, pack_size, num_packs, num_players): cube_wrapper = CubeWrapper(cube) cards = cube_wrapper.cards() total_cards = pack_size * num_packs * num_players total_packs = num_packs * num_players if len(cards) < total_cards: raise ValueError("Not enough cards ({}) for {} packs of size {} and {} players.".format( len(cards), num_packs, pack_size, num_players)) random.shuffle(cards) packs = [] for i in range(total_packs): start = i * pack_size end = start + pack_size packs.append(cards[start:end]) return packs # List of list of Card ORMs
def cube_data(cube_id): cube = Cube.query.get(cube_id) data = CubeData(cube) return render_template( 'cube_data.html', cube=cube, cw=CubeWrapper(cube), d=data, )
def pack_of_cards(cube_id, num_cards, random_seed): num_cards = int(num_cards) randomizer = random.Random(random_seed) cube = Cube.query.get(cube_id) cube_wrapper = CubeWrapper(cube) cards = cube_wrapper.cards() randomizer.shuffle(cards) cards = cards[:num_cards] card_data = {} for card in cards: card_data[card.id] = card.get_json() return render_template( 'pack_of_cards.html', title=f"Pack of {num_cards} cards from {cube.name}", cards=cards, card_data=card_data, )
def cube_cards(cube_id): search_id = request.args.get('search_id') cube = Cube.query.get(cube_id) wrapper = CubeWrapper(cube, search_id) return render_template( 'cube_cards.html', cube=cube, cw=wrapper, t=CardTable(wrapper), add_cards_form=AddCardsForm(), rare_form=CardRarityForm(), )
def cube_achievements(cube_id): form = NewAchievementForm() form.update_as.choices = User.all_names() form.update_as.data = current_user.name form.cube_id.data = cube_id form.group_fields() cube = Cube.query.get(cube_id) return render_template( 'cube_achievements.html', cube=cube, cw=CubeWrapper(cube), form=form, )
def __init__(self, draft_id, user): self.draft = Draft.query.get(draft_id) self.cube = self.draft.cube self.cube_wrapper = CubeWrapper(self.cube) self.seats = self.draft.seats self.seats.sort(key=lambda x: x.order) self.user = user self.seat = next(x for x in self.seats if x.user_id == self.user.id) self.pack = self.seat.waiting_pack() self.deck_builder = DeckBuilder(self.draft.id, self.user.id) self.results_dict = self._game_results_linked()
def cube_interns(cube_id): cube = Cube.query.get(cube_id) card = CubeCard.query.filter( CubeCard.cube_id == cube.id, CubeCard.latest_id == CubeCard.id, ) interns = [x for x in card if 'intern of' in x.oracle_text().lower()] live_interns = [x for x in interns if not x.removed_by_id] dead_interns = [x for x in interns if x.removed_by_id] live_interns.sort(key=lambda x: x.timestamp, reverse=True) dead_interns.sort(key=lambda x: x.removed_by_timestamp, reverse=True) return render_template( 'cube_interns.html', cube=cube, cw=CubeWrapper(cube), live_interns=live_interns, dead_interns=dead_interns, )
class CubeData(object): def __init__(self, cube): self.cube = cube self.cube_wrapper = CubeWrapper(self.cube) self.info = self._pick_info() self.ratios = CreatureRatios(self.cube_wrapper.card_set()) def creature_types(self): types = {} for card in self.cube_wrapper.cards(): for face in card.card_faces(): if 'creature' in face['type_line'].lower(): subtypes = self._extract_subtypes(face['type_line']) for type in subtypes: types.setdefault(type, []).append(card) for key in types: types[key] = list(set(types[key])) types[key].sort(key=lambda x: x.name()) types_list = list(types.items()) types_list.sort() return types_list def first_picks(self): more_than_one = [x for x in self.info.values() if x.first_picks() > 1] counts = [(x.card.name(), x.first_picks()) for x in more_than_one] counts.sort(key=lambda x: (-x[1], x[0])) return counts def highest_picks(self): sorted_picks = [x for x in self.info.values() if len(x.picks) > 1] sorted_picks.sort(key=lambda x: x.average_pick()) return sorted_picks def _extract_subtypes(self, type_line): for i in range(len(type_line)): if not type_line[i].isalpha() and not type_line[i].isspace(): return type_line[i+1:].strip().split() return [] def _pick_info(self): from app.models.cube_models import CubeCard from app.models.draft_models import Draft from app.models.draft_models import PackCard drafts = [ x.id for x in Draft.query.filter(Draft.cube_id == self.cube.id).all() if x.complete ] picks = PackCard.query.filter( PackCard.draft_id.in_(drafts), PackCard.pick_number >= 0, ).all() pick_info = {} for pick in picks: if pick.cube_card.removed_by_timestamp: continue info = pick_info.setdefault(pick.cube_card.latest_id, CardPickInfo(pick.cube_card)) info.picks.append(pick) self._maindeck_info(pick_info) return pick_info def _maindeck_info(self, pick_info): for deck in Deck.query.all(): for card_id in deck.maindeck_ids.split(','): if not card_id: continue card_id = int(card_id) if card_id in pick_info: pick_info[card_id].maindecked += 1
class DeckBuilder(object): def __init__(self, draft_id, user_id): draft = Draft.query.get(draft_id) if draft.parent_id: self.draft = Draft.query.get(draft.parent_id) else: self.draft = draft self.draft_ids = [self.draft.id ] + [x.id for x in self.draft.children()] self.cube = self.draft.cube self.cube_wrapper = CubeWrapper(self.cube) self.user = User.query.get(user_id) self.seats = Seat.query.filter( Seat.draft_id.in_(self.draft_ids), Seat.user_id == self.user.id, ).all() self.deck = self._load_deck() self.card_set = CardSet() for card in self.deck.maindeck(): self.card_set.add_card(card, maindeck=True) for card in self.deck.sideboard(): self.card_set.add_card(card, sideboard=True) for card in self.deck.command(): self.card_set.add_card(card, command=True) def all_cards(self): cards = [] drafts = Draft.query.filter(Draft.id.in_(self.draft_ids)).all() for draft in drafts: cards += self.cube_wrapper.cards(include_removed=True) return list(set(cards)) def basics(self, name): if not self.deck.basic_lands: return '0' for elem in self.deck.basic_lands.split(','): tokens = elem.split() if len(tokens) == 2 and tokens[1] == name: return tokens[0] return '0' def card_data(self): data = {} for card in self.card_set: data[card.id] = card.get_json() return data def deck_list(self, legacy_names=False): """ Return a decklist formatted to be used by Cockatrice and other systems. """ cards = [] for card in self.deck.maindeck(): if legacy_names: cards.append(card.cockatrice_name()) else: cards.append(card.name()) for basic in [ x.strip() for x in self.deck.basic_lands.split(',') if x.strip() ]: if legacy_names: cards.append(basic + "'") else: cards.append(basic) cards.append('\u00a0') # Non-breaking space for card in self.deck.sideboard() + self.deck.command(): if legacy_names: cards.append(card.cockatrice_name()) else: cards.append(card.name()) return cards def is_legacy(self): return self.draft.cube.style_a == 'legacy' def _load_deck(self): existing_deck = self.draft.deck_for(self.user.id) if existing_deck: return existing_deck else: return self._new_deck() def _new_deck(self): deck = Deck() deck.user_id = self.user.id deck.name = f"{self.user.name}'s {self.draft.name} Deck" maindeck = [] sideboard = [] for card in self._picked_cards(): if card.sideboard: sideboard.append(card.cube_card) else: maindeck.append(card.cube_card) deck.set_maindeck(maindeck) deck.set_sideboard(sideboard) db.session.add(deck) db.session.commit() # Create a link between this deck and the draft link = DeckDraftLink() link.deck_id = deck.id link.draft_id = self.draft.id db.session.add(link) db.session.commit() return deck def _picked_cards(self): picked_cards = [] for seat in self.seats: picked_cards += [x.cube_card for x in seat.picks] return picked_cards
def _make_standard_packs(cube, unused, num_packs, num_players): cube_wrapper = CubeWrapper(cube) card_breakdown = { 'all': cube_wrapper.cards(), 'rare': [], # mythics get shuffled in here 'uncommon': [], 'common': [], } distribution = [ ('rare', 1), ('uncommon', 3), ('common', 10), ] separate_double_faced = False # These sets replaced one common slot with a double-faced card of any rarity. if cube.set_code.lower() in ('isd', 'dka'): separate_double_faced = True card_breakdown['double-faced'] = [] distribution = [ ('rare', 1), ('uncommon', 3), ('double-faced', 1), ('common', 9), ] if cube.set_code.lower() == 'khm': distribution.append(('snow-land', 1)) card_breakdown['snow-land'] = [] for card in card_breakdown['all']: if cube.set_code.lower() == 'khm' and 'snow land' in card.type_line().lower(): # One pack in six should have a snow dual. # Since there are ten snow duals, have 10 of each basic to make the ratios right. if 'basic' in card.type_line().lower(): card_breakdown['snow-land'] += [card] * 10 else: card_breakdown['snow-land'].append(card) elif separate_double_faced and '//' in card.name(): # Need to get the distribution right for rarity. if card.rarity() == 'mythic': card_breakdown['double-faced'].append(card) elif card.rarity() == 'rare': card_breakdown['double-faced'] += [card] * 7 elif card.rarity() == 'uncommon': card_breakdown['double-faced'] += [card] * 24 elif card.rarity() == 'common': card_breakdown['double-faced'] += [card] * 88 else: raise ValueError(f"Unknown rarity '{card.rarity()}' on '{card.name()}'") elif card.rarity() == 'mythic': card_breakdown['rare'].append(card) elif card.rarity() == 'rare': card_breakdown['rare'].append(card) card_breakdown['rare'].append(card) # Mythics are twice as rare as regular rares elif card.rarity() == 'uncommon': card_breakdown['uncommon'].append(card) elif card.rarity() == 'common': card_breakdown['common'].append(card) else: raise ValueError(f"Unknown rarity '{card.rarity()}' on '{card.name()}'") return _make_set_packs(card_breakdown, distribution, num_packs * num_players)
def _make_pack_slot_pools(cube_id): cube = Cube.query.get(cube_id) wrapper = CubeWrapper(cube) set_code = cube.set_code.lower() if cube.set_code else 'other' pools = { 'all': wrapper.cards(), 'rare': [], 'uncommon': [], 'common': [], 'basic-land': RoundBuilder._basic_land_pool(), } distribution = [ ('rare', 1), ('uncommon', 3), ('common', 10), ('basic-land', 1), ] # Special distributions/pools for some sets. if set_code == 'khm': pools['snow-land'] = [] elif set_code in ('isd', 'dka'): pools['double-faced'] = [] distribution = [ ('rare', 1), ('double-faced', 1), ('uncommon', 3), ('common', 9), ] # Build pools for card in pools['all']: if set_code == 'khm' and 'snow land' in card.type_line().lower(): # One pack in six should have a snow dual. # Since there are ten snow duals, have 10 of each basic to make the ratios right. if 'basic' in card.type_line().lower(): pools['snow-land'] += [card] * 10 else: pools['snow-land'].append(card) elif 'double-faced' in pools and '//' in card.name(): # Need to get the distribution right for rarity. # There is 1 of each mythic for every 2 of each rare # There is 1 of each rare for every 3 of each uncommon # There is 1 of each rare for every 11 of each common if card.rarity() == 'mythic': pools['double-faced'].append(card) elif card.rarity() == 'rare': pools['double-faced'] += [card] * 2 elif card.rarity() == 'uncommon': pools['double-faced'] += [card] * 6 elif card.rarity() == 'common': pools['double-faced'] += [card] * 22 else: raise ValueError( f"Unknown rarity '{card.rarity()}' on '{card.name()}'") elif card.rarity() == 'mythic': pools['rare'].append(card) elif card.rarity() == 'rare': pools['rare'].append(card) pools['rare'].append( card) # Mythics are twice as rare as regular rares elif card.rarity() == 'uncommon': pools['uncommon'].append(card) elif card.rarity() == 'common': pools['common'].append(card) else: raise ValueError( f"Unknown rarity '{card.rarity()}' on '{card.name()}'") return pools, distribution
def __init__(self, cube): self.cube = cube self.cube_wrapper = CubeWrapper(self.cube) self.info = self._pick_info() self.ratios = CreatureRatios(self.cube_wrapper.card_set())