diff --git a/bricks.py b/bricks.py index 7660f04..50e5b32 100644 --- a/bricks.py +++ b/bricks.py @@ -75,7 +75,7 @@ class Brick: if new_x <= 0 or new_x >= globals.GRID_WIDTH or new_y >= globals.GRID_HEIGHT: print(f"Collision X:{new_x}, Y:{new_y}") return True - if new_y >= 0 and globals.dropgrid[new_y][new_x]: + if new_y >= 0 and globals.grid[new_y][new_x]: return True return False diff --git a/dropzone.py b/dropzone.py index 5417b29..c6fa478 100644 --- a/dropzone.py +++ b/dropzone.py @@ -1,4 +1,5 @@ import pygame +import numpy as np import globals from enums import BrickColor @@ -20,7 +21,7 @@ class DropZone(): for row_idx, row in enumerate(brick.shape): for col_idx, cell in enumerate(row): if cell: - globals.dropgrid[brick.y + row_idx][brick.x + col_idx] = 1 + globals.grid[brick.y + row_idx][brick.x + col_idx] = 1 self.dropzone.blit(brick.brick, (brick.y + row_idx, brick.x + col_idx)) \ No newline at end of file diff --git a/globals.py b/globals.py index dbaad3d..2091c6d 100644 --- a/globals.py +++ b/globals.py @@ -1,22 +1,33 @@ +import numpy as np + def init(): global BRICKS - BRICKS = [ - [[0,1,0], [1,1,1], [0,1,0]], - [[0, 0, 1], [1, 1, 1]], - [[1, 0, 0], [1, 1, 1]], - [[1]], - [[1, 1, 1, 1]], - [[1, 1], [1, 1]], - [[0, 1, 1], [1, 1, 0]], - [[1, 1, 0], [0, 1, 1]], - [[1, 0, 1], [1, 1, 1]], - [[1, 1, 1], [0, 1, 0]] - ] + BRICKS = { + 'I': np.array([[1, 1, 1, 1]]), + 'O': np.array([[1, 1], + [1, 1]]), + 'T': np.array([[0, 1, 0], + [1, 1, 1]]), + 'S': np.array([[0, 1, 1], + [1, 1, 0]]), + 'Z': np.array([[1, 1, 0], + [0, 1, 1]]), + 'J': np.array([[1, 0, 0], + [1, 1, 1]]), + 'L': np.array([[0, 0, 1], + [1, 1, 1]]), + '.': np.array([[1]]), + '+': np.array([[0, 1, 0], + [1, 1, 1], + [0, 1, 0]]), + 'U': np.array([[1, 0, 1], + [1, 1, 1]]) +} global TILE_SIZE TILE_SIZE = 48 global GRID_WIDTH GRID_WIDTH = 10 global GRID_HEIGHT GRID_HEIGHT = 18 - global dropgrid - dropgrid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] \ No newline at end of file + global grid + grid = np.zeros((GRID_HEIGHT, GRID_WIDTH), dtype=int) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 63f1bc3..d174b3a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ +numpy==2.2.5 pygame==2.6.1 -pygamecontrols==0.1.13 +pygamecontrols==0.2.5 diff --git a/tet.py b/tet.py new file mode 100644 index 0000000..7accc18 --- /dev/null +++ b/tet.py @@ -0,0 +1,121 @@ +import pygame +import numpy as np +import random + +# Pygame setup +pygame.init() +CELL_SIZE = 30 +ROWS, COLS = 20, 10 +WIDTH, HEIGHT = COLS * CELL_SIZE, ROWS * CELL_SIZE +screen = pygame.display.set_mode((WIDTH, HEIGHT)) +clock = pygame.time.Clock() + +# Tetromino definitions +TETROMINOS = { + 'I': np.array([[1, 1, 1, 1]]), + 'O': np.array([[1, 1], [1, 1]]), + 'T': np.array([[0, 1, 0], [1, 1, 1]]), + 'S': np.array([[0, 1, 1], [1, 1, 0]]), + 'Z': np.array([[1, 1, 0], [0, 1, 1]]), + 'J': np.array([[1, 0, 0], [1, 1, 1]]), + 'L': np.array([[0, 0, 1], [1, 1, 1]]) +} + +# Colors for pieces +COLORS = { + 0: (0, 0, 0), + 1: (255, 255, 255) +} + +# Game state +grid = np.zeros((ROWS, COLS), dtype=int) + +def new_piece(): + shape = random.choice(list(TETROMINOS.values())) + return shape, [0, COLS // 2 - shape.shape[1] // 2] + +def check_collision(grid, piece, offset): + x, y = offset + h, w = piece.shape + if x + h > ROWS or y < 0 or y + w > COLS: + return True + area = grid[x:x+h, y:y+w] + return np.any(area & piece) + +def place_piece(grid, piece, offset): + x, y = offset + h, w = piece.shape + grid[x:x+h, y:y+w] |= piece + +def clear_lines(grid): + full_rows = [i for i in range(ROWS) if all(grid[i])] + for i in full_rows: + grid[1:i+1] = grid[0:i] + grid[0] = np.zeros(COLS) + +def rotate_piece(piece): + return np.rot90(piece, -1) + +def draw_grid(surface, grid): + for i in range(ROWS): + for j in range(COLS): + pygame.draw.rect(surface, COLORS[grid[i][j]], + (j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE), 0) + pygame.draw.rect(surface, (40, 40, 40), + (j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE), 1) + +def draw_piece(surface, piece, offset): + x, y = offset + for i in range(piece.shape[0]): + for j in range(piece.shape[1]): + if piece[i][j]: + pygame.draw.rect(surface, COLORS[1], + ((y + j) * CELL_SIZE, (x + i) * CELL_SIZE, CELL_SIZE, CELL_SIZE)) + +# Start game +piece, pos = new_piece() +drop_timer = 0 +running = True + +while running: + screen.fill((0, 0, 0)) + draw_grid(screen, grid) + draw_piece(screen, piece, pos) + pygame.display.flip() + clock.tick(60) + drop_timer += 1 + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYDOWN: + new_pos = pos[:] + if event.key == pygame.K_LEFT: + new_pos[1] -= 1 + elif event.key == pygame.K_RIGHT: + new_pos[1] += 1 + elif event.key == pygame.K_DOWN: + new_pos[0] += 1 + elif event.key == pygame.K_UP: + rotated = rotate_piece(piece) + if not check_collision(grid, rotated, pos): + piece = rotated + continue + + if not check_collision(grid, piece, new_pos): + pos = new_pos + + if drop_timer > 30: + drop_pos = [pos[0] + 1, pos[1]] + if not check_collision(grid, piece, drop_pos): + pos = drop_pos + else: + place_piece(grid, piece, pos) + clear_lines(grid) + piece, pos = new_piece() + if check_collision(grid, piece, pos): + print("Game Over") + running = False + drop_timer = 0 + +pygame.quit() diff --git a/tetris.py b/tetris.py index 4f10f48..6ff62c7 100644 --- a/tetris.py +++ b/tetris.py @@ -5,12 +5,12 @@ globals.init() from globals import GRID_WIDTH, GRID_HEIGHT, TILE_SIZE, BRICKS import enums from bricks import Brick -from random import randrange +import random from dropzone import DropZone from dropnext import DropNext from hud import Hud -__version__ = "0.1.3" +__version__ = "0.2.0" # Constants HAT_REPEAT_DELAY = 0 # milliseconds before first repeat @@ -45,8 +45,8 @@ class Tetris: self.dropzone = DropZone(GRID_WIDTH, GRID_HEIGHT) self.dropnext = DropNext(width = TILE_SIZE * 4, height = TILE_SIZE * 4) - self.current = Brick(brick = randrange(0, len(BRICKS)), state = enums.BrickState.Current) - self.next = Brick(brick = randrange(0, len(BRICKS))) + self.current = Brick(brick = random.choice(list(BRICKS.keys())), state = enums.BrickState.Current) + self.next = Brick(brick = random.choice(list(BRICKS.keys()))) self.clock = pygame.time.Clock() self.rumble_timer = pygame.time.get_ticks() @@ -100,6 +100,7 @@ class Tetris: case pygame.QUIT: self.running = False case pygame.KEYDOWN: + #new_pos = pos[:] match event.key: case pygame.K_RIGHT: if self.current.direction == enums.BrickDirection.Dropped: @@ -192,7 +193,7 @@ class Tetris: if not self.current.update(): self.dropzone.lock(self.current) self.current = self.next - self.next = Brick(brick = randrange(0, len(BRICKS))) + self.next = Brick(brick = random.choice(list(BRICKS.keys()))) # Handle hotplugging case pygame.JOYDEVICEADDED: @@ -216,9 +217,11 @@ class Tetris: if "left button" in self.joysticks[self.joy.get_instance_id()].controllers[0].mapping: self.left_button = self.joysticks[self.joy.get_instance_id()].controllers[0].mapping["left button"] + else: self.left_button = None if "right button" in self.joysticks[self.joy.get_instance_id()].controllers[0].mapping: self.right_button = self.joysticks[self.joy.get_instance_id()].controllers[0].mapping["right button"] + else: self.right_button = None case pygame.JOYDEVICEREMOVED: del self.joysticks[event.instance_id]