Javascript:检查周围的表格单元格

时间:2015-07-01 01:26:12

标签: javascript html css

我正在制作名为Dots and Boxes的游戏。

网格上有一堆点:

enter image description here

var sendJsonRequest = function sendJsonRequest(type, id) {
  return new Promise(function (resolve, reject) {
    if (window.XMLHttpRequest) {
        var xmlhttp = new XMLHttpRequest();
    } else {
        var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            request = xmlhttp.responseText; 
            resolve(request);
        }
    }
    xmlhttp.error = reject;
    xmlhttp.open("GET","../RequestHandler.php?"+type+"_"+id,true);
    xmlhttp.send();
}

sendJsonRequest("initial", startID)
.then(function(data) {
  // do stuff
  var json = JSON.parse(data);
  console.log(json);
}, function err(e) {
  console.log(e);
});

单击框的一侧时,它会变黑:

enter image description here enter image description here

<table>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
</table>

一旦你点击第四面,关闭框,该框将改变点击第四面的玩家的颜色:

enter image description here

目前,玩家必须手动点击该框才能更改其颜色。但是,当盒子周围的四边都是黑色的时候,我希望它能自动用颜色填充盒子。

如何在Javascript中使用函数检查框的上方,下方,左侧和右侧的行是否填充为黑色?

function addLine(obj) {
            console.log("Called")

            if (obj.style.backgroundColor != "black") {
                obj.style.backgroundColor = "black";
                changeTurn()
            }
var playerTurn = "Blue";
            changeTurn();
            var number = 0;

            function addLine(obj) {
                console.log("Called")

                if (obj.style.backgroundColor != "black") {
                    obj.style.backgroundColor = "black";
                    changeTurn()
                }
            }
            
            function fillBox(obj) {
                if (playerTurn == "Blue") {
                    obj.style.backgroundColor = "red";
                }
                else if ( playerTurn == "Red") {
                    obj.style.backgroundColor = "blue";
                }
            }

            function changeTurn() {
                if (playerTurn == "Red") {
                    playerTurn = "Blue";
                    document.getElementById('turn').style.color = "blue";

                }
                else if (playerTurn == "Blue") {
                    playerTurn = "Red";
                    document.getElementById('turn').style.color = "red";
                };
                console.log(playerTurn);
                document.getElementById('turn').innerHTML = playerTurn + "'s Turn";
            }
h3 {
    font-family: Arial;
}
table {
    border-collapse: collapse;
}
.vLine {
    width: 10px;
    height: 60px;
}
.box {
    width: 60px;
    height: 60px;
}
.hLine {
    width: 60px;
    height: 10px;
}
.gap {
    width: 10px;
    height: 12px;
    background-color: black;
}
.vLine:hover, .hLine:hover {
    background-color: black;
}

4 个答案:

答案 0 :(得分:3)

这很简单,但它需要很好地利用DOM遍历。

单击一行时,最多只能有两个可填写的框。对于水平线,这些是上下方框,需要跳跃行。对于垂直线,左右。

对于这些方框中的每一个,我们需要确认四条线。同样,我们必须跳过上面和下面的行。

我实际上已经重新创建了游戏,因为它很有趣,因为这里有很多值得学习的东西。所以让我们来看看重要的事情。我们将在此代码中避免使用错误的表单,例如内联处理程序和直接在元素上设置样式,我们将CSS类作为控件,但更完整的选项将是data-attributes

让我们从两个基本的辅助函数开始。

第一个用于将类似数组的对象转换为实际数组,因此我们可以在它们上使用Array.prototype方法。我们主要在nodeList个对象上使用它。

function toArray (o) {
  return Array.prototype.slice.call(o);
}

第二个使用第一个,并在parentNode中为我们提供元素的位置。

function nodeIndex (node) {
  return toArray(node.parentNode.children).indexOf(node);
}

接下来,我们的事件处理程序。他们彼此非常相似。当我们点击一​​行时,我们用另一个辅助函数fill填充该行,该函数向名为.filled的行添加一个新类。然后我们检查可以填充的方框。对于垂直方框,上方和下方的水平线;对于垂直线条,左边和右边是水平方框。我们删除了事件监听器,因此只能单击一次。

function addHLine (e) {
  fill(this);
  this.removeEventListener(e.type, addHLine);
  checkVerticalBoxes(this);
  updateGame();
}

function addVLine (e) {
  fill(this);
  this.removeEventListener(e.type, addVLine);
  checkHorizontalBoxes(this);
  updateGame();
}

现在让我们看一下checkHorizontalBoxescheckVerticalBoxes。水平框位于同一行,从我们的起始行开始编入索引+/-1。垂直框位于从起始行开始索引+/-1的行中,并与该行共享相同的索引。我们有一些if语句,因为有时可能的行或框会超出范围。

function checkHorizontalBoxes (line) {
  var left = line.previousElementSibling,
      right = line.nextElementSibling;

  if (left) checkSurroundingLines(left);
  if (right) checkSurroundingLines(right);
}

function checkVerticalBoxes (line) {
  var index = nodeIndex(line),
      up = line.parentNode.previousElementSibling,
      down = line.parentNode.nextElementSibling;

  if (up) checkSurroundingLines(up.children[index]);
  if (down) checkSurroundingLines(down.children[index]);
}

这两个函数都会在潜在的框上调用checkSurroundingLines。此函数调用另外两个函数 - checkVerticalLinescheckHorizontalLines。开始看模式? isFilledfill类似,它是我们定义的辅助函数,用于测试行是否具有类.filled

function checkHorizontalLines (node, idx) {
  var left = node.previousElementSibling,
      right = node.nextElementSibling;

  return isFilled(left) && isFilled(right);
}

function checkVerticalLines (node, idx) {
  var row = node.parentNode,
      up = row.previousElementSibling.children[idx],
      down = row.nextElementSibling.children[idx];

  return isFilled(up) && isFilled(down);
}

如果这两个人都返回true,我们会填写我们的框。

这是对这种DOM遍历的逻辑的基本缺点。实际代码还有一些额外的东西,所以阅读所有部分。

这是工作游戏:

DEMO

&#13;
&#13;
// Player objects

function Player (name, className, scoreBox) {
  this.name = name;
  this.className = className;
  this.scoreBox = scoreBox;
  this.score = 0;
}

// State

var players = [new Player('Blue', 'blue-mark', 'p1'),
            new Player('Red', 'red-mark', 'p2')],
    currentPlayerIdx = 0,
    currentPlayer = players[currentPlayerIdx],
    turnBox = document.getElementById('turn');

// Helpers

function toArray (o) {
  return Array.prototype.slice.call(o);
}

function nodeIndex (node) {
  return toArray(node.parentNode.children).indexOf(node);
}

function fill (el) {
  el.classList.add('filled');
}

function isFilled (el) {
  return el.classList.contains('filled');
}

// Checks

function checkHorizontalLines (node, idx) {
  var left = node.previousElementSibling,
      right = node.nextElementSibling;
  
  return isFilled(left) && isFilled(right);
}

function checkVerticalLines (node, idx) {
  var row = node.parentNode,
      up = row.previousElementSibling.children[idx],
      down = row.nextElementSibling.children[idx];
      
  return isFilled(up) && isFilled(down);
}

function checkSurroundingLines (node) {
  var idx = nodeIndex(node),
      surrounded = checkVerticalLines (node, idx) && checkHorizontalLines(node, idx);
  
  if (surrounded) {
    node.classList.add('marked');
    node.classList.add(currentPlayer.className);
    return true;
  } 
}

function checkHorizontalBoxes (line) {
  var left = line.previousElementSibling,
      right = line.nextElementSibling;
  
  if (left) checkSurroundingLines(left);
  if (right) checkSurroundingLines(right);
}

function checkVerticalBoxes (line) {
  var index = nodeIndex(line),
      up = line.parentNode.previousElementSibling,
      down = line.parentNode.nextElementSibling;
  
  if (up) checkSurroundingLines(up.children[index]);
  if (down) checkSurroundingLines(down.children[index]);
}


// State sets

function setInfo () {
  turnBox.className = currentPlayer.className;
  turnBox.innerHTML = currentPlayer.name + "'s Turn";
  
  players.forEach(function (p) {
    document.getElementById(p.scoreBox).innerHTML = p.score;
  });
}

function getScores() {
  players.forEach(function (p) {
    p.score = document.querySelectorAll('.box.marked.'+p.className).length;
  });
}

function changeTurn () {
  currentPlayerIdx = 1 - currentPlayerIdx;
  currentPlayer = players[currentPlayerIdx];
  
  setInfo();
}

function updateGame() {
  getScores();
  changeTurn();
}

// Events

function addHLine (e) {
  fill(this);
  this.removeEventListener(e.type, addHLine);
  checkVerticalBoxes(this);
  updateGame();
}

function addVLine (e) {
  fill(this);
  this.removeEventListener(e.type, addVLine);
  checkHorizontalBoxes(this);
  updateGame();
}

function assignHandler (sel, ev, fn) {
  var els = document.querySelectorAll(sel);
  toArray(els).forEach(function (el) {
    el.addEventListener(ev, fn);
  });
}

assignHandler('.hLine', 'click', addHLine);
assignHandler('.vLine', 'click', addVLine);


setInfo();
&#13;
h3 {
  font-family: Arial;
}
table {
  border-collapse: collapse;
}
.vLine {
  width: 10px;
  height: 60px;
}
.box {
  width: 60px;
  height: 60px;
}
.hLine {
  width: 60px;
  height: 10px;
}
.gap {
  width: 10px;
  height: 12px;
  background-color: #333;
}
.vLine:not(.filled):hover,
.hLine:not(.filled):hover {
  background-color: #333;
}

.filled {
  background-color: grey;
}

.marked {
  background-color: green;
}

.box.blue-mark {
  background-color: #3355ff;
}

.box.red-mark {
  background-color: #ff5533;
}

#turn.blue-mark,
#p1 {
  color: #3355ff;
}

#turn.red-mark,
#p2 {
  color: #ff5533;
}
&#13;
<h3 id="turn"></h3>
<p class="counts">
  <span id="p1"></span> -
  <span id="p2"></span>
