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 语言来简单的实现它。

一、游戏预期画面

在这里插入图片描述

二、游戏的实现

1、菜单

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

2、主函数

首先,我们思考一下,本次游戏应该最少执行一次,在执行过程中进行选择,那么我们应该使用 do while 循环来实现。根据选择不同,来执行相应的程序,那么应该使用 switch 语句。
下面我们来看代码:

int main()
{
	int input = 0;
	do
	{
		menu();                        //每次在选择前打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();                    //开始游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);                //根据输入的值来确定是否进行循环,0则退出循环
	return 0;                       //这也是为什么在菜单中把退出游戏设置成0的原因
}

3、game函数的实现

3.1、棋盘的打印

我们根据预期画面可以判断出,我们应该创建一个 3*3 的数组来存放我们下的棋,并且得把数组初始化为空格。我们可以根据坐标来选择落子位置。那么问题来了,我们应该如何打印棋盘呢?

首先,我们来仔细观察一下画面中的棋盘,我们可以发现每次下棋都是下在格子的中间也就是下图:
在这里插入图片描述

也就是说每个数的旁边有空格,同行的数之间用 | 隔开,第二行是由 - 和 | 组成。
我们把第一行和第二行看成一组,我们就可以得到三组(假设第三组第二行存在)
也就是像下图:

在这里插入图片描述
这样我们便能写出如下代码:

#define M 3
#define N 3
void game()
{
	char arr[M][N] = { 0 };
	chu_shi(arr, M, N);                 //初始化数组
	da_yin(arr, M, N);                  //打印棋盘
}
void chu_shi(char arr[M][N], int m, int n)
{
	int i = 0;
	int j = 0;
	for(i=0;i<m;i++)
		for (j = 0; j < n; j++)
		{
			arr[i][j] = ' ';           //将数组初始化为空格
		}
}
void da_yin(char arr[M][N], int m, int n)         
{
	int i = 0;
	for (i; i < m; i++)
	{
		printf(" %c | %c | %c \n", arr[i][0], arr[i][1], arr[i][2]);
	}
	if (i < m - 1)                  //若没有 if 则打印了第三组的第二行,if 起限制作用
		printf("---|---|---\n");
}

这样,我们就打印出来了整个棋盘。但有个小问题,就是如果我改动棋盘大小呢,现在是 3 * 3 ,我改动 M N 的值为 5 5 呢,我们可以看出,行是没错的,但是列只打印了三列,这明显不行,于是我们按照打印行的思路来打印列,同样的将列分成三组,如图:

在这里插入图片描述
我们可以将打印棋盘代码优化成如下:

void da_yin(char arr[M][N], int m, int n)
{
	int i = 0;
	for (i; i < m; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			printf(" %c ", arr[i][j]);
			if (j < n - 1)               //限制 | 的打印
				printf("|");
		}
		printf("\n");
		if (i < m - 1)
		{
			for (j = 0; j < n; j++)
			{
				printf("---");
				if (j < n - 1)           //限制 | 的打印
					printf("|");
			}
		}
		printf("\n");
	}
}

3.2、玩家下棋

接下来我们应该开始正式玩游戏了,也就是输入落子位置。
我们思考一下,我们坐标应该合理,不能超过数组规定,还有我们下的子应该保存下来,下次该点不能被选择。

我们可以写出如下代码:

void wan_jia(char arr[M][N], int m, int n)
{                              
	int x = 0;
	int y = 0;                  //因为玩家可能不知道是从0行0列开始的
	printf("玩家下棋:>\n");     //所以我们就设置成从第1行1列到第3行3列
	while (1)              
	{
		printf("请输入要下棋的坐标:>\n");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= m && y >= 1 && y <= n)
		{                        
		if (arr[x - 1][y - 1] == ' ')
			{
				arr[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法请重新输入");
		}
	}
}

3.3、电脑下棋

现在我们下了一步,接下来轮到电脑了,同样的逻辑,但电脑下棋是随机的,因此我们要生成随机数,随机数的生成代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
	srand((unsigned int)time(NULL));    //设置一个随机数的生成器	
	int m = rand()%3//因为 rand 生成随机数范围0~32767
	printf("%d",m);                   //所以 %3 使随机数生成范围为0~2
}

