diff options
Diffstat (limited to 'tetris')
| -rwxr-xr-x | tetris/tetris.py | 612 | ||||
| -rw-r--r-- | tetris/tetrisb.mid | bin | 0 -> 8612 bytes | |||
| -rw-r--r-- | tetris/tetrisc.mid | bin | 0 -> 14661 bytes | 
3 files changed, 612 insertions, 0 deletions
diff --git a/tetris/tetris.py b/tetris/tetris.py new file mode 100755 index 0000000..b03689e --- /dev/null +++ b/tetris/tetris.py @@ -0,0 +1,612 @@ +#!/usr/bin/python3 +# Tetromino (a Tetris clone) +# By Al Sweigart al@inventwithpython.com +# http://inventwithpython.com/pygame +# Released under a "Simplified BSD" license + +# KRT 17/06/2012 rewrite event detection to deal with mouse use +# mods for joystick use by Robert Scheibe 18.06.2020 + +import random, time, pygame, sys +from pygame.locals import * + +#joystick or gamepad init +pygame.joystick.init() +print ("Joystics: ", pygame.joystick.get_count()) +js = pygame.joystick.Joystick(0) +js.init() + + +FPS = 25 +WINDOWWIDTH = 640 +WINDOWHEIGHT = 480 +BOXSIZE = 20 +BOARDWIDTH = 10 +BOARDHEIGHT = 20 +BLANK = '.' + +MOVESIDEWAYSFREQ = 0.15 +MOVEDOWNFREQ = 0.1 + +XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2) +TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5 + +#               R    G    B +WHITE       = (255, 255, 255) +GRAY        = (185, 185, 185) +GRAY1        = (240, 240, 240) +GRAY2        = (200, 200, 200) +GRAY3        = (160, 160, 160) +GRAY4        = (120, 120, 120) +BLACK       = (  0,   0,   0) +RED         = (155,   0,   0) +LIGHTRED    = (175,  20,  20) +GREEN       = (  0, 155,   0) +LIGHTGREEN  = ( 20, 175,  20) +BLUE        = (  0,   0, 155) +LIGHTBLUE   = ( 20,  20, 175) +YELLOW      = (155, 155,   0) +LIGHTYELLOW = (175, 175,  20) + +BORDERCOLOR = BLUE +BGCOLOR = BLACK +TEXTCOLOR = WHITE +TEXTSHADOWCOLOR = GRAY +#COLORS      = (     BLUE,      GREEN,      RED,      YELLOW) +COLORS      = (     GRAY1,      GRAY2,      GRAY3,      GRAY4) +#LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW) +LIGHTCOLORS      = (     GRAY1,      GRAY2,      GRAY3,      GRAY4) +assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color + +#TEMPLATEWIDTH = i, 5 +TEMPLATEWIDTH = 5 +TEMPLATEHEIGHT = 5 + +S_SHAPE_TEMPLATE = [['.....', +                     '.....', +                     '..OO.', +                     '.OO..', +                     '.....'], +                    ['.....', +                     '..O..', +                     '..OO.', +                     '...O.', +                     '.....']] + +Z_SHAPE_TEMPLATE = [['.....', +                     '.....', +                     '.OO..', +                     '..OO.', +                     '.....'], +                    ['.....', +                     '..O..', +                     '.OO..', +                     '.O...', +                     '.....']] + +I_SHAPE_TEMPLATE = [['..O..', +                     '..O..', +                     '..O..', +                     '..O..', +                     '.....'], +                    ['.....', +                     '.....', +                     'OOOO.', +                     '.....', +                     '.....']] + +O_SHAPE_TEMPLATE = [['.....', +                     '.....', +                     '.OO..', +                     '.OO..', +                     '.....']] + +J_SHAPE_TEMPLATE = [['.....', +                     '.O...', +                     '.OOO.', +                     '.....', +                     '.....'], +                    ['.....', +                     '..OO.', +                     '..O..', +                     '..O..', +                     '.....'], +                    ['.....', +                     '.....', +                     '.OOO.', +                     '...O.', +                     '.....'], +                    ['.....', +                     '..O..', +                     '..O..', +                     '.OO..', +                     '.....']] + +L_SHAPE_TEMPLATE = [['.....', +                     '...O.', +                     '.OOO.', +                     '.....', +                     '.....'], +                    ['.....', +                     '..O..', +                     '..O..', +                     '..OO.', +                     '.....'], +                    ['.....', +                     '.....', +                     '.OOO.', +                     '.O...', +                     '.....'], +                    ['.....', +                     '.OO..', +                     '..O..', +                     '..O..', +                     '.....']] + +T_SHAPE_TEMPLATE = [['.....', +                     '..O..', +                     '.OOO.', +                     '.....', +                     '.....'], +                    ['.....', +                     '..O..', +                     '..OO.', +                     '..O..', +                     '.....'], +                    ['.....', +                     '.....', +                     '.OOO.', +                     '..O..', +                     '.....'], +                    ['.....', +                     '..O..', +                     '.OO..', +                     '..O..', +                     '.....']] + +PIECES = {'S': S_SHAPE_TEMPLATE, +          'Z': Z_SHAPE_TEMPLATE, +          'J': J_SHAPE_TEMPLATE, +          'L': L_SHAPE_TEMPLATE, +          'I': I_SHAPE_TEMPLATE, +          'O': O_SHAPE_TEMPLATE, +          'T': T_SHAPE_TEMPLATE} + + +def main(): +    global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT +    pygame.init() +    pygame.mouse.set_visible(False) +     +    FPSCLOCK = pygame.time.Clock() +    #DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) +    DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT),FULLSCREEN) +    BASICFONT = pygame.font.Font('freesansbold.ttf', 18) +    BIGFONT = pygame.font.Font('freesansbold.ttf', 100) +    pygame.display.set_caption('Tetromino') + +    showTextScreen('-=TETЯIS=-') +    while True: # game loop +        if random.randint(0, 1) == 0: +            pygame.mixer.music.load('tetrisb.mid') +        else: +            pygame.mixer.music.load('tetrisc.mid') +        pygame.mixer.music.play(-1, 0.0) +        runGame() +        pygame.mixer.music.stop() +        showTextScreen('Game Over') + + +def runGame(): +    # setup variables for the start of the game +    board = getBlankBoard() +    lastMoveDownTime = time.time() +    lastMoveSidewaysTime = time.time() +    lastFallTime = time.time() +    movingDown = False # note: there is no movingUp variable +    movingLeft = False +    movingRight = False +    score = 0 +    level, fallFreq = calculateLevelAndFallFreq(score) + +    fallingPiece = getNewPiece() +    nextPiece = getNewPiece() + +    while True: # game loop +        if fallingPiece == None: +            # No falling piece in play, so start a new piece at the top +            fallingPiece = nextPiece +            nextPiece = getNewPiece() +            lastFallTime = time.time() # reset lastFallTime + +            if not isValidPosition(board, fallingPiece): +                return # can't fit a new piece on the board, so game over + +        checkForQuit() +        for event in pygame.event.get(): # event handling loop +            #key events +            if (event.type == KEYUP ): +                if (event.key == K_p): +                    # Pausing the game +                    DISPLAYSURF.fill(BGCOLOR) +                    pygame.mixer.music.stop() +                    showTextScreen('Paused') # pause until a key press +                    pygame.mixer.music.play(-1, 0.0) +                    lastFallTime = time.time() +                    lastMoveDownTime = time.time() +                    lastMoveSidewaysTime = time.time() +                elif (event.key == K_LEFT or event.key == K_a): +                    movingLeft = False +                elif (event.key == K_RIGHT or event.key == K_d): +                    movingRight = False +                elif (event.key == K_DOWN or event.key == K_s): +                    movingDown = False + +            elif (event.type == KEYDOWN ): +                # moving the piece sideways +                if (event.key == K_LEFT or event.key == K_a and isValidPosition(board, fallingPiece, adjX=-1)): +                    fallingPiece['x'] -= 1 +                    movingLeft = True +                    movingRight = False +                    lastMoveSidewaysTime = time.time() + +                elif (event.key == K_RIGHT or event.key == K_d and isValidPosition(board, fallingPiece, adjX=1)): +                    fallingPiece['x'] += 1 +                    movingRight = True +                    movingLeft = False +                    lastMoveSidewaysTime = time.time() + +                # rotating the piece (if there is room to rotate) +                elif (event.key == K_UP or event.key == K_w ): +                    fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']]) +                    if not isValidPosition(board, fallingPiece): +                        fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']]) +                elif (event.key == K_q): # rotate the other direction +                    fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']]) +                    if not isValidPosition(board, fallingPiece): +                        fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']]) + +                # making the piece fall faster with the down key +                elif (event.key == K_DOWN or event.key == K_s): +                    movingDown = True +                    if isValidPosition(board, fallingPiece, adjY=1): +                        fallingPiece['y'] += 1 +                    lastMoveDownTime = time.time() + +                # move the current piece all the way down +                elif event.key == K_SPACE: +                    movingDown = False +                    movingLeft = False +                    movingRight = False +                    for i in range(1, BOARDHEIGHT): +                        if not isValidPosition(board, fallingPiece, adjY=i): +                            break +                    fallingPiece['y'] += i - 1 + +            #joystick events +            elif (event.type == JOYBUTTONDOWN): +                if (js.get_button(9) == 1): +                    # Pausing the game +                    DISPLAYSURF.fill(BGCOLOR) +                    pygame.mixer.music.stop() +                    showTextScreen('Paused') # pause until a key press +                    pygame.mixer.music.play(-1, 0.0) +                    lastFallTime = time.time() +                    lastMoveDownTime = time.time() +                    lastMoveSidewaysTime = time.time() + +                # move the current piece all the way down with Button A +                elif (js.get_button(1) == 1): +                    movingDown = False +                    movingLeft = False +                    movingRight = False +                    for i in range(1, BOARDHEIGHT): +                        if not isValidPosition(board, fallingPiece, adjY=i): +                            break +                    fallingPiece['y'] += i - 1 + +                #rotate with button B +                elif (js.get_button(0) == 1): +                    fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']]) +                    if not isValidPosition(board, fallingPiece): +                        fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']]) + + +            elif (event.type == JOYAXISMOTION): +                # moving the piece sideways +                if (js.get_axis(0) < 0 and isValidPosition(board, fallingPiece, adjX=-1)): +                    fallingPiece['x'] -= 1 +                    movingLeft = True +                    movingRight = False +                    lastMoveSidewaysTime = time.time() + +                elif ((js.get_axis(0) > 0) and isValidPosition(board, fallingPiece, adjX=1)): +                    fallingPiece['x'] += 1 +                    movingRight = True +                    movingLeft = False +                    lastMoveSidewaysTime = time.time() + +                # rotating the piece (if there is room to rotate) +                elif (js.get_axis(1) < 0): +                    fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']]) +                    if not isValidPosition(board, fallingPiece): +                        fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']]) +                #elif (js.get_axis(1) > 0): # rotate the other direction +                #    fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']]) +                #    if not isValidPosition(board, fallingPiece): +                #        fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']]) + +                # making the piece fall faster with the down key +                elif (js.get_axis(1) > 0): +                    movingDown = True +                    if isValidPosition(board, fallingPiece, adjY=1): +                        fallingPiece['y'] += 1 +                    lastMoveDownTime = time.time() + +                elif (js.get_axis(0)== 0.0): +                    movingLeft = False +                    movingRight = False +                    movingDown = False +  +        # handle moving the piece because of user input +        if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ: +            if movingLeft and isValidPosition(board, fallingPiece, adjX=-1): +                fallingPiece['x'] -= 1 +            elif movingRight and isValidPosition(board, fallingPiece, adjX=1): +                fallingPiece['x'] += 1 +            lastMoveSidewaysTime = time.time() + +        if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1): +            fallingPiece['y'] += 1 +            lastMoveDownTime = time.time() + +        # let the piece fall if it is time to fall +        if time.time() - lastFallTime > fallFreq: +            # see if the piece has landed +            if not isValidPosition(board, fallingPiece, adjY=1): +                # falling piece has landed, set it on the board +                addToBoard(board, fallingPiece) +                score += removeCompleteLines(board) +                level, fallFreq = calculateLevelAndFallFreq(score) +                fallingPiece = None +            else: +                # piece did not land, just move the piece down +                fallingPiece['y'] += 1 +                lastFallTime = time.time() + +        # drawing everything on the screen +        DISPLAYSURF.fill(BGCOLOR) +        drawBoard(board) +        drawStatus(score, level) +        drawNextPiece(nextPiece) +        if fallingPiece != None: +            drawPiece(fallingPiece) + +        pygame.display.update() +        FPSCLOCK.tick(FPS) + + +def makeTextObjs(text, font, color): +    surf = font.render(text, True, color) +    return surf, surf.get_rect() + + +def terminate(): +    pygame.quit() +    sys.exit() + + +# KRT 17/06/2012 rewrite event detection to deal with mouse use +def checkForKeyPress(): +    for event in pygame.event.get(): +        if event.type == QUIT:      #event is quit  +            terminate() +        elif event.type == KEYDOWN: +            if event.key == K_ESCAPE:   #event is escape key +                terminate() +            else: +                return event.key   #key found return with it +        elif event.type == JOYBUTTONDOWN or event.type == JOYAXISMOTION: +            if (js.get_button(8) == 1): +                terminate() +            return 1 +    # no quit or key events in queue so return None     +    return None + + + +##def checkForKeyPress(): +##    # Go through event queue looking for a KEYUP event. +##    # Grab KEYDOWN events to remove them from the event queue. +##    checkForQuit() +## +##    for event in pygame.event.get([KEYDOWN, KEYUP]): +##        if event.type == KEYDOWN: +##            continue +##        return event.key +##    return None + + +def showTextScreen(text): +    # This function displays large text in the +    # center of the screen until a key is pressed. +    # Draw the text drop shadow +    titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTSHADOWCOLOR) +    titleRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2)) +    DISPLAYSURF.blit(titleSurf, titleRect) + +    # Draw the text +    titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTCOLOR) +    titleRect.center = (int(WINDOWWIDTH / 2) - 3, int(WINDOWHEIGHT / 2) - 3) +    DISPLAYSURF.blit(titleSurf, titleRect) + +    # Draw the additional "Press a key to play." text. +    pressKeySurf, pressKeyRect = makeTextObjs('Press a key to play. \'Select\' to quit.', BASICFONT, TEXTCOLOR) +    pressKeyRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 100) +    DISPLAYSURF.blit(pressKeySurf, pressKeyRect) + +    while checkForKeyPress() == None: +        pygame.display.update() +        FPSCLOCK.tick() + + +def checkForQuit(): +    for event in pygame.event.get(QUIT): # get all the QUIT events +        terminate() # terminate if any QUIT events are present +    for event in pygame.event.get(KEYUP): # get all the KEYUP events +        if event.key == K_ESCAPE: +            terminate() # terminate if the KEYUP event was for the Esc key +        pygame.event.post(event) # put the other KEYUP event objects back + + +def calculateLevelAndFallFreq(score): +    # Based on the score, return the level the player is on and +    # how many seconds pass until a falling piece falls one space. +    level = int(score / 10) + 1 +    fallFreq = 0.27 - (level * 0.02) +    return level, fallFreq + +def getNewPiece(): +    # return a random new piece in a random rotation and color +    shape = random.choice(list(PIECES.keys())) +    newPiece = {'shape': shape, +                'rotation': random.randint(0, len(PIECES[shape]) - 1), +                'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2), +                'y': -2, # start it above the board (i.e. less than 0) +                'color': random.randint(0, len(COLORS)-1)} +    return newPiece + + +def addToBoard(board, piece): +    # fill in the board based on piece's location, shape, and rotation +    for x in range(TEMPLATEWIDTH): +        for y in range(TEMPLATEHEIGHT): +            if PIECES[piece['shape']][piece['rotation']][y][x] != BLANK: +                board[x + piece['x']][y + piece['y']] = piece['color'] + + +def getBlankBoard(): +    # create and return a new blank board data structure +    board = [] +    for i in range(BOARDWIDTH): +        board.append([BLANK] * BOARDHEIGHT) +    return board + + +def isOnBoard(x, y): +    return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT + + +def isValidPosition(board, piece, adjX=0, adjY=0): +    # Return True if the piece is within the board and not colliding +    for x in range(TEMPLATEWIDTH): +        for y in range(TEMPLATEHEIGHT): +            isAboveBoard = y + piece['y'] + adjY < 0 +            if isAboveBoard or PIECES[piece['shape']][piece['rotation']][y][x] == BLANK: +                continue +            if not isOnBoard(x + piece['x'] + adjX, y + piece['y'] + adjY): +                return False +            if board[x + piece['x'] + adjX][y + piece['y'] + adjY] != BLANK: +                return False +    return True + +def isCompleteLine(board, y): +    # Return True if the line filled with boxes with no gaps. +    for x in range(BOARDWIDTH): +        if board[x][y] == BLANK: +            return False +    return True + + +def removeCompleteLines(board): +    # Remove any completed lines on the board, move everything above them down, and return the number of complete lines. +    numLinesRemoved = 0 +    y = BOARDHEIGHT - 1 # start y at the bottom of the board +    while y >= 0: +        if isCompleteLine(board, y): +            # Remove the line and pull boxes down by one line. +            for pullDownY in range(y, 0, -1): +                for x in range(BOARDWIDTH): +                    board[x][pullDownY] = board[x][pullDownY-1] +            # Set very top line to blank. +            for x in range(BOARDWIDTH): +                board[x][0] = BLANK +            numLinesRemoved += 1 +            # Note on the next iteration of the loop, y is the same. +            # This is so that if the line that was pulled down is also +            # complete, it will be removed. +        else: +            y -= 1 # move on to check next row up +    return numLinesRemoved + + +def convertToPixelCoords(boxx, boxy): +    # Convert the given xy coordinates of the board to xy +    # coordinates of the location on the screen. +    return (XMARGIN + (boxx * BOXSIZE)), (TOPMARGIN + (boxy * BOXSIZE)) + + +def drawBox(boxx, boxy, color, pixelx=None, pixely=None): +    # draw a single box (each tetromino piece has four boxes) +    # at xy coordinates on the board. Or, if pixelx & pixely +    # are specified, draw to the pixel coordinates stored in +    # pixelx & pixely (this is used for the "Next" piece). +    if color == BLANK: +        return +    if pixelx == None and pixely == None: +        pixelx, pixely = convertToPixelCoords(boxx, boxy) +    pygame.draw.rect(DISPLAYSURF, COLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 1, BOXSIZE - 1)) +    pygame.draw.rect(DISPLAYSURF, LIGHTCOLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 4, BOXSIZE - 4)) + + +def drawBoard(board): +    # draw the border around the board +    pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (XMARGIN - 3, TOPMARGIN - 7, (BOARDWIDTH * BOXSIZE) + 8, (BOARDHEIGHT * BOXSIZE) + 8), 5) + +    # fill the background of the board +    pygame.draw.rect(DISPLAYSURF, BGCOLOR, (XMARGIN, TOPMARGIN, BOXSIZE * BOARDWIDTH, BOXSIZE * BOARDHEIGHT)) +    # draw the individual boxes on the board +    for x in range(BOARDWIDTH): +        for y in range(BOARDHEIGHT): +            drawBox(x, y, board[x][y]) + + +def drawStatus(score, level): +    # draw the score text +    scoreSurf = BASICFONT.render('Score: %s' % score, True, TEXTCOLOR) +    scoreRect = scoreSurf.get_rect() +    scoreRect.topleft = (WINDOWWIDTH - 150, 20) +    DISPLAYSURF.blit(scoreSurf, scoreRect) + +    # draw the level text +    levelSurf = BASICFONT.render('Level: %s' % level, True, TEXTCOLOR) +    levelRect = levelSurf.get_rect() +    levelRect.topleft = (WINDOWWIDTH - 150, 50) +    DISPLAYSURF.blit(levelSurf, levelRect) + + +def drawPiece(piece, pixelx=None, pixely=None): +    shapeToDraw = PIECES[piece['shape']][piece['rotation']] +    if pixelx == None and pixely == None: +        # if pixelx & pixely hasn't been specified, use the location stored in the piece data structure +        pixelx, pixely = convertToPixelCoords(piece['x'], piece['y']) + +    # draw each of the boxes that make up the piece +    for x in range(TEMPLATEWIDTH): +        for y in range(TEMPLATEHEIGHT): +            if shapeToDraw[y][x] != BLANK: +                drawBox(None, None, piece['color'], pixelx + (x * BOXSIZE), pixely + (y * BOXSIZE)) + + +def drawNextPiece(piece): +    # draw the "next" text +    nextSurf = BASICFONT.render('Next:', True, TEXTCOLOR) +    nextRect = nextSurf.get_rect() +    nextRect.topleft = (WINDOWWIDTH - 120, 80) +    DISPLAYSURF.blit(nextSurf, nextRect) +    # draw the "next" piece +    drawPiece(piece, pixelx=WINDOWWIDTH-120, pixely=100) + + +if __name__ == '__main__': +    main() diff --git a/tetris/tetrisb.mid b/tetris/tetrisb.mid Binary files differnew file mode 100644 index 0000000..04a78d9 --- /dev/null +++ b/tetris/tetrisb.mid diff --git a/tetris/tetrisc.mid b/tetris/tetrisc.mid Binary files differnew file mode 100644 index 0000000..24a57bd --- /dev/null +++ b/tetris/tetrisc.mid  | 
