【️C语言-游戏设置】---✨三子棋(N满)

发布于:2022-11-09 ⋅ 阅读:(2) ⋅ 点赞:(0) ⋅ 评论:(0)

📘游戏基本介绍:

在这里插入图片描述

嘻嘻,想必大家都玩过吧,有时上课玩,下课玩儿,玩的不亦乐乎。哈哈!

规则:双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。

让我们重温经典,从新拾起我们儿时的回忆。把它带入C世界。

📙基本功能分配:

📕A.游戏相关定义–game.h

简单介绍:

  • game.h这个头文件就像海上巨轮的总舵,总舵发出一个个的** “指令” **,指挥着船员合理进行工作。game.h也是如此“指挥”着game.c从而实现游戏的运行。

这里包含几个关键的点,让我们一起来看看吧😁

- 头文件集合地:

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

这里(游戏)包含了我们会使用的几个头文件,我们可以把这些头文件放在game.h里,这样我们在其他板块引用game.h即可使用其他文件。

- 引用方式:

#include"game.h"
//这里用的是" "而不是<>哦!
  • define的整数定义:
#define ROW 3
#define COL 3
#define WZQ 3

这也是三子棋盘可以变化为N子棋盘的原因,所以这个相当于一个很不错的优化。

- 关键函数的定义:

//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char who_is_win(char board[ROW][COL], int row, int col);


在这里定义关键函数,在我看来其实是梳理程序的一部,建立游戏实现框架,我尝试把这个部分屏蔽,并不影响游戏的运行。

好吧,总部大佬我们已经知道了,接下来我们来看看游戏躯干吧!

game.h

#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
#define WZQ 3


//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char who_is_win(char board[ROW][COL], int row, int col);


📕B. 测试的逻辑–test.c

  • test.c我认为它是游戏的躯干,主题,毕竟主函数在这不是嘛(虽然很细)。好吧,我们来慢慢认识它吧。
int main()
{
	//游戏测试
	test();

	return 0;

}

a.游戏菜单–menu函数

  • 这个菜单与游戏设计的menu一样,直接上链接
void menu()
{
	printf("******************************\n");
	printf("********  1、play   **********\n");
	printf("********  0、eixt   **********\n");
	printf("******************************\n");
} 

b.游戏总框架–test函数:

1.游戏可持续性的实现

  • 这里我们为了是程序实现可持续性,用到了do-while循环来实现。在通过选项判断是否循环,或者进入下个部分。
  • 通过上面菜单,我们有三种选择:
  • 1.进行游戏,进入game()函数。但未脱离循环,函数执行完,仍在循环内。
  • 2.退出游戏,脱离循环,结束程序。
  • 3.非法输入,强行输入其他字符,无法识别,重新输入,继续循环。

- 综上,我们会选择采取Switch语句并且我们可以确定while的判断条件为input输入是否为0。
—————————————————————————代码如下——————————————————————

void test()
{
	int input = 0;
	do
	{
		menu();
	    printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);
}
  • 这里也是 游戏实现框架–game函数与我们简要讲的game.c有着逻辑关系

2.game函数框架:

1.game()函数来理清框架,把游戏逻辑全部理清,是游戏实现的索引。我们用具代码来看看吧。
2.在我看来其实和test.c很相像。
3.具体内容我们就不在这里赘述,我们在game.c中来看吧!

void game()
{

	char board[ROW][COL];
	//初始化棋盘
	init_board(board,ROW,COL);
	//打印棋盘
	print_board(board,ROW,COL);
	//玩家下棋
	char ret = 0;
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = who_is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = who_is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	switch (ret)
	{
	case '#':
		printf("电脑胜利\n");
		break;
	case '*':
		printf("玩家胜利\n");
		break;
	default:
		printf("平局\n");
		break;
	}
	//游戏继续:'C'
	//电脑胜利:'#'
	//玩家胜利:'*'
	//平局:'P'

}

test.c

#include"game.h"

void menu()
{
	printf("******************************\n");
	printf("********  1、play   **********\n");
	printf("********  0、eixt   **********\n");
	printf("******************************\n");
} 
void game()
{

	char board[ROW][COL];
	//初始化棋盘
	init_board(board,ROW,COL);
	//打印棋盘
	print_board(board,ROW,COL);
	//玩家下棋
	char ret = 0;
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = who_is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
		ret = who_is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	switch (ret)
	{
	case '#':
		printf("电脑胜利\n");
		break;
	case '*':
		printf("玩家胜利\n");
		break;
	default:
		printf("平局\n");
		break;
	}
	//游戏继续:'C'
	//电脑胜利:'#'
	//玩家胜利:'*'
	//平局:'P'

}

void test()
{
	//随机数生成器
	srand((unsigned int) time(NULL));
	int input = 0;
	do
	{
		menu();
	    printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);
}

int main()
{
	//游戏测试
	test();

	return 0;

}

📕C.游戏实现–game.c

  • game.c这里就是游戏核心的运行,也是游戏实现运行的关键。来吧,来吧,看看真正的实体吧😁

📗a.棋盘的建立:

这里棋盘建立主要是二维数组,实现的细节很多,好好听哦。

1.棋盘初始化–init函数

这里我们的是思路是建立所需棋盘大小并且还可以更改,这里大家联想到了什么。
对咯,就是game.h里的#define定义整数喽,棋盘需要变化只需要改变定义整数大小即可。

创建一个二维数组:

输入‘空格’完成初始化。

void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)//确定行
	{
		int j = 0;
		for (j = 0; j < col; j++)//确定列
		{
		//行列相乘就是棋盘大小
			board[i][j] = ' ';//初始化
		}
	}
}

这些地方一定要调试检查,不然等代码后面多了,可能就不那么容易了。
有的朋友肯定会提出疑问,我打出来我看不到啊,怎么检查呢。
这里大家可以将数组初始化为‘*’;这样就便于检查喽。

2.棋盘打印–print函数

棋盘打印有很多细节,让我给大家慢慢道来!

首先我们要知道棋盘如何构成:

我们可以把棋盘拆分成两种类型来解释:
1.第一层是由“ | | ”构成的,我们还要拆分变为是由三个“ |”组成,但少了最后一个“|”

for (i = 0; i < row; i++)
	{
		int j = 0;
		//一行一行打
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);//打印空格;
			if (j < col - 1)//少一列
				printf("|");
		}
	}	

2.同样的道理,第二层我们可以细分为由三个“—|"组成,同样少了最后一个”|“这里就不插入代码筹字数咯。
3.细心的朋友一定发现了,我们最后一行也少了一排“—”。代码同理。

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		//一行一行打
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);//打印空格;
			if (j < col - 1)//少一列
				printf("|");
		}
		printf("\n");
		if (i < row - 1)//少一行
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		printf("\n");
	}
}

OK,棋盘已经搭建好了,那便来战胜负吧。

📗b.下棋:

1.玩家下棋–player函数

玩家下棋我们就自定义个函数:void player_move(char board[ROW][COL], int row, int col);
函数需要注意哪些问题呢,下面两点值得注意:

1)程序员与玩家区别

棋盘的自述:

大家好,我是棋盘YYDS,由我来为大家介绍下,程序员和玩家眼中的我。
玩家眼中,我是1,2,3并排走的,坐标范围都1-3.
可是程序员眼中,一眼看穿我的本质,明白我是一个二维数组,所以程序员能够找出我的真实坐标。0,1,2.
但我想让两边都认识我,都不矛盾,那就写个函数吧。

从棋盘的自述中,我们明白了两者的区别,很明显发现,两者认识只是差一,故此,我们便用函数更改

        printf("请输入下棋坐标:>");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);

        if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
		//程序员:真实坐标
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
2)判断输入是否合规

好了,意见统一,但要合法吧!有两法要记住。

~判断是否在范围限制内;
~判断是否输入重复;
if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("输入坐标重复,请重新输入\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}

2.电脑下棋–computer函数

引入 void computer_move(char board[ROW][COL], int row, int col);

1)生成范围内的随机数;
//随机数生成器
	srand((unsigned int) time(NULL));
//**生成器放在主函数哦**

随机数生成了,范围咋办,别急,来答案了。

//这里row==col,都等于3;
		int x = rand() % row;
		int y = rand() % col;
