77
88from tetris .engine import DefaultEngine
99from tetris .engine import Engine
10+ from tetris .types import Board
11+ from tetris .types import MinoType
1012from tetris .types import Move
1113from tetris .types import MoveDelta
1214from tetris .types import MoveKind
1315from tetris .types import PartialMove
1416from tetris .types import PieceType
1517from tetris .types import PlayingStatus
18+ from tetris .types import Seed
1619
1720_default_tiles = {
18- 0 : " " ,
19- PieceType .I : "I" ,
20- PieceType .J : "J" ,
21- PieceType .L : "L" ,
22- PieceType .O : "O" ,
23- PieceType .S : "S" ,
24- PieceType .T : "T" ,
25- PieceType .Z : "Z" ,
26- 8 : "@" ,
27- 9 : "X" ,
21+ MinoType . EMPTY : " " ,
22+ MinoType .I : "I" ,
23+ MinoType .J : "J" ,
24+ MinoType .L : "L" ,
25+ MinoType .O : "O" ,
26+ MinoType .S : "S" ,
27+ MinoType .T : "T" ,
28+ MinoType .Z : "Z" ,
29+ MinoType . GHOST : "@" ,
30+ MinoType . GARBAGE : "X" ,
2831}
2932
3033
3134class BaseGame :
32- def __init__ (self , engine : Engine = DefaultEngine ):
35+ def __init__ (
36+ self ,
37+ engine : Engine = DefaultEngine ,
38+ seed : Optional [Seed ] = None ,
39+ board : Optional [Board ] = None ,
40+ board_size : tuple [int , int ] = (20 , 10 ),
41+ level : int = 0 ,
42+ score : int = 0 ,
43+ ):
3344 self .engine = engine
3445 self .seed = secrets .token_bytes ()
35- self .board = np .zeros ((40 , 10 ), dtype = np .int8 )
46+ if board is None :
47+ # Internally, we use 2x the height to "buffer" the board being pushed above the view
48+ self .board = np .zeros ((board_size [0 ] * 2 , board_size [1 ]), dtype = np .int8 )
49+
50+ else :
51+ self .board = board
52+
3653 self .gravity = engine .gravity (self )
3754 self .queue = engine .queue (seed = self .seed )
3855 self .rs = engine .rs (self .board )
56+
3957 self .scorer = engine .scorer ()
58+ self .scorer .level = level
59+ self .scorer .score = score
60+
4061 self .piece = self .rs .spawn (self .queue .pop ())
4162 self .status = PlayingStatus .playing
4263 self .delta : Optional [MoveDelta ] = None
@@ -51,9 +72,17 @@ def score(self) -> int:
5172 def level (self ) -> int :
5273 return self .scorer .level
5374
75+ @property
76+ def height (self ) -> int :
77+ return self .board .shape [0 ] // 2
78+
79+ @property
80+ def width (self ) -> int :
81+ return self .board .shape [1 ]
82+
5483 def reset (self ) -> None :
5584 self .seed = secrets .token_bytes ()
56- self .board = np . zeros (( 40 , 10 ), dtype = np . int8 )
85+ self .board [:] = 0
5786 self .gravity = self .engine .gravity (self )
5887 self .queue = self .engine .queue (seed = self .seed )
5988 self .rs = self .engine .rs (self .board )
@@ -101,7 +130,7 @@ def _lock_piece(self) -> None:
101130
102131 # If all tiles are out of view (half of the internal size), it's a lock-out
103132 for x , y in piece .minos :
104- if self .piece .x + x > self .board . shape [ 0 ] / 2 :
133+ if self .piece .x + x > self .height :
105134 break
106135
107136 else :
@@ -121,7 +150,14 @@ def _lock_piece(self) -> None:
121150
122151 self .hold_lock = False
123152
124- def render (self , tiles : dict [int , str ] = _default_tiles , lines : int = 20 ) -> str :
153+ def render (
154+ self ,
155+ tiles : dict [MinoType , str ] = _default_tiles ,
156+ lines : Optional [int ] = None ,
157+ ) -> str :
158+ if lines is None :
159+ lines = self .height
160+
125161 board = self .board .copy ()
126162 piece = self .piece
127163 ghost_x = piece .x
0 commit comments