<div id="app"></div>
.play-area__row {
  display: flex;
}
.play-area__card {
  display: inline-block;
  outline: 1px solid black;
  min-height: 20px;
  min-width: 20px;
  text-align: center;
  padding: 5px;
  color: lightblue;
  background: lightblue;
  margin: 5px;
}
.play-area__card--removed {
  background: unset;
  outline: unset;
}
.play-area__card--shown {
  color: black;
}
/*
 * https://frontendeval.com/questions/memory-game
 *
 * Create a card-matching memory game
 */

const App = () => {
  return (
    <React.Fragment>
      <p>Memory game</p>
      <MemoryGame size={4} />
    </React.Fragment>
  );
};
const createGame = (size) => {
  const cardCount = size * size;
  const cardValues = new Array();
  for (let i = 1; i <= cardCount / 2; i++) {
    cardValues.push(i);
    cardValues.push(i);
  }

  const game = new Array();
  for (let i = 0; i < size; i++) {
    game.push(new Array(size));
  }

  for (let i = 0; i < size; i++) {
    for (let j = 0; j < size; j++) {
      const rand = Math.round(Math.random() * cardValues.length);
      if (cardValues[rand] !== undefined) {
        const [num] = cardValues.splice(rand, 1);
        game[i][j] = num;
      } else {
        j--;
      }
    }
  }

  // const sum = game.reduce(
  //   (totalSum, row) => totalSum + row.reduce((rowSum, n) => rowSum + n, 0),
  //   0
  // );
  // const expectedSum = (cardCount / 2) * (1 + cardCount / 2);
  // const isOkay = sum === expectedSum;
  // console.log("isOkay", isOkay);

  return game;
};

function MemoryGame({ size }) {
  const [game, setGame] = React.useState(() => createGame(size));

  const isInProgress = game.some((row) => row.some((item) => !!item));

  return (
    <div>
      {(isInProgress && (
        <MemoryGamePlayArea game={game} setGame={setGame} />
      )) || <button onClick={() => setGame(createGame(size))}>Reset</button>}
    </div>
  );
}

function MemoryGamePlayArea({ game, setGame }) {
  const [firstCard, setFirstCard] = React.useState();
  const [secondCard, setSecondCard] = React.useState();
  const [disabled, setDisabled] = React.useState(false);

  const onCardClick = (i2, j2) => {
    if (!firstCard) setFirstCard([i2, j2]);
    else {
      setSecondCard([i2, j2]);
      setDisabled(true);

      setTimeout(() => {
        setDisabled(false);
        const [i1, j1] = firstCard;
        const num1 = game[i1][j1];
        const num2 = game[i2][j2];

        if (num1 === num2) {
          game[i1][j1] = null;
          game[i2][j2] = null;
          setGame(JSON.parse(JSON.stringify(game)));
        }

        setFirstCard();
        setSecondCard();
      }, 300);
    }
  };

  return (
    <section className="play-area">
      {game.map((row, i) => (
        <div className="play-area__row" key={i}>
          {row.map((num, j) => (
            <Card
              num={num}
              key={"" + i + j + num}
              onClick={disabled ? null : () => onCardClick(i, j)}
              show={
                (firstCard && firstCard[0] === i && firstCard[1] === j) ||
                (secondCard && secondCard[0] === i && secondCard[1] === j)
              }
            />
          ))}
        </div>
      ))}
    </section>
  );
}

function Card({ num, onClick, show }) {
  let className = show
    ? "play-area__card--shown"
    : !num
    ? "play-area__card--removed"
    : "";
  return (
    <span className={`play-area__card ${className}`} onClick={onClick}>
      {num}
    </span>
  );
}

ReactDOM.render(<App />, document.getElementById("app"));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js