|
|
@@ -6,7 +6,8 @@ Competitive tetris server
|
|
|
import random
|
|
|
import pygame
|
|
|
|
|
|
-import shapes
|
|
|
+import tetrominos
|
|
|
+import game
|
|
|
|
|
|
pygame.font.init()
|
|
|
|
|
|
@@ -21,69 +22,6 @@ TOP_LEFT_X = (S_WIDTH - PLAY_WIDTH) // 2
|
|
|
TOP_LEFT_Y = S_HEIGHT - PLAY_HEIGHT
|
|
|
|
|
|
|
|
|
-# SHAPE FORMATS
|
|
|
-
|
|
|
-# index 0 - 6 represent shape
|
|
|
-
|
|
|
-
|
|
|
-class Piece:
|
|
|
- def __init__(self, x, y, shape):
|
|
|
- self.x = x
|
|
|
- self.y = y
|
|
|
- self.shape = shape
|
|
|
- self.color = shapes.SHAPE_COLORS[shapes.SHAPES.index(shape)]
|
|
|
- self.rotation = 0
|
|
|
-
|
|
|
-
|
|
|
-def create_grid(locked_pos={}):
|
|
|
- grid = [[(0,0,0) for _ in range(10)] for _ in range(20)]
|
|
|
-
|
|
|
- for i in range(len(grid)):
|
|
|
- for j in range(len(grid[i])):
|
|
|
- if (j, i) in locked_pos:
|
|
|
- c = locked_pos[(j,i)]
|
|
|
- grid[i][j] = c
|
|
|
- return grid
|
|
|
-
|
|
|
-
|
|
|
-def convert_shape_format(shape):
|
|
|
- positions = []
|
|
|
- format = shape.shape[shape.rotation % len(shape.shape)]
|
|
|
-
|
|
|
- for i, line in enumerate(format):
|
|
|
- row = list(line)
|
|
|
- for j, column in enumerate(row):
|
|
|
- if column == '0':
|
|
|
- positions.append((shape.x + j, shape.y + i))
|
|
|
- return positions
|
|
|
-
|
|
|
-
|
|
|
-def valid_space(shape, grid):
|
|
|
- accepted_pos = [[(j, i) for j in range(10) if grid[i][j] == (0,0,0)] for i in range(20)]
|
|
|
- accepted_pos = [j for sub in accepted_pos for j in sub]
|
|
|
-
|
|
|
- formatted = convert_shape_format(shape)
|
|
|
-
|
|
|
- for pos in formatted:
|
|
|
- if pos not in accepted_pos:
|
|
|
- if pos[1] > -1:
|
|
|
- return False
|
|
|
- return True
|
|
|
-
|
|
|
-
|
|
|
-def check_lost(positions):
|
|
|
- for pos in positions:
|
|
|
- x, y = pos
|
|
|
- if y < 1:
|
|
|
- return True
|
|
|
-
|
|
|
- return False
|
|
|
-
|
|
|
-
|
|
|
-def get_shape():
|
|
|
- return Piece(0, 0, random.choice(shapes.SHAPES))
|
|
|
-
|
|
|
-
|
|
|
def draw_text_middle(surface, text, size, color):
|
|
|
font = pygame.font.SysFont("comicsans", size, bold=True)
|
|
|
label = font.render(text, 1, color)
|
|
|
@@ -101,134 +39,78 @@ def draw_grid(surface, grid):
|
|
|
pygame.draw.line(surface, (128, 128, 128), (sx + j*BLOCK_SIZE, sy),(sx + j*BLOCK_SIZE, sy + PLAY_HEIGHT))
|
|
|
|
|
|
|
|
|
-def clear_rows(grid, locked):
|
|
|
-
|
|
|
- inc = 0
|
|
|
- for i in range(len(grid)-1, -1, -1):
|
|
|
- row = grid[i]
|
|
|
- if (0,0,0) not in row:
|
|
|
- inc += 1
|
|
|
- ind = i
|
|
|
- for j in range(len(row)):
|
|
|
- try:
|
|
|
- del locked[(j,i)]
|
|
|
- except:
|
|
|
- continue
|
|
|
-
|
|
|
- if inc > 0:
|
|
|
- for key in sorted(list(locked), key=lambda x: x[1])[::-1]:
|
|
|
- x, y = key
|
|
|
- if y < ind:
|
|
|
- newKey = (x, y + inc)
|
|
|
- locked[newKey] = locked.pop(key)
|
|
|
-
|
|
|
- return inc
|
|
|
-
|
|
|
-
|
|
|
def draw_next_shape(shape, surface):
|
|
|
font = pygame.font.SysFont('comicsans', 30)
|
|
|
label = font.render('Next Shape', 1, (255,255,255))
|
|
|
|
|
|
sx = TOP_LEFT_X + PLAY_WIDTH + 0
|
|
|
sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
|
|
|
- format = shape.shape[shape.rotation % len(shape.shape)]
|
|
|
+ shape_raw = tetrominos.SHAPES_RAW[shape][0]
|
|
|
|
|
|
- for i, line in enumerate(format):
|
|
|
+ for i, line in enumerate(shape_raw):
|
|
|
row = list(line)
|
|
|
for j, column in enumerate(row):
|
|
|
if column == '0':
|
|
|
- pygame.draw.rect(surface, shape.color, (sx + j*BLOCK_SIZE, sy + i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0)
|
|
|
-
|
|
|
+ pygame.draw.rect(
|
|
|
+ surface, (255,0,0),
|
|
|
+ (sx + j*BLOCK_SIZE, sy + i*BLOCK_SIZE,
|
|
|
+ BLOCK_SIZE, BLOCK_SIZE),
|
|
|
+ 0)
|
|
|
surface.blit(label, (sx + 10, sy - 30))
|
|
|
|
|
|
|
|
|
-def update_score(nscore):
|
|
|
- score = max_score()
|
|
|
-
|
|
|
- with open('scores.txt', 'w') as f:
|
|
|
- if int(score) > nscore:
|
|
|
- f.write(str(score))
|
|
|
- else:
|
|
|
- f.write(str(nscore))
|
|
|
-
|
|
|
-
|
|
|
-def max_score():
|
|
|
- with open('scores.txt', 'r') as f:
|
|
|
- lines = f.readlines()
|
|
|
- score = lines[0].strip()
|
|
|
-
|
|
|
- return score
|
|
|
-
|
|
|
-
|
|
|
-def draw_window(surface, grid, score=0, last_score = 0):
|
|
|
+def draw_game_state_to_window(surface, game_state):
|
|
|
surface.fill((0, 0, 0))
|
|
|
|
|
|
pygame.font.init()
|
|
|
font = pygame.font.SysFont('comicsans', 60)
|
|
|
label = font.render('Tetris', 1, (255, 255, 255))
|
|
|
-
|
|
|
- surface.blit(label, (TOP_LEFT_X + PLAY_WIDTH / 2 - (label.get_width() / 2), 30))
|
|
|
+ surface.blit(label,
|
|
|
+ (TOP_LEFT_X + PLAY_WIDTH / 2 - (label.get_width() / 2), 30))
|
|
|
|
|
|
# current score
|
|
|
font = pygame.font.SysFont('comicsans', 30)
|
|
|
- label = font.render('Score: ' + str(score), 1, (255,255,255))
|
|
|
-
|
|
|
+ label = font.render(f'Score: {game_state.score}', 1, (255,255,255))
|
|
|
sx = TOP_LEFT_X + PLAY_WIDTH + 50
|
|
|
sy = TOP_LEFT_Y + PLAY_HEIGHT/2 - 100
|
|
|
-
|
|
|
surface.blit(label, (sx + 20, sy + 160))
|
|
|
- # last score
|
|
|
- label = font.render('High Score: ' + last_score, 1, (255,255,255))
|
|
|
-
|
|
|
- sx = TOP_LEFT_X - 200
|
|
|
- sy = TOP_LEFT_Y + 200
|
|
|
|
|
|
- surface.blit(label, (sx + 20, sy + 160))
|
|
|
|
|
|
- for i in range(len(grid)):
|
|
|
- for j in range(len(grid[i])):
|
|
|
- pygame.draw.rect(surface, grid[i][j], (TOP_LEFT_X + j*BLOCK_SIZE, TOP_LEFT_Y + i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0)
|
|
|
+ field = game.get_game_state_field(game_state)
|
|
|
+ for row in range(field.shape[0]):
|
|
|
+ for col in range(field.shape[1]):
|
|
|
+ color = (0,0,0)
|
|
|
+ tile = field[row,col]
|
|
|
+ if tile == 1:
|
|
|
+ color = (0,255,0)
|
|
|
+ elif tile == 2:
|
|
|
+ color = (255,0,0)
|
|
|
|
|
|
- pygame.draw.rect(surface, (255, 0, 0), (TOP_LEFT_X, TOP_LEFT_Y, PLAY_WIDTH, PLAY_HEIGHT), 5)
|
|
|
+ pygame.draw.rect(surface, color,
|
|
|
+ (TOP_LEFT_X + col*BLOCK_SIZE,
|
|
|
+ TOP_LEFT_Y + row*BLOCK_SIZE,
|
|
|
+ BLOCK_SIZE, BLOCK_SIZE), 0)
|
|
|
|
|
|
- draw_grid(surface, grid)
|
|
|
- #pygame.display.update()
|
|
|
+ pygame.draw.rect(surface, (255, 0, 0),
|
|
|
+ (TOP_LEFT_X, TOP_LEFT_Y, PLAY_WIDTH, PLAY_HEIGHT), 5)
|
|
|
|
|
|
+ draw_grid(surface, field)
|
|
|
|
|
|
-def main(win): # *
|
|
|
- last_score = max_score()
|
|
|
- locked_positions = {}
|
|
|
- grid = create_grid(locked_positions)
|
|
|
|
|
|
- change_piece = False
|
|
|
+def main(win):
|
|
|
run = True
|
|
|
- current_piece = get_shape()
|
|
|
- current_piece.y = -4
|
|
|
- current_piece.x = 0
|
|
|
- next_piece = get_shape()
|
|
|
+ frame_time = 0
|
|
|
clock = pygame.time.Clock()
|
|
|
- fall_time = 0
|
|
|
- fall_speed = 0.27
|
|
|
- level_time = 0
|
|
|
- score = 0
|
|
|
+
|
|
|
+ def _next_piece_func():
|
|
|
+ return random.choice(range(len(tetrominos.SHAPES)))
|
|
|
+ game_state = game.initial_game_state(_next_piece_func(),
|
|
|
+ _next_piece_func())
|
|
|
|
|
|
while run:
|
|
|
- grid = create_grid(locked_positions)
|
|
|
- fall_time += clock.get_rawtime()
|
|
|
- level_time += clock.get_rawtime()
|
|
|
clock.tick()
|
|
|
-
|
|
|
- if level_time/1000 > 5:
|
|
|
- level_time = 0
|
|
|
- if level_time > 0.12:
|
|
|
- level_time -= 0.005
|
|
|
-
|
|
|
- if fall_time/1000 > fall_speed:
|
|
|
- fall_time = 0
|
|
|
- current_piece.y += 1
|
|
|
- if not(valid_space(current_piece, grid)) and current_piece.y > 0:
|
|
|
- current_piece.y -= 1
|
|
|
- change_piece = True
|
|
|
+ dt_millis = clock.get_time()
|
|
|
+ frame_time += dt_millis
|
|
|
|
|
|
for event in pygame.event.get():
|
|
|
if event.type == pygame.QUIT:
|
|
|
@@ -237,53 +119,32 @@ def main(win): # *
|
|
|
|
|
|
if event.type == pygame.KEYDOWN:
|
|
|
if event.key == pygame.K_LEFT:
|
|
|
- current_piece.x -= 1
|
|
|
- if not(valid_space(current_piece, grid)):
|
|
|
- current_piece.x += 1
|
|
|
+ game_state = game.game_state_shift(game_state, -1)
|
|
|
if event.key == pygame.K_RIGHT:
|
|
|
- current_piece.x += 1
|
|
|
- if not(valid_space(current_piece, grid)):
|
|
|
- current_piece.x -= 1
|
|
|
- if event.key == pygame.K_DOWN:
|
|
|
- current_piece.y += 1
|
|
|
- if not(valid_space(current_piece, grid)):
|
|
|
- current_piece.y -= 1
|
|
|
+ game_state = game.game_state_shift(game_state, 1)
|
|
|
if event.key == pygame.K_UP:
|
|
|
- current_piece.rotation += 1
|
|
|
- if not(valid_space(current_piece, grid)):
|
|
|
- current_piece.rotation -= 1
|
|
|
-
|
|
|
- shape_pos = convert_shape_format(current_piece)
|
|
|
-
|
|
|
- for i in range(len(shape_pos)):
|
|
|
- x, y = shape_pos[i]
|
|
|
- if y > -1:
|
|
|
- grid[y][x] = current_piece.color
|
|
|
-
|
|
|
- if change_piece:
|
|
|
- for pos in shape_pos:
|
|
|
- p = (pos[0], pos[1])
|
|
|
- locked_positions[p] = current_piece.color
|
|
|
- current_piece = next_piece
|
|
|
- current_piece.x = 0
|
|
|
- current_piece.y = -4
|
|
|
- next_piece = get_shape()
|
|
|
- change_piece = False
|
|
|
- score += clear_rows(grid, locked_positions) * 10
|
|
|
-
|
|
|
- draw_window(win, grid, score, last_score)
|
|
|
- draw_next_shape(next_piece, win)
|
|
|
+ game_state = game.game_state_rotate(game_state)
|
|
|
+ if event.key == pygame.K_DOWN:
|
|
|
+ game_state = game.game_state_land(game_state)
|
|
|
+
|
|
|
+ is_game_running = True
|
|
|
+ if frame_time >= 500:
|
|
|
+ game_state, is_game_running = game.advance_game_state(
|
|
|
+ game_state, _next_piece_func)
|
|
|
+ frame_time = 0
|
|
|
+
|
|
|
+ draw_game_state_to_window(win, game_state)
|
|
|
+ draw_next_shape(game_state.next_piece, win)
|
|
|
pygame.display.update()
|
|
|
|
|
|
- if check_lost(locked_positions):
|
|
|
+ if not is_game_running:
|
|
|
draw_text_middle(win, "YOU LOST!", 80, (255,255,255))
|
|
|
pygame.display.update()
|
|
|
pygame.time.delay(1500)
|
|
|
run = False
|
|
|
- update_score(score)
|
|
|
|
|
|
|
|
|
-def main_menu(win): # *
|
|
|
+def main_menu(win):
|
|
|
run = True
|
|
|
while run:
|
|
|
win.fill((0,0,0))
|