1

【C语言题目】玩转三子棋——三子棋的全面解剖

 2 years ago
source link: https://blog.csdn.net/weixin_46913665/article/details/120255462
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.



三子棋是我们先前所学知识的综合运用,它的成功实现会让我们对于C语言的基础知识有一个更深的了解


提示:以下是本篇文章正文内容

一、三子棋完成程序运行结果

在这里插入图片描述

2、棋盘与下棋时简单的交互功能

在这里插入图片描述

3、判断输赢

在这里插入图片描述

上面便是三子棋简单功能介绍。下面我们来用代码实现。

二、三子棋代码实现

1、创建源文件与头文件

【如图】
在这里插入图片描述

2、整体页面的制作

代码如下(示例):

在test.c源文件中

#include "game.h"//引用头文件

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

int main()
{

	int input = 0;
	do
	{
		menu();//菜单函数,打印整个菜单
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			break;
		case 0:
			printf("退出游戏\n");
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}

【打印效果】
在这里插入图片描述

3、制作并打印棋盘

1、在test.c文件中,定义函数game();

#include "game.h"//引用头文件

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

void game()
{
	//三子棋过程
	char board[ROW][COL];//棋盘数组
	//在头文件中直接定义 #define ROW 3  #define COL 3,这时候我们只要包含头文件就可使用
	//用定义的ROW COL,方便以后维护修改代码

	//保存输入的东西,没下棋的时候,棋盘为空,所以要初始化棋盘,也就是把board都给成空格
	InitBoard(board, ROW, COL);//到头文件初始化函数

	//打印棋盘
	DisplaayBoard(board, ROW, COL);
	//本质上打印的是数组的内容,但是打印的时候加了修饰
}
int main()
{

	int input = 0;
	do
	{
		menu();//菜单函数,打印整个菜单
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}

2、在game.h 头文件中

#include <stdio.h>

#define ROW 3
#define COL 3



//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);//因为传过来的是二维数组,所以用二维数组来接受

//打印棋盘
void DisplaayBoard(char board[ROW][COL], int row, int col);

3、在game.c源文件中

#include "game.h"

//在这里实现函数功能

void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)//这里可以直接引用ROW,而不用直接传参过来,原理可行,但代码独立性变差了
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';//里面元素定义为空格
		}
	}
}

void DisplaayBoard(char board[ROW][COL], int row, int col)
{

int i = 0;
	for (i = 0; i < row; i++)
	{
		//打印数据,就是把一行的数据打印出来
		//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);//数据与分割当作一项,这里改成循环
		int j = 0;
		for (j = 0; j < col; j++)//这个循环就把数据与分割当作了一项
		{
			printf(" %c ", board[i][j]);
			if (j<col-1)
				printf("|");
		}
		//这里把列固定写死了,不合适
		printf("\n");

		//打印分割行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j<col-1)
					printf("|");
			}
		}
		printf("\n");
	}

}

4、人机互动下棋

1、在test.c源文件中

#include "game.h"//引用头文件

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

void game()
{
	//三子棋过程
	char board[ROW][COL];//棋盘数组
	//在头文件中直接定义 #define ROW 3  #define COL 3,这时候我们只要包含头文件就可使用
	//用定义的ROW COL,方便以后维护修改代码

	//保存输入的东西,没下棋的时候,棋盘为空,所以要初始化棋盘,也就是把board都给成空格
	InitBoard(board, ROW, COL);//到头文件初始化函数

	//打印棋盘
	DisplaayBoard(board, ROW, COL);
	//本质上打印的是数组的内容,但是打印的时候加了修饰


	//下棋,一人走一步,因此用while循环
	while (1)
	{
		//玩家下
		PlayerMove(board, ROW, COL);//即达到了3,3的效果,又让代码可读性更高,同时未来我们涉及到行和列的地方都可以使用ROW,COL
		DisplaayBoard(board, ROW, COL);//玩家下完棋之后,打印棋盘观察结果

		//电脑下
		ComputerMove(board, ROW, COL);//电脑应该随机下棋
		DisplaayBoard(board, ROW, COL);//电脑下完棋之后,打印棋盘观察结果
	}
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//设计随机数生成,用time函数
	do
	{
		menu();//菜单函数,打印整个菜单
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}

2、在game.h头文件中

#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 DisplaayBoard(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);

3、在game.c源文件中

#include "game.h"


//在这里实现函数功能
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)//这里可以直接引用ROW,而不用直接传参过来,原理可行,但代码独立性变差了
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';//里面元素定义为空格
		}
	}
}


void DisplaayBoard(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]);
			if (j<col - 1)
				printf("|");
		}
		printf("\n");
		//打印分割行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j<col - 1)
					printf("|");
			}
		}
		printf("\n");
	}

}

void PlayerMove(char board[ROW][COL], int row, int col)
{
	printf("玩家走:\n");
	int x = 0;
	int y = 0;
	while (1)//知道走对了位置,才跳出循环
	{
		printf("请输入坐标:");
		scanf("%d%d", &x, &y);//用户输入的是(2,1),但在数组里面显示是(1,0)
		if (x >= 1 && x <= row&&y >= 1 && y <= col)//输入的x,y在这个范围内,坐标是合法的
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;//玩家下棋成功,break跳出循环
			}
			else
			{
				printf("坐标被占用,请重新输入!\n");
			}
		}
		else//其余是非法的
		{
			printf("坐标非法,超出范围");
		}
	}


}

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑走:\n");
	while (1)
	{
		x = rand() % row;//生成的数字要合法范围内,所以模上行数row,row为3,——0-2的范围
		y = rand() % col;//生成的数字要合法范围内,所以模上列数col,col为3,——0-2的范围
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';//玩家下棋用#代替
			break;
		}

	}
}

