Last active
August 14, 2019 19:10
-
-
Save sourencho/0018da7b4fb47d455c621f7173fc50f6 to your computer and use it in GitHub Desktop.
Space Invader game in terminal written for RC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import threading | |
import random | |
import curses | |
import queue | |
import time | |
import sys | |
KEY_ESCAPE = 27 | |
GAME_SPEED = 20 | |
ALIEN_STALL = 4 | |
GAME_WIDTH = 20 | |
GAME_HEIGHT = 10 | |
BACKGROUND_CHAR = ' ' | |
ALIEN_CHAR = '*' | |
CANON_CHAR = '^' | |
# Init global thread safe user input var | |
user_input = None | |
user_input_lock = threading.Lock() | |
def collect_user_input(stdscr): | |
""" Infinite thread which collects user input """ | |
global user_input | |
while True: | |
key = stdscr.getch() | |
with user_input_lock: | |
user_input=key; | |
class Grid: | |
def __init__(self, width, height): | |
self.grid = [[BACKGROUND_CHAR for _ in range(width)] for _ in range(height)] | |
self.width = width | |
self.height = height | |
def draw(self, win): | |
for w in range(self.width): | |
for h in range(self.height): | |
win.addch(h+1, w+1, self.grid[h][w]) | |
def set_all(self, v): | |
""" Set all grid cells values to <v> """ | |
for w in range(self.width): | |
for h in range(self.height): | |
self.grid[h][w] = v | |
def set(self, x, y, v): | |
""" Set grid cell to <v> """ | |
self.grid[y][x] = v | |
@property | |
def top_right(self): | |
return (self.width-1, 0) | |
@property | |
def bottom_right(self): | |
return (self.width-1, self.height-1) | |
@property | |
def top_left(self): | |
return (0, 0) | |
@property | |
def bottom_left(self): | |
return (0, self.height-1) | |
class Character(): | |
def __init__(self, v, x, y, max_x, min_x): | |
self.x = x | |
self.y = y | |
self.v = v | |
self.max_x = max_x | |
self.min_x = min_x | |
self.hor_dir_right = True | |
def move_right(self): | |
self.x = min(self.x + 1, self.max_x) | |
def move_left(self): | |
self.x = max(self.x - 1, self.min_x) | |
def move_horizontal(self): | |
""" Move in a horizontal line """ | |
if self.hor_dir_right: | |
self.move_right() | |
else: | |
self.move_left() | |
if self.x == self.max_x: | |
self.hor_dir_right = False | |
if self.x == self.min_x: | |
self.hor_dir_right = True | |
class Game: | |
def __init__(self, stdscr): | |
# Curses setup | |
curses.noecho() | |
curses.cbreak() | |
curses.curs_set(0) | |
# Init window | |
win = curses.newwin(GAME_HEIGHT+2, GAME_WIDTH+2) | |
win.clear() | |
win.border() | |
# Init user input | |
collect_user_input_thread = threading.Thread( | |
target=collect_user_input, | |
args=(stdscr,), | |
daemon=True, | |
) | |
collect_user_input_thread.start() | |
# Init vars | |
self.win = win | |
win.keypad(True) | |
win.nodelay(1) | |
self.grid = Grid(GAME_WIDTH, GAME_HEIGHT) | |
self.tick = 0 | |
self.current_user_input = None | |
# Init characters | |
self.characters = [] | |
self.aliens = [] | |
self.canon = Character( | |
v=CANON_CHAR, | |
x=self.grid.bottom_left[0], | |
y=self.grid.bottom_right[1], | |
max_x=self.grid.bottom_right[0], | |
min_x=self.grid.bottom_left[0], | |
) | |
alien_1 = Character( | |
v=ALIEN_CHAR, | |
x=self.grid.top_left[0], | |
y=self.grid.top_left[1], | |
max_x=self.grid.top_right[0], | |
min_x=self.grid.top_left[0], | |
) | |
alien_2 = Character( | |
v=ALIEN_CHAR, | |
x=self.grid.top_right[0], | |
y=self.grid.top_right[1]+1, | |
max_x=self.grid.top_right[0], | |
min_x=self.grid.top_left[0], | |
) | |
self.aliens += [alien_1, alien_2] | |
self.characters += self.aliens | |
self.characters += [self.canon] | |
def get_user_input(self): | |
global user_input | |
with user_input_lock: | |
self.current_user_input = user_input | |
user_input = None | |
def update_game_state(self): | |
# Handle input | |
if self.current_user_input == KEY_ESCAPE: | |
self.exit() | |
elif self.current_user_input == curses.KEY_LEFT: | |
self.canon.move_left() | |
elif self.current_user_input == curses.KEY_RIGHT: | |
self.canon.move_right() | |
# Move aliens | |
if self.tick % ALIEN_STALL == 0: | |
for alien in self.aliens: | |
alien.move_horizontal() | |
# Set Background | |
self.grid.set_all(BACKGROUND_CHAR) | |
# Draw Characters | |
for character in self.characters: | |
self.grid.set(character.x, character.y, character.v) | |
self.tick += 1 | |
def draw(self): | |
self.win.clear() | |
self.win.border() | |
self.grid.draw(self.win) | |
self.win.refresh() | |
def step(self): | |
self.get_user_input() | |
self.update_game_state() | |
self.draw() | |
def exit(self): | |
curses.echo() | |
curses.nocbreak() | |
self.win.keypad(0) | |
curses.endwin() | |
sys.exit() | |
def main(stdscr): | |
game = Game(stdscr) | |
while True: | |
game.step() | |
time.sleep(1/GAME_SPEED) | |
if __name__ == "__main__": | |
curses.wrapper(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment