본문 바로가기
c언어

c언어 배열을 이용하여 여러 가지 문제 풀기(주사위 던지기, 극장 예약 시스템 제작하기, 최솟값 찾기, 영상 처리하기, 틱텍토(tic-tac-toe) 게임 만들기)

by 개발자 L 2022. 12. 7.
반응형

c언어 배열을 이용하여 여러 가지 문제 풀기(주사위 던지기, 극장 예약 시스템 제작하기, 최솟값 찾기, 영상 처리하기, 틱텍토(tic-tac-toe) 게임 만들기)

네 안녕하세요, 이번 포스팅에서는 그 동안 배운 것들을 이용하여 여러 가지 문제들을 풀어보는 시간을 가져보도록 하겠습니다.

보통 우리가 프로그래밍을 할 때 데이터를 하나만 다루는 것이 아니라 여러개를 다루는 경우가 훨씬 많고, 그게 대다수이기 때문에 사실상 배열로 시작하여 배열로 끝난다고 봐도 과언이 아닙니다.

그래서 그만큼 연습이 더 필요한 부분이기도 합니다.

그럼 바로 시작해보도록 하겠습니다.

 

1. 주사위 던지기

제일 처음에 해볼 것은 주사위 던지기 게임을 하여 숫자가 나오는 빈도 수를 구하는 것을 할 것입니다.

여기서는 난수를 발생시키는 함수인 rand() 함수를 써서 작성을 해야 합니다.

확률과 통계를 이용해야 하기 때문입니다,

그러면 바로 코드를 작성을 해보도록 하겠습니다.

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

#define SIZE 6

int main()
{
    int frequencies[SIZE] = { 0 };

    for(int i = 0; i < 10000; i++)
    {
        ++frequencies[rand() % 6];
    }

    printf("======\n");
    printf("면 빈도\n");
    printf("======\n");

    for(int i = 0; i < SIZE; i++)
    {
        printf("%d %d\n", i + 1, frequencies[i]);
    }

    return 0;
}

이렇게 한 번 작성을 해봤습니다.

여기서 조금 특이한 것이 보인다면, 바로 배열 안에다가 rand() 함수를 넣은 것일 거라고 생각합니다.

지금까지는 rand 함수를 밖에다만 썼었는데,

이렇게도 쓸 수가 있습니다.

우리는 배열 자체에 난수를 발생시키는 것을 해야 하기 때문에 이렇게 해야 합니다.

그럼 결과를 한 번 보도록 하겠습니다.

======
면 빈도
======
1 1657
2 1679
3 1656
4 1694
5 1652
6 1662

결과가 잘 나온 것을 볼 수가 있습니다.

각각의 수는 1부터 6까지 나오는 빈도 수를 구한 것이고, 던진 횟수는 10000번입니다.

 

2. 극장 예약 시스템 만들기

이번에는 극장 예약 시스템을 한 번 만들어보도록 하겠습니다.

배열을 이용하게 되면 한 번에 많은 데이터를 다룰 수 있다는 장점이 있기 때문에 이러한 큰 데이터를 다루는 것 역시 가능해집니다.

그리고 이 방법은 무한루프를 활용해야 하기 때문에 while문을 사용하여 루프를 돌릴 겁니다.

그럼 바로 시작해보도록 하겠습니다.

#include <stdio.h>

#define SIZE 10

int main()
{
    char ans1;
    int ans2;
    int seats[SIZE] = {0};

    while(1)
    {
        printf("죄석을 예약하시겠습니까?(y/n)");
        scanf("%c", &ans1);

        if(ans1 == 'n')
        {
            break;
        }

        printf("====================\n");
        printf("1 2 3 4 5 6 7 8 9 10\n");
        printf("====================\n");

        for(int i = 0; i < SIZE; i++)
        {
            printf("%d", seats[i]);
        }

        printf("\n");

        printf("몇 번째 좌석을 예약하시겠습니까?");
        scanf("%d", &ans2);

        if(seats[ans2 - 1] == 0)
        {
            seats[ans2 - 1] = 1;
            printf("예약되었습니다.\n");
        }

        else
        {
            printf("이미 예약된 자리입니다.\n");
        }
    }

    return 0;
}

이렇게 작성을 했습니다.

여기서 ans2-1이라고 썼는데, 이렇게 쓰는 이유가 있습니다.

만일에 ans2만 쓴다면 좌석이 차지가 않습니다.

예약을 해도 예약이 된 자리로 뜨는 것이 아니라, 계속 공석이 됩니다.

그래서 그 부분을 방지하기 위해서 그렇게 했습니다.

그럼 결과를 보도록 하겠습니다.

//좌석 예약을 취소한 경우
죄석을 예약하시겠습니까?(y/n)n

C:\c, c++ folder>

//죄석 예약을 하는 경우
죄석을 예약하시겠습니까?(y/n)y
====================
1 2 3 4 5 6 7 8 9 10
====================
0000000000
몇 번째 좌석을 예약하시겠습니까?1
예약되었습니다.
죄석을 예약하시겠습니까?(y/n)
====================
1 2 3 4 5 6 7 8 9 10
====================
1000000000
몇 번째 좌석을 예약하시겠습니까?2
예약되었습니다.
죄석을 예약하시겠습니까?(y/n)
====================
1 2 3 4 5 6 7 8 9 10
====================
1100000000
몇 번째 좌석을 예약하시겠습니까?1
이미 예약된 자리입니다.
죄석을 예약하시겠습니까?(y/n)
====================
1 2 3 4 5 6 7 8 9 10
====================
1100000000
몇 번째 좌석을 예약하시겠습니까?

이런 식으로 빈 자리를 고르면 예약이 되고, 빈자리가 아니라 이미 예약된 자리를 고르면 이미 예약이 된 자리라며 안된다는 문구가 나오는 것을 확인이 가능하며, n을 누른 경우에 프로그램이 종료가 되는 것을 볼 수가 있습니다.

반응형

 

3. 최솟값 찾기

이번에는 최솟값을 찾는 프로그램을 작성을 해보도록 하겠습니다.

최솟값을 작성하는 프로그램은 일단 배열의 요소를 난수로 초기화를 해주고, 배열의 가장 첫 번째 인덱스를 최솟값으로 하겠다 선언을 한 후, 그 인덱스와 비교를 하여 인덱스의 대소 관계에 따라서 바꿔주면 됩니다.

그럼 바로 작성을 해보도록 하겠습니다.

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

#define SIZE 10

int main()
{
    int min, prices[SIZE] = { 0 };

    printf("-----------------------------\n");
    printf("1 2 3 4 5 6 7 8 9 10\n");
    printf("-----------------------------\n");

    srand((unsigned)time(NULL));

    for(int i = 0; i < SIZE; i++)
    {
        prices[i] = (rand() % 100) + 1;
        printf("%-3d", prices[i]);
    }

    printf("\n\n");

    min = prices[0];

    for(int i = 1; i < SIZE; i++)
    {
        if(prices[i] < min)
        {
            min = prices[i];
        }
    }

    printf("최솟값은 %d 입니다.\n", min);

    return 0;
}

이렇게 작성을 해봤습니다.

여기서 -3d가 눈에 띌텐데요,

이건 좌측으로 정렬을 하는데 3자리로 숫자의 표현 범위를 맞추겠다는 뜻입니다.

여기서 '-'가 없다면 우측 정렬이 됩니다.

그럼 결과를 바로 보도록 하겠습니다.

-----------------------------
1 2 3 4 5 6 7 8 9 10
-----------------------------
1 15 99 96 12 84 85 61 2 92

최솟값은 1 입니다.

결과도 잘 나온 것을 볼 수가 있습니다.

 

4. 영상 처리하기

이번에는 영상을 처리해보도록 하겠습니다.

영상은 크게 두 가지입니다.

  • 정적 영상 : 이미지와 같은 사진들
  • 동적 영상 : 동영상, gif와 같이 움직이는 영상들

이 중에서 우리는 정적 영상을 처리하는 방법을 보도록 하겠습니다.

우리가 아는 이미지는 다 0과 1로 구성이 되어있습니다.

다시 말하면 영상 자체가 이진 코드로 다 이루어져 있다는 것입니다.

그래서 이 영상지도를 '비트맵'이라고 부르며, 데이터베이스에서의 자료 저장 형식은 BLOB(bnary large object, 큰 이진 객체)라고 부릅니다.

그리고 보통 이러한 영상처리는 c++이나 파이썬 등의 객체 지향 언어들이 많이 하고,

c언어와 같은 컴파일 언어는 객체 지향 언어들처럼 클래스로 지정하여 덩어리로 수행하지 않기 때문에 별로 적합하지는 않습니다.

그래도 맛만 보시라고 한 번 보여드리도록 하겠습니다.

#include <stdio.h>

void display(int image[8][16]);
void inverse(int img[8][16]);

