0

I am working on a tic-tac-toe algo and am using regexes to solve for the win conditions. the 9 squares are given the 0-8 values:

[0][1][2]

[3][4][5]

[6][7][8]

Each time player 1 or 2 clicks a square the value is pushed to an array and after 3 values are collected the regex starts testing to see if a player has won.

the problem.. for example the regex test to see if any order of 012 102 exist but it can't match 03142.

How can I fix my Regex to look for the 3 numbers even if separated by other numbers?

Let regexWin =  /(?:^|\W)[012][012][012](?:$|\W)/gm,
Mitya
  • 33,629
  • 9
  • 60
  • 107
Peter May
  • 31
  • 5
  • May you share a [mcve] of the issue? – evolutionxbox Jan 31 '21 at 12:12
  • Maybe use `^(?=.*(?:([012])(?!\1)){2}[012]).*` – JvdV Jan 31 '21 at 13:37
  • @evolutionxbox the example is in the question. However, Stack App was giving me issues so I had to change the way i put in my code. the regex above will test for any order of 012 as long as they're together (87 201 6), but if they're broken, then the regex fails (0 3 1 2). on the board each select is based on 0-9 index. depending on how the players play the win order want always be neatly together eg. (0 3 1 2) my working alt solution is below. – Peter May Feb 01 '21 at 20:50
  • @JvdV I tried your solution in the regex tester and it didn't work. Assuming it takes player1 5-moves to win (2 8 1 3 0) the regex needs to be able to find 012 in any order. I posted an non regex alt solution as well, but i'd like to see if i can get a regex solution as my 3rd option. – Peter May Feb 01 '21 at 20:56

3 Answers3

0

Assuming the squares occupied are pushed into the object moves, which contains an array for each player, p1 and p2, just sort the arrays after each push. That way, can guarantee the indices are compared in ascending order.

let moves = {p1: [], p2: []};
function move(player, spaceIndex) {
    moves[player].push(spaceIndex);
    moves[player].sort();
    if (/012|345|678|036|147|258|048|246/.test(moves[player].join('')) {
        //win!
    }
}

From the details given in your question it doesn't sound like you've realised that you need two arrays, not one, otherwise the win algo won't know which player occupies which space, and will report a win merely for three spaces in a row being occupied, even if they're shared between different players.

Mitya
  • 33,629
  • 9
  • 60
  • 107
  • I do have two arrays for each player. Sorry for some reason I kept getting an error every time I tried to share my code in S.O. I have two alternative solutions outside of the regex to this problem including the best practice one which can be found here: https://medium.com/@alvaro.saburido/set-theory-for-arrays-in-es6-eb2f20a61848 I just love Regex's so I was trying to find a solution that way. – Peter May Feb 01 '21 at 20:24
0

You could keep track of the board state, so you don't need to rely on the moves list: define board as an array of length 9, initialised with all 0 (indicating each cell is empty). Then when a move is played set the corresponding slot to either "1" or "2", depending on which player plays that move.

var board = Array(9).fill(0); // initial board
var moves = []; // initial move list
var turn = 1; // first player to move (other player is identified as 2)

// ...

function play(move) { // move is an index in board (0..8)
    board[move] = turn; // turn is 1 or 2
    moves.push(move); // this is the list you already have
    // Use regular expression to detect any 3-in-a-row
    let isWin = /^(?:...)*([12])\1\1|^.?.?([12])..\2..\2|^([12])...\3...\3|^..([12]).\4.\4/.test(board.join(""));
    console.log("is winning move?", isWin); 
    turn = 3 - turn; // toggle the player that is to move
}

This way you also can use board to update the display.

For a full implementation, with rendering, and a minimax algorithm for generating a "best" move, see this answer.

trincot
  • 317,000
  • 35
  • 244
  • 286
0

I did not find the Regex that worked, so I switched to a different idea. Based on the article by Alvaro Saburido I learned how to take the winArray and the 1&2 playerArray for an intersection and test for a win. The article is great and i'll keep searching for a regex solution just for coding fun.

https://medium.com/@alvaro.saburido/set-theory-for-arrays-in-es6-eb2f20a61848

    let win = ["012", "345", "678", "036", "147", "258", "048", "246"];
    function endGameEvaluation() {
    if (joueur1Turn) {
        resultMessage.innerHTML = `<div id="resultMessage">Player 1 Wins!! End of the game</div>`;
        gameWon = true;
        playerTurnMsg.innerHTML = "";
    } else if (joueur2Turn) {
        resultMessage.innerHTML = `<div id="resultMessage">Player 2 Wins!! End of the game</div>`;
        gameWon = true;
        playerTurnMsg.innerHTML = "";
    }
}

function winner(player) {
    for (var i = 0; i < 8; i++) {
        let won = win[i]
        let inCommon = player.filter(x => won.includes(x));
        if (inCommon.length == 3) {
            endGameEvaluation();
        }
    }
}

tdClickArea.forEach(item => {
    item.addEventListener('click', e => {
        let btnArea = e.target;
        let player;

        if (btnArea.innerHTML == "X" || btnArea.innerHTML == "O") {
            alert("tricheur! Choisi une autre case")
        } else {
            if (joueur1Turn) {
                btnArea.innerHTML = "X";
                joueur1Sq.push(btnArea.getAttribute("value"));
                player = joueur1Sq;

            } else if (joueur2Turn) {
                btnArea.innerHTML = "O";
                joueur2Sq.push(btnArea.getAttribute("value"));
                player = joueur2Sq;
            }
        }
        if (joueur1Sq.length >= 3 || joueur2Sq.length >= 3) {
            winner(player);
        }
        counter++;
        changeTurn();
        // Here we end the game if nobody won until the last posibble move
        if (counter == 9 && gameWon == false) {
            resultMessage.innerHTML = `<div id="resultMessage">End of the game</div>`;
        }
    })
});
Peter May
  • 31
  • 5