Building a Tic-Tac-Toe game with an Arduino
In this post, we will be building a simple Tic-Tac-Toe game with an Arduino, while learning how to program and build things in connection with a microcontroller.
This post will be aimed at beginners who know fundamentals of programming with an Arduino and are looking for a project but don’t know how to get started/need some inspiration or just want to learn something new.
Setting up the Hardware
What we’ll need:
Material:
- Arduino Uno
- Breadboard
- 10 LEDs
- Button
- Trim Potentiometer (10kOhm)
- 4 or more 470 Ohm Resistors or equivalent
- Between 10kOhm and 100kOhm Resistor (as pull down)
- Some breadboard compatible wires
Other:
- Computer + cable to program the Arduino
- Basic understanding of breadboards and electronics
- Arduino programming fundamentals
Note that you don’t need exactly these things. I will try to give examples of substitutes in case you don’t have the parts laying around. This is just what I am using.
The playing field
This will be a 3x3 LED Matrix, where we can tell every individual LED what it should do. Since every cell of a Tic Tac Toe field in this game can have 4 states (Empty, X, O, Selected) but our LEDs can only be on or off we will instead define a turned-on LED as Player 1 (X) and a slow blinking LED as Player 2 (O). A turned off LED will mean the cell is empty. A fast blinking LED will mean the cell is selected.
First, we will hook up ground from our Arduino to the negative power rail of the breadboard. Then we will position 9 LEDs, such that we have 3 rows with 3 LEDs in each of the rows. For each row, we will have a ground line, which is connected to the common ground through one 470Ohm capacitor to limit the current flowing through the LEDs. If you don’t do this, your LEDs will break. Every anode (short LED wire) will be connected to the same ground in a row. The cathodes (long LED wire) on the other hand should all be connected to a different rail of the breadboard, so we can connect all of them to a different output on our Arduino. This should look somewhat like this:
Now we can easily hook up every LED to on pin on the Arduino. I used pins 12 - 4 on the Arduino. 12 goes to the LED at the upper left corner, 11 to the upper middle and so on.
Controls
Next, we will need some kind of input. I am using a trimpot, which is a resistor of that you can adjust the impedance by turning a knob. With this we will be able to generate analog values that we map to the cells in the grid. We will also use a button, with which the current player can confirm his/her move and “place” their X or O in the selected cell. Instead of the trimpot you could also use an individual button for every cell, or two rows of 3 buttons along the two axes to specify a “coordinate”. You could even read the values through a serial input from a computer, or look into touch sensors. There are lots of possibilities for the input.
We will now connect the 5V output from the Arduino to the other power rail on our breadboard. To hook up the trimpot, there will be 3 leads: the outer two will be connected to 5V and ground respectively. The middle lead will be the output of the trimpot. Depending on the position of the knob on the trimpot, it will be between 5V and 0V (ground). This lead we will connect to an analog in pin of the Arduino. I am using A5.
To connect the button, we will connect one side to 5V and the other to pin 2 of the Arduino. This way, when we press the button it will connect pin 2 to high voltage. However, right now the pin 2 is floating, when the button isn’t pressed because it isn’t connected to anything with a defined voltage. One might think that this will be treated as 0V, but this isn’t always the case, which can lead to unexpected results. That’s why we will also need the 10kOhm resistor that we will connect from ground to the pin 2/the side of the button that is connected to pin 2. Now, if the button is not pressed, pin 2 will be pulled low, since it is connected to ground through the 10kOhm resistor. If the button is pressed, it will still be connected to high voltage, since the 5V will “overwrite” the ground signal. If the resistor wasn’t there, if the button was pressed, you would short 5V with the ground. And we don’t want that.
We will also want an LED to show us whose turn it is. It will also be either on or blinking. Connect the cathode to pin 3 on the Arduino and the anode to ground, again using a 470Ohm resistor.
That’s it for the hardware! Now we will program the Arduino.
Software
First, we will set up some variables we’ll need. We will need to keep track of the current cell that is selected, this will be a number between 0 (top left) and 8 (bottom right). Also, we will need to know which was the previously selected cell, so when the selected cell changes, we can reset the state of the previously selected cell. We also want to know which players turn it is (1 or 2) and if the game is over because someone has won:
We will also define which pins are responsible for what if you set everything up. And the values that are read from some of the pins. The variable firstLed
references the pin on the Arduino for the upper left LED. The program expects the other pins to be the 8 pins below the first one, so pins 12 - 8:
Next, we will define our 3x3 grid as a 2-dimensional array:
Lastly, we will need some timers and variables that indicate the cycle for the timers. These will be used for making our LEDs blink. A timer for the blinking of player 2’s LEDs and one for the blinking of the LED in the selected cell:
Now we’ll set up some functions to ease our work later. We will want a function to easily switch the players. Also, we will define functions to get
and set
values in our grid. This will make it easy to specify a cell with an int
from 0 - 8.
If a player has won, which we will check later in the code, the function win
will be called to flush the field and only light up the winning cells:
Then will come the setup
function from the Arduino in which we set the modes for the pins on the Arduino:
Now will come the loop
function. First, we will update the timers. If the last change from LOW to HIGH or vice versa was longer than a specified time ago, it will change the cycle. This way our LEDs will be blinking.
Also, we will write the correct value to the LED that indicates which players move it is. And we will write the correct values to all the LEDs in the grid.
We have to update these all the time, as it could be that the cycle of a flashing LED has changed:
The previous code should be executed on every loop, no matter what. The next code will only be executed if no player has won. First, we read the values from the input pins.
Then we set the previously selected position to the currently selected position before we update it. From line 8 - 13 we check which position the read value equals to. The value can be between 1023 (5V) and 0 (0V). We will then multiply it with a number <= 1. This number will gradually increase from 1/9 to 9/9. If the read value is lower than this value we will set the selected position to whatever number was divided by 9 and subtract 1, since indexing begins at 0.
This is equivalent to multiple if else
statements checking the value. But the former way is cleaner… And less to write.
One thing left to do is check if the button is pressed. If so, we will check if the cell at the selected position isn’t occupied by player 1 or 2. If it isn’t, we can set the cell to the current player and switch players afterward.
If the button isn’t pressed, though, we will set the selected cell to the selected state, if it is empty; if the selected cell changed, we will also set the previously selected cell back to empty, if it was in the selected state:
At this point, the only thing left is to check if a player has three in a row and call the win
function if so.
We want to check if there are either 3 of the same player’s marks in the same row or column. We do this 3 times for every row and column. Lastly, we check for the two diagonals.
Finished!
Now just upload the code to the Arduino and play! If a player has won, just press the reset button on the Arduino and the game will restart. (Sadly, you can’t see the blinking LEDs here, so you might as well have to build this to see the game in action)