日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

博弈算法实现简单五子棋

發布時間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 博弈算法实现简单五子棋 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、? 問題介紹

實現交互式五子棋

采用博弈算法

二、? 程序設計與算法分析

l?? 博弈問題簡介:

– 雙人對弈,輪流走步。

– 信息完備,雙方所得到的信息是一樣的。

– 零和,即對一方有利的棋,對另一方肯定是不利的,不存在對雙方均有利或無利的棋。

l?? 博弈的特性:

– 兩個棋手交替地走棋 ;

– 比賽的最終結果,是贏、輸和平局中的一種;

– 可用圖搜索技術進行,但效率很低;?

– 博弈的過程,是尋找置對手于必敗態的過程;

– 雙方都無法干預對方的選擇。

l?? 結論:五子棋問題屬于博弈問題。

l?? 博弈問題的兩種常用算法思路:

– 與或圖直接搜索法:搜索枚舉雙方棋手的下棋方法,構建與或圖直接搜索出答案 ;

u? 與或圖簡介:

與或圖是一個超圖,節點間通過連接符連接。

u? 與或圖節點分類:

n??能解節點:

???終節點是能解節點 ?

???若非終節點有“或”子節點時,當且僅當其子節點至少有一能解時,該非終結點才能解。

???若非終節點有“與”子節點時,當且僅當其子節點均能解時, 該非終節點才能解。

n??不能解節點

???沒有后裔的非終節點是不能解節點。?

???若非終節點有“或”子節點,當且僅當所有子節點均不能解時, ?該非終節點才不能解。 ?

???若非終節點有“與”子節點時,當至少有一個子節點不能解時, 該非終節點才不能解。

u? 與或圖搜索算法:

n??1.建立搜索圖G:=s,計算q(s)=h(s), If Goal(s)Then M(s, Solved) ?

n??2.Until S 被標記為Solved, Do: ?

n??3.Begin ;擴展 ?

n??4.G’:= Find(g) ; G’為根據連接符找到的待擴展局部解圖 ?

n??5.n := G’中任一非終結點 ?

n??6.{nj}:=Expand(n), 計算q(nj)=h(nj),If Goal(nj) Then M(nj,solved) ?

n??7.S:={n} ;回溯,修改指針和耗散值 ?

n??8.Until S為空, Do: ?

n??9.Begin ?

n??10.Remove(m, S) If m的子結點不在s中 ;子結點耗散值已確定 3/21/2014 第二章 與或圖搜索

n??11.修改m的耗散值:?對m的每個連接符i{n1i,n2i,...,nki},計算qi(m)=Ci+q(n1i)+...+q(nki)q(m):=min qi (m)?修改m的指針到min qi (m)對應的連接符上?If(nji,Solved) THEN M(m,Solved) ;某一個連接符已解決

n??12.If M(m,Solved) or q(m)被修改 Then Add(ma,S), ma為m的所有先輩節點

n??13.End

n??14.End

u? 兩個過程:

n??圖生成過程,即擴展節點,從最優的局部途中選擇一個節點擴展

n??計算耗散值的過程?– 對當前的局部圖從新計算耗散值

u? 算法實現:

n??終結點為某一方勝利的狀態:可用true或者false分別標記勝負

n??利用與或圖搜索更新到根節點

n??若根節點為false表示當前棋局狀態為必敗情況。

n??若根節點為true表示當前棋局為必勝狀態。

n??根節點總是盡量選擇狀態為true的子節點進行狀態轉移。

u? 算法局限性:

n??搜索的沒一層都要窮舉所有可能的情況,很容易引起組合爆炸的問題。

?

– 極大極小搜索算法:利用優先深度的搜索,通過對局勢進行評估來選擇狀態轉移的方向。

u? 極大極小搜索簡介:

n??下棋的雙方是對立的;

n??一方為“正方”,這類節點稱為“MAX”節點; ?

n??另一方為“反方”,這類節點稱為“MIN”節點; ?

n??正方從所有子節點中,選取具有最大評估值的節點進行狀態轉移; ?

n??反方從其所有子節點中,選取具有最小評估值的節點驚醒狀態轉移; ?

n??反復進行這種選取,就可以得到雙方各個節點的評估值。這種確定棋步的方法,稱為極大極小搜索法。 ?

u? 對各個局面進行評估:

n??評估的目的:對后面的狀態提前進行考慮,并且以各種狀態的評估值為基礎作出最好的走棋選擇。?????? ?

n??評估的方法:用評價函數對棋局進行評估。贏的評估值設為+∞,輸的評估值設為-∞,平局的評估值設為0。 ?

n??評估的標準:由于下棋的雙方是對立的,只能選擇其中一 方為評估的標準方。 ??? 所有評估站在正方的立場!

