シンプルでわかりやすい5つのゲームをC言語で詳しく解説

序文

C言語の理解を深めるために、1人プレイだけでなく、マンツーマシン、全員対プレイヤーなど、わかりやすい5つのミニゲームを収録しています。勉強した後は盛り上がるゲームで遊ぼう!

1. 準備

5 つの小さなゲームを作成する予定で、5 つの小さなゲームごとにヘッダー ファイルとソース ファイルを作成する必要があります。game1.h/game1.c、game2.h/game2.c、game3.h/game3.c、game4.h/game4.c、game5.h/game5.c です。これの目的は、将来のミニゲームの再構築などの運用を容易にするために、各ゲームを個別に構築することです。これら 5 つの小さなゲームのヘッダー ファイルをインクルードする main.c も必要です。main 関数は呼び出しのみを担当します。すべてのミニゲーム コード関数を他のファイルに実装し、それらを static で装飾し、ユーザーがゲーム関数を使用してミニゲームのコードを間接的に呼び出せるようにします。

2. ゲームメニュー

まず main.c をビルドしましょう:
1. 5 つの小さなゲームのヘッダー ファイルをインクルードする必要があります
2. main 関数をビルドします

#include"game1.h"//包含小游戏1的头文件
#include"game2.h"//包含小游戏2的头文件
#include"game3.h"//包含小游戏3的头文件
#include"game4.h"//包含小游戏4的头文件
#include"game5.h"//包含小游戏5的头文件
#include<time.h>
int main()
{
    
    
	srand((unsigned int)time(NULL));//用来生成随机数
	do
	{
    
    
		int optional = 0;//创建一个接收选择的变量
		menu();
		printf("请输入你的选项:\n");
		scanf("%d", &optional);
		switch (optional)
		{
    
    
		case 1:
			game1();
			break;
		case 2:
			game2();
			break;
		case 3:
			game3();
			break;
		case 4:
			game4();
			break;
		case 5:
			game5();
			break;
		case 0:
			printf("感谢你的游玩,欢迎下次使用。\n");
			exit(0);
		default:
			printf("你的选项输入有误,请重新输入:\n");
			break;
		}
	} while (1);
}

main 関数で do-while ループを使用します。その目的は、ユーザーが選択できるようにすることであり、ユーザーは終了を選択するか失敗するまで終了しません。
scanf 関数は危険で、ユーザーが入力文字を間違えるとバッファ例外が発生します。そこで、受け取りたい選択変数をループに入れ、初期値 0 を与えて、ユーザーが文字を入力したときにプログラムを終了させます。
srand関数は乱数を生成するために使用されますが、rand関数だけを使用すると、生成された乱数が変更されず、繰り返しのプレイ体験が悪化します。
私たちはこれらのゲームを集めたメニューを作成し、ユーザーがどのゲームをプレイするかを選択できるようにしています。

void menu()
{
    
    
	printf("*****************************\n");
	printf("*****1.猜数字   2.三子棋*****\n");
	printf("*****3.扫  雷   4.五子棋*****\n");
	printf("*****5.飞行棋   0.退  出*****\n");
	printf("*****************************\n");
}

基本メニューが完成したので、各ミニゲームのメニューに入ります。
まずミニゲームのヘッダー ファイルを作成しましょう。

#pragma once
#include<stdio.h>
#include<stdlib.h>
void game1();

ミニゲームは、ミニゲームのヘッダー ファイルのゲームに対応しています。ミニゲーム 1 を例に挙げますが、他の 4 つのミニゲームも同様です。
次に、それぞれのソース ファイルを作成します。

#include"game1.h"
static void menu1()
{
    
    
	printf("*****************************\n");
	printf("*****      猜数字       *****\n");
	printf("***** 1.Start the game ******\n");
	printf("***** 0.Exit the game  ******\n");
	printf("*****************************\n");
}
void game1()
{
    
    
	int optional = 0;//创建一个接收选择的变量
	do
	{
    
    
		optional = 0;
		menu1();
		printf("请输入你的选项:\n");
		scanf("%d", &optional);
		switch (optional)
		{
    
    
		case 1:
			//game();
			break;
		case 0:
			printf("猜数字小游戏以退出。\n");
			break;
		default:
			printf("你的选项输入有误,请重新输入:\n");
			break;
		}
	} while (optional);
}

ここでもゲーム 1 を例に挙げます。他のミニゲームのオプションはゲーム 1 と同じで、メニュー機能でミニゲームの名前が変更されるだけです。ループ内のゲーム関数は、この小さなゲームの合成関数を呼び出すために使用されます。
コードが期待どおりかどうかをテストするために実行してみましょう。テストは
ここに画像の説明を挿入
アイデアに従って正常に実行できます。ここで、それぞれの小さなゲームを正式に記述してみましょう。

3. ゲーム内容

1. 数字を当ててください

アイデア: システムは乱数をランダムに生成し、入力した数値と生成された数値を比較することで、その数値が大きすぎるか小さすぎるかを推測するプロンプトが表示されます。

