Tom

Posted on

# Check for a Tic Tac Toe winner with Regular Expressions

Regular Expressions are complicated. The syntax can be very messy and it's all too easy to make mistakes. I am fairly inexperienced with them and so I decided to get some practice during one of my projects. I built a Tic Tac Toe game that uses a Regular Expression to identify the winner. Here I will discuss how I achieved this and if someone has a cleaner solution (as I'm sure there are many!) please comment below.

## The problem

Imagine the following setup. We have built a 3x3 grid as a table in HTML. The grid squares have id's numbered as such:

``````|1|2|3|
|4|5|6|
|7|8|9|
``````

Clicking on a square fills it with an X or O depending on who's turn it is. We need a way of determining if the play that has just been made, won that player the game.

For the sake of this discussion the game is being played by 2 people, each clicking on squares in turn.

## The solution

Before the game starts we define two empty strings to track each player's moves and an array of the free grid squares.

``````let xLocations = '';
let oLocations = '';
let empty = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
``````

When a player clicks on a square we add that square's id to the relevant string. For example, if the first move of the game is X clicking the center square, then `xLocations` becomes equal to the string `'5'`. We also remove that id from the `empty` array.

Next we define every winning combination in Tic Tac Toe.

``````const winners = ['123', '456', '789', '147',
'258', '369', '159', '357'];
``````

After each play, we need to check if the locations string matches any of these winning combinations. However there are several complicating factors:

• The play '321' is technically the same as '123' as the order played is irrelevant.
• The length of the location string will not be constant, eg '17382' is a valid winner as it contains '123'.

Therefore we need to test if a given location string contains any of the winning combinations. My solution is as follows. Every time a square is clicked we run the following function and pass in either `xLocations` or `oLocations` depending on whether X or O just played.

``````const checkWinner = (locations) => {
if (locations.length < 3) return;

for (let i = 0; i < winners.length; i++) {
let regexStr = winners[i].split('').join('|');
let regex = new RegExp(regexStr, 'g');
if (regex.test(locations)) {
if (locations.match(regex).length === 3) return 'win';
}
}
if (empty.length === 0) return 'draw';
};
``````

Let's break this down.

Firstly, as a minimum of 3 plays is required to win, we can discard any `locations` strings shorter than 3. We then loop over the `winners` array and create a Regular Expression for each value in turn. For the first value this would look like the following:

``````// winners[0] = '123'
let regexStr = winners[0].split('').join('|');
// regexStr = '1|2|3|'
let regex = new RegExp(regexStr, 'g');
// regex = /1|2|3/g
``````

This can be used to test if `locations` contains any of those three numbers. Therefore all we need to do is test for strings that match it exactly 3 times.

``````if (regex.test(locations)) {
if (locations.match(regex).length === 3) return 'win';
}
``````

Finally if the line `if (empty.length === 0) return 'draw';` runs true it means that the grid is full and there is no winner.

And that's it! If you have any questions or improvements feel free to comment them below.