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.
- cli.py +110 -67
- {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/METADATA +41 -10
- cli_arcade-2026.2.1.dist-info/RECORD +50 -0
- game_classes/__pycache__/__init__.cpython-312.pyc +0 -0
- game_classes/__pycache__/game_base.cpython-312.pyc +0 -0
- game_classes/__pycache__/game_base.cpython-313.pyc +0 -0
- game_classes/__pycache__/highscores.cpython-312.pyc +0 -0
- game_classes/__pycache__/menu.cpython-312.pyc +0 -0
- game_classes/__pycache__/menu.cpython-313.pyc +0 -0
- game_classes/__pycache__/ptk.cpython-312.pyc +0 -0
- game_classes/__pycache__/ptk.cpython-313.pyc +0 -0
- game_classes/__pycache__/ptk_curses.cpython-313.pyc +0 -0
- game_classes/__pycache__/ptk_game_base.cpython-313.pyc +0 -0
- game_classes/__pycache__/ptk_menu.cpython-313.pyc +0 -0
- game_classes/__pycache__/ptk_tools.cpython-313.pyc +0 -0
- game_classes/__pycache__/tools.cpython-312.pyc +0 -0
- game_classes/__pycache__/tools.cpython-313.pyc +0 -0
- game_classes/game_base.py +5 -5
- game_classes/menu.py +4 -4
- game_classes/ptk.py +435 -0
- game_classes/tools.py +27 -25
- games/byte_bouncer/__pycache__/game.cpython-312.pyc +0 -0
- games/byte_bouncer/__pycache__/game.cpython-313.pyc +0 -0
- games/byte_bouncer/game.py +45 -21
- games/star_ship/__pycache__/game.cpython-312.pyc +0 -0
- games/star_ship/__pycache__/game.cpython-313.pyc +0 -0
- games/star_ship/game.py +64 -24
- games/terminal_tumble/__pycache__/game.cpython-312.pyc +0 -0
- games/terminal_tumble/__pycache__/game.cpython-313.pyc +0 -0
- games/terminal_tumble/game.py +41 -38
- cli_arcade-2026.1.3.dist-info/RECORD +0 -35
- {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/WHEEL +0 -0
- {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/entry_points.txt +0 -0
- {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/licenses/LICENSE +0 -0
- {cli_arcade-2026.1.3.dist-info → cli_arcade-2026.2.1.dist-info}/top_level.txt +0 -0
cli.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
from game_classes import ptk
|
|
2
|
+
from game_classes.tools import verify_terminal_size
|
|
2
3
|
import os
|
|
3
4
|
import importlib.util
|
|
4
5
|
import argparse
|
|
5
6
|
import glob
|
|
6
7
|
import sys
|
|
8
|
+
import re
|
|
9
|
+
import time
|
|
7
10
|
|
|
8
11
|
# helper: recognize Enter from multiple terminals/keypads
|
|
9
12
|
def is_enter_key(ch):
|
|
10
13
|
try:
|
|
11
|
-
enter_vals = {10, 13, getattr(
|
|
14
|
+
enter_vals = {10, 13, getattr(ptk, 'KEY_ENTER', -1), 343, 459}
|
|
12
15
|
except Exception:
|
|
13
16
|
enter_vals = {10, 13}
|
|
14
17
|
return ch in enter_vals
|
|
@@ -48,13 +51,38 @@ def _discover_games():
|
|
|
48
51
|
file_to_check = os.path.join(dirpath, pyfiles[0])
|
|
49
52
|
if not file_to_check:
|
|
50
53
|
continue
|
|
54
|
+
# try to read declared minimum terminal size from the game file
|
|
55
|
+
min_cols = None
|
|
56
|
+
min_rows = None
|
|
57
|
+
try:
|
|
58
|
+
with open(file_to_check, 'r', encoding='utf-8') as fh:
|
|
59
|
+
src = fh.read()
|
|
60
|
+
m = re.search(r'MIN_TERMINAL\s*=\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)', src)
|
|
61
|
+
if m:
|
|
62
|
+
min_cols = int(m.group(1))
|
|
63
|
+
min_rows = int(m.group(2))
|
|
64
|
+
else:
|
|
65
|
+
m1 = re.search(r'MIN_COLS\s*=\s*(\d+)', src)
|
|
66
|
+
m2 = re.search(r'MIN_ROWS\s*=\s*(\d+)', src)
|
|
67
|
+
if m1:
|
|
68
|
+
min_cols = int(m1.group(1))
|
|
69
|
+
if m2:
|
|
70
|
+
min_rows = int(m2.group(1))
|
|
71
|
+
except Exception:
|
|
72
|
+
min_cols = None
|
|
73
|
+
min_rows = None
|
|
51
74
|
# use directory name as the display name
|
|
52
75
|
name = entry.replace('_', ' ').title()
|
|
53
76
|
rel = os.path.relpath(file_to_check, base).replace('\\', '/')
|
|
54
77
|
games.append((name, rel))
|
|
78
|
+
try:
|
|
79
|
+
GAME_MINS[rel] = (min_cols, min_rows)
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
55
82
|
return games
|
|
56
83
|
|
|
57
84
|
|
|
85
|
+
GAME_MINS = {}
|
|
58
86
|
GAMES = _discover_games()
|
|
59
87
|
|
|
60
88
|
|
|
@@ -74,29 +102,29 @@ def _read_console_aliases():
|
|
|
74
102
|
|
|
75
103
|
|
|
76
104
|
def _menu(stdscr):
|
|
77
|
-
|
|
105
|
+
ptk.curs_set(0)
|
|
78
106
|
stdscr.nodelay(False)
|
|
79
107
|
# ensure a cyan color pair is available for the title
|
|
80
|
-
if
|
|
108
|
+
if ptk.has_colors():
|
|
81
109
|
try:
|
|
82
110
|
# init colors
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
ptk.start_color()
|
|
112
|
+
ptk.use_default_colors()
|
|
85
113
|
# try to normalize key colors (0..1000 scale). Must run before init_pair.
|
|
86
|
-
if
|
|
114
|
+
if ptk.can_change_color() and ptk.COLORS >= 8:
|
|
87
115
|
try:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
116
|
+
ptk.init_color(ptk.COLOR_MAGENTA, 1000, 0, 1000)
|
|
117
|
+
ptk.init_color(ptk.COLOR_YELLOW, 1000, 1000, 0)
|
|
118
|
+
ptk.init_color(ptk.COLOR_WHITE, 1000, 1000, 1000)
|
|
119
|
+
ptk.init_color(ptk.COLOR_CYAN, 0, 1000, 1000)
|
|
120
|
+
ptk.init_color(ptk.COLOR_BLUE, 0, 0, 1000)
|
|
121
|
+
ptk.init_color(ptk.COLOR_GREEN, 0, 800, 0)
|
|
122
|
+
ptk.init_color(ptk.COLOR_RED, 1000, 0, 0)
|
|
123
|
+
ptk.init_color(ptk.COLOR_BLACK, 0, 0, 0)
|
|
96
124
|
except Exception:
|
|
97
125
|
pass
|
|
98
126
|
for i in range(1,8):
|
|
99
|
-
|
|
127
|
+
ptk.init_pair(i, i, -1)
|
|
100
128
|
except Exception:
|
|
101
129
|
pass
|
|
102
130
|
sel = 0
|
|
@@ -106,13 +134,13 @@ def _menu(stdscr):
|
|
|
106
134
|
h, w = stdscr.getmaxyx()
|
|
107
135
|
title_h = len(TITLE)
|
|
108
136
|
title_start = 0
|
|
109
|
-
colors = [
|
|
137
|
+
colors = [ptk.COLOR_MAGENTA, ptk.COLOR_MAGENTA, ptk.COLOR_CYAN, ptk.COLOR_CYAN, ptk.COLOR_GREEN, ptk.COLOR_GREEN]
|
|
110
138
|
for i, line in enumerate(TITLE):
|
|
111
139
|
try:
|
|
112
|
-
stdscr.addstr(title_start + i, 0, line,
|
|
140
|
+
stdscr.addstr(title_start + i, 0, line, ptk.color_pair(colors[i]))
|
|
113
141
|
except Exception:
|
|
114
142
|
pass
|
|
115
|
-
stdscr.addstr(title_h + 1, 2, "Use Up/Down, PageUp/PageDown, Enter to start, ESC to quit",
|
|
143
|
+
stdscr.addstr(title_h + 1, 2, "Use Up/Down, PageUp/PageDown, Enter to start, ESC to quit", ptk.color_pair(ptk.COLOR_WHITE))
|
|
116
144
|
start_y = title_h + 3
|
|
117
145
|
# number of lines available for the game list
|
|
118
146
|
avail = max(1, h - start_y - 2)
|
|
@@ -126,9 +154,9 @@ def _menu(stdscr):
|
|
|
126
154
|
for vis_i in range(min(avail, total)):
|
|
127
155
|
i = top + vis_i
|
|
128
156
|
name = GAMES[i][0]
|
|
129
|
-
attr =
|
|
157
|
+
attr = ptk.A_REVERSE if i == sel else ptk.A_NORMAL
|
|
130
158
|
try:
|
|
131
|
-
stdscr.addstr(start_y + vis_i, 2, name[:w-4],
|
|
159
|
+
stdscr.addstr(start_y + vis_i, 2, name[:w-4], ptk.color_pair(ptk.COLOR_CYAN) | attr)
|
|
132
160
|
except Exception:
|
|
133
161
|
pass
|
|
134
162
|
# optional scrollbar indicator when list is long
|
|
@@ -155,15 +183,34 @@ def _menu(stdscr):
|
|
|
155
183
|
stdscr.refresh()
|
|
156
184
|
|
|
157
185
|
ch = stdscr.getch()
|
|
158
|
-
if ch ==
|
|
186
|
+
if ch == ptk.KEY_UP:
|
|
159
187
|
sel = max(0, sel - 1)
|
|
160
|
-
elif ch ==
|
|
188
|
+
elif ch == ptk.KEY_DOWN:
|
|
161
189
|
sel = min(len(GAMES) - 1, sel + 1)
|
|
162
|
-
elif ch ==
|
|
190
|
+
elif ch == ptk.KEY_PPAGE: # Page Up
|
|
163
191
|
sel = max(0, sel - avail)
|
|
164
|
-
elif ch ==
|
|
192
|
+
elif ch == ptk.KEY_NPAGE: # Page Down
|
|
165
193
|
sel = min(len(GAMES) - 1, sel + avail)
|
|
166
194
|
elif is_enter_key(ch):
|
|
195
|
+
# Before leaving the menu, validate the selected game's minimum
|
|
196
|
+
# terminal size (if declared). If it's too small, show an
|
|
197
|
+
# on-screen message and keep the menu running.
|
|
198
|
+
try:
|
|
199
|
+
rel = GAMES[sel][1]
|
|
200
|
+
mins = GAME_MINS.get(rel)
|
|
201
|
+
if mins:
|
|
202
|
+
min_cols, min_rows = mins
|
|
203
|
+
if min_cols and min_rows and (w < int(min_cols) or h < int(min_rows)):
|
|
204
|
+
try:
|
|
205
|
+
msg = f"Terminal too small: {w}x{h}, need {min_cols}x{min_rows}. Resize to start."
|
|
206
|
+
stdscr.addstr(max(0, h-1), 2, msg[:max(0, w-4)], ptk.color_pair(ptk.COLOR_RED))
|
|
207
|
+
stdscr.refresh()
|
|
208
|
+
time.sleep(1.5)
|
|
209
|
+
except Exception:
|
|
210
|
+
pass
|
|
211
|
+
continue
|
|
212
|
+
except Exception:
|
|
213
|
+
pass
|
|
167
214
|
return sel
|
|
168
215
|
elif ch == 27:
|
|
169
216
|
return None
|
|
@@ -174,7 +221,7 @@ def _menu(stdscr):
|
|
|
174
221
|
top = sel - avail + 1
|
|
175
222
|
|
|
176
223
|
|
|
177
|
-
def _run_game_by_index(choice):
|
|
224
|
+
def _run_game_by_index(choice, from_menu=False):
|
|
178
225
|
"""Load and run the game given by numeric index in GAMES."""
|
|
179
226
|
name, relpath = GAMES[choice]
|
|
180
227
|
base = os.path.dirname(__file__)
|
|
@@ -207,7 +254,29 @@ def _run_game_by_index(choice):
|
|
|
207
254
|
pass
|
|
208
255
|
if hasattr(mod, 'main'):
|
|
209
256
|
try:
|
|
210
|
-
|
|
257
|
+
# If the module exposes minimum terminal requirements, verify
|
|
258
|
+
# them before entering the alternate screen so messages are visible
|
|
259
|
+
try:
|
|
260
|
+
min_cols = getattr(mod, 'MIN_COLS', None)
|
|
261
|
+
min_rows = getattr(mod, 'MIN_ROWS', None)
|
|
262
|
+
if min_cols is None and hasattr(mod, 'MIN_TERMINAL'):
|
|
263
|
+
t = getattr(mod, 'MIN_TERMINAL')
|
|
264
|
+
if isinstance(t, (tuple, list)) and len(t) >= 2:
|
|
265
|
+
min_cols, min_rows = t[0], t[1]
|
|
266
|
+
if min_cols is not None and min_rows is not None:
|
|
267
|
+
verify_terminal_size(name, int(min_cols), int(min_rows))
|
|
268
|
+
except SystemExit:
|
|
269
|
+
return
|
|
270
|
+
except Exception:
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
ptk.wrapper(mod.main)
|
|
274
|
+
# If launched via `clia run`, exit the process after the game ends.
|
|
275
|
+
if not from_menu:
|
|
276
|
+
try:
|
|
277
|
+
sys.exit(0)
|
|
278
|
+
except SystemExit:
|
|
279
|
+
raise
|
|
211
280
|
except Exception as e:
|
|
212
281
|
print(f" [ERROR] Error running game {name}: {e}")
|
|
213
282
|
else:
|
|
@@ -389,7 +458,7 @@ def main():
|
|
|
389
458
|
return
|
|
390
459
|
# run the selected game (skip menu)
|
|
391
460
|
try:
|
|
392
|
-
_run_game_by_index(choice)
|
|
461
|
+
_run_game_by_index(choice, from_menu=False)
|
|
393
462
|
except Exception as e:
|
|
394
463
|
print(f" [ERROR] Error running game: {e}")
|
|
395
464
|
return
|
|
@@ -421,47 +490,21 @@ def main():
|
|
|
421
490
|
_reset_game_by_index(choice, yes=yes)
|
|
422
491
|
return
|
|
423
492
|
|
|
424
|
-
#
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
return
|
|
428
|
-
name, relpath = GAMES[choice]
|
|
429
|
-
base = os.path.dirname(__file__)
|
|
430
|
-
path = os.path.join(base, relpath)
|
|
431
|
-
if not os.path.exists(path):
|
|
432
|
-
print(f" [INFO] Game file not found: {path}")
|
|
433
|
-
return
|
|
434
|
-
# Ensure the game's directory is on sys.path so local imports (like `highscores`) resolve
|
|
435
|
-
game_dir = os.path.dirname(path)
|
|
436
|
-
spec = importlib.util.spec_from_file_location(f"cli_game_{choice}", path)
|
|
437
|
-
mod = importlib.util.module_from_spec(spec)
|
|
438
|
-
inserted = []
|
|
439
|
-
try:
|
|
440
|
-
proj_root = os.path.dirname(__file__)
|
|
441
|
-
if game_dir and game_dir not in sys.path:
|
|
442
|
-
sys.path.insert(0, game_dir)
|
|
443
|
-
inserted.append(game_dir)
|
|
444
|
-
if proj_root and proj_root not in sys.path:
|
|
445
|
-
sys.path.insert(0, proj_root)
|
|
446
|
-
inserted.append(proj_root)
|
|
447
|
-
spec.loader.exec_module(mod)
|
|
448
|
-
except Exception as e:
|
|
449
|
-
print(f" [INFO] Failed to load game {name}: {e}")
|
|
450
|
-
return
|
|
451
|
-
finally:
|
|
452
|
-
for p in inserted:
|
|
453
|
-
try:
|
|
454
|
-
sys.path.remove(p)
|
|
455
|
-
except Exception:
|
|
456
|
-
pass
|
|
457
|
-
# call the game's main function if present
|
|
458
|
-
if hasattr(mod, 'main'):
|
|
493
|
+
# Interactive menu loop: verify terminal size before showing menu each time,
|
|
494
|
+
# run the selected game, then return to the menu when the game exits.
|
|
495
|
+
while True:
|
|
459
496
|
try:
|
|
460
|
-
|
|
497
|
+
verify_terminal_size('CLI Arcade', 70, 20)
|
|
498
|
+
except SystemExit:
|
|
499
|
+
return
|
|
500
|
+
choice = ptk.wrapper(_menu)
|
|
501
|
+
if choice is None:
|
|
502
|
+
break
|
|
503
|
+
# Run the selected game (this returns when the game exits, e.g. ESC)
|
|
504
|
+
try:
|
|
505
|
+
_run_game_by_index(choice, from_menu=True)
|
|
461
506
|
except Exception as e:
|
|
462
|
-
print(f" [ERROR] Error running game
|
|
463
|
-
else:
|
|
464
|
-
print(f" [INFO] Game {name} has no main(stdscr) entry point.")
|
|
507
|
+
print(f" [ERROR] Error running game: {e}")
|
|
465
508
|
|
|
466
509
|
|
|
467
510
|
if __name__ == '__main__':
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cli-arcade
|
|
3
|
-
Version: 2026.1
|
|
3
|
+
Version: 2026.2.1
|
|
4
4
|
Summary: Collection of terminal CLI games
|
|
5
5
|
Home-page: https://github.com/Bro-Code-Technologies/cli-arcade/tree/main/windows
|
|
6
6
|
Author: Bro Code Technologies LLC
|
|
@@ -10,7 +10,7 @@ Project-URL: Source, https://github.com/Bro-Code-Technologies/cli-arcade/tree/ma
|
|
|
10
10
|
Project-URL: Issues, https://github.com/Bro-Code-Technologies/cli-arcade/tree/main/windows
|
|
11
11
|
Project-URL: Documentation, https://github.com/Bro-Code-Technologies/cli-arcade/tree/main/windows
|
|
12
12
|
Project-URL: Package, https://pypi.org/project/cli-arcade/
|
|
13
|
-
Keywords: cli,terminal,arcade,games
|
|
13
|
+
Keywords: cli,terminal,arcade,games
|
|
14
14
|
Classifier: Development Status :: 4 - Beta
|
|
15
15
|
Classifier: Environment :: Console
|
|
16
16
|
Classifier: Intended Audience :: End Users/Desktop
|
|
@@ -28,17 +28,17 @@ Requires-Python: >=3.8
|
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
License-File: LICENSE
|
|
30
30
|
Requires-Dist: appdirs
|
|
31
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: prompt_toolkit
|
|
32
32
|
Dynamic: license-file
|
|
33
33
|
|
|
34
34
|
# CLI Arcade
|
|
35
35
|
|
|
36
36
|
Collection of small terminal games bundled with a single CLI launcher.
|
|
37
37
|
|
|
38
|
-
Requirements
|
|
38
|
+
## Requirements
|
|
39
39
|
- Python 3.8+
|
|
40
40
|
|
|
41
|
-
Quick start
|
|
41
|
+
## Quick start
|
|
42
42
|
```powershell
|
|
43
43
|
# list installed console aliases and available games
|
|
44
44
|
clia list
|
|
@@ -56,15 +56,46 @@ clia reset "Byte Bouncer"
|
|
|
56
56
|
clia reset -y # skip confirmation
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Commands
|
|
60
|
-
- `clia` — interactive
|
|
59
|
+
## Commands
|
|
60
|
+
- `clia` — interactive terminal menu
|
|
61
61
|
- `clia list` — print available games and zero-based indices
|
|
62
62
|
- `clia run <index|name>` — run a game directly (index is zero-based)
|
|
63
63
|
- `clia reset [<index|name>] [-y]` — delete highscores for a game or all games
|
|
64
64
|
- Aliases available: `cli-arcade`
|
|
65
65
|
|
|
66
|
-
License
|
|
66
|
+
## License
|
|
67
67
|
- MIT
|
|
68
68
|
|
|
69
|
-
Changelog
|
|
70
|
-
|
|
69
|
+
# Changelog
|
|
70
|
+
|
|
71
|
+
All notable changes to this project will be documented in this file.
|
|
72
|
+
|
|
73
|
+
## 2026.0.0
|
|
74
|
+
- Initial release as CLI Game.
|
|
75
|
+
|
|
76
|
+
## 2026.1.0
|
|
77
|
+
- Project renamed to CLI Arcade.
|
|
78
|
+
- Packaging metadata updated for PyPI.
|
|
79
|
+
- Documentation refresh.
|
|
80
|
+
|
|
81
|
+
### 2026.1.1
|
|
82
|
+
- Updated TITLE ASCII art.
|
|
83
|
+
- Added `clia update` command to check for and install updates from PyPI.
|
|
84
|
+
- (YANKED)
|
|
85
|
+
|
|
86
|
+
### 2026.1.2 (YANKED)
|
|
87
|
+
- Version bump for testing the update mechanism.
|
|
88
|
+
|
|
89
|
+
### 2026.1.3
|
|
90
|
+
- Removed `clia update` command.
|
|
91
|
+
|
|
92
|
+
### 2026.2.0
|
|
93
|
+
- Refactoring using `prompt_toolkit` replacing `windows-curses` for better cross-platform compatibility.
|
|
94
|
+
- Restore/clear alternate screen on run/exit (no UI artifacts).
|
|
95
|
+
- Centralize terminal-size checks in CLI; games expose `MIN_COLS`/`MIN_ROWS`.
|
|
96
|
+
- Byte Bouncer & Star Ship: floor + off-screen right wall; left-column bug fixed.
|
|
97
|
+
- `ptk`: default-attribute/color emission fix; add helper to exit alt screen.
|
|
98
|
+
- Fixed 180 movement glitch in Star Ship.
|
|
99
|
+
|
|
100
|
+
### 2026.2.1
|
|
101
|
+
- Updated key mapping to work with Linux and macOS terminals.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
cli.py,sha256=sf70SoYU89X2b5CENYvqC7z8KntRadlV3IQHJXrgkw0,19567
|
|
2
|
+
cli_arcade-2026.2.1.dist-info/licenses/LICENSE,sha256=1PLSNFyGPi9COkVEeNYcUTuRLqGxzMLLTah05tByD4s,1099
|
|
3
|
+
game_classes/__init__.py,sha256=A0o4vBUktOiONToIJb0hACalszBnFS9ls9WXPn3-KH0,28
|
|
4
|
+
game_classes/game_base.py,sha256=8OaMHwPE6KKhwRCbUoI0Kypf2CN6RVxze_zdqaw7HhE,3499
|
|
5
|
+
game_classes/highscores.py,sha256=dDK-7lFXSMwIqExXGp6_-7QfQz6CIf12lJ22poeCdh4,4061
|
|
6
|
+
game_classes/menu.py,sha256=wx-dITSozG1VCfyFeFKgooRXEijQygDCTBRvoKnd7-8,2798
|
|
7
|
+
game_classes/ptk.py,sha256=v7vXIJVaJUc5JdDtUSyD6QLDJnybXY_ueDEatRqAFuI,12977
|
|
8
|
+
game_classes/tools.py,sha256=sZR4m8NSil4UhmZ2qDsl1iEi1hZMwUPVBFR5hSmiEBc,5106
|
|
9
|
+
game_classes/__pycache__/__init__.cpython-312.pyc,sha256=R0jJUxI2JIcXR4r34VwLgaPHHUCRZBwegLXmlZ2n2LM,197
|
|
10
|
+
game_classes/__pycache__/__init__.cpython-313.pyc,sha256=zSl9ESMUQn5Vo_XXAA8DD7lpFHOtOwBruGvCOJ5qeuo,192
|
|
11
|
+
game_classes/__pycache__/game_base.cpython-312.pyc,sha256=oeqYvfMbSDgwzZ3x2-lnuOVHSo2QWZQgbbYU9j3A8qY,6765
|
|
12
|
+
game_classes/__pycache__/game_base.cpython-313.pyc,sha256=NKAgCBXxM7nZ8mt6wFy1MxUW6DK7EBhqXiLr1sVmtiA,6977
|
|
13
|
+
game_classes/__pycache__/highscores.cpython-312.pyc,sha256=uaBo2z_TbERpOR1V-7oiVyt9iNu2Q1-WtMRuUb5FTY8,6942
|
|
14
|
+
game_classes/__pycache__/highscores.cpython-313.pyc,sha256=0rYMYs6NOzT-f00Me20F-46kUR_x8ZXk2jngO5amLy4,7080
|
|
15
|
+
game_classes/__pycache__/menu.cpython-312.pyc,sha256=fQ-AFhkA97IM3aauTx1J8huAXEcwELuc8QzO589o7ms,4424
|
|
16
|
+
game_classes/__pycache__/menu.cpython-313.pyc,sha256=urqSfGlJJdfTKI1kqXOT0oPXjo1Ny9MyYA8QroVQHzQ,4488
|
|
17
|
+
game_classes/__pycache__/ptk.cpython-312.pyc,sha256=h5ShTY7g8zjU1d3bJZkbcdtJGN6CVvB1IIDL7WNozOk,18602
|
|
18
|
+
game_classes/__pycache__/ptk.cpython-313.pyc,sha256=oSVDS5Cfugqn-cCsLjdoB7upFo6CgYSC5PwVaufS0hI,19240
|
|
19
|
+
game_classes/__pycache__/ptk_curses.cpython-313.pyc,sha256=XqhJ78tqyT0HFd5UV_1_IrGl-W5PuW4WyJve5RiY3uo,13820
|
|
20
|
+
game_classes/__pycache__/ptk_game_base.cpython-313.pyc,sha256=tRzGtoCErAHsUpoEWVjQN4PQ0Gw5N9RW3nJHJW2IT24,6915
|
|
21
|
+
game_classes/__pycache__/ptk_menu.cpython-313.pyc,sha256=jyBSuLVxNm7ueGjAIKhd7D_ZEFpwLv9U1iim_ElClHk,2448
|
|
22
|
+
game_classes/__pycache__/ptk_tools.cpython-313.pyc,sha256=Y-lBpAMlt8HfgQEio4isDGpwXclYUsyzs9wGoeGJkmE,5061
|
|
23
|
+
game_classes/__pycache__/tools.cpython-312.pyc,sha256=X_qDsJLFe5_Y29l4MlH_RuM46dOxvunIUTyBsvYj85o,7273
|
|
24
|
+
game_classes/__pycache__/tools.cpython-313.pyc,sha256=0MS9Cai_D0LaIQ6w6c6BLSJ45XvnUr4BjHSQdFYnQ7Y,7316
|
|
25
|
+
games/__init__.py,sha256=Rg46lnLj_sezpnXaQTS1NXX5OaRhR_EWpNTyYUxycj4,21
|
|
26
|
+
games/byte_bouncer/__init__.py,sha256=8hxIHRyUPB-X6vJXh1432ytKjOcfIzCbdpO5mmWA7iQ,33
|
|
27
|
+
games/byte_bouncer/game.py,sha256=EpTzObLNF_cGfB-xRkUlqCwYt7A7IGk8nENkOdqoV3c,8877
|
|
28
|
+
games/byte_bouncer/__pycache__/byte_bouncer.cpython-313.pyc,sha256=JSTB6-_KCukAAVaZz9TpJ3V4gif_M3n_L5X1KOviSJ8,24605
|
|
29
|
+
games/byte_bouncer/__pycache__/game.cpython-312.pyc,sha256=ITr1oNdZ7TQiDAw_suY1bFHQaiydXB_I7-TMMJMN5h8,13709
|
|
30
|
+
games/byte_bouncer/__pycache__/game.cpython-313.pyc,sha256=0H66QPFncbxvKVlMRPKEXjJDYtw1pPAw5I9sz3kWdIA,13965
|
|
31
|
+
games/byte_bouncer/__pycache__/highscores.cpython-313.pyc,sha256=g3FozKpWGDVsTVETNpb6lRHcKA3ZIsQio8Kh7KCGVCI,3125
|
|
32
|
+
games/star_ship/__init__.py,sha256=X8AdhoccmNVu6yl7MalsxIsMxuEsddRdlvEQmhZMD0A,30
|
|
33
|
+
games/star_ship/game.py,sha256=aN3LVh-kaYppAy-J259Y6XROT5pvEAFDjGd1404oWBw,10992
|
|
34
|
+
games/star_ship/__pycache__/game.cpython-312.pyc,sha256=lOScasiJ9VaNlCua45m_jejQPetUQGROhCxlV_aKi1I,15712
|
|
35
|
+
games/star_ship/__pycache__/game.cpython-313.pyc,sha256=hWqwV12a1VF-_Lc8Uzd7ono4ZN3gNJ4RQ-gK69OayVw,15922
|
|
36
|
+
games/star_ship/__pycache__/highscores.cpython-313.pyc,sha256=nHbEOTWeX0U9D47K2EP5S5uDPI0WW52Eg7h_NesG1OU,3148
|
|
37
|
+
games/star_ship/__pycache__/nibbles.cpython-313.pyc,sha256=Yv2P-GwEc30zA24aO7k0hkk1AOja6K85AFvmjLIffqQ,20510
|
|
38
|
+
games/star_ship/__pycache__/snek.cpython-313.pyc,sha256=3Mwyy6vjGFRmNyAWSqV5JRilLOxiCSndBmwua_Y7VTs,13313
|
|
39
|
+
games/star_ship/__pycache__/star_ship.cpython-313.pyc,sha256=9gJt10MTzVTSwsc4I16TDZTaAXclfhfGTzgfYgZNOKo,28021
|
|
40
|
+
games/terminal_tumble/__init__.py,sha256=axTd7hCgnLRNU8QS73NnQY3q5xjIb3ZYQORJ9Lz8l_E,36
|
|
41
|
+
games/terminal_tumble/game.py,sha256=UbtrMS4wi5MiIMHfreP51VlrHHlfxEPJpFmsLgdUgWs,15633
|
|
42
|
+
games/terminal_tumble/__pycache__/game.cpython-312.pyc,sha256=44n4iwM1rIjtMyeyITib1ewnGqOcI8p-npdpSErJQUs,23684
|
|
43
|
+
games/terminal_tumble/__pycache__/game.cpython-313.pyc,sha256=DrjmLzJYY5QoYXghGh-ORn26Yax9Gmd6fSCZGdgYbRQ,24073
|
|
44
|
+
games/terminal_tumble/__pycache__/highscores.cpython-313.pyc,sha256=UPnU9Zq88q2TZq1qXyo8e2Y_PdOgwvu5mncIkunFah8,3154
|
|
45
|
+
games/terminal_tumble/__pycache__/terminal_tumble.cpython-313.pyc,sha256=7oLpmmkYjpLYRC0bjRoEkHbIUNq7FZVJToGoja94m0o,38016
|
|
46
|
+
cli_arcade-2026.2.1.dist-info/METADATA,sha256=zD5GPdMYROphM5_bhvgOHKbA_kTOdBe65-l6UZWSQOg,3308
|
|
47
|
+
cli_arcade-2026.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
48
|
+
cli_arcade-2026.2.1.dist-info/entry_points.txt,sha256=nNElhZv4nM_lrOYpvnOChtJn1XsWwQr8sI344flzzTQ,56
|
|
49
|
+
cli_arcade-2026.2.1.dist-info/top_level.txt,sha256=gTfYlz7gHYgPY0ugfJivukCOkNzmOisNR6VuJjAXPF4,23
|
|
50
|
+
cli_arcade-2026.2.1.dist-info/RECORD,,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
game_classes/game_base.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
from game_classes import ptk
|
|
2
2
|
import time
|
|
3
3
|
from game_classes.tools import get_terminal_size
|
|
4
4
|
|
|
5
5
|
class GameBase:
|
|
6
|
-
def __init__(self, stdscr, player_name, tick, color=
|
|
6
|
+
def __init__(self, stdscr, player_name, tick, color=ptk.COLOR_GREEN):
|
|
7
7
|
self.stdscr = stdscr
|
|
8
8
|
self.player_name = player_name
|
|
9
9
|
self.tick = tick
|
|
@@ -65,7 +65,7 @@ class GameBase:
|
|
|
65
65
|
return True
|
|
66
66
|
if not getattr(self, 'over', False):
|
|
67
67
|
# toggle pause on Backspace
|
|
68
|
-
if ch in (
|
|
68
|
+
if ch in (ptk.KEY_BACKSPACE, 127, 8):
|
|
69
69
|
self.paused = not getattr(self, 'paused', False)
|
|
70
70
|
# movement only when not paused
|
|
71
71
|
elif not getattr(self, 'paused', False):
|
|
@@ -76,7 +76,7 @@ class GameBase:
|
|
|
76
76
|
try:
|
|
77
77
|
py = max(0, min(self.height, self.height // 2))
|
|
78
78
|
px = max(0, (self.width - len(msg)) // 2)
|
|
79
|
-
self.stdscr.addstr(py, px, msg,
|
|
79
|
+
self.stdscr.addstr(py, px, msg, ptk.color_pair(ptk.COLOR_RED) | ptk.A_BOLD)
|
|
80
80
|
except Exception:
|
|
81
81
|
pass
|
|
82
82
|
|
|
@@ -85,7 +85,7 @@ class GameBase:
|
|
|
85
85
|
try:
|
|
86
86
|
for i, line in enumerate(self.title):
|
|
87
87
|
try:
|
|
88
|
-
self.stdscr.addstr(i, 0, line,
|
|
88
|
+
self.stdscr.addstr(i, 0, line, ptk.color_pair(self.color) | ptk.A_BOLD)
|
|
89
89
|
except Exception:
|
|
90
90
|
pass
|
|
91
91
|
except Exception:
|
game_classes/menu.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from game_classes import ptk
|
|
2
2
|
from game_classes.tools import is_enter_key
|
|
3
3
|
|
|
4
4
|
class Menu:
|
|
@@ -19,7 +19,7 @@ class Menu:
|
|
|
19
19
|
return name.strip() or 'Player'
|
|
20
20
|
elif ch in (27,):
|
|
21
21
|
return False
|
|
22
|
-
elif ch in (
|
|
22
|
+
elif ch in (ptk.KEY_BACKSPACE, 127, 8):
|
|
23
23
|
name = name[:-1]
|
|
24
24
|
elif 32 <= ch <= 126 and len(name) < max_len:
|
|
25
25
|
name += chr(ch)
|
|
@@ -34,7 +34,7 @@ class Menu:
|
|
|
34
34
|
title_height = len(self.game.title)
|
|
35
35
|
for i, line in enumerate(self.game.title):
|
|
36
36
|
try:
|
|
37
|
-
self.game.stdscr.addstr(i, 0, line,
|
|
37
|
+
self.game.stdscr.addstr(i, 0, line, ptk.color_pair(self.game.color) | ptk.A_BOLD)
|
|
38
38
|
except Exception:
|
|
39
39
|
pass
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ class Menu:
|
|
|
51
51
|
self.game.stdscr.refresh()
|
|
52
52
|
while True:
|
|
53
53
|
ch = self.game.stdscr.getch()
|
|
54
|
-
# if ch in (ord('s'), ord('S')) or ch ==
|
|
54
|
+
# if ch in (ord('s'), ord('S')) or ch == ptk.KEY_DOWN:
|
|
55
55
|
if is_enter_key(ch):
|
|
56
56
|
# prompt for name before starting, centered over the title block
|
|
57
57
|
prompt_y = title_height + 3
|