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
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react", "@babel/plugin-proposal-private-property-in-object"]
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"node-sass": "^4.14.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1",
"react-scripts": "4.0.3",
"styled-components": "^5.1.1",
"web-vitals": "^0.2.4"
},
"scripts": {
Expand Down
Binary file added public/apple.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 public/avocado.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 public/banana.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 public/corn.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 public/lemon.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 public/lettuce.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 public/onion.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 public/strawberry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
224 changes: 223 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,225 @@
// import styled from 'styled-components';
/* eslint-disable no-eval */
import React from 'react';
import {
GAME_STATE_NEW,
GAME_STATE_RUNNING,
GAME_STATE_FINISHED,
CARD_STATE_IDLE,
CARD_STATE_ACTIVE,
CARD_STATE_FOUND,
FRUIT_APPLE,
FRUIT_AVOCADO,
FRUIT_BANANA,
FRUIT_CORN,
FRUIT_LEMON,
FRUIT_LETTUCE,
FRUIT_ONION,
FRUIT_STRAWBERRY,
TIME_CARD_ACTIVE
} from './constants'
import './App.scss';

export default function Memory() {
return <main>Votre Memory ici</main>;
const [gameState, setGameState] = React.useState(GAME_STATE_NEW);

const [seconds, setSeconds] = React.useState(0);
const [minutes, setMinutes] = React.useState(0);

const [cardsShowed, setCardsShowed] = React.useState(0);
const [hitsNumber, setHitsNumber] = React.useState(0);

/* eslint-disable no-unused-vars */
const [caseA1State, setCaseA1State] = React.useState({ name: 'A1', state: CARD_STATE_IDLE, value: FRUIT_LETTUCE });
const [caseA2State, setCaseA2State] = React.useState({ name: 'A2', state: CARD_STATE_IDLE, value: FRUIT_ONION });
const [caseA3State, setCaseA3State] = React.useState({ name: 'A3', state: CARD_STATE_IDLE, value: FRUIT_AVOCADO });
const [caseA4State, setCaseA4State] = React.useState({ name: 'A4', state: CARD_STATE_IDLE, value: FRUIT_LEMON });

const [caseB1State, setCaseB1State] = React.useState({ name: 'B1', state: CARD_STATE_IDLE, value: FRUIT_LETTUCE });
const [caseB2State, setCaseB2State] = React.useState({ name: 'B2', state: CARD_STATE_IDLE, value: FRUIT_ONION });
const [caseB3State, setCaseB3State] = React.useState({ name: 'B3', state: CARD_STATE_IDLE, value: FRUIT_APPLE });
const [caseB4State, setCaseB4State] = React.useState({ name: 'B4', state: CARD_STATE_IDLE, value: FRUIT_LEMON });

const [caseC1State, setCaseC1State] = React.useState({ name: 'C1', state: CARD_STATE_IDLE, value: FRUIT_STRAWBERRY });
const [caseC2State, setCaseC2State] = React.useState({ name: 'C2', state: CARD_STATE_IDLE, value: FRUIT_BANANA });
const [caseC3State, setCaseC3State] = React.useState({ name: 'C3', state: CARD_STATE_IDLE, value: FRUIT_STRAWBERRY });
const [caseC4State, setCaseC4State] = React.useState({ name: 'C4', state: CARD_STATE_IDLE, value: FRUIT_CORN });

const [caseD1State, setCaseD1State] = React.useState({ name: 'D1', state: CARD_STATE_IDLE, value: FRUIT_CORN });
const [caseD2State, setCaseD2State] = React.useState({ name: 'D2', state: CARD_STATE_IDLE, value: FRUIT_BANANA });
const [caseD3State, setCaseD3State] = React.useState({ name: 'D3', state: CARD_STATE_IDLE, value: FRUIT_APPLE });
const [caseD4State, setCaseD4State] = React.useState({ name: 'D4', state: CARD_STATE_IDLE, value: FRUIT_AVOCADO });
/* eslint-enable no-unused-vars */

const allCases = [
caseA1State,
caseA2State,
caseA3State,
caseA4State,
caseB1State,
caseB2State,
caseB3State,
caseB4State,
caseC1State,
caseC2State,
caseC3State,
caseC4State,
caseD1State,
caseD2State,
caseD3State,
caseD4State
];

// increment second each second
React.useEffect(() => {
if (gameState === GAME_STATE_RUNNING) {
const interval = setInterval(() => {
setSeconds(seconds => {
if (seconds === 59) {
return 0;
} else {
return seconds + 1;
}
});
}, 1000);

return () => clearInterval(interval);
}
}, [gameState]);

// increment minutes each minute
React.useEffect(() => {
if (gameState === GAME_STATE_RUNNING) {
const minutesInterval = setInterval(() => {
setMinutes(minutes => minutes + 1);
}, 60000);
return () => clearInterval(minutesInterval);
}
}, [gameState]);

// set game status finished if all pairs found
React.useEffect(() => {
let gameFinished = true;
allCases.forEach(oneCase => {
if (oneCase.state !== CARD_STATE_FOUND) {
gameFinished = false;
}
});

gameFinished && setGameState(GAME_STATE_FINISHED);
}, allCases); // eslint-disable-line react-hooks/exhaustive-deps

const reset = () => {
setSeconds(0);
setMinutes(0);
setHitsNumber(0);
setGameState(GAME_STATE_NEW);

// map all cases to set card state idle
// ex. setCaseA1State({...caseA1State, state: CARD_STATE_IDLE});
allCases.forEach(oneCase => {
eval(`setCase${oneCase.name}State`)({ ...eval(`case${oneCase.name}State`), state: CARD_STATE_IDLE})
});
}


const memoryTableStatusClassname = (status) => {
switch (status.state) {
case CARD_STATE_IDLE:
return 'memoryTableItemIdle';
case CARD_STATE_ACTIVE:
return 'memoryTableItemActive';
case CARD_STATE_FOUND:
return 'memoryTableItemFound';
default:
return 'memoryTableItemIdle';
}
}

const displayCard = (setCaseState, caseState) => {
if (gameState === GAME_STATE_NEW) {
setGameState(GAME_STATE_RUNNING);
}

setCaseState({...caseState, state: CARD_STATE_ACTIVE});

if (cardsShowed + 1 < 2) {
setCardsShowed(cardsShowed + 1);

} else {
setCardsShowed(0);
setHitsNumber(hitsNumber + 1);

let pairFound = false

// map all cases to set card state found if pairs
allCases.forEach(oneCase => {
const oneCaseState = eval(`case${oneCase.name}State`);
const setOneCaseState = eval(`setCase${oneCase.name}State`);

// if other card active and not the actual one
if (oneCaseState.state === CARD_STATE_ACTIVE && oneCaseState.name !== caseState.name) {
// if values match, set status to found for the two cards
if (oneCaseState.value === caseState.value) {
setTimeout(() => { setOneCaseState({...oneCaseState, state: CARD_STATE_FOUND}); }, TIME_CARD_ACTIVE);
setTimeout(() => { setCaseState({...caseState, state: CARD_STATE_FOUND}); }, TIME_CARD_ACTIVE);
pairFound = true;
} else {
// else, set card to idle
setTimeout(() => { setOneCaseState({...oneCaseState, state: CARD_STATE_IDLE}); }, TIME_CARD_ACTIVE);
}
}
});

// if no pair found, set actual card to idle
!pairFound && setTimeout(() => { setCaseState({...caseState, state: CARD_STATE_IDLE}) }, TIME_CARD_ACTIVE);
}
}

return (
<main>
<div className="header">
<div className="headerInfos">
<div className="headerInfosTime">
<h2 className="headerInfosTimeTitle">
Temps
</h2>
<p className="headerInfosTimeClock">
{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}
</p>
</div>
<div className="headerInfosHits">
<h2 className="headerInfosHitsTitle">
Nombre de coups
</h2>
<p className="headerInfosHitsNumber">
{hitsNumber}
</p>
</div>
</div>
<p className="headerInstructions">
Cliquez sur une carte pour commencer
</p>
</div>
<div className="memoryTable">
{allCases.map(oneCase => {
const oneCaseState = eval(`case${oneCase.name}State`);
const setOneCaseState = eval(`setCase${oneCase.name}State`);

// bind all cases
return (
<button
key={oneCaseState.name}
disabled={oneCaseState.state === CARD_STATE_FOUND}
className={`memoryTableItem ${memoryTableStatusClassname(oneCaseState)}`}
onClick={() => displayCard(setOneCaseState, oneCaseState)}
>
<img src={`${oneCaseState.value}.png`} alt={oneCaseState.value} />
</button>
)
})}
</div>
<button className="resetButton" onClick={() => reset()}>Réinitialiser</button>
</main>
);
}
94 changes: 94 additions & 0 deletions src/App.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
$border-radius: 3px;

.header {
position: absolute;
top: 0;
}

.header {
&Infos {
display: flex;
width: 98vw;
justify-content: space-between;

&Time {
&Title {
color: grey;
font-size: 1.25rem;
}

&Clock {
font-size: 1.75rem;
font-weight: bold;
}
}

&Hits {
&Title {
color: grey;
font-size: 1.25rem;
}

&Number {
font-size: 1.75rem;
font-weight: bold;
}
}
}

&Instructions {
text-align: center;
}
}

.memoryTable {
margin-top: 15vh;
margin-left: auto;
margin-right: auto;
display: grid;
grid-template-columns: 10rem 10rem 10rem 10rem;
grid-template-rows: 10rem 10rem 10rem 10rem;
gap: 2%;
&Item {
cursor: pointer;
padding: 10%;
background-color: white;
border-radius: $border-radius;
border: none;

&:disabled {
cursor: not-allowed;
}

&Idle {
img {
display: none;
}
}

&Active {
img {
display: block;
}
}

&Found {
opacity: 0.6;
}

img {
width: 8.5rem;
height: 8.5rem;
}
}
}

.resetButton {
margin-top: 6vh;
background-color: rgb(9, 211, 172);
border: none;
padding: 7.5px;
width: 7.5rem;
font-size: 1rem;
border-radius: $border-radius;
}
Loading