1+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
2+ const GRID_WIDTH = 10
3+ const GRID_HEIGHT = 20
4+ const GRID_SIZE = GRID_WIDTH * GRID_HEIGHT
5+
6+
7+ const grid = createGrid ( ) ;
8+ let squares = Array . from ( grid . querySelectorAll ( 'div' ) )
9+ const startBtn = document . querySelector ( '.button' )
10+ const hamburgerBtn = document . querySelector ( '.toggler' )
11+ const menu = document . querySelector ( '.menu' )
12+ const span = document . getElementsByClassName ( 'close' ) [ 0 ]
13+ const scoreDisplay = document . querySelector ( '.score-display' )
14+ const linesDisplay = document . querySelector ( '.lines-score' )
15+ let currentIndex = 0
16+ let currentRotation = 0
17+ const width = 10
18+ let score = 0
19+ let lines = 0
20+ let timerId
21+ let nextRandom = 0
22+ const colors = [
23+ 'url(images/blue_block.png)' ,
24+ 'url(images/pink_block.png)' ,
25+ 'url(images/purple_block.png)' ,
26+ 'url(images/peach_block.png)' ,
27+ 'url(images/yellow_block.png)'
28+ ]
29+
30+
31+ function createGrid ( ) {
32+
33+ let grid = document . querySelector ( ".grid" )
34+ for ( let i = 0 ; i < GRID_SIZE ; i ++ ) {
35+ let gridElement = document . createElement ( "div" )
36+ grid . appendChild ( gridElement )
37+ }
38+
39+
40+ for ( let i = 0 ; i < GRID_WIDTH ; i ++ ) {
41+ let gridElement = document . createElement ( "div" )
42+ gridElement . setAttribute ( "class" , "block3" )
43+ grid . appendChild ( gridElement )
44+ }
45+
46+ let previousGrid = document . querySelector ( ".previous-grid" )
47+
48+ for ( let i = 0 ; i < 16 ; i ++ ) {
49+ let gridElement = document . createElement ( "div" )
50+ previousGrid . appendChild ( gridElement ) ;
51+ }
52+ return grid ;
53+ }
54+
55+
56+
57+ function control ( e ) {
58+ if ( e . keyCode === 39 )
59+ moveright ( )
60+ else if ( e . keyCode === 38 )
61+ rotate ( )
62+ else if ( e . keyCode === 37 )
63+ moveleft ( )
64+ else if ( e . keyCode === 40 )
65+ moveDown ( )
66+ }
67+
68+ document . addEventListener ( 'keydown' , control )
69+
70+
71+ const lTetromino = [
72+ [ 1 , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 , 2 ] ,
73+ [ GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH * 2 + 2 ] ,
74+ [ 1 , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 , GRID_WIDTH * 2 ] ,
75+ [ GRID_WIDTH , GRID_WIDTH * 2 , GRID_WIDTH * 2 + 1 , GRID_WIDTH * 2 + 2 ]
76+ ]
77+
78+ const zTetromino = [
79+ [ 0 , GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 ] ,
80+ [ GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH * 2 , GRID_WIDTH * 2 + 1 ] ,
81+ [ 0 , GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 ] ,
82+ [ GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH * 2 , GRID_WIDTH * 2 + 1 ]
83+ ]
84+
85+ const tTetromino = [
86+ [ 1 , GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH + 2 ] ,
87+ [ 1 , GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH * 2 + 1 ] ,
88+ [ GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH * 2 + 1 ] ,
89+ [ 1 , GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 ]
90+ ]
91+
92+ const oTetromino = [
93+ [ 0 , 1 , GRID_WIDTH , GRID_WIDTH + 1 ] ,
94+ [ 0 , 1 , GRID_WIDTH , GRID_WIDTH + 1 ] ,
95+ [ 0 , 1 , GRID_WIDTH , GRID_WIDTH + 1 ] ,
96+ [ 0 , 1 , GRID_WIDTH , GRID_WIDTH + 1 ]
97+ ]
98+
99+ const iTetromino = [
100+ [ 1 , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 , GRID_WIDTH * 3 + 1 ] ,
101+ [ GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH + 3 ] ,
102+ [ 1 , GRID_WIDTH + 1 , GRID_WIDTH * 2 + 1 , GRID_WIDTH * 3 + 1 ] ,
103+ [ GRID_WIDTH , GRID_WIDTH + 1 , GRID_WIDTH + 2 , GRID_WIDTH + 3 ]
104+ ]
105+
106+ const theTetrominoes = [ lTetromino , zTetromino , tTetromino , oTetromino , iTetromino ]
107+
108+
109+ let random = Math . floor ( Math . random ( ) * theTetrominoes . length )
110+ let current = theTetrominoes [ random ] [ currentRotation ]
111+
112+
113+
114+ let currentPosition = 4
115+
116+ function draw ( ) {
117+ current . forEach ( index => {
118+ squares [ currentPosition + index ] . classList . add ( 'block' )
119+ squares [ currentPosition + index ] . style . backgroundImage = colors [ random ]
120+ } )
121+ }
122+
123+ function undraw ( ) {
124+ current . forEach ( index => {
125+ squares [ currentPosition + index ] . classList . remove ( 'block' )
126+ squares [ currentPosition + index ] . style . backgroundImage = 'none'
127+ } )
128+ }
129+
130+
131+ function moveDown ( ) {
132+ undraw ( )
133+ currentPosition = currentPosition += width
134+ draw ( )
135+ freeze ( )
136+ }
137+
138+ startBtn . addEventListener ( 'click' , ( ) => {
139+ if ( timerId ) {
140+ clearInterval ( timerId )
141+ timerId = null
142+ } else {
143+ draw ( )
144+ timerId = setInterval ( moveDown , 1000 )
145+ nextRandom = Math . floor ( Math . random ( ) * theTetrominoes . length )
146+ displayShape ( )
147+ }
148+ } )
149+
150+
151+ function moveright ( ) {
152+ undraw ( )
153+ const isAtRightEdge = current . some ( index => ( currentPosition + index ) % width === width - 1 )
154+ if ( ! isAtRightEdge ) currentPosition += 1
155+ if ( current . some ( index => squares [ currentPosition + index ] . classList . contains ( 'block2' ) ) ) {
156+ currentPosition -= 1
157+ }
158+ draw ( )
159+ }
160+
161+
162+ function moveleft ( ) {
163+ undraw ( )
164+ const isAtLeftEdge = current . some ( index => ( currentPosition + index ) % width === 0 )
165+ if ( ! isAtLeftEdge ) currentPosition -= 1
166+ if ( current . some ( index => squares [ currentPosition + index ] . classList . contains ( 'block2' ) ) ) {
167+ currentPosition += 1
168+ }
169+ draw ( )
170+ }
171+
172+
173+ function freeze ( ) {
174+
175+ if ( current . some ( index => squares [ currentPosition + index + width ] . classList . contains ( 'block3' ) || squares [ currentPosition + index + width ] . classList . contains ( 'block2' ) ) ) {
176+
177+ current . forEach ( index => squares [ index + currentPosition ] . classList . add ( 'block2' ) )
178+
179+ random = nextRandom
180+ nextRandom = Math . floor ( Math . random ( ) * theTetrominoes . length )
181+ current = theTetrominoes [ random ] [ currentRotation ]
182+ currentPosition = 4
183+ draw ( )
184+ displayShape ( )
185+ addScore ( )
186+ gameOver ( )
187+ }
188+ }
189+ freeze ( )
190+
191+
192+ function rotate ( ) {
193+ undraw ( )
194+ currentRotation ++
195+ if ( currentRotation === current . length ) {
196+ currentRotation = 0
197+ }
198+ current = theTetrominoes [ random ] [ currentRotation ]
199+ draw ( )
200+ }
201+
202+
203+ function gameOver ( ) {
204+ if ( current . some ( index => squares [ currentPosition + index ] . classList . contains ( 'block2' ) ) ) {
205+ scoreDisplay . innerHTML = 'end'
206+ clearInterval ( timerId )
207+ }
208+ }
209+
210+
211+ const displayWidth = 4
212+ const displaySquares = document . querySelectorAll ( '.previous-grid div' )
213+ let displayIndex = 0
214+
215+ const smallTetrominoes = [
216+ [ 1 , displayWidth + 1 , displayWidth * 2 + 1 , 2 ] , /* lTetromino */
217+ [ 0 , displayWidth , displayWidth + 1 , displayWidth * 2 + 1 ] , /* zTetromino */
218+ [ 1 , displayWidth , displayWidth + 1 , displayWidth + 2 ] , /* tTetromino */
219+ [ 0 , 1 , displayWidth , displayWidth + 1 ] , /* oTetromino */
220+ [ 1 , displayWidth + 1 , displayWidth * 2 + 1 , displayWidth * 3 + 1 ] /* iTetromino */
221+ ]
222+
223+ function displayShape ( ) {
224+ displaySquares . forEach ( square => {
225+ square . classList . remove ( 'block' )
226+ square . style . backgroundImage = 'none'
227+ } )
228+ smallTetrominoes [ nextRandom ] . forEach ( index => {
229+ displaySquares [ displayIndex + index ] . classList . add ( 'block' )
230+ displaySquares [ displayIndex + index ] . style . backgroundImage = colors [ nextRandom ]
231+ } )
232+ }
233+
234+
235+ function addScore ( ) {
236+ for ( currentIndex = 0 ; currentIndex < GRID_SIZE ; currentIndex += GRID_WIDTH ) {
237+ const row = [ currentIndex , currentIndex + 1 , currentIndex + 2 , currentIndex + 3 , currentIndex + 4 , currentIndex + 5 , currentIndex + 6 , currentIndex + 7 , currentIndex + 8 , currentIndex + 9 ]
238+ if ( row . every ( index => squares [ index ] . classList . contains ( 'block2' ) ) ) {
239+ score += 10
240+ lines += 1
241+ scoreDisplay . innerHTML = score
242+ linesDisplay . innerHTML = lines
243+ row . forEach ( index => {
244+ squares [ index ] . style . backgroundImage = 'none'
245+ squares [ index ] . classList . remove ( 'block2' ) || squares [ index ] . classList . remove ( 'block' )
246+
247+ } )
248+
249+ const squaresRemoved = squares . splice ( currentIndex , width )
250+ squares = squaresRemoved . concat ( squares )
251+ squares . forEach ( cell => grid . appendChild ( cell ) )
252+ }
253+ }
254+ }
255+
256+
257+ hamburgerBtn . addEventListener ( 'click' , ( ) => {
258+ menu . style . display = 'flex'
259+ } )
260+ span . addEventListener ( 'click' , ( ) => {
261+ menu . style . display = 'none'
262+ } )
263+
264+ } )
0 commit comments