Slay the Spire 2 상관된 난수 버그
Slay the Spire 2 (베타 패치 v0.107.0)에는 서로 다른 의사난수 생성기(PRNG)들이 상관관계를 갖는 중대한 버그가 존재합니다. 이는 Neow가 제공하는 유물과 같은 하나의 무작위 이벤트 결과를 이용해 카드 변형, 물약 드롭, 적 타겟팅 등 미래의 무작위 이벤트 결과를 예측할 수 있음을 의미합니다.
원인: C# System.Random의 선형 시드
Slay the Spire 2는 전투와 같은 한 영역의 난수가 다른 영역(예: 카드 보상)에 영향을 주지 않도록 여러 개의 독립적인 RNG 인스턴스를 사용하려고 합니다. 각 인스턴스는 특정 문자열의 해시(seed + hash("shuffle") 등)로 수정된 시드로 초기화됩니다.
하지만 게임은 표준 C# System.Random 클래스를 사용합니다. 이 클래스가 사용하는 알고리즘은 시작 시드에 대해 선형적입니다. 따라서 알려진 고정값만큼 시드가 차이나는 두 RNG는 예측 가능하고 exploitable한 차이의 출력을 생성합니다. 게임의 RNG 오프셋이 고정되어 있기 때문에, 어느 RNG든 첫 번째 출력은 게임 내 모든 다른 RNG의 첫 번째 출력에 대한 정보를 제공합니다.
상관된 난수가 게임플레이에 미치는 영향
이 상관관계는 시스템적인 편향을 만들어내며, 플레이어에게 즉시 드러나지 않는 방식으로 게임플레이에 영향을 줍니다.
Neow의 유물과 저주
예측 가능성은 Neow가 제공하는 초기 선택에서 가장 뚜렷하게 나타납니다. 예를 들어, 플레이어가 Underdocks act에서 Neow's Bones를 선택하면, 받게 되는 무작위 저주가 Debt일 확률은 약 54%이며, Overgrowth act에서는 같은 유물이 Writhe를 줄 확률이 73%입니다.
다른 Neow 유물도 편향을 보입니다:
- Large Capsule: 이 유물은 절대 일반 등급이 아닙니다. Overgrowth에서는 약 70%가 흔하지 않은 등급이고 30%가 희귀 등급이며, Underdocks에서는 약 63%가 희귀, 37%가 흔하지 않은 등급입니다.
- Leafy Poultice: 첫 번째 카드 변형은 캐릭터의 80장 카드 풀 중 22가지 가능성으로만 제한됩니다.
- Hefty Tablet: 첫 번째 희귀 카드 옵션은 Overgrowth에서는 11가지, Underdocks에서는 3가지로 제한됩니다.
전투와 맵 이벤트
상관관계는 전투 메커니즘과 맵 전용 이벤트에도 확장됩니다:
- Potion Drops: 첫 번째 전투에서 물약이 떨어질 확률은 Underdocks에서는 76%이고 Overgrowth에서는 4%에 불과합니다.
- Enemy Targeting: Defect의 경우, 첫 번째 Underdocks 전투에서 첫 번째 번개 구슬이 왼쪽 적을 맞출 확률은 75%입니다.
- Trash Heap Event: 싱글플레이 모드에서는 Trash Heap 이벤트에서 Rebound 카드를 얻는 것이 수학적으로 불가능합니다. 이는 act RNG와 이벤트 RNG가 특정 결과를 배제하도록 상관관계가 있기 때문입니다.
Act 2와 3의 예측 가능성
예측 가능성은 이후 act에서도 지속됩니다. Act 2의 Doll Room 이벤트는 Neow의 초기 제안과 높은 상관관계를 가집니다. 예를 들어, 플레이어가 런 초반에 Hefty Tablet을 굴렸다면, "one doll" 옵션이 Mr. Struggles일 확률은 91%입니다. 마찬가지로 Crystal Sphere 유물 상자의 위치는 런 첫 전투에서 떨어진 골드 양에 따라 예측할 수 있습니다.
기술적 해결책 및 권고사항
이 문제는 PRNG 선택의 구조적 결함입니다. 상관관계를 없애려면 개발자는 System.Random을 비선형 PRNG으로 교체해야 합니다.
권장 수정 사항
- 비선형 PRNG 구현:
System.Random을 최신 알고리즘인 PCG32와 같은 것으로 교체하면 시드 간 선형 상관관계를 즉시 제거할 수 있습니다. - 맞춤 구현: C# 표준 라이브러리에 의존하지 않고 게임 코드 내에서 직접 PRNG을 구현하면 데스크톱과 모바일 등 서로 다른 플랫폼 간에 시드가 일관되게 유지되어 Slay the Spire 1에서 보였던 버전별 시드 드리프트를 방지할 수 있습니다.
- 카운터 기반 RNG: 카운터 기반 난수 생성기를 사용하면 저장 파일을 로드할 때 RNG 상태를 수동으로 "전진"시킬 필요가 없어집니다.
커뮤니티 인사이트
기술 관찰자들은 이 문제가 Godot 엔진 내에서 C# System.Random을 사용해 개발된 반면, Godot 자체 GDScript RNG는 PCG32를 사용했기 때문에 발생했다고 지적했습니다. PCG32를 사용했다면 이 문제는 완전히 회피될 수 있었습니다. 다른 개발자들은 게임플레이에 중요한 난수 생성기는 플랫폼 의존 라이브러리 코드가 아니라 반드시 게임플레이 코드로 취급해야 일관성과 공정성을 보장할 수 있다고 강조했습니다.