본문 바로가기

programming study/C

[나도코딩]C 프로그래밍 - 입문부터 게임 개발까지 (7)(2020.12.29)

본 내용은 해당 강의 토대로 작성


다차원 배열

다차원 배열(Multidimensional Array)은 1차원 이상의 배열을 말한다. 가지는 차원 수 만큼 괄호를 추가한다.

#include <stdio.h>

int main(void)
{
  int i ; //메모리 공간 하나
  int arr[5]; //1차원 배열
  int arr2[2][5]; //2차원 배열
  int arr3[4][2]; //2차원 배열
  int arr4[3][3][3]; //3차원 배열
  return 0;
}

다차원 배열 좌표

위의 배열들을 예시로 다차원 배열 접근은 어떻게 하는지 알아보자.

int arr[2][5]인 2차원 배열의 경우 메모리 공간이 2행 5열로 생긴다. 각각의 좌표는 아래와 같다.

□□□□□
□□□□□
[0,0][0,1][0,2][0,3][0,4] 
[1,0][1,1][1,2][1,3][1,4]

int arr[3][3][3]인 경우 메모리 공간에 3x3x3으로 생긴다.

□□□ □□□ □□□
□□□ □□□ □□□
□□□ □□□ □□□
[0,0,0][0,0,1][0,0,2] [1,0,0][1,0,1][1,0,2] [2,0,0][2,0,1][2,0,2]
[0,1,0][0,1,1][0,1,2] [1,1,0][1,1,1][1,1,2] [2,1,0][2,1,1][2,1,2] 
[0,2,0][0,2,1][0,2,2] [1,2,0][1,2,1][1,2,2] [2,2,0][2,2,1][2,2,2]

다차원 배열 선언

2차원 배열의 선언은 아래와 같다.

#include <stdio.h>
int main(void)
{
  int arr[2][5]={  //2행 5열 배열
    {1,2,3,4,5},
    {1,2,3,4,5}
  };

  int arr[4][2]={  //4행 2열 배열
    {1,2},
    {1,2},
    {1,2},
    {1,2}
  };
  return 0;
}

3차원 배열의 선언은 아래와 같다.

#include <stdio.h>
int main(void)
{
  int arr[3][3][3]={ //3x3x3 배열
    {
      {1,2,3},
      {1,2,3},
      {1,2,3}
    },
    {
      {1,2,3},
      {1,2,3},
      {1,2,3}
    },
    {
      {1,2,3},
      {1,2,3},
      {1,2,3}}
  };
  return 0;
}

다차원 배열 사용

다차원 배열을 for문을 사용해서 출력할 수 있다.

2차원 배열은 아래처럼 할 수 있다.

#include <stdio.h>
int main(void)
{
  int arr3[4][2]={
    {1,2},
    {3,4},
    {5,6},
    {7,8}
  };
  for (int i=0; i<4;i++)
  {
    for (int j=0; j<2;j++)
    {
      printf("2차원배열 (%d,%d)의 값 : %d\n", i, j, arr3[i][j]); //각 좌표와 배열값까지 출력
    }
    printf("\n");
  }
  return 0;
}

스크린샷 2020-12-29 오후 2.42.41

3차원 배열은 for를 세 번 쓴다. 차원의 수 == for문의 수 라고 생각하면 된다.

#include <stdio.h>
int main(void)
{
  int arr4[3][3][3]={
    {
      {1,2,3},
      {4,5,6},
      {7,8,9}
    },
    {
      {11,12,13},
      {14,15,16},
      {17,18,19}
    },
    {
      {21,22,23},
      {24,25,26},
      {27,28,29}}
  };
  for (int i=0; i<3;i++)
  {
    for (int j=0; j<3;j++)
    {
      for (int k=0; k<3; k++)
      {
        printf("3차원배열 (%d,%d,%d)의 값 : %d\n", i, j, k, arr4[i][j][k]); //각 좌표와 배열값까지 출력
      }
      printf("\n");
    }
    printf("\n");
  }
  return 0;
}

스크린샷 2020-12-29 오후 2.52.06

동물뒤집기

2차원 배열을 사용해서, 동물 카드를 맞추는 게임을 만든다.

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