</p>

<table>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
</table>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您可以将previousElementSiblingnextElementSibling用于下一个和上一个元素,并结合此处提出的方法:Is it possible to get element's numerical index in its parent node without looping?,您可以在上方和下方获取。像这样:

    var indexPos = Array.prototype.indexOf.call(obj.parentNode.children, obj);
    above = obj.parentNode.previousElementSibling.children[indexPos];
    below = obj.parentNode.nextElementSibling.children[indexPos]
    next = obj.nextElementSibling;
    previous = obj.previousElementSibling;

你可以在这里看到,点击一个元素将周围的元素变为绿色。

https://jsfiddle.net/bvc0ta55/

它没有处理错误,例如是否存在遗漏,但由于你的方形元素都被包围了,你不应该面对这个问题。

答案 2 :(得分:0)

通常,您应该尝试将游戏逻辑与DOM分开,并处理游戏对象中的所有内容(数组等)。 但是,在您的情况下,您可以使用data attributes为每个感兴趣的元素分配特定值,创建元素数组,然后再使用这些数据属性来检查框是否被包围。这是一个很有趣的解决方案,因此我通过添加一些内容(创建数组和分配数据属性以及水平/垂直检查)来修改代码。 Jsfiddle:https://jsfiddle.net/pisamce/1ws3oyfe/

我修改了你的addLine函数来调用检查函数:

//check if box is surrounded
var row = +obj.dataset.row;
var col = +obj.dataset.col;
var type = obj.dataset.type;
if(type === 'h'){
    checkHorizontal(row, col);
}
else if(type === 'v'){
    checkVertical(row, col);
}

var bgcolor = 'black';
var hlines = document.getElementsByClassName('hLine');
for(var i=0;i<7;i++){
    for(var j=0;j<6;j++){
        hlines[j+i*6].setAttribute('data-row', i);
        hlines[j+i*6].setAttribute('data-col', j);
        hlines[j+i*6].setAttribute('data-type', 'h');
    }
}
var vlines = document.getElementsByClassName('vLine');
for(var i=0;i<6;i++){
    for(var j=0;j<7;j++){
        vlines[j+i*7].setAttribute('data-row', i);
        vlines[j+i*7].setAttribute('data-col', j);
        vlines[j+i*7].setAttribute('data-type', 'v');
    }
}
var boxes = document.getElementsByClassName('box');
for(var i=0;i<6;i++){
    for(var j=0;j<6;j++){
        boxes[j+i*6].setAttribute('data-row', i);
        boxes[j+i*6].setAttribute('data-col', j);
        boxes[j+i*6].setAttribute('data-type', 'b');
    }
}
function checkHorizontal(row, col){
    //check up
    if(row > 0
       && hlines[(row-1)*6+col].style.backgroundColor === bgcolor
       && hlines[row*6+col].style.backgroundColor === bgcolor
       && vlines[(row-1)*7+col].style.backgroundColor === bgcolor
       && vlines[(row-1)*7+col+1].style.backgroundColor === bgcolor){
           fillBox(boxes[(row-1)*6+col]);
    }
    //check down
    if(row < 6
       && hlines[(row+1)*6+col].style.backgroundColor === bgcolor
       && hlines[row*6+col].style.backgroundColor === bgcolor
       && vlines[row*7+col].style.backgroundColor === bgcolor
       && vlines[row*7+col+1].style.backgroundColor === bgcolor){
           fillBox(boxes[row*6+col]);
    }
}
function checkVertical(row, col){
    //check left
    if(col > 0
       && hlines[row*6+col-1].style.backgroundColor === bgcolor
       && hlines[(row+1)*6+col-1].style.backgroundColor === bgcolor
       && vlines[row*7+col].style.backgroundColor === bgcolor
       && vlines[row*7+col-1].style.backgroundColor === bgcolor){
           fillBox(boxes[row*6+col-1]);
    }
    //check right
    if(col < 5
       && hlines[row*6+col].style.backgroundColor === bgcolor
       && hlines[(row+1)*6+col].style.backgroundColor === bgcolor
       && vlines[row*7+col+1].style.backgroundColor === bgcolor
       && vlines[row*7+col].style.backgroundColor === bgcolor){
           fillBox(boxes[row*6+col]);
    }
}