因此电脑下棋代码我们就可以写出了:

srand((unsigned int)time(NULL));     //我们需要放在 main 函数中
void dian_nao(char arr[M][N], int m, int n)
{
	printf("电脑下棋:>\n");
	while (1)
	{
		int x = rand() % m;
		int y = rand() % n;
		if (arr[x][y] == ' ')
		{
			arr[x][y] = '#';
			break;
		}
	}
}

这样我们就能完成玩家与电脑之间的对弈,但是美中不足的是,我们无法判断输赢。因此,下一步我们判断输赢。

3.4、判断输赢

我们知道游戏过程中会有几种状态:玩家赢、电脑赢、平局、游戏继续,这四种状态我们就分别用 ’ * ’ 、’ # ‘、’ Q ‘、’ C '来作为返回值。
我们知道三子棋的无非就是横或竖或斜三子成线,这样我们便能写出如下代码:

char is_win(char arr[M][N], int m, int n)      
{                                //我们要返回字符所以用 char 
	int i = 0;
	for (i = 0; i < m; i++)     //判断行
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
		{
			return arr[i][0];
		}
	}
	for (i = 0; i < n; i++)     //判断列
	{                         
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
		{
			return arr[0][i];
		}
	}
	       //以下判断两条斜线
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')                      
	{
		return arr[0][0];
	}
	if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
	{
		return arr[1][1];
	}
}

我们已经可以判断输赢了,但是我们似乎还漏了平局。
我们便再创建个函数来遍历 arr 数组,来判断是否还有空格,如果有也就是棋盘没满,返回0,反之返回1。
由此,我们可以写出如下代码:

//如果棋盘满了,返回1
//不满返回0
int is_full(char arr[M][N], int m, int n)
{
	int i = 0;
	for (i = 0; i < m; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			if (' ' == arr[i][j])
				return 0;
		}
	}
	return 1;
}

我们再将其插入判断属于代码中,可以得到:

char is_win(char arr[M][N], int m, int n)
{
	int i = 0;
	for (i = 0; i < m; i++)
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
		{
			return arr[i][0];
		}
	}
	for (i = 0; i < n; i++)
	{
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
		{
			return arr[0][i];
		}
	}
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
	{
		return arr[0][0];
	}
	if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
	{
		return arr[1][1];
	}
	//判断平局
	if (is_full(arr, m, n) == 1)
	{
		return 'Q';
	}

	//继续
	return 'C';
}

3.5、game函数的真正实现

我们将 game 函数的实现过程,调用的函数了解了一遍,下面我们来看具体 game 函数如何实现:

void game()
{
	char ret = 0;
	char arr[M][N] = { 0 };
	chu_shi(arr, M, N);       //首先,我们初始化数组
	da_yin(arr, M, N);        //打印一下棋盘
	while (1)                 //进行游戏
	{
		wan_jia(arr, M, N);         //玩家先下
		ret = is_win(arr, M, N);    //每下一步进行判断
		if (ret != 'C')             //判断是否出现输赢或平局·
			break;             //'C'代表继续,不等于 'C' 即出现了结果
                              //break 跳出循环
		da_yin(arr, M, N);         //每下完一步再打印一下棋盘

		dian_nao(arr, M, N);       //电脑下
		ret = is_win(arr, M, N);
		if (ret != 'C')
			break;

		da_yin(arr, M, N);
	}
	if (ret == '*')
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else
		printf("平局\n");
	da_yin(arr, M, N);        //最后游戏结束打印棋盘
}

注意,我们判断输赢都是在打印棋盘之前的,如果出了结果,我们是不知道棋盘状况的,所以游戏最后应该打印棋盘。

4、游戏具体代码

像这样一个游戏,我们应该分三个部分:game.h、test.c、game.c。
game.h : 包含头文件和对 test.c 中函数的声明。
test.c :将函数封装起来,使得 game.c 尽量简洁
game.c : 游戏主体,包含 main 函数。

