소개
이 프로젝트에서는 C 프로그래밍 언어를 사용하여 간단한 텍스트 기반 오목 게임을 만들 것입니다. 오목은 두 명의 플레이어가 전략적으로 진행하는 보드 게임으로, 가로, 세로 또는 대각선으로 연속된 다섯 개의 돌을 먼저 놓는 것을 목표로 합니다. 우리는 15x15 게임 보드를 사용하여 이 게임을 개발할 것입니다.
👀 미리보기

🎯 과제
이 프로젝트에서 다음을 배우게 됩니다:
- C 에서 2 차원 배열을 사용하여 체스판을 설계하고 구현하는 방법.
- 게임의 흐름을 제어하는 main 함수를 만드는 방법.
- 게임을 초기화하고, 체스판을 출력하고, 플레이어가 턴을 진행할 수 있도록 하는 함수를 구현하는 방법.
- 게임에서 승리 조건을 확인하는 함수를 개발하는 방법.
- 프로그램을 컴파일하고 실행하는 방법.
🏆 성과
이 프로젝트를 완료하면 다음을 수행할 수 있습니다:
- C 에서 2 차원 배열로 작업할 수 있습니다.
- 함수를 사용하여 게임 흐름을 설계하고 구현할 수 있습니다.
- 게임에서 승리 조건을 확인할 수 있습니다.
- C 프로그램을 컴파일하고 실행할 수 있습니다.
프로젝트 파일 생성
먼저, gomoku.c라는 새 파일을 생성하고 선호하는 코드 편집기에서 엽니다.
cd ~/project
touch gomoku.c
체스판 디자인
'O'와 'X'를 사용하여 체스 말 표현
먼저, 보드 위의 각 위치의 "상황"을 기록하기 위해 체스판 (15 * 15) 이 필요합니다. 따라서 chessboard[16][16] 배열을 정의할 수 있습니다. 왜 [15][15]가 아닐까요? 이렇게 하면 배열의 좌표가 체스판의 행과 열에 정확히 일치하여 이후 코드를 작성하기가 더 쉬워집니다.
#include <stdio.h>
#define N 15
// 배열을 정의하고 각 요소에 초기값 0 을 할당합니다.
int chessboard[N + 1][N + 1] = { 0 };
main 함수
main 함수를 작성하기 전에, 게임의 일반적인 흐름을 간략하게 생각해 봅시다. 먼저, 게임의 메인 인터페이스에 들어가서 시작 버튼을 클릭하여 게임에 진입한 다음, 게임 화면을 표시하고 승패를 결정한 후 게임을 종료합니다. 그렇다면, 바둑과 같은 게임의 흐름은 어떨까요?
우선, 게임의 환영 화면에 들어가서 Y 를 입력하여 게임을 시작하고 (Y 가 아니면 게임 종료), 게임 보드를 표시한 다음 두 플레이어가 번갈아 가며 돌을 놓은 후 승패를 결정합니다 (5 개의 돌이 연속으로 있는지 여부).
// 플레이어 1 의 턴인지 플레이어 2 의 턴인지 추적하는 데 사용되며, 홀수는 플레이어 1 의 턴을 나타내고 짝수는 플레이어 2 의 턴을 나타냅니다.
int whoseTurn = 0;
int main(void)
{
// 게임을 초기화하는 사용자 정의 함수, 즉 환영 화면을 표시하고 게임 보드를 표시합니다.
initGame();
// 이 루프는 두 플레이어가 번갈아 가며 진행합니다.
while (1)
{
// 각 사이클은 1 씩 증가하여 두 사람이 번갈아 가며 진행할 수 있습니다.
whoseTurn++;
// 돌을 놓는 작업을 수행하는 사용자 정의 함수입니다.
playChess();
}
return 0;
}
initGame 함수
이 함수에서 구현하려는 기능은 다음과 같습니다.
- 간단한 환영 화면을 표시합니다.
- 'Y' 입력을 요청하고 입력 후 체스판을 표시합니다.
void initGame(void)
{
char c;
printf("Please input 'y' to enter the game:");
c = getchar();
if ('y' != c && 'Y' != c)
exit(0);
//Clear
system("clear");
//Here again we call a custom function whose function is to print out the chessboard.
printChessboard();
}
initGame 함수에서 exit 및 system 함수를 사용하므로 프로그램 상단에 stdlib.h 헤더 파일을 포함해야 합니다.
#include <stdlib.h>
printChessboard 함수
이 함수의 목표는 다음과 같습니다.
- 행과 열 번호를 출력하고 체스판을 출력합니다.
- 배열 요소의 값이 0 이면 별표 (*) 를 출력하여 해당 위치가 비어 있음을 나타냅니다.
- 배열 요소의 값이 1 이면 플레이어 1 의 돌을 나타내는 검은색 원 (X) 을 출력합니다.
- 배열 요소의 값이 2 이면 플레이어 2 의 돌을 나타내는 빈 원 (O) 을 출력합니다.
void printChessboard(void)
{
int i, j;
for (i = 0; i <= N; i++)
{
for (j = 0; j <= N; j++)
{
if (0 == i) //This prints out the column number.
printf("%3d", j);
else if (j == 0) //Print row number.
printf("%3d", i);
else if (1 == chessboard[i][j])
printf(" X");
else if (2 == chessboard[i][j])
printf(" O");
else
printf(" *");
}
printf("\n");
}
}
playChess 함수
이 함수에서 다음을 수행하고자 합니다.
- 플레이어에게 체스 말을 놓을 위치를 입력하라는 메시지를 표시합니다.
- 현재 플레이어 1 의 차례인 경우, 배열의 해당 요소에 값 1 을 할당합니다.
- 현재 플레이어 2 의 차례인 경우, 배열의 해당 요소에 값 2 를 할당합니다.
- 각 이동 후, 현재 플레이어가 이겼는지 여부를 결정합니다.
void playChess(void)
{
int i, j, winner;
//Determine if it is player 1's turn or player 2's turn, and then assign the value to the corresponding element in the array.
if (1 == whoseTurn % 2)
{
printf("Turn to player 1, please input the position, the format is line number + space number + column number: ");
scanf("%d %d", &i, &j);
chessboard[i][j] = 1;
}
else
{
printf("Turn to player 2, please input the position, the format is line number + space number + column number: ");
scanf("%d %d", &i, &j);
chessboard[i][j] = 2;
}
//Print the board again.
system("clear");
printChessboard(); //This function is called again.
/*
*The following section calls the custom function (the judge function).
*It is used to determine whether the current player has won the move or not.
*/
if (judge(i, j, whoseTurn))
{
if (1 == whoseTurn % 2)
printf("Winner is player 1!\n");
else
printf("Winner is player 2!\n");
}
}
judge 함수
함수 매개변수:
- x: 현재 이동의 행 번호
- y: 현재 이동의 열 번호
반환 값:
- 1 또는 0. 1 은 현재 플레이어가 이동 후 이겼음을 의미합니다.
int judge(int x, int y)
{
int i, j;
int t = 2 - whoseTurn % 2;
const int step[4][2]={{-1,0},{0,-1},{1,1},{1,0}};
for(int i=0;i<4;++i)
{
const int d[2]={-1,1};
int count=1;
for(int j=0;j<2;++j)
for(int k=1;k<=4;++k){
int row=x+k*d[j]*step[i][0];
int col=y+k*d[j]*step[i][1];
if( row>=1 && row<=N && col>=1 && col<=N &&
chessboard[x][y]==chessboard[row][col])
count+=1;
else
break;
}
if(count>=5)
return 1;
}
return 0;
}
judge 함수에는 3 개의 중첩된 for 루프가 있으며, 그 목적은 5 개의 연속된 돌이 있는지 확인하는 것입니다.
5 개의 돌로 이루어진 줄은 가로, 세로 또는 대각선 방향일 수 있습니다. 여기서는 시행착오 방식을 사용하여 가로, 세로 및 대각선 방향으로 연속된 돌을 검색합니다. 예를 들어 보겠습니다.

