五子棋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实现)的主要内容,如果未能解决你的问题,请参考以下文章

五子棋人机对战

安卓五子棋-人机对战

AI人机对战五子棋游戏Python(pygame)+AI并实现软件输出

Java Swing局域网对战五子棋(人人和人机对战)

C语言 AI智能,五子棋 人机对战,人人对战

Java五子棋课程设计