/** * Returns a list of the enemies with radius squares of your hero * * @param gameState * @param searchResults * @param radius * @return */ public static List<GameState.Hero> getHeroesAround( AdvancedGameState gameState, Map<GameState.Position, AdvancedMurderBot.DijkstraResult> searchResults, int radius) { List<GameState.Hero> heroes = new LinkedList<>(); for (GameState.Hero currentHero : gameState.getHeroesByPosition().values()) { GameState.Position currentHeroPosition = currentHero.getPos(); if (searchResults.get(currentHeroPosition).getDistance() <= radius && currentHero.getId() != gameState.getMe().getId()) heroes.add(currentHero); } return heroes; }
/** * Creates a new AdvancedGameState by taking he previous AdvancedGameState and updating is using a * new GameState * * @param oldGameState * @param updatedState */ public AdvancedGameState(AdvancedGameState oldGameState, GameState updatedState) { // Copy the stuff we can just re-use this.boardGraph = oldGameState.getBoardGraph(); this.pubs = oldGameState.getPubs(); this.viewUrl = oldGameState.getViewUrl(); // Re-build the hero maps this.heroesByPosition = new HashMap<>(); this.heroesById = new HashMap<>(); for (GameState.Hero currentHero : updatedState.getGame().getHeroes()) { this.heroesByPosition.put(currentHero.getPos(), currentHero); this.heroesById.put(currentHero.getId(), currentHero); } this.me = updatedState.getHero(); // Update the mines this.mines = oldGameState.getMines(); for (Mine currentMine : this.mines.values()) { // Vindinium does the x and y coordinates backwards int tileStart = currentMine.getPosition().getX() * updatedState.getGame().getBoard().getSize() * 2 + (currentMine.getPosition().getY() * 2); // We don't want the whole tile; we want the second char String owner = updatedState.getGame().getBoard().getTiles().substring(tileStart + 1, tileStart + 1 + 1); Mine mine; if (owner.equals("-")) { mine = new Mine(currentMine.getPosition(), null); } else { int ownerId = Integer.parseInt(owner); mine = new Mine(currentMine.getPosition(), this.heroesById.get(ownerId)); } this.mines.put(mine.getPosition(), mine); } }
/** * Creates an AdvancedGameState from a GameState * * @param gameState */ public AdvancedGameState(GameState gameState) { boardGraph = new HashMap<>(); mines = new HashMap<>(); pubs = new HashMap<>(); heroesById = new HashMap<>(); heroesByPosition = new HashMap<>(); viewUrl = gameState.getViewUrl(); // Hero stuffs for (GameState.Hero currentHero : gameState.getGame().getHeroes()) { this.heroesByPosition.put(currentHero.getPos(), currentHero); this.heroesById.put(currentHero.getId(), currentHero); } this.me = gameState.getHero(); // Build the graph sans edges GameState.Board board = gameState.getGame().getBoard(); for (int row = 0; row < board.getSize(); row++) { for (int col = 0; col < board.getSize(); col++) { // Yeah, Vindinium does the x and y coordinates backwards GameState.Position pos = new GameState.Position(row, col); int tileStart = row * board.getSize() * 2 + (col * 2); String tileValue = board.getTiles().substring(tileStart, tileStart + 1 + 1); // We do nothing with tiles that are barriers if (tileValue.equals("##")) { continue; } Vertex v = new Vertex(pos, new LinkedList<Vertex>()); this.boardGraph.put(pos, v); // If its a mine or tavern, we treat it differently // We don't care if its a hero because a separate index for those already exists if (tileValue.startsWith("$")) { String owner = tileValue.substring(1); Mine mine; if (owner.equals("-")) { mine = new Mine(pos, null); } else { int ownerId = Integer.parseInt(owner); mine = new Mine(pos, this.heroesById.get(ownerId)); } this.mines.put(pos, mine); } else if (tileValue.equals("[]")) { Pub pub = new Pub(pos); this.pubs.put(pos, pub); } } } // Add in the edges // This graph doesn't take into account players because they move. That is done elsewhere. for (Vertex currentVertex : this.boardGraph.values()) { GameState.Position currentVertexPosition = currentVertex.getPosition(); // Pubs and mines cannot be passed through if (this.mines.containsKey(currentVertexPosition) || this.pubs.containsKey(currentVertexPosition)) { continue; } // Other players cannot be passed through. However, they move, and mines/pubs don't, so its // easier to make // the bot and path-finding deal with that. We don't take other players into account here. // We can only move NSEW, so no need for a fancy set of nested loops... for (int xDelta = -1; xDelta <= 1; xDelta += 2) { int currentX = currentVertex.getPosition().getX(); int newX = currentX + xDelta; if (newX >= 0 && newX < board.getSize()) { GameState.Position adjacentPos = new GameState.Position(newX, currentVertex.getPosition().getY()); Vertex adjacentVertex = this.boardGraph.get(adjacentPos); if (adjacentVertex != null) { currentVertex.getAdjacentVertices().add(adjacentVertex); } } } for (int yDelta = -1; yDelta <= 1; yDelta += 2) { int currentY = currentVertex.getPosition().getY(); int newY = currentY + yDelta; if (newY >= 0 && newY < board.getSize()) { GameState.Position adjacentPos = new GameState.Position(currentVertex.getPosition().getX(), newY); Vertex adjacentVertex = this.boardGraph.get(adjacentPos); if (adjacentVertex != null) { currentVertex.getAdjacentVertices().add(adjacentVertex); } } } } }
@Override public BotMove makeDecision(AdvancedMurderBot.GameContext context) { logger.info("Deciding which bot to target"); GameState.Hero me = context.getGameState().getMe(); // Are there a crashed bot with mines we can take advantage of? // for(Mine currentMine : context.getGameState().getMines().values()) { // if(currentMine.getOwner() != null && currentMine.getOwner().isCrashed()) { // // GameState.Hero target = currentMine.getOwner(); // AdvancedMurderBot.DijkstraResult currentDijkstraResult = // context.getDijkstraResultMap().get(target.getPos()); // GameState.Position nextPosition = target.getPos(); // // while(null != currentDijkstraResult && currentDijkstraResult.getDistance() > // 1) { // nextPosition = currentDijkstraResult.getPrevious(); // currentDijkstraResult = context.getDijkstraResultMap().get(nextPosition); // } // // logger.info("Going after a crashed bot"); // return BotUtils.directionTowards(currentDijkstraResult.getPrevious(), // nextPosition); // } // } Set<GameState.Hero> heroesWithMines = new HashSet<>(); // Which heroes have mines? for (Mine currentMine : context.getGameState().getMines().values()) { if (currentMine.getOwner() != null) heroesWithMines.add(currentMine.getOwner()); } // Ok, crashed bots. How about bots that aren't squatting? GameState.Hero closestTarget = null; AdvancedMurderBot.DijkstraResult closestTargetDijkstraResult = null; for (GameState.Hero currentHero : heroesWithMines) { AdvancedMurderBot.DijkstraResult currentDijkstraResult = context.getDijkstraResultMap().get(currentHero.getPos()); // We don't want to target bots that we cannot reach if (currentDijkstraResult == null) continue; // We don't want to target ourselves if (currentHero.getId() == context.getGameState().getMe().getId()) continue; // We don't want to target other bots of our type // TODO We probably shouldn't hardcode this name // if(currentHero.getName().equals("Assassin Bot")) // continue; // Are they on their spawn? if (currentHero.getPos().equals(currentHero.getSpawnPos())) continue; // Does he have more HP than we do? if (currentHero.getLife() > 20 && currentHero.getLife() > me.getLife()) continue; // Check the adjacent squares to see if a pub exists Vertex currentHeroVertext = context.getGameState().getBoardGraph().get(currentHero.getPos()); for (Vertex currentVertext : currentHeroVertext.getAdjacentVertices()) { if (context.getGameState().getPubs().containsKey(currentVertext.getPosition())) { continue; } } // Ok, we got this far...it must not be squatting. Is it closest? if (closestTarget == null) { closestTarget = currentHero; closestTargetDijkstraResult = context.getDijkstraResultMap().get(closestTarget.getPos()); continue; } else if (closestTargetDijkstraResult.getDistance() > currentDijkstraResult.getDistance()) { closestTarget = currentHero; closestTargetDijkstraResult = context.getDijkstraResultMap().get(closestTarget.getPos()); } } if (closestTarget != null) { GameState.Position nextMove = closestTarget.getPos(); while (closestTarget != null && closestTargetDijkstraResult.getDistance() > 1) { nextMove = closestTargetDijkstraResult.getPrevious(); closestTargetDijkstraResult = context.getDijkstraResultMap().get(nextMove); } logger.info("Going after another bot"); return BotUtils.directionTowards(closestTargetDijkstraResult.getPrevious(), nextMove); } // Ok, no one worth attacking. logger.info("No bot worth attacking. Deferring."); return noTargetFoundDecisioner.makeDecision(context); }