离线
classes.py
#########################################
# File name: Classes.py #
# Author: David Gurevich #
# Course: ICS3U #
# Instructor: D. Mavrodin #
# --------------------------------------#
# Last Modified: 11/12/2017 @ 20:52 #
#########################################
import pygame
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
ORANGE = (255, 127, 0)
CYAN = (0, 183, 235)
MAGENTA = (255, 0, 255)
YELLOW = (255, 255, 0)
WHITE = (255, 255, 255)
TRANS_WHITE = (255, 255, 255, 50)
COLOURS = [BLACK, RED, GREEN, BLUE, ORANGE, CYAN, MAGENTA, YELLOW, WHITE]
CLR_names = ['black', 'red', 'green', 'blue', 'orange', 'cyan', 'magenta', 'yellow', 'white']
figures = [None, 'Z', 'S', 'J', 'L', 'I', 'T', 'O', None]
class Block(object):
""" A square - basic building block
data: behaviour:
col - column move left/right/up/down
row - row drawself.blocks
clr - colour
"""
def __init__(self, col=1, row=1, clr=1):
self.col = col
self.row = row
self.clr = clr
def __str__(self):
return '(' + str(self.col) + ',' + str(self.row) + ') ' + CLR_names[self.clr]
def __eq__(self, other):
if self.col == other.col and self.row == other.row:
return True
def draw(self, surface, gridsize=20, shadow=False):
x = self.col * gridsize
y = self.row * gridsize
CLR = COLOURS[self.clr]
if not shadow:
pygame.draw.rect(surface, CLR, (x, y, gridsize - 2, gridsize - 2), 0)
pygame.draw.rect(surface, WHITE, (x, y, gridsize, gridsize), 2)
else:
pygame.draw.rect(surface, TRANS_WHITE, (x, y, gridsize, gridsize), 3)
def move_down(self):
self.row = self.row + 1
# --------------------------------------- #
class Cluster(object):
""" Collection of blocks
data:
col - column where the anchor block is located
row - row where the anchor block is located
blocksNo - number of blocks
"""
def __init__(self, col=1, row=1, blocksNo=1):
self.col = col
self.row = row
self.clr = 0
self.blocks = [Block()] * blocksNo
self._colOffsets = [0] * blocksNo
self._rowOffsets = [0] * blocksNo
def _update(self):
for i in range(len(self.blocks)):
blockCOL = self.col + self._colOffsets[i]
blockROW = self.row + self._rowOffsets[i]
blockCLR = self.clr
self.blocks[i] = Block(blockCOL, blockROW, blockCLR)
def draw(self, surface, gridsize, shadow=False):
for block in self.blocks:
block.draw(surface, gridsize, shadow)
def collides(self, other):
""" Compare each block from a cluster to all blocks from another cluster.
Return True only if there is a location conflict.
"""
for block in self.blocks:
for obstacle in other.blocks:
if block == obstacle:
return True
return False
def append(self, other):
""" Append all blocks from another cluster to this one.
"""
for block in other.blocks:
self.blocks.append(block)
# -------------------------------------- #
class Obstacles(Cluster):
""" Collection of tetrominoe blocks on the playing field, left from previous shapes.
"""
def __init__(self, col=0, row=0, blocksNo=0):
Cluster.__init__(self, col, row, blocksNo)
def show(self):
print("\nObstacle: ")
for block in self.blocks:
print((block)._colOffsets)
def findFullRows(self, top, bottom, columns):
fullRows = []
rows = []
for block in self.blocks:
rows.append(block.row)
for row in range(top, bottom):
if rows.count(row) == columns:
fullRows.append(row)
return fullRows
def removeFullRows(self, fullRows):
for row in fullRows:
for i in reversed(range(len(self.blocks))):
if self.blocks[i].row == row:
self.blocks.pop(i)
elif self.blocks[i].row < row:
self.blocks[i].move_down()
# ---------------------------------------#
class Shape(Cluster):
""" A tetrominoe in one of the shapes: Z,S,J,L,I,T,O; consists of 4 x Block() objects
data: behaviour:
col - column move left/right/up/down
row - row draw
clr - colour rotate
* figure/shape is defined by the colour
rot - rotation
"""
def __init__(self, col=1, row=1, clr=1, rot=1, shadow=False):
Cluster.__init__(self, col, row, 4)
self.clr = clr
self.shadow = shadow
self._rot = rot
self._colOffsets = [-1, 0, 0, 1]
self._rowOffsets = [-1, -1, 0, 0]
self._rotate()
def __str__(self):
return figures[self.clr] + ' (' + str(self.col) + ',' + str(self.row) + ') ' + CLR_names[self.clr]
def _rotate(self):
""" offsets are assigned starting from the farthest (most distant) block in reference to the anchor block """
if self.clr == 1:
_colOffsets = [[-1, -1, 0, 0], [-1, 0, 0, 1], [1, 1, 0, 0], [1, 0, 0, -1]]
_rowOffsets = [[1, 0, 0, -1], [-1, -1, 0, 0], [-1, 0, 0, 1], [1, 1, 0, 0]]
elif self.clr == 2:
_colOffsets = [[-1, -1, 0, 0], [1, 0, 0, -1], [1, 1, 0, 0], [-1, 0, 0, 1]]
_rowOffsets = [[-1, 0, 0, 1], [-1, -1, 0, 0], [1, 0, 0, -1], [1, 1, 0, 0]]
elif self.clr == 3:
_colOffsets = [[-1, 0, 0, 0], [-1, -1, 0, 1], [1, 0, 0, 0], [1, 1, 0, -1]]
_rowOffsets = [[1, 1, 0, -1], [-1, 0, 0, 0], [-1, -1, 0, 1], [1, 0, 0, 0]]
elif self.clr == 4:
_colOffsets = [[-1, 0, 0, 0], [1, 1, 0, -1], [1, 0, 0, 0], [-1, -1, 0, 1]]
_rowOffsets = [[-1, -1, 0, 1], [-1, 0, 0, 0], [1, 1, 0, -1], [1, 0, 0, 0]]
elif self.clr == 5:
_colOffsets = [[0, 0, 0, 0], [2, 1, 0, -1], [0, 0, 0, 0], [-2, -1, 0, 1]]
_rowOffsets = [[-2, -1, 0, 1], [0, 0, 0, 0], [2, 1, 0, -1], [0, 0, 0, 0]]
elif self.clr == 6:
_colOffsets = [[0, -1, 0, 0], [-1, 0, 0, 1], [0, 1, 0, 0], [1, 0, 0, -1]] #
_rowOffsets = [[1, 0, 0, -1], [0, -1, 0, 0], [-1, 0, 0, 1], [0, 1, 0, 0]] #
elif self.clr == 7:
_colOffsets = [[-1, -1, 0, 0], [-1, -1, 0, 0], [-1, -1, 0, 0], [-1, -1, 0, 0]]
_rowOffsets = [[0, -1, 0, -1], [0, -1, 0, -1], [0, -1, 0, -1], [0, -1, 0, -1]]
self._colOffsets = _colOffsets[self._rot]
self._rowOffsets = _rowOffsets[self._rot]
self._update()
def move_left(self):
self.col = self.col - 1
self._update()
def move_right(self):
self.col = self.col + 1
self._update()
def move_down(self):
self.row = self.row + 1
self._update()
def move_up(self):
self.row = self.row - 1
self._update()
def rotateClkwise(self):
self._rot = (self._rot + 1) % 4
def rotateCntclkwise(self):
self._rot = (self._rot - 1) % 4
# --------------------------------------- #
class Floor(Cluster):
""" Horizontal line of blocks
data:
col - column where the anchor block is located
row - row where the anchor block is located
blocksNo - number of blocks
"""
def __init__(self, col=1, row=1, blocksNo=1):
Cluster.__init__(self, col, row, blocksNo)
for i in range(blocksNo):
self._colOffsets[i] = i
self._update()
# --------------------------------------- #
class Wall(Cluster):
""" Vertical line of blocks
data:
col - column where the anchor block is located
row - row where the anchor block is located
blocksNo - number of blocks
"""
def __init__(self, col=1, row=1, blocksNo=1):
Cluster.__init__(self, col, row, blocksNo)
for i in range(blocksNo):
self._rowOffsets[i] = i
self._update()
Tetris.py
#########################################
# File name: Tetris.py #
# Author: David Gurevich #
# Course: ICS3U #
# Instructor: D. Mavrodin #
# --------------------------------------#
# Last Modified: 11/12/2017 @ 21:02 #
#########################################
import sys
from random import randint, choice
from Classes import *
pygame.init()
HEIGHT = 600
WIDTH = 575
GRIDSIZE = HEIGHT // 24
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Tetris - David Gurevich")
LVL_1, LVL_2, LVL_3, LVL_4, LVL_5, LVL_6, LVL_7, LVL_8, LVL_9 = 45, 20, 10, 7, 5, 4, 3, 2, 1
LEVELS = [LVL_1, LVL_2, LVL_3, LVL_4, LVL_5, LVL_6, LVL_7, LVL_8, LVL_9]
SCORE = 0
# ---------------------------------------#
COLUMNS = 14
ROWS = 24
LEFT = 0
RIGHT = LEFT + COLUMNS
MIDDLE = LEFT + COLUMNS // 2
TOP = 1
FLOOR = TOP + ROWS
# -------------IMAGES and MUSIC--------------------#
pygame.mixer.set_num_channels(6)
# Channel 0: Background Music
# Channel 1: Block Rotation
# Channel 2: Force Hit
# Channel 3: Line Remove
# Channel 4: Slow Hit
# Channel 5: Tetris Remove
# ---- BACKGROUND IMAGES ---- #
tetris_img = pygame.image.load('images/Tetris.jpg')
grid_img = pygame.image.load('images/gridbg.jpg')
intro_screen = pygame.image.load('images/Intro.jpg')
outro_screen = pygame.image.load('images/Outro.jpg')
# --------------------------- #
# ---- SOUND EFFECTS ---- #
block_rotate = pygame.mixer.Sound('Sounds/block-rotate.ogg')
force_hit = pygame.mixer.Sound('Sounds/force-hit.ogg')
line_remove = pygame.mixer.Sound('Sounds/line-remove.ogg')
slow_hit = pygame.mixer.Sound('Sounds/slow-hit.ogg')
tetris_remove = pygame.mixer.Sound('Sounds/tetris-remove.ogg')
# ----------------------- #
# ---- BACKGROUND MUSIC ---- #
kalinka = pygame.mixer.Sound('Music/kalinka.ogg')
katyusha = pygame.mixer.Sound('Music/katyusha.ogg')
korobushka = pygame.mixer.Sound('Music/korobushka.ogg')
smuglianka = pygame.mixer.Sound('Music/smuglianka.ogg')
bg_music = choice([kalinka, katyusha, korobushka, smuglianka])
# -------------------------- #
# ---- BLOCK PREVIEWS ---- #
cube_block = pygame.image.load('Previews/cube-block.png').convert_alpha()
i_block = pygame.image.load('Previews/i-block.png').convert_alpha()
j_block = pygame.image.load('Previews/j-block.png').convert_alpha()
L_block = pygame.image.load('Previews/L-block.png').convert_alpha()
r_s_block = pygame.image.load('Previews/r-s-block.png').convert_alpha()
s_block = pygame.image.load('Previews/s-block.png').convert_alpha()
t_block = pygame.image.load('Previews/t-block.png').convert_alpha()
block_img_lst = [r_s_block, s_block, L_block, j_block, i_block, t_block, cube_block] # MUST MATCH LIST IN CLASSES.PY
# ------------------------ #
# ---- FAVICON ---- #
favicon = pygame.image.load('images/favicon.png').convert_alpha()
pygame.display.set_icon(favicon)
# ----------------- #
# ---- FONTS ---- #
pygame.font.init()
my_font = pygame.font.SysFont('Arial Black', 21)
# --------------- #
# ------------- FUNCTIONS -------------------- #
def draw_grid():
""" Draw horisontal and vertical lines on the entire game window.
Space between the lines is GRIDSIZE.
"""
for i in range(15):
pygame.draw.line(screen, BLACK, (i * GRIDSIZE, 0), (i * GRIDSIZE, HEIGHT), 1)
for i in range(24):
pygame.draw.line(screen, BLACK, (0, i * GRIDSIZE), (GRIDSIZE * 24, i * GRIDSIZE), 1)
def redraw_screen():
score_text = my_font.render(str(SCORE), True, WHITE)
timer_text = my_font.render(str(round(pygame.time.get_ticks() / 1000, 2)), True, WHITE)
level_text = my_font.render(str(level + 1), True, WHITE)
screen.blit(grid_img, (0, 0))
draw_grid()
screen.blit(tetris_img, (GRIDSIZE * 14, 0))
shape.draw(screen, GRIDSIZE)
shadow.draw(screen, GRIDSIZE, True)
obstacles.draw(screen, GRIDSIZE)
# BLIT FONTS
screen.blit(score_text, ((GRIDSIZE * 14) + 90, 460))
screen.blit(timer_text, ((GRIDSIZE * 14) + 85, 538))
screen.blit(level_text, ((GRIDSIZE * 14) + 100, 380))
# BLIT NEXT SHAPE
screen.blit(block_img_lst[nextShapeNo - 1], ((GRIDSIZE * 14) + 72, 240))
pygame.display.flip()
def drop(my_shape):
flow = False
while not flow:
my_shape.move_down()
if my_shape.collides(floor) or my_shape.collides(obstacles):
my_shape.move_up()
flow = True
if not my_shape.shadow:
pygame.mixer.Channel(2).play(force_hit)
# -------------------------------------------- #
# ------------- MAIN PROGRAM -------------------- #
counter = 0
shapeNo = randint(1, 7)
nextShapeNo = randint(1, 7)
shape = Shape(MIDDLE, TOP, shapeNo)
floor = Floor(LEFT, ROWS, COLUMNS)
leftWall = Wall(LEFT - 1, 0, ROWS)
rightWall = Wall(RIGHT, 0, ROWS)
obstacles = Obstacles(LEFT, FLOOR)
inPlay = False
hasPlayed = False
level = 0
PREV_TETRIS = False
pygame.mixer.Channel(0).play(bg_music, -1)
# ---- INTRO SCREEN ---- #
while not inPlay and not hasPlayed:
screen.blit(intro_screen, (0, 0))
pygame.display.flip()
screen.blit(intro_screen, (0, 0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
inPlay = True
hasPlayed = True
# ---------------------- #
while inPlay:
shadow = Shape(shape.col, shape.row, shape.clr, shape._rot, True)
drop(shadow)
if counter % LEVELS[level] == 0:
shape.move_down()
if shape.collides(floor) or shape.collides(obstacles):
shape.move_up()
obstacles.append(shape)
pygame.mixer.Channel(5).play(slow_hit)
fullRows = obstacles.findFullRows(TOP, FLOOR, COLUMNS)
# --------- CHECK --------- #
if 4 > len(fullRows) > 0:
SCORE += 100 * len(fullRows)
pygame.mixer.Channel(3).play(line_remove)
elif len(fullRows) >= 4:
SCORE += 800 + (100 * (len(fullRows) - 4))
pygame.mixer.Channel(4).play(tetris_remove)
PREV_TETRIS = True
elif len(fullRows) >= 4 and PREV_TETRIS:
SCORE += 1200 + (100 * (len(fullRows) - 4))
PREV_TETRIS = True
pygame.mixer.Channel(4).play(tetris_remove)
# ------------------------ #
obstacles.removeFullRows(fullRows)
shapeNo = nextShapeNo
nextShapeNo = randint(1, 7)
if not shape.row <= 1:
shape = Shape(MIDDLE, TOP, shapeNo)
else:
inPlay = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
inPlay = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
shape.rotateClkwise()
shape._rotate()
if shape.collides(leftWall) or shape.collides(rightWall) or shape.collides(floor) or shape.collides(
obstacles):
shape.rotateCntclkwise()
shape._rotate()
else:
pygame.mixer.Channel(1).play(block_rotate)
if event.key == pygame.K_LEFT:
shape.move_left()
if shape.collides(leftWall):
shape.move_right()
elif shape.collides(obstacles):
shape.move_right()
if event.key == pygame.K_RIGHT:
shape.move_right()
if shape.collides(rightWall):
shape.move_left()
elif shape.collides(obstacles):
shape.move_left()
if event.key == pygame.K_DOWN:
shape.move_down()
if shape.collides(floor) or shape.collides(obstacles):
shape.move_up()
obstacles.append(shape)
fullRows = obstacles.findFullRows(TOP, FLOOR, COLUMNS)
# --------- CHECK --------- #
if 4 > len(fullRows) > 0:
SCORE += 100 * len(fullRows)
pygame.mixer.Channel(3).play(line_remove)
elif len(fullRows) >= 4:
SCORE += 800 + (100 * (len(fullRows) - 4))
pygame.mixer.Channel(4).play(tetris_remove)
PREV_TETRIS = True
elif len(fullRows) >= 4 and PREV_TETRIS:
SCORE += 1200 + (100 * (len(fullRows) - 4))
PREV_TETRIS = True
pygame.mixer.Channel(4).play(tetris_remove)
# ------------------------- #
obstacles.removeFullRows(fullRows)
shapeNo = nextShapeNo
nextShapeNo = randint(1, 7)
shape = Shape(MIDDLE, TOP, shapeNo)
shape = Shape(MIDDLE, TOP, shapeNo)
if event.key == pygame.K_SPACE:
drop(shape)
obstacles.append(shape)
shapeNo = nextShapeNo
nextShapeNo = randint(1, 7)
shape = Shape(MIDDLE, TOP, shapeNo)
fullRows = obstacles.findFullRows(TOP, FLOOR, COLUMNS)
# --------- CHECK --------- #
if 4 > len(fullRows) > 0:
SCORE += 100 * len(fullRows)
pygame.mixer.Channel(3).play(line_remove)
elif len(fullRows) >= 4:
SCORE += 800 + (100 * (len(fullRows) - 4))
pygame.mixer.Channel(4).play(tetris_remove)
PREV_TETRIS = True
elif len(fullRows) >= 4 and PREV_TETRIS:
SCORE += 1200 + (100 * (len(fullRows) - 4))
PREV_TETRIS = True
pygame.mixer.Channel(4).play(tetris_remove)
# ------------------------- #
obstacles.removeFullRows(fullRows)
if 1000 >= SCORE >= 500:
level = 1
elif 1500 >= SCORE > 1000:
level = 2
elif 2000 >= SCORE > 1500:
level = 3
elif 2250 >= SCORE > 2000:
level = 4
elif 2500 >= SCORE > 2250:
level = 5
elif 2750 >= SCORE > 2500:
level = 6
elif 3000 >= SCORE > 2750:
level = 7
elif 3250 >= SCORE > 3000:
level = 8
elif SCORE >= 3250:
level = 9
PREV_TETRIS = False
counter += 1
redraw_screen()
while not inPlay and hasPlayed:
start_timer = pygame.time.get_ticks()
screen.blit(outro_screen, (0, 0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
pygame.quit()
sys.exit(0)
if pygame.time.get_ticks() - start_timer >= 2000:
pygame.quit()
sys.exit(0)
# ----------------------------------------------- #
pygame.quit()
sys.exit("Exited Final")
离线
打开工程
离线
右击运行.
离线
玩一把游戏.
离线
上面的 python 代码编辑器是 pycharm,
大名鼎鼎的 捷克 jetBrains 公司出品的.
离线
都忘记这个帖子是我自己发的了。
离线