【问题标题】:algorithm connect four javascript算法连接四个javascript
【发布时间】:2012-02-17 15:39:54
【问题描述】:

嘿, 我正在尝试在 javascript / jQuery 中实现连接四游戏。首先,这不是家庭作业或任何其他职责。我只是在努力提升我的能力。

我的“游乐场”是一个简单的 html table,它有 7 行和 6 列。
但现在我已经达到了我的水平。我坚持检查是否有 4 个相同的 td 的主要功能。我正在添加一个类来确定它应该在游戏中代表哪种颜色。 首先,我认为我可以使用 .nextAll().prevAll() 来处理这个问题,但这对我不起作用,因为两者之间没有检测到。
因为我正在寻找兄弟姐妹,所以在添加新项目时,只需查找找到的兄弟姐妹的长度,如果它们最终匹配 4,我认为这是正确的,但不是:D 可能有任何类型的@ 987654325@ 为所有 next 提供一个 css 选择器,直到出现不同的情况?

我会将我所有的代码放入这个 jsfiddle:http://jsfiddle.net/LcUVf/5/

也许有人曾经尝试过同样的方法,或者有人提出了一个好主意,我并不是要求任何人完成或完成我的代码。我只是想获得实现这种算法的提示或如何解决它的示例!

无论如何谢谢!

【问题讨论】:

  • 我会维护一个二维数组并使用 HTML 表来显示当前状态。无需多次访问 DOM,检查应该更容易、更快捷。
  • 是的,这也是我的第一个意图,你做过类似的事情吗?顺便说一句:将此作为答案发布,到目前为止,没有任何有用的信息可以让我接受您的答案!

标签: javascript jquery html


【解决方案1】:

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;
}

【讨论】:

    【解决方案2】:

    我发布了游戏的完整版本on Github

    它对提到的算法Sirko 进行了优化。

    为了避免任何不必要的冗余,算法直接检查 DOM 而不是 JS 表。由于该算法需要最少的检查,因此访问 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");
    

    截图:

    【讨论】:

    • 感谢您的回复。如果链接的示例也有未压缩源的副本,那就太好了。
    • 你可以在github.com/jslegers/4inarow找到完整的代码+演示。您还可以在 js1k.com/2014-dragons/demo/1751 找到占用空间优化的版本,压缩后的 HTML+JS+CSS 总大小减少到 1.8kb。
    【解决方案3】:

    一般而言,二维数组更适合检查 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;
    }
    

    编辑:添加了对游乐场边界的检查

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多