일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- this call
- member function pointer
- base from member
- conversion constructor
- vector size
- constructor
- virtual inheritance
- return by reference
- std::endl
- vector capacity
- c++ multi chatting room
- std::cout
- std::ostream
- c++ basic practice
- 더 지니어스 양면포커
- virtual function
- operator overloading
- dynamic_cast
- pointer to member data
- increment operator
- discord bot
- virtual function table
- diamond inheritance
- virtual destructor
- suffix return type
- std::vector
- C++
- placement new
- delete function
- new&delete
- Today
- Total
I'm FanJae.
[EA FC Pro club Draft Bot] 5. Draft Logic 구현 본문
[EA FC Pro club Draft Bot] 5. Draft Logic 구현
FanJae 2024. 9. 4. 22:141. logging 설정
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
- logging을 하도록 기록해두면, 오류를 잡는데 있어서 상당히 유용하다.
- 본인은 우선, 개발을 진행중이였기에 logging level을 DEBUG로 처리하였다.
2. 초기 셋팅
2-1. 기본 전제
- 프로클럽 모드에서 가장 많이 쓰이는 4-3-3 포메이션(DM)을 예시로 하였다.
- 가장 일반적으로 많이 하는 포메이션이고, 여러 인원이 처음 접하기 무난한 포메이션이다.
- 기본적인 드래프트 구현을 우선으로 했다. 남은 포지션 출력 및 충돌 처리는 고려하지 않았다.
- 충돌 처리가 정말 중요한 문제이므로, 별도의 포스트로 다룬다.
- 커스텀 이미지를 사용해야 한다.
- 한번 초기화되면 일반적으로 잘 바뀌지 않는 경우 상수처럼 대문자로 처리하였다.
2-2. 초기값 셋팅 (이모지 가져오기 및 ID값 셋팅)
# 비교적 상수처럼 쓰이는 값들 (EMOJI_MAPPING도 한번 값을 초기화 하면 변하지 않는다)
EMOJI_ID_TARGET = ['ST','LW','RW','CM','CDM','LB','CB','RB','GK']
EMOJI_MAPPING = {
'ST': None,
'LW': None,
'RW': None,
'CM': None,
'CDM': None,
'LB': None,
'CB': None,
'RB': None,
'GK': None,
}
COUNT_TIME = 5
POSITION_COUNT = None
KEYS_LIST = []
async def setup_emojis(guild):
if guild:
emojis = await guild.fetch_emojis()
global KEYS_LIST
global POSITION_COUNT
for emoji_key in EMOJI_MAPPING.keys():
emoji = discord.utils.get(emojis, name=emoji_key)
if emojis:
EMOJI_MAPPING[emoji_key] = str(emoji.id)
KEYS_LIST = list(EMOJI_MAPPING.keys())
POSITION_COUNT = len(KEYS_LIST)
else:
print(f'Guild with ID {SERVER_ID} not found.')
- 이 코드는 이모지를 가져와서 ID값을 셋팅한다.
- 본인의 경우 봇이 실행될 때 이 코드를 실행하게 만들었으며, 추후에는 이 코드 실행 전까지 다른 어떤 명령어도 사용하지 못하게 설정하고자한다.
emojis = await guild.fetch_emojis()
- await guild.fetch_emojis()를 호출해서 서버의 이모지 목록을 가지고 온다. 서버의 모든 이모지를 가져온다.
emoji = discord.utils.get(emojis, name=emoji_key)
- 이모지 목록에서 emoji_key 값과 일치하는 이름을 가진 이모지를 찾아 저장한다.
KEYS_LIST = list(EMOJI_MAPPING.keys())
POSITION_COUNT = len(KEYS_LIST)
- 구현 상의 이유로 키값을 미리 리스트화 하여 따로 꺼내놨다.
3. Draft Logic
- 핵심 코드만 별도 기록하며, 순서는 다음과 같이 진행된다.
① 드래프트를 참여하는 인원을 담아줄 배열을 이 타이밍에 초기화한다.
async def 드래프트(ctx):
global position_member
position_member = {i: [] for i in range(POSITION_COUNT)}
※ 이 변수가 드래프트 명령어를 여럿이 사용할때 충돌이 나게 만드는 가장 큰 원인이다.
※ 이것이 문제가 되는 이유는 추후 별도 포스트로 다룬다.
② 투표 이모지 추가
for emoji_key in EMOJI_MAPPING.keys():
emoji_id = EMOJI_MAPPING[emoji_key]
if emoji_id:
emoji = discord.PartialEmoji(name=emoji_key, id=int(emoji_id))
await message.add_reaction(emoji)
- 미리 저장해놨던 이모지 이름을 순회하면서, 이모지를 표현하는 객체를 생성한다.
③ 이모지 반응을 처리하는 이모지 핸들러 구현
@bot.event
async def on_reaction_add(reaction, user):
# logging.debug("on_reaction_add called") # 디버깅 로그 추가
# logging.info(f"Reaction by: {user}, Reaction: {reaction.emoji}") # 정보 로그
# 봇 반응 제외
if user.bot:
return
emoji_name = str(reaction.emoji).split(':')[1]
if emoji_name in EMOJI_MAPPING:
position = KEYS_LIST.index(emoji_name)
# 현재 포지션에 사용자 ID 추가
if user.id not in position_member[position]:
position_member[position].append(user.id)
- 실제 실행시에는 Logging을 상당히 빡빡하게 잡았었다.
- 이 핸들러는 이모지를 클릭했을때 반응에 사용된 이모지를 추출한다.
- 이모지 이름에 해당하는 위치를 찾아서 사용자의 ID를 추가해 둔다.
- on_reaction_add는 Discord.py 라이브러리에서 제공하는 이벤트 핸들러의 표준 형태다.
※ Discord.py는 비동기식 이벤트 기반 라이브러리로, Discord 서버와의 상호작용 처리를 위해 다양한 이벤트를 제공한다.
- 사용자가 메시지에 반응을 추가하면 on_reaction_add가 호출되는 방식이다.
④ 랜덤 인원 뽑기 로직
# async def 드래프트(ctx) 부분의 연장선이다.
for i in range(len(position_member)):
key_value = KEYS_LIST[i]
if key_value != 'CM' and key_value != 'CB':
choose_player = pick_and_remove(position_member[i], 1)
if not choose_player:
draft_message += f"{key_value} : 없음.\n"
else:
draft_message += f"{key_value} : <@{choose_player[0]}>\n"
else:
choose_player = pick_and_remove(position_member[i], 2)
# 출력 로직 생략
await ctx.send(content=f"{draft_message}")
def pick_and_remove(player_list, count):
logging.debug(f"{player_list}") # 디버깅 로그 추가
if len(player_list) < count:
count = len(player_list)
choose_player = random.sample(player_list,count)
for player in choose_player:
player_list.remove(player)
return choose_player
- player_list를 인자로 받고 뽑아야 하는 인원수를 받아 해당 인원 만큼 뽑아온다.
- 만약 누른 인원이 부족한 경우(Ex. CM은 2명을 뽑지만 1명만 누른 경우)엔 뽑아야 하는 인원수를 조정한다.
- 4-3-3 포메이션에서 CM과 CB를 제외한 경우는 모두 1명이 나와야 하기 때문에 1명만 뽑는다.
- else의 출력 로직은 생략했다.
⑤ 실행결과
- 이전 서버에서 운영하던 방식과 다르게 구현했지만, 여러번 테스트 끝에 정상 작동을 확인하였다.
⑥ git 반영
- 기본적으로 기능을 구현할 때는 특정 기능이 독립적인 성격을 띄고있다면, 가급적 branch를 나눠서 작업한다.
- 이후에 병합을 하더라도 그렇게 작업을 해주는 것이 유용하다.
- 나의 경우 draft_preset이라는 branch를 만들어 그곳에서 작업했다.
git branch "생성할 브랜치 명"
git checkout "이동할 브랜치 명"
git add 파일명.py
git commit -m "커밋할 문자 내용"
git push origin "branch명"
- 다음과 같이 반영되는것을 확인할 수 있다.
⑦ 변경 사항 통합
- 본인의 경우는 어느정도 테스트를 해놓고 난 이후에 문제없이 작동한다고 판단하여, 병합을 진행하였다.
- 필요시 master branch외에 별도로 develop branch 등을 만들어서 반영하고, 그 이후 안정화되면 master를 넣어도 된다.
'Toy Project > EA FC Pro club Draft Bot [Python]' 카테고리의 다른 글
[EA FC Pro club Draft Bot] 7. 봇 호스팅 (GCP 무료 호스팅 이용) (3) | 2024.09.20 |
---|---|
[EA FC Pro club Draft Bot] 6. Draft Logic 개선 (0) | 2024.09.19 |
[EA FC Pro club Draft Bot] 4. Git 연결 (0) | 2024.09.02 |
[EA FC Pro club Draft Bot] 3. Discord 패키지 추가 및 봇 실행 (4) | 2024.09.01 |
[EA FC Pro club Draft Bot] 2. Python 설정. (3) | 2024.09.01 |