C 로 간단한 틱택토 게임 만들기

CBeginner
지금 연습하기

소개

이 프로젝트에서는 C 언어로 간단한 틱택토 (Tic-Tac-Toe) 게임을 만드는 방법을 배웁니다. 이 게임은 3x3 격자에서 두 명의 플레이어가 번갈아 가며 공간을 표시하는 방식으로 진행됩니다. 가로, 세로 또는 대각선으로 자신의 표시를 세 개 먼저 만드는 플레이어가 승리합니다. 모든 공간이 채워졌지만 어느 플레이어도 세 개의 표시를 연속으로 만들지 못하면 게임은 무승부로 끝납니다.

👀 미리보기

Tic Tac Toe game preview

🎯 과제

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

  • 게임 보드를 생성하고 빈 공간으로 초기화하는 방법.
  • 화면을 지우고, 게임 보드를 표시하고, 게임이 종료되었는지 확인하는 함수를 구현하는 방법.
  • 게임의 승자를 결정하는 방법.
  • 플레이어가 턴을 하고 게임과 상호 작용할 수 있도록 메인 게임 루프를 구현하는 방법.

🏆 성과

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

  • C 언어에서 배열을 생성하고 조작할 수 있습니다.
  • 루프와 조건문을 사용하여 게임 로직을 구현할 수 있습니다.
  • 명령줄 인터페이스를 통해 사용자와 상호 작용할 수 있습니다.
  • 코드의 모듈성과 가독성을 향상시키기 위해 코드를 함수로 구성할 수 있습니다.

프로젝트 파일 생성

먼저, tictactoe.c라는 새 파일을 생성하고 선호하는 코드 편집기에서 엽니다.

cd ~/project
touch tictactoe.c
✨ 솔루션 확인 및 연습

상수 정의

이제 C 코드를 작성해야 합니다. 첫 번째 단계는 헤더 파일을 포함하는 것입니다:

#include <stdio.h>
#include <stdbool.h>

보드 크기를 선언합니다:

char board[3][3];
✨ 솔루션 확인 및 연습

게임 보드 초기화

빈 공간으로 게임 보드를 초기화합니다.

void initializeBoard() {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            board[i][j] = ' ';
        }
    }
}

첫 번째 for는 외부 루프로, 변수 i를 사용하여 0부터 2까지 반복하며, 게임 보드의 행 수를 나타냅니다. 두 번째 for는 내부 루프로, 변수 j를 사용하여 0부터 2까지 반복하며, 차례대로 게임 보드의 열 수를 나타냅니다.

board[i][j] = ' ' 내부 루프에서, 게임 보드의 행 i와 열 j에 있는 사각형의 내용을 공백 문자 (' ')로 설정합니다. 이는 전체 게임 보드가 비어있는 상태, 즉 보드에 아무도 플레이하지 않은 상태로 초기화됨을 의미합니다.

✨ 솔루션 확인 및 연습

각 턴 후 화면 지우기

각 턴 후에 화면을 지우는 함수를 구현합니다.

void clearScreen() {
    printf("\033[H\033[J");
}

clearScreen 함수는 주로 각 턴이 끝날 때 이전 게임 상태와 출력을 지우기 위해 터미널 또는 콘솔 화면의 내용을 지우는 데 사용됩니다. 이는 화면의 텍스트 내용을 지우는 특수 이스케이프 시퀀스를 표준 출력 스트림 (일반적으로 터미널 창) 에 출력하여 수행됩니다.

printf("\033[H\033[J")ANSI 이스케이프 시퀀스를 사용합니다:

  • \033ASCII 이스케이프 문자의 8 진수 표현으로, 시퀀스의 시작을 나타냅니다.
  • [H는 커서를 화면의 왼쪽 상단 모서리로 이동하는 것을 나타내며, 화면의 첫 번째 행과 첫 번째 열로 커서를 위치시키는 것과 같습니다.
  • [J는 화면 지우기를 의미합니다. 커서 위치 이후의 모든 텍스트 내용, 즉 이전 게임 상태와 출력을 지웁니다.

이는 clearScreen 함수가 호출될 때마다 다음 게임 세션 전에 새로운 게임 상태를 표시하기 위해 현재 터미널 또는 콘솔 화면의 내용을 지워 더 깔끔한 인터페이스를 제공함을 의미합니다.

✨ 솔루션 확인 및 연습

게임 보드 표시

현재 게임 보드의 상태를 화면에 시각적으로 표시하는 printBoard 함수를 생성하여 플레이어가 보드 위의 조각 위치를 이해할 수 있도록 합니다. 이는 틱택토 게임에서 게임의 상태를 보여주기 위해 사용되는 게임의 일부입니다.

void printBoard() {
    printf("\n");
    printf("  1   2   3\n");
    printf("1 %c | %c | %c\n", board[0][0], board[0][1], board[0][2]);
    printf("  ---------\n");
    printf("2 %c | %c | %c\n", board[1][0], board[1][1], board[1][2]);
    printf("  ---------\n");
    printf("3 %c | %c | %c\n", board[2][0], board[2][1], board[2][2]);
}
✨ 솔루션 확인 및 연습

게임 종료 여부 확인

틱택토 게임이 종료되었는지 여부를 결정하고 부울 값 (true 또는 false) 을 반환하도록 정의된 isGameOver 함수를 구현합니다.

bool isGameOver() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return true;
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return true;
        }
    }

    // Check diagonals
    if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return true;
    }
    if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return true;
    }

    // Check for a draw
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == ' ') {
                return false;
            }
        }
    }

    return true;
}

행에 승자가 있는지 확인: 행의 세 셀이 모두 동일한 플레이어에 의해 점유된 경우 승자가 있으며 함수는 true를 반환합니다.

열에 승자가 있는지 확인: 열의 세 셀이 모두 동일한 플레이어에 의해 점유된 경우 승자가 있으며 함수는 true를 반환합니다.

대각선에 승자가 있는지 확인: 대각선상의 세 칸이 모두 동일한 플레이어에 의해 점유된 경우 승자가 있으며 함수는 true를 반환합니다.

무승부 확인: 마지막으로, 함수는 모든 셀이 점유되었지만 승자가 없는지 확인합니다. 이 경우 게임은 무승부이며 함수는 true를 반환합니다.

✨ 솔루션 확인 및 연습

승자 결정

getWinner라는 함수가 정의되어 있으며, 이 함수의 목적은 틱택토 게임에서 승자가 있는지 확인하고 승자의 플래그 (X 또는 O) 를 반환하거나, 승자가 없는 경우 공백 (' ')을 반환하는 것입니다.

char getWinner() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return board[i][0];
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return board[0][j];
        }
    }

    // Check diagonals
    if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return board[0][0];
    }
    if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return board[0][2];
    }

    return ' '; // No winner
}
✨ 솔루션 확인 및 연습

메인 게임 루프 구현

플레이어가 턴을 진행하고 게임과 상호 작용할 수 있도록 메인 게임 루프를 작성합니다.

int main() {
    initializeBoard();
    int currentPlayer = 1;

    while (1) {
        clearScreen();
        printf("Current board state:\n");
        printBoard();

        int row, col;
        printf("Player %d, please enter a row and column (e.g., 1 2):", currentPlayer);

        while (scanf("%d %d", &row, &col) != 2) {
            printf("Invalid input, please try again: ");
            while (getchar() != '\n');
        }

        if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1] != ' ') {
            printf("Invalid move, please try again.\n");
        } else {
            if (currentPlayer == 1) {
                board[row - 1][col - 1] = 'X';
                currentPlayer = 2;
            } else {
                board[row - 1][col - 1] = 'O';
                currentPlayer = 1;
            }
        }

        if (isGameOver()) {
            clearScreen();
            printf("Game over!\n");
            printBoard();
            char winner = getWinner();
            if (winner != ' ') {
                printf("Player %c wins!\n", winner);
            } else {
                printf("It's a draw!\n");
            }
            break;
        }
    }

    return 0;
}

initializeBoard 함수를 호출하여 게임 보드를 초기화하고 모든 칸을 공백으로 설정합니다.

현재 턴이 누구인지 추적하기 위해 정수 변수 currentPlayer를 생성합니다. 초기 설정은 1 이며, 플레이어 1 을 나타냅니다.

while (1) 메인 루프에서:

  • clearScreen 함수를 호출하여 화면을 지워 각 턴 후에 디스플레이가 새로 고쳐지도록 합니다.
  • printBoard 함수를 호출하여 현재 게임 보드를 출력합니다.

플레이어 입력: 현재 플레이어에게 scanf 함수를 통해 행 및 열 좌표를 입력하도록 요청합니다 (예: Player 1, please enter a row and column (e.g., 1 2):). 입력이 유효하지 않은 경우 (두 개의 정수가 아닌 경우) Invalid input, please try again: 메시지가 표시되고 유효한 입력이 얻어질 때까지 입력 버퍼가 지워집니다.

입력 유효성 검사: 그런 다음 입력된 행 및 열 좌표가 유효한지 (1 에서 3 까지의 범위) 와 선택한 위치가 공백인지 확인합니다. 입력이 유효하지 않거나 선택한 위치가 이미 점유된 경우 Invalid move, please try again. 메시지가 표시되고 플레이어에게 다시 입력하라는 메시지가 표시됩니다.

체스: 입력이 유효하면 코드는 현재 플레이어 (1 또는 2) 에 따라 게임 보드의 해당 위치에 X 또는 O를 배치합니다.

게임 종료 감지: 그런 다음 코드는 isGameOver() 함수를 호출하여 게임이 종료되었는지 확인합니다. 게임이 종료되면 화면이 지워지고 게임이 종료되었다는 메시지가 표시되며, 승자 (있는 경우) 또는 무승부가 포함됩니다.

게임 결과: 승자가 있는 경우 코드는 Player X wins! 또는 Player O wins!를 표시합니다. 승자가 누구인지에 따라 다릅니다. 승자가 없는 경우 코드는 It's a draw!를 표시하여 무승부임을 나타냅니다.

✨ 솔루션 확인 및 연습

프로젝트 컴파일 및 실행

gcc 명령을 실행하여 컴파일합니다.

cd ~/project
gcc -o tictactoe tictactoe.c
./tictactoe
Tic Tac Toe game demo
✨ 솔루션 확인 및 연습

요약

축하합니다! C 로 간단한 틱택토 게임을 성공적으로 만들었습니다. 플레이어는 3x3 그리드에서 턴을 번갈아 가며 자신의 위치를 표시할 수 있습니다. 게임은 각 턴 후에 승자 또는 무승부를 확인하고 그에 따라 결과를 표시합니다. 이 간단한 프로젝트는 C 프로그래밍의 기본 개념을 보여주며, 더 복잡한 게임 개발의 시작점으로 사용할 수 있습니다.