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语言的三子项目讲解

Gitee源码点击我

Part 1 项目分析

一、游戏介绍

游戏在九宫格的棋盘中进行,由两位玩家落子,任意横排,竖排,对角线连成三个即为获胜
对于三子棋游戏需要呈现的内容:
①需要呈现给玩家一个3*3的棋盘
②区分两名玩家的棋子,本项目中为玩家和电脑对局。
③根据落子情况判断是否由任意横排三个格子,竖排三个格子,对角线三个格子的落子都为同一玩家的落子。

二、项目结构

项目采用两个.c文件和一个.h文件的结构来编写项目。
test.c文件中为整个游戏的框架
game.c文件中将游戏中具体的不同的功能编写函数的具体内容
game.h文件中进行函数声明,以及所需的库函数的调用

image.png


Part 2 功能的代码实现

一、主函数框架

首先我们先做出一个游戏大致的框架,从主菜单→用户选择→开始游戏或者退出游戏。

[test.c]
void menu() //菜单函数,1选项为开始游戏,0选项为退出游戏
{
	printf("***********************\n");
	printf("********1.play*********\n");
	printf("********0.exit*********\n");
	printf("***********************\n");
}

void game()
{
    //game函数中实现整个游戏的逻辑
    //而游戏的细化成不同部分的功能,再分别用函数实现
}


int main
{
	int n = 0;  // 记录用户的选项
    scanf("%d",&n);
    do
    {
    	menu(); //打印菜单
        switch(n)
        {
        case 1:  //用户选择1,则进入游戏
        	game();  //进入游戏后,调用game函数,game函数的内容为三子棋游戏的逻辑实现
            break;
        case 0:  //用户选择0,则进入游戏
        	printf("thanks");
        default:
        	printf("输入有误,请重新输入!");
        }
	}while (n);
}

二、功能① —— 棋盘的呈现以及程序框架:

利用**下划线 ’ _ '竖线 ’ | '**的组合,呈现一个3*3的棋盘。如下图

image.png

从用户角度来看,用户在某个格子落子后,该格子就会从空白的状态变为有棋子的状态。

若我们将棋盘和二维数组关联,数组的行和列对应棋盘的行和列。则整个游戏本质上就变成了对二维数组元素的操作,一个格子即对应一个数组元素,不落子时,所有的数组元素值都设置为空格符。玩家或电脑在某一格落子时,则将该格对应的数组元素的值替换成代表棋子的字符(设玩家棋子为"#“,电脑棋子为”*")

于是我们先创建一个和棋盘同规模的数组,并将其初始化

[game.h] 头文件中包含了项目需要的库函数,定义的全局变量以及函数声明。
#include<stdio.h>
#define ROW 3
#define COL 3
//定义全局的变量,分别代表行和列。假设在以后需要修改棋盘的规模,直接修改全局变量的值即可,方便项目的维护和更改。

void InitBoard(char board[ROW][COL],int row,int col);//每定义一个函数,要在头文件处声明。函数必须先声明后调用


--------------------------------------------------------------------------

[game.c] game.c文件中包含游戏具体每个功能的函数实现
#include "game.h" 

void InitBoard(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++)
        {
        	board[i][j] = ' ';
        }
    }
}

--------------------------------------------------------------------------

[test.c]
#include "game.h" //.c文件中只需包含一下头文件,即可使用头文件中的库函数,以及读取到函数的声明

void menu(){...} 

void game()
{
	char board[ROW][COL] = {0}; //创建一个和棋盘同规模的数组
    
    InitBoard(board[ROW][COL],ROW,COL); //调用函数,初始化数组
}

int main(){...}

初始化完成后,便是打印棋盘的工作:

[game.h]
#include<stdio.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);

--------------------------------------------------------------------------
[game.c]
#include "game.h" 

//数组初始化函数
void InitBoard(char board[ROW][COL],int row,int col){...}

