【C语言】三子棋详解(包教包会的那种)

这篇具有很好参考价值的文章主要介绍了【C语言】三子棋详解(包教包会的那种)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【C语言】三子棋详解(包教包会的那种)

君兮_的个人主页

勤时当勉励 岁月不待人

C/C++ 游戏开发


前言

当我们有一定的C语言基础后,就可以独立的设计一些小项目了,今天我来带大家一步一步实现三子棋这个简单的项目并把它改进为五子棋。跟着我一步一步来,我相信到最后你也能独立的编出属于自己的三子棋。话不多说,我们开始吧!

三子棋的实现

  • 三子棋虽然是一个小项目,但是整个过程中还是分不同的模块来实现不同的功能的,接下来我通过画图的方式讲一下具体由哪些模块并带大家一块一块的具体分析实现。
    【C语言】三子棋详解(包教包会的那种)
  • 所有代码分三部分实现,它们分别是负责声明函数的头文件“game.h”,负责函数功能实现的“game.c”,以及负责测试(即用户使用)的“test.c”.

这里为什么要分几部分封装函数我在下面这篇博客中的自定义函数中提到过,感兴趣可以去看看,这里是链接:【C语言初阶】万字解析,带你0基础快速入门C语言(下)

1. 打印菜单

  • 这一步其实是非常简单的,代码如下
void menu()//打印菜单
{
	printf("*****************************\n");
	printf("*********** 1.play **********\n");
	printf("*********** 0.exit **********\n");
	printf("*****************************\n");

}

  • 好了,一个简单的菜单就写好了,但是此时的1和0是没有任何的选择效果的,我们接下来就让我们的菜单真正能做到输入1开始输入0结束的效果。
int main()
{
	srand((unsigned int)time(NULL));//暂时先不管,后面会讲这是什么
	int input = 0;
	
	do {
		
		menu();
		printf("请选择:> ");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			game();	//进入游戏	
			break;
		case 0:
			printf("退出游戏\n");
			break;
			
		default:
			printf("输入错误,请重新输入\n");//输入的非0非1时
			break;
		}
	}while(input);


	return 0;
}
  • 来测试一下我们这个菜单的效果

【C语言】三子棋详解(包教包会的那种)

  • 非常完美,菜单算是打印完成了,我们进行下一步。

2.初始化并打印棋盘

  • 现在我们输入1进入游戏,首先要进行的就是对棋盘的初始化以及打印
  • 我们不妨把黑子白子设为’*‘与’#',由于我们的棋盘是分行与列的,要想打印一个棋盘把我们的”棋子“放进去,使用二维数组再合适不过。
  • 代码如下
void game()
{
	char board[Row][Col] = { 0 };
	InitBoard(board, Row, Col);//初始化棋盘的函数
	PrintBoard(board,Row, Col);//打印棋盘的函数

}
  • 此时有人肯定好奇Row和Col是什么
  • 为了方便我们以后改变棋盘的大小,我们用常数定义了Row和Col
#define Col 3
#define Row 3
  • 这样在我们以后想改变这个棋盘的大小时只需要改变定义的Col与Row的值就好,不需要把程序中每个有关棋盘大小的代码都修改一遍,是不是非常方便?
  • 我们继续
    当我们把二维数组以及对应的参数设计并放进函数里后,我们就要来实现具体函数的功能了,先讲讲初始化函数的实现。

棋盘初始化函数功能的实现

凡是函数我们在调用时都得先声明再使用,因此我们现在头文件中先声明一下。

void InitBoard(char board[Row][Col], int row, int col);//初始化棋盘

具体实现:

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] = ' ';//把棋盘上的每一个元素都初始化为空格,方便我们落子

		}
	}
}
  • 其实没啥好讲的,无非是两个for循环嵌套,看看以上代码后的注释相信聪明的你理解应该没啥问题。

打印棋盘

  • 把棋盘初始化好后,我们就要开始尝试确定棋盘的格式来打印棋盘了,首先还是得在头文件中声明函数。
void PrintBoard(char board[Row][Col], int row, int col);//打印棋盘

代码的实现