u? 極大極小搜索算法:

n??1.T:=(s,max), Open:=(s),Close:=();?

n??2.Lopp1: ?????????????? ;擴展深度至d

n??3.If Open=() Then Goto Loop2?

n??4.N:=First(Open),Remove(n,Open), Add(n,Close)?

n??5.If F(n)=-∞, +∞, 0, Then GotoLoop1 ;?????????????????????????????? 可以被判定

Else {ni}:=Expand(n), Add({ni},T)?

If d(ni)<kThen Add(ni,Open), Goto Loop1

Else 計算f(ni), Goto Loop1

n??6.Loop2: ???????????????????????????????? ;賦值

n??7.If Close=() Then Goto Loop3

Else np:=First(Close)

n??8.If (np Is Max) And (F(npi)有值) ???????????????????? ; npi是np的子結點,且都有值

Then f(np):=maxf(npi), Remove(np,Close)

If (np Is MIN)And (f(npi)有值) ????????????????????????? ; npi是np的子結點,且都有值

THEN f(np):=minf(npi), Remove(np,Close)?

n??9.Goto Loop2

n??10.Loop3:?

n??10.If f(s)有值 Then Exit(End or M(Move, T))?????????????????? ;s被賦值,結束該步

u? alpha-beta剪枝:在極小極大法中,必須求出所有終端節點的評估值,當預先考慮的棋步比較多時,計算量會大大增加。為了提高搜索的效率,引入了通過對評估值的上下限進行估計、從而減少需進行評估的節點范圍。

n??MAX節點的評估下限值:

作為正方出現的MAX節點,假設它的MIN子節點有N個,那么當它的第一

個MIN子節點的評估值為alpha時,則對于其它的子節點,如果有高過alpha的, 就取那最高的值作為該MAX節點的評估值;如果沒有,則該MAX節點的評估值為alpha。總之,該MAX節點的評估值不會低于alpha,這個alpha就稱為該MAX節 點的評估下限值。

n??MIN節點的評估上限值:

作為反方出現的MIN節點,假設它的MAX子節點有N個,那么當它的第一 個MAX子節點的評估值為beta時,則對于其它子節點,如果有低于beta的,就取那個低于beta的值作為該MIN節點的評估值;如果沒有,則該MIN節點的評估值取beta總之,該MIN節點的評估值不會高過beta這個beta就稱為該MIN節點的評估上限值。

n??剪枝條件:

???后輩節點的beta值≤祖先節點的alpha值時,alpha剪枝。

???– 后輩節點的alpha值≥祖先節點的beta值時,beta剪枝。

?

l?? 五子棋算法分析:

u? 因為每一步可走棋的位置很多,所以不適合直接使用與或圖搜索,在這里使用了極大極小搜索,并用alpha-beta剪枝進行了優化。

u? 為方便實現剪枝,而不是全部拓展所有節點,使用了dfs的搜索方式。

u? 維護一個當前棋局的數組和一個當前節點下一層可能走的點的集合。因為涉及到回溯,需要一種高效的插入和刪除的數據結構,我這里采用了HashSet來作為集合的數據結構。

u? 將當前所有下過棋子的點的相鄰點放入到下一層待搜索的集合中,而不是搜索整個棋盤,這樣縮小了搜索的范圍,減少了擴展的節點數。

u? 評估函數使用當前局面連在一線上棋子的個數進行打分,連在一起的個數越多得分越高。

u? 利用java的drawingPanel庫實現了圖形界面。附drawingPanel下載地址:http://www.buildingjavaprograms.com/DrawingPanel.java

?