static void game()
{
    
    
	int num = rand() % 100 + 1;//创建一个变量,用来存放随机数。
	int guess = 0;//创建一个变量,用来存放所猜的数字。	
	//rand函数是用来生成随机数的,我们用rand函数生成的随机数对100取模并加1,可以产生1~100的随机数
	while(1)
	{
    
    
		printf("请输入你猜测的数字:");
		scanf("%d", &guess);
		if (guess == num)
		{
    
    
			printf("恭喜你,猜对了!\n");
			break;
		}
		else if (guess > num)
		{
    
    
			printf("你猜的数字过大,请重新猜测吧!\n");
		}
		else if (guess < num)
		{
    
    
			printf("你猜的数字过小,请重新猜测吧!\n");
		}
	}
	return;
}

単純に実行してみましょう!
ここに画像の説明を挿入
問題なく動作しており、繰り返し再生できます。

2. バックギャモン

アイデア: 配列を使用してチェスの駒を保存し、勝つかどうかを判断します。
1. チェス盤を印刷します
2. プレーヤーがチェスをします
3. プレーヤーが勝つかどうかを決定します
4. コンピューターがチェスをします
5. コンピューターが勝つかどうかを決定します
まず 2 つのマクロを定義します。これらは、設定できるチェス盤のサイズを決定するために使用されます。

#define ROW 3  //行
#define COL 3  //列

ゲーム関数の構築を開始して、ゲームのアイデアをサポートするためにどのようなモジュールが必要かを確認します。

static void game()
{
    
    
	char win;//创建一个变量,存放判段获胜条件的字符。
	//我们把C代表继续,D代表平局,*代表玩家获胜,#代表电脑获胜
	char checkerboard[ROW][COL] = {
    
     0 };//创建一个数组,用来存放棋盘信息
	initialization(checkerboard,ROW,COL);//初始化数组,把数组中的每个元素初始化为空格
	ptintinitialization(checkerboard, ROW, COL);//打印棋盘
	while(1)
	{
    
    
		Player(checkerboard, ROW, COL, '*');//玩家下棋,玩家的标志为 *
		win = Iswin(checkerboard, ROW, COL);//判断是否获胜
		if (win != 'C')
		{
    
    
			break;
		}
		ptintinitialization(checkerboard, ROW, COL);//打印棋盘
		Computer(checkerboard, ROW, COL, '#');//电脑下棋,电脑的标志为 #
		win = Iswin(checkerboard, ROW, COL);
		if (win != 'C')
		{
    
    
			break;
		}
		ptintinitialization(checkerboard, ROW, COL);//打印棋盘
	}
	if (win == 'D')
	{
    
    
		printf("平局\n");
		ptintinitialization(checkerboard, ROW, COL);
	}
	else if (win == '*')
	{
    
    
		printf("恭喜你获得胜利\n");
		ptintinitialization(checkerboard, ROW, COL);
	}
	else
	{
    
    
		printf("很遗憾,你输掉了比赛\n");
		ptintinitialization(checkerboard, ROW, COL);
	}
}

まず使用する配列と勝敗を判定する変数を作成し、その配列を初期化して出力することで、プレイヤーがチェスのプレイできる位置を知ることができます。機能とコンピュータのチェス機能。
配列関数を初期化します。

static void initialization(char arr[ROW][COL],int row,int col)//初始化数组
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
    
    
		for (j = 0; j < col; j++)
		{
    
    
			arr[i][j] = ' ';//把数组中的每个元素赋值为空格
		}
	}
}

チェッカーボードの印刷機能:

static void ptintinitialization(char arr[ROW][COL], int row, int col)//打印棋盘
{
    
    
	int i = 0;
	for (i = 0; i < row; i++)
	{
    
    
		int j = 0;
		for (j = 0; j < col; j++)
		{
    
    
			if (j < col - 1)
			{
    
    
				printf(" %c |", arr[i][j]);//打印棋盘,如果为列中的最后一个元素,则不打印|
			}
			else
			{
    
    
				printf(" %c ", arr[i][j]);
			}
		}
		printf("\n");
		if(i < row-1)//打印棋盘,如果为行中的最后一个元素,则不打印---
		{
    
    
			for (j = 0; j < col; j++)
			{
    
    
				if (j < col)
				{
    
    
					printf("--- ");
				}
			}
		}
		printf("\n");
	}
}

ここに画像の説明を挿入
印刷効果を図に示します。
マクロを次のように変更すると、

#define ROW 9  //行
#define COL 9  //列

ここに画像の説明を挿入
印刷されたチェス盤が拡大することがわかります。これは、各関数の値を変更する必要がないマクロの利点です。マクロで定義された値を変更するだけで、チェス盤のサイズが変更されます。

プレイヤーのチェス機能:

static void Player(char arr[ROW][COL], int row, int col, char ch)//玩家下棋函数
{
    
    
	int x = 0;
	int y = 0;
	while(1)
	{
    
    
		printf("请输入你要下棋的坐标:");
		scanf("%d %d", &x, &y);
		if (x<1 || x>row || y<1 || y>col)//判断坐标是否合法
		{
    
    
			printf("你输入的坐标不合法,请重新输入。\n");
		}
		else if (arr[x - 1][y - 1] != ' ')//判断输入坐标是否被占用
		{
    
    
			printf("你输入的坐标已被占用,请重新输入。\n");
		}
		else
		{
    
    
			arr[x - 1][y - 1] = ch;//数组下标从0开始,所以玩家的真实坐标要进行减1
			break;
		}
	}
}

