@@ -7,10 +7,197 @@ import utils from '../helpers/utils';
77const Step11Page = ( ) => {
88 return (
99 < Frame title = "Step11: Using custom Hooks" >
10- < p > Here comes something in Frame </ p >
10+ < GameGenerator />
1111 </ Frame >
1212 ) ;
1313} ;
1414export 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