c语言三子棋实现。

昨天听老师讲过一遍三子棋(也就是井字棋)的实现,于是今天便尝试着写一下。个人的写法并不是很好,在结果判断的那一块,使用的是穷举的方法(我比较憨没办法,想不出来),所以如果想由此旺达延伸比如变成五子棋的话,个人在这个程序上,认为这是第一大难关。另外呢,这个程序可能在逻辑上有点难懂(主要是main函数中的调用,和几个循环写的比较难看),再次呢就是一堆变量没有做到见名知意。只能说三子棋的实现,我确实是完成了,但效率不高,逻辑不好,而且个人现在在他的扩大上的关键算法没有思考出来,只能说没有失败,但是也没有成功。嘛~继续加油吧。

不是很好的需求小分析和功能实现逻辑

这是写之前我在纸上写的一点东西,现在回过头来整理了一下。(就是懒,你就是个懒怂!)
这个纸上是我的整体构思,由于写完代码我没有注释,所以,可以从这张纸上着手看懂。
在这里插入图片描述

函数整合及大致框架搭建·

由上图可知,
1号和4号功能都是需要输出一个期盼区域和落子信息的,所以,这两个功能可以整合成一个显示函数
落子的信息由循环遍历打印二维数组来实现。
电脑棋子规定为O 玩家棋子规定为X,未落子区域由空格代替。那么在最开始棋盘是什么样子的呢,如果不给玩家一个棋盘作为参考的话,玩家就会无从下手。
所以在最开始我们需要打印一遍棋盘供期参考。但是一开始的二维数组里面万一有值呢?那打印的东西就会被显示出来污染显示的棋盘区域。
所以我们需要在每一次游戏开始时将二维数组全部赋值为‘ ’。这里就需要引入一个能够出事话整个二维数组的功能既为初始化函数
初始化整个数组完毕后游戏就开始了,需要先进行猜先。猜先是具有随机性的就和抛硬币一样。为了保证他的随机性,我使用了随机数。为了保证结果的唯二性,我们对这个随机数%2+1,认为规定他的范围为1-2.
然后载根据这两个结果再结合我们进行的猜测(switch 语句case1 case2)进行猜先判定。
猜先完毕就可以正式开始游戏了!
但是猜先的结果会影响落子的顺序。所以我们需要根据猜先的结果来决定落子先后,在游戏开始的第一次,这一点由猜先函数的返回值作为参数传送给落子函数,依次来决定谁先走。落子之后,在游戏结束或者是出现结果之前,落子是交替进行的,所以我们需要交替改变传给落子函数的参数

c = (c % 2 + 1) % 2; 

以c为参数传递,你可以尝试使用不同的int数进行计算,你胡发现c的制只有两种,且交替出现!!!!!!
(重要!!!)
然后根据c的两种结果,在落子函数中使用if语句将玩家的落子实现,和pc的落子步骤分别实现。
上图说过,一落子,一检查,一反馈。我们可以在落子后就直接对落子区域检查,若合法,则落子完毕,若不合法,则利用循环重新落子。
若落子合法,则我们可以在这一步对落子的结果进行及时的判定,以避免在棋盘已满之前出现胜负结果
因为若胜负提前分出的话,后面的落子是无意义的!!
所以我们需要在落子合法判定纸盒徐苏的对器具结果记性分析。
如上图所说的四种情况。

井字棋的胜负判定是根据是否出现三子连珠在这种情况。三字连住的情况有横连,竖连,和对角线连着三种情况。我们需要做的无非就是循环遍历检查遇三种情况相对应的元素是否相同,且不为空格!!!

在这里我使用的判断方法是一种类似于穷举的方法,咋该程序上是可以使用的,但是如果需要扩大的话,这种算法是显然不可取的。希望我好好学习,以后能想出来233.
然后再结果判定之后,如果胜负已分,则可以跳转处该函数,并反映相应的结果。若胜负未分,则需要查看棋盘的剩余区域时候还有,如果有,那么久继续下期,交换落子之人,如果没有空余区域(指的是‘ ’的元素),则表明棋盘用尽,胜负未分,可以从新开始下一句,或者退出游戏。由此看出,这里还需要一个判断棋盘是否满了的函数。
若胜负未分且棋盘为满,则在落子完毕后,我们需要调用打印函数来打印整个棋盘来实现信息的及时更新与对用户实现反馈。然后交替落子

