Check for a Tic Tac Toe winner with Regular Expressions
Tags: javascript, html, game, regex
Published: 2021-10-18 18:22:26

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](https://thethomasy.github.io/TicTacToe/) 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.

```javascript
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.

```javascript
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.       

```javascript
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:

```javascript
// 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. 

```javascript
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.