public static String awinsIfEven(InputData input) {
    Set<Integer> kingLocs = input.grid.getIndexesOf('K');
    int kingLoc = kingLocs.iterator().next();

    boolean r = true;

    GridChar gridOrig = new GridChar(input.grid);

    // Further reduce the grid in case B can win after A's first move
    for (Direction dir : Direction.values()) {
      Integer childIdx = input.grid.getIndex(kingLoc, dir);
      if (childIdx == null) continue;

      char sq = input.grid.getEntry(childIdx);

      if (sq != '.') continue;

      GridChar gridTry = new GridChar(gridOrig);

      if (tryFirstMove(gridTry, kingLoc, childIdx)) {
        input.grid.setEntry(childIdx, 'T');
      } else {
        return String.format("Case #%d: %s", input.testCase, "A");
      }
    }

    while (r) {
      // r = findLoserSquares(input.grid);
      r = reduceGrid(input.grid, kingLoc);
    }

    for (Direction dir : Direction.values()) {
      Integer childIdx = input.grid.getIndex(kingLoc, dir);
      if (childIdx == null) continue;

      char sq = input.grid.getEntry(childIdx);

      if (sq == 'V') {
        return String.format("Case #%d: %s", input.testCase, "A");
      }

      if (sq == '#' || sq == 'T') {
        continue;
      }

      //            int size = 1 + getConnectedSquareCount(input.grid, childIdx);
      //
      //            if (size % 2 == 0) {
      //                return String.format("Case #%d: %s", input.testCase, "A" );
      //            }

      if (getConnectedSquareCount(input.grid, childIdx))
        return String.format("Case #%d: %s", input.testCase, "A");
    }

    return String.format("Case #%d: %s", input.testCase, "B");
  }
  // Returns true if B wins
  public static boolean tryFirstMove(GridChar grid, int aKingLoc, int bKingLoc) {

    grid.setEntry(aKingLoc, '#');
    grid.setEntry(bKingLoc, 'K');

    boolean r = true;
    while (r) {
      // r = findLoserSquares(input.grid);
      r = reduceGrid(grid, bKingLoc);
    }

    for (Direction dir : Direction.values()) {
      Integer childIdx = grid.getIndex(bKingLoc, dir);
      if (childIdx == null) continue;

      char sq = grid.getEntry(childIdx);

      if (sq == 'V') {
        return true;
      }

      if (sq == '#' || sq == 'T') {
        continue;
      }

      // int size = 1 + getConnectedSquareCount(grid, childIdx);
      if (getConnectedSquareCount(grid, childIdx)) return true;
      //            if (size % 2 == 0) {
      //                return true;
      //            }

    }

    return false;
  }
  public static int getConnectedSquareCountOld(GridChar grid, int startingLoc) {

    Set<Integer> visitedNodes = Sets.newHashSet();

    LinkedList<Integer> toVisit = new LinkedList<>();
    toVisit.add(startingLoc);

    while (!toVisit.isEmpty()) {

      Integer loc = toVisit.poll();

      if (visitedNodes.contains(loc)) continue;

      visitedNodes.add(loc);

      for (Direction dir : Direction.values()) {
        Integer childIdx = grid.getIndex(loc, dir);
        if (childIdx == null) continue;

        char sq = grid.getEntry(childIdx);

        if (sq == '#' || sq == 'K' || sq == 'T') continue;

        toVisit.add(childIdx);
      }
    }

    return visitedNodes.size();
  }
  public static Integer getOpenNodeNextToKing(GridChar grid, int loc) {
    for (Direction dir : Direction.values()) {
      Integer childIdx = grid.getIndex(loc, dir);
      if (childIdx == null) continue;

      char sq = grid.getEntry(childIdx);

      if (sq == '.') {
        return childIdx;
      }
    }

    return null;
  }
  /**
   * Returns the count of squares reachable by the first player
   *
   * @param grid
   * @param startingLoc
   * @return
   */
  public static boolean getConnectedSquareCount(GridChar grid, int startingLoc) {

    // int oldCount = (1+getConnectedSquareCountOld(grid,startingLoc) );

    // The boolean means it is player 1, the connected square co
    Set<Pair<Integer, Boolean>> visitedNodes = Sets.newHashSet();

    LinkedList<Pair<Integer, Boolean>> toVisit = new LinkedList<>();
    toVisit.add(new ImmutablePair<>(startingLoc, true));

    while (!toVisit.isEmpty()) {

      Pair<Integer, Boolean> loc = toVisit.poll();

      if (visitedNodes.contains(loc)) continue;

      visitedNodes.add(loc);

      for (Direction dir : Direction.values()) {
        Integer childIdx = grid.getIndex(loc.getLeft(), dir);
        if (childIdx == null) continue;

        char sq = grid.getEntry(childIdx);

        if (sq == '#' || sq == 'K' || sq == 'T') continue;

        toVisit.add(new ImmutablePair<>(childIdx, !loc.getRight()));
      }
    }

    Set<Integer> fpPoints = Sets.newHashSet();
    Set<Integer> spPoints = Sets.newHashSet();
    for (Pair<Integer, Boolean> p : visitedNodes) {
      if (p.getRight()) fpPoints.add(p.getLeft());
      else spPoints.add(p.getLeft());
    }

    Set<Integer> shared = Sets.intersection(fpPoints, spPoints);
    Set<Integer> union = Sets.union(fpPoints, spPoints);

    if (!shared.isEmpty()) {
      return (union.size() + 1) % 2 == 0;
    } else {
      // log.debug("size {} size {}", fpPoints.size(), spPoints.size());
      return spPoints.size() < fpPoints.size();
    }
    // return (countFP + countSP + 1) % 2 == 0;
  }
  public static boolean reduceGrid(GridChar grid, final int kingLoc) {
    // Create a graph corresponding to grid
    GraphInt graph = new GraphInt();

    int startingLoc = kingLoc; // getOpenNodeNextToKing(grid, kingLoc);

    Set<Integer> visitedNodes = Sets.newHashSet();

    LinkedList<Integer> toVisit = new LinkedList<>();
    toVisit.add(startingLoc);

    while (!toVisit.isEmpty()) {

      Integer loc = toVisit.poll();

      if (visitedNodes.contains(loc)) continue;

      visitedNodes.add(loc);

      for (Direction dir : Direction.values()) {
        Integer childIdx = grid.getIndex(loc, dir);
        if (childIdx == null) continue;

        char sq = grid.getEntry(childIdx);

        if (sq == '#' || sq == 'K' || sq == 'V' || sq == 'T') continue;

        if (loc == kingLoc)
          // graph.addOneWayConnection(loc,childIdx);
          graph.addConnection(loc, childIdx);
        else graph.addConnection(loc, childIdx);

        toVisit.add(childIdx);
      }
    }

    ArticulationPoint ap = new ArticulationPoint(graph);
    // Find bridges
    List<Integer> artPoints = ap.getArticulationPoints();

    for (Integer aPoint : artPoints) {

      if (aPoint == kingLoc) continue;
      Set<Integer> adjNodes = graph.getNeighbors(aPoint);

      Set<Integer> isolatedNodes = Sets.newHashSet();
      Set<Integer> nonIsolatedNodes = Sets.newHashSet();

      adjNodeLoop:
      for (Integer adjNode : adjNodes) {
        if (isolatedNodes.contains(adjNode) || nonIsolatedNodes.contains(adjNode)) continue;

        Set<Integer> nodes =
            GraphIntAlgorithms.getConnectedNodesWithoutNode(graph, adjNode, aPoint);

        // Isolated set must not contain other articulation points
        for (Integer aPointToTest : artPoints) {
          if (nodes.contains(aPointToTest)) {
            nonIsolatedNodes.addAll(nodes);
            continue adjNodeLoop;
          }
        }

        if (!nodes.contains(startingLoc)) {
          isolatedNodes = nodes;
          break;
        }
      }

      // No suitable isolated set found, continue to next articulation point
      if (isolatedNodes == null || isolatedNodes.isEmpty()) continue;

      if (isolatedNodes.size() % 2 == 0) {
        // All the isolated nodes are traps
        for (Integer isoNode : isolatedNodes) {
          grid.setEntry(isoNode, 'T');
        }
        return true;
      } else {
        // The articulation point itself is a trap, as moving to it means
        // the other player can move into an odd numbered field, which is always
        // losing
        grid.setEntry(aPoint, 'T');
        return true;
      }
    }

    return false;
  }