//10마리의 서로 다른 동물 (각 카드 2장씩)
//사용자로부터 2개의 입력값 -> 같은 동물 찾으면 카드 뒤집기
//모든 동물 쌍을 찾으면 게임 종료
//총 실패 횟수 알려주기

int arrayAnimal[4][5];   //4행 5열의 동물 카드 배열 선언
int checkAnimal[4][5];   //뒤집혔는지 여부 확인
void initAnimalArray();  //동물 배열 함수
char * strAnimal[10];    //10개의 공간 배열을 만듦. 들어갈 데이터는 포인터(주소)
void initAnimalName();   //동물 이름 정하기
void shuffleAnimal();    //동물들을 섞음
int getEmptyPosition();  //빈공간을 반환하는 함수
int conv_pos_x(int x);   //x좌표 변환
int conv_pos_y(int y);   //y좌표 변환
void printAnimals();     //동물의 위치(정답) 표시
void printQuestion();    //질문 출력
int foundAllAnimals();   //모든 동물을 찾았는지 확인


int main(void)
{
  srand(time(NULL));  //카드를 랜덤으로 뽑으므로

  initAnimalArray();
  initAnimalName();

  shuffleAnimal();

  int failCount = 0; //실패 횟수
  while(1)
  {
    int select1 = 0; //사용자가 선택한 처음 수
    int select2 = 0; //사용자가 선택한 두번째 수

    printAnimals(); //동물 위치 출력
    printQuestion(); //문제 출력 (카드 상태)
    printf("뒤집을 카드를 2개 고르세요 : ");
    scanf("%d %d", &select1, &select2);

    if (select1 == select2) //같은 카드 선택 시 무효
      continue;
    //좌표에 해당하는 카드를 뒤집어 보고 같은지 안 같은지 확인

    //정수 좌표를 (x,y)로 변환
    int firstSelect_x = conv_pos_x(select1);
    int firstSelect_y = conv_pos_y(select1);

    int secondSelect_x = conv_pos_x(select2);
    int secondSelect_y = conv_pos_y(select2);

    //카드가 뒤집히지 않았는지, 두 동물이 같은지 확인
    if ((checkAnimal[firstSelect_x][firstSelect_y]==0
         && checkAnimal[secondSelect_x][secondSelect_y]==0)
        && (arrayAnimal[firstSelect_x][firstSelect_y]==arrayAnimal[secondSelect_x][secondSelect_y])
       )
    {
      printf("\n\n빙고! : %s 발견\n\n", strAnimal[arrayAnimal[firstSelect_x][firstSelect_y]]);
      checkAnimal[firstSelect_x][firstSelect_y] =1;
      checkAnimal[secondSelect_x][secondSelect_y] =1;
    }
    //다른 동물인 경우
    else
    {
      printf("땡! (틀렸거나, 이미 뒤집힌 카드입니다)\n");
      printf("%d : %s\n", select1,strAnimal[arrayAnimal[firstSelect_x][firstSelect_y]]);
      printf("%d : %s\n", select2,strAnimal[arrayAnimal[secondSelect_x][secondSelect_y]]);
      printf("\n\n");

      failCount++;
    }
    //모든 동물을 찾았는지 여부, 1: 참, 0: 거짓
    if (foundAllAnimals() ==1)
    {
      printf("\n\n 축하합니다! 모든 동물을 다 찾았네요\n");
      printf("지금까지 총 %d번 실수했습니다\n", failCount);
      break;
    }
  }
  return 0;
}

void initAnimalArray()
{
  for (int i=0; i<4; i++)
  {
    for (int j=0; j<5; j++)
    {
      arrayAnimal[i][j] = -1; //4행 5열의 동물카드 배열에 각 -1입력
    }
  }

}

