<div id="app"></div>
.App {
  font-family: sans-serif;
  text-align: center;
}

.game {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 600px;
}

button {
  background-color: lightblue;
  border: none;
  border-radius: 5px;
  padding: 20px;
  font-size: 18px;
  box-shadow: none;
  width: 150px;
}

.cards {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
}

.card {
  height: 30px;
  width: 30px;
  background-color: lightblue;
  padding: 10px;
  font-size: 24px;
}

.hidden {
  color: rgba(0, 0, 0, 0);
}

.show {
  color: black;
}

.removed {
  visibility: hidden;
}

.win {
  margin-bottom: 30px;
}
/*
* https://frontendeval.com/questions/memory-game
*
* Create a card-matching memory game
*/

const NUM_LIST = () => {
  let list = [];
  for (let i = 1; i <= 18; i++) {
    list.push(i);
    list.push(i);
  }
  return list;
};

const App = () => {
  const [start, setStart] = React.useState("init");
  const [nums, setNums] = React.useState(NUM_LIST);
  const [solved, setSolved] = React.useState([]);
  const [opened, setOpened] = React.useState(null);

  const handleStart = () => {
    setStart("start");
    setNums(radomNums());
    setSolved([]);
  };

  const radomNums = () => {
    return nums.sort(() => Math.random() - 0.5);
  };

  const getClassName = (num, index) => {
    if (solved.includes(num)) return "removed";
    if (opened && opened[index] === num) return "show";
    return "hidden";
  };

  const openCard = (num, index) => {
    if (opened && Object.keys(opened).length === 2) return;

    if (opened === null) {
      setOpened({ [index]: num });
    } else if (Object.values(opened)[0] === num) {
      setOpened((pre) => ({ ...pre, [index]: num }));
      setTimeout(() => {
        setSolved((pre) => [...pre, num]);
        setOpened(null);
      }, 1000);
    } else {
      setOpened((pre) => ({ ...pre, [index]: num }));
      setTimeout(() => {
        setOpened(null);
      }, 1000);
    }
  };

  React.useEffect(() => {
    setNums(radomNums());
  }, []);

  React.useEffect(() => {
    if (solved.length === 18) {
      setStart("win");
    }
  }, [solved]);

  return (
    <div className="App">
      <h1>Memory game</h1>
      <div className="game">
        {start === "start" && (
          <div className="cards">
            {nums.map((num, index) => (
              <div
                key={index}
                className={`card ${getClassName(num, index)}`}
                onClick={() => openCard(num, index)}
              >
                {num}
              </div>
            ))}
          </div>
        )}
        <div>
          {start === "win" && (
            <div className="win">Congrats, you win the game!!!!</div>
          )}
          {start !== "start" && (
            <button onClick={handleStart}>
              {start === "win" ? "Play again" : "Play"}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

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