Skip to content

Commit 88ed3d2

Browse files
authoredApr 25, 2023
Add files via upload
0 parents  commit 88ed3d2

File tree

3 files changed

+311
-0
lines changed

3 files changed

+311
-0
lines changed
 

‎index.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Project_B_A</title>
8+
<link rel="stylesheet" href="style.css">
9+
10+
</head>
11+
12+
<body>
13+
<div class="game">
14+
<div class="controls">
15+
<button>start</button>
16+
<div class="stats">
17+
<div class="moves">0 moves</div>
18+
<div class="time"> : 0 sec</div>
19+
</div>
20+
</div>
21+
<div class="board-container">
22+
<div class="board" data-dimension="4"></div>
23+
<div class="win"> YOU WIN ! </div>
24+
</div>
25+
</div>
26+
27+
28+
29+
30+
<script src="script.js" defer></script>
31+
</body>
32+
</html>

‎script.js

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
const selectors = {
2+
boardContainer: document.querySelector('.board-container'),
3+
board: document.querySelector('.board'),
4+
moves: document.querySelector('.moves'),
5+
timer: document.querySelector('.timer'),
6+
start: document.querySelector('button'),
7+
win: document.querySelector('.win')
8+
}
9+
10+
const state = {
11+
gameStarted: false,
12+
flippedCards: 0,
13+
totalFlips: 0,
14+
totalTime: 0,
15+
loop: null
16+
}
17+
18+
const shuffle = array => {
19+
const clonedArray = [...array]
20+
21+
for (let index = clonedArray.length - 1; index > 0; index--) {
22+
const randomIndex = Math.floor(Math.random() * (index + 1))
23+
const original = clonedArray[index]
24+
25+
clonedArray[index] = clonedArray[randomIndex]
26+
clonedArray[randomIndex] = original
27+
}
28+
29+
return clonedArray
30+
}
31+
32+
const pickRandom = (array, items) => {
33+
const clonedArray = [...array]
34+
const randomPicks = []
35+
36+
for (let index = 0; index < items; index++) {
37+
const randomIndex = Math.floor(Math.random() * clonedArray.length)
38+
39+
randomPicks.push(clonedArray[randomIndex])
40+
clonedArray.splice(randomIndex, 1)
41+
}
42+
43+
return randomPicks
44+
}
45+
46+
const generateGame = () => {
47+
const dimensions = selectors.board.getAttribute('data-dimension')
48+
49+
if (dimensions % 2 !== 0) {
50+
throw new Error("The dimension of the board must be an even number.")
51+
}
52+
53+
const emojis = ['🥔', '🍒', '🥑', '🌽', '🥕', '🍇', '🍉', '🍌', '🥭', '🍍']
54+
const picks = pickRandom(emojis, (dimensions * dimensions) / 2)
55+
const items = shuffle([...picks, ...picks])
56+
const cards = `
57+
<div class="board" style="grid-template-columns: repeat(${dimensions}, auto)">
58+
${items.map(item => `
59+
<div class="card">
60+
<div class="card-front"></div>
61+
<div class="card-back">${item}</div>
62+
</div>
63+
`).join('')}
64+
</div>
65+
`
66+
67+
const parser = new DOMParser().parseFromString(cards, 'text/html')
68+
69+
selectors.board.replaceWith(parser.querySelector('.board'))
70+
}
71+
72+
const startGame = () => {
73+
state.gameStarted = true
74+
selectors.start.classList.add('disabled')
75+
76+
state.loop = setInterval(() => {
77+
state.totalTime++
78+
79+
selectors.moves.innerText = `${state.totalFlips} moves`
80+
selectors.timer.innerText = `time: ${state.totalTime} sec`
81+
}, 1000)
82+
}
83+
84+
const flipBackCards = () => {
85+
document.querySelectorAll('.card:not(.matched)').forEach(card => {
86+
card.classList.remove('flipped')
87+
})
88+
89+
state.flippedCards = 0
90+
}
91+
92+
const flipCard = card => {
93+
state.flippedCards++
94+
state.totalFlips++
95+
96+
if (!state.gameStarted) {
97+
startGame()
98+
}
99+
100+
if (state.flippedCards <= 2) {
101+
card.classList.add('flipped')
102+
}
103+
104+
if (state.flippedCards === 2) {
105+
const flippedCards = document.querySelectorAll('.flipped:not(.matched)')
106+
107+
if (flippedCards[0].innerText === flippedCards[1].innerText) {
108+
flippedCards[0].classList.add('matched')
109+
flippedCards[1].classList.add('matched')
110+
}
111+
112+
setTimeout(() => {
113+
flipBackCards()
114+
}, 1000)
115+
}
116+
117+
// If there are no more cards that we can flip, we won the game
118+
if (!document.querySelectorAll('.card:not(.flipped)').length) {
119+
setTimeout(() => {
120+
selectors.boardContainer.classList.add('flipped')
121+
selectors.win.innerHTML = `
122+
<span class="win-text">
123+
You won!<br />
124+
with <span class="highlight">${state.totalFlips}</span> moves<br />
125+
under <span class="highlight">${state.totalTime}</span> seconds
126+
</span>
127+
`
128+
129+
clearInterval(state.loop)
130+
}, 1000)
131+
}
132+
}
133+
134+
const attachEventListeners = () => {
135+
document.addEventListener('click', event => {
136+
const eventTarget = event.target
137+
const eventParent = eventTarget.parentElement
138+
139+
if (eventTarget.className.includes('card') && !eventParent.className.includes('flipped')) {
140+
flipCard(eventParent)
141+
} else if (eventTarget.nodeName === 'BUTTON' && !eventTarget.className.includes('disabled')) {
142+
startGame()
143+
}
144+
})
145+
}
146+
147+
generateGame();
148+
attachEventListeners();

