C 언어로 오목 게임 만들기

CBeginner
지금 연습하기

소개

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

👀 미리보기

Gomoku Game

🎯 과제

이 프로젝트에서 다음을 배우게 됩니다:

  • C 에서 2 차원 배열을 사용하여 체스판을 설계하고 구현하는 방법.
  • 게임의 흐름을 제어하는 main 함수를 만드는 방법.
  • 게임을 초기화하고, 체스판을 출력하고, 플레이어가 턴을 진행할 수 있도록 하는 함수를 구현하는 방법.
  • 게임에서 승리 조건을 확인하는 함수를 개발하는 방법.
  • 프로그램을 컴파일하고 실행하는 방법.

🏆 성과

이 프로젝트를 완료하면 다음을 수행할 수 있습니다:

  • C 에서 2 차원 배열로 작업할 수 있습니다.
  • 함수를 사용하여 게임 흐름을 설계하고 구현할 수 있습니다.
  • 게임에서 승리 조건을 확인할 수 있습니다.
  • C 프로그램을 컴파일하고 실행할 수 있습니다.
이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 중급 레벨의 실험이며 완료율은 75%입니다.학습자들로부터 80%의 긍정적인 리뷰율을 받았습니다.

프로젝트 파일 생성

먼저, 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 함수에서 exitsystem 함수를 사용하므로 프로그램 상단에 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
Gomoku Game
✨ 솔루션 확인 및 연습

요약

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