超简单的c++五子棋游戏,使用easyx图形库制作你的五子棋游戏吧!

首先需要准备编程环境,建议使用vs2022,因为easyx图形库可以一键安装在vs上,比较方便。

easyx官网:EasyX Graphics Library for C++

参考网上其他人的代码,发现很多是使用检测鼠标位置和点击实现落子,我认为比较麻烦,此项目采用使用上下左右按键移动落子框,按下enter实现落子。

界面,棋子绘制

首先先来定义一些我们需要的常量,导入必须的包。

#include <Windows.h>//包含弹窗
#include <iostream>
#include <graphics.h>
#define CHESSSIZE 10//棋子半径
#define GAP 40      //棋盘线之间的间隔
#define INITIALX 40 //初始x值
#define INITIALY 40 //初始y值
#define BOXSIZE 20  //落子框大小
using namespace std;

再来绘制棋盘,这里绘制的是15*15的棋盘,左上角坐标为(40,40)行列间隔为40。

//绘制棋盘
for (int i = GAP; i <= 600; i += GAP)
{
	line(i, GAP, i, 600);
}
for (int j = GAP; j <= 600; j += GAP)
{
		line(GAP, j, 600, j);
}

接着是落子框的绘制,落子框中心需要在线的交点上,初始落子框设定在棋盘左上角。

先定义一个绘制函数

void DrawBox(int lx,int ly,int rx,int ry) 
{
	rectangle(lx, ly, rx, ry);
}

上方函数中,recangle();为easyx中绘制不填充方框的函数。

函数将会在以后的主循环中调用。

方框还需要移动,这里定义一个结构体来方便以后实现方框移动操作。

struct Box
{
	int lx = INITIALX - BOXSIZE;
	int ly = INITIALY - BOXSIZE;
	int rx = INITIALX + BOXSIZE;
	int ry = INITIALY + BOXSIZE;
};

下面是定义绘画单个棋子的函数

void DrawChess(int x,int y,int r,bool type)
{
	//true为实心,false为空心(true为白方,false为黑方)
	if (type)
	{
		fillcircle(x, y, r);
	}else
	{
		circle(x, y, r);
	}
}

调用时,传入棋子的x,y坐标以及半径r,类型type, type用于判断是黑子还是白子。

到这里,基本要素差不多齐全了

棋子储存

如何存储棋子并判断胜负呢?

这里采用一个数组来存储,通过遍历数组来判断胜负。

int main()
{
	initgraph(640, 640);	

	char board[15][15] = { 0 };//储存棋子

	Box movebox;

	bool chess_type = false;//判断棋子类型
    
    bool keyPressed = false; // 按键状态

...
...
...
}

逐行解释代码:

首先使用initgraph()函数创建一个640*640的窗口

然后定义我们的棋盘数组

接着是定义移动落子框

初始化棋子类型为false(这里规定false为黑子)

检测按键是否按下(easyx有消息处理函数,但貌似按键按下和弹起均会检测,就导致按下一次按键会执行两次程序,这里加入按键检测确保按下一次按键只执行一次程序,如果有更好的办法欢迎讨论。)

然后便是游戏的主要部分:主循环

...
...
...
while (1)
{
	cleardevice();

	BeginBatchDraw();

	DrawBoard(board);//绘制棋盘和棋子

	DrawBox(movebox.lx, movebox.ly, movebox.rx, movebox.ry);//绘制落子框

	EndBatchDraw();

	ExMessage mes = getmessage(EX_KEY);
...
...
...
}

这里采用BeginBatchDraw();与BeginBatchDraw();双缓冲来防止闪屏,使用mes接收键盘输入信息。

DrawBoard();函数用于绘画棋盘和棋子,棋盘绘制代码已在上方给出,我们需要不断地绘制场上已有的棋子,绘制代码会在下面给出。

检测用户输入,落子

方框的移动与落子操作,使用switch实现。

