Noughts and Crosses winner declaration in a game of variable square board size I have made a tic tac toe for two players and written down some functions to do the decision making about who is the winner. I have gone through some cases while testing but need advice to see if I am in the right track and also if all the winning positions are being taken care of.
I would like to know even if this can be simplified.

Here:
$state = ["&amp;#10008;","-","-","-","-","&amp;#10008;","-","-","-","-","&amp;#10008;","-","-","-","-‌​","&amp;#10008;"]

The function is for grids starting from 3X3 to 7X7.

PHP

function whoIsWinning($state)
{
    $n = sqrt(count($state));
    $rows = $this-&gt;isWin($state, $this-&gt;genPaths($n, 0,     1,      $n, $n));
    $cols = $this-&gt;isWin($state, $this-&gt;genPaths($n, 0,     $n,     1,  $n));
    $diUp = $this-&gt;isWin($state, $this-&gt;genPaths(1, $n-1,  $n-1,   0,  $n));
    $diDn = $this-&gt;isWin($state, $this-&gt;genPaths(1,  0,     $n+1,   0,  $n));

    if ($rows !== '-') return $rows;
    if ($cols !== '-') return $cols;
    if ($diUp !== '-') return $diUp;
    return $diDn;
}

/**
 * Function to generate the paths to win
 * @param $count
 * @param $start
 * @param $incrementA
 * @param $incrementB
 * @param $lengthToWin
 * @return array
 */
function genPaths($count, $start, $incrementA, $incrementB, $lengthToWin)
{
    $paths = [];
    if($lengthToWin &gt; 3) {
        $lengthToWin = $lengthToWin - 1;
    }
    for ($i = 0; $i &lt; $count; $i++) {
        $path = [];
        for($j = 0; $j &lt; $lengthToWin; $j++) {
            array_push($path, $start + $i * $incrementB + $j * $incrementA);
        }
        array_push($paths, $path);
    }
    return $paths;
}

/**
 * @param $state
 * @param $paths
 * @return string
 */
function isWin($state, $paths)
{
    for ($i = 0; $i &lt; count($paths); $i++) {
        $currentPathResult = $this-&gt;isPathWin($state, $paths[$i]);
        if ($currentPathResult != '-')
            return $currentPathResult;
    }
    return '-';
}

/**
 * @param $state
 * @param $path
 * @return string
 */
function isPathWin($state, $path)
{
    $first = $state[$path[0]];
    for ($j = 1; $j &lt; count($path); $j++) {
        $compareToFirst = $state[$path[$j]];
        if ($compareToFirst != $first)
            return '-';
    }
    return $first;
}
About the logic of assessing victory:

After each player's turn there can only be one winner, so no matter how you want to write the functional parts it would be best practice to write a return as soon as a victor is found so that you don't do any surplus processing.

The sample $state data:


You have provided a 4x4 grid, with only one player's marks on it -- which is not realistic.
You use - to identify unmarked cells -- I think blanks would be simpler/clearer.
Player X has a diagonal sequence of 4 marks, even though victory (by your rules) is attained by 3 consecutive marks on a 4x4 grid.


Some missing diagonal checks:

$diUp and $diDn have $count values of 1.  This is accurate on 3x3 grids only.

Because $lengthToWin is sqrt(count($state))-1, all grids larger than 3x3 will have 4 winning diagonal sequences in each orientation (running down-to-the-right &amp; down-to-the-left).

Considering a 4x4 grid, the winning sequences from upper-left to lower-right are:


[ 0 , 5 , 10 ]
[ 1 , 6 , 11 ]
[ 4 , 9 , 14 ]
[ 5 , 10 , 15]


The first and last sequences share the internal cells with indexes of 5 &amp; 10.

Here is a visual:



The same logic is applied to the sequences that start from the upper-right corner.

To crystallize... a 3x3 grid will have a total of 2 diagonal sequences, all other grids will have a total of 8 diagonal sequences.  The diagonal sequences obey a simple pattern, so you will have no trouble in generating the correct path for your function.

My tinkerings:

I experimented a little with what I understood of your concepts and tried to employ array functions rather than for loops (though they are sometimes slower, I think they are better descriptors of action).  It is not a very tidy snippet (consider it a "napkin" draft), I didn't bother refining it because I am not 100% sure of your usage nor what is the best way to display the result.

function hasWon(array $vals,$mark){
    return empty(array_diff($vals,[$mark])); // retain opponent's mark and blanks, if empty = victory / true
}

function findFirstWinningSequence(array $state,$width,$mark){
    $lengthToWin=($width&gt;3?$width-1:$width);
    // process horizontal and vertical sequences:
    $chunks=array_chunk($state,$width);
    foreach($chunks as $i=&gt;$chunk){
        // full-length/width checks:
        if(hasWon(array_slice($chunk,0,$lengthToWin),$mark)){ // scan rows from left
            return "$mark wins via row ".++$i." from left";
        }
        $column=array_column($chunks,$i);  // cache column elements
        if(hasWon(array_slice($column,0,$lengthToWin),$mark)){ // scan cols from top
            return "$mark wins via column ".++$i." from top";
        }
        // length/width-1 checks
        if($width&gt;3 &amp;&amp; hasWon(array_slice($chunk,-$lengthToWin),$mark)){ // scan rows from right, if needed
            return "$mark wins via row ".++$i." from right";
        } 
        if($width&gt;3 &amp;&amp; hasWon(array_slice($column,-$lengthToWin),$mark)){ // scan cols from bottom, if needed
            return "$mark wins via column ".++$i." from bottom";
        } 
    }
    // process UpperLeft-to-LowerRight diagonal sequences:
    $starts=[0];  // index of first element in set
    if($width&gt;3){
        $starts+=[1,$width,$width+1];
    }
    $increment=$width+1;  // the increment between each cell in sequence
    foreach($starts as $start){
        $keys=array_fill_keys(range($start,$start+$increment*($lengthToWin-1),$increment),null);
        // if $start=0 &amp; $width=4, $keys=array_fill_keys(range(0,0+5*(3-1)=10,5),null); producing: [0=&gt;null,5=&gt;null,10=&gt;null]
        if(hasWon(array_intersect_key($state,$keys),$mark)){
            return "$mark wins via diagonal from ".++$start." running down and to the right";
        }
    }
    // process UpperRight-to-LowerLeft diagonal sequences:
    $starts=[$width-1];  // index of first element in set
    if($width&gt;3){
        $starts+=[$width-2,2*($width-1),2*($width-1)+1];
    }
    $increment=$width-1;  // the increment between each cell in sequence
    foreach($starts as $start){
        $keys=array_fill_keys(range($start,$start+$increment*($lengthToWin-1),$increment),null);
        // if $start=3 &amp; $width=5, $keys=array_fill_keys(range(4,3+4*(3)=15,4),null); producing: [3=&gt;null,7=&gt;null,11=&gt;null,15=&gt;null]
        if(hasWon(array_intersect_key($state,$keys),$mark)){
            return "$mark wins via diagonal from ".++$start." running down and to the left";
        }
    }
}
$state=['X','','','','','X','','','','','X','','','','O','O'];
$width=sqrt(sizeof($state));
$mark='X';
echo findFirstWinningSequence($state,$width,$mark);


To conclude, your coding structure seems suitably segmented/clean.  It only requires the logical tunes ups that I mentioned before my snippet.