IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言对三子棋的实现 -> 正文阅读

[C++知识库]C语言对三子棋的实现

目录

前言

一、问题阐述

二、基本流程

三、步骤

1.主体程序

2.game()函数的设计?

1).棋盘的打印

?2).电脑和玩家的落子

?3).胜负的判定

?4).game函数主体?

四、结果演示

?五、代码呈现


前言

在对C语言的学习达到一定的深度时,可以通过对数组,循环语句的灵活运用来实现较为简答的一些小游戏,三子棋就是比较适合初学着来实现是一个游戏,其中不涉及指针和结构体等较为复杂的知识。


一、问题阐述

通过C语言中的数组以及循环来实现三子棋。

二、基本流程

在代码的编写之前,首先需要理清游戏进行的思路。

1.让玩家选择,进行游戏或者退出程序

2.在屏幕上打印出棋盘

3.玩家用‘*’进行落子,电脑随后用‘#’进行落子

4.电脑判断玩家胜利,或是电脑胜利

5通过判断信息,在屏幕上打印出对胜放的断定。

三、步骤

1.主体程序

首先完成主体程序的编写,通过menu()函数实现菜单,选择1进入程序,选择0退出程序。

void menu()
{
	printf("**************************\n");
	printf("********* 1.play *********\n");
	printf("********* 0.exit *********\n");
	printf("**************************\n");
}
int main()
{
	int input = 0;
	srand((unsigned int) time(NULL));
	do //不管有任何指令,都要先执行一次菜单,所以用do while语句
	{
		menu();
		printf("请选择是否游玩:>");
		scanf("%d", &input);
		switch (input)//用switch case语句来实现进入或退出游戏的选择
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏:");
			break;
		default:
			printf("输入错误,请重新输入");
			break;
		}
	} while (input);
	//菜单界面设置为“0.exit”,让输入的值在计算机中为假,放入while循环中即可立刻退出程序,避免了多余的不必要的操作
	return 0;
}

2.game()函数的设计?

在game()函数的主体中,我们要实现棋盘的打印,电脑和玩家的落子,以及胜负的判定。

1).棋盘的打印

首先,运用宏定义便于之后对于棋盘大小的改变,方便后期实现行列的改变,避免了把代码写死。

#define ROW 3
#define COL 3

然后对存放元素的数组初始化为‘ ’(空格),否则在数组中初始化为零的形况下win系统不会打印,会造成棋盘的格式错误。(如下图,对比)

void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

初始化结束后,通过循环的嵌套来实现数组的打印。?

代码1

可以通过如下代码进行打印,过程较为简单。虽然,这样的代码也可以实现三行三列棋盘的打印,但是无法做到随着宏定义行列的改变而一同改变,行可以变换,列会被限制死,不满足高内聚低耦合。不能算是较好的代码。

void show_board(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]);
		//---|---|---
		if(i<row-1)
			printf("---|---|---\n");
	}
}

改变宏定义数值之后效果如下:

代码2

?如下为更加好的写法。

void show_board(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 < row; j++)
			{

				printf("---");
				if (j != COL - 1)
					printf("|");
			}
			printf("\n");
		}
		
	}
}

改变宏定义后可以很好的实现列的改变,效果如下:

?2).电脑和玩家的落子

玩家输入:

通过比对行列,电脑判断落子是否在棋盘内,格子是否已经落过子,之后实现玩家的下棋。但是要注意非程序员玩家不知道数字的下标从0开始,所以要在比对时把输入的行列减一,防止越界。

void my_move(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	while (1)
	{
		scanf("%d %d", &x, &y);
		//非程序员的玩家会按照习惯输入行列,不做改动会引发数组的越界,要减一
		if (x <= row && x >= 1 && y <= col && y >= 1)//此二者的逻辑不能相反,
			                                         //否则无论如何选择都会显示“格子被占用”
		{
			if (board[x - 1][y - 1] == ' ')          //同上
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("此格已被占用,请重新落子。");
		}
		else
			printf("非法输入,请重新落子。");
	}
}

电脑输入:

通过rand()实现随机数的生成,但是多次实验之后发现生成的随机数是固定的,这时就需要在主函数里调用srand()函数,在srand()的函数调用符号中只要加入一个随时随地在变化的数字就可以实现rand()随机数的生成,因此就需要用到时间戳,因为srand的类型是unsigned int 因而需要给时间戳time(NULL)进行强制内容转换?。

所以在上文中的主函数出现了如下图的srand((unsigned int) time(NULL));

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % 3;//通过rand函数实现随机数
		y = rand() % 3;//%3就会产生小于3的整数0,1,2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

?3).胜负的判定

不管哪方胜利,都返回练成三个的符号,不用重新定义其他变量,让代码更加的简洁。平局返回Q,没有填满盘则返回C实现代码继续运行。

//判断二维数组是否已经被填满
static int is_full(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;
}

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0 ;
	//判断列是否相等
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//列是否相同
			return board[0][i];
	}
	//判断行是否相等
	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];
	}
	//判断对角线是否相等
	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];
	//是否平局,数组被填满则为平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

?4).game函数主体?

void game()
{
	char ret = 0;//定义为char使程序更加简洁,具体见判断处
	char board[ROW][COL] = { 0 };//运用二维数字来实现棋盘中元素的存放
	init_board(board, ROW, COL);
	show_board(board, ROW, COL);
	while (1)
	{
		printf("请输入你想要落子的行列:>");
		my_move(board, ROW, COL);
		printf("\n");
		show_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		printf("电脑的落子:");
		printf("\n"); 
		show_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == 'Q')
		printf("平局。\n");
	//通过‘*’和‘#’来判断,让函数可以直接传回棋盘中的落子,无需设置更多的变量,让代码更加简洁
	else if (ret == '*')
		printf("恭喜你,获胜啦!\n");
	else
		printf("电脑都下不赢,你怕不是头猪?!\n");
	printf("\n");
}

四、结果演示

玩家胜利:
?

电脑胜利:

?平局:

五、代码呈现

编写是代码分了三个文件:test.c(main函数和game函数两大代码主体),game.c(各类函数的编写),game.h(函数的头文件和声明)

test.c:

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

void game()
{
	char ret = 0;//定义为char使程序更加简洁,具体见判断处
	char board[ROW][COL] = { 0 };//运用二维数字来实现棋盘中元素的存放
	init_board(board, ROW, COL);
	show_board(board, ROW, COL);
	while (1)
	{
		printf("请输入你想要落子的行列:>");
		my_move(board, ROW, COL);
		printf("\n");
		show_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		printf("电脑的落子:");
		printf("\n"); 
		show_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == 'Q')
		printf("平局。\n");
	//通过‘*’和‘#’来判断,让函数可以直接传回棋盘中的落子,无需设置更多的变量,让代码更加简洁
	else if (ret == '*')
		printf("恭喜你,获胜啦!\n");
	else
		printf("电脑都下不赢,你怕不是头猪?!\n");
	printf("\n");
}

int main()
{
	int input = 0;
	srand((unsigned int) time(NULL));
	do //不管有任何指令,都要先执行一次菜单,所以用do while语句
	{
		menu();
		printf("请选择是否游玩:>");
		scanf("%d", &input);
		switch (input)//用switch case语句来实现进入或退出游戏的选择
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏:");
			break;
		default:
			printf("输入错误,请重新输入");
			break;
		}
	} while (input);
	//菜单界面设置为“0.exit”,让输入的值在计算机中为假,放入while循环中即可立刻退出程序,避免了多余的不必要的操作
	return 0;
}

game.c:

#include"game.h"

void init_board(char board[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
			//把数字初始化为空格,否则在数组中初始化为零的形况下win系统不会打印,会造成棋盘的格式错误
		}
	}
}

//这样的代码也可以实现三行三列棋盘的打印,但是无法做到随着宏定义行列的改变而一同改变,会被限制死为三列,不满足高内聚低耦合
//void show_board(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]);
//		//---|---|---
//		if(i<row-1)
//			printf("---|---|---\n");
//	}
//}

//如下的写法为更加好的写法
void show_board(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 < row; j++)
			{

				printf("---");
				if (j != COL - 1)
					printf("|");
			}
			printf("\n");
		}
		
	}
}

void my_move(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	while (1)
	{
		scanf("%d %d", &x, &y);
		//非程序员的玩家会按照习惯输入行列,不做改动会引发数组的越界,要减一
		if (x <= row && x >= 1 && y <= col && y >= 1)//此二者的逻辑不能相反,
			                                         //否则无论如何选择都会显示“格子被占用”
		{
			if (board[x - 1][y - 1] == ' ')          //同上
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("此格已被占用,请重新落子。");
		}
		else
			printf("非法输入,请重新落子。");
	}
}

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % 3;//通过rand函数实现随机数
		y = rand() % 3;//%3就会产生小于3的整数0,1,2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断二维数组是否已经被填满
static int is_full(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;
}

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0 ;
	//判断列是否相等
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')//列是否相同
			return board[0][i];
	}
	//判断行是否相等
	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];
	}
	//判断对角线是否相等
	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];
	//是否平局,数组被填满则为平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

?game.h:



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

#define ROW 3//运用宏定义便于之后对于棋盘大小的改变,避免了把代码写死
#define COL 3

//棋盘的初始化
void init_board(char board[ROW][COL], int row, int col);

//展示棋盘上的布局
void show_board(char board[ROW][COL], int row, int col);

//玩家在棋盘上落子
void my_move(char board[ROW][COL], int row, int col);

//电脑在棋盘上落子
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char is_win(char board[ROW][COL], int row, int col);

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:25:04  更:2022-08-06 10:28:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:07:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码