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.
Top comments (1)
I'm working on a tictactoe game. I want to use RegEx to check if someone is winning. make him want you spell