30일 동안 가게를 운영한 가상의 사장이 환생을 1,521번 했다. 하루에 쉰 번 넘게 가게를 접고 다시 열었다는 뜻이다. 30분마다 한 번씩 재개업하는 잡화점이다.
이 숫자가 나온 건 1편에서 말한 그 한 줄, "M2 진행해줘" 뒤였다. 유일한 취소 지점을 판정하지 않은 채 보낸 그 메시지에 로드맵의 M2 목록이 통째로 따라 들어왔다. 직원 네 종류, 도난, 환생, 용사 원정, 유품, 단골, 손님 여섯 종의 특수 규칙, 명성 트리 12노드, 상품 40티어. 그리고 목록 맨 끝에 한 줄이 더 있었다.
30일 시뮬 테스트 통과 (환생 8~14회 구간)
경제 설계 문서에 처음부터 적어 둔 기준이다. 8~14회가 목표였는데 1,521회가 나왔다. 이번 편은 그 차이를 메우는 이야기다.
쏟아진 시스템들
시스템은 하나하나 보면 멀쩡했다. CLAUDE.md 의 절대 규칙대로 순수 C# 모델로 만들어졌고, 수치는 전부 economy.json 으로 빠졌고, 난수는 인터페이스 뒤에 숨었다.
점원은 판매 속도를 올리고, 경비는 도난률을 12% 에서 깎아 내리고, 감정사는 유품 값을 올리면서 마왕군 첩자를 알아본다. 호객꾼은 손님을 끌어온다. 장비가 25개 팔릴 때마다 용사 한 명이 던전에 들어가고, 죽으면 유품이 남고, 살아 돌아오면 35% 확률로 단골이 된다. 기획 단계에서 "전투는 구현하지 않는다"고 정해 뒀기 때문에 원정의 생사는 확률 한 번으로 갈린다. 장비 티어가 던전보다 낮을수록 잘 죽는다.
그리고 환생이 있다. 한 회차 동안 번 돈이 요구치를 넘으면 가게를 접고 명성을 받아 처음부터 다시 연다. 명성은 두 가지 일을 한다. 쌓인 만큼 골드 배율이 올라가고(명성 1당 2%), 남은 만큼 명성 트리 노드를 산다.