void initAnimalName()
{
  strAnimal[0] = " 원숭이 ";
  strAnimal[1] = " 하 마 ";
  strAnimal[2] = " 강아지 ";
  strAnimal[3] = " 고양이 ";
  strAnimal[4] = " 돼 지 ";

  strAnimal[5] = " 코끼리 ";
  strAnimal[6] = " 기 린 ";
  strAnimal[7] = " 낙 타 ";
  strAnimal[8] = " 타 조 ";
  strAnimal[9] = " 호랑이 ";
}
void shuffleAnimal() //20장의 카드에 10마리의 동물이 2번씩 표시
{
  for(int i=0; i < 10; i++) //10종류의 동물
  {
    for(int j=0; j<2; j++) //2마리씩
    {
      int pos = getEmptyPosition();
      int x = conv_pos_x(pos);
      int y = conv_pos_y(pos);

      arrayAnimal [x][y] = i;
    }

  }
}
//좌표에서 빈 공간 찾기
int getEmptyPosition()
{
  while(1)
  {
    int randPos = rand() %20; //0~19사이의 숫자 반환
    //19면 -> (3,4)로 변환해야함
    int x = conv_pos_x(randPos);
    int y = conv_pos_y(randPos);

    if(arrayAnimal[x][y] == -1) //자리가 -1이면 초기값이므로 빈 공간
    {
      return randPos; //지금 위치를 반환
    }
  }
  return 0;
}
int conv_pos_x(int x)
{
  return x/5; //x를 5로 나눈 몫
}
int conv_pos_y(int y)
{
  return y % 5; //y를 5로 나눈 나머지 값
}
void printAnimals()
{
  printf("\n==========================================\n\n");
  for (int i=0; i<4; i++)
  {
    for(int j=0; j<5; j++)
    {
      printf("%8s", strAnimal[arrayAnimal[i][j]]);
    }
    printf("\n");
  }
  printf("\n==========================================\n\n");
}
void printQuestion()
{
  printf("\n\n(문제)\n");
  int seq = 0; //카드를 일렬로 두었을 때의 배열(번호) 0~19


  for(int i = 0; i<4; i++)
  {
    for(int j=0; j<5; j++)
    {
      //정답 시, 카드를 뒤집어 동물 이름 보여주기
      if(checkAnimal[i][j] != 0)
      {
        printf("%8s", strAnimal[arrayAnimal[i][j]]);
      }
      //오답 시, 뒷면 -> 위치를 나타내는 숫자
      else
      {
        printf("%8d", seq);
      }
      seq++;
    }
    printf("\n");
  }
}
int foundAllAnimals()
{
  for(int i=0; i<4; i++)
  {
    for(int j=0; j<5; j++)
    {
      if(checkAnimal[i][j]==0) //게임이 끝나지 않은 경우
        return 0;

    }
  }
  return 1; //모두 다 찾음
}

스크린샷 2020-12-29 오후 5.16.13

느낀점

2차원 배열은 알고리즘 공부하면서 배웠지만, 다시 한 번 개념을 알 수 있었다.

프로젝트는 굉장히 재밌었다. 역시나 처음 실행했을 때는 각종 오류들이 발생했지만, 혼자의 힘으로 문법오류를 찾아냈다. 그 다음에는 문제 출력시 글자끼리 겹치는 것이 보기 좋지 않아서 글자의 앞뒤에 공백을 넣고 두자리인 글자에는 사이에 공백을 하나 더 넣어 모든 글자의 수를 5글자로 맞췄고 그결과, 출력이 깔끔하게 되었다.

그 다음의 오류는 오답시, 정답 처리가 되는 문제가 있었다. 대답을 받아서 확인하는 과정에서 첫번째 대답과 두번째 대답을 비교해야하는데 첫번째 대답끼리 비교했기 때문에 무조건 정답처리가 났었던 것이었다.

솔직히, 지금까지 했던 프로젝트중에 제일 길어서 이걸 어떻게 다 찾지라는 막막함이 있었는데 10분이내로 해결했다. 한 달전만해도 간단한 코드에서 우왕좌왕하면서 두 시간 가까이 헤맸던 것과 비교하면 장족의 발전이다!!

약간의 노하우라면, 오류가 발생하면 처음에는 간단한 문법적 오류를 찾아낸다. 세미콜론과 괄호와 같은 것들. 그리고, 논리적인 문제들은 내가 코드를 입력했을 때 어떤 부분에서 그 기능을 작동하게 했는지 떠올리고 그 부분으로 가서 하나하나 분석해본다.

어제의 프로젝트는 잘 안되어서 섭섭했지만 오늘은 굉장히 만족스럽게 끝낼 수 있어서 성취감이 느껴진다!