• Post author:
  • Post category:Android
  • Reading time:9 mins read

Welcome to part-4 of this Tic Tac Toe Android Kotlin beginner tutorial series where we will be making a fun Tic Tac Toe game app. To go to part 1 of this series, click here.

In the previous tutorial, we added onClickListeners to each of the buttons but we just made a little Toast message appear on click telling us what button was clicked. Now it is time we finally turn all that boring clicking into the Tic Tac Toe game that was promised. Here’s what we will have once we are done with this part.

Video Tutorial

If you would rather watch a video then read all the way, you can check out this. For this article, please watch it from 4:15.

part 3 + part 4 youtube video

The video is a quick walkthrough. For detailed tutorial, please consider reading this article. If you choose to watch the video and find it useful, please give it a thumbsup and subscribe to the channel. Thank you. Lets continue.

Modify GameBoard buttons onClickListener

Previously, in our initButton() function which initializes the gameboard buttons, we were just displaying a toast message when clicked with the code below.

Toast.makeText(
    applicationContext, 
    "button $r$c clicked", 
    Toast.LENGTH_SHORT).show()

Change the above code to the code below.

onBtnClick(btn)

Now our initButton() function looks like this.

private fun initButton(r: Int, c: Int): Button {
        val btn: Button =
            findViewById(resources.getIdentifier("btn$r$c", "id", packageName))
        btn.setOnClickListener{
            onBtnClick(btn)
        }
        return btn
    }

Next, we define the onBtnClick() function. In this, we want to put “X” or “O” on a GameBoard Button only if it is empty i.e. hasn’t already been clicked. If we’ve already clicked it i.e. btn.text is not an empty string, we don’t want to do anything.

Then we’re incrementing the roundCount variable here. On each click, we’re checking if somebody has won with the checkForWin() function. If it returns true, then depending on which player’s turn it is, we can decide who has won and then can run the winning routine with win() function.

If the number of rounds is 9, that means all the 3×3 buttons have been clicked and there is no winner so we say it is a draw().

And finally, if nobody has won and it is not even a draw yet that means the game is still going and we just switch the player turn.

private fun onBtnClick(btn: Button) {
        if(btn.text != "") return
        if(player1Turn){
            btn.text = "X"
        }else{
            btn.text = "O"
        }
        roundCount++

        if(checkForWin()){
            if(player1Turn) win(1) else win(2)
        }else if(roundCount == 9){
            draw()
        }else{
            player1Turn = !player1Turn
        }
    }

The CheckForWin() Function

We get a win when all the buttons on any of the lines shown below have either all “X”es or all “O”s. The winning logic is mostly from the tutorial I was following on youtube from the channel CodingInFlow. You can check that out here.

The fields variable in the code below is a 3×3 array where we populate the array cell with the corresponding button text on the gameboard. Basically a one on one map of the “X”es and “O”s which is easier for us to work with. Then we check for win as per the image above.

private fun checkForWin(): Boolean {
        val fields = Array(3){r->
            Array(3){c->
                buttons[r][c].text
            }
        }

        for(i in 0..2){
            if((fields[i][0] == fields[i][1])&&
                (fields[i][0] == fields[i][2])&&
                (fields[i][0] != "")
            )return true
        }

        for(i in 0..2){
            if(
                (fields[0][i] == fields[1][i])&&
                (fields[0][i] == fields[2][i])&&
                (fields[0][i] != "")
            )return true
        }

        if(
            (fields[0][0] == fields[1][1])&&
            (fields[0][0] == fields[2][2])&&
            (fields[0][0] != "")
        ) return true

        if(
            (fields[0][2] == fields[1][1])&&
            (fields[0][2] == fields[2][0])&&
            (fields[0][2] != "")
        ) return true

        return false

    }

We Have A Winner

The win function takes an integer argument which tells it which player has won. As per that, we increment the winner’s points and then display a small winning Toast message.

Then we update the scores of each players with updateScore() function and clear up the gameboard with clearBoard() function.

private fun win(player:Int){
        if(player == 1) player1Points++ else player2Points++
        Toast.makeText(
            applicationContext, 
            "Player $player Won!",
            Toast.LENGTH_SHORT).show()
        updateScore()
        clearBoard()
    }

Oh It is a Draw!

We display a simple Toast message telling user that it is a draw and then clear up the gameboard with clearBoard() function.

private fun draw(){
        Toast.makeText(
            applicationContext, 
            "Match Draw!", 
            Toast.LENGTH_SHORT).show()
        clearBoard()
    }

Clear The Board And Update The Score

In the clearBoard() function, we set all the button texts to empty strings, reset rounds to 0 and set player1Turn to true.

private fun clearBoard() {
        for (i in 0..2){
            for(j in 0..2){
                buttons[i][j].text = ""
            }
        }
        roundCount = 0
        player1Turn = true
    }

In the updateScore() function, we just update the test in Player 1 and Player 2 TextViews with the updated scores.

private fun updateScore() {
        textViewPlayer1.text = "Player 1: $player1Points"
        textViewPlayer2.text = "Player 2: $player2Points"
    }

The Reset Game Button

Set all game variables to their default values.

btnReset.setOnClickListener{
            player1Points = 0
            player2Points = 0
            updateScore()
            clearBoard()
        }

In the next and final part of this Tic Tac Toe Android Kotlin tutorial series, we’ll turn this simple looking game into the beauty that i promised. Stay tuned.