switch (mes.vkcode)
{
case VK_UP:
	if (movebox.ly >= INITIALY && !keyPressed)
	{
		movebox.ly -= BOXSIZE*2;
		movebox.ry -= BOXSIZE*2;
		keyPressed = true; // 记录按下状态
	}
	break;
case VK_DOWN:
	if (movebox.ry <= 600 && !keyPressed)
	{
		movebox.ly += BOXSIZE*2;
		movebox.ry += BOXSIZE*2;
		keyPressed = true;
	}
	break;
case VK_LEFT:
	if (movebox.lx >= INITIALX && !keyPressed)
	{
		movebox.lx -= BOXSIZE*2;
		movebox.rx -= BOXSIZE*2;
		keyPressed = true;
	}
	break;
case VK_RIGHT:
	if (movebox.rx <= 600 && !keyPressed)
	{
		movebox.lx += BOXSIZE*2;
		movebox.rx += BOXSIZE*2;
		keyPressed = true;
	}
	break;
case VK_RETURN://落子
	if (!keyPressed) 
    {

		int x = (movebox.lx - BOXSIZE) / INITIALX;
		int y = (movebox.ly - BOXSIZE) / INITIALX;

		if (board[y][x] == 0)// 检查是否已有棋子
        {
			board[y][x] = chess_type ? 1 : 2; // 1为白棋,2为黑棋
			chess_type = !chess_type; // 切换棋子类型
		}
		
		keyPressed = true;
	}
}

方框的移动为前几个case语句,落子为最后一个case语句。

case判断的是虚拟键码,可以按照虚拟键码表更改。虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn

这里采用if语句限制方框的移动,防止方框移出棋盘同时检测键盘按下状态,防止switch执行两次。

在用户按下enter键后,会检测数组该位置是否有棋子,若没有,则按照棋子类型落子,落子后切换棋子类型,最后更改按键信息。 

关于落子位置,采用读取方框边角的坐标,将坐标加减BOXSIZE的二倍来定位到棋盘交点。

利用前面定义的board数组来存储我们的棋子信息,使用1来代表白棋,2来代表黑棋。

循环绘制

我们需要循环绘制我们的棋盘和棋子,使其显示在屏幕上,我们可以遍历数组,来找到棋子的类型,通过计算找到棋子位置。

绘制棋子:

for (int i = 0;i < 15; ++i)
	{
		for (int j = 0;j < 15; ++j)
		{
			if (board[i][j] == 1)
			{
				DrawChess(INITIALX + j * BOXSIZE*2, INITIALY + i * BOXSIZE*2, CHESSSIZE, true);
			}
			else if (board[i][j] == 2)
			{
				DrawChess(INITIALX + j * BOXSIZE*2, INITIALY + i * BOXSIZE*2, CHESSSIZE, false);
			}
		}
	}

将前面的棋盘绘制代码与棋子绘制代码放入 DrawBoard();函数中

void DrawBoard(char board[15][15])
{
	//绘制棋盘
	for (int i = GAP; i <= 600; i += GAP)
	{
		line(i, GAP, i, 600);
	}
	for (int j = GAP; j <= 600; j += GAP)
	{
			line(GAP, j, 600, j);
	}
	//绘制已有棋子
	for (int i = 0;i < 15; ++i)
	{
		for (int j = 0;j < 15; ++j)
		{
			if (board[i][j] == 1)
			{
				DrawChess(INITIALX + j * BOXSIZE*2, INITIALY + i * BOXSIZE*2, CHESSSIZE, true);
			}
			else if (board[i][j] == 2)
			{
				DrawChess(INITIALX + j * BOXSIZE*2, INITIALY + i * BOXSIZE*2, CHESSSIZE, false);
			}
		}
	}

}

胜负判断

现在已经能实现在棋盘任意处落子的操作了,接下来就是胜负判断,这里依然写一个胜负判断函数,方便以后调用。

int VectoryCheck(char board[15][15])
{
	//直线检测
	for (int i = 0; i < 15; ++i)
	{
		for (int j = 0; j < 11; ++j)
		{
			if (board[i][j] == 1 && board[i][j + 1] == 1 && board[i][j + 2] == 1 && board[i][j + 3] == 1 && board[i][j + 4] == 1)
			{
				return 1;//白棋获胜
			}
			else if(board[i][j] == 2 && board[i][j + 1] == 2 && board[i][j + 2] == 2 && board[i][j + 3] == 2 && board[i][j + 4] == 2)
			{
				return 2;//黑棋获胜
			}
		}
	}
	// 垂直检测
	for (int j = 0; j < 15; ++j)
	{
		for (int i = 0; i < 11; ++i)
		{
			if (board[i][j] == 1 && board[i + 1][j] == 1 && board[i + 2][j] == 1 && board[i + 3][j] == 1 && board[i + 4][j] == 1)
			{
				return 1;
			}
			else if (board[i][j] == 2 && board[i + 1][j] == 2 && board[i + 2][j] == 2 && board[i + 3][j] == 2 && board[i + 4][j] == 2)
			{
				return 2;
			}
		}
	}
	// 斜向检测(从左上到右下)
	for (int i = 0; i < 11; ++i)
	{
		for (int j = 0; j < 11; ++j)
		{
			if (board[i][j] == 1 && board[i + 1][j + 1] == 1 && board[i + 2][j + 2] == 1 && board[i + 3][j + 3] == 1 && board[i + 4][j + 4] == 1)
			{
				return 1; // 白棋获胜
			}
			else if (board[i][j] == 2 && board[i + 1][j + 1] == 2 && board[i + 2][j + 2] == 2 && board[i + 3][j + 3] == 2 && board[i + 4][j + 4] == 2)
			{
				return 2; // 黑棋获胜
			}
		}
	}

	// 斜向检测(从右上到左下)
	for (int i = 0; i < 11; ++i)
	{
		for (int j = 4; j < 15; ++j)
		{
			if (board[i][j] == 1 && board[i + 1][j - 1] == 1 && board[i + 2][j - 2] == 1 && board[i + 3][j - 3] == 1 && board[i + 4][j - 4] == 1)
			{
				return 1; // 白棋获胜
			}
			else if (board[i][j] == 2 && board[i + 1][j - 1] == 2 && board[i + 2][j - 2] == 2 && board[i + 3][j - 3] == 2 && board[i + 4][j - 4] == 2)
			{
				return 2; // 黑棋获胜
			}
		}
	}

	return false;
}

