/// <summary>
    /// Find random integer in list with integer-specific probability specified by <paramref name="weightsArray"/>.
    /// </summary>
    /// <returns>Random integer from list.</returns>
    /// <param name="intArray">Array of integers to get random integer from.</param>
    /// <param name="weightsArray">Weights specifying integer-specific probability (can be bigger than 1) </param>
    public static int GetRandomIntWeightedProbability(int[] intArray, float[] weightsArray, ConsistentRandom randGen)
    {
        // Find random integer using integer-specific weights

        // check inputs
        if (intArray.Length != weightsArray.Length)
        {
            throw new System.ArgumentException("Input arrays should be of equal length.");
        }

        // Rescale weights
        float sum = 0;

        for (int i = 0; i < weightsArray.Length; i++)
        {
            sum = sum + weightsArray[i];
        }
        for (int i = 0; i < weightsArray.Length; i++)
        {
            weightsArray[i] = weightsArray[i] / sum;
        }
        if (Mathf.Approximately(sum, 0))
        {
            throw new System.ArgumentException("Sum of weights cannot be zero.");
        }

        // Find index using above probabilities
        float p   = (float)randGen.NextDouble();
        int   ind = -1;

        for (int i = 0; i < weightsArray.Length; i++)
        {
            p = p - weightsArray[i];
            if (p <= 0)
            {
                ind = i;
                break;
            }
        }
        if (ind == -1)
        {
            throw new System.Exception("Weighted probability selection failed.");
        }
        return(ind);

        //// DEBUG
        //string str = "";
        //for (int i = 0; i < weightsArray.Length; i++) { str = str + "-" + weightsArray[i]; }
        //Debug.Log(str);
        //// DEBUG
    }
    /// <summary>
    /// Build connections inside maze frame using hunt and kill algorithm.
    /// </summary>
    /// <param name="mazeFrame">Maze frame object.</param>
    /// <param name="huntAfterSegmentLength">Enter hunting mode after segment has grown to this length in world units,
    /// defaults to unlimited (uses smalles Scale value.</param>
    /// <param name="huntProbability">Probability of starting hunt when it arrives, defaults to 0.50.</param>
    protected void BuildMazeFrameUsingHuntAndKill(ref MazeFrame mazeFrame, float huntAfterSegmentLength = float.PositiveInfinity, float huntProbability = 0.50f)
    {
        if (mazeRandGen == null)
        {
            mazeRandGen = new ConsistentRandom();
        }
        if (randGen == null)
        {
            randGen = new ConsistentRandom();
        }

        // Build maze using hunt and kill

        /* 1) Make the initial node the current node and mark it as visited
         * 2) While there are unvisited nodes
         *      1) If the current node has any neighbours which have not been visited
         *          1) Choose randomly one of the unvisited neighbours
         *          2) Remove the wall between the current node and the chosen node
         *          3) Make the chosen node the current node and mark it as visited and add to targets
         *      2) Find random unvisited node next to visited node
         *          1) Make it the current node and mark it as visited and add to targets
         */

        // Check start/end node active status
        if (!mazeFrame.StartNode.IsActive || !mazeFrame.EndNode.IsActive)
        {
            throw new System.Exception("Start/end nodes are required to be active.");
        }

        // Set storage of visit flags and create list for hunt to target
        int nNodesVisited = 0;
        Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count);

        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.IsActive)
            {
                isVisited.Add(nd.Identifier, false);
            }
            else
            {
                isVisited.Add(nd.Identifier, true); nNodesVisited++;
            }
        }
        List <MazeNode> targets = new List <MazeNode>(Mathf.RoundToInt(mazeFrame.Nodes.Count / 2f)); // half of full maze should be more than enough

        // Ignore nodes without neighbors
        foreach (MazeNode nd in mazeFrame.Nodes)
        {
            if (nd.AllNeighbors.Count == 0)
            {
                isVisited[nd.Identifier] = true; nNodesVisited++;
            }
        }

        // Determine huntAfterNConnections
        int huntAfterNConnections = Mathf.RoundToInt(huntAfterSegmentLength / Scale.ComponentMin());

        //Initialize near startNode
        MazeNode currentNode = mazeFrame.StartNode;

        isVisited[currentNode.Identifier] = true;
        nNodesVisited++;
        targets.Add(currentNode);
        int count            = 0;
        int nConnectionsMade = 0;

        while (nNodesVisited < mazeFrame.Nodes.Count)
        {
            // See if there are unvisited neighbors
            List <MazeNode> unvisitedNeighbors = new List <MazeNode>();
            foreach (MazeNode nb in currentNode.AllNeighbors)
            {
                if (!isVisited[nb.Identifier])
                {
                    unvisitedNeighbors.Add(nb);
                }
            }

            if (nConnectionsMade >= huntAfterNConnections)
            {
                if (mazeRandGen.NextDouble() < huntProbability)
                {
                    nConnectionsMade = 0;
                }
            }
            if (unvisitedNeighbors.Count > 0 && nConnectionsMade < huntAfterNConnections)
            {
                // Select random neighbor (2)
                MazeNode selNode = unvisitedNeighbors[mazeRandGen.Next(unvisitedNeighbors.Count)];
                // Add as connected neighbor to current node object(3) and vice versa
                currentNode.AddConnectionByReference(selNode);
                // Switch to new node(4) and record visit
                currentNode = selNode;
                isVisited[currentNode.Identifier] = true;
                nNodesVisited++;
                targets.Add(currentNode);
                nConnectionsMade++;
            }
            else
            {
                // Find random unvisited node neighboring a visited node
                List <int> targetInd = new List <int>(targets.Count);
                for (int i = 0; i < targets.Count; i++)
                {
                    targetInd.Insert(i, i);
                }
                bool             found           = false;
                Stack <MazeNode> targetsToRemove = new Stack <MazeNode>();
                int huntCount = 0;
                while (!found)
                {
                    // sample a random target node
                    int      randInd    = targetInd[mazeRandGen.Next(targetInd.Count)];
                    MazeNode currTarget = targets[randInd];
                    // See if target has unvisited neighbors
                    unvisitedNeighbors = new List <MazeNode>();
                    foreach (MazeNode nb in currTarget.AllNeighbors)
                    {
                        if (!isVisited[nb.Identifier])
                        {
                            unvisitedNeighbors.Add(nb);
                        }
                    }
                    // if it has no unvisited neighors, remove target from list and go again, otherwise, pick random unvisited neighbor
                    if (unvisitedNeighbors.Count == 0)
                    {
                        targetInd.Remove(randInd);
                        targetsToRemove.Push(currTarget);
                    }
                    else
                    {
                        // Select random neighbor and set as currentNode
                        currentNode = currTarget;
                        found       = true;
                    }
                    // Reset connections counter
                    nConnectionsMade = 0;

                    // Safety check
                    huntCount++;
                    if (huntCount > targets.Count)
                    {
                        throw new System.Exception("Hunt did not terminate correctly.");
                    }
                }
                // Remove nodes from targets that had no unvisisted neigbors
                while (targetsToRemove.Count > 0)
                {
                    targets.Remove(targetsToRemove.Pop());
                }
            }

            // Safety check
            count++;
            if (targets.Count == 0 || count > Mathf.Pow(mazeFrame.Nodes.Count, 2))
            {
                throw new System.Exception("Hunt and kill loop did not terminate correctly.");
            }
        }

        // Sanity check
        foreach (MazeNode nb in mazeFrame.Nodes)
        {
            if (!isVisited[nb.Identifier])
            {
                throw new System.Exception("Hunt and kill loop did not terminate correctly.");
            }
        }
    }