Skip to content

Instantly share code, notes, and snippets.

@DZ-TM
Last active January 18, 2024 12:24
Show Gist options
  • Select an option

  • Save DZ-TM/01fbcefa6757b2c75c545cc95006c6f0 to your computer and use it in GitHub Desktop.

Select an option

Save DZ-TM/01fbcefa6757b2c75c545cc95006c6f0 to your computer and use it in GitHub Desktop.
Explanation on how my TicTacToe RegEx works

Note

This gist covers how a TicTacToe regular expression I made works. It was made with a Golang flavour of RegEx in mind.

It is used to check if a player won or lost a TicTacToe game, or if it resulted in a draw.

You are expected to know a basic level of RegEx before reading this.

You can find the tests I made for the RegEx here.

When you have finished reading this gist, try making a RegEx to check if there is a win, loss or draw with connect4 - message me with your attempts and I will check them.

I doubt too many people are reading this, but if you find any typos or misinformation, let me know.

TicTacToe RegEx
^(?:(?:...)*(?:(1)11|(2)22)|.*(?:(?:1..){2}(1)|(?:2..){2}(2))|(?:(?:1...){2}(1)|(?:2...){2}(2))|..(?:(?:1.){2}(1)|(?:2.){2}(2))|[12]*$)

Directory

Why make a RegEx for TicTacToe?

I just thought it would be funny, and it takes up less room than the massive if statement needed to validate it - although at the risk of readability, so I would not recommend doing this unless you're just bodging code together.

How does it work?

It works with some predefined rules on the input, before being given to the RegEx engine. Once that input is given, we make logical decisions based on some concatenation and made-up rules.

Predefined rules

Before the text is given to the RegEx, it has some rules applied to it which is why the RegEx seems a bit vague, or like it would match anything. These rules are:

  • The input will always be 9 digits long because TicTacToe has 9 spots, or boxes.

  • Every 1 stands for Player 1's spot. Every 2 stands for Player 2's spot. 0 stands for an unused spot.

  • For every spot taken by 1, 2 can take a spot. Because of the turns nature of TicTacToe, for every 1, there will be a 2, with 1 more or less at times. This means the imposed limit is that the number of 1s, is equal to the number of 2s, plus or minus 1 or 0.

  • The string is from top to bottom, left to right, meaning from the following table, the string would be "ABCDEFGHI" (we are ignoring the prior rules to explain it better).

    A B C
    D E F
    G H I

Wins, losses and draws logic

There are only 8 ways to win a TicTacToe game, and when one person wins the other loses. When there are no 0s left, the game has resulted in a draw.

In these examples of wins, X accounts for either 1 or 2, but must stay consistent throughout the table (meaning they must stay the same). ? stands for any valid character i.e., 0, 1 and 2.

Under the table is its string equivalent to match against. They are numbered to be referred to later on.

  1. X X X
    ? ? ?
    ? ? ?

    XXX??????

  2. ? ? ?
    X X X
    ? ? ?

    ???XXX???

  3. ? ? ?
    ? ? ?
    X X X

    ??????XXX

  4. X ? ?
    X ? ?
    X ? ?

    X??X??X??

  5. ? X ?
    ? X ?
    ? X ?

    ?X??X??X?

  6. ? ? X
    ? ? X
    ? ? X

    ??X??X??X

  7. X ? ?
    ? X ?
    ? ? X

    X???X???X

  8. ? ? X
    ? X ?
    X ? ?

    ??X?X?X??

Making the RegEx

One thing to keep in mind is that we need to capture either 1 or 2 when there is a match - this is because in the code we need to use this data to give an output of Player X won or the game resulted in a draw.

The total RegEx will have a position asserted at the start of line / string, because it is checked from left to right. I do some odd tricks because of some predefined rules

I will be breaking this up into sections to make it easier to understand - this is where the number in the wins, losses and draws section comes into play.

In the first 3 tables, 1, 2 and 3 we can see a clear pattern as they have 3 random characters, 0, 1 or 2 times, before having three 1 or 2 in a row. We can translate this into the RegEx:

RegEx for 1 to 3 tables
(?:...)*(?:(1)11|(2)22)

In the next 3 tables, 4, 5 and 6, we can see another clear pattern being that each X has exactly 2 random characters between them, with 0, 1 or 2 random characters in front of the them.

RegEx for 4 to 6 tables
.*(?:(?:1..){2}(1)|(?:2..){2}(2))

In the second last table i.e., 7, we can see that each X has 3 random characters between them. In this case, the fact that the entire RegEx is asserted at the start becomes extremely useful.

RegEx for 7 table
(?:(?:1...){2}(1)|(?:2...){2}(2))

In the last table i.e., 8, there is another pattern that can be clearly viewed. This is that there are two random characters that it ends and starts in, then 1 character between each X.

RegEx for 8 table
..(?:(?:1.){2}(1)|(?:2.){2}(2))

To account for draws, we just check if the entire string did not match any 0 from start to end. We check this at the end so it becomes a case of if there are no wins, check for draws.

RegEx for draws
[12]*$

This means when joined together, the entire RegEx becomes the following:

^(?:(?:...)*(?:(1)11|(2)22)|.*(?:(?:1..){2}(1)|(?:2..){2}(2))|(?:(?:1...){2}(1)|(?:2...){2}(2))|..(?:(?:1.){2}(1)|(?:2.){2}(2))|[12]*$)

If all capture groups return empty strings yet the RegEx still matches, the TicTacToe game resulted in a draw. If any capture groups return 1 or 2, that player won and the other lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment