您现在的位置是:首页 >技术交流 >C语言:简单的三子棋练习网站首页技术交流

C语言:简单的三子棋练习

YC@_@ 2024-06-14 17:20:31
简介C语言:简单的三子棋练习


前言

三子棋练习对前面所学的知识进行运用与检验,三子棋这里就需要进行对函数进行封装成功能函数,因为三子棋代码较长肯定不能全部放在main函数中,要是全部放在main函数中代码很难重复使用,可读性也不好。
只要把函数封装成功能函数,代码的可读会也会提高,因为只要在函数前加注释就能很轻松的找到所需要的功能函数,也可以重复使用。


一、总体思路

需要完成的效果图
在这里插入图片描述

接下来介绍一下需要完成的功能

这里打错字了为了节省时间暂时先不改,
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

先来一起思考大概的制作流程
第一步需要打印开始与结束游戏,这一步可想而知就是直接用printf打印出来就行了。

第二步这里需要完成选择开始的效果,这里用switch就能完成选择的效果。

第三步就是打印棋盘,先观察棋盘,棋盘里面一共有三种字符 空格跟分隔符与斜杠,只要用多层循环就能实现,对不对还得自己动手试。
第四步需要人机交互,这里可以先把人机交互功能分给两个功能函数实现。
第五步用常用的输入库函数scanf实现。

这里在补充一点,人机交互肯定是需要重复执行,所以这里直接用死循环


二、头文件

头文件比较重要还是单独拿出来讲解

什么是头文件,在我理解看来,一个项目中有多个源文件,打个比方A文件与B文件他们直接是独立存在的,要是A文件想调用B文件的函数,就得在头文件中声明B文件的函数就行了,话说回来头文件就是让两个独立的源文件直接存在联系。

三子棋头文件完整代码

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

//符合定义
#define ROW 3
#define COL 3

//函数声明  
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void playerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);


//判断输赢
//玩家 - 'x'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col);


三、实现流程

说了这么还是得动手实操,来跟我一起深入浅出的学习吧!

1、第一步

文件目录

game.h头文件用来声明函数
game.c游戏功能的实现
test游戏主体,从这个开始调用game.c

以上后面会细讲

在这里插入图片描述


这里是函数主体布局完整代码,我会分几步讲解

代码如下(示例):

