Programming/C, C++

코딩 자율 학습단 11일차 - 자라나라 머리카락 프로젝트

주눅 2024. 7. 4. 05:09

 

오늘은 배열을 활용해 실제로 게임을 만들어본다!

 

이 게임은 네 개의 약 중 어떤 것이 진짜 발모제인지를 찾는 게임이다.

게임을 실행하면 총 3회 약품을 테스트하는데,

한 번에 2개 또는 3개의 약품을 조합해 머리에 바르고 머리가 나는지 아닌지 체크한다.

 

이런 식으로 테스트를 3회 진행한 후, 진짜 발모제가 몇 번 약인지를 입력한다.

정답이 맞으면 정답 메시지를, 틀리면 오답 메시지와 함께 정답을 알려주고 게임을 종료하면 된다.

 

시작!!!!

 


 

가장 먼저, 어떤 약이 발모제인지를 정하기 위해 난수를 이용한다.

게임이 시작할 때 나오는 문장도 함께 적었다.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(void) {
	srand(time(NULL)); //난수 초기화

	int treatment = rand() % 4; //진짜 발모제를 0~3 범위 안에서 지정
	printf("\n\n === 자라나라 머리카락 게임 === \n\n");

	return 0;
}

 

다음으론 약의 조합을 3회 보여준다. 반복문(for)을 이용하면 될 듯

 

약의 조합은 한 번에 2개, 혹은 3개의 약을 조합한다. 이 수는 난수를 활용해 정한다.

rand () % 2 + 2 ← 이렇게 적으면 0, 1에 각각 +2를 더하는 거니까 2, 3이 나온다!!

 

직전 조합이 약품 2개의 조합이었다면 다음 조합은 3개의 조합이 되게끔 한다. (맞추기 쉽게끔)

이를 위해 직전의 조합과 현재의 조합의 약품 개수를 저장할 변수를 생성하고,

두 변수가 서로 다를 때에만 현재 조합의 결과가 출력되게끔 한다.

↑ 이건 do-while문을 사용해 구현한다 (첫 조합은 앞 결과와 비교할 필요가 없으니까)

 

이후 지금이 몇 번째 실험인지를 알려주는 출력문도 작성했다!

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(void) {
	srand(time(NULL)); //난수 초기화

	int treatment = rand() % 4; //진짜 발모제를 0~3 범위 안에서 지정
	printf("\n\n === 자라나라 머리카락 게임 === \n\n");

	int cntShowBottle = 0; //현재 결과
	int prevCntShowBottle = 0; //직전 결과

	for (int i = 1; i <= 3; i++) { //총 3회 반복
		int bottle[4] = { 0,0,0,0 }; //약병 4개의 배열

		do {
			cntShowBottle = rand() % 2 + 2; //조합될 약병의 개수를 2~3 범위 안에서 지정
		} while (cntShowBottle == prevCntShowBottle); //두 변수의 값이 같으면 계속 반복. 달라지면 빠져나옴
		prevCntShowBottle = cntShowBottle; //현재 값을 직전 값으로 저장

		printf(" > %d번째 테스트 : ", i);
	}

	return 0;
}

 

약을 몇 개 조합할지 정했으니, 어떤 약을 조합할지를 정해야 한다.

cntShowBottle 변수의 수만큼 약병을 선택한다. →for문을 이용해 cntShowBottle만큼 반복한다.

 

난수를 생성해 약병의 번호를 고르고 결과를 변수에 저장한다. 

