【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;
    }

}

 

实现效果:

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

 

posted @ 2021-01-10 17:34  cutter_point  阅读(337)  评论(0编辑  收藏  举报