var playerTurn = "Blue";
            changeTurn();
            var number = 0;

            function addLine(obj) {
                console.log("Called")
                
                if (obj.style.backgroundColor != "black") {
                    obj.style.backgroundColor = "black";
                    changeTurn()
                }
                
                //check if box is surrounded
                var row = +obj.dataset.row;
                var col = +obj.dataset.col;
                var type = obj.dataset.type;
                if(type === 'h'){
                    checkHorizontal(row, col);
                }
                else if(type === 'v'){
                    checkVertical(row, col);
                }
            }
            
            function fillBox(obj) {
                if (playerTurn == "Blue") {
                    obj.style.backgroundColor = "red";
                }
                else if ( playerTurn == "Red") {
                    obj.style.backgroundColor = "blue";
                }
            }

            function changeTurn() {
                if (playerTurn == "Red") {
                    playerTurn = "Blue";
                    document.getElementById('turn').style.color = "blue";

                }
                else if (playerTurn == "Blue") {
                    playerTurn = "Red";
                    document.getElementById('turn').style.color = "red";
                };
                console.log(playerTurn);
                document.getElementById('turn').innerHTML = playerTurn + "'s Turn";
            }
h3 {
    font-family: Arial;
}
table {
    border-collapse: collapse;
}
.vLine {
    width: 10px;
    height: 60px;
}
.box {
    width: 60px;
    height: 60px;
}
.hLine {
    width: 60px;
    height: 10px;
}
.gap {
    width: 10px;
    height: 12px;
    background-color: black;
}
.vLine:hover, .hLine:hover {
    background-color: black;
}
<h3 id="turn"></h3>

<table>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
</table>

答案 3 :(得分:0)

在progess中工作

一开始我只想尝试将逻辑与视图分开,我没有期望产生如此大量的代码: - |无论如何,虽然它远非完美,但我认为初学者可能会在某些方面感到有趣。

