코딩/윤성우의 열혈 C프로그래밍

열혈C도전프로그래밍-도전 프로그래밍3

lee1201zxc 2021. 12. 28. 14:29
300x250
728x90

윤성우의 열혈 C 프로그래밍

406쪽에 위치한 '도전! 프로그래밍 3'의 해설입니다.

 

 

 

 

도전 1

 

 

 

길이가 4×4인 int형 2차원 배열을 선언하고, 모든 요소를 아래 그림의 왼쪽에 있는 형태와 동일하게 초기화하자. 그리고 배열의 요소들을 오른쪽 방향으로 90˚씩 이동시켜서 그 결과를 출력하는 프로그램을 작성해 보자. 참고로 배열이 변경되는 형태는 다음과 같다.

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

13 9 5 1
14 10 6 2
15 11 7 3
16 12 8 4

 

처음에 어떻게 방향을 돌릴지 고민할 것이다.

어떻게 할지 모를 때는 일단 각각의 좌표가 어디로 이동하는지

일일이 적어보고 규칙을 찾으면 된다.

 

 

처음 좌표 회전후 좌표
0, 0 0, 3
0, 1 1, 3
0, 2 2, 3
0, 3 3, 3
1, 0 0, 2
1, 1 1, 2
1, 2 2, 2
1, 3 3, 2

 

이 정도면 규칙이 보이지 않는가?

회전후 x좌표는 처음 좌표의 y좌표가 되고,

회전후 y좌표는 3-(처음 좌표의 x좌표)이다.

이를 이용하여 위치를 바꾸어 주면 된다.

 

 

 

코드

#include<stdio.h>
void turn(int (*a)[4]) //배열 이동 
{
	int b[4][4];
	for(int i=0; i<4; i++)
	{
		for(int z=0; z<4; z++)
			b[i][z]=a[i][z];
	}
	for(int i=0; i<4; i++)
	{
		for(int z=0; z<4; z++)
		{
			a[z][3-i]=b[i][z];
		}
	}
}
int main(void)
{
	int a[4][4];
	for(int i=0; i<16; i++)
		a[i/4][i%4]=i+1; //초기화 
	for(int x=0; x<3; x++)
	{
		for(int i=0; i<4; i++)
		{
			for(int z=0; z<4; z++)
				printf("%d ",a[i][z]);
			printf("\n");
		}
		printf("\n\n");
		turn(a);
	}
}

 

일단 배열a를 만들고 처음값에 맞게 초기화 시킨다. (20~22행)

회전시키는 곳은 turn함수에서 이루어지게 되는데 이차원 배열(a[4][4])을 인자로 주어야하니 매개변수는 (*a)[4]이렇게 써야한다.(main함수의 배열a와 이름만 같음)

그리고 main함수의 배열a와 크기가 같은 이차원배열b[4][4]를 선언해주고 이 배열에 main함수의 a를 덮어 씌운다. (5~9행) 이렇게 하는 이유는 회전하기전 좌표를 토대로 회전후 좌표를 구해야 하기때문에 회전하기전 좌표를 나타내는 배열b가 필요하기 때문이다. 그리고 마지막으로 위에서 말한 방법대로 좌표를 이동시켜주면 된다.(10~16행)

 

 

 

 

도전 2

 

 

 

달팽이 배열을 만들어서 이를 출력하는 프로그램을 작성하고자 한다. 여기서 말하는 달팽이 배열은 다음과 같다.

 

4 X 4 달팽이 배열
5 X 5 달팽이 배열

위 그림에서는 4 X 4의 달팽이 배열과 5 X 5의 달팽이 배열을 보여주고 있다. 이 내용을 참조하여 프로그램 사용자로부터 하나의 숫자 n을 입력 받아서 n X n의 길이에 해당하는 달팽이 배열을 출력해주는 프로그램을 작성해 보자.

★: 가로세로방향으로 채우는 반복문과 세로방향으로 채우는 반복문을 독립적으로 구성하는 것이 쉽게 문제를 해결하는 포인트가 된다.

 

 

처음에 0, 0 에서 시작하여 오른쪽으로 이동하며 숫자를 1씩키우며 출력, 끝에 도달하면 아래로 이동하면서 출력, 그후 왼쪽으로 이동, 그후에는 위로이동... 이 4가지이동을 반복하면 된다. 이 각각의 4가지 이동은 배열의 끝에 도달하거나 다음 이동한곳이 0이 아니면 종료하고 다음 이동을 해야하며 모든 칸을 다 채우면 모든 이동을 종료해야한다.

 

 

코드

#include<stdio.h>
int main(void)
{
	int n,q=0,w=0,e=1;
	printf("숫자를 입력하시오 : ");
	scanf("%d",&n);
	int a[n][n]={};
	a[0][0]=1;
	while(1)
	{
		while(a[q][w+1]==0&&w<n-1)
			a[q][++w]=++e;
		while(a[q+1][w]==0&&q<n-1)
			a[++q][w]=++e;
		while(a[q][w-1]==0&&w>-1)	
			a[q][--w]=++e;
		while(a[q-1][w]==0&&q>-1)
			a[--q][w]=++e;
		if(e==n*n)
			break;
	}
	for(int i=0; i<n; i++)
	{
		for(int z=0; z<n; z++)
			printf("%3d ",a[i][z]);
		printf("\n");
	}
}

 

7행에서 모든 배열을 0으로 초기화 시켜주었고 8행에서 처음 (0, 0)을 1로 초기화 시켜주었다.

그리고 9행의 while문에서 배열을 채우게 된다.

while문안에 4개의 while문이 있는데 이는 차례대로 오른쪽으로 이동, 아래로 이동, 왼쪽으로 이동, 위로 이동이다. 

그리고 각각의 while문은 다음 이동할 곳이 0이고 (그리고) 다음 이동할 곳이 배열의 끝이 아니면 계속 이동하며 숫자를 1씩 키우며 배열을 채워 나간다. 그리고 19행의 if문은 행과 열이 n인 배열을 채워나가다 숫자를 n X n 개를 모두 채우면 while문을 빠져나간다는 의미이다. 

 

 

 

도전 3

 