import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*;public class Ai3{private static DrawingPanel panel=new DrawingPanel(700,700);private static Graphics g=panel.getGraphics();public static boolean isBlack=false;//標志棋子的顏色public static int[][] chessBoard=new int[17][17]; //棋盤棋子的擺放情況:0無子,1黑子,-1白子private static HashSet<Point> toJudge=new HashSet<Point>(); // ai可能會下棋的點private static int dr[]=new int[]{-1,1,-1,1,0,0,-1,1}; // 方向向量private static int dc[]=new int[]{1,-1,-1,1,-1,1,0,0}; //方向向量public static final int MAXN=1<<28;public static final int MINN=-MAXN; private static int searchDeep=4; //搜索深度private static final int size=15; //棋盤大小public static boolean isFinished=false;public static void main(String[] args){MyMouseEvent myMouseEvent=new MyMouseEvent();panel.addMouseListener(myMouseEvent);initChessBoard();}// 初始化函數,匯圖public static void initChessBoard(){isBlack=false;toJudge.clear();panel.clear();panel.setBackground(Color.GRAY);g.setColor(Color.BLACK);for(int i=45;i<=675;i+=45){g.drawLine(45,i,675,i);g.drawLine(i,45,i,675);}// 棋盤上的五個定位基本點,圖中的小圓圈g.setColor(Color.BLACK);g.fillOval(353,353,14,14);g.fillOval(218,218,14,14);g.fillOval(488,218,14,14);g.fillOval(488,488,14,14);g.fillOval(218,488,14,14);// 初始化棋盤for(int i=1;i<=15;++i)for(int j=1;j<=15;++j)chessBoard[i][j]=0;// ai先手g.fillOval(337,337,45,45);chessBoard[8][8]=1;for(int i=0;i<8;++i)if(1<=8+dc[i] && 8+dc[i]<=size && 1<=8+dr[i] && 8+dr[i]<=size){Point now=new Point(8+dc[i],8+dr[i]);if(!toJudge.contains(now))toJudge.add(now);}isBlack=false;}// 通過點擊事件,得到棋子位置進行下棋public static void putChess(int x,int y){if(isBlack)g.setColor(Color.BLACK);else g.setColor(Color.WHITE);g.fillOval(x-22,y-22,45,45);chessBoard[y/45][x/45]=isBlack?1:-1;if(isEnd(x/45,y/45)){String s=Ai3.isBlack?"黑子勝":"白子勝";JOptionPane.showMessageDialog(null,s);isBlack=true;initChessBoard();}else{Point p=new Point(x/45,y/45);if(toJudge.contains(p))toJudge.remove(p);for(int i=0;i<8;++i){Point now=new Point(p.x+dc[i],p.y+dr[i]);if(1<=now.x && now.x<=size && 1<=now.y && now.y<=size && chessBoard[now.y][now.x]==0)toJudge.add(now);}// Iterator it=toJudge.iterator();// while(it.hasNext()){// Point now=(Point)it.next();// System.out.printf("%d\t%d\n",now.x,now.y);// }// System.out.printf("*******************************************************\n");}}// ai博弈入口函數public static void myAI(){Node node=new Node();dfs(0,node,MINN,MAXN,null);Point now=node.bestChild.p;// toJudge.remove(now);putChess(now.x*45,now.y*45);isBlack=false;}// alpha beta dfsprivate static void dfs(int deep,Node root,int alpha,int beta,Point p){if(deep==searchDeep){root.mark=getMark();// System.out.printf("%d\t%d\t%d\n",p.x,p.y,root.mark);return;}ArrayList<Point> judgeSet=new ArrayList<Point>();Iterator it=toJudge.iterator();while(it.hasNext()){Point now=new Point((Point)it.next());judgeSet.add(now);}it=judgeSet.iterator();while(it.hasNext()){Point now=new Point((Point)it.next());Node node=new Node();node.setPoint(now);root.addChild(node);boolean flag=toJudge.contains(now);chessBoard[now.y][now.x]=((deep&1)==1)?-1:1;if(isEnd(now.x,now.y)){root.bestChild=node;root.mark=MAXN*chessBoard[now.y][now.x];chessBoard[now.y][now.x]=0;return;}boolean flags[]=new boolean[8]; //標記回溯時要不要刪掉Arrays.fill(flags,true);for(int i=0;i<8;++i){Point next=new Point(now.x+dc[i],now.y+dr[i]);if(1<=now.x+dc[i] && now.x+dc[i]<=size && 1<=now.y+dr[i] && now.y+dr[i]<=size && chessBoard[next.y][next.x]==0){if(!toJudge.contains(next)){toJudge.add(next);}else flags[i]=false;}}if(flag) toJudge.remove(now);dfs(deep+1,root.getLastChild(),alpha,beta,now);chessBoard[now.y][now.x]=0;if(flag)toJudge.add(now);for(int i=0;i<8;++i)if(flags[i])toJudge.remove(new Point(now.x+dc[i],now.y+dr[i]));// alpha beta剪枝// min層if((deep&1)==1){if(root.bestChild==null || root.getLastChild().mark<root.bestChild.mark){root.bestChild=root.getLastChild();root.mark=root.bestChild.mark;if(root.mark<=MINN)root.mark+=deep;beta=Math.min(root.mark,beta);}if(root.mark<=alpha)return;}// max層else{if(root.bestChild==null || root.getLastChild().mark>root.bestChild.mark){root.bestChild=root.getLastChild();root.mark=root.bestChild.mark;if(root.mark==MAXN)root.mark-=deep;alpha=Math.max(root.mark,alpha);}if(root.mark>=beta)return;}}// if(deep==0) System.out.printf("******************************************\n");}public static int getMark(){int res=0;for(int i=1;i<=size;++i){for(int j=1;j<=size;++j){if(chessBoard[i][j]!=0){// 行boolean flag1=false,flag2=false;int x=j,y=i;int cnt=1;int col=x,row=y;while(--col>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(col>0 && chessBoard[row][col]==0) flag1=true;col=x;row=y;while(++col<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(col<=size && chessBoard[row][col]==0) flag2=true;if(flag1 && flag2)res+=chessBoard[i][j]*cnt*cnt;else if(flag1 || flag2) res+=chessBoard[i][j]*cnt*cnt/4; if(cnt>=5) res=MAXN*chessBoard[i][j];// 列col=x;row=y;cnt=1;flag1=false;flag2=false;while(--row>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(row>0 && chessBoard[row][col]==0) flag1=true;col=x;row=y;while(++row<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(row<=size && chessBoard[row][col]==0) flag2=true;if(flag1 && flag2)res+=chessBoard[i][j]*cnt*cnt;else if(flag1 || flag2)res+=chessBoard[i][j]*cnt*cnt/4;if(cnt>=5) res=MAXN*chessBoard[i][j];// 左對角線col=x;row=y;cnt=1;flag1=false;flag2=false;while(--col>0 && --row>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(col>0 && row>0 && chessBoard[row][col]==0) flag1=true;col=x;row=y;while(++col<=size && ++row<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(col<=size && row<=size && chessBoard[row][col]==0) flag2=true;if(flag1 && flag2) res+=chessBoard[i][j]*cnt*cnt;else if(flag1 || flag2) res+=chessBoard[i][j]*cnt*cnt/4;if(cnt>=5) res=MAXN*chessBoard[i][j];// 右對角線col=x;row=y;cnt=1;flag1=false;flag2=false;while(++row<=size && --col>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(row<=size && col>0 && chessBoard[row][col]==0) flag1=true;col=x;row=y;while(--row>0 && ++col<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(row>0 && col<=size && chessBoard[i][j]==0) flag2=true;if(flag1 && flag2)res+=chessBoard[i][j]*cnt*cnt;else if(flag1 || flag2) res+=chessBoard[i][j]*cnt*cnt/4;if(cnt>=5) res=MAXN*chessBoard[i][j];}}}return res;}// for debugpublic static void debug(){for(int i=1;i<=size;++i){for(int j=1;j<=size;++j){System.out.printf("%d\t",chessBoard[i][j]);}System.out.println("");}}// 判斷是否一方取勝public static boolean isEnd(int x,int y){// 判斷一行是否五子連珠int cnt=1;int col=x,row=y;while(--col>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;col=x;row=y;while(++col<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(cnt>=5){isFinished=true;return true;}// 判斷一列是否五子連珠col=x;row=y;cnt=1;while(--row>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;col=x;row=y;while(++row<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(cnt>=5){isFinished=true;return true;}// 判斷左對角線是否五子連珠col=x;row=y;cnt=1;while(--col>0 && --row>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;col=x;row=y;while(++col<=size && ++row<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(cnt>=5){isFinished=true;return true;}// 判斷右對角線是否五子連珠col=x;row=y;cnt=1;while(++row<=size && --col>0 && chessBoard[row][col]==chessBoard[y][x]) ++cnt;col=x;row=y;while(--row>0 && ++col<=size && chessBoard[row][col]==chessBoard[y][x]) ++cnt;if(cnt>=5){isFinished=true;return true;}return false;} }// 樹節點 class Node{public Node bestChild=null;public ArrayList<Node> child=new ArrayList<Node>();public Point p=new Point();public int mark;Node(){this.child.clear();bestChild=null;mark=0;}public void setPoint(Point r){p.x=r.x;p.y=r.y;}public void addChild(Node r){this.child.add(r);}public Node getLastChild(){return child.get(child.size()-1);} }// 實現鼠標事件接口 class MyMouseEvent implements MouseListener{public void mouseClicked(MouseEvent e){int x=round(e.getX()),y=round(e.getY());if(x>=45 && x<=675 && y>=45 && y<=675 && Ai3.chessBoard[y/45][x/45]==0 && Ai3.isBlack==false){Ai3.putChess(x,y);if(!Ai3.isFinished){Ai3.isBlack=true;Ai3.myAI();}Ai3.isFinished=false;}}// 得到鼠標點擊點附近的棋盤精準點public static int round(int x){return (x%45<22)?x/45*45:x/45*45+45;}public void mouseExited(MouseEvent e){}public void mouseEntered(MouseEvent e){}public void mouseReleased(MouseEvent e){}public void mousePressed(MouseEvent e){} }

總結

以上是生活随笔為你收集整理的博弈算法实现简单五子棋的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。