栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

三子棋的简简单单设计

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

三子棋的简简单单设计

本博客可能有点略长,按照本人设计思路一步步来。(可能会比较繁琐,感谢阅读)


目录

一、项目文件创建

1.头文件:

2.源文件:

二、主函数设计

三、关键函数的设计

四、项目架构整理

1. main.c

 2. game.c

 3. game.h

五、函数详细

 1.int main();

2. void menu();

 3. void game();

 4.void init_chess(char chess[ROW][COL], uint row, uint col);

 5.void display_chess(char chess[ROW][COL], uint row, uint col);

 6.int Pl_move(char chess[ROW][COL], uint row, uint col);

7. int Ai_move(char chess[ROW][COL], uint row, uint col)

 8.int judge_chess(char chess[ROW][COL], uint row, uint col);

 9.void ending(char out);

六、程序验证

 七、优化细节

 八、结语


一、项目文件创建

文件分头文件和源文件看,其中函数稍微比较多需要用到三个文件。

项目取名为"三子棋",其中:

1.头文件:
game.h

2.源文件:
main.c         
game.c

二、主函数设计

int main()

{

        

        do        //先打印循环一遍;

        {

                menu();                           //打印菜单  (中间提示1.play与0.exit )

                int val = 0;

                scanf("%d",&val);         //输入一个选择,放进变量 int val

                                                       //(1表示开始游戏,0表示退出游戏)

                switch( val )

                {

                        case 1:    game();   // 进入游戏环节

        

                        case 0:    printf("游戏已结束!n");    // 显示退出游戏

                                       break;       //退出switch  ,此时val = 0;

        

                        default:    printf("选择错误,请重新输入!n");

                                        break;      //退出switch ,  此时val  !=  0;

                }

        }  while(val)        //val为0时退出,val不为0时继续;

        

        return 0;

}


三、关键函数的设计

void menu()
{
    printf("**************************************************n");
    printf("********************  1.Play  ********************n");
    printf("**************************************************n");
    printf("********************  0.exit  ********************n");
    printf("**************************************************n");

}    //都是对齐的;

void game()
{
     // /初始化多维数组 - init_chess();
    //输出棋盘 - display_chess();
   
    //循环
    // {
    // 
    //    玩家下棋 - Pl_move();
    //    输出棋盘 - display_chess();
    //    判断棋局 - judge_chess();
    // 
    //    电脑下棋 - Ai_move();
    //    输出棋盘 - display_chess();
    //    判断棋局 - judge_chess();
    // 
    // }

}


四、项目架构整理
//从上面二和三中得出以下函数
int main();     //主函数

void menu();    //打印菜单

void game();    //游戏环节

init_chess();   //初始化多维数组

Pl_move();      //play玩家下棋

Ai_move();      //电脑下棋

display_chess();//打印当前棋盘

judge_chess();  //判断棋盘

下面为项目各文件的函数整理:

1. main.c
文件名main.c
//---------------------------------

#include "game.h"

int main()
{

};

//函数中{}中内容还没确定,所以没写,不代表没有;

 2. game.c
 文件名game.c
//-------------------------------

#include "game.h"

void menu()
{};

void game()
{};

void init_chess()
{};

void display_chess()
{};

int Pl_move()
{};

int Ai_move()
{};

int judge_chess()
{};

//函数()中形参还没确定,所以没写,不代表没有;
//函数中{}中内容还没确定,所以没写,不代表没有;

 3. game.h
文件名game.h
//--------------------------------

#include 
#include    // 取用srand(),rand();
#include      //取用time()函数

#define uint unsigned int   //个人习惯。用unsigned int缩写uint,
                            //本项目中uint都可以代替为int,
                            //此项在本博客中第五函数详细后加入;
#define ROW 3    //行,棋盘的行数   //此项在本博客中第五函数详细后加入; 
#define COL 3    //列,棋盘的列数   //此项在本博客中第五函数详细后加入;

void menu();       

void game();

void init_chess();

void display_chess();

int Pl_move();

int Ai_move();			

int judge_chess();

//函数()中形参还没确定,所以没写,不代表没有;

五、函数详细

各函数详细化,为了函数之间更好联通,可能会增加函数和格外变量;

 1.int main();
