Skip to content

Commit 0eb089f

Browse files
committed
step9
1 parent 563a309 commit 0eb089f

File tree

1 file changed

+176
-1
lines changed

1 file changed

+176
-1
lines changed

src/pages/Step9Page.jsx

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,185 @@ import utils from '../helpers/utils';
77
const Step9Page = () => {
88
return (
99
<Frame title="Step9: Resetting a React component">
10-
<p>Here comes something in Frame</p>
10+
<GameGenerator />
1111
</Frame>
1212
);
1313
};
1414
export default Step9Page;
1515

1616
/** https://jscomplete.com/playground/rs3.9 */
17+
const GameStatus = {
18+
NEW: 'NEW',
19+
CHALLENGE: 'CHALLENGE',
20+
PLAYING: 'PLAYING',
21+
WON: 'WON',
22+
LOST: 'LOST',
23+
};
24+
25+
const CellStatus = {
26+
NORMAL: 'white',
27+
HIGHLIGHT: 'lightblue',
28+
CORRECT: 'lightgreen',
29+
WRONG: 'pink',
30+
};
31+
32+
const Messages = {
33+
NEW: 'You will have a few seconds to memorize the blue random cells',
34+
CHALLENGE: 'Remember these blue cells now',
35+
PLAYING: 'Which cells were blue?',
36+
WON: 'Victory!',
37+
LOST: 'Game Over',
38+
};
39+
40+
const Cell = ({ width, gameStatus, isChallenge, isPicked, onClick }) => {
41+
let cellStatus = CellStatus.NORMAL;
42+
if (gameStatus !== GameStatus.NEW) {
43+
if (isPicked) {
44+
cellStatus = isChallenge ? CellStatus.CORRECT : CellStatus.WRONG;
45+
} else if (
46+
isChallenge &&
47+
(gameStatus === GameStatus.CHALLENGE || gameStatus === GameStatus.LOST)
48+
) {
49+
cellStatus = CellStatus.HIGHLIGHT;
50+
}
51+
}
52+
return (
53+
<div
54+
className="cell"
55+
style={{ width: `${width}%`, backgroundColor: cellStatus }}
56+
onClick={onClick}
57+
/>
58+
);
59+
};
60+
61+
const Footer = ({ gameStatus, countdown, startGame, resetGame }) => {
62+
const buttonAreaContent = () => {
63+
switch(gameStatus) {
64+
case GameStatus.NEW:
65+
return <button onClick={startGame}>Start Game</button>;
66+
case GameStatus.CALLENGE:
67+
// fall-through
68+
case GameStatus.PLAYING:
69+
return countdown;
70+
case GameStatus.WON:
71+
// fall-through
72+
case GameStatus.LOST:
73+
return <button onClick={resetGame}>Play Again</button>;
74+
}
75+
};
76+
return (
77+
<>
78+
<div className="message">{Messages[gameStatus]}</div>
79+
<div className="button">{buttonAreaContent()}</div>
80+
</>
81+
);
82+
};
83+
84+
const GameSession = ({
85+
cellIds,
86+
challengeCellIds,
87+
cellWidth,
88+
challengeSize,
89+
challengeSeconds,
90+
playSeconds,
91+
maxWrongAttempts,
92+
}) => {
93+
const [gameStatus, setGameStatus] = useState(GameStatus.NEW);
94+
const [pickedCellIds, setPickedCellIds] = useState([]);
95+
const [countdown, setCountdown] = useState(playSeconds);
96+
97+
useEffect(() => {
98+
let timerId;
99+
if (gameStatus === GameStatus.CHALLENGE) {
100+
timerId = setTimeout(
101+
() => setGameStatus(GameStatus.PLAYING),
102+
1000 * challengeSeconds
103+
);
104+
}
105+
if (gameStatus === GameStatus.PLAYING) {
106+
timerId = setInterval(() => {
107+
setCountdown(countdown => {
108+
if (countdown === 1) {
109+
clearTimeout(timerId);
110+
setGameStatus(GameStatus.LOST);
111+
}
112+
return countdown - 1;
113+
});
114+
}, 1000);
115+
}
116+
return () => clearTimeout(timerId);
117+
}, [challengeSeconds, gameStatus]);
118+
119+
React.useEffect(() => {
120+
const [correctPicks, wrongPicks] = utils.arrayCrossCounts(
121+
pickedCellIds,
122+
challengeCellIds
123+
);
124+
if (correctPicks === challengeSize) {
125+
setGameStatus(GameStatus.WON);
126+
}
127+
if (wrongPicks === maxWrongAttempts) {
128+
setGameStatus(GameStatus.LOST);
129+
}
130+
}, [pickedCellIds]);
131+
132+
const pickCell = cellId => {
133+
if (gameStatus === GameStatus.PLAYING) {
134+
setPickedCellIds(pickedCellIds => {
135+
if (pickedCellIds.includes(cellId)) {
136+
return pickedCellIds;
137+
}
138+
return [...pickedCellIds, cellId];
139+
});
140+
}
141+
};
142+
143+
const resetGame = () => {
144+
setGameStatus(GameStatus.NEW);
145+
setPickedCellIds([]);
146+
setCountdown(playSeconds);
147+
};
148+
149+
return (
150+
<div className="game">
151+
<div className="grid">
152+
{cellIds.map(cellId => (
153+
<Cell
154+
key={cellId}
155+
width={cellWidth}
156+
gameStatus={gameStatus}
157+
isChallenge={challengeCellIds.includes(cellId)}
158+
isPicked={pickedCellIds.includes(cellId)}
159+
onClick={() => pickCell(cellId)}
160+
/>
161+
))}
162+
</div>
163+
<Footer
164+
gameStatus={gameStatus}
165+
countdown={countdown}
166+
startGame={() => setGameStatus(GameStatus.CHALLENGE)}
167+
resetGame={resetGame}
168+
/>
169+
</div>
170+
);
171+
};
172+
173+
const GameGenerator = () => {
174+
const gridSize = 5;
175+
const challengeSize = 6;
176+
const cellIds = utils.createArray(gridSize * gridSize);
177+
const cellWidth = 100 / gridSize;
178+
const challengeCellIds = utils.sampleArray(cellIds, challengeSize);
179+
180+
return (
181+
<GameSession
182+
cellIds={cellIds}
183+
challengeCellIds={challengeCellIds}
184+
cellWidth={cellWidth}
185+
challengeSize={challengeSize}
186+
challengeSeconds={3}
187+
playSeconds={10}
188+
maxWrongAttempts={3}
189+
/>
190+
);
191+
};

0 commit comments

Comments
 (0)