위 체스판에서 (9, 10) 좌표를 기준으로 5 개의 돌로 이루어진 줄이 있는지 확인하는 알고리즘을 설명합니다.
먼저 (9, 10) 에서 시작하여 5 개의 돌로 이루어진 대각선이 있는지 확인합니다. 과정은 다음과 같습니다.
- (9, 10) 에서 시작하여 왼쪽 위 방향으로 검색합니다. 조건을 만족하는 좌표는 (8, 9), (7, 8), (6, 7) 입니다. (5, 6) 은 조건을 만족하지 않으므로 다음 단계로 넘어갑니다.
- 그런 다음 오른쪽 아래 방향으로 검색하여 조건을 만족하는 유일한 좌표인 (10, 11) 을 찾습니다.
- 직선으로 5 개의 점을 찾았으므로 플레이어 2 가 이깁니다.
대각선 방향에 5 개의 돌로 이루어진 줄이 없으면 세로 및 가로 방향을 확인합니다. 이 중 어느 것도 승리 조건을 만족하지 않으면 현재 플레이어가 이길 수 없음을 의미하며 게임은 계속됩니다.
내 체스 말이 '잡혔습니다'
아무도 눈치채지 못했는지 모르겠지만, 저희 오목 게임에서는 이미 자리가 차지되어 있어도, 자신의 체스 말을 놓을 때 원래 있던 체스 말을 "먹을" 수 있습니다.
이는 playChess 함수를 작성할 때 체스 말을 놓을 위치를 확인하지 않았기 때문입니다. 다음과 같이 코드를 수정할 수 있습니다.
void playChess(void)
{
int i, j, winner;
if (1 == whoseTurn % 2)
{
printf("Turn to player 1, please input the position, the format is line number + space number + column number: ");
scanf("%d %d", &i, &j);
//debug
while(chessboard[i][j] != 0)
{
printf("This position has been occupied, please input the position again: ");
scanf("%d %d",&i, &j);
}
//debug
chessboard[i][j] = 1;
}
else
{
printf("Turn to player 2, please input the position, the format is line number + space number + column number: ");
scanf("%d %d", &i, &j);
//debug
while(chessboard[i][j] != 0)
{
printf("This position has been occupied, please input the position again: ");
scanf("%d %d",&i, &j);
}
//debug
chessboard[i][j] = 2;
}
system("clear");
printChessboard();
if (judge(i, j))
{
if (1 == whoseTurn % 2)
printf("The winner is player 1!\n");
else
printf("The winner is player 2!\n");
}
}
함수에 루프를 추가하여, 해당 위치가 이미 차지되어 있으면 메시지를 표시하고 새로운 입력을 요청합니다.
왜 영원히 이길 수 없을까?
5 개의 돌이 일렬로 놓여 "플레이어 1" 또는 "플레이어 2"가 이겼다는 메시지가 표시된 후, "플레이어 *의 차례입니다. 돌의 위치를 입력하세요..."라는 메시지가 표시됩니다. 짜증나지 않나요? 사실, 코드 한 줄만 추가하면 됩니다!
if (judge(i, j))
{
if (1 == whoseTurn % 2)
{
printf("Winner is player 1!\n");
exit(0); //debug
}
else
{
printf("Winner is player 2!\n");
exit(0); //debug
}
}
}
승리 후 승리한 플레이어를 알리는 것 외에도, exit(0)을 사용하여 게임을 종료합니다.
컴파일 및 실행
gcc 명령을 실행하여 컴파일합니다:
cd ~/project
gcc -o gomoku gomoku.c
./gomoku

요약
축하합니다! C 언어를 사용하여 간단한 오목 게임을 만들었습니다. 플레이어는 15x15 보드에 번갈아 가며 돌을 놓고, 한 플레이어가 연속으로 다섯 개의 돌을 놓으면 프로그램이 승자를 선언합니다. 친구들과 이 텍스트 기반 게임을 즐겨보세요!