0 이상 99 이하의 난수를 총 5개 생성하는 프로그램을 작성해보자(힌트: % 연산자를 적당히 활용하면 된다.

 

 

 

난수 생성을 위해선 #include<stdlib.h>를 추가하고 rand()를 이용하면 된다.

rand()을 사용하면 0 ~ 32767 의 값중 하나가 무작위로 나오게 된다. (short형 범위(-32768~32767)의 반)

그런데 문제에선 0 ~ 99 사이의 난수를 생성하라고 한다. 어떻게 해야할까?

바로 rand()를 이용해 나온숫자%100을 하면 된다. 어떤 수를 100으로 나눈 나머지는 항상 0 ~ 99이기 때문이다.

이제 코드를 작성해 보자.

 

 

 

코드

#include<stdio.h>
#include<stdlib.h>
int main(void)
{
	for(int i=0; i<5; i++)
	{
		printf("%d\n",rand()%100);
	}
}

 

 

 

 

도전 4

 

 

도전 3을 여러번 실행하면 실행할 때마다 같은 값이 나온다. 이 문제를 해결하고 
두 개의 주사위를 던졌을 때의 결과를 출력하는 프로그램을 작성해보자. 물론 그 결과는 예측이 불가능해야 한다.

 

 

 

도전 3에서 프로그램을 실행할 때마다 같은 값이 나오는 이유는 이는 실제로 난수가 아니기 때문이다. 

바로 의사난수이다. srand()를 이용하면 난수의 씨드 값을 지정하여 무작위 난수를 만들 수 있는데

프로그램을 실행할 때마다 난수를 일일히 지정해주면 진짜 난수가 아니기 때문에 time함수를 쓴다.

일단 <time.h>를 추가하고 srand(time(NULL))을 써준다. 이렇게 하면 씨드 값으로 1970년 1월 1일 이후 시간 차를 초단위로 변환해서 반환해 준다. 이렇게 하면 프로그램을 실행할 때마다 다른 값을 씨드 값으로 쓰게 되기 때문에 거의 랜덤이라고 할 수 있다. 

 

 

 

코드

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(void)
{
	srand(time(NULL));
	printf("주사위 1의 결과 %d\n", rand()%6+1);
	printf("주사위 2의 결과 %d", rand()%6+1);
	 
}

 

랜덤으로 나온값을 6으로 나눈 나머지를 구하면 0 ~ 6 이 나오는데 주사위 눈은 1 ~ 6 이므로 구한 값에 1을 더해주었다.

 

 

 

 

 

도전 5

가위 바위 보 게임을 만들어 보자. 사용자로부터 가위 바위 보 중에서 하나를 입력 받는다. 그리고 컴퓨터는 난수 생성을 통해서 가위 바위 보 중에서 하나를 선택하게 한다. 이 둘을 비교해서 승자와 패자를 가려주는 프로그램을 작성해 보자. 단 프로그램의 진행은 사용자가 질 때까지 계속되어야 하고, 마지막에 가서는 게임의 결과(예: 4승 3무)까지 출력해 주도록 하자.

 

 

 

앞에서 무작위 난수생성 방법을 배웠으니 이를 이용하는 문제이다. 

 

 

 

코드

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(void)
{
	srand(time(NULL));
	int win=0,draw=0,lose=0,me,com;
	char *a[3]={"바위","가위","보"};
	while(1)
	{
		printf("바위는 1, 가위는 2, 보는 3:");
		scanf("%d",&me);
		com=rand()%3+1;
		if(me==com)
			printf("당신은 %s선택, 컴퓨터는 %s선택, 비겼습니다.\n",a[me-1],a[com-1]),draw++;
		else if((me<com && com-me==1)||me>com && com-me==-2)
			printf("당신은 %s선택, 컴퓨터는 %s선택, 이겼습니다.\n",a[me-1],a[com-1]),win++;
		else
		{
			printf("당신은 %s선택, 컴퓨터는 %s선택, 졌습니다.\n",a[me-1],a[com-1]); 
			lose=1;
		}
		if(lose==1)
			break;
	}
	printf("\n게임의 결과 : %d승, %d무",win,draw);
}

 

바위=1, 가위=2, 보=3이라 두고 사용자로부터 하나를 입력받고 컴퓨터에게는 1 ~ 3중 하나를 무작위로 배정해 준다. 

포인터 배열 *a[3]안에 "바위", "가위", "보" 값을 넣어 주었는데 이는 출력시 편하게 하기 위해서 이다. 

14행의 if문은 서로 같은 것을 내 비겼을때 이며 

16행의 else if는 이겼을때 이다. else if문의 조건에 대해 궁금할수 있는데 (me<com && com-me==1)는 

나와 컴퓨터의 값이 각각 (바위, 가위), (가위, 보)인 이 2가지경우를 의미한다.  (me>com && com-me==-2)는

(보, 바위)인 경우를 의미한다. 바위=1, 가위=2, 보=3인것을 생각하면 된다. 

그리고 위 경우가 모두 아니라면 진경우 이므로 else를 이용하면 된다. 

 

 

 

도전 6

 

 

숫자 맞추기 게임(숫자 야구)을 만들어보자.

 

 

 

야구 게임은 0 ~ 9까지 서로 다른 수 3개를 무작위로 고르고 이 숫자와 순서를 모두 맞춰야 하는 게임이다.

수3개를 하나부르고 각 숫자마다 숫자는 맞지만 위치가 틀리면 볼, 숫자도 맞고 위치도 맞으면 스트라이크가 된다.

이를 이용해 무작위 수가 무엇인지 맞추는 것이다.  

 

아래 나무위키를 참고하면 좋다.

https://namu.wiki/w/%EC%88%AB%EC%9E%90%EC%95%BC%EA%B5%AC  

 

 

 

 

코드

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(void)
{
	srand(time(NULL));
	int com[3],coms[10]={0,},c,me[3],strike,ball,how=1;
	for(int i=0; i<3; i++)
	{
		while(1)
		{
			c=rand()%10;
			if(i==0||(com[i-1]!=c&&com[i-2]!=c))
			{
				com[i]=c;
				coms[c]=1;
				break;
			}
		}
	}
	printf("Start Game!\n");
	while(1)
	{
		printf("3개의 숫자 선택: ");
		scanf("%d %d %d",&me[0],&me[1],&me[2]);
		strike=(me[0]==com[0])+(me[1]==com[1])+(me[2]==com[2]);
		ball=coms[me[0]]+coms[me[1]]+coms[me[2]]-strike;
		printf("%d번째 도전 결과: %dstrike, %dball\n",how++,strike,ball);
		if(strike==3)
			break;
	}
	printf("\n\n%d번째 도전만에 성공!\n",how);
	printf("Game Over!");
	
}

com은 랜덤으로 정한 3자리수, coms는 0 ~ 9 중 특정 숫자가 쓰였는지 보기위한 배열로 coms[0]은0, coms[1]은1....coms[9]는9를 의미한다. , c는 랜덤으로 정한수를 넣어주기 위한 변수, me는 내가 정한 3자리수, strike와 ball은 각각 몇 스트라이크, 볼인지 저장하기 위한 변수, how는 몇번째 도전인지 저장하는 변수이다.

8 ~ 20 행의 for문은 랜덤으로 3자리수를 중복없이 정하기위한 코드이다. 13행의 if문은 랜덤으로 정한수가 이미쓰였는지 안쓰였는지 검사하는 부분이다. 안쓰였다면 com에 숫자를 넣어주고 coms에서 숫자위치에 1값을 넣어준다. 

이렇게 3자리 수를 만들었다면 맞출 시간이다. 

3개의 숫자를 사용자로부터 입력받아 몇 strike, 몇 ball인지 알려주어야 한다. 

여기서 (me[0]==com[0])이런게 쓰였는데 me[0]값과 com[0]값이 같다면 1값을 반환해준다. 이렇게 3가지 자리수를 비교해 모두 맞다면 3strike가 되는것 이다. 

그리고 ball값을 구해야 하는데 coms[me[0]]이런 것이 쓰였다. 이는 me[0]값이 com에서 쓰였는지 알아보기 위한 것이다. ball값을 구할 때 마지막에 strike값을 빼주었는데 이는 strike 값이랑 ball값이 중복되기 때문이다. 어떤수가 stikre값에 포함되면 이는 무조건 ball값에 포함되기 때문에 빼주었다.

 

728x90