첫 1분을 지켜 주려다 봇의 눈을 가렸다: Salmonholic 초반 보호와 숙련자 생존 회귀
Salmonholic 0.22.0에서 모든 사람에게 첫 60초 보호를 넣었다가 숙련자의 90초 생존이 떨어졌다. 공격 금지 규칙이 봇의 시야까지 지운 원인을 같은 60시드 540판으로 찾아 고친 기록이다.

9월 28일 오후에 나간 Salmonholic 0.22.0에는 “첫 1분 보호”가 들어 있다. 경기 시작 후 60초 동안 봇이 사람을 노리지 않고, 연어와 좁혀지는 원도 덜 아프게 한 규칙이다. 그런데 이 보호를 넣은 직후 자동 대전에서 숙련자의 90초 생존율이 오히려 떨어졌다. 같은 커밋(2a20f16) 안에서 원인을 찾아 고쳤고, 작업 이름은 각각 RB6과 RB7이다. 이 글은 보호를 넣은 이유와, 그 보호가 봇의 판단까지 건드린 과정을 정리한 것이다.
왜 첫 1분이었나
0.22.0 커밋 메시지 첫 줄에 출발점이 적혀 있다. CrazyGames 기본 출시(Basic Launch) 점수가 9월 25~27일 기준 데스크톱과 모바일 모두 15점 만점에 4점이었다. 항목별로는 플레이 시간 1점, 다음 날 재방문 2점, 전환 1점이었고 다음 단계로 가려면 9점 이상이 필요했다. 플레이 시간이 1점이라는 것은 처음 들어온 사람이 금방 나간다는 뜻이다.
신입 보호는 이전에도 있었다. 0.15.0에서 완료한 판이 3판 미만인 사람은 처음 90초 동안 봇의 공격 대상에서 빠졌다. RB6은 이 범위를 넓혔다. 판 수와 상관없이 모든 사람에게 짧은 초반 보호를 주고, 봇 공격 말고 다른 피해도 같이 줄였다. 규칙 파일에 새로 들어간 openingGrace 값을 정리하면 다음과 같다.
| 항목 | 0~60초 | 이후 |
|---|---|---|
| 봇이 사람을 노리고 때리기 | 막힘 | 60~75초에 걸쳐 원래 피해로 |
| 사람끼리 피해 | 0 | 60~75초에 걸쳐 원래 피해로 |
| 연어 접촉 피해 | 15% | 60~75초에 걸쳐 원래 피해로 |
| 좁혀지는 원 피해(모든 참가자) | 75초까지 0 | 75~90초에 걸쳐 원래 피해로 |
| 위기 구조 | 체력이 30% 아래로 떨어질 피해를 받으면 경기당 한 번 체력 65%와 3초 보호막 | 없음 |
원이 줄어드는 시간표, 연어 체력, 연어가 한 번에 쓰러지는 규칙은 그대로 두었다. 봇끼리의 전투도 원래 피해량이다. 구조는 한 번 쓰면 끝이고, 그 상태를 자리에 저장해서 재접속으로 다시 채워지지 않게 했다. 구조가 터지면 청록색 회복 입자와 문구가 뜨는데, 온라인에서는 기존 스냅샷의 남는 비트 하나로 신호를 보내서 패킷 길이는 바꾸지 않았다.
같은 30시드로 다시 돌려 보니 숙련자가 나빠졌다
규칙을 바꾸면 사람 대신 자동 조작 대리를 넣어 같은 시드로 돌린다. 사람 1명과 봇 9명, 기본 게임 속도로 시드 1~30을 수정 전후에 똑같이 돌렸다. 대리는 세 종류다. 일반 대리는 0.88초마다 목표를 고친다. 미숙 대리는 3초마다 목표를 고치고 4초씩 움직이다 멈추기를 반복한다. 숙련 대리는 가장 높은 판단 등급으로 0.2초마다 목표를 고친다.
목표였던 미숙 대리의 60초 생존은 93.3%에서 30판 전부(100%)로 올라갔다. 문제는 숙련 대리였다. 90초 생존이 96.7%에서 83.3%로 내려갔다. 처음 보고서에는 “초기 보호로 달라진 위치·성장·교전 조합의 영향”이라고만 적고 넘어갔는데, 보호를 받은 사람이 더 빨리 죽는다는 결과는 그대로 두기 어려웠다. 그래서 RB7을 따로 열었다.
공격 금지가 시야까지 지우고 있었다
먼저 세운 가설은 “보호 덕분에 봇이 더 많이 살아남아서 60초 뒤 경쟁이 세졌다”였다. 숫자는 반대였다. 숙련 대리의 60초 평균 처치는 0.033에서 0.933으로, 평균 레벨은 9.03에서 10.10으로 올랐다. 반면 전체 생존자 수는 9.43에서 8.63으로 줄었다. 살아 있는 봇의 평균 레벨은 10.51과 10.53으로 거의 같았다. 사람이 더 세졌는데도 90초 전에 더 죽은 것이다.
60초가 되자마자 봇이 한꺼번에 몰려든 것도 아니었다. 90초 전 사망 14건 중 11건은 마지막 2초 동안 공격한 상대가 한 명뿐이었다. 가장 이른 사망도 73.8초였다.
원인은 함수 하나였다. 봇이 공격 대상을 고를 때 쓰던 avoidsRookie에 RB6이 “60초 전의 사람” 조건을 붙였다. 그런데 이 함수는 공격 판정 말고도 봇이 주변 상대를 훑는 곳, 미니맵에서 가장 가까운 상대를 찾는 곳, 연어가 얼마나 몰려 있는지 계산하는 곳에서도 쓰이고 있었다. 신입 몇 명에게만 적용될 때는 드러나지 않던 구멍이, 모든 사람에게 60초씩 적용되면서 커졌다.
// 수정 전: 보호 중인 사람은 목록에서 아예 빠진다
if (!q || this.avoidsRookie(p, q) || q.id === p.id || !q.alive || !visible(q.x, q.y)) continue;
// RB7: 보이는 상대는 모두 위험 판단에 넣고, 추격 단계에서만 막는다
if (!q || q.id === p.id || !q.alive || !visible(q.x, q.y)) continue;
// ...
if (hunt && !this.avoidsRookie(p, qn)) { /* 추격 목표 설정 */ }
봇 입장에서는 첫 1분 동안 사람이 보이지 않았다. 원래 봇은 사람을 위험으로 보고 피하면서, 연어를 잡을 공간을 따로 확보한다. 사람이 투명해지자 그 움직임이 사라졌다. 초반의 잘못된 인식이 위치와 성장, 그리고 보호가 풀린 뒤의 교전 경로까지 바꿨다는 것이 보고서의 결론이다.
보호를 늘리는 대신 인식만 되돌렸다
확인은 단일 변경으로 했다. 피해량, 보호 시간, 복귀 구간, 봇 능력치는 그대로 두고 인식만 되돌리자 60초 평균 처치는 0.033, 생존자 수는 9.43, 사람 레벨은 9.00으로 수정 전과 같아졌다.
다른 안도 돌려 봤다. 봇마다 공격을 허용하는 시점을 시드에 따라 최대 20초 또는 30초까지 흩뜨리는 방법이다. 20초 분산은 숙련 생존 83.3%로 부족했고, 30초 분산은 숙련 생존 85.0%를 회복했지만 상위 3위 비율이 기준선보다 6.7%p 올라 정해 둔 상한을 넘었다. 보호를 더 얹어 숫자를 맞추는 것보다 잘못된 인식을 고치는 쪽이 맞다고 보고 그 안을 택했다.
최종 비교는 시드를 60개로 늘려 버전 셋 × 대리 셋, 모두 540판을 돌렸다.
미숙 대리의 60초 생존은 보호를 넣은 뒤 100%를 유지하고, 숙련 대리의 90초 생존은 RB6에서 76.7%로 떨어졌다가 RB7에서 85.0%로 돌아왔다.
| 버전 | 미숙 60초 생존 | 숙련 90초 생존 | 숙련 상위 3위 |
|---|---|---|---|
| 수정 전 | 90.0% | 85.0% | 46.7% |
| RB6 | 100.0% | 76.7% | 48.3% |
| RB7 | 100.0% | 85.0% | 48.3% |
원래 30시드만 떼어 보면 숙련 90초 생존은 29/30 → 25/30 → 29/30이다. 처음 보고된 96.7% → 83.3% 하락을 그대로 재현했고 그대로 되돌렸다. 숙련 대리의 승률은 세 버전 모두 0/60이다. 관측치일 뿐이라 실제 잘하는 사람의 승률을 보장하는 숫자는 아니다.
서버와 브라우저 오프라인 판은 같은 규칙을 각자 구현한다. 그래서 봇 등급 3개 × 시간 경계 4개 × 사람/봇 × 거리 2개 × 체력 상태 2개, 96개 사례를 양쪽에서 돌려 상태와 목표 좌표, 피해량이 일치하는지 시험으로 묶었다. 경기 경로가 바뀌었으니 골든 기록도 일부러 갱신했다. 새 골든은 211.3초에 끝나고, 주인공은 6위다.
정리
이번 일에서 남긴 교훈은 “공격하지 마”와 “보지 마”를 한 함수로 처리하면 안 된다는 것이다. 신입 보호 때부터 같은 함수를 써 왔고, 적용 대상이 적을 때는 문제가 보이지 않았다. 대상을 모든 사람으로 넓히고 같은 시드로 비교하고 나서야 숫자로 드러났다. 그리고 원인을 찾기 전에 보호를 더 얹었다면 30초 분산안처럼 다른 숫자가 상한을 넘었을 것이다.
봇 등급과 신입 보호가 처음 생긴 과정은 매치메이킹과 봇 지능 10단계에, 같은 시드로 수천 판을 돌리는 방법은 밸런스 시뮬레이션 글에 적었다. 첫 1분을 이렇게 신경 쓰게 된 배경은 CrazyGames 반려 뒤 첫 90초를 다시 만든 이야기에 있다. 보호가 들어간 첫 1분은 Salmonholic 게임 페이지에서 직접 해 볼 수 있다.


