Android实现五子棋游戏(二) 人机对战实现

本文详细介绍了在Android上实现五子棋人机对战的思路和代码实现,包括AI.java类的设计,优先级评分算法,以及游戏逻辑和布局分析。通过AI优先级评分和用户优先级评分确定最佳落子点,实现棋局的动态变化。同时提供了游戏的源码下载链接。
摘要由CSDN通过智能技术生成

转载请注明出处:http://blog.csdn.net/a512337862/article/details/76166049

上次的博客:http://blog.csdn.net/a512337862/article/details/74165085 简单介绍了五子棋游戏最基本逻辑的实现。本篇博客在上次博客的基础上增加了简单的游戏AI,实现了五子棋的人机对战功能,效果图如下:

这里写图片描述

下面简单介绍一下实现人机对战的思路以及代码实现:

思路

人机对战的总体思路是通过遍历所有的无棋子的位置,通过模拟在每个无棋子点落子,并根据其周围的棋子来获取该点的两个优先级评分:

  • 模拟用户棋子来获取一个优先级评分,用于防守(用户优先级)

  • 模拟AI棋子来获取一个优先级评分,用于进攻(AI优先级)

然后通过用户优先级以及AI优先级来获取该点的最终的优先级评分,最后遍历所有无棋子的优先级评分,找到优先级最高的点落子。

逻辑代码分析

AI.java

/**
 * Created by ZhangHao on 2017/7/20.
 * 五子棋简单的AI
 */

public class AI implements Runnable {
   
    //棋盘信息
    private int[][] chessArray;
    //电脑执子(默认黑子)
    private int aiChess = FiveChessView.BLACK_CHESS;
    //所有无子位置的信息集合
    private List<Point> pointList;
    //ai落子结束回调
    private AICallBack callBack;
    //棋盘宽高(panelLength)
    private int panelLength;
    /**
     * 评分表(落子优先级评分)
     * FIVE 至少能五子相连
     * LIVE_X 表示X个连在一起的子,两边都没有被堵住
     * DEAD_X 表示X个连在一起的子,一边被堵住
     * DEAD 表示两边被堵住
     */
    private final static int FIVE = 10000;
    private final static int LIVE_FOUR = 4500;
    private final static int DEAD_FOUR = 2000;
    private final static int LIVE_THREE = 900;
    private final static int DEAD_THREE = 400;
    private final static int LIVE_TWO = 150;
    private final static int DEAD_TWO = 70;
    private final static int LIVE_ONE = 30;
    private final static int DEAD_ONE = 15;
    private final static int DEAD = 1;

    public AI(int[][] chessArray, AICallBack callBack) {
        pointList = new ArrayList<>();
        this.chessArray = chessArray;
        this.callBack = callBack;
        this.panelLength = chessArray.length;
    }

    //ai开始落子
    public void aiBout() {
        new Thread(this).start();
    }

    //判断落子的优先级评分
    private void checkPriority(Point p) {
        int aiPriority = checkSelf(p.getX(), p.getY());
        int userPriority = checkUser(p.getX(), p.getY());
        p.setPriority(aiPriority >= userPriority ? aiPriority : userPriority);
    }

    //获取当前点,ai优先级评分
    private int checkSelf(int x, int y) {
        return getHorizontalPriority(x, y, aiChess)
                + getVerticalPriority(x, y, aiChess)
                + getLeftSlashPriority(x, y, aiChess)
                + getRightSlashPriority(x, y, aiChess);
    }

    //获取当前点,玩家优先级评分
    private int checkUser(int x, int y) {
        int userChess;
        if (aiChess == FiveChessView.WHITE_CHESS) {
            userChess = FiveChessView.BLACK_CHESS;
        } else {
            userChess = FiveChessView.WHITE_CHESS;
        }
        return getHorizontalPriority(x, y, userChess)
                + getVerticalPriority(x, y, userChess)
                + getLeftSlashPriority(x, y, userChess)
                + getRightSlashPriority(x, y, userChess);
    }

    //通过线程选择最佳落点
    @Override
    public void run() {
        //清空pointList
        pointList.clear();
        int blankCount = 0;
        for (int i = 0; i < panelLength; i++)
            for (int j = 0; j < panelLength; j++) {
                if (chessArray[i][j] == FiveChessView.NO_CHESS) {
                    Point p = new Point(i, j);
                    checkPriority(p);
                    pointList.add(p);
                    blankCount++;
                }
            }
        //遍历pointList,找到优先级最高的Point
        Point max = pointList.get(0);
        for (Point point : pointList) {
            if (max.getPriority() < point.getPriority()) {
                max = point;
            }
        }
        //AI先手或者用户先手第一次落子时
        if (blankCount >= panelLength * panelLength - 1) {
            max = getStartPoint();
        }
        //休眠2秒
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //落子,并将结果回调
        chessArray[max.getX()][max.getY()] = aiChess;
        callBack.aiAtTheBell();
    }

    public void setAiChess(int aiChess) {
        this.aiChess = aiChess;
    }

    //AI先手或者用户先手第一次落子时,随机获取一个点落子
    private Point getStartPoint() {
        //该点是否可用标识
        boolean isUse = true;
        //在中间位置随机生成一个点
        Random random = new Random();
        int x = random.nextInt(5) + 5;
        int y = random.nextInt(5) + 5;
        //确保周围不存在其他棋子
        for (int i = x - 1; i <= x + 1; i++)
            for (int j = y - 1; j <= y + 1; j++) {
                if (chessArray[i][j] != FiveChessView.NO_CHESS) {
                    isUse = false;
                }
            }
        if (isUse) {
            return new Point(x, y);
        } else {
            return getStartPoint();
        }
    }

    /**
     * 判断指定点chessArray[x][y]横向优先级
     *
     * @param x     数组下标
     * @param y     数组下标
     * @param chess 棋子颜色
     * @return 该点优先级评分
     */
    private int getHorizontalPriority(int x, int y, int chess) {
        //指定棋子相连数
        int connectCount = 1;
        //左边是否被堵住
        boolean isStartStem = false;
        //右边是否被堵住
        boolean isEndStem = false;

        //先向左边计算
        //如果当前位置y = 0,即在棋盘的边缘位置,则左边必然被堵住
        if (y == 0) {
            isStartStem = true;
        } else {
            //遍历左边
            for (int i = y - 1; i >= 0; i--) {
                //如果不是指定棋子
                if (chessArray[x][i] != chess) {
                    //不是自己的棋子,则左边被堵住或者是空位
                    isStartStem = chessArray[x][i] != FiveChessView.NO_CHESS;
                    break;
                } else {
                    connectCount++;
                    if (i == 0) {
                        //在边缘位置,则被挡住
                        isStartStem = true;
                    }
                }
            }
        }

        //再向右边计算
        //如果当前位置y = panelLength,即在棋盘的边缘位置,则右边必然被堵住
        if (y == panelLength - 1) {
            isEndStem = true;
        } else {
            //遍历右边
            for (int i = y + 1; i < panelLength; i++) {
                //如果不是指定棋子
                if (chessArray[x][i] != chess) {
                    //不是自己的棋子,则左边被堵住或者是空位
                    isEndStem = chessArray[x][i] != FiveChessView.NO_CHESS;
                    break;
                } else {
                    connectCount++;
                    if (i == panelLength - 1) {
                        //在边缘位置,则被挡住
                        isEndStem = true;
                    }
                }
            }
        }
        //计算优先级评分
        return calcPriority(connectCount, isStartStem, isEndStem);
    }

    /**
     * 判断指定点chessArray[x][y]纵向优先级
     *
     * @param x     数组下标
     * @param y     数组下标
     * @param chess 棋子颜色
     * @return 该点优先级评分
     */
    private int getVerticalPriority(int x, int y, int chess) {
        //指定棋子相连数
        int connectCount = 1;
        //左边是否被堵住
        boolean isStartStem = false;
        //右边是否被堵住
        boolean isEndStem = false;

        //先向上边计算
        //在棋盘的边缘位置,则上边必然被堵住
        if (x == 0) {
            isStartStem = true;
        } else {
            //向上遍历
            for (int i = x - 1; i >= 0; i--) {
                //如果不是指定棋子
                if (chessArray[i][y] != chess) {
                    //不是自己的棋子,则左边被堵住或者是空位
                    isStartStem = chessArray[i][y] != FiveChessView.NO_CHESS;
                    break;
                } else {
                    connectCount++;
                    if (i == 0) {
                        //在边缘位置,则被挡住
                        isStartStem = true;
                    }
                }
            }
        }

        //再向右边计算
        //如果当前位置y = panelLength,即在棋盘的边缘位置,则下边必然被堵住
        if (x == panelLength - 1) {
            isEndStem = true;
        } else {
            //向下遍历
            for (int i = x + 1; i < panelLength; i++) {
                //如果不是指定棋子
                if (chessArray[i][y] != chess) {
                    //不是自己的棋子,则左边被堵住或者是空位
                    isEndStem = chessArray[i][y] != FiveChessView.NO_CHESS;
                    break;
                } else {
                    connectCount++;
                    if (i == panelLength - 1) {
                        //在边缘位置,则被挡住
                        isEndStem = true;
                    }
                }
            }
        }
        //计算优先级评分
        return calcPriority(connectCount, isStartStem, isEndStem);
    }

    /**
     * 判断指定点chessArray[x][y]左斜(左上到右下)优先级
     *
     * @param x     数组下标
     * @param y     数组下标
     * @param chess 棋子颜色
     * @return 该点优先级评分
     */
    private int getLeftSlashPriority(int x, int y, int chess) {
        //指定棋子相连数
        
源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经过本地编译可运行的,下载完成之后配置相应环境即可使用。源码功能都是经过老师肯定的,都能满足要求,有需要放心下载即可。源码是经
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值