int main ()
{
	uint val_val = 2; //uint定义参考头文件game.h,实际为unsigned int

	do
	{
		menu();
		printf(" >> ");         //为了醒目在哪输入

 		scanf("%u",&val_val);   //玩家输入选择
		 
        switch (val_val)    //通过上述选择判断进入哪个分支;
		{
			 case 0:	            //退出
				 printf("游戏已结束!n");
				 break;
			 case 1:	            //进入游戏环节
				 game();
				 break;
			 default:               //输出错误;需要重新输入
				 printf("输入错误,请重新输入!n");
				 break;
		 }

	} while (val_val);   //val_val为0时退出,val_val不为0时继续

	return 0;

2. void menu();
void menu()     //打印菜单
{
	printf("**************************************************n");
	printf("********************  1.Play  ********************n");
	printf("**************************************************n");
	printf("********************  0.exit  ********************n");
	printf("**************************************************n");

}

 3. void game();
void game()
{

	char chess[ROW][COL] = { '0' };  //多维数组来 存储 棋盘中'棋子',目前是3*3的棋盘;
	char outcome = ' ';              //此变量为表示棋盘状态;

	init_chess(chess, ROW, COL);
	display_chess(chess, ROW, COL);	
	while (outcome == ' ')         //只有棋盘状态还未结束时,继续循环;
	{
		Pl_move(chess, ROW, COL);         //玩家下棋用'*'		
		display_chess(chess, ROW, COL);   //打印棋盘
		outcome = judge_chess(chess, ROW, COL);  //返回判断结果,
                                      //返回值为'*'玩家赢、'#'电脑赢、' '为继续
                                      //想不到好的方法结束游戏并返回主函数,
                                      //于是用一个字符变量代替结果;         

		if (outcome != ' ') break;    //棋盘结果已出后,直接跳出循环去通报就结果;

		Ai_move(chess, ROW, COL);        //电脑下棋用'#'       
		display_chess(chess, ROW, COL);  //打印棋盘
		outcome = judge_chess(chess, ROW, COL);		

		if (outcome != ' ') break;    //棋盘结果已出后,直接跳出循环去通报就结果;

	}
	ending(outcome);     //通报结果
                         //为了游戏结束后可以继续在菜单选择继续游戏或退出游戏,
                         //单独增加一个通报结果的函数;                        
}

 4.void init_chess(char chess[ROW][COL], uint row, uint col);
void init_chess(char chess[ROW][COL], uint row, uint col)  //初始化多维数组函数
{                                                          //上面形参为数组、数组的行、列
	srand((int)time(NULL));   //为随机值赋予时间戳;不懂时间戳可自行百度;

	uint i = 0;                 
	uint j = 0;

	for (i = 0; i < row; i++)      //row行col列的多维数值赋值为‘ ’,在棋盘上表示无棋子;
	{                              //此项目此处为3行3列
		for (j = 0; j < col; j++)
		{
			chess[i][j] = ' ';     
		}
	}


}

 5.void display_chess(char chess[ROW][COL], uint row, uint col);
void display_chess(char chess[ROW][COL], uint row, uint col)   //打印棋盘
{
	uint i = 0;
	uint j = 0;
	printf("当前棋局如下:n");      //表示当前棋局状态;
	for (i = 0; i < row; i++)  //打印每行;
	{
//-----------------------------------------
		for (j = 0; j < col; j++)  //打印每行中的棋子和‘|’;
		{
			printf(" %c ", chess[i][j]);
			if (j < col - 1)      //最后一个棋子后面不需要‘|’
			{
				printf("|");
			}
		}
		printf("n");            //打印完“棋子行”后换行
//------------------------------------------
		for (j = 0; j < col; j++)  //偶数行需要打印分隔符行‘—--|---|---’
		{                          //上面演示为此项目中的3行3列中分隔符行;
			if (i < row - 1)       //“分割行” 总比 “棋子行” 少一行;
			{
				printf("---");
				if (j < col - 1)   //分割行中'|' 总比 '---' 少一个;
				{
					printf("|");
				}
			}
			
		}
//------------------------------------------
		if (i < row - 1)            //打印完一个“棋子行n分割行”后换行,
		{                           //其中最后一行的“棋子行”后面没有“分割行”,
			printf("n");           //自然不需要“分割行”后面的"n";
		}
	}

 6.int Pl_move(char chess[ROW][COL], uint row, uint col);
int Pl_move(char chess[ROW][COL], uint row, uint col)        //玩家下棋
{
	uint x = 0;
	uint y = 0;
	while (1)
	{
		printf("玩家下棋:n");         //提醒玩家下棋
		printf(" >> ");                //醒目输入位置
		int c = scanf("%u%u", &x, &y); // int c用来防止scanf警告,个人习惯;
		printf("n");                  // 上面中变量c无任何作用;


		if (x > 0 && x < row+1 && y < col+1 && y > 0 )//判断位置是多维函数合理范围内;
		{
			if (chess[x-1][y-1] == ' ')   //判断所下位置原本为空才能下;
			{
				chess[x-1][y-1] = '*';    //写入玩家棋子'*'
				return 0;         //正确写入退出“玩家下棋”返回 0
			}
			else
			{
				printf("当前位置已有棋,请重新选择位置!nn"); //当前位置已有棋的提示
				continue;          //未成功下棋,则重新循环,重新选择下棋位置
			}
		}
		else
		{
			printf("输入坐标有误,请重新输入!nn");   //未成功下棋提示,
		}                                           //并重新循环,重新选择下棋位置
	}
	

	return -1;   // 非正确写入退出“玩家下棋”返回 -1
}                // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;

7. int Ai_move(char chess[ROW][COL], uint row, uint col)
int Ai_move(char chess[ROW][COL], uint row, uint col)     //电脑下棋
{                                                         //随机下棋,并不智能;
	printf("n电脑下棋:n");  //提示当前是电脑下棋          //本人目前水平为初学者; 
	while (1)   //如果棋子为写入成功,会循环到写入成功为止;  //如果你们用更好的想法,
	{                                                     //可以给电脑
		int x = rand() % row; //用随机数取3的余数,         //编写更聪明的下棋方法;
		int y = rand() % col; //固定范围为0~2;
		if (chess[x][y] == ' ')  //只有棋子为空时才能写入棋子'#';
		{
			chess[x][y] = '#';
			return 0;         //正确写入退出“电脑下棋”返回 0
		}
	}
	
	//printf("%d %dn",x,y);  //测设专用,此处测试电脑下棋的随机性和正确性
	return -1;   // 非正确写入退出“电脑下棋”返回 -1
}                // 虽然本项目中未用到此功能,而且从逻辑上来说不可能走到这步;

 8.int judge_chess(char chess[ROW][COL], uint row, uint col);
int judge_chess(char chess[ROW][COL], uint row, uint col)   //判断棋局
{
	uint i = 0;    
	uint j = 0;
	
	for (i = 0; i < row; i++)
	{           //例子:chess[i][0] == chess[i][1] && chess[i][1] == '*' 
		if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='*')
		{return '*';}      //玩家横排判定   (博客宽度有限,我把其中空格去掉,
		                   //                   上面例子中空格为平时正常代码)
		if (chess[i][0]==chess[i][1]&&chess[i][1]==chess[i][2]&&chess[i][0]=='#')
		{return '#';}      //电脑横排判定
//-------------------------------------------------------(上面为横排判定)
		if (chess[0][i]==chess[1][i] && chess[1][i]==chess[2][i]&&chess[1][i]=='*')		
		{return '*';}      // 玩家竖排判定

		if (chess[0][i]==chess[1][i]&&chess[1][i]==chess[2][i]&&chess[1][i]=='#')		
		{return '#';}      // 电脑竖排判定
//-------------------------------------------------------(上面为竖排判定)
	}


	if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='*')			
	{return '*';}       // 玩家从左到右对角判定

	if (chess[0][0]==chess[1][1]&&chess[1][1]==chess[2][2]&&chess[1][1]=='#')			
	{return '#';}       // 电脑从左到右对角判定
//-------------------------------------------------------(上面为从左到右对角判定)

	if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='*')			
	{return '*';}       // 玩家从右到左对角判定

	if (chess[0][2]==chess[1][1]&&chess[1][1]==chess[2][0]&&chess[1][1]=='#')			
	{return '#';}       // 电脑从右到左对角判定
