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; } }
實現效果:
黃色爲程序喫豆人,紅色爲外掛喫豆人(只做演示用,或者追殺黃色)