コンピューターチェス機能:

static void Computer(char arr[ROW][COL], int row, int col, char ch)
{
    
    
	while (1)
	{
    
    
		int x = rand() % 3;//产生0~2的数字
		int y = rand() % 3;
		if (arr[x][y] == ' ')//如果坐标未被占用,则电脑下棋,否则产生新的坐标进行判断
		{
    
    
			arr[x][y] = ch;//电脑的坐标的下标和数组下标对应,不需要进行减1操作
			break;
		}
	}
}

関数に勝つかどうかの判断:

static char Iswin(char arr[ROW][COL], int row, int col)//判断是否获胜
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)//判断每一排中是否有人获胜
	{
    
    
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][1] != ' ')
		{
    
    
			return arr[i][1];
		}
	}
	for (i = 0; i < col; i++)//判断每一列中是否有人获胜
	{
    
    
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[1][i] != ' ')
		{
    
    
			return arr[1][i];
		}
	}
	if ((arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[1][1] != ' ') || 
		(arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' '))//判断对角线上是否有人获胜
	{
    
    
		return arr[1][1];
	}
	for (i = 0; i < row; i++)
	{
    
    
		for (j = 0; i < col; i++)
		{
    
    
			if (arr[i][j] == ' ');//判断是否还有空位
			{
    
    
				return 'C';
			}
		}
	}
	return 'D';
}

実行して結果を見てみましょう!
ここに画像の説明を挿入
注:
1. 私たちが使用している scanf は安全ではありません。安全ではない理由を理解するには、対応する解決策を見つけることができます。
2. 使用する配列を初期化する際は間隔に注意 配列パラメータを渡す場合は仮パラメータと実パラメータの型が一致しているかに注意 char型の配列を使用しています仮パラメータは int で受け入れられますか? 誰もが降りて試してみることができます。
3. このコードをリファクタリングすると、全員が互いに対戦できるようになります。ここでは人間対機械の戦いのみが示されていますが、これをリファクタリングして人間対機械の戦いに参加してみましょう。
仮パラメータが int で受け入れられると、配列が範囲外になり、スタック エラーが発生します。

3.掃海艇

アイデア: 配列を使用して地雷の情報を保存し、調査によって地雷が存在しない場所を見つけます。
1. マップを印刷する
2. プレイヤーが掃海を行う3. プレイヤーが地雷を
踏むかどうかを判定する
4. マップを更新する
分析:
ここに画像の説明を挿入します上の画像は 9*9 の掃海マップです。ここに画像の説明を挿入
場所によっては周囲の状況を判断する必要があり、必ず立ち入り禁止が発生します。この問題を解決するにはどうすればよいでしょうか?
判定によって配列が範囲外にならないように、配列を上下左右に1行ずつ拡張します。コードを使用して配列を出力する問題を分析してみましょう。
地図情報を格納する配列とプレイヤーに表示する配列の2つを用意し、プレイヤーに表示する配列は地図情報の配列を格納することで地図上の安全な場所を判断する必要があります。
まず 5 つのマクロを定義します。それぞれ、設定できるマップのサイズと印刷されるマップのサイズを決定します。最後のマクロは地雷の数を保存するために使用されます。これは、後でテストするのにも便利です。

#define ROW 9  //显示的行
#define COL 9  //显示的列
#define ROWS ROW+2  //真实数组的行
#define COLS COL+2  //真实数组的列
#define MINENUM 10  //地雷的个数

次に、ゲーム関数を追加して、具体的なアイデアを確認します。

static void game()
{
    
    
	int winnum = ROW * COL - MINENUM;//设置一个变量,用来存放还有多少个安全的地方
	char minemap[ROWS][COLS] = {
    
     0 };//雷分布的地图
	char showmap[ROWS][COLS] = {
    
     0 };//向玩家展示的地图
	Initialization(minemap, showmap, ROWS, COLS);//初始化数组,把雷分布随机分布在地图中,并且把向玩家展示的地图全部替换为 * 
	//Ptintinitialization(minemap, ROW, COL);//测试代码,用来打印地图
	while (winnum)
	{
    
    
		int x = 0;
		int y = 0;
		Ptintinitialization(showmap, ROW, COL);//打印展示地图
		printf("请输入排雷坐标:");
		scanf("%d %d", &x, &y);
		if (x<1 || x>ROW || y<1 || y>COL)//判断坐标是否合法
		{
    
    
			printf("你输入的坐标不合法,请重新输入。\n");
		}
		else if (showmap[x][y] != '*')//判断坐标是否已被排查过
		{
    
    
			printf("你输入的坐标已被排过雷了,请重新输入。\n");
		}
		else 
		{
    
    
			if (!Determine(minemap, x, y))//!Determine(minemap, x, y)用来判断是否踩到雷
			{
    
    
				Ptintinitialization(minemap, ROW, COL);//打印地雷地图,让玩家知道地雷在哪里。
				break;
			}
			Renewmap(minemap, showmap, x, y);//用来更新向玩家展示的地图
			winnum--;//对安全的地方进行减1
		}
	}
	if(winnum == 0)//当安全地方为0时,证明排雷成功
	{
    
    
		printf("恭喜你排雷成功!\n");
		Ptintinitialization(showmap, ROW, COL);//打印展示地图,让玩家知道自己获胜地图
	}
}

配列関数を初期化します。

static void Initialization(char minemap[ROWS][COLS], char showmap[ROWS][COLS], int rows, int cols)
{
    
    
	int count = MINENUM;//创建一个变量,用来计算放置的雷的数量
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)//把地雷地图全部赋值为字符0
	{
    
    
		for (j = 0; j < cols; j++)
		{
    
    
			minemap[i][j] = '0';
		}
	}
	while (count)//把雷分布随机分布在地雷地图中
	//我们把0代表安全,1代表雷区
	{
    
    
		int x = rand() % ROW + 1;//创建随机变量范围在1~9
		int y = rand() % COL + 1;//创建随机变量范围在1~9
		if (minemap[x][y] == '0')//判断该位置是否已有雷
		{
    
    
			minemap[x][y] = '1';//放置地雷
			count--;
		}
	}
	for (i = 0; i < rows; i++)//把展示地图全部赋值为字符*
	{
    
    
		for (j = 0; j < cols; j++)
		{
    
    
			showmap[i][j] = '*';
		}
	}
}

ここに画像の説明を挿入
表示するマップの行は1~ROW、列は1~COLとなっており、生成される地雷の座標はこの範囲内で管理されます。
マップの印刷機能:

static void Ptintinitialization(char showmap[ROWS][COLS], int row, int col)//打印地图
{
    
    
	int i = 1;
	int j = 1;
	for (i = 1; i <= row; i++)
	{
    
    
		if (i == 1)//当i为1时,需要多一个空格,目的为了和地图对齐
		{
    
    
			printf(" ");
		}
		printf(" %d", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)//打印我们所需要的地图
	{
    
    
		printf("%d", i);
		for (j = 1; j <= col; j++)
		{
    
    
			printf(" %c", showmap[i][j]);
		}
		printf("\n");
	}
}

1 から印刷を開始し、ROW まで印刷し、必要なところだけを印刷します。
私の足を踏むかどうかを判断するには:

static bool Determine(char minemap[ROWS][COLS],int x,int y)
{
    
    
	if (minemap[x][y] == '1')
	{
    
    
		printf("排雷失败,你已死亡!\n");
		return false;
	}
	else
	{
    
    
		return true;
	}
}

表示されている地図を更新します。

static void Renewmap(char minemap[ROWS][COLS], char showmap[ROWS][COLS], int x, int y)
{
    
    
	showmap[x][y] = (minemap[x - 1][y - 1] + minemap[x - 1][y] + minemap[x - 1][y + 1]
				  + minemap[x][y - 1] + minemap[x][y + 1]
				  + minemap[x + 1][y - 1] + minemap[x + 1][y] + minemap[x + 1][y + 1]) - 8*'0' + '0';
}

ここに画像の説明を挿入
プレイヤーに表示されるマップを更新して、周囲の地雷の数を合計し、対応する数に置き換えます。文字 1 と文字 0 を格納する文字配列を使用しているため、ASCLL コード値 0 を減算して整数を取得し、それらを加算してこの正方形の周囲 8 か所の地雷の数を表し、最後に次の値を加算する必要があります。 ASCLL コード値 0 は、対応する文字番号を取得します。
テストしてみましょう:テスト コードのコメントを解除し、マクロで定義されている地雷の数を大きく設定します。最初は 79 に設定しました。実際にプレイするときにプレイしたい難易度に応じて変更できます。地雷
ここに画像の説明を挿入
除去の様子 失敗が正常に表示されるかどうか:
ここに画像の説明を挿入
地雷除去の失敗も正常に表示されます。
注:
1. scanf は安全ではありません
2. 使用する配列を初期化する際の間隔に注意し、配列にアクセスする際には範囲外にならないか注意してください。
3. 復元アイデア:拡張マップの追加(周囲8か所に地雷がない場合、マス目あたりに地雷があることが分かるまで拡張可能)、小さな旗の追加(位置を置き換える)ができます。小さな旗のある地雷とみなされ、変更するには小さな旗をキャンセルするだけです)マインスイーパはその位置でのみ実行できます)、時間を追加します(時間が経過すると、すべての地雷が除去されない場合はゲームが終了します) 、など。

4. バックギャモン

アイデア: どのプレイヤーが 5 つの連続した駒を連続してプレイしたかを判定します (同じ駒の 5 つの連続した駒がある行、列、または対角線) 1. ボードを印刷します。 2. プレイヤー 1 がチェスをプレイします。 3. プレイヤー 1 が勝つかどうかを決定

ます

4. プレーヤー 2 がチェスをプレイします。
5. プレーヤー 2 が勝つかどうかを決定します
。 最初にボードのサイズを定義するために、引き続きマクロを使用します。

#define ROW 9  //棋盘的行
#define COL 9  //棋盘的列

ゲームのアイデアをもう一度見てみましょう。

