How to Build a Tic Tac Toe Game with Both Offline and Online Mode in Android? (original) (raw)

Last Updated : 23 Jul, 2025

In this article, we are going to make a tic-tac-toe game that has both online and offline modes. So for this project, we are going to use **Kotlin and **XML. **Tic-Tac-Toe is a two-player game. Each player has **X or **O. Both the player plays one by one simultaneously. In one move, players need to select one position in the 3x3 grid and put their mark at that place. The game runs continuously until one may wins.

In the previous article, we have built a simple Tic Tac Toe Game in Android but in this article, we have the following additional features inside the app:

Basic Terminologies

Step by Step Implementation: Offline Mode

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio.

**Note: that select Kotlin as the programming language.

Step 2: Add View Binding

Navigate **Gradle Scripts > build.gradle.kts (Module :app) and the add the following code anywhere under the android tag.

android {
...
buildFeatures {
viewBinding = true
}
...
}

Step 3: Working with the MainActivity file and it's layout

Navigate to **MainActivity.kt and **activity_main.xml and make the following changes.

MainActivity.kt `

package org.geeksforgeeks.tictactoe

import android.content.Intent import android.os.Bundle import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import org.geeksforgeeks.tictactoe.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

private val binding : ActivityMainBinding by lazy {
    ActivityMainBinding.inflate(layoutInflater)
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContentView(binding.root)
    ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
        val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
        v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
        insets
    }

    binding.playOfflineBtn.setOnClickListener {
        createOfflineGame()
    }
}

private fun createOfflineGame(){
    GameData.saveGameModel(
        GameModel(
            gameStatus = GameStatus.JOINED
        )
    )
    startGame()
}

private fun startGame(){
    startActivity(Intent(this,GameActivity::class.java))
}

}

activity\_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TicTacToe"
    android:textSize="32sp"
    android:textColor="@color/black"
    android:textStyle="bold"
    app:layout_constraintBottom_toTopOf="@+id/play_offline_btn"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_chainStyle="packed" />

<Button
    android:id="@+id/play_offline_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="32dp"
    android:text="Play Offline"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

`

**Design UI:

tic-tac-toe-main

Step 4: Create a new Game Model and Object

Navigate **app > kotlin+java > {package-name} right click on it and create two new Koltin class files with the names **GameModel.kt and **GameData.kt. Below is the code for both the files.

GameData.kt `

package org.geeksforgeeks.tictactoe

import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData

object GameData { private var _gameModel : MutableLiveData = MutableLiveData() var gameModel : LiveData = _gameModel

fun saveGameModel(model : GameModel){
    _gameModel.postValue(model)
}

}

GameModel.kt

package org.geeksforgeeks.tictactoe

import kotlin.random.Random

data class GameModel ( var gameId : String = "-1", var filledPos : MutableList = mutableListOf("","","","","","","","",""), var winner : String ="", var gameStatus : GameStatus = GameStatus.CREATED, var currentPlayer : String = (arrayOf("X","O"))[Random.nextInt(2)] )

enum class GameStatus{ CREATED, JOINED, INPROGRESS, FINISHED }

`

Step 5: Create a new activity for Offline Mode

Navigate **app > kotlin+java > {package-name} right click on it and select **New > Activity > Empty Views Activity and save the name as **GameActivity.kt. Now, navigate to the kotlin and xml file of the activity and make the following changes.

