Welcome to Tic Tac Toe Android App Tutorial. In this post we will build a super cool Tic Tac Toe app for android that is impossible to beat. This game would be impossible to beat because we are going to use the Artificial Intelligence here.
If you want to see the final app that we will build in this series then you can see this video.
Tic Tac Toe in Android using MiniMax Algorithm
I wanted my phone to think the best move of Tic Tac Toe.
And with MiniMax algorithm I did it.https://www.youtube.com/SimplifiedCoding
Subscribe now to learn how to do the same:But now I can not beat my phone. :/
LIKE, TAG & SHARE
Posted by Simplified Coding on Wednesday, May 1, 2019
The key point of this app is the MiniMax Algorithm that makes this Tic Tac Toe algorithm. So if you want to learn how you can build this game then keep reading.
Table of Contents
Tic Tac Toe Android App Tutorial – Video
You can also go through this video series, if you want more clear and step by step guide of every step that is required to build this game.
But if you are OK with a written tutorial then lets move ahead.
Building Tic Tac Toe Android App
Creating an Android Studio Project
As always we will start by creating a new Android Studio project.
- Create a new project named Tic Tac Toe using an Empty Activity.
- When you are in the above screen, make sure you select the Language as Kotlin and you mark the option use androidx.* artifacts.
Adding new Material Design
- Now first we will add the new material design dependency. So go inside app level build.gradle file and add this line.
1 2 3 |
implementation 'com.google.android.material:material:1.0.0' |
- Once we have added this dependency and synced our project, we will change our applications theme inside styles.xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources> |
Creating Game UI
- First you need some drawable resources to make the UI as you see below. You can get the drawables from this link.
- After downloading the drawables paste them inside the drawable folder of your project.
- Now define the following colors in your colors.xml file.
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3991aa</color> <color name="colorPrimaryDark">#184b5a</color> <color name="colorBackground">#26697c</color> <color name="colorAccent">#0A3664</color> </resources> |
- Come inside activity_main.xml and write the following xml code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:background="@color/colorBackground" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:id="@+id/image_view_name" android:layout_marginTop="20dp" android:layout_centerHorizontal="true" android:layout_alignParentTop="true" android:background="@drawable/app_name" android:layout_width="280dp" android:layout_height="55dp"/> <TextView android:layout_marginBottom="25dp" android:layout_above="@id/layout_board" android:layout_centerHorizontal="true" android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textColor="@android:color/white" tools:text="Computer Won" android:id="@+id/text_view_result" android:layout_width="wrap_content" android:layout_height="wrap_content" app:fontFamily="casual" android:textStyle="bold" android:textAllCaps="false" android:textSize="30sp"/> <GridLayout android:padding="16dp" android:alignmentMode="alignBounds" android:useDefaultMargins="true" android:layout_centerInParent="true" android:rowCount="3" android:columnCount="3" android:id="@+id/layout_board" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:layout_marginBottom="15dp" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:id="@+id/button_restart" android:textAllCaps="false" android:text="Restart" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout> |
- The above code will generate the following layout.
- Below the “Computer Won” text we have the GridLayout that is not visible because we have nothing inside. Inside this GridLayout we will generate our Tic Tac Toe board.
Generating Tic Tac Toe Board
- Now come inside MainActivity.kt and here we will create a function to display the tic tac toe board. Here we are generating the board using a 3 by 3 ImageView array.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
class MainActivity : AppCompatActivity() { //Creating a 2D Array of ImageViews private val boardCells = Array(3) { arrayOfNulls<ImageView>(3) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //calling the function to load our tic tac toe board loadBoard() } /* * This function is generating the tic tac toe board * */ private fun loadBoard() { for (i in boardCells.indices) { for (j in boardCells.indices) { boardCells[i][j] = ImageView(this) boardCells[i][j]?.layoutParams = GridLayout.LayoutParams().apply { rowSpec = GridLayout.spec(i) columnSpec = GridLayout.spec(j) width = 250 height = 230 bottomMargin = 5 topMargin = 5 leftMargin = 5 rightMargin = 5 } boardCells[i][j]?.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary)) layout_board.addView(boardCells[i][j]) } } } } |
- After writing the above code you can try running your application and you will see the following result.
- Bingo! we have generated our Tic Tac Toe board. Now we need to make the Player Movement. And for this we already pasted the drawable resources (Make sure you have the drawable resource for circle and cross in your drawable folder).
Adding Click Listener to Cells
When player wants to make a move, the player will tap on a cell. So first we will attach an OnClickListener on each cell (or each ImageView that is in our board or array).
- Inside your class MainActivity define an inner class that will implement the OnClickListener.
1 2 3 4 5 6 7 8 9 10 11 12 |
inner class CellClickListener( val i: Int, val j: Int ) : View.OnClickListener { override fun onClick(p0: View?) { //here we will code the move } } |
- Now we will attach it to each ImageView in our array, So inside loadBoard() function, just above the line where you are adding the cell to GridLayout add this line.
1 2 3 4 |
//attached a click listener to the board boardCells[i][j]?.setOnClickListener(CellClickListener(i, j)) |
- If you are confused then here is the code of MainActivity after adding the above codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
class MainActivity : AppCompatActivity() { //Creating a 2D Array of ImageViews private val boardCells = Array(3) { arrayOfNulls<ImageView>(3) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //calling the function to load our tic tac toe board loadBoard() } /* * This function is generating the tic tac toe board * */ private fun loadBoard() { for (i in boardCells.indices) { for (j in boardCells.indices) { boardCells[i][j] = ImageView(this) boardCells[i][j]?.layoutParams = GridLayout.LayoutParams().apply { rowSpec = GridLayout.spec(i) columnSpec = GridLayout.spec(j) width = 250 height = 230 bottomMargin = 5 topMargin = 5 leftMargin = 5 rightMargin = 5 } boardCells[i][j]?.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary)) //attached a click listener to the board boardCells[i][j]?.setOnClickListener(CellClickListener(i, j)) layout_board.addView(boardCells[i][j]) } } } inner class CellClickListener( val i: Int, val j: Int ) : View.OnClickListener { override fun onClick(p0: View?) { //here we will code the move } } } |
- Inside our click listener class (that is the inner class CellClickListener) we have two parameters i and j. This is because we want to know the index of the cell that is clicked.Â
Creating Board
- Now we need one more board. And this board is our actual board in which we will work internally. And the board that we generated above is just for the user visualization. When we will make any changes on the internal board we will also update the visual board which is the array of ImageView.
- For this internal board we will create a separate class and this class will contain a number of functions and properties. But before creating this class we need one more class to save the Cell of our Board.
- Create a data class named Cell and write the following code.
1 2 3 |
data class Cell(val i: Int, val j: Int) |
- The above class will only store the indices of a particular cell.
- Now we will create one more class. So create a new file named Board.kt and write the following kotlin code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
class Board { //Strings for PLAYER and COMPUTER companion object { const val PLAYER = "O" const val COMPUTER = "X" } //This is our internal board //and for this we used a 3 by 3 array of Strings val board = Array(3) { arrayOfNulls<String>(3) } //This property is giving us //a list of all the empty cells val availableCells: List<Cell> get() { val cells = mutableListOf<Cell>() for (i in board.indices) { for (j in board.indices) { if (board[i][j].isNullOrEmpty()) { cells.add(Cell(i, j)) } } } return cells } //this property will tell //if the game is over or not val isGameOver: Boolean get() = hasComputerWon() || hasPlayerWon() || availableCells.isEmpty() //These functions are checking //Weather the computer or player has won or not fun hasComputerWon(): Boolean { if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == COMPUTER || board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == COMPUTER ) { return true } for (i in board.indices) { if ( board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == COMPUTER || board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == COMPUTER ) { return true } } return false } fun hasPlayerWon(): Boolean { if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == PLAYER || board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == PLAYER ) { return true } for (i in board.indices) { if ( board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == PLAYER || board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == PLAYER ) { return true } } return false } //in this var we will store the computersMove var computersMove: Cell? = null //this is our minimax function to calculate //the best move for the computer fun minimax(depth: Int, player: String): Int { if (hasComputerWon()) return +1 if (hasPlayerWon()) return -1 if (availableCells.isEmpty()) return 0 var min = Integer.MAX_VALUE var max = Integer.MIN_VALUE for (i in availableCells.indices) { val cell = availableCells[i] if (player == COMPUTER) { placeMove(cell, COMPUTER) val currentScore = minimax(depth + 1, PLAYER) max = Math.max(currentScore, max) if (currentScore >= 0) { if (depth == 0) computersMove = cell } if (currentScore == 1) { board[cell.i][cell.j] = "" break } if (i == availableCells.size - 1 && max < 0) { if (depth == 0) computersMove = cell } } else if (player == PLAYER) { placeMove(cell, PLAYER) val currentScore = minimax(depth + 1, COMPUTER) min = Math.min(currentScore, min) if (min == -1) { board[cell.i][cell.j] = "" break } } board[cell.i][cell.j] = "" } return if (player == COMPUTER) max else min } //this function is placing a move in the given cell fun placeMove(cell: Cell, player: String) { board[cell.i][cell.j] = player } } |
- The class above is also having our minimax algorithm that will calculate the best move for the computer.
Making Moves
- Now come inside MainActivity.kt and modify it as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
package net.simplifiedcoding.tictactoe import android.os.Bundle import android.view.View import android.widget.GridLayout import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private val boardCells = Array(3) { arrayOfNulls<ImageView>(3) } //creating the board instance var board = Board() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) loadBoard() //restart functionality button_restart.setOnClickListener { //creating a new board instance //it will empty every cell board = Board() //setting the result to empty text_view_result.text = "" //this function will map the internal board //to the visual board mapBoardToUi() } } //function is mapping //the internal board to the ImageView array board private fun mapBoardToUi() { for (i in board.board.indices) { for (j in board.board.indices) { when (board.board[i][j]) { Board.PLAYER -> { boardCells[i][j]?.setImageResource(R.drawable.circle) boardCells[i][j]?.isEnabled = false } Board.COMPUTER -> { boardCells[i][j]?.setImageResource(R.drawable.cross) boardCells[i][j]?.isEnabled = false } else -> { boardCells[i][j]?.setImageResource(0) boardCells[i][j]?.isEnabled = true } } } } } private fun loadBoard() { for (i in boardCells.indices) { for (j in boardCells.indices) { boardCells[i][j] = ImageView(this) boardCells[i][j]?.layoutParams = GridLayout.LayoutParams().apply { rowSpec = GridLayout.spec(i) columnSpec = GridLayout.spec(j) width = 250 height = 230 bottomMargin = 5 topMargin = 5 leftMargin = 5 rightMargin = 5 } boardCells[i][j]?.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary)) boardCells[i][j]?.setOnClickListener(CellClickListener(i, j)) layout_board.addView(boardCells[i][j]) } } } inner class CellClickListener( private val i: Int, private val j: Int ) : View.OnClickListener { override fun onClick(p0: View?) { //checking if the game is not over if (!board.isGameOver) { //creating a new cell with the clicked index val cell = Cell(i, j) //placing the move for player board.placeMove(cell, Board.PLAYER) //calling minimax to calculate the computers move board.minimax(0, Board.COMPUTER) //performing the move for computer board.computersMove?.let { board.placeMove(it, Board.COMPUTER) } //mapping the internal board to visual board mapBoardToUi() } //Displaying the results //according to the game status when { board.hasComputerWon() -> text_view_result.text = "Computer Won" board.hasPlayerWon() -> text_view_result.text = "Player Won" board.isGameOver -> text_view_result.text = "Game Tied" } } } } |
- Now run your application and try beating the Tic Tac Toe.
- No way man it is impossible to beat this Tic Tac Toe.Â
Tic Tac Toe Android App Tutorial – Source Code
- In case you are having problem following the instructions, you can get your hands into my source code as well. But to get this you need to SHARE this post.
Tic Tac Toe Android App Tutorial Source Code
So that’s all for this Tic Tac Toe tutorial friends. If you have any questions then let me know in the comment section below.