모델 쪽 명성 트리는 선행 조건과 최대 레벨을 검사하고, 노드를 사도 골드 배율이 줄지 않도록 "누적 명성"과 "쓸 수 있는 명성"을 따로 셌다. 공들인 설계였다. 화면은 달랐다. 선행 관계는 어디에도 그려져 있지 않았고, 같은 이름이 두 줄씩 있는 이유는 비용 숫자로 짐작해야 했다. 트리라고 불렀지만 플레이어에게는 가격표 붙은 버튼 열두 개였다. 나는 이걸 보고도 "진행"을 보냈다.
하루 쉰 번 재개업하는 가게
환생 횟수를 재려면 누군가 30일을 플레이해야 한다. Claude Code 는 그 누군가를 코드로 만들었다. AutoPlayer, "합리적으로 잘하는 플레이어"를 흉내 내는 클래스다. 하루를 3분짜리 세션 네 번으로 잡고, 세션 사이 몇 시간과 밤새 꺼 둔 시간은 오프라인 정산으로 메웠다. 진열대·손님·직원·명성 노드는 전부 게임이 실제로 쓰는 데이터에서 읽었다. 게임 그대로의 수치로 30일을 돈다.
여기서 1편의 그 절대 규칙 1번이 값을 했다. 시뮬레이션이 Unity 를 참조하지 않으니, Unity 배치 모드 테스트를 기다릴 필요 없이 시뮬레이션 소스만 물린 dotnet 콘솔 프로그램으로 30일을 몇 초 만에 돌릴 수 있었다. Claude Code 는 이 방법을 아예 프로젝트 메모에 "밸런스 반복은 dotnet 콘솔 하니스로" 남겨 뒀다.
그렇게 처음 돌린 결과가 1,521회였다.
용의자는 둘이었다. 하나는 명성이다. 환생할수록 명성이 쌓이고 골드 배율은 계속 오르는데, 환생 요구치는 10억 골드로 고정이었다. 배율이 오를수록 같은 10억을 채우는 시간이 짧아지고, 회차가 쌓이면 몇 초 만에 다음 환생이 열린다. 다른 하나는 상품 티어다. 티어가 하나 오를 때마다 원가가 1.65배가 되는데, 한 회차 안에서 40티어 끝까지 올라갈 수 있었다. 1.65배씩 39번이면 매출이 감당할 수 없는 쪽으로 터진다.
범인은 하나가 아니었다
먼저 티어를 잡았다. 한 회차에서 다룰 수 있는 최대 티어를 환생 횟수에 묶었다. 처음엔 3티어까지, 환생할 때마다 한 칸씩.
결과는 1,248회. 거의 줄지 않았다. 티어는 공범이었을 뿐 주범이 아니었다는 뜻이다. 주범은 명성 배율이 계속 오르는데 목표선은 제자리인 구조, 그러니까 결승선이 움직이지 않는 경주였다. 그래서 결승선을 움직였다.
public double RequiredLifetimeGold(int prestigeCount)
{
double growth = _prestige.requiredGrowth <= 0.0 ? 1.0 : _prestige.requiredGrowth;
return _prestige.requiredLifetimeGold * Math.Pow(growth, Math.Max(0, prestigeCount));
}
n번째 환생의 요구치는 1e9 × growth^n 이다. 환생할수록 다음 결승선이 기하급수로 멀어진다. 명성 공식도 이 요구치로 나누게 바꿔서, 요구치를 딱 채우면 회차마다 명성이 40 안팎으로 들어오게 했다. 배율이 커지는 속도와 결승선이 멀어지는 속도가 맞물리면 환생 간격은 어느 지점에서 더 줄지 않는다.
성장률 2.0 을 넣자 23회가 나왔다. 2.6 에서 9회. 60일을 돌리면 12회, 시드를 1·777·99999 로 바꿔도 모두 9회였다. 1,521 → 1,248 → 23 → 9. 값은 설정 파일에 들어갔고, 경제 설계 문서의 환생 절에는 "요구치 성장은 수렴 장치다"라는 설명이 붙었다.
"progress": { "baseMaxTier": 3, "tierPerPrestige": 1 },
"prestige": { "requiredLifetimeGold": 1000000000.0, "requiredGrowth": 2.6,
"fameCoef": 40.0, "fameExp": 0.5, "goldMultPerFame": 0.02 }
폭주를 숫자로 확인하고, 용의자를 둘로 나누고, 하나씩 막아서 수렴시켰다. 지금 다시 따라가 봐도 납득이 가는 수사였다. 목표가 숫자로 있고, 레버가 설정 파일에 있고, 한 번 돌리는 데 몇 초면 되는 일. Claude Code 가 가장 잘하는 종류의 일이었다. M2 완료 보고는 짧았다.
30일 시뮬 환생 8~14회 통과
규칙 몇 줄짜리 사장님
사건이 해결됐다고 생각했다. 이 연재를 준비하며 AutoPlayer 를 다시 열기 전까지는.
AutoPlayer 는 15초마다 이 순서로 결정한다.
- 환생할 수 있으면 바로 환생한다.
- 살 수 있는 명성 노드를 산다.
- 손님 구성 기준 최적 가격 배율을 모든 진열대에 똑같이 건다.
- 잠긴 진열대를 열 수 있으면 연다.
- 재고 원가가 골드의 10% 이하인 가장 높은 티어로 상품을 바꾼다.
- 비용이 골드의 5% 이하인 가장 싼 직원을 올린다.
- 비용이 골드의 15% 이하인 가장 싼 진열대를 올린다.
- 유품은 모두 감정하고 판다.
이게 전부다. 명성 노드는 불러온 순서대로 산다. 어떤 노드를 먼저 살지, 경비와 호객꾼 중 뭘 먼저 올릴지 고민하는 코드는 한 줄도 없다. "여유 골드의 몇 %"라는 규칙만 있다.
그런데 이 플레이어가 목표 구간에 정확히 들어왔다. 시뮬레이터로서는 성공이다. 게임으로서는 판결문이다. 판단 없는 규칙으로 풀리는 게임이라면, 플레이어가 판단할 자리가 없다는 뜻이니까.
로드맵에는 사실 M2 의 판단 지점도 적혀 있었다. 30일 시뮬에서 "20분 이상 아무 선택지도 없는 구간"이 나오면 곡선을 다시 잡는다. 테스트로 옮겨진 건 환생 횟수 하나였다. 셀 수 있는 기준은 맞춰졌고, 재미에 가장 가까운 기준은 숫자가 되지 못한 채 넘어갔다.
전부 곱셈의 한 항
왜 판단이 필요 없었는지는 시뮬레이션의 판매 코드를 한 식으로 펼치면 보인다. 진열대 하나의 초당 매출은 이렇다.
매출/초 = 기본 속도 · 진열대 레벨 보정
· (1 + 점원)
· 평판 · 호객꾼 · 단골
· 구매율(m) · (1 − 도난률)
· 정가(티어) · m
· 명성 골드 배율
M2 에서 쏟아진 시스템 대부분이 이 곱셈의 항 하나였다. 점원도, 호객꾼도, 단골도, 경비도, 명성도. 곱셈에는 순서가 없고, 항끼리 서로를 막지 않는다. 어느 항을 올려도 결과는 커진다. 그러면 가장 싼 항부터 올리는 탐욕 규칙이 거의 최적이 된다. AutoPlayer 의 5%·10%·15% 가 통과한 이유가 이것이다.
곱셈 바깥에 설 수 있었던 건 둘뿐이다. 하나는 가격 배율 m 이다. 올리면 구매율이 떨어지는, 서로 반대로 움직이는 유일한 레버였다. 하지만 1편에서 풀었듯 그 최적값은 식 하나로 나오고, AutoPlayer 는 그 값을 모든 진열대에 그냥 걸었다. 다른 하나는 성기사다. 90초마다 가게를 둘러보는 정의의 사도라서 "정가를 지킬까, 바가지를 씌울까"라는 진짜 갈림길이 될 수 있었다. 그런데 규칙은 "진열대가 하나라도 정가를 넘으면 평판 −3"이었다. 진열대마다 고를 여지가 없으니 갈림길이 아니라 조금씩 새는 세금이 됐다.
도둑도 기획에서는 물건을 들고 튀는 손님이었지만 코드에서는 12% 라는 비율이었고, 경비를 올려 하한 5% 에 닿으면 더 신경 쓸 이유도 없었다. 원정은 "던전 앞 잡화점"이라는 세계관과 가장 맞닿은 시스템이었지만, 생사는 확률 한 번이었고 사장이 끼어들 방법은 없었다.
시스템이 하나 늘 때마다 숫자는 조금 더 빨리 올랐고 테스트는 조금 더 늘었다. 그 한가운데에서 진열대 앞 슬라이더를 움직이는 핵심 루프는 M1 과 똑같았다. 1,521회를 9회로 줄인 수사는 훌륭했다. 다만 그 수사가 증명한 건, 내가 판정하지 않고 넘긴 질문 바깥에 곱셈 항을 겹겹이 둘렀다는 사실이었다. 숫자가 맞았을 때 그게 무엇을 뜻하는지 읽는 일은 테스트가 대신해 주지 않는다.
다음 편은 그 곱셈의 바깥쪽, 앱을 꺼 둔 시간이다. 8시간 동안 벌었을 돈을 공식 한 번으로 정산하는 오프라인 보상은, 이 게임에서 가장 단단한 코드였다.