void PrintBoard(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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");//区分每一列的元素,在每个棋子下打印“---”
				if (j < col - 1)
					printf("|");
			}
			printf("\n");//一行打完换下一行
		}
	}
}

  • 这里讲一下为啥有时候i和j的范围是(row-1)和(col-1)

  • 其实非常简单啊,主要是为了美观。比如咱们打印一个3*3的棋盘,实际上只需要两行以及两列的分隔线就够了,再更加直接一点,如果我们想把一条线分割成三部分,是不是只需要切两次就足够了?

  • 可能上面这段画有点抽象,咱们配合下面实际打印出的棋盘的图来想象一下。
    【C语言】三子棋详解(包教包会的那种)

  • 配合图看是不是就直观多了?

  • 如果觉得ok的话,那我们接下来来实现下棋的这部分代码


3.玩家下棋与模拟电脑下棋

  • 啊,我相信咱们程序猿的水平可能战胜阿尔法狗有点困难,但是与电脑随机落子下棋肯定绰绰有余,甚至咱们得让着它才能让它赢
  • 接下来我们讲讲我们该怎么下

玩家下棋

  • 同上,引用函数得先声明
void PlayBoard(char board[Row][Col], int row, int col);//玩家落子
  • 那么我们现在想想,我们该怎么下呢?
    - 首先咱们得先判断一下咱们下棋是否合法,总不能下着下着下到棋盘外了吧??
  • 其次,我们得判断一下我们现在选择的这个坐标是否有棋子,一个坐标下无法存放两个棋子的
    代码实现一下:
void PlayBoard(char board[Row][Col], int row, int col)
{
	int x, y = 0;
	while (1)
	{
	   //先提示一下此时轮到玩家了以及此时玩家应该做什么,
		printf("玩家下棋>:\n");
		printf("请输入要下的坐标,中间用空格隔开\n");
		scanf("%d %d", &x, &y);
		//判断输入坐标的合法性
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';//落子,把该坐标中空格改为'*'
				break;
			}
			else
				printf("坐标已被占用,请重新输入\n");//当判断此坐标不为空格时,说明该坐标已经有“棋子”了
		}
		else
			printf("坐标非法,请重新输入\n");//选择的坐标不在我们规定的棋盘内

	}

}
  • 注意:
  • 在判断该位置是否有“棋子”时,我们使用的是board[x - 1][y - 1].。
  • 程序和我们程序猿当然知道咱们的数组下标是从0开始的呀,但是,如果是一个没接触过编程的人来玩这个游戏时,ta会想当然的认为棋盘的坐标是从1开始的,比如第一行第一列的坐标,我们知道是(0,0),可ta想的却是(1,1),为了方便这些用户游玩我们的三子棋,我们最好就把每个位置的坐标设置成从1开始。这就是上面判断是否有“棋子”以及落子时使用board[x - 1][y - 1]的原因。

电脑下棋

  • 我们自己与自己玩自然无趣,此时不妨让电脑陪我们下棋。
  • 其实电脑下棋与我们玩家下棋类似,但是我们首先要解决一个问题,怎么让它随机的落子呢?
  • 为了让电脑能随机的下棋,我们得保证它每一次落子的坐标都变化,那有没有什么东西是一直在变化能被我们使用实现生成随机落子坐标的呢?
  • 此时我们想到,有一个东西正无时无刻不在发生变化,那就是时间,恰好符合我们的要求。
  • 接下来先讲讲怎么通过时间戳生成随机数。
随机数的生成
  • 什么是时间戳?
  • 时间戳是计算机科学中常见的术语,是指一种记录时间的方式,通常用于记录某个事件的发生时间或者文件的创建、修改时间等信息。简单的说,时间戳就是一串数字,表示从某个固定时间点(1970年1月1日00:00:00)开始到现在的时间长度,通常以秒为单位计算。
  • 生成随机数的函数
#include<stolib>//使用该函数所需的头文件
rand()
  • 我们通过msdn来认识一下它

【C语言】三子棋详解(包教包会的那种)

  • rand的返回值为一个整型,注意最后一句话哦:

