匹配数组中的子数组。方案中的方案
Posted
技术标签:
【中文标题】匹配数组中的子数组。方案中的方案【英文标题】:Matching sub-array in array. Scheme in scheme 【发布时间】:2012-05-08 02:21:21 【问题描述】:好的,考虑一下:
我有一个包含arrays
、-1
、a
和b
的大数组。
-1
表示该字段为空:
var board = [
[-1,-1, a],
[-1,-1, b],
[ b,-1, a]
]
现在我想再次检查较小的数组:
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...
在这个例子中,我使用了井字游戏。
但我可以成为任何人。
【问题讨论】:
我假设,对于井字游戏,在solution
模式中,您不想匹配零而是空单元格。
您可以尝试将数组转换为 1 级深度以使比较更容易。但我不知道任何数组较浅的 sn-p... :(
【参考方案1】:
您可以做的是编译模式以提高速度。与相同语言相同的方式允许编译正则表达式以提高速度。
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); )
注意,上面的最后一个片段假设您正在使用许多流行的高阶编程库之一,例如 lodash,在数组原型上提供 collect
和 any
函数, 如果不使用普通的旧 for 循环。
【讨论】:
【参考方案2】:非常有趣的问题。 +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 或 b),如果是,我们就有赢家。
否则,检查任何列是否具有相同的播放器
否则,检查对角线是否有玩家
这是三个获胜的案例。
首先,我创建了一个可以获取一组行的函数(例如:[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 或更高的矩阵,尽管我没有测试它们。
我没有多少时间来测试解决方案,所以如果您发现任何错误,请告诉我。
【讨论】:
对硬连线井字游戏投反对票,但没有解决 OP 的一般问题,即如何将任何模式与任何棋盘匹配。 嗯.. 真的吗? :) OP 似乎认为我正确回答了他的问题。匹配 any 板上的 any 模式将 NOT 在此论坛中得到解答,您宁愿为此需要一本书。 OP 想要在任何尺寸的板上进行井字游戏模式匹配(行/列/diag 中的单个值),该解决方案完全能够处理,并且以更简单的方式进行。【参考方案3】:不,你只需要三个嵌套循环:一个循环你的模式,两个循环你的二维运动场:
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;
【讨论】:
【参考方案4】:你可以让你的板子变成一个字符串:
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 替换为 0,将 a 替换为 1 然后简单地比较字符串
这比在另一个循环中包含 10 个不同的循环要快得多
【讨论】:
这也是我的第一个想法,但这意味着您需要为每一行定义模式。如果中间行是 1,1,1,则您的解决方案将不匹配,除非您还添加 0,0,0,1,1,1,0,0,0, 到匹配中。这适用于 3x 领域,但扩展到 9x9 会提供很多解决方案。此外,您还需要为每次检查制作比赛场地的副本以进行替换,并且由于 javascript 会引用数组,因此您需要为每次检查克隆数组,从而循环遍历所有行和列以创建新数组(或使用c = board.splice(0) 是为了可读性,而不是速度)。【参考方案5】:你总是需要循环来完成这一切。你可以让它更容易阅读和更灵活。下面的代码适用于任何数量大于 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));
【讨论】:
以上是关于匹配数组中的子数组。方案中的方案的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Mongo DB 中的子文档数组中删除匹配条件的子文档