C 언어로 2048 게임 만들기

CBeginner
지금 연습하기

소개

2048 은 같은 숫자를 가진 인접한 타일을 병합하여 2048 타일에 도달하는 것을 목표로 하는 인기 있는 숫자 퍼즐 게임입니다. 이 프로젝트에서는 C 언어로 간단한 2048 게임을 만드는 방법을 배웁니다. 게임 보드 초기화부터 게임 로직 구현, 게임 실행까지 단계별 지침을 제공합니다.

👀 미리보기

2048 Game

🎯 과제

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

  • 프로젝트 파일 생성 방법
  • 게임에 대한 상수를 정의하는 방법
  • 게임 루프를 실행하기 위한 main() 함수 구현 방법
  • 게임 보드 초기화 방법
  • 게임 상태를 확인하는 함수 구현 방법
  • 타일 이동 로직 생성 방법
  • 게임 보드 표시 방법
  • 게임 컴파일 및 테스트 방법

🏆 성과

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

  • 게임을 위한 C 프로그램 생성
  • 게임 보드를 나타내기 위해 배열 사용
  • 타일 병합을 위한 게임 로직 구현
  • 게임 보드 표시
  • 플레이어 입력 처리
  • 게임 오버 및 승리 조건 확인

프로젝트 파일 생성

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

cd ~/project
touch 2048.c

상수 정의

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

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

main() 함수를 작성하기 전에, 몇 가지 상수를 정의하기 위한 기본적인 작업을 완료해 보겠습니다:

#define SIZE 4
#define WIN_SCORE 2048

int board[SIZE][SIZE];
int score = 0;

main 함수

int main() {
    srand(time(NULL));
    init_board();
    print_board();

    while (1) {
        if (is_won()) {
            printf("You won!\n");
            break;
        }

        if (is_full() && !can_move()) {
            printf("Game over!\n");
            break;
        }

        int direction;
        printf("Enter the move direction (0-Up, 1-Down, 2-Left, 3-Right): ");
        scanf("%d", &direction);

        if (move(direction)) {
            // Print the board after the move
            print_board();
        }
    }

    return 0;
}
  • init board(): 이 함수 호출은 게임 보드를 초기화하고, 게임 보드의 모든 셀을 0 으로 설정한 다음, 두 개의 초기 임의 숫자 (2 또는 4) 를 무작위 위치에 생성합니다.
  • print_board(): 이 함수는 현재 점수와 각 셀의 숫자를 포함하여 게임 보드의 현재 상태를 표시하는 데 사용됩니다.
  • while (1): 이것은 게임 종료 조건이 충족될 때까지 게임을 계속 실행하는 무한 루프입니다.

2048 게임의 주요 흐름은 여기에 구현되어 있으며, 게임 보드 초기화, 숫자 블록 이동, 게임 승리 또는 패배 판단, 그리고 게임 진행을 제어하기 위한 플레이어 입력을 기다리는 것을 포함합니다.

게임 보드 초기화

게임 보드를 초기화하기 위해, 보드를 설정하고 두 개의 초기 임의 숫자를 생성하는 함수 init_board를 만들 것입니다.

void init_board() {
    // Initialize the board by setting all cells to 0
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            board[i][j] = 0;
        }
    }

    // Generate two initial random numbers
    for (int k = 0; k < 2; k++) {
        int i = rand() % SIZE;
        int j = rand() % SIZE;
        int value = (rand() % 2 + 1) * 2; // Generate 2 or 4 randomly
        board[i][j] = value;
    }
}

이 함수는 게임 시작 시 게임 보드의 모든 셀을 비우고, 무작위 위치에 두 개의 초기 임의 숫자 블록을 생성하여 플레이어에게 초기 게임 상태를 제공합니다.

게임 상태 확인 함수 구현

플레이어가 이겼는지, 보드가 가득 찼는지, 유효한 이동이 남아 있는지 확인하는 함수가 필요합니다. 다음은 해당 함수들입니다.

int is_full() {
    // Check if the board is full
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == 0) {
                return 0; // Board is not full
            }
        }
    }
    return 1; // Board is full
}

int is_won() {
    // Check if the player has won
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == WIN_SCORE) {
                return 1; // Player has won
            }
        }
    }
    return 0; // Player has not won
}

int can_move() {
    // Check if there are any valid moves left
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == 0) {
                return 1; // There are still empty cells to move
            }
            if (j > 0 && board[i][j] == board[i][j - 1]) {
                return 1; // Can move left
            }
            if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) {
                return 1; // Can move right
            }
            if (i > 0 && board[i][j] == board[i - 1][j]) {
                return 1; // Can move up
            }
            if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) {
                return 1; // Can move down
            }
        }
    }
    return 0; // No valid moves left
}
  • int is_full(): 이 함수는 보드가 가득 찼는지, 즉 모든 셀이 채워져 있는지 확인하는 데 사용됩니다.

  • int is_won(): 이 함수는 플레이어가 이겼는지, 즉 게임에서 승리하기 위한 WIN_SCORE (일반적으로 2048) 값을 가진 셀이 있는지 확인합니다.

  • int can_move(): 이 함수는 게임을 계속 진행할 수 있도록 유효한 이동 단계가 남아 있는지 확인하는 데 사용됩니다.

타일 이동 로직 생성

move 함수에서 타일 이동 로직을 구현합니다. 이 함수는 플레이어의 상, 하, 좌, 우 네 방향으로의 이동을 처리합니다. 또한 이동이 유효한지 확인하고, 점수를 업데이트하며, 새로운 임의의 숫자를 생성합니다.

