cli-arcade 2026.1.3__py3-none-any.whl → 2026.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. cli.py +110 -67
  2. {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/METADATA +41 -10
  3. cli_arcade-2026.2.1.dist-info/RECORD +50 -0
  4. game_classes/__pycache__/__init__.cpython-312.pyc +0 -0
  5. game_classes/__pycache__/game_base.cpython-312.pyc +0 -0
  6. game_classes/__pycache__/game_base.cpython-313.pyc +0 -0
  7. game_classes/__pycache__/highscores.cpython-312.pyc +0 -0
  8. game_classes/__pycache__/menu.cpython-312.pyc +0 -0
  9. game_classes/__pycache__/menu.cpython-313.pyc +0 -0
  10. game_classes/__pycache__/ptk.cpython-312.pyc +0 -0
  11. game_classes/__pycache__/ptk.cpython-313.pyc +0 -0
  12. game_classes/__pycache__/ptk_curses.cpython-313.pyc +0 -0
  13. game_classes/__pycache__/ptk_game_base.cpython-313.pyc +0 -0
  14. game_classes/__pycache__/ptk_menu.cpython-313.pyc +0 -0
  15. game_classes/__pycache__/ptk_tools.cpython-313.pyc +0 -0
  16. game_classes/__pycache__/tools.cpython-312.pyc +0 -0
  17. game_classes/__pycache__/tools.cpython-313.pyc +0 -0
  18. game_classes/game_base.py +5 -5
  19. game_classes/menu.py +4 -4
  20. game_classes/ptk.py +435 -0
  21. game_classes/tools.py +27 -25
  22. games/byte_bouncer/__pycache__/game.cpython-312.pyc +0 -0
  23. games/byte_bouncer/__pycache__/game.cpython-313.pyc +0 -0
  24. games/byte_bouncer/game.py +45 -21
  25. games/star_ship/__pycache__/game.cpython-312.pyc +0 -0
  26. games/star_ship/__pycache__/game.cpython-313.pyc +0 -0
  27. games/star_ship/game.py +64 -24
  28. games/terminal_tumble/__pycache__/game.cpython-312.pyc +0 -0
  29. games/terminal_tumble/__pycache__/game.cpython-313.pyc +0 -0
  30. games/terminal_tumble/game.py +41 -38
  31. cli_arcade-2026.1.3.dist-info/RECORD +0 -35
  32. {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/WHEEL +0 -0
  33. {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/entry_points.txt +0 -0
  34. {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/licenses/LICENSE +0 -0
  35. {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/top_level.txt +0 -0
games/star_ship/game.py CHANGED
@@ -1,4 +1,4 @@
1
- import curses
1
+ from game_classes import ptk
2
2
  import os
3
3
  import time
4
4
  import random
@@ -15,7 +15,7 @@ except Exception:
15
15
  from game_classes.highscores import HighScores
16
16
  from game_classes.game_base import GameBase
17
17
  from game_classes.menu import Menu
18
- from game_classes.tools import verify_terminal_size, init_curses, is_enter_key, glyph
18
+ from game_classes.tools import verify_terminal_size, init_ptk, is_enter_key, glyph
19
19
 
20
20
  TITLE = [
21
21
  " ______ ______ _ ",
@@ -25,6 +25,10 @@ TITLE = [
25
25
  " /_/ "
26
26
  ]
27
27
 
28
+ # minimum terminal size required to run this game (cols, rows)
29
+ MIN_COLS = 70
30
+ MIN_ROWS = 20
31
+
28
32
  class Game(GameBase):
29
33
  def __init__(self, stdscr, player_name='Player'):
30
34
  self.title = TITLE
@@ -33,7 +37,7 @@ class Game(GameBase):
33
37
  'stars': {'player': 'Player', 'value': 1},
34
38
  'length': {'player': 'Player', 'value': 3},
35
39
  })
36
- super().__init__(stdscr, player_name, 0.12, curses.COLOR_GREEN)
40
+ super().__init__(stdscr, player_name, 0.12, ptk.COLOR_GREEN)
37
41
  self.init_scores([['score', 0], ['stars', 0], ['length', 0]])
38
42
 
39
43
  # game state
@@ -41,6 +45,8 @@ class Game(GameBase):
41
45
  self.special_expire = None
42
46
  self.next_special_at = time.time() + random.uniform(8, 18)
43
47
  self.dir = (0, 1)
48
+ # track the direction that was used for the last completed step
49
+ self._dir_at_last_step = self.dir
44
50
  self.stars = []
45
51
  cy = self.height // 2
46
52
  cx = self.width // 2
@@ -95,17 +101,17 @@ class Game(GameBase):
95
101
 
96
102
  # draw high scores below title
97
103
  new_score = ' ***NEW High Score!' if self.new_highs.get('score', False) else ''
98
- self.stdscr.addstr(info_y + 0, info_x, f'High Score: {int(self.high_scores["score"]["value"]):,} ({self.high_scores["score"]["player"]}){new_score}', curses.color_pair(curses.COLOR_GREEN))
104
+ self.stdscr.addstr(info_y + 0, info_x, f'High Score: {int(self.high_scores["score"]["value"]):,} ({self.high_scores["score"]["player"]}){new_score}', ptk.color_pair(ptk.COLOR_GREEN))
99
105
  new_ship_length = ' ***NEW Longest Ship!' if self.new_highs.get('length', False) else ''
100
- self.stdscr.addstr(info_y + 1, info_x, f'Longest Ship: {int(self.high_scores["length"]["value"]):,} ({self.high_scores["length"]["player"]}){new_ship_length}', curses.color_pair(curses.COLOR_BLUE))
106
+ self.stdscr.addstr(info_y + 1, info_x, f'Longest Ship: {int(self.high_scores["length"]["value"]):,} ({self.high_scores["length"]["player"]}){new_ship_length}', ptk.color_pair(ptk.COLOR_BLUE))
101
107
  new_stars = ' ***NEW Most Stars!' if self.new_highs.get('stars', False) else ''
102
- self.stdscr.addstr(info_y + 2, info_x, f'Most Stars: {int(self.high_scores["stars"]["value"]):,} ({self.high_scores["stars"]["player"]}){new_stars}', curses.color_pair(curses.COLOR_BLUE))
108
+ self.stdscr.addstr(info_y + 2, info_x, f'Most Stars: {int(self.high_scores["stars"]["value"]):,} ({self.high_scores["stars"]["player"]}){new_stars}', ptk.color_pair(ptk.COLOR_BLUE))
103
109
 
104
110
  # draw game info below title
105
111
  self.stdscr.addstr(info_y + 4, info_x, f'Player: {self.player_name}')
106
- self.stdscr.addstr(info_y + 5, info_x, f'Score: {int(self.scores["score"]):,}', curses.color_pair(curses.COLOR_GREEN))
107
- self.stdscr.addstr(info_y + 6, info_x, f'Ship Length: {int(self.scores["length"]):,}', curses.color_pair(curses.COLOR_BLUE))
108
- self.stdscr.addstr(info_y + 7, info_x, f'Stars: {int(self.scores["stars"]):,}', curses.color_pair(curses.COLOR_BLUE))
112
+ self.stdscr.addstr(info_y + 5, info_x, f'Score: {int(self.scores["score"]):,}', ptk.color_pair(ptk.COLOR_GREEN))
113
+ self.stdscr.addstr(info_y + 6, info_x, f'Ship Length: {int(self.scores["length"]):,}', ptk.color_pair(ptk.COLOR_BLUE))
114
+ self.stdscr.addstr(info_y + 7, info_x, f'Stars: {int(self.scores["stars"]):,}', ptk.color_pair(ptk.COLOR_BLUE))
109
115
 
110
116
  self.stdscr.addstr(info_y + 9 , info_x, '↑ | w : Up')
111
117
  self.stdscr.addstr(info_y + 10, info_x, '← | a : Left')
@@ -118,22 +124,47 @@ class Game(GameBase):
118
124
 
119
125
  def draw(self):
120
126
  self.draw_info()
127
+ # draw a green floor and a right wall (similar to Byte Bouncer)
128
+ try:
129
+ block = glyph('BLOCK')
130
+ except Exception:
131
+ block = '#'
132
+ # floor just below the visible play area
133
+ floor_y = self.height
134
+ try:
135
+ for fx in range(0, self.width + 1):
136
+ try:
137
+ self.stdscr.addch(floor_y, fx, block, ptk.color_pair(ptk.COLOR_BLUE))
138
+ except Exception:
139
+ pass
140
+ except Exception:
141
+ pass
142
+ # right wall drawn off-screen to the right (safe to attempt)
143
+ right_col = self.width + 1
144
+ try:
145
+ for wy in range(0, floor_y + 1):
146
+ try:
147
+ self.stdscr.addch(wy, right_col, block, ptk.color_pair(ptk.COLOR_BLUE))
148
+ except Exception:
149
+ pass
150
+ except Exception:
151
+ pass
121
152
  # draw game elements (ship + star) on top of title/info
122
153
  try:
123
154
  # draw yellow stars
124
155
  for fy, fx in list(getattr(self, 'stars', [])):
125
- self.stdscr.addch(fy, fx, '*', curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
156
+ self.stdscr.addch(fy, fx, '*', ptk.color_pair(ptk.COLOR_YELLOW) | ptk.A_BOLD)
126
157
  # draw special magenta (if present)
127
158
  if getattr(self, 'special', None) is not None:
128
159
  sy, sx = self.special
129
- self.stdscr.addch(sy, sx, glyph('CIRCLE_FILLED'), curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
160
+ self.stdscr.addch(sy, sx, glyph('CIRCLE_FILLED'), ptk.color_pair(ptk.COLOR_MAGENTA) | ptk.A_BOLD)
130
161
  # ship: head and body
131
162
  for idx, (sy, sx) in enumerate(self.ship):
132
163
  try:
133
164
  if idx == 0:
134
- self.stdscr.addch(sy, sx, glyph('CIRCLE_FILLED'), curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
165
+ self.stdscr.addch(sy, sx, glyph('CIRCLE_FILLED'), ptk.color_pair(ptk.COLOR_GREEN) | ptk.A_BOLD)
135
166
  else:
136
- self.stdscr.addch(sy, sx, glyph('CIRCLE_FILLED'), curses.color_pair(curses.COLOR_BLUE))
167
+ self.stdscr.addch(sy, sx, glyph('CIRCLE_FILLED'), ptk.color_pair(ptk.COLOR_BLUE))
137
168
  except Exception:
138
169
  pass
139
170
  except Exception:
@@ -171,6 +202,13 @@ class Game(GameBase):
171
202
  return
172
203
  # move head
173
204
  self.ship.insert(0, (nh, nx))
205
+ # remember the direction actually used for this step so input
206
+ # handling can forbid immediate 180-degree reversals relative
207
+ # to the last moved direction.
208
+ try:
209
+ self._dir_at_last_step = (dy, dx)
210
+ except Exception:
211
+ pass
174
212
  # eating: normal yellow stars
175
213
  if (nh, nx) in self.stars:
176
214
  try:
@@ -207,23 +245,25 @@ class Game(GameBase):
207
245
 
208
246
  def movement(self, ch):
209
247
  new_dir = None
210
- if ch in (curses.KEY_UP, ord('w')):
248
+ if ch in (ptk.KEY_UP, ord('w')):
211
249
  new_dir = (-1, 0)
212
- elif ch in (curses.KEY_DOWN, ord('s')):
250
+ elif ch in (ptk.KEY_DOWN, ord('s')):
213
251
  new_dir = (1, 0)
214
- elif ch in (curses.KEY_LEFT, ord('a')):
252
+ elif ch in (ptk.KEY_LEFT, ord('a')):
215
253
  new_dir = (0, -1)
216
- elif ch in (curses.KEY_RIGHT, ord('d')):
254
+ elif ch in (ptk.KEY_RIGHT, ord('d')):
217
255
  new_dir = (0, 1)
218
256
  if new_dir:
219
- # prevent immediate 180-degree turns
220
- cy, cx = self.dir
221
- if (new_dir[0], new_dir[1]) != (-cy, -cx):
257
+ # prevent immediate 180-degree turns relative to the direction
258
+ # that was used for the last completed movement step. This stops
259
+ # very fast opposing key presses from reversing the ship between
260
+ # ticks and causing self-collisions.
261
+ last = getattr(self, '_dir_at_last_step', self.dir)
262
+ if (new_dir[0], new_dir[1]) != (-last[0], -last[1]):
222
263
  self.dir = new_dir
223
264
 
224
265
  def main(stdscr):
225
- verify_terminal_size('Star Ship')
226
- init_curses(stdscr)
266
+ init_ptk(stdscr)
227
267
  while True:
228
268
  game = Game(stdscr)
229
269
  menu = Menu(game)
@@ -235,9 +275,9 @@ def main(stdscr):
235
275
 
236
276
  if __name__ == '__main__':
237
277
  try:
238
- curses.wrapper(main)
278
+ ptk.wrapper(main)
239
279
  except KeyboardInterrupt:
240
280
  try:
241
- curses.endwin()
281
+ ptk.endwin()
242
282
  except Exception:
243
283
  pass
@@ -1,4 +1,4 @@
1
- import curses
1
+ from game_classes import ptk
2
2
  import random
3
3
  import os
4
4
  import math
@@ -16,7 +16,7 @@ except Exception:
16
16
  from game_classes.highscores import HighScores
17
17
  from game_classes.game_base import GameBase
18
18
  from game_classes.menu import Menu
19
- from game_classes.tools import verify_terminal_size, init_curses, is_enter_key
19
+ from game_classes.tools import verify_terminal_size, init_ptk, is_enter_key
20
20
 
21
21
  TITLE = [
22
22
  '___________ .__ .__ ___________ ___. .__ ',
@@ -27,6 +27,10 @@ TITLE = [
27
27
  r' \/ \/ \/ \/ \/ \/ \/ '
28
28
  ]
29
29
 
30
+ # minimum terminal size required to run this game (cols, rows)
31
+ MIN_COLS = 100
32
+ MIN_ROWS = 30
33
+
30
34
  SHAPES = {
31
35
  'I': [[(0,1),(1,1),(2,1),(3,1)], [(2,0),(2,1),(2,2),(2,3)]],
32
36
  'O': [[(0,0),(1,0),(0,1),(1,1)]],
@@ -38,13 +42,13 @@ SHAPES = {
38
42
  }
39
43
 
40
44
  COLORS = {
41
- 'I': curses.COLOR_WHITE,
42
- 'O': curses.COLOR_BLUE,
43
- 'T': curses.COLOR_CYAN,
44
- 'S': curses.COLOR_GREEN,
45
- 'Z': curses.COLOR_RED,
46
- 'J': curses.COLOR_MAGENTA,
47
- 'L': curses.COLOR_YELLOW,
45
+ 'I': ptk.COLOR_WHITE,
46
+ 'O': ptk.COLOR_BLUE,
47
+ 'T': ptk.COLOR_CYAN,
48
+ 'S': ptk.COLOR_GREEN,
49
+ 'Z': ptk.COLOR_RED,
50
+ 'J': ptk.COLOR_MAGENTA,
51
+ 'L': ptk.COLOR_YELLOW,
48
52
  }
49
53
 
50
54
  class Piece:
@@ -90,7 +94,7 @@ class Game(GameBase):
90
94
  'lines': {'player': 'Player', 'value': 0},
91
95
  'level': {'player': 'Player', 'value': 1},
92
96
  })
93
- super().__init__(stdscr, player_name, 0.5, curses.COLOR_RED)
97
+ super().__init__(stdscr, player_name, 0.5, ptk.COLOR_RED)
94
98
  self.msg_height = self.height - 22
95
99
  self.init_scores([['score', 0], ['lines', 0], ['level', 1]])
96
100
 
@@ -104,7 +108,7 @@ class Game(GameBase):
104
108
  self.drop_timer = 0
105
109
  self.msg_log = deque(maxlen=self.msg_height)
106
110
 
107
- def push_message(self, text, color_const=curses.COLOR_WHITE):
111
+ def push_message(self, text, color_const=ptk.COLOR_WHITE):
108
112
  try:
109
113
  self.msg_log.append((text, color_const))
110
114
  except Exception:
@@ -113,7 +117,7 @@ class Game(GameBase):
113
117
  def handle_new_highs(self, metric):
114
118
  if not self.new_highs[metric]:
115
119
  cap_metric = metric.capitalize()
116
- self.push_message(f'New High {cap_metric}!', curses.COLOR_YELLOW)
120
+ self.push_message(f'New High {cap_metric}!', ptk.COLOR_YELLOW)
117
121
  super().handle_new_highs(metric)
118
122
 
119
123
  def next_piece(self):
@@ -156,18 +160,18 @@ class Game(GameBase):
156
160
  points = int(base_points * multiplier)
157
161
  # push a rolling message for this clear
158
162
  label = ''
159
- color = curses.COLOR_WHITE
163
+ color = ptk.COLOR_WHITE
160
164
  if cleared == 2:
161
165
  label = 'Double!'
162
- color = curses.COLOR_CYAN
166
+ color = ptk.COLOR_CYAN
163
167
  points = int(points * 1.5)
164
168
  elif cleared == 3:
165
169
  label = 'Triple!'
166
- color = curses.COLOR_BLUE
170
+ color = ptk.COLOR_BLUE
167
171
  points = int(points * 2.0)
168
172
  elif cleared == 4:
169
173
  label = 'Full Stack!'
170
- color = curses.COLOR_GREEN
174
+ color = ptk.COLOR_GREEN
171
175
  points = int(points * 3.0)
172
176
  self.scores['score'] += points
173
177
  self.push_message(f'+{points} {label}', color)
@@ -194,7 +198,7 @@ class Game(GameBase):
194
198
  self.scores['score'] += bonus
195
199
  # push slam bonus message if any
196
200
  if bonus > 0:
197
- self.push_message(f'+{bonus} Slam Bonus!', curses.COLOR_MAGENTA)
201
+ self.push_message(f'+{bonus} Slam Bonus!', ptk.COLOR_MAGENTA)
198
202
  try:
199
203
  self.update_high_scores()
200
204
  except Exception:
@@ -223,7 +227,7 @@ class Game(GameBase):
223
227
  if py >= 0 and px >= 0:
224
228
  color = COLORS.get(self.next.shape, 1)
225
229
  try:
226
- self.stdscr.addstr(py, px, '[]', curses.color_pair(color))
230
+ self.stdscr.addstr(py, px, '[]', ptk.color_pair(color))
227
231
  except Exception:
228
232
  try:
229
233
  self.stdscr.addstr(py, px, '[]')
@@ -233,19 +237,19 @@ class Game(GameBase):
233
237
  pass
234
238
  except Exception:
235
239
  pass
236
- self.stdscr.addstr(info_y + 0 , 55, f'High Score: {int(self.high_scores["score"]["value"]):,} ({self.high_scores["score"]["player"]})', curses.color_pair(curses.COLOR_GREEN))
237
- self.stdscr.addstr(info_y + 1 , 55, f'High Lines: {int(self.high_scores["lines"]["value"]):,} ({self.high_scores["lines"]["player"]})', curses.color_pair(curses.COLOR_BLUE))
238
- self.stdscr.addstr(info_y + 2 , 55, f'High Level: {int(self.high_scores["level"]["value"]):,} ({self.high_scores["level"]["player"]})', curses.color_pair(curses.COLOR_MAGENTA))
240
+ self.stdscr.addstr(info_y + 0 , 55, f'High Score: {int(self.high_scores["score"]["value"]):,} ({self.high_scores["score"]["player"]})', ptk.color_pair(ptk.COLOR_GREEN))
241
+ self.stdscr.addstr(info_y + 1 , 55, f'High Lines: {int(self.high_scores["lines"]["value"]):,} ({self.high_scores["lines"]["player"]})', ptk.color_pair(ptk.COLOR_BLUE))
242
+ self.stdscr.addstr(info_y + 2 , 55, f'High Level: {int(self.high_scores["level"]["value"]):,} ({self.high_scores["level"]["player"]})', ptk.color_pair(ptk.COLOR_MAGENTA))
239
243
  # player name
240
244
  info_y += 4
241
245
  try:
242
- self.stdscr.addstr(info_y, 43, f'Player: {self.player_name}', curses.A_BOLD)
246
+ self.stdscr.addstr(info_y, 43, f'Player: {self.player_name}', ptk.A_BOLD)
243
247
  except Exception:
244
248
  pass
245
249
  self.stdscr.addstr(info_y + 1, 43, '====================================================')
246
- self.stdscr.addstr(info_y + 2, 43, f'Score: {int(self.scores["score"]):,}', curses.color_pair(curses.COLOR_GREEN))
247
- self.stdscr.addstr(info_y + 3, 43, f'Lines: {int(self.scores["lines"]):,}', curses.color_pair(curses.COLOR_BLUE))
248
- self.stdscr.addstr(info_y + 4, 43, f'Level: {int(self.scores["level"]):,}', curses.color_pair(curses.COLOR_MAGENTA))
250
+ self.stdscr.addstr(info_y + 2, 43, f'Score: {int(self.scores["score"]):,}', ptk.color_pair(ptk.COLOR_GREEN))
251
+ self.stdscr.addstr(info_y + 3, 43, f'Lines: {int(self.scores["lines"]):,}', ptk.color_pair(ptk.COLOR_BLUE))
252
+ self.stdscr.addstr(info_y + 4, 43, f'Level: {int(self.scores["level"]):,}', ptk.color_pair(ptk.COLOR_MAGENTA))
249
253
 
250
254
  self.stdscr.addstr(info_y + 5, 43, '====================================================')
251
255
  # draw rolling message log (most recent at top)
@@ -266,7 +270,7 @@ class Game(GameBase):
266
270
  break
267
271
  y = start_y + idx
268
272
  try:
269
- self.stdscr.addstr(y, preview_x, text, curses.color_pair(color_const))
273
+ self.stdscr.addstr(y, preview_x, text, ptk.color_pair(color_const))
270
274
  except Exception:
271
275
  try:
272
276
  self.stdscr.addstr(y, preview_x, text)
@@ -310,9 +314,9 @@ class Game(GameBase):
310
314
  self.stdscr.addstr(y_off, x*2, ' |')
311
315
  elif ch != ' ':
312
316
  color = COLORS.get(ch, 1)
313
- attr = curses.color_pair(color)
317
+ attr = ptk.color_pair(color)
314
318
  if ch == 'J':
315
- attr |= curses.A_DIM
319
+ attr |= ptk.A_DIM
316
320
  self.stdscr.addstr(y_off, x*2, '[]', attr)
317
321
  else:
318
322
  self.stdscr.addstr(y_off, x*2, ' ')
@@ -323,9 +327,9 @@ class Game(GameBase):
323
327
  y_off = y + len(self.title)
324
328
  if 0 <= y < self.height - 6 and 0 <= x < len(self.board[0]):
325
329
  color = COLORS.get(self.current.shape, 1)
326
- attr = curses.color_pair(color)
330
+ attr = ptk.color_pair(color)
327
331
  if self.current.shape == 'J':
328
- attr |= curses.A_DIM
332
+ attr |= ptk.A_DIM
329
333
  self.stdscr.addstr(y_off, x*2, '[]', attr)
330
334
 
331
335
  # draw borders and (shifted by top margin)
@@ -347,20 +351,19 @@ class Game(GameBase):
347
351
  self.lock_piece()
348
352
 
349
353
  def movement(self, ch):
350
- if ch in (curses.KEY_LEFT, ord('a')):
354
+ if ch in (ptk.KEY_LEFT, ord('a')):
351
355
  self.current.move(-1,0,self.board)
352
- elif ch in (curses.KEY_RIGHT, ord('d')):
356
+ elif ch in (ptk.KEY_RIGHT, ord('d')):
353
357
  self.current.move(1,0,self.board)
354
- elif ch in (curses.KEY_DOWN, ord('s')):
358
+ elif ch in (ptk.KEY_DOWN, ord('s')):
355
359
  self.current.move(0,1,self.board)
356
- elif ch in (curses.KEY_UP, ord('w')):
360
+ elif ch in (ptk.KEY_UP, ord('w')):
357
361
  self.current.rotate(self.board)
358
362
  elif is_enter_key(ch) or ch == ord(' '):
359
363
  self.hard_drop()
360
364
 
361
365
  def main(stdscr):
362
- verify_terminal_size('Terminal Tumble', 100, 30)
363
- init_curses(stdscr)
366
+ init_ptk(stdscr)
364
367
  while True:
365
368
  game = Game(stdscr)
366
369
  menu = Menu(game)
@@ -372,9 +375,9 @@ def main(stdscr):
372
375
 
373
376
  if __name__ == '__main__':
374
377
  try:
375
- curses.wrapper(main)
378
+ ptk.wrapper(main)
376
379
  except KeyboardInterrupt:
377
380
  try:
378
- curses.endwin()
381
+ ptk.endwin()
379
382
  except Exception:
380
383
  pass
@@ -1,35 +0,0 @@
1
- cli.py,sha256=-YynPCpDpXEmF8uf41-7HDswp_IQpIYZpYuGrBNJ5PQ,17495
2
- cli_arcade-2026.1.3.dist-info/licenses/LICENSE,sha256=1PLSNFyGPi9COkVEeNYcUTuRLqGxzMLLTah05tByD4s,1099
3
- game_classes/__init__.py,sha256=A0o4vBUktOiONToIJb0hACalszBnFS9ls9WXPn3-KH0,28
4
- game_classes/game_base.py,sha256=q779LzYsK8rO0rWrj2iB5407wuLiZvuFiRBx6qQEfPQ,3505
5
- game_classes/highscores.py,sha256=dDK-7lFXSMwIqExXGp6_-7QfQz6CIf12lJ22poeCdh4,4061
6
- game_classes/menu.py,sha256=6frg15Ibx_H89g-KmHv53d6uWSUsxluhXMGvn4Nj7xY,2795
7
- game_classes/tools.py,sha256=CBtS_4n8TgIwfRlGftR5oGg4Nc7u3uetQ0rO-9AffAs,4974
8
- game_classes/__pycache__/__init__.cpython-313.pyc,sha256=zSl9ESMUQn5Vo_XXAA8DD7lpFHOtOwBruGvCOJ5qeuo,192
9
- game_classes/__pycache__/game_base.cpython-313.pyc,sha256=p6NUAgjNMXwPoNkQq01tUZm_ZYX18jaIMRZOm35vKsk,6962
10
- game_classes/__pycache__/highscores.cpython-313.pyc,sha256=0rYMYs6NOzT-f00Me20F-46kUR_x8ZXk2jngO5amLy4,7080
11
- game_classes/__pycache__/menu.cpython-313.pyc,sha256=wseCBPJ5XKtVI8scyJrBZxAW2avD2JF3J0IpRUYzsR8,4459
12
- game_classes/__pycache__/tools.cpython-313.pyc,sha256=l3e625GBKAo_lEbuDWKYzgzRyS4d6lQMcA_O0lc7JnI,7345
13
- games/__init__.py,sha256=Rg46lnLj_sezpnXaQTS1NXX5OaRhR_EWpNTyYUxycj4,21
14
- games/byte_bouncer/__init__.py,sha256=8hxIHRyUPB-X6vJXh1432ytKjOcfIzCbdpO5mmWA7iQ,33
15
- games/byte_bouncer/game.py,sha256=9SFBre-QvjTa5maKP-XjI-Fh-1dNgxeYxxtglrccnA4,8105
16
- games/byte_bouncer/__pycache__/byte_bouncer.cpython-313.pyc,sha256=JSTB6-_KCukAAVaZz9TpJ3V4gif_M3n_L5X1KOviSJ8,24605
17
- games/byte_bouncer/__pycache__/game.cpython-313.pyc,sha256=9HjBQLhORVJtEw-W5Hg5N7OKuU-hn7LimU7-QRvMHQk,13029
18
- games/byte_bouncer/__pycache__/highscores.cpython-313.pyc,sha256=g3FozKpWGDVsTVETNpb6lRHcKA3ZIsQio8Kh7KCGVCI,3125
19
- games/star_ship/__init__.py,sha256=X8AdhoccmNVu6yl7MalsxIsMxuEsddRdlvEQmhZMD0A,30
20
- games/star_ship/game.py,sha256=Mph_USmrroynsli43vD3luadYAOiPXmbs0JMOCDGxMk,9545
21
- games/star_ship/__pycache__/game.cpython-313.pyc,sha256=cd5IpXpj9WMeW12hLnvYq4pD-SZFkR9m270OsgkvmMQ,14624
22
- games/star_ship/__pycache__/highscores.cpython-313.pyc,sha256=nHbEOTWeX0U9D47K2EP5S5uDPI0WW52Eg7h_NesG1OU,3148
23
- games/star_ship/__pycache__/nibbles.cpython-313.pyc,sha256=Yv2P-GwEc30zA24aO7k0hkk1AOja6K85AFvmjLIffqQ,20510
24
- games/star_ship/__pycache__/snek.cpython-313.pyc,sha256=3Mwyy6vjGFRmNyAWSqV5JRilLOxiCSndBmwua_Y7VTs,13313
25
- games/star_ship/__pycache__/star_ship.cpython-313.pyc,sha256=9gJt10MTzVTSwsc4I16TDZTaAXclfhfGTzgfYgZNOKo,28021
26
- games/terminal_tumble/__init__.py,sha256=axTd7hCgnLRNU8QS73NnQY3q5xjIb3ZYQORJ9Lz8l_E,36
27
- games/terminal_tumble/game.py,sha256=p4lrFHBoHZJLpsUr0jXmR9FI8OTZmwfGIS1Z29MoYzU,15699
28
- games/terminal_tumble/__pycache__/game.cpython-313.pyc,sha256=-xFF1wgupqiSoHz3QZwMNHxhAIV_UyIp0YifRdg8ijA,24104
29
- games/terminal_tumble/__pycache__/highscores.cpython-313.pyc,sha256=UPnU9Zq88q2TZq1qXyo8e2Y_PdOgwvu5mncIkunFah8,3154
30
- games/terminal_tumble/__pycache__/terminal_tumble.cpython-313.pyc,sha256=7oLpmmkYjpLYRC0bjRoEkHbIUNq7FZVJToGoja94m0o,38016
31
- cli_arcade-2026.1.3.dist-info/METADATA,sha256=y6cO0eJZ6QTKb7Z00WUEPnx7OEJQcMreKZcaFG9MVhM,2302
32
- cli_arcade-2026.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
33
- cli_arcade-2026.1.3.dist-info/entry_points.txt,sha256=nNElhZv4nM_lrOYpvnOChtJn1XsWwQr8sI344flzzTQ,56
34
- cli_arcade-2026.1.3.dist-info/top_level.txt,sha256=gTfYlz7gHYgPY0ugfJivukCOkNzmOisNR6VuJjAXPF4,23
35
- cli_arcade-2026.1.3.dist-info/RECORD,,