본문 바로가기
개발 공부/C언어

#18 [열혈 C 프로그래밍] 도전! 프로그래밍3 - 1번, 2번, 3번, 4번, 5번, 6번 코드와 해설

by 반달bear 2022. 8. 4.
반응형

 

 

1번

 

이렇게도 돌려보고 저렇게도 돌려보고 한 배열안에서 어떻게든 해결하려고 했는데 결국 손으로 그리는 방식이 제일 간단하다는 생각을 하게 되었습니다... 배열을 관련없는 곳에 저장 후에 차례차례 저장하기. 

 

다 풀고 나면 왜 이런 생각을 못한거지? 싶은 생각이 드는데 이 자체가 제가 발전한 증거가 아닐까...하고 생각하려 합니다..ㅜㅜㅜ

 

#include<stdio.h>
void Rotate(int (*arr)[4],int repeat );

int main()
{
    int arr[][4]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
    
    Rotate(arr,3); //3번 회전 시키기

  

    return 0;
}

void Rotate(int (*arr)[4],int repeat)
{
    int alter[4][4]={0};
    
    for (int z= 0; z < repeat; z++)
    {
        for(int i=0;i<4;i++) //arr배열을 alter배열에 저장
        {
            for(int j=0;j<4;j++)
            {
                alter[i][j]=arr[i][j];
            }
        }

        for(int i=0;i<4;i++) //alter에 있는걸 돌려서 arr배열에 저장
        {
            for(int j=0;j<4;j++)
            {
                arr[j][3-i]=alter[i][j];
            }
        }

        for(int i=0;i<4;i++) //arr배열 1회전출력
        {
            for(int j=0; j<4;j++)
            {
                printf("%d ",arr[i][j]);
            }
            printf("\n");
        }

        printf("\n"); //출력 후 다음 배열 출력 구분 위해 띄워주기
        printf("\n");
            
    }
    
    
}
 

 

 


 

2번

 

#include<stdio.h>
void Snail(int(*arr)[100],int num);
void Output(int(*arr)[100],int num);

int main()
{
    int num;
    printf("100 이하의 숫자를 입력하세요 : ");
    scanf("%d", &num); //배열 크기 입력
    
    int arr[100][100]; //배열선언
    
    Snail(arr,num); //배열에 달팽이 숫자 저장

    Output(arr,num); //배열 출력
    return 0;
}

void Snail(int(*arr)[100],int num) //num=4
{
    int i=0,j=0;
    int count=1;
    int re=0; // 전체 for을 얼마나 반복할건지 정하는 숫자

    if(num%2 == 0) //만약 num이 짝수이면
    {
        re=(num/2)-1;
    }
    else
    {
        re=(num/2);
    }
    
    int fornum=num-1;
    int secondfornum = 0;

    for(int z=0;z<re;z++)
    {
        for(; j<fornum;j++) // 가로+
        {
            arr[i][j]=count;
            count++;
        } //여기까지 i=0 j=3 count=4 //아래의 주석들은 n=4일때의 가정.

        for(; i<fornum;i++) // 세로+
        {
            arr[i][j]=count;
            count++;
        }//count=7 i=3 j=3

        for(;j>secondfornum;j--)  //가로-
        {
            arr[i][j]=count;
            count++;
        } // count=10,i=3, j=0

        for(;i>(secondfornum+1);i--) //세로-
        {
            arr[i][j]=count;
            count++;
        }//count=12, i=1, j=0

        fornum--;
        secondfornum++;

    }

    if(num%2==0) // 나머지 ㄷ와 - 만들기 , 짝수라면 ㄷ
    {
        for(; j<fornum;j++) // 가로+
        {
            arr[i][j]=count;
            count++;
        } //여기까지

        for(; i<fornum;i++) // 세로+
        {
            arr[i][j]=count;
            count++;
        }//
        arr[i][j]=count;
        count++;
        arr[i][j-1]=count;
    }
    else //배열이 홀수라면 -
    {
        arr[i][j]=count;
        count++;
        arr[i][j+1]=count;
    }
        

    
}



void Output(int(*arr)[100],int num) //출력함수
{ 
    for(int i=0;i<num;i++)
    {
        for(int j=0;j<num;j++){
            printf("%d, ",arr[i][j]);
        }
        printf("\n");
    }
}
 
 

푸는데 진짜 힘들었다...

 

우선 규칙성을 찾는데 집중했습니다.

