一、移动规则的制定
关于中国象棋游戏棋子移动规则有马走日,象走田等说法,下面就将各类棋子的移动规则进行说明及实现(开局时默认红棋在下方,黑棋在上方)。棋子移动规则中fromX、fromY为起始点坐标,toX、toY为落子点坐标。
1:兵/卒的移动规则,兵/卒在未越过楚河汉界之前只能前行,越过后可以左右移动和前行,不可后退,每次只移动一格位置。
bool myWidget::bing_moveRule(int fromX, int fromY, int toX, int toY)
{
//红兵
if(chessmap[fromY][fromX]==bing)
{
qDebug()<<QString("红兵行走");
if(toY>fromY)
return false;//兵不可回走
if(toY>4&&(fromY==toY))
return false;//过河前只可直走
if(fromY-toY+abs(fromX-toX)>1)
return false;//只可走一步直线
else
return true;
}
//黑卒
else if(chessmap[fromY][fromX]==zhu)
{
if(toY<fromY)
return false;//卒不可回走
if(toY<5&&(fromY==toY))
return false;//过河前只可直走
if(toY-fromY+abs(fromX-toX)>1)
return false;//只可走一步直线
else
return true;
}
return true;
}
2:炮的移动规则,炮是直行的,炮在吃子时,必须与被吃的棋子在同一直线上,且炮与该棋子之间有且只有另一个棋子。
bool myWidget::pao_moveRule(int fromX, int fromY, int toX, int toY)
{
int numTemp=0;//记录炮吃子时路线上的棋子数
if(toX<0||toX>=row||toY<0||toY>=line)
return false;
if(fromX!=toX&&fromY!=toY)
return false;//炮只可走直线
//炮不吃子时
if(chessmap[toY][toX]<30)
{
if(toY<fromY)
{//上移
for(int i=fromY-1;i>toY;i--)
if(chessmap[i][fromX]>30)
return false;
}
if(fromY<toY)
{//下移
for(int i=fromY+1;i<toY;i++)
if(chessmap[i][fromX]>30)
return false;
}
if(fromX<toX)
{//右移
for(int i=fromX+1;i<toX;i++)
if(chessmap[fromY][i]>30)
return false;
}
if(fromX>toX)
{//左移
for(int i=fromX-1;i>toX;i--)
if(chessmap[fromY][i]>30)
return false;
}
}
//炮吃子时
else
{
if(toY<fromY)
{//上移
for(int i=fromY-1;i>toY;i--)
if(chessmap[i][fromX]>30)
numTemp++;
}
if(fromY<toY)
{//下移
for(int i=fromY+1;i<toY;i++)
if(chessmap[i][fromX]>30)
numTemp++;
}
if(fromX<toX)
{//右移
for(int i=fromX+1;i<toX;i++)
if(chessmap[fromY][i]>30)
numTemp++;
}
if(fromX>toX)
{//左移
for(int i=fromX-1;i>toX;i--)
if(chessmap[fromY][i]>30)
numTemp++;
}
if(numTemp!=1)
return false;
}
return true;
}
3:车的移动规则,车的移动规则与炮的类似,只是车在吃子时,车与被吃子之间是没有其他棋子的。
bool myWidget::ju_moveRule(int fromX, int fromY, int toX, int toY)
{
int numTemp=0;//记录车吃子时路线上的棋子数
if(toX<0||toX>=row||toY<0||toY>=line)
return false;
if(fromX!=toX&&fromY!=toY)
return false;//车只可走直线
//车不吃子时
if(chessmap[toY][toX]<30)
{
if(toY<fromY)
{//上移
for(int i=fromY-1;i>toY;i--)
if(chessmap[i][fromX]>30)
return false;
}
if(fromY<toY)
{//下移
for(int i=fromY+1;i<toY;i++)
if(chessmap[i][fromX]>30)
return false;
}
if(fromX<toX)
{//右移
for(int i=fromX+1;i<toX;i++)
if(chessmap[fromY][i]>30)
return false;
}
if(fromX>toX)
{//左移
for(int i=fromX-1;i>toX;i--)
if(chessmap[fromY][i]>30)
return false;
}
}
//车吃子时
else
{
if(toY<fromY)
{//上移
for(int i=fromY-1;i>toY;i--)
if(chessmap[i][fromX]>30)
numTemp++;
}
if(fromY<toY)
{//下移
for(int i=fromY+1;i<toY;i++)
if(chessmap[i][fromX]>30)
numTemp++;
}
if(fromX<toX)
{//右移
for(int i=fromX+1;i<toX;i++)
if(chessmap[fromY][i]>30)
numTemp++;
}
if(fromX>toX)
{//左移
for(int i=fromX-1;i>toX;i--)
if(chessmap[fromY][i]>30)
numTemp++;
}
if(numTemp!=0)
return false;
}
return true;
}
4:马的移动规则,俗称马走日,同时要注意蹩马脚的情况。
bool myWidget::ma_moveRule(int fromX, int fromY, int toX, int toY)
{
int i=-1,j=-1;
//马走日字
if(!((abs(toX-fromX)==1&&abs(toY-fromY)==2)||(abs(toX-fromX)==2&&abs(toY-fromY)==1)))
return false;
//蹩马脚的情况
if(toX-fromX==2)
{
j=fromX+1;i=fromY;
}
else if(fromX-toX==2)
{
j=fromX-1;i=fromY;
}
else if(fromY-toY==2)
{
j=fromX;i=fromY-1;
}
else if(toY-fromY==2)
{
j=fromX;i=fromY+1;
}
if(i>=0&&j>=0&&chessmap[i][j]>30)
return false;
return true;
}
5:象/相的移动规则。其移动规则与马的移动规则类似,相走田字,也存在蹩脚的情况。同时需注意象/相不可越过楚河汉界。
bool myWidget::xiang_moveRule(int fromX, int fromY, int toX, int toY)
{
//红相
if(chessmap[fromY][fromX]==Rxiang)
{
if(toY<5)
return false;//相不可过河
}
//黑象
else if(chessmap[fromY][fromX]==Bxiang)
{
if(toY>4)
return false;//象不可过河
}
if(abs(fromX-toX)!=2||abs(fromY-toY)!=2)
return false;//相/象走田字
if(chessmap[(fromY+toY)/2][(fromX+toX)/2]!=0)
return false;//相/象眼被塞
return true;
}
6:士/仕的移动规则。士/仕不可以出九宫格,每次只沿对角线走动一格。
bool myWidget::shi_moveRule(int fromX, int fromY, int toX, int toY)
{
if(chessmap[fromY][fromX]==Rshi)
{
if(toX<3||toX>5||toY<7)
return false;//红士必须在九宫格内行走
}
else if(chessmap[fromY][fromX]==Bshi)
{
if(toX<3||toX>5||toY>2)
return false;//黑士必须在九宫格内行走
}
if(abs(toX-fromX)!=1||abs(toY-fromY)!=1)
return false;//士沿对角线走一步
return true;
}
7:将/帅的移动规则,将/帅与士一样不可以出九宫格,但将/帅是走直线一步。
bool myWidget::jiang_moveRule(int fromX, int fromY, int toX, int toY)
{
if(chessmap[fromY][fromX]==Rshuai)
{
if(toX<3||toX>5||toY<7)
return false;//红帅必须在九宫格内行走
}
else if(chessmap[fromY][fromX]==Bjiang)
{
if(toX<3||toX>5||toY>2)
return false;//黑将必须在九宫格内行走
}
if((abs(toX-fromX)+abs(toY-fromY))!=1)
return false;//将/帅走一步直线
return true;
}
二、移动规则的使用
1:为了更加方便使用以上的各类棋子移动规则,可定义一个函数,在里面使用switch函数根据具体的棋子来选择其对应的移动规则。
bool myWidget::game_moveRule(int fromX, int fromY, int toX, int toY)
{
bool ans=false;
//避免越界
if(toX<0||toX>=col||toY<0||toY>=row)
return ans;
//避免吃掉己方棋子
if(chessmap[fromY][fromX]>30&&chessmap[fromY][fromX]<40&&chessmap[toY][toX]>30&&chessmap[toY][toX]<40)
return ans;
if(chessmap[fromY][fromX]>40&&chessmap[toY][toX]>40)
return ans;
switch(chessmap[fromY][fromX])
{
case bing:case zhu:
ans=bing_moveRule(fromX,fromY,toX,toY);break;
case Rpao:case Bpao:
ans=pao_moveRule(fromX,fromY,toX,toY);break;
case Rju:case Bju:
ans=ju_moveRule(fromX,fromY,toX,toY);break;
case Rma:case Bma:
ans=ma_moveRule(fromX,fromY,toX,toY);break;
case Rxiang:case Bxiang:
ans=xiang_moveRule(fromX,fromY,toX,toY);break;
case Rshi:case Bshi:
ans=shi_moveRule(fromX,fromY,toX,toY);break;
case Rshuai:case Bjiang:
ans=jiang_moveRule(fromX,fromY,toX,toY);break;
}
return ans;
}
2:因为加入了移动规则,所以需要对mousePressEvent函数内容进行改动。加入棋子的移动判定,同时加入棋子第一次被点击后的效果图的显示。
改动后的mousePressEvent函数
//鼠标点击(按压)事件
void myWidget::mousePressEvent(QMouseEvent *event)
{
QPoint point=event->pos();
int w=this->width()/cool;
int h=this->height()/row;
X=point.x()/w;//x轴对应的下标
Y=point.y()/h;//y轴所对应的下标
emit clicked(X,Y,mou);//发出信号
if(mou==0&&chessmap[Y][X]>30)
{
fromX=X;fromY=Y;//记下移动棋子初始位置下标
chessmap[fromY][fromX]+=20;//修改为棋子点击效果图的值
qDebug()<<QString("初始坐标:")<<"("<<Y<<","<<X<<")";
mou=1;//第一次点击完成
update();//展示出棋子的被点击效果
}
else if(mou==1)
{
chessmap[fromY][fromX]-=20;//修改为棋子值
if(game_moveRule(fromX,fromY,X,Y))
{
chessmap[Y][X]=chessmap[fromY][fromX];//将目标位置的值改为被移动棋子的值
chessmap[fromY][fromX]=chessboardVaule[fromY][fromX];//将棋子初始位置换位棋盘的值
qDebug()<<QString("目的坐标:")<<"("<<Y<<","<<X<<")";
}
mou=0;//第二次点击完成
update();//重绘
}
}
到此棋子的移动规则就添加完成。双人象棋游戏的基本需求也就只剩下一方每次只走一步的设定,悔棋的操作以及输赢的判定。实现这些功能双人象棋游戏就可以完成啦。