고른 약병은 배열의 값을 0에서 1로 바꿔 선택되었음을 표시하고 중복을 골라낸다.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(void) {
	srand(time(NULL)); //난수 초기화

	int treatment = rand() % 4; //진짜 발모제를 0~3 범위 안에서 지정
	printf("\n\n === 자라나라 머리카락 게임 === \n\n");

	int cntShowBottle = 0; //현재 결과
	int prevCntShowBottle = 0; //직전 결과

	for (int i = 1; i <= 3; i++) { //총 3회 반복
		int bottle[4] = { 0,0,0,0 }; //약병 4개의 배열

		do {
			cntShowBottle = rand() % 2 + 2; //조합될 약병의 개수를 2~3 범위 안에서 지정
		} while (cntShowBottle == prevCntShowBottle); //두 변수의 값이 같으면 계속 반복. 달라지면 빠져나옴
		prevCntShowBottle = cntShowBottle; //현재 값을 직전 값으로 저장

		printf(" > %d번째 테스트 : ", i);

		int isIncluded = 0; //발모제가 포함되었는지를 확인한다.
		for (int j = 0; i < cntShowBottle; j++) { //현재 조합할 약의 개수만큼 반복
			int randBottle = rand() % 4; //약병의 번호를 고른다 (0~3)

			if (bottle[randBottle] == 0) { //아직 선택되지 않은 약병이라면
				bottle[randBottle] = 1; //값을 1로 변경

				if (randBottle == treatment) { //선택한 약병이 발모제라면
					isIncluded = 1;
				}
			}
			else {
				j--; // bottle[randBottle]이 1이라면 이미 선택된 약병이므로 중복. 반복 횟수를 줄여 다시 선택한다
			}
		}

	}

	return 0;
}

 

 

약병이 모두 선택되었으니 bottle 배열을 체크해 몇 번이 선택되었는지를 확인한다.

↑ for문을 사용해 bottle의 요소를 하나하나 체크한다

 

만약 요소의 값이 1이라면 해당하는 인덱스를 출력한다.

이때, 인덱스의 값을 그대로 출력하지 않고 값에 1을 더한다!! (1, 2, 3, 4로 표시되도록)

이후 isIncluded의 값을 확인해 고른 약병 중에 발모제가 있는지를 확인하고,

발모제의 유무에 맞는 실험 결과를 화면에 출력한다.

 

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(void) {
	srand(time(NULL)); //난수 초기화

	int treatment = rand() % 4; //진짜 발모제를 0~3 범위 안에서 지정
	printf("\n\n === 자라나라 머리카락 게임 === \n\n");

	int cntShowBottle = 0; //현재 결과
	int prevCntShowBottle = 0; //직전 결과

	for (int i = 1; i <= 3; i++) { //총 3회 반복
		int bottle[4] = { 0,0,0,0 }; //약병 4개의 배열

		do {
			cntShowBottle = rand() % 2 + 2; //조합될 약병의 개수를 2~3 범위 안에서 지정
		} while (cntShowBottle == prevCntShowBottle); //두 변수의 값이 같으면 계속 반복. 달라지면 빠져나옴
		prevCntShowBottle = cntShowBottle; //현재 값을 직전 값으로 저장

		printf(" > %d번째 테스트 : ", i);

		int isIncluded = 0; //발모제가 포함되었는지를 확인한다.
		for (int j = 0; i < cntShowBottle; j++) { //현재 조합할 약의 개수만큼 반복
			int randBottle = rand() % 4; //약병의 번호를 고른다 (0~3)

			if (bottle[randBottle] == 0) { //아직 선택되지 않은 약병이라면
				bottle[randBottle] = 1; //값을 1로 변경

				if (randBottle == treatment) { //선택한 약병이 발모제라면
					isIncluded = 1;
				}
			}
			else {
				j--; // bottle[randBottle]이 1이라면 이미 선택된 약병이므로 중복. 반복 횟수를 줄여 다시 선택한다
			}
		}

		for (int k = 0; k < 4; k++) {
			if (bottle[k] == 1) { //선택된 약품의 번호를 화면에 표시
				printf("%d ", k + 1); //0~3을 1~4로 표시하기 위해 1을 더했음
			}
		}

		printf("번 물약을 머리에 바릅니다.\n\n");

		if (isIncluded == 1) { //발모제가 포함되어 있다면 
			printf(">> 성공! 머리카락이 났어요!!\n");
		}
		else { //포함되어 있지 않다면
			printf(">> 실패! 머리카락이 나지 않았어요. ㅠㅠ\n");
		}

		printf("\n    계속하려면 Enter를 누르세요.  \n");
		(void) getchar(); //Enter를 입력받음. 

	}

	return 0;
}

 

이 과정에서 한번 중간 결과를 보고 싶어서 코드를 실행시켜 봤는데

제대로 실행되지 않고 선택된 약품을 골라내질 못했었다... ㄱㅡ

깃허브에 있는 실습 예제 코드와 열심히 비교해 봐도 틀린 부분이 하나도 없는 것 같아서

대체 뭐가 문제지?? 했는데 문제는 바로...

 

ㅠㅠㅠ

이거였다 ㅠㅠㅠㅠㅠㅠㅠ 챗gpt가 짚어줌

게시판에 질문 글 써볼까? 하다 생각나서 물어봤는데...

바로 수정했다!! 이제 문제없이 잘 작동됨😭

 

지금 보니까 너무 다르게 보이는 j와 i...

아까는 정말 문제없어 보였는데... 

 

진짜 챗gpt한테 뽀뽀해주고 싶다...

 

 

아무튼 이어서 작성!!

테스트 결과를 총 3회 보여주고 난 이후에는 정답을 입력받아야 한다.

 

scanf_s()로 정수를 입력받고, 이 값을 변수에 저장한 후

변수의 값과 treatment + 1의 값이 같은지를 비교해 정답인지 아닌지를 판단한다.

(사용자는 1~4 범위의 답을 말하고, treatment는 0~3 사이의 숫자니 1을 더해 범위를 같게 한다)

 

만일 정답이 아니라면 정답이 몇 번이었는지도 함께 출력해 준다.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(void) {
	srand(time(NULL)); //난수 초기화

	int treatment = rand() % 4; //진짜 발모제를 0~3 범위 안에서 지정
	printf("\n\n === 자라나라 머리카락 게임 === \n\n");

	int cntShowBottle = 0; //현재 결과
	int prevCntShowBottle = 0; //직전 결과

	for (int i = 1; i <= 3; i++) { //총 3회 반복
		int bottle[4] = { 0,0,0,0 }; //약병 4개의 배열

		do {
			cntShowBottle = rand() % 2 + 2; //조합될 약병의 개수를 2~3 범위 안에서 지정
		} while (cntShowBottle == prevCntShowBottle); //두 변수의 값이 같으면 계속 반복. 달라지면 빠져나옴
		prevCntShowBottle = cntShowBottle; //현재 값을 직전 값으로 저장

		printf(" > %d번째 테스트 : ", i);

		int isIncluded = 0; //발모제가 포함되었는지를 확인한다.
		for (int j = 0; j < cntShowBottle; j++) { //현재 조합할 약의 개수만큼 반복
			int randBottle = rand() % 4; //약병의 번호를 고른다 (0~3)

			if (bottle[randBottle] == 0) { //아직 선택되지 않은 약병이라면
				bottle[randBottle] = 1; //값을 1로 변경

				if (randBottle == treatment) { //선택한 약병이 발모제라면
					isIncluded = 1;
				}
			}
			else {
				j--; // bottle[randBottle]이 1이라면 이미 선택된 약병이므로 중복. 반복 횟수를 줄여 다시 선택한다
			}
		}

		for (int k = 0; k < 4; k++) {
			if (bottle[k] == 1) { //선택된 약품의 번호를 화면에 표시
				printf("%d ", k + 1); //0~3을 1~4로 표시하기 위해 1을 더했음
			}
		}

		printf("번 물약을 머리에 바릅니다.\n\n");

		if (isIncluded == 1) { //발모제가 포함되어 있다면 
			printf(">> 성공! 머리카락이 났어요!!\n");
		}
		else { //포함되어 있지 않다면
			printf(">> 실패! 머리카락이 나지 않았어요. ㅠㅠ\n");
		}

		printf("\n    계속하려면 Enter를 누르세요.  \n");
		(void)getchar(); //Enter를 입력받음. 

	}

	//입력받기
	printf("\n\n 발모제는 몇 번일까요? ");

	int answer;
	scanf_s("%d", &answer); //정수를 입력받는다.

	/* 사용자는 1~4, 코드는 0~3 범위의 수를 사용하기 때문에 treatment 값에 +1을 더해 범위를 맞춤*/
	if (answer == treatment + 1) { //정답이라면
		printf("\n>> 정답입니다!\n");
	}
	else { //아니라면
		printf("\n>> 땡! 틀렸어요. 정답은 %d번입니다. \n", treatment + 1);
	}

	return 0;
}