4.1、game.h

#pragma once
#include<stdio.h>

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

#define M 3
#define N 3

//初始化数组
void chu_shi(char arr[M][N], int m, int n);
//打印棋盘
void da_yin(char arr[M][N], int m, int n);
//玩家下棋
void wan_jia(char arr[M][N], int m, int n);
//电脑下棋
void dian_nao(char arr[M][N], int m, int n);
//判断游戏状态
char is_win(char arr[M][N], int m, int n);

4.2、test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
     //初始化数组
void chu_shi(char arr[M][N], int m, int n)
{
	int i = 0;
	int j = 0;
	for(i=0;i<m;i++)
		for (j = 0; j < n; j++)
		{
			arr[i][j] = ' ';
		}
}
       //打印棋盘
void da_yin(char arr[M][N], int m, int n)
{
	int i = 0;
	for (i; i < m; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			printf(" %c ", arr[i][j]);
			if (j < n - 1)
				printf("|");
		}
		printf("\n");
		if (i < m - 1)
		{
			for (j = 0; j < n; j++)
			{
				printf("---");
				if (j < n - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}
        //玩家下棋
void wan_jia(char arr[M][N], int m, int n)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋:>\n");
	while (1)
	{
		printf("请输入要下棋的坐标:>\n");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= m && y >= 1 && y <= n)
		{
			if (arr[x - 1][y - 1] == ' ')
			{
				arr[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法请重新输入");
		}
	}
}
      //电脑下棋
void dian_nao(char arr[M][N], int m, int n)
{
	printf("电脑下棋:>\n");
	while (1)
	{
		int x = rand() % m;
		int y = rand() % n;
		if (arr[x][y] == ' ')
		{
			arr[x][y] = '#';
			break;
		}
	}
}
   //如果棋盘满了,返回1
   //不满返回0
int is_full(char arr[M][N], int m, int n)
{
	int i = 0;
	for (i = 0; i < m; i++)
	{
		int j = 0;
		for (j = 0; j < n; j++)
		{
			if (' ' == arr[i][j])
				return 0;
		}
	}
	return 1;
}
         //判断输赢
char is_win(char arr[M][N], int m, int n)
{
	int i = 0;
	for (i = 0; i < m; i++)
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
		{
			return arr[i][0];
		}
	}
	for (i = 0; i < n; i++)
	{
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
		{
			return arr[0][i];
		}
	}
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
	{
		return arr[0][0];
	}
	if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
	{
		return arr[1][1];
	}
	//判断平局
	if (is_full(arr, m, n) == 1)
	{
		return 'Q';
	}

	//继续
	return 'C';
}

4.3、game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"        //包含 game.h
void menu()
{
	printf("*****************************\n"); 
	printf("*******    1.play    ********\n"); 
	printf("*******    0.exit    ********\n");
	printf("*****************************\n ");
}
void game()
{
	char ret = 0;
	char arr[M][N] = { 0 };
	chu_shi(arr, M, N);
	da_yin(arr, M, N);
	while (1)
	{
		wan_jia(arr, M, N);
		ret = is_win(arr, M, N);
		if (ret != 'C')
			break;

		da_yin(arr, M, N);

		dian_nao(arr, M, N);
		ret = is_win(arr, M, N);
		if (ret != 'C')
			break;

		da_yin(arr, M, N);
	}
	if (ret == '*')
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else
		printf("平局\n");
	da_yin(arr, M, N);
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));     //设置一个随机数的生成器
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

这样我们的三子棋游戏就圆满完成了,咋一看似乎头晕晕的,但是如果我们把它拆分为一个个小步骤,我们便能轻易理解并写出来。
学习就是这样,碰到难题往往拆分步骤就变简单了。

下期预告

下期肯定要给大家带来惊险又刺激的扫雷了。
那我们下期见了~

  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:29:41 
 
开发: 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年4日历 -2024/4/26 1:23:31-

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