C++ 实现对战AI五子棋

avatar
作者
猴君
阅读量:1

 个人主页:日刷百题

系列专栏〖C/C++小游戏〗〖Linux〗〖数据结构〗 C语言

🌎欢迎各位点赞👍+收藏⭐️+留言📝 

前言:

     为了能够快速上手一门语言,我们往往在学习了基本语法后,采用写一个小项目的方式来加深理解语言的语法及运用,本文采用c++去实现对战AI五子棋,采用面向对象开发的一款游戏,里面应用了类和对象以及vector容器等知识。

 一、项目效果展示


 


二、游戏思路

 


 

三、游戏框架

我们这里创建四个类(可以简单的理解为加强版的结构体),玩家类、AI类、棋盘类、棋盘控制类,将玩家类、AI类、棋盘类作为参数传给棋盘控制类,棋盘控制类获取这三个类的信息,从而可以控制游戏的运行,而玩家和AI要进行下棋操作时,需要棋盘信息,所以在玩家和AI类设置棋盘类。

根据上面分析,我们搭建好框架,先实现棋盘类功能,再实现AI和玩家类,最后实现棋盘控制类

四、棋盘类实现

4.1  用棋盘类构造函数初始化

4.1.1  checkerboard.h

#include<graphics.h>//eaysx头文件 #include<vector> using namespace std; enum  GameResult { BLACK_WIN,WHITE_WIN,DRAW ,CONTINUE}; class checkerboard { public: 	//构造函数初始化成员变量 	checkerboard(int BoardSize, int margin_x, int margin_y, float ChessSize) 	{ 		this->BoardSize = BoardSize;//几线棋盘 		this->margin_x=margin_x; 		this->margin_y = margin_y; 		this->ChessSize = ChessSize;//棋子大小  		 //加载黑子和白子图片到黑子和白子变量 		loadimage(&BLACK_IMG, "res/black.png", ChessSize, ChessSize, true); 		loadimage(&WHITE_IMG, "res/white.png", ChessSize, ChessSize, true); 		 	//棋盘初始化 		for (int i = 0; i < ChessSize; i++) 		{ 			vector<int> row; 			for (int j = 0; j < ChessSize; j++) 			{ 				row.push_back(0); 			} 			BoardMap.push_back(row); 		}  		 		 gameresult= CONTINUE; 		  	}  	  	int BoardSize;//棋盘大小 	float ChessSize;//棋子大小 	vector<vector<int>> BoardMap;//表示棋盘落子情况 private: 	IMAGE BLACK_IMG;//黑棋图片变量 	IMAGE WHITE_IMG;//白棋图片变量 	 	int margin_x;//左侧边界45 	int margin_y;//右侧边界45   };  

 4.2  棋盘类初始化函数

注:

这个初始化函数和棋盘类构造函数的初始化一样,为什么再初始化一次呢?因为我们后面进行游戏运行时,一局结束,再来一局还需要再调用一次棋盘初始化,而定义棋盘类只能调用一次构造函数,所以再创一个棋盘类初始化函数

4.2.1  checkerboard.h

pubilc: void Init();//棋盘初始化

4.2.2   checkerboard.cpp

