Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SQL from "./components/SQL";
import Landingpage from "./pages/michael/Landingpage";
import CsharpQuiz1 from "./pages/michael/mini-game/Quiz1";
import AuthenticationPage from "./pages/authentication/Loginpage";
import MatchingCardGame from "./pages/matching-card-game/Card";

const router = createBrowserRouter([
{
Expand Down Expand Up @@ -52,6 +53,10 @@ const router = createBrowserRouter([
path: "/games/CsharpQuiz1",
element: <CsharpQuiz1 />,
},
{
path: "/games/matchingGame",
element: <MatchingCardGame />,
},
]);

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
Expand Down
66 changes: 66 additions & 0 deletions src/pages/matching-card-game/Card.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital@1&display=swap');


.matching-card-body {
box-sizing: border-box;
display: flex;
flex-direction: column;
margin: 0;
font-family: Montserrat, sans-serif;
min-height: 100vh;
border: 10px solid white;
background-color: white;
}


#main {
display: flex;
flex: 1;
}

#main > .matching-card-cards {
flex: 1;
background: rgb(106, 64, 117);
padding: 0em; /* reduce padding */
}

#main > .matching-card-input {
flex: 0 0 20%;
background: #d0c1a6;
order: 2;
padding: 1.5em; /* reduce padding */
}

.matching-card-header{
background: #ffffff;
height: 4vh;
text-align: center;
padding: 0.1em;
}

.matching-card-cards, .matching-card-input{
padding: 1em;
}


#main .matching-card-cards .card-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 20px;
}

.NewGame {
border: 2px solid #d682e1;
transition-duration: 0.4s;
}

.NewGame:hover {
background-color: white;
color: black;
}






194 changes: 194 additions & 0 deletions src/pages/matching-card-game/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import React, { useEffect, useState } from "react";
import "./Card.css";
import SingleCard from "./component/SingleCard";

import cSharp from "./img/cSharp.png";
import Java1 from "./img/Java-1.png";
import Java2 from "./img/Java-2.png";
import JS1 from "./img/JS-1.png";
import JS2 from "./img/JS-2.png";
import Python1 from "./img/Python-1.png";
import Python2 from "./img/Python-2.png";
import Php1 from "./img/Php-1.png";
import SQL1 from "./img/SQL-1.png";
import SQL2 from "./img/SQL-2.png";

const cardClassName = "card-images";

const cardImages = [
{ id: 1, src: cSharp, matched: false, className: cardClassName },
{ id: 2, src: Java1, matched: false, className: cardClassName },
{ id: 3, src: JS1, matched: false, className: cardClassName },
{ id: 4, src: Python1, matched: false, className: cardClassName },
{ id: 5, src: Php1, matched: false, className: cardClassName },
{ id: 6, src: SQL1, matched: false, className: cardClassName },
];

const cardImages2 = [
{ id: 111, src: cSharp, matched: false, className: cardClassName },
{ id: 222, src: Java2, matched: false, className: cardClassName },
{ id: 333, src: JS2, matched: false, className: cardClassName },
{ id: 444, src: Python2, matched: false, className: cardClassName },
{ id: 555, src: Php1, matched: false, className: cardClassName },
{ id: 666, src: SQL2, matched: false, className: cardClassName },
];