//-------------------------------------------------------(上面为从右到左对角判定)

	for (i = 0; i < row; i++)         //当上面未成功时,还有空位就返回' ',
	{                                 //表示棋局继续;
		for (j = 0; j < col; j++)
		{
			if (chess[i][j] == ' ')
				return ' ';
		}
	}
//-------------------------------------------------------(未成功且还有空位判定)

	return '1';  //当棋局未成功且无空位时,判定为平局,返回'1',表示平局;
//-------------------------------------------------------(未成功且无空位,平局判定)
}

 9.void ending(char out);
void ending(char out)   //out值来自棋盘判定函数返回值
{
	switch (out)   //根据judge_chess();返回值判断进入哪个分支
	{
		case '*':  
				printf("您赢了!n恭喜玩家获胜!n"); //游戏玩家赢
				break;
		case '#':
				printf("您输了!n恭喜电脑获胜!n"); //游戏电脑赢
				break;
		case '1':
				printf("平局!n");                  //游戏平局
				break;
	}

	printf("这场游戏结束了。nn");    //多个'n'让后面的菜单和上一场游戏分割开来
}

六、程序验证

刚开始时:

选择退出:

 

 选择进入游戏: (此处打印的棋盘,多次错误,多次调整才建立好打印棋盘函数)

 玩家下棋正常时: (输入坐标,按下回车便会打印棋盘,并且开始电脑下棋并打印)

 玩家输入坐标错误时:

 玩家输入坐标有棋时:

