五子棋AI图形界面人机对战(JAVA实现)

Posted shiyicode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五子棋AI图形界面人机对战(JAVA实现)相关的知识,希望对你有一定的参考价值。

前言

改了又改,查了又查,想了又想,我真的不知道怎样让它再聪明了,大多时候走的都是正确的,但偶尔会蹦出那么一步臭棋,全盘皆输。希望有相关经验的道友看到后可以指出原因和不足。

效果图

按钮什么的还未完成,只是能实现正常的下棋了。

完成过程

UI部分

本来准备找张棋盘图片做背景,想了下我们还有人机界面课呢,权当复习一下java GUI了,事实上过程比我想象中简单许多。现在界面部分输出游戏结果和按钮什么的还没来得及弄,只是凑合可以玩了,闲了再加上,先贴出来,找点动力再说。

棋盘部分 -- 实现落子,撤子,判断是否成五子(游戏结束)。对局面的评估函数(这个很重要,下面专门会解释)
AI部分(这个更重要了) , 主要功能就是得出AI的下一步走法。

alpha_beta剪枝搜索

未了解过此算法的可先去看
最大最小搜索
Alpha-Beta剪枝
上面两文解释的很清楚了,在此就不再赘述。

估值函数

判断是否能成5, 如果是机器方的话给予100000分,如果是人方的话给予-100000 分;
判断是否能成活4或者是双死4或者是死4活3,如果是机器方的话给予10000分,如果是人方的话给予-10000分;
判断是否已成双活3,如果是机器方的话给予5000分,如果是人方的话给予-5000 分;
判断是否成死3活3,如果是机器方的话给予1000分,如果是人方的话给予-1000 分;
判断是否能成死4,如果是机器方的话给予500分,如果是人方的话给予-500分;
判断是否能成单活3,如果是机器方的话给予200分,如果是人方的话给予-200分;
判断是否已成双活2,如果是机器方的话给予100分,如果是人方的话给予-100分;
判断是否能成死3,如果是机器方的话给予50分,如果是人方的话给予-50分;
判断是否能成双活2,如果是机器方的话给予10分,如果是人方的话给予-10分;
判断是否能成活2,如果是机器方的话给予5分,如果是人方的话给予-5分;
判断是否能成死2,如果是机器方的话给予3分,如果是人方的话给予-3分。

存在的问题以及未完成的地方

最主要的问题就是偶尔会走臭棋。反复调试找原因,甚至把搜索方法又重新写了一遍,还是无果。觉得问题出现在估值函数上,但又想不出如何解决。
未完成的地方当然是提高搜索的效率了,每次估值都是要全盘进行估值,这样造成的时间影响是很大的,还可以用位棋盘来保存局面的情况,每走一步进行更新,每次回溯时再恢复,这样估值时之用考虑最近的一步,而不是全局了。这个等日后有时间再好好改进一下吧。
对于输赢的判断已经有了,但是还少了UI部分的显示,只是仅仅输出提示在命令行上。

代码

AI走法搜索部分 --

import java.util.Random;

/**
 * Created by shiyi on 16/3/14.
 */
public class Robot 

    private static ChessBoard chess = ChessBoard.getInstance();
    private Robot() 
    private static int depth = 1;
    private static int robotColor = chess.BLACK;
    private static Robot robot = new Robot();

    public static Robot getRobot() 
        return robot;
    

    /* alpha_beta剪枝搜索 */
    public int alpha_betaFind(int depth, int alpha, int beta, int color, int prex, int prey) 

        if(depth >= Robot.depth || 0 != chess.isEnd(prex, prey, color%2+1)) 

            int ans = chess.reckon(robotColor) - chess.reckon(robotColor%2 + 1);

            if(depth % 2 == 0)
                ans = -ans;

            return ans;
        

        for(int x=1; x<=chess.N; x++) 
            for(int y=1; y<=chess.N; y++) 

                if(!chess.isEmpty(x, y))
                    continue;

                chess.makeMove(x, y, color);
                int val = -alpha_betaFind(depth+1, -beta, -alpha, color%2+1, x, y);

                chess.unMove(x, y);

                if(val >= beta)
                    return beta;

                if(val > alpha)
                    alpha = val;
            
        
        return alpha;
    

    /* 返回AI走法 */
    public int[] getNext(int color) 
        int rel[] = new int[2];
        int ans = -100000000;

        Random random = new Random();

        for(int x=1; x<=chess.N; x++) 
            for(int y=1; y<=chess.N; y++) 

                if(!chess.isEmpty(x, y))
                    continue;

                chess.makeMove(x, y, color);

                int val = -alpha_betaFind(0, -100000000, 100000000, color%2 + 1, x, y);

                int ra = random.nextInt(100);
                if(val > ans || val == ans && ra >= 50) 
                    ans = val;
                    rel[0] = x;
                    rel[1] = y;
                
                chess.unMove(x, y);
            
        
        return rel;
    

