我正在制作名为Dots and Boxes的游戏。
网格上有一堆点:
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);
});
单击框的一侧时,它会变黑:
<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>
一旦你点击第四面,关闭框,该框将改变点击第四面的玩家的颜色:
目前,玩家必须手动点击该框才能更改其颜色。但是,当盒子周围的四边都是黑色的时候,我希望它能自动用颜色填充盒子。
如何在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;
}
答案 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();
}
现在让我们看一下checkHorizontalBoxes
和checkVerticalBoxes
。水平框位于同一行,从我们的起始行开始编入索引+/-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
。此函数调用另外两个函数 - checkVerticalLines
和checkHorizontalLines
。开始看模式? isFilled
与fill
类似,它是我们定义的辅助函数,用于测试行是否具有类.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 强>
// 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;
答案 1 :(得分:1)
您可以将previousElementSibling
和nextElementSibling
用于下一个和上一个元素,并结合此处提出的方法: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)
一开始我只想尝试将逻辑与视图分开,我没有期望产生如此大量的代码: - |无论如何,虽然它远非完美,但我认为初学者可能会在某些方面感到有趣。
为了对代码本身做一点评论,逻辑位于类(javascript部分的前半部分)中,而视图是底部的混乱函数(javascript部分的后半部分)。 Cell classe及其子类(EdgeCell和CoreCell)代表矩形。边缘单元是可点击的单元,核心单元是最大的单元。
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;