pytris/tetris-main.py

1232 lines
58 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import List
import pygame, random, datetime
from string import Template
BLOCK_SIZE = 30
FIELD_SIZE_X = 10
FIELD_SIZE_Y = 20
GUIDELINES = ["Current", "NES like"]
class Block:
def __init__(self, color):
self.color_str = str(color)
self.color = pygame.Color(color)
def __str__(self):
return f"Block {self.color_str}"
def __repr__(self):
return f"Block {self.color_str}"
TETROMINOS = [
[
[
[None, None, Block((240, 160, 0)), None],
[Block((240, 160, 0)), Block((240, 160, 0)), Block((240, 160, 0)), None],
[None, None, None, None],
[None, None, None, None]
],
[
[None, Block((240, 160, 0)), None, None],
[None, Block((240, 160, 0)), None, None],
[None, Block((240, 160, 0)), Block((240, 160, 0)), None],
[None, None, None, None]
],
[
[None, None, None, None],
[Block((240, 160, 0)), Block((240, 160, 0)), Block((240, 160, 0)), None],
[Block((240, 160, 0)), None, None, None],
[None, None, None, None]
],
[
[Block((240, 160, 0)), Block((240, 160, 0)), None, None],
[None, Block((240, 160, 0)), None, None],
[None, Block((240, 160, 0)), None, None],
[None, None, None, None]
]
], # 0, L
[
[
[Block((0, 0, 240)), None, None, None],
[Block((0, 0, 240)), Block((0, 0, 240)), Block((0, 0, 240)), None],
[None, None, None, None],
[None, None, None, None]
],
[
[None, Block((0, 0, 240)), Block((0, 0, 240)), None],
[None, Block((0, 0, 240)), None, None],
[None, Block((0, 0, 240)), None, None],
[None, None, None, None]
],
[
[None, None, None, None],
[Block((0, 0, 240)), Block((0, 0, 240)), Block((0, 0, 240)), None],
[None, None, Block((0, 0, 240)), None],
[None, None, None, None]
],
[
[None, Block((0, 0, 240)), None, None],
[None, Block((0, 0, 240)), None, None],
[Block((0, 0, 240)), Block((0, 0, 240)), None, None],
[None, None, None, None]
]
], # 1, J
[
[
[None, Block((0, 240, 0)), Block((0, 240, 0)), None],
[Block((0, 240, 0)), Block((0, 240, 0)), None, None],
[None, None, None, None],
[None, None, None, None]
],
[
[None, Block((0, 240, 0)), None, None],
[None, Block((0, 240, 0)), Block((0, 240, 0)), None],
[None, None, Block((0, 240, 0)), None],
[None, None, None, None]
],
[
[None, None, None, None],
[None, Block((0, 240, 0)), Block((0, 240, 0)), None],
[Block((0, 240, 0)), Block((0, 240, 0)), None, None],
[None, None, None, None]
],
[
[Block((0, 240, 0)), None, None, None],
[Block((0, 240, 0)), Block((0, 240, 0)), None, None],
[None, Block((0, 240, 0)), None, None],
[None, None, None, None]
]
], # 2, S
[
[
[Block((240, 0, 0)), Block((240, 0, 0)), None, None],
[None, Block((240, 0, 0)), Block((240, 0, 0)), None],
[None, None, None, None],
[None, None, None, None]
],
[
[None, None, Block((240, 0, 0)), None],
[None, Block((240, 0, 0)), Block((240, 0, 0)), None],
[None, Block((240, 0, 0)), None, None],
[None, None, None, None]
],
[
[None, None, None, None],
[Block((240, 0, 0)), Block((240, 0, 0)), None, None],
[None, Block((240, 0, 0)), Block((240, 0, 0)), None],
[None, None, None, None]
],
[
[None, Block((240, 0, 0)), None, None],
[Block((240, 0, 0)), Block((240, 0, 0)), None, None],
[Block((240, 0, 0)), None, None, None],
[None, None, None, None]
]
], # 3, Z
[
[
[None, Block((160, 0, 240)), None, None],
[Block((160, 0, 240)), Block((160, 0, 240)), Block((160, 0, 240)), None],
[None, None, None, None],
[None, None, None, None]
],
[
[None, Block((160, 0, 240)), None, None],
[None, Block((160, 0, 240)), Block((160, 0, 240)), None],
[None, Block((160, 0, 240)), None, None],
[None, None, None, None]
],
[
[None, None, None, None],
[Block((160, 0, 240)), Block((160, 0, 240)), Block((160, 0, 240)), None],
[None, Block((160, 0, 240)), None, None],
[None, None, None, None]
],
[
[None, Block((160, 0, 240)), None, None],
[Block((160, 0, 240)), Block((160, 0, 240)), None, None],
[None, Block((160, 0, 240)), None, None],
[None, None, None, None]
]
], # 4, T
[
[
[None, None, None, None],
[Block((0, 240, 240)), Block((0, 240, 240)), Block((0, 240, 240)), Block((0, 240, 240))],
[None, None, None, None],
[None, None, None, None]
],
[
[None, None, Block((0, 240, 240)), None],
[None, None, Block((0, 240, 240)), None],
[None, None, Block((0, 240, 240)), None],
[None, None, Block((0, 240, 240)), None]
],
[
[None, None, None, None],
[None, None, None, None],
[Block((0, 240, 240)), Block((0, 240, 240)), Block((0, 240, 240)), Block((0, 240, 240))],
[None, None, None, None]
],
[
[None, Block((0, 240, 240)), None, None],
[None, Block((0, 240, 240)), None, None],
[None, Block((0, 240, 240)), None, None],
[None, Block((0, 240, 240)), None, None]
]
], # 5, I
[
[
[Block((255, 240, 0)), Block((255, 240, 0)), None, None],
[Block((255, 240, 0)), Block((255, 240, 0)), None, None],
[None, None, None, None],
[None, None, None, None]
]
] # 6, O
]
class DeltaTemplate(Template):
delimiter = "%"
def strfdelta(tdelta, fmt):
d = {"D": tdelta.days}
hours, rem = divmod(tdelta.seconds, 3600)
minutes, seconds = divmod(rem, 60)
hours += d["D"] * 24
d["S"] = '{:02d}'.format(seconds)
d["s"] = seconds
t = DeltaTemplate(fmt)
d["Z"] = '{:02d}'.format(int(tdelta.microseconds / 10000))
d["z"] = '{:1d}'.format(int(tdelta.microseconds / 100000))
d["H"] = hours
d["M"] = '{:02d}'.format(minutes)
d["m"] = minutes
return t.substitute(**d)
class TetrisGameplay:
def __init__(self, size_x=FIELD_SIZE_X, size_y=FIELD_SIZE_Y, level=0, buffer_zone=20, srs=True, lock_delay=True, seven_bag=True, ghost_piece=True, hold=True, hard_drop=True, handling=(167, 33), nes_mechanics=False, next_len=3):
self.buffer_y = buffer_zone
self.FIELD = list(range(size_y + buffer_zone))
y = 0
while y != len(self.FIELD):
self.FIELD[y] = list(range(size_x))
x = 0
while x != size_x:
self.FIELD[y][x] = None
x += 1
y += 1
self.current_posx = 4
self.current_posy = self.buffer_y - 2
self.can_hard_drop = hard_drop
self.support_combo_and_btb_bonuses = False
self.support_srs = srs
self.handling = handling
self.support_hold = hold
self.nes_mechanics = nes_mechanics
self.support_ghost_piece = ghost_piece
self.support_lock_delay = lock_delay
self.support_garbage = False
self.seven_bag_random = seven_bag
self.next_length = next_len
self.score = [
0, # 0, Soft Drop
0, # 1, Hard Drop
0, # 2, Single
0, # 3, Double
0, # 4, Triple
0, # 5, Tetris
0, # 6, T-Spin Mini no lines
0, # 7, T-Spin Mini Single
0, # 8, T-Spin Mini Double
0, # 9, T-Spin no lines
0, # 10, T-Spin Single
0, # 11, T-Spin Double
0, # 12, T-Spin Triple
0, # 13, Combo Bonus
0 # 14, Back-to-Back bonus
]
self.cleared_lines = [
0, # Single
0, # Double
0, # Triple
0 # Tetris
]
self.pieces = [
0, # L piece
0, # J piece
0, # S piece
0, # Z piece
0, # T piece
0, # I piece
0 # O piece
]
self.game_time = 0
self.next_queue = []
if self.seven_bag_random:
self.next_queue = [0, 1, 2, 3, 4, 5, 6]
random.shuffle(self.next_queue)
self.current_id = self.next_queue[0]
self.next_queue.pop(0)
else:
self.current_id = random.randint(0, 6)
self.next_queue = [random.randint(0, 6) for i in range(self.next_length+1)]
self.hold_id = None
self.hold_locked = False
self.spin_is_last_move = False
self.spin_is_kick_t_piece = False
self.current_spin_id = 0
self.pieces[self.current_id] += 1
self.lock_delay_run = False
self.lock_delay_frames = 30
self.level = level
self.lines_for_level_up = self.gravity_and_lines_table()[1]
self.start_level = level
self.game_over = False
def spawn_tetromino(self):
if self.collision(4, self.buffer_y-2, self.next_queue[0], 0):
self.game_over = True
self.current_posx = 4
self.current_posy = self.buffer_y - 2
self.current_id = self.next_queue[0]
self.hold_locked = False
self.spin_is_last_move = False
self.spin_is_kick_t_piece = False
self.pieces[self.current_id] += 1
self.current_spin_id = 0
self.next_queue.pop(0)
if len(self.next_queue) == self.next_length:
if self.seven_bag_random:
next_bag = [0, 1, 2, 3, 4, 5, 6]
random.shuffle(next_bag)
self.next_queue.extend(next_bag)
else:
ext = [random.randint(0, 6) for i in range(self.next_length+1)]
self.next_queue.extend(ext)
def hold_tetromino(self):
self.current_spin_id = 0
self.spin_is_kick_t_piece = False
self.reset_lock_delay()
if self.hold_id is not None:
self.current_id, self.hold_id = self.hold_id, self.current_id
self.current_posx = 4
self.current_posy = self.buffer_y - 2
self.hold_locked = True
else:
self.hold_id = self.current_id
self.spawn_tetromino()
self.hold_locked = True
def __str__(self):
return f"size_x={len(self.FIELD[0])}, size_y={len(self.FIELD)}, buffer_y: {self.buffer_y}"
def gravity_and_lines_table(self):
return 1 / (0.8 - ((self.level - 1) * 0.007)) ** (self.level - 1) * 0.016666, self.level * 10 + 10
def clear_lines(self):
cleared = 0
frames_delay = 0
t_spin = False
t_spin_mini = False
height = None
t_spin_corners = [
[[(0, 0), (2, 0)], [(0, 2), (2, 2)]],
[[(2, 0), (2, 2)], [(0, 0), (0, 2)]],
[[(0, 2), (2, 2)], [(0, 0), (2, 0)]],
[[(0, 2), (0, 0)], [(2, 0), (2, 2)]]
]
if self.current_id == 4 and self.spin_is_last_move:
front_col = 0
back_col = 0
for i in t_spin_corners[self.current_spin_id][0]:
if self.current_posy+i[1] >= len(self.FIELD) or self.current_posx+i[0] >= len(self.FIELD[self.current_posy+i[1]]) or self.current_posy+i[1] < 0 or self.current_posx+i[0] < 0 or self.FIELD[self.current_posy+i[1]][self.current_posx+i[0]] is not None:
front_col += 1
for i in t_spin_corners[self.current_spin_id][1]:
if self.current_posy+i[1] >= len(self.FIELD) or self.current_posx+i[0] >= len(self.FIELD[self.current_posy+i[1]]) or self.current_posy+i[1] < 0 or self.current_posx+i[0] < 0 or self.FIELD[self.current_posy+i[1]][self.current_posx+i[0]] is not None:
back_col += 1
if (front_col == 2 and back_col >= 1) or (back_col == 2 and front_col == 1 and self.spin_is_kick_t_piece):
t_spin = True
elif back_col == 2 and front_col == 1:
t_spin_mini = True
y = len(self.FIELD)
for i in self.FIELD:
ic = 0
for k in i:
if k is not None:
ic += 1
if ic == FIELD_SIZE_X:
cleared += 1
self.FIELD.remove(i)
new = list(range(FIELD_SIZE_X))
x = 0
while x != FIELD_SIZE_X:
new[x] = None
x += 1
self.FIELD.insert(0, new)
y -= 1
if ic > 0 and height is None:
height = y
if cleared > 0:
self.cleared_lines[cleared - 1] += cleared
if t_spin:
if cleared == 1:
self.score[10] += 800 * (min(self.level, 29) + 1)
elif cleared == 2:
self.score[11] += 1200 * (min(self.level, 29) + 1)
elif cleared == 3:
self.score[12] += 1600 * (min(self.level, 29) + 1)
elif t_spin_mini:
if cleared == 1:
self.score[7] += 200 * (min(self.level, 29) + 1)
elif cleared == 2:
self.score[8] += 400 * (min(self.level, 29) + 1)
if cleared == 1:
self.score[2] += 100 * (min(self.level, 29) + 1)
elif cleared == 2:
self.score[3] += 300 * (min(self.level, 29) + 1)
elif cleared == 3:
self.score[4] += 500 * (min(self.level, 29) + 1)
elif cleared == 4:
self.score[5] += 800 * (min(self.level, 29) + 1)
if sum(self.cleared_lines) >= self.lines_for_level_up:
self.level += 1
self.lines_for_level_up += 10
else:
if t_spin:
self.score[9] += 400 * (min(self.level, 29) + 1)
elif t_spin_mini:
self.score[6] += 100 * (min(self.level, 29) + 1)
return 0
def collision(self, next_posx, next_posy, next_id, next_spin_id):
i1 = next_posy
k1 = next_posx
for i in TETROMINOS[next_id][next_spin_id]:
for k in i:
if k and (i1 >= len(self.FIELD) or k1 >= len(self.FIELD[i1]) or i1 < 0 or k1 < 0 or self.FIELD[i1][k1]):
return True
k1 += 1
k1 = next_posx
i1 += 1
return False
def spin(self, reverse=False):
self.reset_lock_delay()
if self.current_id != 6:
if reverse:
future_spin_id = self.current_spin_id - 1
else:
future_spin_id = self.current_spin_id + 1
future_spin_id %= 4
if not self.collision(self.current_posx, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.spin_is_last_move = True
return
if self.support_srs:
if self.current_id != 5:
if (self.current_spin_id == 0 or self.current_spin_id == 2) and future_spin_id == 1:
if not self.collision(self.current_posx-1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-1, self.current_posy-1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.current_posy -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx, self.current_posy+2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posy += 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-1, self.current_posy+2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.current_posy += 2
self.spin_is_last_move = True
self.spin_is_kick_t_piece = True
return
elif self.current_spin_id == 1 and (future_spin_id == 0 or future_spin_id == 2):
if not self.collision(self.current_posx+1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy+1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx, self.current_posy-2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posy -= 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy-2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy -= 2
self.spin_is_last_move = True
self.spin_is_kick_t_piece = True
return
elif (self.current_spin_id == 0 or self.current_spin_id == 2) and future_spin_id == 3:
if not self.collision(self.current_posx+1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy-1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx, self.current_posy+2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posy += 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy+2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy += 2
self.spin_is_last_move = True
self.spin_is_kick_t_piece = True
return
elif self.current_spin_id == 3 and (future_spin_id == 0 or future_spin_id == 2):
if not self.collision(self.current_posx-1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy+1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.current_posy += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx, self.current_posy-2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posy -= 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy-2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy -= 2
self.spin_is_last_move = True
self.spin_is_kick_t_piece = True
return
else:
if (self.current_spin_id == 0 and future_spin_id == 1) or (self.current_spin_id == 3 and future_spin_id == 2):
if not self.collision(self.current_posx-2, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-2, self.current_posy+1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 2
self.current_posy += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy-2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy -= 2
self.spin_is_last_move = True
return
elif (self.current_spin_id == 1 and future_spin_id == 0) or (self.current_spin_id == 2 and future_spin_id == 3):
if not self.collision(self.current_posx+2, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+2, self.current_posy-1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 2
self.current_posy -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-1, self.current_posy+2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.current_posy += 2
self.spin_is_last_move = True
return
elif (self.current_spin_id == 1 and future_spin_id == 2) or (self.current_spin_id == 0 and future_spin_id == 3):
if not self.collision(self.current_posx-1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+2, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-1, self.current_posy-2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 1
self.current_posy -= 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+2, self.current_posy+1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 2
self.current_posy += 1
self.spin_is_last_move = True
return
elif (self.current_spin_id == 2 and future_spin_id == 1) or (self.current_spin_id == 3 and future_spin_id == 0):
if not self.collision(self.current_posx+1, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-2, self.current_posy, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx+1, self.current_posy+2, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx += 1
self.current_posy += 2
self.spin_is_last_move = True
return
elif not self.collision(self.current_posx-2, self.current_posy-1, self.current_id, future_spin_id):
self.current_spin_id = future_spin_id
self.current_posx -= 2
self.current_posy -= 1
self.spin_is_last_move = True
return
def move_side(self, x_change):
if not self.collision(self.current_posx + x_change, self.current_posy, self.current_id, self.current_spin_id):
self.current_posx += x_change
self.reset_lock_delay()
self.spin_is_last_move = False
def save_state(self):
i1 = self.current_posy
k1 = self.current_posx
if self.current_id is not None:
for i in TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k:
self.FIELD[i1][k1] = k
k1 += 1
k1 = self.current_posx
i1 += 1
def ghost_piece_y(self):
y = self.current_posy
while not self.collision(self.current_posx, y + 1, self.current_id,
self.current_spin_id):
y += 1
return y
def reset_lock_delay(self):
self.lock_delay_frames = 30
self.lock_delay_run = False
def move_down(self, instant=False):
if not self.collision(self.current_posx, self.current_posy + 1, self.current_id, self.current_spin_id):
if instant:
add_to_score = 0
while not self.collision(self.current_posx, self.current_posy + 1, self.current_id,
self.current_spin_id):
self.current_posy += 1
add_to_score += 2
self.score[1] += add_to_score
self.spin_is_last_move = False
else:
self.current_posy += 1
self.spin_is_last_move = False
return True
else:
return False
def draw_game(self):
win.fill((25, 25, 25))
pygame.draw.rect(win, (0, 0, 0),
(5, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * FIELD_SIZE_X, BLOCK_SIZE * FIELD_SIZE_Y))
x = 0
y = -self.buffer_y
for i in self.FIELD:
for k in i:
window_x = 5 + BLOCK_SIZE * x
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * y
if k is not None:
pygame.draw.rect(win, k.color, (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE), width=2)
else:
pygame.draw.rect(win, (25, 25, 25), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE), width=1)
x += 1
x = 0
y += 1
i1 = self.current_posy - self.buffer_y
k1 = self.current_posx
if self.current_id is not None:
for i in TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * k1
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * i1
pygame.draw.rect(win, (int(k.color[0]*self.lock_delay_frames/30), int(k.color[1]*self.lock_delay_frames/30), int(k.color[2]*self.lock_delay_frames/30)), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE), width=2)
k1 += 1
k1 = self.current_posx
i1 += 1
if self.support_ghost_piece:
i1 = self.ghost_piece_y() - self.buffer_y
k1 = self.current_posx
for i in TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * k1
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * i1
pygame.draw.rect(win, (k.color[0], k.color[1], k.color[2]), (window_x+5, window_y+5, BLOCK_SIZE-10, BLOCK_SIZE-10), width=10, border_radius=1)
k1 += 1
k1 = self.current_posx
i1 += 1
x_offset = 0
for q in range(0, self.next_length):
i1 = 0
k1 = 0
for i in TETROMINOS[self.next_queue[q]][0]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + 10 * k1 + x_offset
window_y = (BLOCK_SIZE * 2 + 30) + 10 * i1
pygame.draw.rect(win, k.color, (window_x, window_y, 10, 10))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 10, 10), width=1)
k1 += 1
k1 = 0
i1 += 1
x_offset += 45
if self.support_hold:
if self.hold_id is not None:
i1 = 0
k1 = 0
for i in TETROMINOS[self.hold_id][0]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + 10 * k1
window_y = (BLOCK_SIZE * 2 + 75) + 10 * i1
pygame.draw.rect(win, k.color, (window_x, window_y, 10, 10))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 10, 10), width=1)
k1 += 1
k1 = 0
i1 += 1
win.blit(FONT.render("SCORE", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 25 + BLOCK_SIZE * 7))
total_score = sum(self.score)
win.blit(FONT.render(f"{total_score:06d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 50 + BLOCK_SIZE * 7))
win.blit(FONT.render("LINES", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 90 + BLOCK_SIZE * 7))
total_lines = sum(self.cleared_lines)
win.blit(FONT.render(f"{total_lines:03d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 115 + BLOCK_SIZE * 7))
win.blit(FONT.render("LV", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 155 + BLOCK_SIZE * 7))
win.blit(FONT.render(f"{self.level:02d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 180 + BLOCK_SIZE * 7))
try:
pps = sum(self.pieces) / self.game_time
except ZeroDivisionError:
pps = 0
win.blit(FONT.render("PPS", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 120 + BLOCK_SIZE * k1, 155 + BLOCK_SIZE * 7))
if pps >= 1:
win.blit(FONT.render(f"{pps:1.1f}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 120 + BLOCK_SIZE * k1, 180 + BLOCK_SIZE * 7))
else:
win.blit(FONT.render(f".{int(pps*100):02d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 120 + BLOCK_SIZE * k1, 180 + BLOCK_SIZE * 7))
win.blit(FONT.render("TIME", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 220 + BLOCK_SIZE * 7))
if self.game_time < 10:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%s.%Z'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
elif self.game_time < 60:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%S.%z'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
elif self.game_time < 3600:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%m:%S'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
else:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%H:%M:%S'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
if self.game_over:
text_size_x = FONT.size("GAME")[0]
pygame.draw.rect(win, (0, 0, 0), (
BLOCK_SIZE * (FIELD_SIZE_X / 2) - text_size_x, BLOCK_SIZE * FIELD_SIZE_Y / 2, text_size_x * 2 + 10, 60))
win.blit(FONT.render("GAME", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X / 2 - text_size_x / 2, 5 + BLOCK_SIZE * FIELD_SIZE_Y / 2))
win.blit(FONT.render("OVER", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X / 2 - text_size_x / 2, 5 + BLOCK_SIZE * FIELD_SIZE_Y / 2 + 25))
pygame.display.update()
def draw_game_stats(self, on_pause):
win.fill((25, 25, 25))
if on_pause:
win.blit(FONT.render("GAME PAUSED", 1, (255, 255, 255)), (25, 25))
else:
win.blit(FONT.render("STATISTIC", 1, (255, 255, 255)), (25, 25))
total_score = sum(self.score)
win.blit(FONT.render(f"SCORE {total_score:16d}", 1, (255, 255, 255)), (25, 100))
total_pieces = sum(self.pieces)
win.blit(FONT.render(f"PIECES {total_pieces:15d}", 1, (255, 255, 255)), (25, 130))
total_lines = sum(self.cleared_lines)
win.blit(FONT.render(f"LINES {total_lines:16d}", 1, (255, 255, 255)), (25, 160))
try:
pps = total_pieces / self.game_time
except ZeroDivisionError:
pps = 0
win.blit(FONT.render(f"PIECES PER SECOND {pps:0.2f}", 1, (255, 255, 255)), (25, 190))
win.blit(FONT.render(f"LEVELS {self.start_level:02d}-{self.level:02d}", 1, (255, 255, 255)), (25, 220))
win.blit(
FONT.render(f"TIME {strfdelta(datetime.timedelta(seconds=self.game_time), '%H:%M:%S.%Z'):>17s}", 1, (255, 255, 255)),
(25, 250))
win.blit(SMALL_FONT.render(f"BURNED TIMES LINES PIECES TABLE", 1, (255, 255, 255)), (25, 290))
win.blit(
SMALL_FONT.render(f"SINGLE {self.cleared_lines[0]:5d} {self.cleared_lines[0]:5d} L {self.pieces[0]:<4d} J {self.pieces[1]:<4d}", 1, (255, 255, 255)),
(25, 310))
double_times = int(self.cleared_lines[1] / 2)
win.blit(SMALL_FONT.render(f"DOUBLE {double_times:5d} {self.cleared_lines[1]:5d} S {self.pieces[2]:<4d} Z {self.pieces[3]:<4d}", 1, (255, 255, 255)),
(25, 325))
triple_times = int(self.cleared_lines[2] / 3)
win.blit(SMALL_FONT.render(f"TRIPLE {triple_times:5d} {self.cleared_lines[2]:5d} T {self.pieces[4]:<4d} O {self.pieces[6]:<4d}", 1, (255, 255, 255)),
(25, 340))
tetris_times = int(self.cleared_lines[3] / 4)
win.blit(SMALL_FONT.render(f"TETRIS {tetris_times:5d} {self.cleared_lines[3]:5d} I {self.pieces[5]:<10d}", 1, (255, 255, 255)),
(25, 355))
win.blit(SMALL_FONT.render(f"SCORE TABLE", 1, (255, 255, 255)), (25, 380))
win.blit(SMALL_FONT.render(f"SOFT DROPS {self.score[0]:14d}", 1, (255, 255, 255)), (25, 400))
win.blit(SMALL_FONT.render(f"HARD DROPS {self.score[1]:14d}", 1, (255, 255, 255)), (25, 415))
win.blit(SMALL_FONT.render(f"SINGLE {self.score[2]:18d}", 1, (255, 255, 255)), (25, 430))
win.blit(SMALL_FONT.render(f"DOUBLE {self.score[3]:18d}", 1, (255, 255, 255)), (25, 445))
win.blit(SMALL_FONT.render(f"TRIPLE {self.score[4]:18d}", 1, (255, 255, 255)), (25, 460))
win.blit(SMALL_FONT.render(f"TETRIS {self.score[5]:18d}", 1, (255, 255, 255)), (25, 475))
win.blit(SMALL_FONT.render(f"T-SPIN MINI NO L. {self.score[6]:7d}", 1, (255, 255, 255)), (25, 490))
win.blit(SMALL_FONT.render(f"T-SPIN MINI SINGLE {self.score[7]:6d}", 1, (255, 255, 255)), (25, 505))
win.blit(SMALL_FONT.render(f"T-SPIN MINI DOUBLE {self.score[8]:6d}", 1, (255, 255, 255)), (25, 520))
win.blit(SMALL_FONT.render(f"T-SPIN NO LINES {self.score[9]:9d}", 1, (255, 255, 255)), (25, 535))
win.blit(SMALL_FONT.render(f"T-SPIN SINGLE {self.score[10]:11d}", 1, (255, 255, 255)), (25, 550))
win.blit(SMALL_FONT.render(f"T-SPIN DOUBLE {self.score[11]:11d}", 1, (255, 255, 255)), (25, 565))
win.blit(SMALL_FONT.render(f"T-SPIN TRIPLE {self.score[12]:11d}", 1, (255, 255, 255)), (25, 580))
win.blit(SMALL_FONT.render(f"COMBO BONUS {self.score[13]:13d}", 1, (255, 255, 255)), (25, 595))
win.blit(SMALL_FONT.render(f"BACK-TO-BACK BONUS {self.score[14]:6d}", 1, (255, 255, 255)), (25, 610))
pygame.display.update()
class NesLikeTetris(TetrisGameplay):
def __init__(self, size_x=FIELD_SIZE_X, size_y=FIELD_SIZE_Y, level=0):
super().__init__(size_x, size_y, level, 2, False, False, False, False, False, False, (267, 100), True, 1)
def __str__(self):
ans = f"size_x={len(self.FIELD[0])}, size_y={len(self.FIELD)}, Field:"
for i in self.FIELD:
ans += f"\n{i}"
return ans
def gravity_and_lines_table(self):
if self.level == 0:
return 1/48, 10
elif self.level == 1:
return 1/43, 20
elif self.level == 2:
return 1/38, 30
elif self.level == 3:
return 1/33, 40
elif self.level == 4:
return 1/28, 50
elif self.level == 5:
return 1/23, 60
elif self.level == 6:
return 1/18, 70
elif self.level == 7:
return 1/13, 80
elif self.level == 8:
return 1/8, 90
elif self.level == 9:
return 1/6, 100
elif 10 <= self.level <= 12:
return 1/5, 100
elif 13 <= self.level <= 15:
return 1/4, 100
elif self.level == 16:
return 1/3, 110
elif self.level == 17:
return 1/3, 120
elif self.level == 18:
return 1/3, 130
elif self.level == 19:
return 1/2, 140
elif self.level == 20:
return 1/2, 150
elif self.level == 21:
return 1/2, 160
elif self.level == 22:
return 1/2, 170
elif self.level == 23:
return 1/2, 180
elif self.level == 24:
return 1/2, 190
elif 25 <= self.level <= 28:
return 1/2, 200
else:
return 1, 200
def clear_lines(self):
cleared = 0
frames_delay = 0
height = None
y = len(self.FIELD)
for i in self.FIELD:
ic = 0
for k in i:
if k is not None:
ic += 1
if ic == FIELD_SIZE_X:
cleared += 1
self.FIELD.remove(i)
new = list(range(FIELD_SIZE_X))
x = 0
while x != FIELD_SIZE_X:
new[x] = None
x += 1
self.FIELD.insert(0, new)
y -= 1
if ic > 0 and height is None:
height = y
frames_delay += 10 + (2 * int(height / 4))
if cleared >= 0:
self.cleared_lines[cleared - 1] += cleared
frames_delay += 18
if cleared == 1:
self.score[2] += 40 * (min(self.level, 29) + 1)
elif cleared == 2:
self.score[3] += 100 * (min(self.level, 29) + 1)
elif cleared == 3:
self.score[4] += 300 * (min(self.level, 29) + 1)
elif cleared == 4:
self.score[5] += 1200 * (min(self.level, 29) + 1)
if sum(self.cleared_lines) >= self.lines_for_level_up:
self.level += 1
self.lines_for_level_up += 10
return frames_delay
def draw_game(self):
win.fill((25, 25, 25))
pygame.draw.rect(win, (0, 0, 0),
(5, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * FIELD_SIZE_X, BLOCK_SIZE * FIELD_SIZE_Y))
x = 0
y = -self.buffer_y
for i in self.FIELD:
for k in i:
window_x = 5 + BLOCK_SIZE * x
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * y
if k is not None:
pygame.draw.rect(win, k.color, (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE), width=2)
x += 1
x = 0
y += 1
i1 = self.current_posy - self.buffer_y
k1 = self.current_posx
if self.current_id is not None:
for i in TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * k1
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * i1
pygame.draw.rect(win, (int(k.color[0]*self.lock_delay_frames/30), int(k.color[1]*self.lock_delay_frames/30), int(k.color[2]*self.lock_delay_frames/30)), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE), width=2)
k1 += 1
k1 = self.current_posx
i1 += 1
x_offset = 0
for q in range(0, self.next_length):
i1 = 0
k1 = 0
for i in TETROMINOS[self.next_queue[q]][0]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1 + x_offset
window_y = (BLOCK_SIZE * 2 + 30) + BLOCK_SIZE * i1
pygame.draw.rect(win, k.color, (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, BLOCK_SIZE, BLOCK_SIZE), width=1)
k1 += 1
k1 = 0
i1 += 1
x_offset += 45
if self.support_hold:
if self.hold_id is not None:
i1 = 0
k1 = 0
for i in TETROMINOS[self.hold_id][0]:
for k in i:
if k is not None:
window_x = 5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + 10 * k1
window_y = (BLOCK_SIZE * 2 + 75) + 10 * i1
pygame.draw.rect(win, k.color, (window_x, window_y, 10, 10))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 10, 10), width=1)
k1 += 1
k1 = 0
i1 += 1
win.blit(FONT.render("SCORE", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 25 + BLOCK_SIZE * 7))
total_score = sum(self.score)
win.blit(FONT.render(f"{total_score:06d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 50 + BLOCK_SIZE * 7))
win.blit(FONT.render("LINES", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 90 + BLOCK_SIZE * 7))
total_lines = sum(self.cleared_lines)
win.blit(FONT.render(f"{total_lines:03d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 115 + BLOCK_SIZE * 7))
win.blit(FONT.render("LV", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 155 + BLOCK_SIZE * 7))
win.blit(FONT.render(f"{self.level:02d}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 180 + BLOCK_SIZE * 7))
try:
tetris_rate = int((self.cleared_lines[3] / total_lines) * 100)
except ZeroDivisionError:
tetris_rate = 0
win.blit(FONT.render("TRT", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 120 + BLOCK_SIZE * k1, 155 + BLOCK_SIZE * 7))
if tetris_rate == 100:
win.blit(FONT.render(f"{tetris_rate}", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 120 + BLOCK_SIZE * k1, 180 + BLOCK_SIZE * 7))
else:
win.blit(FONT.render(f"{tetris_rate:02d}%", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 120 + BLOCK_SIZE * k1, 180 + BLOCK_SIZE * 7))
win.blit(FONT.render("TIME", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 220 + BLOCK_SIZE * 7))
if self.game_time < 10:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%s.%Z'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
elif self.game_time < 60:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%S.%z'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
elif self.game_time < 3600:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%m:%S'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
else:
win.blit(FONT.render(strfdelta(datetime.timedelta(seconds=self.game_time), '%H:%M:%S'), 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X + 20 + BLOCK_SIZE * k1, 245 + BLOCK_SIZE * 7))
if self.game_over:
text_size_x = FONT.size("GAME")[0]
pygame.draw.rect(win, (0, 0, 0), (
BLOCK_SIZE * (FIELD_SIZE_X / 2) - text_size_x, BLOCK_SIZE * FIELD_SIZE_Y / 2, text_size_x * 2 + 10, 60))
win.blit(FONT.render("GAME", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X / 2 - text_size_x / 2, 5 + BLOCK_SIZE * FIELD_SIZE_Y / 2))
win.blit(FONT.render("OVER", 1, (255, 255, 255)),
(5 + BLOCK_SIZE * FIELD_SIZE_X / 2 - text_size_x / 2, 5 + BLOCK_SIZE * FIELD_SIZE_Y / 2 + 25))
pygame.display.update()
def draw_game_stats(self, on_pause):
win.fill((25, 25, 25))
if on_pause:
win.blit(FONT.render("GAME PAUSED", 1, (255, 255, 255)), (25, 25))
else:
win.blit(FONT.render("STATISTIC", 1, (255, 255, 255)), (25, 25))
total_score = sum(self.score)
win.blit(FONT.render(f"SCORE {total_score:16d}", 1, (255, 255, 255)), (25, 100))
total_pieces = sum(self.pieces)
win.blit(FONT.render(f"PIECES {total_pieces:15d}", 1, (255, 255, 255)), (25, 130))
total_lines = sum(self.cleared_lines)
win.blit(FONT.render(f"LINES {total_lines:16d}", 1, (255, 255, 255)), (25, 160))
try:
tetris_rate = self.cleared_lines[3] / total_lines
except ZeroDivisionError:
tetris_rate = 0
win.blit(FONT.render(f"TETRIS RATE {tetris_rate:10.2%}", 1, (255, 255, 255)), (25, 190))
win.blit(FONT.render(f"LEVELS {self.start_level:02d}-{self.level:02d}", 1, (255, 255, 255)), (25, 220))
win.blit(
FONT.render(f"TIME {strfdelta(datetime.timedelta(seconds=self.game_time), '%H:%M:%S.%Z'):>17s}", 1, (255, 255, 255)),
(25, 250))
win.blit(SMALL_FONT.render(f"BURNED TIMES LINES PIECES TABLE", 1, (255, 255, 255)), (25, 290))
win.blit(
SMALL_FONT.render(f"SINGLE {self.cleared_lines[0]:5d} {self.cleared_lines[0]:5d} L {self.pieces[0]:<4d} J {self.pieces[1]:<4d}", 1, (255, 255, 255)),
(25, 310))
double_times = int(self.cleared_lines[1] / 2)
win.blit(SMALL_FONT.render(f"DOUBLE {double_times:5d} {self.cleared_lines[1]:5d} S {self.pieces[2]:<4d} Z {self.pieces[3]:<4d}", 1, (255, 255, 255)),
(25, 325))
triple_times = int(self.cleared_lines[2] / 3)
win.blit(SMALL_FONT.render(f"TRIPLE {triple_times:5d} {self.cleared_lines[2]:5d} T {self.pieces[4]:<4d} O {self.pieces[6]:<4d}", 1, (255, 255, 255)),
(25, 340))
tetris_times = int(self.cleared_lines[3] / 4)
win.blit(SMALL_FONT.render(f"TETRIS {tetris_times:5d} {self.cleared_lines[3]:5d} I {self.pieces[5]:<10d}", 1, (255, 255, 255)),
(25, 355))
win.blit(SMALL_FONT.render(f"SCORE TABLE", 1, (255, 255, 255)), (25, 380))
win.blit(SMALL_FONT.render(f"DROPS {self.score[0]:7d}", 1, (255, 255, 255)), (25, 400))
win.blit(SMALL_FONT.render(f"SINGLE {self.score[2]:6d}", 1, (255, 255, 255)), (25, 415))
win.blit(SMALL_FONT.render(f"DOUBLE {self.score[3]:6d}", 1, (255, 255, 255)), (25, 430))
win.blit(SMALL_FONT.render(f"TRIPLE {self.score[4]:6d}", 1, (255, 255, 255)), (25, 445))
win.blit(SMALL_FONT.render(f"TETRIS {self.score[5]:6d}", 1, (255, 255, 255)), (25, 460))
pygame.display.update()
def draw_main_menu(selected, sel_lvl, sel_gl):
win.fill((25, 25, 25))
win.blit(FONT.render("TETRIS by dan63047", 1, (255, 255, 255)), (25, 25))
win.blit(FONT.render("", 1, (255, 255, 255)), (25, 100 + 30 * selected))
win.blit(FONT.render("Start", 1, (255, 255, 255)), (50, 100))
win.blit(FONT.render(f"Level: {sel_lvl:02d}", 1, (255, 255, 255)), (50, 130)) # ↑↓
win.blit(FONT.render(f"Guideline: {GUIDELINES[sel_gl]}", 1, (255, 255, 255)), (50, 160))
pygame.display.update()
def main():
GAME_RUN = True
selected_level = 0
selected_gl = 0
ticks_before_stats = 180
g = 0
menu_select = 0
on_pause = False
corrupt_hard_drop = False
field = None
state = "main menu"
pygame.key.set_repeat(267, 100)
while GAME_RUN:
clock.tick(60)
pressed_keys = []
for event in pygame.event.get():
if event.type == pygame.QUIT:
GAME_RUN = False
if event.type == pygame.KEYDOWN:
pressed_keys.append(event.key)
if pygame.K_SPACE in pressed_keys and corrupt_hard_drop:
pressed_keys.remove(pygame.K_SPACE)
if event.type == pygame.KEYUP and event.key == pygame.K_SPACE:
corrupt_hard_drop = False
keys = pygame.key.get_pressed()
if state == "main menu":
draw_main_menu(menu_select, selected_level, selected_gl)
if pygame.K_RETURN in pressed_keys:
if menu_select == 0:
state = "pregameplay"
if pygame.K_DOWN in pressed_keys and menu_select != 2:
menu_select += 1
if pygame.K_UP in pressed_keys and menu_select != 0:
menu_select -= 1
if pygame.K_RIGHT in pressed_keys and selected_level != 29 and menu_select == 1:
selected_level += 1
elif pygame.K_LEFT in pressed_keys and selected_level != 0 and menu_select == 1:
selected_level -= 1
if pygame.K_RIGHT in pressed_keys and selected_gl != 1 and menu_select == 2:
selected_gl += 1
elif pygame.K_LEFT in pressed_keys and selected_gl != 0 and menu_select == 2:
selected_gl -= 1
elif state == "pregameplay":
ticks_before_stats = 300
if selected_gl == 0:
field = TetrisGameplay(level=selected_level)
elif selected_gl == 1:
field = NesLikeTetris(level=selected_level)
pygame.key.set_repeat(field.handling[0], field.handling[1])
state = "gameplay"
elif state == "gameplay":
field.draw_game()
if not field.game_over:
if pygame.K_r in pressed_keys:
state = "pregameplay"
if pygame.K_p in pressed_keys:
on_pause = True
state = "gameplay_stats"
if pygame.K_UP in pressed_keys or pygame.K_x in pressed_keys:
field.spin()
if pygame.K_z in pressed_keys:
field.spin(True)
if pygame.K_c in pressed_keys and field.support_hold and not field.hold_locked:
field.hold_tetromino()
if pygame.K_DOWN in pressed_keys:
g += 1
field.score[0] += 1
if pygame.K_LEFT in pressed_keys:
field.move_side(-1)
if pygame.K_RIGHT in pressed_keys:
field.move_side(1)
if pygame.K_SPACE in pressed_keys and not corrupt_hard_drop and field.can_hard_drop:
field.move_down(True)
field.save_state()
field.clear_lines()
field.spawn_tetromino()
field.lock_delay_run = False
field.lock_delay_frames = 30
corrupt_hard_drop = True
field.game_time += clock.get_time()/1000
if field.game_over:
ticks_before_stats -= 1
if not field.game_over:
g += field.gravity_and_lines_table()[0]
while g >= 1:
if field.support_lock_delay:
if not field.move_down():
field.lock_delay_run = True
else:
if not field.move_down():
field.save_state()
ticks_gone = -field.clear_lines()
field.spawn_tetromino()
g -= 1
if field.lock_delay_run:
field.lock_delay_frames -= 1
if field.lock_delay_frames <= 0 or not field.support_lock_delay:
field.save_state()
field.clear_lines()
field.spawn_tetromino()
field.reset_lock_delay()
if ticks_before_stats <= 0:
state = "gameplay_stats"
elif state == "gameplay_stats":
field.draw_game_stats(on_pause)
if pygame.K_BACKSPACE in pressed_keys:
state = "main menu"
elif pygame.K_r in pressed_keys:
state = "pregameplay"
if pygame.K_p in pressed_keys:
on_pause = False
state = "gameplay"
if __name__ == "__main__":
pygame.init()
win = pygame.display.set_mode((600, 800))
clock = pygame.time.Clock()
pygame.display.set_caption("dan63047 Tetris")
pygame.font.init()
FONT = pygame.font.Font("PressStart2P-vaV7.ttf", 25)
SMALL_FONT = pygame.font.Font("PressStart2P-vaV7.ttf", 15)
main()
pygame.quit()