콜드스타트: 매칭이 실패하는 게 정상인 시기
1:1 듀얼은 실시간 상대가 필요하다. 서비스 초기엔 동시 접속자 수가 매칭 큐를 채우기엔 부족해서, 큐에 넣고 기다리면 대부분 타임아웃이 난다.
상대 없음을 그대로 보여줄지, 대전처럼 보이는 뭔가를 대신 세울지 정해야 했다. 첫인상이 빈 큐면 좋을 게 없어서 후자로 갔다.
시드 결정적 보드가 먼저 있었다
mulberry32 시드로 보드를 결정적으로 생성하는 구조(initialBoard.ts)가 이미 있었다. 같은 시드면 같은 판이 나오니까, 누군가 그 시드로 플레이한 기록을 저장해뒀다가 나중에 재생할 수 있다.
솔로 모드(나혼) 결과를 자동으로 수집해 재료로 쌓기 시작했다. 상대가 없을 때 그중 하나를 골라 재생한다.
수집 경로가 세 번 바뀐 이유
처음엔 고스트의 MMR을 별도로 관리했는데, 그 값이 실제 실력을 반영하는지 검증할 방법이 없었다. 랭크 체계를 하나 더 만든 셈이라 유지비만 늘었다.
ghost_mmr을 고스트 고유값 대신 기록을 제출한 사람의 MMR로 바꿨다. 고스트는 별개 존재가 아니라 과거 플레이 기록 재생이라고 다시 정의했다.
수집 시점도 계속 옮겨 다녔다. 솔로 플레이만 재료였다가, 부트스트랩 구간에 한해 다시 열었다가, 결국 라이브 듀얼(클래식·나혼) 결과까지 넣었다. 매 단계에서 그 시점 기록이 지금 매칭 품질에 맞는지 다시 확인해야 했다.
익명화
고스트가 실제 플레이 기록이라 상대 닉네임에 진짜 유저 이름이 그대로 노출됐다. 랜덤 닉네임과 MMR 지터를 씌웠다. 처음엔 숫자+명사 조합이었는데 사람 이름처럼 보여서, 명사+명사 조합에 서울 근교 산 이름으로 한정했다.
지금 상태
고스트 후보를 고를 때 초기엔 고정된 시드 풀 안에서만 찾았는데, 그 풀 밖에 쌓인 기록은 영영 선택되지 않았다. 필터를 걷어내고서야 수집한 기록이 실제로 쓰였다. 라이브 대전이 붙는 시간대엔 진짜 상대가 우선이고, 매칭이 비는 시간대에만 고스트가 대신 선다.