1

I have created a simple tik-tac-toe game and have some bots you can play with. I am trying to have a delay between consecutive bot turns as otherwise all sequential bot moves appear on the screen at the same time

here is what I have tried in my code:

    // bot functionality
private fun botTurn(botDifficulty: Int) {
    var botMove = 100
    when (botDifficulty) {
        1 -> botMove = easyBot(confirmedMoves)
        2 -> botMove = mediumBot(activePlayer, confirmedMoves, maxPlayers)
        3 -> botMove = hardBot(activePlayer, confirmedMoves, maxPlayers)
        else -> Toast.makeText(this, "What sort of bot is this??", Toast.LENGTH_SHORT).show()
    }
    
    trueCellID = botMove
    // colour chosen segment
    setSegmentColor(trueCellID, -1, activePlayer)
    // TODO add 1-2 second delay after confirming move?
    Log.d("Wait", "start of wait $activePlayer")
    Thread.sleep(1000)
    Log.d("Wait", "end of wait $activePlayer")
    
    confirmMove()
}

using Thread.sleep seems to just delay all the UI updates until after all botPlayer sleeps have happened. I have also tried using Handler.postDelayed, GlobalScope.launch with a delay block and runOnUiThread with a SystemClock.sleep(1000)- these have the same problem of doing all the bot waiting, then the UI updating.

I even tried to adapt this solution a try - how to wait for Android runOnUiThread to be finished? but got the same result - big delay then all the UI updates.

Is there a fix for this or have I missed something simple?

Sebi
  • 8,323
  • 6
  • 48
  • 76
  • I know this is besides the point, but with an enum instead of `Int` for the bot level you wouldn't have to deal with the extra `when` branch (if it doesn't make sense in your code). Also, the `when` could be used as an expression instead: `val botMove = when(botDifficulty) {...}` – Joffrey May 25 '21 at 15:40
  • How are you calling `botTurn`? Do you call it sequentially for each bot? Or do you use coroutines to call them in parallel? – Joffrey May 25 '21 at 15:42
  • Also, what exactly do you want to happen? Do you want bots to play sequentially, one every second? What about this segment color part? – Joffrey May 25 '21 at 15:44
  • thanks, an enum would certainly be useful to make the code more readable! `confirmMove()` increments the `activePlayer` and then checks if the active player is a bot or human. if its a bot then the `botTurn` is called. – MechatronicsStudent May 25 '21 at 16:29
  • I want each bot to either setSegmentColor-wait-confirmMove or wait inbetween bots. At the moment the app waits then the UI shows botA & botB moves rather than botA-wait-botB – MechatronicsStudent May 25 '21 at 16:33
  • is there some sort of check I can do to make sure the segment has been coloured on screen before calling sleep & confirm move? – MechatronicsStudent May 25 '21 at 16:38
  • Wait, your description sounds like you perform subsequent turns by going deeper into a call stack. Do you mean that `confrimMove()` invokes `botTurn()` and vice versa? This isn't really a good idea ;-) It would be much cleaner if `botTurn()` would only perform a decision/move by a bot and another code would decide whether it is a bot or human turn, call `botTurn()` and wait between turns. – broot May 25 '21 at 19:29
  • And going back to your question: both `Handler.postDelayed()` and `GlobalScope.launch()` should do the trick, so I guess you did something wrong. For example, you could make `botTurn()` suspendable, invoke it inside `GlobalScope.laucnh()` and replace `Thread.sleep()` with `delay()` - it should work without blocking UI. – broot May 25 '21 at 19:36
  • Ok thanks, ill retry the `Handler.postDelayed()`. Nothing is blocking the UI - the bot turns are just too fast really, they come out instantly when its not a humans go. I have a `waitYourTurn()` function that checks whos turn it is and if they are a bot, if they are a bot then `botTurn()` - this should fill a segment (UI) then `confirmMove()` - this saves the move and increments the `activePlayer` and calls `waitYourTurn()` again. – MechatronicsStudent May 25 '21 at 19:50
  • Here is the github https://github.com/Kovah101/Tri-Taptical/blob/master/app/src/main/java/com/github/kovah101/tri_taptical/MainActivity.kt - if that helps – MechatronicsStudent May 25 '21 at 19:53
  • Code is pretty complicated, but I think I was right: you make turns in this pattern: `botTurn()` -> `confirmMove()` -> `checkForWinner()` -> `waitForBots()` -> `botTurn()`, so with each turn you go deeper and deeper into the call stack. I guess you don't have problems only because tic-tac-toe has a very limited number of turns. – broot May 25 '21 at 20:07
  • Regarding your problem: try again with your `postDelayed()` solution (commented out in the code), but this time put both `setSegmentColor()` and `confirmMove()` into a `postDelayed()` block. But this will totally change the code flow, so you may end up with crashes or other errors. – broot May 25 '21 at 20:07
  • Does this answer your question? [Repeat a task with a time delay?](https://stackoverflow.com/questions/6242268/repeat-a-task-with-a-time-delay) – Sebi May 25 '21 at 20:20
  • @broot - your suggestion of putting both functions in thr `postDelayed()` block has worked perfectly! thank you - no other crashes or errors either which is always nice! If you want to officially answer I will confirm it, otherwise ill put your answer down at the end of the day, thanks again – MechatronicsStudent May 26 '21 at 14:03

1 Answers1

0

As broot suggested in the comments, putting both setSegmentColor() and confirmMove() inside the postDelayed() block achieved the desired delay between botTurn()

val botDelay = 1500L

        Handler().postDelayed(
            {
                // colour chosen segment then save move
                setSegmentColor(botMove, -1, activePlayer)
                confirmMove(botMove)
            },
            botDelay
        )