头文件

#ifndef SUBCHESS
#define SUBCHESS
#include<stdio.h>
#include<time.h>
#include<Windows.h>
#pragma warning(disable:4996)
#endif
#define ROW 3//二维数组的行
#define COL 3//二维数组的列
void menu();//主菜单
int menu2();//游戏结束时弹出的是否还要在玩一局的菜单
int guess();//猜先函数
void play(int a,char chess[][COL]);//落子函数
void init(char chess[][COL] ,int b, int c);//初始化二维数组
int judge(char chess[][COL]);//结果判定函数
void show(char a[][COL]);//打印棋盘
int full(char a[][COL]);//判断棋盘状态函数

由此看来整个功能实现的大致框架就是
在这里插入图片描述

main文件

#include "Subchess.h"
int main()
{
	srand((unsigned long)time(NULL));
	char chess[ROW][COL];

	int b = 1;
	do
	{
		menu();
		int a = 0;
		printf("\n\t\t请选择相应的功能\n");
		scanf("%d", &a);
		switch (a)
		{
		case 1:
			init(chess, ROW, COL);
			printf("初始化棋盘\n");
			show(chess);
			int c = 0, d = 0;
			c = guess();
			int e = 0;
			
			do
			{
		
				play(c, chess);
				d=judge(chess);
				
				show(chess);
				
				c = (c % 2 + 1) % 2; 
				e = full(chess);
				switch (d)
				{
				case 1:printf("你赢了!\n"); goto loop;
		       case -1:printf("电脑赢了\n"); goto loop;
				}
				e=full(chess);
				switch (e)
				{
				case 0:printf("继续下!\n"); break;
				case 2:printf("胜负未分\n"); goto loop;
				}

			} while (!e);
		loop: if (menu2())
			{
				b = 1;
			}
			else{
				b = 0;
			}
			continue;
			

		case 2:b = 0; break;
		
			
		default:printf("选项输入错误 请输入正确的功能选项!\n\n");
		}
	} while (b);
	printf("\t\t\tさようなら!!");
	system("pause");
	
}

函数实现

#include "Subchess.h"
void menu()
{
	system("color 04");

	printf("\t\t\t 欢迎来到井字棋游戏\n");
	printf("\t\t\t********************\n");
	printf("\t\t\t****            ****\n");
	printf("\t\t\t####################\n");
	printf("\t\t\t***1#开始  2#退出***\n");
	printf("\t\t\t********************\n");
}
int menu2()
{
	int a = 0;
	printf("\t\t\t是否再来一局?\n");
	printf("\t\t1.yes\t\t\t2.no\n");
	while (1)
	{
		scanf("%d", &a);
		switch (a)
		{
		case 1:return a = 1;



		case 2:
			return a = 0;

		default:printf("请输入相应的功能选项!");
		}
	}

}
int guess()
{
	while (1)
	{
		int a = 0;
			a=rand() % 2 + 1;
		printf("三秒后硬币将落地,请猜正反面!\n");
		printf("  \t\t 3    \r"); Sleep(1000);
		printf("  \t\t 2    \r"); Sleep(1000);
		printf("  \t\t 1    \r"); Sleep(1000);
		printf("\t\t请猜\t——1.正面       2.反面———\n");
		int b = 0;
		scanf("%d", &b);
		int c = a % 2;
		switch (b)
		{
		case 1:
			if (c)
			{
				printf("硬币为%d面,为正面,你先行!\n", c);
				
				return 1;
			}
			printf("硬币为%d面,为反面,pc先行!\n", c+2);
			 return 0;

		case 2:
			if (c)
			{
				printf("硬币为%d面,为正面,pc先行!\n", c );
				Sleep(5000);
				return 0;
			}
			printf("硬币为%d面,为反面,你先行!\n", c+2);
			Sleep(5000);
			return 1;
		default:
			printf("猜先错误,从新再来!\n");

		}
	}
}
void init(char a[][COL], int b, int c)
{
	for (int x = 0; x < b; x++)
	{
		for (int y = 0; y < c; y++)
		{
			a[x][y] = ' ';
		}
		
	}
}

int full(char a[][COL])
{
	int fulll = 1;
	for (int x = 0; x < ROW; x++)
	{
		for (int y = 0; y < COL; y++)
		{
			if (a[x][y] == ' ')
			{
				fulll = 0;
				return 0;//未满
			}

		}

	}
	if (fulll)
	{
		return 2;
	}
}