static void game()
{
    
    
	char win;//创建一个变量,存放判段获胜条件的字符。
	//我们把C代表继续,D代表平局,白棋(@)代表玩家1获胜,黑棋(O)代表玩家2获胜
	char checkerboard[ROW][COL] = {
    
     0 };//棋盘数组
	Initialization(checkerboard, ROW, COL);//初始化数组,把棋盘的所有位置都赋值为 *
	Ptintinitialization(checkerboard, ROW, COL);//打印棋盘
	while (1)
	{
    
    
		Player(checkerboard, ROW, COL, '@');//玩家1下棋,玩家1的标志为 @
		win = Iswin(checkerboard, ROW, COL);//判断是否获胜
		if (win != 'C')
		{
    
    
			break;
		}
		Ptintinitialization(checkerboard, ROW, COL);//打印棋盘
		Player(checkerboard, ROW, COL, 'O');//玩家2下棋,玩家2的标志为 O
		win = Iswin(checkerboard, ROW, COL);//判断是否获胜
		if (win != 'C')
		{
    
    
			break;
		}
		Ptintinitialization(checkerboard, ROW, COL);//打印棋盘
	}
	if (win == 'D')
	{
    
    
		printf("平局\n");
		Ptintinitialization(checkerboard, ROW, COL);
	}
	else if (win == '@')
	{
    
    
		printf("玩家1获胜\n");
		Ptintinitialization(checkerboard, ROW, COL);
	}
	else
	{
    
    
		printf("玩家2获胜\n");
		Ptintinitialization(checkerboard, ROW, COL);
	}
}

バックギャモンの考え方は、コンピュータが別のプレイヤーに置き換えられることを除いて、バックギャモンの考え方とまったく同じです。したがって、ゲーム関数のコードは基本的にはあまり変更されていません。チェスをプレイするプレイヤーは同じ思考を持っているため、チェスをプレイする 2 人のプレイヤーの思考を解決するには 1 つの関数だけが必要です。
配列関数を初期化します。

static void Initialization(char arr[ROW][COL], int row, int col)//初始化数组,把棋盘的所有位置都赋值为 *
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)//把展示地图全部赋值为字符*
	{
    
    
		for (j = 0; j < col; j++)
		{
    
    
			arr[i][j] = '*';
		}
	}
}

配列を初期化するという考え方は、マインスイーパでプレイヤーに表示されるマップとまったく同じです。
マップの印刷機能:

static void Ptintinitialization(char arr[ROW][COL], int row, int col)//打印棋盘
{
    
    
	int i = 0;
	int j = 0;
	for (i = 1; i <= row; i++)
	{
    
    
		if (i == 1)//当i为1时,需要多两个空格,目的为了和地图对齐
		{
    
    
			printf("  ");
		}
		if (i < 9)//当i小于9时,需要多一个个空格,目的为了和地图对齐
		{
    
    
			printf(" %d ", i);
		}
		else//当i大于于9时,不需要多一个个空格
		{
    
    
			printf(" %d", i);
		}
	}
	printf("\n");
	for (i = 0; i < row; i++)//打印我们所需要的地图
	{
    
    
		printf("%2d", i + 1);
		for (j = 0; j < col; j++)
		{
    
    
			printf(" %c ", arr[i][j]);//需要多一个个空格,目的为了和地图对齐
		}
		printf("\n");
	}
}

バックギャモン マップは自由に拡大および縮小できますが、マップが上の行および列と一致しているかどうかに注意する必要があります。
プレイヤーのチェス機能:

int x = 0;//用来接收玩家下棋的坐标
int y = 0;//用来接收玩家下棋的坐标
static void  Player(char arr[ROW][COL], int row, int col, char ch)//玩家下棋函数,通过ch来判断是玩家1还是玩家2
{
    
    
	int player = 0;//创建一个变量来确定是玩家几
	if (ch == '@')//如果ch为白棋(@),则代表玩家1
	{
    
    
		player = 1;
	}
	else//反之则为玩家2
	{
    
    
		player = 2;
	}
	while (1)
	{
    
    
		printf("请玩家 %d 输入要下棋的坐标:",player);
		scanf("%d %d", &x, &y);
		if (x<1 || x>row || y<1 || y>col)//判断坐标是否合法
		{
    
    
			printf("你输入的坐标不合法,请重新输入。\n");
		}
		else if (arr[x - 1][y - 1] != '*')//判断输入坐标是否被占用
		{
    
    
			printf("你输入的坐标已被占用,请重新输入。\n");
		}
		else
		{
    
    
			arr[x - 1][y - 1] = ch;//数组下标从0开始,所以玩家的真实坐标要进行减1
			break;
		}
	}
}

プレイヤーのチェスの座標をグローバル変数として設定し、5 人の息子が連続しているかどうかを判断できるようにします。
関数に勝つかどうかの判断:
ここに画像の説明を挿入

