Recursive function that allows me to do stuff in the meantime

Hello,

I need the player to be able to use an hint if he wants to solve some puzzle game and then I figured out that to explore the possibilities he have and to find a solution if it exist I have to do that trough a recursive function like this one:

bool MyRecursiveFunction()
{
       // some tests and adds to a List of possibilities
     if(MyRecursiveFunction())
          //do stuff
     else
          //other stuff
}

I did that already to found a solution of my puzzles when I need to test them but now I need that to be available for players I wonder how the same thing can occurs without freezing the engine. Then I could put some animation while it is searching and put a cancel button. I thought I could use some coroutine to do that but since my function needs to return a bool i have no clue of what I should do.

Any help please?

One possibility would be to run it on a separate thread (pretty nice tutorial here ), and have that trigger a callback on the main thread when done. That would probably be the best if you don’t rely heavily on some aspects of the UnityEngine namespace like physics, but this can be a bit tricky, and if it does need access to things like raycasting, then it’s not an option.

An alternative would be coroutines like you said:

//kind of hacky, but since we can't pass in 'out' or 'ref' variables into a coroutine, oh well
//another possibility would be using callbacks/delegates, but I didn't feel like getting into that
public bool recursiveResult;

public void StartSearch ()
{
    //start the waiting animation loop
    //animation.Play();

    StartCoroutine(RecursiveFunctionWait());
}

public void FinishSearch ()
{
    //stop the waiting animation loop
    //animation.Stop();

    if(recursiveResult)
    {
        //full recursion returned true, probably found the solution or whatever
    }
    else
    {
        //no solution, or whatever returning false meant before
    }
}

private IEnumerator RecursiveFunctionWait ()
{
    //basically the same as using yield return StartCoroutine(), but I get the feeling that it's more performance friendly (my coworkers always use this way)
    var routine = MyRecursiveFunction();
    //basically yields whenever the one of the recursion levels does until it's done and would have returned a value if called normally
    while (routine.MoveNext())
        yield return null;

    FinishSearch();
}

private IEnumerator MyRecursiveFunction ()
{
    // some tests and adds to a List of possibilities

    //starts the next recursion level
    var routine = MyRecursiveFunction();
    while (routine.MoveNext())
        yield return null;

    //somewhere in these you'll want to add the "yield return null;" lines
    //probably after about 10 milliseconds have passed since the last yield (around there gives good trade off between responsiveness and speed)
    //System.DateTime is usually what I use for this, ex: if(System.DateTime.Now.TimeOfDay.TotalMilliseconds > lastYield + delay)...
    if (recursiveResult)
    {
        //do stuff
    }
    else
    {
        //other stuff
    }
}

It would kind of nice to see the actually recursive function for reference though. Anyways, I hope this helps, and good luck!

Not sure why a recursive is needed. Nevertheless you can use Update or a Coroutine. There’s not much difference to the user.

void Update() 
{
    if (MyRecursiveFunction()) {
        // do stuff
    }
}

IEnumerator MyCoroutine()
{
    if (MyRecursiveFunction()) {
        // do stuff
    }
    yield return new WaitForEndOfFrame();
}

Since I thought it was not possible to use yield in a recursive form I managed after some thinking to do my exploration of possibilities without recursivity like this:

IEnumerator HintCoroutine()
	{
		remainingSquares = new List<int>();
		idListCopy = new List<int>();
		possibleMovesForHints = new List<List<int>>();
		foreach(int sq in idList)
		{
			idListCopy.Add(sq);
		}
		nbMovesForHints=squaresButtonsID.Count-idListCopy.Count;
		for(int i=0; i<=nbMovesForHints; i++)
		{
			possibleMovesForHints.Add(new List<int>());
		}
		bool loop = true;
		int it=0;
		List<int>iterators = new List<int>();
		for(int i=0; i<=nbMovesForHints;i++)
		{
			iterators.Add(0);
		}
		while(loop)
		{
			GetPossibleMovesForHints(it);
			if(possibleMovesForHints[it].Count==0)
			{
				if(idListCopy.Count==squaresButtonsID.Count)
				{
					loop=false;
					StartCoroutine(BlinkSquare(possibleMovesForHints[0][iterators[0]]));
				}
				else
				{
					if(idListCopy.Count==idList.Count)
					{
						loop = false;
						NoSolution();
					}
					else
					{
						for(int i=1; i<=it+1; i++)
						{
							if(it-i<0)
							{
								NoSolution();
								loop = false;
								break;
							}
							else if(iterators[it-i]!=possibleMovesForHints[it-i].Count-1)
							{
								iterators[it-i]++;
								it=it-i;
								for(int j=1; j<=i; j++)
								{
									idListCopy.RemoveAt(idListCopy.Count-1);
								}
								break;
							}
						}
						if(iterators[0]==possibleMovesForHints[0].Count-1 && it==0)
						{
							NoSolution();
							loop = false;
						}
					}
				}
			}
			else if(iterators[it]<possibleMovesForHints[it].Count)
			{
				idListCopy.Add(possibleMovesForHints[it][iterators[it]]);
				it++;
				iterators[it]=0;
			}
			yield return null;
		}
	}