Сопоставление под-массива в массиве. Схема в схеме
Хорошо, рассмотрим это:
У меня есть большой массив, содержащий arrays
, -1
, a
и b
.
-1
означает, что поле пусто:
var board = [
[-1,-1, a],
[-1,-1, b],
[ b,-1, a]
]
Теперь я хочу проверить меньшие массивы agains это:
var solutions = [
[
[1, 1, 1]
],
[
[1],
[1],
[1]
],
[
[1],
[0,1],
[0,0,1]
],
[
[0,0,1],
[0,1],
[1]
]
]
, чтобы увидеть, соответствует ли одно существующее значение из board
шаблону в solutions
.
Соответствует ли a
какой-либо из паттернов?
Совпадает ли b
с каким-либо шаблоном?
Может ли кто-нибудь из вас увидеть лучший способ, чем сделать сумасшедшую вложенную петля:
var q,w,e,r,t,y;
q=w=e=r=t=y=0;
for( ; q < 3; q++ ) {
for( ; w < 3; w++ ) {
for( ; e < SOLUTIONS.length; e++ ) {
.... and so on...
}
}
}
В этом примере я использовал крестики-нолики. Но я могу быть кем угодно.
5 ответов:
Очень интересный вопрос. +1 :) Вот мой взгляд на это.
Проверьте мою скрипку http://jsfiddle.net/BuddhiP/J9bLC/ для полного решения. Я попытаюсь объяснить основные моменты здесь.
Я начинаю с такой доски, как эта. Я использовал 0 вместо -1, потому что это проще.Моя стратегия проста.var a = 'a', b = 'b'; var board = [ [a, 0, a], [b, b, b], [a, 0, a] ];
Это три выигрышных случая. Сначала я создал функцию, которая может принимать множество строк (например: [a,0,b]) и проверять, содержит ли вся строка одно и то же значение, и если это значение не равно нулю (или -1 в вашем случае).
- проверьте, есть ли в любой из строк один и тот же Игрок (a или b), если да, то у нас есть победитель.
- в противном случае проверьте, есть ли в любом из столбцов то же самое игрок
- еще, проверьте, есть ли у диагоналей игрок
Здесь я беру уникальные значения подряд, и если я могу найти только одно уникальное значение, которое не равно нулю, то он является победителем.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; };
Если в строках нет победителя, я проверяю столбцы. Чтобы повторно использовать свой код, я использую _.zip() метод преобразования столбцов в строки, а затем использовать ту же функцию выше, чтобы проверить, если у нас есть победитель.
Если я все еще не найду победителя, самое время проверить диагонали. Я написал эту функцию, чтобы извлечь две диагонали из доски в виде двух рядов и использовать ту же функцию checkForWinner, чтобы увидеть, доминируют ли диагонали у кого-либо из игроков.var board2 = _.zip.apply(this, board); winner = checkForWinner.apply(this, board2);
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]; };
Наконец, здесь я действительно проверяю доску на наличие победитель:
// 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); } }
Если кто-то из вас задается вопросом, почему я использую метод apply() вместо прямого вызова функции, то причина в том, что метод apply() позволяет передавать элементы массива в виде списка аргументов функции.
Я считаю, что это должно работать и для 4x4 или более высоких матриксов, хотя я их не тестировал. У меня было очень мало времени, чтобы проверить решение, поэтому, пожалуйста, дайте мне знать, если вы обнаружите какие-либо ошибки.
Что вы можете сделать, так это скомпилировать шаблоны для скорости. Точно так же, как одни и те же языки позволяют регулярным выражениям компилироваться для скорости.
function compile(pattern) { var code = "matcher = function(a) { return " var first = null for (var n = 0; n < pattern.length; n++) { for (var m = 0; m < pattern[n].length; m++) { if (pattern[n][m] == 0) continue var nm = "["+n+"]["+m+"]" if (first == null) { code += "a" + nm + " != -1"; first = " && a" + nm + " == " } code += first + "a" + nm } } code += "; }"; eval(code); return matcher }
Так что же это делает?
Например
compile([[1],[0,1],[0,0,1]]).toString()
Создаст следующую функцию
"function (a) { return a[0][0] != -1 && a[0][0] == a[0][0] && a[0][0] == a[1][1] && a[0][0] == a[2][2]; }"
Так как же вы его используете?
Для сопоставления позиций на доске используйте его следующим образом
var patterns = solutions.collect(function(each) { return compile(each); }) var matches = patterns.any(function(each) { return each(board); })
NB, самый последний отсек выше предполагает, что вы используете один из многих популярных высших порядков библиотеки программирования, как например lodash , чтобы обеспечить
collect
иany
функции на прототипе массива, если не использовать обычный старый для циклов вместо этого.
Нет, вам нужно только три вложенных цикла: один для петель над вашими шаблонами и два для петель вашего двумерного игрового поля:
Хорошо, вам может понадобиться четвертый цикл для игроков (function checkPatterns(patterns, player, field) { pattern: for (var p=0; p<patterns.length; p++) { for (var i=0; i<patterns[p].length; i++) for (var j=0; j<patterns[p][i].length; j++) if (patterns[p][i][j] && player !== field[i][j]) continue pattern; // else we've matched all return p; } // else none was found return -1; } function getSolution(player) return SOLUTIONS[checkPatterns(SOLUTIONS, player, currentBOARD)] || null; }
players.any(getSolution)
), но это не делает его более сумасшедшим и может быть встроено только для двух игроков.Однако, возможно, проще, чем формулировать "массивы шаблонов", построить алгоритмы для самих шаблонов:
function hasWon(player, field) { vert: for (var i=0; i<field.length; i++) { for (var j=0; j<field[i].length; j++) if (field[i][j] !== player) continue vert; return "vertical"; } hor: for (var j=0; j<field[0].length; j++) { for (var i=0; i<field.length; i++) if (field[i][j] !== player) continue hor; return "horizontal"; } for (var i=0, l=true, r=true, l=field.length; i<l; i++) { l == l && field[i][i] === player; r == r && field[l-i-1][l-i-1] === player; } if (l || r) return "diagonal"; return null; }
Вы можете сделать так, чтобы ваша доска была строкой:
var board = "-1,-1,a, -1,-1,b, b,-1,a"
И ваши решения могут быть массивом строк (аналогично плате)
var solutions = [ "1,1,1, 0,0,0, 0,0,0" , "1,0,0, 0,1,0, 0,0,1"
]
Затем для сравнения замените -1 и b на 0s, а a на 1s затем просто сравните строки
Это намного быстрее, чем иметь 10 различных циклов внутри другого цикла
Вам всегда будут нужны петли, чтобы пройти через все это. Вы можете просто сделать его более легким для чтения и более гибким. Приведенный ниже код будет работать для любого количества строк / колов больше 1 и с простой настройкой также для более чем 2 игроков.
var board1 = [ [-1,-1, 'a'], [-1,-1, 'b'], ['b',-1, 'a'] ]; var board2 = [ ['a','a', 'a'], [-1,-1, 'b'], ['b',-1, 'a'] ]; var board3 = [ [-1,'b', 'a'], [-1,'b', 'b'], ['b','b', 'a'] ]; var board4 = [ ['a',-1, 'a'], [-1,'a', 'b'], ['b',-1, 'a'] ]; var solutions = [ [ [1, 1, 1] ], [ [1], [1], [1] ], [ [1], [0,1], [0,0,1] ], [ [0,0,1], [0,1], [1] ] ]; function checkForWinner(playfield) { var sl = solutions.length; //solutions var bl = playfield.length; //board length var bw = playfield[0].length; //board width while(sl--) { //foreach solution var l = solutions[sl].length; if (l==1) { //horizontal //loop trough board length to find a match var tl = bl; while(tl--) { var pat = playfield[tl].join('') var r = checkRow(pat) if (r!==false) return r; } } else { //vertical or diagonal var l1 = solutions[sl][0].length; var l2 = solutions[sl][1].length; if (l1==l2) { //vertical var tw = bw; while (tw--) { //loop for each column var pat = ""; var tl = l; while(tl--) { //loop for vertical pat += playfield[tl][tw]; } var r = checkRow(pat) if (r!==false) return r; } } else { //diagonal var pat = ""; while(l--) { //loop for vertical var tw = solutions[sl][l].length; while (tw--) { //loop for horizontal if (solutions[sl][l][tw]!=0) pat += playfield[l][tw]; } } var r = checkRow(pat) if (r!==false) return r; } } } return 'no winner'; } function checkRow(pat) { if (!(pat.indexOf('a')>=0 || pat.indexOf('-1')>=0)) { //only b on row. player B won return 'B'; } if (!(pat.indexOf('b')>=0 || pat.indexOf('-1')>=0)) { //only a on row. player A won return 'A'; } return false; } console.log(checkForWinner(board1)); console.log(checkForWinner(board2)); console.log(checkForWinner(board3)); console.log(checkForWinner(board4));