HY, 我试图在javascript / jQuery中实现Connect Four Game。首先,这不是家庭作业或任何其他职责。我只是想提升自己的能力。
我的“游乐场”是一个简单的html table
,有7行6列
但现在我已经到了我的身边。我坚持检查是否有4个相同的td的主要功能。我正在添加一个类来确定它应该在游戏中代表哪种颜色。
首先,我认为我可以使用.nextAll()
和.prevAll()
处理此事,但这对我不起作用,因为之间没有检测。
因为我正在寻找兄弟姐妹,在添加一个新项目时,只是查看了找到的兄弟姐妹的长度,如果他们最终匹配4我认为这是对的,但不是没有:D是否有任何类型的{ {1}}它提供了所有下一个css选择器,直到出现不同的东西?
我会将所有代码放入此jsfiddle:http://jsfiddle.net/LcUVf/5/
也许某人曾经尝试过同样的事情,或者有人提出了一个好主意我不会要求任何人做或完成我的代码。我只想获得实现这样一个算法或示例如何解决它的提示!
无论如何,谢谢!答案 0 :(得分:12)
DOM遍历不是特别有效,因此,当你可以避免它时,我建议这样做。将它构建为2D数组以存储和更新游戏状态是有意义的。该表只是数组的直观表示。
我知道,通常情况下,您将构建数组,其中行作为第一维,列作为第二维,但为了能够将碎片添加到每个列的“堆栈”,我会创建第一个维度列和第二维是行。
要做检查,看看我做的小提琴:
http://jsfiddle.net/Koviko/4dTyw/
有4个方向可供选择:南北,东西,东北 - 西南和东南 - 西北。这可以表示为具有为X和Y定义的增量的对象:
directions = [
{ x: 0, y: 1 }, // North-South
{ x: 1, y: 0 }, // East-West
{ x: 1, y: 1 }, // Northeast-Southwest
{ x: 1, y: -1 } // Southeast-Northwest
];
然后,循环通过该对象并循环通过你的“桌子”,从最远的边界开始,这件作品可能有助于获胜。因此,由于您需要连续4件,所以当前放置的部件可以在任何方向上赢得最多3件。
minX = Math.min(Math.max(placedX - (3 * directions[i].x), 0), pieces.length - 1);
minY = Math.min(Math.max(placedY - (3 * directions[i].y), 0), pieces[0].length - 1);
maxX = Math.max(Math.min(placedX + (3 * directions[i].x), pieces.length - 1), 0);
maxY = Math.max(Math.min(placedY + (3 * directions[i].y), pieces[0].length - 1), 0);
为了避免使用小于和大于(我遇到过)的任何问题,请计算循环遍历各个部分之前的步骤数,而不是使用计算出的边界作为条件。
steps = Math.max(Math.abs(maxX - minX), Math.abs(maxY - minY));
最后,遍历项目,保持与最后放置的棋子相匹配的连续棋子的数量。
function isVictory(pieces, placedX, placedY) {
var i, j, x, y, maxX, maxY, steps, count = 0,
directions = [
{ x: 0, y: 1 }, // North-South
{ x: 1, y: 0 }, // East-West
{ x: 1, y: 1 }, // Northeast-Southwest
{ x: 1, y: -1 } // Southeast-Northwest
];
// Check all directions
outerloop:
for (i = 0; i < directions.length; i++, count = 0) {
// Set up bounds to go 3 pieces forward and backward
x = Math.min(Math.max(placedX - (3 * directions[i].x), 0), pieces.length - 1);
y = Math.min(Math.max(placedY - (3 * directions[i].y), 0), pieces[0].length - 1);
maxX = Math.max(Math.min(placedX + (3 * directions[i].x), pieces.length - 1), 0);
maxY = Math.max(Math.min(placedY + (3 * directions[i].y), pieces[0].length - 1), 0);
steps = Math.max(Math.abs(maxX - x), Math.abs(maxY - y));
for (j = 0; j < steps; j++, x += directions[i].x, y += directions[i].y) {
if (pieces[x][y] == pieces[placedX][placedY]) {
// Increase count
if (++count >= 4) {
break outerloop;
}
} else {
// Reset count
count = 0;
}
}
}
return count >= 4;
}
答案 1 :(得分:2)
通常,2维数组更适合检查4行。您可以执行以下操作:
function check( lastPiece, playground, player ) {
// check length in each direction
var l = 1,
i = 1;
// top to bottom
while( (playground[ lastPiece.x ][ lastPiece.y - i ] === player) && ((lastPiece.y - i) >= 0) ) { l += 1; i += 1; };
i = 1;
while( (playground[ lastPiece.x ][ lastPiece.y + i ] === player) && ((lastPiece.y + i) <= MAX_Y) ) { l += 1; i += 1; };
if ( l >= 4 ) { return true; }
// left to right
l = 1;
while( (playground[ lastPiece.x - i][ lastPiece.y ] === player) && ((lastPiece.x - i) >= 0) ) { l += 1; i += 1; };
i = 1;
while( (playground[ lastPiece.x + i][ lastPiece.y ] === player) && ((lastPiece.x + i) <= MAX_X) ) { l += 1; i += 1; };
if ( l >= 4 ) { return true; }
// same for top left to bottom right and bottom left to top right
// . . .
// if we got no hit until here, there is no row of 4
return false;
}
编辑:添加了对游乐场边框的检查
答案 2 :(得分:2)
我发布了游戏的完整版本on Github。
它实现了所提到的algorythm Sirko的优化变体。
为了避免任何不必要的冗余,algorythm直接检查DOM而不是JS表。由于该algorythm需要最少量的检查,因此访问DOM的性能开销可以忽略不计。
当前玩家和用于跟踪游戏是否已经结束的标志基本上是存储在JS本身中的唯一状态。
我甚至使用DOM来存储字符串。它没有外部依赖性,IE6以上的所有IE版本以及现代浏览器都支持它。
代码针对文件大小和性能进行了优化。最新版本还包括动画,即使游戏的总JS代码在缩小后仍然只有 1.216字节。
这是完整的,未缩小的JS代码:
(function (doc, win, onclick, gid, classname, content, showMessage) {
var
a, b, c, colorLabel, cid, players, current, finished, newgameLabel, wonLabel, laststart = 1,
cellAt = function (i, j) {
return doc[gid](cid + i + j);
},
isCurrentColor = function (i, j) {
return cellAt(i, j)[classname] === players[current];
},
start = function () {
current = laststart = (laststart + 1) % 2;
finished = 0;
colorLabel[content] = colorLabel[classname] = players[current = (current + 1) % 2];
for (a = 1; a < 7; a++)
for (b = 1; b < 8; b++)
cellAt(a, b)[classname] = '';
},
makeMove = function (i, j, s) {
s > 0 && (cellAt(s, j)[classname] = '');
cellAt(s + 1, j)[classname] = players[current];
s === i - 1 ? function (i, j) {
return function (i, j) {
for (a = j - 1; 0 < a && isCurrentColor(i, a); a--) {
}
for (b = j + 1; 8 > b && isCurrentColor(i, b); b++) {
}
return 4 < b - a;
}(i, j) || function (i, j) {
for (c = i + 1; 7 > c && isCurrentColor(c, j); c++) {
}
return 3 < c - i;
}(i, j) || function (i, j) {
for (a = i - 1, b = j - 1; 0 < a && !(1 > b) && isCurrentColor(a, b); a--)
b--;
for (c = i + 1, b = j + 1; 7 > c && !(7 < b) && isCurrentColor(c, b); c++)
b++;
return 4 < c - a
}(i, j) || function (i, j) {
for (a = i - 1, b = j + 1; 0 < a && !(7 < b) && isCurrentColor(a, b); a--)
b++;
for (c = i + 1, b = j - 1; 7 > c && !(1 > b) && isCurrentColor(c, b); c++)
b--;
return 4 < c - a;
}(i, j);
}(i, j)
? finished = 1 && win[showMessage](doc[gid](wonLabel)[content].replace("%s", players[current].toLowerCase())) && start()
: colorLabel[content] = colorLabel[classname] = players[current = (current + 1) % 2]
: setTimeout(function () {
makeMove(i, j, s + 1)
}, 20);
};
return function (n, w, c, h, p1, p2) {
cid = c;
newgameLabel = n;
wonLabel = w;
colorLabel = doc[gid](c);
players = [doc[gid](p1)[content], doc[gid](p2)[content]];
for (a = 1; a < 7; a++)
for (b = 1; b < 8; b++)
cellAt(a, b)[onclick] = function (b, a) {
return function () {
if (!finished)
for (a = 6; a > 0; a--)
if (!cellAt(a, b)[classname]) {
makeMove(a, b, 0);
break;
}
};
}(b);
;
doc[gid](h)[onclick] = function () {
win[showMessage](doc[gid](newgameLabel)[content]) && start()
};
start();
};
})(document, window, "onclick", "getElementById", "className", "innerHTML", "confirm")("newgame", "won", "color", "restart", "p1", "p2");