落子撤子以及估值 --ChessBoard

/**
 * Created by shiyi on 16/3/14.
 */
public class ChessBoard 
    public final int N = 15;
    public final int EMPTY = 0;
    public final int BLACK = 1;
    public final int WHITE = 2;

    public int[][] board = new int[N+1][N+1];

    private ChessBoard() 
    private static final ChessBoard chess = new ChessBoard();

    /* 返回类单例 */
    public static ChessBoard getInstance() 
        return chess;
    

    /*  判断该位置是否无子 */
    public boolean isEmpty(int x, int y) 
        return board[x][y] == EMPTY;
    

    /* 落子 */
    public void makeMove(int x, int y, int color) 
        board[x][y] = color;
    

    /* 撤子 */
    public void unMove(int x, int y) 
        board[x][y] = EMPTY;
    

    public int reckon(int color) 

        int dx[] = 1, 0, 1, 1;
        int dy[] = 0, 1, 1, -1;
        int ans = 0;

        for(int x=1; x<=N; x++) 
            for (int y = 1; y <= N; y++) 
                if (board[x][y] != color)
                    continue;

                int num[][] = new int[2][100];

                for (int i = 0; i < 4; i++) 
                    int sum = 1;
                    int flag1 = 0, flag2 = 0;

                    int tx = x + dx[i];
                    int ty = y + dy[i];
                    while (tx > 0 && tx <= N
                            && ty > 0 && ty <= N
                            && board[tx][ty] == color) 
                        tx += dx[i];
                        ty += dy[i];
                        ++sum;
                    

                    if(tx > 0 && tx <= N
                            && ty > 0 && ty <= N
                            && board[tx][ty] == EMPTY)
                        flag1 = 1;

                    tx = x - dx[i];
                    ty = y - dy[i];
                    while (tx > 0 && tx <= N
                            && ty > 0 && ty <= N
                            && board[tx][ty] == color) 
                        tx -= dx[i];
                        ty -= dy[i];
                        ++sum;
                    

                    if(tx > 0 && tx <= N
                            && ty > 0 && ty <= N
                            && board[tx][ty] == EMPTY)
                        flag2 = 1;

                    if(flag1 + flag2 > 0)
                        ++num[flag1 + flag2 - 1][sum];
                

                //成5
                if(num[0][5] + num[1][5] > 0)
                    ans = Math.max(ans, 100000);
                    //活4 | 双死四 | 死4活3
                else if(num[1][4] > 0
                        || num[0][4] > 1
                        || (num[0][4] > 0 && num[1][3] > 0))
                    ans = Math.max(ans, 10000);
                    //双活3
                else if(num[1][3] > 1)
                    ans = Math.max(ans, 5000);
                    //死3活3
                else if(num[1][3] > 0 && num[0][3] > 0)
                    ans = Math.max(ans, 1000);
                    //死4
                else if(num[0][4] > 0)
                    ans = Math.max(ans, 500);
                    //单活3
                else if(num[1][3] > 0)
                    ans = Math.max(ans, 200);
                    //双活2
                else if(num[1][2] > 1)
                    ans = Math.max(ans, 100);
                    //死3
                else if(num[0][3] > 0)
                    ans = Math.max(ans, 50);
                    //双活2
                else if(num[1][2] > 1)
                    ans = Math.max(ans, 10);
                    //单活2
                else if(num[1][2] > 0)
                    ans = Math.max(ans, 5);
                    //死2
                else if(num[0][2] > 0)
                    ans = Math.max(ans, 1);

            
        

        return ans;
    

    /* 判断局面是否结束 0未结束 1WHITE赢 2BLACK赢 */
    public int isEnd(int x, int y, int color) 
        int dx[] = 1, 0, 1, 1;
        int dy[] = 0, 1, 1, -1;

        for (int i = 0; i < 4; i++) 
            int sum = 1;

            int tx = x + dx[i];
            int ty = y + dy[i];
            while (tx > 0 && tx <= N
                    && ty > 0 && ty <= N
                    && board[tx][ty] == color) 
                tx += dx[i];
                ty += dy[i];
                ++sum;
            

            tx = x - dx[i];
            ty = y - dy[i];
            while (tx > 0 && tx <= N
                    && ty > 0 && ty <= N
                    && board[tx][ty] == color) 

                tx -= dx[i];
                ty -= dy[i];
                ++sum;
            


            if(sum >= 5)
                return color;
        
        return 0;
    

