Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tic-Tac-Toe game -- Regex wants more characters than in the 3x3 board?

I have the code below for a 3x3 tic-tac-toe game. It works perfectly fine, but there are things I don't understand.

The objective of the function is to return:

  • -1 if the board is not yet finished (there are empty spots),
  • 1 if "X" won,
  • 2 if "O" won,
  • 0 if it's a cat's game (i.e. a draw).

function isSolved(board) {
   board = board.join('-').replace(/,/g,'');
   if(/222|2...2...2|2....2....2|2..2..2/.test(board)) return 2;
   if(/111|1...1...1|1....1....1|1..1..1/.test(board)) return 1;
   if(/0/.test(board)) return -1;
   return 0;

}

var result = isSolved([[0, 0, 1],[0, 1, 2],[2, 1, 0]]); //board is 3 dimensional array.

console.log(result); // -1

I don't understand a part of the regex in the if statement, i.e 1....1....1, since the maximum input the board can take is 9; but here it seems to be 11. Why is that?

The code is absolutely fine, but I don't understand what's happening. Could you explain?

like image 255
Arjun Kumar Avatar asked Dec 24 '22 00:12

Arjun Kumar


1 Answers

The regex looks at 11 characters because board has been joined with two extra - characters:

board = board.join('-')

Presumably, the original board is a 2D array, and the commas introduced by this join (as the nested arrays are stringified in the process), are removed with:

.replace(/,/g,'');

So an original board like this:

[
    [1, 0, 1],
    [2, 2, 0],
    [0, 0, 0]
]

...is turned into a string with .join("-"):

"1,0,1-2,2,0-0,0,0"

...and finally cleaned of the commas:

"101-220-000".

The additional separator makes it easier to find some patterns without raising false positives. For instance, when there is a match with 222, one can be sure that they will be in one row, and a match with 1..1..1 will likewise detect the three possible vertical 3-in-a-rows without false positives as it can only have a match that starts at position 0, 1 or 2. The 1....1....1 is 11 characters long and can only match at position 0 for one of the diagonals. Finally, 1..1..1 can also only match at one position, i.e. position 2, as otherwise one of the hyphens would conflict with a 1 in the pattern. A match represents the opposite diagonal.

Further improvement

One could merge two regexes into one (saving some execution time), by using a back reference, and use some logic to join all possibilities in one expression:

function isSolved(board) {
   board = board.join('-').replace(/,/g,'');
   var match = board.search(/([12])(\1|...\1...|....\1....|..\1..)\1/);
   return +(board[match] || board.includes("0") && -1);
}
like image 54
trincot Avatar answered Dec 26 '22 00:12

trincot