function App() {
const [cards, setCards] = useState<Card[]>([]);
const [turns, setTurns] = useState<number>(0);
const [choiceOne, setChoiceOne] = useState<Card | null>(null);
const [choiceTwo, setChoiceTwo] = useState<Card | null>(null);
const [disabled, setDisabled] = useState<boolean>(false);
const [time, setTime] = useState<number>(0);
const [score, setScore] = useState<number>(0);

//shuffle cards
const shuffleCards = () => {
const shuffledCards = [...cardImages, ...cardImages2]
.sort(() => Math.random() - 0.5)
.map((card) => ({ ...card }));

setChoiceOne(null);
setChoiceTwo(null);
setCards(shuffledCards);
setTurns(0);
setTime(0);
};

type Card = {
id: number;
src: string;
matched: boolean;
};

// handle a choice
const handleChoice = (card: Card) => {
if (!choiceOne) {
setChoiceOne(card);
} else if (!choiceTwo && choiceOne.id !== card.id) {
setChoiceTwo(card);
}
};

useEffect(() => {
const matchingPairs: { [key: number]: number } = {
1: 111,
2: 222,
3: 333,
4: 444,
5: 555,
6: 666,
// Add more matching pairs as needed
};

//handles card matching
const handleCardMatching = () => {
if (choiceOne && choiceTwo) {
setDisabled(true);
if (
choiceOne.id === choiceTwo.id ||
matchingPairs[choiceOne.id] === choiceTwo.id ||
matchingPairs[choiceTwo.id] === choiceOne.id
) {
setCards((prevCards) =>
prevCards.map((card) => {
if (card.id === choiceOne.id || card.id === choiceTwo.id) {
return { ...card, matched: true };
} else {
return card;
}
})
);
resetTurn();
} else {
setTimeout(() => {
resetTurn();
setChoiceOne(null);
setChoiceTwo(null);
}, 1000);
}
}
};

handleCardMatching();
const calculatedScore = totalScore(time, turns);
setScore(calculatedScore);
}, [choiceOne, choiceTwo, time, turns]);

// add timer
useEffect(() => {
const timerId = setInterval(() => {
setTime((prevTime) => prevTime + 1);
}, 1000);
if (cards.every((card) => card.matched)) {
clearInterval(timerId);
}
return () => clearInterval(timerId);
}, [cards]);

// reset choices and increase turn
const resetTurn = () => {
setChoiceOne(null);
setChoiceTwo(null);
setTurns((prevTurns) => prevTurns + 1);
setDisabled(false);
};

//game start
useEffect(() => {
shuffleCards();
}, []);

//calculating scores

const totalScore = (time: number, turns: number): number => {
const baseScore = 1000;
const timePenalty = 2;
const turnPenalty = 3;

const timePenaltyTotal = time * timePenalty;
const turnPenaltyTotal = turns * turnPenalty;

const score = Math.max(baseScore - timePenaltyTotal - turnPenaltyTotal, 0);

return score;
};

return (
<div>
<div>
<div className="matching-card-body">
<div className="matching-card-header">MATCHING CARDS</div>
<div id="main">
<div className="matching-card-cards">
<div className="card-grid">
{/* adding the shuffled cards */}
{cards.map((card) => (
<SingleCard
key={card.id}
card={card}
handleChoice={handleChoice}
flipped={
card === choiceOne || card === choiceTwo || card.matched
}
disabled={disabled}
/>
))}
</div>
</div>
<div className="matching-card-input">
<button className="NewGame" onClick={shuffleCards}>
New Game
</button>
<p>Turns: {turns}</p>
<p>Time: {time}</p> <br /> <br />
<p>Score: {score}</p>
</div>
</div>
</div>
</div>
</div>
);
}

export default App;
Binary file added src/pages/matching-card-game/component/Cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions src/pages/matching-card-game/component/SingleCard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.matching-card{
position: relative;
}

.matching-card img{
width: 140px !important;
display: block;
border: 2px solid #000000;
border-radius: 6px;
height: 200px;;
}

.matching-card .matching-card-front {
transform: rotateY(90deg);
transition: all ease-in 0.1s;
position: absolute;
}
.flipped .matching-card-front {
transform: rotateY(0deg);
transition-delay: 0.2s;
}

.matching-card .matching-card-back {
transition: all ease-in 0.2s;
transition-delay: 0.2s;
}
.flipped .matching-card-back {
transform: rotateY(90deg);
transition-delay: 0s;
}
38 changes: 38 additions & 0 deletions src/pages/matching-card-game/component/SingleCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import "./SingleCard.css";
import Cover from "./Cover.png";

interface Props {
card: {
id: number;
src: string;
matched: boolean;
};
handleChoice: (card: { id: number; src: string; matched: boolean }) => void;
flipped: boolean;
disabled: boolean;
}

const SingleCard: React.FC<Props> = ({
card,
handleChoice,
flipped,
disabled,
}) => {
const handleClick = () => {
if (!disabled) {
handleChoice(card);
}
};

return (
<div className="matching-card">
<div className={flipped ? "flipped" : ""}>
<img className="matching-card-front" src={card.src} alt="front" />
<img className="matching-card-back" src={Cover} onClick={handleClick} alt="back" />
</div>
</div>
);
};

export default SingleCard;
Binary file added src/pages/matching-card-game/img/JS-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/JS-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/Java-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/Java-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/Php-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/Python-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/Python-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/SQL-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/SQL-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pages/matching-card-game/img/cSharp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/pages/matching-card-game/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ReactDOM from "react-dom";

import Card from "./Card";

ReactDOM.render(<Card />, document.getElementById("root"));