三子棋-基于鹏哥C语言视频示例实现

三子棋-基于鹏哥C语言视频示例实现

项目简介

实现人机对战三子棋的功能,代码托管在gitee上,点此访问,整个项目由两个源文件(main.c和game.c)和一个头文件(game.h)组成。

main.c

main.c中main函数为程序入口,提供一个菜单供用户输入指令开始游戏或者退出程序。main函数实现如下所示。

int main() {
	srand((unsigned int)time(NULL));
	while(1){
		menu();
		int randnum = rand();
		int input = 0;
		scanf("%d", &input); //暂时忽略输入格式不正确出现死循环的情况
		while (1 != input && 0 != input) {
			printf("输入无效,请重新输入!");
			scanf("%d", &input);
		};
		if (0 == input) {
			//printf("play game");
			game(randnum%2); //随机先后方
		};
		if (1 == input) {
			break;
		};
	};
	return 0;
}

游戏开始时需要用到随机数决定先手方,因此使用srand设置随机数种子以时间戳作为参数实现伪随机效果。接着打印菜单,菜单栏效果如下图所示。
请添加图片描述
scanf传入输入结果,当输入为1时程序退出,当输入为0时开始游戏,程序跳转到game.c的game函数中,跳转时根据随机数的奇偶性确定先行方。输入其他数字则会提示重新输入。注意这里未考虑非数字型字符串会带来的程序异常。

game.c

game.c中的game函数代码如下所示,体现三子棋游戏的整个逻辑。game函数体中有initdesk、plact、cpact和check四个函数,分别对应游戏过程中初始化棋盘、玩家落子、电脑落子和判断当前局势这些核心功能,函数封装也实现于game.c中。

void game(int i) {
	char desk[3][3] = { 0 };//定义棋盘
	int choice[2] = { 0,0 };//
	int checkvalue = 0; //平局 玩家赢 电脑赢 游戏未结束
	initdesk(desk);
	
	if (0 == i) { //判断先后手
		while (0 == checkvalue) { //根据游戏状态判断游戏是否结束
			plact(desk, choice); //玩家落子
			checkvalue = check(desk, choice, '*'); //判断落子后的游戏状态
			if (0 != checkvalue) { //根据游戏状态判断游戏是否结束
				break;
			};
			cpact(desk, choice); //电脑落子
			checkvalue = check(desk, choice, '#');
		};
	};
	if (1 == i) {
		while (0 == checkvalue) {
			cpact(desk, choice);
			checkvalue = check(desk, choice, '#');
			if (0 != checkvalue) {
				break;
			}
			plact(desk, choice);
			checkvalue = check(desk, choice, '*');
		};
	};
}

整个游戏逻辑如下。以玩家先行为例,首先玩家落子,判断玩家是否胜利,如果玩家胜利则游戏结束,反之,电脑落子,判断电脑是否胜利,电脑胜利则游戏结束,反之又轮到玩家落子,如此循环往复,直至一方胜利。当棋盘已满无子可下时如仍未分出胜负,则双方平局。

initdesk

initdesk的功能是将表示棋盘上棋子落点坐标的char [3][3]数组中的所有元素初始化为空格字符,并按固定结构打印,打印功能封装为printdesk函数,代码如下所示。

void printdesk(char desk[3][3]) {
	int i = 0;
	for (i = 0; i < 3; i++) {
		int j = 0;
		for (j = 0; j < 3; j++) {
			printf("----");
		};
		printf("-\n");
		for (j = 0; j < 3; j++) {
			printf("| %c ", *(*(desk + i) + j));
		};
		printf("|\n");
	};
	int j = 0;
	for (j = 0; j < 3; j++) {
		printf("----");
	};
	printf("-\n");
}

格式上使用‘-‘和‘|‘两种字符构成棋盘的边框和中间的分隔线,实现棋盘可视化。棋子落于棋盘上3*3的方框中,每个方框包含三个空格符。游戏中玩家落子处中间的空格符会被替换成‘*‘,而电脑落子处会被替换成‘#‘,有两侧的空白符棋盘的可视化效果更好。
请添加图片描述

plact 和 cpact

plact和cpact两个函数分别表示玩家落子和电脑落子,玩家落子位置通过输入流传入,电脑落子位置随机,通过确定落子位置对应的数组元素是否为空格符判断落子位置的有效性,无效则重新落子。落子确认有效后保存落子位置,传入到check函数中进行局势分析。

check