4、此时打印效果

在这里插入图片描述

在这里,代码能走,但不能判断输赢,所以接下来,我们实现最后一步判断输赢

5、判断输赢

1、在test.c源文件中

#include "game.h"//引用头文件

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

void game()
{
	//三子棋过程
	char board[ROW][COL];//棋盘数组
	//在头文件中直接定义 #define ROW 3  #define COL 3,这时候我们只要包含头文件就可使用
	//用定义的ROW COL,方便以后维护修改代码

	//保存输入的东西,没下棋的时候,棋盘为空,所以要初始化棋盘,也就是把board都给成空格
	InitBoard(board, ROW, COL);//到头文件初始化函数

	//打印棋盘
	DisplaayBoard(board, ROW, COL);
	//本质上打印的是数组的内容,但是打印的时候加了修饰


	//下棋,一人走一步,因此用while循环
	char ret = 0;
	while (1)
	{
		//玩家下
		PlayerMove(board, ROW, COL);//即达到了3,3的效果,又让代码可读性更高,同时未来我们涉及到行和列的地方都可以使用ROW,COL
		DisplaayBoard(board, ROW, COL);//玩家下完棋之后,打印棋盘观察结果
		ret = Iswin(board, ROW, COL);//判断输赢的函数,有返回值,判断结果

		if (ret != 'C')//如果不是返回其他三种情况,游戏结束
		{
			break;//跳出循环
		}
		//电脑下
		ComputerMove(board, ROW, COL);//电脑应该随机下棋
		DisplaayBoard(board, ROW, COL);//电脑下完棋之后,打印棋盘观察结果
		ret = Iswin(board, ROW, COL);//判断输赢的函数,有返回值,判断结果

		if (ret != 'C')//如果不是返回其他三种情况,游戏结束
		{
			break;//跳出循环
		}

		ret = Iswin(board, ROW, COL);//判断输赢的函数,有返回值,判断结果
		if (ret != 'C')//如果不是返回其他三种情况,游戏结束
		{
			break;//跳出循环
		}
	}

	if (ret == '*')//如果不是返回其他三种情况,游戏结束
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
	{
		printf("平局\n");
	}
	DisplaayBoard(board, ROW, COL);//无论结果怎样,都把最终棋盘打印出来
	//需要更改行和列的时候,直接回到头文件中改就可以了
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//设计随机数生成,用time函数
	do
	{
		menu();//菜单函数,打印整个菜单
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
}

2、在game.h头文件中

#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 DisplaayBoard(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);

//判断游戏输赢
//要返回四种不同的状态
//1、玩家赢——返回‘*’
//2、电脑赢——返回‘#’
//3、平局——返回‘Q’
//4、继续——返回‘C’
char Iswin(char board[ROW][COL], int row, int col);

3、在game.c源文件中

#include "game.h"


//在这里实现函数功能
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)//这里可以直接引用ROW,而不用直接传参过来,原理可行,但代码独立性变差了
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';//里面元素定义为空格
		}
	}
}


void DisplaayBoard(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]);
			if (j<col - 1)
				printf("|");
		}
		printf("\n");
		//打印分割行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j<col - 1)
					printf("|");
			}
		}
		printf("\n");
	}

}

void PlayerMove(char board[ROW][COL], int row, int col)
{
	printf("玩家走:\n");
	int x = 0;
	int y = 0;
	while (1)//知道走对了位置,才跳出循环
	{
		printf("请输入坐标:");
		scanf("%d%d", &x, &y);//用户输入的是(2,1),但在数组里面显示是(1,0)
		if (x >= 1 && x <= row&&y >= 1 && y <= col)//输入的x,y在这个范围内,坐标是合法的
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;//玩家下棋成功,break跳出循环
			}
			else
			{
				printf("坐标被占用,请重新输入!\n");
			}
		}
		else//其余是非法的
		{
			printf("坐标非法,超出范围");
		}
	}


}

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑走:\n");
	while (1)
	{
		x = rand() % row;//生成的数字要合法范围内,所以模上行数row,row为3,——0-2的范围
		y = rand() % col;//生成的数字要合法范围内,所以模上列数col,col为3,——0-2的范围
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';//玩家下棋用#代替
			break;
		}

	}
}


//返回1表示棋盘满了
//返回0表示棋盘没满
int IsFull(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;//棋盘不满返回0
			}
		}
	}
	return 1;//棋盘满了返回1,

}


char Iswin(char board[ROW][COL], int row, int col)
{
	//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][1] != ' ')
			return board[i][1];
	}
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[i][1] != ' ')
			return board[1][i];
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		return board[1][1];
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
		return board[1][1];


	//2、判断平局
	if (1 == IsFull(board, ROW, COL))
	{
		return 'Q';
	}

	//3、游戏继续
	return 'C';
}

4、最终实现结果

在这里插入图片描述


以上是通过本人学习的理解和网上资料的整理有关三子棋代码实现的内容,有错漏之处,还请各位多多包涵与指出,共同进步,共同成长!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK