Last active
November 1, 2015 13:53
-
-
Save todoa2c/f3601a21d0fcd24b3fcd to your computer and use it in GitHub Desktop.
15パズルのReact.js版
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head lang="en"> | |
<meta charset="UTF-8"> | |
<title>15 puzzle with React.js</title> | |
<style> | |
table { | |
empty-cells:show; | |
} | |
.front { | |
width: 40px; | |
height: 40px; | |
cursor: pointer; | |
text-align: center; | |
border-style: groove; | |
font-weight: bold; | |
font-size: 24px; | |
} | |
.placed { | |
background-color: #ffd9d6; | |
} | |
.number-0 { | |
color: green; | |
} | |
.number-1 { | |
color: gold; | |
} | |
.number-2 { | |
color: blue; | |
} | |
.number-3 { | |
color: red; | |
} | |
</style> | |
<script src="https://fb.me/react-0.14.1.js"></script> | |
<script src="https://fb.me/react-dom-0.14.1.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script> | |
</head> | |
<body> | |
<div id="main"> | |
</div> | |
<script type="text/babel" src="puzzle.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 15パズルのパリティ計算(偶奇性の計算) | |
* @param {Array.<number>} numbers | |
* @returns {number} | |
* @see http://www.geocities.jp/m_hiroi/puzzle/parity.html | |
*/ | |
var parity = function(numbers) { | |
if (numbers.length === 1) { | |
return 0; | |
} else { | |
var head = numbers[0]; | |
var tail = numbers.slice(1); | |
return parity(tail) + tail.filter(function(x) {return x < head;}).length; | |
} | |
}; | |
/** | |
* Fisher-Yates shuffle (副作用あり版) | |
*/ | |
var shuffle = function(values) { | |
var i = values.length; | |
while(i){ | |
var j = Math.floor(Math.random()*i); | |
var t = values[--i]; | |
values[i] = values[j]; | |
values[j] = t; | |
} | |
return values; | |
}; | |
/** | |
* パズルゲーム本体 | |
*/ | |
var Puzzle = React.createClass({ | |
/** | |
* パズルの初期状態 | |
* @returns {{matrix: *[], count: number}} | |
*/ | |
getInitialState: function () { | |
return { | |
matrix: [[]], | |
count: 0 | |
}; | |
}, | |
/** | |
* startボタンを押したときに行う、ゲームの初期化処理 | |
*/ | |
startGame: function () { | |
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; | |
// パズルが正解になる並び順になるまで作りなおす | |
while (true) { | |
shuffle(numbers); | |
console.log(numbers); | |
if (parity(numbers) % 2 === 0) break; | |
} | |
// 4x4の配列に、パズルを挿入する | |
var matrix = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, null]]; | |
for (var i = 0; i < 4; i++) { | |
for (var j = 0; j < 4; j++) { | |
matrix[i][j] = numbers[4 * i + j]; | |
} | |
} | |
matrix[4 - 1][4 - 1] = null; | |
// 状態を更新する。パズルは新規に作られ、カウントは0から開始。 | |
this.setState({matrix: matrix, count: 0}); | |
}, | |
/** | |
* あるセルがクリックされたとき、クリックされたセルの前後左右にnullセルがあれば、そのセルと値を交換する | |
* @param {{row: number, col: number}} clickPosition | |
*/ | |
handleClick: function(clickPosition) { | |
var row = clickPosition.row, col = clickPosition.col; | |
// クリックされた位置の前後左右を見て、前後左右のどこかにnullがあればnullと数字を入れ替える | |
var directions = [[row - 1, col], [row + 1, col], [row, col - 1], [row, col + 1]]; | |
var matrix = this.state.matrix; | |
for (var i = 0; i < directions.length; i++) { | |
try { | |
if (this.state.matrix[directions[i][0]][directions[i][1]] === null) { | |
matrix[directions[i][0]][directions[i][1]] = matrix[row][col]; | |
matrix[row][col] = null; | |
// 状態を更新する。matrixは入れ替えたあとのmatrix、countは+1した結果 | |
this.setState({matrix: matrix, count: this.state.count + 1}); | |
break; | |
} | |
} catch(ignore) { | |
} | |
} | |
}, | |
/** | |
* 終了済みかを返す。 | |
* @returns {boolean} | |
*/ | |
isFinished: function() { | |
for (var i = 0; i < 4; i++) { | |
for (var j = 0; j < 4; j++) { | |
// matrix[i][j]が順番に並んでない状態になったとき、 | |
// i, jが共に3(つまり右下を検証中)でかつmatrix[i][j] == nullの時、パズルが解けたと言える。 | |
if (this.state.matrix[i][j] !== 4 * i + j + 1) { | |
return !!(i === 4 - 1 && j === 4 - 1 | |
&& this.state.matrix[i][j] === null); | |
} | |
} | |
} | |
return true; | |
}, | |
render: function () { | |
return ( | |
<div> | |
<button onClick={this.startGame} value="Start">Start</button> | |
<table border="1" cellSpacing="0"> | |
<Matrix matrix={this.state.matrix} onClick={this.handleClick}/> | |
</table> | |
<div>Clicked {this.state.count} times.</div> | |
{this.isFinished() && <div>Congratulations!</div>} | |
</div> | |
); | |
} | |
}); | |
/** | |
* パズルの4x4マトリックス | |
*/ | |
var Matrix = React.createClass({ | |
/** クリック時の処理は、上コンポーネントに下コンポーネントから渡された値を流すだけ */ | |
handleClick: function(clickPosition) { | |
this.props.onClick(clickPosition); | |
}, | |
/** | |
* 4x4のtableを作る | |
* @returns {XML} | |
*/ | |
render: function () { | |
var self = this; | |
return ( | |
<tbody> | |
{this.props.matrix.map(function (row, i) { | |
return ( | |
<tr key={i}> | |
{row.map(function(cell, j) { | |
return <Cell key={cell} row={i} col={j} number={cell} onClick={self.handleClick} /> | |
})} | |
</tr> | |
); | |
})} | |
</tbody> | |
) | |
} | |
}); | |
/** | |
* 15パズルの1つ1つのセル | |
*/ | |
var Cell = React.createClass({ | |
/** | |
* セルがクリックされたら、クリックされた位置を渡す。 | |
* @param e | |
*/ | |
handleClick: function(e) { | |
// 上位コンポーネント(つまりMatrixから渡されたonClick(つまりMatrixのhandleClick)を実行する | |
this.props.onClick({row: this.props.row, col: this.props.col}) | |
}, | |
render: function() { | |
var lineNumber = "number-" + Math.floor((this.props.number - 1) / 4); | |
var placed = this.props.number === (this.props.row * 4 + this.props.col + 1) && 'placed'; | |
var className = ['front', lineNumber, placed].join(' '); | |
return <td className={className} onClick={this.handleClick}>{this.props.number}</td> | |
} | |
}); | |
// #main に対してReact.jsで描画する | |
ReactDOM.render( | |
<Puzzle />, | |
document.getElementById('main') | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment