/** * 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); } }
@Override public GameState call() throws Exception { HttpContent content; HttpRequest request; HttpResponse response; GameState gameState = null; AdvancedGameState advancedGameState; try { // Initial request logger.info("Sending initial request..."); content = new UrlEncodedContent(apiKey); request = REQUEST_FACTORY.buildPostRequest(gameUrl, content); request.setReadTimeout(0); // Wait forever to be assigned to a game response = request.execute(); gameState = response.parseAs(GameState.class); logger.info("Game URL: {}", gameState.getViewUrl()); advancedGameState = new AdvancedGameState(gameState); // Game loop while (!gameState.getGame().isFinished() && !gameState.getHero().isCrashed()) { logger.info("Taking turn " + gameState.getGame().getTurn()); BotMove direction = bot.move(advancedGameState); Move move = new Move(apiKey.getKey(), direction.toString()); HttpContent turn = new UrlEncodedContent(move); HttpRequest turnRequest = REQUEST_FACTORY.buildPostRequest(new GenericUrl(gameState.getPlayUrl()), turn); HttpResponse turnResponse = turnRequest.execute(); gameState = turnResponse.parseAs(GameState.class); advancedGameState = new AdvancedGameState(advancedGameState, gameState); } } catch (Exception e) { logger.error("Error during game play", e); } logger.info("Game over"); return gameState; }
/** * 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); } } } } }