C언어 – 확률 조작 랜덤 룰렛을 만들어 보자! (중복 없이)

게임 룰렛 시스템을 예시로 들어서 개발해보았습니다!

사용자가 룰렛을 돌리면 (일반,레어,유니크) 중 하나의 아이템을 얻을 수 있습니다!
그리고 아이템은 중복 없이 획득할 수 있습니다!
(‘유니크’ 가 가장 좋은 아이템!)

이 룰렛은 최대 10번까지 돌릴 수 있다고 가정합시다!
그런데 개발자는 적어도 8회 이상을 돌린 유저에게만 좋은 아이템(유니크)을 주고 싶습니다.

이를 CLI(Command Line Interface)로 표현해보았습니다!

같은 이치로 (3회 이상, 5회 이상, 10회 이상) 이렇게 LOCK을 걸어두면 다음과 같이 실행됩니다!

(3회 이상)
(5회 이상)
(10회 이상 / 맨 마지막에만 얻을 수 있음!)

자, 이제 소스를 봐가며 로직을 이해해봅시다!

#include <stdio.h>
#define n 8 // (n회차 이상부터 좋은 아이템 나옴!) 0 < n <= 10

int main() {
	int idx[10] = { 0 }; // 인덱스 배열(중복 방지)

	// 문자열 저장 2차원 배열 (10개의 아이템 획득 문구, 문구 길이 최대 99)
	char comp[10][100] = {
		"일반 아이템(1)을 획득하였습니다!",
		"일반 아이템(2)을 획득하였습니다!",
		"일반 아이템(3)을 획득하였습니다!",
		"일반 아이템(4)을 획득하였습니다!",
		"일반 아이템(5)을 획득하였습니다!",
		"일반 아이템(6)을 획득하였습니다!",
		"레어 아이템(1)을 획득하였습니다!",
		"레어 아이템(2)을 획득하였습니다!",
		"레어 아이템(3)을 획득하였습니다!",
		"[[유니크]] 아이템을 획득하였습니다!!!!" // 소스상 랜덤 수가 9일 때 좋은 아이템!!
	};

	printf("\n* %d회차부터 좋은 아이템이 나오는 확률 조작 룰렛 *\n", n);
	printf(" (중복 아이템은 없음)\n\n");

	for (int i = 0; i < 10; i++) {
		printf("\n- 사용자(%d)이 룰렛을 열 번 돌렸을 때:\n\n", i + 1);

		// 초기화 
		for (int i = 0; i < 10; i++)
			idx[i] = 0;
		
		for (int i = 0; i < 10; i++) { // i는 룰렛이 시도됐던 횟수 (처음에는 0회 시도 됐음)
			int a = rand() % 10;

			// 기존에 이미 나왔으면 다시 돌림 (또는 ↓)
			// 랜덤 수가 9(유니크)지만 룰렛이 n-1회 미만 시행 됐으면 다시 돌림  
			// ex) n=9; 8회 미만 시행됐으면 다시 돌림 -> 8회 이상 시행 됐으면 현재 9회차이고 유니크 가능!

			while (idx[a] != 0 || (a == 9 && i < n - 1)) {
				a = rand() % 10;
			}

			// 룰렛이 n-1회 이상 돌아갔고 a=9(유니크)일 때, 유니크 띄움!
			printf("%s\n", comp[a]);

			idx[a] = 1; // 중복 방지
		}
	}
}

전체 소스와 같으나, 안 쪽에 있는 For문만 이해하면 됩니다!

for (int i = 0; i < 10; i++) { // i는 룰렛이 시도됐던 횟수 (처음에는 0회 시도 됐음)
			int a = rand() % 10;

			// 기존에 이미 나왔으면 다시 돌림 (또는 ↓)
			// 랜덤 수가 9(유니크)지만 룰렛이 n-1회 미만 시행 됐으면 다시 돌림  
			// ex) n=9; 8회 미만 시행됐으면 다시 돌림 -> 8회 이상 시행 됐으면 현재 9회차이고 유니크 가능!

			while (idx[a] != 0 || (a == 9 && i < n - 1)) {
				a = rand() % 10;
			}

			// 룰렛이 n-1회 이상 돌아갔고 a=9(유니크)일 때, 유니크 띄움!
			printf("%s\n", comp[a]);

			idx[a] = 1; // 중복 방지
		}

– 기호 상수, 변수 및 배열 설명!

* a: 랜덤 수 (0~9)가 저장되는 변수로 for문이 진행될 때마다 변한다.
* idx[10]: 랜덤 수의 개수를 배열의 크기로 쓰는 배열이다. 랜덤 중복 방지를 위해 필요하다!
* comp[10][100]: ‘아이템 획득 문구’들이 담겨있는 배열이다. comp[9]가 ‘유니크’ 아이템에 해당된다! 만약 a가 3이면 comp[3]을 출력한다!
* for문 i: 룰렛이 시도 완료된 횟수를 저장하는 변수이다. 첫 번째 시도일 때는 i가 0, 열 번째 시도일 때는 i가 9이다.
* n: n회차 이상부터 유니크 아이템을 얻을 수 있다. 사용자가 정하는 기호 상수이다.


– 중복없는 랜덤 수를 어떻게 만들까?

랜덤 수가 나왔으면, idx배열의 (랜덤 수 번째 인덱스) 자리의 값이 0(false)인지, 1(true)인지 확인하고 0일 때만 해당 랜덤수를 활용하면 됩니다!

반대로 1이면 다시 랜덤 함수를 써서 랜덤 수를 구합니다!

즉 idx배열의 값이 1(true)이면 이미 해당 랜덤 수가 전에 나왔다고 받아 들이고,
0(false)이면 처음 배정된 랜덤 수로 받아 들이는 로직입니다!

위가 성립하려면 처음 idx배열의 원소는 모두 0(false)로 초기화돼야 하며,
한 번 사용한 랜덤 수는 idx배열의 (랜덤 수 번째 인덱스)자리에 1을 줘야 합니다!

코드를 통해 예시를 들어 보겠습니다!

[첫 번째 시도] a가 1 -> idx[1]==0이니 pass -> 1출력 -> idx[1]=1
[두 번째 시도] a가 2 -> idx[2]==0이니 pass -> 2출력 -> idx[2]=1
[세 번째 시도] a가 1 -> idx [1]!=0이니 다시 랜덤 돌림 -> a가 3 -> idx[3]==0이니 pass -> 3출력 -> idx[3]=1
[결국 시도할 때마다, 이전과 다른 숫자를 한 번 출력하니까 랜덤 수가 (0~9)면 열 번째 시도에서 모두 출력! ]


– n회 이상일 때만 (특별한 수) 출력을 어떻게 만들까?

시도 완료 횟수를 저장하는 i변수가 필요합니다.
i변수의 값이 (n-1)회보다 작은데, 랜덤 값이 (특별한 수)면 다시 랜덤 값을 배정해서 억지로 (특별한 수) 출력을 피합니다.
그렇게 i변수의 값이 (n-1)회 이상이 되고, 랜덤 값이 (특별한 수)면 그제야 (특별한 수)를 출력하는 것입니다.

코드를 통해 예시를 들어 보겠습니다!

[첫 번째 시도] a가 1 -> a!=9이니 pass -> 1출력 -> i+1 (1회 시도 완료)
[두 번째 시도] a가 9 -> i==1이니 다시 랜덤 돌림 -> a=8 ->a!=9이니 pass -> 8출력 -> i+1 (2회 시도 완료)
[…]
[여덟 번째 시도] a가 9 -> i==7이니 pass (이전에 7회의 일반 수를 출력했다는 것, 현재 8회차) -> (특별한 수: 9출력) -> i+1 (8회 시도 완료)
[…]


전체 소스는 위 두 개의 로직을 합쳐 놓은 것입니다!
설명은 주석을 참고하시기 바랍니다!

긴 글 읽어주셔서 감사합니다!! ^~^

Leave a Reply

Your email address will not be published. Required fields are marked *