int main()
{
    int image[8][16] = 
    {
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
        {1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1},
        {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
        {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1},
        {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
        {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
    };

    printf("변환 전 이미지\n");
    display(image);
    inverse(image);
    printf("\n\n변환 후 이미지\n");
    display(image);

    return 0;
}

void display(int image[8][16])
{
    for(int r = 0; r < 8; r++)
    {
        for(int c = 0; c < 16; c++)
        {
            if(image[r][c] == 0)
            {
                printf("*");
            }

            else
            {
                printf("_");
            }
        }

        printf("\n");
    }
}

void inverse(int img[8][16])
{
    for(int r = 0; r < 8; r++)
    {
        for(int c = 0; c < 16; c++)
        {
            if(img[r][c] == 0)
            {
                img[r][c] = 1;
            }

            else
            {
                img[r][c] = 0;
            }
        }
    }
}

이렇게 작성을 했고요,

결과도 한 번 보도록 하겠습니다.

변환 전 이미지
________________
____*___________
___**___________
___***__**______
__********______
_***********____
_************___
________________


변환 후 이미지
****************
****_***********
***__***********
***___**__******
**________******
*___________****
*____________***
****************

결과도 잘 나온 것을 볼 수가 있습니다.

 

5. 틱텍토(tic-tac-toe) 게임하기

이번에는 틱텍토(tic-tac-toe) 게임을 한 번 해보도록 하겠습니다.

틱텍토 게임은 두 명이 o와 x를 번갈아가며 두는 게임이고, 한 줄 빙고를 먼저 만들어내는 사람이 이기는 게임입니다.

그래서 사람 대 사람 버전과 컴퓨터 대 사람 버전 두 개를 모두 작성을 해보도록 하겠습니다.

먼저 사람 대 사람 버전입니다.

#include <stdio.h>
#define ROWS 3
#define COLS 3

int winCheck(int sum);
int win(int b[][COLS]);
void display(int b[][COLS];)

int main(void)
{
	int board[3][3];
	char turn = 'X'; //처음 turn을 X로 시작

	int r, c;
	int i, j;
	int count;
	int lose;

	for (i = 0; i < ROWS; i++)
		for (j = 0; j < COLS; j++)
			board[i][j] = 0;

	count = 0;
	display(board);

	do
	{
		printf("Player %c(행 열):", turn);
		scanf("%d %d", &r, &c);
		printf("\n");

		if (board[r][c]) //이미 채워져있으면 다시 입력 프롬프트 실행
			continue;		//코드 이전부분으로 돌아감
			
		count++; //심볼이 입력된 횟수 증가

		if (turn == 'X') {

			board[r][c] = 1;
			turn = 'O'; //턴을 바꿔줌
		}
		else {
			board[r][c] = -1;
			turn = 'X';
		}
		display(board);

	} while ((lose = !win(board)) && count < 9); //count가 9 미만이면 3x3 판 기준으로 다 채워지지 않은 상태

	if (lose && count == 9)
		printf("Nobody wins!\n");
}

int winCheck(int sum)
{
	if (sum == 3)
	{
		printf("Player X가 이겼습니다!\n");
		return 1;
	}
	else if (sum == -3)
	{
		printf("Player O가 이겼습니다!\n");
		return 1;
	}

	return 0; //아무도 안이김
}

int win(int b[][COLS])
{
	int i, j;
	int sum;
	// 가로로 세개씩 체크
	for (i = 0; i < ROWS; i++)
	{
		sum = 0; //각각 체크해야하므로 매번 초기화
		for (j = 0; j < COLS; j++)
			sum += b[i][j];

		if (winCheck(sum)) return 1;
	}

	// 세로로 세개씩 체크
	for (j = 0; j< COLS; j++)
	{
		sum = 0;
		for (i = 0; i < ROWS; i++)
			sum += b[i][j];
		if (winCheck(sum)) return 1;
	}
	// 대각선 체크
	sum = 0;
	for (i = 0; i < ROWS; i++)
	{
		sum += b[i][i];
	}
	if (winCheck(sum)) return 1;

	// 역대각선 체크
	sum = 0;
	for (i = 0; i < ROWS; i++) 
	{
		sum += b[i][ROWS-1- i];
	}
	if (winCheck(sum)) return 1;

	return 0;
}

void display(int b[][COLS])
{
	char ch;
	int i, j;

	printf("    0 1 2\n");
	printf("   ------\n");
	for (i = 0; i < COLS; i++) 
	{
		printf("%d |", i);
		for (j = 0; j < COLS; j++) 
		{
			if (b[i][j] == 1)
				ch = 'X';
			else if (b[i][j] == -1)
				ch = 'O';
			else
				ch = ' ';
			printf(" %c", ch);
		}
		printf("\n");
	}
}

이렇게 작성을 합니다.

틱텍토 게임 자체가 알고리즘이기 때문에 그냥 보시면 됩니다.

설명을 한다기보다는 알고리즘 교육이라 생각하시면 됩니다.

그럼 결과도 바로 보도록 하겠습니다.

    0 1 2
   ------
0 |
1 |
2 |
Player X(행 열):1 2

    0 1 2
   ------
0 |
1 |     X
2 |
Player O(행 열):2 2

    0 1 2
   ------
0 |
1 |     X
2 |     O
Player X(행 열):

이렇게 하나씩 결과가 나옵니다.

이어서 컴퓨터와 대전하는 버전도 작성을 해보도록 하겠습니다.

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

char matrix[3][3]; /* 틱택토 구조 */

char check(void);
void init_matrix(void);
void get_player_move(void);
void get_computer_move(void);
void disp_matrix(void);

int main()
{
    char done;
    
    printf("틱텍토 게임에 오신 것을 환영합니다.\n");
    printf("컴퓨터와 대결을 합니다.\n");

    done = ' ';
    init_matrix();

    do 
    {
        disp_matrix();
        get_player_move();
        done = check(); /* 승자 보여주기 */

        if(done != ' ')
        {
            break;
        } /* 승자가 나옴*/

        get_computer_move();
        done = check(); /* 승자가 있다면 출력 */
    }while(done == ' ');

    if(done == 'X')
    { 
        printf("당신이 이겼습니다.!\n");
    }
    else 
    {
        printf("텀퓨터가 이겼습니다!!!!\n");
    }

    disp_matrix(); /* 마지막 위치 보여주기 */

    return 0;
}

/* 틱택토 초기화 */
void init_matrix(void) 
{
    int i, j;

    for(i = 0; i < 3; i++)
    {
        for(j = 0; j < 3; j++)
        {
            matrix[i][j] = ' ';
        }
    }
}

/* 플레이어가 놓을 자리 가져오고 확인하기 */
void get_player_move(void) 
{
    int x, y;

    printf("X,Y 좌표를 입력하세요: ");
    scanf("%d%*c%d", & x, & y);

    x--;
    y--;

    if(matrix[x][y] != ' ') 
    {
        printf("Invalid move, try again.\n");
        get_player_move();
    }

    else
    {
        matrix[x][y] = 'X';
    }
}

/* 컴퓨터가 놓을 자리 가져오기 */
void get_computer_move(void)
{
    int i, j;

    for(i = 0; i < 3; i++) 
    {
        for (j = 0; j < 3; j++)
        {
            if (matrix[i][j] == ' ') 
            {
                break;
            }
        }
    if(matrix[i][j] == ' ') 
    {
        break;
    }
    }

    if(i * j == 9) 
        {
            printf("비겼습니다\n");
            exit(0);
        }

    else
    {
        matrix[i][j] = 'O';
    }
}

/* 틱택토 화면 보이기. */
void disp_matrix(void)
{
    int t;

    for(t = 0; t < 3; t++) 
    {
        printf(" %c | %c | %c ", matrix[t][0],  
        matrix[t][1], matrix[t][2]);
        if (t != 2) printf("\n---|---|---\n");
    }

    printf("\n");
}

/* 승자가 있는지 체크. */
char check(void)
{
    int i;

    for (i = 0; i < 3; i++) /* 행 체크 */
    {
        if (matrix[i][0] == matrix[i][1] && matrix[i][0] == matrix[i][2])
        {
            return matrix[i][0];
        }
    } 

    for(i = 0; i < 3; i++) /* 열 체크 */
    {
        if (matrix[0][i] == matrix[1][i] && matrix[0][i] == matrix[2][i])
        {
            return matrix[0][i];
        }
    }

  /* 대각행렬 태스트 */
    if(matrix[0][0] == matrix[1][1] && matrix[1][1] == matrix[2][2])
    {
        return matrix[0][0];
    }

    if(matrix[0][2] == matrix[1][1] && matrix[1][1] == matrix[2][0])
    {
        return matrix[0][2];
    }

    return ' ';
}

이렇게 쓸 수 있습니다.

제가 이번에는 변수를 좀 다르게 썼는데,

행렬을 영어로 matrix라고 하기 때문에 이렇게도 많이 쓴다는 것을 보여드리고 싶었습니다.

그럼 결과도 바로 보도록 하겠습니다.

컴퓨터와 대결을 합니다.
   |   |
---|---|---
   |   |
---|---|---
   |   |
X,Y 좌표를 입력하세요: 1 2
 O | X |   
---|---|---
   |   |
---|---|---
   |   |   
X,Y 좌표를 입력하세요:

이번에는 한 번에 두 개가 찍힙니다.

컴퓨터가 바로 상황을 인지하고서 대응을 한 것입니다.

 

여기까지 배열을 이용하여 여러 가지 문제를 풀어봤는데, 도움이 되셨나요?

다음 포스팅부터는 포인터에 대하여 나갈 것입니다.

긴 글 읽어주신 독자분들께 진심으로 감사드립니다 ~

반응형

댓글