그림으로 그렸더니 반복되는 패턴이 있었습니다.

편의를 위해 화살표 있는 네모를 'ㅁ' 짝수의 마지막에 있는 패턴을 'ㄷ' 홀수 마지막에 있는 패턴을 '-'라고 하겠습니다.

n=5라면 ㅁx2, -x1 로 구성된거라고 생각하면 됩니다.

 

 

무튼 저렇게 그려보니까 ㅁ가 반복되고 마지막이 홀수면 - 짝수면 ㄷ로 끝난다는 걸 알게되었습니다.

그러면 ㅁ이 n에 따라 얼마나 달라지는지 규칙을 찾아야 했습니다.

 

적어놓고 보니까 숫자를 /2했을때의 몫 만큼 ㅁ가 반복된다는 사실을 발견했습니다.

이때 짝수면 -1을 해줍니다.

 

n=3은 3/2=1이니까 ㅁx1

n=4는 4/2=2 근데 짝수니까 2-1 해서 ㅁx1

n=5 5/2=2 니까 ㅁx2

n=6 6/2=3 근데 짝수니까 3-1해서 ㅁx2

 

와 같은 규칙을 발견했습니다.

 

그래서 이 코드가 생겼습니다.

int re=0; // 전체 for을 얼마나 반복할건지 정하는 숫자

    if(num%2 == 0) //만약 num이 짝수이면
    {
        re=(num/2)-1; 
    }
    else
    {
        re=(num/2);
    }
 

ㅁ가 얼마나 반복될지는 정했고 그럼 이제 ㅁ를 for문으로 만들어줘야합니다.

 
 

n=5인 배열을 그렸고 for을 어떻게 패턴화 시킬지 그려보았습니다.

이 문제를 풀면서 제일 힘들었던 지점이 이 부분인데 for문을 돌리고 나면 i가 i++되서 빠져나옵니다.

오른쪽 그림을 보면 보통 for안에서 i가 4까지 되서 나온다는 인식하고 있는데 그 이후에 i가 다시 조건문으로 가서 i++이 된 이후 i<5의 조건을 충족하지 못해서 {}안으로 들어가지 못하고 튕긴다는 사실은 잘 인식하지 못하는 것 같습니다.

for(i=0; i<5; i++)라면 i가 4가 될때까지 반복문이 돌아가고 튕기지만 튕길때는 i가 5가 된 상태로 튕깁니다.

 

그럼 코드를 보겠습니다.

for(; j<fornum;j++) // 가로+
        {
            arr[i][j]=count;
            count++;
        } //여기까지 i=0 j=3 count=4
 

이렇게 코드를 짰는데 n=5로 고정되었다는 가정하에 보기 편하게 바꿔보겠습니다.

for(j=0; j<4;j++) // 가로+
        {
            arr[i][j]=count;
            count++;
        } //여기까지 i=0 j=4 count=5
 

이 코드는 처음 가로로 숫자가 움직이는 걸 표현한 것입니다.

j는 반복문을 4번까지 돌리고 튕깁니다.

 
즉 여기보이는 첫번째 빨간선까지 for이 돌아갑니다.

반복문 안에서는 (0,3)까지만 하고 튕기고 튕기면서 좌표는 (0,4)가 됩니다.

그럼 이거를 그대로 다음 for문에서 이어받아서 

        for(i=0; i<4;i++) // 세로+
        {
            arr[i][j]=count;
            count++;
        }//count=8 i=4 j=4
 

arr[i][j] = count;로 (0,4)에 5인 count를 저장해줄 수 있습니다.

왜? for문을 시작할때는 i++가 동작하지 않으니까!

for(i=0; i<4) 이후에 {}가 실행되고 i++를 해주는거니까 (0,4)로 끝난 위의 for문을 그대로 이어받아서 아무런 첨가없이 arr[i][j] = count를 해줄 수 있는 것입니다.

 

 

여기까지 보고 다시 위의 코드로 돌아가면 이해될것....  이해되기를 바랍니다 ^^;

진짜 힘들었습니다.

해설은 사실 안하려고 했음 왜냐면 생각하는게 너무 힘들어서 다시 떠올린다는 생각만 해도 스트레스를 받았기 때문...

정작 문제 푼 시간은 길지 않은데 스트레스는 계속 받았습니다//.......

 


3번 

 

코드 먼저.

첫번째 버전

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

int main(){
    
    int i=0, random;

    printf("난수의 범위 0부터 %d까지 \n", RAND_MAX);

    while(i<5)
    {
        random= rand()%100;
        if(random<=99)
        {
            printf("99이하 난수는 %d\n",random);
            i++;
        }
    }
    

    return 0;
}
 

rand함수의 난수 생성 범위는 아래와 같이 설정할 수 있습니다.

 

 

rand()%a > 0부터 a-1 까지 범위의 난수 생성

rand()%a+4 > 4부터 a+3까지의 범위의 난수 생성

rand()%10+4 이렇게 하면 3부터 9+4인 13까지의 난수가 생성되는 것을 알 수 있습니다.

 

 

두번째 방법인데 시간이 많이 걸리는 방법입니다.

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

int main(){
    
    int i=0, random;

    printf("난수의 범위 0부터 %d까지 \n", RAND_MAX);

    while(i<5)
    {
        random= rand();
        if(random<=99)
        {
            printf("99이하 난수는 %d\n",random);
            i++;
        }
    }
    

    return 0;
}
 

RAND_MAX 자체를 바꾸려고 했지만 그럴 수 없었습니다. 그렇다면 내가 원하는 숫자(99이하)가 나올때까지 반복하는 수 밖에 없겠다 싶어서 i 증감연산자를 반복문 안으로 넣었고 그러려면 while문이 적합하다고 생각했습니다.

 


 

5번

 

코드 먼저.

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

void NameNum(int choice, int random, char ** Name_user, char ** Name_computer)
{
    char * str3 = "보", * str2 ="가위", * str1 = "바위", * str = "잘못입력하셨습니다";

    if(choice==1)
        (*Name_user)=str1;
    else if(choice==2)
        (*Name_user)=str2;
    else if(choice==3)
        (*Name_user)=str3;
    else    
        (*Name_user)=str;
    
     if(random==1)
        (*Name_computer)=str1;
    else if(random==2)
        (*Name_computer)=str2;
    else if(random==3)
        (*Name_computer)=str3;
    else    
        (*Name_computer)=str;

}

int main(){
    srand((int)time(NULL));
    
    int choice=0;
    int loose=0,win=0,nothing=0;
    
    while(loose<1)
    {
        
        int random=rand()%3+1;
        //난수 범위 0~2인데 여기서 +1씩해서 1~3이 범위가 됨
        char * user, * computer;
 
        printf("바위는 1, 가위는 2, 보는 3을 입력하세요 : ");
        scanf("%d",&choice);
        
        NameNum(choice, random, &user, &computer); 
        //1이면 바위 출력 2면 가위출력 3이면 보 출력하는 함수

        if(choice == random)
        {
            printf("당신은 %s 선택, 컴퓨터는 %s 선택, 비겼습니다.\n\n", user, computer);
            nothing++; // 
        }
        else if((choice==1 && random ==2) //내가 바위 컴퓨터가 가위
        ||(choice==2 && random ==3) // 내가 가위 컴퓨터가 보
        ||(choice==3 && random ==1)) //내가 보 컴퓨터가 바위
        {
            printf("당신은 %s 선택, 컴퓨터는 %s 선택, 이겼습니다\n\n", user, computer);
            win++;
        }
        else
        {
            printf("당신은 %s 선택, 컴퓨터는 %s 선택, 졌습니다\n\n", user, computer);
            loose++;
        }
    
    }
    printf("게임의 결과 : %d승, %d무", win,nothing);


    return 0;
}
 

이 코드에서는 NameNum함수에 공을 많이 들였습니다. 사용자가 1을 입력하면 바위를, 2를 입력하면 가위를 3을 입력하면 보를 출력해야했는데 이제까지 익숙하게 사용하던 int가 아닌 char을 사용해야 했고 포인터의 포인터를 자유자재로 사용해야했기에 나름대로의 도전이었다고 할 수 있습니다. 

 

 

main함수에서 if문을 사용해 if choice==1이면 str[]="보"; 이런식으로 사용할 수도 있었지만 바로 전에 char *argv[]를 공부했었기에 char에 포인터의 포인터를 적용해보고 싶어서 따로 NameNum함수를 만들었습니다.

 

 

원리는 간단합니다. 우선 NameNum 함수 안에 포인터 변수인 str1, str2, str3를 선언하여 메모리상에 "가위 바위 보"를 저장하고 이들의 주소값을 포인터 변수인 str1, str2, str3에 저장합니다. <char * str3 = "보", * str2 ="가위", * str1 = "바위", * str = "잘못입력하셨습니다";> 

 

 

