Compare commits
No commits in common. "ef06cce4c7e9ad0d14206803815e3b84c8eb92d3" and "4b0d06fc6538ac86e691e64e22f8e715e91bd894" have entirely different histories.
ef06cce4c7
...
4b0d06fc65
@ -1,7 +1,5 @@
|
|||||||
# np-tetris
|
# np-tetris
|
||||||
|
|
||||||
`main.py`: tetris in `numpy`
|
tetris in numpy
|
||||||
`tetris.py`: tetris with `curses`
|
|
||||||
`snake.py`: snake with `curses` and `numpy`
|
|
||||||
|
|
||||||
Copyright 2024 Michael Pilosov, PhD
|
Copyright 2024 Michael Pilosov, PhD
|
||||||
|
162
tetris.py
162
tetris.py
@ -1,162 +0,0 @@
|
|||||||
import curses
|
|
||||||
import time
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
PIECES = {
|
|
||||||
"O": np.array([[1, 1], [1, 1]]), # 2x2 block
|
|
||||||
"I": np.array([[1, 1, 1, 1]]), # 4x1 horizontal bar
|
|
||||||
"L": np.array([[1, 0], [1, 0], [1, 1]]), # L-shaped
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def check_placement(piece, board, i, j):
|
|
||||||
"""Check if a piece can be placed on the board. Return True if possible, False otherwise."""
|
|
||||||
n, m = piece.shape
|
|
||||||
if i + n > board.shape[0] or j + m > board.shape[1]:
|
|
||||||
return False # Out of bounds
|
|
||||||
for x in range(n):
|
|
||||||
for y in range(m):
|
|
||||||
if piece[x, y] == 1 and board[i + x, j + y] == 1:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def place_piece(piece, board, i, j):
|
|
||||||
"""Place a piece on the board at the specified position."""
|
|
||||||
board[i : i + piece.shape[0], j : j + piece.shape[1]] += piece
|
|
||||||
return board
|
|
||||||
|
|
||||||
|
|
||||||
def attempt_rotate(piece, board, i, j):
|
|
||||||
"""Attempt to rotate a piece."""
|
|
||||||
rotated_piece = np.rot90(piece)
|
|
||||||
if check_placement(rotated_piece, board, i, j):
|
|
||||||
return rotated_piece
|
|
||||||
return piece
|
|
||||||
|
|
||||||
|
|
||||||
def clear_rows(board):
|
|
||||||
"""Clear completed rows from the board."""
|
|
||||||
clear_indices = np.where(np.all(board == 1, axis=1))[0]
|
|
||||||
if clear_indices.size > 0:
|
|
||||||
board = np.delete(board, clear_indices, axis=0)
|
|
||||||
new_rows = np.zeros((len(clear_indices), board.shape[1]))
|
|
||||||
board = np.vstack([new_rows, board])
|
|
||||||
return board
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_shadow(piece, board, i, j):
|
|
||||||
"""Calculate the shadow position for the current piece."""
|
|
||||||
while check_placement(piece, board, i + 1, j):
|
|
||||||
i += 1
|
|
||||||
return i
|
|
||||||
|
|
||||||
|
|
||||||
def display_board(stdscr, board, piece, i, j):
|
|
||||||
"""Display the board and the piece."""
|
|
||||||
stdscr.clear()
|
|
||||||
shadow_i = calculate_shadow(piece, board, i, j)
|
|
||||||
n, m = piece.shape
|
|
||||||
for y in range(board.shape[0]):
|
|
||||||
for x in range(board.shape[1]):
|
|
||||||
char = " " # Default background
|
|
||||||
if (
|
|
||||||
y >= shadow_i
|
|
||||||
and y < shadow_i + n
|
|
||||||
and x >= j
|
|
||||||
and x < j + m
|
|
||||||
and piece[y - shadow_i, x - j]
|
|
||||||
):
|
|
||||||
char = "*" # Shadow
|
|
||||||
elif y >= i and y < i + n and x >= j and x < j + m and piece[y - i, x - j]:
|
|
||||||
char = "#" # Piece
|
|
||||||
elif board[y, x]:
|
|
||||||
char = "#" # Static pieces
|
|
||||||
stdscr.addstr(y, x * 2, char)
|
|
||||||
stdscr.refresh()
|
|
||||||
|
|
||||||
|
|
||||||
def main(stdscr):
|
|
||||||
curses.curs_set(0)
|
|
||||||
stdscr.nodelay(True)
|
|
||||||
stdscr.keypad(True)
|
|
||||||
|
|
||||||
board = np.zeros((20, 10), dtype=int)
|
|
||||||
piece_types = list(PIECES.keys())
|
|
||||||
current_piece = PIECES[np.random.choice(piece_types)]
|
|
||||||
START_POS = (0, 4)
|
|
||||||
i, j = START_POS
|
|
||||||
auto_gravity = True
|
|
||||||
fall_rate = 0.5 # Seconds between falls
|
|
||||||
|
|
||||||
# Initial instruction
|
|
||||||
stdscr.addstr(
|
|
||||||
22,
|
|
||||||
0,
|
|
||||||
"Press SPACE to start. Use keys (a=left, d=right, s=down, w=rotate, S=drop, G=toggle gravity, Q=quit):",
|
|
||||||
)
|
|
||||||
while stdscr.getch() != ord(" "):
|
|
||||||
pass
|
|
||||||
|
|
||||||
last_fall_time = time.time()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
display_board(stdscr, board, current_piece, i, j)
|
|
||||||
|
|
||||||
if not check_placement(current_piece, board, *START_POS):
|
|
||||||
stdscr.addstr(22, 0, "GAME OVER! Press any key to exit.")
|
|
||||||
stdscr.getch()
|
|
||||||
break
|
|
||||||
|
|
||||||
# Handle key inputs
|
|
||||||
key = stdscr.getch()
|
|
||||||
if key == ord("q"):
|
|
||||||
break
|
|
||||||
elif (
|
|
||||||
key == ord("a")
|
|
||||||
and j > 0
|
|
||||||
and check_placement(current_piece, board, i, j - 1)
|
|
||||||
):
|
|
||||||
j -= 1
|
|
||||||
elif (
|
|
||||||
key == ord("d")
|
|
||||||
and j + current_piece.shape[1] < board.shape[1]
|
|
||||||
and check_placement(current_piece, board, i, j + 1)
|
|
||||||
):
|
|
||||||
j += 1
|
|
||||||
elif key == ord("w"):
|
|
||||||
new_piece = attempt_rotate(current_piece, board, i, j)
|
|
||||||
if check_placement(new_piece, board, i, j):
|
|
||||||
current_piece = new_piece
|
|
||||||
elif key == ord("s"):
|
|
||||||
if check_placement(current_piece, board, i + 1, j):
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
board = place_piece(current_piece, board, i, j)
|
|
||||||
board = clear_rows(board)
|
|
||||||
current_piece = PIECES[np.random.choice(piece_types)]
|
|
||||||
i, j = START_POS
|
|
||||||
elif key == ord("S"):
|
|
||||||
i = calculate_shadow(current_piece, board, i, j)
|
|
||||||
board = place_piece(current_piece, board, i, j)
|
|
||||||
board = clear_rows(board)
|
|
||||||
current_piece = PIECES[np.random.choice(piece_types)]
|
|
||||||
i, j = START_POS
|
|
||||||
elif key == ord("g"):
|
|
||||||
auto_gravity = not auto_gravity
|
|
||||||
|
|
||||||
# Gravity effect
|
|
||||||
if auto_gravity and time.time() - last_fall_time >= fall_rate:
|
|
||||||
if check_placement(current_piece, board, i + 1, j):
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
board = place_piece(current_piece, board, i, j)
|
|
||||||
board = clear_rows(board)
|
|
||||||
current_piece = PIECES[np.random.choice(piece_types)]
|
|
||||||
i, j = START_POS
|
|
||||||
last_fall_time = time.time()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
curses.wrapper(main)
|
|
Loading…
Reference in New Issue
Block a user