From dd99fcb633862a0251102183d61fd5d978bc68ba Mon Sep 17 00:00:00 2001 From: Jan Lerking Date: Wed, 23 Apr 2025 13:45:29 +0200 Subject: [PATCH 1/4] Working on rotation. /JL --- bricks.py | 35 ++++++++++++++++++++--------------- dropnext.py | 5 +++-- dropzone.py | 12 ++++++------ globals.py | 22 ++++++++++++++++++++++ hud.py | 22 +++++++++++----------- tetris.py | 13 ++++++++----- 6 files changed, 70 insertions(+), 39 deletions(-) create mode 100644 globals.py diff --git a/bricks.py b/bricks.py index e39d81c..0a42f12 100644 --- a/bricks.py +++ b/bricks.py @@ -1,21 +1,9 @@ import pygame +from globals import BRICKS, TILE_SIZE, GRID_WIDTH, GRID_HEIGHT, grid import enums from random import choice -BRICKS = [ - " X " + "\n" + "XXX" + "\n" + " X ", - " X" + "\n" + "XXX", - "X " + "\n" + "XXX", - "X", - "XXXX", - "XX" + "\n" + "XX", - " XX" + "\n" + "XX ", - "XX " + "\n" + " XX", - "X X" + "\n" + "XXX", - "XXX" + "\n" + " X " -] -TILE_SIZE = 48 class Brick: def __init__(self, brick, state = enums.BrickState.Next): @@ -64,9 +52,26 @@ class Brick: self.y += 1 self.location = (self.x, self.y) - def rotate(self): - print("Rotating") + def rotate_clockwise(self, shape): + return [list(row)[::-1] for row in zip(*shape)] + def rotate(self): + new_shape = self.rotate_clockwise(self.layout) + if not self.collision(new_shape, self.x, self.y): + self.layout = new_shape + + def collision(self, shape, x, y): + for row_idx, row in enumerate(shape): + for col_idx, cell in enumerate(row): + if cell: + new_x = x +col_idx + new_y = y + row_idx + if new_x <= 0 or new_x >= GRID_WIDTH or new_y >= GRID_HEIGHT: + return True + if new_y >= 0 and grid[new_y][new_x]: + return True + return False + def set_state(self, state): self.state = state print(f"State set to {self.state}") diff --git a/dropnext.py b/dropnext.py index 3897901..8ae2aad 100644 --- a/dropnext.py +++ b/dropnext.py @@ -1,4 +1,5 @@ import pygame +from globals import TILE_SIZE from enums import BrickColor class DropNext(): @@ -8,8 +9,8 @@ class DropNext(): self.height = height self.dropnext.fill((0, 0, 0)) # Fill with black - def draw(self, screen, tile_size): - screen.blit(self.dropnext, (tile_size * 15, tile_size * 2)) + def draw(self, screen): + screen.blit(self.dropnext, (TILE_SIZE * 15, TILE_SIZE * 2)) def draw_block(self, brick): self.dropnext.fill(BrickColor.Black.value) diff --git a/dropzone.py b/dropzone.py index 86df9f1..3fa40fd 100644 --- a/dropzone.py +++ b/dropzone.py @@ -1,18 +1,18 @@ import pygame +from globals import TILE_SIZE, grid from enums import BrickColor class DropZone(): - def __init__(self, tile_size, width, height): - self.dropzone = pygame.Surface((width * tile_size, height * tile_size)) + def __init__(self, width, height): + self.dropzone = pygame.Surface((width * TILE_SIZE, height * TILE_SIZE)) self.width = width self.height = height - self.tile_size = tile_size self.grid_row = [" "] * self.width - self.grid = [self.grid_row] * self.height + grid = [self.grid_row] * self.height def draw(self, screen): - screen.blit(self.dropzone, (self.tile_size * 4, self.tile_size * 1)) + screen.blit(self.dropzone, (TILE_SIZE * 4, TILE_SIZE * 1)) def draw_brick(self, brick, location): self.dropzone.fill(BrickColor.Black.value) - self.dropzone.blit(brick, (location[0] * self.tile_size, location[1] * self.tile_size)) + self.dropzone.blit(brick, (location[0] * TILE_SIZE, location[1] * TILE_SIZE)) diff --git a/globals.py b/globals.py new file mode 100644 index 0000000..2daa792 --- /dev/null +++ b/globals.py @@ -0,0 +1,22 @@ +def init(): + global BRICKS + BRICKS = [ + " X " + "\n" + "XXX" + "\n" + " X ", + " X" + "\n" + "XXX", + "X " + "\n" + "XXX", + "X", + "XXXX", + "XX" + "\n" + "XX", + " XX" + "\n" + "XX ", + "XX " + "\n" + " XX", + "X X" + "\n" + "XXX", + "XXX" + "\n" + " X " + ] + global TILE_SIZE + TILE_SIZE = 48 + global GRID_WIDTH + GRID_WIDTH = 10 + global GRID_HEIGHT + GRID_HEIGHT = 18 + global grid + grid = None \ No newline at end of file diff --git a/hud.py b/hud.py index 6b2948a..505c9ef 100644 --- a/hud.py +++ b/hud.py @@ -1,9 +1,10 @@ import pygame import os +from globals import TILE_SIZE from enums import BrickColor class Hud: - def __init__(self, screen_width, screen_height, tile_size, font_size=36, highscore_file="highscore.txt"): + def __init__(self, screen_width, screen_height, font_size=36, highscore_file="highscore.txt"): self.highscore_file = highscore_file self.highscore = self.load_highscore() @@ -11,7 +12,6 @@ class Hud: self.color = BrickColor.Red.value self.screen_width = screen_width self.screen_height = screen_height - self.tile_size = tile_size self.reset() @@ -56,24 +56,24 @@ class Hud: # Score (top-left) score_text = self.font.render(f"Score:", True, self.color) score = self.font.render(f"{self.score}", True, self.color) - screen.blit(score_text, (self.tile_size / 2, self.tile_size)) - screen.blit(score,(self.tile_size / 2, self.tile_size + 24)) + screen.blit(score_text, (TILE_SIZE / 2, TILE_SIZE)) + screen.blit(score,(TILE_SIZE / 2, TILE_SIZE + 24)) lines_text = self.font.render(f"Lines:", True, self.color) lines = self.font.render(f"{self.lines}", True, self.color) - screen.blit(lines_text, (self.tile_size / 2, self.tile_size * 2 + 24)) - screen.blit(lines, (self.tile_size / 2, self.tile_size * 3)) + screen.blit(lines_text, (TILE_SIZE / 2, TILE_SIZE * 2 + 24)) + screen.blit(lines, (TILE_SIZE / 2, TILE_SIZE * 3)) level_text = self.font.render(f"Level:", True, self.color) level = self.font.render(f"{self.level}", True, self.color) - screen.blit(level_text, (self.tile_size / 2, self.tile_size * 4)) - screen.blit(level, (self.tile_size / 2, self.tile_size * 4 + 24)) + screen.blit(level_text, (TILE_SIZE / 2, TILE_SIZE * 4)) + screen.blit(level, (TILE_SIZE / 2, TILE_SIZE * 4 + 24)) # Highscore (top-center) highscore_text = self.font.render(f"High Score:", True, self.color) highscore = self.font.render(f"{self.highscore}", True, self.color) - screen.blit(highscore_text, (self.tile_size / 2, self.tile_size * 5 + 24)) - screen.blit(highscore, (self.tile_size / 2, self.tile_size * 6)) + screen.blit(highscore_text, (TILE_SIZE / 2, TILE_SIZE * 5 + 24)) + screen.blit(highscore, (TILE_SIZE / 2, TILE_SIZE * 6)) next_text = self.font.render("Next:", True, self.color) - screen.blit(next_text, (self.tile_size * 15, self.tile_size)) \ No newline at end of file + screen.blit(next_text, (TILE_SIZE * 15, TILE_SIZE)) \ No newline at end of file diff --git a/tetris.py b/tetris.py index 3d678c4..ee15702 100644 --- a/tetris.py +++ b/tetris.py @@ -1,13 +1,15 @@ import pygame import pygameControls as PC +from globals import init +from globals import GRID_WIDTH, GRID_HEIGHT, TILE_SIZE, BRICKS import enums -from bricks import Brick, BRICKS, TILE_SIZE +from bricks import Brick from random import randrange from dropzone import DropZone from dropnext import DropNext from hud import Hud -__version__ = "0.0.4" +__version__ = "0.0.5" # Constants HAT_REPEAT_DELAY = 0 # milliseconds before first repeat @@ -45,8 +47,8 @@ class Tetris: print(self.next.layout) print(self.next.color) - self.hud = Hud(self.screen_width, self.screen_height, TILE_SIZE) - self.dropzone = DropZone(TILE_SIZE, width = 10, height = 18) + self.hud = Hud(self.screen_width, self.screen_height) + self.dropzone = DropZone(TILE_SIZE, GRID_WIDTH, GRID_HEIGHT) self.dropnext = DropNext(width = TILE_SIZE * 4, height = TILE_SIZE * 4) self.clock = pygame.time.Clock() @@ -68,7 +70,7 @@ class Tetris: self.hud.draw(self.screen) self.dropzone.draw(self.screen) self.dropzone.draw_brick(self.current.brick, self.current.location) - self.dropnext.draw(self.screen, TILE_SIZE) + self.dropnext.draw(self.screen) self.dropnext.draw_block(self.next.brick) self.handle_input() @@ -190,4 +192,5 @@ class Tetris: self.running = False if __name__ == "__main__": + globals.init() game = Tetris() \ No newline at end of file From a1910993dc4c0f0778a178cdb74ef398f57af7a4 Mon Sep 17 00:00:00 2001 From: Jan Lerking Date: Wed, 23 Apr 2025 15:16:17 +0200 Subject: [PATCH 2/4] Updated. /JL --- tetris.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tetris.py b/tetris.py index ee15702..e888d85 100644 --- a/tetris.py +++ b/tetris.py @@ -1,6 +1,7 @@ import pygame import pygameControls as PC -from globals import init +import globals +globals.init() from globals import GRID_WIDTH, GRID_HEIGHT, TILE_SIZE, BRICKS import enums from bricks import Brick @@ -48,7 +49,7 @@ class Tetris: print(self.next.color) self.hud = Hud(self.screen_width, self.screen_height) - self.dropzone = DropZone(TILE_SIZE, GRID_WIDTH, GRID_HEIGHT) + self.dropzone = DropZone(GRID_WIDTH, GRID_HEIGHT) self.dropnext = DropNext(width = TILE_SIZE * 4, height = TILE_SIZE * 4) self.clock = pygame.time.Clock() @@ -192,5 +193,4 @@ class Tetris: self.running = False if __name__ == "__main__": - globals.init() game = Tetris() \ No newline at end of file From 5cbbf9bdcbe725247a9b33c11c123cd27e37d99d Mon Sep 17 00:00:00 2001 From: Jan Lerking Date: Thu, 24 Apr 2025 06:26:41 +0200 Subject: [PATCH 3/4] Updated. /JL --- bricks.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bricks.py b/bricks.py index 0a42f12..1bf9b2c 100644 --- a/bricks.py +++ b/bricks.py @@ -64,7 +64,7 @@ class Brick: for row_idx, row in enumerate(shape): for col_idx, cell in enumerate(row): if cell: - new_x = x +col_idx + new_x = x + col_idx new_y = y + row_idx if new_x <= 0 or new_x >= GRID_WIDTH or new_y >= GRID_HEIGHT: return True @@ -77,8 +77,11 @@ class Brick: print(f"State set to {self.state}") def move_right(self): - self.x += 1 - self.location = (self.x, self.y) + if self.collision(self.layout, self.x, self.y): + return + else: + self.x += 1 + self.location = (self.x, self.y) def move_left(self): self.x -= 1 From 3b76f02f92c022c46558dd6abd96bfd3874064ad Mon Sep 17 00:00:00 2001 From: Jan Lerking Date: Fri, 25 Apr 2025 17:12:10 +0200 Subject: [PATCH 4/4] Rotation is now working. /JL --- bricks.py | 44 +++++++++++++++++++++++++++----------------- dropnext.py | 4 ++-- dropzone.py | 11 +++++------ globals.py | 24 ++++++++++++------------ tetris.py | 18 +++++++++++------- 5 files changed, 57 insertions(+), 44 deletions(-) diff --git a/bricks.py b/bricks.py index 1bf9b2c..bc9893f 100644 --- a/bricks.py +++ b/bricks.py @@ -1,5 +1,5 @@ import pygame -from globals import BRICKS, TILE_SIZE, GRID_WIDTH, GRID_HEIGHT, grid +import globals import enums from random import choice @@ -7,7 +7,7 @@ from random import choice class Brick: def __init__(self, brick, state = enums.BrickState.Next): - self.layout = [] + self.shape = [] self.color = choice(list(enums.BrickColor)) self.set_state(state) self.angle = 0 @@ -31,19 +31,19 @@ class Brick: self.img = pygame.image.load("assets/magenta_block.png").convert_alpha() def load_brick(self, brick): - self.layout = [l for l in BRICKS[brick].splitlines()] + self.shape = [l for l in globals.BRICKS[brick]] - self.rows = len(self.layout) - self.cols = len(self.layout[0]) - self.width = self.cols * TILE_SIZE - self.height = self.rows * TILE_SIZE + self.rows = len(self.shape) + self.cols = len(self.shape[0]) + self.width = self.cols * globals.TILE_SIZE + self.height = self.rows * globals.TILE_SIZE def draw_brick(self): self.brick = pygame.Surface((self.width, self.height)) - for y, row in enumerate(self.layout): + for y, row in enumerate(self.shape): for x, char in enumerate(row): - if char == "X": - self.brick.blit(self.block_image, (1 + x * TILE_SIZE, 1 + y * TILE_SIZE)) + if char: + self.brick.blit(self.block_image, (1 + x * globals.TILE_SIZE, 1 + y * globals.TILE_SIZE)) def is_current(self): return True if self.state == enums.BrickState.Current else False @@ -56,9 +56,16 @@ class Brick: return [list(row)[::-1] for row in zip(*shape)] def rotate(self): - new_shape = self.rotate_clockwise(self.layout) + new_shape = self.rotate_clockwise(self.shape) + print(new_shape) if not self.collision(new_shape, self.x, self.y): - self.layout = new_shape + self.shape = new_shape + self.rows = len(self.shape) + self.cols = len(self.shape[0]) + self.width = self.cols * globals.TILE_SIZE + self.height = self.rows * globals.TILE_SIZE + self.draw_brick() + print(self.shape) def collision(self, shape, x, y): for row_idx, row in enumerate(shape): @@ -66,9 +73,9 @@ class Brick: if cell: new_x = x + col_idx new_y = y + row_idx - if new_x <= 0 or new_x >= GRID_WIDTH or new_y >= GRID_HEIGHT: + if new_x <= 0 or new_x >= globals.GRID_WIDTH or new_y >= globals.GRID_HEIGHT: return True - if new_y >= 0 and grid[new_y][new_x]: + if new_y >= 0 and globals.dropgrid[new_y][new_x]: return True return False @@ -77,15 +84,18 @@ class Brick: print(f"State set to {self.state}") def move_right(self): - if self.collision(self.layout, self.x, self.y): + if self.collision(self.shape, self.x, self.y): return else: self.x += 1 self.location = (self.x, self.y) def move_left(self): - self.x -= 1 - self.location = (self.x, self.y) + if self.collision(self.shape, self.x, self.y): + return + else: + self.x -= 1 + self.location = (self.x, self.y) def drop(self): print("Dropping") \ No newline at end of file diff --git a/dropnext.py b/dropnext.py index 8ae2aad..bc838f8 100644 --- a/dropnext.py +++ b/dropnext.py @@ -1,5 +1,5 @@ import pygame -from globals import TILE_SIZE +import globals from enums import BrickColor class DropNext(): @@ -10,7 +10,7 @@ class DropNext(): self.dropnext.fill((0, 0, 0)) # Fill with black def draw(self, screen): - screen.blit(self.dropnext, (TILE_SIZE * 15, TILE_SIZE * 2)) + screen.blit(self.dropnext, (globals.TILE_SIZE * 15, globals.TILE_SIZE * 2)) def draw_block(self, brick): self.dropnext.fill(BrickColor.Black.value) diff --git a/dropzone.py b/dropzone.py index 3fa40fd..5d679bb 100644 --- a/dropzone.py +++ b/dropzone.py @@ -1,18 +1,17 @@ import pygame -from globals import TILE_SIZE, grid +import globals from enums import BrickColor class DropZone(): def __init__(self, width, height): - self.dropzone = pygame.Surface((width * TILE_SIZE, height * TILE_SIZE)) + self.dropzone = pygame.Surface((width * globals.TILE_SIZE, height * globals.TILE_SIZE)) self.width = width self.height = height - self.grid_row = [" "] * self.width - grid = [self.grid_row] * self.height + print(globals.dropgrid) def draw(self, screen): - screen.blit(self.dropzone, (TILE_SIZE * 4, TILE_SIZE * 1)) + screen.blit(self.dropzone, (globals.TILE_SIZE * 4, globals.TILE_SIZE * 1)) def draw_brick(self, brick, location): self.dropzone.fill(BrickColor.Black.value) - self.dropzone.blit(brick, (location[0] * TILE_SIZE, location[1] * TILE_SIZE)) + self.dropzone.blit(brick, (location[0] * globals.TILE_SIZE, location[1] * globals.TILE_SIZE)) diff --git a/globals.py b/globals.py index 2daa792..dbaad3d 100644 --- a/globals.py +++ b/globals.py @@ -1,16 +1,16 @@ def init(): global BRICKS BRICKS = [ - " X " + "\n" + "XXX" + "\n" + " X ", - " X" + "\n" + "XXX", - "X " + "\n" + "XXX", - "X", - "XXXX", - "XX" + "\n" + "XX", - " XX" + "\n" + "XX ", - "XX " + "\n" + " XX", - "X X" + "\n" + "XXX", - "XXX" + "\n" + " X " + [[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]] ] global TILE_SIZE TILE_SIZE = 48 @@ -18,5 +18,5 @@ def init(): GRID_WIDTH = 10 global GRID_HEIGHT GRID_HEIGHT = 18 - global grid - grid = None \ No newline at end of file + global dropgrid + dropgrid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] \ No newline at end of file diff --git a/tetris.py b/tetris.py index e888d85..24fbf3c 100644 --- a/tetris.py +++ b/tetris.py @@ -40,18 +40,18 @@ class Tetris: self.screen_width, self.screen_height = (20 * TILE_SIZE), (20 * TILE_SIZE) self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) pygame.display.set_caption("Tetris " + __version__) - - self.current = Brick(brick = randrange(0, len(BRICKS)), state = enums.BrickState.Current) - print(self.current.layout) - print(self.current.color) - self.next = Brick(brick = randrange(0, len(BRICKS))) - print(self.next.layout) - print(self.next.color) self.hud = Hud(self.screen_width, self.screen_height) 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) + print(self.current.shape) + print(self.current.color) + self.next = Brick(brick = randrange(0, len(BRICKS))) + print(self.next.shape) + print(self.next.color) + self.clock = pygame.time.Clock() self.rumble_timer = pygame.time.get_ticks() self.fall_speed = 1000 # in milliseconds @@ -121,6 +121,7 @@ class Tetris: if self.current.direction == enums.BrickDirection.Dropped: break self.current.rotate() + self.dropzone.draw_brick(self.current.brick, self.current.location) case pygame.K_RETURN: if self.current.direction == enums.BrickDirection.Dropped: break @@ -137,17 +138,20 @@ class Tetris: break self.current.direction = enums.BrickDirection.Right self.current.move_right() + self.dropzone.draw_brick(self.current.brick, self.current.location) elif self.hat_x == -1: if self.current.direction == enums.BrickDirection.Dropped: break self.current.direction = enums.BrickDirection.Left self.current.move_left() + self.dropzone.draw_brick(self.current.brick, self.current.location) case pygame.JOYBUTTONDOWN: if event.button == 2: if self.current.direction == enums.BrickDirection.Dropped: break self.current.rotate() + self.dropzone.draw_brick(self.current.brick, self.current.location) elif event.button == 0: if self.current.direction == enums.BrickDirection.Dropped: break