Bootstrap

C++图形化编程(五子棋)

手把手教你写五子棋程序

  • 编译环境:vs2019;
  • 注意事项:项目属性中高级的字符集选项要设置为多字节;
  • 关于图形库EasyX:需要用到该图形化库的库函数,没有下载安装的需要先下载安装改库,关于这个库的函数可自己找相关文档了解;
  • 由于代码较多,为了方便管理,我添加了6个头文件来结构化模块化,所以不贴出完整代码,会挑出主要部分来叙述,我会将源文件上传CSDN,有需要可自行下载;
  • 注:文章作为自己成长的记录,不足之处还请多指教,师承大帅。

项目分析

  • 开始界面:一个简单的开始界面,有开始游戏和退出两个按钮和功能;
  • 下面我们首先来看一下程序截图,然后封装这两个按钮:
程序截图

在这里插入图片描述

使用结构体封装按钮代码:
typedef struct MYBUTTON
{
	int x;
	int y;
	int width;
	int height;
	char* str;
	COLORREF cur_Color;
	COLORREF in_Color;
	COLORREF out_Color;
}MyButton, * p_MyButton;
  • 首先定义一个结构体,包含按钮的x,y轴坐标,宽,高按钮颜色等;
关于结构体别名
  • 不加typedef:struct 结构名;然后就可以用结构体名来定义结构体变量;
  • 加typedef: typedef struct 结构体名 { 结构体 }结构体别名XXX;
  • 结构体别名就相当于:struct 结构体名 结构体别名;
  • 使用别名可以很方便的定义结构体变量。
初始化按钮结构体:
p_MyButton setButton(int x,int y,int width,int height,
	COLORREF in_color,COLORREF out_color,const char *str)
{
	p_MyButton pb = (p_MyButton)malloc(sizeof(MyButton));
	assert(pb);
	
	pb->x = x;
	pb->y = y;
	pb->width = width;
	pb->height = height;
	pb->cur_Color = out_color;
	pb->in_Color = in_color;
	pb->out_Color = out_color;
    //这里这样写是因为字符串不能直接赋值
	int strLong = strlen(str) + 1;
	pb->str = (char*)malloc(sizeof(char) * strLong);
	assert(pb->str);
	strcpy_s(pb->str, strLong, str);

	return pb;
}
封装按钮
void draw_Button(p_MyButton pb)
{
    //这里调用easyx的库函数,easyx是一个封装的图形库
	setlinecolor(BLUE);
	setfillcolor(pb->cur_Color);
	fillrectangle(pb->x, pb->y, pb->x + pb->width, 
	pb->y + pb->height);

	settextcolor(WHITE);
	setbkmode(TRANSPARENT);

	settextstyle(30, 0, "楷体");

	int textw = textwidth(pb->str);
	int texth = textheight(pb->str);

	int x = pb->x + (pb->width - textw) / 2;
	int y = pb->y + (pb->height - texth) / 2;
	outtextxy(x, y, pb->str);
}
关于文字居中问题:

  • 可以据此图分析其原理,文字居中就相当于求解 ( a, b)这样的一个简单数学题;
游戏问题:
  • 我们可以将整个棋盘理解为一个二维数组,0表示未落子为空的状态,1表示黑子(或白子),2表示白字(或黑子);
  • 考虑到后期能会为玩家添加某些属性,所以我定义了这样一个结构体来管理(包含了玩家走的步数和唯一标识的ID,这个ID我用来区分和标识是该谁落子和谁落子):