当玩家赢时:

 

当电脑赢时:   (电脑下棋函数很随机,所以电脑很难赢,需要优化)

 平局时:

继续游戏时:

 不继续游戏,退出时:

//----------------------------------------------------------------------------//

 基本把能测试的功能都测试了一遍;

但是在个别的地方进行其他测试可能会发生有趣的现象;

比如:在菜单处选择时输入字母时,此时菜单会无限循环。

如下图:

而此处比如要对菜单的选择输入值进行优化;

不过这是下一步的步骤了;

//----------------------------------------------------------------------------//

 七、优化细节

上面说到菜单处输入字母会循环输出菜单,导致卡死;

这是代码的bug的表现,需要对输入部分进行限制;

//----------------------------------------------------------------------------//以下是我自己优化失败经历;

这期间我把输入值(val_val)类型改为char,但是我还是不满意其表现;

char val_val 表现为输入字母时,还是会循环字母个数遍菜单;

//----------------------------------------------------------------------------//

而我又把char类型改为char []类型,而后面switch只判断char [0]这个首元素,

但对于字母串,还是会循环一遍菜单;而且这样输入1开头的多个数字串时,

都会被认为1,从而进入游戏环节;这又是我不满意的表现,最后我上网去百度;

//----------------------------------------------------------------------------//以上是我自己优化失败经历

网上找到一种scanf()的优化方法;

表现为:

while(scanf("%u",&val_val) != 1) //这个方法可以读到字母时忽略它(百度所知)
		 {
		    getchar();  
			continue;	 
		 }

这种方法可以让val_val读入字母时,不会循环,

他会进入while循环中用getchar();把键盘缓冲区中把非正整数的字符,

用读取的方式,循环去除这些非正整数的字符。然后一直等待你输入正整数为止;

表现:

可以看出无论是字符或者汉字字符,还有字母,都不会出现菜单循环,

除非是出现数字类,才会提醒输入错误,对于此,我很满意了;

于是把这段加入到int main()中:

int main ()
{
	uint val_val = 2;

	do
	{
		menu();
		printf(" >> ");

 		 while(scanf("%u",&val_val) != 1) //这个方法可以读到字母或字符时忽略它
		 {                                //(百度所知)
			char c = getchar();  //这个用char c防止getchar()警告;
			continue;	         //此处char c是无意义的,仅仅只是个人习惯;
		 }    
		
		 switch (val_val)
		 {
			 case 0:	//退出
				 printf("游戏已结束!n");
				 break;
			 case 1:	//进入游戏环节
				 game();
				 break;
			 default://输出错误;
				 printf("输入错误,请重新输入!n");
				 break;
		 }

	} while (val_val);

	return 0;
}

 八、结语

最后这三子棋中,肯定还有其他的地方需要被优化,就需要大家自己去琢磨和思考了;

比如————让电脑更好的下赢棋;

比如————用中的system("cls); 清屏,达到每次下棋都会是全新的棋盘刷新,表现在输出窗口中。达到简洁的目的;

这都需要大家自己去探索了。

还可以修改game.h中的ROW和COL的代表的数。把棋盘变为5*5或10*10或更大......

 不过这样的话,判断棋盘函数 可能就需要重新调整了;

//---------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------

本标题的简简单单可能对初学者不太简单,

需要反复练习代码才能熟练;

最后的最后,希望大家能再进一步,坚持下去,献上我最美好的祝福;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/937857.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号