@Override
    public List<Node> findShortestPath() {
        final Map<Node, Pair<Node, Long>> closed = new HashMap<Node, Pair<Node, Long>>(100);
        final Map<Node, Pair<Node, Long>> open = new HashMap<Node, Pair<Node, Long>>(100);

        open.put(start, Pair.from((Node) null, start.getValue() + guide.estimateDistanceToGoal(start)));

        while (!open.isEmpty()) {
            long min = Long.MAX_VALUE;
            Node current = null, from = null;
            for (final Entry<Node, Pair<Node, Long>> entry : open.entrySet()) {
                if (entry.getValue().getSecond() < min) {
                    min = entry.getValue().getSecond();
                    current = entry.getKey();
                    from = entry.getValue().getFirst();
                }
            }

            open.remove(current);
            final long distanceUntilNow = (from == null ? 0 : closed.get(from).getSecond()) + current.getValue();
            closed.put(current, Pair.from(from, distanceUntilNow));

            if (guide.isGoal(current)) {
                return reconstruct(closed, current);
            }

            for (final Node neighbor : current) {
                if (closed.containsKey(neighbor)) {
                    continue;
                }

                final long estimatedDistance = distanceUntilNow + neighbor.getValue() + guide.estimateDistanceToGoal(current);
                if (!open.containsKey(neighbor) || estimatedDistance < open.get(neighbor).getSecond()) {
                    open.remove(neighbor);
                    open.put(neighbor, Pair.from(current, estimatedDistance));
                }
            }
        }

        return null;
    }