三子棋--c语言实现(详解)包含完整程序附件

前言:为了可以理解代码,我们需要达到的水平为:掌握几种循环、二维数组以及一些基本的c语言知识。

一、三子棋实现的整体逻辑:

1.把棋盘初始化。 

        在三子棋游戏中,一个有9个格子,所以我们需要创建一个3*3的二维数组,如图所示         

将二维数组初始化为‘ ’ 。

二维数组初始化完成之后,要将二维数组以一定的格式打印出来,由于使用的是控制台,没有学控件,所以要用字符设计一个棋盘的样子:

大概这个样子:

 2.自己下棋设计。

        给自己下的棋规定一个符号为" * ",因为棋盘本质上为二维数组,所以下棋时,只需要更改二维数组即可。

3.计算机下棋设计。

        给计算机下的棋规定一个符号为" # "以便区分,在计算机下棋时,让计算机生成随机数,让生成的随机数保证在二维数组内不越界。

4.判断输赢

        三子棋判断输赢只有一种方法,即三个符号为一线,利用这个特性,设计出横着三个,竖着三个的判断。同时还要判断棋盘是否还有空位,如果有空位则继续下棋,没有空位,且分不出输赢则退出程序。

用流程图表示:

 流程已知,开始敲代码:

二、代码阶段

1.创建二维数组并且将二维数组初始化:

void PrintBoard(char* board) {
		for (int i = 0; i <= 2; i++) {
			for (int j = 0; j <= 2; j++) {
				printf("_%c_", *(board + i * 3 + j));
				if (j<=1) {
					printf("|");
				}
			}
			printf("\n");
		}
	}

使用malloc申请地址空间,在访问二维数组时,假象将二维数组一维化

2.打印棋盘:

void PrintBoard(char* board) {
	for (int i = 0; i <= 2; i++) {
		for (int j = 0; j <= 2; j++) {
			printf("_%c_", *(board + i * 3 + j));
			if (j<=1) {
				printf("|");
			}
		}
		printf("\n");
	}
}

难点在于:  printf("_%c_", *(board + i * 3 + j)); 

这行代码,打印出来的字符是:符号"_" +通过下标访问二维数组获取到的值+"_"。2

3.用户下棋 

//人下棋为
void personBoard(char *board) {
	int i, j;
	printf("请输入要下棋的位置:");
	scanf_s("%d", &i);
	scanf_s("%d", &j);
//这里可以为一个限制i与j大小的语句,以防止i与j在访问数组时发生越界行为。
	while ((i < 1 || i >= 4) || (j < 1 || j >= 4)) {
		printf("\n输入位置有错,请重新输入:");
		scanf_s("%d", &i);
		scanf_s("%d", &j);
	}
	i -= 1; //我们通过scanf_s输入的是非程序员思维,即数组的第一个下标为1,
            //所以要访问到数组首地址时,需要i-=1。
	j -= 1;

	do
	{
		//首先判断下棋的地方是否已经有棋
		if (*(board + 3 * i + j) != ' ') {//有旗了
			printf("下的地方已经有棋子了,请重新输入:\n");
			scanf_s("%d", &i);
			scanf_s("%d", &j);
			i--;
			j--;
		}else {
			*(board + 3 * i + j) = '*';
			//打印棋盘:
			PrintBoard(board);
			break;
		}
	} while (1);
}

        用户在进行下棋时,通过输入x与y坐标,对应在x,y坐标处下子,首先程序应该接收我们输入的数据i与j,并判断i与j是否在二维数组中越界;如果发生越界行为,要求用户重新输入。

        输入数据没有问题后,判断输入的坐标是否已经有棋子,如果有则要求重新输入,如果没有则将"*"放在该坐标处,打印棋盘。

4.电脑下棋

void copmuterBoard(char *board) {
	do
	{
		srand(time(NULL)); // 设置随机数种子
		int i = rand() % 10 / 3; // 生成一个随机数
		int j = rand() % 10 / 3;
		if (*(board + 3 * i + j) != ' ') {//说明有棋子,需要重新下
			continue;
		}
		else {//为空
			*(board + 3 * i + j) = '#';
			PrintBoard(board);
			break;
		}
	} while (1);
}

        电脑下棋这里只使用最简单的例子,不考虑算法。使用srand生成随机种子,经过rand() % 10 / 3计算后,可以让生成的随机值落在0-3这个区间上。把生成的随机值当作电脑的x与y坐标进行输入,同样经过判断是否有棋子在该坐标处,如果有则重新生成,知道可以落子。

5.判断空位

在进行下棋时,只有当棋盘中有空位时,即当二维数组中仍然有位置时,才能放棋子。

static unsigned int isFull(char *board) {
	for (int i = 0; i <= 8; i++) {
		if (*(board + i) == ' ') {
			//有空位
			return 0;
		}
	}
	return 1;
}

判断空位很容易,只要将二维数组遍历一遍,如果里面有字符' ',则代表有空位。有空位返回0,无空位返回1。

6.判断胜负