enum Direction
{
    
    
	LEFT,
	RIGHT,
	UP,
	DOWN,
	LEFTUP,
	RIGHTDOWN,
	RIGHTUP,
	LEFTDOWN
};
static char Iswin(char arr[ROW][COL], int row, int col)//判断是否获胜
{
    
    
	static chessnum = 0;//创建一个静态变量,用来存放棋盘中使用的格数
	chessnum++;//每进行一次判断调用就对棋盘使用格数进行+1
	int winnum1 = 1 + Wincount(arr, ROW, COL, LEFT) + Wincount(arr, ROW, COL, RIGHT);
	//确定左和右共计多少相同个棋子,需要加上自身的棋子
	int winnum2 = 1 + Wincount(arr, ROW, COL, UP) + Wincount(arr, ROW, COL, DOWN);
	//确定上和下共计多少相同个棋子,需要加上自身的棋子
	int winnum3 = 1 + Wincount(arr, ROW, COL, LEFTUP) + Wincount(arr, ROW, COL, RIGHTDOWN);
	//确定左上和右下共计多少相同个棋子,需要加上自身的棋子
	int winnum4 = 1 + Wincount(arr, ROW, COL, RIGHTUP) + Wincount(arr, ROW, COL, LEFTDOWN);
	//确定右上和左下共计多少相同个棋子,需要加上自身的棋子
	if (winnum1 >= 5 || winnum2 >= 5 || winnum3 >= 5 || winnum4 >= 5)//判断是否获胜
	{
    
    
		return arr[x - 1][y - 1];//返回获胜棋子的字符
	}
	else
	{
    
    
		if (chessnum == ROW * COL)//如果棋盘使用格数等于棋盘的格数,证明平局
		{
    
    
			return 'D';
		}
		else//如果棋盘使用格数不等于棋盘的格数,证明还有空位,继续进行比赛
		{
    
    
			return 'C';
		}
	}
}

これら 8 つの方向に同じ数のチェスの駒を数える必要があります。5 回連続 * の状況はなく、保存されるアドレスは常に最後のプレイヤーのアドレスになります。8方向をメニュータイプで表します。

static int Wincount(char arr[ROW][COL], int row, int col, enum Direction dir)
{
    
    
	int count = 0;//记录相同棋子个数
	int _x = x - 1;//要对坐标进行减1,因为我们的棋盘从1开始,数组下标从0开始
	int _y = y - 1;//要对坐标进行减1,因为我们的棋盘从1开始,数组下标从0开始
	//用局部变量保存,避免在这里修改全局变量的值
	while (1)
	{
    
    
		switch (dir)
		{
    
    
		case LEFT:
			_y--;//对中心坐标的列坐标进行减1
			break;
		case RIGHT:
			_y++;//对中心坐标的列坐标进行加1
			break;
		case UP:
			_x++;
			break;
		case DOWN:
			_x--;
			break;
		case LEFTUP:
			_y--;
			_x++;
			break;
		case RIGHTDOWN:
			_y++;
			_x--;
			break;
		case RIGHTUP:
			_y++;
			_x++;
			break;
		case LEFTDOWN:
			_y--;
			_x--;
			break;
		default:
			break;
		}
		if (_x < 0 || _x > ROW || _y < 0 || _y>COL)//判断位置是否合法
		{
    
    
			return count;
		}
		else if(arr[_x][_y] != arr[x-1][y-1])//判断是否和上一个玩家的棋子相同
		{
    
    
			return count;
		}
		else
		{
    
    
			count++;
		}
	}
}

同じ方向の特定の駒の数を判断するための無限ループとして設定します。メニュー タイプは渡すパラメータとは異なるため、スイッチ ブランチでは異なるルートを選択します。
注:
配列は範囲外です。
Wuzilianzhu を判断するための
アイデアの再構築: ネットワークに参加したり、オンライン対戦を実現したり、チェスを後悔する機能を追加したりできます。

5.ルード

遊び方:1. 開始位置が同じであることを除き、残りのプレイヤーの位置が他のプレイヤーの位置と同じになると、スタンピードが発生します。
2. プレーヤーが小道具を踏むと、対応する小道具操作を実行します
。 3. 最初に端に到達したプレーヤーが勝利の
アイデアになります:
1. 配列を使用してマップ情報を保存します
。 2. 構造体を使用してプレーヤー情報を構築します
。 3. を使用します。構造体 プレイヤー情報を格納する配列
4. ランダム関数を使用してふるい操作をシミュレートする
まず、ゲームのアイデアを構築しましょう。

int map[100];//创建一个地图大小为100的地图。并且把地图元素全部初始化为0
//为了对数组进行方便的操作,我们把数组设置为全局变量,使每个函数可以直接访问数组
static void game()
{
    
    
	struct Player play[2];//创建一个玩家数组,里面存放玩家信息
	Initializationmap();//初始化地图数组,把地图相应位置填上道具元素,
	//我们把地图数组设置为了全局函数,所以不要需要传参
	Initializationplay(play);//初始化玩家数组,加入玩家信息,并把玩家位置置于地图开始位置
	Ptintinitialization(play);//打印棋盘,地图数组为全局函数,所以不要对地图传参。
	//需要玩家信息来判断地图要填的字符,所以要对玩家进行传参
	while (1)//使玩家交替进行游玩,直到产生胜利者
	{
    
    
		if (play[0].flags == false)//判断玩家1是否处于暂停回合。
		{
    
    
			Play(&play[0], &play[1],play);//玩家1进行游戏
			Ptintinitialization(play);//打印棋盘
		}
		else//处于暂停回合,把暂停回合改为继续
		{
    
    
			play[0].flags = false;
		}
		if (play[0].position >= 99)//判断玩家1是否获胜
		{
    
    
			printf("%s侥幸赢了%s\n", play[0].name, play[1].name);
			break;
		}
		if (play[1].flags == false)//判断玩家2是否处于暂停回合。
		{
    
    
			Play(&play[1], &play[0],play);//玩家2进行游戏
			Ptintinitialization(play);//打印棋盘
		}
		else
		{
    
    
			play[1].flags = false;//更改暂停选项
		}
		if (play[1].position >= 99)//判断玩家2是否获胜
		{
    
    
			printf("%s侥幸赢了%s\n", play[1].name, play[0].name);
			break;
		}
	}
}

