#!/usr/bin/env python3 import sys import sqlite3 as sql import os from os.path import isfile as fileexists import subprocess import json from gomoku import Gomoku from isola import Isola from brandubh import Brandubh version = '0.8.1' home_folder = os.path.expanduser('~') username = os.path.split(home_folder)[-1] database = '/var/lib/oberon/oberon.sqlite' basefile = '/var/lib/oberon/' def print_header(): print('\nO B E R O N v{}\n'.format(version)) ############################## ######------------------####### ###### Top Lvl Cmds ######## ######------------------####### ############################## # Binds to the HISTORY command def show_game_record(): q = "SELECT rowid, p1, p2, winner, game_type FROM games WHERE winner is not NULL and (p1 = ? or p2 = ?)" v = (username, username) res = db_do(q, v) if res == False: print('Database error. Contact the sysadmin or try another time') print_header() print('{:^8} {:^10} {:^10} {:^10} {:^12}'.format('ID', 'Player 1', 'Player 2', 'Winner', 'Game Type')) print('-------- ---------- ---------- ---------- ------------') for x in res: print('{:^8} {:^10} {:^10} {:^10} {:^12}'.format(x[0], x[1], x[2], x[3], x[4])) if not len(res): print('You have not completed any games yet...') print('\nTo view a historical game, run "oberon play [game_id]"') return True # Binds to the PLAY command def play_game(gid): q = "SELECT rowid, p1, p2, game_board, turn, game_type, winner FROM games WHERE rowid = ?" v = (gid,) res = db_do(q,v) if not res: print('Invalid game id') return False elif res[0][1] != username and res[0][2] != username: print('Invalid selection. You are not playing game {}'.format(gid)) return False state = { 'id': res[0][0], 'p1': res[0][1], 'p2': res[0][2], 'board': json.loads(res[0][3]), 'turn_number': res[0][4], 'game_type': res[0][5], 'winner': res[0][6], 'current_ps_turn': res[0][2] if res[0][4] % 2 == 0 else res[0][1], 'piece_index': 1 if res[0][4] % 2 == 0 else 0 } if state['game_type'] == 'gomoku': game = Gomoku(state) elif state['game_type'] == 'isola': game = Isola(state) elif state['game_type'] == 'brandubh': game = Brandubh(state) print_header() game.print_challenge_text() game.print_board() if not state['current_ps_turn'] == username: print('\nIt is your opponents turn.\n') return True if game.winner: print('You are viewing a game that has already finished') return True play = game.get_input() if not play: sys.exit(0) else: q2 = "UPDATE games SET game_board = ?, turn = ?, winner = ? WHERE rowid = ?" v2 = (json.dumps(play['board']), state['turn_number'] + 1, play['winner'], gid) res2 = db_do(q2, v2, True) if play['winner']: enemy = state['p1'] if state['p1'] != username else state['p2'] msg_path = basefile + '{}'.format(enemy) writemode = 'a' if fileexists(msg_path) else 'w' with open(msg_path, writemode) as f: f.write('{} defeated you in {} game {}\n'.format(username, state['game_type'], gid)) if not res2: print('Error updating database, sorry.\nTry again later?\n...or contact the sysadmin.') return False print(play['message']) # Binds to the LIST command def list_games(): q = "SELECT rowid, p1, p2, game_type, game_status, turn FROM games WHERE winner is NULL and (p1 = ? or p2 = ?)" v = (username, username) res = db_do(q, v) if res == False: print('Database error. Contact the sysadmin or try another time.') return False print_header() print('{:^8} {:^10} {:^10} {:^22} {:^12}'.format('ID', 'Player 1', 'Player 2', 'Game Status', 'Game Type')) print('-------- ---------- ---------- ---------------------- ------------') for x in res: pturn = x[2] if x[5] % 2 == 0 else x[1] print("{:^8} {:^10} {:^10} {:^22} {:^12}".format(x[0], x[1], x[2], pturn +"'s turn", x[3])) if not len(res): print('You are not currently playing any games...') print('') return True # Binds to the HELP command def display_help(): print_header() print('syntax: oberon [command [option]]\n') print('{:20} {}'.format('help','display this message')) print('{:20} {}'.format('list', 'display your currently open games')) print('{:20} {}'.format('history', 'display your win/loss record')) print('{:20} {}'.format('create [user]', 'create a game against the user provided')) print('{:20} {}'.format('play [game_id]', 'make a move or view the board for the game id provided')) print('') print('if you do not want to play games simply add a file named ".killoberon" to your home directory and users will not be able to create games with you') print('') # Binds to the CREATE command def create_game(enemy): if enemy == username: print('This system is designed for multiuser games. You cannot create a game against yourself.') return False enemies = get_user_list() if not enemy in enemies: print('There is no avialable player with the name {}.\nGame not created.'.format(enemy)) return False if fileexists('/home/{}/.killoberon'.format(enemy)): print('{} does not wish to play games in oberon. Please try creating a game with a different user.'.format(enemy)) return False print_header() print('Which game would you like to play?\n\n(1) Gomoku/Five In a Row\n(2) Isola\n(3) Brandubh\n\nor (Q)uit') while True: game = input('> ') if game.lower() == 'q': sys.exit(0) elif not game in ['1', '2', '3']: continue break if game == '1': game_board = [[' ยท ' for y in range(15)] for x in range(15)] game_choice = 'gomoku' elif game == '2': board = [[ ' - ' for y in range(7)] for x in range(7)] board[0][3] = ' X ' board[6][3] = ' O ' game_board = {'board': board, 'p1': [0,3], 'p2': [6,3]} game_choice = 'isola' elif game == '3': game_board = [[ ' - ' for y in range(7)] for x in range(7)] game_board[0][0] = ' * ' game_board[0][6] = ' * ' game_board[6][0] = ' * ' game_board[6][6] = ' * ' for x in [0,1,5,6]: game_board[3][x] = ' a ' game_board[x][3] = ' a ' for x in [2,4]: game_board[3][x] = ' d ' game_board[x][3] = ' d ' game_board[3][3] = ' D ' game_choice = 'brandubh' player = '0' print('') while not player in ['1','2']: print('Play as player 1 or player 2 (enter the number)') player = input('> ') if player == '2': temp = enemy enemy = username player = temp else: player = username q = "INSERT INTO games VALUES (?, ?, ?, ?, ?, ?, ?)" v = (player, enemy, game_choice, 'playing', json.dumps(game_board), 1, None) res = db_do(q, v, True) if res: print('Game between {} and {} created!'.format(username, enemy)) return True return False # Binds to the AVAILABLE command def available_moves(): q = "SELECT rowid, p1, p2, turn FROM games WHERE winner is null and (p1 = ? or p2 = ?)" v = (username, username) res = db_do(q, v) p1 = [x[0] for x in res if x[1] == username and x[3] % 2 != 0] p2 = [x[0] for x in res if x[2] == username and x[3] % 2 == 0] print('- - - -') if len(p1) + len(p2) > 0: p1 = ', '.join(str(x) for x in p1) p2 = ', '.join(str(x) for x in p2) spacer = ', ' if p1 and p2 else '' print('\033[1;32mIt is your turn in the following game(s): {}{}{}\033[0m'.format(p1, spacer, p2)) else: print('It is not your turn in any games...\n') ############################## ######------------------####### ###### Sys Utilities ######## ######------------------####### ############################## def parse_args(): args = sys.argv arg_len = len(args) if arg_len == 1: display_help() available_moves() sys.exit(0) if arg_len == 2: if args[1] == 'list': l = list_games() if l: sys.exit(0) sys.exit(1) elif args[1] == 'history': h = show_game_record() if h: sys.exit(0) sys.exit(1) elif args[1] == 'available': available_moves() sys.exit(0) elif args[1] == 'create': print('Must include an opponent name.\nFind one in chat or cspc!') sys.exit(1) elif args[1] == 'play': print('Must include a game id.\nRun "oberon list" to view your open games.') sys.exit(1) elif args[1] == 'help': display_help() sys.exit(0) else: print('Invalid arguments. Run "oberon help".') sys.exit(1) elif arg_len == 3: if args[1] == 'create': if create_game(args[2]): sys.exit(0) sys.exit(1) elif args[1] == 'play': try: game_id = int(args[2]) except: print('Invalid game id.') sys.exit(1) play_game(game_id) sys.exit(0) else: print('Invalid arguments. Run "oberon help".') sys.exit(1) else: print('Unknown paramaters: {}'.format(' '.join(args[3:]))) sys.exit(1) def get_user_list(): res = subprocess.run(['awk', '-F', ':', '{if ($7 == "/usr/local/bin/colorsh") print $1}', '/etc/passwd'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) res_list = res.stdout.decode('utf-8').split('\n') if res_list[0] == '': return [] return res_list ############################## ######------------------####### ###### DB Utilities ######## ######------------------####### ############################## def check_and_build_db(db_path): if not os.path.isfile(db_path): conn = sql.connect(db_path) c = conn.cursor() c.execute("CREATE TABLE games (p1 text NOT NULL, p2 text DEFAULT NULL, game_type text NOT NULL, game_status text DEFAULT NULL, game_board text, turn INTEGER, winner text DEFAULT NULL)") conn.commit() conn.close() def db_do(query, var=False, noresval=False): global messages db_path = database if os.path.isfile(db_path): conn = sql.connect(db_path) c = conn.cursor() if var: c.execute(query, var) else: c.execute(query) if noresval: out = c.rowcount else: out = [] for row in c: out.append(row) conn.commit() conn.close() return out else: return False if __name__ == '__main__': check_and_build_db(database) parse_args()