Skip to content

Instantly share code, notes, and snippets.

@Rapptz
Created January 26, 2016 08:42
Show Gist options
  • Save Rapptz/a2074e62ef53d6672373 to your computer and use it in GitHub Desktop.
Save Rapptz/a2074e62ef53d6672373 to your computer and use it in GitHub Desktop.
The random map generator for Splatoon tournaments.
from __future__ import division
import random
from collections import Counter, namedtuple
import bisect
import math
# for my own purposes.
debug = False
def weighted_choice(choices):
values, weights = zip(*choices)
total = 0
cum_weights = []
for w in weights:
total += w
cum_weights.append(total)
x = random.random() * total
i = bisect.bisect(cum_weights, x)
return values[i]
def debug_print(*args, **kwargs):
if debug:
print(*args, **kwargs)
maps = [
'Urchin Underpass',
'Walleye Warehouse',
'Saltspray Rig',
'Blackbelly Skatepark',
'Arowana Mall',
'Port Mackerel',
'Kelp Dome',
'Bluefin Depot',
'Moray Towers',
'Camp Triggerfish',
'Flounder Heights',
'Hammerhead Bridge',
"Museum d'Alfonsino",
'Mahi-Mahi Resort',
'Piranha Pit',
'Ancho-V Games'
]
# probability for each is the second parameter
# e.g. 40 -> 40%
# modes = [('Splat Zones', 45), ('Rainmaker', 10), ('Tower Control', 45)]
modes = ['Splat Zones', 'Rainmaker', 'Tower Control']
Game = namedtuple('Game', ['map', 'mode'])
# the total number of rounds being played -- including Losers for DE and GF2
rounds = 7
# the number of games per round
games_per_round = 9
# the total number of times a game/mode combo can be played overall in the tournament
overall_count = math.ceil(math.log(rounds, 2))
# number of times RM can show up
maximum_rainmaker = (games_per_round / 2) - 1
# for output purposes, note if it's a double elimination tournament.
is_double_elimination = False
assert games_per_round % 2 != 0
# the last game in a decisive victory set, e.g. Bo5 -> 3, Bo7 -> 4
best_of_game = math.ceil(games_per_round / 2.0)
def can_be_added_to_pool(game, overall_pool, pool, previous_pool):
if overall_pool.count(game) == overall_count:
debug_print('overall_pool -> False')
return False
counter = Counter((entry.mode for entry in pool))
has_one_of_each_mode = all(counter[mode] for mode in modes)
if len(pool) < best_of_game:
if not has_one_of_each_mode and counter[game.mode] > 0:
debug_print('every best_of_game needs a unique mode')
return False
if counter[game.mode] > (maximum_rainmaker - 1) and game.mode == 'Rainmaker':
debug_print('too much rainmaker')
return False
# map/mode from previous round cannot be played
if game in previous_pool:
debug_print('duplicate from other game')
return False
# the previous mode cannot be played
if len(pool) and game.mode == pool[-1].mode:
debug_print('previous mode')
return False
if counter[game.mode] == best_of_game - 1:
debug_print('too much of this mode')
return False
for entry in pool:
# no duplicate maps
if entry.map == game.map:
debug_print('duplicate map')
return False
# well considering we're back here, let's add it
debug_print('perfectly fine')
return True
def pool_to_string(pool):
result = []
for i, game in enumerate(pool):
result.append('Game {0}: {1.mode} on {1.map}'.format(i + 1, game))
return '\n'.join(result)
# so we say we need 5 random picks from the permutations above..
pool = []
previous_pool = []
overall_pool = []
for r in range(rounds):
# make sure it's a unique choice
while len(pool) < games_per_round:
stage = random.choice(maps)
# mode = weighted_choice(modes)
mode = random.choice(modes)
game = Game(map=stage, mode=mode)
debug_print('Round {}: {} (pool: {})'.format(r + 1, game, pool))
if can_be_added_to_pool(game, overall_pool, pool, previous_pool):
pool.append(game)
overall_pool.append(game)
# print('valid')
output = pool_to_string(pool)
if is_double_elimination:
number = r + 1
# rounds is GF2, and rounds // 2 - 1 is GF1, etc.
gf1 = (rounds // 2) - 1
losers_finals = rounds - 1
losers_semi = rounds - 2
winners_finals = gf1 - 1
winners_semis = gf1 - 2
if number < gf1:
print("Winner's Round {} has:\n{}\n".format(number, output))
elif number == gf1:
print("Winner's Grand Finals has:\n{}\n".format(output))
elif number == winners_finals:
print("Winner's Finals has:\n{}\n".format(output))
elif number == winners_semis:
print("Winner's Semifinals has:\n{}\n".format(output))
elif number == losers_finals:
print("Loser's Finals has:\n{}\n".format(output))
elif number == losers_semi:
print("Loser's Semifinals has:\n{}\n".format(output))
elif number == rounds:
print("Winner's Grand Finals 2 has:\n{}\n".format(output))
else:
print("Loser's Round {} has:\n{}\n".format(number - gf1, output))
else:
print('Round {} has:\n{}\n'.format(r + 1, output))
previous_pool = list(pool)
pool = []
print('Sanity Check:')
overall = Counter(overall_pool)
modes = Counter(map(lambda x: x[1], overall_pool))
maps = Counter(map(lambda x: x[0], overall_pool))
print(modes)
print(maps)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment