aboutsummaryrefslogtreecommitdiff
path: root/originaltetris/tetris2.py
blob: d5fccdf98adfa9a320bbf2feeaf2ed383c376bb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
##############THIS IS THE ORIGINAL TETRIS CODE.##############
####PROVIDED ONLY FOR REFERENCE.  DON'T WORRY ABOUT IT.######                                                                
                                                                     
                                                                     
                                             
from graphics import *
import random

############################################################
# BLOCK CLASS
############################################################

class Block(Rectangle):
    ''' Block class:
        Implement a block for a tetris piece
        Attributes: x - type: int
                    y - type: int
        specify the position on the tetris board
        in terms of the square grid
    '''
    # BLOCK SIZE defined in pixels. So from now on, each coordinate is multiplied by 30
    # (see below)
    BLOCK_SIZE = 30
    OUTLINE_WIDTH = 3

    def __init__(self, pos, color):
        self.x = pos.x
        self.y = pos.y
        
        p1 = Point(pos.x*Block.BLOCK_SIZE + Block.OUTLINE_WIDTH,
                   pos.y*Block.BLOCK_SIZE + Block.OUTLINE_WIDTH)
        p2 = Point(p1.x + Block.BLOCK_SIZE, p1.y + Block.BLOCK_SIZE)

        Rectangle.__init__(self, p1, p2)
        self.setWidth(Block.OUTLINE_WIDTH)
        self.setFill(color)
        self.color = color

    def can_move(self, board, dx, dy):
        ''' Parameters: dx - type: int
                        dy - type: int

            Return value: type: bool
                        
            checks if the block can move dx squares in the x direction
            and dy squares in the y direction
            Returns True if it can, and False otherwise
            HINT: use the can_move method on the Board object
        '''

        if board.can_move(self.x + dx, self.y + dy) == True:
            return True
        return False
    
    def move(self, dx, dy):
        ''' Parameters: dx - type: int
                        dy - type: int
                        
            moves the block dx squares in the x direction
            and dy squares in the y direction
        '''

        self.x += dx
        self.y += dy

        Rectangle.move(self, dx*Block.BLOCK_SIZE, dy*Block.BLOCK_SIZE)


############################################################
# SHAPE CLASS
############################################################

class Shape():
    ''' Shape class:
        Base class for all the tetris shapes
        Attributes: blocks - type: list - the list of blocks making up the shape
                    rotation_dir - type: int - the current rotation direction of the shape
                    shift_rotation_dir - type: Boolean - whether or not the shape rotates
    '''

    def __init__(self, coords, color):
        # Define empty list
        self.blocks = []
        self.rotation_dir = 1
        ### A boolean to indicate if a shape shifts rotation direction or not.
        ### Defaults to false since only 3 shapes shift rotation directions (I, S and Z)
        self.shift_rotation_dir = False

        # for each coordinate, make a block object out of it and append to list
        for pos in coords:
            self.blocks.append(Block(pos, color))



    def get_blocks(self):
        '''returns the list of blocks
        '''
        #return list of blocks
        return self.blocks
        pass

    def draw(self, win):
        ''' Parameter: win - type: CanvasFrame

            Draws the shape:
            i.e. draws each block
        '''
        # For each block in list, draw block
        for block in self.blocks:
            block.draw(win)

    def move(self, dx, dy):
        ''' Parameters: dx - type: int
                        dy - type: int

            moves the shape dx squares in the x direction
            and dy squares in the y direction, i.e.
            moves each of the blocks
        '''
        for block in self.blocks:
            block.move(dx, dy)

    def can_move(self, board, dx, dy):
        ''' Parameters: dx - type: int
                        dy - type: int

            Return value: type: bool
                        
            checks if the shape can move dx squares in the x direction
            and dy squares in the y direction, i.e.
            check if each of the blocks can move
            Returns True if all of them can, and False otherwise
           
        '''
        
        #Checks each block to see if it can move dx and dy
        for blocks in self.blocks:
            if blocks.can_move(board, dx, dy) == False:
                return False
        return True
        # default implementation (MUST CHANGE)
        #return True
    
    def get_rotation_dir(self):
        ''' Return value: type: int
        
            returns the current rotation direction
        '''
        return self.rotation_dir

    def can_rotate(self, board):
        ''' Parameters: board - type: Board object
            Return value: type : bool
            
            Checks if the shape can be rotated.
            
            1. Get the rotation direction using the get_rotation_dir method
            2. Compute the position of each block after rotation and check if
            the new position is valid
            3. If any of the blocks cannot be moved to their new position,
            return False
                        
            otherwise all is good, return True
        '''
        #Get rotation direction from other method, and center
        rotation_direction = self.get_rotation_dir()
        center = self.center_block

        #For each block in list:
        for block in self.blocks:
            # Use these formulas to determine new coordinates (provided for us)
            x = center.x - rotation_direction*center.y + rotation_direction*block.y
            y = center.y + rotation_direction*center.x - rotation_direction*block.x
            # If these posistions are not valid on the board(other piece there
            #or out of bounds), return False
            if board.can_move(x, y) == False:
                return False
        return True
        
        
        

    def rotate(self, board):
        ''' Parameters: board - type: Board object

            rotates the shape:
            1. Get the rotation direction using the get_rotation_dir method
            2. Compute the position of each block after rotation
            3. Move the block to the new position
            
        '''    

        
        # For each block defined in shape
        for block in self.blocks:
            #Grab rotation direction and center block
            rotation_direction = self.get_rotation_dir()
            center = self.center_block
            #Compute new coordinates and call move method
            x = center.x - rotation_direction*center.y + rotation_direction*block.y
            y = center.y + rotation_direction*center.x - rotation_direction*block.x
            # Use x - block.x for dx, y-block.y for dy
            block.move(x - block.x, y - block.y)

        ### This should be at the END of your rotate code. 
        ### DO NOT touch it. Default behavior is that a piece will only shift
        ### rotation direciton after a successful rotation. This ensures that 
        ### pieces which switch rotations definitely remain within their 
        ### accepted rotation positions.
        if self.shift_rotation_dir:
            self.rotation_dir *= -1

        

############################################################
# ALL SHAPE CLASSES
############################################################

# For each shape in the game, we gave the coordinates for each block in the
# Tetronimo, using the center block as reference.  We then initialized a shape
# object with the list of coords and a color.  Self.center_block is used
# above for rotating; it tells us what the center block is
 
class I_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x - 2, center.y),
                  Point(center.x - 1, center.y),
                  Point(center.x    , center.y),
                  Point(center.x + 1, center.y)]
        Shape.__init__(self, coords, 'blue')
        self.shift_rotation_dir = True
        self.center_block = self.blocks[2]


class J_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x - 1, center.y),
                  Point(center.x    , center.y),
                  Point(center.x + 1, center.y),
                  Point(center.x + 1, center.y + 1)]
        Shape.__init__(self, coords, 'orange')
        self.center_block = self.blocks[1]


class L_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x - 1, center.y),
                  Point(center.x    , center.y),
                  Point(center.x + 1, center.y),
                  Point(center.x - 1, center.y + 1)]
        Shape.__init__(self, coords, 'cyan')
        self.center_block = self.blocks[1]


class O_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x    , center.y),
                  Point(center.x - 1, center.y),
                  Point(center.x   , center.y + 1),
                  Point(center.x - 1, center.y + 1)]
        Shape.__init__(self, coords, 'red')
        self.center_block = self.blocks[0]

    def rotate(self, board):
        # Override Shape's rotate method since O_Shape does not rotate
        return 



class S_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x    , center.y),
                  Point(center.x    , center.y + 1),
                  Point(center.x + 1, center.y),
                  Point(center.x - 1, center.y + 1)]
        Shape.__init__(self, coords, 'green')
        self.center_block = self.blocks[0]
        self.shift_rotation_dir = True
        self.rotation_dir = -1


class T_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x - 1, center.y),
                  Point(center.x    , center.y),
                  Point(center.x + 1, center.y),
                  Point(center.x    , center.y + 1)]
        Shape.__init__(self, coords, 'yellow')
        self.center_block = self.blocks[1]


class Z_shape(Shape):
    def __init__(self, center):
        coords = [Point(center.x - 1, center.y),
                  Point(center.x    , center.y), 
                  Point(center.x    , center.y + 1),
                  Point(center.x + 1, center.y + 1)]      
        Shape.__init__(self, coords, 'magenta')
        self.shift_rotation_dir = True
        self.rotation_dir = -1
        self.center_block = self.blocks[1]



############################################################
# BOARD CLASS
############################################################

class Board():
    ''' Board class: it represents the Tetris board

        Attributes: width - type:int - width of the board in squares
                    height - type:int - height of the board in squares
                    canvas - type:CanvasFrame - where the pieces will be drawn
                    grid - type:Dictionary - keeps track of the current state of
                    the board; stores the blocks for a given position
    '''
    
    def __init__(self, win, width, height):
        self.width = width
        self.height = height

        # create a canvas to draw the tetris shapes on
        self.canvas = CanvasFrame(win, self.width * Block.BLOCK_SIZE,
                                        self.height * Block.BLOCK_SIZE)
        self.canvas.setBackground('black')
        # Create Scoreboard with same width, but a height of 100 pixels
        self.scoreboard = Scoreboard(win, width, 100)

        # create an empty dictionary
        # currently we have no shapes on the board
        self.grid = {}

    def draw_shape(self, shape):
        ''' Parameters: shape - type: Shape
            Return value: type: bool

            draws the shape on the board if there is space for it
            and returns True, otherwise it returns False
        '''
        # If the shape can move function returns True, draw new shape
        if shape.can_move(self, 0, 0):
            shape.draw(self.canvas)
            return True
        # If not, no shape can be drawn (game over)
        else:
            self.game_over()

    def can_move(self, x, y):
        ''' Parameters: x - type:int
                        y - type:int
            Return value: type: bool

            1. check if it is ok to move to square x,y
            if the position is outside of the board boundaries, can't move there
            return False

            2. if there is already a block at that postion, can't move there
            return False

            3. otherwise return True
            
        '''
        # if this x, y coordinates are within the board and there are no pieces there (aka no keys
        # with the coordinates in the dictionary), return True
        if (x >= 0 and x < self.width) and (y >= 0 and y < self.height) and (x, y) not in self.grid.keys():
            return True
        return False
            
        pass

    def add_shape(self, shape):
        ''' Parameter: shape - type:Shape
            
            add a shape to the grid, i.e.
            add each block to the grid using its
            (x, y) coordinates as a dictionary key

            Hint: use the get_blocks method on Shape to
            get the list of blocks
            
        '''
        
        #For each block in a given shape, add it to the self.grid dictionary
        for block in shape.get_blocks():
            self.grid[(block.x, block.y)] = block


    def delete_row(self, y):
        ''' Parameters: y - type:int

            remove all the blocks in row y
            to remove a block you must remove it from the grid
            and erase it from the screen.
            If you dont remember how to erase a graphics object
            from the screen, take a look at the Graphics Library
            handout
            
        '''
        # If the row is complete (use is_row_complete method)
        if self.is_row_complete(y) == True:
            #For every key in the block dictionary,
            for item in self.grid.keys():
                # If the y coordinate of that key matches the row that must be removed 
                if item[1] == y:
                    #Undraw the item and delete it from the dictionary
                    self.grid[item].undraw()
                    del self.grid[item]
                    
    
    def is_row_complete(self, y):        
        ''' Parameter: y - type: int
            Return value: type: bool

            for each block in row y
            check if there is a block in the grid (use the in operator) 
            if there is one square that is not occupied, return False
            otherwise return True
            
        '''
        # Define empty dictionary to keep track of blocks in row
        y_list = []
        # for every block on the board, if it is in this row, append to list
        for block in self.grid.keys():
            if block[1] == y:
                y_list.append(block)
        

        # If the row is full (i.e. length of the list is the same as the width of the board)
        # empty the list and return True
        if len(y_list) == self.width:
            y_list = []
            return True
        return False

    
    def move_down_rows(self, y_start):
        ''' Parameters: y_start - type:int                        

            for each row from y_start to the top
                for each column
                    check if there is a block in the grid
                    if there is, remove it from the grid
                    and move the block object down on the screen
                    and then place it back in the grid in the new position

        '''
        # For each row from y_start to the top
        for row in range(y_start, 0, -1):
            # And each column on the board
            for column in range(0, self.width):
                # If there is a block there, move it down 1, delete from grid
                # and add new coordinates to the grid
                if (column, row) in self.grid.keys():
                    #print "WE MADE IT"
                    self.grid[(column, row)].move(0, 1)
                    block = self.grid[(column, row)]
                    del self.grid[(column, row)]
                    self.grid[(column, row + 1)] = block
    
    def remove_complete_rows(self):
        ''' removes all the complete rows
            1. for each row, y, 
            2. check if the row is complete
                if it is,
                    delete the row
                    move all rows down starting at row y - 1

        '''
        # Define empty list for keeping track of how many lines deleted(scoring purposes)
        lines_completed = []

        #For each row in the board, if it is complete, delete, move down rows,
        #Append the list
        for row in range(0, self.height):
            if self.is_row_complete(row) == True:
                #print "YADDA YADDA"
                self.delete_row(row)
                self.move_down_rows(row - 1)
                lines_completed.append('a')
        # Call the add_score method in the scoreboard class with the # of completed lines
        self.scoreboard.add_score(len(lines_completed))

    def game_over(self):
        ''' display "Game Over !!!" message in the center of the board
            HINT: use the Text class from the graphics library
        '''
        # Displays game_over message on screen
        game_over_message = Text(Point(150, 250), "GAME OVER \n BEETCHES")
        game_over_message.setStyle('bold')
        game_over_message.setSize(25)
        game_over_message.setTextColor('white')
        game_over_message.setFace('helvetica')
        game_over_message.draw(self.canvas)

            
############################################################
# SCOREBOARD CLASS
############################################################

class Scoreboard():

    
    def __init__(self, win, width, height):
        self.width = width
        self.height = height

        # create a canvas to draw the tetris shapes on
        self.canvas = CanvasFrame(win, self.width * Block.BLOCK_SIZE,
                                        100)
        self.canvas.setBackground('black')

        # Start the score off at 0, level off at 1
        self.current_score = 0
        self.current_level = 1

        # Text object displaying the Score
        self.score = Text(Point(75, 25), "Score: " + str(self.current_score))
        self.score.setStyle('bold')
        self.score.setSize(18)
        self.score.setTextColor('white')
        self.score.setFace('helvetica')

        # Text object displaying the Level
        self.levels = Text(Point(75, 50), "Level: " + str(self.current_level))
        self.levels.setStyle('bold')
        self.levels.setSize(18)
        self.levels.setTextColor('white')
        self.levels.setFace('helvetica')

        # Draw the Text objects on the screen
        self.score.draw(self.canvas)
        self.levels.draw(self.canvas)

        # Set the current_level = the method defined below
        self.current_level = self.increase_level()

    # This method is called from remove_complete_rows method, using len(lines_completed) as parameter
    def add_score(self, lines):
        # Depending on # of lines completed, add score accordingly
        if lines == 1:
            self.current_score += 100
        elif lines == 2:
            self.current_score += 300
        elif lines == 3:
            self.current_score += 450
        elif lines == 4:
            self.current_score += 700
        #Change Text Object
        self.score.setText("Score: "+ str(self.current_score))

    def increase_level(self):
        # Depending on Score, change level and text object
        while self.current_score <= 1000:
            self.current_level = 1
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 1000 and self.current_score <= 5000:
            self.current_level = 2
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 5000 and self.current_score <= 10000:
            self.current_level = 3
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 10000 and self.current_score <= 15000:
            self.current_level = 4
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 15000 and self.current_score <= 19000:
            self.current_level = 5
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 19000 and self.current_score <= 23000:
            self.current_level = 6
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 23000 and self.current_score <= 27000:
            self.current_level = 7
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 27000 and self.current_score <= 30000:
            self.current_level = 8
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 30000 and self.current_score <= 33000:
            self.current_level = 9
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        while self.current_score > 33000:
            self.current_level = 10
            self.levels.setText("Level: "+ str(self.current_level))
            return self.current_level
        

############################################################
# TETRIS CLASS
############################################################

class Tetris():
    ''' Tetris class: Controls the game play
        Attributes:
            SHAPES - type: list (list of Shape classes)
            DIRECTION - type: dictionary - converts string direction to (dx, dy)
            BOARD_WIDTH - type:int - the width of the board
            BOARD_HEIGHT - type:int - the height of the board
            board - type:Board - the tetris board
            win - type:Window - the window for the tetris game
            delay - type:int - the speed in milliseconds for moving the shapes
            current_shapes - type: Shape - the current moving shape on the board
    '''
    
    SHAPES = [I_shape, J_shape, L_shape, O_shape, S_shape, T_shape, Z_shape]
    DIRECTION = {'Left':(-1, 0), 'Right':(1, 0), 'Down':(0, 1)}
    BOARD_WIDTH = 10
    BOARD_HEIGHT = 20
    
    def __init__(self, win):
        self.board = Board(win, self.BOARD_WIDTH, self.BOARD_HEIGHT)
        self.win = win
        self.delay = 1000 #ms

        # sets up the keyboard events
        # when a key is called the method key_pressed will be called
        self.win.bind_all('<Key>', self.key_pressed)

        # set the current shape to a random new shape
        self.current_shape = self.create_new_shape()

        # Draw the current_shape on the board (take a look at the
        # draw_shape method in the Board class)
        self.board.draw_shape(self.current_shape)

        # For Step 9:  animate the shape!
        self.animate_shape()
        
        

    def create_new_shape(self):
        ''' Return value: type: Shape
            
            Create a random new shape that is centered
             at y = 0 and x = int(self.BOARD_WIDTH/2)
            return the shape
        '''
        # Pick a random number from 0 to 6
        rand_shape = random.randint(0, 6)
        # That number corresponds to a shape in SHAPES list. Use coordinates to tell where to draw
        new_shape = self.SHAPES[rand_shape](Point(int(self.BOARD_WIDTH/2), 0))
        return new_shape
        
        pass
    
    def animate_shape(self):
        ''' animate the shape - move down at equal intervals
            specified by the delay attribute
        '''
        self.board.scoreboard.increase_level()
        #Depending onlevel, change the delay for animation to get shorter and shorter
        # move the shape down
        if self.board.scoreboard.current_level == 1:
            self.delay = 1000
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 2:
            self.delay = 900
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 3:
            self.delay = 800
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 4:
            self.delay = 650
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 5:
            self.delay = 500
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 6:
            self.delay = 400
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 7:
            self.delay = 300
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 8:
            self.delay = 200
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 9:
            self.delay = 100
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        elif self.board.scoreboard.current_level == 10:
            self.delay = 50
            self.do_move('Down')
            self.win.after(self.delay, self.animate_shape)
        
    
    def do_move(self, direction):
        ''' Parameters: direction - type: string
            Return value: type: bool

            Move the current shape in the direction specified by the parameter:
            First check if the shape can move. If it can, move it and return True
            Otherwise if the direction we tried to move was 'Down',
            1. add the current shape to the board
            2. remove the completed rows if any 
            3. create a new random shape and set current_shape attribute
            4. If the shape cannot be drawn on the board, display a
               game over message

            return False

        '''
        # space bar is hit, move shape down until it can't move anymore
        if direction == 'space':           
            while self.current_shape.can_move(self.board, self.DIRECTION['Down'][0], self.DIRECTION['Down'][1]) == True:
                self.current_shape.move(self.DIRECTION['Down'][0], self.DIRECTION['Down'][1])
            # add new shape to grid, create new shape to draw, and check for completed rows
            self.board.add_shape(self.current_shape)
            self.current_shape = self.create_new_shape()
            self.board.draw_shape(self.current_shape)
            self.board.remove_complete_rows()
            
        #if the shape can move in the direction given by DIRECTION list, move it there       
        elif self.current_shape.can_move(self.board, self.DIRECTION[direction][0], self.DIRECTION[direction][1]) == True:
            self.current_shape.move(self.DIRECTION[direction][0], self.DIRECTION[direction][1])
            return True

        else:
            # if the shape can't  move any moore
            if direction == 'Down':
                # add new shape to grid, create new shape to draw, and check for completed rows
                self.board.add_shape(self.current_shape)
                self.current_shape = self.create_new_shape()
                self.board.draw_shape(self.current_shape)
                self.board.remove_complete_rows()
            return False
        

    def do_rotate(self):
        ''' Checks if the current_shape can be rotated and
            rotates if it can
        '''
        
        
        #If shape can rotate, rotate it
        if self.current_shape.can_rotate(self.board) == True:
            self.current_shape.rotate(self.board)
        
        
    
    def key_pressed(self, event):
        ''' this function is called when a key is pressed on the keyboard
            it currenly just prints the value of the key

            Modify the function so that if the user presses the arrow keys
            'Left', 'Right' or 'Down', the current_shape will move in
            the appropriate direction

            if the user presses the space bar 'space', the shape will move
            down until it can no longer move and is added to the board

            if the user presses the 'Up' arrow key ,
                the shape should rotate.

        '''
            
        
        key = event.keysym
        # If key is up, call do_rotate function
        if key == 'Up':
            self.do_rotate()
        
        # Else, call do_move function
        else:
            self.do_move(key)


       
################################################################
# Start the game
################################################################

win = Window("Tetris")
game = Tetris(win)
win.mainloop()