int main()
{
	int input = 0;
	do					
	{
		menu();			//开始界面打印
		printf("请选择>");
		scanf("%d", &input);	//输入坐标
		switch (input)			//开始与结束选项
		{
			case 1:
				printf("三子棋游戏
");
				game();		//三子棋内部的实现
				break;
			case 0:
				printf("退出游戏
");
				
			default:
				printf("选择错误,请重新输入
");
				break;
		}
	} while (input);

		return 0;
}

打印开始界面其实很简单用printf就能实现。


1.0、do-while补充说明

这里在对do-while循环不熟的人补充说明一下,对这个人就是我。do-while先执行在判断,判断结果为假直接结束,所以在这里运行do-while非常合适。

do
{

}
while();

1.1、menu()

开始界面打印
代码如下(示例):

void menu()
{
	printf("*********************************
");
	printf("*********   1.play   ************
");
	printf("*********   0.exit   ************
");
	printf("*********************************
");
}

**打印开始界面其实很简单用printf就能实现。**


二、game函数三子棋核心功能

1.0game函数内部讲解

game()函数是三子棋核心函数

		switch (input)
		{
			case 1:
				printf("三子棋游戏
");
				game();		//game()函数是三子棋核心函数
				break;
			case 0:
				printf("退出游戏
");
				
			default:
				printf("选择错误,请重新输入
");
				break;
		}

完整game功能函数

void game()
{
	//存储数据 - 二维数组
	char board[ROW][COL];
	//初始化棋盘 - 初始化
	InitBoard(board, ROW, COL);
	//打印一下棋盘 - 本质是打印数组的内容
	DisplayBoard(board, ROW, COL);
	char ret = 0;
	while (1)
	{
		//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;

		//玩家下棋
		playerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret=IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("玩家赢!
");
	else if (ret == '#')
		printf("电脑赢!
");
	else
		printf("平局!
");
}

1.1、InitBoard(初始化棋盘)

InitBoard完整代码

//初始化棋盘
void InitBoard(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] = ' ';
		}
	}
}

补充#define知识

#define预处理指令又称宏定义,可以定义常量往深入讲也可以定义函数,在三子棋中#define用来定义常量

	#define ROW 3
	#define COL 3
	//二维数组用来存储棋盘数据
	char board[ROW][COL];		//这种定义的好处是可以让三子棋变成五子棋
	//初始化棋盘 - 初始化
	InitBoard(board, ROW, COL);			//1.把数组传入,2.初始化棋盘时用循环时肯定需要一个限定值,那就得把行跟列传过去
void InitBoard(char board[ROW][COL], int row, int col)	//函数传参,char board[ROW][COL]这里传了个二维数组,后面的传的就是行跟列
{
	
	int i = 0;
	for (i = 0; i< row; i++)	//初始化行
	{																
		int j = 0;
		for (j = 0; j < col; j++)	//初始化列
		{
			board[i][j] = ' ';		//把初始化的空格传给二维数组
		}
	}
}

1.2、DisplayBoard(打印棋盘完整代码)

DisplayBoard完整代码

//打印棋盘
void DisplayBoard(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]);	//数据的打印,%c两边还有两空格让打印的数据更加美观
			if (j < col - 1)		//
			 	printf("|");
		}
		printf("
");
		if (i < row - 1)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("
");
		}
	}
}

1.2.1、数据与分隔符的打印

为了让代码变得更加灵活,可以先遍历行跟列,让数据一个一个打印,在这里只需要改变行跟列的值就可以让棋盘变成n子棋,在这里打印的循序是先打印数据在打印|然后在打印—,

for (i = 0; i < row; i++)		//遍历行
	{
		int j = 0;
		for (j = 0; j < col; j++)		//遍历列
		{
			printf(" %c ", board[i][j]);	//数据的打印,%c两边还有两空格让打印的数据更加美观
			if (j < col - 1)		//col - 1为了美观行会少打印最后一列
			 	printf("|");
		}
		printf("
");		//第一行数据跟分隔符打印完换行
		if (i < row - 1)		//少打印最后一行---
		{
			int j = 0;
			for (j = 0; j < col; j++)		//换行后在打印---
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("
");
		}

1.3、playerMove(玩家下棋)

playerMove完整代码


//玩家下棋
void playerMove(char board[ROW][COL], int row, int col)
{
	//定义x,y坐标
	int x = 0;	
	int y = 0;
	printf("玩家走:>
");

	while (1)		//死循环
	{
		printf("请输入下棋坐标:>");
		scanf("%d %d", &x, &y);
		//判断坐标合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//下棋
			//坐标是否被占用
			if (board[x - 1][y - 1] == ' ')		//数组下标是从0开始计算,正常人思维是从1开始计算,所以要-1
			{
				//x-1是之前x>1判断坐标是否被占用
				//玩家下棋
				board[x - 1][y - 1] = '*';
				break;		//下完一次退出
			}
			else
			{
				printf("坐标被占用,请重新输入
");
			}
		}
		else
		{
			printf("坐标非法,请重新输入
");
		}
	}
	
}

1.4、ComputerMove(电脑下棋)

ComputerMove完整代码

//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	printf("电脑下棋:>
");
	//给电脑随机下棋坐标
	while (1)
	{
		//电脑自动下棋核心部分
		x = rand() % row;		//限制电脑x的坐标,假如row值为3,行随机范围为0-3,y同理
		y = rand() % col;

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

1.5、重难点判断输赢

判断输赢
玩家 - ‘x’
电脑赢 - ‘#’
平局 - ‘Q’
继续 - ‘C’

IsWin完整代码

//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
	//赢
	//行
	int i = 0;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	//列
	for (i = 0; i < row; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];

		}
	}
	//对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		return board[1][1];
	if(board[0][2] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		return board[1][1];
	//平局

	if (IsFull(board, row, col)==1)
	{
		return 'Q';
	}
	//继续
	return 'C';
}

1.5.1、行

加张图有利于理解
在这里插入图片描述

用循环遍历每行下棋情况,还有就是不能等于空格

//行
	int i = 0;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];		//返回第一列遍历每行的值
		}
	}
	

1.5.2、列

加张图有利于理解
在这里插入图片描述

跟行同理

//列
	for (i = 0; i < row; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];

		}
	}
	

1.5.3、对角线

加张图有利于理解
在这里插入图片描述

//对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		return board[1][1];
	if(board[0][2] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		return board[1][1];

1.5.4、平局

棋盘man了返回Q

if (IsFull(board, row, col)==1)
	{
		return 'Q';
	}

IsFull完整代码

棋盘为空格返回0,棋盘满了返回1

int IsFull(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++)
		{
			if (board[i][j]==' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

1.5.5、继续

不是以上任何一种情况直接返回C

//继续
	return 'C';

三、结尾函数的嵌套调用

玩家先后的下棋顺序可以调换顺序,最后的输赢打印放在while循环外就行就可以不需要重复打印

char ret = 0;
	while (1)
	{
		//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')		//只要不等于C结束游戏
			break;

		//玩家下棋
		playerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret=IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("玩家赢!
");
	else if (ret == '#')
		printf("电脑赢!
");
	else
		printf("平局!
");

总结

三子棋代码比较长,难度适中能很好的检验前面所学知识,有理解不好的地方还请大佬指教!

字算比较多有打错字的地方可以在评论区指正

希望能帮到各位

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。