【C语言】三子棋详解(包教包会的那种)

  • 博主只会用,英语能力有限,这里征用一下百度翻译
    【C语言】三子棋详解(包教包会的那种)
  • 也就是说,在使用rand之前,我们得先调用一下srand为其设定生成随机数的起点(或者生成器)”
  • srand函数msdn定义如下:
    【C语言】三子棋详解(包教包会的那种)
  • 当我们调用srand函数时,必须满足的条件是传给它的值是一个变化的值,此时就可以用我们前面讲到的时间戳了
#include<time.h>//使用time函数所需头文件
srand((unsigned int)time(NULL));//把时间函数置空传给srand同时由于srand要求参数必须为unsigned int型,把time(NULL)强制类型转换一下
  • 注意,rand生成的其实是一个伪随机数。

  • 好了,到此我们就可以开始尝试实现电脑下棋了
  • 先在头文件中声明函数
void ComputerBoard(char board[Row][Col], int row, int col);//电脑下棋

具体代码如下:

void ComputerBoard(char board[Row][Col], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");//提示此时轮到电脑下
	while(1)
	{
		x = rand() % row;//为了防止落子到棋盘外,使生成的随机数范围为0到row-1
		y = rand() % col;//同理,生成0到col-1的数
		
		if (board[x][y] == ' ')//判断此时该坐标是否被占用
		{
			board[x][y] = '#';//落子
			break;
		}

	}
  • 除了上面随机数的生成,其他都与玩家下棋类似,这里不做缀叙了。

4.判断输赢

  • 这里我们离实现三子棋就差一步了,那就是判断输赢。
  • 这里我们把具体的情况总结一下
  • 1)玩家下的棋存在连续三个横着或竖着或者两条对角线,此时玩家胜
  • 2)同理,上面也是电脑的获胜条件。
  • 3)当我们把棋盘所有格子(坐标)都占满了,此时还没有出现上面两种情况,说明此时平局。
    首先还是先在头文件里声明函数
int IsFull(char board[Row][Col], int row, int col);//棋盘是否已满
int Is_Win(char board[Row][Col], int row, int col);//判断谁赢

具体代码:

int IsFull(char board[Row][Col], int row, int col)//判断棋盘是否已满
{
	int i ,j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')//还有空格,说明没满
				return 0;
		}
	}
	return 1;//没有空格了,说明已满,返回1
}


int Is_Win(char board[Row][Col], int row, int col)
{
	int x = 0;
	int y = 0;
	//竖着
	if (board[x][0] == board[x][1] && board[x][1] == board[x][2] && board[x][0] !=' ')
	{
		return board[x][0];//返回三个坐标中随意哪个都行,因为如果满足上面获胜条件,三个坐标中一定放着‘*’或者‘#’,下面的判断同理
	}
	//横着
	if (board[0][y] == board[1][y] && board[1][y] == board[2][y] && board[0][y] != ' ')
	{
		return board[0][y];
	}
	//两条对角线
	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)//返回值如果为1说明棋盘满了
	{
		return 'Q';
	}
	return 'C';

}
  • 注意:
  • 上面这段代码中,如果玩家赢了,就返回‘*’
  • 如果电脑赢了,就返回‘#’
  • 如果平局,返回‘Q’
  • 如果上面三种情况都没发生,说明还没分出胜负,返回‘C’

我们的game函数

当我们把上面要实现的功能都封装在一个函数中,就能实现我们的三子棋游戏啦,此时我们来讲讲game函数中上述函数是怎样组合放置的。

  • 由于这里的game在主程序所在文件中,不需要声明
void game()
{
	char board[Row][Col] = { 0 };
	InitBoard(board, Row, Col);
	PrintBoard(board,Row, Col);
	char ret = 0;
	while (1)
	{
		PlayBoard(board, Row, Col);//玩家下棋
		PrintBoard(board, Row, Col);//打印此时的棋盘
		ret = Is_Win(board, Row, Col);//把返回值存放在ret里
		if (ret != 'C')//判断是否分出胜负,返回‘C’才说明需要继续
		break; 
		
		ComputerBoard(board, Row, Col);//电脑下棋
		PrintBoard(board, Row, Col);//打印此时的棋盘
		ret = Is_Win(board, Row, Col);//与上面同理
		if (ret != 'C')
		break;
	}
	if (ret == '*')
		printf("玩家赢\n");
	if (ret == '#')
		printf("电脑赢\n");
	if (ret == 'Q')
		printf("平局\n");
}
  • 这样我们的三子棋项目就完成了,我们来具体玩一下吧

