Making a tic-tac-toe in Python doesn’t require much knowledge about the language itself, the true challenge consists of how to approach the project and what will be the logic that we will apply to it.
The way I am going to resolve it is simple, a game playing with the computer in a infinite loop that will continue until some of the following conditions are met.
1) The user completes a line.
2) The computer completes a line.
3) The board gets complete without a winner.
Making a flowchart es super important for understanding in a graphical way each step in a piece of software, by this way you can know the different paths the app can take based on the result of each algorithm that is written.
The general flowchart I have done is the following.
The solution applied in this case was to split the whole app in different algorithms or modules to simplify the code reading and error finding. An algorithm is a sequence of lines code lines that receives input data, process it on some way and generates output data. Each algorithm then becomes an individual function, for example making the user move, winner validation, etc. Having a modular app allows to separate the overall code in little fragments that resides on independent files. Each file contains a function that makes specific task.
Separating code in different files also simplifies the flowchart because now you can have one general flowchart and the rest of the processes in separate diagrams, the idea is to treat the whole application as a set of independent sub-applications that will receive data and return new processed data to the next sub-application inside a loop that will repeat forever until the game ends.
To represent the board in code I choose to use a list because I thought it was the simplest way, the general idea is to initialize the list with integer numbers between 0 and 8 when the game begins and replace these numbers with an “X” when the user plays and “O” when the computer plays. I cannot use tuples because they are read-only, so my choices were a list or a dictionary.
When the game begins, it shows the board on the console as a grid of three rows and tree columns enumerated from 1 to 9. Each cell represents a valid cell to play. Because app lacks GUI, the keyboard is used to make the play by input a number between 1 and 9.
The module that controls the user move is called jugada_usuario and consist of an infinite loop that receives the input value and make three validations.
1) The input value is a number.
2) The input value is between 1 and 9 (available cells to play)
3) The input value is available to play.
The game will show the corresponding error message and will ask the user to input a new value each time that any of the mentioned conditions are met. If the play is valid, the played cell will change it’s actual number to an “X”. Although the board is shown with numbers from 1 to 9, the real list contains numbers from 0 to 8, doing this I can easily change the list value calling the index for saving the player’s play.
The user’s play flowchart is the following.
Once the user has made his move is moment to check the status of the game. This task is done by the function called valida_usuario that checks the following possibilities.
1) The user completes a line and win.
2) The computer completes a line and win.
3) The board gets completed without a winner (draw). 4) There is no winner or draw and the game continues it’s flow.
The user wins when:
1) Completes a row.
2) Completes a column.
3) Completes a diagonal.
Computer wins when:
1) Completes a row.
2) Completes a column.
3) Completes a diagonal.
Draw its declared when all values in the list are string items and neither of the above conditions are met.
The utility of doing those evaluations inside a function is to write a single general process to be applied not only to user play’s but also with computer play’s. It decreases the lines of code, the app gets lighter and performance gets better.
Assuming that there is no winner after valida_ganador function is run, the game generates the computer play by calling jugada_compu function. The function first checks the game level that is pre-configured by code. If level is 1 or it is the first computer move in the game it only generates a random number between 1 and 9 and check if the cell is already played. If it’s played, the function starts over and generate a new random number, this process repeats in an infinite loop until a valid number is generated.
If level is 2 and it is not the first computer play in the game, the function validates a series of combinations that could potentially end in a completed line by the user. This is done by iterating on each element of the list looking for any of the following combinations.
For rows:
For columns:
For diagonals:
If any of the above conditions are met, the computer generates the corresponding value for avoiding the user to complete the line. It may be the case where the computer has more than one different option to avoid a potential user line depending on the user strategy at that moment on the game. If this is the case the computer will generate the “O” for the first condition met on the validation process. If neither the above is met, the computer generates a random number between 1 and 9 and save it on the play list.
The flowchart of computer move is the following.
After the computer plays, the software calls again to the valida_ganador function to make a new check of the status’s game.
The complete cycle of jugada_usuario, valida_ganador, jugada_compu and valida_ganador again, is repeated inside an infinite loop that only ends if a winner appears or the board gets completed and it’s a draw.
Once the game ends, it shows on console the result and ask the user to play again. If the answer is yes, the play list is reset, and a new game cycle is started. If it’s no, the program ends with a goodbye message. If the user enters any value that is neither “y” or “n” it shows a “no valid option, try again” message and ask the user to enter a new value.
To avoid issues whit capital letters, the app converts any string input to capitalize automatically. To decorate the game a bit, it prints the user plays in red and computer plays in yellow. I also added a simulated opacity for the numbers showed available to play by printing it in dark grey, all by using termcolor.
This is my first version of tic-tac-toe, a game where I tried to apply basic concepts of Python like loops, conditionals, data types, functions and modules.
The very first version was written with all the code in one single file. This new version is the first remanufactured that includes modularization and some spice for making a little better looking.