C 言語で五目並べゲームを作成する

CCBeginner
今すぐ練習

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

はじめに

このプロジェクトでは、C言語を使って簡単なテキストベースの五目並べゲームを作成します。五目並べは2人用の戦略盤ゲームで、横、縦、斜めのいずれかで5個の石が連続して並ぶことが目的です。このゲームは 15x15 の盤面を使って開発します。

👀 プレビュー

五目並べゲーム

🎯 タスク

このプロジェクトでは、以下のことを学びます。

  • C言語で二次元配列を使ってチェス盤を設計・実装する方法
  • ゲームの流れを制御するメイン関数を作成する方法
  • ゲームを初期化し、チェス盤を表示し、プレイヤーに手番を回す機能を実装する方法
  • ゲームで勝利条件をチェックする関数を開発する方法
  • プログラムをコンパイルして実行する方法

🏆 成果

このプロジェクトを完了すると、以下のことができるようになります。

  • C言語の二次元配列を扱うことができる
  • 関数を使ってゲームの流れを設計・実装することができる
  • ゲームで勝利条件をチェックすることができる
  • 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以外を入力するとゲームを終了します)。ゲーム盤を表示し、2人のプレイヤーが交互に駒を置き、次に勝敗を判定します(5個の駒が連続して並んでいるかどうか)。

// プレイヤー1の番かプレイヤー2の番かを追跡するために使用され、奇数がプレイヤー1の番を表し、偶数がプレイヤー2の番を表します。
int whoseTurn = 0;

int main(void)
{
	// ゲームを初期化するカスタム関数、つまりウェルカム画面を表示してゲーム表示盤に入ります。
	initGame();

	// このループは2人のプレイヤーが交互に行動するためのものです。
	while (1)
	{
		// 各サイクルで1増やされるので、2人が交互に行動できます。
		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");

	//ここで再び、チェス盤を表示する機能を持つカスタム関数を呼び出します。
	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)		//これは列番号を表示します。
				printf("%3d", j);
			else if (j == 0)	//行番号を表示します。
				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;

	// プレイヤー1の番かプレイヤー2の番かを判定し、その後配列の対応する要素に値を代入する。
	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;
	}

	// 盤面を再度表示する。
	system("clear");
	printChessboard();	// この関数を再度呼び出す。

	/*
	* 以下のセクションでは、カスタム関数(判定関数)を呼び出します。
	* 現在のプレイヤーがその手番で勝利したかどうかを判定するために使用されます。
	*/
	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」の勝利が表示された後、「現在はプレイヤー*の番です。駒を置く位置を入力してください...」と表示されます。イライラしませんよね?実際には、コードを1行追加するだけです!

	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の盤面で交互に駒を置くことができ、あるプレイヤーが5つの連続した駒を並べたとき、プログラムはそのプレイヤーを勝者と宣言します。友人たちと一緒にこのテキストベースのゲームを楽しんでください!