为了对代码本身做一点评论,逻辑位于类(javascript部分的前半部分)中,而视图是底部的混乱函数(javascript部分的后半部分)。 Cell classe及其子类(EdgeCell和CoreCell)代表矩形。边缘单元是可点击的单元,核心单元是最大的单元。

&#13;
&#13;
var GameError = extend(Error, {
    ctor: function () {
        Error.apply(this, arguments);
    }
});

var Game = extend(Object, {
    ctor: function () {
        var size = prompt('Board size: ', 3) || 3;
        this.board = new Board(size);
        this.currentPlayer = 1;
        this.players = [
            new Player(1, '#05A'),
            new Player(2, '#A11')
        ];
    },
    getBoard: function () {
        return this.board;
    },
    getCurrentPlayer: function () {
        return this.players[this.currentPlayer - 1];
    },
    nextTurn: function () {
        this.currentPlayer = this.currentPlayer ^ 3;
    },
    select: function (edge) {
        var coreCells, player;
        edge.switchOn();
        player = this.getCurrentPlayer();
        coreCells = this.board.getCoreCells(edge);
        each(coreCells, function (i, cell) {
            if (cell) {
                cell.incr();
                if (cell.isDone()) {
                    player.incrScore();
                }
            }
        });
    }
});

var Board = extend(Object, {
    ctor: function (width) {
        this.width = width;
        this.cells = this.makeCells();
    },
    getWidth: function () {
        return this.width;
    },
    getCell: function (i, j) {
        try { return this.cells[i][j]; }
        catch (e) { return null; }
    },
    makeCells: function () {
        var l = this.width * 2 + 1;
        var rows = new Array(l);
        loop.call(this, l, function (i) {
            var min = (i % 2) * 2 + 1;
            rows[i] = new Array(l);
            loop.call(this, l, function (j) {
                rows[i][j] = this.createCell(
                    min + j % 2, [i, j]
                );
            });
        });
        return rows;
    },
    createCell: function (type, idx) {
        var ctor;
        switch (type) {
            case Cell.types.CORNER: ctor = Cell; break;
            case Cell.types.H_EDGE: ctor = EdgeCell; break;
            case Cell.types.V_EDGE: ctor = EdgeCell; break;
            case Cell.types.CORE: ctor = CoreCell; break;
            default: throw new Error('Cell type not valid.');
        }
        return new ctor(type, idx);
    },
    getCoreCells: function (edge) {
        var i, j;
        i = edge.getRow();
        j = edge.getColumn();
        return i % 2 ? [
            this.getCell(i, j - 1),
            this.getCell(i, j + 1)
        ] : [
            this.getCell(i - 1, j),
            this.getCell(i + 1, j)
        ];
    }
});

var Player = extend(Object, {
    ctor: function (num, color) {
        this.num = num;
        this.color = color;
        this.score = 0;
    },
    getNum: function () {
        return this.num;
    },
    getColor: function () {
        return this.color;
    },
    getScore: function () {
        return this.score;
    },
    incrScore: function () {
        return this.score++;
    },
    toString: function () {
        return (
            '<span style="color:' + this.color + '">' +
                'Player ' + this.num +
            '</span>'
        );
    }
});

var Cell = extend(Object, {
    ctor: function (type, index) {
        this.type = type;
        this.index = index;
    },
    getType: function () {
        return this.type;
    },
    getIndex: function () {
        return this.index;
    },
    getRow: function () {
        return this.index[0];
    },
    getColumn: function () {
        return this.index[1];
    },
    toString: function () {
        return (
            Cell.names[this.type - 1]
        ) + (
            ' (' + this.index + ')'
        );
    }
});

Cell.types = {
    CORNER: 1,
    H_EDGE: 2,
    V_EDGE: 3,
    CORE: 4
};

Cell.names = [
    'corner',
    'h-edge',
    'v-edge',
    'core'
];

var EdgeCell = extend(Cell, {
    ctor: function (type, index) {
        Cell.call(this, type, index);
        this.on = false;
    },
    isOn: function () {
        return this.on;
    },
    switchOn: function () {
        if (!this.isOn()) this.on = true;
        else throw new GameError(this + ' already on.');
    }
});

