Hard drops and fix moving
This commit is contained in:
@@ -96,7 +96,8 @@ class Action(NamedTuple):
|
|||||||
# In Game
|
# In Game
|
||||||
ROTATE_LEFT = auto()
|
ROTATE_LEFT = auto()
|
||||||
ROTATE_RIGHT = auto()
|
ROTATE_RIGHT = auto()
|
||||||
DROP = auto()
|
SOFT_DROP = auto()
|
||||||
|
HARD_DROP = auto()
|
||||||
SWAP_HOLD = auto()
|
SWAP_HOLD = auto()
|
||||||
MOVE_LEFT = auto()
|
MOVE_LEFT = auto()
|
||||||
MOVE_RIGHT = auto()
|
MOVE_RIGHT = auto()
|
||||||
@@ -259,11 +260,13 @@ class LazyVariable:
|
|||||||
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
PLACE_POINTS = 10
|
NORMAL_PLACE_POINTS = 10
|
||||||
|
HARD_PLACE_POINTS = 20
|
||||||
CLEAR_POINTS = 100
|
CLEAR_POINTS = 100
|
||||||
MOVE_SPEED = 20 # cells per second
|
MOVE_SPEED = 20 # cells per second
|
||||||
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
|
||||||
CLEAR_SPEED = 5 # frames per block
|
CLEAR_SPEED = 5 # 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)
|
||||||
@@ -307,13 +310,22 @@ class Game:
|
|||||||
"Rotate the current piece right.",
|
"Rotate the current piece right.",
|
||||||
),
|
),
|
||||||
Action(
|
Action(
|
||||||
pygame.K_s,
|
pygame.K_c,
|
||||||
Action.Type.SWAP_HOLD,
|
Action.Type.SWAP_HOLD,
|
||||||
False,
|
False,
|
||||||
"Swap the current and held pieces.",
|
"Swap the current and held pieces.",
|
||||||
),
|
),
|
||||||
Action(
|
Action(
|
||||||
pygame.K_DOWN, Action.Type.DROP, True, "Drop the current piece."
|
pygame.K_DOWN,
|
||||||
|
Action.Type.SOFT_DROP,
|
||||||
|
True,
|
||||||
|
"Soft drop the current piece.",
|
||||||
|
),
|
||||||
|
Action(
|
||||||
|
pygame.K_SPACE,
|
||||||
|
Action.Type.HARD_DROP,
|
||||||
|
True,
|
||||||
|
"Hard drop the current piece.",
|
||||||
),
|
),
|
||||||
Action(
|
Action(
|
||||||
pygame.K_LEFT,
|
pygame.K_LEFT,
|
||||||
@@ -537,6 +549,8 @@ class Game:
|
|||||||
self.handle_key_event(event.type, event.key)
|
self.handle_key_event(event.type, event.key)
|
||||||
|
|
||||||
def swap_with_hold(self):
|
def swap_with_hold(self):
|
||||||
|
if self.did_swap:
|
||||||
|
return
|
||||||
to_swap = self.held_piece
|
to_swap = self.held_piece
|
||||||
used_next = False
|
used_next = False
|
||||||
if not to_swap:
|
if not to_swap:
|
||||||
@@ -544,6 +558,8 @@ class Game:
|
|||||||
used_next = True
|
used_next = True
|
||||||
if not self.intersects_board(*self.current_block_pos, to_swap):
|
if not self.intersects_board(*self.current_block_pos, to_swap):
|
||||||
self.held_piece = self.current_block
|
self.held_piece = self.current_block
|
||||||
|
self.move_piece_back_to_top()
|
||||||
|
self.did_swap = True
|
||||||
self.current_block = to_swap
|
self.current_block = to_swap
|
||||||
if used_next:
|
if used_next:
|
||||||
self.next_piece = self.random_tetromino()
|
self.next_piece = self.random_tetromino()
|
||||||
@@ -570,8 +586,7 @@ class Game:
|
|||||||
if not self.intersects_board(*np, new_piece):
|
if not self.intersects_board(*np, new_piece):
|
||||||
self.current_block = new_piece
|
self.current_block = new_piece
|
||||||
self.current_block_pos = np
|
self.current_block_pos = np
|
||||||
# give the user some leeway to splin many times
|
self.did_user_move_or_spin = True
|
||||||
self.subcell_drop = 0
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def move_current_piece(self, dir: Direction):
|
def move_current_piece(self, dir: Direction):
|
||||||
@@ -579,8 +594,7 @@ class Game:
|
|||||||
self.subcell_move += dx
|
self.subcell_move += dx
|
||||||
move_cell = int(self.subcell_move)
|
move_cell = int(self.subcell_move)
|
||||||
self.subcell_move -= move_cell
|
self.subcell_move -= move_cell
|
||||||
# give the user some leeway to move in tight quarters
|
self.did_user_move_or_spin = True
|
||||||
self.subcell_drop = 0
|
|
||||||
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
|
||||||
@@ -594,7 +608,10 @@ class Game:
|
|||||||
& set(self.pending_actions)
|
& set(self.pending_actions)
|
||||||
):
|
):
|
||||||
self.subcell_move = 0
|
self.subcell_move = 0
|
||||||
if Action.Type.DROP not in self.pending_actions:
|
if not (
|
||||||
|
{Action.Type.SOFT_DROP, Action.Type.HARD_DROP}
|
||||||
|
& set(self.pending_actions)
|
||||||
|
):
|
||||||
self.stop_drop = False
|
self.stop_drop = False
|
||||||
while self.pending_actions:
|
while self.pending_actions:
|
||||||
match self.pending_actions.pop(0):
|
match self.pending_actions.pop(0):
|
||||||
@@ -604,8 +621,10 @@ class Game:
|
|||||||
self.rotate_current_piece(Direction.RIGHT)
|
self.rotate_current_piece(Direction.RIGHT)
|
||||||
case Action.Type.SWAP_HOLD:
|
case Action.Type.SWAP_HOLD:
|
||||||
self.swap_with_hold()
|
self.swap_with_hold()
|
||||||
case Action.Type.DROP if not self.stop_drop:
|
case Action.Type.SOFT_DROP if not self.stop_drop:
|
||||||
self.doing_drop = True
|
self.soft_drop = True
|
||||||
|
case Action.Type.HARD_DROP if not self.stop_drop:
|
||||||
|
self.hard_drop = True
|
||||||
case Action.Type.MOVE_LEFT:
|
case Action.Type.MOVE_LEFT:
|
||||||
self.move_current_piece(Direction.LEFT)
|
self.move_current_piece(Direction.LEFT)
|
||||||
case Action.Type.MOVE_RIGHT:
|
case Action.Type.MOVE_RIGHT:
|
||||||
@@ -615,33 +634,50 @@ class 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):
|
||||||
self.score += Game.PLACE_POINTS
|
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:
|
||||||
self.board[y + dy][x + dx] = piece.color
|
self.board[y + dy][x + dx] = piece.color
|
||||||
# prevent user from accidentally dropping next piece too
|
# prevent user from accidentally dropping next piece too
|
||||||
self.stop_drop = True
|
self.stop_drop = True
|
||||||
|
self.subcell_drop = 0
|
||||||
|
|
||||||
|
def cells_for_hard_drop(self):
|
||||||
|
cx, cy = self.current_block_pos
|
||||||
|
dy = 0
|
||||||
|
while not self.intersects_board(cx, cy + dy, self.current_block):
|
||||||
|
dy += 1
|
||||||
|
return dy
|
||||||
|
|
||||||
def advance_piece(self):
|
def advance_piece(self):
|
||||||
speed = (
|
speed = Game.NORMAL_DROP_SPEED
|
||||||
Game.FAST_DROP_SPEED if self.doing_drop else Game.NORMAL_DROP_SPEED
|
if self.did_user_move_or_spin:
|
||||||
)
|
speed = Game.MOVED_PROP_SPEED
|
||||||
|
elif self.soft_drop:
|
||||||
|
speed = Game.FAST_DROP_SPEED
|
||||||
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:
|
||||||
|
move_cell = self.cells_for_hard_drop()
|
||||||
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.current_block_pos = Cell(cp.x, cp.y + move_cell)
|
self.current_block_pos = Cell(cp.x, cp.y + move_cell)
|
||||||
else:
|
# don't place the pice if the user moved it
|
||||||
|
elif not self.did_user_move_or_spin:
|
||||||
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
|
||||||
):
|
):
|
||||||
self.place_piece(cp.x, cp.y + dy, self.current_block)
|
self.place_piece(cp.x, cp.y + dy, self.current_block)
|
||||||
self.current_block = None
|
self.current_block = None
|
||||||
|
self.did_swap = False
|
||||||
return
|
return
|
||||||
self.game_over = True
|
self.game_over = True
|
||||||
|
|
||||||
@@ -811,7 +847,9 @@ class Game:
|
|||||||
self.help_mode = False
|
self.help_mode = False
|
||||||
|
|
||||||
def game_loop(self):
|
def game_loop(self):
|
||||||
self.doing_drop = False
|
self.soft_drop = False
|
||||||
|
self.hard_drop = False
|
||||||
|
self.did_user_move_or_spin = False
|
||||||
|
|
||||||
self.maybe_clear_rows()
|
self.maybe_clear_rows()
|
||||||
if Action.Type.OPEN_HELP in self.pending_actions:
|
if Action.Type.OPEN_HELP in self.pending_actions:
|
||||||
@@ -846,6 +884,7 @@ class Game:
|
|||||||
self.next_piece = None
|
self.next_piece = None
|
||||||
self.swap_in_next_piece(True)
|
self.swap_in_next_piece(True)
|
||||||
self.held_piece = None
|
self.held_piece = None
|
||||||
|
self.did_swap = False
|
||||||
self.subcell_move = 0
|
self.subcell_move = 0
|
||||||
self.subcell_drop = 0
|
self.subcell_drop = 0
|
||||||
self.clearing_rows = set()
|
self.clearing_rows = set()
|
||||||
@@ -945,6 +984,13 @@ class Game:
|
|||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
self.clock.tick(Game.FRAMERATE)
|
self.clock.tick(Game.FRAMERATE)
|
||||||
|
|
||||||
|
def move_piece_back_to_top(self):
|
||||||
|
self.current_block_pos = Cell(
|
||||||
|
self.BOARD_SIZE.width // 2
|
||||||
|
- math.ceil(self.current_block.width / 2),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
def swap_in_next_piece(self, force: bool = False):
|
def swap_in_next_piece(self, force: bool = False):
|
||||||
if not self.next_piece or force:
|
if not self.next_piece or force:
|
||||||
self.next_piece = self.random_tetromino()
|
self.next_piece = self.random_tetromino()
|
||||||
@@ -952,12 +998,7 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
self.current_block = self.next_piece
|
self.current_block = self.next_piece
|
||||||
self.next_piece = self.random_tetromino()
|
self.next_piece = self.random_tetromino()
|
||||||
# top left corner
|
self.move_piece_back_to_top()
|
||||||
self.current_block_pos = Cell(
|
|
||||||
self.BOARD_SIZE.width // 2
|
|
||||||
- math.ceil(self.current_block.width / 2),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
|
|
||||||
def clear_board(self):
|
def clear_board(self):
|
||||||
self.board = make_matrix(*Game.BOARD_SIZE)
|
self.board = make_matrix(*Game.BOARD_SIZE)
|
||||||
|
|||||||
Reference in New Issue
Block a user