C语言项目----五子棋

来源:微信公众号「编程学习基地」

五子棋项目设计

1.0版本,实现基本的双人对决,判断输赢

简单的双人对决很简单,AI才是重点内容,涉及了大量的算法和数学知识,贪婪算法,博弈树、评估函数、极大极小值搜索、启发式搜索、α-β剪枝等等,博主不是研究算法的,可能就是做到给个评分机制,简单AI实现

地图

传统的地图是15*15,这次的项目也不例外

#define MAP_ROW 15
#define MAP_COL 15
int Map[MAP_ROW][MAP_COL];

为了美观,给地图添加一个外边框,上下左右留30个像素

请添加图片描述

双方对决标志

两个人对决,白子下在什么位置,黑子即将落在什么位置

COORD currentPos, waitPos;

注意:COORD是Easyx提供的类,或者说是windows.h提供的坐标类

typedef struct _COORD {
    SHORT X;
    SHORT Y;
} COORD, *PCOORD;

轮到谁下棋了,也得给个标志

int flag;	// 1白棋 或 -1黑棋

主体设计

初始化,游戏绘制,数据更新

需要实现的功能:
void GameInit();	//游戏初始化
void GameDraw();	//游戏绘制
void GameUpdate();	//数据更新
bool Juge();		//返回游戏结果	比赛结束返回1 仍比赛中返回0 
void Game_Juge();	//判断
主函数
int main()
{
	initgraph(420 + 60, 420 + 60, 0);
	GameInit();
	while (1)
	{
		GameDraw();
		GameUpdate();
	}
	closegraph();
	return 0;
}

初始化

初始化比较简单,因为地图不是图片资源,所以只需要初始化一些数据

void GameInit()
{
	//初始化当前坐标和待下坐标
	ZeroMemory(&Map, sizeof(Map));
	ZeroMemory(&currentPos, sizeof(COORD));
	ZeroMemory(&waitPos, sizeof(COORD));

	Map[7][7] = -1;	//黑棋先下
	flag = 1;		//白子后下
	currentPos.X = 7;
	currentPos.Y = 7;
}

游戏绘制

地图绘制的话我是一步一步绘制

第一步:绘制整个地图,480*480

第二步:绘制棋盘左、上的数字和字母

第三步:绘制棋盘行列线

上面的步骤是绘制棋盘,下面就开始绘制棋子和标识

第四步:绘制棋子

第五步:绘制标识(标识就是最近下棋位置和等待落子位置

完整代码如下:

void GameDraw()
{
	BeginBatchDraw();
	/*******************		绘制棋盘			********************/
	wchar_t arr[4];
	setbkcolor(WHITE);					//设置窗口背景颜色
	setbkmode(1);						//设置文字背景透明
	setlinestyle(PS_SOLID, 2);          //线样式设置
	setlinecolor(BLACK);				//设置线颜色
	settextcolor(BLACK);				//设置文字颜色
	setfillcolor(RGB(255, 205, 150));   // 填充颜色设置
	cleardevice();						//清屏
	solidrectangle(0, 0, 480, 480);		//绘制无边框的正方形
	//绘制行
	for (int row = 1; row <= 15; row++)
	{
		outtextxy(10, row * 30 - 15, 64 + row);
		line(30, row * 30, 420 + 30, row * 30);
	}
	//绘制列
	for (int col = 1; col <= 15; col++)
	{
		wsprintf(arr, L"%d", col);
		outtextxy(col * 30 - 5, 5, arr);
		line(col * 30, 30, col * 30, 420 + 30);
	}
	setfillcolor(BLACK);   // 填充颜色设置
	solidcircle(4 * 30, 4 * 30, 5);
	solidcircle(12 * 30, 4 * 30, 5);
	solidcircle(4 * 30, 12 * 30, 5);
	solidcircle(12 * 30, 12 * 30, 5);
	solidcircle(8 * 30, 8 * 30, 5);

	/*******************		绘制棋子			********************/
	for (int y = 0; y < MAP_ROW; y++)
	{
		for (int x = 0; x < MAP_COL; x++)
		{
			if (Map[y][x] == 1)
			{
				//绘制白子
				setfillcolor(WHITE);   // 填充颜色设置
				solidcircle((x + 1) * 30, (y + 1) * 30, 10);
			}
			if (Map[y][x] == -1)
			{
				//绘制白子
				setfillcolor(BLACK);   // 填充颜色设置
				solidcircle((x + 1) * 30, (y + 1) * 30, 10);
			}
		}
	}
	/*******************		绘制状态			********************/
	setlinecolor(RED);				//设置线颜色
	for (int y = 0; y < 2; y++)
	{
		for (int x = 0; x < 2; x++)
		{
			//currentPos绘制
			//画横线
			line(currentPos.X * 30 + 30 - 15, currentPos.Y * 30 + 30 - 15 + y * 30,
				currentPos.X * 30 + 30 - 5, currentPos.Y * 30 + 30 - 15 + y * 30);
			line(currentPos.X * 30 + 30 + 5, currentPos.Y * 30 + 30 - 15 + y * 30,
				currentPos.X * 30 + 30 + 15, currentPos.Y * 30 + 30 - 15 + y * 30);
			//画竖线
			line(currentPos.X * 30 + 30 - 15 + 30 * y, currentPos.Y * 30 + 30 - 15,
				currentPos.X * 30 + 30 - 15 + 30 * y, currentPos.Y * 30 + 30 - 5);
			line(currentPos.X * 30 + 30 - 15 + 30 * y, currentPos.Y * 30 + 30 + 5,
				currentPos.X * 30 + 30 - 15 + 30 * y, currentPos.Y * 30 + 30 + 15);
			//waitPos绘制
			line(waitPos.X * 30 + 30 - 15, waitPos.Y * 30 + 30 - 15 + y * 30,
				waitPos.X * 30 + 30 - 5, waitPos.Y * 30 + 30 - 15 + y * 30);
			line(waitPos.X * 30 + 30 + 5, waitPos.Y * 30 + 30 - 15 + y * 30,
				waitPos.X * 30 + 30 + 15, waitPos.Y * 30 + 30 - 15 + y * 30);
			//画竖线
			line(waitPos.X * 30 + 30 - 15 + 30 * y, waitPos.Y * 30 + 30 - 15,
				waitPos.X * 30 + 30 - 15 + 30 * y, waitPos.Y * 30 + 30 - 5);
			line(waitPos.X * 30 + 30 - 15 + 30 * y, waitPos.Y * 30 + 30 + 5,
				waitPos.X * 30 + 30 - 15 + 30 * y, waitPos.Y * 30 + 30 + 15);
		}
	}
	EndBatchDraw();
}

数据更新

这需要你了解鼠标消息,了解书标配移动消息WM_MOUSEMOVE和鼠标左键点击消息WM_LBUTTONUP;其实这里换成鼠标左键双击WM_LBUTTONDBLCLK更好。

更多详细鼠标操作请查看Easyx提供的帮助文档示例程序

void GameUpdate()
{
	MOUSEMSG msg = GetMouseMsg(); // 获取鼠标信息
	int x = msg.x;
	int y = msg.y;
	switch (msg.uMsg)
	{
	case WM_MOUSEMOVE:
		// 鼠标移动 确保鼠标在棋盘区域
		if (x >= 30 && x <= 450 && y >= 30 && y <= 450)
		{
			waitPos.X = (x - 30 + 15) / 30;
			waitPos.Y = (y - 30 + 15) / 30;
		}
		break;
	case WM_LBUTTONUP:
		//确保鼠标在棋盘区域
		if (x >= 15 && x <= 465 && y >= 15 && y <= 465)
		{
			//此处无子
			if (Map[(y - 30 + 15) / 30][(x - 30 + 15) / 30] == 0)
			{
				//下子,改变currentPos
				currentPos.X = (x - 30 + 15) / 30;
				currentPos.Y = (y - 30 + 15) / 30;
				//map赋值
				Map[currentPos.Y][currentPos.X] = flag;
				flag = -flag;
			}
		}
		GameDraw();
		Game_Juge();
		break;	// 按鼠标右键退出程序
	}
}

判断输赢

上面的所有操作都是计算地图位置,简单无脑的计算,所以代码量也很少,才150行.现在到了输赢判断阶段,会有大量的逻辑操作,因为你要判断4个方向是否五子连珠其实也可以说是8个方向

因为上面描述了一个标识坐标currentPos,就是最近一次落子的地方,我们只需要判断最近一次落子的地方是否存在五子连珠就可以

左右判断

左右找的规则就是找左边(currentPos.X - index)第n个和currentpos不一样的子,

找右边(currentPos.X + index)第n个和currentpos不一样的子,上下左右东南西北都找一遍即可。因为篇幅问题只举例左右判断。

	int begin = 0, end = 0;
	/*========左右========*/
	for (int index = 1; index <= 5; index++)
	{
		//往左找
		if (currentPos.X - index >= 0 && Map[currentPos.Y][currentPos.X - index] != Map[currentPos.Y][currentPos.X])
		{
			begin = index;
			break;
		}
		else if (currentPos.X - index < 0)
		{
			begin = index;
			break;
		}
	}
	for (int index = 1; index <= 5; index++)
	{
		//往右找
		if (currentPos.X + index < 15 && Map[currentPos.Y][currentPos.X + index] != Map[currentPos.Y][currentPos.X])
		{
			end = index;
			break;
		}
		else if (currentPos.X + index >= 15)
		{
			end = index;
			break;
		}
	}
	if (begin + end >= 6)
	{
		return true;
	}

很繁琐有木有,代码还多的一批,因为以前没写过五子棋,自己找感觉判断的。后来写的差不多,遇到了AI问题,就去博客逛了逛,发现一个思路一致但是代码更精练的同学写的一篇博客,地址如下

https://www.cnblogs.com/liugangjiayou/p/10686886.html

我想狡辩一下:其实我代码虽然多,但是一旦横向判断五子连着,就结束后面的判断,整体可能…卧槽,狡辩不下去了,好吧是我菜。

源码获取

五子棋双人对战版
五子棋AI对战版

关注微信公众号 编程学习基地 发送关键字 五子棋 免费获取源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeRoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值