棋盘一方棋子三子相连即为胜利,除了为横成三子,竖成三子,还有左斜和右斜共四种情况。这里实现的check函数可以推广到N子棋中。分析的具体思路是依次考虑这四种情况通过滑动计数的方式判断相连棋子数量是否满足胜利条件。在每种情况下,分两次从落子点位置开始,以相反方向分别向两端滑动确认方框中的字符。整个check过程中当相连棋子数量满足胜利条件即可认为当前落子方胜利,否则需要继续确认直到胜利条件全部不满足。不胜再判断是否平局,判断条件就是棋子是否已占满棋盘,棋盘有空则继续游戏,否则平局。check函数代码如下。

int check(char desk[3][3], int choice[2], char who) {
	//横
	int count = 0; //连子计数
	int turn = 0; //记录滑动转向,滑动过程先从落子位置向右滑动计数,到末端后然后再回起始位置向左滑动,自此到末端后完成计数此时turn=2,注意要考虑落子位置的重复计数!
	int shift = 1; //shift参数控制滑动
	while ((turn < 2) && (count < 2)) {
		if ((0 <= *choice) && (2 >= *choice)) {
			if ((*(choice+1) + shift >= 0) && (*(choice+1) + shift <= 2)) {
				if (who == *(*(desk + *choice) + *(choice + 1) + shift)) { //计算落子位置横向连子的个数
					count++;
					shift = shift /abs(shift) + shift;
				}
				else{
					shift = -1;
					turn++;

				}
			}
			else {
				shift = -1;
				turn++;
			}
		}
		else {
			shift = -1;
			turn++;
		}
	}
	if ((2 == count) && (who == '*')) {
		printf("玩家赢了\n");
		return 1;
	}
	if ((2 == count) && (who == '#')) {
		printf("电脑赢了\n");
		return 2;
	}

	//竖
	count = 0;
	turn = 0;
	shift = 1;
	while ((turn < 2) && (count < 2)) {
		if ((0 <= *(choice+1)) && (2 >= *(choice+1))) {
			if ((*choice + shift >= 0) && (*choice + shift <= 2)) {
				if (who == *(*(desk + *choice + shift) + *(choice + 1))) {
					count++;
					shift = shift / abs(shift) + shift;
				}
				else {
					shift = -1;
					turn++;

				}
			}
			else {
				shift = -1;
				turn++;
			}
		}
		else {
			shift = -1;
			turn++;
		}
	}
	if ((2 == count) && (who == '*')) {
		printf("玩家赢了\n");
		return 1;
	}
	if ((2 == count) && (who == '#')) {
		printf("电脑赢了\n");
			return 2;
	}

	//右斜
	count = 0;
	turn = 0;
	shift = 1;
	while ((turn < 2) && (count < 2)) {
		if ((*choice + shift >= 0) && (*choice + shift <= 2)) {
			if ((*(choice+1) + shift >= 0) && (*(choice+1) + shift <= 2)) {
				if (who == *(*(desk + *choice + shift) + *(choice + 1) + shift)) {
					count++;
					shift = shift / abs(shift) + shift;
				}
				else {
					shift = -1;
					turn++;

				}
			}
			else {
				shift = -1;
				turn++;
			}
		}
		else {
			shift = -1;
			turn++;
		}
	}
	if ((2 == count) && (who == '*')) {
		printf("玩家赢了\n");
		return 1;
	}
	if ((2 == count) && (who == '#')) {
		printf("电脑赢了\n");
			return 2;
	}

	//左斜
	count = 0;
	turn = 0;
	shift = 1;
	while ((turn < 2) && (count < 2)) {
		if ((*choice + shift >= 0) && (*choice + shift <= 2)) {
			if ((*(choice+1) - shift >= 0) && (*(choice+1) - shift <= 2)) {
				if (who == *(*(desk + *choice + shift) + *(choice + 1) - shift)) {
					count++;
					shift = shift / abs(shift) + shift;
				}
				else {
					shift = -1;
					turn++;

				}
			}
			else {
				shift = -1;
				turn++;
			}
		}
		else {
			shift = -1;
			turn++;
		}
	}
	if ((2 == count) && (who == '*')) {
		printf("玩家赢了\n");
		return 1;
	}
	if ((2 == count) && (who == '#')) {
		printf("电脑赢了\n");
			return 2;
	}

	count = 0;
	int i = 0;
	for (i = 0; i < 3; i++) {
		int j = 0;
		for (j = 0; j < 3; j++) {
			if (' ' == *(*(desk + i) + j)){
				count++;
			}
		};
	};//可以直接判断不等于的情况,不用计数
	if (0 == count) {
		printf("平局\n");
		return 3;
	}
	return 0;
}

整个三子棋项目的实现思路大概就是这样。第一次写CSDN,嘿嘿。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值