Exemplo n.º 1
0
    def test_re_cache_cleared(self):
        pattern = "^(..+?)\1+$"
        key = (str, pattern, 0)

        re.compile(pattern)
        self.assertIn(key, re._cache)

        run_game(game, get_next_move_1, get_next_move_1)
        self.assertNotIn(key, re._cache)
Exemplo n.º 2
0
    def test_exception(self):
        # X | . | .
        # --|---|--
        # . | . | .
        # --|---|--
        # . | . | .

        result = run_game(game, get_next_move_1, get_next_move_9)

        self.assertEqual(result.result_type, ResultType.EXCEPTION)
        self.assertEqual(result.score, 1)
        self.assertEqual(result.move_list, [0])
        self.assertIn("KeyError: 123", result.traceback)
Exemplo n.º 3
0
    def test_invalid_state(self):
        # X | . | .
        # --|---|--
        # . | . | .
        # --|---|--
        # . | . | .

        result = run_game(game, get_next_move_1, get_next_move_11)

        expected_result = Result(
            result_type=ResultType.INVALID_STATE,
            score=1,
            move_list=[0],
            traceback=None,
            invalid_move=None,
        )

        self.assertEqual(result, expected_result)
Exemplo n.º 4
0
    def test_timeout_when_opcode_limit_not_set(self):
        # X | O | X
        # --|---|--
        # O | X | O
        # --|---|--
        # X | . | .

        result = run_game(game, get_next_move_1, get_next_move_10)

        expected_result = Result(
            result_type=ResultType.COMPLETE,
            score=1,
            move_list=[0, 1, 2, 3, 4, 5, 6],
            traceback=None,
            invalid_move=None,
        )

        self.assertEqual(result, expected_result)
Exemplo n.º 5
0
    def test_all_params(self):
        # X | O | X
        # --|---|--
        # O | X | O
        # --|---|--
        # X | . | .

        result = run_game(game, get_next_move_7, get_next_move_7)

        expected_result = Result(
            result_type=ResultType.COMPLETE,
            score=1,
            move_list=[0, 1, 2, 3, 4, 5, 6],
            traceback=None,
            invalid_move=None,
        )

        self.assertEqual(result, expected_result)
Exemplo n.º 6
0
    def test_draw(self):
        # X | X | O
        # --|---|--
        # O | O | X
        # --|---|--
        # X | X | O

        result = run_game(game, get_next_move_3, get_next_move_4)

        expected_result = Result(
            result_type=ResultType.COMPLETE,
            score=0,
            move_list=[0, 2, 1, 3, 5, 4, 6, 8, 7],
            traceback=None,
            invalid_move=None,
        )

        self.assertEqual(result, expected_result)
Exemplo n.º 7
0
    def test_bot2_wins(self):
        # . | X | O
        # --|---|--
        # X | O | X
        # --|---|--
        # O | . | .

        result = run_game(game, get_next_move_2, get_next_move_2)

        expected_result = Result(
            result_type=ResultType.COMPLETE,
            score=-1,
            move_list=[1, 2, 3, 4, 5, 6],
            traceback=None,
            invalid_move=None,
        )

        self.assertEqual(result, expected_result)
Exemplo n.º 8
0
    def test_timeout(self):
        # X | . | .
        # --|---|--
        # . | . | .
        # --|---|--
        # . | . | .

        result = run_game(game,
                          get_next_move_1,
                          get_next_move_10,
                          opcode_limit=1000)

        expected_result = Result(
            result_type=ResultType.TIMEOUT,
            score=1,
            move_list=[0],
            traceback=None,
            invalid_move=None,
        )

        self.assertEqual(result, expected_result)
Exemplo n.º 9
0
def play_game(bot1_id, bot2_id):
    if (Game.objects.filter(
            bot1_id=bot1_id,
            bot2_id=bot2_id).count() == settings.BOTANY_NUM_ROUNDS):
        return

    # TODO: run game in subprocess and ensure environment variables are not accessible
    game = loader.load_module_from_dotted_path(settings.BOTANY_GAME_MODULE)

    bot1 = Bot.objects.get(id=bot1_id)
    bot2 = Bot.objects.get(id=bot2_id)

    if bot1.is_inactive or bot2.is_inactive:
        return

    mod1 = loader.create_module_from_str("mod1", bot1.code)
    mod2 = loader.create_module_from_str("mod2", bot2.code)

    return runner.run_game(
        game,
        mod1.get_next_move,
        mod2.get_next_move,
        opcode_limit=settings.BOTANY_OPCODE_LIMIT,
    )
Exemplo n.º 10
0
def tournament(path1, path2, pathn, full_output, num_rounds, opcode_limit):
    game = utils.load_game_module()

    paths = [path1, path2] + list(pathn)
    bots = []

    for path in paths:
        mod = utils.create_bot_module("mod", path)
        bots.append({
            "path": path,
            "fn": mod.get_next_move,
            "num_played": 0,
            "num_wins": 0,
            "num_draws": 0,
            "num_losses": 0,
            "score": 0,
        })

    if num_rounds is None:
        num_rounds = utils.get_setting("botany_num_rounds")

    if opcode_limit is None:
        opcode_limit = utils.get_setting("botany_opcode_limit")

    if opcode_limit == 0:
        opcode_limit = None

    if not tracer.opcode_limit_supported:
        print("Opcode limiting not supported in this version of Python")
        print()
        opcode_limit = None

    for bot1 in bots:
        for bot2 in bots:
            if bot1 == bot2:
                continue

            if full_output:
                print(f"{bot1['path']} vs {bot2['path']}")

            for ix in range(num_rounds):
                result = runner.run_game(game,
                                         bot1["fn"],
                                         bot2["fn"],
                                         opcode_limit=opcode_limit)

                bot1["num_played"] += 1
                bot2["num_played"] += 1

                if result.score == 1:
                    bot1["num_wins"] += 1
                    bot1["score"] += 1
                    bot2["num_losses"] += 1
                    bot2["score"] -= 1

                    winning_bot = "bot1"
                    losing_bot = "bot2"

                elif result.score == 0:
                    bot1["num_draws"] += 1
                    bot2["num_draws"] += 1

                    winning_bot = None
                    losing_bot = None

                elif result.score == -1:
                    bot1["num_losses"] += 1
                    bot1["score"] -= 1
                    bot2["num_wins"] += 1
                    bot2["score"] += 1

                    winning_bot = "bot2"
                    losing_bot = "bot1"

                else:
                    assert False

                if winning_bot is None:
                    result_summary = "game drawn"
                else:
                    result_summary = f"{winning_bot} wins"

                if result.result_type == runner.ResultType.INVALID_MOVE:
                    result_extra = f"{losing_bot} made an invalid move"
                elif result.result_type == runner.ResultType.EXCEPTION:
                    result_extra = f"{losing_bot} raised an exception"
                elif result.result_type == runner.ResultType.TIMEOUT:
                    result_extra = f"{losing_bot} exceeded the opcode limit"
                elif result.result_type == runner.ResultType.INVALID_STATE:
                    result_extra = f"{losing_bot} returned an invalid state"
                else:
                    assert result.result_type == runner.ResultType.COMPLETE
                    result_extra = None

                if full_output:
                    items = [
                        str(ix).rjust(len(str(num_rounds))),
                        result_summary.ljust(10),
                        "".join(str(move) for move in result.move_list),
                    ]

                    if result_extra:
                        items.append(result_extra)

                    print("  " + " ".join(items))

            if full_output:
                print()

    if full_output:
        print()

    max_path_width = max(len(bot["path"]) for bot in bots)
    max_col_width = len(str(num_rounds * len(bots) * len(bots)))

    def sortkey(bot):
        return [-bot["score"], bot["num_played"], -bot["num_wins"]]

    bots = sorted(bots, key=sortkey)

    row_items = [
        "Bot".center(max_path_width),
        "P".center(max_col_width),
        "W".center(max_col_width),
        "D".center(max_col_width),
        "L".center(max_col_width),
        "Score",
    ]
    header_row = " | ".join(row_items)
    print(header_row)

    dividing_row = header_row.replace("|", "+")
    dividing_row = re.sub("[^+]", "-", dividing_row)
    print(dividing_row)

    for bot in bots:
        row_items = [
            bot["path"].ljust(max_path_width),
            str(bot["num_played"]).rjust(max_col_width),
            str(bot["num_wins"]).rjust(max_col_width),
            str(bot["num_draws"]).rjust(max_col_width),
            str(bot["num_losses"]).rjust(max_col_width),
            str(bot["score"]).rjust(max_col_width),
        ]

        print(" | ".join(row_items))
Exemplo n.º 11
0
def play(path1, path2, opcode_limit):
    game = utils.load_game_module()

    def get_next_move_human(board):
        available_moves = game.available_moves(board)

        while True:
            col = input("> ")
            try:
                col = int(col)
            except ValueError:
                continue

            if col not in available_moves:
                continue

            return col

    def wrap_bot_fn(fn):
        param_list = runner.get_param_list(fn)

        def wrapped(board, move_list, token, state):
            input()

            all_args = {
                "board": board,
                "move_list": move_list,
                "token": token,
                "state": state,
            }

            args = {
                param: value
                for param, value in all_args.items() if param in param_list
            }

            return fn(**args)

        return wrapped

    if opcode_limit is None:
        opcode_limit = utils.get_setting("botany_opcode_limit")

    if opcode_limit == 0:
        opcode_limit = None

    if not tracer.opcode_limit_supported:
        print("Opcode limiting not supported in this version of Python")
        print()
        opcode_limit = None

    if path1 == "human":
        fn1 = get_next_move_human
    else:
        mod1 = utils.create_bot_module("mod1", path1)
        fn1 = wrap_bot_fn(mod1.get_next_move)

    if path2 == "human":
        fn2 = get_next_move_human
    else:
        mod2 = utils.create_bot_module("mod2", path2)
        fn2 = wrap_bot_fn(mod2.get_next_move)

    result = runner.run_game(game,
                             fn1,
                             fn2,
                             opcode_limit=opcode_limit,
                             display_board=True)

    if result.score == 1:
        winning_bot = f"bot1 ({path1})"
        losing_bot = f"bot2 ({path2})"
    elif result.score == 0:
        winning_bot = None
        losing_bot = None
    elif result.score == -1:
        winning_bot = f"bot2 ({path2})"
        losing_bot = f"bot1 ({path1})"
    else:
        assert False

    if result.result_type == runner.ResultType.INVALID_MOVE:
        print(f"{losing_bot} made an invalid move: {result.invalid_move}")
    elif result.result_type == runner.ResultType.EXCEPTION:
        print(f"{losing_bot} raised an exception:")
        print(result.traceback)
    elif result.result_type == runner.ResultType.TIMEOUT:
        print(f"{losing_bot} exceeded the opcode limit")
    elif result.result_type == runner.ResultType.INVALID_STATE:
        print(f"{losing_bot} returned an invalid state")
    else:
        assert result.result_type == runner.ResultType.COMPLETE

    if winning_bot is None:
        print("game drawn")
    else:
        print(f"{winning_bot} wins")

    print()