diff --git a/.idea/generai.iml b/.idea/generai.iml new file mode 100644 index 0000000..6711606 --- /dev/null +++ b/.idea/generai.iml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..acc5f7d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..158a43c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..a3167f2 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,743 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + list + TIME_AVAILABLE + operator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1496945152529 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file://$PROJECT_DIR$/../../Info 2/sandbox.py + 62 + + + file://$PROJECT_DIR$/../../Info 2/sandbox.py + 67 + + + file://$PROJECT_DIR$/game.py + 102 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/game.py b/game.py index 0a2b99b..c2b131d 100644 --- a/game.py +++ b/game.py @@ -1,12 +1,38 @@ -# -import sys import traceback from helper import print_scoresheets - from generala import get_random_dice, valid_play, play_value +"""Library necessary to copy the list "scoresheets" to pass by value to the plugin""" +from copy import deepcopy +"""Libraries necessary for timing the plugin's response""" +import signal +"""Library for displaying the winner""" +import operator +"""Libraries for sandboxing""" +from sandboxing import getPlay#for the sandboxing +TIME_AVAILABLE = 1#time available to compute the plugin's response + +class Timeout(): + """Timeout class using ALARM signal.""" + + class Timeout(Exception): + pass + + def __init__(self, sec): + self.sec = sec + + def __enter__(self): + signal.signal(signal.SIGALRM, self.raise_timeout) + signal.alarm(self.sec) + + def __exit__(self, *args): + signal.alarm(0) # disable alarm + + def raise_timeout(self, *args): + raise Timeout.Timeout() -class Player():#segundo comentario de prueba + +class Player():# def __init__(self, name): self.name = name @@ -31,18 +57,17 @@ class Game(): def __init__(self, nscoresheets): self.nscoresheets = nscoresheets self.nplayers = 0 - self.players_plugins = {} - self.players = [] + self.players_plugins = {} #the name of the file of the plugins + self.players = [] #the name() of the plugins self.scoresheets = [] for i in range(nscoresheets): self.scoresheets.append({}) - def add_player(self, player): - name = player.name() - self.players_plugins[name] = player - self.players.append(name) + def add_player(self, namePlayer,plugin): + self.players_plugins[namePlayer] = plugin + self.players.append(namePlayer) for i in range(self.nscoresheets): - self.scoresheets[i][name] = {} + self.scoresheets[i][namePlayer] = {} self.nplayers = self.nplayers + 1 def start(self): @@ -61,38 +86,65 @@ def start(self): def results(self): print_scoresheets(self.scoresheets) + """calculating the winner""" + total = {} + for player in self.players: + total[player] = 0 + for scoresheet in self.scoresheets: + for player in scoresheet: + for play, score in scoresheet[player].items(): + total[player] += score + print "The winner is!:",max(total.iteritems(),key=operator.itemgetter(1))[0] def turn(self, player): - plugin = self.players_plugins[player] - r = get_random_dice(5) + plugin = self.players_plugins[player]#name of the module + print "turn", plugin + r = get_random_dice(5)#obtain the dices as a list eg: [2,1,4,4,6] nroll = 5 - for i in range(3): + for i in range(3):#ith opportunity of the current player try: - bonus = (nroll == 5) - roll, decision, scoresheet = plugin.play(i, r, bonus, self.players, self.scoresheets) - decision = decision.upper() if decision else None - nroll = len(roll) - if nroll > 0: - r0 = get_random_dice(nroll) - i = 0 - for new_r in roll: - r[new_r] = r0[i] - i = i+1 - else: - if not valid_play(decision): - raise Exception('Play [{0}] is invalid'.format(decision)) - scoresheet = self.scoresheets[scoresheet][player] - if decision not in scoresheet: - scoresheet[decision] = play_value(decision, r, bonus) - else: - raise Exception('Decision [{0}] is already taken'.format(decision)) - break - - + """It executes the corresponding turn for the plugin with TIME_AVAILABLE seconds to decide""" + with Timeout(TIME_AVAILABLE): + bonus = (nroll == 5) + """ + The return parameters of the play function of the plugin should be: + roll: which dices should be re-rolled referencing the indexes of the r list + decision: the final decision of the turn as a string. The avalaible decisions are stated in helper.py + scorsheet: which scoresheet should the play be applied + """ + copyScoresheets = deepcopy(self.scoresheets)#make a copy of the scoresheets because we want to send them by value not by reference for safety + """getPlay from the sandboxing.py with the parameters + plugin: name of the file's plugin eg: serial_dicer.py + i: ith opportunity to throw again the dice + r: the dices itself + bonus: if that throw contains a bonus + player: the current players playing the game + Scoresheets: the poins currently assigned + """ + roll, decision, scoresheet = getPlay(plugin,i, r, bonus, self.players, copyScoresheets) + #roll, decision, scoresheet = plugin.play(i, r, bonus, self.players, copyScoresheets)<--reemplazado + decision = decision.upper() if decision else None + nroll = len(roll)#how many dices should be re-roll + if nroll > 0:#the player has decided re-roll again at least one dice + r0 = get_random_dice(nroll)#it obtains "nroll" new dices + i = 0 + for new_r in roll:#it updates the new dices list + r[new_r] = r0[i] + i = i+1 + else:#the player has decided that no dice shoul be re-rolled + if not valid_play(decision):#the player has not stated a valid play in the format stated in generala.py + raise Exception('Play [{0}] is invalid'.format(decision)) + scoresheet = self.scoresheets[scoresheet][player]#retrieve the plays that player has already played in that scoresheet + if decision not in scoresheet: + #The player adds a new play in that scoresheet + scoresheet[decision] = play_value(decision, r, bonus) + else: + raise Exception('Decision [{0}] is already taken'.format(decision)) + break + except Timeout.Timeout:#Times up for the ith opportunity + print "Plugin Timeout",plugin + break except Exception as e: print(traceback.format_exc()) print("Error inesperadamente inesperado: {0}".format(e)) # NOTIFY - - - diff --git a/game.pyc b/game.pyc new file mode 100644 index 0000000..9a7dc9b Binary files /dev/null and b/game.pyc differ diff --git a/generai.py b/generai.py index fe066ce..2fdcf90 100644 --- a/generai.py +++ b/generai.py @@ -1,13 +1,13 @@ +# coding=utf-8 from os import listdir from os.path import isfile, join from importlib import import_module from game import Game - +from sandboxing import getName # plugins plugins = {} -plugins_list = [] - +plugins_list=[] def load_plugins(): print("Loading plugins...") plugin_files = [f for f in listdir('plugins') if isfile(join('plugins', @@ -18,11 +18,13 @@ def load_plugins(): plugin_name = plugin_file[:-3] print("Loading plugin {0}".format(plugin_name)) try: - m = import_module('plugins.{0}'.format(plugin_name)) - plugins_loaded = plugins_loaded + 1 - name = m.name() - plugins[name] = m - plugins_list.append(m) + name = getName(plugin_file) + if(name is not None): + plugins_loaded = plugins_loaded + 1 + plugins[name] = plugin_file + plugins_list.append(name) + else: + raise Exception(name) except Exception as e: print("Plugin {0} was not loaded {1}".format(plugin_name, e)) invalid_plugins = invalid_plugins + 1 @@ -33,7 +35,7 @@ def list_plugins(title=True): print("---------------- PLUGINS -----------------") i = 0 for plugin in plugins_list: - print("{0}) {1}".format(i, plugin.name())) + print("{0}) {1}".format(i, plugin)) i = i + 1 print("\n") @@ -41,12 +43,12 @@ def start_game(): print("---------------- GENERALA -----------------") list_plugins(False) - players_input = input("Ingrese los jugadores (separándolos con ,): ") + players_input = raw_input("Ingrese los jugadores (separándolos con ,): ") players = [int(x.strip()) for x in players_input.split(",")] - nscoresheets = int(input("Cuántas casillas? ")) + nscoresheets = int(raw_input("Cuántas casillas? ")) game = Game(nscoresheets) for player in players: - game.add_player(plugins_list[player]) + game.add_player(plugins_list[player],plugins[plugins_list[player]]) game.start() game.results() @@ -78,7 +80,7 @@ def menu(): def process_option(opt): for option in options: - if (isinstance(option["val"], list) and opt in option["val"]) or (opt == option["val"]): + if (isinstance(option["val"], list) and opt in option["val"]) or (str(opt) == option["val"]): option["fn"]() if __name__ == "__main__": diff --git a/generala.pyc b/generala.pyc new file mode 100644 index 0000000..cf0f020 Binary files /dev/null and b/generala.pyc differ diff --git a/helper.pyc b/helper.pyc new file mode 100644 index 0000000..5b55e32 Binary files /dev/null and b/helper.pyc differ diff --git a/plugins/__init__.pyc b/plugins/__init__.pyc new file mode 100644 index 0000000..adcc47c Binary files /dev/null and b/plugins/__init__.pyc differ diff --git a/plugins/console.py b/plugins/console.py index f874508..17a102d 100644 --- a/plugins/console.py +++ b/plugins/console.py @@ -8,7 +8,7 @@ def name(): # this plugin uses console I/O to make plays def roll_dice_eval(roll_dice): - return [int(x) - 1 for x in roll_dice if x in string.digits] + return [int(x) - 1 for x in list(roll_dice)] def play(roll_no, dice, bonus, players, scoresheets): print_scoresheets(scoresheets) @@ -18,6 +18,6 @@ def play(roll_no, dice, bonus, players, scoresheets): if roll_dice: return roll_dice_eval(roll_dice), None, None else: - decision = input("jugada? ") - scoresheet = int(input("casilla {0}? ".format(str(list(range(len(scoresheets))))))) + decision = raw_input("jugada? ") + scoresheet = int(raw_input("casilla {0}? ".format(str(list(range(len(scoresheets))))))) return ([], decision, scoresheet) diff --git a/plugins/console.pyc b/plugins/console.pyc new file mode 100644 index 0000000..29905a4 Binary files /dev/null and b/plugins/console.pyc differ diff --git a/plugins/random_dicer.py b/plugins/random_dicer.py index d34f3a3..c43335b 100644 --- a/plugins/random_dicer.py +++ b/plugins/random_dicer.py @@ -1,7 +1,7 @@ def name(): return "random_roshkero 1.0" -def play(dice, servido, players, scoresheets): +def play(turno, dice, servido, players, scoresheets): print("MY TURN! -> {0} [{1}]".format(1, dice)) return (1, 2, 3) diff --git a/plugins/random_dicer.pyc b/plugins/random_dicer.pyc new file mode 100644 index 0000000..a175d46 Binary files /dev/null and b/plugins/random_dicer.pyc differ diff --git a/plugins/se1.py b/plugins/se1.py new file mode 100644 index 0000000..555da0a --- /dev/null +++ b/plugins/se1.py @@ -0,0 +1,26 @@ + +def name(): + return "SERIAL 1" + +# this plugin will play serially to the next available free space +# in its corresponding scoresheet +def play(roll_no, dice, bonus, players, scoresheets): + # where + i = 0 + for ss in scoresheets: + my_ss = ss[name()] + if '4' not in my_ss: + return [], '4', i + elif '5' not in my_ss: + return [], '5', i + elif '6' not in my_ss: + return [], '6', i + elif 'ESCALERA' not in my_ss: + return [], 'ESCALERA', i + elif 'FULLHOUSE' not in my_ss: + return [], 'FULLHOUSE', i + elif 'POKER' not in my_ss: + return [], 'POKER', i + elif 'GENERALA' not in my_ss: + return [], 'GENERALA', i + i = i + 1 \ No newline at end of file diff --git a/plugins/se2.py b/plugins/se2.py new file mode 100644 index 0000000..c62695c --- /dev/null +++ b/plugins/se2.py @@ -0,0 +1,29 @@ + +def name(): + return "SERIAL 2" + + +# this plugin will play serially to the next available free space +# in its corresponding scoresheet + +def play(roll_no, dice, bonus, players, scoresheets): + # where + + i = 0 + for ss in scoresheets: + my_ss = ss[name()] + if '4' not in my_ss: + return [], '4', i + elif '5' not in my_ss: + return [], '5', i + elif '6' not in my_ss: + return [], '6', i + elif 'ESCALERA' not in my_ss: + return [], 'ESCALERA', i + elif 'FULLHOUSE' not in my_ss: + return [], 'FULLHOUSE', i + elif 'POKER' not in my_ss: + return [], 'POKER', i + elif 'GENERALA' not in my_ss: + return [], 'GENERALA', i + i = i + 1 diff --git a/plugins/serial_dicer.pyc b/plugins/serial_dicer.pyc new file mode 100644 index 0000000..d7b7996 Binary files /dev/null and b/plugins/serial_dicer.pyc differ diff --git a/plugins/while1.py b/plugins/while1.py new file mode 100644 index 0000000..7e28158 --- /dev/null +++ b/plugins/while1.py @@ -0,0 +1,10 @@ + +def name(): + return "while1" + +# this plugin will play serially to the next available free space +# in its corresponding scoresheet +def play(roll_no, dice, bonus, players, scoresheets): + # where + while(1): + pass \ No newline at end of file diff --git a/sandbox.py b/sandbox.py new file mode 100644 index 0000000..3206d02 --- /dev/null +++ b/sandbox.py @@ -0,0 +1,62 @@ + +from RestrictedPython import compile_restricted #for compiling the code safely +from RestrictedPython.Guards import safe_builtins#the builtins that are safe +from RestrictedPython.Guards import full_write_guard#never lets restricted code modify (assign, delete an attribute or item) it shouldn't to +import sys#for manipulating the parameters recieved +import ast +"""preparing the safe_builtins""" +safe_builtins["_getiter_"]= list +safe_builtins["type"]= __builtins__.type +safe_builtins["_getattr_"]= getattr +safe_builtins["_write_"]= full_write_guard + +SliceType = type(slice(0)) +DisallowedObject = [] + +class AccessDenied (Exception): + pass + +def guarded_getitem(ob, index): + if type(index) is SliceType and index.step is None: + start = index.start + stop = index.stop + if start is None: + start = 0 + if stop is None: + v = ob[start:] + else: + v = ob[start:stop] + else: + v = ob[index] + if v is DisallowedObject: + raise AccessDenied + return v + +safe_builtins["_getitem_"]= guarded_getitem + + +restricted_globals = dict(__builtins__ = safe_builtins)# +"""open the plugin for retrieving the code as a string""" +with open(sys.argv[1], 'r') as plugin: + data=plugin.read() +loc = {}#the locals for the sandboxed environment +"""compling the code with the restricted funcionalities such as import, using names starting with underscore _, etc""" +byte_code = compile_restricted(data, sys.argv[1], 'exec') +"""executing the byte code with the restricted globals""" +exec(byte_code, restricted_globals, loc) +"""updating the globals""" +restricted_globals.update(loc) +for nombre, value in restricted_globals.items(): + globals()[nombre] = value +"""executes the function in sanbox.py environment, by this point the code should be safe""" +if (sys.argv[2]=="name"): + print restricted_globals[sys.argv[2]]() +else: + """parsing the parameters""" + roll_no = ast.literal_eval(sys.argv[3]) + dice = ast.literal_eval(sys.argv[4]) + bonus = ast.literal_eval(sys.argv[5]) + players =ast.literal_eval(sys.argv[6]) + scoresheets = ast.literal_eval(sys.argv[7]) + """executing the play""" + print restricted_globals["play"](roll_no,dice,bonus,players,scoresheets) \ No newline at end of file diff --git a/sandboxing.py b/sandboxing.py new file mode 100644 index 0000000..ad1f47b --- /dev/null +++ b/sandboxing.py @@ -0,0 +1,32 @@ +import subprocess #for calling a new process in a sandboxed environment +import ast#for parsing the sandboxed plugin response + + +"""In every function we passed the sanboxed environment: +1) the name of the file's plugin(SE ENTIENDE? el nombre de los archivos de los plugins, no el name() del plugin EJ: serial_dicer.py) +2) The function it should be executing --> name() or play(). In the case of play we send the corresponding values needed +""" +def getName(plugin): + modulo = "plugins/" + plugin + try: + output = (subprocess.check_output(["python","sandbox.py",modulo,"name"], stderr=subprocess.STDOUT)).strip("\n") + return output + except subprocess.CalledProcessError as e: + print "Ocurrio un error en el sandboxing" + print "El error es el sgte:\n", e.output + +def getPlay(plugin,roll_no,dice,bonus,players,scoresheets): + modulo = "plugins/" + plugin + try: + output = (subprocess.check_output(["python","sandbox.py",modulo,"play",str(roll_no),str(dice),str(bonus),str(players),str(scoresheets)], stderr=subprocess.STDOUT)).strip("\n") + #parsing the output + variable = output[1:-1] + variable = variable.replace(" ", "") + nueva = variable.split(",") + roll=ast.literal_eval(nueva[0]) + decision=ast.literal_eval(nueva[1]) + scoresheet=ast.literal_eval(nueva[2]) + return list(roll), str(decision), int(scoresheet) + except subprocess.CalledProcessError as e: + print "Ocurrio un error en el sandboxing" + print "El error es el sgte:\n", e.output diff --git a/sandboxing.pyc b/sandboxing.pyc new file mode 100644 index 0000000..d4667c7 Binary files /dev/null and b/sandboxing.pyc differ