便宜上、マップ配列をグローバル配列として設定し、パラメーターの受け渡しを減らします。また、初期化後に配列を変更する必要はありません。また、プレーヤー情報 (位置、一時停止位置にあるかどうか) を保存するための構造体配列も設定します。
マップ関数を初期化します。

static void Initializationmap()//初始化地图数组
//地图数组中
//0代表什么都没有
//1代表道具1(幸运轮盘),2代表道具2(地雷),3代表道具3(暂停),4代表道具4(时空隧道)
{
    
    
	int luckyturn[] = {
    
    1, 20, 45, 60, 75, 90};//在相应的位置放入 幸运轮盘
	for (int i = 0; i < sizeof(luckyturn) / sizeof(luckyturn[0]); i++)
	//sizeof(luckyturn) / sizeof(luckyturn[0])用来求出数组元素的个数
	{
    
    
		map[luckyturn[i]] = 1;
	}
	int Landmine[] = {
    
     3, 6, 19, 25, 36, 49, 69, 70, 80 };//在相应的位置放入 地雷
	for (int i = 0; i < sizeof(Landmine) / sizeof(Landmine[0]); i++)
	{
    
    
		map[Landmine[i]] = 2;
	}
	int puse[] = {
    
     2, 11, 26, 35, 44, 59, 71, 88 };//在相应的位置放入 暂停
	for (int i = 0; i < sizeof(puse) / sizeof(puse[0]); i++)
	{
    
    
		map[puse[i]] = 3;
	}
	int timetunnel[] = {
    
     5, 15, 30, 50, 77 };//在相应的位置放入 时空隧道
	for (int i = 0; i < sizeof(timetunnel) / sizeof(timetunnel[0]); i++)
	{
    
    
		map[timetunnel[i]] = 4;
	}
}

最初のマップ操作により、適切な小道具を希望の位置に配置することができ、小道具の位置は好みに応じて変更できます。
プレーヤー関数を初期化します。

static void Initializationplay(struct Player *play)
{
    
    
	printf("请输入玩家A的姓名\n");
	scanf("%s", &(play[0].name));
	while (!strcmp(play[0].name,""))//判断玩家A的姓名是否为空
	{
    
    
		printf("玩家姓名不能为空,请重新输入玩家A的姓名\n");
		scanf("%s", &(play[0].name));
	}
	printf("请输入玩家B的姓名\n");
	scanf("%s", &(play[1].name));
	while (1)
	{
    
    
		if (!strcmp(play[1].name, ""))//判断玩家B的姓名是否为空
		{
    
    
			printf("玩家姓名不能为空,请重新输入玩家B的姓名\n");
			scanf("%s", &(play[1].name));
		}
		else if(!strcmp(play[1].name, play[0].name))//判断玩家B的姓名和玩家A是否相同
		{
    
    
			printf("玩家姓名不能一致,请重新输入玩家B的姓名\n");
			scanf("%s", &(play[1].name));
		}
		else
		{
    
    
			break;
		}
	}
	play[0].position = 0;//把玩家A的位置置于0位置(地图开始位置)
	play[0].flags = false;//把玩家A的暂停条件置为假。
	play[1].position = 0;//把玩家B的位置置于0位置(地图开始位置)
	play[1].flags = false;//把玩家B的暂停条件置为假。
}


この関数では、文字列比較を実行しました。文字列比較に == を直接使用することはできません。マップ関数を表示するには、strcmp 関数を使用する必要があります。

extern char Drawstrmap(const struct Player* play, int i);//对Drawstrmap函数进行声明
static void Ptintinitialization(const struct Player* play) //打印棋盘
//由于不会对玩家信息进行更改,我们把参数设置为const,防止在函数中误改
{
    
    
		printf("图例:幸运轮盘:#    地雷:@    暂停:I     时空隧道:>     \n");//向玩家展示道具信息
		//第一段
		for (int i = 0; i < 30; i++)
		{
    
    
			printf("%c ", Drawstrmap(play, i));
		}
		printf("\n");
		//第一竖列
		for (int i = 30; i < 35; i++)
		{
    
    
			for (int j = 0; j <= 28; j++)
			{
    
    
				printf("  ");
			}
			printf("%c ", Drawstrmap(play, i));
			printf("\n");
		}
		//第二段
		for (int i = 64; i >= 35; i--)//地图为从前向后打打印
		{
    
    
			printf("%c ", Drawstrmap(play, i));
		}
		printf("\n");
		//第二数列
		for (int i = 65; i <= 69; i++)
		{
    
    
			printf("%c ", Drawstrmap(play, i));
			printf("\n");
		}
	    //第三行竖列
		for (int i = 70; i <= 99; i++)
		{
    
    
			printf("%c ", Drawstrmap(play, i));
		}
		printf("\n");//画完地图换行
}

