맨 아래에 간단한 요약이 있습니다. 천천히 읽어보시고 이해가 안되시면 아래의 요약을 봐주세요.

사건설명: 

한 유저가 영웅카드팩 200개를 개봉하였는데 피요르긴과 사이카 카드가 100장씩 나오는 버그를 제보함



유저들이 확률조작이라 하며 해명을 요구함.

공지사항에서 14억분의 1의 확률로 일어난 버그라고 해명함.

이 글은 14억분의 1이라는 확률이 어떻게 나왔는지에 관한 가설을 설명하는 글임.

https://www.inven.co.kr/board/lostark/4811/6096723

본인은 개발자가 아니기 때문에 위 글을 읽고 이해한 바를 바탕으로 설명하겠습니다.

디테일한 오류는 있을 수 있지만 전체적인 틀은 비슷할 것 같습니다.

컴퓨터에서 랜덤 함수를 짠다고 해 봅시다.(R)

이때 랜덤함수는 하나의 정수를 받아서(seed) 랜덤의 정수를 반환하는 함수입니다.

예를 들어 1-9 사이의 수를 시드로 받아서 1-9 사이의 수를 반환하는 함수를 짜면 아래와 같습니다.

R(1)=5
R(2)=1
R(3)=7
R(4)=2
R(5)=4
R(6)=8
R(7)=9
R(8)=3
R(9)=6

그리고 1-9에 해당하는 보상 테이블을 설정해 봅시다.

예를 들어 3으로 나눈 나머지를 기준으로 1-광잃쿠 2-에스더루테란 0-카멘

그리고 광잃쿠, 에스더루테란, 카멘이 각각 1/3의 확률로 나오는 카드팩을 만듭니다.

유저가 카드팩을 개봉합니다.

이때 시드값을 설정해야 합니다.

시드값은 주로 현재시간을 기반으로 해서 만듭니다.

예를들어 현재 시간이 11시 48분 34초 31이라고 해봅시다. 

시 분 초 1/100초를 모두 더해서 나온 숫자의 1의자리 숫자를 시드값으로 지정해봅시다. (0은 편의상 고려하지 않음)

11+48+34+31=124

R(4)=2 --> 3으로 나눈 나머지가 2이므로 에스더 루테란 당첨.

그리고 게임에서 주로 많이 쓰는 시드 부여 테크닉이 있습니다.

매번 실시간으로 시드를 부여하는 것이 아니라 이전에 랜덤함수에서 나왔던 값을 다시 새로운 시드로 부여하는 방식입니다.

예를들어 첫 시드값은 4이고 카드팩을 8장 개봉한다고 해봅시다.

R(4)=2 - 에스더루테란
R(2)=1 - 광잃쿠
R(1)=5 - 에스더루테란
R(5)=4 - 광잃쿠
R(4)=2 - 에스더루테란
R(2)=1 - 광잃쿠
R(1)=5 - 에스더루테란
R(5)=4 - 광잃쿠

신기하게도 모든 카드가 나올 확률은 1/3이었지만 에스더 루테란과 광잃쿠만 무한히 나오는 루프에 빠져버립니다.

즉 시작 시드값이 4,2,1,5일 경우 광잃쿠와 에스더 루테란이 번갈아 가면서 무한히 나온다는 거죠.

이제 이 방식을 이용해서 최대한 많은 시드와 랜덤함수를 만들어 봅시다.



이때 int변수를 사용했다고 가정을 하고 이 때 표현 가능한 변수의 범위는 2^32 = 4294967296 입니다.

이를 바탕으로 랜덤함수 표를 만들어 보면 아래와 같이 만들 수 있습니다. (편의상 자연수로 표현)



영웅 카드팩의 경우 73개의 보상 테이블을 균등하게 분배해야 합니다.

그런데 시드값의 범위는 4294967296이고 73으로 나누어 떨어지지 않으므로,

필연적으로 보상이 없는 시드값이 존재하거나 일부 카드가 보상테이블에 더 많이 배치되어야 합니다.

(이 경우 모든 카드의 확률이 완벽히 똑같지는 않지만 분모가 42억으로 매우 크기 때문에 무시할 정도로 차이가 작음)

스마게는 전자를 택한 것 같습니다. (특정 카드의 보상테이블 출현 빈도가 더 적다면 오해의 소지가 있기 때문에)

그리고 이 경우 랜덤값을 반환은 하지만 보상은 없기에 카드팩을 소모하지 않고 다음 시드값만 반환하는 경우를 위와 같이 생각할 수 있습니다.

위와 같은 상황에서 우연히도 나의 시작 시드값이 1,2,3 중에 하나일 경우 무한루프에 빠집니다.

내 시작 시드가 1이고 10장의 카드팩을 개봉해 봅시다.

R(1)=2 - 피요르긴
R(2)=3 - 사이카
R(3)=1 - 보상x, 카드팩 소모x

R(1)=2 - 피요르긴
R(2)=3 - 사이카
R(3)=1 - 보상x, 카드팩 소모x

R(1)=2 - 피요르긴
R(2)=3 - 사이카
R(3)=1 - 보상x, 카드팩 소모x

R(1)=2 - 피요르긴
R(2)=3 - 사이카
R(3)=1 - 보상x, 카드팩 소모x

R(1)=2 - 피요르긴
R(2)=3 - 사이카
R(3)=1 - 보상x, 카드팩 소모x

결과 - 피요르긴과 사이카가 5 5로 반복되는 보상을 받게됩니다.

익숙한 상황이죠?

그런데 공교롭게도 4294967296/3=1431655765.33

만약 모든 시드가 부여될 확률이 똑같다면 1,2,3을 시작 시드로 가질 확률은 놀랍게도

14억 3145만 5765 분의 1 확률입니다. 익숙한 숫자죠.

결국 랜덤함수를 구현할 때 루프가 생길 경우 특정카드가 반복적으로 나올 가능성이 있습니다.

정말 우연히도 시드값과 랜덤함수의 결과값이 같을 경우 똑같은 카드만 계속 나오는 루프에 빠질 가능성도 있는거지요.

결국 시드값 부여하는 방식을 바꾸거나, 랜덤함수의 루프를 없애는 방향으로 수정해야 이런일이 반복되지 않을 것 같습니다.

그렇다면 유저 차원에서 이런 루프를 벗어날 방법은 무엇일까요?

해답은 버그걸린 분의 유튜브를 참고하시면 알 수 있습니다.

카드팩을 "나누기"로 쪼개면 됩니다.

쉬운 말로 요약하자면,

1. 경우의 수는 총 42억 9496만 7296개였다.

2. 이중에 3개의 경우에서 위의 문제가 발생했다.

3. 3/4294967296=1/1431655765.33 --> 14억분의 1의 확률이다.

4. 반복되는 경우를 벗어나고 싶으면 카드팩을 나누기로 나눠서 뽑자.

긴 글 읽어주셔서 감사합니다.