使用for循环遍历数组,检测横向,纵向,斜向是否有相同的五个棋子。注意初始化i和j的值以及判断条件,不要超出数组范围。

利用返回值判断胜负,返回1则白棋胜,返回2则黑棋胜。

if (VectoryCheck(board) == 1)
		{
			MessageBox(NULL, "白方胜利!", "结束", MB_SYSTEMMODAL);
			closegraph();
		}
		else if (VectoryCheck(board) == 2)
		{
			MessageBox(NULL, "黑方胜利!", "结束", MB_SYSTEMMODAL);
			closegraph();
		}

胜利后弹出弹窗并关闭窗口。

之后在主函数调用胜负判断函数,加上按键释放的判断就行了

按键释放判断:

if (mes.message == WM_KEYUP)
		{
			keyPressed = false; // 释放按键时重置状态
		}

完整代码

#include <Windows.h>//包含弹窗
#include <iostream>
#include <graphics.h>
#define CHESSSIZE 10
#define GAP 40
#define INITIALX 40
#define INITIALY 40
#define BOXSIZE 20
using namespace std;
struct Box
{
	int lx = INITIALX - BOXSIZE;
	int ly = INITIALY - BOXSIZE;
	int rx = INITIALX + BOXSIZE;
	int ry = INITIALY + BOXSIZE;
};
void DrawChess(int x,int y,int r,bool type)
{
	//true为实心,false为空心(true为白方,false为黑方)
	if (type)
	{
		fillcircle(x, y, r);
	}else
	{
		circle(x, y, r);
	}
}
void DrawBox(int lx,int ly,int rx,int ry) 
{
	rectangle(lx, ly, rx, ry);
}
void DrawBoard(char board[15][15])
{
	//绘制棋盘
	for (int i = GAP; i <= 600; i += GAP)
	{
		line(i, GAP, i, 600);
	}
	for (int j = GAP; j <= 600; j += GAP)
	{
			line(GAP, j, 600, j);
	}
	//绘制已有棋子
	for (int i = 0;i < 15; ++i)
	{
		for (int j = 0;j < 15; ++j)
		{
			if (board[i][j] == 1)
			{
				DrawChess(INITIALX + j * BOXSIZE*2, INITIALY + i * BOXSIZE*2, CHESSSIZE, true);
			}
			else if (board[i][j] == 2)
			{
				DrawChess(INITIALX + j * BOXSIZE*2, INITIALY + i * BOXSIZE*2, CHESSSIZE, false);
			}
		}
	}

}
int VectoryCheck(char board[15][15])
{
	//直线检测
	for (int i = 0; i < 15; ++i)
	{
		for (int j = 0; j < 11; ++j)
		{
			if (board[i][j] == 1 && board[i][j + 1] == 1 && board[i][j + 2] == 1 && board[i][j + 3] == 1 && board[i][j + 4] == 1)
			{
				return 1;//白棋获胜
			}
			else if(board[i][j] == 2 && board[i][j + 1] == 2 && board[i][j + 2] == 2 && board[i][j + 3] == 2 && board[i][j + 4] == 2)
			{
				return 2;//黑棋获胜
			}
		}
	}
	// 垂直检测
	for (int j = 0; j < 15; ++j)
	{
		for (int i = 0; i < 11; ++i)
		{
			if (board[i][j] == 1 && board[i + 1][j] == 1 && board[i + 2][j] == 1 && board[i + 3][j] == 1 && board[i + 4][j] == 1)
			{
				return 1;
			}
			else if (board[i][j] == 2 && board[i + 1][j] == 2 && board[i + 2][j] == 2 && board[i + 3][j] == 2 && board[i + 4][j] == 2)
			{
				return 2;
			}
		}
	}
	// 斜向检测(从左上到右下)
	for (int i = 0; i < 11; ++i)
	{
		for (int j = 0; j < 11; ++j)
		{
			if (board[i][j] == 1 && board[i + 1][j + 1] == 1 && board[i + 2][j + 2] == 1 && board[i + 3][j + 3] == 1 && board[i + 4][j + 4] == 1)
			{
				return 1; // 白棋获胜
			}
			else if (board[i][j] == 2 && board[i + 1][j + 1] == 2 && board[i + 2][j + 2] == 2 && board[i + 3][j + 3] == 2 && board[i + 4][j + 4] == 2)
			{
				return 2; // 黑棋获胜
			}
		}
	}

	// 斜向检测(从右上到左下)
	for (int i = 0; i < 11; ++i)
	{
		for (int j = 4; j < 15; ++j)
		{
			if (board[i][j] == 1 && board[i + 1][j - 1] == 1 && board[i + 2][j - 2] == 1 && board[i + 3][j - 3] == 1 && board[i + 4][j - 4] == 1)
			{
				return 1; // 白棋获胜
			}
			else if (board[i][j] == 2 && board[i + 1][j - 1] == 2 && board[i + 2][j - 2] == 2 && board[i + 3][j - 3] == 2 && board[i + 4][j - 4] == 2)
			{
				return 2; // 黑棋获胜
			}
		}
	}

	return false;
}


