극한탈출 9시간 9명 9의 문(Zero Escape: Nine Hours, Nine Persons, Nine Doors)이라는 전자오락을 플레이하고 있다. 주 장르는 방탈출 게임이지만 미스터리 소설이라고도 볼 수 있겠다. 플레이어의 선택에 따라 이야기의 진행과 등장인물들의 운명이 달라지는 것 같다. 나는 아직 초반부를 플레이 중이다. 이후의 전개가 어떻게 될지는 모르는 상태다.

내가 게임 전개를 모르기 때문에, 아마 이 글에는 스포일러가 없을 것이다. 그래도 혹시 모르니 스포일러에 주의하자.

배경

등장인물들은 알 수 없는 이유로 어떤 선박에 납치된 채 깨어난다. 선박에서 탈출하기 위해서는 여러 개의 문을 통과해 마지막에 9가 적힌 문을 나가야 한다. 서로 협력해도 모자랄 판에 의심과 갈등은 점차 커지고… 누가 목숨을 잃을지 모르는 극한의 상황이다.

나는 모든 사람을 무사히 탈출시키고 싶다. 어떻게 해야 할까? 모든 선택에 그에 따른 결과가 있을 것이라는 점을 감안하면, 눈 앞에 닥친 문제를 해결하는 동시에 멀리까지 내다보아야 한다. 최선의 선택을 하기 위해 머리를 굴리다가 암산으로는 멀리 내다보기가 어려워서 프로그래밍까지 동원해 보았다.

규칙

최종 목적지인 9가 적힌 문을 빠져 나가려면 누구를 살리고 누구를 포기해야 할지 판단해야 한다. 문을 통과할 때는 규칙이 있기 때문이다.

  • 선박에 갇혀 있는 사람은 8명인데 각기 1부터 8까지 번호가 부여되어 있다.
  • 문을 통과할 때는 한 번에 3명, 4명, 또는 5명이 함께 통과해야 한다. 그 외의 조합으로는 통과할 수 없다.
  • 문에는 수가 적혀있는데 문을 통과하려면 그 수와 문을 통과하는 사람들의 수의 합계의 디지털 루트가 같아야 한다.

디지털 루트란 자연수를 10진법 한자리 수로 줄이는 방법이다. 각 자리수의 수를 자리는 무시하고 수만 합하는 과정을 최종결과가 10 미만이 될때까지 반복한다. 파이썬으로 정의하면 다음과 같다.

def digital_root(n):
    if n < 10:
        return n
    else:
        return digital_root(n // 10 + n % 10)

이 함수는 완벽하지는 않고 n < 100 일 때만 제대로 동작한다. 게임의 상황상 가장 큰 수를 가진 다섯 명이 함께 문을 통과해도 (4+5+6+7+8 = 30) 밖에 되지 않기 문제가 되지 않는다.

문을 나가는 방법

그렇다면 모두 함께 9가 적힌 최종 관문을 통과할 수 있을지 생각해 보자. 사람이 여덟 명이기 때문에 모두가 탈출하려면 다섯 명이 먼저 통과하고 남은 세 명이 통과하거나, 네 명이 먼저 통과하고 남은 네 명이 통과하면 된다.

문제는 어떤 조합으로 사람들을 통과시켜야 하는 것인가다. 이것을 미리 알고 있어야 누구를 반드시 살려야 하는지, 누구를 포기해야하는지 알 수 있다.

먼저 게임 참가자들을 3명씩, 4명씩, 5명씩 묶은 모든 조합을 구한다. 파이썬의 itertools.combinations 함수를 쓰면 중복을 제외하고(즉, 순서 구별 없이) 가능한 모든 조합을 구할 수 있다.

from itertools import combinations

# 참가자들
candidates = [1, 2, 3, 4, 5, 6, 7, 8]

# 3명, 4명, 5명으로 만들 수 있는 모든 조합
groups = list(combinations(candidates, 3)) +
         list(combinations(candidates, 4)) +
         list(combinations(candidates, 5))

182개의 조합이 가능하다. 이 중 디지털 루트가 9가 되는 조합을 찾아야 한다.

먼저, 각 그룹에 디지털 루트 값을 덧붙이고, 디지털 루트가 9인 것을 필터한다. 값을 덧붙이는 과정 없이 바로 필터해도 되지만, 값을 덧붙여두면 다른 문을 통과할 때도 참고할 수 있을 것 같다.

# 각 조합에 디지털 루트 계산결과를 덧붙임
groups = [(group, digital_root(sum(group))) for group in groups]

# 디지털 루트가 9인 조합 찾기
groups_for_9 = list(filter(lambda group: group[1] == 9, groups))

통과할 수 있는 모든 조합을 구했다. 다음 조합이 9를 통과할 수 있다.

(1, 2, 6)
(1, 3, 5)
(2, 3, 4)
(3, 7, 8)
(4, 6, 8)
(5, 6, 7)
(1, 2, 7, 8)
(1, 3, 6, 8)
(1, 4, 5, 8)
(1, 4, 6, 7)
(2, 3, 5, 8)
(2, 3, 6, 7)
(2, 4, 5, 7)
(3, 4, 5, 6)
(1, 2, 3, 4, 8)
(1, 2, 3, 5, 7)
(1, 2, 4, 5, 6)
(1, 5, 6, 7, 8)
(2, 4, 6, 7, 8)
(3, 4, 5, 7, 8)

모두를 살리는 방법

9를 통과할 수 있는 조합은 20가지다. 이들을 두 개씩 합쳐 보면, 두 번에 걸쳐 모든 사람이 9의 문을 통과할 수 있는지 확인할 수 있다. 개수가 몇개 되지 않아서 눈으로 직접 세어 가며 조합했다.

(1, 2, 3, 4, 8),     (5, 6, 7)
(1, 2, 3, 5, 7),     (4, 6, 8)
(1, 2, 4, 5, 6),     (3, 7, 8)
(1, 5, 6, 7, 8),     (2, 3, 4)
(2, 4, 6, 7, 8),     (1, 3, 5)
(3, 4, 5, 7, 8),     (1, 2, 6)
(1, 2, 7, 8),        (3, 4, 5, 6)
(1, 3, 6, 8),        (2, 4, 5, 7)
(1, 4, 5, 8),        (2, 3, 6, 7)
(1, 4, 6, 7),        (2, 3, 5, 8)

신기하게도 모든 조합이 자기와 맞는 짝을 갖고 있었다. 즉, 한 조합의 사람들이 9의 문을 빠져나가면 남은 사람들은 다음 차례에 자동으로 9의 문을 빠져나갈 수 있다.

9의 문까지 모든 사람이 생존해서 도달하기만 한다면, 모든 사람이 살 수 있는 것이다. 하지만 아직까지는 등장인물들이 이를 깨닫지 못하고 5명이 나가면 3명은 포기해야 한다고 믿고 있다.

게임은 매우 치밀하게 구성되어 있는 것 같다. 앞으로 어떻게 전개될지는 알 수 없다. 매 순간 어떤 결정이 가장 현명할지 고민할 뿐이다.