다음 main함수에 포인터 변수인 user과 computer을 만들고 이 포인터 변수의 주소를 매개변수로 포인터의 포인터 변수(Name_user, Name_computer)로 전달합니다. 

 

 

그 후 내가 입력한 값이 1이 된다면 포인터의 포인터 변수에 있는 값 (*Name_user) = &user의 주소값에 전에 선언해준 str1, str2, str3 즉 "보, 가위, 바위, 잘못입력하셨습니다"의 주소값을 저장합니다. 

 

 

그러면 이제 (*Name_user)에는 메모리에 저장되어 있는 "보, 가위, 바위, 잘못입력하셨습니다"의 주소값이 저장되게 됩니다. 

 
 
 
 

만약 "보, 가위, 바위, 잘못입력하셨습니다"를 scanf 함수를 사용하여 상수가 아닌 변수로 사용되게 하고 싶다면 char str1[]="보"와 같이 변수형태 문자열을 입력할 수 있는 배열을 사용하는 것이 맞다고 생각합니다.

 

 

내가 char * str="보"와 같이 포인터로 선언한 것은 "보"가 상수형태의 문자열이 되는게 맞다고 생각해서이다. 배열은 상수형태 포인터, 변수형태 문자열이고 포인터는 변수형태 포인터, 상수형태 문자열이니까 말입니다. 

 

 

위의 배열과 포인터가 이해가지 않는 사람은 이 링크를 참조하면 좋을 것 같습니다.

#13 C언어 변수형태/상수형태 배열과 포인터의 차이점과 공통점 (tistory.com)

 


6번

 

 

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

int main()
{
    srand((int)time(NULL));
    int computer[3]={0}, user[3]={0};
    
    for(int i=0;i<3;i++) //0과 9사이의 값을 답으로 저장
    {
        computer[i]=rand()%10; 
    }

    
    printf("\n\n\nSTART GAME!!\n<숫자랑 위치가 다 맞으면 strike 숫자만 맞으면 ball> \n\n");
    
    int z=0,count=0;
    

    while(z<1)//정확히 맞추지 않으면 반복문을 계속 실행
    {
        int strike=0, ball=0; //유저가 입력할때마다 strike와 ball은 새로고침됨

        printf("3개의 숫자 선택 :");
        scanf("%d %d %d",&user[0], &user[1], &user[2]);
        
        for(int i=0;i<3;i++)//strike ball 함수
        {
            
            for(int j=i+1;j<3;j++) //strike와 중복되면 안되기 때문에 j=i+1로 설정
            {
                if(user[i]==computer[j])
                {
                    ball++;
                }
            }
            
            if(user[i]==computer[i]) //숫자랑 위치까지 맞는지 확인하는 ball 함수
            {
                strike++;
            }
        }
         
        if(strike==3)//strike이 3이라면 다 맞췄기 때문에 반복문 나감
        {
            z++; 
        }

        count++;
        printf("%d번째 도전 결과 : %dstrike, %dball!!! \n\n",count,strike,ball);
        
    }
    printf("GAME OVER!!\n\n");

    return 0;
}
 

코딩을 끝내고 게임을 세번 정도 돌려봤는데 처음엔 잘되다가 막판에 이상한 결과가 나타났었습니다.

 

 
 
 
지금보면 당연한 결과인거 같은데 막상 게임을 하고 있을때는 

이거 뭐 어째야 하나... 싶었습니다. 코드를 다 뜯어봐도 이상한게 없는데 어떻게 2strike 1ball이 나오는거지??????라고 생각했습니다. 그도 그럴것이 앞전에 두번째와 세번째 자리는 5와 2로 2strike가 확실했기 때문입니다.

 

 

그러면 제일 앞자리가 1ball인건데 2strike와 1ball이 같이 올수 있나???? 하면서 엄청 당황했었습니다. 이렇게 생각하게 된건 전체를 본게 아니라 딱 저 자리에 대한 ball이라고 생각했기 때문입니다.

 

 

첫째자리에 온 숫자가 1ball이라면 3strike여야 하는거 아닌가 라는 생각에 사로잡혀 있어서 코드를 계속 뜯어봤고 결과는 예상하다시피 2,5와 중복된 숫자라서 2strike 1ball이 나온 것이었습니다...ㅎㅎ(멍청이)

 

무튼 이렇게 도전 프로그래밍3 끝!!!

 

 

반응형

댓글