前言:在看完上一期《象棋人工智能算法的C++實(shí)現(xiàn)(一)》后,是不是對(duì)這個(gè)項(xiàng)目感到有點(diǎn)小興奮呢?但是我首先要聲明的是,這并不是最前沿的人工智能,所用的算法或許不是最快速的,只是閑的沒事做著玩的一個(gè)小項(xiàng)目。歡迎各位攻城獅、各位行業(yè)大牛的討論、批評(píng)與指正。
創(chuàng)一個(gè)小群,供大家學(xué)習(xí)交流聊天
如果有對(duì)學(xué)C++方面有什么疑惑問題的,或者有什么想說的想聊的大家可以一起交流學(xué)習(xí)一起進(jìn)步呀。
也希望大家對(duì)學(xué)C++能夠持之以恒
C++愛好群,
如果你想要學(xué)好C++最好加入一個(gè)組織,這樣大家學(xué)習(xí)的話就比較方便,還能夠共同交流和分享資料,給你推薦一個(gè)學(xué)習(xí)的組織:快樂學(xué)習(xí)C++組織 可以點(diǎn)擊組織二字,可以直達(dá)

有了上一期的鋪墊,本期就可以實(shí)現(xiàn)諸如馬走日、象走田等各種棋子的走棋算法了。為了方便后期人工智能算法的實(shí)現(xiàn),我們寫一個(gè)總的canMove函數(shù),在這個(gè)總的canMove函數(shù)里調(diào)用各種類型棋子的canMove函數(shù)來判斷各種棋子選擇的路徑能不能走得通。
總的canMove函數(shù)的源代碼:
bool?Board::canMove(int?moveid,?int?killid,?int?row,?int?col)
{
if(killid==-1||!sameColor(moveid,killid))
{
switch(_s[moveid]._type)
{
case?Stone::JIANG:
return?canMoveJIANG(moveid,row,col,killid);
break;
case?Stone::SHI:
return?canMoveSHI(moveid,row,col,killid);
break;
case?Stone::XIANG:
return?canMoveXIANG(moveid,row,col,killid);
break;
case?Stone::CHE:
return?canMoveCHE(moveid,row,col,killid);
break;
case?Stone::MA:
return?canMoveMA(moveid,row,col,killid);
break;
case?Stone::PAO:
return?canMovePAO(moveid,row,col,killid);
break;
case?Stone::BING:
return?canMoveBING(moveid,row,col,killid);
break;
default:?break;
}
}
//move的棋子和kill的棋子是相同顏色的
if(sameColor(moveid,killid))
{
/*換選擇*/
_selectid=killid;
update();
return?false;
}
return?true;
}
本期博客主要介紹相對(duì)簡單的士、兵、相、馬的走棋算法。
1.士的走棋算法
士的走棋規(guī)則:只能在米字格(大本營)內(nèi)行走,且一次只能沿著對(duì)角線斜著走一步。
上canMoveSHI函數(shù)的源代碼:
bool?Board::canMoveSHI(int?moveid,int?row,int?col,int?killid)
{
if(_s[moveid]._red)
{
//判斷紅方士的縱向行走是否超出米字格范圍
if(row>2)?return?false;
}
else
{
//判斷黑方士的縱向行走是否超出米字格范圍
if(row<7)?return?false;
}
//判斷紅黑雙方方士的橫向行走是否超出米字格范圍
if(col<3)?return?false;
if(col>5)?return?false;
//判斷是否為沿著對(duì)角線斜著行走
int?dr=_s[moveid]._row-row;
int?dc=_s[moveid]._col-col;
if(abs(dr)==1&&abs(dc)==1)
return?true;
return?false;
}
算法解析:紅方和黑方的米字格范圍不同,紅方米字格的范圍:row:0~2,col:3~5;黑方米字格的范圍:row:7~9,col:3~5。要想讓士沿著對(duì)角線斜著走,就是想讓移動(dòng)前后的橫坐標(biāo)差的絕對(duì)值和縱坐標(biāo)差的絕對(duì)值都為1,橫縱各移動(dòng)一個(gè)單位長度。
士的行走圖示:
2.兵的走棋算法
兵的走棋規(guī)則:無論過沒過河,縱向上都不能走回頭路。沒過河的時(shí)候只能縱向走,過了河才可以橫向走。
上canMoveBING函數(shù)的源代碼:
bool?Board::canMoveBING(int?moveid,int?row,int?col,int?killid)
{
/*兵的走棋規(guī)則*/
int?dr=_s[moveid]._row-row;
int?dc=_s[moveid]._col-col;
//紅棋
if(_s[moveid]._red)
{
//過河前
if(_s[moveid]._row>=3&&_s[moveid]._row<=4)
{
//豎著走
if(dr==-1&&dc==0)
return?true;
//橫著走
else
return?false;
}
//過河后
else
{
if(abs(dr)==1&&abs(dc)==0||(abs(dc)==1&&abs(dr)==0))
{
//豎著走
if(dr==1)
//豎著走走了回頭路就要返回錯(cuò)誤
return?false;
//橫著走
else
return?true;
}
else
return?false;
}
}
//黑棋
else
{
//過河前
if(_s[moveid]._row>=5&&_s[moveid]._row<=6)
{
//豎著走
if(dr==1&&dc==0)
return?true;
//橫著走
else
return?false;
}
//過河后
else
{
//豎著走
if(abs(dr)==1&&abs(dc)==0||(abs(dc)==1&&abs(dr)==0))
{
//豎著走走了回頭路就要返回錯(cuò)誤
if(dr==-1)
return?false;
//橫著走
else
return?true;
}
else
return?false;
}
}
return?true;
}
算法解析:兵的走棋算法的實(shí)現(xiàn)就比士的要難,既要處理每次移動(dòng)的長度又要區(qū)分過河前和過河后的情況。實(shí)現(xiàn)不能走回頭路的方法是計(jì)算行坐標(biāo)差,以下圖所示為例,以上一期博客所述的坐標(biāo)系為準(zhǔn),紅方在上,黑方在下,則紅兵不能走回頭路的控制條件就是行坐標(biāo)差(當(dāng)前位置的行坐標(biāo)-目標(biāo)位置的行坐標(biāo))必須為-1,黑兵不能走回頭路的控制條件就是行坐標(biāo)差(當(dāng)前位置的行坐標(biāo)-目標(biāo)位置的行坐標(biāo))必須為1。實(shí)現(xiàn)過河與否的判斷則是通過簡單的行坐標(biāo)范圍來界定。實(shí)現(xiàn)步長為1的控制則更為簡單,即行坐標(biāo)差的絕對(duì)值為1且列坐標(biāo)差的絕對(duì)值為0(或行坐標(biāo)差的絕對(duì)值為0且列坐標(biāo)差的絕對(duì)值為1)。
3.相的走棋算法
相的走棋規(guī)則:走田字格。若所跨田字格的中心位置有棋子存在,此時(shí)為“別象眼”,相便不能完成行走。相不能過河。
上canMoveXIANG函數(shù)的源代碼:
bool?Board::canMoveXIANG(int?moveid,int?row,int?col,int?killid)
{
//不能過河
if(_s[moveid]._red)
{
if(row>4)?return?false;
}
else
{
if(row<5)?return?false;
}
//走田字格
int?dr=_s[moveid]._row-row;
int?dc=_s[moveid]._col-col;
int?medium_r=(_s[moveid]._row+row)/2;
int?medium_c=(_s[moveid]._col+col)/2;
if(abs(dr)==2&&abs(dc)==2)
//別象眼檢驗(yàn)
if(!beStone(medium_r,medium_c))
return?true;
return?false;
}
算法解析:實(shí)現(xiàn)走田字格的控制條件即是行坐標(biāo)差的絕對(duì)值為2且列坐標(biāo)差的絕對(duì)值為2(橫縱方向上的移動(dòng)步長都為2)。實(shí)現(xiàn)過河與否的判斷則是通過簡單的行坐標(biāo)范圍來界定。別象眼檢驗(yàn)是相的走棋算法的核心部分,此處運(yùn)用了一點(diǎn)初中數(shù)學(xué)知識(shí),即中點(diǎn)坐標(biāo)的求法,田字格對(duì)角線上的中點(diǎn)的坐標(biāo)為((相的當(dāng)前行坐標(biāo)+目標(biāo)位置行坐標(biāo))/2,(相的當(dāng)前位置列坐標(biāo)+目標(biāo)位置列坐標(biāo))/2)。確定了象眼的行列坐標(biāo)再調(diào)用上期博客介紹的beStone函數(shù)即可得知象眼處是否有棋子,若有棋子相則不能完成行走。
相的行走圖示:
4.馬的走棋算法
馬的走棋規(guī)則:走日字格(橫向移動(dòng)1格且豎向移動(dòng)2格or橫向移動(dòng)2格且豎向移動(dòng)1格)。移動(dòng)2格的那個(gè)方向上距離馬當(dāng)前位置最近的位置上若有棋子存在,則此時(shí)為“別馬腿”,馬便不能完成行走。
上canMoveMA函數(shù)的源代碼:
bool?Board::canMoveMA(int?moveid,int?row,int?col,int?killid)
{
int?dr=_s[moveid]._row-row;
int?dc=_s[moveid]._col-col;
int?medium_r=(_s[moveid]._row+row)/2;
int?medium_c=(_s[moveid]._col+col)/2;
if(abs(dr)==1&&abs(dc)==2||(abs(dr)==2&&abs(dc)==1))
{
if(abs(dr)==2)
{
//別馬腿檢驗(yàn)
if(beStone(medium_r,_s[moveid]._col)==false)
return?true;
}
else
{
//別馬腿檢驗(yàn)
if(beStone(_s[moveid]._row,medium_c)==false)
return?true;
}
}
return?false;
}
算法解析:實(shí)現(xiàn)走日字格的方法是控制行坐標(biāo)差的絕對(duì)值為1且列坐標(biāo)差的絕對(duì)值為2(或行坐標(biāo)差的絕對(duì)值為2且列坐標(biāo)差的絕對(duì)值為1)。要想實(shí)現(xiàn)別馬腿的檢驗(yàn)就要先確定馬的“絆腳石”的行列坐標(biāo),若馬的移動(dòng)步長為2的方向?yàn)闄M向,則該“絆腳石”的坐標(biāo)為(馬的當(dāng)前位置行坐標(biāo),(馬的當(dāng)前位置列坐標(biāo)+目標(biāo)位置列坐標(biāo))/2);若馬的移動(dòng)步長為2的方向?yàn)榭v向,則該“絆腳石”的坐標(biāo)為((馬的當(dāng)前位置行坐標(biāo)+目標(biāo)位置行坐標(biāo))/2,馬的當(dāng)前位置列坐標(biāo))。
馬的行走圖示:
看到這里可能有人會(huì)說,我不會(huì)使用Qt開發(fā)項(xiàng)目,不用擔(dān)心,整個(gè)項(xiàng)目的算法都可以抽象到控制臺(tái)應(yīng)用程序(黑框框)中,大家可以先動(dòng)腦筋實(shí)現(xiàn)一下。如果后期還有人提問在黑框框中如何實(shí)現(xiàn),我會(huì)視具體情況在后面的博客中加以介紹。
這個(gè)項(xiàng)目我會(huì)連載,后期各種算法的實(shí)現(xiàn)敬請(qǐng)期待!
- The End -