Update scoring and handling
This commit is contained in:
@@ -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,10 +649,24 @@ class Game:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def move_current_piece(self, dir: Direction):
|
def move_current_piece(self, dir: Direction):
|
||||||
dx = dir * Game.MOVE_SPEED * self.frame_time()
|
if self.was_move_down != dir:
|
||||||
self.subcell_move += dx
|
self.subcell_move = 0
|
||||||
move_cell = int(self.subcell_move)
|
move_cell = dir
|
||||||
self.subcell_move -= move_cell
|
self.was_move_down = dir
|
||||||
|
self.inhibit_next_move = Game.MOVE_INITIAL_SLOWDOWN
|
||||||
|
else:
|
||||||
|
dx = dir * Game.MOVE_SPEED * self.frame_time()
|
||||||
|
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)
|
||||||
|
self.subcell_move -= move_cell
|
||||||
if move_cell:
|
if move_cell:
|
||||||
new_pos = Cell(
|
new_pos = Cell(
|
||||||
self.current_block_pos.x + move_cell, self.current_block_pos.y
|
self.current_block_pos.x + move_cell, self.current_block_pos.y
|
||||||
@@ -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(
|
||||||
|
"/",
|
||||||
|
[
|
||||||
|
" *",
|
||||||
|
" * ",
|
||||||
|
" * ",
|
||||||
|
" * ",
|
||||||
|
"* ",
|
||||||
|
],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user