unsigned int isWin(char *board) {
	//判断输赢
	for (int i = 0; i <= 2;i++) { //在1-2-3行上
		if ((*(board + 3 * i + 0) == '*' && *(board + 3 * i + 1) == '*' && *(board + 3 * i + 2) == '*')
			|| (*(board + 3 * i + 0) == '#' && *(board + 3 * i + 1) == '#' && *(board + 3 * i + 2) == '#'))
		{
			if (*(board + 3 * i + 0) == '*') {//人影了
				printf("你赢了");
				return 2;
			}
			else {
				printf("你输了");
				return 1;
			}
		}
	}
	for (int j = 0; j <= 2;j++) {
		if ((*(board + 3 * 0 + j) == '*' && *(board + 3 * 1 + j) == '*' && *(board + 3 * 2 + j) == '*')
			|| (*(board + 3 * 0 + j) == '#' && *(board + 3 * 1 + j) == '#' && *(board + 3 * 2 + j) == '#'))
		{
			if (*(board + 3 * 0 + j) == '*') {//人影了
				printf("你赢了");
				return 2;
			}
			else {
				printf("你输了");
				return 1;
			}
		}
	}
	if (*(board + 3 * 0 + 0)=='*'&& *(board + 3 * 1 + 1) == '*'&& *(board + 3 * 2 + 2) == '*'||
		*(board + 3 * 0 + 0) == '#' && *(board + 3 * 1 + 1) == '#' && *(board + 3 * 2 + 2) == '#'
		) {
		if (*(board + 3 * 1 + 1) == '*') {//人影了
			printf("你赢了");
			return 2;
		}
		else {
			printf("你输了");
			return 1;
		}
	}
	if (*(board + 3 * 0 + 2) == '*' && *(board + 3 * 1 + 1) == '*' && *(board + 3 * 2 + 0) == '*' ||
		*(board + 3 * 0 + 2) == '#' && *(board + 3 * 1 + 1) == '#' && *(board + 3 * 2 + 0) == '#'
		) {
		if (*(board + 3 * 1 + 1) == '*') {//人影了
			printf("你赢了");
			return 2;
		}
		else {
			printf("你输了");
			return 1;
		}
	}
	//都没有赢?
	return 0;
}

别被这么长的代码给吓到了,看第一个for循环:if里面是啥意思呢?

if ((*(board + 3 * i + 0) == '*' && *(board + 3 * i + 1) == '*' && *(board + 3 * i + 2) == '*')
            || (*(board + 3 * i + 0) == '#' && *(board + 3 * i + 1) == '#' && *(board + 3 * i + 2) == '#'))

在这里暂且将i换为0,则有

if ((*(board + 3 * 0 + 0) == '*' && *(board + 3 * 0 + 1) == '*' && *(board + 3 * 0 + 2) == '*')
            || (*(board + 3 * 0 + 0) == '#' && *(board + 3 * 0 + 1) == '#' && *(board + 3 * 0 + 2) == '#'))

*(board + 3 * 0 + 0) -> *(board) 数组首元素  --第一行第一列的值,对应二维数组为arr[0][1]

*(board + 3 * 0 + 1) ->*(board+1)数组第二个元素--第一行第二列的值,对应二维数组为arr[0][2]

*(board + 3 * 0 + 2) ->*(board+1)数组第三个元素--第一行第三列的值,对应二维数组为arr[0][3]

只有这三个值都为'*'或者都为‘#’才表示用户或者电脑有一方获胜。

这仅仅代表的是第一行,那如果数组的第二行中全为'*'呢?,

是不是我们要接着判断第二行?

第二行咋判断呢,原理和第一行一样,让第二行的每一个值如果都相等且等于'*'或者'#',则有一方获胜

第三行同样如此。

至此第一个for循环结束

第二个for循环,则是判断第一、二、三列中的值是否相同,原理与第一个for循环相似。

第三个与第四个if语句,分辨判断两条对角线上的元素是否相同

7.将代码根据逻辑合并

void game() {
	char* board = InitBoard();
	//首先打印棋盘:
	PrintBoard(board);
	//开始下棋-从人开始
	while (1) {
		if (isFull(board) == 0 && isWin(board) == 0) {//棋盘没有满,且没有人赢
			personBoard(board);
		}else {//棋盘满了,或者有人赢了
			break;//退出
		}
		if (isFull(board) == 0 && isWin(board) == 0) {//棋盘没有满,且没有人赢
			printf("轮到电脑下了\n");
			copmuterBoard(board);
		}else {//棋盘满了,或者有人赢了
			break;//退出
		}
		if (isFull(board) == 1 && isWin(board) == 0) {//棋盘满了,且没有人赢
			printf("平局!\n");
			break;
		}
	}
    free(board);//记得释放内存

}

首先将棋盘初始化,并打印出棋盘以供用户下棋。

在while循环中,轮到用户或者电脑下棋时,先判断一下棋盘是否满了或者是否有人赢了,当棋盘没有满且没有人赢时,用户/电脑才可以下棋;当棋盘满了但仍然没有人赢时,宣布平局。

8.主函数的代码:

void gameStart() {
	int isPlay = 0;
	do
	{
		printf("菜单\n");
		printf("1.play\n");
		printf("2.退出\n请选择:");
		scanf_s("%d", &isPlay);
		switch (isPlay)
		{
		case 1:
			game();
			break;
		case 2:
			printf("退出游戏");
			break;
		default:
			printf("输入有误!请重新输入:\n");
			scanf_s("%d", &isPlay);
			break;
		}
	} while (isPlay != 2);
}
void main() {
	gameStart();
}

结束语:

三子棋并不算难,可以锻炼自己的代码逻辑能力,可以试着写一写,冲呀!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蠢 愚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值