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

}

 

實現效果:

黃色爲程序喫豆人,紅色爲外掛喫豆人(只做演示用,或者追殺黃色)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章