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. Player play one move at a time simultaneously. In a move, a player can choose any position from a 3×3 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 3×3 grid. The status of the game will be displayed at the bottom. A sample GIF is given below to get an idea about what we are going to do in this article.
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 themes.xml file:
<style name=”AppTheme” parent=”Theme.AppCompat.NoActionBar”>
Step 3: Working with the activity_main.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.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" android:background = "@color/green" tools:context = ".MainActivity" > <!--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 = "45sp" android:textStyle = "bold" app:fontFamily = "cursive" 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_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toBottomOf = "@+id/textView" app:srcCompat = "@drawable/grid" /> < LinearLayout android:id = "@+id/linearLayout" android:layout_width = "0dp" android:layout_height = "420dp" android:orientation = "vertical" app:layout_constraintBottom_toBottomOf = "@+id/imageView" app:layout_constraintEnd_toEndOf = "@+id/imageView" app:layout_constraintStart_toStartOf = "@+id/imageView" app:layout_constraintTop_toTopOf = "@+id/imageView" > < 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/imageView0" 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/imageView1" 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/imageView2" 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/imageView3" 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/imageView4" 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/imageView5" 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/imageView6" 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/imageView7" 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/imageView8" 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--> < TextView android:id = "@+id/status" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginBottom = "15sp" android:text = "Status" android:textSize = "28sp" android:textStyle = "italic" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toBottomOf = "@+id/linearLayout" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
Step 4: Working with the MainActivity.java file
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 MainActivity.java file. Comments are added inside the code to understand the code in more detail.
Java
import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class MainActivity 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" ; } else { winnerStr = "O has won" ; } // 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.imageView0)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView1)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView2)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView3)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView4)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView5)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView6)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView7)).setImageResource( 0 ); ((ImageView) findViewById(R.id.imageView8)).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_main); } } |