//取余一个数会得到比这个数小的结果,从而限定范围。
2)判断落子位置是否重复;

有空白就输入,没空白就算喽

if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}

📗c.胜负判断–is_win函数

引入char who_is_win(char board[ROW][COL], int row, int col);

1.玩家/电脑胜利:

这里我是采用了计数的方法,大家可以借鉴借鉴哈。
并且玩家和电脑判断高度相似,我就拿玩家举例子喽。
电脑判断只需把‘*’改为‘#’。

1)行列判断

	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		int count1 = 0, count2 = 0; 
		//玩家处判断
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == '*')
			//行判断
				count1++;
			if (board[j][i] == '*')
			//列判断
				count2++;
			
		}
		switch (count1)
		{
		case WZQ:
			return '*';	
		}
		switch (count2)
		{
		case WZQ:
			return '*';
		}
		
2)对角线判断
    int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		int count3 = 0, count4 = 0;
		for (j = 0; j < col; j++)
		{
            if (board[j][j] == '*')
            //这里判断的是主对角线
				count3++;
			if (board[j][ROW - 1 - j] == '*')
			//判断的是副对角线
				count4++;
		}		
         switch (count3)
		{
		case WZQ:
			return '*';
		}
		switch(count4)
		{
		case WZQ:
			return '*';
		}
}		

综上,其实我们可以把两种判断放在一起,通用一个循环,并且要注意一个点,就是count的归零。尤其小心哈

2.平局:

引入static int is_full(char board[ROW][COL], int row, int col);

~full_board函数

核心: 度过上面两个判断,判断是否还有空格,若无,则平局。

int i = 0;
	int count = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] != ' ')
			//这里是所有格子,判断是否有空格子
				count++;	
		}
	}
	if (count == ROW*COL)//这里是所有格子数量
		return 1;
	else
		return 0;

3.游戏继续:

上面所有都结束了,那只能是继续喽。

判断结束,返回字符,在宣布结果。

好了,游戏就讲完喽,三个代码分享如下:

game.c

#include"game.h"

void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		//一行一行打
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);//打印空格;
			if (j < col - 1)//少一列
				printf("|");
		}
		printf("\n");
		if (i < row - 1)//少一行
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		printf("\n");
	}
}

void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋:\n");
	while (1)
	{
		printf("请输入下棋坐标:>");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("输入坐标重复,请重新输入\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
}

void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:\n");
	while (1)
	{	
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

static int is_full(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int count = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] != ' ')
				count++;	
		}
	}
	if (count == ROW*COL)
		return 1;
	else
		return 0;
	
}

char who_is_win(char board[ROW][COL], int row, int col)
{
	//玩家胜利或者电脑胜利:判断行n列n斜杠n
	
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		int count1 = 0, count2 = 0; 
		int count3 = 0, count4 = 0;
		//玩家处判断
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == '*')
				count1++;
			if (board[j][i] == '*')
				count2++;
			if (board[j][j] == '*')
				count3++;
			if (board[j][ROW - 1 - j] == '*')
				count4++;
		}
		switch (count1)
		{
		case WZQ:
			return '*';	
		}
		switch (count2)
		{
		case WZQ:
			return '*';
		}
		switch (count3)
		{
		case WZQ:
			return '*';
		}
		switch(count4)
		{
		case WZQ:
			return '*';
		}

		//电脑处判断
		count1 = 0, count2 = 0;
		count3 = 0, count4 = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == '#')
				count1++;
			if (board[j][i] == '#')
				count2++;
			if (board[j][j] == '#')
				count3++;
			if (board[j][ROW - 1 - j] == '#')
				count4++;
		}
		switch (count1)
		{
		case WZQ:
			return '#';
		}
		switch (count2)
		{
		case WZQ:
			return '#';
		}
		switch (count3)
		{ 
		case WZQ:
			return '#';
		}
		switch (count4)
		{
		case WZQ:
			return '#';
		}
	}

	if (is_full(board, ROW, COL) == 1)
			return 'P';
    
	 return 'C';
}

篇幅太长咯,抱歉了哈!
最后美图奉上:

在这里插入图片描述

遥望彩霞,奔赴梦想,我们都在,一起加油(兄弟们)!!!