Skip to content

Instantly share code, notes, and snippets.

@sourencho
Last active August 14, 2019 19:10
Show Gist options
  • Save sourencho/0018da7b4fb47d455c621f7173fc50f6 to your computer and use it in GitHub Desktop.
Save sourencho/0018da7b4fb47d455c621f7173fc50f6 to your computer and use it in GitHub Desktop.
Space Invader game in terminal written for RC
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