실행 결과

 

문제없이 작동하는 것 확인!!

은근 재밌어서 뭔가 열받고 웃긴다 ㅠㅠ 

 

그리고 모처럼 위에 챗gpt를 썼으니까...

이번에 작성한 코드도 첨삭받을만한 부분이 없는지 한 번 물어봤다.

 

먼소리여

 

1번 항목은 무시해도 될 것 같았다.

어차피 비주얼 스튜디오를 사용하고 있으니까

 

그리고 3번은...

포맷팅이라는 게 뭔지도 물어봤는데, 코드가 쓰여진 모양을 말하는 거였음!!

보기 예쁘게 정리했다는 소리였다 ㅋㅋ ㅠㅠ

 

대망의 2번

"getchar() 함수를 두 번 호출하여 개행 문자를 처리하는 것이 좋습니다."

 

 

오호...

사용자가 입력을 하지 않았는데 프로그램이 혼자 급발진을 할 수도 있단 말 같다

 

질문의 수준이 참;;

 

이 이후에 코드도 함께 보내줬는데...

 

// 입력 버퍼를 비우는 함수
void clearInputBuffer() {
    int c;
    while ((c = getchar()) != '\n' && c != EOF) {
        // 입력 버퍼에 남아 있는 문자를 제거
    }
}

이런 사용자 정의 함수를 추가해서

printf("\n    계속하려면 Enter를 누르세요.  \n");

clearInputBuffer(); // 입력 버퍼를 비운다
getchar(); // 실제로 Enter 키를 입력받음

이런 식으로 코드를 수정했더라

 

....

챗gpt는 정말 착하다

 

대충 요약

 

scanf()를 사용해 데이터를 입력받으려 한다.

사용자는 '42'를 입력하고 엔터를 누르는데, 이때 '42'와 '\n(엔터)'가 입력 버퍼에 저장된다. 

 

이후 '42'는 scanf()가 읽어 들여 변수에 저장해 가지만,

뒤에 있는 '\n'는 입력 버퍼에 그대로 남아 있게 되는 것

 

이렇게 입력 버퍼에 개행 문자가 남아있으면 다음 입력 함수가 실행될 때

이 개행 문자를 읽어들여 버리는 등의 문제가 발생할 수 있으니, 이것을 비워줘야 한다고 한다.

 

그리고 비우는 방법이 바로

(1) getchar()를 사용하거나

(2) 위의 예제 같은 버퍼 청소 함수를 사용하는 것

 

 

...........................

이런 게 있구나

일단 알아만 두기로 했다


근데 내가 지금 거의 열흘 넘게 포스팅을 작성하면서 생각한 건데

나 너무 책의 본문을 그대로 옮기고 있나? 싶기도 하고...

-__- + 출판사한테 혼날 것 같은데

 

이건 아무래도 좀 NG인 것 같으니까ㅠㅠ

내일부턴 다른 방식으로 글을 써봐야겠음 +ㅅ+

사실 오늘도 좀 신경 쓰긴 했다...

내가 작성한 코드 + 설명만 간단히 적는 게 좋을 것 같음

 

오늘 공부 끝!