Open In App

How to Build a Tic Tac Toe Game in Android?

Last Updated : 24 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In this article, we will be building a Tic Tac Toe Game Project using Java and XML in Android. The Tic Tac Toe Game is based on a two-player game. Each player chooses between X and O. The Player plays one move at a time simultaneously. In a move, a player can choose any position from a 3x3 grid. The goal here is to get three consecutive X or O in a horizontal, vertical, or diagonal direction. There will be a single activity in this application. This activity will show a 3x3 grid. The status of the game will be displayed at the bottom.

Step-by-Step Implementation

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 Java as the programming language.

Step 2: Before going to the coding section first you have to do some pre-task

Add Images: All the images are listed below. Save them in your drawable folder in resources. Go to the app > res > drawable and paste the following files:

Change the style to NoActionBar in the themes.xml file: 

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">

Add fonts to be used in the application: refer to How to Add Custom Fonts in Android.

Step 3: Working with the activity_main.xml and activity_game.xml file

The XML codes are used to build the structure of the activity as well as its styling part. It contains a TextView at the very top of the activity to display the title. Then it contains an ImageView of the grid and in each box, there is an ImageView. At the bottom of the activity, there is a TextView to display the status of the game.

Below is the code for the activity_main.xml file.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tile_page_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:fontFamily="@font/audiowide"
        android:text="@string/app_title"
        android:textSize="@dimen/title_size"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/perm_data_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="52dp"
        android:fontFamily="@font/audiowide"
        android:text="Players Score"
        android:textSize="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tile_page_1" />

    <TextView
        android:id="@+id/score_playerA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/audiowide"
        android:text="0"
        android:textSize="@dimen/player_size"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/perm_data_2"
        app:layout_constraintTop_toTopOf="@+id/perm_data_2" />

    <TextView
        android:id="@+id/perm_data_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="52dp"
        android:fontFamily="@font/audiowide"
        android:text="Player A : "
        android:textSize="@dimen/player_size"
        app:layout_constraintEnd_toStartOf="@+id/score_playerA"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/perm_data_1" />

    <TextView
        android:id="@+id/score_playerB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="28dp"
        android:fontFamily="@font/audiowide"
        android:text="0"
        android:textSize="@dimen/player_size"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/perm_data_3"
        app:layout_constraintTop_toBottomOf="@+id/score_playerA" />

    <TextView
        android:id="@+id/perm_data_3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="28dp"
        android:fontFamily="@font/audiowide"
        android:text="Player B : "
        android:textSize="@dimen/player_size"
        app:layout_constraintEnd_toStartOf="@+id/score_playerB"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/perm_data_2" />

    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="51dp"
        android:fontFamily="@font/audiowide"
        android:background="@color/green"
        android:text="Start Game"
        android:textSize="@dimen/button_size"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/perm_data_3"
        app:layout_constraintVertical_bias="0.081" />

</androidx.constraintlayout.widget.ConstraintLayout>

Output UI:


Below is the Layout for Game Activity:

activity_game.xml
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity_game">

    <!--title text-->
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="23dp"
        android:text="GFG Tic Tac Toe"
        android:textSize="@dimen/title_size"
        android:textStyle="bold"
        app:fontFamily="@font/audiowide"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!--image of the grid-->
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:contentDescription="Start"
        app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/linearLayout"
        app:srcCompat="@drawable/grid" />

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="0dp"
        android:layout_height="420dp"
        android:layout_marginTop="35dp"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="@+id/imageView"
        app:layout_constraintStart_toStartOf="@+id/imageView"
        app:layout_constraintTop_toBottomOf="@+id/textView">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal">

            <!--images of the grid boxes-->
            <ImageView
                android:id="@+id/block1"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="0" />

            <ImageView
                android:id="@+id/block2"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="1" />

            <ImageView
                android:id="@+id/block3"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="2" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/block4"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="3" />

            <ImageView
                android:id="@+id/block5"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="4" />

            <ImageView
                android:id="@+id/block6"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="5" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/block7"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="6" />

            <ImageView
                android:id="@+id/block8"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="7" />

            <ImageView
                android:id="@+id/block9"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:onClick="playerTap"
                android:padding="20sp"
                android:tag="8" />
        </LinearLayout>

    </LinearLayout>

    <!--game status text display-->
    <!--game status text display-->

    <TextView
        android:id="@+id/status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:fontFamily="@font/audiowide"
        android:text="Status"
        android:textSize="@dimen/dimension_size"
        android:textStyle="italic"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/linearLayout" />

    <Button
        android:id="@+id/exit_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Exit Game"
        android:textSize="@dimen/button_size"
        app:layout_constraintBottom_toBottomOf="parent"
        android:background="@color/green"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/status" />