var CoreCell = extend(Cell, {
    ctor: function (type, index) {
        Cell.call(this, type, index);
        this.count = 0;
    },
    isDone: function () {
        return this.count === 4;
    },
    incr: function () {
        if (!this.isDone()) this.count++;
        else throw new GameError(this + ' already done.');
    }
});

onload = function () {
    var game = new Game();
    var boardEl = makeBoardDom(game.getBoard());
    document.body.appendChild(boardEl);
    setupListeners(game);
    refreshLog(game);
};

function makeBoardDom (board) {
    var w = board.getWidth() * 2 + 1;
    var boardEl = document.createElement('div');
    boardEl.setAttribute('id', 'board');
    loop(w, function (i) {
        var rowEl = document.createElement('div');
        rowEl.setAttribute('class', 'row');
        boardEl.appendChild(rowEl);
        loop(w, function (j) {
            var cell = board.getCell(i, j);
            var cellEl = document.createElement('div');
            cellEl.setAttribute('class', Cell.names[cell.getType() - 1]);
            rowEl.appendChild(cellEl);
        });
    });
    return boardEl;
}

function setupListeners (game) {
    document.addEventListener('click', function (ev) {
        if (ev.target.className.indexOf('edge') + 1) {
            onEdgeClicked(game, ev.target);
        }
    });
}

function onEdgeClicked (game, edgeEl) {
    var idx, edge, coreCells, board;
    idx = getIndex(edgeEl);
    board = game.getBoard();
    edge = board.getCell.apply(board, idx);
    if (!edge.isOn()) {
        game.select(edge);
        edgeEl.className += ' on';
        coreCells = board.getCoreCells(edge);
        each(coreCells, function (i, cell) {
            if (cell && cell.isDone()) {
                refreshCoreCell(game, cell);
            }
        });
        game.nextTurn();
        refreshLog(game);
    }
}

function refreshCoreCell (game, cell) {
    var boardEl, rowEl, cellEl, player;
    player = game.getCurrentPlayer();
    boardEl = document.getElementById('board');
    rowEl = boardEl.childNodes[cell.getRow()];
    cellEl = rowEl.childNodes[cell.getColumn()];
    cellEl.style.background = player.getColor();
}

function refreshLog (game) {
    var turnEl = document.getElementById('turn');
    var scoresEl = document.getElementById('scores');
    var players = game.players.slice();
    players[0] += ': ' + players[0].getScore();
    players[1] += ': ' + players[1].getScore();
    turnEl.innerHTML = 'Turn: ' + game.getCurrentPlayer();
    scoresEl.innerHTML = players.join('<br />');
}

function getIndex (el) {
    var rowEl = el.parentNode;
    var boardEl = rowEl.parentNode;
    var indexOf = Array.prototype.indexOf;
    var j = indexOf.call(rowEl.childNodes, el);
    var i = indexOf.call(boardEl.childNodes, rowEl);
    return [i, j];
}

function each (list, fn) {
    var i, n = list.length;
    for (i = 0; i < n; i++) {
        fn.call(this, i, list[i]);
    }
}

function loop (n, fn) {
    var i = 0;
    while (i < n) {
        fn.call(this, i++);
    }
}

function extend (parent, proto) {
    var ctor = proto.ctor;
    delete proto.ctor;
    ctor.prototype = Object.create(parent.prototype);
    ctor.prototype.constructor = ctor;
    for (var k in proto) ctor.prototype[k] = proto[k];
    return ctor;
}
&#13;
.row div {
    float: left;
}

.row::after {
    content: " ";
    display: block;
    clear: both;
}

.corner {
    width: 20px;
    height: 20px;
    background: #333;
}

.h-edge {
    width: 50px;
    height: 20px;
}

.v-edge {
    width: 20px;
    height: 50px;
}

.v-edge:hover,
.h-edge:hover {
    cursor: pointer;
    background: #999;
}

.v-edge.on,
.h-edge.on {
    cursor: pointer;
    background: #333;
}

.core {
    width: 50px;
    height: 50px;
}

#turn, #scores {
    font: normal 16px Courier;
    margin-bottom: .5em;
}
&#13;
<div id="scores"></div>
<div id="turn"></div>
&#13;
&#13;
&#13;