function AStar(G, s, t) {
  let closedList = [];
  let openList = [s];

  G.nodes.forEach(function(node) {
    node.dist = Infinity;
    node.estimatedDist = Infinity;
    node.parent = null;
  });

  s.dist = 0;
  s.estimatedDist = s.heuristic;

  while(openList.length !== 0) {
    openList.sort((a, b) => (a.estimatedDist - b.estimatedDist));
    let current = openList.shift();
    closedList.push(current);
    if (current.equals(t)) {
      return t;
    }

    let adjacent = G.getAdjacentNodes(current);
    adjacent.forEach(function(node) {
      if (closedList.find(node)) {
        return;
      }
      if (!openList.find(node)) {
        openList.push(node);
      }

      let newDist = current.dist + W(current, node);
      if (newDist < node.dist) {
        node.dist = newDist;
        node.estimatedDist = newDist + node.heuristic;
        node.parent = current;
      }
    });
  }
}