Very interesting question. +1 :) Here is my take on this.
Check my fiddle http://jsfiddle.net/BuddhiP/J9bLC/ for full solution. I'll try to explain the main points in here.
I start with a board like this. I've used 0 instead of -1 because its easier.
var a = 'a', b = 'b';
var board = [
[a, 0, a],
[b, b, b],
[a, 0, a]
];
My Strategy is simple.
- Check if any of the rows has the same player (a or b), if so we have a winner.
- Else, Check if any of the columns has the same player
- Else, Check if diagonals has a player
Those are the three winning cases.
First I created a function which can take set of rows (Ex: [a,0,b]), and check if entire row contains the same value, and if that value is not zero (or -1 in your case).
checkForWinner = function () {
lines = Array.prototype.slice.call(arguments);
// Find compact all rows to unique values.
var x = _.map(lines, function (l) {
return _.uniq(l);
});
// Find the rows where all threee fields contained the same value.
var y = _.filter(x, function (cl) {
return (cl.length == 1 && cl[0] !== 0);
});
var w = (y.length > 0) ? y[0] : null;
return w;
};
Here I take unique values in a row, and if I can find only one unique value which is not ZERO, the he is the winner.
If there is no winner in the rows, I then check for columns. In order to reuse my code, I use _.zip() method to transform columns into rows and then use the same function above to check if we have a winner.
var board2 = _.zip.apply(this, board);
winner = checkForWinner.apply(this, board2);
If I still don't find a winner, time to check the diagonals. I've written this function to extract two diagonals from the board as two rows, and use the same checkForWinner function to see if diagonals are dominated by any of the players.
extractDiagonals = function (b) {
var d1 = _.map(b, function (line, index) {
return line[index];
});
var d2 = _.map(b, function (line, index) {
return line[line.length - index - 1];
});
return [d1, d2];
};
Finally this is where I actually check the board for a winner:
// Check rows
winner = checkForWinner.apply(this, board);
if (!winner) {
var board2 = _.zip.apply(this, board);
// Check columns, now in rows
winner = checkForWinner.apply(this, board2);
if (!winner) {
var diags = extractDiagonals(board);
// Check for the diagonals now in two rows.
winner = checkForWinner.apply(this, diags);
}
}
If any of you wonder why I use apply() method instead of directly calling the function, reason is apply() allows you to pass an array elements as a list of arguments to a function.
I believe this should work for 4x4 or higher matrics as well, although I did not test them.
I had very little time to test the solution, so please let me know if you find any errors.