int main()
{
	initgraph(640, 640);	

	char board[15][15] = { 0 };

	Box movebox;

	bool chess_type = false;//判断棋子类型

	bool keyPressed = false; // 按键状态,由于按键的按下和弹起均会被检测,会导致switch语句被执行两次,故加入按键检测。

	


	while (1)
	{
		cleardevice();

		BeginBatchDraw();

		DrawBoard(board);//绘制棋盘和棋子

		DrawBox(movebox.lx, movebox.ly, movebox.rx, movebox.ry);

		EndBatchDraw();

		ExMessage mes = getmessage(EX_KEY);

		//控制方框移动与落子
		switch (mes.vkcode)
		{
		case VK_UP:
			if (movebox.ly >= INITIALY && !keyPressed)
			{
				movebox.ly -= BOXSIZE*2;
				movebox.ry -= BOXSIZE*2;
				keyPressed = true; // 记录按下状态
			}
			break;
		case VK_DOWN:
			if (movebox.ry <= 600 && !keyPressed)
			{
				movebox.ly += BOXSIZE*2;
				movebox.ry += BOXSIZE*2;
				keyPressed = true;
			}
			break;
		case VK_LEFT:
			if (movebox.lx >= INITIALX && !keyPressed)
			{
				movebox.lx -= BOXSIZE*2;
				movebox.rx -= BOXSIZE*2;
				keyPressed = true;
			}
			break;
		case VK_RIGHT:
			if (movebox.rx <= 600 && !keyPressed)
			{
				movebox.lx += BOXSIZE*2;
				movebox.rx += BOXSIZE*2;
				keyPressed = true;
			}
			break;
		case VK_RETURN://落子
			if (!keyPressed)
			{

				int x = (movebox.lx - BOXSIZE) / INITIALX;
				int y = (movebox.ly - BOXSIZE) / INITIALX;

				if (board[y][x] == 0)
				{ // 检查是否已有棋子
					board[y][x] = chess_type ? 1 : 2; // 1为白棋,2为黑棋
					chess_type = !chess_type; // 切换棋子类型
				}
				
				keyPressed = true;
			}
		}
		if (VectoryCheck(board) == 1)
		{
			MessageBox(NULL, "白方胜利!", "结束", MB_SYSTEMMODAL);
			closegraph();
		}
		else if (VectoryCheck(board) == 2)
		{
			MessageBox(NULL, "黑方胜利!", "结束", MB_SYSTEMMODAL);
			closegraph();
		}
		// 检测按键释放,重置状态
		if (mes.message == WM_KEYUP)
		{
			keyPressed = false; // 释放按键时重置状态
		}

		
	}

	return 0;
}

运行结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值