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 좌표를 입력하세요:
이번에는 한 번에 두 개가 찍힙니다.
컴퓨터가 바로 상황을 인지하고서 대응을 한 것입니다.
여기까지 배열을 이용하여 여러 가지 문제를 풀어봤는데, 도움이 되셨나요?
다음 포스팅부터는 포인터에 대하여 나갈 것입니다.
긴 글 읽어주신 독자분들께 진심으로 감사드립니다 ~
'c언어' 카테고리의 다른 글
c언어 간접 참조 연산자 알아보기(*, &) (0) | 2022.12.07 |
---|---|
c언어 포인터 맛보기 (0) | 2022.12.07 |
c언어 2차원 배열에 대하여 알아보기 (0) | 2022.12.07 |
c언어 탐색 알고리즘에 대하여 알아보기 (0) | 2022.12.07 |
c언어 정렬 알고리즘에 대하여 알아보기(선택 정렬, 오름차순 정렬, 내림차순 정렬) (0) | 2022.12.06 |
댓글