pytris/pytris.py

1429 lines
67 KiB
Python
Raw Normal View History

2021-05-22 16:39:54 +00:00
from typing import List
import pygame, random, datetime
from string import Template
BLOCK_SIZE = 30
2021-05-23 14:16:36 +00:00
GUIDELINES = ["Current", "NES like"]
2021-05-22 16:39:54 +00:00
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}"
class DeltaTemplate(Template):
delimiter = "%"
2021-05-23 14:16:36 +00:00
2021-05-22 16:39:54 +00:00
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:
2021-05-26 16:05:23 +00:00
def __init__(self, level=1, 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=4):
2021-05-22 16:39:54 +00:00
self.buffer_y = buffer_zone
2021-05-24 19:56:33 +00:00
self.FIELD = list(range(20 + buffer_zone))
2021-05-22 16:39:54 +00:00
y = 0
while y != len(self.FIELD):
2021-05-24 19:56:33 +00:00
self.FIELD[y] = list(range(10))
2021-05-22 16:39:54 +00:00
x = 0
2021-05-24 19:56:33 +00:00
while x != 10:
2021-05-22 16:39:54 +00:00
self.FIELD[y][x] = None
x += 1
y += 1
2021-05-24 19:56:33 +00:00
self.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
]
2021-05-22 16:39:54 +00:00
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
2021-05-25 19:55:39 +00:00
self.soft_drop = False
self.soft_drop_speed = 0.5
2021-05-22 16:39:54 +00:00
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
]
2021-05-23 20:19:31 +00:00
self.score_up = 0
self.for_what_id = 0
2021-05-24 19:56:33 +00:00
self.for_what_score = ["SINGLE", "DOUBLE", "TRIPLE", "QUAD", "T-SPIN MINI", "T-SPIN MINI SINGLE", "T-SPIN MINI DOUBLE", "T-SPIN", "T-SPIN SINGLE", "T-SPIN DOUBLE", "T-SPIN TRIPLE"]
2021-05-23 20:19:31 +00:00
self.for_what_delay = 0
2021-05-22 16:39:54 +00:00
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
2021-05-23 20:19:31 +00:00
self.combo = -1
self.back_to_back = -1
2021-05-22 16:39:54 +00:00
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.lock_delay_run = False
self.lock_delay_frames = 30
self.level = level
2021-05-23 14:16:36 +00:00
self.lines_for_level_up = self.gravity_and_lines_table()[1]
self.start_level = level
2021-05-22 16:39:54 +00:00
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.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}"
2021-05-23 14:16:36 +00:00
def gravity_and_lines_table(self):
2021-05-26 16:05:23 +00:00
return 1 / (0.8 - ((self.level - 1) * 0.007)) ** (self.level - 1) * 0.016666, self.level * 10
2021-05-23 14:16:36 +00:00
2021-05-22 16:39:54 +00:00
def clear_lines(self):
cleared = 0
2021-05-23 20:19:31 +00:00
difficult = False
2021-05-22 16:39:54 +00:00
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
2021-05-24 19:56:33 +00:00
if ic == 10:
2021-05-22 16:39:54 +00:00
cleared += 1
self.FIELD.remove(i)
2021-05-24 19:56:33 +00:00
new = list(range(10))
2021-05-22 16:39:54 +00:00
x = 0
2021-05-24 19:56:33 +00:00
while x != 10:
2021-05-22 16:39:54 +00:00
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
2021-05-23 20:19:31 +00:00
self.combo += 1
self.score_up = 0
2021-05-22 16:39:54 +00:00
if t_spin:
2021-05-23 20:19:31 +00:00
difficult = True
2021-05-22 16:39:54 +00:00
if cleared == 1:
2021-05-26 16:05:23 +00:00
self.score[10] += 800 * min(self.level, 15)
self.score_up += 800 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 8
2021-05-22 16:39:54 +00:00
elif cleared == 2:
2021-05-26 16:05:23 +00:00
self.score[11] += 1200 * min(self.level, 15)
self.score_up += 1200 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 9
2021-05-22 16:39:54 +00:00
elif cleared == 3:
2021-05-26 16:05:23 +00:00
self.score[12] += 1600 * min(self.level, 15)
self.score_up += 1600 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 10
2021-05-22 16:39:54 +00:00
elif t_spin_mini:
2021-05-23 20:19:31 +00:00
difficult = True
2021-05-22 16:39:54 +00:00
if cleared == 1:
2021-05-26 16:05:23 +00:00
self.score[7] += 200 * min(self.level, 15)
self.score_up += 200 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 5
2021-05-22 16:39:54 +00:00
elif cleared == 2:
2021-05-26 16:05:23 +00:00
self.score[8] += 400 * min(self.level, 15)
self.score_up += 400 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 6
else:
if cleared == 1:
2021-05-26 16:05:23 +00:00
self.score[2] += 100 * min(self.level, 15)
self.score_up += 100 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 0
elif cleared == 2:
2021-05-26 16:05:23 +00:00
self.score[3] += 300 * min(self.level, 15)
self.score_up += 300 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 1
elif cleared == 3:
2021-05-26 16:05:23 +00:00
self.score[4] += 500 * min(self.level, 15)
self.score_up += 500 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 2
elif cleared == 4:
2021-05-26 16:05:23 +00:00
self.score[5] += 800 * min(self.level, 15)
self.score_up += 800 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 3
difficult = True
2021-05-26 16:05:23 +00:00
if sum(self.cleared_lines) >= self.lines_for_level_up and self.level != 15:
2021-05-22 16:39:54 +00:00
self.level += 1
self.lines_for_level_up += 10
2021-05-23 20:19:31 +00:00
if difficult:
self.back_to_back += 1
if self.back_to_back > 0:
self.score[14] += int((self.score_up*3/2) - self.score_up)
self.score_up += int((self.score_up*3/2) - self.score_up)
else:
self.back_to_back = -1
if self.combo > 0:
2021-05-26 16:05:23 +00:00
self.score[13] += 50 * self.combo * min(self.level, 15)
self.score_up += 50 * self.combo * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_delay = 3
2021-05-22 16:39:54 +00:00
else:
2021-05-23 20:19:31 +00:00
self.combo = -1
2021-05-22 16:39:54 +00:00
if t_spin:
2021-05-26 16:05:23 +00:00
self.score[9] += 400 * min(self.level, 15)
self.score_up = 400 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 7
self.for_what_delay = 3
2021-05-22 16:39:54 +00:00
elif t_spin_mini:
2021-05-26 16:05:23 +00:00
self.score[6] += 100 * min(self.level, 15)
self.score_up = 100 * min(self.level, 15)
2021-05-23 20:19:31 +00:00
self.for_what_id = 4
self.for_what_delay = 3
2021-05-22 16:39:54 +00:00
return 0
def collision(self, next_posx, next_posy, next_id, next_spin_id):
i1 = next_posy
k1 = next_posx
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[next_id][next_spin_id]:
2021-05-22 16:39:54 +00:00
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()
2021-05-25 19:55:39 +00:00
if self.current_id is not None and self.current_id != 6:
2021-05-22 16:39:54 +00:00
if reverse:
future_spin_id = self.current_spin_id - 1
else:
future_spin_id = self.current_spin_id + 1
2021-05-26 16:05:23 +00:00
future_spin_id %= len(self.TETROMINOS[self.current_id])
2021-05-22 16:39:54 +00:00
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:
2021-05-25 19:55:39 +00:00
if self.current_id is not None and self.current_id != 5:
2021-05-22 16:39:54 +00:00
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):
2021-05-25 19:55:39 +00:00
if self.current_id is not None and not self.collision(self.current_posx + x_change, self.current_posy, self.current_id, self.current_spin_id):
2021-05-22 16:39:54 +00:00
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:
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.current_id][self.current_spin_id]:
2021-05-22 16:39:54 +00:00
for k in i:
if k:
self.FIELD[i1][k1] = k
k1 += 1
k1 = self.current_posx
i1 += 1
2021-05-23 20:19:31 +00:00
self.pieces[self.current_id] += 1
2021-05-22 16:39:54 +00:00
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
2021-05-23 14:16:36 +00:00
def move_down(self, instant=False):
2021-05-25 19:55:39 +00:00
if self.current_id is not None and not self.collision(self.current_posx, self.current_posy + 1, self.current_id, self.current_spin_id):
2021-05-22 16:39:54 +00:00
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
2021-05-25 19:55:39 +00:00
if self.soft_drop:
self.score[0] += 1
2021-05-22 16:39:54 +00:00
return True
else:
return False
def draw_game(self):
win.fill((25, 25, 25))
pygame.draw.rect(win, (0, 0, 0),
2021-05-24 19:56:33 +00:00
(130, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * 10, BLOCK_SIZE * 20))
2021-05-22 16:39:54 +00:00
x = 0
y = -self.buffer_y
for i in self.FIELD:
for k in i:
2021-05-24 19:56:33 +00:00
window_x = 130 + BLOCK_SIZE * x
2021-05-22 16:39:54 +00:00
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:
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.current_id][self.current_spin_id]:
2021-05-22 16:39:54 +00:00
for k in i:
if k is not None:
2021-05-24 19:56:33 +00:00
window_x = 130 + BLOCK_SIZE * k1
2021-05-22 16:39:54 +00:00
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
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.current_id][self.current_spin_id]:
2021-05-22 16:39:54 +00:00
for k in i:
if k is not None:
2021-05-24 19:56:33 +00:00
window_x = 130 + BLOCK_SIZE * k1
2021-05-22 16:39:54 +00:00
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * i1
2021-05-25 19:55:39 +00:00
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=5, border_radius=1)
2021-05-22 16:39:54 +00:00
k1 += 1
k1 = self.current_posx
i1 += 1
2021-05-25 19:55:39 +00:00
y_offset = 0
2021-05-22 16:39:54 +00:00
for q in range(0, self.next_length):
i1 = 0
k1 = 0
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.next_queue[q]][0]:
2021-05-22 16:39:54 +00:00
for k in i:
if k is not None:
2021-05-25 19:55:39 +00:00
window_x = 470 + 25 * k1
window_y = 65 + 25 * i1 + y_offset
pygame.draw.rect(win, k.color, (window_x, window_y, 25, 25))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 25, 25), width=2)
2021-05-22 16:39:54 +00:00
k1 += 1
k1 = 0
i1 += 1
2021-05-25 19:55:39 +00:00
y_offset += 60
2021-05-22 16:39:54 +00:00
if self.support_hold:
if self.hold_id is not None:
i1 = 0
k1 = 0
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.hold_id][0]:
2021-05-22 16:39:54 +00:00
for k in i:
if k is not None:
2021-05-25 19:55:39 +00:00
window_x = 20 + 25 * k1
window_y = 65 + 25 * i1
pygame.draw.rect(win, k.color, (window_x, window_y, 25, 25))
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 25, 25), width=1)
2021-05-22 16:39:54 +00:00
k1 += 1
k1 = 0
i1 += 1
2021-05-24 19:56:33 +00:00
win.blit(MEDIUM_FONT.render("SCORE", 1, (255, 255, 255)), (440, 562))
win.blit(MEDIUM_FONT.render(f"{sum(self.score):5d}", 1, (255, 255, 255)), (440, 582))
win.blit(MEDIUM_FONT.render("LINES", 1, (255, 255, 255)), (440, 622))
win.blit(MEDIUM_FONT.render(f"{sum(self.cleared_lines):5d}", 1, (255, 255, 255)), (440, 642))
win.blit(MEDIUM_FONT.render("LV", 1, (255, 255, 255)), (80, 502))
win.blit(MEDIUM_FONT.render(f"{self.level:02d}", 1, (255, 255, 255)), (80, 522))
win.blit(MEDIUM_FONT.render("PPS", 1, (255, 255, 255)), (60, 562))
2021-05-22 16:39:54 +00:00
try:
2021-05-23 14:16:36 +00:00
pps = sum(self.pieces) / self.game_time
2021-05-22 16:39:54 +00:00
except ZeroDivisionError:
2021-05-23 14:16:36 +00:00
pps = 0
2021-05-24 19:56:33 +00:00
win.blit(MEDIUM_FONT.render(f"{pps:6.2f}", 1, (255, 255, 255)), (0, 582))
win.blit(MEDIUM_FONT.render("TIME", 1, (255, 255, 255)), (40, 622))
win.blit(MEDIUM_FONT.render(f"{strfdelta(datetime.timedelta(seconds=self.game_time), '%m:%S'):>6s}", 1, (255, 255, 255)), (0, 642))
if self.for_what_delay > 0.1:
2021-05-23 20:19:31 +00:00
win.blit(FONT.render(self.for_what_score[self.for_what_id], 1, (230*(min(self.for_what_delay, 1))+25, 230*(min(self.for_what_delay, 1))+25, 230*(min(self.for_what_delay, 1))+25)),
2021-05-25 19:55:39 +00:00
(300-int(FONT.size(self.for_what_score[self.for_what_id])[0]/2), 670))
2021-05-23 20:19:31 +00:00
win.blit(
FONT.render(f"+{self.score_up}", 1, (230*(min(self.for_what_delay, 1))+25, 230*(min(self.for_what_delay, 1))+25, 230*(min(self.for_what_delay, 1))+25)),
2021-05-25 19:55:39 +00:00
(300-int(FONT.size(f"+{self.score_up}")[0]/2), 695))
2021-05-23 20:19:31 +00:00
if self.combo > 0:
win.blit(
FONT.render(f"COMBO × {self.combo}", 1, (
230 * (min(self.for_what_delay, 1)) + 25, 230 * (min(self.for_what_delay, 1)) + 25,
230 * (min(self.for_what_delay, 1)) + 25)),
2021-05-25 19:55:39 +00:00
(300-int(FONT.size(f"COMBO × {self.combo}")[0]/2), 720))
2021-05-23 20:19:31 +00:00
if self.back_to_back > 0:
win.blit(
FONT.render(f"BACK-TO-BACK × {self.back_to_back}", 1, (
230 * (min(self.for_what_delay, 1)) + 25, 230 * (min(self.for_what_delay, 1)) + 25,
230 * (min(self.for_what_delay, 1)) + 25)),
2021-05-25 19:55:39 +00:00
(300-int(FONT.size(f"BACK-TO-BACK × {self.back_to_back}")[0]/2), 745))
2021-05-22 16:39:54 +00:00
if self.game_over:
text_size_x = FONT.size("GAME")[0]
2021-05-25 19:55:39 +00:00
pygame.draw.rect(win, (0, 0, 0), (223, 327, text_size_x+10, 60))
pygame.draw.rect(win, (255, 0, 0), (223, 327, text_size_x + 10, 60), width=2)
win.blit(FONT.render("GAME", 1, (255, 255, 255)), (230, 335))
win.blit(FONT.render("OVER", 1, (255, 255, 255)), (230, 360))
2021-05-22 16:39:54 +00:00
pygame.display.update()
2021-05-28 14:45:15 +00:00
def draw_game_stats(self):
2021-05-22 16:39:54 +00:00
win.fill((25, 25, 25))
2021-05-28 14:45:15 +00:00
if self.game_over:
2021-05-22 16:39:54 +00:00
win.blit(FONT.render("STATISTIC", 1, (255, 255, 255)), (25, 25))
2021-05-28 14:45:15 +00:00
else:
win.blit(FONT.render("GAME PAUSED", 1, (255, 255, 255)), (25, 25))
2021-05-22 16:39:54 +00:00
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:
2021-05-23 14:16:36 +00:00
pps = total_pieces / self.game_time
2021-05-22 16:39:54 +00:00
except ZeroDivisionError:
2021-05-23 14:16:36 +00:00
pps = 0
win.blit(FONT.render(f"PIECES PER SECOND {pps:0.2f}", 1, (255, 255, 255)), (25, 190))
2021-05-22 16:39:54 +00:00
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)
2021-05-24 19:56:33 +00:00
win.blit(SMALL_FONT.render(f"QUAD {tetris_times:7d} {self.cleared_lines[3]:5d} I {self.pieces[5]:<10d}", 1, (255, 255, 255)),
2021-05-22 16:39:54 +00:00
(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))
2021-05-24 19:56:33 +00:00
win.blit(SMALL_FONT.render(f"QUAD {self.score[5]:20d}", 1, (255, 255, 255)), (25, 475))
2021-05-22 16:39:54 +00:00
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):
2021-05-25 19:55:39 +00:00
def __init__(self, level=0):
super().__init__(level, 2, False, False, False, False, False, False, (267, 100), True, 1)
2021-05-26 16:05:23 +00:00
self.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]
]
], # 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]
]
], # 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],
[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, 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]
]
], # 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
]
2021-05-22 16:39:54 +00:00
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
2021-05-23 14:16:36 +00:00
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
2021-05-22 16:39:54 +00:00
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
2021-05-24 19:56:33 +00:00
if ic == 10:
2021-05-22 16:39:54 +00:00
cleared += 1
self.FIELD.remove(i)
2021-05-24 19:56:33 +00:00
new = list(range(10))
2021-05-22 16:39:54 +00:00
x = 0
2021-05-24 19:56:33 +00:00
while x != 10:
2021-05-22 16:39:54 +00:00
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))
2021-05-23 20:19:31 +00:00
if cleared > 0:
2021-05-22 16:39:54 +00:00
self.cleared_lines[cleared - 1] += cleared
frames_delay += 18
2021-05-23 20:19:31 +00:00
self.score_up = 0
self.for_what_delay = 3
2021-05-22 16:39:54 +00:00
if cleared == 1:
self.score[2] += 40 * (min(self.level, 29) + 1)
2021-05-23 20:19:31 +00:00
self.score_up += 40 * (min(self.level, 29) + 1)
self.for_what_id = 0
2021-05-22 16:39:54 +00:00
elif cleared == 2:
self.score[3] += 100 * (min(self.level, 29) + 1)
2021-05-23 20:19:31 +00:00
self.score_up += 100 * (min(self.level, 29) + 1)
self.for_what_id = 1
2021-05-22 16:39:54 +00:00
elif cleared == 3:
self.score[4] += 300 * (min(self.level, 29) + 1)
2021-05-23 20:19:31 +00:00
self.score_up += 300 * (min(self.level, 29) + 1)
self.for_what_id = 2
2021-05-22 16:39:54 +00:00
elif cleared == 4:
self.score[5] += 1200 * (min(self.level, 29) + 1)
2021-05-23 20:19:31 +00:00
self.score_up += 1200 * (min(self.level, 29) + 1)
self.for_what_id = 3
2021-05-22 16:39:54 +00:00
if sum(self.cleared_lines) >= self.lines_for_level_up:
self.level += 1
self.lines_for_level_up += 10
return frames_delay
2021-05-23 14:16:36 +00:00
def draw_game(self):
win.fill((25, 25, 25))
pygame.draw.rect(win, (0, 0, 0),
2021-05-25 19:55:39 +00:00
(130, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * 10, BLOCK_SIZE * 20))
2021-05-23 14:16:36 +00:00
x = 0
y = -self.buffer_y
for i in self.FIELD:
for k in i:
2021-05-25 19:55:39 +00:00
window_x = 130 + BLOCK_SIZE * x
2021-05-23 14:16:36 +00:00
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:
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.current_id][self.current_spin_id]:
2021-05-23 14:16:36 +00:00
for k in i:
if k is not None:
2021-05-25 19:55:39 +00:00
window_x = 130 + BLOCK_SIZE * k1
2021-05-23 14:16:36 +00:00
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
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.next_queue[q]][0]:
2021-05-23 14:16:36 +00:00
for k in i:
if k is not None:
2021-05-25 19:55:39 +00:00
window_x = 130 + BLOCK_SIZE * 10 + 20 + BLOCK_SIZE * k1 + x_offset
2021-05-23 14:16:36 +00:00
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
2021-05-24 19:56:33 +00:00
for i in self.TETROMINOS[self.hold_id][0]:
2021-05-23 14:16:36 +00:00
for k in i:
if k is not None:
2021-05-25 19:55:39 +00:00
window_x = 10 + BLOCK_SIZE * k1
window_y = (BLOCK_SIZE * 2 + 30) + 10 * 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)
2021-05-23 14:16:36 +00:00
k1 += 1
k1 = 0
i1 += 1
2021-05-25 19:55:39 +00:00
win.blit(MEDIUM_FONT.render("SCORE", 1, (255, 255, 255)), (440, 562))
win.blit(MEDIUM_FONT.render(f"{sum(self.score):5d}", 1, (255, 255, 255)), (440, 582))
win.blit(MEDIUM_FONT.render("LINES", 1, (255, 255, 255)), (440, 622))
win.blit(MEDIUM_FONT.render(f"{sum(self.cleared_lines):5d}", 1, (255, 255, 255)), (440, 642))
win.blit(MEDIUM_FONT.render("LV", 1, (255, 255, 255)), (80, 502))
win.blit(MEDIUM_FONT.render(f"{self.level:02d}", 1, (255, 255, 255)), (80, 522))
win.blit(MEDIUM_FONT.render("TRT", 1, (255, 255, 255)), (60, 562))
2021-05-23 14:16:36 +00:00
try:
2021-05-25 19:55:39 +00:00
tetris_rate = int((self.cleared_lines[3] / sum(self.cleared_lines)) * 100)
2021-05-23 14:16:36 +00:00
except ZeroDivisionError:
tetris_rate = 0
if tetris_rate == 100:
2021-05-25 19:55:39 +00:00
win.blit(MEDIUM_FONT.render(f"{tetris_rate}", 1, (255, 255, 255)), (60, 582))
2021-05-23 14:16:36 +00:00
else:
2021-05-25 19:55:39 +00:00
win.blit(MEDIUM_FONT.render(f"{tetris_rate:02d}%", 1, (255, 255, 255)), (60, 582))
win.blit(MEDIUM_FONT.render("TIME", 1, (255, 255, 255)), (40, 622))
win.blit(MEDIUM_FONT.render(f"{strfdelta(datetime.timedelta(seconds=self.game_time), '%m:%S'):>6s}", 1, (255, 255, 255)), (0, 642))
if self.for_what_delay > 0.1:
win.blit(FONT.render(self.for_what_score[self.for_what_id], 1, (
230 * (min(self.for_what_delay, 1)) + 25, 230 * (min(self.for_what_delay, 1)) + 25,
230 * (min(self.for_what_delay, 1)) + 25)),
(120, 670))
2021-05-23 20:19:31 +00:00
win.blit(
2021-05-25 19:55:39 +00:00
FONT.render(f"+{self.score_up}", 1, (
230 * (min(self.for_what_delay, 1)) + 25, 230 * (min(self.for_what_delay, 1)) + 25,
230 * (min(self.for_what_delay, 1)) + 25)),
(120, 695))
2021-05-23 14:16:36 +00:00
if self.game_over:
text_size_x = FONT.size("GAME")[0]
2021-05-25 19:55:39 +00:00
pygame.draw.rect(win, (0, 0, 0), (223, 327, text_size_x + 10, 60))
pygame.draw.rect(win, (255, 0, 0), (223, 327, text_size_x + 10, 60), width=2)
win.blit(FONT.render("GAME", 1, (255, 255, 255)), (230, 335))
win.blit(FONT.render("OVER", 1, (255, 255, 255)), (230, 360))
2021-05-23 14:16:36 +00:00
pygame.display.update()
2021-05-28 14:45:15 +00:00
def draw_game_stats(self):
2021-05-23 14:16:36 +00:00
win.fill((25, 25, 25))
2021-05-28 14:45:15 +00:00
if self.game_over:
2021-05-23 14:16:36 +00:00
win.blit(FONT.render("STATISTIC", 1, (255, 255, 255)), (25, 25))
2021-05-28 14:45:15 +00:00
else:
win.blit(FONT.render("GAME PAUSED", 1, (255, 255, 255)), (25, 25))
2021-05-23 14:16:36 +00:00
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)
2021-05-24 19:56:33 +00:00
win.blit(SMALL_FONT.render(f"QUAD {tetris_times:7d} {self.cleared_lines[3]:5d} I {self.pieces[5]:<10d}", 1, (255, 255, 255)),
2021-05-23 14:16:36 +00:00
(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))
2021-05-24 19:56:33 +00:00
win.blit(SMALL_FONT.render(f"QUAD {self.score[5]:8d}", 1, (255, 255, 255)), (25, 460))
2021-05-23 14:16:36 +00:00
pygame.display.update()
2021-05-22 16:39:54 +00:00
def draw_main_menu(selected, sel_lvl, sel_gl):
win.fill((25, 25, 25))
2021-05-28 14:45:15 +00:00
win.blit(FONT.render("PYTRIS by dan63047", 1, (255, 255, 255)), (25, 25))
2021-05-22 16:39:54 +00:00
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
2021-05-26 16:05:23 +00:00
selected_level = [1, 0]
2021-05-22 16:39:54 +00:00
selected_gl = 0
ticks_before_stats = 180
2021-05-23 14:16:36 +00:00
g = 0
2021-05-25 19:55:39 +00:00
delay_before_spawn = -1
2021-05-22 16:39:54 +00:00
menu_select = 0
on_pause = False
2021-05-25 19:55:39 +00:00
corrupted_keys = []
2021-05-22 16:39:54 +00:00
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)
2021-05-25 19:55:39 +00:00
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE and pygame.K_SPACE in corrupted_keys:
corrupted_keys.remove(pygame.K_SPACE)
if event.key == pygame.K_DOWN and state == "gameplay":
corrupted_keys.append(pygame.K_DOWN)
if event.key == pygame.K_UP and state == "gameplay" and pygame.K_UP in corrupted_keys:
corrupted_keys.remove(pygame.K_UP)
if event.key == pygame.K_x and state == "gameplay" and pygame.K_x in corrupted_keys:
corrupted_keys.remove(pygame.K_x)
if event.key == pygame.K_z and pygame.K_z in corrupted_keys:
corrupted_keys.remove(pygame.K_z)
for i in pressed_keys:
if i in corrupted_keys:
pressed_keys.remove(i)
2021-05-22 16:39:54 +00:00
keys = pygame.key.get_pressed()
if state == "main menu":
2021-05-26 16:05:23 +00:00
draw_main_menu(menu_select, selected_level[selected_gl], selected_gl)
2021-05-22 16:39:54 +00:00
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
2021-05-26 16:05:23 +00:00
if pygame.K_RIGHT in pressed_keys and menu_select == 1 and ((selected_gl == 0 and selected_level[selected_gl] != 15) or (selected_gl == 1 and selected_level[selected_gl] != 29)):
selected_level[selected_gl] += 1
elif pygame.K_LEFT in pressed_keys and menu_select == 1 and ((selected_gl == 0 and selected_level[selected_gl] != 1) or (selected_gl == 1 and selected_level[selected_gl] != 0)):
selected_level[selected_gl] -= 1
2021-05-22 16:39:54 +00:00
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
2021-05-25 19:55:39 +00:00
delay_before_spawn = -1
2021-05-22 16:39:54 +00:00
if selected_gl == 0:
2021-05-26 16:05:23 +00:00
field = TetrisGameplay(selected_level[selected_gl])
2021-05-23 14:16:36 +00:00
elif selected_gl == 1:
2021-05-26 16:05:23 +00:00
field = NesLikeTetris(selected_level[selected_gl])
2021-05-22 16:39:54 +00:00
pygame.key.set_repeat(field.handling[0], field.handling[1])
state = "gameplay"
elif state == "gameplay":
field.draw_game()
if not field.game_over:
2021-05-23 14:16:36 +00:00
if pygame.K_r in pressed_keys:
2021-05-22 16:39:54 +00:00
state = "pregameplay"
2021-05-23 14:16:36 +00:00
if pygame.K_p in pressed_keys:
2021-05-22 16:39:54 +00:00
on_pause = True
state = "gameplay_stats"
2021-05-26 16:05:23 +00:00
if pygame.K_UP in pressed_keys:
2021-05-22 16:39:54 +00:00
field.spin()
2021-05-25 19:55:39 +00:00
corrupted_keys.append(pygame.K_UP)
2021-05-26 16:05:23 +00:00
if pygame.K_x in pressed_keys:
field.spin()
2021-05-25 19:55:39 +00:00
corrupted_keys.append(pygame.K_x)
2021-05-23 14:16:36 +00:00
if pygame.K_z in pressed_keys:
2021-05-22 16:39:54 +00:00
field.spin(True)
2021-05-25 19:55:39 +00:00
corrupted_keys.append(pygame.K_z)
2021-05-23 14:16:36 +00:00
if pygame.K_c in pressed_keys and field.support_hold and not field.hold_locked:
2021-05-22 16:39:54 +00:00
field.hold_tetromino()
2021-05-23 14:16:36 +00:00
if pygame.K_DOWN in pressed_keys:
2021-05-25 19:55:39 +00:00
field.soft_drop = True
if pygame.K_DOWN in corrupted_keys:
field.soft_drop = False
corrupted_keys.remove(pygame.K_DOWN)
2021-05-23 14:16:36 +00:00
if pygame.K_LEFT in pressed_keys:
2021-05-22 16:39:54 +00:00
field.move_side(-1)
2021-05-23 14:16:36 +00:00
if pygame.K_RIGHT in pressed_keys:
2021-05-22 16:39:54 +00:00
field.move_side(1)
2021-05-25 19:55:39 +00:00
if pygame.K_SPACE in pressed_keys and field.can_hard_drop:
2021-05-23 14:16:36 +00:00
field.move_down(True)
2021-05-22 16:39:54 +00:00
field.save_state()
2021-05-23 14:16:36 +00:00
field.clear_lines()
2021-05-22 16:39:54 +00:00
field.spawn_tetromino()
field.lock_delay_run = False
field.lock_delay_frames = 30
2021-05-25 19:55:39 +00:00
corrupted_keys.append(pygame.K_SPACE)
2021-05-22 16:39:54 +00:00
field.game_time += clock.get_time()/1000
2021-05-23 20:19:31 +00:00
if field.for_what_delay > 0:
field.for_what_delay -= clock.get_time()/1000
2021-05-22 16:39:54 +00:00
if field.game_over:
ticks_before_stats -= 1
2021-05-23 14:16:36 +00:00
if not field.game_over:
g += field.gravity_and_lines_table()[0]
2021-05-25 19:55:39 +00:00
if field.soft_drop:
g += field.soft_drop_speed
2021-05-23 20:19:31 +00:00
if g > 22:
g = 22
2021-05-23 14:16:36 +00:00
while g >= 1:
2021-05-22 16:39:54 +00:00
if field.support_lock_delay:
if not field.move_down():
field.lock_delay_run = True
else:
if not field.move_down():
2021-05-25 19:55:39 +00:00
if delay_before_spawn == -1:
field.save_state()
delay_before_spawn = field.clear_lines()
field.current_id = None
2021-05-23 14:16:36 +00:00
g -= 1
2021-05-25 19:55:39 +00:00
if field.nes_mechanics:
if delay_before_spawn > -1:
delay_before_spawn -= 1
if delay_before_spawn == 0:
field.spawn_tetromino()
2021-05-22 16:39:54 +00:00
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()
2021-05-23 14:16:36 +00:00
field.clear_lines()
2021-05-22 16:39:54 +00:00
field.spawn_tetromino()
field.reset_lock_delay()
if ticks_before_stats <= 0:
state = "gameplay_stats"
elif state == "gameplay_stats":
2021-05-28 14:45:15 +00:00
field.draw_game_stats()
2021-05-22 16:39:54 +00:00
if pygame.K_BACKSPACE in pressed_keys:
2021-05-26 16:05:23 +00:00
pygame.key.set_repeat(267, 100)
2021-05-22 16:39:54 +00:00
state = "main menu"
elif pygame.K_r in pressed_keys:
state = "pregameplay"
2021-05-23 20:19:31 +00:00
if pygame.K_p in pressed_keys and not field.game_over:
2021-05-22 16:39:54 +00:00
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)
2021-05-24 19:56:33 +00:00
MEDIUM_FONT = pygame.font.Font("PressStart2P-vaV7.ttf", 20)
2021-05-22 16:39:54 +00:00
SMALL_FONT = pygame.font.Font("PressStart2P-vaV7.ttf", 15)
main()
pygame.quit()