package com.hypixel.hytale.server.npc.navigation; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.ObjectIterator; import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class AStarDebugBase { public static final char CENTER = ' '; public static final char CROSS = '┼'; public static final char HLINE = '─'; public static final char VLINE = '│'; public static final char OPEN_NODE = '◆'; public static final char CLOSED_NODE = '◇'; public static final char CLOSED_PATH_NODE = '◯'; public static final char OPEN_PATH_NODE = '◉'; public static final char BLOCKED_NODE = '×'; public static final char START_POSITION = '@'; public static final char END_POSITION = 'Ω'; public static final String BORDER_PATTERN = "─┼".repeat(1025); public static final String CENTER_PATTERN = " │".repeat(1025); protected AStarBase aStarBase; protected HytaleLogger logger; protected HytaleLogger.Api loggerInfo; public AStarDebugBase(AStarBase base, @Nonnull HytaleLogger logger) { this.aStarBase = base; this.logger = logger; this.loggerInfo = logger.at(Level.INFO); } public void dumpOpens(MotionController controller) { int openCount = this.aStarBase.getOpenCount(); List openNodes = this.aStarBase.getOpenNodes(); int maxLength = -1; for (int i = 0; i < openCount; i++) { int length = openNodes.get(i).getLength(); if (length > maxLength) { maxLength = length; } } this.loggerInfo .log( "== A* iter=%s opens=%s total=%s maxLength=%s %s", this.aStarBase.getIterations(), openCount, this.aStarBase.getVisitedBlocks().size(), maxLength, this.getExtraLogString(controller) ); for (int ix = 0; ix < openCount; ix++) { this.loggerInfo.log("%2d %s", ix, openNodes.get(ix).toString()); } } public void dumpPath() { AStarNode node = this.aStarBase.getPath(); this.loggerInfo .log("== A* Path iter=%s opens=%s total=%s", this.aStarBase.getIterations(), this.aStarBase.getOpenCount(), this.aStarBase.getVisitedBlocks().size()); while (node != null) { this.loggerInfo.log("%s", node.toString()); node = node.getNextPathNode(); } } public void dumpMap(boolean drawPath, MotionController controller) { int openCount = this.aStarBase.getOpenCount(); List openNodes = this.aStarBase.getOpenNodes(); AStarNode start = null; boolean finalPath = false; if (drawPath) { AStarNode path = this.aStarBase.getPath(); if (path != null) { start = path; finalPath = true; } else if (openCount > 0) { start = openNodes.get(openCount - 1); } } this.dumpMap(start, finalPath, controller); } public void dumpMap(@Nullable AStarNode pathNode, boolean isFinalPath, MotionController controller) { long startPositionIndex = this.aStarBase.getStartPositionIndex(); Long2ObjectMap visitedBlocks = this.aStarBase.getVisitedBlocks(); int s = AStarBase.xFromIndex(startPositionIndex); int e = this.getDumpMapRegionX(s); int minX; int maxX; if (s < e) { minX = s; maxX = e; } else { minX = e; maxX = s; } s = AStarBase.zFromIndex(startPositionIndex); e = this.getDumpMapRegionZ(s); int minZ; int maxZ; if (s < e) { minZ = s; maxZ = e; } else { minZ = e; maxZ = s; } ObjectIterator> fastIterator = Long2ObjectMaps.fastIterator(visitedBlocks); while (fastIterator.hasNext()) { AStarNode node = (AStarNode)((Entry)fastIterator.next()).getValue(); int x = AStarBase.xFromIndex(node.getPositionIndex()); int z = AStarBase.zFromIndex(node.getPositionIndex()); if (x < minX) { minX = x; } if (x > maxX) { maxX = x; } if (z < minZ) { minZ = z; } if (z > maxZ) { maxZ = z; } } int rows = maxZ - minZ + 1; int columns = maxX - minX + 1; int offset = minX & 1; boolean evenStart = (minZ & 1) == 0; String first = "'" + (evenStart ? CENTER_PATTERN : BORDER_PATTERN).substring(offset, offset + columns) + "'"; String second = "'" + (evenStart ? BORDER_PATTERN : CENTER_PATTERN).substring(offset, offset + columns) + "'"; StringBuilder[] map = new StringBuilder[rows]; for (int i = 0; i < rows; i += 2) { map[i] = new StringBuilder(first); if (i + 1 < rows) { map[i + 1] = new StringBuilder(second); } } fastIterator = Long2ObjectMaps.fastIterator(visitedBlocks); while (fastIterator.hasNext()) { AStarNode nodex = (AStarNode)((Entry)fastIterator.next()).getValue(); this.plot(nodex.getPositionIndex(), (char)(nodex.isInvalid() ? '×' : (nodex.isOpen() ? '◆' : '◇')), map, minX, minZ); } int openCount = this.aStarBase.getOpenCount(); int maxLength; if (pathNode != null) { maxLength = pathNode.getLength(); while (pathNode != null) { this.plot(pathNode.getPositionIndex(), (char)(pathNode.isOpen() ? '◉' : '◯'), map, minX, minZ); pathNode = isFinalPath ? pathNode.getNextPathNode() : pathNode.getPredecessor(); } } else { List openNodes = this.aStarBase.getOpenNodes(); int index = openCount; maxLength = 0; while (--index >= 0) { int pos = openCount - index; if (pos > 51) { break; } this.plot(openNodes.get(index).getPositionIndex(), (char)(pos >= 26 ? pos - 26 + 97 : pos + 65), map, minX, minZ); } } this.plot(startPositionIndex, '@', map, minX, minZ); this.drawMapFinish(map, minX, minZ); this.loggerInfo .log( "== A* iter=%s, opens=%s total=%s maxLength=%s %s", this.aStarBase.getIterations(), openCount, visitedBlocks.size(), maxLength, this.getExtraLogString(controller) ); for (StringBuilder stringBuilder : map) { this.loggerInfo.log(stringBuilder.toString()); } } protected void plot(long positionIndex, char character, @Nonnull StringBuilder[] map, int minX, int minZ) { int row = AStarBase.zFromIndex(positionIndex) - minZ; int column = AStarBase.xFromIndex(positionIndex) - minX + 1; map[row].setCharAt(column, character); } protected void drawMapFinish(StringBuilder[] map, int minX, int minZ) { } protected int getDumpMapRegionZ(int def) { return def; } protected int getDumpMapRegionX(int def) { return def; } @Nonnull protected String getExtraLogString(MotionController controller) { return ""; } }