GameActivity.kt `

package org.geeksforgeeks.tictactoe

import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import org.geeksforgeeks.tictactoe.databinding.ActivityGameBinding

class GameActivity : AppCompatActivity(), View.OnClickListener {

private val binding: ActivityGameBinding by lazy {
    ActivityGameBinding.inflate(layoutInflater)
}
private var gameModel : GameModel? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContentView(binding.root)
    ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
        val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
        v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
        insets
    }

    binding.btn0.setOnClickListener(this)
    binding.btn1.setOnClickListener(this)
    binding.btn2.setOnClickListener(this)
    binding.btn3.setOnClickListener(this)
    binding.btn4.setOnClickListener(this)
    binding.btn5.setOnClickListener(this)
    binding.btn6.setOnClickListener(this)
    binding.btn7.setOnClickListener(this)
    binding.btn8.setOnClickListener(this)

    binding.startGameBtn.setOnClickListener {
        startGame()
    }

    GameData.gameModel.observe(this){
        gameModel = it
        setUI()
    }
}

private fun setUI(){
    gameModel?.apply {
        binding.btn0.text = filledPos[0]
        binding.btn1.text = filledPos[1]
        binding.btn2.text = filledPos[2]
        binding.btn3.text = filledPos[3]
        binding.btn4.text = filledPos[4]
        binding.btn5.text = filledPos[5]
        binding.btn6.text = filledPos[6]
        binding.btn7.text = filledPos[7]
        binding.btn8.text = filledPos[8]

        binding.startGameBtn.visibility = View.VISIBLE

        binding.gameStatusText.text =
            when(gameStatus){
                GameStatus.CREATED -> {
                    binding.startGameBtn.visibility = View.INVISIBLE
                    "Game ID :"+ gameId
                }
                GameStatus.JOINED ->{
                    "Click on start game"
                }
                GameStatus.INPROGRESS ->{
                    binding.startGameBtn.visibility = View.INVISIBLE
                    currentPlayer + " turn"
                }
                GameStatus.FINISHED ->{
                    if(winner.isNotEmpty()) winner + " Won"
                    else "DRAW"
                }
            }

    }
}


private fun startGame(){
    gameModel?.apply {
        updateGameData(
            GameModel(
                gameId = gameId,
                gameStatus = GameStatus.INPROGRESS
            )
        )
    }
}

private fun updateGameData(model : GameModel){
    GameData.saveGameModel(model)
}

private fun checkForWinner(){
    val winningPos = arrayOf(
        intArrayOf(0,1,2),
        intArrayOf(3,4,5),
        intArrayOf(6,7,8),
        intArrayOf(0,3,6),
        intArrayOf(1,4,7),
        intArrayOf(2,5,8),
        intArrayOf(0,4,8),
        intArrayOf(2,4,6),
    )

    gameModel?.apply {
        for ( i in winningPos){
            //012
            if(
                filledPos[i[0]] == filledPos[i[1]] &&
                filledPos[i[1]]== filledPos[i[2]] &&
                filledPos[i[0]].isNotEmpty()
            ){
                gameStatus = GameStatus.FINISHED
                winner = filledPos[i[0]]
            }
        }

        if( filledPos.none(){ it.isEmpty() }){
            gameStatus = GameStatus.FINISHED
        }


        updateGameData(this)

    }
}

override fun onClick(v: View?) {
    gameModel?.apply {
        if(gameStatus!= GameStatus.INPROGRESS){
            Toast.makeText(applicationContext,"Game not started", Toast.LENGTH_SHORT).show()
            return
        }
        //game is in progress
        val clickedPos =(v?.tag  as String).toInt()
        if(filledPos[clickedPos].isEmpty()){
            filledPos[clickedPos] = currentPlayer
            currentPlayer = if(currentPlayer=="X") "O" else "X"
            checkForWinner()
            updateGameData(this)
        }
    }
}

}

activity\_game.xml

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/game_status_text"
    android:gravity="center"
    android:textSize="16sp"
    android:layout_margin="10dp"
    android:textStyle="bold"
    android:text="Game not started"/>


<GridLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:columnCount="3">

    <Button
        android:id="@+id/btn_0"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="0"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="1"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="2"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="3"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="4"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="5"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_6"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="6"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_7"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="7"
        android:textSize="60sp"
        tools:text="X" />

    <Button
        android:id="@+id/btn_8"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:background="@color/black"
        android:tag="8"
        android:textSize="60sp"
        tools:text="X" />
</GridLayout>

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_margin="20dp"
    android:text="Start game"
    android:id="@+id/start_game_btn"/>

`

**Design UI:

tic-tac-toe-game-layout

Output:

Let's try to play in offline-mode:

Online Mode

Step 1: Integrate Firebase and add Firestore support

Here we are going to use firebase for our backend functionality. So what we are doing here is listening on a particular codes database and if got any change in the database, then running an event on the client-side to update the move made by the opponent.

To add firebase and firestore to your application, refer to How to Use Firebase Firestore as a Realtime Database in Android?

Step 2: Working with MainActivity and it's layout file

Navigate to **MainActivity.kt and **activity_main.xml and make the following changes.