void checkerboard::Init() {     initgraph(L, W, 1);//窗口大小     //加载到窗口棋盘图片     loadimage(0, "res/棋盘2.jpg", L,W,true);     //播放声音   /*  mciSendString("play res/start.WAV", 0, 0, 0);*/     //加载黑子和白子图片到黑子和白子变量     //loadimage(&BLACK_IMG, "res/black.png", ChessSize, ChessSize, true);     //loadimage(&WHITE_IMG, "res/white.png", ChessSize, ChessSize, true);      //后面没有调用构造函数,调用初始化,所有在这里还需要对容器归0     for (int i = 0; i < ChessSize; i++)     {                  for (int j = 0; j < ChessSize; j++)         {             BoardMap[i][j] = 0;         }              }      gameresult = CONTINUE;    }

4.3  检查鼠标点击是否有效

注:

虽然代码很长,但是思路很简单,先计算点击位置(x,y) 附近的4个落棋位置的实际坐标位置,然后再计算点击位置到这四个落棋位置之间的距离,如果落棋位置与点击位置距离小于棋子大小的0.4倍,就认为这个落棋位置是玩家想要落棋的位置,存储在pos中。若此时该位置没有其他棋子,则为有效点击,返回真。

4.3.1 checkerboard.h

棋盘类外:

//落子位置 struct ChessPos { 	int row; 	int col; }; enum  chess_type{CHESS_WHITE=-1,CHESS_BLACK=1};

棋盘类内:

public: bool ClickBord(int x, int y, ChessPos& pos);//检查有效点击

4.3.2 checkerboard.cpp

bool checkerboard::ClickBord(int x, int y, ChessPos& pos) {     //保证在棋盘内     if (x >= margin_x && x <= (L - margin_x) && y >= margin_y && y <= (W - margin_y))     {         int col = (x - margin_x) / ChessSize;         int row = (y - margin_y) / ChessSize;         //该位置左上角的交点的坐标         int LTPos_x = margin_x + ChessSize * col;         int LTPos_y = margin_y + ChessSize * row;          int critical = ChessSize * 0.4;//临界值         //鼠标点击位置与右上角交点之间的距离         int distance1 = sqrt((x - LTPos_x) * (x - LTPos_x) + (y - LTPos_y) * (y - LTPos_y));//勾股定理            //该位置右上角的交点的坐标         int RTPos_x = LTPos_x + ChessSize;         int RTPos_y = LTPos_y;           //鼠标点击位置与右上角交点之间的距离         int distance2 = sqrt((x - RTPos_x) * (x - RTPos_x) + (y - RTPos_y) * (y - RTPos_y));//勾股定理           //该位置左下角的交点的坐标         int LDPos_x = LTPos_x;         int LDPos_y = LTPos_y + ChessSize;           //鼠标点击位置与左下角交点之间的距离         int distance3 = sqrt((x - LDPos_x) * (x - LDPos_x) + (y - LDPos_y) * (y - LDPos_y));//勾股定理             //该位置右下角的交点的坐标         int RDPos_x = LTPos_x + ChessSize;         int RDPos_y = LTPos_y + ChessSize;           //鼠标点击位置与右下角交点之间的距离         int distance4 = sqrt((x - RDPos_x) * (x - RDPos_x) + (y - RDPos_y) * (y - RDPos_y));//勾股定理         if (distance1 <= critical)         {             pos.col = col;             pos.row = row;             if (BoardMap[pos.row][pos.col] == 0)//该坐标没有棋子             {                 return true;             }             return false;          }         else if (distance2 <= critical)         {             pos.col = col + 1;             pos.row = row;             if (BoardMap[pos.row][pos.col] == 0)//该坐标没有棋子             {                 return true;             }             return false;           }         else if (distance3 <= critical)         {             pos.col = col;             pos.row = row + 1;             if (BoardMap[pos.row][pos.col ] == 0)//该坐标没有棋子             {                 return true;             }             return false;           }         else if (distance4 <= critical)         {             pos.col = col + 1;             pos.row = row + 1;             if (BoardMap[pos.row ][pos.col] == 0)//该坐标没有棋子             {                 return true;             }             return false;           }         else         {             return false;         }     }      else     {         return false;     } }

4.4   下棋

功能:实现记录最后一次落子的位置以及最后一次下棋是玩家方还是AI方,在棋盘二维数组记录落子数据。

 4.4.1 checkerboard.h

 棋盘类外:

//落子位置 struct ChessPos { 	int row; 	int col; }; enum  chess_type{CHESS_WHITE=-1,CHESS_BLACK=1};

棋盘类内:

public: 	void PlayChess(ChessPos& pos,chess_type type);//下棋

  4.4.2 checkerboard.cpp

void checkerboard::PlayChess(ChessPos& pos, chess_type type) {     int x = margin_x + ChessSize * pos.col-ChessSize*0.5;     int y = margin_y + ChessSize * pos.row- ChessSize *0.5;             BoardMap[pos.row][pos.col] = type;         lastpos.row = pos.row;         lastpos.col = pos.col;         lasttype = type;          if (type == CHESS_BLACK)     {         putimagePNG(x, y, &BLACK_IMG);             }     else     {         putimagePNG(x, y, &WHITE_IMG);      } }

4.5  判断棋盘是否下满

4.5.1 checkerboard.h

bool BoardFull();

4.5.2 checkerboard.cpp

bool checkerboard::BoardFull() {     for (int row = 0; row < BoardSize; row++)     {         for (int col = 0; col < BoardSize; col++)         {             if (BoardMap[row][col] == 0)//棋盘没满             {                 return false;             }         }    }     return true; }

 4.6  判断游戏状态

4.6.1 checkerboard.h

 棋盘类外:

enum  GameResult { BLACK_WIN,WHITE_WIN,DRAW ,CONTINUE};

 棋盘类内: 

private: GameResult gameresult; GameResult IsWin();

4.6.2 checkerboard.cpp 

GameResult checkerboard::IsWin() {     bool boardfull = BoardFull();          //每个方向记连续棋子个数     int Black_Num = 0;     int  White_Num = 0;     if (lasttype == CHESS_BLACK)//黑子方     {         //计算四个方向是否有5个         for (int i = 0; i <= 1; i++)//分横,竖,上斜,下斜4个方向         {             for (int j = -1; j <= 1; j++)             {                 //每个方向记连续棋子个数                 Black_Num = 0;                 White_Num = 0;                   if ((i == 0 && j == 0) || (i == 0 && j == 1))                 {                     continue;                 }                 for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中的单向                 {                     int Cur_row = lastpos.row + i * k;                     int Cur_col = lastpos.col + j * k;                     if (Cur_row >= 0 && Cur_row < BoardSize &&                         Cur_col >= 0 && Cur_col < BoardSize &&                         BoardMap[Cur_row][Cur_col] == CHESS_BLACK)                     {                         Black_Num++;                     }                      else//超出棋盘或者是白子或者空白                     {                         break;                     }                 }                 for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中单向的另一个方向                 {                     int Cur_row = lastpos.row - i * k;                     int Cur_col = lastpos.col - j * k;                     if (Cur_row >= 0 && Cur_row < BoardSize &&                         Cur_col >= 0 && Cur_col < BoardSize &&                         BoardMap[Cur_row][Cur_col] == CHESS_BLACK)                     {                         Black_Num++;                     }                     else//超出棋盘或者是白子或者空白                     {                         break;                     }                 }                 //判断游戏状态                 if (Black_Num == 4)//5个黑子,游戏结束                 {                                          return BLACK_WIN;                 }                 else                 {                     if (boardfull)                     {                         return  DRAW;                     }                                      }              }         }      }     else//白子方     {         //计算四个方向是否有5个         for (int i = 0; i <= 1; i++)//分横,竖,上斜,下斜4个方向         {             for (int j = -1; j <= 1; j++)             {                 //每个方向记连续棋子个数                 Black_Num = 0;                 White_Num = 0;                   if ((i == 0 && j == 0) || (i == 0 && j == 1))                 {                     continue;                 }                 for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中的单向                 {                     int Cur_row = lastpos.row + i * k;                     int Cur_col = lastpos.col + j * k;                     if (Cur_row >= 0 && Cur_row < BoardSize &&                         Cur_col >= 0 && Cur_col < BoardSize &&                         BoardMap[Cur_row][Cur_col] == CHESS_WHITE)                     {                         White_Num++;                     }                      else//超出棋盘或者是黑子或者空白                     {                         break;                     }                 }                 for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中单向的另一个方向                 {                     int Cur_row = lastpos.row - i * k;                     int Cur_col = lastpos.col - j * k;                     if (Cur_row >= 0 && Cur_row < BoardSize &&                         Cur_col >= 0 && Cur_col < BoardSize &&                         BoardMap[Cur_row][Cur_col] == CHESS_WHITE)                     {                        White_Num++;                     }                     else//超出棋盘或者是黑子或者空白                     {                         break;                     }                 }                 //判断游戏状态                 if (White_Num == 4)//5个白子,游戏结束                 {                      return WHITE_WIN;                 }                 else                 {                     if (boardfull)                     {                         return  DRAW;                     }                  }              }         }      }     return CONTINUE; }

4.7   判断游戏是否结束

4.7.1 checkerboard.h

bool CheckOver();//检查游戏是否结束 

4.7.2  checkerboard.cpp

bool checkerboard::CheckOver() {     gameresult = IsWin();     if (gameresult == BLACK_WIN)     {         Sleep(2000);         loadimage(0, "res/胜利.jpg",W,L,true);         //播放声音         mciSendString("play res/胜利.mp3", 0, 0, 0);         _getch();         return true;     }     else if (gameresult == WHITE_WIN)     {         Sleep(2000);         loadimage(0, "res/失败.jpg",W , L, true);         //播放声音         mciSendString("play res/失败.mp3", 0, 0, 0);         _getch();//暂停,按任意键继续         return true;     }     else if (gameresult == DRAW)     {         Sleep(2000);         loadimage(0, "res/平局.png",W , L , true);         _getch();         return true;              }     else//继续游戏     {         return false;     }  }

五、玩家类实现 

5.1  用玩家类构造函数初始化

5.1.1  chess_player.h

#include"checkerboard.h" #include"AI.h" class chess_player { public: 	chess_player(checkerboard& checkerboard) 	{ 		this->checkerboard = &checkerboard; 	} 	 	void go(); private: 	checkerboard* checkerboard; };

5.2  玩家下棋

5.2.1 chess_player.h

void go();

5.2.2  chess_player.cpp 

void chess_player::go() { 	 	ChessPos pos; 	while (1) 	{ 		MOUSEMSG mousemsg = GetMouseMsg();//鼠标信息结构体变量 		bool click_board = checkerboard->ClickBord(mousemsg.x, mousemsg.y, pos); 		if (mousemsg.uMsg == WM_LBUTTONDOWN &&click_board )//用到checkboard对象的成员 		{ 			printf("%d,%d\n", pos.row, pos.col); 			break; 		} 	} 	 	checkerboard->PlayChess(pos, CHESS_BLACK);//黑子下棋的位置(渲染和记录) }

六、AI类实现

6.1 用AI类构造函数初始化

6.1.1  AI.h

#include"checkerboard.h" #include<vector> class AI { public: 	AI(checkerboard& checkerboard)//AI构造函数 	{ 		this->checkerboard = &checkerboard; 		for (int i = 0; i <checkerboard.ChessSize; i++) 		{ 			vector<int> row; 			for (int j = 0; j <checkerboard.ChessSize; j++) 			{ 				row.push_back(0); 			} 			ScoreMap.push_back(row); 		} 	}  	void go();  private: 	checkerboard* checkerboard; 	vector<vector<int>>  ScoreMap; 	void  CalculateScore(); 	ChessPos   MaxScore(); 	 };  

6.2  AI计算权值最高的棋盘空白位置

计算棋盘空白位置的权值,首先对该位置的横、竖、上斜、下斜四个方位做判断,以该位置为起点,每个方位只需要在单方向上判断4个棋子位,反方向判断四个棋子位,统计连续的白子或者黑子个数,根据下面的表格给出相应权重值,选择出累计权值最高的位置为AI落子点。

 6.2.1  AI.h 

private: 	 	vector<vector<int>>  ScoreMap; 	void  CalculateScore();

  6.2.2  AI.cpp

void AI::CalculateScore() { 	int Black_Num = 0; 	int White_Num = 0; 	int Empty_Num = 0; 	for(int row=0;row<checkerboard->BoardSize;row++) 		for (int col = 0; col< checkerboard->BoardSize; col++) 		{ 			if (checkerboard->BoardMap[row][col] != 0)//有棋子,则跳过判断 			{ 				continue; 			} 			//先假设下黑子,计分情况 			for (int i = 0; i <= 1; i++)//分横,竖,上斜,下斜4个方向 			{ 				for (int j = -1; j <= 1; j++) 				{ 					//每个方向记连续棋子个数 					Black_Num = 0; 					 White_Num = 0; 					 Empty_Num = 0;  					if ((i == 0 && j == 0) || (i ==0 && j == 1)) 					{ 						continue; 					} 					for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中的单向 					{ 						int Cur_row = row + i * k; 						int Cur_col = col + j * k; 						if (Cur_row>=0&&Cur_row<checkerboard->BoardSize&& 							Cur_col>=0&&Cur_col<checkerboard->BoardSize&& 							checkerboard->BoardMap[Cur_row][Cur_col] == CHESS_BLACK) 						{ 							Black_Num++; 						} 						else if(Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] ==0) 						{ 							Empty_Num++; 							break; 						} 						else//超出棋盘或者是白子 						{ 							break; 						} 					} 					for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中单向的另一个方向 					{ 						int Cur_row = row - i * k; 						int Cur_col = col - j * k; 						if (Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] == CHESS_BLACK) 						{ 							Black_Num++; 						} 						else if (Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] == 0) 						{ 							Empty_Num++; 							break; 						} 						else//超出棋盘或者是白子 						{ 							break; 						} 					} 					//该位置的得分情况 					if (Black_Num == 1)//2个黑子 					{ 						ScoreMap[row][col] += 10; 					} 					else if (Black_Num == 2)//连续三个黑子 					{ 						if (Empty_Num == 1) 						{ 							ScoreMap[row][col] += 30; 						} 						else if(Empty_Num==2) 						{ 							ScoreMap[row][col] += 40; 						} 					} 					else if (Black_Num == 3)//连续4个黑子 					{ 						if (Empty_Num == 1) 						{ 							ScoreMap[row][col] += 60; 						} 						else if (Empty_Num == 2) 						{ 							ScoreMap[row][col] += 200; 						} 					} 					else if (Black_Num == 4)//连续5个黑子 					{ 						ScoreMap[row][col] += 20000; 					} 				} 			} 			//假设该位置下白子,计分情况 			for (int i = 0; i <= 1; i++)//分横,竖,上斜,下斜4个方向 			{ 				for (int j = -1; j <= 1; j++) 				{ 					//每个方向记连续棋子个数 					Black_Num = 0; 					White_Num = 0; 					Empty_Num = 0;  					if ((i == 0 && j == 0) || (i == 0 && j == 1)) 					{ 						continue; 					} 					for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中的单向 					{ 						int Cur_row = row + i * k; 						int Cur_col = col + j * k; 						if (Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] == CHESS_WHITE) 						{ 							White_Num++; 						} 						else if (Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] == 0) 						{ 							Empty_Num++; 							break; 						} 						else//超出棋盘或者是黑子 						{ 							break; 						} 					} 					for (int k = 1; k <= 4; k++)//最多判断五个子,四个方向中单向的另一个方向 					{ 						int Cur_row = row - i * k; 						int Cur_col = col - j * k; 						if (Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] == CHESS_WHITE) 						{ 							White_Num++; 						} 						else if (Cur_row >= 0 && Cur_row < checkerboard->BoardSize && 							Cur_col >= 0 && Cur_col < checkerboard->BoardSize && 							checkerboard->BoardMap[Cur_row][Cur_col] == 0) 						{ 							Empty_Num++; 							break; 						} 						else//超出棋盘或者是黑子 						{ 							break; 						} 					} 					//该位置的得分情况 					if (White_Num == 0)//1个白子 					{ 						ScoreMap[row][col] += 5; 					} 					else if (White_Num == 1)//连续2个白子 					{ 						 						ScoreMap[row][col] += 10; 						 					} 					else if (White_Num == 2)//连续3个白子 					{ 						if (Empty_Num == 1) 						{ 							ScoreMap[row][col] += 25; 						} 						else if (Empty_Num == 2) 						{ 							ScoreMap[row][col] += 50; 						} 					} 					else if (White_Num == 3)//连续4个白子 					{ 						if (Empty_Num == 1) 						{ 							ScoreMap[row][col] += 55; 						} 						else if (Empty_Num == 2) 						{ 							ScoreMap[row][col] += 300; 						} 					} 					else if (White_Num == 4)//连续5个白子 					{ 						ScoreMap[row][col] += 30000;  					} 				} 			} 		} } 

6.3  选择权值最高的棋盘位置

 6.3.1  AI.h 

private: ChessPos   MaxScore();

 6.3.2  AI.cpp

ChessPos AI::MaxScore() { 	int max = 0; 	vector<ChessPos> maxscore_pos; 	ChessPos pos; 	CalculateScore(); 	for (int row = 0; row < checkerboard->BoardSize; row++) 	{ 		for (int col = 0; col < checkerboard->BoardSize; col++) 		{ 			if (ScoreMap[row][col] > max) 			{ 				max = ScoreMap[row][col]; 				maxscore_pos.clear(); 				pos.row = row; 				pos.col = col; 				maxscore_pos.push_back(pos); 			} 			else if (ScoreMap[row][col] == max) 			{ 				pos.row = row; 				pos.col = col; 				maxscore_pos.push_back(pos); 			} 		} 	}  	//计分棋盘归0 	for (int i = 0; i < checkerboard->ChessSize; i++) 	{ 		for (int j = 0; j < checkerboard->ChessSize; j++) 		{ 			ScoreMap[i][j] = 0; 		} 		 	} 	int index = rand() % maxscore_pos.size(); 	return maxscore_pos[index]; }

 6.4 AI下棋

 6.4.1  AI.h 

void go();

 6.4.2  AI.cpp

void AI::go() { 	ChessPos pos = MaxScore(); 	checkerboard->PlayChess(pos, CHESS_WHITE);//白子下棋的位置(渲染和记录) } 

 七、棋盘控制类实现

7.1 用棋盘控制类构造函数初始化

7.1.1 ChessGame.h

#include"chess_player.h" #include"AI.h" #include"checkerboard.h" class ChessGame { public: 	ChessGame(chess_player& chess_player, AI& ai, checkerboard& checkerboard)//构造函数初始化 	{ 		this->chess_player = &chess_player; 		this->ai = &ai; 		this->checkerboard = &checkerboard; 	} 	void play();//开始游戏   //创建数据成员变量 private: 	chess_player* chess_player; 	AI* ai; 	checkerboard* checkerboard;  };  

7.2 控制游戏进行

7.2.1 ChessGame.h 

void play();//开始游戏

7.2.2 ChessGame.cpp

//开始游戏  void ChessGame::play() { again: 	checkerboard->Init(); 	while (1) 	{ 		//棋手先走 		chess_player->go(); 		if (checkerboard->CheckOver()) 		{ 			goto  again; 			 		} 		//AI走 		ai->go(); 		if (checkerboard->CheckOver()) 		{ 			 			goto  again;  		}  	} }

八、主函数

#include<iostream> #include"ChessGame.h" int main() { 	srand((unsigned int)time(NULL)); 	checkerboard checkerboard( 13, 45*0.7, 45*0.7,67.25*0.7);//自动调用构造函数 	chess_player chess_player(checkerboard);//自动调用构造函数 	AI ai(checkerboard);//自动调用构造函数; 	ChessGame chessgame(chess_player,ai, checkerboard);//引用传值 	 	chessgame.play(); 	return 0; 	  }

完整代码及素材:c和c++代码: 争取每日一更。。。。。。 - Gitee.comicon-default.png?t=N7T8https://gitee.com/daily-brush-100-questions/c-language-training-camp/tree/master/c++AI%E4%BA%94%E5%AD%90%E6%A3%8B/c++AI%E4%BA%94%E5%AD%90%E6%A3%8B

希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,百题一定会认真阅读!

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!