def test_can_run_round_robin_double_tourney_of_any_size(self): for d in range(5): if d == 0: decks = 8 elif d == 1: decks = random.randint(9,16) elif d == 2: decks = random.randint(17,32) elif d == 3: decks = random.randint(33,64) elif d == 4: decks = random.randint(65,128) print('Testing can run round robin-double tourney of size '+str(decks)) t = _create_tourney(decks,4,'MODERN','CONSTRUCTED','ROUND_ROBIN','DOUBLE') self.client.get(reverse('tourney:tourney', args=(t.slug,))) num_rounds = count_rounds(t) num_loser_rounds = count_loser_rounds(t) for r in range(num_rounds): round_number = r+1 for m in Match.objects.filter(tourney=t, round=round_number, is_loser=False, is_complete=False): if m.first_deck and m.second_deck: Client().post('/tourney/'+t.slug+'/submit/', {'first_deck_wins': 2, 'second_deck_wins': 0, 'match_id': m.id}) for r in range(num_loser_rounds): round_number = r+1 for m in Match.objects.filter(tourney=t, round=round_number, is_loser=True, is_complete=False): if m.first_deck and m.second_deck: Client().post('/tourney/'+t.slug+'/submit/', {'first_deck_wins': 2, 'second_deck_wins': 0, 'match_id': m.id}) self.assertTrue(t.winning_deck)
def start(tourney): print('*****STARTING: '+tourney.name+'*****') deck_count = len(tourney.decks.all()) print('Number of decks in tourney: '+str(deck_count)) if deck_count < 4: tourney_status = 'Error: too few decks' return tourney_status elif deck_count > 128: tourney_status = 'Error: too many decks' return tourney_status elif tourney.qr_bracket == "SINGLE" and tourney.bracket == "DOUBLE" and not deck_count >= 8: tourney_status = 'Error: require at least 8 decks for a Single-Double Tournament' return tourney_status elif tourney.qr_bracket == "ROUND_ROBIN" and tourney.bracket == "DOUBLE" and not deck_count >= 8: tourney_status = 'Error: require at least 8 decks for a Round Robin-Double Tournament' return tourney_status elif tourney.qr_bracket == "ROUND_ROBIN" and tourney.bracket == "ROUND_ROBIN" and deck_count < 6: tourney_status = 'Error: require at least 6 decks for a Round Robin-Round Robin Tournament' return tourney_status #DETERMINE QR BRACKET if tourney.qr_bracket == "SINGLE" or tourney.qr_bracket == "DOUBLE": if deck_count == 4: qr_matches = 2 qr_loser_matches = 1 deck_count_after_qr = 2 elif 8 >= deck_count > 4: qr_matches = 4 qr_loser_matches = 2 deck_count_after_qr = 4 elif 16 >= deck_count > 8: qr_matches = 8 qr_loser_matches = 4 deck_count_after_qr = 8 elif 32 >= deck_count > 16: qr_matches = 16 qr_loser_matches = 8 deck_count_after_qr = 16 elif 64 >= deck_count > 32: qr_matches = 32 qr_loser_matches = 16 deck_count_after_qr = 32 elif 128 >= deck_count > 64: qr_matches = 64 qr_loser_matches = 32 deck_count_after_qr = 64 if tourney.qr_bracket == "ROUND_ROBIN": qr_groups = int(ceil(float(deck_count) / tourney.qr_rr_group_size)) qr_matches_per_group = (tourney.qr_rr_group_size * (tourney.qr_rr_group_size-1)) / 2 deck_count_after_qr = qr_groups*tourney.qr_rr_num_advance if tourney.bracket == "SINGLE" and deck_count_after_qr < 2: tourney_status = 'Error: too few decks to make bracket after round robin play' return tourney_status if tourney.bracket == "DOUBLE" and deck_count_after_qr < 4: tourney_status = 'Error: too few decks to make bracket after round robin play' return tourney_status #DETERMINE BRACKET LAYOUT if tourney.bracket == "SINGLE" or tourney.bracket == "DOUBLE": rounds = count_rounds(tourney) if deck_count_after_qr == 2: matches = 1 elif 4 >= deck_count_after_qr > 2: matches = 3 elif 8 >= deck_count_after_qr > 4: matches = 7 elif 16 >= deck_count_after_qr > 8: matches = 15 elif 32 >= deck_count_after_qr > 16: matches = 31 elif 64 >= deck_count_after_qr > 16: matches = 63 elif 128 >= deck_count_after_qr > 64: matches = 127 if tourney.bracket == "ROUND_ROBIN": groups = int(ceil(float(deck_count_after_qr) / tourney.rr_group_size)) matches_per_group = (tourney.rr_group_size * (tourney.rr_group_size-1)) / 2 deck_count_after_rr = groups*tourney.rr_num_advance if deck_count_after_rr < 2: tourney_status = 'Error: too few decks to make bracket after round robin play' return tourney_status rounds = count_rounds(tourney) if deck_count_after_rr == 2: matches = 1 elif 4 >= deck_count_after_rr > 2: matches = 3 elif 8 >= deck_count_after_rr > 4: matches = 7 elif 16 >= deck_count_after_rr > 8: matches = 15 elif 32 >= deck_count_after_rr > 16: matches = 31 elif 64 >= deck_count_after_rr > 16: matches = 63 elif 128 >= deck_count_after_rr > 64: matches = 127 ############################ ######## QR BRACKET ######## ############################ if tourney.qr_bracket == "SINGLE" or tourney.qr_bracket == "DOUBLE": #Create QR Match Slots for m in range(qr_matches): match = Match(tourney = tourney, round = 1, position = m+1, elimination = tourney.qr_elimination, is_qualifier = True, is_active = True ) match.save() if tourney.qr_bracket == "ROUND_ROBIN": #Create QR Match Slots for g in range(qr_groups): g = Group(tourney = tourney, round = 1, size = tourney.qr_rr_group_size, is_active = True ) g.save() for m in range(int(qr_matches_per_group)): match = Match(tourney = tourney, round = 1, position = m+1, group = g, elimination = tourney.qr_elimination, is_qualifier = True, is_active = True ) match.save() #Populate QR Match Slots print('Populating QR Match Slots') deck_list = tourney.decks.all() user_list = [] for d in deck_list: user_list.append(d.user) counted_user_list = Counter(user_list) # The old way of organizing the user list, in case the new one breaks. Can delete in a few versions # organized_user_list = sorted(counted_user_list, key=lambda u: (-counted_user_list[u], u)) organized_user_list = sorted(counted_user_list, key=lambda u: -counted_user_list[1]) for u in organized_user_list: user_deck_list = tourney.decks.filter(user=u) if tourney.qr_bracket == "ROUND_ROBIN": for d in user_deck_list: #hopefully pick a group where you don't already have a deck filtered_groups = Group.objects.filter(tourney=tourney) for g in filtered_groups: if g.size == len(g.decks.all()): filtered_groups = filtered_groups.exclude(id=g.id) else: for group_deck in g.decks.all(): if d.user == group_deck.user: filtered_groups = filtered_groups.exclude(id=g.id) break if not filtered_groups: #if every group has at least one of his deck, there is nothing more we can do. filtered_groups = Group.objects.filter(tourney=tourney) #now we know what groups would be good for this deck, so go ahead and place him in one placed = False while placed == False: possible_group = filtered_groups.order_by('?')[:1].get() if possible_group.size > len(possible_group.decks.all()): gs = GroupStats(deck=d, group=possible_group, points = 0, margin = 0) gs.save() placed = True match_count_for_deck = tourney.qr_rr_group_size-1 empty_matches_allowed = match_count_for_deck-len(possible_group.decks.all())+1 for i in range(match_count_for_deck): place(tourney,1,False,d,possible_group,empty_matches_allowed) else: for d in user_deck_list: place(tourney,1,False,d) if tourney.qr_bracket == "DOUBLE": #Create QR Loser Match Slots for m in range(qr_loser_matches): match = Match(tourney = tourney, round = 1, position = m+1, elimination = tourney.qr_elimination, is_qualifier = True, is_active = False, is_loser = True ) match.save() ################################## ############ BRACKET ############ ################################## if tourney.bracket == "SINGLE" or tourney.bracket == "DOUBLE": #Create Remaining Rounds Match Slots print("Bracket is " + str(tourney.bracket) + ". Creating Matches...") print("matches = " + str(matches)) for r in range(rounds-1): print("r = " + str(r)) for m in range(int(ceil(matches/(2.0**(r+1))))): print("m = " + str(m)) match = Match(tourney = tourney, round = r+2, position = m+1, elimination = tourney.elimination ) if tourney.bracket == "SINGLE": if match.round == rounds: match.is_final = True match.elimination = tourney.final_elimination if match.round == rounds-1: match.is_semi = True match.elimination = tourney.semi_elimination if tourney.bracket == "DOUBLE": if match.round == rounds: match.is_semi = True match.elimination = tourney.semi_elimination match.save() if tourney.bracket == "ROUND_ROBIN": #Create Round Robin Round for g in range(groups): g = Group(tourney = tourney, round = 2, size = tourney.rr_group_size, is_active = True ) g.save() for m in range(int(matches_per_group)): match = Match(tourney = tourney, round = 2, position = m+1, group = g, elimination = tourney.elimination, is_active = False ) if match.round == rounds-1: match.is_semi = True match.elimination = tourney.semi_elimination match.save() #Create Remaining Rounds Match Slots for r in range(rounds-2): for m in range(int(ceil(matches/(2.0**(r+1))))): match = Match(tourney = tourney, round = r+3, position = m+1, elimination = tourney.elimination ) if match.round == rounds: match.is_final = True match.elimination = tourney.final_elimination if match.round == rounds-1: match.is_semi = True match.elimination = tourney.semi_elimination match.save() if tourney.bracket == "DOUBLE": #Create Remaining Rounds Loser Match Slots loser_rounds = count_loser_rounds(tourney) for r in range(loser_rounds): this_round = r+2 for m in range(loser_matches_in_round(tourney.qr_bracket, deck_count_after_qr, this_round) ): match = Match(tourney = tourney, round = this_round, position = m+1, elimination = tourney.elimination, is_loser = True ) if match.round == loser_rounds+1: match.is_final = True match.elimination = tourney.final_elimination if match.round == loser_rounds: match.is_semi = True match.elimination = tourney.semi_elimination match.save() ################################## ########### UNIVERSAL ############ ################################## #after all user decks are placed, assign byes. assign_byes(tourney,1) tourney.has_started = True tourney.save() tourney_status = 'active' return tourney_status
def tourney(request, tourney_slug, template_name="tourney/tourney.html" ): tourney = Tourney.objects.get(slug=tourney_slug) user = request.user logged_in = False if User.objects.filter(username=user.username): logged_in = True if tourney.is_finished: tourney_status = 'finished' elif tourney.has_started: tourney_status = 'active' elif tourney.registration_deadline > timezone.now(): tourney_status = 'registration' user_decks_entered = 0 if user.username: choices = [] user_decks = user.deck_set.filter(is_active=True) #filter decks for format if tourney.format == 'STANDARD': user_decks = user_decks.exclude(format='MODERN') user_decks = user_decks.exclude(format='LEGACY') user_decks = user_decks.exclude(format='VINTAGE') elif tourney.format == 'MODERN': user_decks = user_decks.exclude(format='LEGACY') user_decks = user_decks.exclude(format='VINTAGE') elif tourney.format == 'LEGACY': user_decks = user_decks.exclude(format='VINTAGE') elif tourney.format == 'BLOCK': user_decks = user_decks.filter(format='BLOCK') #filter decks for type if tourney.type == 'CONSTRUCTED': user_decks = user_decks.exclude(type='DRAFT') user_decks = user_decks.exclude(type='SEALED') elif tourney.type == 'DRAFT': user_decks = user_decks.filter(type='DRAFT') elif tourney.type == 'SEASON': user_decks = user_decks.filter(type='SEASON') elif tourney.type == 'SEALED': user_decks = user_decks.filter(type='SEALED') elif tourney.type == 'COMMANDER': user_decks = user_decks.filter(type='COMMANDER') #only allow decks that aren't already in this tournament if user_decks: for d in user_decks: already_registered = False for rd in tourney.decks.all(): if rd == d: already_registered = True user_decks_entered += 1 if not already_registered: choices.append((d.name,d.name)) else: choices = {("no_decks","No decks!"),} decks_remaining = tourney.max_decks_per_player - user_decks_entered if request.method == 'POST': if 'unregister' in request.POST: deck = request.POST['deck'] d = Deck.objects.get(name=deck) tourney.decks.remove(Deck.objects.get(name=deck)) tourney.save() messages.success(request, deck+' successfully unregistered.') #also, return the unbound registration form with correct info decks_remaining += 1 choices.append((d,d.name)) form = RegistrationForm(choices=choices) else: form = RegistrationForm(request.POST, choices=choices) if form.is_valid(): deck_is_valid_submission = True #Make sure they haven't submitted too many decks already max_submissions = tourney.max_decks_per_player user_submissions = 0 for d in tourney.decks.all(): if d.user == user: user_submissions += 1 if user_submissions >= max_submissions: messages.error(request, 'Error: You have already registered the maximum number of decks for this tournament.') deck_is_valid_submission = False return HttpResponseRedirect('/tourney/'+tourney.slug) #Make sure the deck isn't already in this tourney deck = request.POST['deck'] if user_decks: for d in user_decks: if deck == d.name: for t in d.tourney_set.all(): if t == tourney: messages.error(request, 'Error: '+deck+' is already registered for this tournament.') deck_is_valid_submission = False return HttpResponseRedirect('/tourney/'+tourney.slug) else: messages.error(request, "Error: you don't seem to have any decks.") deck_is_valid_submission = False return HttpResponseRedirect('/tourney/'+tourney.slug) #if deck is a valid submission, register it if deck_is_valid_submission: print(deck) tourney.decks.add(Deck.objects.get(name=deck)) tourney.save() messages.success(request, deck+' successfully registered.') return HttpResponseRedirect('/tourney/'+tourney.slug) else: form = RegistrationForm(choices=choices) elif tourney.start_date < timezone.now(): #start the tournament tourney_status = start_tourney.start(tourney) else: tourney_status = 'pending' if tourney.has_started: if tourney.bracket == "SINGLE" or tourney.bracket == "DOUBLE" or tourney.bracket == "ROUND_ROBIN": rounds = [] num_groups = len(Group.objects.filter(tourney=tourney, round=2)) deck_count = len(tourney.decks.all()) num_rounds = count_rounds(tourney) for round_number in range(num_rounds): rn = round_number+1 if rn <= num_rounds: matches = Match.objects.filter(tourney=tourney,round=rn,is_loser=False) rounds.append(matches) if tourney.bracket == "DOUBLE": loser_rounds = [] num_loser_rounds = count_loser_rounds(tourney) r1_loser_matches = Match.objects.filter(tourney=tourney,round=1,is_loser=True) loser_rounds.append(r1_loser_matches) for round_number in range(num_loser_rounds): #lots of offsets because we don't add the qualifying round here rn = round_number+2 if rn <= num_loser_rounds+1: if num_loser_rounds+1 > rn: loser_matches = Match.objects.filter(tourney=tourney,round=rn,is_loser=True) loser_rounds.append(loser_matches) elif num_loser_rounds+1 == rn: finals_match = Match.objects.get(tourney=tourney,round=rn,is_loser=True) if tourney.qr_bracket == "ROUND_ROBIN": qr_groups = Group.objects.filter(tourney=tourney, round=1) num_qr_groups = len(qr_groups.all()) qr_stats = [] for g in qr_groups: qr_group_decks = g.decks.order_by('-groupstats__points').order_by('-groupstats__margin') group_stats = [] for d in qr_group_decks.all(): gs = GroupStats.objects.get(group=g, deck=d) stats = (d, gs.points, gs.margin) group_stats.append(stats) qr_stats.append(group_stats) if tourney.bracket == "ROUND_ROBIN": r2_groups = Group.objects.filter(tourney=tourney, round=2) r2_stats = [] for g in r2_groups: r2_group_decks = g.decks.order_by('-groupstats__points').order_by('-groupstats__margin') group_stats = [] for d in r2_group_decks.all(): gs = GroupStats.objects.get(group=g, deck=d) stats = (d, gs.points, gs.margin) group_stats.append(stats) r2_stats.append(group_stats) print('Tourney status: '+tourney_status) if tourney_status == 'Error: too few decks': messages.error(request, "Error: Too Few Decks Entered in Tournament") elif tourney_status == 'Error: too many decks': messages.error(request, "Error: Too Many Decks Entered in Tournament") elif tourney_status == 'Error: too few decks to make Double Elimination bracket after Round Robin elimination': messages.error(request, tourney_status) elif tourney_status == 'Error: require at least 8 decks for a Single-Double Tournament': messages.error(request, tourney_status) elif tourney_status == 'Error: require at least 8 decks for a Round Robin-Double Tournament': messages.error(request, tourney_status) elif tourney_status == 'Error: too many decks to make bracket after round robin play': messages.error(request, tourney_status) elif tourney_status == 'Error: require at least 6 decks for a Round Robin-Round Robin Tournament': messages.error(request, tourney_status) elif tourney_status == 'Error: too few decks to make bracket after round robin play': messages.error(request, tourney_status) return render(request, template_name, locals(), context_instance=RequestContext(request))