static char Drawstrmap(const struct Player* play, int i)//打印地图元素
{
    
    
	char ch;
	if (play[0].position == play[1].position && play[0].position == i)
	//当玩家1和玩家2的位置处于起始位置时
	{
    
    
		ch = 'M';
	}
	else if (play[0].position == i)//当玩家1位于当前位置时
	{
    
    
		ch = 'A';
	}
	else if (play[1].position == i)//当玩家2位于当前位置时
	{
    
    
		ch = 'B';
	}
	else
	{
    
    
		switch (map[i])
		{
    
    
		case 0://地图数组元素为0时
			ch = 'O';
			break;
		case 1:
			ch = '#';
			break;
		case 2:
			ch = '@';
			break;
		case 3:
			ch = 'I';
			break;
		case 4:
			ch = '>';
			break;
		}
	}
	return ch;
}

ここに画像の説明を挿入
ここでは、Drawstrmap 関数を構築します。目的は、マップ配列内の整数要素を必要な文字要素に構築することです。マップの位置を渡して、どの文字を印刷するかを決定します。マップ配列は変更しません。
再生機能:

static void Play(struct Player *play1, struct Player* play2, struct Player* play)
//play1为当前玩家,play2为另一名玩家,play为玩家数组,传玩家数组方便进行位置判断
{
    
    
	int points = rand() % 6 + 1;//设置随机变量,范围为1~6
	printf("%s请按任意键开始致筛子\n", play1->name);
	system("pause");//暂停屏幕
	printf("%s筛到了%d,前进%d格,", play1->name, points, points);
	system("pause");
	play1->position += points;//对玩家位置进行更新
	Weizhi(play);//判断玩家位置是否在合法的范围内
	if (play1->position == play2->position)//现在玩家的位置和另一名玩家位置相同时
	{
    
    
		printf("%s踩到了%s,%s退6格,", play1->name, play2->name, play2->name);
		system("pause");
		play2->position -= 6;//对另一名玩家位置进行更新
		Weizhi(play);//判断玩家位置是否在合法的范围内
		printf("%s已退6格,", play2->name);
		system("pause");
	}
	else
	{
    
    
		switch (map[play1->position])//检查本位玩家是否进行到道具位置
		{
    
    
		case 0:
			printf("%s踩到了方块,安全,", play1->name);
			system("pause");
			break;
		case 1:
		{
    
    
			printf("%s踩到了幸运转盘,1————交换位置,2————轰炸对方,请选择按键\n", play1->name);
			int a = 0;//用来接收用户的选择
			scanf("%d", &a);
			while (1)
			{
    
    
				if (a == 1)
				{
    
    
					printf("%s选择了交换位置,", play1->name);
					system("pause");
					int b = play1->position;
					play1->position = play2->position;
					play2->position = b;
					Weizhi(play);//判断玩家位置是否在合法的范围内
					printf("交换完成,");
					system("pause");
					break;
				}
				else if (a == 2)
				{
    
    
					printf("%s选择了轰炸对方,%s向后退6格,", play1->name, play2->name);
					system("pause");
					play2->position -= 6;
					Weizhi(play);
					printf("执行成功,\n");
					system("pause");
					break;
				}
				else
				{
    
    
					printf("输入不正确,请重新输入");
					int ch;
					while ((ch = fgetc(stdin)) != EOF && ch != '\n');
					//清除缓冲区,防止缓冲区问题使程序不能正常进行
					scanf("%d", &a);
				}
			}
		}
			break;
		case 2:
			printf("%s踩到了地雷,退6格,", play1->name);
			system("pause");
			play1->position -= 6;
			Weizhi(play);
			break;
		case 3:
			printf("%s踩到了暂停,下一回合禁止行动", play1->name);
			system("pause");
			play1->flags = true;
			break;
		case 4:
			printf("%s踩到了时空隧道,前进十格", play1->name);
			system("pause");
			play1->position += 10;
			Weizhi(play);
			break;
		}
	}
	Weizhi(play);
	system("cls");//清除屏幕
}

ふるいをシミュレートし、再生関数で小道具を使用します。scanf 関数のバッファ問題を解決し、無限ループを回避するためにクリア バッファを設定します。
注:
配列は範囲外です。各プレイヤーの位置がマップ内にあることを確認したいと考えています。
文字列比較
再構築のアイデア: 1. ネットワークに参加してオンライン対戦を実現 2. ラッキールーレットを別途関数にカプセル化 3. さらに小道具を追加します。4. 他のプレイヤーに加わります。5ポイント加算でき、ポイントは小道具と交換できます。

要約する

5 つのミニゲームはすべて終了しました。小規模なゲームを作成するときに注意すべき点は次のとおりです:
scanf 関数は安全ではありません。安全な関数を見つけて置き換えるか、scanf のセキュリティ問題を解決してください。
整数配列と同様に型配列も配列に注意してください範囲外であるため、配列パラメータに注意してください。
頻繁に使用されるコードを関数として構築し、コードの冗長性を削減します。

おすすめ

転載: blog.csdn.net/2301_76986069/article/details/130986103