QT项目三:中国象棋

1,简介

QT实现中国象棋单机版,本来以为会花费不少时间的。由于之前2个游戏项目的练手,这个主要只在走棋算法部分花了些时间研究,最后一共2个晚上完成。

由于只是QT学习,没有考虑更多复杂功能,比如人机对战等,那个可能算法就得花很长时间。

 

2,效果

 

 

3,设计思路

 

准备2个棋盘棋子图片:

 

棋子类:

 

#pragma once
#include "qpainter.h"


enum ITEM_TYPE{
	ITEM_SHUAI = 0,	//帅
	ITEM_SHI,		//士
	ITEM_XIANG,		//象
	ITEM_JU,			//车
	ITEM_MA,			//马
	ITEM_PAO,		//炮
	ITEM_BING,		//兵
	ITEM_MAX,
};

enum ITEM_COLOR{
	COLOR_RED = 0,	//红方
	COLOR_BLACK,		//黑方
	COLOR_MAX,
};

class Item
{
public:
	Item(){}
	Item(ITEM_TYPE t,ITEM_COLOR c,QPoint pt);	
	~Item(void);

public:
	ITEM_TYPE m_type;		//棋子类型
	ITEM_COLOR m_color;	//红方黑方

	QPoint m_pt;			//位置
	bool m_bShow;			//是否显示,实现选中时闪烁效果
};


可以看到,棋子类型的定义和图片中棋子顺序一致,是为了直接以此枚举值计算棋子在图片中的位置。

 

 

MainWindow.h:

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "Item.h"
#include "qmap.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

	void InitItems();								//初始化所有棋子位置
	void NewGame();									//新游戏

	bool FindItemAtPoint(QPoint pt,Item& item);					//给定一个棋盘上的点,查找是否存在棋子
	bool DeleteItemAtPoint(QPoint pt,bool& bDeleteSHUAI);				//bDeleteSHUAI:是否打掉的是“帅”或“将”
	void SetItemShow(Item item,bool bShow);						//设置棋子显隐
	bool MoveItem(Item item,QPoint ptMoveTo);					//走棋逻辑
	void ChangeItemPoint(QPoint ptItem,QPoint pt);					//改变棋子位置属性

	void DrawItem(QPainter& painter,Item item);					//绘制棋子

	//获取棋子能移动的区域
	void GetMoveArea(Item item,QVector<QPoint>& area);
	void GetMoveArea_JU(Item item,QVector<QPoint>& area);
	void GetMoveArea_MA(Item item,QVector<QPoint>& area);
	void GetMoveArea_XIANG(Item item,QVector<QPoint>& area);
	void GetMoveArea_SHI(Item item,QVector<QPoint>& area);
	void GetMoveArea_SHUAI(Item item,QVector<QPoint>& area);
	void GetMoveArea_PAO(Item item,QVector<QPoint>& area);
	void GetMoveArea_BING(Item item,QVector<QPoint>& area);
protected:
	void paintEvent(QPaintEvent *);
	void mousePressEvent(QMouseEvent *);
	void timerEvent(QTimerEvent *);

private:
    Ui::MainWindow *ui;

	int m_nItemWidth;
	int m_nItemHeight;
	QPixmap m_ItemsImage;		//棋子图片

	QVector<Item> m_items;		//所有棋子
	Item m_SelectedItem;
	bool m_bExistSelectedItem;	//是否已存在选中的棋子
	bool m_bIsRedTurn;		//当前该红方下
};

#endif // MAINWINDOW_H
					


其中主要逻辑都在mousePressEvent 鼠标响应:

 

 

//鼠标点击,走棋、吃棋等逻辑
void MainWindow::mousePressEvent(QMouseEvent * e)
{
	//获得鼠标点所对应的棋盘点pt
	QPoint pt;
	pt.setX( (e->pos().x() - START_X ) / RECT_WIDTH);
	pt.setY( (e->pos().y() - START_Y ) / RECT_HEIGHT);

	//是否有选中的棋子
	if(m_bExistSelectedItem)
	{
		//已存在棋子,判断鼠标点击的是否是选中棋子
		if (pt == m_SelectedItem.m_pt)
		{
			//再次点击已经选择的棋子,什么也不做
			return;
		}
		//点击其它棋子
		Item ClickedItem;
		if (FindItemAtPoint(pt,ClickedItem))
		{
			//点击的同色的另外一个棋子,改选
			if ( (m_bIsRedTurn && ClickedItem.m_color == COLOR_RED) ||
				 (!m_bIsRedTurn && ClickedItem.m_color != COLOR_RED))
			{
				SetItemShow(m_SelectedItem,true);
				m_SelectedItem = ClickedItem;
				return;
			}
		}
		//点击的异色棋子,判断是否能走能吃
		QVector<QPoint> moveArea;
		GetMoveArea(m_SelectedItem,moveArea);		//获取已选择棋子的可移动区域
		if (moveArea.contains(pt))					
		{
			//包含当前鼠标点中的棋子,则能吃
			bool bDeleteSHUAI = false;
			DeleteItemAtPoint(pt,bDeleteSHUAI);
			ChangeItemPoint(m_SelectedItem.m_pt,pt);
			if (bDeleteSHUAI)
			{
				QString str = m_bIsRedTurn?QStringLiteral("红方胜利!"):QStringLiteral("黑方胜利!");
				QMessageBox::information(NULL,  "GAME OVER  ",str, QMessageBox::Yes , QMessageBox::Yes);
				NewGame();
				return ;
			}
			m_bExistSelectedItem = false;
			m_bIsRedTurn = !m_bIsRedTurn;
			update();
			return ;
		}
		else
		{
			//不能走到该位置
			return ;
		}
	}
	else
	{
		//当前没有选中棋子
		Item ClickedItem;
		if (FindItemAtPoint(pt,ClickedItem))
		{
			//如果点中一个棋子,是当前走棋方的颜色,就选中了
			if ( (m_bIsRedTurn && ClickedItem.m_color == COLOR_RED) ||
				(!m_bIsRedTurn && ClickedItem.m_color == COLOR_BLACK))
			{
				m_SelectedItem = ClickedItem;
				m_bExistSelectedItem = true;
				return;
			}
		}
	}
}


里面最重要的就是这个GetMoveArea 函数,用来获取每个棋子的可走动的区域:

 

 

void MainWindow::GetMoveArea(Item item,QVector<QPoint>& moveArea)
{
	switch (item.m_type)
	{
	case ITEM_JU:
		{
			GetMoveArea_JU(item,moveArea);
			break;
		}
	case ITEM_MA:
		{
			GetMoveArea_MA(item,moveArea);
			break;
		}
	case ITEM_XIANG:
		{
			GetMoveArea_XIANG(item,moveArea);
			break;
		}
	case ITEM_SHI:
		{
			GetMoveArea_SHI(item,moveArea);
			break;
		}
	case ITEM_SHUAI:
		{
			GetMoveArea_SHUAI(item,moveArea);
			break;
		}
	case ITEM_PAO:
		{
			GetMoveArea_PAO(item,moveArea);
			break;
		}
	case ITEM_BING:
		{
			GetMoveArea_BING(item,moveArea);
			break;
		}
	}
}

 

 

比如对于棋子“马”,求可以走到的位置:

 

void MainWindow::GetMoveArea_MA( Item item,QVector<QPoint>& moveArea )
{
	//棋子“马”的计算可移动区域算法简介:
	//1,求出8个待选位置,8个位置的偏移是(-2,-1)(-2,1)(2,-1)(2,1)(1,-2)(1,2)(-1,-2)(-1,2)存在关系:|x|+|y|=3
	//2,判断待选位置是否在棋盘内
	//3,判断中间是否有卡位的棋子
	//4,位置上是否已存在同色棋子
	Item item2;
	for (int i = -2; i<=2; i++)
	{
		for(int j = -2; j<=2; j++)
		{
			if (qAbs(i) + qAbs(j) == 3)
			{
				QPoint ptNew = item.m_pt + QPoint(i,j);
				if (ptNew.x() >= 0 && ptNew.x() <= 8 && ptNew.y()>=0 && ptNew.y() <= 9)
				{

				}
				else
				{
					continue;
				}

				//求该方向行走路线的 卡位元素位置
				QPoint ptDirect(0,0);	
				if (qAbs(i) > qAbs(j))
				{
					if (i>0)
					{
						ptDirect = QPoint(1,0);
					}
					else
					{
						ptDirect = QPoint(-1,0);
					}
				}
				else
				{
					if (j>0)
					{
						ptDirect = QPoint(0,1);
					}
					else
					{
						ptDirect = QPoint(0,-1);
					}
				}
				QPoint ptHit = item.m_pt + ptDirect;	//马的卡位元素位置
				if (FindItemAtPoint(ptHit,item2))
				{
					//卡位
					continue;
				}
				if (FindItemAtPoint(ptNew ,item2) && item.m_color == item2.m_color)
				{
					//有本组item
					continue;
				}
				moveArea.append(ptNew);
			}
		}
	}
}


其它棋子类似,就是仔细处理下就OK了。

 

 

4,源码

 

源码已上传至群文件,可在学习群免费下载!

群号码:1149411109

群名称:Qt实战派学习群

 

 

 

 

 

 

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逆枫゛

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

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

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

打赏作者

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

抵扣说明:

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

余额充值