해금 선을 올린 날, 이미 넘은 사람 몫은 남겼다
타누비의 동료는 누적 점수에 따라 차례로 열립니다. 이날 저는 가장 안쪽에 놓여 있던 여우의 해금 선을 올렸습니다. 이유는 한 줄로 적을 수 있습니다. 마지막 한 명이 마지막답지 않았기 때문입니다. 다만 해금 조건이라는 것은 설정 파일의 숫자이면서, 동시에 이미 그 숫자를 넘어 동료를 손에 넣은 사람의 기록이기도 합니다. 선만 옮기면 어제까지 여우를 쓰던 사람이 다음에 페이지를 열었을 때 여우가 잠겨 있는 일이 생깁니다. 이 글은 선을 올리기로 한 판단과, 이미 넘은 사람의 몫을 어떻게 남겼는지, 그리고 그 "남긴다"를 다짐으로 끝내지 않으려고 어떤 테스트를 썼는지에 대한 기록입니다. 여기 나오는 숫자는 지금 게임 화면의 값과 다릅니다. 이 글은 2026년 7월 25일 시점의 이야기이고, 그 뒤에 한 번 더 올렸기 때문입니다. 그 사정은 마지막 절에 적었습니다.
작성일: 2026-07-25
마지막 한 명이 마지막답지 않았다
여우는 당시 배열에서 가장 안쪽에 있던 동료였습니다. 벽에 붙어 미끄러지고 반대쪽으로 차고 오르는, 다른 누구도 갖지 않은 움직임을 씁니다. 모으는 재미가 있는 게임에서 맨 마지막에 놓는 것은 거기 닿았다는 사실 자체가 하나의 사건이 되었으면 합니다. 한참을 달린 끝에 겨우 열리기 때문에, 열리는 순간에 의미가 생깁니다. 순서상 마지막에 있다는 것과 마지막다운 무게를 갖는다는 것은 서로 다른 이야기입니다.
이때 제 손에 있던 것은 그 둘이 어긋나 있다는 제 판단뿐이었습니다. 몇 명이 얼마 만에 여우에 닿고 있었는지를 세어서 확인한 것은 아닙니다. 그러니 이건 계측에 뒷받침된 결론이 아니라 설계 의도에서 나온 견해입니다. 그걸 알면서도 마지막 한 명을 "특별한 마지막"으로 만들기 위해 선을 올리기로 정했습니다. 근거가 얇다는 걸 자각한 만큼, 나중에 되돌릴 수 있는 형태로 둘 것, 그리고 되돌릴 수 없는 것 — 사람이 이미 손에 넣은 것 — 에는 손대지 않을 것을 함께 조건으로 걸었습니다.
난이도나 해금 조건 조정은 코드로 보면 가장 값싼 변경입니다. 상수 하나만 고쳐 쓰면 끝납니다. 싼 것은 쓰는 쪽에서만 그렇고, 그 변경의 값은 마침 그 선 근처에 서 있던 사람이 치릅니다. 이번으로 치면 옛 선은 넘었지만 새 선에는 못 미친 사람입니다. 값싼 변경일수록 그 값을 누가 치르는지를 먼저 세어 보는 편이 낫다 — 이 건에서 제가 가장 강하게 남긴 생각은 그 지점이었습니다.
선을 옮기면 이미 넘은 사람의 발밑이 움직인다
해금 판정은 그 시점에 "누적 점수가 지금 임계 이상인가"라는 한 줄짜리 식이었습니다. 이 형태면 임계를 올리는 순간 과거의 판정까지 소급해서 다시 쓰입니다. 누적 점수는 줄어들지 않으니 본인의 기록은 아무것도 잃지 않았는데, 판정식의 오른쪽이 바뀌었다는 이유만으로 결과가 뒤집힙니다. 옛 선과 새 선 사이에 있는 사람에게는 자기가 한 것은 없는데 가진 것이 사라진 것과 같습니다.
이때 제가 무서웠던 것은 돌아오는 항의가 아니라 돌아오지 않는 쪽이었습니다. 페이지를 열었더니 있어야 할 여우가 잠겨 있고, 뭔가 잘못됐나 잠깐 생각하다가 그대로 닫는 것. 이쪽에는 아무것도 도착하지 않습니다. 모으는 놀이는 모은 것이 계속 쌓여 간다는 전제 위에 서 있습니다. 그 전제를 한 번이라도 깨면 다음에 열릴 것을 향해 갈 이유까지 함께 옅어진다 — 이건 제 짐작이고 재어 본 적은 없지만, 모은 쪽의 입장에서 생각하면 충분히 그럴 법하다고 봅니다.
그래서 이 변경에는 부품이 두 개 필요해졌습니다. 하나는 새 선. 다른 하나는 옛 선을 이미 넘어 있던 사람의 몫을 새 선 바깥에 안전하게 놓아 둘 자리입니다. 뒤쪽이 없다면 앞쪽은 넣지 않는 편이 낫다고 봤습니다.
가진 것은 빼앗지 않는다
놓아 둘 자리로 만든 것은 진행 데이터 안의 "영구 해금 목록"입니다. 여기에 들어간 동료는 누적 점수가 얼마든 열린 채로 남습니다. 해금 판정은 한 줄짜리 식에서 "영구 해금 목록에 있는가, 또는 지금 임계를 넘었는가"라는 두 갈래로 바뀌었습니다. 점수는 지금까지대로 누구에게나 열려 있는 길로 남고, 그 옆에 지나간 일은 취소하지 않는다는 길이 한 줄 더 놓인 모양입니다.
옛 선을 넘어 있던 사람을 그 목록으로 옮기는 작업은 딱 한 번 돕니다. 구현에서는 올리기 전의 임계를 별도 상수로 남겨 두고, 처음 읽어 들일 때 그 시점의 누적 점수가 옛 선에 닿아 있으면 해당 동료를 목록에 넣고 저장합니다. 끝났는지 여부는 진행 데이터 안의 플래그로 기억합니다. 플래그가 서 있으면 그다음부터는 아무것도 하지 않습니다. 흡수가 도는 자리는 진행 데이터를 읽는 세 입구 — 허브, 게임 본편, 도감 — 전부입니다. 어디로 들어와도 같은 결과가 나오기를 바랐기 때문이고, 입구마다 다른 얼굴을 보이는 구제는 구제로서 믿을 수 없다고 생각합니다.
함께, 다음 목표를 가리키는 표시 쪽도 고쳤습니다. 영구 해금으로 이미 가진 동료를 "다음에 열릴 것"으로 제시하면, 이미 쓰고 있는 상대를 향해 진척 바가 차오르는 말이 안 되는 화면이 됩니다. 구제는 데이터 안에서 조용히 끝나는 것만으로는 부족했고, 화면에 나오는 말과 어긋나지 않는 데까지가 일이었습니다. 이 해금 정보는 단말 쪽에 두는 값이라 지형 생성이나 점수 판정, 순위표에는 손대지 않습니다. 손이 닿는 범위를 처음부터 좁혀 두면, 틀렸을 때 망가지는 범위도 좁아집니다.
"빼앗지 않는다"를 테스트로 바꾸기
무손실이라는 말은 입으로만 하면 다짐일 뿐입니다. 다짐은 반년 뒤 리팩터링에서 사라집니다. 그래서 같은 PR 안에서 이 약속을 그 형태 그대로 테스트로 옮겨 적었습니다. 우리말로 쓰면 두 문장입니다. "옛 선을 넘어 있던 사람은 앞으로 어떻게 되든 여우를 갖고 있다." "새로 시작한 사람은 옛 선에 닿은 정도로는 열리지 않는다."
검사로 놓은 것은 그 둘을 포함한 한 벌입니다. 옛 선과 정확히 같은 누적 점수를 가진 기존 기록은 흡수되어 열린 채로 남는다는 것. 완전히 같은 누적 점수라도 새로 시작한 사람은 열리지 않고, 새 선까지 모아야 비로소 열린다는 것. 흡수가 두 번 돌아도 결과가 변하지 않는다는 것 — 한 번 끝난 기록을 다시 통과시키면 변경 없음으로 같은 것이 돌아옵니다. 경계도 양쪽에서 짚었습니다. 옛 선과 딱 같으면 흡수되고, 거기서 1점 모자란 기록은 흡수되지 않습니다. 그리고 영속입니다. 흡수한 뒤 다시 읽어도 남아 있을 것, 나아가 그 뒤에 누적 점수가 사라져도 여우는 열린 채일 것. 주 저장본과 백업을 맞댔을 때 한쪽에서만 흡수가 끝나 있어도, 합친 결과는 가진 것이 줄지 않는 쪽 — 양쪽의 합집합 — 으로 기웁니다.
써 놓고 보니 이 테스트들은 기능의 설명이 아니라는 걸 알게 됐습니다. 임계가 얼마인지도, 목록이 어떤 형태인지도 언젠가 바뀝니다. 여기에 고정한 것은 "한 번 손에 넣은 것은 줄지 않는다"는 약속 하나이고, 테스트 이름도 그렇게 읽히도록 붙였습니다. 나중에 온 누군가가 이 검사를 빨갛게 만들었다면, 그 사람이 깬 것은 구현이 아니라 약속 쪽입니다. 문서에 적어 둔 문장은 안 읽힐 수 있지만, 빨개진 테스트는 그보다 훨씬 눈에 띌 것이라고 봅니다.
남은 한계, 그리고 이 글의 시점
여기까지가 2026년 7월 25일의 이야기입니다. 지금 타누비에서 여우 해금에 필요한 점수는 이 글이 다루는 숫자보다 높습니다. 이 글 조금 뒤에 놀 수 있는 범위 자체를 다시 보는 작업이 있었고, 거기서 여우 선은 더 위 — 5500까지 올라갔습니다. 지금 값과 캐릭터별 해금 순서는 공략 쪽에 정리해 두었으니 오늘의 목표로 읽고 싶은 분은 그쪽을 보시면 됩니다. 이 글은 어디까지나 선을 올리기로 정한 날의 판단 기록입니다.
그리고 그 두 번째 상향에서 이번 설계의 약한 데가 드러났습니다. 흡수는 "끝났는지"를 플래그 하나로만 기억합니다. 그런데 두 번째 상향이 오자, 첫 번째에서 플래그를 이미 써 버린 사람에게는 두 번째 몫의 구제가 자동으로 돌지 않습니다. 플래그를 상향마다 나눠 갖는 형태로 다시 만들면 고쳐지지만, 그건 진행 데이터의 구조에 손을 대는 이야기라 별도 작업으로 남겨 두었습니다. 지금으로서는 그 한계가 소스 주석에 적혀 있을 뿐입니다.
적어 두고 싶은 것은 이것이 구현의 누락이라기보다 제 예상의 잘못이었다는 점입니다. 한 번뿐인 구제로 만들면 한 번밖에 듣지 않습니다. 두 번째가 있을 줄 알았다면 처음부터 횟수를 셀 수 있는 형태로 만들었을 것입니다. 조정은 한 번으로 끝난다는 전제 쪽이, 이 경우에는 틀린 것이었습니다.
마지막으로, 확인하지 못한 것도 적어 둡니다. 그날의 흡수로 몇 명이 실제로 구제됐는지 저는 모릅니다. 해금 기록은 단말 안에 있고, 이쪽에서 세러 가는 장치를 만들어 두지 않았기 때문입니다. 그래서 이 작업의 성과는 아무도 아무것도 눈치채지 못했다는 형태로만 나타납니다. 아무 일도 없었음을 확인할 방법이 없는 채로도, 그래도 넣을 값어치는 있었다고 봅니다. 손에 넣은 것이 말없이 사라지는 경험은 한 번이면 충분하기 때문입니다.