typedef struct PCUSER
{
	int User_ID;
	int User_Step;

}Pc_User, * p_PcUser;
下面展示主函数:
int main()
{
	StartMenu();

	ExMessage m;
	//初始化按钮
	p_MyButton pb_1_play = setButton(100, 100, 200, 40, RED, BLUE, "play game");
	p_MyButton pb_2_exit = setButton(100, 160, 200, 40, BLUE, RED, "exit");
	
	//初始化玩家
	p_PcUser pc_1 = setUser(1, 0);
	p_PcUser pc_2 = setUser(2, 0);

	//初始化棋子
	p_MyChess pq_1 = setChess(5, WHITE);
	p_MyChess pq_2 = setChess(5, BLACK);

	int Arr_Chess[21][21] = { 0 };

	//胜利function
	bool isWin(const int Arr[][21],p_PcUser pc);
	void textWin();

	//void show(const int Arr[][21]);

	BeginBatchDraw();
	while (1)
	{
		FlushBatchDraw();
		peekmessage(&m, EM_MOUSE);
		draw_Button(pb_1_play);
		draw_Button(pb_2_exit);
		if (click_Button(m, pb_1_play))
		{
			FlushBatchDraw();
			break;
		}
		if (click_Button(m, pb_2_exit))
		{
			exit(OVERFLOW);
		}
	}

	drawMap();
	//drawChess(200, 200, BLACK);
	
	while (1)
	{
		FlushBatchDraw();
		while (peekmessage(&m, EM_MOUSE))
		{
			//cout << "进来了" << endl;
			if (pc_1->User_Step <= pc_2->User_Step && m.message == WM_LBUTTONDOWN
				&& Arr_Chess[getX_Click(m)+4][getY_Click(m)+4]==0)
			{
				cout << "进来if条件了" << endl;
				if (Click_Draw_Chess(m, pq_1))
				{
					Arr_Chess[getX_Click(m) + 4][getY_Click(m) + 4] = pc_1->User_ID;
					pc_1->User_Step++;
					//show(Arr_Chess);
				}
				//cout << pc_1->User_Step << ' ' << pc_2->User_Step << endl;
				
				break;
			}
			else 
				if(pc_1->User_Step > pc_2->User_Step && m.message == WM_LBUTTONDOWN
					&& Arr_Chess[getX_Click(m)+4][getY_Click(m)+4] == 0)
			{
					if (Click_Draw_Chess(m, pq_2))
					{
						Arr_Chess[getX_Click(m) + 4][getY_Click(m) + 4] = pc_2->User_ID;
						pc_2->User_Step++;
						//show(Arr_Chess);
				    }
				
				//cout << pc_1->User_Step << ' ' << pc_2->User_Step << endl;
				break;
			}
		}
		FlushBatchDraw();
		//cout << "老弟来啦!" << endl;
		if (pc_1->User_Step >= 5 && isWin(Arr_Chess, pc_1))
		{
			textWin();
			FlushBatchDraw();
			//cout << "玩家1胜利" << endl;
			break;
		}
		if (pc_2->User_Step >= 5 && isWin(Arr_Chess, pc_2))
		{
			textWin();
			FlushBatchDraw();
			//cout << "玩家2胜利" << endl;
			break;
		}
	}

	while (1);

	EndBatchDraw();

	return 0;
}
  • 要解决的冲突问题:该谁落子;棋子不能覆盖(后手落子的玩家不能覆盖前一个玩家的落子);

  • 要解决的判断胜负问题:我是用二维数组来做的棋盘,用全盘扫描来判断是否有玩家符合,所以要考虑扫描溢出的问题,下面我们来看一下扫面图解:
    在这里插入图片描述

  • 玩家胜出的情况无非就是我圈出来的这几种,所以判断胜负只需要做一个二维数组的全盘扫描,但是要注意扫描溢出问题,所以在定义棋盘数组的时候可以定义得大一些,我就吧棋盘的二维数组定义为2121的,但实际用到的只有1313;

  • 关于音乐、音效,贴图等功能,各位可先自行尝试,后续我会分享有关内容。

  • 下面来看一看运行截图:
    在这里插入图片描述

  • 关于该项目的主要几点和内容分析我就分析到这儿,如有疑问可私信或评论讨论,不足之处还请各位大佬指教,最后附上项目的网盘链接(嫌弃下载慢的可在我上传的资源中下载):https://pan.baidu.com/s/1txsNC7zMbJD3mvOcJog0WA 提取码:8bbr

;