<div id="app"></div>
.memory-game {
height: 300px;
width: 300px;
margin: auto;
h1 {
text-align: center;
}
@media screen and (min-width: 480px) {
height: 400px;
width: 400px;
}
@media screen and (min-width: 720px) {
height: 500px;
width: 500px;
}
}
.game {
text-align: center;
background-color: #2196F3;
height: 100%;
&__board {
display: grid;
grid-template-columns: auto auto auto auto auto auto;
padding: 5px;
gap: 5px;
margin-bottom: 1rem;
&__card {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid transparent;
border-radius: 4px;
aspect-ratio: 1;
position: relative;
&--content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 30px;
}
&:hover {
cursor: pointer;
opacity: 0.8;
}
}
}
}
.show {
visibility: visible;
}
.hide {
visibility: hidden;
}
View Compiled
/*
* https://frontendeval.com/questions/memory-game
*
* Create a card-matching memory game
*/
const Card = ({ key, num, choose, matched }) => {
handleClick = (e) => {
const isPairChosen = choose();
e.target.firstChild.classList.remove('hide');
e.target.classList.add('show');
}
return (
<div>
{
matched ? <></> : (
<div
className={`game__board__card`}
onClick={ handleClick }
>
<div className='game__board__card--content hide'>{ num }</div>
</div>
)
}
</div>
)
}
const Board = ({ rows, cols }) => {
const totalCards = rows * cols;
const [board, setBoard] = React.useState([]);
const [c1, setC1] = React.useState(null);
const [c2, setC2] = React.useState(null);
const [currentKey, setCurrentKey] = React.useState(null);
const [gameFinished, setGameFinished] = React.useState(false);
React.useEffect(() => {
const cards = shuffle(generateCards());
setBoard(cards);
}, [])
React.useEffect(() => {
// c1 & c2, c1 != c2 (hide and reset turn)
// c1 & c2, c1 == c2 (disable and reset turn)
if (c1 && c2) {
document.querySelector('.game').style.pointerEvents = 'none';
if (c1 !== c2) {
setTimeout(() => {
hideCards();
resetTurn();
document.querySelector('.game').style.pointerEvents = 'auto';
}, 3000);
} else { // they are equal, now remove them from the game
setTimeout(() => {
disableCards();
resetTurn();
checkWin();
document.querySelector('.game').style.pointerEvents = 'auto';
}, 3000);
}
}
}, [c1, c2])
hideCards = () => {
document.querySelectorAll('.show').forEach(el => {
el.firstChild.classList.remove('show');
el.firstChild.classList.add('hide');
});
}
disableCards = () => {
// console.log('disabling cards');
setBoard(prevBoard => {
return prevBoard.map(card => {
const { key, num, choose } = card.props;
if (num === c1) {
return <Card key={key} num={ num } choose={ choose } show={ false } matched={ true } />;
} else {
return card;
}
})
})
}
resetTurn = () => {
// console.log('resetting turn', c1, c2);
setC1(null);
setC2(null);
}
generateNumber = (i) => {
return i % 2 == 0 ? Math.ceil((i+1)/2) : Math.ceil(i/2);
}
choose = (key, num) => {
// console.log('clicked', num, 'key', key);
if (key === currentKey || c2) {
return;
}
setCurrentKey(key);
c1 ? setC2(num) : setC1(num);
}
checkWin = () => {
let isFinished = true;
for (const card of board) {
if (!card.props.matched) {
isFinished = false;
break;
}
}
if (isFinished) { setGameFinished(true); }
}
resetGame = () => {
setCurrentKey(null);
setC1(null);
setC2(null);
setGameFinished(false);
setBoard(shuffle(generateCards()));
}
generateCards = () => {
return Array.from({ length: totalCards }, (_, i) => {
const value = generateNumber(i);
return (
<Card
key={i}
num={ value }
choose={ () => choose(i, value) }
matched={ false }
/>
)
})
}
shuffle = (unsortedArray) => {
const shuffled = unsortedArray
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
return shuffled;
}
return (
<div className='game'>
<div className='game__board'>
{ board.map(card => card) }
</div>
{ gameFinished && <button onClick={ resetGame }>Play Again</button> }
</div>
)
}
const MemoryGame = () => {
return (
<div className='memory-game'>
<h1>Memory Game 🧠</h1>
<Board rows={6} cols={6} />
</div>
)
}
ReactDOM.render(<MemoryGame />, document.getElementById('app'));
View Compiled
This Pen doesn't use any external CSS resources.