Skip to content

Commit 14da2eb

Browse files
committed
step11
1 parent 6763299 commit 14da2eb

File tree

1 file changed

+188
-1
lines changed

1 file changed

+188
-1
lines changed

src/pages/Step11Page.jsx

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,197 @@ import utils from '../helpers/utils';
77
const Step11Page = () => {
88
return (
99
<Frame title="Step11: Using custom Hooks">
10-
<p>Here comes something in Frame</p>
10+
<GameGenerator />
1111
</Frame>
1212
);
1313
};
1414
export default Step11Page;
1515

1616
/** https://jscomplete.com/playground/rs3.11 */
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+
autoStart,
93+
resetGame,
94+
}) => {
95+
const [gameStatus, setGameStatus] = useState(
96+
autoStart ? GameStatus.CHALLENGE : GameStatus.NEW
97+
);
98+
const [pickedCellIds, setPickedCellIds] = useState([]);
99+
const [countdown, setCountdown] = useState(playSeconds);
100+
101+
useEffect(() => {
102+
let timerId;
103+
if (gameStatus === GameStatus.CHALLENGE) {
104+
timerId = setTimeout(
105+
() => setGameStatus(GameStatus.PLAYING),
106+
1000 * challengeSeconds
107+
);
108+
}
109+
if (gameStatus === GameStatus.PLAYING) {
110+
timerId = setInterval(() => {
111+
setCountdown(countdown => {
112+
if (countdown === 1) {
113+
clearTimeout(timerId);
114+
setGameStatus(GameStatus.LOST);
115+
}
116+
return countdown - 1;
117+
});
118+
}, 1000);
119+
}
120+
return () => clearTimeout(timerId);
121+
}, [challengeSeconds, gameStatus]);
122+
123+
React.useEffect(() => {
124+
const [correctPicks, wrongPicks] = utils.arrayCrossCounts(
125+
pickedCellIds,
126+
challengeCellIds
127+
);
128+
if (correctPicks === challengeSize) {
129+
setGameStatus(GameStatus.WON);
130+
}
131+
if (wrongPicks === maxWrongAttempts) {
132+
setGameStatus(GameStatus.LOST);
133+
}
134+
}, [pickedCellIds]);
135+
136+
const pickCell = cellId => {
137+
if (gameStatus === GameStatus.PLAYING) {
138+
setPickedCellIds(pickedCellIds => {
139+
if (pickedCellIds.includes(cellId)) {
140+
return pickedCellIds;
141+
}
142+
return [...pickedCellIds, cellId];
143+
});
144+
}
145+
};
146+
147+
return (
148+
<div className="game">
149+
<div className="grid">
150+
{cellIds.map(cellId => (
151+
<Cell
152+
key={cellId}
153+
width={cellWidth}
154+
gameStatus={gameStatus}
155+
isChallenge={challengeCellIds.includes(cellId)}
156+
isPicked={pickedCellIds.includes(cellId)}
157+
onClick={() => pickCell(cellId)}
158+
/>
159+
))}
160+
</div>
161+
<Footer
162+
gameStatus={gameStatus}
163+
countdown={countdown}
164+
startGame={() => setGameStatus(GameStatus.CHALLENGE)}
165+
resetGame={resetGame}
166+
/>
167+
</div>
168+
);
169+
};
170+
171+
const useGameId = () => {
172+
const [gameId, setGameId] = useState(1);
173+
174+
return {
175+
gameId,
176+
isNewGame: gameId === 1,
177+
renewGame: () => setGameId(gameId => gameId + 1),
178+
};
179+
};
180+
181+
const GameGenerator = () => {
182+
const { gameId, isNewGame, renewGame } = useGameId();
183+
184+
const gridSize = 5;
185+
const challengeSize = 6;
186+
const cellIds = utils.createArray(gridSize * gridSize);
187+
const cellWidth = 100 / gridSize;
188+
const challengeCellIds = utils.sampleArray(cellIds, challengeSize);
189+
190+
return (
191+
<GameSession
192+
cellIds={cellIds}
193+
challengeCellIds={challengeCellIds}
194+
cellWidth={cellWidth}
195+
challengeSize={challengeSize}
196+
challengeSeconds={3}
197+
playSeconds={10}
198+
maxWrongAttempts={3}
199+
autoStart={!isNewGame}
200+
resetGame={renewGame}
201+
/>
202+
);
203+
};

0 commit comments

Comments
 (0)