Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # exalted.py
- import random
- from collections import Counter
- # type player = nonce * skill * trophies
- def init_players(num_players, starting_trophies):
- players = [init_player(starting_trophies) for _ in range(num_players)]
- return sorted(players)
- def init_player(starting_trophies):
- return (
- random.random(),
- random.gauss(0, 1),
- starting_trophies,
- )
- def get_trophies(player):
- _nonce, _skill, trophies = player
- return trophies
- def get_player_with_updated_trophies(player, change):
- nonce, skill, trophies = player
- return nonce, skill, trophies + change
- def get_skill(player):
- _nonce, skill, _trophies = player
- return skill
- REQS = [
- (0, "forbidden"),
- (100, "sinful-1"),
- (300, "sinful-2"),
- (500, "sinful-3"),
- (700, "agony-1"),
- (900, "agony-2"),
- (1200, "agony-3"),
- (1500, "lotus"),
- (2000, "nirvana"),
- ]
- def get_league(player):
- trophies = get_trophies(player)
- result = None
- for required_trophies, league in REQS:
- if trophies >= required_trophies:
- result = league
- # can also break on else
- return result
- def _test_get_league():
- assert get_league(init_player(1200)) == "agony-3"
- assert get_league(init_player(0)) == "forbidden"
- assert get_league(init_player(1499)) == "agony-3"
- assert get_league(init_player(2000)) == "nirvana"
- assert get_league(init_player(4000)) == "nirvana"
- def divide_players_into_leagues(players):
- leagues = {}
- for p in players:
- league_name = get_league(p)
- league_players = leagues.get(league_name, [])
- leagues[league_name] = league_players
- league_players.append(p)
- return leagues
- def _test_divide_players_into_leagues():
- players = [
- init_player(1200),
- init_player(0),
- init_player(1499),
- init_player(2000),
- init_player(4000),
- ]
- expected = {
- "agony-3": [players[0], players[2]],
- "forbidden": [players[1]],
- "nirvana": [players[3], players[4]],
- }
- assert divide_players_into_leagues(players) == expected
- def even_distribution_algo(n, bucket_size):
- # find number of buckets
- num_buckets = int(n / bucket_size) + (1 if n % bucket_size > 0 else 0)
- # find gap to all buckets full
- gap = num_buckets * bucket_size - n
- # init full buckets
- buckets = [bucket_size] * num_buckets
- # round robin subtract from each bucket until gap is accounted for
- for i in range(gap):
- j = i % num_buckets
- buckets[j] -= 1
- buckets = list(reversed(buckets))
- return buckets
- def _test_even_distribution_algo():
- assert even_distribution_algo(43, 20) == [15, 14, 14]
- assert even_distribution_algo(1413, 20) == [20] * 64 + [19] * 7
- def shuffled(l):
- result = l[:]
- random.shuffle(result)
- return result
- def randomly_partition_by_ratio_list(s, distribution):
- assert len(s) == sum(distribution)
- l = shuffled(s)
- result = []
- for size in distribution:
- if size > 0:
- result.append(l[-size:])
- del l[-size:]
- else:
- assert size == 0
- result.append([])
- return result
- def flatten(l):
- return [element for sublist in l for element in sublist]
- def _test_randomly_partition_by_ratio_list():
- substrate = [0, 1, 2, 3, 4, 5, 6, 7]
- distribution = [4, 0, 1, 3]
- result = randomly_partition_by_ratio_list(substrate, distribution)
- assert [len(l) for l in result] == distribution
- assert set(flatten(result)) == set(substrate)
- def divide_set_into_brackets(s):
- distribution = even_distribution_algo(len(s), 20)
- return randomly_partition_by_ratio_list(s, distribution)
- def bracketize(players):
- leagues = divide_players_into_leagues(players)
- result = []
- for league_name in leagues:
- s = leagues[league_name]
- brackets = divide_set_into_brackets(s)
- for bracket in brackets:
- result.append((league_name, bracket))
- return result
- def rank_players(players):
- # just use their raw skill rating, higher is better
- return list(reversed(sorted(players, key=get_skill)))
- rewards_table = None
- def init_rewards_table():
- global rewards_table
- table = {
- "forbidden": [100] * 7 + [50] * 5 + [0] * 8,
- "sinful": [150] * 5 + [100] * 3 + [0] * 4 + [-75] * 3 + [-100] * 5,
- "agony": [200] * 3 + [100] * 3 + [0] * 6 + [-75] * 3 + [-150] * 5,
- "lotus": [100] * 3 + [0] * 7 + [-100] * 10,
- "nirvana": [100] * 5 + [-200] * 15,
- }
- assert all([len(l) == 20 for l in table.values()])
- # For brackets with fewer than 20 players, chop values from the rewards
- def chop(base_list, indices):
- for i in indices:
- base_list[i] = None
- return [e for e in base_list if e is not None]
- chopping_order = [0, 19, 9, 15, 4] + \
- [12, 16, 13, 8, 1, 17, 3, 6, 18, 5, 14, 10, 7, 11, 2]
- rewards_table = {}
- for bracket_size in range(1, 21):
- rewards_table[bracket_size] = {}
- for league_name in table:
- base_list = table[league_name][:]
- chop_n = 20 - bracket_size
- new_list = chop(base_list, chopping_order[:chop_n])
- rewards_table[bracket_size][league_name] = new_list
- def stringify_rewards_table():
- rows = []
- for bracket_size in rewards_table:
- for league_name in rewards_table[bracket_size]:
- bracket_rewards = rewards_table[bracket_size][league_name]
- for i in range(len(bracket_rewards)):
- rank = i + 1
- rows.append((bracket_size, league_name,
- rank, bracket_rewards[i]))
- rows = sorted(rows)
- return "\n".join([", ".join([str(e) for e in row]) for row in rows])
- def get_player_rewards(league, ranked_players):
- name_mapping = {
- "forbidden": "forbidden",
- "sinful-1": "sinful",
- "sinful-2": "sinful",
- "sinful-3": "sinful",
- "agony-1": "agony",
- "agony-2": "agony",
- "agony-3": "agony",
- "lotus": "lotus",
- "nirvana": "nirvana",
- }
- if rewards_table is None:
- _init_rewards_table()
- league = name_mapping[league]
- bracket_size = len(ranked_players)
- return rewards_table[bracket_size][league]
- def run_single_bracket(league, players):
- ranked_players = rank_players(players)
- result = []
- for reward, player in zip(get_player_rewards(league, ranked_players), ranked_players):
- result.append(get_player_with_updated_trophies(player, reward))
- return result
- def run_abyss_cycle(players):
- brackets = bracketize(players)
- all_updated_players = []
- for league_name, bracket_players in brackets:
- updated_players = run_single_bracket(league_name, bracket_players)
- all_updated_players += updated_players
- return sorted(all_updated_players)
- def stringify_player(player):
- skill = get_skill(player)
- trophies = get_trophies(player)
- league = get_league(player)
- return f"player, {skill}, {trophies}, {league}"
- def stringify_players(players):
- return "\n".join([stringify_player(p) for p in players])
- def get_leagues_summary(players):
- league_only = [get_league(p) for p in players]
- return Counter(league_only)
- def stringify_summary(c):
- leagues = [league_name for _, league_name in REQS]
- row = [c[league_name] for league_name in leagues]
- return ", ".join([str(n) for n in row])
- if __name__ == "__main__":
- _test_get_league()
- _test_even_distribution_algo()
- _test_divide_players_into_leagues()
- _test_randomly_partition_by_ratio_list()
- # Audit the rewards table
- with open("rewards_table.csv", "w") as f:
- init_rewards_table()
- f.write(stringify_rewards_table())
- random.seed(102750340)
- players = init_players(10000, 1200)
- # print(stringify_players(players))
- with open("exalted_summaries.csv", "w") as f:
- for _ in range(500):
- players = run_abyss_cycle(players)
- summary = get_leagues_summary(players)
- s = stringify_summary(summary)
- print(s)
- f.write(s)
- f.write("\n")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement