0

I'm coding a tic tac toe app in which the user can play against the computer. In one of the difficulties, I've implemented an algorithm (minimax) to find the best move, and on the first move, the function which uses this algorithm to make the move seems to take a noticeable time to run. I want to make a 'thinking' image pop up on the screen while the computer 'thinks' (executes the function). I've tried displaying an image before and after the function runs, but this doesn't seem to work.

In my 'onClick' method which handles button pressing, this is the section of the code that is supposed to display the thinking image, make the move, and then make the thinking image invisible once the move is made:

    displayThink(true);

    //if game not over, computer makes move
    makeMove(difficulty);

    displayThink(false);

where displayThink is:

public void displayThink(boolean display)
{
    ImageView thinkingImage = (ImageView)findViewById(R.id.thinkingImage);


    if(display)
        thinkingImage.setVisibility(View.VISIBLE);
    else
        thinkingImage.setVisibility(View.INVISIBLE);

}

and makeMove(difficulty) is the function that is taking the noticeable time to run.

EDIT: This is the full context of the makeMove() function which is called in the onClick function, which is the designated onClick method used to handle button presses in the activity:

 public void onClick(View v)
   {
    //overwrite button
    Button b = (Button)v;
    b.setEnabled(false);
    b.setText(userString);

    //check result
    Pair<Boolean, String> p = checkEnd();
    Log.d("msg","checking end: end = " + p.first);

    if(p.first)
    {
        Log.d("msg","launching result with string " + p.second);
        launchResult(p.second);
    }

    displayThink(true);

    //if game not over, computer makes move
    makeMove(difficulty);

    displayThink(false);

    //check result again
    p = checkEnd();
    if(p.first)
    {
        Log.d("msg","launching result with string " + p.second);
        launchResult(p.second);
    }

}

And this is the imageView part of the xml file that deals with the image I want to display while the computer is 'thinking'.

 <ImageView
        android:id="@+id/thinkingImage"
        android:layout_width="196dp"
        android:layout_height="264dp"
        android:layout_marginTop="160dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/thinking" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • Is your makeMove() code running on UI thread? – skafle Jun 21 '21 at 19:15
  • Sorry, I'm not sure what that means. I looked up UI thread and I don't think so, but could you elaborate? – yusefwilson Jun 21 '21 at 19:35
  • That means, if your long running task is running in your main thread or UI thread, you won't be able to update the UI until the task finishes otherwise you will get a crash. So could you please show how your makeMove() function is called? Also your imageview xml. – skafle Jun 21 '21 at 19:50
  • Sure, I'll edit the question. – yusefwilson Jun 21 '21 at 20:28
  • I see you are calling everything in UI thread. That could be the reason you are not seeing your imageview. Now, you just follow the steps described in the answer by @javdromero – skafle Jun 22 '21 at 00:08

1 Answers1

2

You could use an AsyncTask

private class MakeMove extends AsyncTask<Void, Void, Void> {
        
        @Override
        protected void onPreExecute() {
            // Runs on UI thread
            displayThink(true);
        }

        @Override
        protected Void doInBackground(Void... params) {
            // This doesn't run on the UI thread
            makemove();
        }

        @Override
        protected void onPostExecute(Void args) {
           // Runs on UI thread
           displayThink(false);
        }
    }

EDIT:

This can be called like:

buttonX.setOnClickListener(new OnClickListener() {
    public void onClick(View v)
    {
        MakeMove makeMove = new MakeMove();
        makeMove.execute();
    } 
});

No arguments passed since they are optional.

javdromero
  • 1,850
  • 2
  • 11
  • 19
  • Thanks! How would I implement these functions in my onClick method? Would I make a MakeMove object and then call the functions on that object? Also, what do I pass as the arguments to these functions? – yusefwilson Jun 21 '21 at 20:27
  • @yusefwilson I edited it to add a implementation. – javdromero Jun 21 '21 at 20:35
  • Thanks for clarifying - how would this new private class have access to the functions makeMove and displayThink? Or would I just change my current activity class to extend AsyncTask as well? – yusefwilson Jun 21 '21 at 21:41
  • It's not necessary, just add that class in your activity as if it were another method. – javdromero Jun 21 '21 at 21:47
  • Thanks again - the doInBackground function needs to take an integer so I can give that information to the makeMove function which is being called inside of it, and I don't see how to do that with your implementation. Would I change one of the Voids to Integer? – yusefwilson Jun 21 '21 at 21:52
  • Yes, I updated the asynctask link, there you can find more information. – javdromero Jun 21 '21 at 21:57