Commit 8fb99d7b authored by Dimitris Strovolidis's avatar Dimitris Strovolidis

Implemented remaining functions

Add backgrounds
Add game grid
Add collision
Add README
parent aff535af
Dimitris Strovolidis [email protected]
Requirements
Python 2.7.x or 3.5.x
PyGame 1.9.x
Run
python tetris.py
How to play
Move Left Left Arrow
Move Right Right Arrow
Rotate Clockwise Up Arrow
Rotate Counterclockwise Down Arrow
Speed Up Space
Exit ESC
Font Creator
codeman38 | [email protected] | http://www.zone38.net/
import os
import pygame
class GameArea:
def __init__(self, columns, rows, block_dimensions, screen_res):
self.score = 0
self.game_running = True
self.block_width = block_dimensions[0]
self.block_height = block_dimensions[1]
self.area_width = columns * self.block_width
self.area_height = rows * self.block_height
# top left corner of game area
self.min_coord = ((screen_res[0] - self.area_width) / 2,
(screen_res[1] - self.area_height) / 2)
# bottom right corner of game area
self.max_coord = (self.min_coord[0] + self.area_width,
self.min_coord[1] + self.area_height)
# center coord of game area
self.center_coord = (self.min_coord[0] + (columns * self.block_width) // 2,
self.min_coord[1])
self.columns = columns
self.rows = rows
# rows x columns grid
self.grid = [[-1 for i in range(self.columns)] for j in range(self.rows)]
self.background = pygame.image.load(os.path.join('sprites', 'grid_background.png'))
self.background = pygame.transform.scale(self.background,
(self.area_width, self.area_height))
def collides(self, tetromino_coords):
"""
Detects collision between blocks.
:param tetromino_coords: list of coordinates (x, y)
:return: True if blocks collide
"""
rect_list = []
for coord in tetromino_coords:
rect_list.append(pygame.Rect(coord, (self.block_width, self.block_height)))
for i in range(self.rows):
for j in range(self.columns):
if self.grid[i][j] >= 0:
coord = (self.min_coord[0] + j * self.block_width,
self.min_coord[1] + i * self.block_height)
rect = pygame.Rect(coord, (self.block_width, self.block_height))
if not rect.collidelist(rect_list) < 0:
return True
return False
def get_game_status(self):
return self.game_running
def get_grid_indexes(self, coords):
"""
Convert coordinates to grid indexes.
:param coords: list of coordinates (x, y)
:return: list of indexes (row, column)
"""
grid_index_list = []
for coord in coords:
column_index = int((coord[0] - self.min_coord[0]) // self.block_width)
row_index = int((coord[1] - self.min_coord[1]) // self.block_height)
grid_index_list.append((row_index, column_index))
return grid_index_list
def update_grid(self, coords, color_index):
"""
Converts coordinates to grid indexes using get_grid_indexes
and assigns color_index to the corresponding cells.
:param coords: list of coordinates (x, y)
"""
indexes_list = self.get_grid_indexes(coords)
for row_index, column_index in indexes_list:
self.grid[row_index][column_index] = color_index
# search for full rows
for row_index, column_index in indexes_list:
# check game over
if row_index == 0: # there is a block at the first row
self.game_running = False
full_row = True
for j in range(self.columns):
if self.grid[row_index][j] == -1: # cell is empty
full_row = False
break
# delete the row if it is full
if full_row:
del self.grid[row_index]
self.score += 1
# insert a new line at the beginning of the grid
self.grid.insert(0, [-1 for i in range(self.columns)])
def show(self, screen, color_blocks):
"""
Blit background and all blocks.
:param screen: screen surface
:param color_blocks: block sprites tuple
"""
screen.blit(self.background, self.min_coord)
for i in range(self.rows):
for j in range(self.columns):
if self.grid[i][j] >= 0: # if cell isn't empty (empty -> -1)
# cell value is color index
color_index = self.grid[i][j]
coord_x = self.min_coord[0] + j * self.block_width
coord_y = self.min_coord[1] + i * self.block_height
screen.blit(color_blocks[color_index], (coord_x, coord_y))
def get_score(self):
return self.score
def get_min_coord(self):
return self.min_coord
def get_max_coord(self):
return self.max_coord
def get_center_coord(self):
return self.center_coord
\ No newline at end of file
import os
import pygame
from tetromino import Tetromino
from game_area import GameArea
def write(font, message, color):
text = font.render(str(message), True, color)
......@@ -8,8 +9,11 @@ def write(font, message, color):
return text
def center_text(screen, surface):
return (screen.get_width() - surface.get_width()) / 2
def main():
black_color = (0, 0, 0)
text_color = (255, 255, 255)
# screen variables
min_screen_coord = (0, 0)
......@@ -20,31 +24,15 @@ def main():
block_width, block_height = 32, 32
columns = 10
rows = 20
# (width, height) of game area
game_area = (columns * block_width, rows * block_height)
# top left corner of game area
game_min_coord = ((screen_res[0] - game_area[0]) / 2, (screen_res[1] -
game_area[1]) / 2)
# bottom right corner of game area
game_max_coord = (game_min_coord[0] + game_area[0], game_min_coord[1] + game_area[1])
game_center_coord = (game_min_coord[0] + (columns * block_width) // 2,
game_min_coord[1])
pygame.init()
pygame.mixer.init()
pygame.display.set_caption("Tetris")
screen = pygame.display.set_mode(screen_res)
# create the black background
background = pygame.Surface(screen.get_size())
background.fill(black_color)
background = background.convert()
# create game area
game_area_color = (20, 20, 20)
game_surface = pygame.Surface(game_area)
game_surface.fill(game_area_color)
game_surface = game_surface.convert()
# load background
background = pygame.image.load(os.path.join('sprites', 'background.png'))
background = pygame.transform.scale(background, screen.get_size())
# load font
font = pygame.font.Font(os.path.join('fonts', 'prstartk.ttf'), 18)
......@@ -68,16 +56,23 @@ def main():
color_blocks = (lightblue_block, yellow_block, purple_block, blue_block, orange_block,
green_block, red_block)
random_tetromino = Tetromino((block_width, block_height), color_blocks, game_center_coord,
game_min_coord, game_max_coord)
# create game area
game_area = GameArea(columns, rows, (block_width, block_height), screen_res)
# create a random tetromino
random_tetromino = Tetromino((block_width, block_height), color_blocks,
game_area.get_center_coord(), game_area.get_min_coord(),
game_area.get_max_coord())
FPS = 60
game_running = True
clock = pygame.time.Clock()
total_time = 0
while game_running:
time = clock.tick(FPS) / 100.0
total_time += time / 10.0
game_running = game_area.get_game_status()
# check keyboard input
for event in pygame.event.get():
if event.type == pygame.QUIT:
......@@ -86,21 +81,63 @@ def main():
game_running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT:
random_tetromino.move_right()
# if tetromino collides undo move right
if game_area.collides(random_tetromino.get_coords()):
random_tetromino.move_left()
elif event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT:
random_tetromino.move_left()
# if tetromino collides undo move left
if game_area.collides(random_tetromino.get_coords()):
random_tetromino.move_right()
elif event.type == pygame.KEYDOWN and event.key == pygame.K_UP:
random_tetromino.rotate_cw()
# if tetromino collides undo rotate clockwise
if game_area.collides(random_tetromino.get_coords()):
random_tetromino.rotate_ccw()
elif event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN:
random_tetromino.rotate_ccw()
# if tetromino collides undo rotate counterclockwise
if game_area.collides(random_tetromino.get_coords()):
random_tetromino.rotate_cw()
elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
# increase tetromino speed
random_tetromino.speed_up()
elif event.type == pygame.KEYUP and event.key == pygame.K_SPACE:
# reset tetromino speed (normal speed)
random_tetromino.reset_speed()
time_string = "Time " + '{0:02d}'.format(int(total_time // 60))\
+ ":" + '{0:02d}'.format(int(total_time % 60))
score_string = "Score: " + str(game_area.get_score())
score_surface = write(font, score_string, text_color)
time_surface = write(font, time_string, text_color)
screen.blit(background, min_screen_coord)
screen.blit(game_surface, game_min_coord)
screen.blit(score_surface, (15, 100))
screen.blit(time_surface, (15, 150))
game_area.show(screen, color_blocks)
random_tetromino.show(screen)
pygame.display.flip()
random_tetromino.move_down(time)
bottom = random_tetromino.move_down(time)
collision = game_area.collides(random_tetromino.get_coords())
# tetromino collided with a block or reached bottom
if collision or bottom:
game_area.update_grid(random_tetromino.get_coords(), random_tetromino.get_color_index())
random_tetromino = Tetromino((block_width, block_height), color_blocks,
game_area.get_center_coord(), game_area.get_min_coord(),
game_area.get_max_coord())
text_surface = write(font, "Game Over", text_color)
screen.blit(text_surface, (center_text(screen, text_surface),
max_screen_coord[1] / 2))
pygame.display.flip()
clock.tick(0.5)
pygame.quit()
if __name__ == '__main__':
main()
......@@ -140,15 +140,16 @@ class Tetromino:
tetrominoes = (i_tetromino, o_tetromino, t_tetromino, j_tetromino,
l_tetromino, s_tetromino, z_tetromino)
speed = 5
normal_speed = 12
fast_speed = 100
def __init__(self, block_dimensions, color_blocks, coord, min_coord: tuple, max_coord: tuple):
def __init__(self, block_dimensions, color_blocks, coord, min_coord, max_coord):
"""
Create a random tetromino.
"""
random_index = randint(0, len(Tetromino.tetrominoes) - 1)
self.random_tetromino = Tetromino.tetrominoes[random_index]
self.block_color = color_blocks[random_index]
self.random_index = randint(0, len(Tetromino.tetrominoes) - 1)
self.random_tetromino = Tetromino.tetrominoes[self.random_index]
self.block_color = color_blocks[self.random_index]
# select a random frame
self.current_angle = randint(0, 3)
......@@ -157,6 +158,8 @@ class Tetromino:
self.block_width = block_dimensions[0]
self.block_height = block_dimensions[1]
self.speed = Tetromino.normal_speed
# game bounds
self.min_coord = tuple(min_coord)
self.max_coord = (max_coord[0] - self.block_width,
......@@ -166,7 +169,7 @@ class Tetromino:
# coordinates for each block of the tetromino
# list of coordinates [x, y]
self.blocks_coords= self.build()
self.blocks_coords = self.build()
def build(self):
"""
......@@ -190,10 +193,22 @@ class Tetromino:
y += self.block_height
return tetromino_coord
def get_coords(self):
return self.blocks_coords
def get_color_index(self):
return self.random_index
def speed_up(self):
self.speed = Tetromino.fast_speed
def reset_speed(self):
self.speed = Tetromino.normal_speed
def show(self, screen):
"""
Blit all blocks
:param screen: surface
Blit all blocks.
:param screen: screen surface
"""
for coord in self.blocks_coords:
screen.blit(self.block_color, coord)
......@@ -201,10 +216,18 @@ class Tetromino:
def move_down(self, time):
"""
Move down all blocks.
:return: True if tetromino reached bottom
"""
# check if all blocks are inside the bounds
for coord in self.blocks_coords:
if coord[1] >= self.max_coord[1]: # reached the bottom of the game area
return True
# move all blocks
for coord in self.blocks_coords:
coord[1] += Tetromino.speed * time
self.center_coord[1] += Tetromino.speed * time
coord[1] += self.speed * time
self.center_coord[1] += self.speed * time
return False
def move_left(self):
"""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment