C 言語で 2048 ゲームを作成する

CCBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

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つの初期乱数(2または4)を生成します。
  • print_board(): この関数は、ゲームボードの現在の状態を表示するために使用されます。現在のスコアと各セルの数字が含まれます。
  • while (1): これは無限ループで、ゲーム終了条件が満たされるまでゲームを実行し続けます。

ここでは、2048ゲームの主要な流れが実装されています。ゲームボードの初期化、数字ブロックの移動、ゲームの勝敗判定、およびプレイヤーの入力を待ってゲームの進行を制御することが含まれます。

✨ 解答を確認して練習

ゲームボードを初期化する

ゲームボードを初期化するために、ボードをセットアップして2つの初期乱数を生成する 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;
    }
}

この関数の役割は、ゲーム開始時にゲームボード上のすべてのセルを空にし、ランダムな位置に2つの初期乱数ブロックを生成し、プレイヤーに初期のゲーム状態を提供することです。

✨ 解答を確認して練習

ゲーム状態をチェックする関数を実装する

プレイヤーが勝利したか、ボードが満杯か、有効な移動が残っているかをチェックする関数が必要です。以下がそれらの関数です。

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 関数でタイルを移動するロジックを実装します。この関数は、プレイヤーの上、下、左、右の4方向の移動を処理します。また、移動が有効かどうかをチェックし、スコアを更新し、新しい乱数を生成します。

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ゲームを楽しんで、さらに改良してみてください!