【C语言】三子棋详解(包教包会的那种)这里博主提醒大家:

游戏虽好,可不要贪玩哦!!!

【C语言】三子棋详解(包教包会的那种)


源码

以下为三子棋具体的每个文件中的源码哦,需要自取

game.h

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Col 3
#define Row 3
void InitBoard(char board[Row][Col], int row, int col);//初始化棋盘
void PrintBoard(char board[Row][Col], int row, int col);//打印棋盘
void PlayBoard(char board[Row][Col], int row, int col);//玩游戏
void ComputerBoard(char board[Row][Col], int row, int col);//电脑下
int IsFull(char board[Row][Col], int row, int col);//棋盘是否已满
int Is_Win(char board[Row][Col], int row, int col);//判断谁赢

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] = ' ';

		}
	}
}


void PrintBoard(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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

void PlayBoard(char board[Row][Col], int row, int col)
{
	int x, y = 0;
	while (1)
	{
		printf("玩家下棋>:\n");
		printf("请输入要下的坐标,中间用空格隔开\n");
		scanf("%d %d", &x, &y);
		//判断输入坐标的合法性
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
				printf("坐标已被占用,请重新输入\n");
		}
		else
			printf("坐标非法,请重新输入\n");

	}

}


void ComputerBoard(char board[Row][Col], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");
	while(1)
	{
		x = rand() % row;//生成0-row-1的数
		y = rand() % col;//生成0-col-1的数
		
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}

	}



}
int IsFull(char board[Row][Col], int row, int col)
{
	int i ,j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}


int Is_Win(char board[Row][Col], int row, int col)
{
	int x = 0;
	int y = 0;
	//竖着
	if (board[x][0] == board[x][1] && board[x][1] == board[x][2] && board[x][0] !=' ')
	{
		return board[x][0];
	}
	//横着
	if (board[0][y] == board[1][y] && board[1][y] == board[2][y] && board[0][y] != ' ')
	{
		return board[0][y];
	}
	//对角线
	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()//打印菜单
{
	printf("*****************************\n");
	printf("*********** 1.play **********\n");
	printf("*********** 0.exit **********\n");
	printf("*****************************\n");

}

void game()
{
	char board[Row][Col] = { 0 };
	InitBoard(board, Row, Col);
	PrintBoard(board,Row, Col);
	char ret = 0;
	while (1)
	{
		PlayBoard(board, Row, Col);
		PrintBoard(board, Row, Col);
		ret = Is_Win(board, Row, Col);
		if (ret != 'C')
		break; 
		
		ComputerBoard(board, Row, Col);
		PrintBoard(board, Row, Col);
		ret = Is_Win(board, Row, Col);
		if (ret != 'C')
		break;
	}
	if (ret == '*')
		printf("玩家赢\n");
	if (ret == '#')
		printf("电脑赢\n");
	if (ret == 'Q')
		printf("平局\n");
}
int main()
{
	srand((unsigned int)time(NULL));
	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);


	return 0;
}

总结

以上就是今天的所有内容啦,希望你在看完后不要光看不练,只有自己亲手敲过之后,才能知道自己到底会不会,眼高手低是万万不可的哦!!文章来源地址https://www.toymoban.com/news/detail-502128.html

  • 如果你有任何疑问欢迎在评论区指出或者私信我哦!
  • 创作不易,如果感觉文章内容对你有所帮助的话,留下三连再走呗!你们的支持就是我创作的动力,下次再见啦!!!
    -【C语言】三子棋详解(包教包会的那种)

