象棋人工智能算法的C++實(shí)現(xiàn)(二)

前言:在看完上一期《象棋人工智能算法的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 -

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容