generated from mwc/project_game
143 lines
3.1 KiB
Python
143 lines
3.1 KiB
Python
import inspect
|
|
import os
|
|
import random
|
|
import sys
|
|
import retro
|
|
import tetris
|
|
from retro.agent import ArrowKeyAgent
|
|
|
|
|
|
|
|
# Expose Game name expected by create_game helper
|
|
game = tetris.Tetris()
|
|
|
|
|
|
# --- Factory helpers and small harness ---
|
|
|
|
def create_game():
|
|
"""Create a Game instance with minimal sensible defaults."""
|
|
try:
|
|
return Game()
|
|
except TypeError:
|
|
try:
|
|
sig = inspect.signature(Game)
|
|
kwargs = {}
|
|
if 'agents' in sig.parameters:
|
|
kwargs['agents'] = []
|
|
if 'state' in sig.parameters:
|
|
kwargs['state'] = {}
|
|
return Game(**kwargs)
|
|
except Exception:
|
|
for args in (([], {}), ([], None), ([],), (None,)):
|
|
try:
|
|
return Game(*args)
|
|
except Exception:
|
|
continue
|
|
raise
|
|
|
|
|
|
def create_agent(game=None):
|
|
try:
|
|
return ArrowKeyAgent(game)
|
|
except TypeError:
|
|
return ArrowKeyAgent()
|
|
|
|
|
|
def draw_screen(game: Game):
|
|
# simple text render
|
|
game.clear()
|
|
s = game.draw_board()
|
|
game.refresh()
|
|
# store/return the string for tests
|
|
return s
|
|
|
|
|
|
def main():
|
|
argv = sys.argv[1:]
|
|
if len(argv) >= 1 and argv[0] == 'play':
|
|
# Try to run a terminal player. Use curses if available.
|
|
try:
|
|
play_in_terminal(create_game())
|
|
except Exception as e:
|
|
print('Terminal play failed:', e)
|
|
print('Run without args to run smoke tests instead.')
|
|
return
|
|
|
|
game = create_game()
|
|
agent = create_agent(game)
|
|
|
|
while not game.is_over():
|
|
action = agent.get_action()
|
|
game.step(action)
|
|
draw_screen(game)
|
|
|
|
print('Game Over! Score:', game.get_score())
|
|
|
|
def play_in_terminal(game):
|
|
"""Simple curses-based player loop.
|
|
|
|
Controls:
|
|
- Left/Right arrows or 'a'/'d' to move
|
|
- Up arrow or 'w' to rotate
|
|
- Down arrow or 's' to soft drop
|
|
- Space for hard drop
|
|
- 'q' to quit
|
|
"""
|
|
|
|
keymap = {
|
|
# map keys to the Testris.action integer codes:
|
|
# 0 noop, 1 left, 2 right, 3 rotate, 4 soft drop
|
|
curses.KEY_LEFT: 1,
|
|
curses.KEY_RIGHT: 2,
|
|
curses.KEY_UP: 3,
|
|
curses.KEY_DOWN: 4,
|
|
ord('a'): 1,
|
|
ord('d'): 2,
|
|
ord('w'): 3,
|
|
ord('s'): 4,
|
|
# space will perform a hard drop (special handling)
|
|
ord(' '): 'hard_drop',
|
|
ord('q'): 'quit',
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- Simple smoke tests ---
|
|
|
|
def test_game_initialization():
|
|
g = create_game()
|
|
assert g is not None
|
|
assert g.get_score() == 0
|
|
assert not g.is_over()
|
|
|
|
|
|
def test_game_step():
|
|
g = create_game()
|
|
score0 = g.get_score()
|
|
g.step(0)
|
|
assert g.get_score() >= score0
|
|
|
|
|
|
def test_draw_board_returns_str():
|
|
g = create_game()
|
|
s = g.draw_board()
|
|
assert isinstance(s, str)
|
|
|
|
|
|
def test_game_over_sequence():
|
|
g = create_game()
|
|
# run a few steps to ensure no immediate crash
|
|
for _ in range(50):
|
|
if g.is_over():
|
|
break
|
|
g.step(0)
|
|
assert g.get_score() >= 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|