@@ -7,10 +7,185 @@ import utils from '../helpers/utils';
77const 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} ;
1414export 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