/******************************************************************************* * Copyright 2015 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.ai.pfa.indexed; import org.junit.Assert; import org.junit.Test; import com.badlogic.gdx.ai.pfa.Connection; import com.badlogic.gdx.ai.pfa.DefaultConnection; import com.badlogic.gdx.ai.pfa.DefaultGraphPath; import com.badlogic.gdx.ai.pfa.GraphPath; import com.badlogic.gdx.ai.pfa.Heuristic; import com.badlogic.gdx.utils.Array; public class IndexedAStarPathFinderTest { @Test public void searchNodePath_WhenSearchingAdjacentTile_ExpectedOuputPathLengthEquals2 () { // @off - disable libgdx formatter final String graphDrawing = "..........\n" + "..........\n" + ".........."; // @on - enable libgdx formatter final MyGraph graph = createGraphFromTextRepresentation(graphDrawing); final IndexedAStarPathFinder<MyNode> pathfinder = new IndexedAStarPathFinder<>(graph); final GraphPath<MyNode> outPath = new DefaultGraphPath<>(); // @off - disable libgdx formatter // 0123456789 // .......... 0 // .....S.... 10 // .....E.... 20 // @on - enable libgdx formatter final boolean searchResult1 = pathfinder.searchNodePath(graph.nodes.get(15), graph.nodes.get(25), new ManhattanDistance(), outPath); Assert.assertTrue("Unexpected search result", searchResult1); Assert.assertEquals("Unexpected number of nodes in path", 2, outPath.getCount()); // @off - disable libgdx formatter // 0123456789 // .......... 0 // .....SE... 10 // .......... 20 // @on - enable libgdx formatter outPath.clear(); final boolean searchResult2 = pathfinder.searchNodePath(graph.nodes.get(15), graph.nodes.get(16), new ManhattanDistance(), outPath); Assert.assertTrue("Unexpected search result", searchResult2); Assert.assertEquals("Unexpected number of nodes in path", 2, outPath.getCount()); // @off - disable libgdx formatter // 0123456789 // .......... 0 // ....ES.... 10 // .......... 20 // @on - enable libgdx formatter outPath.clear(); final boolean searchResult3 = pathfinder.searchNodePath(graph.nodes.get(15), graph.nodes.get(14), new ManhattanDistance(), outPath); Assert.assertTrue("Unexpected search result", searchResult3); Assert.assertEquals("Unexpected number of nodes in path", 2, outPath.getCount()); // @off - disable libgdx formatter // 0123456789 // .....E.... 0 // .....S.... 10 // .......... 20 // @on - enable libgdx formatter outPath.clear(); final boolean searchResult4 = pathfinder.searchNodePath(graph.nodes.get(15), graph.nodes.get(5), new ManhattanDistance(), outPath); Assert.assertTrue("Unexpected search result", searchResult4); Assert.assertEquals("Unexpected number of nodes in path", 2, outPath.getCount()); } @Test public void searchNodePath_WhenSearchCanHitDeadEnds_ExpectedOuputPathFound () { // @off - disable libgdx formatter final String graphDrawing = ".#.#.......#..#...............\n" + ".#............#.....#..#####..\n" + "...#.#######..#.....#.........\n" + ".#.#.#........#.....########..\n" + ".###.#....#####.....#......##.\n" + ".#...#....#.........#...##....\n" + ".#####....#.........#....#....\n" + ".#........#.........#....#####\n" + ".####....##.........#......#..\n" + "....#...............#......#.."; // @on - enable libgdx formatter final MyGraph graph = createGraphFromTextRepresentation(graphDrawing); final IndexedAStarPathFinder<MyNode> pathfinder = new IndexedAStarPathFinder<>(graph); final GraphPath<MyNode> outPath = new DefaultGraphPath<>(); // @off - disable libgdx formatter // 012345678901234567890123456789 // S#.#.......#..#............... 0 // .#............#.....#..#####.. 30 // ...#.#######..#.....#......... 60 // .#.#.#........#.....########.. 90 // .###.#....#####.....#......##. 120 // .#...#....#.........#...##.... 150 // .#####....#.........#....#.... 180 // .#E.......#.........#....##### 210 // .####....##.........#......#.. 240 // ....#...............#......#.. 270 // @on - enable libgdx formatter final boolean searchResult = pathfinder.searchNodePath(graph.nodes.get(0), graph.nodes.get(212), new ManhattanDistance(), outPath); Assert.assertTrue("Unexpected search result", searchResult); Assert.assertEquals("Unexpected number of nodes in path", 32, outPath.getCount()); } @Test public void searchNodePath_WhenDestinationUnreachable_ExpectedNoOuputPathFound () { // @off - disable libgdx formatter final String graphDrawing = ".....#....\n" + ".....#....\n" + ".....#...."; // @on - enable libgdx formatter final MyGraph graph = createGraphFromTextRepresentation(graphDrawing); final IndexedAStarPathFinder<MyNode> pathfinder = new IndexedAStarPathFinder<>(graph); final GraphPath<MyNode> outPath = new DefaultGraphPath<>(); // @off - disable libgdx formatter // 0123456789 // S....#...E 0 // .....#.... 10 // .....#.... 20 // @on - enable libgdx formatter final boolean searchResult = pathfinder.searchNodePath(graph.nodes.get(0), graph.nodes.get(9), new ManhattanDistance(), outPath); Assert.assertFalse("Unexpected search result", searchResult); } private static MyGraph createGraphFromTextRepresentation (final String graphTextRepresentation) { final String[][] tiles = createStringTilesFromGraphTextRepresentation(graphTextRepresentation); final int numRows = tiles[0].length; final int numCols = tiles.length; final MyNode[][] nodes = new MyNode[numCols][numRows]; final Array<MyNode> indexedNodes = new Array<>(numCols * numRows); int index = 0; for (int y = 0; y < numRows; y++) { for (int x = 0; x < numCols; x++, index++) { nodes[x][y] = new MyNode(index, x, y, 4); indexedNodes.add(nodes[x][y]); } } for (int y = 0; y < numRows; y++, index++) { for (int x = 0; x < numCols; x++, index++) { if (tiles[x][y].equals("#")) { continue; } if (x - 1 >= 0 && tiles[x - 1][y].equals(".")) { nodes[x][y].getConnections().add(new DefaultConnection<MyNode>(nodes[x][y], nodes[x - 1][y])); } if (x + 1 < numCols && tiles[x + 1][y].equals(".")) { nodes[x][y].getConnections().add(new DefaultConnection<MyNode>(nodes[x][y], nodes[x + 1][y])); } if (y - 1 >= 0 && tiles[x][y - 1].equals(".")) { nodes[x][y].getConnections().add(new DefaultConnection<MyNode>(nodes[x][y], nodes[x][y - 1])); } if (y + 1 < numRows && tiles[x][y + 1].equals(".")) { nodes[x][y].getConnections().add(new DefaultConnection<MyNode>(nodes[x][y], nodes[x][y + 1])); } } } return new MyGraph(indexedNodes); } private static String[][] createStringTilesFromGraphTextRepresentation (final String graphTextRepresentation) { final String[] rows = graphTextRepresentation.split("\n"); final int numRows = rows.length; final int numCols = rows[0].length(); final String[][] tiles = new String[numCols][numRows]; for (int y = 0; y < numRows; y++) { final String row = rows[y]; for (int x = 0; x < numCols; x++) { tiles[x][y] = "" + row.charAt(x); } } return tiles; } private static class MyNode { private final int index; private final int x; private final int y; private final Array<Connection<MyNode>> connections; public MyNode (final int index, final int x, final int y, final int capacity) { this.index = index; this.x = x; this.y = y; this.connections = new Array<>(capacity); } public int getIndex () { return index; } public Array<Connection<MyNode>> getConnections () { return connections; } @Override public String toString () { return "IndexedNodeFake [index=" + index + ", x=" + x + ", y=" + y + ", connections=" + connections + "]"; } } private static class MyGraph implements IndexedGraph<MyNode> { protected Array<MyNode> nodes; public MyGraph (Array<MyNode> nodes) { this.nodes = nodes; } @Override public int getIndex (MyNode node) { return node.getIndex(); } @Override public Array<Connection<MyNode>> getConnections (MyNode fromNode) { return fromNode.getConnections(); } @Override public int getNodeCount () { return nodes.size; } } private static class ManhattanDistance implements Heuristic<MyNode> { @Override public float estimate (final MyNode node, final MyNode endNode) { return Math.abs(endNode.x - node.x) + Math.abs(endNode.y - node.y); } } }