Arrange Game

This game can be a very nice brain exercise, which focuses on prompting your ability to arrange numbers in the right order in a very short time, this game depends on the level you choose. Start with 3 squares x 3, and see how it goes, then you can make it wider as you improve in this brain exercise.


Prepare Project for Arrange Game

To create the example project for this example, open command prompt, navigate to a convenient location, and run the command as shown below :

create-react-app example8

Now navigate to the project folder and add the React Select package to the project as shown below :

cd example8
npm i react-select

src\App.js
import React from "react";
import GameBody from "./GameBody";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <GameBody />
      </header>
    </div>
  );
}

export default App;

src\GameBody.js
import React from "react";
import WinnerMessage from "./WinnerMessage";
import StartMenu from "./StartMenu";

class GameBody extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      gameData: [],
      initialGameData: [],
      gameFinished: false,
      gameStarted: false,
      gameDimension: 0
    };

    this.onItemClick = this.onItemClick.bind(this);
    this.makeMove = this.makeMove.bind(this);
    this.changeCoordinates = this.changeCoordinates.bind(this);
    this.isGameFinished = this.isGameFinished.bind(this);
    this.setGameDimension = this.setGameDimension.bind(this);
  }

  onItemClick(row, cellIndex) {
    const rowIndex = this.state.gameData.indexOf(row);
    this.makeMove(cellIndex, rowIndex);
  }

  setGameDimension(selectedDimension) {
    let initialArray = [];
    for (let i = 0; i < Math.pow(selectedDimension.value, 2); i++) {
      initialArray[i] = i;
    }
    //delete 0 from the beggining and add to the end
    initialArray.shift();
    initialArray.push(0);
    this.setState(_ => ({
      gameStarted: true,
      gameDimension: selectedDimension.value,
      initialGameData: [...initialArray],
      gameData: initialArray
        .sort(() => Math.random() - 0.5)
        .reduce(
          (rows, key, index) =>
            (index % selectedDimension.value === 0
              ? rows.push([key])
              : rows[rows.length - 1].push(key)) && rows,
          []
        )
    }));
  }

  makeMove(cellIndex, rowIndex) {
    if (cellIndex > 0 && this.state.gameData[rowIndex][cellIndex - 1] === 0) {
      this.changeCoordinates(rowIndex, cellIndex - 1, rowIndex, cellIndex);
    } else if (
      rowIndex < this.state.gameData.length - 1 &&
      this.state.gameData[rowIndex + 1][cellIndex] === 0
    ) {
      this.changeCoordinates(rowIndex + 1, cellIndex, rowIndex, cellIndex);
    } else if (
      cellIndex < this.state.gameData.length - 1 &&
      this.state.gameData[rowIndex][cellIndex + 1] === 0
    ) {
      this.changeCoordinates(rowIndex, cellIndex + 1, rowIndex, cellIndex);
    } else if (
      rowIndex > 0 &&
      this.state.gameData[rowIndex - 1][cellIndex] === 0
    ) {
      this.changeCoordinates(rowIndex - 1, cellIndex, rowIndex, cellIndex);
    }
  }

  changeCoordinates(spaceRow, spaceCell, targetRow, targetCell) {
    this.setState(state => {
      let gameData = state.gameData;
      gameData[spaceRow][spaceCell] = gameData[targetRow][targetCell];
      gameData[targetRow][targetCell] = 0;
      return {
        gameData
      };
    });
  }

  isGameFinished() {
    const currentState = this.state.gameData.reduce((a, b) => a.concat(b));
    const initialState = this.state.initialGameData;
    return initialState.every(function(row, index) {
      return initialState[index] === currentState[index];
    });
  }

  renderGameData() {
    return this.state.gameData.map((row, index) => {
      return (
        <tr key={index}>
          {row.map((cell, index, row) => {
            return (
              <td key={index} onClick={_ => this.onItemClick(row, index)}>
                {cell !== 0 ? cell : ""}
              </td>
            );
          })}
        </tr>
      );
    });
  }

  componentDidUpdate(prevProps, prevState) {
    if (
      this.isGameFinished() &&
      this.state.gameStarted &&
      !this.state.gameFinished
    ) {
      this.setState(_ => ({
        gameFinished: true
      }));
    }
  }

  render() {
    return (
      <div>
        {!this.state.gameStarted && (
          <StartMenu setGameDimension={this.setGameDimension} />
        )}
        {!this.state.gameFinished && this.state.gameStarted && (
          <table>
            <tbody>{this.renderGameData()}</tbody>
          </table>
        )}
        {this.state.gameFinished && this.state.gameStarted && <WinnerMessage />}
      </div>
    );
  }
}

export default GameBody;

src\StartMenu.js
import React from "react";
import Select from "react-select";

class StartMenu extends React.Component {
  render() {
    const gameDimensions = [
      {
        label: "three",
        value: "3"
      },
      {
        label: "four",
        value: "4"
      },
      {
        label: "five",
        value: "5"
      },
      {
        label: "six",
        value: "6"
      }
    ];

    return (
      <div>
        <label className={"dimension-label"}>Choose game dimension</label>
        <br />
        <Select
          options={gameDimensions}
          onChange={this.props.setGameDimension}
        />
      </div>
    );
  }
}

export default StartMenu;

src\Wrapper.js
import React from "react";

class WinnerMessage extends React.Component {
  render() {
    return (
      <div className={"win-msg"}>
        <h1>Winner</h1>
      </div>
    );
  }
}

export default WinnerMessage;

src\WinnerMessage.js
import React from "react";

function Navbar(props) {
    return (
        <header className="container-fluid fixed-top" >
            <div className="row">
                <h1 className="col-sm-8">Celebrity Memory Game</h1>
                <nav className="col-sm-4">
                    <p>Score: <span>{props.currentScore}</span></p>
                    <p>Top Score: <span>{props.highScore}</span> </p>
                    {props.children}
                </nav>
            </div>
        </header>        
    )
}

export default Navbar;

src\App.css

Replace the placeholder content of App.css with given below content :

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 40vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: grey;
}

.App-link {
  color: #61dafb;
}

.dimension-label {
  color: white;
}

.winner-msg {
  font-size: 5rem;
}

table {
  color: white;
  border-collapse: collapse;
}

table td,
table th {
  border: 5px solid white;
  padding: 2vw;
  min-width: 3vw;
}

table tr:first-child td {
  border-top: 0;
}

table tr:last-child td {
  border-bottom: 0;
}

table tr td:first-child,
table tr th:first-child {
  border-left: 0;
}

table tr td:last-child,
table tr th:last-child {
  border-right: 0;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.win-msg {
  text-align: center;
  color: #000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  letter-spacing: 1px;
}

h1 {
  background-size: cover;
  color: #000;
  background-clip: text;
  text-transform: uppercase;
  font-size: 10rem;
  margin: 10px 0;
}

.white-mode {
  text-decoration: none;
  padding: 7px 10px;
  background-color: #122;
  border-radius: 3px;
  color: #FFF;
  transition: .35s ease-in-out;
  position: absolute;
  left: 15px;
  bottom: 15px;
  font-family: "Montserrat";
}


Most Helpful This Week