MainActivity.kt `

package org.geeksforgeeks.tictactoe

import android.content.Intent import android.os.Bundle import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.google.firebase.Firebase import com.google.firebase.firestore.firestore import org.geeksforgeeks.tictactoe.databinding.ActivityMainBinding import kotlin.random.Random import kotlin.random.nextInt

class MainActivity : AppCompatActivity() {

private val binding : ActivityMainBinding by lazy {
    ActivityMainBinding.inflate(layoutInflater)
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContentView(binding.root)
    ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
        val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
        v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
        insets
    }

    binding.playOfflineBtn.setOnClickListener {
        createOfflineGame()
    }

    binding.createOnlineGameBtn.setOnClickListener {
        createOnlineGame()
    }

    binding.joinOnlineGameBtn.setOnClickListener {
        joinOnlineGame()
    }
}

private fun createOfflineGame(){
    GameData.saveGameModel(
        GameModel(
            gameStatus = GameStatus.JOINED
        )
    )
    startGame()
}

private fun createOnlineGame(){
    GameData.myID = "X"
    GameData.saveGameModel(
        GameModel(
            gameStatus = GameStatus.CREATED,
            gameId = Random.nextInt(1000..9999).toString()
        )
    )
    startGame()
}

private fun joinOnlineGame(){
    val gameId = binding.gameIdInput.text.toString()
    if(gameId.isEmpty()){
        binding.gameIdInput.error = "Please enter game ID"
        return
    }
    GameData.myID = "O"
    Firebase.firestore.collection("games")
        .document(gameId)
        .get()
        .addOnSuccessListener {
            val model = it?.toObject(GameModel::class.java)
            if(model==null){
                binding.gameIdInput.error = "Please enter valid game ID"
            }else{
                model.gameStatus = GameStatus.JOINED
                GameData.saveGameModel(model)
                startGame()
            }
        }

}

private fun startGame(){
    startActivity(Intent(this,GameActivity::class.java))
}

}

activity\_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TicTacToe"
    android:textColor="@color/black"
    android:textSize="32sp"
    android:textStyle="bold"
    app:layout_constraintBottom_toTopOf="@+id/play_offline_btn"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_chainStyle="packed" />

<Button
    android:id="@+id/play_offline_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:layout_marginBottom="16dp"
    android:text="Play Offline"
    app:layout_constraintBottom_toTopOf="@+id/textView2"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

<TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Or"
    android:textSize="20sp"
    android:textStyle="bold"
    app:layout_constraintBottom_toTopOf="@+id/create_online_game_btn"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/play_offline_btn" />

<Button
    android:id="@+id/create_online_game_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:layout_marginBottom="16dp"
    android:text="Create Game Online"
    app:layout_constraintBottom_toTopOf="@+id/textView3"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView2" />

<TextView
    android:id="@+id/textView3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Or"
    android:textSize="20sp"
    android:textStyle="bold"
    app:layout_constraintBottom_toTopOf="@+id/game_id_input"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/create_online_game_btn" />

<EditText
    android:id="@+id/game_id_input"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="16dp"
    android:layout_marginBottom="8dp"
    android:hint="Enter game Id"
    android:inputType="number"
    android:textSize="24sp"
    android:textStyle="bold"
    app:layout_constraintBottom_toTopOf="@+id/join_online_game_btn"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView3" />

<Button
    android:id="@+id/join_online_game_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Join Game Online"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/game_id_input" />

</androidx.constraintlayout.widget.ConstraintLayout>

`

**Design UI:

tic-tac-toe-main-layout-online

Step 2: Make changes in GameData.kt

Navigate to GameData.kt and make the following changes

**GameData.kt:

Kotlin `

package org.geeksforgeeks.tictactoe

import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.google.firebase.firestore.ktx.firestore import com.google.firebase.ktx.Firebase

object GameData { private var _gameModel : MutableLiveData = MutableLiveData() var gameModel : LiveData = _gameModel var myID = ""

fun saveGameModel(model : GameModel){
    _gameModel.postValue(model)
    if(model.gameId!="-1"){
        Firebase.firestore.collection("games")
            .document(model.gameId)
            .set(model)
    }

}

fun fetchGameModel(){
    gameModel.value?.apply {
        if(gameId!="-1"){
            Firebase.firestore.collection("games")
                .document(gameId)
                .addSnapshotListener { value, error ->
                    val model = value?.toObject(GameModel::class.java)
                    _gameModel.postValue(model)
                }
        }
    }
}

}

`

Step 3: Make changes in GameActivity.kt

Navigate to **GameActivity.kt and make the following changes

