DEV Community

ZeeshanAli-0704
ZeeshanAli-0704

Posted on

React Coding Challenge : Card Flip Game

import "./styles.css";
import React, { useState, useEffect } from "react";

const values = [1, 2, 3, 4, 5];

type Card = {
  id: number;
  value: number;
  isFlipped: boolean;
  isMatched: boolean;
};

function getShuffledCards(): Card[] {
  const deck = [...values, ...values].map((value, index) => ({
    id: index,
    value,
    isFlipped: false,
    isMatched: false,
  }));

  // Shuffle
  return deck.sort(() => Math.random() - 0.5);
}

export default function App() {
  const [cards, setCards] = useState<Card[]>(getShuffledCards);
  const [selected, setSelected] = useState<number[]>([]);

  useEffect(() => {
    if (selected.length !== 2) return;

    const [first, second] = selected;
    const card1 = cards[first];
    const card2 = cards[second];

    if (card1.value === card2.value) {
      setCards((prev) =>
        prev.map((card, i) =>
          i === first || i === second ? { ...card, isMatched: true } : card
        )
      );
      setSelected([]);
    } else {
      // flip back after delay
      setTimeout(() => {
        setCards((prev) =>
          prev.map((card, i) =>
            i === first || i === second ? { ...card, isFlipped: false } : card
          )
        );
        setSelected([]);
      }, 800);
    }
  }, [selected, cards]);

  const handleCardClick = (index: number) => {
    if (
      cards[index].isFlipped ||
      cards[index].isMatched ||
      selected.length === 2
    )
      return;

    setCards((prev) =>
      prev.map((card, i) => (i === index ? { ...card, isFlipped: true } : card))
    );

    setSelected((prev) => [...prev, index]);
  };

  return (
    <div className="App">
      <div className="grid">
        {cards.map((card, index) => (
          <div
            key={card.id}
            className={`card ${
              card.isFlipped || card.isMatched ? "flipped" : ""
            }`}
            onClick={() => handleCardClick(index)}
          >
            <div className="card-inner">
              {card.isFlipped || card.isMatched ? card.value : "?"}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode
.App {
  font-family: sans-serif;
  padding: 20px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(4, 80px);
  gap: 12px;
  justify-content: center;
}

.card {
  width: 80px;
  height: 80px;
  background: #2c3e50;
  color: white;
  font-size: 24px;
  cursor: pointer;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  transition: transform 0.3s, background 0.3s;
}

.card.flipped {
  background: #27ae60;
  transform: scale(1.05);
}

.card-inner {
  font-weight: bold;
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)