int move(int dir) {
    int moved = 0;

    // Store the current board state
    int prev_board[SIZE][SIZE];
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            prev_board[i][j] = board[i][j];
        }
    }

    // Move upwards
    if (dir == 0) {
        for (int j = 0; j < SIZE; j++) {
            for (int i = 1; i < SIZE; i++) {
                if (board[i][j] != 0) {
                    int k = i;
                    while (k > 0 && board[k - 1][j] == 0) {
                        board[k - 1][j] = board[k][j];
                        board[k][j] = 0;
                        k--;
                        moved = 1;
                    }
                    if (k > 0 && board[k - 1][j] == board[k][j]) {
                        board[k - 1][j] *= 2;
                        score += board[k - 1][j];
                        board[k][j] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Move downwards
    else if (dir == 1) {
        for (int j = 0; j < SIZE; j++) {
            for (int i = SIZE - 2; i >= 0; i--) {
                if (board[i][j] != 0) {
                    int k = i;
                    while (k < SIZE - 1 && board[k + 1][j] == 0) {
                        board[k + 1][j] = board[k][j];
                        board[k][j] = 0;
                        k++;
                        moved = 1;
                    }
                    if (k < SIZE - 1 && board[k + 1][j] == board[k][j]) {
                        board[k + 1][j] *= 2;
                        score += board[k + 1][j];
                        board[k][j] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Move left
    else if (dir == 2) {
        for (int i = 0; i < SIZE; i++) {
            for (int j = 1; j < SIZE; j++) {
                if (board[i][j] != 0) {
                    int k = j;
                    while (k > 0 && board[i][k - 1] == 0) {
                        board[i][k - 1] = board[i][k];
                        board[i][k] = 0;
                        k--;
                        moved = 1;
                    }
                    if (k > 0 && board[i][k - 1] == board[i][k]) {
                        board[i][k - 1] *= 2;
                        score += board[i][k - 1];
                        board[i][k] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Move right
    else if (dir == 3) {
        for (int i = 0; i < SIZE; i++) {
            for (int j = SIZE - 2; j >= 0; j--) {
                if (board[i][j] != 0) {
                    int k = j;
                    while (k < SIZE - 1 && board[i][k + 1] == 0) {
                        board[i][k + 1] = board[i][k];
                        board[i][k] = 0;
                        k++;
                        moved = 1;
                    }
                    if (k < SIZE - 1 && board[i][k + 1] == board[i][k]) {
                        board[i][k + 1] *= 2;
                        score += board[i][k + 1];
                        board[i][k] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Check if the move was successful
    if (moved) {
        // Generate a new random number
        int i = rand() % SIZE;
        int j = rand() % SIZE;
        while (board[i][j] != 0) {
            i = rand() % SIZE;
            j = rand() % SIZE;
        }
        board[i][j] = (rand() % 2 + 1) * 2; // Generate 2 or 4

        // Print the board after the move
        print_board();
    }

    // Check if the move was successful
    if (moved) {
        return 1;
    } else {
        // If the move failed, restore the previous board state
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                board[i][j] = prev_board[i][j];
            }
        }
        return 0;
    }
}
  • 먼저 이동이 발생했는지 여부를 표시하는 변수 moved를 정의합니다. 초기 값은 0 으로, 이동이 발생하지 않았음을 나타냅니다. 이동 실패 시 이전 상태로 복원할 수 있도록 현재 게임 보드의 상태를 저장하기 위해 prev_board라는 임시 2 차원 배열을 생성합니다.

  • 현재 게임 보드의 상태를 prev_board에 복사하여 이동 실패 시 게임 보드의 현재 상태를 저장합니다.

  • dir 매개변수 값 (0 은 위, 1 은 아래, 2 는 왼쪽, 3 은 오른쪽) 에 따라 해당 이동 연산을 수행합니다.

  • 이동 또는 병합이 발생하면 moved 플래그가 1 로 설정되어 게임 상태가 변경되었음을 나타냅니다. 이동 또는 병합 연산이 성공적으로 수행되면 새로운 임의의 숫자가 생성되어 게임 보드의 빈 공간에 새로운 숫자 블록을 생성하는 데 사용됩니다. 마지막으로, 이동이 발생하면 (moved가 1) 함수는 1 을 반환하여 이동이 성공했음을 나타냅니다. 이동이 실패하면 (이동 또는 병합 연산이 없음) 게임 보드가 이전 상태로 복원되어 0 을 반환하여 이동이 실패했음을 나타냅니다.

게임 보드 표시

게임 보드를 표시하기 위해 터미널을 지우고 보드의 현재 상태를 출력하는 print_board 함수를 생성합니다.

void print_board() {
    // Clear the terminal
    system("clear");

    printf("Score: %d\n", score);

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

여기서의 주요 목적은 터미널 화면을 지운 다음 화면 상단에 현재 점수를 표시하고, 플레이어가 게임 보드의 숫자 블록과 점수를 명확하게 볼 수 있도록 게임 보드의 상태를 화면에 출력하는 것입니다. 이는 플레이어가 게임의 현재 상태를 알 수 있도록 하는 사용자 친화적인 인터페이스를 제공하는 데 도움이 됩니다.

컴파일 및 테스트

터미널에 다음 명령을 입력하여 컴파일하고 실행합니다.

cd ~/project
gcc -o 2048 2048.c
./2048

2048 Game

요약

이 프로젝트에서 C 로 기본적인 2048 게임을 만드는 방법을 배웠습니다. 게임 보드를 초기화하고, 게임 상태를 확인하는 함수를 구현했으며, 타일을 이동하는 로직을 생성하고, 게임 보드를 표시하고, 게임을 실행했습니다. 2048 게임을 즐겁게 플레이하고 더 발전시켜 보세요!

✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습✨ 솔루션 확인 및 연습