Сопоставление под-массива в массиве. Схема в схеме


Хорошо, рассмотрим это:

У меня есть большой массив, содержащий 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 5

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]
       ];
Моя стратегия проста.
  1. проверьте, есть ли в любой из строк один и тот же Игрок (a или b), если да, то у нас есть победитель.
  2. в противном случае проверьте, есть ли в любом из столбцов то же самое игрок
  3. еще, проверьте, есть ли у диагоналей игрок
Это три выигрышных случая. Сначала я создал функцию, которая может принимать множество строк (например: [a,0,b]) и проверять, содержит ли вся строка одно и то же значение, и если это значение не равно нулю (или -1 в вашем случае).
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() метод преобразования столбцов в строки, а затем использовать ту же функцию выше, чтобы проверить, если у нас есть победитель.

var board2 = _.zip.apply(this, board);
winner = checkForWinner.apply(this, board2);
Если я все еще не найду победителя, самое время проверить диагонали. Я написал эту функцию, чтобы извлечь две диагонали из доски в виде двух рядов и использовать ту же функцию checkForWinner, чтобы увидеть, доминируют ли диагонали у кого-либо из игроков.
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));