egang commited on
Commit
429e5f2
Β·
verified Β·
1 Parent(s): 72fc5e1

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +180 -0
app.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import sys
3
+ import time
4
+ import termios
5
+ import tty
6
+ from typing import List, Tuple, Optional
7
+
8
+ # Board dimensions
9
+ WIDTH, HEIGHT = 10, 20
10
+
11
+ # Tetromino shapes
12
+ SHAPES = [
13
+ [[1, 1, 1, 1]], # I
14
+ [[1, 1], [1, 1]], # O
15
+ [[0, 1, 0], [1, 1, 1]], # T
16
+ [[0, 1, 1], [1, 1, 0]], # S
17
+ [[1, 1, 0], [0, 1, 1]], # Z
18
+ [[1, 0, 0], [1, 1, 1]], # J
19
+ [[0, 0, 1], [1, 1, 1]], # L
20
+ ]
21
+
22
+ # Colors (ANSI)
23
+ COLORS = ["\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m", "\033[37m"]
24
+ RESET = "\033[0m"
25
+
26
+ class Tetromino:
27
+ def __init__(self, shape: List[List[int]], color: str):
28
+ self.shape = shape
29
+ self.color = color
30
+ self.x = WIDTH // 2 - len(shape[0]) // 2
31
+ self.y = 0
32
+
33
+ def rotated(self) -> List[List[int]]:
34
+ """Return the shape rotated 90Β° clockwise."""
35
+ return [list(row) for row in zip(*self.shape[::-1])]
36
+
37
+ class Tetris:
38
+ def __init__(self):
39
+ self.board: List[List[Optional[str]]] = [[None] * WIDTH for _ in range(HEIGHT)]
40
+ self.current: Tetromino = self.new_piece()
41
+ self.next: Tetromino = self.new_piece()
42
+ self.game_over = False
43
+ self.score = 0
44
+ self.level = 1
45
+ self.drop_time = 0.5 # seconds between drops
46
+
47
+ def new_piece(self) -> Tetromino:
48
+ shape = random.choice(SHAPES)
49
+ color = random.choice(COLORS)
50
+ return Tetromino(shape, color)
51
+
52
+ def valid(self, shape: List[List[int]], x: int, y: int) -> bool:
53
+ for dy, row in enumerate(shape):
54
+ for dx, cell in enumerate(row):
55
+ if cell:
56
+ nx, ny = x + dx, y + dy
57
+ if nx < 0 or nx >= WIDTH or ny >= HEIGHT or (ny >= 0 and self.board[ny][nx]):
58
+ return False
59
+ return True
60
+
61
+ def lock_piece(self):
62
+ shape = self.current.shape
63
+ x, y = self.current.x, self.current.y
64
+ for dy, row in enumerate(shape):
65
+ for dx, cell in enumerate(row):
66
+ if cell and y + dy >= 0:
67
+ self.board[y + dy][x + dx] = self.current.color
68
+ self.clear_lines()
69
+ self.current = self.next
70
+ self.next = self.new_piece()
71
+ if not self.valid(self.current.shape, self.current.x, self.current.y):
72
+ self.game_over = True
73
+
74
+ def clear_lines(self):
75
+ new_board = [row for row in self.board if any(cell is None for cell in row)]
76
+ lines_cleared = HEIGHT - len(new_board)
77
+ if lines_cleared:
78
+ self.score += (lines_cleared ** 2) * 100 * self.level
79
+ for _ in range(lines_cleared):
80
+ new_board.insert(0, [None] * WIDTH)
81
+ self.board = new_board
82
+ self.level = self.score // 1000 + 1
83
+ self.drop_time = max(0.05, 0.5 - (self.level - 1) * 0.05)
84
+
85
+ def move(self, dx: int, dy: int) -> bool:
86
+ new_x = self.current.x + dx
87
+ new_y = self.current.y + dy
88
+ if self.valid(self.current.shape, new_x, new_y):
89
+ self.current.x, self.current.y = new_x, new_y
90
+ return True
91
+ return False
92
+
93
+ def rotate(self):
94
+ rotated = self.current.rotated()
95
+ if self.valid(rotated, self.current.x, self.current.y):
96
+ self.current.shape = rotated
97
+ else:
98
+ # Try wall kicks (simple)
99
+ for dx in (-1, 1, -2, 2):
100
+ if self.valid(rotated, self.current.x + dx, self.current.y):
101
+ self.current.x += dx
102
+ self.current.shape = rotated
103
+ break
104
+
105
+ def hard_drop(self):
106
+ while self.move(0, 1):
107
+ pass
108
+ self.lock_piece()
109
+
110
+ def draw(self):
111
+ # Clear screen + hide cursor
112
+ print("\033[2J\033[?25l", end="")
113
+ # Draw board with border
114
+ output = []
115
+ output.append("Score: {} Level: {}".format(self.score, self.level))
116
+ output.append("β”Œ" + "─" * WIDTH + "┐")
117
+ for y in range(HEIGHT):
118
+ line = []
119
+ for x in range(WIDTH):
120
+ if (0 <= y - self.current.y < len(self.current.shape) and
121
+ 0 <= x - self.current.x < len(self.current.shape[0]) and
122
+ self.current.shape[y - self.current.y][x - self.current.x]):
123
+ line.append(self.current.color + "β– " + RESET)
124
+ else:
125
+ cell = self.board[y][x]
126
+ line.append(cell + "β– " + RESET if cell else " ")
127
+ output.append("β”‚" + "".join(line) + "β”‚")
128
+ output.append("β””" + "─" * WIDTH + "β”˜")
129
+ # Draw next
130
+ output.append("Next:")
131
+ for row in self.next.shape:
132
+ line = []
133
+ for cell in row:
134
+ line.append(self.next.color + "β– " + RESET if cell else " ")
135
+ output.append(" " + "".join(line))
136
+ output.append("\nControls: ← β†’ ↓ rotate: ↑ drop: space quit: q")
137
+ print("\n".join(output), end="", flush=True)
138
+
139
+ def run(self):
140
+ last_drop = time.time()
141
+ while not self.game_over:
142
+ self.draw()
143
+ ch = self.getch()
144
+ if ch == '\x1b': # ESC sequence
145
+ ch += sys.stdin.read(2)
146
+ if ch in ('q', '\x03'): # q or Ctrl-C
147
+ break
148
+ elif ch == '\x1b[A': # up
149
+ self.rotate()
150
+ elif ch == '\x1b[B': # down
151
+ self.move(0, 1)
152
+ elif ch == '\x1b[C': # right
153
+ self.move(1, 0)
154
+ elif ch == '\x1b[D': # left
155
+ self.move(-1, 0)
156
+ elif ch == ' ':
157
+ self.hard_drop()
158
+ last_drop = time.time()
159
+ # Auto-drop
160
+ if time.time() - last_drop > self.drop_time:
161
+ if not self.move(0, 1):
162
+ self.lock_piece()
163
+ last_drop = time.time()
164
+ print("\033[?25h\033[2JGame Over! Score: {}".format(self.score))
165
+
166
+ def getch(self) -> str:
167
+ fd = sys.stdin.fileno()
168
+ old = termios.tcgetattr(fd)
169
+ try:
170
+ tty.setraw(fd)
171
+ ch = sys.stdin.read(1)
172
+ return ch
173
+ finally:
174
+ termios.tcsetattr(fd, termios.TCSADRAIN, old)
175
+
176
+ if __name__ == "__main__":
177
+ try:
178
+ Tetris().run()
179
+ except KeyboardInterrupt:
180
+ print("\033[?25h") # restore cursor