<!DOCTYPE html>
<html>
<head>
<script data-require="react@15.2.0" data-semver="15.2.0" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react.min.js"></script>
<script data-require="react@15.2.0" data-semver="15.2.0" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react-dom.min.js"></script>
<link data-require="bootstrap-css@3.3.6" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="container" class="container"></div>
<script src="script.js"></script>
</body>
</html>
const Tile = (props) => {
let className = "valid-";
let isGameEnded = props.isGameEnded;
let isValid = props.isValid;
className += (isValid && !isGameEnded) ? "true" : "false";
className += " col-xs-3";
let num = null;
if(props.number > 0) {
num = <div className="number">{props.number}</div>
}
return (
<div className={className} onClick={props.onClick.bind(props.game, props.number)}>{num}</div>
)
};
const TilesFrame = (props) => {
let tiles = [];
let validIndexes = props.validIndexes;
for (let i = 0; i < props.randomNumbers.length; i++) {
let rNumber = props.randomNumbers[i];
tiles.push(<Tile game={props.game} onClick={props.tileClicked} isGameEnded={props.isGameEnded} isValid={validIndexes.indexOf(i) != -1} number={rNumber}/>)
}
return (
<div id="tiles-frame">
{tiles}
</div>
)
}
const DoneFrame = (props) => {
return (
<div id = "done-frame" className = "well text-center">
<h2>{props.resultMessage}</h2>
<button className="btn btn-default" onClick={props.resetGame}>
Play again
</button>
</div>
)
}
const SliderFrame = (props) => {
let gameLevel = props.gameLevel;
let currentLevel = props.level;
return (
<div id = "slider-frame">
Level
<input type="range"
min="5"
max="40"
step="2"
value={currentLevel}
onChange={gameLevel}
/>
</div>
)
}
class Game extends React.Component {
constructor(props) {
super(props);
this.randomNumbers.bind(this);
this.tileClicked.bind(this);
this.checkValidClick.bind(this);
this.getAdjacentIndexes.bind(this);
this.updateDoneStatus.bind(this);
this.gameLevel.bind(this);
this.state = {
randomNumbers : this.randomNumbers(4, 25),
resultMessage : "",
size: 4,
level: 25
};
}
resetGame () {
var currentLevel = this.state.level;
this.setState({
randomNumbers : this.randomNumbers(4, currentLevel),
resultMessage : "",
size: 4,
level: currentLevel
});
}
randomNumbers (size, level) {
var len = size*size;
var rnArray = Array.apply(null, {length: len}).map(Number.call, Number);
console.log(level)
for (var i = 0; i < level; i++) {
var index1 = Math.floor(Math.random() * len);
var index2 = Math.floor(Math.random() * len);
rnArray[index2] = [rnArray[index1], rnArray[index1] = rnArray[index2]][0];
}
return rnArray;
}
tileClicked (clickedTile) {
if (this.state.resultMessage) {
return;
}
var randomNumbers = this.state.randomNumbers;
var clickedIndex = randomNumbers.indexOf(clickedTile);
if (clickedIndex === -1) {
throw new Error("Unexpected: Invalid Index");
}
var adjacentIndexes = this.getAdjacentIndexes(clickedIndex, 4);
var zeroIndex = this.checkValidClick(clickedIndex, adjacentIndexes);
if (zeroIndex === -1) {
return;
}
randomNumbers[zeroIndex] = [randomNumbers[clickedIndex], randomNumbers[clickedIndex] = randomNumbers[zeroIndex]][0];
this.setState({
randomNumbers: randomNumbers
});
this.updateDoneStatus();
}
checkValidClick (clickedIndex, indexes) {
var randomNumbers = this.state.randomNumbers;
for (var i = 0; i < indexes.length; i++) {
if (randomNumbers[indexes[i]] === 0) {
return indexes[i];
}
}
return -1;
}
getAdjacentIndexes (index, size) {
var adjacentElements = [];
var leftIndex = index - 1;
var rightIndex = index + 1;
var upIndex = index - size;
var downIndex = index + size;
if (leftIndex > -1 && (leftIndex % size) != (size - 1)) {
adjacentElements.push(leftIndex);
}
if (rightIndex < (size*size) && (rightIndex % size) != 0) {
adjacentElements.push(rightIndex);
}
if (upIndex > -1) {
adjacentElements.push(upIndex);
}
if (downIndex < (size*size)) {
adjacentElements.push(downIndex);
}
return adjacentElements;
}
updateDoneStatus () {
var randomNumbers = this.state.randomNumbers;
var success = true;
for (var i = 0, j = i + 1; j < randomNumbers.length - 1; ++i, ++j) {
if (randomNumbers[i] > randomNumbers[j]) {
success = false;
break;
}
}
if (success) {
this.setState({
resultMessage: "Done!!!"
});
}
}
gameLevel(e) {
this.setState({
level: e.currentTarget.valueAsNumber
});
}
render () {
var randomNumbers = this.state.randomNumbers;
var resultMessage = this.state.resultMessage;
var size = this.state.size;
var level = this.state.level;
var success = false;
if (resultMessage) {
success = true;
}
return (
<div id="game">
<h1>Tiles!!!</h1>
<div> Move the blank tile to the top left or bottom right corner
with all the tiles in asending order.</div>
<div className="clearfix">
<TilesFrame randomNumbers={randomNumbers}
tileClicked={this.tileClicked}
game={this}
validIndexes={this.getAdjacentIndexes(randomNumbers.indexOf(0), size)}
isGameEnded={success}
/>
<SliderFrame gameLevel={this.gameLevel}
level = {level}/>
<DoneFrame resultMessage={resultMessage}
resetGame={this.resetGame.bind(this)}
/>
</div>
</div>
)
}
}
ReactDOM.render(
<Game />,
document.getElementById("container"));
body {
font-family: 'Open Sans', sans-serif;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
width: 60%;
min-width: 400px;
}
#tiles-frame {
float:left;
padding:10px;
width: 60%;
display:block;
overflow:auto;
}
.col-xs-3 {
background-color: #BBBBBB;
border: 2px solid white;
padding-bottom: 20%;
position: relative;
text-align: center;
}
.number {
position:absolute;
z-index: 1;
background-color: #888888;
width: 25px;
height: 25px;
line-height: 25px;
top:35%;
left: 35%;
border-radius: 50%;
}
.col-xs-3:hover {
background-color: #333333;
}
.col-xs-3.valid-true:active {
background-color: #EEEEEE;
}
#done-frame {
float: left;
width:60%;
}
.valid-false {
cursor: not-allowed;
}
.valid-true {
cursor: pointer;
}
#slider-frame {
width: 20%;
text-align: center;
float: left;
}
input[type=range]
{
writing-mode: bt-lr; /* IE */
-webkit-appearance: slider-vertical; /* WebKit */
-moz-appearance: slider-vertical; /* WebKit */
width: 8px;
margin-top: 30px;
margin-left: 50%;
}