</androidx.constraintlayout.widget.ConstraintLayout>

Output UI:


Step 4: Adding Functionalities to Layouts

Main Activity is the one which we will first access while opening the application. So, let us add Functionalities to the Start Button and Maintain the Score of Player A and Player B with it.

MainActivity.java
package org.geeksforgeeks.tic_tac_toe_game;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.content.SharedPreferences;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button gameButton=findViewById(R.id.start_button);
        // Load the saved scores from SharedPreferences
        TextView scorePlayerA = findViewById(R.id.score_playerA);
        TextView scorePlayerB = findViewById(R.id.score_playerB);

        // Set click listener for the add note button
        gameButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, activity_game.class);
                int REQUEST_CODE_ADD_NOTE=1;
                startActivityForResult(intent, REQUEST_CODE_ADD_NOTE);
            }
        });

        SharedPreferences prefs = getSharedPreferences("game_prefs", MODE_PRIVATE);
        int scoreA = prefs.getInt("score_playerA", 0);
        int scoreB = prefs.getInt("score_playerB", 0);

        scorePlayerA.setText(String.valueOf(scoreA));
        scorePlayerB.setText(String.valueOf(scoreB));
    }
}


After going through that main Activity we will start with our game. And the game Activity java is mentioned below:

We will create a two-dimensional array that will store all the winning positions. We will create a function that will run when a box inside the grid is clicked. Inside this function, we will first check if the box selected is empty or not. After that, we will set the image of X if the last move was of O or we will set the image of O if the last move was of X. Then we will check if the move has reached the move position and then reset the game. Below is the code for the Game.java file. Comments are added inside the code to understand the code in more detail.

activity_game.java
package org.geeksforgeeks.tic_tac_toe_game;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Arrays;

public class activity_game extends AppCompatActivity {
    boolean gameActive = true;

    // Player representation
    // 0 - X
    // 1 - O
    int activePlayer = 0;
    int[] gameState = {2, 2, 2, 2, 2, 2, 2, 2, 2};

