Update scoring and handling

This commit is contained in:
2026-05-01 18:39:06 -07:00
parent c6c5e2720a
commit c6c77e9091
+89 -25
View File
@@ -300,14 +300,26 @@ class LazyVariable:
class Game: class Game:
NORMAL_PLACE_POINTS = 10 # Points
HARD_PLACE_POINTS = 20 DROP_POINTS = 1
CLEAR_POINTS = 100 HARD_DROP_POINTS = 2
POINTS_PER_LINE = [100, 300, 500, 800]
LINES_PER_LEVEL = 10
MOVE_SPEED = 20 # cells per second MOVE_SPEED = 20 # cells per second
MOVE_INITIAL_SLOWDOWN = 3.0 # cells
DROP_LEVEL_SCALE = 0.025
# These are *= 1 + (DROP_LEVEL_SCALE * (LEVEL - 1))
NORMAL_DROP_SPEED = 3 # cells per second NORMAL_DROP_SPEED = 3 # cells per second
FAST_DROP_SPEED = 30 # cells per second FAST_DROP_SPEED = 30 # cells per second
MOVED_PROP_SPEED = 1 # cells per second MOVED_DROP_SPEED = 1 # cells per second
CLEAR_SPEED = 10 # frames per block
DROP_CLEAR_SCALE = 0.025
# This is *= 1 - (DROP_CLEAR_SCALE * (LEVEL - 1))
CLEAR_SPEED = 15 # frames per block
BOARD_SIZE = Size(10, 20) # Cell count BOARD_SIZE = Size(10, 20) # Cell count
CELL_SIZE = Size(30, 30) CELL_SIZE = Size(30, 30)
CELL_BORDER_SIZE = CELL_SIZE.width // 10 CELL_BORDER_SIZE = CELL_SIZE.width // 10
@@ -391,7 +403,7 @@ class Game:
pygame.K_h, pygame.K_h,
Action.Type.OPEN_HELP, Action.Type.OPEN_HELP,
False, False,
"Toggle the help menu.", "Toggle the help menu/pause.",
), ),
) )
GAME_OVER_ACTION_MAP = Action.make_map( GAME_OVER_ACTION_MAP = Action.make_map(
@@ -620,7 +632,8 @@ class Game:
if cell and self.board[y + cy][x + cx]: if cell and self.board[y + cy][x + cx]:
return True return True
except IndexError: except IndexError:
continue # a cell was out of bounds of the board, this is a hit!
return True
return False return False
def rotate_current_piece(self, times: int): def rotate_current_piece(self, times: int):
@@ -636,8 +649,22 @@ class Game:
return return
def move_current_piece(self, dir: Direction): def move_current_piece(self, dir: Direction):
if self.was_move_down != dir:
self.subcell_move = 0
move_cell = dir
self.was_move_down = dir
self.inhibit_next_move = Game.MOVE_INITIAL_SLOWDOWN
else:
dx = dir * Game.MOVE_SPEED * self.frame_time() dx = dir * Game.MOVE_SPEED * self.frame_time()
self.subcell_move += dx self.subcell_move += dx
if self.inhibit_next_move > 0.0:
oi = self.inhibit_next_move
self.inhibit_next_move -= abs(self.subcell_move)
if self.inhibit_next_move <= 0.0:
self.inhibit_next_move = 0.0
self.subcell_move -= oi
else:
self.subcell_move = 0
move_cell = int(self.subcell_move) move_cell = int(self.subcell_move)
self.subcell_move -= move_cell self.subcell_move -= move_cell
if move_cell: if move_cell:
@@ -658,10 +685,13 @@ class Game:
self.did_user_move_or_spin = True self.did_user_move_or_spin = True
def process_game_actions(self): def process_game_actions(self):
if not ( move_set = {Action.Type.MOVE_LEFT, Action.Type.MOVE_RIGHT} & set(
{Action.Type.MOVE_LEFT, Action.Type.MOVE_RIGHT} self.pending_actions
& set(self.pending_actions) )
): move_both = len(move_set) == 2
if not move_set:
self.was_move_down = None
self.inhibit_next_move = 0.0
self.subcell_move = 0 self.subcell_move = 0
if not ( if not (
{Action.Type.SOFT_DROP, Action.Type.HARD_DROP} {Action.Type.SOFT_DROP, Action.Type.HARD_DROP}
@@ -680,19 +710,17 @@ class Game:
self.soft_drop = True self.soft_drop = True
case Action.Type.HARD_DROP if not self.stop_drop: case Action.Type.HARD_DROP if not self.stop_drop:
self.hard_drop = True self.hard_drop = True
case Action.Type.MOVE_LEFT: case Action.Type.MOVE_LEFT if not move_both:
self.move_current_piece(Direction.LEFT) self.move_current_piece(Direction.LEFT)
case Action.Type.MOVE_RIGHT: self.was_move_down = Direction.LEFT
case Action.Type.MOVE_RIGHT if not move_both:
self.move_current_piece(Direction.RIGHT) self.move_current_piece(Direction.RIGHT)
self.was_move_down = Direction.RIGHT
case Action.Type.RESTART: case Action.Type.RESTART:
self.start_new_game() self.start_new_game()
self.pending_actions = [] self.pending_actions = []
def place_piece(self, x: int, y: int, piece: Tetromino): def place_piece(self, x: int, y: int, piece: Tetromino):
if self.hard_drop:
self.score += Game.HARD_PLACE_POINTS
else:
self.score += Game.NORMAL_PLACE_POINTS
for dy, row in enumerate(piece.shape): for dy, row in enumerate(piece.shape):
for dx, cell in enumerate(row): for dx, cell in enumerate(row):
if cell: if cell:
@@ -711,21 +739,27 @@ class Game:
def advance_piece(self): def advance_piece(self):
speed = Game.NORMAL_DROP_SPEED speed = Game.NORMAL_DROP_SPEED
if self.did_user_move_or_spin: if self.did_user_move_or_spin:
speed = Game.MOVED_PROP_SPEED speed = Game.MOVED_DROP_SPEED
elif self.soft_drop: elif self.soft_drop:
speed = Game.FAST_DROP_SPEED speed = Game.FAST_DROP_SPEED
speed *= 1 + (Game.DROP_CLEAR_SCALE * (self.level - 1))
self.subcell_drop += speed * self.frame_time() self.subcell_drop += speed * self.frame_time()
move_cell = int(self.subcell_drop) move_cell = int(self.subcell_drop)
self.subcell_drop -= move_cell self.subcell_drop -= move_cell
if self.hard_drop: if self.hard_drop:
move_cell = self.cells_for_hard_drop() move_cell = self.cells_for_hard_drop()
if not move_cell:
return
cp = self.current_block_pos cp = self.current_block_pos
if not self.intersects_board( if not self.intersects_board(
cp.x, cp.y + move_cell, self.current_block cp.x, cp.y + move_cell, self.current_block
): ):
self.score += move_cell * Game.DROP_POINTS * self.level
self.current_block_pos = Cell(cp.x, cp.y + move_cell) self.current_block_pos = Cell(cp.x, cp.y + move_cell)
# don't place the piece if the user moved it # don't place the piece if the user moved it
elif not self.did_user_move_or_spin: elif not self.did_user_move_or_spin:
if self.hard_drop:
self.score += move_cell * Game.HARD_DROP_POINTS * self.level
for dy in range(move_cell, -1, -1): for dy in range(move_cell, -1, -1):
if not self.intersects_board( if not self.intersects_board(
cp.x, cp.y + dy, self.current_block cp.x, cp.y + dy, self.current_block
@@ -744,6 +778,11 @@ class Game:
else: else:
return zip(range(bw // 2, -1, -1), range(bw // 2, bw)) return zip(range(bw // 2, -1, -1), range(bw // 2, bw))
def calc_clear_speed(self):
return Game.CLEAR_SPEED * (
1 - (Game.DROP_CLEAR_SCALE * (self.level - 1))
)
def maybe_clear_rows(self): def maybe_clear_rows(self):
def do_single_clear(y: int): def do_single_clear(y: int):
row = self.board[y] row = self.board[y]
@@ -761,16 +800,25 @@ class Game:
if not self.clearing_rows: if not self.clearing_rows:
for y, row in enumerate(self.board): for y, row in enumerate(self.board):
if all(row): if all(row):
self.score += self.CLEAR_POINTS
self.clearing_rows.add(y) self.clearing_rows.add(y)
if self.clearing_rows:
self.score += (
Game.POINTS_PER_LINE[len(self.clearing_rows) - 1]
* self.level
)
else: else:
if self.clearing_frames == self.CLEAR_SPEED: speed = self.calc_clear_speed()
if self.clearing_frames >= speed:
if not any(self.board[next(iter(self.clearing_rows))]): if not any(self.board[next(iter(self.clearing_rows))]):
self.cleared_this_level += len(self.clearing_rows)
if self.cleared_this_level >= 10:
self.level += self.cleared_this_level // 10
self.cleared_this_level %= 10
# Order matters here # Order matters here
for y in sorted(self.clearing_rows): for y in sorted(self.clearing_rows):
drop_rows_above_for_clear(y) drop_rows_above_for_clear(y)
self.clearing_rows = set() self.clearing_rows = set()
self.clearing_frames = self.CLEAR_SPEED self.clearing_frames = speed
else: else:
for y in self.clearing_rows: for y in self.clearing_rows:
do_single_clear(y) do_single_clear(y)
@@ -780,8 +828,9 @@ class Game:
def draw_score_and_instructions(self): def draw_score_and_instructions(self):
text = ( text = (
"Press <h> \nfor help.\n" "Press <h> \nfor help\nor pause.\n"
"\n" "\n"
f"Level: {self.level:02}\n"
f"Score: {self.score:05}\n" f"Score: {self.score:05}\n"
f" HS: {self.high_score:05}" f" HS: {self.high_score:05}"
) )
@@ -955,6 +1004,8 @@ class Game:
self.process_and_draw_help_overlay() self.process_and_draw_help_overlay()
def start_new_game(self): def start_new_game(self):
self.was_move_down = None
self.inhibit_next_move = 0.0
self.cur_random_peice_seq = None self.cur_random_peice_seq = None
self.clear_board() self.clear_board()
self.next_piece = None self.next_piece = None
@@ -965,8 +1016,11 @@ class Game:
self.subcell_drop = 0 self.subcell_drop = 0
self.clearing_rows = set() self.clearing_rows = set()
# immediately clear the first block # immediately clear the first block
self.clearing_frames = Game.CLEAR_SPEED
self.score = 0 self.score = 0
self.level = 1
self.cleared_this_level = 0
# depends on level
self.clearing_frames = self.calc_clear_speed()
self.held_piece = None self.held_piece = None
self.game_over = False self.game_over = False
@@ -1063,8 +1117,8 @@ class Game:
def move_piece_back_to_top(self): def move_piece_back_to_top(self):
self.current_block_pos = Cell( self.current_block_pos = Cell(
self.BOARD_SIZE.width // 2 self.BOARD_SIZE.width // 2
- math.ceil(self.current_block.width / 2), - math.ceil(self.current_block.bounding_box_width / 2),
0, 0 - self.current_block.y_adj,
) )
def swap_in_next_piece(self, force: bool = False): def swap_in_next_piece(self, force: bool = False):
@@ -1525,6 +1579,16 @@ GLYPH_DATA = [
"** ", "** ",
], ],
), ),
Font.Glyph(
"/",
[
" *",
" * ",
" * ",
" * ",
"* ",
],
),
] ]