您好,登錄后才能下訂單哦!
今天我想要分享一下我做五子棋AI的思路。因?yàn)樵谧鲞@個(gè)之前,我沒有接觸過任何像這種類似的東西。通過這一次,我也算是有所了解,我的思路也是來自很多網(wǎng)絡(luò)上的博客,看了很多,最終總結(jié)出了自己的這樣一個(gè)。
那我的五子棋是15*15的大小(一般也就是這樣的一個(gè)大?。N业腁I算法要求每一次落子之后都要去計(jì)算每一個(gè)空暇的位置的“分值”,簡單的說,我們需要一個(gè)存放棋子的數(shù)組,表示是否存放了棋子,還要一個(gè)計(jì)算每一個(gè)空格的數(shù)組來記錄“分?jǐn)?shù)”,這個(gè)分?jǐn)?shù)是后期AI用來運(yùn)算的基礎(chǔ),也是你AI難度控制的點(diǎn)。
我現(xiàn)有的思路就是分兩部分。首先是如果是玩家先落子,那么要求電腦AI隨即在你落子的地方的任意一個(gè)方向,隨機(jī)落子,這是第一步。接下來以后就正式進(jìn)入到算法中去。
首先初始化你的分?jǐn)?shù)數(shù)組,讓他們?nèi)繛榱?。然后在每一次落子之后進(jìn)行全盤的遍歷,如果發(fā)現(xiàn)該處為空白,于是檢查其四周八個(gè)方向(當(dāng)然如果是邊緣位置就相對(duì)修改,判斷是否出了邊界)。若在空白處,且發(fā)現(xiàn)在某一對(duì)角線方向發(fā)現(xiàn)有一個(gè)其他顏色的棋子,那么相對(duì)的給這個(gè)空白區(qū)域的分?jǐn)?shù)數(shù)組加上一定的分值,然后繼續(xù)往這個(gè)方向檢測(cè)是否還有連續(xù)的同一顏色的棋子,若沒有則檢查其他方向或者檢測(cè)下一個(gè)空白位置。若是還在同一方向上面找到了相同顏色的棋子,那么第二個(gè)棋子的出現(xiàn),你可以給改空白處加上雙倍的分值,表明這個(gè)空白位置更加重要。一次類推,繼續(xù)檢測(cè)。(PS:因?yàn)樽罱KAI棋子落在什么地方,依靠的是最后遍歷整個(gè)分?jǐn)?shù)數(shù)組,然后根據(jù)分?jǐn)?shù)的高低來進(jìn)行判斷落子落在哪里的,在下面講)。
經(jīng)過上一遍的遍歷,每一次落子都會(huì)使得分?jǐn)?shù)數(shù)組得到一些變化,每一次都會(huì)導(dǎo)致AI判斷的變化。在這個(gè)基礎(chǔ)上,每一次落子還要進(jìn)行一次對(duì)自己本身棋子顏色的一個(gè)遍歷,判斷自己的情況,同時(shí)加分加在分?jǐn)?shù)數(shù)組之中,這樣一來,電腦就會(huì)根據(jù)自己的棋子的情況以及玩家的落子情況進(jìn)行判斷,哪一個(gè)地方更加適合落子。
因?yàn)槲沂堑谝淮巫鯝I,網(wǎng)絡(luò)上搜到的一些思想一般也是這種類似的遍歷思想。理解了以后寫代碼就比較方便。最后可能會(huì)有一些點(diǎn)的分?jǐn)?shù)是相同的,所以還有設(shè)置一下隨機(jī)落子。把分?jǐn)?shù)相同的地點(diǎn)隨機(jī)落子。
個(gè)人感覺AI的強(qiáng)弱是根據(jù)你每一次給他增加分?jǐn)?shù)的多少來確定的。這個(gè)我的AI有時(shí)候也會(huì)抽風(fēng),不過一般情況比較正常,可能運(yùn)氣也占了一部分,當(dāng)初設(shè)計(jì)加分的時(shí)候其實(shí)沒想那么多,現(xiàn)在卻發(fā)現(xiàn)好像還不錯(cuò)。
大家要多去實(shí)踐練習(xí),多改改分?jǐn)?shù)可能就會(huì)出來不錯(cuò)的AI了,o(^▽^)o。
下面貼上我的代碼!
void GameScene::Robot(int *x, int *y, int *Sum) { ExWhile1 = true; if (*Sum == 1) { while (ExWhile1) { ChessOne(*x, *y); if (ch[*x][*y] == 2){ ExWhile1 = false; } } ch[*x][*y] = tp; //記錄這個(gè)點(diǎn) printpart(*x, *y, tp); //打印出電腦AI第一次落子 isTouch = true; tp++; tp = tp % 2; } else //從第2步開始,使用評(píng)分系統(tǒng) { Findscore(*x, *y); } } void GameScene::Findscore(int &x, int &y) //查找評(píng)分最高的坐標(biāo) { srand((unsigned)time(NULL)); int i, j, x1, x2, y1, y2, lx; int Max = 0; ChessScore(); //調(diào)用評(píng)分函數(shù) for (i = 0; i<15; i++) { for (j = 0; j<15; j++) { if (Score[i][j]>Max) { Max = Score[i][j]; //獲取所有點(diǎn)中,評(píng)分最高的 x1 = i; y1 = j; } } } x2 = x1; y2 = y1; for (i = 0; i<15; i++) //可能的話,有評(píng)分相同的多個(gè)點(diǎn) { for (j = 0; j<15; j++) { if (Score[i][j] == Max&&i != x2&&j != y2) //在這么多個(gè)相同分?jǐn)?shù)的點(diǎn)中,隨機(jī)找一個(gè) { lx = rand() % 10; if (lx<5) { x2 = i, y2 = j; break; } } } } if (x2 != x1 || y2 != y1) //棋盤上有2個(gè)最高分 { lx = rand() % 10; //隨機(jī)一個(gè) if (lx>6) { x = x1, y = y1; } else { x = x2, y = y2; } } else //棋盤上只有一個(gè)最高分 { x = x1, y = y1; } Max = 0; //清空最大值 ch[x][y] = tp; //記錄這個(gè)點(diǎn) printpart(x, y, tp); //打印出電腦AI落子 if (winerValue==2) { isTouch = true; } tp++; tp = tp % 2; } inline void GameScene::ChessOne(int &x, int &y) //玩家走第1步時(shí)的落子 { int i, j; srand((unsigned)time(NULL)); //隨機(jī)數(shù)隨著時(shí)間的改變而改變 for (i = 0; i<15; i++) { for (j = 0; j<15; j++) { if (ch[i][j] == 0) //如果找到了玩家的棋子,在它的8個(gè)方的任意一點(diǎn)落子 { int lx = rand() % 7; if (lx == 0) { x = i + 1; y = j + 1; if (ch[x][y] == 2){ break; } } else if (lx == 1) { x = i + 1; y = j - 1; if (ch[x][y] == 2){ break; } } else if (lx == 2) { x = i - 1; y = j - 1; if (ch[x][y] == 2){ break; } } else if (lx == 3) { x = i - 1; y = j + 1; if (ch[x][y] == 2){ break; } } else if (lx == 4) { x = i - 1; y = j; //上 if (ch[x][y] == 2){ break; } } else if (lx == 5) { x = i; y = j - 1; //左 if (ch[x][y] == 2){ break; } } else if (lx == 6) { x = i; y = j + 1; //右 if (ch[x][y] == 2){ break; } } else { x = i + 1; y = j; //下 if (ch[x][y] == 2){ break; } } } } } } void GameScene::ChessScore() { int x, y, i, j, k; //循環(huán)變量 int number1 = 0, number2 = 0; //number用來統(tǒng)計(jì)玩家或電腦棋子連成個(gè)數(shù) int empty = 0; //empty用來統(tǒng)計(jì)空點(diǎn)個(gè)數(shù) memset(Score, 0, sizeof(Score)); //把評(píng)分?jǐn)?shù)組先清零 for (x = 0; x<15; x++) { for (y = 0; y<15; y++) { if (ch[x][y] == 2) //如果這個(gè)點(diǎn)為空 { for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) //判斷8個(gè)方向 { if (i != 0 || j != 0) //若是都為0的話,那不就是原坐標(biāo)嘛 { //對(duì)玩家落點(diǎn)評(píng)分 for (k = 1; i <= 4; k++) //循環(huán)4次 { //這點(diǎn)沒越界 且這點(diǎn)存在黑子(玩家) if (x + k*i >= 0 && x + k*i <= 14 && y + k*j >= 0 && y + k*j <= 14 && ch[x + k*i][y + k*j] == 0) { number1++; } else if (ch[x + k*i][y + k*j] == 2) //這點(diǎn)是個(gè)空點(diǎn),+1后退出 { empty++; break; } else //否則是墻或者對(duì)方的棋子了 { break; } } for (k = -1; k >= -4; k--) //向它的相反方向判斷 { //這點(diǎn)沒越界 且這點(diǎn)存在黑子(玩家) if (x + k*i >= 0 && x + k*i <= 14 && y + k*j >= 0 && y + k*j <= 14 && ch[x + k*i][y + k*j] == 0) { number1++; } else if (ch[x + k*i][y + k*j] == 2) //這點(diǎn)是個(gè)空點(diǎn),+1后退出 { empty++; break; } else { break; } } if (number2 == 1) //2個(gè)棋子 { Score[x][y] += 1; } else if (number1 == 2) //3個(gè)棋子 { if (empty == 1) { Score[x][y] += 5; //有一個(gè)空點(diǎn)+5分 死3 } else if (empty == 2) { Score[x][y] += 10; //有兩個(gè)空點(diǎn)+10分 活3 } } else if (number1 == 3) //4個(gè)棋子 { if (empty == 1) { Score[x][y] += 20; //有一個(gè)空點(diǎn)+20分 死4 } else if (empty == 2) { Score[x][y] += 100; //有2個(gè)空點(diǎn)+100分 活4 } } else if (number1 >= 4) { Score[x][y] += 1000; //對(duì)方有5個(gè)棋子,分?jǐn)?shù)要高點(diǎn),先堵 } empty = 0; //統(tǒng)計(jì)空點(diǎn)個(gè)數(shù)的變量清零 //對(duì)電腦落點(diǎn)評(píng)分 for (k = 1; i <= 4; k++) //循環(huán)4次 { //這點(diǎn)沒越界 且這點(diǎn)存在白子(電腦) if (x + k*i >= 0 && x + k*i <= 14 && y + k*j >= 0 && y + k*j <= 14 && ch[x + k*i][y + k*j] == 1) { number2++; } else if (ch[x + k*i][y + k*j] == 2) { empty++; break; //空點(diǎn) } else { break; } } for (k = -1; k >= -4; k--) //向它的相反方向判斷 { if (x + k*i >= 0 && x + k*i <= 14 && y + k*j >= 0 && y + k*j <= 14 && ch[x + k*i][y + k*j] == 1) { number2++; } else if (ch[x + k*i][y + k*j] == 2) { empty++; break; } else { break; //注釋與上面玩家版相同 } } if (number2 == 0) { Score[x][y] += 1; //1個(gè)棋子 } else if (number2 == 1) { Score[x][y] += 2; //2個(gè)棋子 } else if (number2 == 2) //3個(gè)棋子 { if (empty == 1) { Score[x][y] += 8; //死3 } else if (empty == 2) { Score[x][y] += 30; //活3 } } else if (number2 == 3) //4個(gè)棋子 { if (empty == 1) { Score[x][y] += 50; //死4 } else if (empty == 2) { Score[x][y] += 200; //活4 } } else if (number2 >= 4) { Score[x][y] += 10000; //自己落在這點(diǎn)能形成5個(gè),也就能勝利了,分?jǐn)?shù)最高 } number1 = 0; //清零,以便下次重新統(tǒng)計(jì) number2 = 0; empty = 0; } } } } } } }
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。