**GameActivity.kt:

Kotlin `

package org.geeksforgeeks.tictactoe

import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import org.geeksforgeeks.tictactoe.databinding.ActivityGameBinding

class GameActivity : AppCompatActivity(), View.OnClickListener {

private val binding: ActivityGameBinding by lazy {
    ActivityGameBinding.inflate(layoutInflater)
}
private var gameModel : GameModel? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContentView(binding.root)
    ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
        val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
        v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
        insets
    }

    GameData.fetchGameModel()

    binding.btn0.setOnClickListener(this)
    binding.btn1.setOnClickListener(this)
    binding.btn2.setOnClickListener(this)
    binding.btn3.setOnClickListener(this)
    binding.btn4.setOnClickListener(this)
    binding.btn5.setOnClickListener(this)
    binding.btn6.setOnClickListener(this)
    binding.btn7.setOnClickListener(this)
    binding.btn8.setOnClickListener(this)

    binding.startGameBtn.setOnClickListener {
        startGame()
    }

    GameData.gameModel.observe(this){
        gameModel = it
        setUI()
    }
}

private fun setUI(){
    gameModel?.apply {
        binding.btn0.text = filledPos[0]
        binding.btn1.text = filledPos[1]
        binding.btn2.text = filledPos[2]
        binding.btn3.text = filledPos[3]
        binding.btn4.text = filledPos[4]
        binding.btn5.text = filledPos[5]
        binding.btn6.text = filledPos[6]
        binding.btn7.text = filledPos[7]
        binding.btn8.text = filledPos[8]

        binding.startGameBtn.visibility = View.VISIBLE

        binding.gameStatusText.text =
            when(gameStatus){
                GameStatus.CREATED -> {
                    binding.startGameBtn.visibility = View.INVISIBLE
                    "Game ID :"+ gameId
                }
                GameStatus.JOINED ->{
                    "Click on start game"
                }
                GameStatus.INPROGRESS ->{
                    binding.startGameBtn.visibility = View.INVISIBLE
                    when(GameData.myID){
                        currentPlayer -> "Your turn"
                        else ->  currentPlayer + " turn"
                    }

                }
                GameStatus.FINISHED ->{
                    if(winner.isNotEmpty()) {
                        when(GameData.myID){
                            winner -> "You won"
                            else ->   winner + " Won"
                        }

                    }
                    else "DRAW"
                }
            }

    }
}


private fun startGame(){
    gameModel?.apply {
        updateGameData(
            GameModel(
                gameId = gameId,
                gameStatus = GameStatus.INPROGRESS
            )
        )
    }
}

private fun updateGameData(model : GameModel){
    GameData.saveGameModel(model)
}

private fun checkForWinner(){
    val winningPos = arrayOf(
        intArrayOf(0,1,2),
        intArrayOf(3,4,5),
        intArrayOf(6,7,8),
        intArrayOf(0,3,6),
        intArrayOf(1,4,7),
        intArrayOf(2,5,8),
        intArrayOf(0,4,8),
        intArrayOf(2,4,6),
    )

    gameModel?.apply {
        for ( i in winningPos){
            //012
            if(
                filledPos[i[0]] == filledPos[i[1]] &&
                filledPos[i[1]]== filledPos[i[2]] &&
                filledPos[i[0]].isNotEmpty()
            ){
                gameStatus = GameStatus.FINISHED
                winner = filledPos[i[0]]
            }
        }

        if( filledPos.none(){ it.isEmpty() }){
            gameStatus = GameStatus.FINISHED
        }


        updateGameData(this)

    }
}

override fun onClick(v: View?) {
    gameModel?.apply {
        if(gameStatus!= GameStatus.INPROGRESS){
            Toast.makeText(applicationContext,"Game not started", Toast.LENGTH_SHORT).show()
            return
        }

        if(gameId!="-1" && currentPlayer!=GameData.myID ){
            Toast.makeText(applicationContext,"Not your turn",Toast.LENGTH_SHORT).show()
            return
        }

        val clickedPos =(v?.tag  as String).toInt()
        if(filledPos[clickedPos].isEmpty()){
            filledPos[clickedPos] = currentPlayer
            currentPlayer = if(currentPlayer=="X") "O" else "X"
            checkForWinner()
            updateGameData(this)
        }
    }
}

}

`

Output:

Let's try to play in offline-mode: