Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
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()
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #7
0
    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
Beispiel #8
0
    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
Beispiel #9
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)
    # 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
Beispiel #10
0
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
Beispiel #12
0
    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)
Beispiel #13
0
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,
Beispiel #14
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)
    # 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
Beispiel #16
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)
    # 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
Beispiel #17
0
    def __init__(self, pk, cwd, cmd):
        self.pk = pk
        self.pipe = sandbox.get_sandbox(cwd)
        self.pipe.start(cmd)

        self.wealth = 0
Beispiel #18
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
Beispiel #19
0
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()