zoukankan      html  css  js  c++  java
  • 【game】1、pacman利用bfs进行搜索路径自动吃豆

    1.设计思路

    设计思路有几个,一步步优化来的

    v0.1 比较复杂,而且进行了2次bfs,浪费了大量时间

    v0.2 简化了2次bfs的操作,但是有很多不必要的判断逻辑,并且考虑不够全

    v0.3 极大简化了逻辑,并对幽灵,玩家的路径进行探索

    2.编码实现

    这里只提供玩家实现,不提供主程序

    import java.util.Arrays;
    import java.util.LinkedList;
    import java.util.Queue;
    
    
    /**
     * 人类玩家,使用运行时输入的方式
     */
    public class CutterPlayer extends PacmanPlayer {
        private int nextPosition;
        private final String jarName;
        // 当前行,列
        private int position[] = new int[2];
        // 方向
        private final static int[][] DIR = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
        public static final int BOARD_SIZE = 21; // 地图尺寸
        private static final int LEFT = 1; //
        private static final int UP = 2; //
        private static final int RIGHT = 3; //
        private static final int DOWN = 4; //
        private static final int MY_BARRIER = -999;
        private static final int DEEP = 21;
        private static final int CAN_NOT_ARRVIDE = -5;
        private static final int CAN_NOT_EAT = -5;
        private static final int CAN_NOT_ARRVIDE_MYSELF = -2;
    
    
        public CutterPlayer(String jarname) throws Exception {
            this.jarName = jarname;
        }
    
        @Override
        public void init() {
            super.init();
            nextPosition = 0;
        }
    
        @Override
        public String getName() {
            return jarName;
        }
    
        @Override
        public PlayerType getType() {
            return PlayerType.JAVA;
        }
    
        @Override
        public void ready(int no, int timeLimit) throws Exception {
            position[0] = no / 21;
            position[1] = no % 21;
            System.out.println("init[" + position[0] + ":" + position[1] + "]");
        }
    
        /**
         * 该接口在游戏中每回合都会被调用一次,用于告知选手程序当前局面,选手程序自行决定行动。
         *
         * @param board 局面的位置列表,假设a行b列(0<=a,b<21),那么对应的值是            21*a+b,当前位置为ghost为-1,为空表示-2
         *    ,为-3表示大豆子,为-4表示小豆子,为-5、-6、-7表示不同的障碍,
         *   其余>=0 为该位置的pacman所代表的力量(包括一个自己)
         * @param strength 当前的力量,由服务器传回来
         * @return 方向0代表不动,1,2,3,4 分别代表左、上、右、下,其他输入皆非法
         * @throws Exception
         */
        @Override
        public int run(int[] board, int strength) throws Exception {
    
            // board转换为二维数组
            boolean haveTarget = false;
            int[][] erweiboard = new int[BOARD_SIZE][BOARD_SIZE];
            for (int i = 0; i < board.length; ++i) {
                if (board[i] > -5) haveTarget = true;
                if (isCannotArrivederweiboard(erweiboard, i)) {
                    continue;
                }
                // 2.判断是否是幽灵
                // 3.判断是否是玩家
                // 3.1 玩家力量是否比自己小X
                if (isGhost(board, i) || isStrongPlayer(board, i, strength)) {
                    setCannotArrived(erweiboard, i);
                    continue;
                }
                // 4.计算当前二维数组位置
                // 5.填充进入数组
                erweiboard[i/21][i%21] = board[i];
            }
    
            if (!haveTarget) return 0;
    
            // 6.获取当前需要递归层数
            // 7.添加当前位置加入遍历队列
            Queue<Node> nextSteps = new LinkedList<>();  // 广度的队列
            boolean[][] flag = new boolean[BOARD_SIZE][BOARD_SIZE]; // 标记
            flag[position[0]][position[1]] = true;
            Node root = new Node(new int[] {position[0], position[1]});
            root.curScore = -999;
            nextSteps.offer(root);
            // 8.初始化当前层级
            int curdeep = 0;
            Node maxNode = null;
            boolean init = true;
            // 9.设置当前最大得分节点为当前节点,分值为-999
            // 10.判断队列是否为空&&当前层级<最大递归层级&&最大得分节点不是当前节点
            while (!nextSteps.isEmpty() && curdeep <= DEEP && (maxNode != null || init || maxNode.curScore != -2)) {
                // 11.循环单当前队列长度所有节点
                for (int i = 0; i < nextSteps.size(); i++) {
                    Node sonNode = nextSteps.poll();
                    int[] next = sonNode.value;
                    // 遍历这个点的4个方向
                    // 12.获取当前循环节点的方向&位置
                    for (int j = 1; j < 5; j++) {
                        int[] temp = getNextDirection(j, next);
                        // 13.是否遍历过
                        // 14.是否识别为障碍
                        if (!flag[temp[0]][temp[1]] && erweiboard[temp[0]][temp[1]] > CAN_NOT_ARRVIDE) {
    //                        System.out.println("erweiboard[" +temp[0]+ "][" + temp[1] + "]:" + erweiboard[temp[0]][temp[1]]);
                            // 15.创建新的路径节点
                            Node node = new Node(temp);
                            node.root = sonNode;
                            node.direction = j;
                            if (erweiboard[temp[0]][temp[1]] == -2) {
                                // 如果是-2 不可取
                                node.curScore = CAN_NOT_EAT;
                            } else {
                                node.curScore = erweiboard[temp[0]][temp[1]];
                            }
                            // 16.判断这个节点是否比当前最大得分节点还高
                            // 16.1 更新最大得分节点
                            if (maxNode == null || node.curScore > maxNode.curScore) {
                                maxNode = node;
                                init = false;
                            }
                            // 17.加入队列
                            nextSteps.offer(node);
                            flag[temp[0]][temp[1]] = true;
                        }
                    }
                }
                // 18.循环结束,层级++
                curdeep++;
            }
    
            // 19.方向并查集向上求得路径最后一个父节点是当前位置的节点
            while (maxNode != null && maxNode.root != null && !Arrays.equals(maxNode.root.value, position)) {
                maxNode = maxNode.root;
            }
            // 20.是否为空,为空return 0
            if (maxNode == null || Arrays.equals(position, maxNode.value)) return 0;
            // 21.更新当前坐标
            position = getNextDirection(maxNode.direction, position);
            // 22.返回方向
            System.out.println("direction:" + maxNode.direction + "=>[" + position[0] + ":" + position[1] + "], score:" + maxNode.curScore);
            return maxNode.direction;
    
        }
    
        public static int[] getNextDirection(int direction, int[] prePosition) {
            int[] newPosition = new int[] {prePosition[0], prePosition[1]};
            switch (direction) {
                case UP:
                    // 如果是顶部,则跳转到底部,其他方向同理,边界是打通的。
                    if (prePosition[0] == 0) {
                        newPosition[0] = BOARD_SIZE - 1;
                    } else {
                        newPosition[0] = prePosition[0] - 1;
                    }
                    break;
                case DOWN:
                    if (prePosition[0] == BOARD_SIZE - 1) {
                        newPosition[0] = 0;
                    } else {
                        newPosition[0] = prePosition[0] + 1;
                    }
                    break;
                case LEFT:
                    if (prePosition[1] == 0) {
                        newPosition[1] = BOARD_SIZE - 1;
                    } else {
                        newPosition[1] = prePosition[1] - 1;
                    }
                    break;
                case RIGHT:
                    if (prePosition[1] == BOARD_SIZE - 1) {
                        newPosition[1] = 0;
                    } else {
                        newPosition[1] = prePosition[1] + 1;
                    }
                    break;
                case 0:
                    break; // 不动代表新位置还是原来的位置
                default: // 错误输入
                    throw new RuntimeException("输入错误");
            }
            return newPosition;
        }
    
        private boolean isCannotArrived(int[] board, int site) {
            if (board[site] < CAN_NOT_ARRVIDE) {
                return true;
            }
    
            return false;
        }
    
        private boolean isCannotArrivederweiboard(int[][] erweiboard, int site) {
            int[] tmpposition = new int[2];
            tmpposition[0] = site / 21;
            tmpposition[1] = site % 21;
            if (erweiboard[tmpposition[0]][tmpposition[1]] != 0 || Arrays.equals(tmpposition, position)) {
                return true;
            }
    
            return false;
        }
    
        private boolean isGhost(int[] board, int site) {
            if (board[site] == -1) {
                return true;
            }
    
            return false;
        }
    
        private boolean isStrongPlayer(int[] board, int site, int strength) {
            if (board[site] >= 0 && board[site] >= strength - 2) {
                return true;
            }
    
            return false;
        }
    
        private void setCannotArrived(int[][] erweiboard, int site) {
            // 设置4个方向都不可达
            int[] prePosition = new int[2];
            prePosition[0] = site / 21;
            prePosition[1] = site % 21;
    
            for (int direction = 1; direction <= 4; ++direction) {
                int[] newPosition = new int[] {prePosition[0], prePosition[1]};
                switch (direction) {
                    case UP:
                        // 如果是顶部,则跳转到底部,其他方向同理,边界是打通的。
                        if (prePosition[0] == 0) {
                            newPosition[0] = BOARD_SIZE - 1;
                        } else {
                            newPosition[0] = prePosition[0] - 1;
                        }
                        break;
                    case DOWN:
                        if (prePosition[0] == BOARD_SIZE - 1) {
                            newPosition[0] = 0;
                        } else {
                            newPosition[0] = prePosition[0] + 1;
                        }
                        break;
                    case LEFT:
                        if (prePosition[1] == 0) {
                            newPosition[1] = BOARD_SIZE - 1;
                        } else {
                            newPosition[1] = prePosition[1] - 1;
                        }
                        break;
                    case RIGHT:
                        if (prePosition[1] == BOARD_SIZE - 1) {
                            newPosition[1] = 0;
                        } else {
                            newPosition[1] = prePosition[1] + 1;
                        }
                        break;
                    case 0:
                        break; // 不动代表新位置还是原来的位置
                    default: // 错误输入
                        throw new RuntimeException("输入错误");
                }
                erweiboard[newPosition[0]][newPosition[1]] = MY_BARRIER;
                erweiboard[prePosition[0]][prePosition[1]] = CAN_NOT_ARRVIDE;
            }
        }
    
    
    
        /**
         * 用于保存路径
         */
        public static class Node {
            private int[] value;
    
            private int direction;
    
            private Node root;
    
            private int curScore = 0;
    
            public Node(int[] value) {
                this.value = value;
            }
    
            @Override
            public boolean equals(Object obj) {
                if (obj instanceof Node) {
                    Node other = (Node) obj;
                    return this.value[0] == other.value[0] && this.value[1] == other.value[1];
                }
    
                return super.equals(obj);
            }
        }
    
        public int getNextPosition() {
            return nextPosition;
        }
    
        public void setNextPosition(int nextPosition) {
            this.nextPosition = nextPosition;
        }
    
    }

    实现效果:

    黄色为程序吃豆人,红色为外挂吃豆人(只做演示用,或者追杀黄色)

  • 相关阅读:
    程序经理_产品经理_项目经理
    github上排名靠前的java项目之_storm
    垂直型与水平型电子商务网站的理解
    关于驱动更新的一点学习
    Balanced Binary Tree
    Gray Code
    Best Time to Buy and Sell Stock II
    Best Time to Buy and Sell Stock
    Maximum Depth of Binary Tree
    Next Permutation
  • 原文地址:https://www.cnblogs.com/cutter-point/p/14258901.html
Copyright © 2011-2022 走看看