void play(int a,char chess[][COL])
{
	int x = 0, y = 0;
	int xp = 0, yp = 0;
	int m = 0, n = 0;
	int mp = 0, np = 0;
	if (a)
	{
		printf("轮到你了!\t");
		do
		{
			 n= 0;
			do
			{
				m = 0;
				printf("请输入你落子的行列位置!(第几行第几列)###\n");
				scanf("%d,%d", &x,&y);
				if (x > 3 || x < 0 || y>3 || y < 0)
				{
					printf("\n位置输入错误请重新输入!\n");
					m = 1;
				}
			} while (m);
			if (chess[x - 1][y - 1] != ' ')
			{
				printf("该位置已有落子,不可再落子该处!请重新输入!\n");
				n = 1;
				if (full(chess) == 2)
				{
					break;
				}
			
			}
			else
			{
				chess[x - 1][y - 1] = 'X';
			}
		} while (n);
	}
	else
	{
		printf("轮到电脑了!\t");
		do
		{
			np = 0;
			mp = 0;
			xp = rand() % 3 + 1;
			yp = rand() % 3 + 1;

			if (chess[xp - 1][yp - 1] != ' ')
			{
				np = 1;
			}
		} while (np);
		printf("电脑落子于第  %d  行 第  %d  列\n", xp, yp);
		chess[xp - 1][yp - 1] = 'O';
	}
}
int judge(char a[][COL])
{

	int FULL = 0;
	int row = 0, col = 0;

	for (int row = 0; row < ROW; row++)
	{
		if ((a[row][1] == a[row][0]) && (a[row][0] == a[row][2]) && a[row][0] != 0)
		{
			if (a[row][0] == 'X')
			{

				printf("你赢了");
				return 1;
			}
			if (a[row][0] == 'O')
			{
				printf("电脑赢了!!\n");
				return -1;
			}

		}
	}
	for (int col = 0; col < ROW;col++)
	{
		if ((a[0][col] == a[1][col]) && (a[0][col] == a[2][col]) && a[0][col] != 0)
		{
			if (a[0][col] == 'X')
			{

				printf("你赢了");
				return 1;
			}
			if (a[0][col] == 'O')
			{
				printf("电脑赢了!!\n");
				return -1;
			}
		}
	}
	if (((a[0][0] == a[1][1]) && (a[1][1] == a[2][2])) || ((a[0][2] == a[1][1]) && (a[1][1] == a[2][0])))
	{
		if (a[1][1] == 'X'&&a[1][1] != ' ')
		{
			printf("你赢了");
			return 1;
		}
		if (a[1][1] == 'O'&&a[1][1] != ' ')
		{
			printf("电脑赢了!!\n");
			return -1;
		}
	}
	
}
		void show(char a[][COL])
		{

			printf("||   ||  1 ||  2 ||  3 ||\n");
			printf("=========================\n");

			for (int x = 0; x < ROW; x++)
			{
				printf("|| %d ||", x + 1);

				for (int y = 0; y < COL; y++)
				{

					printf("  %c ||", a[x][y]);

				}
				printf("\n");
				printf("=========================\n");
			}
		}

运行结果
在这里插入图片描述
这个是菜单和落子过程,主要是最后一句合法性判断!!!
在这里插入图片描述
电脑胜利 游戏结束,弹出小菜单(我太菜了!qwq)
在这里插入图片描述
胜负未分
在这里插入图片描述
我赢了哈哈哈
以上就是我的井字棋实现了。虽然逻辑很奇怪,但是毕竟还是实现了,这貌似也是我第一次写一个将近300行以上的代码。以往的都是小规模的习题。经过这次,我觉得把,贴近生活实际,有具体的,现实的逻辑的程序还是蛮难的。尤其是功能一多,模块一多,彼此互相调用,再加上各种循环,就很难控制,有时候写错逻辑,在运行是出现死循环,都要找个半天。只能说以后还是要多练习。

最最最重要,复习昨天的一句话!数组传参发生降维!降维成指向数组内部元素类型的指针!。

发布了13 篇原创文章 · 获赞 13 · 访问量 755

猜你喜欢

转载自blog.csdn.net/jiewaikexue/article/details/102825488