pytris/pytris.py

1856 lines
90 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.

import time
from typing import List
import pygame, random, datetime, threading
from string import Template
BLOCK_SIZE = 30
GUIDELINES = ["Modern", "Classic"]
MODES = ["Endless", "Time limited", "Lines limited", "vs Bot (Garbage)", "vs Bot (Score)"]
TIME_LIMITS_SEC = [120, 180, 300, 600, 1800, 3600, 86400]
LINES_LIMITS = [40, 80, 120, 150, 300, 500, 1000]
state = "main menu"
menu_select = 0
selected_gl = 0
selected_mode = 0 # 0 - Endless; 1 - Time limit; 2 - Lines limit
selected_target = 0
session = []
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 = "%"
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 + hours*60
return t.substitute(**d)
class TetrisGameplay:
def __init__(self, mode=0, lvl=1, buffer_zone=20, player="P1", 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):
self.buffer_y = buffer_zone
self.FIELD = list(range(20 + buffer_zone))
y = 0
while y != len(self.FIELD):
self.FIELD[y] = list(range(10))
x = 0
while x != len(self.FIELD[y]):
self.FIELD[y][x] = None
x += 1
y += 1
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
]
self.g = 0
self.current_posx = 4
self.player = player
self.current_posy = self.buffer_y - 2
self.can_hard_drop = hard_drop
self.mode = mode
self.support_combo_and_btb_bonuses = False
self.support_srs = srs
self.handling = handling
self.support_hold = hold
self.soft_drop = False
self.soft_drop_speed = 0.5
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.score_up = 0
self.for_what_id = 0
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"]
self.notification = {"for_what": None, "mode": None, "number": None, "combo": None, "b2b": None, "t-spin": False, "t-spin_mini": False, "pc": False, "game_time": None}
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.combo = -1
self.back_to_back = -1
self.next_queue = []
self.garbage_queue = []
self.attack = 0
self.send_attack = 0
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
if self.mode == 0:
self.level = lvl
self.start_level = lvl
else:
self.level = 1
self.start_level = 1
self.level_limit = 30
self.lock_delay_f_limit = min(30, 90 - 3 * self.level)
self.lock_delay_frames = self.lock_delay_f_limit
self.lock_delay_times_left = 15
if self.mode == 1:
self.target = TIME_LIMITS_SEC[lvl]
elif self.mode == 2:
self.target = LINES_LIMITS[lvl]
elif self.mode == 3:
self.target = 1
self.support_garbage = True
self.lines_for_level_up = self.gravity_and_lines_table()[1]
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.lock_delay_times_left = 15
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
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
def clear_lines(self):
cleared = 0
self.send_attack = 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 = sum(
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
for i in t_spin_corners[self.current_spin_id][0]
)
back_col = sum(
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
for i in t_spin_corners[self.current_spin_id][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 = sum(k is not None for k in i)
if ic == 10:
cleared += 1
self.FIELD.remove(i)
new = list(range(10))
x = 0
while x != 10:
new[x] = None
x += 1
self.FIELD.insert(0, new)
y -= 1
if ic > 0 and height is None:
height = y
if cleared > 0:
difficult = False
self._extracted_from_clear_lines_58(cleared, t_spin, difficult, t_spin_mini)
else:
self.combo = -1
if t_spin:
self._extracted_from_clear_lines_125(9, 400, 7, t_spin, t_spin_mini)
elif t_spin_mini:
self._extracted_from_clear_lines_125(6, 100, 4, t_spin, t_spin_mini)
self.attack += self.send_attack
if self.support_garbage:
return 0, self.send_attack
else:
return 0
def _extracted_from_clear_lines_58(self, cleared, t_spin, difficult, t_spin_mini):
self.cleared_lines[cleared - 1] += cleared
wt = 0
if self.mode == 2:
self.target -= cleared
if self.target <= 0:
self.target = 0
self.game_over = True
self.combo += 1
self.score_up = 0
if t_spin:
difficult = True
if cleared == 1:
self.send_attack = 2
self.score[10] += 800 * min(self.level, self.level_limit)
self.score_up += 800 * min(self.level, self.level_limit)
wt = 8
elif cleared == 2:
self.send_attack = 4
self.score[11] += 1200 * min(self.level, self.level_limit)
self.score_up += 1200 * min(self.level, self.level_limit)
wt = 9
elif cleared == 3:
self.send_attack = 6
self.score[12] += 1600 * min(self.level, self.level_limit)
self.score_up += 1600 * min(self.level, self.level_limit)
wt = 10
elif t_spin_mini:
difficult = True
if cleared == 1:
self.send_attack = 0
self.score[7] += 200 * min(self.level, self.level_limit)
self.score_up += 200 * min(self.level, self.level_limit)
wt = 5
elif cleared == 2:
self.send_attack = 1
self.score[8] += 400 * min(self.level, self.level_limit)
self.score_up += 400 * min(self.level, self.level_limit)
wt = 6
elif cleared == 1:
self.send_attack = 0
self.score[2] += 100 * min(self.level, self.level_limit)
self.score_up += 100 * min(self.level, self.level_limit)
wt = 0
elif cleared == 2:
self.send_attack = 1
self.score[3] += 300 * min(self.level, self.level_limit)
self.score_up += 300 * min(self.level, self.level_limit)
wt = 1
elif cleared == 3:
self.send_attack = 2
self.score[4] += 500 * min(self.level, self.level_limit)
self.score_up += 500 * min(self.level, self.level_limit)
wt = 2
elif cleared == 4:
self.send_attack = 4
self.score[5] += 800 * min(self.level, self.level_limit)
self.score_up += 800 * min(self.level, self.level_limit)
wt = 3
difficult = True
if sum(self.cleared_lines) >= self.lines_for_level_up and self.level != self.level_limit and self.mode != 3:
self.level += 1
self.lines_for_level_up += 10
self.lock_delay_f_limit = min(30, 90 - 3 * self.level)
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)
self.send_attack += 1
else:
self.back_to_back = -1
if self.combo > 0:
self.send_attack += int(self.combo / 3) + 1
self.score[13] += 50 * self.combo * min(self.level, self.level_limit)
self.score_up += 50 * self.combo * min(self.level, self.level_limit)
self.notification = {"for_what": wt, "mode": "score", "number": self.score_up, "combo": self.combo,
"b2b": self.back_to_back, "t-spin": t_spin, "t-spin_mini": t_spin_mini,
"pc": False, "game_time": self.game_time}
self.for_what_delay = 3
def _extracted_from_clear_lines_125(self, wt_id, scr, what, t_spin, t_spin_mini):
self.score[wt_id] += scr * min(self.level, self.level_limit)
self.score_up = scr * min(self.level, self.level_limit)
self.notification = {"for_what": what, "mode": "score", "number": scr, "combo": self.combo, "b2b": self.back_to_back, "t-spin": t_spin, "t-spin_mini": t_spin_mini, "pc": False, "game_time": self.game_time}
def collision(self, next_posx, next_posy, figure_id, spin_id):
i1 = next_posy
k1 = next_posx
for i in self.TETROMINOS[figure_id][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):
if self.current_id is not None and 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 %= len(self.TETROMINOS[self.current_id])
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
if self.support_srs:
if self.current_id is not None and self.current_id != 5:
if self.current_spin_id in [0, 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
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
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
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
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
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
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
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
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
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
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
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
elif self.current_spin_id == 3 and future_spin_id in [0, 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
if self.lock_delay_run:
if self.lock_delay_times_left > 0:
self.reset_lock_delay()
if self.collision(self.current_posx, self.current_posy+1, self.current_id, self.current_spin_id):
self.lock_delay_run = True
else:
self.lock_delay_frames = 0
def move_side(self, x_change):
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):
self.current_posx += x_change
if self.lock_delay_run:
if self.lock_delay_times_left > 0:
self.reset_lock_delay()
if self.collision(self.current_posx, self.current_posy+1, self.current_id, self.current_spin_id):
self.lock_delay_run = True
else:
self.lock_delay_frames = 1
self.spin_is_last_move = False
def save_state(self):
k1 = self.current_posx
if self.current_id is not None:
i1 = self.current_posy
for i in self.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
self.pieces[self.current_id] += 1
if len(self.garbage_queue) > 0:
garbage_row = [None, Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30)), Block((30, 30, 30))]
random.shuffle(garbage_row)
for i in range(self.garbage_queue[0]):
self.FIELD.append(garbage_row)
self.FIELD.remove(self.FIELD[0])
self.garbage_queue.remove(self.garbage_queue[0])
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 = self.lock_delay_f_limit
self.lock_delay_run = False
self.lock_delay_times_left -= 1
def move_down(self, instant=False):
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):
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
if self.soft_drop:
self.score[0] += 1
return True
else:
return False
def bot_move(self):
bot_move = self.player.run_ai(self)
if bot_move == "S+":
self.spin()
elif bot_move == "R":
self.move_side(1)
elif bot_move == "L":
self.move_side(-1)
elif bot_move == "HD":
self.move_down(True)
self.save_state()
self.clear_lines()
self.spawn_tetromino()
self.lock_delay_run = False
self.lock_delay_frames = 30
self.lock_delay_times_left = 15
def draw_game(self):
win.fill((25, 25, 25))
pygame.draw.rect(win, (0, 0, 0),
(130, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * 10, BLOCK_SIZE * 20))
x = 0
y = -self.buffer_y
for i in self.FIELD:
for k in i:
window_x = 130 + 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 self.TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k is not None:
window_x = 130 + BLOCK_SIZE * k1
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * i1
pygame.draw.rect(win, (int(k.color[0]*self.lock_delay_frames/max(self.lock_delay_f_limit, 1)), int(k.color[1]*self.lock_delay_frames/max(self.lock_delay_f_limit, 1)), int(k.color[2]*self.lock_delay_frames/max(self.lock_delay_f_limit, 1))), (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 self.TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k is not None:
window_x = 130 + 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=5, border_radius=1)
k1 += 1
k1 = self.current_posx
i1 += 1
y_offset = 0
for q in range(0, self.next_length):
i1 = 0
k1 = 0
for i in self.TETROMINOS[self.next_queue[q]][0]:
for k in i:
if k is not None:
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)
k1 += 1
k1 = 0
i1 += 1
y_offset += 60
if self.support_hold:
if self.hold_id is not None:
i1 = 0
k1 = 0
for i in self.TETROMINOS[self.hold_id][0]:
for k in i:
if k is not None:
window_x = 20 + 25 * k1
window_y = 65 + 25 * i1
if self.hold_locked:
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 25, 25))
else:
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)
k1 += 1
k1 = 0
i1 += 1
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))
try:
pps = sum(self.pieces) / self.game_time
except ZeroDivisionError:
pps = 0
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.mode > 0:
if self.mode == 1:
win.blit(MEDIUM_FONT.render("TIME", 1, (255, 255, 255)), (440, 482))
win.blit(MEDIUM_FONT.render(f"{strfdelta(datetime.timedelta(seconds=self.target), '%m:%S')}", 1, (255, 255, 255)), (440, 522))
elif self.mode == 2:
win.blit(MEDIUM_FONT.render("LINES", 1, (255, 255, 255)), (440, 482))
win.blit(MEDIUM_FONT.render(f"{self.target:5d}", 1, (255, 255, 255)), (440, 522))
win.blit(MEDIUM_FONT.render("LEFT", 1, (255, 255, 255)), (440, 502))
if self.notification['for_what'] is not None and self.notification['game_time']+2.9 >= self.game_time:
win.blit(FONT.render(self.for_what_score[self.notification['for_what']], 1, (230*(min(self.notification['game_time']+3-self.game_time, 1))+25, 230*(min(self.notification['game_time']+3-self.game_time, 1))+25, 230*(min(self.notification['game_time']+3-self.game_time, 1))+25)),
(300-int(FONT.size(self.for_what_score[self.notification['for_what']])[0]/2), 670))
win.blit(
FONT.render(f"+{self.notification['number']}", 1, (230*(min(self.notification['game_time']+3-self.game_time, 1))+25, 230*(min(self.notification['game_time']+3-self.game_time, 1))+25, 230*(min(self.notification['game_time']+3-self.game_time, 1))+25)),
(300-int(FONT.size(f"+{self.notification['number']}")[0]/2), 695))
if self.notification["combo"] > 0:
win.blit(
FONT.render(f"COMBO × {self.notification['combo']}", 1, (
230 * (min(self.notification['game_time']+3-self.game_time, 1)) + 25, 230 * (min(self.notification['game_time']+3-self.game_time, 1)) + 25,
230 * (min(self.notification['game_time']+3-self.game_time, 1)) + 25)),
(300-int(FONT.size(f"COMBO × {self.notification['combo']}")[0]/2), 720))
if self.notification['b2b'] > 0:
win.blit(
FONT.render(f"BACK-TO-BACK × {self.notification['b2b']}", 1, (
230 * (min(self.notification['game_time']+3-self.game_time, 1)) + 25, 230 * (min(self.notification['game_time']+3-self.game_time, 1)) + 25,
230 * (min(self.notification['game_time']+3-self.game_time, 1)) + 25)),
(300-int(FONT.size(f"BACK-TO-BACK × {self.notification['b2b']}")[0]/2), 745))
if self.game_over:
text_size_x = FONT.size("GAME")[0]
pygame.draw.rect(win, (0, 0, 0), (223, 327, text_size_x+10, 60))
if self.mode > 0 and self.target <= 0:
pygame.draw.rect(win, (0, 255, 0), (223, 327, text_size_x + 10, 60), width=2)
win.blit(FONT.render("WELL", 1, (255, 255, 255)), (230, 335))
win.blit(FONT.render("DONE", 1, (255, 255, 255)), (230, 360))
else:
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))
pygame.display.update()
def draw_game_stats(self):
win.fill((25, 25, 25))
if self.game_over:
win.blit(FONT.render("STATISTIC", 1, (255, 255, 255)), (25, 25))
else:
win.blit(FONT.render("GAME PAUSED", 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"QUAD {tetris_times:7d} {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"QUAD {self.score[5]:20d}", 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 ClassicTetris(TetrisGameplay):
def __init__(self, mode=0, target=0, player="P1"):
super().__init__(mode, target, 2, player, False, False, False, False, False, False, (267, 100), True, 1)
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
]
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 == 10:
cleared += 1
self.FIELD.remove(i)
new = list(range(10))
x = 0
while x != 10:
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
if self.mode == 2:
self.target -= cleared
if self.target <= 0:
self.target = 0
self.game_over = True
frames_delay += 18
self.score_up = 0
self.for_what_delay = 3
if cleared == 1:
self.score[2] += 40 * (min(self.level, 29) + 1)
self.score_up += 40 * (min(self.level, 29) + 1)
self.for_what_id = 0
elif cleared == 2:
self.score[3] += 100 * (min(self.level, 29) + 1)
self.score_up += 100 * (min(self.level, 29) + 1)
self.for_what_id = 1
elif cleared == 3:
self.score[4] += 300 * (min(self.level, 29) + 1)
self.score_up += 300 * (min(self.level, 29) + 1)
self.for_what_id = 2
elif cleared == 4:
self.score[5] += 1200 * (min(self.level, 29) + 1)
self.score_up += 1200 * (min(self.level, 29) + 1)
self.for_what_id = 3
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),
(130, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * 10, BLOCK_SIZE * 20))
x = 0
y = -self.buffer_y
for i in self.FIELD:
for k in i:
window_x = 130 + 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 self.TETROMINOS[self.current_id][self.current_spin_id]:
for k in i:
if k is not None:
window_x = 130 + 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 self.TETROMINOS[self.next_queue[q]][0]:
for k in i:
if k is not None:
window_x = 130 + BLOCK_SIZE * 10 + 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 self.TETROMINOS[self.hold_id][0]:
for k in i:
if k is not None:
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)
k1 += 1
k1 = 0
i1 += 1
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))
try:
tetris_rate = int((self.cleared_lines[3] / sum(self.cleared_lines)) * 100)
except ZeroDivisionError:
tetris_rate = 0
if tetris_rate == 100:
win.blit(MEDIUM_FONT.render(f"{tetris_rate}", 1, (255, 255, 255)), (60, 582))
else:
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.mode > 0:
if self.mode == 1:
win.blit(MEDIUM_FONT.render("TIME", 1, (255, 255, 255)), (440, 482))
win.blit(MEDIUM_FONT.render(f"{strfdelta(datetime.timedelta(seconds=self.target), '%m:%S')}", 1, (255, 255, 255)), (440, 522))
elif self.mode == 2:
win.blit(MEDIUM_FONT.render("LINES", 1, (255, 255, 255)), (440, 482))
win.blit(MEDIUM_FONT.render(f"{self.target:5d}", 1, (255, 255, 255)), (440, 522))
win.blit(MEDIUM_FONT.render("LEFT", 1, (255, 255, 255)), (440, 502))
if self.notification['for_what'] is not None and self.notification['game_time'] + 2.9 >= self.game_time:
win.blit(FONT.render(self.for_what_score[self.notification['for_what']], 1, (
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25)),
(300 - int(FONT.size(self.for_what_score[self.notification['for_what']])[0] / 2), 670))
win.blit(
FONT.render(f"+{self.notification['number']}", 1, (
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25)),
(300 - int(FONT.size(f"+{self.notification['number']}")[0] / 2), 695))
if self.notification["combo"] > 0:
win.blit(
FONT.render(f"COMBO × {self.notification['combo']}", 1, (
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25)),
(300 - int(FONT.size(f"COMBO × {self.notification['combo']}")[0] / 2), 720))
if self.notification['b2b'] > 0:
win.blit(
FONT.render(f"BACK-TO-BACK × {self.notification['b2b']}", 1, (
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25,
230 * (min(self.notification['game_time'] + 3 - self.game_time, 1)) + 25)),
(300 - int(FONT.size(f"BACK-TO-BACK × {self.notification['b2b']}")[0] / 2), 745))
if self.game_over:
text_size_x = FONT.size("GAME")[0]
pygame.draw.rect(win, (0, 0, 0), (223, 327, text_size_x + 10, 60))
if self.mode > 0 and self.target <= 0:
pygame.draw.rect(win, (0, 255, 0), (223, 327, text_size_x + 10, 60), width=2)
win.blit(FONT.render("WELL", 1, (255, 255, 255)), (230, 335))
win.blit(FONT.render("DONE", 1, (255, 255, 255)), (230, 360))
else:
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))
pygame.display.update()
def draw_game_stats(self):
win.fill((25, 25, 25))
if self.game_over:
win.blit(FONT.render("STATISTIC", 1, (255, 255, 255)), (25, 25))
else:
win.blit(FONT.render("GAME PAUSED", 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"QUAD {tetris_times:7d} {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"QUAD {self.score[5]:8d}", 1, (255, 255, 255)), (25, 460))
pygame.display.update()
class BotAI:
def __init__(self):
self.skill = 0
self.name = "Bot"
self.state = "idle"
self.selected_move = (None, None) # rot, pos
def generate_moves(self, field):
test_spin_id = 0
test_mino_id = field.current_id
test_hold_mino_id = field.hold_id if field.hold_id else field.next_queue[0]
moves = []
while test_spin_id < len(field.TETROMINOS[test_mino_id]):
x = 0
while x < len(field.FIELD[0]):
if not field.collision(x, 0, test_mino_id, test_spin_id):
y = field.current_posy
while not field.collision(x, y + 1, test_mino_id, test_spin_id):
y += 1
moves.append((test_spin_id, x, y, False))
x += 1
test_spin_id += 1
test_spin_id = 0
while test_spin_id < len(field.TETROMINOS[test_hold_mino_id]):
x = 0
while x < len(field.FIELD[0]):
if not field.collision(x, 0, test_hold_mino_id, test_spin_id):
y = field.current_posy
while not field.collision(x, y + 1, test_hold_mino_id, test_spin_id):
y += 1
moves.append((test_spin_id, x, y, True))
x += 1
test_spin_id += 1
answer = random.choice(moves)
return answer[0], answer[1]
def run_ai(self, field):
if self.selected_move == (None, None):
self.selected_move = self.generate_moves(field)
print(self.selected_move)
if field.current_spin_id != self.selected_move[0] and field.current_id != 6:
return "S+"
elif field.current_posx < self.selected_move[1]:
return "R"
elif field.current_posx > self.selected_move[1]:
return "L"
else:
self.selected_move = (None, None)
return "HD"
def draw_main_menu(selected, sel_gl, sel_md, sel_trg):
win.fill((25, 25, 25))
win.blit(FONT.render("PYTRIS 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"Mode: {MODES[sel_md]}", 1, (255, 255, 255)), (50, 130))
if sel_md == 0:
win.blit(FONT.render(f"Level: {sel_trg:02d}", 1, (255, 255, 255)), (50, 160)) # ↑↓
elif sel_md == 1:
win.blit(FONT.render(f"Time: {strfdelta(datetime.timedelta(seconds=TIME_LIMITS_SEC[sel_trg]), '%H:%M:%S.%Z')}", 1, (255, 255, 255)), (50, 160))
elif sel_md == 2:
win.blit(FONT.render(f"Lines: {LINES_LIMITS[sel_trg]}", 1, (255, 255, 255)), (50, 160))
win.blit(FONT.render(f"Guideline: {GUIDELINES[sel_gl]}", 1, (255, 255, 255)), (50, 190))
win.blit(FONT.render(f"Exit", 1, (255, 255, 255)), (50, 220))
pygame.display.update()
def mind_of_stupid_idiot():
while not session[1].game_over:
try:
session[1].bot_move()
time.sleep(0.25)
except IndexError:
time.sleep(1)
except Exception as e:
print(e)
def draw_vs_field(field, offset, size=30):
pygame.draw.rect(win, (0, 0, 0), (offset + 100, (BLOCK_SIZE * 2 + 5), BLOCK_SIZE * 10, BLOCK_SIZE * 20))
x = 0
y = -field.buffer_y
for i in field.FIELD:
for k in i:
window_x = offset + 100 + 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 = field.current_posy - field.buffer_y
k1 = field.current_posx
if field.current_id is not None:
for i in field.TETROMINOS[field.current_id][field.current_spin_id]:
for k in i:
if k is not None:
window_x = offset + 100 + BLOCK_SIZE * k1
window_y = (BLOCK_SIZE * 2 + 5) + BLOCK_SIZE * i1
pygame.draw.rect(win, (
int(k.color[0] * field.lock_delay_frames / max(field.lock_delay_f_limit, 1)),
int(k.color[1] * field.lock_delay_frames / max(field.lock_delay_f_limit, 1)),
int(k.color[2] * field.lock_delay_frames / max(field.lock_delay_f_limit, 1))),
(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 = field.current_posx
i1 += 1
if field.support_ghost_piece:
i1 = field.ghost_piece_y() - field.buffer_y
k1 = field.current_posx
for i in field.TETROMINOS[field.current_id][field.current_spin_id]:
for k in i:
if k is not None:
window_x = offset + 100 + 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=5, border_radius=1)
k1 += 1
k1 = field.current_posx
i1 += 1
y_offset = 0
for q in range(field.next_length):
k1 = 0
for i1, i in enumerate(field.TETROMINOS[field.next_queue[q]][0]):
for k in i:
if k is not None:
window_x = offset + 440 + 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)
k1 += 1
k1 = 0
y_offset += 60
garbage_meter_y_offset = 640
garbage_meter_color = (255, 0, 0)
for g in field.garbage_queue:
for i in range(g):
window_y = garbage_meter_y_offset
pygame.draw.rect(win, garbage_meter_color, (offset + 60, window_y, 30, 30), width=2)
garbage_meter_y_offset -= 30
garbage_meter_color = (255, 120, 0)
garbage_meter_y_offset -= 5
if field.support_hold and field.hold_id is not None:
k1 = 0
for i1, i in enumerate(field.TETROMINOS[field.hold_id][0]):
for k in i:
if k is not None:
window_x = offset + 20 + 25 * k1
window_y = 30 + 25 * i1
if field.hold_locked:
pygame.draw.rect(win, (0, 0, 0), (window_x, window_y, 25, 25))
else:
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)
k1 += 1
k1 = 0
try:
pps = sum(field.pieces) / field.game_time
except ZeroDivisionError:
pps = 0
try:
apm = field.attack / (field.game_time / 60)
except ZeroDivisionError:
apm = 0
win.blit(MEDIUM_FONT.render(f"{pps:.2f} PPS", 1, (255, 255, 255)), (offset + 410, 522))
win.blit(MEDIUM_FONT.render(f"{sum(field.pieces)} P", 1, (255, 255, 255)), (offset + 410, 542))
win.blit(MEDIUM_FONT.render(strfdelta(datetime.timedelta(seconds=field.game_time), '%m:%S'), 1, (255, 255, 255)), (offset + 410, 502))
win.blit(MEDIUM_FONT.render(f"{apm:.0f} APM", 1, (255, 255, 255)), (offset + 410, 562))
win.blit(MEDIUM_FONT.render(f"{field.attack} ATK", 1, (255, 255, 255)), (offset + 410, 582))
if field.mode == 3:
try:
win.blit(MEDIUM_FONT.render(field.player, 1, (255, 255, 255)), (offset + 410, 482))
except:
win.blit(MEDIUM_FONT.render(field.player.name, 1, (255, 255, 255)), (offset + 410, 482))
if field.notification['for_what'] is not None and field.notification['game_time'] + 2.9 >= field.game_time:
win.blit(FONT.render(field.for_what_score[field.notification['for_what']], 1, (
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25)),
(300 - int(FONT.size(field.for_what_score[field.notification['for_what']])[0] / 2), 670))
win.blit(
FONT.render(f"+{field.notification['number']}", 1, (
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25)),
(300 - int(FONT.size(f"+{field.notification['number']}")[0] / 2), 695))
if field.notification["combo"] > 0:
win.blit(
FONT.render(f"COMBO × {field.notification['combo']}", 1, (
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25)),
(300 - int(FONT.size(f"COMBO × {field.notification['combo']}")[0] / 2), 720))
if field.notification['b2b'] > 0:
win.blit(
FONT.render(f"BACK-TO-BACK × {field.notification['b2b']}", 1, (
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25,
230 * (min(field.notification['game_time'] + 3 - field.game_time, 1)) + 25)),
(300 - int(FONT.size(f"BACK-TO-BACK × {field.notification['b2b']}")[0] / 2), 745))
if field.game_over:
text_size_x = FONT.size("GAME")[0]
pygame.draw.rect(win, (0, 0, 0), (offset + 223, 327, text_size_x + 10, 60))
if field.mode > 0 and field.target <= 0:
pygame.draw.rect(win, (0, 255, 0), (offset + 223, 327, text_size_x + 10, 60), width=2)
win.blit(FONT.render("WELL", 1, (255, 255, 255)), (offset + 230, 335))
win.blit(FONT.render("DONE", 1, (255, 255, 255)), (offset + 230, 360))
else:
pygame.draw.rect(win, (255, 0, 0), (offset + 223, 327, text_size_x + 10, 60), width=2)
win.blit(FONT.render("GAME", 1, (255, 255, 255)), (offset + 230, 335))
win.blit(FONT.render("OVER", 1, (255, 255, 255)), (offset + 230, 360))
def draw_vs_gameplay(session):
win.fill((25, 25, 25))
for f in session:
if f.player == "P1":
draw_vs_field(f, 0)
elif len(session) == 2 and f.player.name == "Bot":
draw_vs_field(f, 600)
pygame.display.update()
def render_main():
while True:
if state == "main menu":
draw_main_menu(menu_select, selected_gl, selected_mode, selected_target)
elif state == "gameplay":
if selected_mode == 3:
draw_vs_gameplay(session)
else:
session[0].draw_game()
elif state == "gameplay_stats":
session[0].draw_game_stats()
def main():
GAME_RUN = True
global session
ticks_before_stats = 180
global state
global menu_select
global selected_gl
global selected_mode
global selected_target
delay_before_spawn = -1
on_pause = False
corrupted_keys = []
field = None
piece_movement = 0
incoming_garbage = []
first_movement = True
render_tread = threading.Thread(name="drawing", target=render_main, daemon=True)
render_tread.start()
movement_delay = 0
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:
if event.key == pygame.K_LEFT:
piece_movement = -1
elif event.key == pygame.K_RIGHT:
piece_movement = 1
pressed_keys.append(event.key)
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)
if event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
piece_movement = 0
first_movement = True
try:
movement_delay = field.handling[0]
except:
pass
for i in pressed_keys:
if i in corrupted_keys:
pressed_keys.remove(i)
keys = pygame.key.get_pressed()
if state == "main menu":
if pygame.K_RETURN in pressed_keys and menu_select == 0:
state = "pregameplay"
if pygame.K_DOWN in pressed_keys and menu_select != 4:
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_mode != 3 and menu_select == 1:
selected_mode += 1
selected_target = 0
elif pygame.K_LEFT in pressed_keys and selected_mode != 0 and menu_select == 1:
selected_mode -= 1
selected_target = 0
if selected_mode == 0:
if pygame.K_RIGHT in pressed_keys and menu_select == 2 and selected_target != 30:
selected_target += 1
elif pygame.K_LEFT in pressed_keys and menu_select == 2 and selected_target != 0:
selected_target -= 1
elif selected_mode == 1:
if pygame.K_RIGHT in pressed_keys and menu_select == 2 and selected_target != len(TIME_LIMITS_SEC)-1:
selected_target += 1
elif pygame.K_LEFT in pressed_keys and menu_select == 2 and selected_target != 0:
selected_target -= 1
elif selected_mode == 2:
if pygame.K_RIGHT in pressed_keys and menu_select == 2 and selected_target != len(LINES_LIMITS)-1:
selected_target += 1
elif pygame.K_LEFT in pressed_keys and menu_select == 2 and selected_target != 0:
selected_target -= 1
if pygame.K_RIGHT in pressed_keys and selected_gl != 1 and menu_select == 3:
selected_gl += 1
elif pygame.K_LEFT in pressed_keys and selected_gl != 0 and menu_select == 3:
selected_gl -= 1
if pygame.K_RETURN in pressed_keys and menu_select == 4:
GAME_RUN = False
elif state == "pregameplay":
ticks_before_stats = 300
delay_before_spawn = -1
if selected_gl == 0:
session = [TetrisGameplay(selected_mode, max(selected_target, 1))] if selected_mode == 0 else [TetrisGameplay(selected_mode, selected_target)]
if selected_mode == 3:
session.append(TetrisGameplay(selected_mode, selected_target, player=BotAI()))
bots_tread = threading.Thread(name="pendehos", target=mind_of_stupid_idiot, daemon=True)
bots_tread.start()
pygame.display.set_mode((1200, 800))
elif selected_gl == 1:
session = [ClassicTetris(selected_mode, selected_target)]
if selected_mode == 3:
session.append(ClassicTetris(selected_mode, selected_target, BotAI()))
bots_tread = threading.Thread(name="pendehos", target=mind_of_stupid_idiot, daemon=True)
bots_tread.start()
pygame.display.set_mode((1200, 800))
pygame.key.set_repeat(session[0].handling[0], session[0].handling[1])
movement_delay = session[0].handling[0]
state = "gameplay"
elif state == "gameplay":
field = session[0]
frame_time = clock.get_time()/1000
if not field.game_over:
if field.player == "P1":
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:
field.spin()
corrupted_keys.append(pygame.K_UP)
if pygame.K_x in pressed_keys:
field.spin()
corrupted_keys.append(pygame.K_x)
if pygame.K_z in pressed_keys:
field.spin(True)
corrupted_keys.append(pygame.K_z)
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:
field.soft_drop = True
if pygame.K_DOWN in corrupted_keys:
field.soft_drop = False
corrupted_keys.remove(pygame.K_DOWN)
if pygame.K_SPACE in pressed_keys and field.can_hard_drop:
field.move_down(True)
_extracted_from_main_136(field, incoming_garbage)
field.lock_delay_run = False
field.lock_delay_frames = 30
corrupted_keys.append(pygame.K_SPACE)
if piece_movement != 0:
movement_delay -= frame_time * 1000
if first_movement:
field.move_side(piece_movement)
first_movement = False
elif movement_delay <= 0:
field.move_side(piece_movement)
movement_delay = field.handling[1]
for field in session:
field.game_time += frame_time
for gb in incoming_garbage:
if gb[1] != field.player:
field.garbage_queue.append(gb[0])
incoming_garbage.remove(gb)
if field.mode == 1:
field.target -= frame_time
if field.target <= 0:
field.target = 0
field.game_over = True
elif field.mode == 3 and field.target == 0:
field.game_over = True
if not field.game_over:
field.g += field.gravity_and_lines_table()[0]
if field.soft_drop:
field.g += field.soft_drop_speed
field.g = min(field.g, 22)
while field.g >= 1:
if field.support_lock_delay:
if not field.move_down() or field.collision(field.current_posx, field.current_posy+1, field.current_id, field.current_spin_id):
field.lock_delay_run = True
elif (
not field.move_down()
and delay_before_spawn == -1
):
field.save_state()
delay_before_spawn = field.clear_lines()
field.current_id = None
field.g -= 1
if field.nes_mechanics:
if delay_before_spawn > -1:
delay_before_spawn -= 1
if delay_before_spawn == 0:
field.spawn_tetromino()
if field.lock_delay_run:
if field.lock_delay_frames > 0:
field.lock_delay_frames -= 1
if (field.lock_delay_frames == 0 or not field.support_lock_delay) and field.collision(field.current_posx, field.current_posy+1, field.current_id, field.current_spin_id):
_extracted_from_main_136(field, incoming_garbage)
field.reset_lock_delay()
field.reset_lock_delay()
for field in session:
if field.game_over:
if field.player == "P1":
ticks_before_stats -= 1
elif field.mode == 3:
session[0].target -= 1
if ticks_before_stats <= 0:
state = "gameplay_stats"
elif state == "gameplay_stats":
if pygame.K_BACKSPACE in pressed_keys:
pygame.key.set_repeat(267, 100)
pygame.display.set_mode((600, 800))
state = "main menu"
elif pygame.K_r in pressed_keys:
state = "pregameplay"
if pygame.K_p in pressed_keys and not session[0].game_over:
on_pause = False
state = "gameplay"
def _extracted_from_main_136(field, ig):
field.save_state()
if field.mode == 3:
atk =field.clear_lines()[1]
if atk > 0:
ig.append([atk, field.player])
else:
field.clear_lines()
field.spawn_tetromino()
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)
MEDIUM_FONT = pygame.font.Font("PressStart2P-vaV7.ttf", 20)
SMALL_FONT = pygame.font.Font("PressStart2P-vaV7.ttf", 15)
main()
pygame.quit()