본문 바로가기

블로그 / 기술

사람 대신 수천 판을 돌리다 — 골든 게이트, 시드 스윕, 소크 테스트

혼자 만드는 10인 배틀로얄의 밸런스를 헤드리스 시뮬레이션으로 검증한 방법. 결정론 골든 테스트, 30~100시드 스윕, 30분 소크, 그리고 잘못된 지표 때문에 헛돈 이야기까지.

Salmonholic ·· 5분 읽기

Salmonholic 경기 초반 화면. 흰 고양이 주변으로 공격 범위 원이 그려져 있고 초밥과 연어가 흩어져 있다.

10인 배틀로얄의 밸런스를 혼자 확인하려면 한 판에 4분씩, 열 명이 필요하다. 둘 다 없었다. 그래서 사람 대신 게임을 헤드리스로 돌려 수치를 뽑는 도구부터 만들었다. 이 도구들과 결과 보고서는 9월 23일 커밋 89960db(“golden gate, seed sweeps, soak + QA/launch reports”)로 저장소에 들어갔다. 이 글은 그 세 가지 도구가 무엇을 잡아냈고, 무엇을 잘못 쟀는지에 대한 기록이다.

세 가지 도구: 골든, 스윕, 소크

도구 하는 일 걸리는 시간
골든 게이트 시드 11 경기를 두 번 돌려 서로 같은지, 저장된 기준 파일과 같은지 바이트 단위로 비교 약 40초
시드 스윕 시드 30~100개를 최대 4개 병렬로 돌려 경기 길이·사망 원인·무기별 승률을 집계 100시드 8~10분
소크 메뉴→경기→결과를 자동으로 30분 반복하며 스크립트 오류, 메모리 30% 이상 증가, 끝나지 않는 경기를 감시 30분

핵심은 --fixed-fps 60이다. 가변 프레임으로 돌리면 같은 시드도 머신 부하에 따라 결과가 달라진다. 실제로 시드 11이 고정 프레임에서는 GOLD가 188.9초에 이기고, 가변 프레임에서는 BLUE가 183.9초에 이겼다. 고정 프레임으로 바꾸자 결과가 매번 같아졌고, 240초짜리 경기가 약 18초에 끝났다. 프레임 시간 같은 벽시계 수치는 골든 비교에서 뺐다.

골든은 규칙이 하나 있다. 밸런스를 일부러 바꿔서 기준이 달라지면 --update로 갱신하고 커밋 메시지에 이유를 쓴다. 이후 봇 등급을 바꾼 0.14.5, 신입 보호를 넣은 0.15.0 커밋에도 “golden regenerated”가 빠짐없이 적혀 있다. GitHub Actions 워크플로에도 10초 스모크와 골든 검사를 넣어 두었다.

소크는 지루하지만 안심이 된다. 9월 21일 30분 소크는 27판을 치르고 결과 화면 27번을 모두 띄웠으며, 메모리 증가는 1.66%로 통과했다.

밸런스 v1: 경기가 너무 빨리 끝났다

첫 스윕에서 드러난 문제는 경기 아크였다. 안전지대가 다섯 단계로 줄어드는데, 4단계까지 가는 판이 100판 중 1판뿐이었다. 대부분 3단계에서 결판이 났고, 초반 30초 안에 몬스터 떼에 죽는 시드도 9개 있었다. 우승자의 89%가 FIRE TRAIL(진화하면 INFERNO)을 들고 있었다.

존 타이밍, 단계별 PvP 배율, 초반 몬스터 수, 무기 수치를 한 번에 손보고 같은 100시드를 다시 돌렸다.

지표 (100시드) 변경 전 변경 후
4단계 도달 1% 100%
경기 길이 중앙값 183.8초 255.6초
30초 전 몬스터에 죽은 시드 9개 0개
우승자의 FIRE 보유율 89% 40%

밸런스 v1 전후 우승자 무기 보유율 막대그래프 FIRE는 내려왔지만 상향한 도끼와 채찍이 새 상위가 됐다. 목표였던 35% 이하는 이 방식으로는 달성하지 못했다.

잘못된 자로 재고 있었다

다음 라운드(v2)에서 “FIRE 40%“를 35% 아래로 내리려다 이상한 점을 발견했다. 우승자는 거의 항상 무기 여섯 칸을 꽉 채운다. 무기가 15종이면 아무 무기나 평균 보유율이 6/15, 즉 40%다. “모든 무기 35% 이하”는 처음부터 수학적으로 불가능한 목표였다.

그래서 지표를 “우승자가 입힌 피해 중 그 무기의 비중”으로 바꿨다. 게임 코드에 무기별 피해 누적을 넣고 다시 재자, 가장 높은 무기가 시작 무기인 MAGIC WAND 28.6%였다. 지배적인 무기는 사실 없었던 셈이다.

비슷한 일이 한 번 더 있었다. v2에서 “3레벨 앞선 쪽이 결투에서 이기는 비율 99%“가 나와 레벨 차 스노볼이 너무 세다고 봤는데, 이 통계는 레벨 차 3 이상을 전부 한 통에 넣고 있었다. v3에서 정확히 3~5레벨 차만 따로 세니 97%였다. 성장에 따른 PvP 피해 감쇠를 0.6에서 1.0으로 바꾸고 레벨 우위 배율을 0.05에서 0.015로 낮춰 75%로 맞췄다. 결투 시간 중앙값은 2.7초에서 3.3초가 됐다. 두 번 다 수치보다 자를 먼저 의심했어야 했다.

원만 천천히 닫으면 된다

v2에서 “PvP는 몇 방에 결판”이 되도록 바꾸자 경기 길이가 214초로 줄었다. 한 판이 짧다면 원을 조금 더 천천히 닫으면 되지 않을까 싶어서, 존 시간표에만 곱하는 배율을 만들어 네 가지 값을 30시드씩 돌렸다.

존 시간 배율별 경기 길이와 결투 p90 막대그래프 배율 1.3부터는 결투 p90이 10초를 넘어 쫓고 도망치는 싸움이 늘어졌다.

존 배율 경기 중앙값 결투 p90 3~5레벨 우위 승률
1.0 216.1초 9.3초 75%
1.2 (채택) 259.6초 9.2초 71%
1.3 280.3초 11.4초 61%
1.4 302.4초 15.0초 78%

스윕 로그를 보니 모든 판이 네 번째 축소가 시작되고 2.5~3.3초 뒤에 끝났다. 결판을 부르는 건 봇의 사냥 시작이 아니라 원의 축소였다. 봇을 건드리지 않고 존만 늦춰도 경기가 길어진 이유다.

사람의 판정은 따로 남는다

시뮬레이션은 사람을 대신하지 못한다. 스윕 속 “사람”은 반응 0.88초에 첫 카드를 고르는 대리 봇이라 실제 숙련자나 초보와 다르다. 보고서마다 이 한계를 적어 두었고, 봇 등급 조정처럼 사람 경험에 직접 닿는 결정은 실제 온라인 판과 라이브 통계로 한 번 더 확인했다. 그 과정은 빈자리 봇과 매치메이킹 글에 있다. 이렇게 맞춘 한 판은 Salmonholic 게임 페이지에서 직접 해 볼 수 있다.

#게임밸런스#테스트자동화#헤드리스#Godot#시뮬레이션

함께 읽으면 좋은 글

전체 글 →