到了这里,关于【C语言】三子棋详解(包教包会的那种)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何使用JMeter测试https请求?800字教程包教包会!

    HTTP与HTTPS略有不同,所以第一次使用JMeter测试https请求时遇到了问题,百度一番后找到解决方法:加载证书。 下面内容主要记录这次操作,便于后续参考: 操作浏览器 :谷歌 (1)下载被测网站证书,点击安全锁,选择安全证书   (2)查看证书的详细信息,显示所有后点击

    2024年02月05日
    浏览(44)
  • Big Data Tools插件(详细讲解安装,连接,包教包会!!!)

    🐮博主syst1m 带你 acquire knowledge! ✨博客首页——syst1m的博客💘 😘《CTF专栏》超级详细的解析,宝宝级教学让你从蹒跚学步到健步如飞🙈 😎《大数据专栏》大数据从0到秃头👽,从分析到决策,无所不能❕ 🔥 《python面向对象(人狗大战)》突破百万的阅读量,上过各种各样

    2024年02月03日
    浏览(33)
  • Ubuntu开机自启动脚本+设置(手把手教程,包教包会)

    一、写一个脚本 在 Documents 文件夹(随意放在你习惯的位置)中建立一个init.sh脚本,写上开机后需要执行的动作 init.sh内容为(这是我开机后需要执行的动作,比如进到我的gopath文件夹中执行main文件,学习go语言的懂得都懂。如果是python文件,可以写成python test.py ,反正当作

    2024年02月04日
    浏览(35)
  • 安卓模拟器安装burp证书&夜神&雷电思路通用。包教包会

    提醒:电脑该浏览器设备已安装burp证书。雷电3.102版本举例。 burp下载的证书格式为 der ,所以安装后再导出 ber 。火狐浏览器举例。安装好后,wifi代理指向burp即可。 思路要点: 从浏览器导出burp证书(后缀为cer)—— 拖入模拟器——移动到手机私有位置——设置—— 安全——

    2024年02月11日
    浏览(79)
  • 新手小白必看——基于STM32+ESP8266模块连接阿里云平台读取传感器数据(从0到1,包教包会)

    目录 前言             本次我们学习一下STM32F103驱动ESP8266去连接阿里云的实操过程,在次过程中我会详细讲解开发过程中遇到的问题以及面对问题的解决,希望大家在这篇博客里面也有所收获。 一、ESP8266WIFI模块的固件库刷新(MQTT) 1.固件库刷新 1.1 固件库烧录下载 1.2 固

    2024年04月26日
    浏览(33)
  • 【C语言】三子棋----详解

     💓博客主页:江池俊的博客 ⏩收录专栏:C语言初阶之路 💻代码仓库:江池俊的代码仓库 🎪 社区:GeekHub社区 (欢迎大家加入与我一起探讨学习经验) 🍁 如果觉得博主的文章还不错的话,请点赞👍收藏🌟 三连支持一下博主💞 🔥编译环境:Visual Studio 2022 目录 前言 一、

    2024年02月13日
    浏览(26)
  • 【初阶C语言】实现三子棋(详解+全部源码)

    大家好,我是你们熟悉的恒川 今天我们用C语言来实现三子棋 实现的过程很难,但我们一定要不放弃 本游戏用到了三个文件 两个源文件:三子棋做法.c ,game.c 一个头文件:game.h 想要做好游戏,首要任务就是要把模块想好。 先打印一个游戏菜单 玩家选择是否(1\\0)是否进入

    2023年04月16日
    浏览(31)
  • C语言之详解数组【附三子棋和扫雷游戏实战】

    1、数组的创建 数组是一组相同类型元素的集合。 数组的创建方式: 首先我们就来看看数组如何创建~~ 对于整型、字符型、浮点型的数据可以创建 [] 内的数字便是这个数组的大小,表示这个数组中可以存放多少元素。 除了数字也可以是一个表达式放里面 虽然指定数组大小可

    2024年02月03日
    浏览(30)
  • C语言经典小游戏之三子棋(超详解释+源码)

    “纵有疾风来,人生不言弃,风乍起,合当奋意向此生。” 今天我们一起来学习一下三子棋小游戏用C语言怎么写出来? 《三子棋》是一款古老的民间传统游戏,又被称为黑白棋、圈圈叉叉棋、井字棋、一条龙、九宫棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子

    2024年02月14日
    浏览(41)
  • 三子棋(c语言)

    三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏规则是双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利。但因棋盘太小,三子棋在很多时候会出现和棋的局面。 先开一个test.c文件用来进行游戏的逻

    2024年02月03日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包