*완성이라고 하기엔 흠이 많은 프로그램입니다.
문제 정의
롤 사설 경기에서 플레이어들의 정보를 바탕으로 균형 잡힌 팀을 자동으로 구성해주는 프로그램입니다.
로직 설명
- 플레이어들의 정보를 수집합니다. 플레이어의 정보에는 [닉네임, 랭크 티어, 주 포지션, 서브 포지션들] 이 있습니다.
- 포지션별로 플레이어들을 분류합니다. 인원이 적은 포지션부터 우선적으로 배정합니다.
- 배정되지 않은 플레이어들은 랜덤하게 남은 포지션에 할당합니다.
코드 원문은 깃허브에서 확인할 수 있습니다.
# main.py
def build_team():
# 포지션을 담을 리스트
positions = defaultdict(list) # 각 포지션에 해당하는 플레이어들을 담기 위한 딕셔너리, 기본값은 빈 리스트
for lane in total_lane:
positions[lane] # 각 포지션을 키로 초기화 (실제 동작은 없지만 구조를 명확히 하기 위해)
# 포지션 리스트 채우기
for player in players:
name, tier, primary_pos, *secondary_pos = player # 플레이어의 이름, 티어, 주 포지션, 서브 포지션들을 할당
mmr = MMR.tier_to_mmr(tier) # 플레이어의 티어를 MMR 값으로 변환
positions[primary_pos].append((name, mmr, primary_pos)) # 주 포지션에 플레이어 추가
for pos in secondary_pos:
positions[pos].append((name, mmr, pos)) # 각 서브 포지션에도 플레이어를 추가
# >> 포지션 별로 몰리는 라인도 생기고, 그렇지 않은 라인도 있을 것이다
# 인원이 적은 라인에서 먼저 배정을 해주자.
tmp_positions = sorted([(lane, len(positions[lane])) for lane in total_lane], key=lambda x: x[1])
# 각 포지션에 할당된 플레이어 수를 기반으로 포지션을 오름차순 정렬
tm1, tm2 = {}, {} # 팀 1과 팀 2를 위한 딕셔너리, 각 포지션의 플레이어를 저장
assigned_players = set() # 이미 팀에 배정된 플레이어들을 추적하기 위한 집합
for pos, n in tmp_positions:
random.shuffle(positions[pos]) # 해당 포지션에 있는 플레이어들의 순서를 랜덤하게 섞음
for i in range(min(2, len(positions[pos]))): # 각 포지션에서 최대 2명까지 플레이어를 팀에 배정
player = positions[pos].pop() # player = (name, mmr, position) 형태로 플레이어를 리스트에서 꺼냄
if player[0] in assigned_players:
continue # 이미 배정된 플레이어는 건너뜀
# Todo: random하게 팀 배정해야 됨. 순서대로가 아니라.
if len(tm1) <= len(tm2):
tm1[pos] = (player[0], player[1]) # 팀 1에 플레이어 배정
else:
tm2[pos] = (player[0], player[1]) # 팀 2에 플레이어 배정
assigned_players.add(player[0]) # 팀 배정을 받은 유저로 추가
# 배정을 받지 못한 플레이어들 처리
remaining_players = [] # 아직 배정되지 않은 플레이어들을 담을 리스트
for pos in total_lane:
for player in positions[pos]:
if player[0] not in assigned_players:
remaining_players.append(player) # 아직 팀에 배정되지 않은 플레이어를 추가
random.shuffle(remaining_players) # 남은 플레이어들을 랜덤하게 섞음
# 빈 자리에 남은 플레이어 배정
for pos in total_lane:
if pos not in tm1 and remaining_players:
p = remaining_players.pop() # 남은 플레이어를 꺼내서
tm1[pos] = (p[0], p[1]) # 팀 1의 빈 포지션에 배정
if pos not in tm2 and remaining_players:
p = remaining_players.pop() # 남은 플레이어를 꺼내서
tm2[pos] = (p[0], p[1]) # 팀 2의 빈 포지션에 배정
print_team(tm1, tm2) # 최종 팀 구성 출력
Q. 왜 포지션 리스트에 들어 있는 플레이어들을 섞는가?
random.shuffle(positions[pos]) # 해당 포지션에 있는 플레이어들의 순서를 랜덤하게 섞음
A. 포지션이 3명 이상 겹치는 경우도 분명 있을 것이라 판단했다. 사람들이 input 순서에 따라 라인 배정을 받는 것을 원치 않았다. 그래서 선택된 포지션을 플레이 할 수 있는 플레이어들을 셔플하고 pop을 함으로써 최대한 무작위로, 하지만 자신이 운용 가능한 라인에 배정되도록 했다.
개발 배경
부대에서 선임, 동기, 후임들과 롤 5:5 내전을 하게 된 재미있는 기회가 있었다. 나가면 많아야 3판이 다라서, 팀 밸런싱에 고민을 많이 했다.
팀 빌딩에는 여러 가지의 경우의 수가 나올 것이라 기대했다. 실제로 팀을 꾸리다보니, 관리하기 어려울 정도로 다양한 팀 밸런싱이 이루어졌다. 추가로 막상해보니 직접 경우의 수를 따지기가 번거롭기도 했다. 그래서 주어진 조건 안에서 랜덤한 방법으로 팀을 여러 개 뽑아내주는 프로그램이 있으면 좋겠다고 생각했다. 그러면 여러 종류의 팀을 볼 수 있을 것이고, 제안 받은 여러 안들 중 리밸런싱을 하거나 마음에 드는 것을 채택해서 바로 팀으로 결정될 수 있다는 장점이 있다고 생각했기 때문이다.
문제점
위 로직대로는 주 포지션과 서브 포지션에 대한 경계가 사실 없다. 그래서 주 포지션과 서브 포지션을 따로 보관하고 팀 배정에 활용했어야 했다.
댓글