UI部分

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;

/**
 * Created by shiyi on 16/3/15.
 */
public class UI 
    private ChessBoard chess = ChessBoard.getInstance();
    private int userColor = chess.WHITE;
    private int robotColor = chess.BLACK;
    private Frame frame = new Frame("绘制棋盘");
    private MyChess drawArea = new MyChess();
    private Robot robot = Robot.getRobot();

    public void init()

        Panel p = new Panel();
        //机器执先
        chess.makeMove(chess.N/2+1, chess.N/2+1, chess.BLACK);

        drawArea.setPreferredSize(new Dimension(720, 720));

        drawArea.addMouseListener(new MouseListener() 
            @Override
            public void mouseClicked(MouseEvent e) 
                int x = e.getX();
                int y = e.getY();
                int i = (x-60)/40+1;
                int j = (y-60)/40+1;
                System.out.println(i+"---"+j);
                if(chess.isEmpty(i, j)) 

                    chess.makeMove(i, j, userColor);
                    int rel = chess.isEnd(i, j, userColor);
                    if(rel != 0) 
                        System.out.println("玩家胜利");
                        return;
                    
                    drawArea.repaint();
                    int rob[] = robot.getNext(robotColor);
                    chess.makeMove(rob[0], rob[1], robotColor);
                    rel = chess.isEnd(rob[0], rob[1], robotColor);
                    if(rel != 0) 
                        System.out.println("机器胜利");
                        return;
                    
                    drawArea.repaint();
                

            

            @Override
            public void mousePressed(MouseEvent e) 

            

            @Override
            public void mouseReleased(MouseEvent e) 

            

            @Override
            public void mouseEntered(MouseEvent e) 

            

            @Override
            public void mouseExited(MouseEvent e) 

            
        );

        frame.add(drawArea);
        frame.setSize(900, 720);
        frame.setVisible(true);
    



class MyChess extends Canvas 

    private ChessBoard chess = ChessBoard.getInstance();
    private final int N = chess.N + 2;
    private final int square = 40;
    private final int stx = square;
    private final int sty = square;
    private final int length = (N-1)*square;

    public void drawPiece(int color, int x, int y, Graphics g) 
        if (color == chess.BLACK) 
            g.setColor(new Color(0, 0, 0));
            g.fillArc(stx + x * square - 19, sty + y * square - 19, 38, 38, 0, 360);
         else if (color == chess.WHITE) 
            g.setColor(new Color(255, 255, 255));
            g.fillArc(stx + x * square - 19, sty + y * square - 19, 38, 38, 0, 360);
        
    

    public void paint(Graphics g) 

        g.setColor(new Color(0, 0, 0));
        g.fillRect(stx-8, sty-8, stx+(N-2)*square+15, sty+(N-2)*square+15);
        g.setColor(new Color(139, 255, 71));
        g.fillRect(stx-4, sty-4, stx+(N-2)*square+7, sty+(N-2)*square+7);

        g.setColor(new Color(0, 0, 0));

        g.fillArc(stx+8*square-6, sty+8*square-6, 12, 12, 0, 360);
        g.fillArc(stx+4*square-6, sty+4*square-6, 12, 12, 0, 360);
        g.fillArc(stx+4*square-6, sty+12*square-6, 12, 12, 0, 360);
        g.fillArc(stx+12*square-6, sty+4*square-6, 12, 12, 0, 360);
        g.fillArc(stx+12*square-6, sty+12*square-6, 12, 12, 0, 360);

        for(int i = 0; i < N; i++) 

            g.drawLine(stx+i*square, sty, stx+i*square, sty+length);
            g.drawLine(stx, sty+i*square, stx+length, sty+i*square);
        

        for(int i=1; i<=chess.N; i++) 
            for(int j=1; j<=chess.N; j++) 
                drawPiece(chess.board[i][j], i, j, g);
            
        
    

Main

/**
 * Created by shiyi on 16/3/14.
 */
public class Main 
    public static void main(String[] args) 
        System.out.println("游戏开始");

        UI ui = new UI();
        ui.init();
    

以上是关于五子棋AI图形界面人机对战(JAVA实现)的主要内容,如果未能解决你的问题,请参考以下文章

python实现五子棋-人机对战/人人对战(动图演示+源码分享)

五子棋人机对战

五子棋程序设计实现技术文档

五子棋程序设计实现技术文档

五子棋程序设计实现技术文档

五子棋项目的实现人机对战类的具体设计