‎style.css

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
@font-face {
2+
font-family: Fredoka;
3+
src: url(./FredokaOne-Regular.ttf);
4+
}
5+
6+
html {
7+
width: 100%;
8+
height: 100%;
9+
background: linear-gradient(325deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
10+
font-family: Fredoka;
11+
}
12+
13+
.game {
14+
position: absolute;
15+
top: 50%;
16+
left: 50%;
17+
transform: translate(-50%, -50%);
18+
}
19+
20+
.controls {
21+
display: flex;
22+
gap: 20px;
23+
margin-bottom: 20px;
24+
}
25+
26+
button {
27+
background: #282A3A;
28+
color: #FFF;
29+
border-radius: 5px;
30+
padding: 10px 20px;
31+
border: 0;
32+
cursor: pointer;
33+
font-family: Fredoka;
34+
font-size: 18pt;
35+
}
36+
37+
.disabled {
38+
color: #757575;
39+
}
40+
41+
.stats {
42+
color: #FFF;
43+
font-size: 14pt;
44+
}
45+
46+
.board-container {
47+
position: relative;
48+
}
49+
50+
.board,
51+
.win {
52+
border-radius: 5px;
53+
box-shadow: 0 25px 50px rgb(33 33 33 / 25%);
54+
background: linear-gradient(135deg, #6f00fc 0%,#fc7900 50%,#fcc700 100%);
55+
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
56+
backface-visibility: hidden;
57+
}
58+
59+
.board {
60+
padding: 20px;
61+
display: grid;
62+
grid-template-columns: repeat(4, auto);
63+
grid-gap: 20px;
64+
}
65+
66+
.board-container.flipped .board {
67+
transform: rotateY(180deg) rotateZ(50deg);
68+
}
69+
70+
.board-container.flipped .win {
71+
transform: rotateY(0) rotateZ(0);
72+
}
73+
74+
.card {
75+
position: relative;
76+
width: 100px;
77+
height: 100px;
78+
cursor: pointer;
79+
}
80+
81+
.card-front,
82+
.card-back {
83+
position: absolute;
84+
border-radius: 5px;
85+
width: 100%;
86+
height: 100%;
87+
background: #282A3A;
88+
transition: transform .6s cubic-bezier(0.4, 0.0, 0.2, 1);
89+
backface-visibility: hidden;
90+
}
91+
92+
.card-back {
93+
transform: rotateY(180deg) rotateZ(50deg);
94+
font-size: 28pt;
95+
user-select: none;
96+
text-align: center;
97+
line-height: 100px;
98+
background: #FDF8E6;
99+
}
100+
101+
.card.flipped .card-front {
102+
transform: rotateY(180deg) rotateZ(50deg);
103+
}
104+
105+
.card.flipped .card-back {
106+
transform: rotateY(0) rotateZ(0);
107+
}
108+
109+
.win {
110+
position: absolute;
111+
top: 0;
112+
left: 0;
113+
width: 100%;
114+
height: 100%;
115+
text-align: center;
116+
background: #FDF8E6;
117+
transform: rotateY(180deg) rotateZ(50deg);
118+
}
119+
120+
.win-text {
121+
position: absolute;
122+
top: 50%;
123+
left: 50%;
124+
transform: translate(-50%, -50%);
125+
font-size: 21pt;
126+
color: #282A3A;
127+
}
128+
129+
.highlight {
130+
color: #6f00fc;
131+
}

0 commit comments

Comments
 (0)
Please sign in to comment.