//打印棋盘函数
void DisplayBoard(char board[ROW][COL],int row,int col)
{
	int i = 0;
    int j = 0;
    printf(" ");
    for (i = 1; i <= col; i++)
    {
        printf(" %d  ", i);
    }
    printf("\n");
    for (i = 0; i < row; i++)
    {
        printf("%d", i + 1);
        for (j = 0; j < col; j++)
        {
            
            printf(" %c ",board[i][j]);
            if (j < col - 1)
            {
                printf("|");
            }
        }
        printf("\n");
        printf(" ");
        for (j = 0; j < col; j++)
        {
            
            if (i < row - 1)
            {
                printf("---");
                if (j < col - 1)
                {
                    printf("|");
                }
            }
        }
        printf("\n");
    }
}
--------------------------------------------------------------------------
    
[test.c]
#include "game.h" 

void menu(){...} 

void game()
{
	char board[ROW][COL] = {0}; 
    
    InitBoard(board[ROW][COL],ROW,COL); 
    DisplayBoard(board[ROW][COL],ROW,COL);//调用函数,打印棋盘
}

int main(){...}

[解析]
image.png
棋盘分三行打印,每行的打印中包括两个模块
模块一:[空格符]+[数组元素占用]+空格符+’ |
由于最后一列不需要打印竖线,则竖线’ | '单独分出来加上前置条件打印

模块二:‘—’ +’ | ’
由于最后一行不需要打印,所以加上前置条件再打印
[优化]
为了方便用户输入坐标,可以标明棋盘的横纵坐标
image.png
优化的代码也写入了上述代码中。

三、功能② —— 实现玩家输入坐标的落子功能

其实现思想为:玩家输入坐标,程序将该坐标对应到二维数组的元素,并将其替换成玩家的棋子。而在初始化棋盘的时候,我们已经将数组的每个元素初始化为空格符’ ‘。即实现过程为:玩家输入坐标→程序找到对应的数组元素→将空格符替换成玩家棋子**’ * '。**

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
    int y = 0;
    printf("请输入坐标落子:>");
    scanf("%d %d", &x, &y);
    board[x - 1][y - 1] = '*';
}

image.png

除了落子之外,我们还需要考虑下面两个问题:

  1. 如果玩家输入的坐标不在棋盘范围之内(即数组的越界访问),程序该做何回应
  2. 如果玩家输入的坐标处已经有棋子了,程序该作何回应

解决方案:

  1. 规定好玩家的输入范围,如果越界,则提示输入错误,并重新输入
  2. 输入坐标后,程序检查该坐标处对应的数组元素值是否为空格符,如果不为空格符,则表示该格已经落子
[game.h]
#include<stdio.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);

--------------------------------------------------------------------------
[game.c]
#include "game.h" 

//数组初始化函数
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)
{
	int x = 0;
	int y = 0;
	printf("请输入坐标落子:>");
	while (1)
	{
		scanf("%d %d", &x, &y);
		if ((x > 0 && x < ROW) && (y > 0 && y < COL))
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该格已经落子,请重新输入:>");
			}
		}
		else
		{
			printf("输入有误,请输入1-3之间的数字:>");
		}
	}
}
--------------------------------------------------------------------------
    
[test.c]
#include "game.h" 

void menu(){...} 

void game()
{
	char board[ROW][COL] = {0}; 
    
    InitBoard(board[ROW][COL],ROW,COL); 
    DisplayBoard(board[ROW][COL],ROW,COL);
    
    while(1) //此处加循环时为了方便观察落子的条件判断是否生效
    {
        PlayerMove(board[ROW][COL],ROW,COL);//调用函数,让玩家落子
    	DisplayBoard(board, ROW, COL); //落子之后再次打印棋盘
    }
    
}

int main(){...}

[运行结果]
image.png

四、功能③ —— 电脑落子

玩家落子之后,轮到电脑落子。而电脑落子需要解决的问题:

  1. 随机性(由于是简单项目,不考虑电脑的智能下棋)
  2. 随机坐标不可越界
  3. 随机落子时需要检验该坐标下是否有棋子,若有棋子则需要再重新生成坐标

解决方案

  1. 利用随机函数rand和srand来实现随机坐标的生成
  2. 生成随机数%3,则得到的结果只能为0,1,2。解决了越界的问题
  3. 重复检测与玩家落子函数中的重复检测相同
[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 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);

--------------------------------------------------------------------------
    
[game.c]
#include "game.h" 

