pygames
BIN
python_games/4row_arrow.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
python_games/4row_black.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
python_games/4row_board.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
python_games/4row_computerwinner.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
python_games/4row_humanwinner.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
python_games/4row_red.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
python_games/4row_tie.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
python_games/Grass_Block.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
python_games/Plain_Block.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
python_games/RedSelector.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
python_games/Rock.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
python_games/Selector.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
python_games/Star.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
python_games/Tree_Short.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
python_games/Tree_Tall.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
python_games/Tree_Ugly.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
python_games/Wall_Block_Tall.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
python_games/Wood_Block_Tall.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
python_games/badswap.wav
Normal file
BIN
python_games/beep1.ogg
Normal file
BIN
python_games/beep2.ogg
Normal file
BIN
python_games/beep3.ogg
Normal file
BIN
python_games/beep4.ogg
Normal file
11
python_games/blankpygame.py
Normal file
@ -0,0 +1,11 @@
|
||||
import pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
pygame.init()
|
||||
DISPLAYSURF = pygame.display.set_mode((400, 300))
|
||||
pygame.display.set_caption('Hello Pygame World!')
|
||||
while True: # main game loop
|
||||
for event in pygame.event.get():
|
||||
if event.type == QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
BIN
python_games/boy.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
python_games/cat.png
Normal file
After Width: | Height: | Size: 12 KiB |
47
python_games/catanimation.py
Normal file
@ -0,0 +1,47 @@
|
||||
import pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
pygame.init()
|
||||
|
||||
FPS = 30 # frames per second setting
|
||||
fpsClock = pygame.time.Clock()
|
||||
|
||||
# set up the window
|
||||
DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
|
||||
pygame.display.set_caption('Animation')
|
||||
|
||||
WHITE = (255, 255, 255)
|
||||
catImg = pygame.image.load('cat.png')
|
||||
catx = 10
|
||||
caty = 10
|
||||
direction = 'right'
|
||||
|
||||
while True: # the main game loop
|
||||
DISPLAYSURF.fill(WHITE)
|
||||
|
||||
if direction == 'right':
|
||||
catx += 5
|
||||
if catx == 280:
|
||||
direction = 'down'
|
||||
elif direction == 'down':
|
||||
caty += 5
|
||||
if caty == 220:
|
||||
direction = 'left'
|
||||
elif direction == 'left':
|
||||
catx -= 5
|
||||
if catx == 10:
|
||||
direction = 'up'
|
||||
elif direction == 'up':
|
||||
caty -= 5
|
||||
if caty == 10:
|
||||
direction = 'right'
|
||||
|
||||
DISPLAYSURF.blit(catImg, (catx, caty))
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
pygame.display.update()
|
||||
fpsClock.tick(FPS)
|
BIN
python_games/catgirl.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
41
python_games/drawing.py
Normal file
@ -0,0 +1,41 @@
|
||||
import pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
pygame.init()
|
||||
|
||||
# set up the window
|
||||
DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
|
||||
pygame.display.set_caption('Drawing')
|
||||
|
||||
# set up the colors
|
||||
BLACK = ( 0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (255, 0, 0)
|
||||
GREEN = ( 0, 255, 0)
|
||||
BLUE = ( 0, 0, 255)
|
||||
|
||||
# draw on the surface object
|
||||
DISPLAYSURF.fill(WHITE)
|
||||
pygame.draw.polygon(DISPLAYSURF, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106)))
|
||||
pygame.draw.line(DISPLAYSURF, BLUE, (60, 60), (120, 60), 4)
|
||||
pygame.draw.line(DISPLAYSURF, BLUE, (120, 60), (60, 120))
|
||||
pygame.draw.line(DISPLAYSURF, BLUE, (60, 120), (120, 120), 4)
|
||||
pygame.draw.circle(DISPLAYSURF, BLUE, (300, 50), 20, 0)
|
||||
pygame.draw.ellipse(DISPLAYSURF, RED, (300, 200, 40, 80), 1)
|
||||
pygame.draw.rect(DISPLAYSURF, RED, (200, 150, 100, 50))
|
||||
|
||||
pixObj = pygame.PixelArray(DISPLAYSURF)
|
||||
pixObj[380][280] = BLACK
|
||||
pixObj[382][282] = BLACK
|
||||
pixObj[384][284] = BLACK
|
||||
pixObj[386][286] = BLACK
|
||||
pixObj[388][288] = BLACK
|
||||
del pixObj
|
||||
|
||||
# run the game loop
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
pygame.display.update()
|
518
python_games/flippy.py
Normal file
@ -0,0 +1,518 @@
|
||||
# Flippy (an Othello or Reversi clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
# Based on the "reversi.py" code that originally appeared in "Invent
|
||||
# Your Own Computer Games with Python", chapter 15:
|
||||
# http://inventwithpython.com/chapter15.html
|
||||
|
||||
import random, sys, pygame, time, copy
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 10 # frames per second to update the screen
|
||||
WINDOWWIDTH = 640 # width of the program's window, in pixels
|
||||
WINDOWHEIGHT = 480 # height in pixels
|
||||
SPACESIZE = 50 # width & height of each space on the board, in pixels
|
||||
BOARDWIDTH = 8 # how many columns of spaces on the game board
|
||||
BOARDHEIGHT = 8 # how many rows of spaces on the game board
|
||||
WHITE_TILE = 'WHITE_TILE' # an arbitrary but unique value
|
||||
BLACK_TILE = 'BLACK_TILE' # an arbitrary but unique value
|
||||
EMPTY_SPACE = 'EMPTY_SPACE' # an arbitrary but unique value
|
||||
HINT_TILE = 'HINT_TILE' # an arbitrary but unique value
|
||||
ANIMATIONSPEED = 25 # integer from 1 to 100, higher is faster animation
|
||||
|
||||
# Amount of space on the left & right side (XMARGIN) or above and below
|
||||
# (YMARGIN) the game board, in pixels.
|
||||
XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH * SPACESIZE)) / 2)
|
||||
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * SPACESIZE)) / 2)
|
||||
|
||||
# R G B
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = ( 0, 0, 0)
|
||||
GREEN = ( 0, 155, 0)
|
||||
BRIGHTBLUE = ( 0, 50, 255)
|
||||
BROWN = (174, 94, 0)
|
||||
|
||||
TEXTBGCOLOR1 = BRIGHTBLUE
|
||||
TEXTBGCOLOR2 = GREEN
|
||||
GRIDLINECOLOR = BLACK
|
||||
TEXTCOLOR = WHITE
|
||||
HINTCOLOR = BROWN
|
||||
|
||||
|
||||
def main():
|
||||
global MAINCLOCK, DISPLAYSURF, FONT, BIGFONT, BGIMAGE
|
||||
|
||||
pygame.init()
|
||||
MAINCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
pygame.display.set_caption('Flippy')
|
||||
FONT = pygame.font.Font('freesansbold.ttf', 16)
|
||||
BIGFONT = pygame.font.Font('freesansbold.ttf', 32)
|
||||
|
||||
# Set up the background image.
|
||||
boardImage = pygame.image.load('flippyboard.png')
|
||||
# Use smoothscale() to stretch the board image to fit the entire board:
|
||||
boardImage = pygame.transform.smoothscale(boardImage, (BOARDWIDTH * SPACESIZE, BOARDHEIGHT * SPACESIZE))
|
||||
boardImageRect = boardImage.get_rect()
|
||||
boardImageRect.topleft = (XMARGIN, YMARGIN)
|
||||
BGIMAGE = pygame.image.load('flippybackground.png')
|
||||
# Use smoothscale() to stretch the background image to fit the entire window:
|
||||
BGIMAGE = pygame.transform.smoothscale(BGIMAGE, (WINDOWWIDTH, WINDOWHEIGHT))
|
||||
BGIMAGE.blit(boardImage, boardImageRect)
|
||||
|
||||
# Run the main game.
|
||||
while True:
|
||||
if runGame() == False:
|
||||
break
|
||||
|
||||
|
||||
def runGame():
|
||||
# Plays a single game of reversi each time this function is called.
|
||||
|
||||
# Reset the board and game.
|
||||
mainBoard = getNewBoard()
|
||||
resetBoard(mainBoard)
|
||||
showHints = False
|
||||
turn = random.choice(['computer', 'player'])
|
||||
|
||||
# Draw the starting board and ask the player what color they want.
|
||||
drawBoard(mainBoard)
|
||||
playerTile, computerTile = enterPlayerTile()
|
||||
|
||||
# Make the Surface and Rect objects for the "New Game" and "Hints" buttons
|
||||
newGameSurf = FONT.render('New Game', True, TEXTCOLOR, TEXTBGCOLOR2)
|
||||
newGameRect = newGameSurf.get_rect()
|
||||
newGameRect.topright = (WINDOWWIDTH - 8, 10)
|
||||
hintsSurf = FONT.render('Hints', True, TEXTCOLOR, TEXTBGCOLOR2)
|
||||
hintsRect = hintsSurf.get_rect()
|
||||
hintsRect.topright = (WINDOWWIDTH - 8, 40)
|
||||
|
||||
while True: # main game loop
|
||||
# Keep looping for player and computer's turns.
|
||||
if turn == 'player':
|
||||
# Player's turn:
|
||||
if getValidMoves(mainBoard, playerTile) == []:
|
||||
# If it's the player's turn but they
|
||||
# can't move, then end the game.
|
||||
break
|
||||
movexy = None
|
||||
while movexy == None:
|
||||
# Keep looping until the player clicks on a valid space.
|
||||
|
||||
# Determine which board data structure to use for display.
|
||||
if showHints:
|
||||
boardToDraw = getBoardWithValidMoves(mainBoard, playerTile)
|
||||
else:
|
||||
boardToDraw = mainBoard
|
||||
|
||||
checkForQuit()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == MOUSEBUTTONUP:
|
||||
# Handle mouse click events
|
||||
mousex, mousey = event.pos
|
||||
if newGameRect.collidepoint( (mousex, mousey) ):
|
||||
# Start a new game
|
||||
return True
|
||||
elif hintsRect.collidepoint( (mousex, mousey) ):
|
||||
# Toggle hints mode
|
||||
showHints = not showHints
|
||||
# movexy is set to a two-item tuple XY coordinate, or None value
|
||||
movexy = getSpaceClicked(mousex, mousey)
|
||||
if movexy != None and not isValidMove(mainBoard, playerTile, movexy[0], movexy[1]):
|
||||
movexy = None
|
||||
|
||||
# Draw the game board.
|
||||
drawBoard(boardToDraw)
|
||||
drawInfo(boardToDraw, playerTile, computerTile, turn)
|
||||
|
||||
# Draw the "New Game" and "Hints" buttons.
|
||||
DISPLAYSURF.blit(newGameSurf, newGameRect)
|
||||
DISPLAYSURF.blit(hintsSurf, hintsRect)
|
||||
|
||||
MAINCLOCK.tick(FPS)
|
||||
pygame.display.update()
|
||||
|
||||
# Make the move and end the turn.
|
||||
makeMove(mainBoard, playerTile, movexy[0], movexy[1], True)
|
||||
if getValidMoves(mainBoard, computerTile) != []:
|
||||
# Only set for the computer's turn if it can make a move.
|
||||
turn = 'computer'
|
||||
|
||||
else:
|
||||
# Computer's turn:
|
||||
if getValidMoves(mainBoard, computerTile) == []:
|
||||
# If it was set to be the computer's turn but
|
||||
# they can't move, then end the game.
|
||||
break
|
||||
|
||||
# Draw the board.
|
||||
drawBoard(mainBoard)
|
||||
drawInfo(mainBoard, playerTile, computerTile, turn)
|
||||
|
||||
# Draw the "New Game" and "Hints" buttons.
|
||||
DISPLAYSURF.blit(newGameSurf, newGameRect)
|
||||
DISPLAYSURF.blit(hintsSurf, hintsRect)
|
||||
|
||||
# Make it look like the computer is thinking by pausing a bit.
|
||||
pauseUntil = time.time() + random.randint(5, 15) * 0.1
|
||||
while time.time() < pauseUntil:
|
||||
pygame.display.update()
|
||||
|
||||
# Make the move and end the turn.
|
||||
x, y = getComputerMove(mainBoard, computerTile)
|
||||
makeMove(mainBoard, computerTile, x, y, True)
|
||||
if getValidMoves(mainBoard, playerTile) != []:
|
||||
# Only set for the player's turn if they can make a move.
|
||||
turn = 'player'
|
||||
|
||||
# Display the final score.
|
||||
drawBoard(mainBoard)
|
||||
scores = getScoreOfBoard(mainBoard)
|
||||
|
||||
# Determine the text of the message to display.
|
||||
if scores[playerTile] > scores[computerTile]:
|
||||
text = 'You beat the computer by %s points! Congratulations!' % \
|
||||
(scores[playerTile] - scores[computerTile])
|
||||
elif scores[playerTile] < scores[computerTile]:
|
||||
text = 'You lost. The computer beat you by %s points.' % \
|
||||
(scores[computerTile] - scores[playerTile])
|
||||
else:
|
||||
text = 'The game was a tie!'
|
||||
|
||||
textSurf = FONT.render(text, True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
textRect = textSurf.get_rect()
|
||||
textRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
|
||||
DISPLAYSURF.blit(textSurf, textRect)
|
||||
|
||||
# Display the "Play again?" text with Yes and No buttons.
|
||||
text2Surf = BIGFONT.render('Play again?', True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
text2Rect = text2Surf.get_rect()
|
||||
text2Rect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 50)
|
||||
|
||||
# Make "Yes" button.
|
||||
yesSurf = BIGFONT.render('Yes', True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
yesRect = yesSurf.get_rect()
|
||||
yesRect.center = (int(WINDOWWIDTH / 2) - 60, int(WINDOWHEIGHT / 2) + 90)
|
||||
|
||||
# Make "No" button.
|
||||
noSurf = BIGFONT.render('No', True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
noRect = noSurf.get_rect()
|
||||
noRect.center = (int(WINDOWWIDTH / 2) + 60, int(WINDOWHEIGHT / 2) + 90)
|
||||
|
||||
while True:
|
||||
# Process events until the user clicks on Yes or No.
|
||||
checkForQuit()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == MOUSEBUTTONUP:
|
||||
mousex, mousey = event.pos
|
||||
if yesRect.collidepoint( (mousex, mousey) ):
|
||||
return True
|
||||
elif noRect.collidepoint( (mousex, mousey) ):
|
||||
return False
|
||||
DISPLAYSURF.blit(textSurf, textRect)
|
||||
DISPLAYSURF.blit(text2Surf, text2Rect)
|
||||
DISPLAYSURF.blit(yesSurf, yesRect)
|
||||
DISPLAYSURF.blit(noSurf, noRect)
|
||||
pygame.display.update()
|
||||
MAINCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def translateBoardToPixelCoord(x, y):
|
||||
return XMARGIN + x * SPACESIZE + int(SPACESIZE / 2), YMARGIN + y * SPACESIZE + int(SPACESIZE / 2)
|
||||
|
||||
|
||||
def animateTileChange(tilesToFlip, tileColor, additionalTile):
|
||||
# Draw the additional tile that was just laid down. (Otherwise we'd
|
||||
# have to completely redraw the board & the board info.)
|
||||
if tileColor == WHITE_TILE:
|
||||
additionalTileColor = WHITE
|
||||
else:
|
||||
additionalTileColor = BLACK
|
||||
additionalTileX, additionalTileY = translateBoardToPixelCoord(additionalTile[0], additionalTile[1])
|
||||
pygame.draw.circle(DISPLAYSURF, additionalTileColor, (additionalTileX, additionalTileY), int(SPACESIZE / 2) - 4)
|
||||
pygame.display.update()
|
||||
|
||||
for rgbValues in range(0, 255, int(ANIMATIONSPEED * 2.55)):
|
||||
if rgbValues > 255:
|
||||
rgbValues = 255
|
||||
elif rgbValues < 0:
|
||||
rgbValues = 0
|
||||
|
||||
if tileColor == WHITE_TILE:
|
||||
color = tuple([rgbValues] * 3) # rgbValues goes from 0 to 255
|
||||
elif tileColor == BLACK_TILE:
|
||||
color = tuple([255 - rgbValues] * 3) # rgbValues goes from 255 to 0
|
||||
|
||||
for x, y in tilesToFlip:
|
||||
centerx, centery = translateBoardToPixelCoord(x, y)
|
||||
pygame.draw.circle(DISPLAYSURF, color, (centerx, centery), int(SPACESIZE / 2) - 4)
|
||||
pygame.display.update()
|
||||
MAINCLOCK.tick(FPS)
|
||||
checkForQuit()
|
||||
|
||||
|
||||
def drawBoard(board):
|
||||
# Draw background of board.
|
||||
DISPLAYSURF.blit(BGIMAGE, BGIMAGE.get_rect())
|
||||
|
||||
# Draw grid lines of the board.
|
||||
for x in range(BOARDWIDTH + 1):
|
||||
# Draw the horizontal lines.
|
||||
startx = (x * SPACESIZE) + XMARGIN
|
||||
starty = YMARGIN
|
||||
endx = (x * SPACESIZE) + XMARGIN
|
||||
endy = YMARGIN + (BOARDHEIGHT * SPACESIZE)
|
||||
pygame.draw.line(DISPLAYSURF, GRIDLINECOLOR, (startx, starty), (endx, endy))
|
||||
for y in range(BOARDHEIGHT + 1):
|
||||
# Draw the vertical lines.
|
||||
startx = XMARGIN
|
||||
starty = (y * SPACESIZE) + YMARGIN
|
||||
endx = XMARGIN + (BOARDWIDTH * SPACESIZE)
|
||||
endy = (y * SPACESIZE) + YMARGIN
|
||||
pygame.draw.line(DISPLAYSURF, GRIDLINECOLOR, (startx, starty), (endx, endy))
|
||||
|
||||
# Draw the black & white tiles or hint spots.
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
centerx, centery = translateBoardToPixelCoord(x, y)
|
||||
if board[x][y] == WHITE_TILE or board[x][y] == BLACK_TILE:
|
||||
if board[x][y] == WHITE_TILE:
|
||||
tileColor = WHITE
|
||||
else:
|
||||
tileColor = BLACK
|
||||
pygame.draw.circle(DISPLAYSURF, tileColor, (centerx, centery), int(SPACESIZE / 2) - 4)
|
||||
if board[x][y] == HINT_TILE:
|
||||
pygame.draw.rect(DISPLAYSURF, HINTCOLOR, (centerx - 4, centery - 4, 8, 8))
|
||||
|
||||
|
||||
def getSpaceClicked(mousex, mousey):
|
||||
# Return a tuple of two integers of the board space coordinates where
|
||||
# the mouse was clicked. (Or returns None not in any space.)
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if mousex > x * SPACESIZE + XMARGIN and \
|
||||
mousex < (x + 1) * SPACESIZE + XMARGIN and \
|
||||
mousey > y * SPACESIZE + YMARGIN and \
|
||||
mousey < (y + 1) * SPACESIZE + YMARGIN:
|
||||
return (x, y)
|
||||
return None
|
||||
|
||||
|
||||
def drawInfo(board, playerTile, computerTile, turn):
|
||||
# Draws scores and whose turn it is at the bottom of the screen.
|
||||
scores = getScoreOfBoard(board)
|
||||
scoreSurf = FONT.render("Player Score: %s Computer Score: %s %s's Turn" % (str(scores[playerTile]), str(scores[computerTile]), turn.title()), True, TEXTCOLOR)
|
||||
scoreRect = scoreSurf.get_rect()
|
||||
scoreRect.bottomleft = (10, WINDOWHEIGHT - 5)
|
||||
DISPLAYSURF.blit(scoreSurf, scoreRect)
|
||||
|
||||
|
||||
def resetBoard(board):
|
||||
# Blanks out the board it is passed, and sets up starting tiles.
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
board[x][y] = EMPTY_SPACE
|
||||
|
||||
# Add starting pieces to the center
|
||||
board[3][3] = WHITE_TILE
|
||||
board[3][4] = BLACK_TILE
|
||||
board[4][3] = BLACK_TILE
|
||||
board[4][4] = WHITE_TILE
|
||||
|
||||
|
||||
def getNewBoard():
|
||||
# Creates a brand new, empty board data structure.
|
||||
board = []
|
||||
for i in range(BOARDWIDTH):
|
||||
board.append([EMPTY_SPACE] * BOARDHEIGHT)
|
||||
|
||||
return board
|
||||
|
||||
|
||||
def isValidMove(board, tile, xstart, ystart):
|
||||
# Returns False if the player's move is invalid. If it is a valid
|
||||
# move, returns a list of spaces of the captured pieces.
|
||||
if board[xstart][ystart] != EMPTY_SPACE or not isOnBoard(xstart, ystart):
|
||||
return False
|
||||
|
||||
board[xstart][ystart] = tile # temporarily set the tile on the board.
|
||||
|
||||
if tile == WHITE_TILE:
|
||||
otherTile = BLACK_TILE
|
||||
else:
|
||||
otherTile = WHITE_TILE
|
||||
|
||||
tilesToFlip = []
|
||||
# check each of the eight directions:
|
||||
for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
|
||||
x, y = xstart, ystart
|
||||
x += xdirection
|
||||
y += ydirection
|
||||
if isOnBoard(x, y) and board[x][y] == otherTile:
|
||||
# The piece belongs to the other player next to our piece.
|
||||
x += xdirection
|
||||
y += ydirection
|
||||
if not isOnBoard(x, y):
|
||||
continue
|
||||
while board[x][y] == otherTile:
|
||||
x += xdirection
|
||||
y += ydirection
|
||||
if not isOnBoard(x, y):
|
||||
break # break out of while loop, continue in for loop
|
||||
if not isOnBoard(x, y):
|
||||
continue
|
||||
if board[x][y] == tile:
|
||||
# There are pieces to flip over. Go in the reverse
|
||||
# direction until we reach the original space, noting all
|
||||
# the tiles along the way.
|
||||
while True:
|
||||
x -= xdirection
|
||||
y -= ydirection
|
||||
if x == xstart and y == ystart:
|
||||
break
|
||||
tilesToFlip.append([x, y])
|
||||
|
||||
board[xstart][ystart] = EMPTY_SPACE # make space empty
|
||||
if len(tilesToFlip) == 0: # If no tiles flipped, this move is invalid
|
||||
return False
|
||||
return tilesToFlip
|
||||
|
||||
|
||||
def isOnBoard(x, y):
|
||||
# Returns True if the coordinates are located on the board.
|
||||
return x >= 0 and x < BOARDWIDTH and y >= 0 and y < BOARDHEIGHT
|
||||
|
||||
|
||||
def getBoardWithValidMoves(board, tile):
|
||||
# Returns a new board with hint markings.
|
||||
dupeBoard = copy.deepcopy(board)
|
||||
|
||||
for x, y in getValidMoves(dupeBoard, tile):
|
||||
dupeBoard[x][y] = HINT_TILE
|
||||
return dupeBoard
|
||||
|
||||
|
||||
def getValidMoves(board, tile):
|
||||
# Returns a list of (x,y) tuples of all valid moves.
|
||||
validMoves = []
|
||||
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if isValidMove(board, tile, x, y) != False:
|
||||
validMoves.append((x, y))
|
||||
return validMoves
|
||||
|
||||
|
||||
def getScoreOfBoard(board):
|
||||
# Determine the score by counting the tiles.
|
||||
xscore = 0
|
||||
oscore = 0
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if board[x][y] == WHITE_TILE:
|
||||
xscore += 1
|
||||
if board[x][y] == BLACK_TILE:
|
||||
oscore += 1
|
||||
return {WHITE_TILE:xscore, BLACK_TILE:oscore}
|
||||
|
||||
|
||||
def enterPlayerTile():
|
||||
# Draws the text and handles the mouse click events for letting
|
||||
# the player choose which color they want to be. Returns
|
||||
# [WHITE_TILE, BLACK_TILE] if the player chooses to be White,
|
||||
# [BLACK_TILE, WHITE_TILE] if Black.
|
||||
|
||||
# Create the text.
|
||||
textSurf = FONT.render('Do you want to be white or black?', True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
textRect = textSurf.get_rect()
|
||||
textRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
|
||||
|
||||
xSurf = BIGFONT.render('White', True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
xRect = xSurf.get_rect()
|
||||
xRect.center = (int(WINDOWWIDTH / 2) - 60, int(WINDOWHEIGHT / 2) + 40)
|
||||
|
||||
oSurf = BIGFONT.render('Black', True, TEXTCOLOR, TEXTBGCOLOR1)
|
||||
oRect = oSurf.get_rect()
|
||||
oRect.center = (int(WINDOWWIDTH / 2) + 60, int(WINDOWHEIGHT / 2) + 40)
|
||||
|
||||
while True:
|
||||
# Keep looping until the player has clicked on a color.
|
||||
checkForQuit()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == MOUSEBUTTONUP:
|
||||
mousex, mousey = event.pos
|
||||
if xRect.collidepoint( (mousex, mousey) ):
|
||||
return [WHITE_TILE, BLACK_TILE]
|
||||
elif oRect.collidepoint( (mousex, mousey) ):
|
||||
return [BLACK_TILE, WHITE_TILE]
|
||||
|
||||
# Draw the screen.
|
||||
DISPLAYSURF.blit(textSurf, textRect)
|
||||
DISPLAYSURF.blit(xSurf, xRect)
|
||||
DISPLAYSURF.blit(oSurf, oRect)
|
||||
pygame.display.update()
|
||||
MAINCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def makeMove(board, tile, xstart, ystart, realMove=False):
|
||||
# Place the tile on the board at xstart, ystart, and flip tiles
|
||||
# Returns False if this is an invalid move, True if it is valid.
|
||||
tilesToFlip = isValidMove(board, tile, xstart, ystart)
|
||||
|
||||
if tilesToFlip == False:
|
||||
return False
|
||||
|
||||
board[xstart][ystart] = tile
|
||||
|
||||
if realMove:
|
||||
animateTileChange(tilesToFlip, tile, (xstart, ystart))
|
||||
|
||||
for x, y in tilesToFlip:
|
||||
board[x][y] = tile
|
||||
return True
|
||||
|
||||
|
||||
def isOnCorner(x, y):
|
||||
# Returns True if the position is in one of the four corners.
|
||||
return (x == 0 and y == 0) or \
|
||||
(x == BOARDWIDTH and y == 0) or \
|
||||
(x == 0 and y == BOARDHEIGHT) or \
|
||||
(x == BOARDWIDTH and y == BOARDHEIGHT)
|
||||
|
||||
|
||||
def getComputerMove(board, computerTile):
|
||||
# Given a board and the computer's tile, determine where to
|
||||
# move and return that move as a [x, y] list.
|
||||
possibleMoves = getValidMoves(board, computerTile)
|
||||
|
||||
# randomize the order of the possible moves
|
||||
random.shuffle(possibleMoves)
|
||||
|
||||
# always go for a corner if available.
|
||||
for x, y in possibleMoves:
|
||||
if isOnCorner(x, y):
|
||||
return [x, y]
|
||||
|
||||
# Go through all possible moves and remember the best scoring move
|
||||
bestScore = -1
|
||||
for x, y in possibleMoves:
|
||||
dupeBoard = copy.deepcopy(board)
|
||||
makeMove(dupeBoard, computerTile, x, y)
|
||||
score = getScoreOfBoard(dupeBoard)[computerTile]
|
||||
if score > bestScore:
|
||||
bestMove = [x, y]
|
||||
bestScore = score
|
||||
return bestMove
|
||||
|
||||
|
||||
def checkForQuit():
|
||||
for event in pygame.event.get((QUIT, KEYUP)): # event handling loop
|
||||
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
python_games/flippybackground.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
python_games/flippyboard.png
Normal file
After Width: | Height: | Size: 333 KiB |
363
python_games/fourinarow.py
Normal file
@ -0,0 +1,363 @@
|
||||
# Four-In-A-Row (a Connect Four clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import random, copy, sys, pygame
|
||||
from pygame.locals import *
|
||||
|
||||
BOARDWIDTH = 7 # how many spaces wide the board is
|
||||
BOARDHEIGHT = 6 # how many spaces tall the board is
|
||||
assert BOARDWIDTH >= 4 and BOARDHEIGHT >= 4, 'Board must be at least 4x4.'
|
||||
|
||||
DIFFICULTY = 2 # how many moves to look ahead. (>2 is usually too much)
|
||||
|
||||
SPACESIZE = 50 # size of the tokens and individual board spaces in pixels
|
||||
|
||||
FPS = 30 # frames per second to update the screen
|
||||
WINDOWWIDTH = 640 # width of the program's window, in pixels
|
||||
WINDOWHEIGHT = 480 # height in pixels
|
||||
|
||||
XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * SPACESIZE) / 2)
|
||||
YMARGIN = int((WINDOWHEIGHT - BOARDHEIGHT * SPACESIZE) / 2)
|
||||
|
||||
BRIGHTBLUE = (0, 50, 255)
|
||||
WHITE = (255, 255, 255)
|
||||
|
||||
BGCOLOR = BRIGHTBLUE
|
||||
TEXTCOLOR = WHITE
|
||||
|
||||
RED = 'red'
|
||||
BLACK = 'black'
|
||||
EMPTY = None
|
||||
HUMAN = 'human'
|
||||
COMPUTER = 'computer'
|
||||
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, REDPILERECT, BLACKPILERECT, REDTOKENIMG
|
||||
global BLACKTOKENIMG, BOARDIMG, ARROWIMG, ARROWRECT, HUMANWINNERIMG
|
||||
global COMPUTERWINNERIMG, WINNERRECT, TIEWINNERIMG
|
||||
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
pygame.display.set_caption('Four in a Row')
|
||||
|
||||
REDPILERECT = pygame.Rect(int(SPACESIZE / 2), WINDOWHEIGHT - int(3 * SPACESIZE / 2), SPACESIZE, SPACESIZE)
|
||||
BLACKPILERECT = pygame.Rect(WINDOWWIDTH - int(3 * SPACESIZE / 2), WINDOWHEIGHT - int(3 * SPACESIZE / 2), SPACESIZE, SPACESIZE)
|
||||
REDTOKENIMG = pygame.image.load('4row_red.png')
|
||||
REDTOKENIMG = pygame.transform.smoothscale(REDTOKENIMG, (SPACESIZE, SPACESIZE))
|
||||
BLACKTOKENIMG = pygame.image.load('4row_black.png')
|
||||
BLACKTOKENIMG = pygame.transform.smoothscale(BLACKTOKENIMG, (SPACESIZE, SPACESIZE))
|
||||
BOARDIMG = pygame.image.load('4row_board.png')
|
||||
BOARDIMG = pygame.transform.smoothscale(BOARDIMG, (SPACESIZE, SPACESIZE))
|
||||
|
||||
HUMANWINNERIMG = pygame.image.load('4row_humanwinner.png')
|
||||
COMPUTERWINNERIMG = pygame.image.load('4row_computerwinner.png')
|
||||
TIEWINNERIMG = pygame.image.load('4row_tie.png')
|
||||
WINNERRECT = HUMANWINNERIMG.get_rect()
|
||||
WINNERRECT.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
|
||||
|
||||
ARROWIMG = pygame.image.load('4row_arrow.png')
|
||||
ARROWRECT = ARROWIMG.get_rect()
|
||||
ARROWRECT.left = REDPILERECT.right + 10
|
||||
ARROWRECT.centery = REDPILERECT.centery
|
||||
|
||||
isFirstGame = True
|
||||
|
||||
while True:
|
||||
runGame(isFirstGame)
|
||||
isFirstGame = False
|
||||
|
||||
|
||||
def runGame(isFirstGame):
|
||||
if isFirstGame:
|
||||
# Let the computer go first on the first game, so the player
|
||||
# can see how the tokens are dragged from the token piles.
|
||||
turn = COMPUTER
|
||||
showHelp = True
|
||||
else:
|
||||
# Randomly choose who goes first.
|
||||
if random.randint(0, 1) == 0:
|
||||
turn = COMPUTER
|
||||
else:
|
||||
turn = HUMAN
|
||||
showHelp = False
|
||||
|
||||
# Set up a blank board data structure.
|
||||
mainBoard = getNewBoard()
|
||||
|
||||
while True: # main game loop
|
||||
if turn == HUMAN:
|
||||
# Human player's turn.
|
||||
getHumanMove(mainBoard, showHelp)
|
||||
if showHelp:
|
||||
# turn off help arrow after the first move
|
||||
showHelp = False
|
||||
if isWinner(mainBoard, RED):
|
||||
winnerImg = HUMANWINNERIMG
|
||||
break
|
||||
turn = COMPUTER # switch to other player's turn
|
||||
else:
|
||||
# Computer player's turn.
|
||||
column = getComputerMove(mainBoard)
|
||||
animateComputerMoving(mainBoard, column)
|
||||
makeMove(mainBoard, BLACK, column)
|
||||
if isWinner(mainBoard, BLACK):
|
||||
winnerImg = COMPUTERWINNERIMG
|
||||
break
|
||||
turn = HUMAN # switch to other player's turn
|
||||
|
||||
if isBoardFull(mainBoard):
|
||||
# A completely filled board means it's a tie.
|
||||
winnerImg = TIEWINNERIMG
|
||||
break
|
||||
|
||||
while True:
|
||||
# Keep looping until player clicks the mouse or quits.
|
||||
drawBoard(mainBoard)
|
||||
DISPLAYSURF.blit(winnerImg, WINNERRECT)
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == MOUSEBUTTONUP:
|
||||
return
|
||||
|
||||
|
||||
def makeMove(board, player, column):
|
||||
lowest = getLowestEmptySpace(board, column)
|
||||
if lowest != -1:
|
||||
board[column][lowest] = player
|
||||
|
||||
|
||||
def drawBoard(board, extraToken=None):
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
|
||||
# draw tokens
|
||||
spaceRect = pygame.Rect(0, 0, SPACESIZE, SPACESIZE)
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
spaceRect.topleft = (XMARGIN + (x * SPACESIZE), YMARGIN + (y * SPACESIZE))
|
||||
if board[x][y] == RED:
|
||||
DISPLAYSURF.blit(REDTOKENIMG, spaceRect)
|
||||
elif board[x][y] == BLACK:
|
||||
DISPLAYSURF.blit(BLACKTOKENIMG, spaceRect)
|
||||
|
||||
# draw the extra token
|
||||
if extraToken != None:
|
||||
if extraToken['color'] == RED:
|
||||
DISPLAYSURF.blit(REDTOKENIMG, (extraToken['x'], extraToken['y'], SPACESIZE, SPACESIZE))
|
||||
elif extraToken['color'] == BLACK:
|
||||
DISPLAYSURF.blit(BLACKTOKENIMG, (extraToken['x'], extraToken['y'], SPACESIZE, SPACESIZE))
|
||||
|
||||
# draw board over the tokens
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
spaceRect.topleft = (XMARGIN + (x * SPACESIZE), YMARGIN + (y * SPACESIZE))
|
||||
DISPLAYSURF.blit(BOARDIMG, spaceRect)
|
||||
|
||||
# draw the red and black tokens off to the side
|
||||
DISPLAYSURF.blit(REDTOKENIMG, REDPILERECT) # red on the left
|
||||
DISPLAYSURF.blit(BLACKTOKENIMG, BLACKPILERECT) # black on the right
|
||||
|
||||
|
||||
def getNewBoard():
|
||||
board = []
|
||||
for x in range(BOARDWIDTH):
|
||||
board.append([EMPTY] * BOARDHEIGHT)
|
||||
return board
|
||||
|
||||
|
||||
def getHumanMove(board, isFirstMove):
|
||||
draggingToken = False
|
||||
tokenx, tokeny = None, None
|
||||
while True:
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == MOUSEBUTTONDOWN and not draggingToken and REDPILERECT.collidepoint(event.pos):
|
||||
# start of dragging on red token pile.
|
||||
draggingToken = True
|
||||
tokenx, tokeny = event.pos
|
||||
elif event.type == MOUSEMOTION and draggingToken:
|
||||
# update the position of the red token being dragged
|
||||
tokenx, tokeny = event.pos
|
||||
elif event.type == MOUSEBUTTONUP and draggingToken:
|
||||
# let go of the token being dragged
|
||||
if tokeny < YMARGIN and tokenx > XMARGIN and tokenx < WINDOWWIDTH - XMARGIN:
|
||||
# let go at the top of the screen.
|
||||
column = int((tokenx - XMARGIN) / SPACESIZE)
|
||||
if isValidMove(board, column):
|
||||
animateDroppingToken(board, column, RED)
|
||||
board[column][getLowestEmptySpace(board, column)] = RED
|
||||
drawBoard(board)
|
||||
pygame.display.update()
|
||||
return
|
||||
tokenx, tokeny = None, None
|
||||
draggingToken = False
|
||||
if tokenx != None and tokeny != None:
|
||||
drawBoard(board, {'x':tokenx - int(SPACESIZE / 2), 'y':tokeny - int(SPACESIZE / 2), 'color':RED})
|
||||
else:
|
||||
drawBoard(board)
|
||||
|
||||
if isFirstMove:
|
||||
# Show the help arrow for the player's first move.
|
||||
DISPLAYSURF.blit(ARROWIMG, ARROWRECT)
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick()
|
||||
|
||||
|
||||
def animateDroppingToken(board, column, color):
|
||||
x = XMARGIN + column * SPACESIZE
|
||||
y = YMARGIN - SPACESIZE
|
||||
dropSpeed = 1.0
|
||||
|
||||
lowestEmptySpace = getLowestEmptySpace(board, column)
|
||||
|
||||
while True:
|
||||
y += int(dropSpeed)
|
||||
dropSpeed += 0.5
|
||||
if int((y - YMARGIN) / SPACESIZE) >= lowestEmptySpace:
|
||||
return
|
||||
drawBoard(board, {'x':x, 'y':y, 'color':color})
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick()
|
||||
|
||||
|
||||
def animateComputerMoving(board, column):
|
||||
x = BLACKPILERECT.left
|
||||
y = BLACKPILERECT.top
|
||||
speed = 1.0
|
||||
# moving the black tile up
|
||||
while y > (YMARGIN - SPACESIZE):
|
||||
y -= int(speed)
|
||||
speed += 0.5
|
||||
drawBoard(board, {'x':x, 'y':y, 'color':BLACK})
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick()
|
||||
# moving the black tile over
|
||||
y = YMARGIN - SPACESIZE
|
||||
speed = 1.0
|
||||
while x > (XMARGIN + column * SPACESIZE):
|
||||
x -= int(speed)
|
||||
speed += 0.5
|
||||
drawBoard(board, {'x':x, 'y':y, 'color':BLACK})
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick()
|
||||
# dropping the black tile
|
||||
animateDroppingToken(board, column, BLACK)
|
||||
|
||||
|
||||
def getComputerMove(board):
|
||||
potentialMoves = getPotentialMoves(board, BLACK, DIFFICULTY)
|
||||
# get the best fitness from the potential moves
|
||||
bestMoveFitness = -1
|
||||
for i in range(BOARDWIDTH):
|
||||
if potentialMoves[i] > bestMoveFitness and isValidMove(board, i):
|
||||
bestMoveFitness = potentialMoves[i]
|
||||
# find all potential moves that have this best fitness
|
||||
bestMoves = []
|
||||
for i in range(len(potentialMoves)):
|
||||
if potentialMoves[i] == bestMoveFitness and isValidMove(board, i):
|
||||
bestMoves.append(i)
|
||||
return random.choice(bestMoves)
|
||||
|
||||
|
||||
def getPotentialMoves(board, tile, lookAhead):
|
||||
if lookAhead == 0 or isBoardFull(board):
|
||||
return [0] * BOARDWIDTH
|
||||
|
||||
if tile == RED:
|
||||
enemyTile = BLACK
|
||||
else:
|
||||
enemyTile = RED
|
||||
|
||||
# Figure out the best move to make.
|
||||
potentialMoves = [0] * BOARDWIDTH
|
||||
for firstMove in range(BOARDWIDTH):
|
||||
dupeBoard = copy.deepcopy(board)
|
||||
if not isValidMove(dupeBoard, firstMove):
|
||||
continue
|
||||
makeMove(dupeBoard, tile, firstMove)
|
||||
if isWinner(dupeBoard, tile):
|
||||
# a winning move automatically gets a perfect fitness
|
||||
potentialMoves[firstMove] = 1
|
||||
break # don't bother calculating other moves
|
||||
else:
|
||||
# do other player's counter moves and determine best one
|
||||
if isBoardFull(dupeBoard):
|
||||
potentialMoves[firstMove] = 0
|
||||
else:
|
||||
for counterMove in range(BOARDWIDTH):
|
||||
dupeBoard2 = copy.deepcopy(dupeBoard)
|
||||
if not isValidMove(dupeBoard2, counterMove):
|
||||
continue
|
||||
makeMove(dupeBoard2, enemyTile, counterMove)
|
||||
if isWinner(dupeBoard2, enemyTile):
|
||||
# a losing move automatically gets the worst fitness
|
||||
potentialMoves[firstMove] = -1
|
||||
break
|
||||
else:
|
||||
# do the recursive call to getPotentialMoves()
|
||||
results = getPotentialMoves(dupeBoard2, tile, lookAhead - 1)
|
||||
potentialMoves[firstMove] += (sum(results) / BOARDWIDTH) / BOARDWIDTH
|
||||
return potentialMoves
|
||||
|
||||
|
||||
def getLowestEmptySpace(board, column):
|
||||
# Return the row number of the lowest empty row in the given column.
|
||||
for y in range(BOARDHEIGHT-1, -1, -1):
|
||||
if board[column][y] == EMPTY:
|
||||
return y
|
||||
return -1
|
||||
|
||||
|
||||
def isValidMove(board, column):
|
||||
# Returns True if there is an empty space in the given column.
|
||||
# Otherwise returns False.
|
||||
if column < 0 or column >= (BOARDWIDTH) or board[column][0] != EMPTY:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isBoardFull(board):
|
||||
# Returns True if there are no empty spaces anywhere on the board.
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if board[x][y] == EMPTY:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isWinner(board, tile):
|
||||
# check horizontal spaces
|
||||
for x in range(BOARDWIDTH - 3):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if board[x][y] == tile and board[x+1][y] == tile and board[x+2][y] == tile and board[x+3][y] == tile:
|
||||
return True
|
||||
# check vertical spaces
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT - 3):
|
||||
if board[x][y] == tile and board[x][y+1] == tile and board[x][y+2] == tile and board[x][y+3] == tile:
|
||||
return True
|
||||
# check / diagonal spaces
|
||||
for x in range(BOARDWIDTH - 3):
|
||||
for y in range(3, BOARDHEIGHT):
|
||||
if board[x][y] == tile and board[x+1][y-1] == tile and board[x+2][y-2] == tile and board[x+3][y-3] == tile:
|
||||
return True
|
||||
# check \ diagonal spaces
|
||||
for x in range(BOARDWIDTH - 3):
|
||||
for y in range(BOARDHEIGHT - 3):
|
||||
if board[x][y] == tile and board[x+1][y+1] == tile and board[x+2][y+2] == tile and board[x+3][y+3] == tile:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
python_games/gameicon.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
python_games/gem1.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
python_games/gem2.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
python_games/gem3.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
python_games/gem4.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
python_games/gem5.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
python_games/gem6.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
python_games/gem7.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
549
python_games/gemgem.py
Normal file
@ -0,0 +1,549 @@
|
||||
# Gemgem (a Bejeweled clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
"""
|
||||
This program has "gem data structures", which are basically dictionaries
|
||||
with the following keys:
|
||||
'x' and 'y' - The location of the gem on the board. 0,0 is the top left.
|
||||
There is also a ROWABOVEBOARD row that 'y' can be set to,
|
||||
to indicate that it is above the board.
|
||||
'direction' - one of the four constant variables UP, DOWN, LEFT, RIGHT.
|
||||
This is the direction the gem is moving.
|
||||
'imageNum' - The integer index into GEMIMAGES to denote which image
|
||||
this gem uses.
|
||||
"""
|
||||
|
||||
import random, time, pygame, sys, copy
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 30 # frames per second to update the screen
|
||||
WINDOWWIDTH = 600 # width of the program's window, in pixels
|
||||
WINDOWHEIGHT = 600 # height in pixels
|
||||
|
||||
BOARDWIDTH = 8 # how many columns in the board
|
||||
BOARDHEIGHT = 8 # how many rows in the board
|
||||
GEMIMAGESIZE = 64 # width & height of each space in pixels
|
||||
|
||||
# NUMGEMIMAGES is the number of gem types. You will need .png image
|
||||
# files named gem0.png, gem1.png, etc. up to gem(N-1).png.
|
||||
NUMGEMIMAGES = 7
|
||||
assert NUMGEMIMAGES >= 5 # game needs at least 5 types of gems to work
|
||||
|
||||
# NUMMATCHSOUNDS is the number of different sounds to choose from when
|
||||
# a match is made. The .wav files are named match0.wav, match1.wav, etc.
|
||||
NUMMATCHSOUNDS = 6
|
||||
|
||||
MOVERATE = 25 # 1 to 100, larger num means faster animations
|
||||
DEDUCTSPEED = 0.8 # reduces score by 1 point every DEDUCTSPEED seconds.
|
||||
|
||||
# R G B
|
||||
PURPLE = (255, 0, 255)
|
||||
LIGHTBLUE = (170, 190, 255)
|
||||
BLUE = ( 0, 0, 255)
|
||||
RED = (255, 100, 100)
|
||||
BLACK = ( 0, 0, 0)
|
||||
BROWN = ( 85, 65, 0)
|
||||
HIGHLIGHTCOLOR = PURPLE # color of the selected gem's border
|
||||
BGCOLOR = LIGHTBLUE # background color on the screen
|
||||
GRIDCOLOR = BLUE # color of the game board
|
||||
GAMEOVERCOLOR = RED # color of the "Game over" text.
|
||||
GAMEOVERBGCOLOR = BLACK # background color of the "Game over" text.
|
||||
SCORECOLOR = BROWN # color of the text for the player's score
|
||||
|
||||
# The amount of space to the sides of the board to the edge of the window
|
||||
# is used several times, so calculate it once here and store in variables.
|
||||
XMARGIN = int((WINDOWWIDTH - GEMIMAGESIZE * BOARDWIDTH) / 2)
|
||||
YMARGIN = int((WINDOWHEIGHT - GEMIMAGESIZE * BOARDHEIGHT) / 2)
|
||||
|
||||
# constants for direction values
|
||||
UP = 'up'
|
||||
DOWN = 'down'
|
||||
LEFT = 'left'
|
||||
RIGHT = 'right'
|
||||
|
||||
EMPTY_SPACE = -1 # an arbitrary, nonpositive value
|
||||
ROWABOVEBOARD = 'row above board' # an arbitrary, noninteger value
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, GEMIMAGES, GAMESOUNDS, BASICFONT, BOARDRECTS
|
||||
|
||||
# Initial set up.
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
pygame.display.set_caption('Gemgem')
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 36)
|
||||
|
||||
# Load the images
|
||||
GEMIMAGES = []
|
||||
for i in range(1, NUMGEMIMAGES+1):
|
||||
gemImage = pygame.image.load('gem%s.png' % i)
|
||||
if gemImage.get_size() != (GEMIMAGESIZE, GEMIMAGESIZE):
|
||||
gemImage = pygame.transform.smoothscale(gemImage, (GEMIMAGESIZE, GEMIMAGESIZE))
|
||||
GEMIMAGES.append(gemImage)
|
||||
|
||||
# Load the sounds.
|
||||
GAMESOUNDS = {}
|
||||
GAMESOUNDS['bad swap'] = pygame.mixer.Sound('badswap.wav')
|
||||
GAMESOUNDS['match'] = []
|
||||
for i in range(NUMMATCHSOUNDS):
|
||||
GAMESOUNDS['match'].append(pygame.mixer.Sound('match%s.wav' % i))
|
||||
|
||||
# Create pygame.Rect objects for each board space to
|
||||
# do board-coordinate-to-pixel-coordinate conversions.
|
||||
BOARDRECTS = []
|
||||
for x in range(BOARDWIDTH):
|
||||
BOARDRECTS.append([])
|
||||
for y in range(BOARDHEIGHT):
|
||||
r = pygame.Rect((XMARGIN + (x * GEMIMAGESIZE),
|
||||
YMARGIN + (y * GEMIMAGESIZE),
|
||||
GEMIMAGESIZE,
|
||||
GEMIMAGESIZE))
|
||||
BOARDRECTS[x].append(r)
|
||||
|
||||
while True:
|
||||
runGame()
|
||||
|
||||
|
||||
def runGame():
|
||||
# Plays through a single game. When the game is over, this function returns.
|
||||
|
||||
# initalize the board
|
||||
gameBoard = getBlankBoard()
|
||||
score = 0
|
||||
fillBoardAndAnimate(gameBoard, [], score) # Drop the initial gems.
|
||||
|
||||
# initialize variables for the start of a new game
|
||||
firstSelectedGem = None
|
||||
lastMouseDownX = None
|
||||
lastMouseDownY = None
|
||||
gameIsOver = False
|
||||
lastScoreDeduction = time.time()
|
||||
clickContinueTextSurf = None
|
||||
|
||||
while True: # main game loop
|
||||
clickedSpace = None
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == KEYUP and event.key == K_BACKSPACE:
|
||||
return # start a new game
|
||||
|
||||
elif event.type == MOUSEBUTTONUP:
|
||||
if gameIsOver:
|
||||
return # after games ends, click to start a new game
|
||||
|
||||
if event.pos == (lastMouseDownX, lastMouseDownY):
|
||||
# This event is a mouse click, not the end of a mouse drag.
|
||||
clickedSpace = checkForGemClick(event.pos)
|
||||
else:
|
||||
# this is the end of a mouse drag
|
||||
firstSelectedGem = checkForGemClick((lastMouseDownX, lastMouseDownY))
|
||||
clickedSpace = checkForGemClick(event.pos)
|
||||
if not firstSelectedGem or not clickedSpace:
|
||||
# if not part of a valid drag, deselect both
|
||||
firstSelectedGem = None
|
||||
clickedSpace = None
|
||||
elif event.type == MOUSEBUTTONDOWN:
|
||||
# this is the start of a mouse click or mouse drag
|
||||
lastMouseDownX, lastMouseDownY = event.pos
|
||||
|
||||
if clickedSpace and not firstSelectedGem:
|
||||
# This was the first gem clicked on.
|
||||
firstSelectedGem = clickedSpace
|
||||
elif clickedSpace and firstSelectedGem:
|
||||
# Two gems have been clicked on and selected. Swap the gems.
|
||||
firstSwappingGem, secondSwappingGem = getSwappingGems(gameBoard, firstSelectedGem, clickedSpace)
|
||||
if firstSwappingGem == None and secondSwappingGem == None:
|
||||
# If both are None, then the gems were not adjacent
|
||||
firstSelectedGem = None # deselect the first gem
|
||||
continue
|
||||
|
||||
# Show the swap animation on the screen.
|
||||
boardCopy = getBoardCopyMinusGems(gameBoard, (firstSwappingGem, secondSwappingGem))
|
||||
animateMovingGems(boardCopy, [firstSwappingGem, secondSwappingGem], [], score)
|
||||
|
||||
# Swap the gems in the board data structure.
|
||||
gameBoard[firstSwappingGem['x']][firstSwappingGem['y']] = secondSwappingGem['imageNum']
|
||||
gameBoard[secondSwappingGem['x']][secondSwappingGem['y']] = firstSwappingGem['imageNum']
|
||||
|
||||
# See if this is a matching move.
|
||||
matchedGems = findMatchingGems(gameBoard)
|
||||
if matchedGems == []:
|
||||
# Was not a matching move; swap the gems back
|
||||
GAMESOUNDS['bad swap'].play()
|
||||
animateMovingGems(boardCopy, [firstSwappingGem, secondSwappingGem], [], score)
|
||||
gameBoard[firstSwappingGem['x']][firstSwappingGem['y']] = firstSwappingGem['imageNum']
|
||||
gameBoard[secondSwappingGem['x']][secondSwappingGem['y']] = secondSwappingGem['imageNum']
|
||||
else:
|
||||
# This was a matching move.
|
||||
scoreAdd = 0
|
||||
while matchedGems != []:
|
||||
# Remove matched gems, then pull down the board.
|
||||
|
||||
# points is a list of dicts that tells fillBoardAndAnimate()
|
||||
# where on the screen to display text to show how many
|
||||
# points the player got. points is a list because if
|
||||
# the playergets multiple matches, then multiple points text should appear.
|
||||
points = []
|
||||
for gemSet in matchedGems:
|
||||
scoreAdd += (10 + (len(gemSet) - 3) * 10)
|
||||
for gem in gemSet:
|
||||
gameBoard[gem[0]][gem[1]] = EMPTY_SPACE
|
||||
points.append({'points': scoreAdd,
|
||||
'x': gem[0] * GEMIMAGESIZE + XMARGIN,
|
||||
'y': gem[1] * GEMIMAGESIZE + YMARGIN})
|
||||
random.choice(GAMESOUNDS['match']).play()
|
||||
score += scoreAdd
|
||||
|
||||
# Drop the new gems.
|
||||
fillBoardAndAnimate(gameBoard, points, score)
|
||||
|
||||
# Check if there are any new matches.
|
||||
matchedGems = findMatchingGems(gameBoard)
|
||||
firstSelectedGem = None
|
||||
|
||||
if not canMakeMove(gameBoard):
|
||||
gameIsOver = True
|
||||
|
||||
# Draw the board.
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
drawBoard(gameBoard)
|
||||
if firstSelectedGem != None:
|
||||
highlightSpace(firstSelectedGem['x'], firstSelectedGem['y'])
|
||||
if gameIsOver:
|
||||
if clickContinueTextSurf == None:
|
||||
# Only render the text once. In future iterations, just
|
||||
# use the Surface object already in clickContinueTextSurf
|
||||
clickContinueTextSurf = BASICFONT.render('Final Score: %s (Click to continue)' % (score), 1, GAMEOVERCOLOR, GAMEOVERBGCOLOR)
|
||||
clickContinueTextRect = clickContinueTextSurf.get_rect()
|
||||
clickContinueTextRect.center = int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2)
|
||||
DISPLAYSURF.blit(clickContinueTextSurf, clickContinueTextRect)
|
||||
elif score > 0 and time.time() - lastScoreDeduction > DEDUCTSPEED:
|
||||
# score drops over time
|
||||
score -= 1
|
||||
lastScoreDeduction = time.time()
|
||||
drawScore(score)
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def getSwappingGems(board, firstXY, secondXY):
|
||||
# If the gems at the (X, Y) coordinates of the two gems are adjacent,
|
||||
# then their 'direction' keys are set to the appropriate direction
|
||||
# value to be swapped with each other.
|
||||
# Otherwise, (None, None) is returned.
|
||||
firstGem = {'imageNum': board[firstXY['x']][firstXY['y']],
|
||||
'x': firstXY['x'],
|
||||
'y': firstXY['y']}
|
||||
secondGem = {'imageNum': board[secondXY['x']][secondXY['y']],
|
||||
'x': secondXY['x'],
|
||||
'y': secondXY['y']}
|
||||
highlightedGem = None
|
||||
if firstGem['x'] == secondGem['x'] + 1 and firstGem['y'] == secondGem['y']:
|
||||
firstGem['direction'] = LEFT
|
||||
secondGem['direction'] = RIGHT
|
||||
elif firstGem['x'] == secondGem['x'] - 1 and firstGem['y'] == secondGem['y']:
|
||||
firstGem['direction'] = RIGHT
|
||||
secondGem['direction'] = LEFT
|
||||
elif firstGem['y'] == secondGem['y'] + 1 and firstGem['x'] == secondGem['x']:
|
||||
firstGem['direction'] = UP
|
||||
secondGem['direction'] = DOWN
|
||||
elif firstGem['y'] == secondGem['y'] - 1 and firstGem['x'] == secondGem['x']:
|
||||
firstGem['direction'] = DOWN
|
||||
secondGem['direction'] = UP
|
||||
else:
|
||||
# These gems are not adjacent and can't be swapped.
|
||||
return None, None
|
||||
return firstGem, secondGem
|
||||
|
||||
|
||||
def getBlankBoard():
|
||||
# Create and return a blank board data structure.
|
||||
board = []
|
||||
for x in range(BOARDWIDTH):
|
||||
board.append([EMPTY_SPACE] * BOARDHEIGHT)
|
||||
return board
|
||||
|
||||
|
||||
def canMakeMove(board):
|
||||
# Return True if the board is in a state where a matching
|
||||
# move can be made on it. Otherwise return False.
|
||||
|
||||
# The patterns in oneOffPatterns represent gems that are configured
|
||||
# in a way where it only takes one move to make a triplet.
|
||||
oneOffPatterns = (((0,1), (1,0), (2,0)),
|
||||
((0,1), (1,1), (2,0)),
|
||||
((0,0), (1,1), (2,0)),
|
||||
((0,1), (1,0), (2,1)),
|
||||
((0,0), (1,0), (2,1)),
|
||||
((0,0), (1,1), (2,1)),
|
||||
((0,0), (0,2), (0,3)),
|
||||
((0,0), (0,1), (0,3)))
|
||||
|
||||
# The x and y variables iterate over each space on the board.
|
||||
# If we use + to represent the currently iterated space on the
|
||||
# board, then this pattern: ((0,1), (1,0), (2,0))refers to identical
|
||||
# gems being set up like this:
|
||||
#
|
||||
# +A
|
||||
# B
|
||||
# C
|
||||
#
|
||||
# That is, gem A is offset from the + by (0,1), gem B is offset
|
||||
# by (1,0), and gem C is offset by (2,0). In this case, gem A can
|
||||
# be swapped to the left to form a vertical three-in-a-row triplet.
|
||||
#
|
||||
# There are eight possible ways for the gems to be one move
|
||||
# away from forming a triple, hence oneOffPattern has 8 patterns.
|
||||
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
for pat in oneOffPatterns:
|
||||
# check each possible pattern of "match in next move" to
|
||||
# see if a possible move can be made.
|
||||
if (getGemAt(board, x+pat[0][0], y+pat[0][1]) == \
|
||||
getGemAt(board, x+pat[1][0], y+pat[1][1]) == \
|
||||
getGemAt(board, x+pat[2][0], y+pat[2][1]) != None) or \
|
||||
(getGemAt(board, x+pat[0][1], y+pat[0][0]) == \
|
||||
getGemAt(board, x+pat[1][1], y+pat[1][0]) == \
|
||||
getGemAt(board, x+pat[2][1], y+pat[2][0]) != None):
|
||||
return True # return True the first time you find a pattern
|
||||
return False
|
||||
|
||||
|
||||
def drawMovingGem(gem, progress):
|
||||
# Draw a gem sliding in the direction that its 'direction' key
|
||||
# indicates. The progress parameter is a number from 0 (just
|
||||
# starting) to 100 (slide complete).
|
||||
movex = 0
|
||||
movey = 0
|
||||
progress *= 0.01
|
||||
|
||||
if gem['direction'] == UP:
|
||||
movey = -int(progress * GEMIMAGESIZE)
|
||||
elif gem['direction'] == DOWN:
|
||||
movey = int(progress * GEMIMAGESIZE)
|
||||
elif gem['direction'] == RIGHT:
|
||||
movex = int(progress * GEMIMAGESIZE)
|
||||
elif gem['direction'] == LEFT:
|
||||
movex = -int(progress * GEMIMAGESIZE)
|
||||
|
||||
basex = gem['x']
|
||||
basey = gem['y']
|
||||
if basey == ROWABOVEBOARD:
|
||||
basey = -1
|
||||
|
||||
pixelx = XMARGIN + (basex * GEMIMAGESIZE)
|
||||
pixely = YMARGIN + (basey * GEMIMAGESIZE)
|
||||
r = pygame.Rect( (pixelx + movex, pixely + movey, GEMIMAGESIZE, GEMIMAGESIZE) )
|
||||
DISPLAYSURF.blit(GEMIMAGES[gem['imageNum']], r)
|
||||
|
||||
|
||||
def pullDownAllGems(board):
|
||||
# pulls down gems on the board to the bottom to fill in any gaps
|
||||
for x in range(BOARDWIDTH):
|
||||
gemsInColumn = []
|
||||
for y in range(BOARDHEIGHT):
|
||||
if board[x][y] != EMPTY_SPACE:
|
||||
gemsInColumn.append(board[x][y])
|
||||
board[x] = ([EMPTY_SPACE] * (BOARDHEIGHT - len(gemsInColumn))) + gemsInColumn
|
||||
|
||||
|
||||
def getGemAt(board, x, y):
|
||||
if x < 0 or y < 0 or x >= BOARDWIDTH or y >= BOARDHEIGHT:
|
||||
return None
|
||||
else:
|
||||
return board[x][y]
|
||||
|
||||
|
||||
def getDropSlots(board):
|
||||
# Creates a "drop slot" for each column and fills the slot with a
|
||||
# number of gems that that column is lacking. This function assumes
|
||||
# that the gems have been gravity dropped already.
|
||||
boardCopy = copy.deepcopy(board)
|
||||
pullDownAllGems(boardCopy)
|
||||
|
||||
dropSlots = []
|
||||
for i in range(BOARDWIDTH):
|
||||
dropSlots.append([])
|
||||
|
||||
# count the number of empty spaces in each column on the board
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT-1, -1, -1): # start from bottom, going up
|
||||
if boardCopy[x][y] == EMPTY_SPACE:
|
||||
possibleGems = list(range(len(GEMIMAGES)))
|
||||
for offsetX, offsetY in ((0, -1), (1, 0), (0, 1), (-1, 0)):
|
||||
# Narrow down the possible gems we should put in the
|
||||
# blank space so we don't end up putting an two of
|
||||
# the same gems next to each other when they drop.
|
||||
neighborGem = getGemAt(boardCopy, x + offsetX, y + offsetY)
|
||||
if neighborGem != None and neighborGem in possibleGems:
|
||||
possibleGems.remove(neighborGem)
|
||||
|
||||
newGem = random.choice(possibleGems)
|
||||
boardCopy[x][y] = newGem
|
||||
dropSlots[x].append(newGem)
|
||||
return dropSlots
|
||||
|
||||
|
||||
def findMatchingGems(board):
|
||||
gemsToRemove = [] # a list of lists of gems in matching triplets that should be removed
|
||||
boardCopy = copy.deepcopy(board)
|
||||
|
||||
# loop through each space, checking for 3 adjacent identical gems
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
# look for horizontal matches
|
||||
if getGemAt(boardCopy, x, y) == getGemAt(boardCopy, x + 1, y) == getGemAt(boardCopy, x + 2, y) and getGemAt(boardCopy, x, y) != EMPTY_SPACE:
|
||||
targetGem = boardCopy[x][y]
|
||||
offset = 0
|
||||
removeSet = []
|
||||
while getGemAt(boardCopy, x + offset, y) == targetGem:
|
||||
# keep checking if there's more than 3 gems in a row
|
||||
removeSet.append((x + offset, y))
|
||||
boardCopy[x + offset][y] = EMPTY_SPACE
|
||||
offset += 1
|
||||
gemsToRemove.append(removeSet)
|
||||
|
||||
# look for vertical matches
|
||||
if getGemAt(boardCopy, x, y) == getGemAt(boardCopy, x, y + 1) == getGemAt(boardCopy, x, y + 2) and getGemAt(boardCopy, x, y) != EMPTY_SPACE:
|
||||
targetGem = boardCopy[x][y]
|
||||
offset = 0
|
||||
removeSet = []
|
||||
while getGemAt(boardCopy, x, y + offset) == targetGem:
|
||||
# keep checking, in case there's more than 3 gems in a row
|
||||
removeSet.append((x, y + offset))
|
||||
boardCopy[x][y + offset] = EMPTY_SPACE
|
||||
offset += 1
|
||||
gemsToRemove.append(removeSet)
|
||||
|
||||
return gemsToRemove
|
||||
|
||||
|
||||
def highlightSpace(x, y):
|
||||
pygame.draw.rect(DISPLAYSURF, HIGHLIGHTCOLOR, BOARDRECTS[x][y], 4)
|
||||
|
||||
|
||||
def getDroppingGems(board):
|
||||
# Find all the gems that have an empty space below them
|
||||
boardCopy = copy.deepcopy(board)
|
||||
droppingGems = []
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT - 2, -1, -1):
|
||||
if boardCopy[x][y + 1] == EMPTY_SPACE and boardCopy[x][y] != EMPTY_SPACE:
|
||||
# This space drops if not empty but the space below it is
|
||||
droppingGems.append( {'imageNum': boardCopy[x][y], 'x': x, 'y': y, 'direction': DOWN} )
|
||||
boardCopy[x][y] = EMPTY_SPACE
|
||||
return droppingGems
|
||||
|
||||
|
||||
def animateMovingGems(board, gems, pointsText, score):
|
||||
# pointsText is a dictionary with keys 'x', 'y', and 'points'
|
||||
progress = 0 # progress at 0 represents beginning, 100 means finished.
|
||||
while progress < 100: # animation loop
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
drawBoard(board)
|
||||
for gem in gems: # Draw each gem.
|
||||
drawMovingGem(gem, progress)
|
||||
drawScore(score)
|
||||
for pointText in pointsText:
|
||||
pointsSurf = BASICFONT.render(str(pointText['points']), 1, SCORECOLOR)
|
||||
pointsRect = pointsSurf.get_rect()
|
||||
pointsRect.center = (pointText['x'], pointText['y'])
|
||||
DISPLAYSURF.blit(pointsSurf, pointsRect)
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
progress += MOVERATE # progress the animation a little bit more for the next frame
|
||||
|
||||
|
||||
def moveGems(board, movingGems):
|
||||
# movingGems is a list of dicts with keys x, y, direction, imageNum
|
||||
for gem in movingGems:
|
||||
if gem['y'] != ROWABOVEBOARD:
|
||||
board[gem['x']][gem['y']] = EMPTY_SPACE
|
||||
movex = 0
|
||||
movey = 0
|
||||
if gem['direction'] == LEFT:
|
||||
movex = -1
|
||||
elif gem['direction'] == RIGHT:
|
||||
movex = 1
|
||||
elif gem['direction'] == DOWN:
|
||||
movey = 1
|
||||
elif gem['direction'] == UP:
|
||||
movey = -1
|
||||
board[gem['x'] + movex][gem['y'] + movey] = gem['imageNum']
|
||||
else:
|
||||
# gem is located above the board (where new gems come from)
|
||||
board[gem['x']][0] = gem['imageNum'] # move to top row
|
||||
|
||||
|
||||
def fillBoardAndAnimate(board, points, score):
|
||||
dropSlots = getDropSlots(board)
|
||||
while dropSlots != [[]] * BOARDWIDTH:
|
||||
# do the dropping animation as long as there are more gems to drop
|
||||
movingGems = getDroppingGems(board)
|
||||
for x in range(len(dropSlots)):
|
||||
if len(dropSlots[x]) != 0:
|
||||
# cause the lowest gem in each slot to begin moving in the DOWN direction
|
||||
movingGems.append({'imageNum': dropSlots[x][0], 'x': x, 'y': ROWABOVEBOARD, 'direction': DOWN})
|
||||
|
||||
boardCopy = getBoardCopyMinusGems(board, movingGems)
|
||||
animateMovingGems(boardCopy, movingGems, points, score)
|
||||
moveGems(board, movingGems)
|
||||
|
||||
# Make the next row of gems from the drop slots
|
||||
# the lowest by deleting the previous lowest gems.
|
||||
for x in range(len(dropSlots)):
|
||||
if len(dropSlots[x]) == 0:
|
||||
continue
|
||||
board[x][0] = dropSlots[x][0]
|
||||
del dropSlots[x][0]
|
||||
|
||||
|
||||
def checkForGemClick(pos):
|
||||
# See if the mouse click was on the board
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if BOARDRECTS[x][y].collidepoint(pos[0], pos[1]):
|
||||
return {'x': x, 'y': y}
|
||||
return None # Click was not on the board.
|
||||
|
||||
|
||||
def drawBoard(board):
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
pygame.draw.rect(DISPLAYSURF, GRIDCOLOR, BOARDRECTS[x][y], 1)
|
||||
gemToDraw = board[x][y]
|
||||
if gemToDraw != EMPTY_SPACE:
|
||||
DISPLAYSURF.blit(GEMIMAGES[gemToDraw], BOARDRECTS[x][y])
|
||||
|
||||
|
||||
def getBoardCopyMinusGems(board, gems):
|
||||
# Creates and returns a copy of the passed board data structure,
|
||||
# with the gems in the "gems" list removed from it.
|
||||
#
|
||||
# Gems is a list of dicts, with keys x, y, direction, imageNum
|
||||
|
||||
boardCopy = copy.deepcopy(board)
|
||||
|
||||
# Remove some of the gems from this board data structure copy.
|
||||
for gem in gems:
|
||||
if gem['y'] != ROWABOVEBOARD:
|
||||
boardCopy[gem['x']][gem['y']] = EMPTY_SPACE
|
||||
return boardCopy
|
||||
|
||||
|
||||
def drawScore(score):
|
||||
scoreImg = BASICFONT.render(str(score), 1, SCORECOLOR)
|
||||
scoreRect = scoreImg.get_rect()
|
||||
scoreRect.bottomleft = (10, WINDOWHEIGHT - 6)
|
||||
DISPLAYSURF.blit(scoreImg, scoreRect)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
python_games/grass1.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
python_games/grass2.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
python_games/grass3.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
python_games/grass4.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
python_games/horngirl.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
445
python_games/inkspill.py
Normal file
@ -0,0 +1,445 @@
|
||||
# Ink Spill (a Flood It clone)
|
||||
# http://inventwithpython.com/pygame
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import random, sys, webbrowser, copy, pygame
|
||||
from pygame.locals import *
|
||||
|
||||
# There are different box sizes, number of boxes, and
|
||||
# life depending on the "board size" setting selected.
|
||||
SMALLBOXSIZE = 60 # size is in pixels
|
||||
MEDIUMBOXSIZE = 20
|
||||
LARGEBOXSIZE = 11
|
||||
|
||||
SMALLBOARDSIZE = 6 # size is in boxes
|
||||
MEDIUMBOARDSIZE = 17
|
||||
LARGEBOARDSIZE = 30
|
||||
|
||||
SMALLMAXLIFE = 10 # number of turns
|
||||
MEDIUMMAXLIFE = 30
|
||||
LARGEMAXLIFE = 64
|
||||
|
||||
FPS = 30
|
||||
WINDOWWIDTH = 640
|
||||
WINDOWHEIGHT = 480
|
||||
boxSize = MEDIUMBOXSIZE
|
||||
PALETTEGAPSIZE = 10
|
||||
PALETTESIZE = 45
|
||||
EASY = 0 # arbitrary but unique value
|
||||
MEDIUM = 1 # arbitrary but unique value
|
||||
HARD = 2 # arbitrary but unique value
|
||||
|
||||
difficulty = MEDIUM # game starts in "medium" mode
|
||||
maxLife = MEDIUMMAXLIFE
|
||||
boardWidth = MEDIUMBOARDSIZE
|
||||
boardHeight = MEDIUMBOARDSIZE
|
||||
|
||||
|
||||
# R G B
|
||||
WHITE = (255, 255, 255)
|
||||
DARKGRAY = ( 70, 70, 70)
|
||||
BLACK = ( 0, 0, 0)
|
||||
RED = (255, 0, 0)
|
||||
GREEN = ( 0, 255, 0)
|
||||
BLUE = ( 0, 0, 255)
|
||||
YELLOW = (255, 255, 0)
|
||||
ORANGE = (255, 128, 0)
|
||||
PURPLE = (255, 0, 255)
|
||||
|
||||
# The first color in each scheme is the background color, the next six are the palette colors.
|
||||
COLORSCHEMES = (((150, 200, 255), RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE),
|
||||
((0, 155, 104), (97, 215, 164), (228, 0, 69), (0, 125, 50), (204, 246, 0), (148, 0, 45), (241, 109, 149)),
|
||||
((195, 179, 0), (255, 239, 115), (255, 226, 0), (147, 3, 167), (24, 38, 176), (166, 147, 0), (197, 97, 211)),
|
||||
((85, 0, 0), (155, 39, 102), (0, 201, 13), (255, 118, 0), (206, 0, 113), (0, 130, 9), (255, 180, 115)),
|
||||
((191, 159, 64), (183, 182, 208), (4, 31, 183), (167, 184, 45), (122, 128, 212), (37, 204, 7), (88, 155, 213)),
|
||||
((200, 33, 205), (116, 252, 185), (68, 56, 56), (52, 238, 83), (23, 149, 195), (222, 157, 227), (212, 86, 185)))
|
||||
for i in range(len(COLORSCHEMES)):
|
||||
assert len(COLORSCHEMES[i]) == 7, 'Color scheme %s does not have exactly 7 colors.' % (i)
|
||||
bgColor = COLORSCHEMES[0][0]
|
||||
paletteColors = COLORSCHEMES[0][1:]
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, LOGOIMAGE, SPOTIMAGE, SETTINGSIMAGE, SETTINGSBUTTONIMAGE, RESETBUTTONIMAGE
|
||||
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
|
||||
# Load images
|
||||
LOGOIMAGE = pygame.image.load('inkspilllogo.png')
|
||||
SPOTIMAGE = pygame.image.load('inkspillspot.png')
|
||||
SETTINGSIMAGE = pygame.image.load('inkspillsettings.png')
|
||||
SETTINGSBUTTONIMAGE = pygame.image.load('inkspillsettingsbutton.png')
|
||||
RESETBUTTONIMAGE = pygame.image.load('inkspillresetbutton.png')
|
||||
|
||||
pygame.display.set_caption('Ink Spill')
|
||||
mousex = 0
|
||||
mousey = 0
|
||||
mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
|
||||
life = maxLife
|
||||
lastPaletteClicked = None
|
||||
|
||||
while True: # main game loop
|
||||
paletteClicked = None
|
||||
resetGame = False
|
||||
|
||||
# Draw the screen.
|
||||
DISPLAYSURF.fill(bgColor)
|
||||
drawLogoAndButtons()
|
||||
drawBoard(mainBoard)
|
||||
drawLifeMeter(life)
|
||||
drawPalettes()
|
||||
|
||||
checkForQuit()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == MOUSEBUTTONUP:
|
||||
mousex, mousey = event.pos
|
||||
if pygame.Rect(WINDOWWIDTH - SETTINGSBUTTONIMAGE.get_width(),
|
||||
WINDOWHEIGHT - SETTINGSBUTTONIMAGE.get_height(),
|
||||
SETTINGSBUTTONIMAGE.get_width(),
|
||||
SETTINGSBUTTONIMAGE.get_height()).collidepoint(mousex, mousey):
|
||||
resetGame = showSettingsScreen() # clicked on Settings button
|
||||
elif pygame.Rect(WINDOWWIDTH - RESETBUTTONIMAGE.get_width(),
|
||||
WINDOWHEIGHT - SETTINGSBUTTONIMAGE.get_height() - RESETBUTTONIMAGE.get_height(),
|
||||
RESETBUTTONIMAGE.get_width(),
|
||||
RESETBUTTONIMAGE.get_height()).collidepoint(mousex, mousey):
|
||||
resetGame = True # clicked on Reset button
|
||||
else:
|
||||
# check if a palette button was clicked
|
||||
paletteClicked = getColorOfPaletteAt(mousex, mousey)
|
||||
elif event.type == KEYDOWN:
|
||||
# support up to 9 palette keys
|
||||
try:
|
||||
key = int(event.unicode)
|
||||
except:
|
||||
key = None
|
||||
|
||||
if key != None and key > 0 and key <= len(paletteColors):
|
||||
paletteClicked = key - 1
|
||||
|
||||
if paletteClicked != None and paletteClicked != lastPaletteClicked:
|
||||
# a palette button was clicked that is different from the
|
||||
# last palette button clicked (this check prevents the player
|
||||
# from accidentally clicking the same palette twice)
|
||||
lastPaletteClicked = paletteClicked
|
||||
floodAnimation(mainBoard, paletteClicked)
|
||||
life -= 1
|
||||
|
||||
resetGame = False
|
||||
if hasWon(mainBoard):
|
||||
for i in range(4): # flash border 4 times
|
||||
flashBorderAnimation(WHITE, mainBoard)
|
||||
resetGame = True
|
||||
pygame.time.wait(2000) # pause so the player can bask in victory
|
||||
elif life == 0:
|
||||
# life is zero, so player has lost
|
||||
drawLifeMeter(0)
|
||||
pygame.display.update()
|
||||
pygame.time.wait(400)
|
||||
for i in range(4):
|
||||
flashBorderAnimation(BLACK, mainBoard)
|
||||
resetGame = True
|
||||
pygame.time.wait(2000) # pause so the player can suffer in their defeat
|
||||
|
||||
if resetGame:
|
||||
# start a new game
|
||||
mainBoard = generateRandomBoard(boardWidth, boardHeight, difficulty)
|
||||
life = maxLife
|
||||
lastPaletteClicked = None
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def checkForQuit():
|
||||
# Terminates the program if there are any QUIT or escape key events.
|
||||
for event in pygame.event.get(QUIT): # get all the QUIT events
|
||||
pygame.quit() # terminate if any QUIT events are present
|
||||
sys.exit()
|
||||
for event in pygame.event.get(KEYUP): # get all the KEYUP events
|
||||
if event.key == K_ESCAPE:
|
||||
pygame.quit() # terminate if the KEYUP event was for the Esc key
|
||||
sys.exit()
|
||||
pygame.event.post(event) # put the other KEYUP event objects back
|
||||
|
||||
|
||||
def hasWon(board):
|
||||
# if the entire board is the same color, player has won
|
||||
for x in range(boardWidth):
|
||||
for y in range(boardHeight):
|
||||
if board[x][y] != board[0][0]:
|
||||
return False # found a different color, player has not won
|
||||
return True
|
||||
|
||||
|
||||
def showSettingsScreen():
|
||||
global difficulty, boxSize, boardWidth, boardHeight, maxLife, paletteColors, bgColor
|
||||
|
||||
# The pixel coordinates in this function were obtained by loading
|
||||
# the inkspillsettings.png image into a graphics editor and reading
|
||||
# the pixel coordinates from there. Handy trick.
|
||||
|
||||
origDifficulty = difficulty
|
||||
origBoxSize = boxSize
|
||||
screenNeedsRedraw = True
|
||||
|
||||
while True:
|
||||
if screenNeedsRedraw:
|
||||
DISPLAYSURF.fill(bgColor)
|
||||
DISPLAYSURF.blit(SETTINGSIMAGE, (0,0))
|
||||
|
||||
# place the ink spot marker next to the selected difficulty
|
||||
if difficulty == EASY:
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (30, 4))
|
||||
if difficulty == MEDIUM:
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (8, 41))
|
||||
if difficulty == HARD:
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (30, 76))
|
||||
|
||||
# place the ink spot marker next to the selected size
|
||||
if boxSize == SMALLBOXSIZE:
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (22, 150))
|
||||
if boxSize == MEDIUMBOXSIZE:
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (11, 185))
|
||||
if boxSize == LARGEBOXSIZE:
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (24, 220))
|
||||
|
||||
for i in range(len(COLORSCHEMES)):
|
||||
drawColorSchemeBoxes(500, i * 60 + 30, i)
|
||||
|
||||
pygame.display.update()
|
||||
|
||||
screenNeedsRedraw = False # by default, don't redraw the screen
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == KEYUP:
|
||||
if event.key == K_ESCAPE:
|
||||
# Esc key on settings screen goes back to game
|
||||
return not (origDifficulty == difficulty and origBoxSize == boxSize)
|
||||
elif event.type == MOUSEBUTTONUP:
|
||||
screenNeedsRedraw = True # screen should be redrawn
|
||||
mousex, mousey = event.pos # syntactic sugar
|
||||
|
||||
# check for clicks on the difficulty buttons
|
||||
if pygame.Rect(74, 16, 111, 30).collidepoint(mousex, mousey):
|
||||
difficulty = EASY
|
||||
elif pygame.Rect(53, 50, 104, 29).collidepoint(mousex, mousey):
|
||||
difficulty = MEDIUM
|
||||
elif pygame.Rect(72, 85, 65, 31).collidepoint(mousex, mousey):
|
||||
difficulty = HARD
|
||||
|
||||
# check for clicks on the size buttons
|
||||
elif pygame.Rect(63, 156, 84, 31).collidepoint(mousex, mousey):
|
||||
# small board size setting:
|
||||
boxSize = SMALLBOXSIZE
|
||||
boardWidth = SMALLBOARDSIZE
|
||||
boardHeight = SMALLBOARDSIZE
|
||||
maxLife = SMALLMAXLIFE
|
||||
elif pygame.Rect(52, 192, 106,32).collidepoint(mousex, mousey):
|
||||
# medium board size setting:
|
||||
boxSize = MEDIUMBOXSIZE
|
||||
boardWidth = MEDIUMBOARDSIZE
|
||||
boardHeight = MEDIUMBOARDSIZE
|
||||
maxLife = MEDIUMMAXLIFE
|
||||
elif pygame.Rect(67, 228, 58, 37).collidepoint(mousex, mousey):
|
||||
# large board size setting:
|
||||
boxSize = LARGEBOXSIZE
|
||||
boardWidth = LARGEBOARDSIZE
|
||||
boardHeight = LARGEBOARDSIZE
|
||||
maxLife = LARGEMAXLIFE
|
||||
elif pygame.Rect(14, 299, 371, 97).collidepoint(mousex, mousey):
|
||||
# clicked on the "learn programming" ad
|
||||
webbrowser.open('http://inventwithpython.com') # opens a web browser
|
||||
elif pygame.Rect(178, 418, 215, 34).collidepoint(mousex, mousey):
|
||||
# clicked on the "back to game" button
|
||||
return not (origDifficulty == difficulty and origBoxSize == boxSize)
|
||||
|
||||
for i in range(len(COLORSCHEMES)):
|
||||
# clicked on a color scheme button
|
||||
if pygame.Rect(500, 30 + i * 60, MEDIUMBOXSIZE * 3, MEDIUMBOXSIZE * 2).collidepoint(mousex, mousey):
|
||||
bgColor = COLORSCHEMES[i][0]
|
||||
paletteColors = COLORSCHEMES[i][1:]
|
||||
|
||||
|
||||
def drawColorSchemeBoxes(x, y, schemeNum):
|
||||
# Draws the color scheme boxes that appear on the "Settings" screen.
|
||||
for boxy in range(2):
|
||||
for boxx in range(3):
|
||||
pygame.draw.rect(DISPLAYSURF, COLORSCHEMES[schemeNum][3 * boxy + boxx + 1], (x + MEDIUMBOXSIZE * boxx, y + MEDIUMBOXSIZE * boxy, MEDIUMBOXSIZE, MEDIUMBOXSIZE))
|
||||
if paletteColors == COLORSCHEMES[schemeNum][1:]:
|
||||
# put the ink spot next to the selected color scheme
|
||||
DISPLAYSURF.blit(SPOTIMAGE, (x - 50, y))
|
||||
|
||||
|
||||
def flashBorderAnimation(color, board, animationSpeed=30):
|
||||
origSurf = DISPLAYSURF.copy()
|
||||
flashSurf = pygame.Surface(DISPLAYSURF.get_size())
|
||||
flashSurf = flashSurf.convert_alpha()
|
||||
for start, end, step in ((0, 256, 1), (255, 0, -1)):
|
||||
# the first iteration on the outer loop will set the inner loop
|
||||
# to have transparency go from 0 to 255, the second iteration will
|
||||
# have it go from 255 to 0. This is the "flash".
|
||||
for transparency in range(start, end, animationSpeed * step):
|
||||
DISPLAYSURF.blit(origSurf, (0, 0))
|
||||
r, g, b = color
|
||||
flashSurf.fill((r, g, b, transparency))
|
||||
DISPLAYSURF.blit(flashSurf, (0, 0))
|
||||
drawBoard(board) # draw board ON TOP OF the transparency layer
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
DISPLAYSURF.blit(origSurf, (0, 0)) # redraw the original surface
|
||||
|
||||
|
||||
def floodAnimation(board, paletteClicked, animationSpeed=25):
|
||||
origBoard = copy.deepcopy(board)
|
||||
floodFill(board, board[0][0], paletteClicked, 0, 0)
|
||||
|
||||
for transparency in range(0, 255, animationSpeed):
|
||||
# The "new" board slowly become opaque over the original board.
|
||||
drawBoard(origBoard)
|
||||
drawBoard(board, transparency)
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def generateRandomBoard(width, height, difficulty=MEDIUM):
|
||||
# Creates a board data structure with random colors for each box.
|
||||
board = []
|
||||
for x in range(width):
|
||||
column = []
|
||||
for y in range(height):
|
||||
column.append(random.randint(0, len(paletteColors) - 1))
|
||||
board.append(column)
|
||||
|
||||
# Make board easier by setting some boxes to same color as a neighbor.
|
||||
|
||||
# Determine how many boxes to change.
|
||||
if difficulty == EASY:
|
||||
if boxSize == SMALLBOXSIZE:
|
||||
boxesToChange = 100
|
||||
else:
|
||||
boxesToChange = 1500
|
||||
elif difficulty == MEDIUM:
|
||||
if boxSize == SMALLBOXSIZE:
|
||||
boxesToChange = 5
|
||||
else:
|
||||
boxesToChange = 200
|
||||
else:
|
||||
boxesToChange = 0
|
||||
|
||||
# Change neighbor's colors:
|
||||
for i in range(boxesToChange):
|
||||
# Randomly choose a box whose color to copy
|
||||
x = random.randint(1, width-2)
|
||||
y = random.randint(1, height-2)
|
||||
|
||||
# Randomly choose neighbors to change.
|
||||
direction = random.randint(0, 3)
|
||||
if direction == 0: # change left and up neighbor
|
||||
board[x-1][y] == board[x][y]
|
||||
board[x][y-1] == board[x][y]
|
||||
elif direction == 1: # change right and down neighbor
|
||||
board[x+1][y] == board[x][y]
|
||||
board[x][y+1] == board[x][y]
|
||||
elif direction == 2: # change right and up neighbor
|
||||
board[x][y-1] == board[x][y]
|
||||
board[x+1][y] == board[x][y]
|
||||
else: # change left and down neighbor
|
||||
board[x][y+1] == board[x][y]
|
||||
board[x-1][y] == board[x][y]
|
||||
return board
|
||||
|
||||
|
||||
def drawLogoAndButtons():
|
||||
# draw the Ink Spill logo and Settings and Reset buttons.
|
||||
DISPLAYSURF.blit(LOGOIMAGE, (WINDOWWIDTH - LOGOIMAGE.get_width(), 0))
|
||||
DISPLAYSURF.blit(SETTINGSBUTTONIMAGE, (WINDOWWIDTH - SETTINGSBUTTONIMAGE.get_width(), WINDOWHEIGHT - SETTINGSBUTTONIMAGE.get_height()))
|
||||
DISPLAYSURF.blit(RESETBUTTONIMAGE, (WINDOWWIDTH - RESETBUTTONIMAGE.get_width(), WINDOWHEIGHT - SETTINGSBUTTONIMAGE.get_height() - RESETBUTTONIMAGE.get_height()))
|
||||
|
||||
|
||||
def drawBoard(board, transparency=255):
|
||||
# The colored squares are drawn to a temporary surface which is then
|
||||
# drawn to the DISPLAYSURF surface. This is done so we can draw the
|
||||
# squares with transparency on top of DISPLAYSURF as it currently is.
|
||||
tempSurf = pygame.Surface(DISPLAYSURF.get_size())
|
||||
tempSurf = tempSurf.convert_alpha()
|
||||
tempSurf.fill((0, 0, 0, 0))
|
||||
|
||||
for x in range(boardWidth):
|
||||
for y in range(boardHeight):
|
||||
left, top = leftTopPixelCoordOfBox(x, y)
|
||||
r, g, b = paletteColors[board[x][y]]
|
||||
pygame.draw.rect(tempSurf, (r, g, b, transparency), (left, top, boxSize, boxSize))
|
||||
left, top = leftTopPixelCoordOfBox(0, 0)
|
||||
pygame.draw.rect(tempSurf, BLACK, (left-1, top-1, boxSize * boardWidth + 1, boxSize * boardHeight + 1), 1)
|
||||
DISPLAYSURF.blit(tempSurf, (0, 0))
|
||||
|
||||
|
||||
def drawPalettes():
|
||||
# Draws the six color palettes at the bottom of the screen.
|
||||
numColors = len(paletteColors)
|
||||
xmargin = int((WINDOWWIDTH - ((PALETTESIZE * numColors) + (PALETTEGAPSIZE * (numColors - 1)))) / 2)
|
||||
for i in range(numColors):
|
||||
left = xmargin + (i * PALETTESIZE) + (i * PALETTEGAPSIZE)
|
||||
top = WINDOWHEIGHT - PALETTESIZE - 10
|
||||
pygame.draw.rect(DISPLAYSURF, paletteColors[i], (left, top, PALETTESIZE, PALETTESIZE))
|
||||
pygame.draw.rect(DISPLAYSURF, bgColor, (left + 2, top + 2, PALETTESIZE - 4, PALETTESIZE - 4), 2)
|
||||
|
||||
|
||||
def drawLifeMeter(currentLife):
|
||||
lifeBoxSize = int((WINDOWHEIGHT - 40) / maxLife)
|
||||
|
||||
# Draw background color of life meter.
|
||||
pygame.draw.rect(DISPLAYSURF, bgColor, (20, 20, 20, 20 + (maxLife * lifeBoxSize)))
|
||||
|
||||
for i in range(maxLife):
|
||||
if currentLife >= (maxLife - i): # draw a solid red box
|
||||
pygame.draw.rect(DISPLAYSURF, RED, (20, 20 + (i * lifeBoxSize), 20, lifeBoxSize))
|
||||
pygame.draw.rect(DISPLAYSURF, WHITE, (20, 20 + (i * lifeBoxSize), 20, lifeBoxSize), 1) # draw white outline
|
||||
|
||||
|
||||
def getColorOfPaletteAt(x, y):
|
||||
# Returns the index of the color in paletteColors that the x and y parameters
|
||||
# are over. Returns None if x and y are not over any palette.
|
||||
numColors = len(paletteColors)
|
||||
xmargin = int((WINDOWWIDTH - ((PALETTESIZE * numColors) + (PALETTEGAPSIZE * (numColors - 1)))) / 2)
|
||||
top = WINDOWHEIGHT - PALETTESIZE - 10
|
||||
for i in range(numColors):
|
||||
# Find out if the mouse click is inside any of the palettes.
|
||||
left = xmargin + (i * PALETTESIZE) + (i * PALETTEGAPSIZE)
|
||||
r = pygame.Rect(left, top, PALETTESIZE, PALETTESIZE)
|
||||
if r.collidepoint(x, y):
|
||||
return i
|
||||
return None # no palette exists at these x, y coordinates
|
||||
|
||||
|
||||
def floodFill(board, oldColor, newColor, x, y):
|
||||
# This is the flood fill algorithm.
|
||||
if oldColor == newColor or board[x][y] != oldColor:
|
||||
return
|
||||
|
||||
board[x][y] = newColor # change the color of the current box
|
||||
|
||||
# Make the recursive call for any neighboring boxes:
|
||||
if x > 0:
|
||||
floodFill(board, oldColor, newColor, x - 1, y) # on box to the left
|
||||
if x < boardWidth - 1:
|
||||
floodFill(board, oldColor, newColor, x + 1, y) # on box to the right
|
||||
if y > 0:
|
||||
floodFill(board, oldColor, newColor, x, y - 1) # on box to up
|
||||
if y < boardHeight - 1:
|
||||
floodFill(board, oldColor, newColor, x, y + 1) # on box to down
|
||||
|
||||
|
||||
def leftTopPixelCoordOfBox(boxx, boxy):
|
||||
# Returns the x and y of the left-topmost pixel of the xth & yth box.
|
||||
xmargin = int((WINDOWWIDTH - (boardWidth * boxSize)) / 2)
|
||||
ymargin = int((WINDOWHEIGHT - (boardHeight * boxSize)) / 2)
|
||||
return (boxx * boxSize + xmargin, boxy * boxSize + ymargin)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
python_games/inkspilllogo.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
python_games/inkspillresetbutton.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
python_games/inkspillsettings.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
python_games/inkspillsettingsbutton.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
python_games/inkspillspot.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
58
python_games/launcher.sh
Normal file
@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
# Thanks to KenT for contributions
|
||||
|
||||
RET=0
|
||||
SOUND=$(zenity --list --width=350 --height=250 --radiolist \
|
||||
--title="Choose the Audio Output" \
|
||||
--column "Select" --column="Output" TRUE "Leave as is" FALSE "Auto" FALSE "Force Headphones" FALSE "Force HDMI" )
|
||||
RET=$?
|
||||
echo $SOUND
|
||||
if [ "$SOUND" = "Leave as is" ]; then
|
||||
echo "Leave as is"
|
||||
elif [ "$SOUND" = "Auto" ]; then
|
||||
amixer -c 0 cset numid=3 0
|
||||
echo "Auto set"
|
||||
elif [ "$SOUND" = "Force Headphones" ]; then
|
||||
amixer -c 0 cset numid=3 1
|
||||
echo "Headphones set"
|
||||
elif [ "$SOUND" = "Force HDMI" ]; then
|
||||
amixer -c 0 cset numid=3 2
|
||||
echo "HDMI set"
|
||||
else
|
||||
echo "cancel"
|
||||
fi
|
||||
|
||||
while [ $RET -eq 0 ]; do
|
||||
GAME=$(zenity --width=350 --height=700 --list \
|
||||
--title="Choose the Python game to launch" \
|
||||
--text="Games by Al Sweigart. Manual at inventwithpython.com/pygame" \
|
||||
--column="Script name" --column="Description" \
|
||||
flippy "A game like reversi" \
|
||||
fourinarow "Get four in a row" \
|
||||
gemgem "A tile matching puzzle" \
|
||||
inkspill "Flood the screen with pixels" \
|
||||
memorypuzzle "Test your memory" \
|
||||
pentomino "5 block Tetris" \
|
||||
simulate "Repeat the pattern" \
|
||||
slidepuzzle "Traditional slide puzzle" \
|
||||
squirrel "Eat the smaller squirrels" \
|
||||
starpusher "Sokoban" \
|
||||
tetromino "Tetris-like game" \
|
||||
tetrominoforidiots "Tetris for Idiots" \
|
||||
wormy "Snake-like game" \
|
||||
website "inventwithpython.com")
|
||||
RET=$?
|
||||
echo $RET
|
||||
if [ "$RET" -eq 0 ]
|
||||
then
|
||||
if [ "$GAME" = "website" ]
|
||||
then
|
||||
sensible-browser "http://inventwithpython.com/pygame"
|
||||
else
|
||||
if [ "$GAME" != "" ]; then
|
||||
cd /home/pi/python_games
|
||||
python $GAME.py
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
BIN
python_games/match0.wav
Normal file
BIN
python_games/match1.wav
Normal file
BIN
python_games/match2.wav
Normal file
BIN
python_games/match3.wav
Normal file
BIN
python_games/match4.wav
Normal file
BIN
python_games/match5.wav
Normal file
292
python_games/memorypuzzle.py
Normal file
@ -0,0 +1,292 @@
|
||||
# Memory Puzzle
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import random, pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 30 # frames per second, the general speed of the program
|
||||
WINDOWWIDTH = 640 # size of window's width in pixels
|
||||
WINDOWHEIGHT = 480 # size of windows' height in pixels
|
||||
REVEALSPEED = 8 # speed boxes' sliding reveals and covers
|
||||
BOXSIZE = 40 # size of box height & width in pixels
|
||||
GAPSIZE = 10 # size of gap between boxes in pixels
|
||||
BOARDWIDTH = 10 # number of columns of icons
|
||||
BOARDHEIGHT = 7 # number of rows of icons
|
||||
assert (BOARDWIDTH * BOARDHEIGHT) % 2 == 0, 'Board needs to have an even number of boxes for pairs of matches.'
|
||||
XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH * (BOXSIZE + GAPSIZE))) / 2)
|
||||
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE + GAPSIZE))) / 2)
|
||||
|
||||
# R G B
|
||||
GRAY = (100, 100, 100)
|
||||
NAVYBLUE = ( 60, 60, 100)
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (255, 0, 0)
|
||||
GREEN = ( 0, 255, 0)
|
||||
BLUE = ( 0, 0, 255)
|
||||
YELLOW = (255, 255, 0)
|
||||
ORANGE = (255, 128, 0)
|
||||
PURPLE = (255, 0, 255)
|
||||
CYAN = ( 0, 255, 255)
|
||||
|
||||
BGCOLOR = NAVYBLUE
|
||||
LIGHTBGCOLOR = GRAY
|
||||
BOXCOLOR = WHITE
|
||||
HIGHLIGHTCOLOR = BLUE
|
||||
|
||||
DONUT = 'donut'
|
||||
SQUARE = 'square'
|
||||
DIAMOND = 'diamond'
|
||||
LINES = 'lines'
|
||||
OVAL = 'oval'
|
||||
|
||||
ALLCOLORS = (RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, CYAN)
|
||||
ALLSHAPES = (DONUT, SQUARE, DIAMOND, LINES, OVAL)
|
||||
assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDWIDTH * BOARDHEIGHT, "Board is too big for the number of shapes/colors defined."
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
|
||||
mousex = 0 # used to store x coordinate of mouse event
|
||||
mousey = 0 # used to store y coordinate of mouse event
|
||||
pygame.display.set_caption('Memory Game')
|
||||
|
||||
mainBoard = getRandomizedBoard()
|
||||
revealedBoxes = generateRevealedBoxesData(False)
|
||||
|
||||
firstSelection = None # stores the (x, y) of the first box clicked.
|
||||
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
startGameAnimation(mainBoard)
|
||||
|
||||
while True: # main game loop
|
||||
mouseClicked = False
|
||||
|
||||
DISPLAYSURF.fill(BGCOLOR) # drawing the window
|
||||
drawBoard(mainBoard, revealedBoxes)
|
||||
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == MOUSEMOTION:
|
||||
mousex, mousey = event.pos
|
||||
elif event.type == MOUSEBUTTONUP:
|
||||
mousex, mousey = event.pos
|
||||
mouseClicked = True
|
||||
|
||||
boxx, boxy = getBoxAtPixel(mousex, mousey)
|
||||
if boxx != None and boxy != None:
|
||||
# The mouse is currently over a box.
|
||||
if not revealedBoxes[boxx][boxy]:
|
||||
drawHighlightBox(boxx, boxy)
|
||||
if not revealedBoxes[boxx][boxy] and mouseClicked:
|
||||
revealBoxesAnimation(mainBoard, [(boxx, boxy)])
|
||||
revealedBoxes[boxx][boxy] = True # set the box as "revealed"
|
||||
if firstSelection == None: # the current box was the first box clicked
|
||||
firstSelection = (boxx, boxy)
|
||||
else: # the current box was the second box clicked
|
||||
# Check if there is a match between the two icons.
|
||||
icon1shape, icon1color = getShapeAndColor(mainBoard, firstSelection[0], firstSelection[1])
|
||||
icon2shape, icon2color = getShapeAndColor(mainBoard, boxx, boxy)
|
||||
|
||||
if icon1shape != icon2shape or icon1color != icon2color:
|
||||
# Icons don't match. Re-cover up both selections.
|
||||
pygame.time.wait(1000) # 1000 milliseconds = 1 sec
|
||||
coverBoxesAnimation(mainBoard, [(firstSelection[0], firstSelection[1]), (boxx, boxy)])
|
||||
revealedBoxes[firstSelection[0]][firstSelection[1]] = False
|
||||
revealedBoxes[boxx][boxy] = False
|
||||
elif hasWon(revealedBoxes): # check if all pairs found
|
||||
gameWonAnimation(mainBoard)
|
||||
pygame.time.wait(2000)
|
||||
|
||||
# Reset the board
|
||||
mainBoard = getRandomizedBoard()
|
||||
revealedBoxes = generateRevealedBoxesData(False)
|
||||
|
||||
# Show the fully unrevealed board for a second.
|
||||
drawBoard(mainBoard, revealedBoxes)
|
||||
pygame.display.update()
|
||||
pygame.time.wait(1000)
|
||||
|
||||
# Replay the start game animation.
|
||||
startGameAnimation(mainBoard)
|
||||
firstSelection = None # reset firstSelection variable
|
||||
|
||||
# Redraw the screen and wait a clock tick.
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def generateRevealedBoxesData(val):
|
||||
revealedBoxes = []
|
||||
for i in range(BOARDWIDTH):
|
||||
revealedBoxes.append([val] * BOARDHEIGHT)
|
||||
return revealedBoxes
|
||||
|
||||
|
||||
def getRandomizedBoard():
|
||||
# Get a list of every possible shape in every possible color.
|
||||
icons = []
|
||||
for color in ALLCOLORS:
|
||||
for shape in ALLSHAPES:
|
||||
icons.append( (shape, color) )
|
||||
|
||||
random.shuffle(icons) # randomize the order of the icons list
|
||||
numIconsUsed = int(BOARDWIDTH * BOARDHEIGHT / 2) # calculate how many icons are needed
|
||||
icons = icons[:numIconsUsed] * 2 # make two of each
|
||||
random.shuffle(icons)
|
||||
|
||||
# Create the board data structure, with randomly placed icons.
|
||||
board = []
|
||||
for x in range(BOARDWIDTH):
|
||||
column = []
|
||||
for y in range(BOARDHEIGHT):
|
||||
column.append(icons[0])
|
||||
del icons[0] # remove the icons as we assign them
|
||||
board.append(column)
|
||||
return board
|
||||
|
||||
|
||||
def splitIntoGroupsOf(groupSize, theList):
|
||||
# splits a list into a list of lists, where the inner lists have at
|
||||
# most groupSize number of items.
|
||||
result = []
|
||||
for i in range(0, len(theList), groupSize):
|
||||
result.append(theList[i:i + groupSize])
|
||||
return result
|
||||
|
||||
|
||||
def leftTopCoordsOfBox(boxx, boxy):
|
||||
# Convert board coordinates to pixel coordinates
|
||||
left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
|
||||
top = boxy * (BOXSIZE + GAPSIZE) + YMARGIN
|
||||
return (left, top)
|
||||
|
||||
|
||||
def getBoxAtPixel(x, y):
|
||||
for boxx in range(BOARDWIDTH):
|
||||
for boxy in range(BOARDHEIGHT):
|
||||
left, top = leftTopCoordsOfBox(boxx, boxy)
|
||||
boxRect = pygame.Rect(left, top, BOXSIZE, BOXSIZE)
|
||||
if boxRect.collidepoint(x, y):
|
||||
return (boxx, boxy)
|
||||
return (None, None)
|
||||
|
||||
|
||||
def drawIcon(shape, color, boxx, boxy):
|
||||
quarter = int(BOXSIZE * 0.25) # syntactic sugar
|
||||
half = int(BOXSIZE * 0.5) # syntactic sugar
|
||||
|
||||
left, top = leftTopCoordsOfBox(boxx, boxy) # get pixel coords from board coords
|
||||
# Draw the shapes
|
||||
if shape == DONUT:
|
||||
pygame.draw.circle(DISPLAYSURF, color, (left + half, top + half), half - 5)
|
||||
pygame.draw.circle(DISPLAYSURF, BGCOLOR, (left + half, top + half), quarter - 5)
|
||||
elif shape == SQUARE:
|
||||
pygame.draw.rect(DISPLAYSURF, color, (left + quarter, top + quarter, BOXSIZE - half, BOXSIZE - half))
|
||||
elif shape == DIAMOND:
|
||||
pygame.draw.polygon(DISPLAYSURF, color, ((left + half, top), (left + BOXSIZE - 1, top + half), (left + half, top + BOXSIZE - 1), (left, top + half)))
|
||||
elif shape == LINES:
|
||||
for i in range(0, BOXSIZE, 4):
|
||||
pygame.draw.line(DISPLAYSURF, color, (left, top + i), (left + i, top))
|
||||
pygame.draw.line(DISPLAYSURF, color, (left + i, top + BOXSIZE - 1), (left + BOXSIZE - 1, top + i))
|
||||
elif shape == OVAL:
|
||||
pygame.draw.ellipse(DISPLAYSURF, color, (left, top + quarter, BOXSIZE, half))
|
||||
|
||||
|
||||
def getShapeAndColor(board, boxx, boxy):
|
||||
# shape value for x, y spot is stored in board[x][y][0]
|
||||
# color value for x, y spot is stored in board[x][y][1]
|
||||
return board[boxx][boxy][0], board[boxx][boxy][1]
|
||||
|
||||
|
||||
def drawBoxCovers(board, boxes, coverage):
|
||||
# Draws boxes being covered/revealed. "boxes" is a list
|
||||
# of two-item lists, which have the x & y spot of the box.
|
||||
for box in boxes:
|
||||
left, top = leftTopCoordsOfBox(box[0], box[1])
|
||||
pygame.draw.rect(DISPLAYSURF, BGCOLOR, (left, top, BOXSIZE, BOXSIZE))
|
||||
shape, color = getShapeAndColor(board, box[0], box[1])
|
||||
drawIcon(shape, color, box[0], box[1])
|
||||
if coverage > 0: # only draw the cover if there is an coverage
|
||||
pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, coverage, BOXSIZE))
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def revealBoxesAnimation(board, boxesToReveal):
|
||||
# Do the "box reveal" animation.
|
||||
for coverage in range(BOXSIZE, (-REVEALSPEED) - 1, -REVEALSPEED):
|
||||
drawBoxCovers(board, boxesToReveal, coverage)
|
||||
|
||||
|
||||
def coverBoxesAnimation(board, boxesToCover):
|
||||
# Do the "box cover" animation.
|
||||
for coverage in range(0, BOXSIZE + REVEALSPEED, REVEALSPEED):
|
||||
drawBoxCovers(board, boxesToCover, coverage)
|
||||
|
||||
|
||||
def drawBoard(board, revealed):
|
||||
# Draws all of the boxes in their covered or revealed state.
|
||||
for boxx in range(BOARDWIDTH):
|
||||
for boxy in range(BOARDHEIGHT):
|
||||
left, top = leftTopCoordsOfBox(boxx, boxy)
|
||||
if not revealed[boxx][boxy]:
|
||||
# Draw a covered box.
|
||||
pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
|
||||
else:
|
||||
# Draw the (revealed) icon.
|
||||
shape, color = getShapeAndColor(board, boxx, boxy)
|
||||
drawIcon(shape, color, boxx, boxy)
|
||||
|
||||
|
||||
def drawHighlightBox(boxx, boxy):
|
||||
left, top = leftTopCoordsOfBox(boxx, boxy)
|
||||
pygame.draw.rect(DISPLAYSURF, HIGHLIGHTCOLOR, (left - 5, top - 5, BOXSIZE + 10, BOXSIZE + 10), 4)
|
||||
|
||||
|
||||
def startGameAnimation(board):
|
||||
# Randomly reveal the boxes 8 at a time.
|
||||
coveredBoxes = generateRevealedBoxesData(False)
|
||||
boxes = []
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
boxes.append( (x, y) )
|
||||
random.shuffle(boxes)
|
||||
boxGroups = splitIntoGroupsOf(8, boxes)
|
||||
|
||||
drawBoard(board, coveredBoxes)
|
||||
for boxGroup in boxGroups:
|
||||
revealBoxesAnimation(board, boxGroup)
|
||||
coverBoxesAnimation(board, boxGroup)
|
||||
|
||||
|
||||
def gameWonAnimation(board):
|
||||
# flash the background color when the player has won
|
||||
coveredBoxes = generateRevealedBoxesData(True)
|
||||
color1 = LIGHTBGCOLOR
|
||||
color2 = BGCOLOR
|
||||
|
||||
for i in range(13):
|
||||
color1, color2 = color2, color1 # swap colors
|
||||
DISPLAYSURF.fill(color1)
|
||||
drawBoard(board, coveredBoxes)
|
||||
pygame.display.update()
|
||||
pygame.time.wait(300)
|
||||
|
||||
|
||||
def hasWon(revealedBoxes):
|
||||
# Returns True if all the boxes have been revealed, otherwise False
|
||||
for i in revealedBoxes:
|
||||
if False in i:
|
||||
return False # return False if any boxes are covered.
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
167
python_games/memorypuzzle_obfuscated.py
Normal file
@ -0,0 +1,167 @@
|
||||
import random, pygame, sys
|
||||
from pygame.locals import *
|
||||
def hhh():
|
||||
global a, b
|
||||
pygame.init()
|
||||
a = pygame.time.Clock()
|
||||
b = pygame.display.set_mode((640, 480))
|
||||
j = 0
|
||||
k = 0
|
||||
pygame.display.set_caption('Memory Game')
|
||||
i = c()
|
||||
hh = d(False)
|
||||
h = None
|
||||
b.fill((60, 60, 100))
|
||||
g(i)
|
||||
while True:
|
||||
e = False
|
||||
b.fill((60, 60, 100))
|
||||
f(i, hh)
|
||||
for eee in pygame.event.get():
|
||||
if eee.type == QUIT or (eee.type == KEYUP and eee.key == K_ESCAPE):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif eee.type == MOUSEMOTION:
|
||||
j, k = eee.pos
|
||||
elif eee.type == MOUSEBUTTONUP:
|
||||
j, k = eee.pos
|
||||
e = True
|
||||
bb, ee = m(j, k)
|
||||
if bb != None and ee != None:
|
||||
if not hh[bb][ee]:
|
||||
n(bb, ee)
|
||||
if not hh[bb][ee] and e:
|
||||
o(i, [(bb, ee)])
|
||||
hh[bb][ee] = True
|
||||
if h == None:
|
||||
h = (bb, ee)
|
||||
else:
|
||||
q, fff = s(i, h[0], h[1])
|
||||
r, ggg = s(i, bb, ee)
|
||||
if q != r or fff != ggg:
|
||||
pygame.time.wait(1000)
|
||||
p(i, [(h[0], h[1]), (bb, ee)])
|
||||
hh[h[0]][h[1]] = False
|
||||
hh[bb][ee] = False
|
||||
elif ii(hh):
|
||||
jj(i)
|
||||
pygame.time.wait(2000)
|
||||
i = c()
|
||||
hh = d(False)
|
||||
f(i, hh)
|
||||
pygame.display.update()
|
||||
pygame.time.wait(1000)
|
||||
g(i)
|
||||
h = None
|
||||
pygame.display.update()
|
||||
a.tick(30)
|
||||
def d(ccc):
|
||||
hh = []
|
||||
for i in range(10):
|
||||
hh.append([ccc] * 7)
|
||||
return hh
|
||||
def c():
|
||||
rr = []
|
||||
for tt in ((255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 128, 0), (255, 0, 255), (0, 255, 255)):
|
||||
for ss in ('a', 'b', 'c', 'd', 'e'):
|
||||
rr.append( (ss, tt) )
|
||||
random.shuffle(rr)
|
||||
rr = rr[:35] * 2
|
||||
random.shuffle(rr)
|
||||
bbb = []
|
||||
for x in range(10):
|
||||
v = []
|
||||
for y in range(7):
|
||||
v.append(rr[0])
|
||||
del rr[0]
|
||||
bbb.append(v)
|
||||
return bbb
|
||||
def t(vv, uu):
|
||||
ww = []
|
||||
for i in range(0, len(uu), vv):
|
||||
ww.append(uu[i:i + vv])
|
||||
return ww
|
||||
def aa(bb, ee):
|
||||
return (bb * 50 + 70, ee * 50 + 65)
|
||||
def m(x, y):
|
||||
for bb in range(10):
|
||||
for ee in range(7):
|
||||
oo, ddd = aa(bb, ee)
|
||||
aaa = pygame.Rect(oo, ddd, 40, 40)
|
||||
if aaa.collidepoint(x, y):
|
||||
return (bb, ee)
|
||||
return (None, None)
|
||||
def w(ss, tt, bb, ee):
|
||||
oo, ddd = aa(bb, ee)
|
||||
if ss == 'a':
|
||||
pygame.draw.circle(b, tt, (oo + 20, ddd + 20), 15)
|
||||
pygame.draw.circle(b, (60, 60, 100), (oo + 20, ddd + 20), 5)
|
||||
elif ss == 'b':
|
||||
pygame.draw.rect(b, tt, (oo + 10, ddd + 10, 20, 20))
|
||||
elif ss == 'c':
|
||||
pygame.draw.polygon(b, tt, ((oo + 20, ddd), (oo + 40 - 1, ddd + 20), (oo + 20, ddd + 40 - 1), (oo, ddd + 20)))
|
||||
elif ss == 'd':
|
||||
for i in range(0, 40, 4):
|
||||
pygame.draw.line(b, tt, (oo, ddd + i), (oo + i, ddd))
|
||||
pygame.draw.line(b, tt, (oo + i, ddd + 39), (oo + 39, ddd + i))
|
||||
elif ss == 'e':
|
||||
pygame.draw.ellipse(b, tt, (oo, ddd + 10, 40, 20))
|
||||
def s(bbb, bb, ee):
|
||||
return bbb[bb][ee][0], bbb[bb][ee][1]
|
||||
def dd(bbb, boxes, gg):
|
||||
for box in boxes:
|
||||
oo, ddd = aa(box[0], box[1])
|
||||
pygame.draw.rect(b, (60, 60, 100), (oo, ddd, 40, 40))
|
||||
ss, tt = s(bbb, box[0], box[1])
|
||||
w(ss, tt, box[0], box[1])
|
||||
if gg > 0:
|
||||
pygame.draw.rect(b, (255, 255, 255), (oo, ddd, gg, 40))
|
||||
pygame.display.update()
|
||||
a.tick(30)
|
||||
def o(bbb, cc):
|
||||
for gg in range(40, (-8) - 1, -8):
|
||||
dd(bbb, cc, gg)
|
||||
def p(bbb, ff):
|
||||
for gg in range(0, 48, 8):
|
||||
dd(bbb, ff, gg)
|
||||
def f(bbb, pp):
|
||||
for bb in range(10):
|
||||
for ee in range(7):
|
||||
oo, ddd = aa(bb, ee)
|
||||
if not pp[bb][ee]:
|
||||
pygame.draw.rect(b, (255, 255, 255), (oo, ddd, 40, 40))
|
||||
else:
|
||||
ss, tt = s(bbb, bb, ee)
|
||||
w(ss, tt, bb, ee)
|
||||
def n(bb, ee):
|
||||
oo, ddd = aa(bb, ee)
|
||||
pygame.draw.rect(b, (0, 0, 255), (oo - 5, ddd - 5, 50, 50), 4)
|
||||
def g(bbb):
|
||||
mm = d(False)
|
||||
boxes = []
|
||||
for x in range(10):
|
||||
for y in range(7):
|
||||
boxes.append( (x, y) )
|
||||
random.shuffle(boxes)
|
||||
kk = t(8, boxes)
|
||||
f(bbb, mm)
|
||||
for nn in kk:
|
||||
o(bbb, nn)
|
||||
p(bbb, nn)
|
||||
def jj(bbb):
|
||||
mm = d(True)
|
||||
tt1 = (100, 100, 100)
|
||||
tt2 = (60, 60, 100)
|
||||
for i in range(13):
|
||||
tt1, tt2 = tt2, tt1
|
||||
b.fill(tt1)
|
||||
f(bbb, mm)
|
||||
pygame.display.update()
|
||||
pygame.time.wait(300)
|
||||
def ii(hh):
|
||||
for i in hh:
|
||||
if False in i:
|
||||
return False
|
||||
return True
|
||||
if __name__ == '__main__':
|
||||
hhh()
|
749
python_games/pentomino.py
Normal file
@ -0,0 +1,749 @@
|
||||
# Pentomino (a 5-block 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
|
||||
|
||||
import random, time, pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
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)
|
||||
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)
|
||||
LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW)
|
||||
assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color
|
||||
|
||||
TEMPLATEWIDTH = 5
|
||||
TEMPLATEHEIGHT = 5
|
||||
|
||||
F_TEMPLATE = [['.....',
|
||||
'..OO.',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'.OOO.',
|
||||
'...O.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'.OO..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.O...',
|
||||
'.OOO.',
|
||||
'..O..',
|
||||
'.....']]
|
||||
RF_TEMPLATE = [['.....',
|
||||
'.OO..',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'...O.',
|
||||
'.OOO.',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'.OOO.',
|
||||
'.O...',
|
||||
'.....']]
|
||||
I_TEMPLATE = [['..O..',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'..O..'],
|
||||
['.....',
|
||||
'.....',
|
||||
'OOOOO',
|
||||
'.....',
|
||||
'.....']]
|
||||
L_TEMPLATE = [['..O..',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'.OOOO',
|
||||
'.O...',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'..O..'],
|
||||
['.....',
|
||||
'...O.',
|
||||
'OOOO.',
|
||||
'.....',
|
||||
'.....']]
|
||||
J_TEMPLATE = [['..O..',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.O...',
|
||||
'.OOOO',
|
||||
'.....',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'..O..'],
|
||||
['.....',
|
||||
'.....',
|
||||
'OOOO.',
|
||||
'...O.',
|
||||
'.....']]
|
||||
N_TEMPLATE = [['.....',
|
||||
'.OO..',
|
||||
'..OOO',
|
||||
'.....',
|
||||
'.....'],
|
||||
['.....',
|
||||
'...O.',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'..O..'],
|
||||
['.....',
|
||||
'.....',
|
||||
'OOO..',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['..O..',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'.O...',
|
||||
'.....']]
|
||||
RN_TEMPLATE = [['.....',
|
||||
'..OO.',
|
||||
'OOO..',
|
||||
'.....',
|
||||
'.....'],
|
||||
['..O..',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'...O.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'..OOO',
|
||||
'.OO..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.O...',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'..O..']]
|
||||
P_TEMPLATE = [['.....',
|
||||
'..OO.',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'.OOO.',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'.OO..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.OO..',
|
||||
'.OOO.',
|
||||
'.....',
|
||||
'.....']]
|
||||
RP_TEMPLATE = [['.....',
|
||||
'.OO..',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..OO.',
|
||||
'.OOO.',
|
||||
'.....',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'.OOO.',
|
||||
'.OO..',
|
||||
'.....']]
|
||||
T_TEMPLATE = [['.....',
|
||||
'.OOO.',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'...O.',
|
||||
'.OOO.',
|
||||
'...O.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'..O..',
|
||||
'.OOO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.O...',
|
||||
'.OOO.',
|
||||
'.O...',
|
||||
'.....']]
|
||||
U_TEMPLATE = [['.....',
|
||||
'.O.O.',
|
||||
'.OOO.',
|
||||
'.....',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'.OOO.',
|
||||
'.O.O.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'.....']]
|
||||
V_TEMPLATE = [['..O..',
|
||||
'..O..',
|
||||
'..OOO',
|
||||
'.....',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'..OOO',
|
||||
'..O..',
|
||||
'..O..'],
|
||||
['.....',
|
||||
'.....',
|
||||
'OOO..',
|
||||
'..O..',
|
||||
'..O..'],
|
||||
['..O..',
|
||||
'..O..',
|
||||
'OOO..',
|
||||
'.....',
|
||||
'.....']]
|
||||
W_TEMPLATE = [['.....',
|
||||
'.O...',
|
||||
'.OO..',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..OO.',
|
||||
'.OO..',
|
||||
'.O...',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.OO..',
|
||||
'..OO.',
|
||||
'...O.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'...O.',
|
||||
'..OO.',
|
||||
'.OO..',
|
||||
'.....']]
|
||||
X_TEMPLATE = [['.....',
|
||||
'..O..',
|
||||
'.OOO.',
|
||||
'..O..',
|
||||
'.....']]
|
||||
Y_TEMPLATE = [['.....',
|
||||
'..O..',
|
||||
'OOOO.',
|
||||
'.....',
|
||||
'.....'],
|
||||
['..O..',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'.OOOO',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'..O..']]
|
||||
RY_TEMPLATE = [['.....',
|
||||
'.....',
|
||||
'OOOO.',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['..O..',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.....',
|
||||
'.OOOO',
|
||||
'..O..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'..O..']]
|
||||
Z_TEMPLATE = [['.....',
|
||||
'.OO..',
|
||||
'..O..',
|
||||
'..OO.',
|
||||
'.....'],
|
||||
['.....',
|
||||
'...O.',
|
||||
'.OOO.',
|
||||
'.O...',
|
||||
'.....']]
|
||||
RZ_TEMPLATE = [['.....',
|
||||
'..OO.',
|
||||
'..O..',
|
||||
'.OO..',
|
||||
'.....'],
|
||||
['.....',
|
||||
'.O...',
|
||||
'.OOO.',
|
||||
'...O.',
|
||||
'.....']]
|
||||
|
||||
|
||||
|
||||
PIECES = {'F': F_TEMPLATE,
|
||||
'RF': RF_TEMPLATE,
|
||||
'I': I_TEMPLATE,
|
||||
'L': L_TEMPLATE,
|
||||
'J': J_TEMPLATE,
|
||||
'N': N_TEMPLATE,
|
||||
'RN': RN_TEMPLATE,
|
||||
'P': P_TEMPLATE,
|
||||
'RP': RP_TEMPLATE,
|
||||
'T': T_TEMPLATE,
|
||||
'U': U_TEMPLATE,
|
||||
'V': V_TEMPLATE,
|
||||
'W': W_TEMPLATE,
|
||||
'X': X_TEMPLATE,
|
||||
'Y': Y_TEMPLATE,
|
||||
'RY': RY_TEMPLATE,
|
||||
'Z': Z_TEMPLATE,
|
||||
'RZ': RZ_TEMPLATE}
|
||||
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
|
||||
BIGFONT = pygame.font.Font('freesansbold.ttf', 100)
|
||||
pygame.display.set_caption('Pentomino')
|
||||
|
||||
showTextScreen('Pentomino')
|
||||
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
|
||||
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
|
||||
|
||||
# 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
|
||||
# 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.', 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 pentomino 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()
|
BIN
python_games/pinkgirl.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
python_games/princess.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
251
python_games/simulate.py
Normal file
@ -0,0 +1,251 @@
|
||||
# Simulate (a Simon clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import random, sys, time, pygame
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 30
|
||||
WINDOWWIDTH = 640
|
||||
WINDOWHEIGHT = 480
|
||||
FLASHSPEED = 500 # in milliseconds
|
||||
FLASHDELAY = 200 # in milliseconds
|
||||
BUTTONSIZE = 200
|
||||
BUTTONGAPSIZE = 20
|
||||
TIMEOUT = 4 # seconds before game over if no button is pushed.
|
||||
|
||||
# R G B
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = ( 0, 0, 0)
|
||||
BRIGHTRED = (255, 0, 0)
|
||||
RED = (155, 0, 0)
|
||||
BRIGHTGREEN = ( 0, 255, 0)
|
||||
GREEN = ( 0, 155, 0)
|
||||
BRIGHTBLUE = ( 0, 0, 255)
|
||||
BLUE = ( 0, 0, 155)
|
||||
BRIGHTYELLOW = (255, 255, 0)
|
||||
YELLOW = (155, 155, 0)
|
||||
DARKGRAY = ( 40, 40, 40)
|
||||
bgColor = BLACK
|
||||
|
||||
XMARGIN = int((WINDOWWIDTH - (2 * BUTTONSIZE) - BUTTONGAPSIZE) / 2)
|
||||
YMARGIN = int((WINDOWHEIGHT - (2 * BUTTONSIZE) - BUTTONGAPSIZE) / 2)
|
||||
|
||||
# Rect objects for each of the four buttons
|
||||
YELLOWRECT = pygame.Rect(XMARGIN, YMARGIN, BUTTONSIZE, BUTTONSIZE)
|
||||
BLUERECT = pygame.Rect(XMARGIN + BUTTONSIZE + BUTTONGAPSIZE, YMARGIN, BUTTONSIZE, BUTTONSIZE)
|
||||
REDRECT = pygame.Rect(XMARGIN, YMARGIN + BUTTONSIZE + BUTTONGAPSIZE, BUTTONSIZE, BUTTONSIZE)
|
||||
GREENRECT = pygame.Rect(XMARGIN + BUTTONSIZE + BUTTONGAPSIZE, YMARGIN + BUTTONSIZE + BUTTONGAPSIZE, BUTTONSIZE, BUTTONSIZE)
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, BASICFONT, BEEP1, BEEP2, BEEP3, BEEP4
|
||||
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
pygame.display.set_caption('Simulate')
|
||||
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 16)
|
||||
infoSurf = BASICFONT.render('Match the pattern by clicking on the button or using the Q, W, A, S keys.', 1, DARKGRAY)
|
||||
infoRect = infoSurf.get_rect()
|
||||
infoRect.topleft = (10, WINDOWHEIGHT - 25)
|
||||
|
||||
# load the sound files
|
||||
BEEP1 = pygame.mixer.Sound('beep1.ogg')
|
||||
BEEP2 = pygame.mixer.Sound('beep2.ogg')
|
||||
BEEP3 = pygame.mixer.Sound('beep3.ogg')
|
||||
BEEP4 = pygame.mixer.Sound('beep4.ogg')
|
||||
|
||||
# Initialize some variables for a new game
|
||||
pattern = [] # stores the pattern of colors
|
||||
currentStep = 0 # the color the player must push next
|
||||
lastClickTime = 0 # timestamp of the player's last button push
|
||||
score = 0
|
||||
# when False, the pattern is playing. when True, waiting for the player to click a colored button:
|
||||
waitingForInput = False
|
||||
|
||||
while True: # main game loop
|
||||
clickedButton = None # button that was clicked (set to YELLOW, RED, GREEN, or BLUE)
|
||||
DISPLAYSURF.fill(bgColor)
|
||||
drawButtons()
|
||||
|
||||
scoreSurf = BASICFONT.render('Score: ' + str(score), 1, WHITE)
|
||||
scoreRect = scoreSurf.get_rect()
|
||||
scoreRect.topleft = (WINDOWWIDTH - 100, 10)
|
||||
DISPLAYSURF.blit(scoreSurf, scoreRect)
|
||||
|
||||
DISPLAYSURF.blit(infoSurf, infoRect)
|
||||
|
||||
checkForQuit()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == MOUSEBUTTONUP:
|
||||
mousex, mousey = event.pos
|
||||
clickedButton = getButtonClicked(mousex, mousey)
|
||||
elif event.type == KEYDOWN:
|
||||
if event.key == K_q:
|
||||
clickedButton = YELLOW
|
||||
elif event.key == K_w:
|
||||
clickedButton = BLUE
|
||||
elif event.key == K_a:
|
||||
clickedButton = RED
|
||||
elif event.key == K_s:
|
||||
clickedButton = GREEN
|
||||
|
||||
|
||||
|
||||
if not waitingForInput:
|
||||
# play the pattern
|
||||
pygame.display.update()
|
||||
pygame.time.wait(1000)
|
||||
pattern.append(random.choice((YELLOW, BLUE, RED, GREEN)))
|
||||
for button in pattern:
|
||||
flashButtonAnimation(button)
|
||||
pygame.time.wait(FLASHDELAY)
|
||||
waitingForInput = True
|
||||
else:
|
||||
# wait for the player to enter buttons
|
||||
if clickedButton and clickedButton == pattern[currentStep]:
|
||||
# pushed the correct button
|
||||
flashButtonAnimation(clickedButton)
|
||||
currentStep += 1
|
||||
lastClickTime = time.time()
|
||||
|
||||
if currentStep == len(pattern):
|
||||
# pushed the last button in the pattern
|
||||
changeBackgroundAnimation()
|
||||
score += 1
|
||||
waitingForInput = False
|
||||
currentStep = 0 # reset back to first step
|
||||
|
||||
elif (clickedButton and clickedButton != pattern[currentStep]) or (currentStep != 0 and time.time() - TIMEOUT > lastClickTime):
|
||||
# pushed the incorrect button, or has timed out
|
||||
gameOverAnimation()
|
||||
# reset the variables for a new game:
|
||||
pattern = []
|
||||
currentStep = 0
|
||||
waitingForInput = False
|
||||
score = 0
|
||||
pygame.time.wait(1000)
|
||||
changeBackgroundAnimation()
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def terminate():
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
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 flashButtonAnimation(color, animationSpeed=50):
|
||||
if color == YELLOW:
|
||||
sound = BEEP1
|
||||
flashColor = BRIGHTYELLOW
|
||||
rectangle = YELLOWRECT
|
||||
elif color == BLUE:
|
||||
sound = BEEP2
|
||||
flashColor = BRIGHTBLUE
|
||||
rectangle = BLUERECT
|
||||
elif color == RED:
|
||||
sound = BEEP3
|
||||
flashColor = BRIGHTRED
|
||||
rectangle = REDRECT
|
||||
elif color == GREEN:
|
||||
sound = BEEP4
|
||||
flashColor = BRIGHTGREEN
|
||||
rectangle = GREENRECT
|
||||
|
||||
origSurf = DISPLAYSURF.copy()
|
||||
flashSurf = pygame.Surface((BUTTONSIZE, BUTTONSIZE))
|
||||
flashSurf = flashSurf.convert_alpha()
|
||||
r, g, b = flashColor
|
||||
sound.play()
|
||||
for start, end, step in ((0, 255, 1), (255, 0, -1)): # animation loop
|
||||
for alpha in range(start, end, animationSpeed * step):
|
||||
checkForQuit()
|
||||
DISPLAYSURF.blit(origSurf, (0, 0))
|
||||
flashSurf.fill((r, g, b, alpha))
|
||||
DISPLAYSURF.blit(flashSurf, rectangle.topleft)
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
DISPLAYSURF.blit(origSurf, (0, 0))
|
||||
|
||||
|
||||
def drawButtons():
|
||||
pygame.draw.rect(DISPLAYSURF, YELLOW, YELLOWRECT)
|
||||
pygame.draw.rect(DISPLAYSURF, BLUE, BLUERECT)
|
||||
pygame.draw.rect(DISPLAYSURF, RED, REDRECT)
|
||||
pygame.draw.rect(DISPLAYSURF, GREEN, GREENRECT)
|
||||
|
||||
|
||||
def changeBackgroundAnimation(animationSpeed=40):
|
||||
global bgColor
|
||||
newBgColor = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
|
||||
|
||||
newBgSurf = pygame.Surface((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
newBgSurf = newBgSurf.convert_alpha()
|
||||
r, g, b = newBgColor
|
||||
for alpha in range(0, 255, animationSpeed): # animation loop
|
||||
checkForQuit()
|
||||
DISPLAYSURF.fill(bgColor)
|
||||
|
||||
newBgSurf.fill((r, g, b, alpha))
|
||||
DISPLAYSURF.blit(newBgSurf, (0, 0))
|
||||
|
||||
drawButtons() # redraw the buttons on top of the tint
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
bgColor = newBgColor
|
||||
|
||||
|
||||
def gameOverAnimation(color=WHITE, animationSpeed=50):
|
||||
# play all beeps at once, then flash the background
|
||||
origSurf = DISPLAYSURF.copy()
|
||||
flashSurf = pygame.Surface(DISPLAYSURF.get_size())
|
||||
flashSurf = flashSurf.convert_alpha()
|
||||
BEEP1.play() # play all four beeps at the same time, roughly.
|
||||
BEEP2.play()
|
||||
BEEP3.play()
|
||||
BEEP4.play()
|
||||
r, g, b = color
|
||||
for i in range(3): # do the flash 3 times
|
||||
for start, end, step in ((0, 255, 1), (255, 0, -1)):
|
||||
# The first iteration in this loop sets the following for loop
|
||||
# to go from 0 to 255, the second from 255 to 0.
|
||||
for alpha in range(start, end, animationSpeed * step): # animation loop
|
||||
# alpha means transparency. 255 is opaque, 0 is invisible
|
||||
checkForQuit()
|
||||
flashSurf.fill((r, g, b, alpha))
|
||||
DISPLAYSURF.blit(origSurf, (0, 0))
|
||||
DISPLAYSURF.blit(flashSurf, (0, 0))
|
||||
drawButtons()
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
|
||||
def getButtonClicked(x, y):
|
||||
if YELLOWRECT.collidepoint( (x, y) ):
|
||||
return YELLOW
|
||||
elif BLUERECT.collidepoint( (x, y) ):
|
||||
return BLUE
|
||||
elif REDRECT.collidepoint( (x, y) ):
|
||||
return RED
|
||||
elif GREENRECT.collidepoint( (x, y) ):
|
||||
return GREEN
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
332
python_games/slidepuzzle.py
Normal file
@ -0,0 +1,332 @@
|
||||
# Slide Puzzle
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import pygame, sys, random
|
||||
from pygame.locals import *
|
||||
|
||||
# Create the constants (go ahead and experiment with different values)
|
||||
BOARDWIDTH = 4 # number of columns in the board
|
||||
BOARDHEIGHT = 4 # number of rows in the board
|
||||
TILESIZE = 80
|
||||
WINDOWWIDTH = 640
|
||||
WINDOWHEIGHT = 480
|
||||
FPS = 30
|
||||
BLANK = None
|
||||
|
||||
# R G B
|
||||
BLACK = ( 0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
BRIGHTBLUE = ( 0, 50, 255)
|
||||
DARKTURQUOISE = ( 3, 54, 73)
|
||||
GREEN = ( 0, 204, 0)
|
||||
|
||||
BGCOLOR = DARKTURQUOISE
|
||||
TILECOLOR = GREEN
|
||||
TEXTCOLOR = WHITE
|
||||
BORDERCOLOR = BRIGHTBLUE
|
||||
BASICFONTSIZE = 20
|
||||
|
||||
BUTTONCOLOR = WHITE
|
||||
BUTTONTEXTCOLOR = BLACK
|
||||
MESSAGECOLOR = WHITE
|
||||
|
||||
XMARGIN = int((WINDOWWIDTH - (TILESIZE * BOARDWIDTH + (BOARDWIDTH - 1))) / 2)
|
||||
YMARGIN = int((WINDOWHEIGHT - (TILESIZE * BOARDHEIGHT + (BOARDHEIGHT - 1))) / 2)
|
||||
|
||||
UP = 'up'
|
||||
DOWN = 'down'
|
||||
LEFT = 'left'
|
||||
RIGHT = 'right'
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, BASICFONT, RESET_SURF, RESET_RECT, NEW_SURF, NEW_RECT, SOLVE_SURF, SOLVE_RECT
|
||||
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
pygame.display.set_caption('Slide Puzzle')
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', BASICFONTSIZE)
|
||||
|
||||
# Store the option buttons and their rectangles in OPTIONS.
|
||||
RESET_SURF, RESET_RECT = makeText('Reset', TEXTCOLOR, TILECOLOR, WINDOWWIDTH - 120, WINDOWHEIGHT - 90)
|
||||
NEW_SURF, NEW_RECT = makeText('New Game', TEXTCOLOR, TILECOLOR, WINDOWWIDTH - 120, WINDOWHEIGHT - 60)
|
||||
SOLVE_SURF, SOLVE_RECT = makeText('Solve', TEXTCOLOR, TILECOLOR, WINDOWWIDTH - 120, WINDOWHEIGHT - 30)
|
||||
|
||||
mainBoard, solutionSeq = generateNewPuzzle(80)
|
||||
SOLVEDBOARD = getStartingBoard() # a solved board is the same as the board in a start state.
|
||||
allMoves = [] # list of moves made from the solved configuration
|
||||
|
||||
while True: # main game loop
|
||||
slideTo = None # the direction, if any, a tile should slide
|
||||
msg = 'Click tile or press arrow keys to slide.' # contains the message to show in the upper left corner.
|
||||
if mainBoard == SOLVEDBOARD:
|
||||
msg = 'Solved!'
|
||||
|
||||
drawBoard(mainBoard, msg)
|
||||
|
||||
checkForQuit()
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == MOUSEBUTTONUP:
|
||||
spotx, spoty = getSpotClicked(mainBoard, event.pos[0], event.pos[1])
|
||||
|
||||
if (spotx, spoty) == (None, None):
|
||||
# check if the user clicked on an option button
|
||||
if RESET_RECT.collidepoint(event.pos):
|
||||
resetAnimation(mainBoard, allMoves) # clicked on Reset button
|
||||
allMoves = []
|
||||
elif NEW_RECT.collidepoint(event.pos):
|
||||
mainBoard, solutionSeq = generateNewPuzzle(80) # clicked on New Game button
|
||||
allMoves = []
|
||||
elif SOLVE_RECT.collidepoint(event.pos):
|
||||
resetAnimation(mainBoard, solutionSeq + allMoves) # clicked on Solve button
|
||||
allMoves = []
|
||||
else:
|
||||
# check if the clicked tile was next to the blank spot
|
||||
|
||||
blankx, blanky = getBlankPosition(mainBoard)
|
||||
if spotx == blankx + 1 and spoty == blanky:
|
||||
slideTo = LEFT
|
||||
elif spotx == blankx - 1 and spoty == blanky:
|
||||
slideTo = RIGHT
|
||||
elif spotx == blankx and spoty == blanky + 1:
|
||||
slideTo = UP
|
||||
elif spotx == blankx and spoty == blanky - 1:
|
||||
slideTo = DOWN
|
||||
|
||||
elif event.type == KEYUP:
|
||||
# check if the user pressed a key to slide a tile
|
||||
if event.key in (K_LEFT, K_a) and isValidMove(mainBoard, LEFT):
|
||||
slideTo = LEFT
|
||||
elif event.key in (K_RIGHT, K_d) and isValidMove(mainBoard, RIGHT):
|
||||
slideTo = RIGHT
|
||||
elif event.key in (K_UP, K_w) and isValidMove(mainBoard, UP):
|
||||
slideTo = UP
|
||||
elif event.key in (K_DOWN, K_s) and isValidMove(mainBoard, DOWN):
|
||||
slideTo = DOWN
|
||||
|
||||
if slideTo:
|
||||
slideAnimation(mainBoard, slideTo, 'Click tile or press arrow keys to slide.', 8) # show slide on screen
|
||||
makeMove(mainBoard, slideTo)
|
||||
allMoves.append(slideTo) # record the slide
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def terminate():
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
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 getStartingBoard():
|
||||
# Return a board data structure with tiles in the solved state.
|
||||
# For example, if BOARDWIDTH and BOARDHEIGHT are both 3, this function
|
||||
# returns [[1, 4, 7], [2, 5, 8], [3, 6, BLANK]]
|
||||
counter = 1
|
||||
board = []
|
||||
for x in range(BOARDWIDTH):
|
||||
column = []
|
||||
for y in range(BOARDHEIGHT):
|
||||
column.append(counter)
|
||||
counter += BOARDWIDTH
|
||||
board.append(column)
|
||||
counter -= BOARDWIDTH * (BOARDHEIGHT - 1) + BOARDWIDTH - 1
|
||||
|
||||
board[BOARDWIDTH-1][BOARDHEIGHT-1] = BLANK
|
||||
return board
|
||||
|
||||
|
||||
def getBlankPosition(board):
|
||||
# Return the x and y of board coordinates of the blank space.
|
||||
for x in range(BOARDWIDTH):
|
||||
for y in range(BOARDHEIGHT):
|
||||
if board[x][y] == BLANK:
|
||||
return (x, y)
|
||||
|
||||
|
||||
def makeMove(board, move):
|
||||
# This function does not check if the move is valid.
|
||||
blankx, blanky = getBlankPosition(board)
|
||||
|
||||
if move == UP:
|
||||
board[blankx][blanky], board[blankx][blanky + 1] = board[blankx][blanky + 1], board[blankx][blanky]
|
||||
elif move == DOWN:
|
||||
board[blankx][blanky], board[blankx][blanky - 1] = board[blankx][blanky - 1], board[blankx][blanky]
|
||||
elif move == LEFT:
|
||||
board[blankx][blanky], board[blankx + 1][blanky] = board[blankx + 1][blanky], board[blankx][blanky]
|
||||
elif move == RIGHT:
|
||||
board[blankx][blanky], board[blankx - 1][blanky] = board[blankx - 1][blanky], board[blankx][blanky]
|
||||
|
||||
|
||||
def isValidMove(board, move):
|
||||
blankx, blanky = getBlankPosition(board)
|
||||
return (move == UP and blanky != len(board[0]) - 1) or \
|
||||
(move == DOWN and blanky != 0) or \
|
||||
(move == LEFT and blankx != len(board) - 1) or \
|
||||
(move == RIGHT and blankx != 0)
|
||||
|
||||
|
||||
def getRandomMove(board, lastMove=None):
|
||||
# start with a full list of all four moves
|
||||
validMoves = [UP, DOWN, LEFT, RIGHT]
|
||||
|
||||
# remove moves from the list as they are disqualified
|
||||
if lastMove == UP or not isValidMove(board, DOWN):
|
||||
validMoves.remove(DOWN)
|
||||
if lastMove == DOWN or not isValidMove(board, UP):
|
||||
validMoves.remove(UP)
|
||||
if lastMove == LEFT or not isValidMove(board, RIGHT):
|
||||
validMoves.remove(RIGHT)
|
||||
if lastMove == RIGHT or not isValidMove(board, LEFT):
|
||||
validMoves.remove(LEFT)
|
||||
|
||||
# return a random move from the list of remaining moves
|
||||
return random.choice(validMoves)
|
||||
|
||||
|
||||
def getLeftTopOfTile(tileX, tileY):
|
||||
left = XMARGIN + (tileX * TILESIZE) + (tileX - 1)
|
||||
top = YMARGIN + (tileY * TILESIZE) + (tileY - 1)
|
||||
return (left, top)
|
||||
|
||||
|
||||
def getSpotClicked(board, x, y):
|
||||
# from the x & y pixel coordinates, get the x & y board coordinates
|
||||
for tileX in range(len(board)):
|
||||
for tileY in range(len(board[0])):
|
||||
left, top = getLeftTopOfTile(tileX, tileY)
|
||||
tileRect = pygame.Rect(left, top, TILESIZE, TILESIZE)
|
||||
if tileRect.collidepoint(x, y):
|
||||
return (tileX, tileY)
|
||||
return (None, None)
|
||||
|
||||
|
||||
def drawTile(tilex, tiley, number, adjx=0, adjy=0):
|
||||
# draw a tile at board coordinates tilex and tiley, optionally a few
|
||||
# pixels over (determined by adjx and adjy)
|
||||
left, top = getLeftTopOfTile(tilex, tiley)
|
||||
pygame.draw.rect(DISPLAYSURF, TILECOLOR, (left + adjx, top + adjy, TILESIZE, TILESIZE))
|
||||
textSurf = BASICFONT.render(str(number), True, TEXTCOLOR)
|
||||
textRect = textSurf.get_rect()
|
||||
textRect.center = left + int(TILESIZE / 2) + adjx, top + int(TILESIZE / 2) + adjy
|
||||
DISPLAYSURF.blit(textSurf, textRect)
|
||||
|
||||
|
||||
def makeText(text, color, bgcolor, top, left):
|
||||
# create the Surface and Rect objects for some text.
|
||||
textSurf = BASICFONT.render(text, True, color, bgcolor)
|
||||
textRect = textSurf.get_rect()
|
||||
textRect.topleft = (top, left)
|
||||
return (textSurf, textRect)
|
||||
|
||||
|
||||
def drawBoard(board, message):
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
if message:
|
||||
textSurf, textRect = makeText(message, MESSAGECOLOR, BGCOLOR, 5, 5)
|
||||
DISPLAYSURF.blit(textSurf, textRect)
|
||||
|
||||
for tilex in range(len(board)):
|
||||
for tiley in range(len(board[0])):
|
||||
if board[tilex][tiley]:
|
||||
drawTile(tilex, tiley, board[tilex][tiley])
|
||||
|
||||
left, top = getLeftTopOfTile(0, 0)
|
||||
width = BOARDWIDTH * TILESIZE
|
||||
height = BOARDHEIGHT * TILESIZE
|
||||
pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (left - 5, top - 5, width + 11, height + 11), 4)
|
||||
|
||||
DISPLAYSURF.blit(RESET_SURF, RESET_RECT)
|
||||
DISPLAYSURF.blit(NEW_SURF, NEW_RECT)
|
||||
DISPLAYSURF.blit(SOLVE_SURF, SOLVE_RECT)
|
||||
|
||||
|
||||
def slideAnimation(board, direction, message, animationSpeed):
|
||||
# Note: This function does not check if the move is valid.
|
||||
|
||||
blankx, blanky = getBlankPosition(board)
|
||||
if direction == UP:
|
||||
movex = blankx
|
||||
movey = blanky + 1
|
||||
elif direction == DOWN:
|
||||
movex = blankx
|
||||
movey = blanky - 1
|
||||
elif direction == LEFT:
|
||||
movex = blankx + 1
|
||||
movey = blanky
|
||||
elif direction == RIGHT:
|
||||
movex = blankx - 1
|
||||
movey = blanky
|
||||
|
||||
# prepare the base surface
|
||||
drawBoard(board, message)
|
||||
baseSurf = DISPLAYSURF.copy()
|
||||
# draw a blank space over the moving tile on the baseSurf Surface.
|
||||
moveLeft, moveTop = getLeftTopOfTile(movex, movey)
|
||||
pygame.draw.rect(baseSurf, BGCOLOR, (moveLeft, moveTop, TILESIZE, TILESIZE))
|
||||
|
||||
for i in range(0, TILESIZE, animationSpeed):
|
||||
# animate the tile sliding over
|
||||
checkForQuit()
|
||||
DISPLAYSURF.blit(baseSurf, (0, 0))
|
||||
if direction == UP:
|
||||
drawTile(movex, movey, board[movex][movey], 0, -i)
|
||||
if direction == DOWN:
|
||||
drawTile(movex, movey, board[movex][movey], 0, i)
|
||||
if direction == LEFT:
|
||||
drawTile(movex, movey, board[movex][movey], -i, 0)
|
||||
if direction == RIGHT:
|
||||
drawTile(movex, movey, board[movex][movey], i, 0)
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
def generateNewPuzzle(numSlides):
|
||||
# From a starting configuration, make numSlides number of moves (and
|
||||
# animate these moves).
|
||||
sequence = []
|
||||
board = getStartingBoard()
|
||||
drawBoard(board, '')
|
||||
pygame.display.update()
|
||||
pygame.time.wait(500) # pause 500 milliseconds for effect
|
||||
lastMove = None
|
||||
for i in range(numSlides):
|
||||
move = getRandomMove(board, lastMove)
|
||||
slideAnimation(board, move, 'Generating new puzzle...', animationSpeed=int(TILESIZE / 3))
|
||||
makeMove(board, move)
|
||||
sequence.append(move)
|
||||
lastMove = move
|
||||
return (board, sequence)
|
||||
|
||||
|
||||
def resetAnimation(board, allMoves):
|
||||
# make all of the moves in allMoves in reverse.
|
||||
revAllMoves = allMoves[:] # gets a copy of the list
|
||||
revAllMoves.reverse()
|
||||
|
||||
for move in revAllMoves:
|
||||
if move == UP:
|
||||
oppositeMove = DOWN
|
||||
elif move == DOWN:
|
||||
oppositeMove = UP
|
||||
elif move == RIGHT:
|
||||
oppositeMove = LEFT
|
||||
elif move == LEFT:
|
||||
oppositeMove = RIGHT
|
||||
slideAnimation(board, oppositeMove, '', animationSpeed=int(TILESIZE / 2))
|
||||
makeMove(board, oppositeMove)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
python_games/squirrel.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
396
python_games/squirrel.py
Normal file
@ -0,0 +1,396 @@
|
||||
# Squirrel Eat Squirrel (a 2D Katamari Damacy clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import random, sys, time, math, pygame
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 30 # frames per second to update the screen
|
||||
WINWIDTH = 640 # width of the program's window, in pixels
|
||||
WINHEIGHT = 480 # height in pixels
|
||||
HALF_WINWIDTH = int(WINWIDTH / 2)
|
||||
HALF_WINHEIGHT = int(WINHEIGHT / 2)
|
||||
|
||||
GRASSCOLOR = (24, 255, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
RED = (255, 0, 0)
|
||||
|
||||
CAMERASLACK = 90 # how far from the center the squirrel moves before moving the camera
|
||||
MOVERATE = 9 # how fast the player moves
|
||||
BOUNCERATE = 6 # how fast the player bounces (large is slower)
|
||||
BOUNCEHEIGHT = 30 # how high the player bounces
|
||||
STARTSIZE = 25 # how big the player starts off
|
||||
WINSIZE = 300 # how big the player needs to be to win
|
||||
INVULNTIME = 2 # how long the player is invulnerable after being hit in seconds
|
||||
GAMEOVERTIME = 4 # how long the "game over" text stays on the screen in seconds
|
||||
MAXHEALTH = 3 # how much health the player starts with
|
||||
|
||||
NUMGRASS = 80 # number of grass objects in the active area
|
||||
NUMSQUIRRELS = 30 # number of squirrels in the active area
|
||||
SQUIRRELMINSPEED = 3 # slowest squirrel speed
|
||||
SQUIRRELMAXSPEED = 7 # fastest squirrel speed
|
||||
DIRCHANGEFREQ = 2 # % chance of direction change per frame
|
||||
LEFT = 'left'
|
||||
RIGHT = 'right'
|
||||
|
||||
"""
|
||||
This program has three data structures to represent the player, enemy squirrels, and grass background objects. The data structures are dictionaries with the following keys:
|
||||
|
||||
Keys used by all three data structures:
|
||||
'x' - the left edge coordinate of the object in the game world (not a pixel coordinate on the screen)
|
||||
'y' - the top edge coordinate of the object in the game world (not a pixel coordinate on the screen)
|
||||
'rect' - the pygame.Rect object representing where on the screen the object is located.
|
||||
Player data structure keys:
|
||||
'surface' - the pygame.Surface object that stores the image of the squirrel which will be drawn to the screen.
|
||||
'facing' - either set to LEFT or RIGHT, stores which direction the player is facing.
|
||||
'size' - the width and height of the player in pixels. (The width & height are always the same.)
|
||||
'bounce' - represents at what point in a bounce the player is in. 0 means standing (no bounce), up to BOUNCERATE (the completion of the bounce)
|
||||
'health' - an integer showing how many more times the player can be hit by a larger squirrel before dying.
|
||||
Enemy Squirrel data structure keys:
|
||||
'surface' - the pygame.Surface object that stores the image of the squirrel which will be drawn to the screen.
|
||||
'movex' - how many pixels per frame the squirrel moves horizontally. A negative integer is moving to the left, a positive to the right.
|
||||
'movey' - how many pixels per frame the squirrel moves vertically. A negative integer is moving up, a positive moving down.
|
||||
'width' - the width of the squirrel's image, in pixels
|
||||
'height' - the height of the squirrel's image, in pixels
|
||||
'bounce' - represents at what point in a bounce the player is in. 0 means standing (no bounce), up to BOUNCERATE (the completion of the bounce)
|
||||
'bouncerate' - how quickly the squirrel bounces. A lower number means a quicker bounce.
|
||||
'bounceheight' - how high (in pixels) the squirrel bounces
|
||||
Grass data structure keys:
|
||||
'grassImage' - an integer that refers to the index of the pygame.Surface object in GRASSIMAGES used for this grass object
|
||||
"""
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, BASICFONT, L_SQUIR_IMG, R_SQUIR_IMG, GRASSIMAGES
|
||||
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
pygame.display.set_icon(pygame.image.load('gameicon.png'))
|
||||
DISPLAYSURF = pygame.display.set_mode((WINWIDTH, WINHEIGHT))
|
||||
pygame.display.set_caption('Squirrel Eat Squirrel')
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 32)
|
||||
|
||||
# load the image files
|
||||
L_SQUIR_IMG = pygame.image.load('squirrel.png')
|
||||
R_SQUIR_IMG = pygame.transform.flip(L_SQUIR_IMG, True, False)
|
||||
GRASSIMAGES = []
|
||||
for i in range(1, 5):
|
||||
GRASSIMAGES.append(pygame.image.load('grass%s.png' % i))
|
||||
|
||||
while True:
|
||||
runGame()
|
||||
|
||||
|
||||
def runGame():
|
||||
# set up variables for the start of a new game
|
||||
invulnerableMode = False # if the player is invulnerable
|
||||
invulnerableStartTime = 0 # time the player became invulnerable
|
||||
gameOverMode = False # if the player has lost
|
||||
gameOverStartTime = 0 # time the player lost
|
||||
winMode = False # if the player has won
|
||||
|
||||
# create the surfaces to hold game text
|
||||
gameOverSurf = BASICFONT.render('Game Over', True, WHITE)
|
||||
gameOverRect = gameOverSurf.get_rect()
|
||||
gameOverRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
|
||||
|
||||
winSurf = BASICFONT.render('You have achieved OMEGA SQUIRREL!', True, WHITE)
|
||||
winRect = winSurf.get_rect()
|
||||
winRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
|
||||
|
||||
winSurf2 = BASICFONT.render('(Press "r" to restart.)', True, WHITE)
|
||||
winRect2 = winSurf2.get_rect()
|
||||
winRect2.center = (HALF_WINWIDTH, HALF_WINHEIGHT + 30)
|
||||
|
||||
# camerax and cameray are the top left of where the camera view is
|
||||
camerax = 0
|
||||
cameray = 0
|
||||
|
||||
grassObjs = [] # stores all the grass objects in the game
|
||||
squirrelObjs = [] # stores all the non-player squirrel objects
|
||||
# stores the player object:
|
||||
playerObj = {'surface': pygame.transform.scale(L_SQUIR_IMG, (STARTSIZE, STARTSIZE)),
|
||||
'facing': LEFT,
|
||||
'size': STARTSIZE,
|
||||
'x': HALF_WINWIDTH,
|
||||
'y': HALF_WINHEIGHT,
|
||||
'bounce':0,
|
||||
'health': MAXHEALTH}
|
||||
|
||||
moveLeft = False
|
||||
moveRight = False
|
||||
moveUp = False
|
||||
moveDown = False
|
||||
|
||||
# start off with some random grass images on the screen
|
||||
for i in range(10):
|
||||
grassObjs.append(makeNewGrass(camerax, cameray))
|
||||
grassObjs[i]['x'] = random.randint(0, WINWIDTH)
|
||||
grassObjs[i]['y'] = random.randint(0, WINHEIGHT)
|
||||
|
||||
while True: # main game loop
|
||||
# Check if we should turn off invulnerability
|
||||
if invulnerableMode and time.time() - invulnerableStartTime > INVULNTIME:
|
||||
invulnerableMode = False
|
||||
|
||||
# move all the squirrels
|
||||
for sObj in squirrelObjs:
|
||||
# move the squirrel, and adjust for their bounce
|
||||
sObj['x'] += sObj['movex']
|
||||
sObj['y'] += sObj['movey']
|
||||
sObj['bounce'] += 1
|
||||
if sObj['bounce'] > sObj['bouncerate']:
|
||||
sObj['bounce'] = 0 # reset bounce amount
|
||||
|
||||
# random chance they change direction
|
||||
if random.randint(0, 99) < DIRCHANGEFREQ:
|
||||
sObj['movex'] = getRandomVelocity()
|
||||
sObj['movey'] = getRandomVelocity()
|
||||
if sObj['movex'] > 0: # faces right
|
||||
sObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (sObj['width'], sObj['height']))
|
||||
else: # faces left
|
||||
sObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (sObj['width'], sObj['height']))
|
||||
|
||||
|
||||
# go through all the objects and see if any need to be deleted.
|
||||
for i in range(len(grassObjs) - 1, -1, -1):
|
||||
if isOutsideActiveArea(camerax, cameray, grassObjs[i]):
|
||||
del grassObjs[i]
|
||||
for i in range(len(squirrelObjs) - 1, -1, -1):
|
||||
if isOutsideActiveArea(camerax, cameray, squirrelObjs[i]):
|
||||
del squirrelObjs[i]
|
||||
|
||||
# add more grass & squirrels if we don't have enough.
|
||||
while len(grassObjs) < NUMGRASS:
|
||||
grassObjs.append(makeNewGrass(camerax, cameray))
|
||||
while len(squirrelObjs) < NUMSQUIRRELS:
|
||||
squirrelObjs.append(makeNewSquirrel(camerax, cameray))
|
||||
|
||||
# adjust camerax and cameray if beyond the "camera slack"
|
||||
playerCenterx = playerObj['x'] + int(playerObj['size'] / 2)
|
||||
playerCentery = playerObj['y'] + int(playerObj['size'] / 2)
|
||||
if (camerax + HALF_WINWIDTH) - playerCenterx > CAMERASLACK:
|
||||
camerax = playerCenterx + CAMERASLACK - HALF_WINWIDTH
|
||||
elif playerCenterx - (camerax + HALF_WINWIDTH) > CAMERASLACK:
|
||||
camerax = playerCenterx - CAMERASLACK - HALF_WINWIDTH
|
||||
if (cameray + HALF_WINHEIGHT) - playerCentery > CAMERASLACK:
|
||||
cameray = playerCentery + CAMERASLACK - HALF_WINHEIGHT
|
||||
elif playerCentery - (cameray + HALF_WINHEIGHT) > CAMERASLACK:
|
||||
cameray = playerCentery - CAMERASLACK - HALF_WINHEIGHT
|
||||
|
||||
# draw the green background
|
||||
DISPLAYSURF.fill(GRASSCOLOR)
|
||||
|
||||
# draw all the grass objects on the screen
|
||||
for gObj in grassObjs:
|
||||
gRect = pygame.Rect( (gObj['x'] - camerax,
|
||||
gObj['y'] - cameray,
|
||||
gObj['width'],
|
||||
gObj['height']) )
|
||||
DISPLAYSURF.blit(GRASSIMAGES[gObj['grassImage']], gRect)
|
||||
|
||||
|
||||
# draw the other squirrels
|
||||
for sObj in squirrelObjs:
|
||||
sObj['rect'] = pygame.Rect( (sObj['x'] - camerax,
|
||||
sObj['y'] - cameray - getBounceAmount(sObj['bounce'], sObj['bouncerate'], sObj['bounceheight']),
|
||||
sObj['width'],
|
||||
sObj['height']) )
|
||||
DISPLAYSURF.blit(sObj['surface'], sObj['rect'])
|
||||
|
||||
|
||||
# draw the player squirrel
|
||||
flashIsOn = round(time.time(), 1) * 10 % 2 == 1
|
||||
if not gameOverMode and not (invulnerableMode and flashIsOn):
|
||||
playerObj['rect'] = pygame.Rect( (playerObj['x'] - camerax,
|
||||
playerObj['y'] - cameray - getBounceAmount(playerObj['bounce'], BOUNCERATE, BOUNCEHEIGHT),
|
||||
playerObj['size'],
|
||||
playerObj['size']) )
|
||||
DISPLAYSURF.blit(playerObj['surface'], playerObj['rect'])
|
||||
|
||||
|
||||
# draw the health meter
|
||||
drawHealthMeter(playerObj['health'])
|
||||
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT:
|
||||
terminate()
|
||||
|
||||
elif event.type == KEYDOWN:
|
||||
if event.key in (K_UP, K_w):
|
||||
moveDown = False
|
||||
moveUp = True
|
||||
elif event.key in (K_DOWN, K_s):
|
||||
moveUp = False
|
||||
moveDown = True
|
||||
elif event.key in (K_LEFT, K_a):
|
||||
moveRight = False
|
||||
moveLeft = True
|
||||
if playerObj['facing'] != LEFT: # change player image
|
||||
playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
|
||||
playerObj['facing'] = LEFT
|
||||
elif event.key in (K_RIGHT, K_d):
|
||||
moveLeft = False
|
||||
moveRight = True
|
||||
if playerObj['facing'] != RIGHT: # change player image
|
||||
playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
|
||||
playerObj['facing'] = RIGHT
|
||||
elif winMode and event.key == K_r:
|
||||
return
|
||||
|
||||
elif event.type == KEYUP:
|
||||
# stop moving the player's squirrel
|
||||
if event.key in (K_LEFT, K_a):
|
||||
moveLeft = False
|
||||
elif event.key in (K_RIGHT, K_d):
|
||||
moveRight = False
|
||||
elif event.key in (K_UP, K_w):
|
||||
moveUp = False
|
||||
elif event.key in (K_DOWN, K_s):
|
||||
moveDown = False
|
||||
|
||||
elif event.key == K_ESCAPE:
|
||||
terminate()
|
||||
|
||||
if not gameOverMode:
|
||||
# actually move the player
|
||||
if moveLeft:
|
||||
playerObj['x'] -= MOVERATE
|
||||
if moveRight:
|
||||
playerObj['x'] += MOVERATE
|
||||
if moveUp:
|
||||
playerObj['y'] -= MOVERATE
|
||||
if moveDown:
|
||||
playerObj['y'] += MOVERATE
|
||||
|
||||
if (moveLeft or moveRight or moveUp or moveDown) or playerObj['bounce'] != 0:
|
||||
playerObj['bounce'] += 1
|
||||
|
||||
if playerObj['bounce'] > BOUNCERATE:
|
||||
playerObj['bounce'] = 0 # reset bounce amount
|
||||
|
||||
# check if the player has collided with any squirrels
|
||||
for i in range(len(squirrelObjs)-1, -1, -1):
|
||||
sqObj = squirrelObjs[i]
|
||||
if 'rect' in sqObj and playerObj['rect'].colliderect(sqObj['rect']):
|
||||
# a player/squirrel collision has occurred
|
||||
|
||||
if sqObj['width'] * sqObj['height'] <= playerObj['size']**2:
|
||||
# player is larger and eats the squirrel
|
||||
playerObj['size'] += int( (sqObj['width'] * sqObj['height'])**0.2 ) + 1
|
||||
del squirrelObjs[i]
|
||||
|
||||
if playerObj['facing'] == LEFT:
|
||||
playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
|
||||
if playerObj['facing'] == RIGHT:
|
||||
playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
|
||||
|
||||
if playerObj['size'] > WINSIZE:
|
||||
winMode = True # turn on "win mode"
|
||||
|
||||
elif not invulnerableMode:
|
||||
# player is smaller and takes damage
|
||||
invulnerableMode = True
|
||||
invulnerableStartTime = time.time()
|
||||
playerObj['health'] -= 1
|
||||
if playerObj['health'] == 0:
|
||||
gameOverMode = True # turn on "game over mode"
|
||||
gameOverStartTime = time.time()
|
||||
else:
|
||||
# game is over, show "game over" text
|
||||
DISPLAYSURF.blit(gameOverSurf, gameOverRect)
|
||||
if time.time() - gameOverStartTime > GAMEOVERTIME:
|
||||
return # end the current game
|
||||
|
||||
# check if the player has won.
|
||||
if winMode:
|
||||
DISPLAYSURF.blit(winSurf, winRect)
|
||||
DISPLAYSURF.blit(winSurf2, winRect2)
|
||||
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
|
||||
|
||||
|
||||
def drawHealthMeter(currentHealth):
|
||||
for i in range(currentHealth): # draw red health bars
|
||||
pygame.draw.rect(DISPLAYSURF, RED, (15, 5 + (10 * MAXHEALTH) - i * 10, 20, 10))
|
||||
for i in range(MAXHEALTH): # draw the white outlines
|
||||
pygame.draw.rect(DISPLAYSURF, WHITE, (15, 5 + (10 * MAXHEALTH) - i * 10, 20, 10), 1)
|
||||
|
||||
|
||||
def terminate():
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
def getBounceAmount(currentBounce, bounceRate, bounceHeight):
|
||||
# Returns the number of pixels to offset based on the bounce.
|
||||
# Larger bounceRate means a slower bounce.
|
||||
# Larger bounceHeight means a higher bounce.
|
||||
# currentBounce will always be less than bounceRate
|
||||
return int(math.sin( (math.pi / float(bounceRate)) * currentBounce ) * bounceHeight)
|
||||
|
||||
def getRandomVelocity():
|
||||
speed = random.randint(SQUIRRELMINSPEED, SQUIRRELMAXSPEED)
|
||||
if random.randint(0, 1) == 0:
|
||||
return speed
|
||||
else:
|
||||
return -speed
|
||||
|
||||
|
||||
def getRandomOffCameraPos(camerax, cameray, objWidth, objHeight):
|
||||
# create a Rect of the camera view
|
||||
cameraRect = pygame.Rect(camerax, cameray, WINWIDTH, WINHEIGHT)
|
||||
while True:
|
||||
x = random.randint(camerax - WINWIDTH, camerax + (2 * WINWIDTH))
|
||||
y = random.randint(cameray - WINHEIGHT, cameray + (2 * WINHEIGHT))
|
||||
# create a Rect object with the random coordinates and use colliderect()
|
||||
# to make sure the right edge isn't in the camera view.
|
||||
objRect = pygame.Rect(x, y, objWidth, objHeight)
|
||||
if not objRect.colliderect(cameraRect):
|
||||
return x, y
|
||||
|
||||
|
||||
def makeNewSquirrel(camerax, cameray):
|
||||
sq = {}
|
||||
generalSize = random.randint(5, 25)
|
||||
multiplier = random.randint(1, 3)
|
||||
sq['width'] = (generalSize + random.randint(0, 10)) * multiplier
|
||||
sq['height'] = (generalSize + random.randint(0, 10)) * multiplier
|
||||
sq['x'], sq['y'] = getRandomOffCameraPos(camerax, cameray, sq['width'], sq['height'])
|
||||
sq['movex'] = getRandomVelocity()
|
||||
sq['movey'] = getRandomVelocity()
|
||||
if sq['movex'] < 0: # squirrel is facing left
|
||||
sq['surface'] = pygame.transform.scale(L_SQUIR_IMG, (sq['width'], sq['height']))
|
||||
else: # squirrel is facing right
|
||||
sq['surface'] = pygame.transform.scale(R_SQUIR_IMG, (sq['width'], sq['height']))
|
||||
sq['bounce'] = 0
|
||||
sq['bouncerate'] = random.randint(10, 18)
|
||||
sq['bounceheight'] = random.randint(10, 50)
|
||||
return sq
|
||||
|
||||
|
||||
def makeNewGrass(camerax, cameray):
|
||||
gr = {}
|
||||
gr['grassImage'] = random.randint(0, len(GRASSIMAGES) - 1)
|
||||
gr['width'] = GRASSIMAGES[0].get_width()
|
||||
gr['height'] = GRASSIMAGES[0].get_height()
|
||||
gr['x'], gr['y'] = getRandomOffCameraPos(camerax, cameray, gr['width'], gr['height'])
|
||||
gr['rect'] = pygame.Rect( (gr['x'], gr['y'], gr['width'], gr['height']) )
|
||||
return gr
|
||||
|
||||
|
||||
def isOutsideActiveArea(camerax, cameray, obj):
|
||||
# Return False if camerax and cameray are more than
|
||||
# a half-window length beyond the edge of the window.
|
||||
boundsLeftEdge = camerax - WINWIDTH
|
||||
boundsTopEdge = cameray - WINHEIGHT
|
||||
boundsRect = pygame.Rect(boundsLeftEdge, boundsTopEdge, WINWIDTH * 3, WINHEIGHT * 3)
|
||||
objRect = pygame.Rect(obj['x'], obj['y'], obj['width'], obj['height'])
|
||||
return not boundsRect.colliderect(objRect)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
3492
python_games/starPusherLevels.txt
Normal file
BIN
python_games/star_solved.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
python_games/star_title.png
Normal file
After Width: | Height: | Size: 91 KiB |
598
python_games/starpusher.py
Normal file
@ -0,0 +1,598 @@
|
||||
# Star Pusher (a Sokoban clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
import random, sys, copy, os, pygame
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 30 # frames per second to update the screen
|
||||
WINWIDTH = 800 # width of the program's window, in pixels
|
||||
WINHEIGHT = 600 # height in pixels
|
||||
HALF_WINWIDTH = int(WINWIDTH / 2)
|
||||
HALF_WINHEIGHT = int(WINHEIGHT / 2)
|
||||
|
||||
# The total width and height of each tile in pixels.
|
||||
TILEWIDTH = 50
|
||||
TILEHEIGHT = 85
|
||||
TILEFLOORHEIGHT = 40
|
||||
|
||||
CAM_MOVE_SPEED = 5 # how many pixels per frame the camera moves
|
||||
|
||||
# The percentage of outdoor tiles that have additional
|
||||
# decoration on them, such as a tree or rock.
|
||||
OUTSIDE_DECORATION_PCT = 20
|
||||
|
||||
BRIGHTBLUE = ( 0, 170, 255)
|
||||
WHITE = (255, 255, 255)
|
||||
BGCOLOR = BRIGHTBLUE
|
||||
TEXTCOLOR = WHITE
|
||||
|
||||
UP = 'up'
|
||||
DOWN = 'down'
|
||||
LEFT = 'left'
|
||||
RIGHT = 'right'
|
||||
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, IMAGESDICT, TILEMAPPING, OUTSIDEDECOMAPPING, BASICFONT, PLAYERIMAGES, currentImage
|
||||
|
||||
# Pygame initialization and basic set up of the global variables.
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
|
||||
# Because the Surface object stored in DISPLAYSURF was returned
|
||||
# from the pygame.display.set_mode() function, this is the
|
||||
# Surface object that is drawn to the actual computer screen
|
||||
# when pygame.display.update() is called.
|
||||
DISPLAYSURF = pygame.display.set_mode((WINWIDTH, WINHEIGHT))
|
||||
|
||||
pygame.display.set_caption('Star Pusher')
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
|
||||
|
||||
# A global dict value that will contain all the Pygame
|
||||
# Surface objects returned by pygame.image.load().
|
||||
IMAGESDICT = {'uncovered goal': pygame.image.load('RedSelector.png'),
|
||||
'covered goal': pygame.image.load('Selector.png'),
|
||||
'star': pygame.image.load('Star.png'),
|
||||
'corner': pygame.image.load('Wall_Block_Tall.png'),
|
||||
'wall': pygame.image.load('Wood_Block_Tall.png'),
|
||||
'inside floor': pygame.image.load('Plain_Block.png'),
|
||||
'outside floor': pygame.image.load('Grass_Block.png'),
|
||||
'title': pygame.image.load('star_title.png'),
|
||||
'solved': pygame.image.load('star_solved.png'),
|
||||
'princess': pygame.image.load('princess.png'),
|
||||
'boy': pygame.image.load('boy.png'),
|
||||
'catgirl': pygame.image.load('catgirl.png'),
|
||||
'horngirl': pygame.image.load('horngirl.png'),
|
||||
'pinkgirl': pygame.image.load('pinkgirl.png'),
|
||||
'rock': pygame.image.load('Rock.png'),
|
||||
'short tree': pygame.image.load('Tree_Short.png'),
|
||||
'tall tree': pygame.image.load('Tree_Tall.png'),
|
||||
'ugly tree': pygame.image.load('Tree_Ugly.png')}
|
||||
|
||||
# These dict values are global, and map the character that appears
|
||||
# in the level file to the Surface object it represents.
|
||||
TILEMAPPING = {'x': IMAGESDICT['corner'],
|
||||
'#': IMAGESDICT['wall'],
|
||||
'o': IMAGESDICT['inside floor'],
|
||||
' ': IMAGESDICT['outside floor']}
|
||||
OUTSIDEDECOMAPPING = {'1': IMAGESDICT['rock'],
|
||||
'2': IMAGESDICT['short tree'],
|
||||
'3': IMAGESDICT['tall tree'],
|
||||
'4': IMAGESDICT['ugly tree']}
|
||||
|
||||
# PLAYERIMAGES is a list of all possible characters the player can be.
|
||||
# currentImage is the index of the player's current player image.
|
||||
currentImage = 0
|
||||
PLAYERIMAGES = [IMAGESDICT['princess'],
|
||||
IMAGESDICT['boy'],
|
||||
IMAGESDICT['catgirl'],
|
||||
IMAGESDICT['horngirl'],
|
||||
IMAGESDICT['pinkgirl']]
|
||||
|
||||
startScreen() # show the title screen until the user presses a key
|
||||
|
||||
# Read in the levels from the text file. See the readLevelsFile() for
|
||||
# details on the format of this file and how to make your own levels.
|
||||
levels = readLevelsFile('starPusherLevels.txt')
|
||||
currentLevelIndex = 0
|
||||
|
||||
# The main game loop. This loop runs a single level, when the user
|
||||
# finishes that level, the next/previous level is loaded.
|
||||
while True: # main game loop
|
||||
# Run the level to actually start playing the game:
|
||||
result = runLevel(levels, currentLevelIndex)
|
||||
|
||||
if result in ('solved', 'next'):
|
||||
# Go to the next level.
|
||||
currentLevelIndex += 1
|
||||
if currentLevelIndex >= len(levels):
|
||||
# If there are no more levels, go back to the first one.
|
||||
currentLevelIndex = 0
|
||||
elif result == 'back':
|
||||
# Go to the previous level.
|
||||
currentLevelIndex -= 1
|
||||
if currentLevelIndex < 0:
|
||||
# If there are no previous levels, go to the last one.
|
||||
currentLevelIndex = len(levels)-1
|
||||
elif result == 'reset':
|
||||
pass # Do nothing. Loop re-calls runLevel() to reset the level
|
||||
|
||||
|
||||
def runLevel(levels, levelNum):
|
||||
global currentImage
|
||||
levelObj = levels[levelNum]
|
||||
mapObj = decorateMap(levelObj['mapObj'], levelObj['startState']['player'])
|
||||
gameStateObj = copy.deepcopy(levelObj['startState'])
|
||||
mapNeedsRedraw = True # set to True to call drawMap()
|
||||
levelSurf = BASICFONT.render('Level %s of %s' % (levelNum + 1, len(levels)), 1, TEXTCOLOR)
|
||||
levelRect = levelSurf.get_rect()
|
||||
levelRect.bottomleft = (20, WINHEIGHT - 35)
|
||||
mapWidth = len(mapObj) * TILEWIDTH
|
||||
mapHeight = (len(mapObj[0]) - 1) * TILEFLOORHEIGHT + TILEHEIGHT
|
||||
MAX_CAM_X_PAN = abs(HALF_WINHEIGHT - int(mapHeight / 2)) + TILEWIDTH
|
||||
MAX_CAM_Y_PAN = abs(HALF_WINWIDTH - int(mapWidth / 2)) + TILEHEIGHT
|
||||
|
||||
levelIsComplete = False
|
||||
# Track how much the camera has moved:
|
||||
cameraOffsetX = 0
|
||||
cameraOffsetY = 0
|
||||
# Track if the keys to move the camera are being held down:
|
||||
cameraUp = False
|
||||
cameraDown = False
|
||||
cameraLeft = False
|
||||
cameraRight = False
|
||||
|
||||
while True: # main game loop
|
||||
# Reset these variables:
|
||||
playerMoveTo = None
|
||||
keyPressed = False
|
||||
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT:
|
||||
# Player clicked the "X" at the corner of the window.
|
||||
terminate()
|
||||
|
||||
elif event.type == KEYDOWN:
|
||||
# Handle key presses
|
||||
keyPressed = True
|
||||
if event.key == K_LEFT:
|
||||
playerMoveTo = LEFT
|
||||
elif event.key == K_RIGHT:
|
||||
playerMoveTo = RIGHT
|
||||
elif event.key == K_UP:
|
||||
playerMoveTo = UP
|
||||
elif event.key == K_DOWN:
|
||||
playerMoveTo = DOWN
|
||||
|
||||
# Set the camera move mode.
|
||||
elif event.key == K_a:
|
||||
cameraLeft = True
|
||||
elif event.key == K_d:
|
||||
cameraRight = True
|
||||
elif event.key == K_w:
|
||||
cameraUp = True
|
||||
elif event.key == K_s:
|
||||
cameraDown = True
|
||||
|
||||
elif event.key == K_n:
|
||||
return 'next'
|
||||
elif event.key == K_b:
|
||||
return 'back'
|
||||
|
||||
elif event.key == K_ESCAPE:
|
||||
terminate() # Esc key quits.
|
||||
elif event.key == K_BACKSPACE:
|
||||
return 'reset' # Reset the level.
|
||||
elif event.key == K_p:
|
||||
# Change the player image to the next one.
|
||||
currentImage += 1
|
||||
if currentImage >= len(PLAYERIMAGES):
|
||||
# After the last player image, use the first one.
|
||||
currentImage = 0
|
||||
mapNeedsRedraw = True
|
||||
|
||||
elif event.type == KEYUP:
|
||||
# Unset the camera move mode.
|
||||
if event.key == K_a:
|
||||
cameraLeft = False
|
||||
elif event.key == K_d:
|
||||
cameraRight = False
|
||||
elif event.key == K_w:
|
||||
cameraUp = False
|
||||
elif event.key == K_s:
|
||||
cameraDown = False
|
||||
|
||||
if playerMoveTo != None and not levelIsComplete:
|
||||
# If the player pushed a key to move, make the move
|
||||
# (if possible) and push any stars that are pushable.
|
||||
moved = makeMove(mapObj, gameStateObj, playerMoveTo)
|
||||
|
||||
if moved:
|
||||
# increment the step counter.
|
||||
gameStateObj['stepCounter'] += 1
|
||||
mapNeedsRedraw = True
|
||||
|
||||
if isLevelFinished(levelObj, gameStateObj):
|
||||
# level is solved, we should show the "Solved!" image.
|
||||
levelIsComplete = True
|
||||
keyPressed = False
|
||||
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
|
||||
if mapNeedsRedraw:
|
||||
mapSurf = drawMap(mapObj, gameStateObj, levelObj['goals'])
|
||||
mapNeedsRedraw = False
|
||||
|
||||
if cameraUp and cameraOffsetY < MAX_CAM_X_PAN:
|
||||
cameraOffsetY += CAM_MOVE_SPEED
|
||||
elif cameraDown and cameraOffsetY > -MAX_CAM_X_PAN:
|
||||
cameraOffsetY -= CAM_MOVE_SPEED
|
||||
if cameraLeft and cameraOffsetX < MAX_CAM_Y_PAN:
|
||||
cameraOffsetX += CAM_MOVE_SPEED
|
||||
elif cameraRight and cameraOffsetX > -MAX_CAM_Y_PAN:
|
||||
cameraOffsetX -= CAM_MOVE_SPEED
|
||||
|
||||
# Adjust mapSurf's Rect object based on the camera offset.
|
||||
mapSurfRect = mapSurf.get_rect()
|
||||
mapSurfRect.center = (HALF_WINWIDTH + cameraOffsetX, HALF_WINHEIGHT + cameraOffsetY)
|
||||
|
||||
# Draw mapSurf to the DISPLAYSURF Surface object.
|
||||
DISPLAYSURF.blit(mapSurf, mapSurfRect)
|
||||
|
||||
DISPLAYSURF.blit(levelSurf, levelRect)
|
||||
stepSurf = BASICFONT.render('Steps: %s' % (gameStateObj['stepCounter']), 1, TEXTCOLOR)
|
||||
stepRect = stepSurf.get_rect()
|
||||
stepRect.bottomleft = (20, WINHEIGHT - 10)
|
||||
DISPLAYSURF.blit(stepSurf, stepRect)
|
||||
|
||||
if levelIsComplete:
|
||||
# is solved, show the "Solved!" image until the player
|
||||
# has pressed a key.
|
||||
solvedRect = IMAGESDICT['solved'].get_rect()
|
||||
solvedRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
|
||||
DISPLAYSURF.blit(IMAGESDICT['solved'], solvedRect)
|
||||
|
||||
if keyPressed:
|
||||
return 'solved'
|
||||
|
||||
pygame.display.update() # draw DISPLAYSURF to the screen.
|
||||
FPSCLOCK.tick()
|
||||
|
||||
|
||||
def isWall(mapObj, x, y):
|
||||
"""Returns True if the (x, y) position on
|
||||
the map is a wall, otherwise return False."""
|
||||
if x < 0 or x >= len(mapObj) or y < 0 or y >= len(mapObj[x]):
|
||||
return False # x and y aren't actually on the map.
|
||||
elif mapObj[x][y] in ('#', 'x'):
|
||||
return True # wall is blocking
|
||||
return False
|
||||
|
||||
|
||||
def decorateMap(mapObj, startxy):
|
||||
"""Makes a copy of the given map object and modifies it.
|
||||
Here is what is done to it:
|
||||
* Walls that are corners are turned into corner pieces.
|
||||
* The outside/inside floor tile distinction is made.
|
||||
* Tree/rock decorations are randomly added to the outside tiles.
|
||||
|
||||
Returns the decorated map object."""
|
||||
|
||||
startx, starty = startxy # Syntactic sugar
|
||||
|
||||
# Copy the map object so we don't modify the original passed
|
||||
mapObjCopy = copy.deepcopy(mapObj)
|
||||
|
||||
# Remove the non-wall characters from the map data
|
||||
for x in range(len(mapObjCopy)):
|
||||
for y in range(len(mapObjCopy[0])):
|
||||
if mapObjCopy[x][y] in ('$', '.', '@', '+', '*'):
|
||||
mapObjCopy[x][y] = ' '
|
||||
|
||||
# Flood fill to determine inside/outside floor tiles.
|
||||
floodFill(mapObjCopy, startx, starty, ' ', 'o')
|
||||
|
||||
# Convert the adjoined walls into corner tiles.
|
||||
for x in range(len(mapObjCopy)):
|
||||
for y in range(len(mapObjCopy[0])):
|
||||
|
||||
if mapObjCopy[x][y] == '#':
|
||||
if (isWall(mapObjCopy, x, y-1) and isWall(mapObjCopy, x+1, y)) or \
|
||||
(isWall(mapObjCopy, x+1, y) and isWall(mapObjCopy, x, y+1)) or \
|
||||
(isWall(mapObjCopy, x, y+1) and isWall(mapObjCopy, x-1, y)) or \
|
||||
(isWall(mapObjCopy, x-1, y) and isWall(mapObjCopy, x, y-1)):
|
||||
mapObjCopy[x][y] = 'x'
|
||||
|
||||
elif mapObjCopy[x][y] == ' ' and random.randint(0, 99) < OUTSIDE_DECORATION_PCT:
|
||||
mapObjCopy[x][y] = random.choice(list(OUTSIDEDECOMAPPING.keys()))
|
||||
|
||||
return mapObjCopy
|
||||
|
||||
|
||||
def isBlocked(mapObj, gameStateObj, x, y):
|
||||
"""Returns True if the (x, y) position on the map is
|
||||
blocked by a wall or star, otherwise return False."""
|
||||
|
||||
if isWall(mapObj, x, y):
|
||||
return True
|
||||
|
||||
elif x < 0 or x >= len(mapObj) or y < 0 or y >= len(mapObj[x]):
|
||||
return True # x and y aren't actually on the map.
|
||||
|
||||
elif (x, y) in gameStateObj['stars']:
|
||||
return True # a star is blocking
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def makeMove(mapObj, gameStateObj, playerMoveTo):
|
||||
"""Given a map and game state object, see if it is possible for the
|
||||
player to make the given move. If it is, then change the player's
|
||||
position (and the position of any pushed star). If not, do nothing.
|
||||
|
||||
Returns True if the player moved, otherwise False."""
|
||||
|
||||
# Make sure the player can move in the direction they want.
|
||||
playerx, playery = gameStateObj['player']
|
||||
|
||||
# This variable is "syntactic sugar". Typing "stars" is more
|
||||
# readable than typing "gameStateObj['stars']" in our code.
|
||||
stars = gameStateObj['stars']
|
||||
|
||||
# The code for handling each of the directions is so similar aside
|
||||
# from adding or subtracting 1 to the x/y coordinates. We can
|
||||
# simplify it by using the xOffset and yOffset variables.
|
||||
if playerMoveTo == UP:
|
||||
xOffset = 0
|
||||
yOffset = -1
|
||||
elif playerMoveTo == RIGHT:
|
||||
xOffset = 1
|
||||
yOffset = 0
|
||||
elif playerMoveTo == DOWN:
|
||||
xOffset = 0
|
||||
yOffset = 1
|
||||
elif playerMoveTo == LEFT:
|
||||
xOffset = -1
|
||||
yOffset = 0
|
||||
|
||||
# See if the player can move in that direction.
|
||||
if isWall(mapObj, playerx + xOffset, playery + yOffset):
|
||||
return False
|
||||
else:
|
||||
if (playerx + xOffset, playery + yOffset) in stars:
|
||||
# There is a star in the way, see if the player can push it.
|
||||
if not isBlocked(mapObj, gameStateObj, playerx + (xOffset*2), playery + (yOffset*2)):
|
||||
# Move the star.
|
||||
ind = stars.index((playerx + xOffset, playery + yOffset))
|
||||
stars[ind] = (stars[ind][0] + xOffset, stars[ind][1] + yOffset)
|
||||
else:
|
||||
return False
|
||||
# Move the player upwards.
|
||||
gameStateObj['player'] = (playerx + xOffset, playery + yOffset)
|
||||
return True
|
||||
|
||||
|
||||
def startScreen():
|
||||
"""Display the start screen (which has the title and instructions)
|
||||
until the player presses a key. Returns None."""
|
||||
|
||||
# Position the title image.
|
||||
titleRect = IMAGESDICT['title'].get_rect()
|
||||
topCoord = 50 # topCoord tracks where to position the top of the text
|
||||
titleRect.top = topCoord
|
||||
titleRect.centerx = HALF_WINWIDTH
|
||||
topCoord += titleRect.height
|
||||
|
||||
# Unfortunately, Pygame's font & text system only shows one line at
|
||||
# a time, so we can't use strings with \n newline characters in them.
|
||||
# So we will use a list with each line in it.
|
||||
instructionText = ['Push the stars over the marks.',
|
||||
'Arrow keys to move, WASD for camera control, P to change character.',
|
||||
'Backspace to reset level, Esc to quit.',
|
||||
'N for next level, B to go back a level.']
|
||||
|
||||
# Start with drawing a blank color to the entire window:
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
|
||||
# Draw the title image to the window:
|
||||
DISPLAYSURF.blit(IMAGESDICT['title'], titleRect)
|
||||
|
||||
# Position and draw the text.
|
||||
for i in range(len(instructionText)):
|
||||
instSurf = BASICFONT.render(instructionText[i], 1, TEXTCOLOR)
|
||||
instRect = instSurf.get_rect()
|
||||
topCoord += 10 # 10 pixels will go in between each line of text.
|
||||
instRect.top = topCoord
|
||||
instRect.centerx = HALF_WINWIDTH
|
||||
topCoord += instRect.height # Adjust for the height of the line.
|
||||
DISPLAYSURF.blit(instSurf, instRect)
|
||||
|
||||
while True: # Main loop for the start screen.
|
||||
for event in pygame.event.get():
|
||||
if event.type == QUIT:
|
||||
terminate()
|
||||
elif event.type == KEYDOWN:
|
||||
if event.key == K_ESCAPE:
|
||||
terminate()
|
||||
return # user has pressed a key, so return.
|
||||
|
||||
# Display the DISPLAYSURF contents to the actual screen.
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick()
|
||||
|
||||
|
||||
def readLevelsFile(filename):
|
||||
assert os.path.exists(filename), 'Cannot find the level file: %s' % (filename)
|
||||
mapFile = open(filename, 'r')
|
||||
# Each level must end with a blank line
|
||||
content = mapFile.readlines() + ['\r\n']
|
||||
mapFile.close()
|
||||
|
||||
levels = [] # Will contain a list of level objects.
|
||||
levelNum = 0
|
||||
mapTextLines = [] # contains the lines for a single level's map.
|
||||
mapObj = [] # the map object made from the data in mapTextLines
|
||||
for lineNum in range(len(content)):
|
||||
# Process each line that was in the level file.
|
||||
line = content[lineNum].rstrip('\r\n')
|
||||
|
||||
if ';' in line:
|
||||
# Ignore the ; lines, they're comments in the level file.
|
||||
line = line[:line.find(';')]
|
||||
|
||||
if line != '':
|
||||
# This line is part of the map.
|
||||
mapTextLines.append(line)
|
||||
elif line == '' and len(mapTextLines) > 0:
|
||||
# A blank line indicates the end of a level's map in the file.
|
||||
# Convert the text in mapTextLines into a level object.
|
||||
|
||||
# Find the longest row in the map.
|
||||
maxWidth = -1
|
||||
for i in range(len(mapTextLines)):
|
||||
if len(mapTextLines[i]) > maxWidth:
|
||||
maxWidth = len(mapTextLines[i])
|
||||
# Add spaces to the ends of the shorter rows. This
|
||||
# ensures the map will be rectangular.
|
||||
for i in range(len(mapTextLines)):
|
||||
mapTextLines[i] += ' ' * (maxWidth - len(mapTextLines[i]))
|
||||
|
||||
# Convert mapTextLines to a map object.
|
||||
for x in range(len(mapTextLines[0])):
|
||||
mapObj.append([])
|
||||
for y in range(len(mapTextLines)):
|
||||
for x in range(maxWidth):
|
||||
mapObj[x].append(mapTextLines[y][x])
|
||||
|
||||
# Loop through the spaces in the map and find the @, ., and $
|
||||
# characters for the starting game state.
|
||||
startx = None # The x and y for the player's starting position
|
||||
starty = None
|
||||
goals = [] # list of (x, y) tuples for each goal.
|
||||
stars = [] # list of (x, y) for each star's starting position.
|
||||
for x in range(maxWidth):
|
||||
for y in range(len(mapObj[x])):
|
||||
if mapObj[x][y] in ('@', '+'):
|
||||
# '@' is player, '+' is player & goal
|
||||
startx = x
|
||||
starty = y
|
||||
if mapObj[x][y] in ('.', '+', '*'):
|
||||
# '.' is goal, '*' is star & goal
|
||||
goals.append((x, y))
|
||||
if mapObj[x][y] in ('$', '*'):
|
||||
# '$' is star
|
||||
stars.append((x, y))
|
||||
|
||||
# Basic level design sanity checks:
|
||||
assert startx != None and starty != None, 'Level %s (around line %s) in %s is missing a "@" or "+" to mark the start point.' % (levelNum+1, lineNum, filename)
|
||||
assert len(goals) > 0, 'Level %s (around line %s) in %s must have at least one goal.' % (levelNum+1, lineNum, filename)
|
||||
assert len(stars) >= len(goals), 'Level %s (around line %s) in %s is impossible to solve. It has %s goals but only %s stars.' % (levelNum+1, lineNum, filename, len(goals), len(stars))
|
||||
|
||||
# Create level object and starting game state object.
|
||||
gameStateObj = {'player': (startx, starty),
|
||||
'stepCounter': 0,
|
||||
'stars': stars}
|
||||
levelObj = {'width': maxWidth,
|
||||
'height': len(mapObj),
|
||||
'mapObj': mapObj,
|
||||
'goals': goals,
|
||||
'startState': gameStateObj}
|
||||
|
||||
levels.append(levelObj)
|
||||
|
||||
# Reset the variables for reading the next map.
|
||||
mapTextLines = []
|
||||
mapObj = []
|
||||
gameStateObj = {}
|
||||
levelNum += 1
|
||||
return levels
|
||||
|
||||
|
||||
def floodFill(mapObj, x, y, oldCharacter, newCharacter):
|
||||
"""Changes any values matching oldCharacter on the map object to
|
||||
newCharacter at the (x, y) position, and does the same for the
|
||||
positions to the left, right, down, and up of (x, y), recursively."""
|
||||
|
||||
# In this game, the flood fill algorithm creates the inside/outside
|
||||
# floor distinction. This is a "recursive" function.
|
||||
# For more info on the Flood Fill algorithm, see:
|
||||
# http://en.wikipedia.org/wiki/Flood_fill
|
||||
if mapObj[x][y] == oldCharacter:
|
||||
mapObj[x][y] = newCharacter
|
||||
|
||||
if x < len(mapObj) - 1 and mapObj[x+1][y] == oldCharacter:
|
||||
floodFill(mapObj, x+1, y, oldCharacter, newCharacter) # call right
|
||||
if x > 0 and mapObj[x-1][y] == oldCharacter:
|
||||
floodFill(mapObj, x-1, y, oldCharacter, newCharacter) # call left
|
||||
if y < len(mapObj[x]) - 1 and mapObj[x][y+1] == oldCharacter:
|
||||
floodFill(mapObj, x, y+1, oldCharacter, newCharacter) # call down
|
||||
if y > 0 and mapObj[x][y-1] == oldCharacter:
|
||||
floodFill(mapObj, x, y-1, oldCharacter, newCharacter) # call up
|
||||
|
||||
|
||||
def drawMap(mapObj, gameStateObj, goals):
|
||||
"""Draws the map to a Surface object, including the player and
|
||||
stars. This function does not call pygame.display.update(), nor
|
||||
does it draw the "Level" and "Steps" text in the corner."""
|
||||
|
||||
# mapSurf will be the single Surface object that the tiles are drawn
|
||||
# on, so that it is easy to position the entire map on the DISPLAYSURF
|
||||
# Surface object. First, the width and height must be calculated.
|
||||
mapSurfWidth = len(mapObj) * TILEWIDTH
|
||||
mapSurfHeight = (len(mapObj[0]) - 1) * TILEFLOORHEIGHT + TILEHEIGHT
|
||||
mapSurf = pygame.Surface((mapSurfWidth, mapSurfHeight))
|
||||
mapSurf.fill(BGCOLOR) # start with a blank color on the surface.
|
||||
|
||||
# Draw the tile sprites onto this surface.
|
||||
for x in range(len(mapObj)):
|
||||
for y in range(len(mapObj[x])):
|
||||
spaceRect = pygame.Rect((x * TILEWIDTH, y * TILEFLOORHEIGHT, TILEWIDTH, TILEHEIGHT))
|
||||
if mapObj[x][y] in TILEMAPPING:
|
||||
baseTile = TILEMAPPING[mapObj[x][y]]
|
||||
elif mapObj[x][y] in OUTSIDEDECOMAPPING:
|
||||
baseTile = TILEMAPPING[' ']
|
||||
|
||||
# First draw the base ground/wall tile.
|
||||
mapSurf.blit(baseTile, spaceRect)
|
||||
|
||||
if mapObj[x][y] in OUTSIDEDECOMAPPING:
|
||||
# Draw any tree/rock decorations that are on this tile.
|
||||
mapSurf.blit(OUTSIDEDECOMAPPING[mapObj[x][y]], spaceRect)
|
||||
elif (x, y) in gameStateObj['stars']:
|
||||
if (x, y) in goals:
|
||||
# A goal AND star are on this space, draw goal first.
|
||||
mapSurf.blit(IMAGESDICT['covered goal'], spaceRect)
|
||||
# Then draw the star sprite.
|
||||
mapSurf.blit(IMAGESDICT['star'], spaceRect)
|
||||
elif (x, y) in goals:
|
||||
# Draw a goal without a star on it.
|
||||
mapSurf.blit(IMAGESDICT['uncovered goal'], spaceRect)
|
||||
|
||||
# Last draw the player on the board.
|
||||
if (x, y) == gameStateObj['player']:
|
||||
# Note: The value "currentImage" refers
|
||||
# to a key in "PLAYERIMAGES" which has the
|
||||
# specific player image we want to show.
|
||||
mapSurf.blit(PLAYERIMAGES[currentImage], spaceRect)
|
||||
|
||||
return mapSurf
|
||||
|
||||
|
||||
def isLevelFinished(levelObj, gameStateObj):
|
||||
"""Returns True if all the goals have stars in them."""
|
||||
for goal in levelObj['goals']:
|
||||
if goal not in gameStateObj['stars']:
|
||||
# Found a space with a goal but no star on it.
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def terminate():
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
python_games/tetrisb.mid
Normal file
BIN
python_games/tetrisc.mid
Normal file
523
python_games/tetromino.py
Normal file
@ -0,0 +1,523 @@
|
||||
# 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
|
||||
|
||||
import random, time, pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
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)
|
||||
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)
|
||||
LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW)
|
||||
assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color
|
||||
|
||||
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()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
|
||||
BIGFONT = pygame.font.Font('freesansbold.ttf', 100)
|
||||
pygame.display.set_caption('Tetromino')
|
||||
|
||||
showTextScreen('Tetromino')
|
||||
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
|
||||
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
|
||||
|
||||
# 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
|
||||
# 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.', 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()
|
420
python_games/tetrominoforidiots.py
Normal file
@ -0,0 +1,420 @@
|
||||
# Tetromino for Idiots
|
||||
# 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
|
||||
|
||||
import random, time, pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
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)
|
||||
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)
|
||||
LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW)
|
||||
assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color
|
||||
|
||||
TEMPLATEWIDTH = 5
|
||||
TEMPLATEHEIGHT = 5
|
||||
|
||||
SHAPE_TEMPLATE = [['.....',
|
||||
'.....',
|
||||
'..O..',
|
||||
'.....',
|
||||
'.....']]
|
||||
|
||||
PIECES = {'A': SHAPE_TEMPLATE}
|
||||
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
|
||||
BIGFONT = pygame.font.Font('freesansbold.ttf', 60)
|
||||
pygame.display.set_caption('Tetromino for Idiots')
|
||||
|
||||
showTextScreen('Tetromino for Idiots')
|
||||
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
|
||||
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
|
||||
|
||||
# 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
|
||||
# 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) - 2, int(WINDOWHEIGHT / 2) - 2)
|
||||
DISPLAYSURF.blit(titleSurf, titleRect)
|
||||
|
||||
# Draw the additional "Press a key to play." text.
|
||||
pressKeySurf, pressKeyRect = makeTextObjs('Press a key to play.', 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()
|
229
python_games/wormy.py
Normal file
@ -0,0 +1,229 @@
|
||||
# Wormy (a Nibbles clone)
|
||||
# By Al Sweigart al@inventwithpython.com
|
||||
# http://inventwithpython.com/pygame
|
||||
# Released under a "Simplified BSD" license
|
||||
|
||||
#KRT 14/06/2012 modified Start Screen and Game Over screen to cope with mouse events
|
||||
#KRT 14/06/2012 Added a non-busy wait to Game Over screen to reduce processor loading from near 100%
|
||||
import random, pygame, sys
|
||||
from pygame.locals import *
|
||||
|
||||
FPS = 15
|
||||
WINDOWWIDTH = 640
|
||||
WINDOWHEIGHT = 480
|
||||
CELLSIZE = 20
|
||||
assert WINDOWWIDTH % CELLSIZE == 0, "Window width must be a multiple of cell size."
|
||||
assert WINDOWHEIGHT % CELLSIZE == 0, "Window height must be a multiple of cell size."
|
||||
CELLWIDTH = int(WINDOWWIDTH / CELLSIZE)
|
||||
CELLHEIGHT = int(WINDOWHEIGHT / CELLSIZE)
|
||||
|
||||
# R G B
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = ( 0, 0, 0)
|
||||
RED = (255, 0, 0)
|
||||
GREEN = ( 0, 255, 0)
|
||||
DARKGREEN = ( 0, 155, 0)
|
||||
DARKGRAY = ( 40, 40, 40)
|
||||
BGCOLOR = BLACK
|
||||
|
||||
UP = 'up'
|
||||
DOWN = 'down'
|
||||
LEFT = 'left'
|
||||
RIGHT = 'right'
|
||||
|
||||
HEAD = 0 # syntactic sugar: index of the worm's head
|
||||
|
||||
def main():
|
||||
global FPSCLOCK, DISPLAYSURF, BASICFONT
|
||||
|
||||
pygame.init()
|
||||
FPSCLOCK = pygame.time.Clock()
|
||||
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
|
||||
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
|
||||
pygame.display.set_caption('Wormy')
|
||||
|
||||
showStartScreen()
|
||||
while True:
|
||||
runGame()
|
||||
showGameOverScreen()
|
||||
|
||||
|
||||
def runGame():
|
||||
# Set a random start point.
|
||||
startx = random.randint(5, CELLWIDTH - 6)
|
||||
starty = random.randint(5, CELLHEIGHT - 6)
|
||||
wormCoords = [{'x': startx, 'y': starty},
|
||||
{'x': startx - 1, 'y': starty},
|
||||
{'x': startx - 2, 'y': starty}]
|
||||
direction = RIGHT
|
||||
|
||||
# Start the apple in a random place.
|
||||
apple = getRandomLocation()
|
||||
|
||||
while True: # main game loop
|
||||
for event in pygame.event.get(): # event handling loop
|
||||
if event.type == QUIT:
|
||||
terminate()
|
||||
elif event.type == KEYDOWN:
|
||||
if (event.key == K_LEFT or event.key == K_a) and direction != RIGHT:
|
||||
direction = LEFT
|
||||
elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT:
|
||||
direction = RIGHT
|
||||
elif (event.key == K_UP or event.key == K_w) and direction != DOWN:
|
||||
direction = UP
|
||||
elif (event.key == K_DOWN or event.key == K_s) and direction != UP:
|
||||
direction = DOWN
|
||||
elif event.key == K_ESCAPE:
|
||||
terminate()
|
||||
|
||||
# check if the worm has hit itself or the edge
|
||||
if wormCoords[HEAD]['x'] == -1 or wormCoords[HEAD]['x'] == CELLWIDTH or wormCoords[HEAD]['y'] == -1 or wormCoords[HEAD]['y'] == CELLHEIGHT:
|
||||
return # game over
|
||||
for wormBody in wormCoords[1:]:
|
||||
if wormBody['x'] == wormCoords[HEAD]['x'] and wormBody['y'] == wormCoords[HEAD]['y']:
|
||||
return # game over
|
||||
|
||||
# check if worm has eaten an apply
|
||||
if wormCoords[HEAD]['x'] == apple['x'] and wormCoords[HEAD]['y'] == apple['y']:
|
||||
# don't remove worm's tail segment
|
||||
apple = getRandomLocation() # set a new apple somewhere
|
||||
else:
|
||||
del wormCoords[-1] # remove worm's tail segment
|
||||
|
||||
# move the worm by adding a segment in the direction it is moving
|
||||
if direction == UP:
|
||||
newHead = {'x': wormCoords[HEAD]['x'], 'y': wormCoords[HEAD]['y'] - 1}
|
||||
elif direction == DOWN:
|
||||
newHead = {'x': wormCoords[HEAD]['x'], 'y': wormCoords[HEAD]['y'] + 1}
|
||||
elif direction == LEFT:
|
||||
newHead = {'x': wormCoords[HEAD]['x'] - 1, 'y': wormCoords[HEAD]['y']}
|
||||
elif direction == RIGHT:
|
||||
newHead = {'x': wormCoords[HEAD]['x'] + 1, 'y': wormCoords[HEAD]['y']}
|
||||
wormCoords.insert(0, newHead)
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
drawGrid()
|
||||
drawWorm(wormCoords)
|
||||
drawApple(apple)
|
||||
drawScore(len(wormCoords) - 3)
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
|
||||
def drawPressKeyMsg():
|
||||
pressKeySurf = BASICFONT.render('Press a key to play.', True, DARKGRAY)
|
||||
pressKeyRect = pressKeySurf.get_rect()
|
||||
pressKeyRect.topleft = (WINDOWWIDTH - 200, WINDOWHEIGHT - 30)
|
||||
DISPLAYSURF.blit(pressKeySurf, pressKeyRect)
|
||||
|
||||
|
||||
|
||||
# KRT 14/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
|
||||
# no quit or key events in queue so return None
|
||||
return None
|
||||
|
||||
|
||||
def showStartScreen():
|
||||
titleFont = pygame.font.Font('freesansbold.ttf', 100)
|
||||
titleSurf1 = titleFont.render('Wormy!', True, WHITE, DARKGREEN)
|
||||
titleSurf2 = titleFont.render('Wormy!', True, GREEN)
|
||||
|
||||
degrees1 = 0
|
||||
degrees2 = 0
|
||||
|
||||
#KRT 14/06/2012 rewrite event detection to deal with mouse use
|
||||
pygame.event.get() #clear out event queue
|
||||
|
||||
while True:
|
||||
DISPLAYSURF.fill(BGCOLOR)
|
||||
rotatedSurf1 = pygame.transform.rotate(titleSurf1, degrees1)
|
||||
rotatedRect1 = rotatedSurf1.get_rect()
|
||||
rotatedRect1.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2)
|
||||
DISPLAYSURF.blit(rotatedSurf1, rotatedRect1)
|
||||
|
||||
rotatedSurf2 = pygame.transform.rotate(titleSurf2, degrees2)
|
||||
rotatedRect2 = rotatedSurf2.get_rect()
|
||||
rotatedRect2.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2)
|
||||
DISPLAYSURF.blit(rotatedSurf2, rotatedRect2)
|
||||
|
||||
drawPressKeyMsg()
|
||||
#KRT 14/06/2012 rewrite event detection to deal with mouse use
|
||||
if checkForKeyPress():
|
||||
return
|
||||
pygame.display.update()
|
||||
FPSCLOCK.tick(FPS)
|
||||
degrees1 += 3 # rotate by 3 degrees each frame
|
||||
degrees2 += 7 # rotate by 7 degrees each frame
|
||||
|
||||
|
||||
def terminate():
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
def getRandomLocation():
|
||||
return {'x': random.randint(0, CELLWIDTH - 1), 'y': random.randint(0, CELLHEIGHT - 1)}
|
||||
|
||||
|
||||
def showGameOverScreen():
|
||||
gameOverFont = pygame.font.Font('freesansbold.ttf', 150)
|
||||
gameSurf = gameOverFont.render('Game', True, WHITE)
|
||||
overSurf = gameOverFont.render('Over', True, WHITE)
|
||||
gameRect = gameSurf.get_rect()
|
||||
overRect = overSurf.get_rect()
|
||||
gameRect.midtop = (WINDOWWIDTH / 2, 10)
|
||||
overRect.midtop = (WINDOWWIDTH / 2, gameRect.height + 10 + 25)
|
||||
|
||||
DISPLAYSURF.blit(gameSurf, gameRect)
|
||||
DISPLAYSURF.blit(overSurf, overRect)
|
||||
drawPressKeyMsg()
|
||||
pygame.display.update()
|
||||
pygame.time.wait(500)
|
||||
#KRT 14/06/2012 rewrite event detection to deal with mouse use
|
||||
pygame.event.get() #clear out event queue
|
||||
while True:
|
||||
if checkForKeyPress():
|
||||
return
|
||||
#KRT 12/06/2012 reduce processor loading in gameover screen.
|
||||
pygame.time.wait(100)
|
||||
|
||||
def drawScore(score):
|
||||
scoreSurf = BASICFONT.render('Score: %s' % (score), True, WHITE)
|
||||
scoreRect = scoreSurf.get_rect()
|
||||
scoreRect.topleft = (WINDOWWIDTH - 120, 10)
|
||||
DISPLAYSURF.blit(scoreSurf, scoreRect)
|
||||
|
||||
|
||||
def drawWorm(wormCoords):
|
||||
for coord in wormCoords:
|
||||
x = coord['x'] * CELLSIZE
|
||||
y = coord['y'] * CELLSIZE
|
||||
wormSegmentRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
|
||||
pygame.draw.rect(DISPLAYSURF, DARKGREEN, wormSegmentRect)
|
||||
wormInnerSegmentRect = pygame.Rect(x + 4, y + 4, CELLSIZE - 8, CELLSIZE - 8)
|
||||
pygame.draw.rect(DISPLAYSURF, GREEN, wormInnerSegmentRect)
|
||||
|
||||
|
||||
def drawApple(coord):
|
||||
x = coord['x'] * CELLSIZE
|
||||
y = coord['y'] * CELLSIZE
|
||||
appleRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
|
||||
pygame.draw.rect(DISPLAYSURF, RED, appleRect)
|
||||
|
||||
|
||||
def drawGrid():
|
||||
for x in range(0, WINDOWWIDTH, CELLSIZE): # draw vertical lines
|
||||
pygame.draw.line(DISPLAYSURF, DARKGRAY, (x, 0), (x, WINDOWHEIGHT))
|
||||
for y in range(0, WINDOWHEIGHT, CELLSIZE): # draw horizontal lines
|
||||
pygame.draw.line(DISPLAYSURF, DARKGRAY, (0, y), (WINDOWWIDTH, y))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|