def compile(self, bot_dir, globs, errors, timelimit): with CD(bot_dir): files = safeglob_multi(globs) errored = False box = get_sandbox(bot_dir) try: if self.separate: for filename in files: cmdline = " ".join(self.args + [filename]) cmd_out, cmd_errors = _run_cmd(box, cmdline, timelimit) if not cmd_errors: for vglob in self.vglobs: box.check_path(vglob, cmd_errors) if cmd_errors: cmd_errors += cmd_out if cmd_errors: errors += cmd_errors return False else: cmdline = " ".join(self.args + files) cmd_out, cmd_errors = _run_cmd(box, cmdline, timelimit) if not cmd_errors: for vglob in self.vglobs: box.check_path(vglob, cmd_errors) if cmd_errors: cmd_errors += cmd_out if cmd_errors: errors += cmd_errors return False box.retrieve() finally: box.release() return True
def setup(self, map_data): self.sandbox = get_sandbox(self.wd, False) self.sandbox.start(self.cmd) if not self.sandbox.is_alive: raise Exception('The bot crashed at start. Check the commands.') self.sandbox.pause() map_data += 'ready\n' self.sandbox.write(map_data) self.get_moves() # Wait for go from bot (moves will be empty) self.turn += 1 return self
def main(argv): global current_bot Host, Port = "127.0.0.1", 18889 Host = argv[0] server = SocketServer.ThreadingTCPServer((Host, Port), MyServer) bot_cwd = os.getcwd() bot_cmd = argv[1] sandbox = get_sandbox(bot_cwd, False) sandbox.start(bot_cmd) bots.append(sandbox) current_bot = sandbox server.serve_forever()
def compile(self, bot_dir, globs, errors, timelimit): with CD(bot_dir): files = safeglob_multi(globs) box = get_sandbox(bot_dir) try: if self.separate: for filename in files: cmdline = " ".join(self.args + [filename]) cmd_out, cmd_errors = _run_cmd(box, cmdline, timelimit) cmd_errors = self.cmd_error_filter(cmd_out, cmd_errors) if not cmd_errors: for ofile in self.out_files: box.check_path(ofile, cmd_errors) if self.out_ext: oname = os.path.splitext( filename)[0] + self.out_ext box.check_path(oname, cmd_errors) if cmd_errors: cmd_errors += cmd_out if cmd_errors: errors += cmd_errors return False else: cmdline = " ".join(self.args + files) cmd_out, cmd_errors = _run_cmd(box, cmdline, timelimit) cmd_errors = self.cmd_error_filter(cmd_out, cmd_errors) if not cmd_errors: for ofile in self.out_files: box.check_path(ofile, cmd_errors) if self.out_ext: for filename in files: oname = os.path.splitext( filename)[0] + self.out_ext box.check_path(oname, cmd_errors) if cmd_errors: cmd_errors += cmd_out if cmd_errors: errors += cmd_errors return False box.retrieve() finally: box.release() return True
def compile(self, bot_dir, globs, errors, timelimit): with CD(bot_dir): sources = safeglob_multi(globs) box = get_sandbox(bot_dir) try: for source in sources: head, ext = os.path.splitext(source) if ext in self.replacements: target = head + self.replacements[ext] else: errors.append("Could not determine target for source file %s." % source) return False cmdline = " ".join(self.args + [self.outflag, target, source]) cmd_out, cmd_errors = _run_cmd(box, cmdline, timelimit) if cmd_errors: errors += cmd_errors return False box.retrieve() finally: box.release() return True
def run_game(game, botcmds, options): # file descriptors for replay and streaming formats replay_log = options.get('replay_log', None) stream_log = options.get('stream_log', None) verbose_log = options.get('verbose_log', None) # file descriptors for bots, should be list matching # of bots input_logs = options.get('input_logs', [None]*len(botcmds)) output_logs = options.get('output_logs', [None]*len(botcmds)) error_logs = options.get('error_logs', [None]*len(botcmds)) capture_errors = options.get('capture_errors', False) capture_errors_max = options.get('capture_errors_max', 510) turns = int(options['turns']) loadtime = float(options['loadtime']) / 1000 turntime = float(options['turntime']) / 1000 strict = options.get('strict', False) end_wait = options.get('end_wait', 0.0) location = options.get('location', 'localhost') game_id = options.get('game_id', 0) error = '' bots = [] bot_status = [] bot_turns = [] if capture_errors: error_logs = [HeadTail(log, capture_errors_max) for log in error_logs] try: # create bot sandboxes for b, bot in enumerate(botcmds): bot_cwd, bot_cmd = bot sandbox = get_sandbox(bot_cwd, secure=options.get('secure_jail', None)) sandbox.start(bot_cmd) bots.append(sandbox) bot_status.append('survived') bot_turns.append(0) # ensure it started if not sandbox.is_alive: bot_status[-1] = 'crashed 0' bot_turns[-1] = 0 if verbose_log: verbose_log.write('bot %s did not start\n' % b) game.kill_player(b) sandbox.pause() if stream_log: stream_log.write(game.get_player_start()) stream_log.flush() if verbose_log: verbose_log.write('running for %s turns\n' % turns) for turn in range(turns+1): if turn == 0: game.start_game() # send game state to each player for b, bot in enumerate(bots): if game.is_alive(b): if turn == 0: start = game.get_player_start(b) + 'ready\n' bot.write(start) if input_logs and input_logs[b]: input_logs[b].write(start) input_logs[b].flush() else: state = 'turn ' + str(turn) + '\n' + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() bot_turns[b] = turn if turn > 0: if stream_log: stream_log.write('turn %s\n' % turn) stream_log.write('score %s\n' % ' '.join([str(s) for s in game.get_scores()])) stream_log.write(game.get_state()) stream_log.flush() game.start_turn() # get moves from each player if turn == 0: time_limit = loadtime else: time_limit = turntime if options.get('serial', False): simul_num = int(options['serial']) # int(True) is 1 else: simul_num = len(bots) bot_moves = [[] for b in bots] error_lines = [[] for b in bots] statuses = [None for b in bots] bot_list = [(b, bot) for b, bot in enumerate(bots) if game.is_alive(b)] random.shuffle(bot_list) for group_num in range(0, len(bot_list), simul_num): pnums, pbots = zip(*bot_list[group_num:group_num + simul_num]) moves, errors, status = get_moves(game, pbots, pnums, time_limit, turn) for p, b in enumerate(pnums): bot_moves[b] = moves[p] error_lines[b] = errors[p] statuses[b] = status[p] # handle any logs that get_moves produced for b, errors in enumerate(error_lines): if errors: if error_logs and error_logs[b]: error_logs[b].write(unicode('\n').join(errors)+unicode('\n')) # set status for timeouts and crashes for b, status in enumerate(statuses): if status != None: bot_status[b] = status bot_turns[b] = turn # process all moves bot_alive = [game.is_alive(b) for b in range(len(bots))] if turn > 0 and not game.game_over(): for b, moves in enumerate(bot_moves): if game.is_alive(b): valid, ignored, invalid = game.do_moves(b, moves) if output_logs and output_logs[b]: output_logs[b].write('# turn %s\n' % turn) if valid: if output_logs and output_logs[b]: output_logs[b].write('\n'.join(valid)+'\n') output_logs[b].flush() if ignored: if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s ignored actions:\n' % (turn, b)) error_logs[b].write('\n'.join(ignored)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(ignored)+'\n') output_logs[b].flush() if invalid: if strict: game.kill_player(b) bot_status[b] = 'invalid' bot_turns[b] = turn if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s invalid actions:\n' % (turn, b)) error_logs[b].write('\n'.join(invalid)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(invalid)+'\n') output_logs[b].flush() if turn > 0: game.finish_turn() # send ending info to eliminated bots bots_eliminated = [] for b, alive in enumerate(bot_alive): if alive and not game.is_alive(b): bots_eliminated.append(b) for b in bots_eliminated: if verbose_log: verbose_log.write('turn %4d bot %s eliminated\n' % (turn, b)) if bot_status[b] == 'survived': # could be invalid move bot_status[b] = 'eliminated' bot_turns[b] = turn score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bots[b].write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() if end_wait: bots[b].resume() if bots_eliminated and end_wait: if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for b in bots_eliminated: bots[b].kill() if verbose_log: stats = game.get_stats() stat_keys = sorted(stats.keys()) s = 'turn %4d stats: ' % turn if turn % 50 == 0: verbose_log.write(' '*len(s)) for key in stat_keys: values = stats[key] verbose_log.write(' {0:^{1}}'.format(key, max(len(key), len(str(values))))) verbose_log.write('\n') verbose_log.write(s) for key in stat_keys: values = stats[key] if type(values) == list: values = '[' + ','.join(map(str,values)) + ']' #verbose_log.write(' {0:^{1}}'.format(values, str(max(len(key), len(str(values)))))) verbose_log.write('\n') #alive = [game.is_alive(b) for b in range(len(bots))] #if sum(alive) <= 1: if game.game_over(): break # send bots final state and score, output to replay file game.finish_game() score_line ='score %s\n' % ' '.join(map(str, game.get_scores())) status_line = 'status %s\n' % ' '.join(bot_status) status_line += 'playerturns %s\n' % ' '.join(map(str, bot_turns)) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line if stream_log: stream_log.write(end_line) stream_log.write(game.get_state()) stream_log.flush() if verbose_log: verbose_log.write(score_line) verbose_log.write(status_line) verbose_log.flush() for b, bot in enumerate(bots): if game.is_alive(b): score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() except Exception as e: # TODO: sanitize error output, tracebacks shouldn't be sent to workers error = traceback.format_exc() if verbose_log: verbose_log.write(traceback.format_exc()) # error = str(e) finally: if end_wait: for bot in bots: bot.resume() if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for bot in bots: if bot.is_alive: bot.kill() bot.release() if error: game_result = { 'error': error } else: scores = game.get_scores() game_result = { 'challenge': game.__class__.__name__.lower(), 'location': location, 'game_id': game_id, 'status': bot_status, 'playerturns': bot_turns, 'score': scores, 'rank': [sorted(scores, reverse=True).index(x) for x in scores], 'replayformat': 'json', 'replaydata': game.get_replay(), 'game_length': turn } if capture_errors: game_result['errors'] = [head.headtail() for head in error_logs] if replay_log: json.dump(game_result, replay_log, sort_keys=True) return game_result
def judge_submission(submission): def copy(source, dest): while True: chunk = source.read(1024*1024) if not chunk: break dest.write(chunk) def download(attachment, destination): # TODO: add MD5 verification to downloaded files logger.info("downloading %s ..", attachment.file.url) copy(urllib.urlopen(attachment.file.url), open(destination, "wb")) def unzip_and_sanitize(archive, data_dir): logger.info("unzipping %s ..", archive) file = zipfile.ZipFile(archive, "r") for name in file.namelist(): dest = os.path.join(data_dir, os.path.basename(name)) logger.info("generating %s ..", dest) copy(file.open(name), open(dest, "wb")) sanitize_data(dest) def download_data(problem): attachments = Attachment.objects.filter(problem=problem) """ There are three cases: created, modified, removed. I am too lazy to handle all these cases optimally, so I'm going to evaluate the hash of all those paths, and compare them to re-download all or do nothing. """ entries_to_download = [] for entry in attachments: basename = os.path.basename(entry.file.name) ext = basename.split(".")[-1].lower() if basename != 'judge.py' and ext not in ["in", "out", "zip"]: continue entries_to_download.append((entry, basename)) joined_entries = "@".join(map(lambda x: x[0].file.name, entries_to_download)) md5 = hashlib.md5(joined_entries).hexdigest() pathhash_name = md5 + '.pathhash' pathhash_path = os.path.join(data_dir, pathhash_name) if os.path.exists(data_dir) and os.path.exists(pathhash_name): return if os.path.exists(data_dir): shutil.rmtree(data_dir) os.makedirs(data_dir) for pair in entries_to_download: entry, basename = pair ext = basename.split(".")[-1].lower() destination = os.path.join(data_dir, basename) download(entry, destination) if ext == "zip": unzip_and_sanitize(destination, data_dir) else: sanitize_data(destination) open(pathhash_path, 'w').close() def sanitize_data(filename): # line endings: DOS -> UNIX file = open(filename, 'r+b') body = file.read().replace('\r\n', '\n') file.seek(0) file.write(body) file.truncate() file.close() def get_ioset(): io = {} for file in glob.glob(os.path.join(data_dir, "*")): if file.endswith(".in") or file.endswith(".out"): tokens = file.split(".") basename = ".".join(tokens[:-1]) if basename not in io: io[basename] = {} io[basename][tokens[-1]] = file if not io: raise Exception("Judge I/O data not found.") for key, value in io.iteritems(): if len(value) != 2: raise Exception("Non-matching pairs in judge I/O data. See: %s" % str(io)) return io sandbox_env = None try: logger.info("Checking language module..") # 언어별 채점 모듈 존재 여부부터 확인하기 if submission.language not in languages.modules: raise Exception("Can't find judge module for language %s" % submission.language) language_module = languages.modules[submission.language] problem = submission.problem # 결과 differ 모듈 확인 if problem.judge_module not in differs.modules: raise Exception("Can't find diff module %s" % problem.judge_module) differ_module = differs.modules[problem.judge_module] assert problem.judge_module != 'special_judge' or problem.slug in ['TRAPCARD', 'WORDCHAIN', 'MEETINGROOM'] # 문제 채점 데이터를 다운받고 채점 준비 logger.info("Downloading judge i/o set..") data_dir = os.path.join(settings.JUDGE_SETTINGS["WORKDIR"], "data/%d-%s" % (problem.id, problem.slug)) download_data(submission.problem) ioset = get_ioset() logger.info("Initiating sandbox..") # 샌드박스 생성 # 컴파일할 때 메모리가 더 필요할 수도 있으니, 샌드박스에 메모리는 # 문제 제한보다 더 많이 준다. MINMEMORYSIZE 만큼은 항상 주도록 한다. sandbox_memory = max(settings.JUDGE_SETTINGS['MINMEMORYSIZE'], problem.last_revision.memory_limit) sandbox_env = sandbox.get_sandbox(sandbox_memory) logger.info("Compiling..") # 컴파일 submission.state = Submission.COMPILING submission.save() result = language_module.setup(sandbox_env, submission.source) if result["status"] != "ok": submission.state = Submission.COMPILE_ERROR submission.message = result["message"] return logger.info("Freezing sandbox..") # set sandbox in copy-on-write mode: will run sandbox_env.mount_home("cow") # let's run now logger.info("Running..") submission.state = Submission.RUNNING submission.save() total_time, max_memory = 0, 64 for io in ioset.itervalues(): inp = os.path.basename(io["in"]) sandbox_env.put_file(io["in"], inp) result = language_module.run(sandbox_env, inp, problem.last_revision.time_limit / 1000., problem.last_revision.memory_limit) # RTE 혹은 MLE? if result["status"] != "ok": if result["verdict"] == "TLE": submission.state = Submission.TIME_LIMIT_EXCEEDED elif result["verdict"] == "MLE": submission.state = Submission.RUNTIME_ERROR submission.message = u"메모리 제한 초과" elif result["verdict"] == "RTE": submission.state = Submission.RUNTIME_ERROR submission.message = result["message"] return # 전체 시간이 시간 초과면 곧장 TLE # TODO: 채점 데이터별 시간 제한 지원 total_time += float(result["time"]) max_memory = max(max_memory, int(result["memory"])) if total_time > problem.last_revision.time_limit / 1000.: submission.state = Submission.TIME_LIMIT_EXCEEDED return # differ 에 보내자 output = sandbox_env.get_file_path(result["output"]) if not differ_module.judge(data_dir, io["in"], output, io["out"]): submission.time = int(total_time * 1000) submission.memory = max_memory submission.state = Submission.WRONG_ANSWER return submission.time = int(total_time * 1000) submission.memory = max_memory submission.state = Submission.ACCEPTED except: submission.state = Submission.CANT_BE_JUDGED try: submission.message = u"\n".join([ u"채점 중 예외가 발생했습니다.", u"스택 트레이스:", print_stack_trace()]) except Exception as e: submission.message = u"오류 인코딩 중 에러: %s" % e.message finally: submission.save() if sandbox_env: sandbox_env.teardown()
def run_game(game, botcmds, options): # file descriptors for replay and streaming formats replay_log = options.get('replay_log', None) stream_log = options.get('stream_log', None) verbose_log = options.get('verbose_log', None) debug_log = options.get('debug_log', None) debug_in_replay = options.get('debug_in_replay', None) debug_max_length = options.get('debug_max_length', None) debug_max_count = options.get('debug_max_count', None) # file descriptors for bots, should be list matching # of bots input_logs = options.get('input_logs', [None]*len(botcmds)) output_logs = options.get('output_logs', [None]*len(botcmds)) error_logs = options.get('error_logs', [None]*len(botcmds)) capture_errors = options.get('capture_errors', False) capture_errors_max = options.get('capture_errors_max', 510) turns = int(options['turns']) loadtime = float(options['loadtime']) / 1000 turntime = float(options['turntime']) / 1000 extratime = float(options['extratime']) / 1000 strict = options.get('strict', False) end_wait = options.get('end_wait', 0.0) location = options.get('location', 'localhost') game_id = options.get('game_id', 0) error = '' bots = [] bot_status = [] bot_turns = [] bot_extra_times = [extratime for _ in range(len(botcmds))] debug_msgs = [[] for _ in range(len(botcmds))] debug_msgs_length = [0 for _ in range(len(botcmds))] debug_msgs_count = [0 for _ in range(len(botcmds))] debug_msgs_exceeded = [False for _ in range(len(botcmds))] #helper function to add messages for replay data def add_debug_messages(bot_index, turn, level, messages): if (not debug_in_replay) or len(messages) == 0: return # In order to calculate this only if we not already exceeded if not debug_msgs_exceeded[bot_index]: messages_size = sum(map(lambda m: len(m), messages)) debug_msgs_length[bot_index] += messages_size debug_msgs_count[bot_index] += len(messages) if (debug_msgs_count[bot_index] > debug_max_count) or ( debug_msgs_length[bot_index] > debug_max_length): # update the calculated exceeded debug_msgs_exceeded[bot_index] = True if debug_msgs_exceeded[bot_index] and level != 2: debug_msgs[bot_index].append([turn+1, 2, ["Exceeded debug messages limit."]]) if error_logs and error_logs[bot_index]: error_logs[bot_index].write("Exceeded debug messages limit.\n") else: debug_msgs[bot_index].append([turn+1, level, messages]) if capture_errors: error_logs = [HeadTail(log, capture_errors_max) for log in error_logs] try: # TODO: where did this come from?? do we need it?? for b, bot in enumerate(botcmds): # this struct is given to us from the playgame.py file bot_cwd, bot_path, bot_name = bot # generate the appropriate command from file extension bot_cmd = generate_cmd(bot_path) # generate the sandbox from the bot working directory sandbox = get_sandbox(bot_cwd, protected_files=[bot_path], secure=options.get('secure_jail', None)) if bot_cmd: sandbox.start(bot_cmd) bots.append(sandbox) bot_status.append('alive') bot_turns.append(0) # ensure it started if not sandbox.is_alive: bot_status[-1] = 'crashed 0' bot_turns[-1] = 0 if verbose_log: verbose_log.write('bot %s did not start\n' % bot_name) game.kill_player(b) sandbox.pause() if not bot_cmd: # couldnt generate bot command - couldnt recognize the language of the code add_debug_messages(b, 0, 2, ["Couldnt recognize code language. Are you sure code files are correct?"]) if stream_log: # stream the start info - including non-player info stream_log.write(game.get_player_start()) stream_log.flush() if verbose_log: verbose_log.write('running for %s turns\n' % turns) for turn in range(turns+1): if turn == 0: game.start_game() # send game state to each player for b, bot in enumerate(bots): if game.is_alive(b): if turn == 0: start = game.get_player_start(b) + 'ready\n' bot.write(start) if input_logs and input_logs[b]: input_logs[b].write(start) input_logs[b].flush() else: state = 'turn ' + str(turn) + '\n' + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() bot_turns[b] = turn if turn > 0: if stream_log: stream_log.write('turn %s\n' % turn) stream_log.write('score %s\n' % ' '.join([str(s) for s in game.get_scores()])) stream_log.write(game.get_state()) stream_log.flush() game.start_turn() is_serial = options.get('serial', False) # get moves from each player if turn == 0: time_limit = loadtime elif turn == 1: time_limit = max([turntime * 10, 1.500]) else: time_limit = turntime # here is our safe zone, we take factor of 3 for our running more than we show to players time_limit *= 3 if is_serial: simul_num = 1 else: simul_num = len(bots) bot_moves = [[] for b in bots] error_lines = [[] for b in bots] statuses = [None for b in bots] bot_list = [(b, bot) for b, bot in enumerate(bots) if game.is_alive(b)] #random.shuffle(bot_list) for group_num in range(0, len(bot_list), simul_num): pnums, pbots = zip(*bot_list[group_num:group_num + simul_num]) if is_serial: turn_time_limit = time_limit + bot_extra_times[pnums[0]] else: turn_time_limit = time_limit # get the moves from each bot moves, errors, status, moves_time = get_moves(game, pbots, pnums, turn_time_limit, turn) # if running in serial, deduct the exceeded time from the bot time quota if is_serial and moves_time > time_limit: bot_extra_times[pnums[0]] -= moves_time - time_limit for p, b in enumerate(pnums): bot_moves[b] = moves[p] error_lines[b] = errors[p] statuses[b] = status[p] # print debug messages from bots if debug_log: for b, moves in enumerate(bot_moves): bot_name = botcmds[b][2] messages = [] for move in [m for m in moves if m.startswith('m')]: # if not move.startswith('m'): # # break since messages come only before orders # break try: messages.append(base64.b64decode(move.split(' ')[1])) except: messages.append("Invalid debug message") if messages: debug_log.write('turn %4d bot %s Debug prints:\n' % (turn, bot_name)) debug_log.write('Debug>> ' + '\nDebug>> '.join(messages)+'\n') add_debug_messages(b, turn, 0, messages) stop_messages = [] for move in [m for m in moves if m.startswith('s')]: stop_messages.append(base64.b64decode(move.split(' ')[1])) if stop_messages: add_debug_messages(b, turn, 3, stop_messages) #todo: separate debug from stop messages? # handle any logs that get_moves produced for b, errors in enumerate(error_lines): if errors: if error_logs and error_logs[b]: error_logs[b].write(unicode('\n').join(errors)+unicode('\n')) add_debug_messages(b, turn, 2, [unicode('\n').join(errors)+unicode('\n')]) # error_logs[b].write(unicode('\n').join(unicode(errors))+unicode('\n')) #add_debug_messages(b, turn, 2, [unicode('\n').join(unicode(errors))+unicode('\n')]) # set status for timeouts and crashes for b, status in enumerate(statuses): if status != None: bot_status[b] = status bot_turns[b] = turn # process all moves bot_alive = [game.is_alive(b) for b in range(len(bots))] if turn > 0 and not game.game_over(): for b, moves in enumerate(bot_moves): valid, ignored, invalid = game.do_moves(b, moves) bot_name = botcmds[b][2] if output_logs and output_logs[b]: output_logs[b].write('# turn %s\n' % turn) if valid: if output_logs and output_logs[b]: output_logs[b].write('\n'.join(valid)+'\n') output_logs[b].flush() if ignored: if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s ignored actions:\n' % (turn, bot_name)) error_logs[b].write('\n'.join(ignored)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(ignored)+'\n') output_logs[b].flush() add_debug_messages(b, turn, 1, ignored) if invalid: if strict: game.kill_player(b) bot_status[b] = 'invalid' bot_turns[b] = turn if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot [%s] invalid actions:\n' % (turn, bot_name)) error_logs[b].write('\n'.join(invalid)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(invalid)+'\n') output_logs[b].flush() add_debug_messages(b, turn, 1, invalid) if turn > 0: game.finish_turn() # send ending info to eliminated bots bots_eliminated = [] for b, alive in enumerate(bot_alive): if alive and not game.is_alive(b): bots_eliminated.append(b) for b in bots_eliminated: if verbose_log: verbose_log.write('turn %4d bot %s defeated\n' % (turn, bot_name)) if bot_status[b] == 'alive': # could be invalid move bot_status[b] = 'defeated' bot_turns[b] = turn score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bots[b].write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() if end_wait: bots[b].resume() if bots_eliminated and end_wait: if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for b in bots_eliminated: bots[b].kill() # with verbose log we want to display the following <pirateCount> <treasureCount> <Ranking/leading> <scores> if verbose_log: stats = game.get_stats() stat_keys = sorted(stats.keys()) s = 'turn %4d stats: ' % turn if turn % 50 == 0: verbose_log.write(' '*len(s)) for key in stat_keys: values = stats[key] verbose_log.write(' {0:^{1}}'.format(key, max(len(key), len(str(values))))) verbose_log.write('\n') verbose_log.write(s) for key in stat_keys: values = stats[key] if type(values) == list: values = '[' + ','.join(map(str,values)) + ']' verbose_log.write(' {0:^{1}}'.format(values, max(len(key), len(str(values))))) verbose_log.write('\n') else: # no verbose log - print progress every 20 turns if turn % 20 == 0: turn_prompt = "turn #%d of max %d\n" % (turn,turns) sys.stdout.write(turn_prompt) #alive = [game.is_alive(b) for b in range(len(bots))] #if sum(alive) <= 1: if game.game_over(): break # send bots final state and score, output to replay file game.finish_game() score_line ='score %s\n' % ' '.join(map(str, game.get_scores())) status_line = '' if game.get_winner() and len(game.get_winner()) == 1: winner = game.get_winner()[0] winner_line = 'player %s [%s] is the Winner!\n' % (winner + 1, botcmds[winner][2]) else: winner_line = 'Game finished at a tie - there is no winner' status_line += winner_line end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line if stream_log: stream_log.write(end_line) stream_log.write(game.get_state()) stream_log.flush() if verbose_log: verbose_log.write(score_line) verbose_log.write(status_line) verbose_log.flush() else: sys.stdout.write(score_line) sys.stdout.write(status_line) for b, bot in enumerate(bots): if game.is_alive(b): score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() except Exception as e: # TODO: sanitize error output, tracebacks shouldn't be sent to workers error = traceback.format_exc() sys.stderr.write('Error Occurred\n') sys.stderr.write(str(e) + '\n') if verbose_log: verbose_log.write(error) # error = str(e) finally: if end_wait: for bot in bots: bot.resume() if verbose_log and end_wait > 1: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for bot in bots: if bot.is_alive: bot.kill() bot.release() if error: game_result = { 'error': error } else: scores = game.get_scores() game_result = { 'challenge': game.__class__.__name__.lower(), 'location': location, 'game_id': game_id, 'status': bot_status, 'playerturns': bot_turns, 'score': scores, 'winner_names': [botcmds[win][2] for win in game.get_winner()], 'rank': [sorted(scores, reverse=True).index(x) for x in scores], 'replayformat': 'json', 'replaydata': game.get_replay(), 'game_length': turn, 'debug_messages': debug_msgs, } if capture_errors: game_result['errors'] = [head.headtail() for head in error_logs] if replay_log: json.dump(game_result, replay_log, sort_keys=True) return game_result
def get_runner(bot, game_id, max_debug_length, max_debug_count, input_logs=None, output_logs=None, error_logs=None, secure=None, extra_cmd_args=None): """ Creates a runner and returns it :param bot: the tuple of the bot commands and working directories :type bot: tuple :param game_id: the id of the runner in the game :type game_id: int :param max_debug_length: the max quota of the size ( memory ) of the msgs :type max_debug_length: int :param max_debug_count: the quota of the the amount of msgs allowed :type max_debug_count: int :param input_logs: the input log buffer :type input_logs: file / None :param output_logs: the output log buffer :type output_logs: file / None :param error_logs: the error log buffer :type error_logs: file / None :param secure: flag if the sandbox should be secure ( for the sandbox) :type secure: bool :param extra_cmd_args: extra arguments for the cmd to run the runner ( mainly for tests ) :type extra_cmd_args: list[str] :return: A runner object :rtype: Runner """ bot_cwd, bot_path, bot_name = bot # generate the appropriate command from file extension bot_cmd = RunnerFactory.generate_cmd(bot_path) if extra_cmd_args: bot_cmd += " " + " ".join(extra_cmd_args) # generate the sandbox from the bot working directory sandbox = get_sandbox(bot_cwd, protected_files=[bot_path], secure=secure) if bot_cmd: sandbox.start(bot_cmd) else: # couldn't generate bot command - couldn't recognize the language of the code raise RuntimeError( "Couldn't recognize code language. Are you sure code files are correct?" ) # ensure it started if not sandbox.is_alive: sandbox.pause() raise RuntimeError('bot %s did not start' % bot_name) return Runner(runner=sandbox, name=bot_name, game_id=game_id, max_debug_length=max_debug_length, max_debug_count=max_debug_count, input_logs=input_logs, output_logs=output_logs, error_logs=error_logs)
variation = [] # current board evaluation by computer evaluation = 0 from sandbox import get_sandbox def increment(w, l): """ increments the counters for humans and AIs """ global options options[3] += w options[4] += l open('wl', 'w').write(str(options[3]) + ' ' + str(options[4])) cwd = os.getcwd() bot = get_sandbox(cwd) @app.route('/') @app.route('/home/') def home(): """ renders the home page """ global board global wait global options global evaluation global variation return render_template('home.html', options=options, wait=wait, board=board,
def run_game(game, botcmds, options): # file descriptors for replay and streaming formats replay_log = options.get('replay_log', None) stream_log = options.get('stream_log', None) verbose_log = options.get('verbose_log', None) # file descriptors for bots, should be list matching # of bots input_logs = options.get('input_logs', [None]*len(botcmds)) output_logs = options.get('output_logs', [None]*len(botcmds)) error_logs = options.get('error_logs', [None]*len(botcmds)) capture_errors = options.get('capture_errors', False) capture_errors_max = options.get('capture_errors_max', 510) turns = int(options['turns']) loadtime = float(options['loadtime']) / 1000 turntime = float(options['turntime']) / 1000 strict = options.get('strict', False) end_wait = options.get('end_wait', 0.0) location = options.get('location', 'localhost') game_id = options.get('game_id', 0) ack_turn_zero = options.get('ack_turn_zero', False) error = '' bots = [] bot_status = [] bot_turns = [] fischer_time = False timebank_limit = 0 if 'timebank' in options and options['timebank'] > 0 : fischer_time = True timebank_limit = options['timebank'] #print(fischer_time) #print(timebank_limit) if capture_errors: error_logs = [HeadTail(log, capture_errors_max) for log in error_logs] try: # create bot sandboxes for b, bot in enumerate(botcmds): bot_cwd, bot_cmd = bot sandbox = get_sandbox(bot_cwd, secure=options.get('secure_jail', None), verbose=verbose_log) sandbox.start(bot_cmd) bots.append(sandbox) bot_status.append('survived') bot_turns.append(0) # ensure it started if not sandbox.is_alive: bot_status[-1] = 'crashed 0' bot_turns[-1] = 0 if verbose_log: verbose_log.write('bot %s did not start\n' % b) game.kill_player(b) sandbox.pause() if fischer_time: timebank = [options['timebank'] for bot in bots] else: timebank = [0 for bot in bots] if stream_log: stream_log.write(game.get_player_start()) stream_log.flush() if verbose_log: verbose_log.write('running for %s turns\n' % turns) for turn in range(turns+1): if turn == 0: game.start_game() bot_indices = game.bots_to_play(turn) # bots_to_play = get_bots (bots, bot_indices) # update timebanks if using fischer time if turn == 0: time_limit = loadtime else: time_limit = turntime if fischer_time: for i, _ in enumerate(timebank): timebank[i] = min (timebank_limit, timebank[i] + time_limit * 1000) else: group_timelimit = time_limit # send game state to each player for b, bot in enumerate(bots): #print((b, bot)) if game.is_alive(b): if turn == 0: start = game.get_player_start(b) #+ 'ready\n' bot.write(start) if input_logs and input_logs[b]: input_logs[b].write(start) input_logs[b].flush() elif b in bot_indices: bot_time = turntime if fischer_time: bot_time = timebank[b] / 1000 #print(fischer_time) #print(bot_time) state = game.get_player_state(b, bot_time) #state = 'turn ' + str(turn) + '\n' + game.get_player_state(b, bot_time) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() bot_turns[b] = turn if turn > 0: if stream_log: stream_log.write('turn %s\n' % turn) stream_log.write('score %s\n' % ' '.join([str(s) for s in game.get_scores()])) stream_log.write(game.get_state()) stream_log.flush() game.start_turn() # get moves from each player if 'parallel' in options and not fischer_time: simul_num = int(options['parallel']) else: simul_num = 1 #if options.get('serial', False): #simul_num = int(options['serial']) # int(True) is 1 #else: #simul_num = len(bots) times_used = [0 for b in bots] bot_moves = [[] for b in bots] error_lines = [[] for b in bots] statuses = [None for b in bots] bot_list = [(b, bot) for b, bot in enumerate(bots) if game.is_alive(b)] random.shuffle(bot_list) #print(bot_list) if (turn > 0) or ack_turn_zero: for group_num in range(0, len(bot_list), simul_num): pnums, pbots = zip(*bot_list[group_num:group_num + simul_num]) if fischer_time: # Note that this assumes parallel bots and fischer time are not going to be combined. Some extra effort needed to change this. group_timelimit = timebank[pnums[0]] / 1000 #print(group_timelimit) moves, errors, status, times = get_moves(game, pbots, pnums, bot_indices, group_timelimit, timebank, fischer_time, turn) for p, b in enumerate(pnums): bot_moves[b] = moves[p] error_lines[b] = errors[p] statuses[b] = status[p] times_used[b] = times[p] #print (times, times_used, pnums) if fischer_time: for i, time_used in enumerate(times_used): timebank[i] -= time_used * 1000 #print (timebank) # handle any logs that get_moves produced for b, errors in enumerate(error_lines): if errors: if error_logs and error_logs[b]: error_logs[b].write(unicode('\n').join(errors)+unicode('\n')) # set status for timeouts and crashes for b, status in enumerate(statuses): if status != None: bot_status[b] = status bot_turns[b] = turn # process all moves bot_alive = [game.is_alive(b) for b in range(len(bots))] if turn > 0 and not game.game_over(): for b, moves in enumerate(bot_moves): if game.is_alive(b): valid, ignored, invalid = game.do_moves(b, moves) if output_logs and output_logs[b]: output_logs[b].write('# turn %s\n' % turn) if valid: if output_logs and output_logs[b]: output_logs[b].write('\n'.join(valid)+'\n') output_logs[b].flush() if ignored: if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s ignored actions:\n' % (turn, b)) error_logs[b].write('\n'.join(ignored)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(ignored)+'\n') output_logs[b].flush() if invalid: if strict: game.kill_player(b) bot_status[b] = 'invalid' bot_turns[b] = turn if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s invalid actions:\n' % (turn, b)) error_logs[b].write('\n'.join(invalid)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(invalid)+'\n') output_logs[b].flush() if turn > 0: game.finish_turn() # send ending info to eliminated bots bots_eliminated = [] for b, alive in enumerate(bot_alive): if alive and not game.is_alive(b): bots_eliminated.append(b) for b in bots_eliminated: if verbose_log: verbose_log.write('turn %4d bot %s eliminated\n' % (turn, b)) if bot_status[b] == 'survived': # could be invalid move bot_status[b] = 'eliminated' bot_turns[b] = turn score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b, 1) #+ 'go\n' bots[b].write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() if end_wait: bots[b].resume() if bots_eliminated and end_wait: if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for b in bots_eliminated: bots[b].kill() if verbose_log: stats = game.get_stats() stat_keys = sorted(stats.keys()) s = 'turn %4d stats: ' % turn if turn % 50 == 0: verbose_log.write(' '*len(s)) for key in stat_keys: values = stats[key] verbose_log.write(' {0:^{1}}'.format(key, max(len(key), len(str(values))))) verbose_log.write('\n') verbose_log.write(s) for key in stat_keys: values = stats[key] if type(values) == list: values = '[' + ','.join(map(str,values)) + ']' verbose_log.write(' {0:^{1}}'.format(values, max(len(key), len(str(values))))) verbose_log.write('\n') #alive = [game.is_alive(b) for b in range(len(bots))] #if sum(alive) <= 1: if game.game_over(): break # send bots final state and score, output to replay file game.finish_game() score_line ='Final rank %s\n' % ' '.join(map(str, game.get_scores())) status_line = 'status %s\n' % ' '.join(bot_status) status_line += 'playerturns %s\n' % ' '.join(map(str, bot_turns)) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line if stream_log: stream_log.write(end_line) stream_log.write(game.get_state()) stream_log.flush() if verbose_log: verbose_log.write(score_line) verbose_log.write(status_line) verbose_log.flush() for b, bot in enumerate(bots): if game.is_alive(b): score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b, turntime) #+ 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() except Exception as e: # TODO: sanitize error output, tracebacks shouldn't be sent to workers error = traceback.format_exc() if verbose_log: verbose_log.write(traceback.format_exc()) # error = str(e) finally: if end_wait: for bot in bots: bot.resume() if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for bot in bots: if bot.is_alive: bot.kill() bot.release() if error: game_result = { 'error': error } else: scores = game.get_scores() game_result = { 'challenge': game.__class__.__name__.lower(), 'location': location, 'game_id': game_id, 'status': bot_status, 'playerturns': bot_turns, 'score': scores, 'rank': [sorted(scores, reverse=True).index(x) for x in scores], 'replayformat': 'json', 'replaydata': game.get_replay(), 'game_length': turn } # This ugly hack should be replaced with a better way # of communicating the bot IDs to the report processor print("botcmds:") print(botcmds) print([(cmd) for cmd in botcmds]) bot_ids = [os.path.split(cmd[-1])[-1] for cmd in botcmds] #bot_ids = [cmd[0].split('/')[-2] for cmd in botcmds] #bot_ids = [1, 2] # Previous version used cmd[0] for the line above, check. game_result['bot_ids'] = bot_ids if capture_errors: game_result['errors'] = [head.headtail() for head in error_logs] if replay_log: json.dump(game_result, replay_log, sort_keys=True) return game_result
def run_game(game, botcmds, options): # file descriptors for replay and streaming formats replay_log = options.get('replay_log', None) stream_log = options.get('stream_log', None) verbose_log = options.get('verbose_log', None) # file descriptors for bots, should be list matching # of bots input_logs = options.get('input_logs', [None]*len(botcmds)) output_logs = options.get('output_logs', [None]*len(botcmds)) error_logs = options.get('error_logs', [None]*len(botcmds)) capture_errors = options.get('capture_errors', False) capture_errors_max = options.get('capture_errors_max', 510) turns = int(options['turns']) loadtime = float(options['loadtime']) / 1000 turntime = float(options['turntime']) / 1000 strict = options.get('strict', False) end_wait = options.get('end_wait', 0.0) location = options.get('location', 'localhost') game_id = options.get('game_id', 0) ack_turn_zero = options.get('ack_turn_zero', True) error = '' bots = [] bot_status = [] bot_turns = [] fischer_time = False timebank_limit = 0 if 'timebank' in options and options['timebank'] > 0 : fischer_time = True timebank_limit = options['timebank'] #print(fischer_time) #print(timebank_limit) if capture_errors: error_logs = [HeadTail(log, capture_errors_max) for log in error_logs] try: # create bot sandboxes for b, bot in enumerate(botcmds): bot_cwd, bot_cmd = bot sandbox = get_sandbox(bot_cwd, secure=options.get('secure_jail', None), verbose=verbose_log) sandbox.start(bot_cmd) bots.append(sandbox) bot_status.append('survived') bot_turns.append(0) # ensure it started if not sandbox.is_alive: bot_status[-1] = 'crashed 0' bot_turns[-1] = 0 if verbose_log: verbose_log.write('bot %s did not start\n' % b) game.kill_player(b) sandbox.pause() if fischer_time: timebank = [options['timebank'] for bot in bots] else: timebank = [0 for bot in bots] if stream_log: stream_log.write(game.get_player_start()) stream_log.flush() if verbose_log: verbose_log.write('running for %s turns\n' % turns) for turn in range(turns+1): if turn == 0: game.start_game() bot_indices = game.bots_to_play(turn) # bots_to_play = get_bots (bots, bot_indices) # update timebanks if using fischer time if turn == 0: time_limit = loadtime else: time_limit = turntime if fischer_time: for i, _ in enumerate(timebank): timebank[i] = min (timebank_limit, timebank[i] + time_limit * 1000) else: group_timelimit = time_limit # send game state to each player for b, bot in enumerate(bots): if game.is_alive(b): if turn == 0: start = game.get_player_start(b) #+ 'ready\n' bot.write(start) if input_logs and input_logs[b]: input_logs[b].write(start) input_logs[b].flush() elif b in bot_indices: bot_time = turntime if fischer_time: bot_time = timebank[b] / 1000 state = game.get_player_state(b, bot_time) bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() bot_turns[b] = turn if turn > 0: if stream_log: stream_log.write('turn %s\n' % turn) stream_log.write('score %s\n' % ' '.join([str(s) for s in game.get_scores()])) stream_log.write(game.get_state()) stream_log.flush() game.start_turn() # get moves from each player if 'parallel' in options and not fischer_time: simul_num = int(options['parallel']) else: simul_num = 1 times_used = [0 for b in bots] bot_moves = [[] for b in bots] error_lines = [[] for b in bots] statuses = [None for b in bots] bot_list = [(b, bot) for b, bot in enumerate(bots) if game.is_alive(b)] random.shuffle(bot_list) if (turn > 0) or ack_turn_zero: for group_num in range(0, len(bot_list), simul_num): pnums, pbots = zip(*bot_list[group_num:group_num + simul_num]) if fischer_time: # Note that this assumes parallel bots and fischer time are not going to be combined. Some extra effort needed to change this. group_timelimit = timebank[pnums[0]] / 1000 moves, errors, status, times = get_moves(game, pbots, pnums, bot_indices, group_timelimit, timebank, fischer_time, turn) for p, b in enumerate(pnums): bot_moves[b] = moves[p] error_lines[b] = errors[p] statuses[b] = status[p] times_used[b] = times[p] if fischer_time: for i, time_used in enumerate(times_used): timebank[i] -= time_used * 1000 # handle any logs that get_moves produced for b, errors in enumerate(error_lines): if errors: if error_logs and error_logs[b]: error_logs[b].write(unicode('\n').join(errors)+unicode('\n')) # set status for timeouts and crashes for b, status in enumerate(statuses): if status != None: bot_status[b] = status bot_turns[b] = turn # process all moves bot_alive = [game.is_alive(b) for b in range(len(bots))] if turn > 0 and not game.game_over(): for b, moves in enumerate(bot_moves): if game.is_alive(b): valid, ignored, invalid = game.do_moves(b, moves) if output_logs and output_logs[b]: output_logs[b].write('# turn %s\n' % turn) if valid: if output_logs and output_logs[b]: output_logs[b].write('\n'.join(valid)+'\n') output_logs[b].flush() if ignored: if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s ignored actions:\n' % (turn, b)) error_logs[b].write('\n'.join(ignored)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(ignored)+'\n') output_logs[b].flush() if invalid: if strict: game.kill_player(b) bot_status[b] = 'invalid' bot_turns[b] = turn if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s invalid actions:\n' % (turn, b)) error_logs[b].write('\n'.join(invalid)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(invalid)+'\n') output_logs[b].flush() if turn > 0: game.finish_turn() # send ending info to eliminated bots bots_eliminated = [] for b, alive in enumerate(bot_alive): if alive and not game.is_alive(b): bots_eliminated.append(b) for b in bots_eliminated: if verbose_log: verbose_log.write('turn %4d bot %s eliminated\n' % (turn, b)) if bot_status[b] == 'survived': # could be invalid move bot_status[b] = 'eliminated' bot_turns[b] = turn score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b, 1) #+ 'go\n' bots[b].write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() if end_wait: bots[b].resume() if bots_eliminated and end_wait: if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for b in bots_eliminated: bots[b].kill() if verbose_log: stats = game.get_stats() stat_keys = sorted(stats.keys()) s = 'turn %4d stats: ' % turn if turn % 50 == 0: verbose_log.write(' '*len(s)) for key in stat_keys: values = stats[key] verbose_log.write(' {0:^{1}}'.format(key, max(len(key), len(str(values))))) verbose_log.write('\n') verbose_log.write(s) for key in stat_keys: values = stats[key] if type(values) == list: values = '[' + ','.join(map(str,values)) + ']' verbose_log.write(' {0:^{1}}'.format(values, max(len(key), len(str(values))))) verbose_log.write('\n') if game.game_over(): break # send bots final state and score, output to replay file game.finish_game() score_line ='score %s\n' % ' '.join(map(str, game.get_scores())) status_line = 'status %s\n' % ' '.join(bot_status) status_line += 'playerturns %s\n' % ' '.join(map(str, bot_turns)) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line if stream_log: stream_log.write(end_line) stream_log.write(game.get_state()) stream_log.flush() if verbose_log: verbose_log.write(score_line) verbose_log.write(status_line) verbose_log.flush() for b, bot in enumerate(bots): if game.is_alive(b): score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b, turntime) #+ 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() except Exception as e: # TODO: sanitize error output, tracebacks shouldn't be sent to workers error = traceback.format_exc() if verbose_log: verbose_log.write(traceback.format_exc()) finally: if end_wait: for bot in bots: bot.resume() if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for bot in bots: if bot.is_alive: bot.kill() bot.release() if error: game_result = { 'error': error } else: scores = game.get_scores() game_result = { 'challenge': game.__class__.__name__.lower(), 'location': location, 'game_id': game_id, 'status': bot_status, 'playerturns': bot_turns, 'score': scores, 'rank': [sorted(scores, reverse=True).index(x) for x in scores], 'replayformat': 'json', 'replaydata': game.get_replay(), 'game_length': turn } # This ugly hack should be replaced with a better way # of communicating the bot IDs to the report processor print("botcmds:") print(botcmds) bot_ids = [os.path.split(cmd[0])[-2] for cmd in botcmds] # Previous version used cmd[0] for the line above, check. game_result['bot_ids'] = bot_ids if capture_errors: game_result['errors'] = [head.headtail() for head in error_logs] if replay_log: json.dump(game_result, replay_log, sort_keys=True) return game_result
def run_game(game, botcmds, options): # file descriptors for replay and streaming formats replay_log = options.get('replay_log', None) stream_log = options.get('stream_log', None) verbose_log = options.get('verbose_log', None) # file descriptors for bots, should be list matching # of bots input_logs = options.get('input_logs', [None] * len(botcmds)) output_logs = options.get('output_logs', [None] * len(botcmds)) error_logs = options.get('error_logs', [None] * len(botcmds)) capture_errors = options.get('capture_errors', False) capture_errors_max = options.get('capture_errors_max', 510) turns = int(options['turns']) loadtime = float(options['loadtime']) / 1000 turntime = float(options['turntime']) / 1000 strict = options.get('strict', False) end_wait = options.get('end_wait', 0.0) location = options.get('location', 'localhost') game_id = options.get('game_id', 0) error = '' bots = [] bot_status = [] bot_turns = [] if capture_errors: error_logs = [HeadTail(log, capture_errors_max) for log in error_logs] try: # create bot sandboxes for b, bot in enumerate(botcmds): bot_cwd, bot_cmd = bot sandbox = get_sandbox(bot_cwd, secure=options.get('secure_jail', None)) sandbox.start(bot_cmd) bots.append(sandbox) bot_status.append('survived') bot_turns.append(0) # ensure it started if not sandbox.is_alive: bot_status[-1] = 'crashed 0' bot_turns[-1] = 0 if verbose_log: verbose_log.write('bot %s did not start\n' % b) game.kill_player(b) sandbox.pause() if stream_log: stream_log.write(game.get_player_start()) stream_log.flush() if verbose_log: verbose_log.write('running for %s turns\n' % turns) for turn in range(turns + 1): if turn == 0: game.start_game() # send game state to each player for b, bot in enumerate(bots): if game.is_alive(b): if turn == 0: start = game.get_player_start(b) + 'ready\n' bot.write(start) if input_logs and input_logs[b]: input_logs[b].write(start) input_logs[b].flush() else: state = 'turn ' + str( turn) + '\n' + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() bot_turns[b] = turn if turn > 0: if stream_log: stream_log.write('turn %s\n' % turn) stream_log.write( 'score %s\n' % ' '.join([str(s) for s in game.get_scores()])) stream_log.write(game.get_state()) stream_log.flush() game.start_turn() # get moves from each player if turn == 0: time_limit = loadtime else: time_limit = turntime if options.get('serial', False): simul_num = int(options['serial']) # int(True) is 1 else: simul_num = len(bots) bot_moves = [[] for b in bots] error_lines = [[] for b in bots] statuses = [None for b in bots] bot_list = [(b, bot) for b, bot in enumerate(bots) if game.is_alive(b)] random.shuffle(bot_list) for group_num in range(0, len(bot_list), simul_num): pnums, pbots = zip(*bot_list[group_num:group_num + simul_num]) moves, errors, status = get_moves(game, pbots, pnums, time_limit, turn) for p, b in enumerate(pnums): bot_moves[b] = moves[p] error_lines[b] = errors[p] statuses[b] = status[p] # handle any logs that get_moves produced for b, errors in enumerate(error_lines): if errors: if error_logs and error_logs[b]: error_logs[b].write( unicode('\n').join(errors) + unicode('\n')) # set status for timeouts and crashes for b, status in enumerate(statuses): if status != None: bot_status[b] = status bot_turns[b] = turn # process all moves bot_alive = [game.is_alive(b) for b in range(len(bots))] if turn > 0 and not game.game_over(): for b, moves in enumerate(bot_moves): if game.is_alive(b): valid, ignored, invalid = game.do_moves(b, moves) if output_logs and output_logs[b]: output_logs[b].write('# turn %s\n' % turn) if valid: if output_logs and output_logs[b]: output_logs[b].write('\n'.join(valid) + '\n') output_logs[b].flush() if ignored: if error_logs and error_logs[b]: error_logs[b].write( 'turn %4d bot %s ignored actions:\n' % (turn, b)) error_logs[b].write('\n'.join(ignored) + '\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(ignored) + '\n') output_logs[b].flush() if invalid: if strict: game.kill_player(b) bot_status[b] = 'invalid' bot_turns[b] = turn if error_logs and error_logs[b]: error_logs[b].write( 'turn %4d bot %s invalid actions:\n' % (turn, b)) error_logs[b].write('\n'.join(invalid) + '\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(invalid) + '\n') output_logs[b].flush() if turn > 0: game.finish_turn() # send ending info to eliminated bots bots_eliminated = [] for b, alive in enumerate(bot_alive): if alive and not game.is_alive(b): bots_eliminated.append(b) for b in bots_eliminated: if verbose_log: verbose_log.write('turn %4d bot %s eliminated\n' % (turn, b)) if bot_status[b] == 'survived': # could be invalid move bot_status[b] = 'eliminated' bot_turns[b] = turn score_line = 'score %s\n' % ' '.join( [str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join( map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join( map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len( bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bots[b].write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() if end_wait: bots[b].resume() if bots_eliminated and end_wait: if verbose_log: verbose_log.write( 'waiting {0} seconds for bots to process end turn\n'. format(end_wait)) time.sleep(end_wait) for b in bots_eliminated: bots[b].kill() if verbose_log: stats = game.get_stats() stat_keys = sorted(stats.keys()) s = 'turn %4d stats: ' % turn if turn % 50 == 0: verbose_log.write(' ' * len(s)) for key in stat_keys: values = stats[key] verbose_log.write(' {0:^{1}}'.format( key, max(len(key), len(str(values))))) verbose_log.write('\n') verbose_log.write(s) for key in stat_keys: values = stats[key] if type(values) == list: values = '[' + ','.join(map(str, values)) + ']' verbose_log.write(' {0:^{1}}'.format( values, max(len(key), len(str(values))))) verbose_log.write('\n') #alive = [game.is_alive(b) for b in range(len(bots))] #if sum(alive) <= 1: if game.game_over(): break # send bots final state and score, output to replay file game.finish_game() score_line = 'score %s\n' % ' '.join(map(str, game.get_scores())) status_line = 'status %s\n' % ' '.join(bot_status) status_line += 'playerturns %s\n' % ' '.join(map(str, bot_turns)) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line if stream_log: stream_log.write(end_line) stream_log.write(game.get_state()) stream_log.flush() if verbose_log: verbose_log.write(score_line) verbose_log.write(status_line) verbose_log.flush() for b, bot in enumerate(bots): if game.is_alive(b): score_line = 'score %s\n' % ' '.join( [str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join( map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join( map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len( bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() except Exception as e: # TODO: sanitize error output, tracebacks shouldn't be sent to workers error = traceback.format_exc() if verbose_log: verbose_log.write(traceback.format_exc()) # error = str(e) finally: if end_wait: for bot in bots: bot.resume() if verbose_log: verbose_log.write( 'waiting {0} seconds for bots to process end turn\n'. format(end_wait)) time.sleep(end_wait) for bot in bots: if bot.is_alive: bot.kill() bot.release() if error: game_result = {'error': error} else: scores = game.get_scores() game_result = { 'challenge': game.__class__.__name__.lower(), 'location': location, 'game_id': game_id, 'status': bot_status, 'playerturns': bot_turns, 'score': scores, 'rank': [sorted(scores, reverse=True).index(x) for x in scores], 'replayformat': 'json', 'replaydata': game.get_replay(), 'game_length': turn } if capture_errors: game_result['errors'] = [head.headtail() for head in error_logs] if replay_log: ### monkey patch to limit float presicion to 3 digits ;( from json import encoder encoder.FLOAT_REPR = lambda o: format(o, '.3f') json.dump(game_result, replay_log, sort_keys=True) return game_result
def __init__(self, pk, cwd, cmd): self.pk = pk self.pipe = sandbox.get_sandbox(cwd) self.pipe.start(cmd) self.wealth = 0
def run_game(game, botcmds, options): # file descriptors for replay and streaming formats replay_log = options.get('replay_log', None) stream_log = options.get('stream_log', None) verbose_log = options.get('verbose_log', None) debug_log = options.get('debug_log', None) debug_in_replay = options.get('debug_in_replay', None) debug_max_length = options.get('debug_max_length', None) debug_max_count = options.get('debug_max_count', None) # file descriptors for bots, should be list matching # of bots input_logs = options.get('input_logs', [None]*len(botcmds)) output_logs = options.get('output_logs', [None]*len(botcmds)) error_logs = options.get('error_logs', [None]*len(botcmds)) capture_errors = options.get('capture_errors', False) capture_errors_max = options.get('capture_errors_max', 510) turns = int(options['turns']) loadtime = float(options['loadtime']) / 1000 turntime = float(options['turntime']) / 1000 extratime = float(options['extratime']) / 1000 strict = options.get('strict', False) end_wait = options.get('end_wait', 0.0) location = options.get('location', 'localhost') game_id = options.get('game_id', 0) error = '' bots = [] bot_status = [] bot_turns = [] bot_extra_times = [extratime for _ in range(len(botcmds))] debug_msgs = [[] for _ in range(len(botcmds))] debug_msgs_length = [0 for _ in range(len(botcmds))] debug_msgs_count = [0 for _ in range(len(botcmds))] debug_msgs_exceeded = [False for _ in range(len(botcmds))] #helper function to add messages for replay data def add_debug_messages(bot_index, turn, level, messages): if (not debug_in_replay) or len(messages) == 0: return # In order to calculate this only if we not already exceeded if not debug_msgs_exceeded[bot_index]: messages_size = sum(map(lambda m: len(m), messages)) debug_msgs_length[bot_index] += messages_size debug_msgs_count[bot_index] += len(messages) if (debug_msgs_count[bot_index] > debug_max_count) or ( debug_msgs_length[bot_index] > debug_max_length): # update the calculated exceeded debug_msgs_exceeded[bot_index] = True if debug_msgs_exceeded[bot_index] and level != 2: debug_msgs[bot_index].append([turn+1, 2, ["Exceeded debug messages limit."]]) if error_logs and error_logs[bot_index]: error_logs[bot_index].write("Exceeded debug messages limit.\n") else: debug_msgs[bot_index].append([turn+1, level, messages]) if capture_errors: error_logs = [HeadTail(log, capture_errors_max) for log in error_logs] try: # TODO: where did this come from?? do we need it?? for b, bot in enumerate(botcmds): # this struct is given to us from the playgame.py file bot_cwd, bot_path, bot_name = bot # generate the appropriate command from file extension bot_cmd = generate_cmd(bot_path) # generate the sandbox from the bot working directory sandbox = get_sandbox(bot_cwd, protected_files=[bot_path], secure=options.get('secure_jail', None)) if bot_cmd: sandbox.start(bot_cmd) bots.append(sandbox) bot_status.append('alive') bot_turns.append(0) # ensure it started if not sandbox.is_alive: bot_status[-1] = 'crashed 0' bot_turns[-1] = 0 if verbose_log: verbose_log.write('bot %s did not start\n' % bot_name) game.kill_player(b) sandbox.pause() if not bot_cmd: # couldnt generate bot command - couldnt recognize the language of the code add_debug_messages(b, 0, 2, ["Couldnt recognize code language. Are you sure code files are correct?"]) if stream_log: # stream the start info - including non-player info stream_log.write(game.get_player_start()) stream_log.flush() if verbose_log: verbose_log.write('running for %s turns\n' % turns) for turn in range(turns+1): if turn == 0: game.start_game() # send game state to each player for b, bot in enumerate(bots): if game.is_alive(b): if turn == 0: start = game.get_player_start(b) + 'ready\n' bot.write(start) if input_logs and input_logs[b]: input_logs[b].write(start) input_logs[b].flush() else: state = 'turn ' + str(turn) + '\n' + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() bot_turns[b] = turn if turn > 0: if stream_log: stream_log.write('turn %s\n' % turn) stream_log.write('score %s\n' % ' '.join([str(s) for s in game.get_scores()])) stream_log.write(game.get_state()) stream_log.flush() game.start_turn() is_serial = options.get('serial', False) # get moves from each player if turn == 0: time_limit = loadtime elif turn == 1: time_limit = max([turntime * 10, 1.500]) else: time_limit = turntime if is_serial: simul_num = 1 else: simul_num = len(bots) bot_moves = [[] for b in bots] error_lines = [[] for b in bots] statuses = [None for b in bots] bot_list = [(b, bot) for b, bot in enumerate(bots) if game.is_alive(b)] #random.shuffle(bot_list) for group_num in range(0, len(bot_list), simul_num): pnums, pbots = zip(*bot_list[group_num:group_num + simul_num]) if is_serial: turn_time_limit = time_limit + bot_extra_times[pnums[0]] else: turn_time_limit = time_limit # get the moves from each bot moves, errors, status, moves_time = get_moves(game, pbots, pnums, turn_time_limit, turn) # if running in serial, deduct the exceeded time from the bot time quota if is_serial and moves_time > time_limit: bot_extra_times[pnums[0]] -= moves_time - time_limit for p, b in enumerate(pnums): bot_moves[b] = moves[p] error_lines[b] = errors[p] statuses[b] = status[p] # print debug messages from bots if debug_log: for b, moves in enumerate(bot_moves): bot_name = botcmds[b][2] messages = [] for move in [m for m in moves if m.startswith('m')]: # if not move.startswith('m'): # # break since messages come only before orders # break try: messages.append(base64.b64decode(move.split(' ')[1])) except: messages.append("Invalid debug message") if messages: debug_log.write('turn %4d bot %s Debug prints:\n' % (turn, bot_name)) debug_log.write('Debug>> ' + '\nDebug>> '.join(messages)+'\n') add_debug_messages(b, turn, 0, messages) stop_messages = [] for move in [m for m in moves if m.startswith('s')]: stop_messages.append(base64.b64decode(move.split(' ')[1])) if stop_messages: add_debug_messages(b, turn, 3, stop_messages) #todo: separate debug from stop messages? # handle any logs that get_moves produced for b, errors in enumerate(error_lines): if errors: if error_logs and error_logs[b]: error_logs[b].write(unicode('\n').join(errors)+unicode('\n')) add_debug_messages(b, turn, 2, [unicode('\n').join(errors)+unicode('\n')]) # error_logs[b].write(unicode('\n').join(unicode(errors))+unicode('\n')) #add_debug_messages(b, turn, 2, [unicode('\n').join(unicode(errors))+unicode('\n')]) # set status for timeouts and crashes for b, status in enumerate(statuses): if status != None: bot_status[b] = status bot_turns[b] = turn # process all moves bot_alive = [game.is_alive(b) for b in range(len(bots))] if turn > 0 and not game.game_over(): for b, moves in enumerate(bot_moves): valid, ignored, invalid = game.do_moves(b, moves) bot_name = botcmds[b][2] if output_logs and output_logs[b]: output_logs[b].write('# turn %s\n' % turn) if valid: if output_logs and output_logs[b]: output_logs[b].write('\n'.join(valid)+'\n') output_logs[b].flush() if ignored: if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot %s ignored actions:\n' % (turn, bot_name)) error_logs[b].write('\n'.join(ignored)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(ignored)+'\n') output_logs[b].flush() add_debug_messages(b, turn, 1, ignored) if invalid: if strict: game.kill_player(b) bot_status[b] = 'invalid' bot_turns[b] = turn if error_logs and error_logs[b]: error_logs[b].write('turn %4d bot [%s] invalid actions:\n' % (turn, bot_name)) error_logs[b].write('\n'.join(invalid)+'\n') error_logs[b].flush() if output_logs and output_logs[b]: output_logs[b].write('\n'.join(invalid)+'\n') output_logs[b].flush() add_debug_messages(b, turn, 1, invalid) if turn > 0: game.finish_turn() # send ending info to eliminated bots bots_eliminated = [] for b, alive in enumerate(bot_alive): if alive and not game.is_alive(b): bots_eliminated.append(b) for b in bots_eliminated: if verbose_log: verbose_log.write('turn %4d bot %s defeated\n' % (turn, bot_name)) if bot_status[b] == 'alive': # could be invalid move bot_status[b] = 'defeated' bot_turns[b] = turn score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bots[b].write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() if end_wait: bots[b].resume() if bots_eliminated and end_wait: if verbose_log: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for b in bots_eliminated: bots[b].kill() # with verbose log we want to display the following <pirateCount> <treasureCount> <Ranking/leading> <scores> if verbose_log: stats = game.get_stats() stat_keys = sorted(stats.keys()) s = 'turn %4d stats: ' % turn if turn % 50 == 0: verbose_log.write(' '*len(s)) for key in stat_keys: values = stats[key] verbose_log.write(' {0:^{1}}'.format(key, max(len(key), len(str(values))))) verbose_log.write('\n') verbose_log.write(s) for key in stat_keys: values = stats[key] if type(values) == list: values = '[' + ','.join(map(str,values)) + ']' verbose_log.write(' {0:^{1}}'.format(values, max(len(key), len(str(values))))) verbose_log.write('\n') else: # no verbose log - print progress every 20 turns if turn % 20 == 0: turn_prompt = "turn #%d of max %d\n" % (turn,turns) sys.stdout.write(turn_prompt) #alive = [game.is_alive(b) for b in range(len(bots))] #if sum(alive) <= 1: if game.game_over(): break # send bots final state and score, output to replay file game.finish_game() score_line ='score %s\n' % ' '.join(map(str, game.get_scores())) status_line = '' if game.get_winner() and len(game.get_winner()) == 1: winner = game.get_winner()[0] winner_line = 'player %s [%s] is the Winner!\n' % (winner + 1, botcmds[winner][2]) else: winner_line = 'Game finished at a tie - there is no winner' status_line += winner_line end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line if stream_log: stream_log.write(end_line) stream_log.write(game.get_state()) stream_log.flush() if verbose_log: verbose_log.write(score_line) verbose_log.write(status_line) verbose_log.flush() else: sys.stdout.write(score_line) sys.stdout.write(status_line) for b, bot in enumerate(bots): if game.is_alive(b): score_line ='score %s\n' % ' '.join([str(s) for s in game.get_scores(b)]) status_line = 'status %s\n' % ' '.join(map(str, game.order_for_player(b, bot_status))) status_line += 'playerturns %s\n' % ' '.join(map(str, game.order_for_player(b, bot_turns))) end_line = 'end\nplayers %s\n' % len(bots) + score_line + status_line state = end_line + game.get_player_state(b) + 'go\n' bot.write(state) if input_logs and input_logs[b]: input_logs[b].write(state) input_logs[b].flush() except Exception as e: # TODO: sanitize error output, tracebacks shouldn't be sent to workers error = traceback.format_exc() sys.stderr.write('Error Occurred\n') sys.stderr.write(str(e) + '\n') if verbose_log: verbose_log.write(error) # error = str(e) finally: if end_wait: for bot in bots: bot.resume() if verbose_log and end_wait > 1: verbose_log.write('waiting {0} seconds for bots to process end turn\n'.format(end_wait)) time.sleep(end_wait) for bot in bots: if bot.is_alive: bot.kill() bot.release() if error: game_result = { 'error': error } else: scores = game.get_scores() game_result = { 'challenge': game.__class__.__name__.lower(), 'location': location, 'game_id': game_id, 'status': bot_status, 'playerturns': bot_turns, 'score': scores, 'winner_names': [botcmds[win][2] for win in game.get_winner()], 'rank': [sorted(scores, reverse=True).index(x) for x in scores], 'replayformat': 'json', 'replaydata': game.get_replay(), 'game_length': turn, 'debug_messages': debug_msgs, } if capture_errors: game_result['errors'] = [head.headtail() for head in error_logs] if replay_log: json.dump(game_result, replay_log, sort_keys=True) return game_result
def judge_submission(submission): def copy(source, dest): while True: chunk = source.read(1024 * 1024) if not chunk: break dest.write(chunk) def download(attachment, destination): # TODO: add MD5 verification to downloaded files url = settings.JUDGE_SETTINGS['WEBSERVER'] + attachment.file.url logger.info("downloading %s ..", url) copy(urllib.urlopen(url), open(destination, "wb")) def unzip_and_sanitize(archive, data_dir): logger.info("unzipping %s ..", archive) file = zipfile.ZipFile(archive, "r") for name in file.namelist(): dest = os.path.join(data_dir, os.path.basename(name)) logger.info("generating %s ..", dest) copy(file.open(name), open(dest, "wb")) sanitize_data(dest) def download_data(problem): attachments = Attachment.objects.filter(problem=problem) """ There are three cases: created, modified, removed. I am too lazy to handle all these cases optimally, so I'm going to evaluate the hash of all those paths, and compare them to re-download all or do nothing. """ entries_to_download = [] for entry in attachments: basename = os.path.basename(entry.file.name) ext = basename.split(".")[-1].lower() if basename != 'checker' and ext not in ["in", "out", "zip"]: continue entries_to_download.append((entry, basename)) joined_entries = "@".join( map(lambda x: x[0].file.name, entries_to_download)) md5 = hashlib.md5(joined_entries).hexdigest() pathhash_name = md5 + '.pathhash' pathhash_path = os.path.join(data_dir, pathhash_name) if os.path.exists(data_dir) and os.path.exists(pathhash_name): return if os.path.exists(data_dir): shutil.rmtree(data_dir) os.makedirs(data_dir) for pair in entries_to_download: entry, basename = pair ext = basename.split(".")[-1].lower() destination = os.path.join(data_dir, basename) download(entry, destination) if ext == "zip": unzip_and_sanitize(destination, data_dir) else: sanitize_data(destination) open(pathhash_path, 'w').close() def sanitize_data(filename): # line endings: DOS -> UNIX file = open(filename, 'r+b') body = file.read().replace('\r\n', '\n') file.seek(0) file.write(body) file.truncate() file.close() def get_ioset(): io = {} for file in glob.glob(os.path.join(data_dir, "*")): if file.endswith(".in") or file.endswith(".out"): tokens = file.split(".") basename = ".".join(tokens[:-1]) if basename not in io: io[basename] = {} io[basename][tokens[-1]] = file if not io: raise Exception("Judge I/O data not found.") for key, value in io.iteritems(): if len(value) != 2: raise Exception( "Non-matching pairs in judge I/O data. See: %s" % str(io)) return io sandbox_env = None try: logger.info("Checking language module..") # 언어별 채점 모듈 존재 여부부터 확인하기 if submission.language not in languages.modules: raise Exception("Can't find judge module for language %s" % submission.language) language_module = languages.modules[submission.language] problem = submission.problem # 결과 differ 모듈 확인 if not hasattr(differs, problem.judge_module): raise Exception("Can't find diff module %s" % problem.judge_module) differ = getattr(differs, problem.judge_module) # 문제 채점 데이터를 다운받고 채점 준비 logger.info("Downloading judge i/o set..") data_dir = os.path.join(settings.JUDGE_SETTINGS["WORKDIR"], "data/%d-%s" % (problem.id, problem.slug)) download_data(submission.problem) ioset = get_ioset() logger.info("Initiating sandbox..") sandbox_env = sandbox.get_sandbox() logger.info("Compiling..") # 컴파일 submission.state = Submission.COMPILING submission.save() result = language_module.setup(sandbox_env, submission.source) if result["status"] != "ok": submission.state = Submission.COMPILE_ERROR submission.message = result["message"] return logger.info("Freezing sandbox..") # set sandbox in copy-on-write mode: will run sandbox_env.mount_home("cow") # let's run now logger.info("Running..") submission.state = Submission.RUNNING submission.save() total_time, max_memory = 0, 64 for io in ioset.itervalues(): inp = os.path.basename(io["in"]) sandbox_env.put_file(io["in"], inp) result = language_module.run( sandbox_env, inp, problem.last_revision.time_limit / 1000., problem.last_revision.memory_limit) # RTE 혹은 MLE? if result["status"] != "ok": if result["verdict"] == "TLE": submission.state = Submission.TIME_LIMIT_EXCEEDED elif result["verdict"] == "MLE": submission.state = Submission.RUNTIME_ERROR submission.message = '\n'.join( [u"메모리 제한 초과", result["message"]]) elif result["verdict"] == "RTE": submission.state = Submission.RUNTIME_ERROR submission.message = result["message"] return # 전체 시간이 시간 초과면 곧장 TLE # TODO: 채점 데이터별 시간 제한 지원 total_time += float(result["time"]) max_memory = max(max_memory, int(result["memory"])) if total_time > problem.last_revision.time_limit / 1000.: submission.state = Submission.TIME_LIMIT_EXCEEDED return # differ 에 보내자 output = sandbox_env.get_file_path(result["output"]) if not differ(open(io["in"]), open(output), open(io["out"]), data_dir, sandbox_env): submission.time = int(total_time * 1000) submission.memory = max_memory submission.state = Submission.WRONG_ANSWER return submission.time = int(total_time * 1000) submission.memory = max_memory submission.state = Submission.ACCEPTED except Exception as e1: submission.state = Submission.CANT_BE_JUDGED try: print e1.message print print_stack_trace() submission.message = u"\n".join([ u"채점 중 예외가 발생했습니다.", u"익셉션: %s" % e1.message, u"스택 트레이스:", print_stack_trace() ]) except Exception as e2: submission.message = u"오류 인코딩 중 에러: %s" % e2.message finally: submission.save() if sandbox_env: sandbox_env.teardown()