    // State meanings:
    //    0 - X
    //    1 - O
    //    2 - Null
    // put all win positions in a 2D array
    int[][] winPositions = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
            {0, 3, 6}, {1, 4, 7}, {2, 5, 8},
            {0, 4, 8}, {2, 4, 6}};
    public static int counter = 0;

    // this function will be called every time a
    // players tap in an empty box of the grid
    public void playerTap(View view) {
        ImageView img = (ImageView) view;
        int tappedImage = Integer.parseInt(img.getTag().toString());

        // game reset function will be called
        // if someone wins or the boxes are full
        if (!gameActive) {
            gameReset(view);
            //Reset the counter
            counter = 0;
        }

        // if the tapped image is empty
        if (gameState[tappedImage] == 2) {
            // increase the counter
            // after every tap
            counter++;

            // check if its the last box
            if (counter == 9) {
                // reset the game
                gameActive = false;
            }

            // mark this position
            gameState[tappedImage] = activePlayer;

            // this will give a motion
            // effect to the image
            img.setTranslationY(-1000f);

            // change the active player
            // from 0 to 1 or 1 to 0
            if (activePlayer == 0) {
                // set the image of x
                img.setImageResource(R.drawable.x);
                activePlayer = 1;
                TextView status = findViewById(R.id.status);

                // change the status
                status.setText("O's Turn - Tap to play");
            } else {
                // set the image of o
                img.setImageResource(R.drawable.o);
                activePlayer = 0;
                TextView status = findViewById(R.id.status);

                // change the status
                status.setText("X's Turn - Tap to play");
            }
            img.animate().translationYBy(1000f).setDuration(300);
        }
        int flag = 0;
        // Check if any player has won if counter is > 4 as min 5 taps are
        // required to declare a winner
        if (counter > 4) {
            for (int[] winPosition : winPositions) {
                if (gameState[winPosition[0]] == gameState[winPosition[1]] &&
                        gameState[winPosition[1]] == gameState[winPosition[2]] &&
                        gameState[winPosition[0]] != 2) {
                    flag = 1;

                    // Somebody has won! - Find out who!
                    String winnerStr;

                    // game reset function be called
                    gameActive = false;
                    if (gameState[winPosition[0]] == 0) {
                        winnerStr = "X has won";
                        updateScore("score_playerA");
                    } else {
                        winnerStr = "O has won";
                        updateScore("score_playerB");
                    }
                    // Update the status bar for winner announcement
                    TextView status = findViewById(R.id.status);
                    status.setText(winnerStr);
                }
            }
            // set the status if the match draw
            if (counter == 9 && flag == 0) {
                TextView status = findViewById(R.id.status);
                status.setText("Match Draw");
            }
        }
    }

    // reset the game
    public void gameReset(View view) {
        gameActive = true;
        activePlayer = 0;

        //set all position to Null
        Arrays.fill(gameState, 2);

        // remove all the images from the boxes inside the grid
        ((ImageView) findViewById(R.id.block1)).setImageResource(0);
        ((ImageView) findViewById(R.id.block2)).setImageResource(0);
        ((ImageView) findViewById(R.id.block3)).setImageResource(0);
        ((ImageView) findViewById(R.id.block4)).setImageResource(0);
        ((ImageView) findViewById(R.id.block5)).setImageResource(0);
        ((ImageView) findViewById(R.id.block6)).setImageResource(0);
        ((ImageView) findViewById(R.id.block7)).setImageResource(0);
        ((ImageView) findViewById(R.id.block8)).setImageResource(0);
        ((ImageView) findViewById(R.id.block9)).setImageResource(0);

        TextView status = findViewById(R.id.status);
        status.setText("X's Turn - Tap to play");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game);

        Button exit=findViewById(R.id.exit_button);
        exit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity_game.this, MainActivity.class);
                int REQUEST_CODE_ADD_NOTE=1;
                startActivityForResult(intent, REQUEST_CODE_ADD_NOTE);
            }
        });
    }

    private void updateScore(String player) {
        SharedPreferences prefs = getSharedPreferences("game_prefs", MODE_PRIVATE);
        int currentScore = prefs.getInt(player, 0);
        currentScore++;
        SharedPreferences.Editor editor = prefs.edit();
        editor.putInt(player, currentScore);
        editor.apply();
    }
}


Step 5: Resources Associated with the Application

We Need to make certain changes in the dimensions, string and colors so use the layouts mentioned above without any error

dimens.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="title_size">24sp</dimen>
    <dimen name="player_size">20sp</dimen>
    <dimen name="image_width">40sp</dimen>
    <dimen name="image_length">40sp</dimen>
    <dimen name="dimension_size">24sp</dimen>
    <dimen name="button_size">20sp</dimen>
</resources>
strings.xml
<resources>
    <string name="app_name">Test_Layouts</string>
    <string name="app_title">Tic Tac Toe Game - GFG</string>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="green">#A8DF8E</color>
    <color name="colorPrimary">#829460</color>
</resources>
themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Base.Theme.Test_Layouts" parent="Theme.Material3.DayNight.NoActionBar">
        <!-- Customize your light theme here. -->
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
        <item name="colorPrimary">@color/colorPrimary</item>
    </style>

    <style name="Theme.Test_Layouts" parent="Base.Theme.Test_Layouts" />
</resources>


Output:


Final Application to Build a Simple Notes App


Note: The Application we have created is like a template for your Notes Application you can make changes in it as you want.
The GitHub Link for the Application is : Click Here to access the Complete Code for Tic Tac Toe Android Application


Click Here to Check More Android Projects


Next Article
Practice Tags :

Similar Reads