From 70053aa94d0d27d7567f1b0764cc8ce9e6d96df0 Mon Sep 17 00:00:00 2001 From: Alexander Rosenberg Date: Fri, 1 May 2026 03:49:27 -0700 Subject: [PATCH] Finish game --- tetris.py | 889 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 457 insertions(+), 432 deletions(-) diff --git a/tetris.py b/tetris.py index aa8b6dc..c892aef 100644 --- a/tetris.py +++ b/tetris.py @@ -1,7 +1,7 @@ import pygame import pygame.draw as draw from collections import namedtuple, defaultdict -from typing import NamedTuple, Any, Iterable, Optional +from typing import NamedTuple, Any, Iterable, Optional, Callable from functools import cache import random import math @@ -107,7 +107,7 @@ class Action(NamedTuple): MOVE_RIGHT = auto() OPEN_HELP = auto() - # Game over + # Game over (also in game) RESTART = auto() # Help @@ -241,6 +241,23 @@ class Font: return surf +class LazyVariable: + def __init__(self, thunk: Callable[[], Any]): + self.thunk = thunk + self.need_eval = True + self.value = None + + def __get__(self, instance: Any, owner: Optional[type] = None): + if self.need_eval: + self.value = self.thunk() + self.need_eval = False + return self.value + + def __set__(self, instance: Any, value: Any): + self.value = value + self.need_eval = False + + class Game: PLACE_POINTS = 10 CLEAR_POINTS = 100 @@ -293,7 +310,7 @@ class Game: pygame.K_s, Action.Type.SWAP_HOLD, False, - "Swap the current and held piece.", + "Swap the current and held pieces.", ), Action( pygame.K_DOWN, Action.Type.DROP, True, "Drop the current piece." @@ -310,6 +327,12 @@ class Game: True, "Move the current piece right.", ), + Action( + pygame.K_r, + Action.Type.RESTART, + False, + "Start a new game.", + ), Action( pygame.K_h, Action.Type.OPEN_HELP, @@ -335,435 +358,10 @@ class Game: ), ) - FONT = Font( - 6, - 3, - 3, - [ - Font.Glyph( - " ", - [ - " ", - " ", - " ", - " ", - " ", - ], - ), - Font.Glyph( - "a", - [ - "*****", - "* *", - "*****", - "* *", - "* *", - ], - ), - Font.Glyph( - "b", - [ - "**** ", - "* *", - "*****", - "* *", - "**** ", - ], - ), - Font.Glyph( - "c", - [ - "*****", - "* *", - "* ", - "* *", - "*****", - ], - ), - Font.Glyph( - "d", - [ - "**** ", - "* *", - "* *", - "* *", - "**** ", - ], - ), - Font.Glyph( - "e", - [ - "*****", - "* ", - "**** ", - "* ", - "*****", - ], - ), - Font.Glyph( - "f", - [ - "*****", - "* ", - "**** ", - "* ", - "* ", - ], - ), - Font.Glyph( - "g", - [ - "*****", - "* ", - "* **", - "* *", - "*****", - ], - ), - Font.Glyph( - "h", - [ - "* *", - "* *", - "*****", - "* *", - "* *", - ], - ), - Font.Glyph( - "i", - [ - "*****", - " * ", - " * ", - " * ", - "*****", - ], - ), - Font.Glyph( - "j", - [ - "*****", - " * ", - " * ", - "* * ", - " ** ", - ], - ), - Font.Glyph( - "k", - [ - "* *", - "* * ", - "*** ", - "* * ", - "* *", - ], - ), - Font.Glyph( - "l", - [ - "* ", - "* ", - "* ", - "* ", - "*****", - ], - ), - Font.Glyph( - "m", - [ - "* *", - "** **", - "* * *", - "* *", - "* *", - ], - ), - Font.Glyph( - "n", - [ - "* *", - "** *", - "* * *", - "* **", - "* *", - ], - ), - Font.Glyph( - "o", - [ - " *** ", - "* *", - "* *", - "* *", - " *** ", - ], - ), - Font.Glyph( - "p", - [ - "**** ", - "* *", - "**** ", - "* ", - "* ", - ], - ), - Font.Glyph( - "q", - [ - " *** ", - "* *", - "* * *", - "* * ", - " ** *", - ], - ), - Font.Glyph( - "r", - [ - "**** ", - "* *", - "**** ", - "* * ", - "* *", - ], - ), - Font.Glyph( - "s", - [ - " ****", - "* ", - " *** ", - " *", - "**** ", - ], - ), - Font.Glyph( - "t", - [ - "*****", - " * ", - " * ", - " * ", - " * ", - ], - ), - Font.Glyph( - "u", - [ - "* *", - "* *", - "* *", - "* *", - " *** ", - ], - ), - Font.Glyph( - "v", - [ - "* *", - "* *", - "* *", - " * * ", - " * ", - ], - ), - Font.Glyph( - "w", - [ - "* *", - "* *", - "* * *", - "** **", - "* *", - ], - ), - Font.Glyph( - "x", - [ - "* *", - " * * ", - " * ", - " * * ", - "* *", - ], - ), - Font.Glyph( - "y", - [ - "* *", - " * * ", - " * ", - " * ", - " * ", - ], - ), - Font.Glyph( - "z", - [ - "*****", - " * ", - " * ", - " * ", - "*****", - ], - ), - Font.Glyph( - "0", - [ - " *** ", - "* *", - "* * *", - "* *", - " *** ", - ], - ), - Font.Glyph( - "1", - [ - " ** ", - "* * ", - " * ", - " * ", - "*****", - ], - ), - Font.Glyph( - "2", - [ - " *** ", - "* *", - " * ", - " * ", - " ****", - ], - ), - Font.Glyph( - "3", - [ - " *** ", - "* *", - " ***", - "* *", - " *** ", - ], - ), - Font.Glyph( - "4", - [ - "* * ", - "* * ", - "*****", - " * ", - " * ", - ], - ), - Font.Glyph( - "5", - [ - "*****", - "* ", - "**** ", - " *", - "**** ", - ], - ), - Font.Glyph( - "6", - [ - " ****", - "* ", - "**** ", - "* *", - " *** ", - ], - ), - Font.Glyph( - "7", - [ - "*****", - " * ", - " * ", - " * ", - "* ", - ], - ), - Font.Glyph( - "8", - [ - " *** ", - "* *", - " *** ", - "* *", - " *** ", - ], - ), - Font.Glyph( - "9", - [ - " *** ", - "* *", - " ****", - " *", - "**** ", - ], - ), - Font.Glyph( - ".", - [ - " ", - " ", - " ", - " ", - " *", - ], - ), - Font.Glyph( - "!", - [ - " *", - " *", - " *", - " ", - " *", - ], - ), - Font.Glyph( - ":", - [ - " ", - " *", - " ", - " *", - " ", - ], - ), - Font.Glyph( - "<", - [ - " **", - " ** ", - "* ", - " ** ", - " **", - ], - ), - Font.Glyph( - ">", - [ - "** ", - " ** ", - " *", - " ** ", - "** ", - ], - ), - ], - ) - SMALL_FONT = FONT.scaled(4, 2, 2) - TINY_FONT = FONT.scaled(2, 2, 2) + # The actual font data is long, so I put it at the end + FONT = LazyVariable(lambda: Font(6, 3, 3, GLYPH_DATA)) + SMALL_FONT = LazyVariable(lambda: Game.FONT.scaled(4, 2, 2)) + TINY_FONT = LazyVariable(lambda: Game.FONT.scaled(2, 2, 2)) CONTROL_DISPLAY_BORDER = 5 INFO_BOX_BORDER_SIZE = 5 HELP_MENU_INSET = 10 @@ -1006,6 +604,9 @@ class Game: self.move_current_piece(Direction.LEFT) case Action.Type.MOVE_RIGHT: self.move_current_piece(Direction.RIGHT) + case Action.Type.RESTART: + self.start_new_game() + self.pending_actions = [] def place_piece(self, x: int, y: int, piece: Tetromino): self.score += Game.PLACE_POINTS @@ -1376,6 +977,430 @@ class Game: self.loop() +GLYPH_DATA = [ + Font.Glyph( + " ", + [ + " ", + " ", + " ", + " ", + " ", + ], + ), + Font.Glyph( + "a", + [ + "*****", + "* *", + "*****", + "* *", + "* *", + ], + ), + Font.Glyph( + "b", + [ + "**** ", + "* *", + "*****", + "* *", + "**** ", + ], + ), + Font.Glyph( + "c", + [ + "*****", + "* *", + "* ", + "* *", + "*****", + ], + ), + Font.Glyph( + "d", + [ + "**** ", + "* *", + "* *", + "* *", + "**** ", + ], + ), + Font.Glyph( + "e", + [ + "*****", + "* ", + "**** ", + "* ", + "*****", + ], + ), + Font.Glyph( + "f", + [ + "*****", + "* ", + "**** ", + "* ", + "* ", + ], + ), + Font.Glyph( + "g", + [ + "*****", + "* ", + "* **", + "* *", + "*****", + ], + ), + Font.Glyph( + "h", + [ + "* *", + "* *", + "*****", + "* *", + "* *", + ], + ), + Font.Glyph( + "i", + [ + "*****", + " * ", + " * ", + " * ", + "*****", + ], + ), + Font.Glyph( + "j", + [ + "*****", + " * ", + " * ", + "* * ", + " ** ", + ], + ), + Font.Glyph( + "k", + [ + "* *", + "* * ", + "*** ", + "* * ", + "* *", + ], + ), + Font.Glyph( + "l", + [ + "* ", + "* ", + "* ", + "* ", + "*****", + ], + ), + Font.Glyph( + "m", + [ + "* *", + "** **", + "* * *", + "* *", + "* *", + ], + ), + Font.Glyph( + "n", + [ + "* *", + "** *", + "* * *", + "* **", + "* *", + ], + ), + Font.Glyph( + "o", + [ + " *** ", + "* *", + "* *", + "* *", + " *** ", + ], + ), + Font.Glyph( + "p", + [ + "**** ", + "* *", + "**** ", + "* ", + "* ", + ], + ), + Font.Glyph( + "q", + [ + " *** ", + "* *", + "* * *", + "* * ", + " ** *", + ], + ), + Font.Glyph( + "r", + [ + "**** ", + "* *", + "**** ", + "* * ", + "* *", + ], + ), + Font.Glyph( + "s", + [ + " ****", + "* ", + " *** ", + " *", + "**** ", + ], + ), + Font.Glyph( + "t", + [ + "*****", + " * ", + " * ", + " * ", + " * ", + ], + ), + Font.Glyph( + "u", + [ + "* *", + "* *", + "* *", + "* *", + " *** ", + ], + ), + Font.Glyph( + "v", + [ + "* *", + "* *", + "* *", + " * * ", + " * ", + ], + ), + Font.Glyph( + "w", + [ + "* *", + "* *", + "* * *", + "** **", + "* *", + ], + ), + Font.Glyph( + "x", + [ + "* *", + " * * ", + " * ", + " * * ", + "* *", + ], + ), + Font.Glyph( + "y", + [ + "* *", + " * * ", + " * ", + " * ", + " * ", + ], + ), + Font.Glyph( + "z", + [ + "*****", + " * ", + " * ", + " * ", + "*****", + ], + ), + Font.Glyph( + "0", + [ + " *** ", + "* *", + "* * *", + "* *", + " *** ", + ], + ), + Font.Glyph( + "1", + [ + " ** ", + "* * ", + " * ", + " * ", + "*****", + ], + ), + Font.Glyph( + "2", + [ + " *** ", + "* *", + " * ", + " * ", + " ****", + ], + ), + Font.Glyph( + "3", + [ + " *** ", + "* *", + " ***", + "* *", + " *** ", + ], + ), + Font.Glyph( + "4", + [ + "* * ", + "* * ", + "*****", + " * ", + " * ", + ], + ), + Font.Glyph( + "5", + [ + "*****", + "* ", + "**** ", + " *", + "**** ", + ], + ), + Font.Glyph( + "6", + [ + " ****", + "* ", + "**** ", + "* *", + " *** ", + ], + ), + Font.Glyph( + "7", + [ + "*****", + " * ", + " * ", + " * ", + "* ", + ], + ), + Font.Glyph( + "8", + [ + " *** ", + "* *", + " *** ", + "* *", + " *** ", + ], + ), + Font.Glyph( + "9", + [ + " *** ", + "* *", + " ****", + " *", + "**** ", + ], + ), + Font.Glyph( + ".", + [ + " ", + " ", + " ", + " ", + " *", + ], + ), + Font.Glyph( + "!", + [ + " *", + " *", + " *", + " ", + " *", + ], + ), + Font.Glyph( + ":", + [ + " ", + " *", + " ", + " *", + " ", + ], + ), + Font.Glyph( + "<", + [ + " **", + " ** ", + "* ", + " ** ", + " **", + ], + ), + Font.Glyph( + ">", + [ + "** ", + " ** ", + " *", + " ** ", + "** ", + ], + ), +] + + if __name__ == "__main__": game = Game() game.run()