Python is a popular programming language due to its readability, flexibility, and beginner-friendliness. It is fantastic for creating graphical user interfaces (GUI), and today, it offers a lot of frameworks that anyone may utilize to begin designing a GUI quickly.
GUIs are one of the essential components of any Android application. So, if you’re learning Python and want to improve your GUI development skills, this post is for you. This article will provide a simple method for creating a tic-tac-toe game for Android.
Table of Contents
Why should you build a Tic Tac Toe Android app?
Playing games is a natural approach to improving fine motor skills and promoting social connection. Tic-tac-toe can be used to develop various cognitive abilities, such as counting, spatial awareness, and the ability to recognize colors and shapes.
Players must focus or concentrate for brief periods to win a tic-tac-toe game. Focus and concentration can be improved with practice in these areas. An incredible skill to have in every aspect of life is this one. Consequently, many individuals are creating mobile apps for this kind of game.
What tools do Python developers prefer for building Python GUIs?
Embarcadero provides several tools for creating Python GUIs, including PyScripter, Delphi Community Edition, DelphiFMX, PythonFMXBuilder, and Delphi4PythonExporter.
DelphiFMX is a natively generated Python package created using the Python4Delphi library/bridge. Thanks to its open-source nature, Python programmers can access the Delphi programming language’s rich and mature FireMonkey (FMX) GUI framework. DelphiFMX Python package enables GUI creation for various operating systems, including Windows, macOS, Linux, and Android.
The Delphi4PythonExporter is a crucial tool/addon for a robust Python and Delphi ecosystem. Python developers can use Delphi’s premier UI design tools to speed up the authoring of design code in Python. The Exporter can be helpful to Delphi developers as well. They can use their Delphi experience to create amazing programs and then provide Python equivalents of those applications, giving you additional options and a different perspective.
PythonFMXBuilder is a Python application builder for Android applications that uses DelphiFMX. This allows you to deploy an individual Python script to your phone and integrate it within an Android app.
PySripter is another helpful tool, an Integrated Development Environment (IDE) for Python on Windows, which is free and open-source. It is built using Python and Delphi using the same Python4Delphi that powers DelphiFMX. Initially created as a simple IDE to offer a powerful scripting solution for Delphi applications.
Now that we know our needed tools let’s start making our app.
How can you create a Tic-Tac-Toe game for Android?
Let’s create a basic calculator application for both Android and desktop devices! Using the Python and Delphi tools mentioned above is a good approach. So let’s get started!
What are the requirements to create this game?
Python, PythonFMXBuilder, Delphi4PythonExporter, Delphi, and any text editor or IDE (PyScripter is recommended) that supports Python are required for this tutorial.
If you don’t have these installed, we recommend checking out this Delphi Python GUI Project Setup article to learn more about installing all the recommended tools.
You can grab the code for this tutorial from our Git repository at: https://github.com/Embarcadero/PythonBlogExamples/tree/main/TicTacToe_GUI
How to start a basic form for the app in Delphi?
Open Delphi CE and create a blank application by navigating to File > New > Multi-Device Application > Blank Application > Ok
. Here we have named our project TicTacToeProject
.
To get a detailed understanding of what each section of the above Delphi IDE mean/represent, please go through the free eBook bundle that we developed. This eBook explains the ideology around Delphi-Python EcoSystem, all Python GUI offerings, and much more.
Let’s start by giving our form a name. Right click on the form and click on QuickEdit
. Here we will name our form as TicTacToeGUI
and display the title as Tic Tac Toe Game
.
Because we want to make a fully functional multiplayer tic tac-toe game, we should include some labels that will allow the user to be updated with the game’s status and so on. We will be using the TLabel
component from the standard palette for this.
Let’s go ahead and even change the label text style to make the app more attractive. We will do this using TextSettings
. Head on over to the Object Inspector
and search for TextSettings
. Select Font
under it and click on the ...
to see options allowing you to change the label font, size and style according to your needs.
Next, rename the text of each label by right-clicking on that label and selecting Quick Edit
. It is important to follow an intuitive naming convention to name the labels so that it is easier to track them.
Next, we need to create some lines to create a tic-tac-toe grid. So go to the Palette
and search for TLine
. Insert the four lines, as appropriate, to create a 3×3 grid.
Now, let’s change the form’s background color to make it more appealing. We can do this by navigating to the Object Inspector
and selecting the form’s fill
property.
We need to add functionality to the grid boxes so we can play our game. So let’s add buttons to each box in the grid. These buttons will add an X or O to that box when clicked. Go to the Palette
and add the buttons using the TButton
component.
You should even change the font size and style of this button text (as we did for the TLabels
above) so that the “X” and “O”s are more prominent. We can even align the text to the Center
so that the markers appear symmetric in each box (button).
After these changes, our form will look much more visually attractive:
Finally, we added a reset button to help us reset our game to initial conditions. We may also see how our app appears on desktop and Android phones. Switch the Style from Windows
to Android
to verify this. This will alter how our form appears.
Now, double-click on each button on the form. The form’s buttons can be clicked twice to construct Click
methods in the .pas
file that corresponds to our .fmx
for each button. This makes it easier to add click functionality to each button.
To ensure that the method is preserved when we save the .pas
file, add at least one comment (//
) after each button Click
method. Since we don’t export the run-time implementation using the Delphi4PythonExporter, there is no need to write any run-time implementation code.
How to export this form as a Python script?
You can save your project as a Python script by selecting Tools > Export To Python > Export Current Entire Project
.
Delphi will generate two Python scripts, which you can rename to TicTacToe.py
and TicTacToeMain.py
, in your chosen directory. Along with the Python file, a .pyfmx
file containing all the form’s visual information is saved in that directory. The exported TicTacToeMain.py
will have the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from delphifmx import * from TicTacToe import TicTacToeGUI def main(): Application.Initialize() Application.Title = 'TicTacToe' Application.MainForm = TicTacToeGUI(Application) Application.MainForm.Show() Application.Run() if __name__ == '__main__': main() |
How to add functionality to the game?
Now that our app is ready, we can add logic to our game. So go to your project folder and open up TicTacToe.py
.
First, we need to create a 3×3 grid that will help us program our grid logic. So in the __init__
function, add a nested 2D list that contains the text of each button corresponding to their position in the grid using the .Text
attribute:
1 2 3 4 |
self.board = [[self.Pos1.Text, self.Pos2.Text, self.Pos3.Text], [self.Pos4.Text, self.Pos5.Text, self.Pos6.Text], [self.Pos7.Text, self.Pos8.Text, self.Pos9.Text]] |
Next, we create our players as self.p1
and self.p2
and create an integer variable self.player_status
that will help us know each player’s turn. Player 1 moves if player_status
is 1
, and Player 2 moves if player_status
is 2
.
Here is what our __init__
function looks like:
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 |
def __init__(self, owner): self.Title = None self.Status = None self.Line1 = None self.Line2 = None self.Line3 = None self.Line4 = None self.Pos1 = None self.Pos2 = None self.Pos3 = None self.Pos4 = None self.Pos6 = None self.Pos5 = None self.Pos7 = None self.Pos8 = None self.Pos9 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "TicTacToe.pyfmx")) self.board = [[self.Pos1.Text, self.Pos2.Text, self.Pos3.Text], [self.Pos4.Text, self.Pos5.Text, self.Pos6.Text], [self.Pos7.Text, self.Pos8.Text, self.Pos9.Text]] self.p1 = "X" self.p2 = "O" self.player_status = 1 |
What will happen when any of the buttons in the grid are clicked?
We need to add some functionality to the tic-tac-toe app that will help us display our moves on the app, but only in empty slots of the grid to not overwrite the moves made by the other player.
First, let’s create a class method named make_move
that takes in four parameters, the player who moved, the row and column positions, and the button to be updated. We first check if the move is valid. If there is already a move made in a slot, meaning if the Text
of a button is X or O, we shall display a try again message:
1 2 3 4 |
def make_move(self, player, row, col, Pos): if Pos.Text != "": self.Status.Text = "Please try again!" |
If there is no X
or O
in a slot, meaning the button’s Text
is empty, we have to add an X
for player 1’s move or an O
otherwise. Once the move is made, we toggle the player_status
to
indicate the other player’s turn:
1 2 3 4 5 6 7 8 |
else: self.board[row][col] = player Pos.Text = player if player == "X": self.player_status = 2 else: self.player_status = 1 |
Finally, we check whether the move has resulted in a winner, which we will check using a has_winner()
method (we will define it later), or check if all the buttons are full, indicating a tie.
A tie would occur if all the boxes of the tic-tac-toe grid that were initially empty, now contain a X or O. So we can check this by creating a string of all the elements in the board. If its length is nine and there was no winner, there would have been a draw. If it is neither a win nor a tie, then the game continues, and we display whose turn it is. Here is what our function looks like:
1 2 3 4 5 6 7 |
if self.has_winner()==True: pass elif len("".join([self.board[i][j] for i in range(3) for j in range(3)])) == 9: self.Status.Text = "Tie! Game Over!" else: self.Status.Text = "Player "+ str(self.player_status) + " Move" |
Here is what the final method looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def make_move(self, player, row, col, Pos): if Pos.Text != "": self.Status.Text = "Please try again!" else: self.board[row][col] = player Pos.Text = player if player == "X": self.player_status = 2 else: self.player_status = 1 if self.has_winner()==True: pass elif len("".join([self.board[i][j] for i in range(3) for j in range(3)])) == 9: self.Status.Text = "Tie! Game Over!" else: self.Status.Text = "Player "+ str(self.player_status) + " Move" |
Now head to the Button Click
functions and call the make_move
function with the relevant parameters. If player_status
is one then player 1 moves, so we call the make_move
method with X
as the display string, the position of the Button on the 3×3 grid, and finally, the button itself so that it can be updated. We repeat this function call for all the other buttons. Here is what our Click
functions look like:
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 |
def Pos1Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 0, 0, self.Pos1) else: self.make_move(self.p2, 0, 0, self.Pos1) def Pos2Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 0, 1, self.Pos2) else: self.make_move(self.p2, 0, 1, self.Pos2) def Pos3Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 0, 2, self.Pos3) else: self.make_move(self.p2, 0, 2, self.Pos3) def Pos4Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 1, 0, self.Pos4) else: self.make_move(self.p2, 1, 0, self.Pos4) def Pos5Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 1, 1, self.Pos5) else: self.make_move(self.p2, 1, 1, self.Pos5) def Pos6Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 1, 2, self.Pos6) else: self.make_move(self.p2, 1, 2, self.Pos6) def Pos7Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 2, 0, self.Pos7) else: self.make_move(self.p2, 2, 0, self.Pos7) def Pos8Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 2, 1, self.Pos8) else: self.make_move(self.p2, 2, 1, self.Pos8) def Pos9Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 2, 2, self.Pos9) else: self.make_move(self.p2, 2, 2, self.Pos9) |
Note: The column and row positions are the positions of the Buttons relative to the tic-tac-toe grid.
How to check when the game has ended?
Now that we have defined how we will make our moves, we need a mechanism to check whether the player wins. As mentioned previously, we will do this using the has_winner() method. For this method, we will check our tic-tac-toe grid for a winning pattern, which would be the same marker in one row, column, or diagonal. To do this, we run a for loop over the columns and rows of the board and see whether a pattern exists. If player 1 or player 2 wins, we assign it to a winner
variable:
1 2 3 4 5 6 7 8 9 10 11 |
def has_winner(self): winner = None for i in range(3): # Check row combos if self.board[i][0] != "" and self.board[i][0] == self.board[i][1] and self.board[i][1] == self.board[i][2]: winner = self.board[i][0] # Check column combos elif self.board[0][i] != "" and self.board[0][i] == self.board[1][i] and self.board[1][i] == self.board[2][i]: winner = self.board[0][i] |
Similar to checking the columns and rows, we also check for the diagonals:
1 2 3 4 5 6 |
# Check Diagonals if self.board[0][0] != "" and self.board[0][0] == self.board[1][1] and self.board[1][1] == self.board[2][2]: winner= self.board[0][0] elif self.board[0][2] != "" and self.board[0][2] == self.board[1][1] and self.board[1][1] == self.board[2][0]: winner = self.board[0][2] |
Finally, if we have a winner, we shall update the Status
label at the bottom of the application. Here is our final method:
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 |
def has_winner(self): winner = None for i in range(3): # Check row combos if self.board[i][0] != "" and self.board[i][0] == self.board[i][1] and self.board[i][1] == self.board[i][2]: winner = self.board[i][0] # Check column combos elif self.board[0][i] != "" and self.board[0][i] == self.board[1][i] and self.board[1][i] == self.board[2][i]: winner = self.board[0][i] # Check Diagonals if self.board[0][0] != "" and self.board[0][0] == self.board[1][1] and self.board[1][1] == self.board[2][2]: winner= self.board[0][0] elif self.board[0][2] != "" and self.board[0][2] == self.board[1][1] and self.board[1][1] == self.board[2][0]: winner = self.board[0][2] if winner != None: if winner == "X": self.Status.Text = "Player 1 Won the Match" else: self.Status.Text = "Player 2 Won the Match" return True |
How to reset the game?
Now that our game is ready, we need a way to reset the game. Thankfully, we have created a Reset
button that we will use to reset the game.
First, head to the __init__
method, copy the additional code we have added to the initial __init__
function, and paste it into the ResetClick()
method. Next, we need to reset the Buttons’ Text
attribute as well as the Status
label. To do this, we simply empty the Text attribute of each button and re-initialize the Status.Text
field. Our method code looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def ResetClick(self, Sender): self.board = [["", "", ""], ["", "", ""], ["", "", ""]] self.p1 = "X" self.p2 = "O" self.player_status = 1 self.Pos1.Text = "" self.Pos2.Text = "" self.Pos3.Text = "" self.Pos4.Text = "" self.Pos5.Text = "" self.Pos6.Text = "" self.Pos7.Text = "" self.Pos8.Text = "" self.Pos9.Text = "" self.Status.Text = "Player "+ str(self.player_status) + " Move" |
After all this, TicTacToe.py
will contain the following 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 150 151 152 153 154 155 |
import os from delphifmx import * class TicTacToeGUI(Form): def __init__(self, owner): self.Title = None self.Status = None self.Line1 = None self.Line2 = None self.Line3 = None self.Line4 = None self.Pos1 = None self.Pos2 = None self.Pos3 = None self.Pos4 = None self.Pos6 = None self.Pos5 = None self.Pos7 = None self.Pos8 = None self.Pos9 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "TicTacToe.pyfmx")) self.board = [[self.Pos1.Text, self.Pos2.Text, self.Pos3.Text], [self.Pos4.Text, self.Pos5.Text, self.Pos6.Text], [self.Pos7.Text, self.Pos8.Text, self.Pos9.Text]] self.p1 = "X" self.p2 = "O" self.player_status = 1 def FormCreate(self, Sender): pass def Line4Click(self, Sender): pass def make_move(self, player, row, col, Pos): if Pos.Text != "": self.Status.Text = "Please try again!" else: self.board[row][col] = player Pos.Text = player if player == "X": self.player_status = 2 else: self.player_status = 1 if self.has_winner()==True: pass elif len("".join([self.board[i][j] for i in range(3) for j in range(3)])) == 9: self.Status.Text = "Tie! Game Over!" else: self.Status.Text = "Player "+ str(self.player_status) + " Move" def Pos1Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 0, 0, self.Pos1) else: self.make_move(self.p2, 0, 0, self.Pos1) def Pos2Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 0, 1, self.Pos2) else: self.make_move(self.p2, 0, 1, self.Pos2) def Pos3Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 0, 2, self.Pos3) else: self.make_move(self.p2, 0, 2, self.Pos3) def Pos4Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 1, 0, self.Pos4) else: self.make_move(self.p2, 1, 0, self.Pos4) def Pos5Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 1, 1, self.Pos5) else: self.make_move(self.p2, 1, 1, self.Pos5) def Pos6Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 1, 2, self.Pos6) else: self.make_move(self.p2, 1, 2, self.Pos6) def Pos7Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 2, 0, self.Pos7) else: self.make_move(self.p2, 2, 0, self.Pos7) def Pos8Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 2, 1, self.Pos8) else: self.make_move(self.p2, 2, 1, self.Pos8) def Pos9Click(self, Sender): if self.player_status == 1: self.make_move(self.p1, 2, 2, self.Pos9) else: self.make_move(self.p2, 2, 2, self.Pos9) def has_winner(self): winner = None for i in range(3): # Check row combos if self.board[i][0] != "" and self.board[i][0] == self.board[i][1] and self.board[i][1] == self.board[i][2]: winner = self.board[i][0] # Check column combos elif self.board[0][i] != "" and self.board[0][i] == self.board[1][i] and self.board[1][i] == self.board[2][i]: winner = self.board[0][i] # Check Diagonals if self.board[0][0] != "" and self.board[0][0] == self.board[1][1] and self.board[1][1] == self.board[2][2]: winner= self.board[0][0] elif self.board[0][2] != "" and self.board[0][2] == self.board[1][1] and self.board[1][1] == self.board[2][0]: winner = self.board[0][2] if winner != None: if winner == "X": self.Status.Text = "Player 1 Won the Match" else: self.Status.Text = "Player 2 Won the Match" return True def ResetClick(self, Sender): self.board = [["", "", ""], ["", "", ""], ["", "", ""]] self.p1 = "X" self.p2 = "O" self.player_status = 1 self.Pos1.Text = "" self.Pos2.Text = "" self.Pos3.Text = "" self.Pos4.Text = "" self.Pos5.Text = "" self.Pos6.Text = "" self.Pos7.Text = "" self.Pos8.Text = "" self.Pos9.Text = "" self.Status.Text = "Player "+ str(self.player_status) + " Move" |
With this, our desktop application is ready, and you can run TicTacToeMain.py
to play.
How to Build Tic-Tac-Toe As An Android Application Using PythonFMXBuilder?
The Delphi4Python Exporter exports the tic-tac-toe application with initialization logic for a desktop-based application. Therefore, we will use the PythonFMXBuilder to convert it to an Android app. Check out the instruction provided here to set up your PythonFMXBuilder application if you didn’t setup earlier.
So, open up PythonFMXBuilder and click on Project > New project
on the toolbar. Enter the name of the project that you want to build here, which would be TicTacToe
in this case.
Before adding our Python files, we need to change some code to allow us to run our project on Android. Let’s create a new file named TicTacToeMain_Android.py
and paste the contents of TicTacToeMain.py
.
Next, add your Python files and any other supporting project files you used to build your application. Here we will not be adding TicTacToeMain.py
as it will only be used to run our desktop application:
Next, right-click on TicTacToeMain_Android.py
file and set it to the main file. But before we can build an .apk
, let’s some important changes to our code.
First off, Android applications are initialized automatically by Delphi applications. Because the program will be initialized automatically, we don’t need to initialize it or set a title manually. The Application.MainForm
doesn’t need to be initialized either but just needs a variable. Last but not least, similar to the initialization, we don’t need to launch or destroy the program because Android takes care of both tasks.
Here is what our TicTacToeMain_Android.py
looks like:
1 2 3 4 5 6 7 8 9 10 |
from delphifmx import * from TicTacToe import TicTacToeGUI def main(): Application.MainForm = TicTacToeGUI(Application) Application.MainForm.Show() if __name__ == '__main__': main() |
Save the file with the code changes you’ve just made.
We are now ready to convert our app to Android. PythonFMXBuilder gives developers three options for building applications: build, deploy, and run. You can learn more about these options by reading PythonGUI’s e-book. You can easily install the app on your mobile device thanks to PythonFMXBuilder. For the time being, we’ll use a .apk
file to create an Android application. Click the Build Project
button on the toolbar to create your file.
You will be prompted with a “Build process done
” notification in the Messages box once the.apk has been entirely generated. Your generated file will be in the pathtoPythonFMXBuilderGUIexeFolderappsTicTacToebin
folder.
Just copy and paste this .apk
file into your phone’s storage. If you access this file from your phone and click on it, you will be prompted with an installation message, which you should proceed with. Then, your tic-tac-toe game will be ready for usage.
Here is what our tic-tac-toe game looks like on an Android device:
The following three screenshots illustrate the scenarios of either of the players winning and a tie.
Are you prepared to create your more apps using DelphiFMX?
Congratulations on completing this tutorial. We hope that it has helped you realize how easy Python GUI development is when using the correct tools.
This tutorial demonstrated the effectiveness of Delphi for Android application development. We saw how DelphiFMX is a very powerful tool with a wide range of features and customization choices. Additionally, we demonstrated how the PyScripter makes Python programming even more enjoyable and simple. So if you want to learn more about Python GUI development, we recommended downloading the Python GUI getting started e-book to learn more about the features of Python GUI.
What are the FAQs regarding this topic?
What is P4D?
A free set of components called Python for Delphi (P4D) integrates the Python DLL into Delphi. They make it simple to run Python programs and to develop new Python modules and types. Python extensions can be developed as DLLs, among other things.
What is an Android app?
Android Apps are software programs that are created to operate on an Android device or emulator. The acronym APK, which stands for Android package, is often used to refer to a file.
Why is PyScripter a popular IDE?
PyScripter began as a straightforward IDE that offered a robust scripting solution for Delphi programs. Over time, it developed into a fully functional stand-alone Python IDE. It was created in Delphi. However, Python scripts can be added using Python4Delphi (P4D). Because it is written in a compiled language, it is lightweight compared to other IDEs.
The Python interpreter and debugger, file explorer, project manager, and other features of PyScripter can all be used. In addition, it can be used for creating simple programs as well as in Quantum Chemistry, Machine Learning, and many other domains.
Can you build GUIs in Python?
It might be challenging to design a straightforward graphical user interface that is compatible with many platforms. However, this is not always the case. You and your users may design visually stunning user interfaces using Python and the DelphiFMX and DelphiVCL packages from Embarcadero!