//数组初始化函数
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)
{
	int x = rand() % ROW;
	int y = rand() % COl;
	while (1)
	{
		if (board[x][y] == ' ')
		{
			board[x][y] = '*';
			break;
		}
	}
}

--------------------------------------------------------------------------
    
[test.c]
#include "game.h" 

void menu(){...} 

void game()
{
	char board[ROW][COL] = {0}; 
    
    InitBoard(board[ROW][COL],ROW,COL); 
    DisplayBoard(board[ROW][COL],ROW,COL);
    while(1) 
    {
        PlayerMove(board[ROW][COL],ROW,COL);
    	DisplayBoard(board, ROW, COL);
        ComputerMove(board, ROW, COL);
        DisplayBoard(board, ROW, COL);
    }
}

int main(){
	srand((unsigned int)time(NULL)); //生成随机数种子
    ...
}

[运行结果]
image.png

五、功能④ —— 判断输赢

判断输赢得逻辑:

  1. 若下棋过程中,任意一方的棋子在行/列/对角线占满时,则该方获胜
  2. 若所有格子都被占满时还未出现1中情况,则双方平局

具体实现方法:

  1. 玩家和电脑落子后,分别调用判断输赢函数进行判断,若有某一方获胜,则函数返回值为该方棋子对应的字符。即玩家获胜时,函数返回 ‘*’,游戏结束;或者电脑获胜时,函数返回’#',游戏结束
  2. 解决平局的情况需要定义一个判断棋盘是否已满的函数,每次落子除了判断输赢之外,再判断一次棋盘是否已满,棋盘满子时,返回1,游戏结束,宣布平局
  3. 如果没有平局或输赢的情况出现,则判断输赢的函数返回字符 ‘C’,游戏继续
[game.h]
#include<stdio.h>
#include<stdlib.h>  
#include<time.h>
#include<windows.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);

//检查棋盘是否满子函数声明
int IsFull(char board[ROW][COL], int row, int col);

//判断是否有赢家产生函数声明
char CheckWin(char board[ROW][COL], int row, int col);

--------------------------------------------------------------------------
    
[game.c]
#include "game.h" 

//数组初始化函数
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){...}

//判断棋盘是否满子函数
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;
			}
		}
	}
	return 1;
}

//判断是否产生赢家
char CheckWin(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 < col; 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][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

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

	//继续游戏
	return C;
}
--------------------------------------------------------------------------
    
[test.c]
#include "game.h" 

void menu(){...} 

void game()
{
	char board[ROW][COL] = {0};
    char ret = CheckWin;
    
    InitBoard(board[ROW][COL],ROW,COL); 
    DisplayBoard(board[ROW][COL],ROW,COL);
    while(1) 
    {
        PlayerMove(board[ROW][COL],ROW,COL);
        ret = checkwin(char board[ROW][COL], int row, int col); //玩家落子后,即进行判断
        if(ret != 'C')  //如果判断输赢函数的返回值不为字符'C',则意味着有赢家或平局产生,跳出循环
        {
        	break;
        }
    	DisplayBoard(board, ROW, COL);

        ComputerMove(board, ROW, COL);
        ret = checkwin(char board[ROW][COL], int row, int col); //电脑落子后,即进行判断,原理相同
        if(ret != 'C')  
        {
        	break;
        }
        DisplayBoard(board, ROW, COL);
    }
    //跳出循环则意味着有结果产生,则根据函数的返回结果,打印出游戏结果。
    if (ret == '*')
	{
		DisplayBoard(board, ROW, COL);
		printf("玩家获胜\n");
	}
	else if (ret == '#')
	{
		DisplayBoard(board, ROW, COL);
		printf("电脑获胜\n");
	}
	else if (ret == 'Q')
	{
		DisplayBoard(board, ROW, COL);
		printf("平分秋色\n");
	}
}

int main(){...}

[运行结果]
image.png


Part 3 细节优化

完整的游戏逻辑实现之后,从运行结果会发现,棋盘一直在重复打印,导致程序运行出来的观感体验并不好。所以我们可以结合system()函数和Sleep()函数,来实现一个较为完善的游戏(代码见附录1下的完整代码),效果如下图

运行结果.gif

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-14 23:31:36  更:2022-04-14 23:35:07 
 
开发: 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 23:49:48-

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