@Override
  public void render(Graphics g) {

    if (map.needsToBeRendered() || reRender) {
      Graphics g1 = cachedViewport.getGraphics();

      // Draw a black background
      g1.setColor(Color.BLACK);
      g1.fillRect(0, 0, viewportWidth, viewportHeight);

      // Get the avatar's location. This location will be shown in the center of the viewport.
      Point logicalPoint = avatar.getLocation();
      Point pixelPoint = new Point(viewportWidth / 2, viewportHeight / 2);

      // Create a 2D graphcis obj
      Graphics2D g2 = (Graphics2D) g1.create();

      // Clear the existing status bar locations
      this.entityLocationTuples.clear();

      breadthFirstRender(logicalPoint, pixelPoint, g2);

      map.setNeedsToBeRendered(false);
      reRender = false;
      g1.dispose();
    }

    // draws the viewport
    g.drawImage(cachedViewport, 0, 0, viewportWidth, viewportHeight, getDisplay());

    for (EntityLocationTuple et : this.entityLocationTuples) {
      drawEntityHealthBar(g, et);
    }

    if (displayDebugInformation) {
      g.setColor(Color.WHITE);
      g.drawString(
          avatar.getLocation().toString(),
          viewportWidth - g.getFontMetrics().stringWidth(avatar.getLocation().toString()) - 50,
          25);
    }
  }
  // This will be used in the BF traversal to get the list of adjacent tiles.
  private ArrayList<TileNode> getAdjacentTiles(TileNode tile) {
    ArrayList<TileNode> adjacentTiles = new ArrayList<>();

    // Get the tile adjacent to the north.
    Point northLogicalPoint = new Point(tile.logicalPoint); // Get the tiles logical point
    northLogicalPoint.translate(0, -1);

    Point northPixelPoint = new Point(tile.pixelPoint); // Get the tiles pixel point;
    northPixelPoint.translate(0, -vertDistanceBtwnTiles);

    Tile northTile = map.getTileAt(northLogicalPoint);
    if (northTile != null) {
      adjacentTiles.add(new TileNode(northTile, northLogicalPoint, northPixelPoint));
    }

    // Get the tile to the south of the current position.
    Point southLogicalPoint = new Point(tile.logicalPoint);
    southLogicalPoint.translate(0, 1);

    Point southPixelPoint = new Point(tile.pixelPoint);
    southPixelPoint.translate(0, vertDistanceBtwnTiles);

    Tile southTile = map.getTileAt(southLogicalPoint);
    if (southTile != null) {
      adjacentTiles.add(new TileNode(southTile, southLogicalPoint, southPixelPoint));
    }

    // Get the tile to the north west of the current position.
    Point northWestLogicalPoint = new Point(tile.logicalPoint);
    northWestLogicalPoint.translate(-1, 0);

    Point northWestPixelPoint = new Point(tile.pixelPoint);
    northWestPixelPoint.translate(-horizDistanceBtwnTiles, -vertDistanceBtwnTiles / 2);

    Tile northWestTile = map.getTileAt(northWestLogicalPoint);
    if (northWestTile != null) {
      adjacentTiles.add(new TileNode(northWestTile, northWestLogicalPoint, northWestPixelPoint));
    }

    // Get the tile to the south east of the current position.
    Point southEastLogicalPoint = new Point(tile.logicalPoint);
    southEastLogicalPoint.translate(1, 0);

    Point southEastPixelPoint = new Point(tile.pixelPoint);
    southEastPixelPoint.translate(horizDistanceBtwnTiles, vertDistanceBtwnTiles / 2);

    Tile southEastTile = map.getTileAt(southEastLogicalPoint);
    if (southEastTile != null) {
      adjacentTiles.add(new TileNode(southEastTile, southEastLogicalPoint, southEastPixelPoint));
    }

    // Get the tile to the north east of the current position.
    Point northEastLogicaPoint = new Point(tile.logicalPoint);
    northEastLogicaPoint.translate(1, -1);

    Point northEastPixelPoint = new Point(tile.pixelPoint);
    northEastPixelPoint.translate(horizDistanceBtwnTiles, -vertDistanceBtwnTiles / 2);

    Tile northEastTile = map.getTileAt(northEastLogicaPoint);
    if (northEastTile != null) {
      adjacentTiles.add(new TileNode(northEastTile, northEastLogicaPoint, northEastPixelPoint));
    }

    // Get the tile to the south west of the current position.
    Point southWestLogicalPoint = new Point(tile.logicalPoint);
    southWestLogicalPoint.translate(-1, 1);

    Point southWestPixelPoint = new Point(tile.pixelPoint);
    southWestPixelPoint.translate(-horizDistanceBtwnTiles, vertDistanceBtwnTiles / 2);

    Tile southWestTile = map.getTileAt(southWestLogicalPoint);
    if (southWestTile != null) {
      adjacentTiles.add(new TileNode(southWestTile, southWestLogicalPoint, southWestPixelPoint));
    }

    return adjacentTiles;
  }
  // This will traverse through all the tiles using a breadth first search. It will then render that
  // tile.
  private void breadthFirstRender(Point logicalPoint, Point pixelPoint, Graphics2D g) {

    // Get the radius of visibliity.
    int radiusOfVisibility = avatar.getRadiusOfVisiblility();

    // This hashmap will keep track of what we have already renderred in our traversal.
    HashMap<Point, Boolean> hasBeenRendered = new HashMap<>();

    // Create an empty queue of tile nodes.
    Queue<TileNode> tileQueue = new LinkedList<>();

    // Convert the first tile into a TileNode and push it into the queue.
    TileNode root = new TileNode(map.getTileAt(logicalPoint), logicalPoint, pixelPoint);
    root.distanceFromAvatar = 0;

    // Offset the root based upon the offset ammound
    root.pixelPoint.translate((int) viewportOffset.getX(), (int) viewportOffset.getY());
    tileQueue.offer(root); // offer is analogous to push (or enqueue).

    while (!tileQueue.isEmpty()) {

      // Pop the current tile off the queue.
      TileNode currentTileNode = tileQueue.poll(); // poll is analogous to pop (or dequeue).

      // Check to see if the tile has already been renderred.
      if ((hasBeenRendered.get(currentTileNode.logicalPoint) == null)
          || !hasBeenRendered.get(currentTileNode.logicalPoint)
              && isInRangeOfViewport(currentTileNode.pixelPoint)) {
        hasBeenRendered.put(
            currentTileNode.logicalPoint, true); // Mark the tile as having been renderred.

        // Render the current Tile
        if (!displayDebugInformation) {
          if (currentTileNode.distanceFromAvatar < radiusOfVisibility) {

            // Mark this tile as having been seen.
            seenTiles.put(new Point(currentTileNode.logicalPoint), new Tile(currentTileNode.tile));

            // Set the opacity based on the distance from the avatar.
            float opacity =
                1.0f
                    - (1 - MIN_OPACITY)
                        * (currentTileNode.distanceFromAvatar / (float) radiusOfVisibility);
            opacity = opacity < MIN_OPACITY ? MIN_OPACITY : opacity;

            renderTile(currentTileNode, g, opacity); // Render the tile.
          } else if (seenTiles.get(currentTileNode.logicalPoint) != null) {
            currentTileNode.tile =
                seenTiles.get(
                    currentTileNode.logicalPoint); // Switch out the actual tile with the seen tile.
            renderTile(currentTileNode, g, SEEN_OPACITY);
          }
        } else {

          // Yes, Austin put this in. You can tell by the spacing.
          renderTile(currentTileNode, g, 1f);
        }

        // Push all the adjacent nodes onto the queue
        for (TileNode tileNode : getAdjacentTiles(currentTileNode)) {
          tileNode.distanceFromAvatar = currentTileNode.distanceFromAvatar + 1;
          tileQueue.offer(tileNode);
        }
      }
    }
  }