Пример #1
0
def test_board_intermediate_move_single_player() -> None:
    b = Board()

    logging_observer = LoggingObserver()
    b.add_observer(logging_observer)

    # Can't place a tile if they haven't moved
    r = b.intermediate_move(IntermediateMove(make_tiles()[2], "green"))
    assert r.is_error()
    assert r.error(
    ) == "cannot place a tile for player green since they are not alive"

    # An initial move for green
    r = b.initial_move(
        InitialMove(BoardPosition(9, 4), index_to_tile(0), Port.TopLeft,
                    "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(9, 4)) == index_to_tile(0)
    assert b._board_state.live_players["green"] == (BoardPosition(9, 4),
                                                    Port.TopLeft)

    # Add another tile and they move
    r = b.intermediate_move(IntermediateMove(index_to_tile(1), "green"))
    assert r.is_ok()
    assert b._board_state.live_players["green"] == (
        BoardPosition(x=9, y=3),
        Port.TopRight,
    )

    # And place a tile that will draw them off the edge of the board
    assert logging_observer.all_messages() == []
    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert "green" not in b._board_state.live_players
    assert logging_observer.all_messages() == ["exited_board: green"]
Пример #2
0
def test_validate_place_tile_unnecessary_loop() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightBottom))
    )
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 0))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 1))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(3, 1))
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(4), "red"))
    assert r.is_ok()
    assert r.value()
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(5), "red"))
    assert r.is_ok()
    assert r.value()
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(5).rotate(), "red"))
    assert r.is_ok()
    assert not r.value()

    r2 = rc.validate_move(
        bs,
        [index_to_tile(5), index_to_tile(4)],
        IntermediateMove(index_to_tile(4), "red"),
    )
    assert r2.is_error()
    assert (
        r2.error()
        == "player chose a loopy move when this does not create a loop: Tile(idx=5, edges=[(0, 7), (1, 5), (2, 6), (3, 4)])"
    )
Пример #3
0
def test_board_to_html() -> None:
    # Just test that it returns a string and rely on manual testing to verify that it
    # looks roughly correct
    b = Board()
    assert isinstance(b.get_board_state().to_html(), str)

    board_r = Board.create_board_from_moves(
        [
            InitialMove(BoardPosition(5, 0), index_to_tile(2),
                        Port.BottomRight, "blue"),
            InitialMove(BoardPosition(9, 2), index_to_tile(3), Port.TopLeft,
                        "white"),
            InitialMove(BoardPosition(9, 4), index_to_tile(4), Port.TopLeft,
                        "green"),
        ],
        [
            IntermediateMove(index_to_tile(5), "blue"),
            IntermediateMove(index_to_tile(6), "white"),
        ],
    )
    assert board_r.is_ok()
    b2 = board_r.value().get_board_state()
    assert isinstance(b2.to_html(), str)
    assert b2.to_html() != b.get_board_state().to_html()
    assert b2.to_html(automatic_refresh=True) != b2.to_html(
        automatic_refresh=False)
Пример #4
0
def test_place_tile_remove_other_player() -> None:
    # Black kills red by driving them off the edge of the board
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 2), Port.RightBottom))
    )
    bs = bs.with_live_players(
        bs.live_players.set("black", (BoardPosition(4, 2), Port.LeftTop))
    )
    bs = bs.with_tile(index_to_tile(2), BoardPosition(2, 0))
    bs = bs.with_tile(index_to_tile(2), BoardPosition(2, 1))
    bs = bs.with_tile(index_to_tile(3), BoardPosition(2, 2))

    bs = bs.with_tile(index_to_tile(2), BoardPosition(4, 0))
    bs = bs.with_tile(index_to_tile(2), BoardPosition(4, 1))
    bs = bs.with_tile(index_to_tile(32).rotate(), BoardPosition(4, 2))

    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(2), "red"))
    assert r.is_ok()
    assert r.value()
    move = IntermediateMove(index_to_tile(2), "black")
    r = rc.is_move_suicidal(bs, move)
    assert r.is_ok()
    assert not r.value()
    assert rc.validate_move(bs, [index_to_tile(2), index_to_tile(3)], move).is_ok()
    board = Board(bs)
    assert board.intermediate_move(move).is_ok()

    assert "red" not in board.live_players
    assert board.live_players["black"] == (BoardPosition(x=2, y=2), 6)
    bs = bs.with_tile(index_to_tile(2), BoardPosition(4, 2))
Пример #5
0
def test_board_intermediate_move_loop_moves_no_one_else() -> None:
    # If player A places a tile that sends them into a loop and player B is next to the would be placed tile,
    # player B does not move
    b = Board()

    logging_observer = LoggingObserver()
    b.add_observer(logging_observer)

    # An initial move for green
    r = b.initial_move(
        InitialMove(BoardPosition(0, 1), index_to_tile(4), Port.RightBottom,
                    "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(0, 1)) == index_to_tile(4)
    assert b.live_players["green"] == (BoardPosition(0, 1), Port.RightBottom)

    # More moves for green to setup what we need to make a loop
    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 1)) == index_to_tile(4)
    assert b._board_state.live_players["green"] == (
        BoardPosition(1, 1),
        Port.BottomLeft,
    )

    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 2)) == index_to_tile(4)
    assert b._board_state.live_players["green"] == (BoardPosition(1, 2),
                                                    Port.LeftTop)

    # And set up white next to the where the would-be loop would be created
    assert b.place_tile_at_index_with_scissors(index_to_tile(4),
                                               BoardPosition(0, 3)).is_ok()
    b._board_state = b._board_state.with_live_players(
        b._board_state.live_players.set("white",
                                        (BoardPosition(0, 3), Port.TopRight)))

    # This tile placement will make a loop. Test that it is detected, the player is removed, and a message is broadcast
    # to all of the defined observers. According to assignment 6 the player should be removed but the tile
    # should not be on the board.
    assert logging_observer.all_messages() == []
    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert "green" not in b.live_players
    assert b._board_state.get_tile(BoardPosition(0, 2)) is None
    assert logging_observer.all_messages() == [
        "entered_loop: green",
        "exited_board: green",
    ]

    # And white is still in the same place
    assert b._board_state.live_players["white"] == (BoardPosition(0, 3),
                                                    Port.TopRight)
Пример #6
0
def test_validate_place_tile_loop_someone_else() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightBottom))
    )
    bs = bs.with_live_players(
        bs.live_players.set("white", (BoardPosition(4, 0), Port.LeftTop))
    )
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 0))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 1))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(3, 1))
    bs = bs.with_tile(index_to_tile(2), BoardPosition(4, 0))

    # The move is suicidal for red but not for white
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(13).rotate(), "white"))
    assert r.is_ok()
    assert not r.value()
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(13).rotate(), "red"))
    assert r.is_ok()
    assert r.value()
    # But it does create a loop no matter who places it
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(13).rotate(), "white"))
    assert r.is_ok()
    assert r.value()
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(13).rotate(), "red"))
    assert r.is_ok()
    assert r.value()
    # And thus it is illegal
    r = rc.is_move_illegal(bs, IntermediateMove(index_to_tile(13).rotate(), "white"))
    assert r.is_ok()
    assert r.value()
    r = rc.is_move_illegal(bs, IntermediateMove(index_to_tile(13).rotate(), "red"))
    assert r.is_ok()
    assert r.value()

    # Red and white are both not allowed to place the move since it causes a loop
    r2 = rc.validate_move(
        bs,
        [index_to_tile(13).rotate().rotate(), index_to_tile(6)],
        IntermediateMove(index_to_tile(13).rotate(), "white"),
    )
    assert r2.is_error()
    assert (
        r2.error()
        == "player chose a loopy move when this does not create a loop: Tile(idx=13, edges=[(0, 7), (1, 2), (3, 5), (4, 6)])"
    )
    r2 = rc.validate_move(
        bs,
        [index_to_tile(13).rotate().rotate(), index_to_tile(6)],
        IntermediateMove(index_to_tile(13).rotate(), "red"),
    )
    assert r2.is_error()
    assert (
        r2.error()
        == "player chose a loopy move when this does not create a loop: Tile(idx=13, edges=[(0, 7), (1, 2), (3, 5), (4, 6)])"
    )
Пример #7
0
    def _get_check_intermediate_move(
            self, color: ColorString, board: Board, tiles: List[Tile],
            player: PlayerInterface) -> Result[IntermediateMove]:
        """
        Gets the intermediate move from the player and checks whether it is valid based on the rulechecker. If any errors, the player is added as a cheater.
        Returns an error if cheating, or the chosen move if it is valid
        """
        r_move = self._handle_player_timeout(
            color, lambda: player.generate_move(deepcopy(tiles),
                                                board.get_board_state()))
        if r_move.is_error():
            self._cheaters.add(color)
            return error(r_move.error())

        intermediate_move = IntermediateMove(r_move.value(), color)
        r_rule = self._rule_checker.validate_move(board.get_board_state(),
                                                  tiles, intermediate_move)

        for observer in self._observers:
            observer.intermediate_move_played(color, tiles,
                                              board.get_board_state(),
                                              intermediate_move,
                                              r_rule.is_ok())

        if r_rule.is_error():
            self._cheaters.add(color)
            return error(r_rule.error())

        return ok(intermediate_move)
Пример #8
0
def main(host: str, port: int) -> None:
    """
    Connect to the server at host:port via TCP to render a board state to the GUI via a GraphicalPlayerObserver
    :param host:    The host to connect to
    :param port:    The port to connect to
    :return:        None
    """
    gpo = GraphicalPlayerObserver()

    json_stream = NetworkJSONStream.tcp_client_to(host, port)
    json_stream.send_message(AUTHORS)

    state_pats_json_r = json_stream.receive_message()
    board = setup_board(state_pats_json_r.assert_value())
    gpo.set_players(list(board.live_players.keys()))

    turn_pat_json = json_stream.receive_message().assert_value()
    act_pat = ActionPat.from_json(turn_pat_json[0]  # pylint: disable=unsubscriptable-object
                                  )
    gpo.set_color(act_pat.player)
    offered_tiles: List[Tile] = [
        index_to_tile(tile_idx) for tile_idx in turn_pat_json[1:]
    ]
    gpo.intermediate_move_offered(offered_tiles, board.get_board_state())
    move = IntermediateMove(
        tile_pattern_to_tile(act_pat.tile_pat.tile_index,
                             act_pat.tile_pat.rotation_angle),
        act_pat.player,
    )
    gpo.intermediate_move_played(offered_tiles, board.get_board_state(), move)
    json_stream.close()
Пример #9
0
def main() -> None:
    """
    Connect to the server at host:port via TCP to render a board state to the GUI via a GraphicalPlayerObserver
    """
    json_stream = StdinStdoutJSONStream()

    state_pats_json_r = json_stream.receive_message()
    board = setup_board(state_pats_json_r.assert_value())

    turn_pat_json = json_stream.receive_message().assert_value()
    act_pat = ActionPat.from_json(turn_pat_json[0]  # pylint: disable=unsubscriptable-object
                                  )
    offered_tiles: List[Tile] = [
        index_to_tile(tile_idx) for tile_idx in turn_pat_json[1:]  # pylint: disable=unsubscriptable-object
    ]
    move = IntermediateMove(
        tile_pattern_to_tile(act_pat.tile_pat.tile_index,
                             act_pat.tile_pat.rotation_angle),
        act_pat.player,
    )
    rule_checker = RuleChecker()
    r = rule_checker.validate_move(board.get_board_state(), offered_tiles,
                                   move)
    if r.is_error():
        json_stream.send_message("cheating")
    else:
        json_stream.send_message("legal")

    if DEBUG:
        board.get_board_state().debug_display_board()
        r = board.intermediate_move(move)
        if r.is_error():
            print("Failed to render #2")
        else:
            board.get_board_state().debug_display_board()
Пример #10
0
def test_is_move_suicidal_inexistent() -> None:
    rc = RuleChecker()
    bs = BoardState()

    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(22), "red"))
    assert r.is_error()
    assert r.error() == "player red is not alive thus the move cannot be suicidal"
Пример #11
0
def test_validate_place_tile_required_suicide() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightTop))
    )
    bs = bs.with_tile(index_to_tile(2), BoardPosition(2, 0))
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(4), "red"))
    assert r.is_ok()
    assert r.value()
    r2 = rc.validate_move(
        bs,
        [index_to_tile(4), index_to_tile(4)],
        IntermediateMove(index_to_tile(4).rotate(), "red"),
    )
    assert r2.is_ok()
Пример #12
0
def test_board_intermediate_move_remove_player_after_loop() -> None:
    b = Board()

    logging_observer = LoggingObserver()
    b.add_observer(logging_observer)

    # An initial move for green
    r = b.initial_move(
        InitialMove(BoardPosition(0, 1), index_to_tile(4), Port.RightBottom,
                    "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(0, 1)) == index_to_tile(4)
    assert b.live_players["green"] == (BoardPosition(0, 1), Port.RightBottom)

    # More moves for green that would create a loop
    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 1)) == index_to_tile(4)
    assert b._board_state.live_players["green"] == (
        BoardPosition(1, 1),
        Port.BottomLeft,
    )

    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 2)) == index_to_tile(4)
    assert b._board_state.live_players["green"] == (BoardPosition(1, 2),
                                                    Port.LeftTop)

    assert logging_observer.all_messages() == []

    # This tile placement will make a loop. Test that it is detected, the player is removed, and a message is broadcast
    # to all of the defined observers. According to assignment 6 the player should be removed but the tile
    # should not be on the board.
    r = b.intermediate_move(IntermediateMove(index_to_tile(4), "green"))
    assert r.is_ok()
    assert "green" not in b.live_players
    assert b._board_state.get_tile(BoardPosition(0, 2)) is None
    assert logging_observer.all_messages() == [
        "entered_loop: green",
        "exited_board: green",
    ]
Пример #13
0
def test_validate_place_tile_inexistent_player() -> None:
    rc = RuleChecker()
    bs = BoardState()

    r = rc.validate_move(
        bs,
        [index_to_tile(1), index_to_tile(2)],
        IntermediateMove(index_to_tile(1), "red"),
    )
    assert r.is_error()
    assert r.error() == "cannot place a tile for player red since they are not alive"
Пример #14
0
def test_is_move_suicidal_walk_off_edge() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightTop))
    )
    bs = bs.with_tile(index_to_tile(2), BoardPosition(2, 0))
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(4), "red"))
    assert r.is_ok()
    assert r.value()
Пример #15
0
def main() -> None:
    """
    Main function for the xboard integration test. Creates a board, applies action pats, and prints
    data to stdout as described in assignment 4: http://www.ccs.neu.edu/home/matthias/4500-f19/4.html
    :return:    None
    """
    stdin_stream = StdinStdoutJSONStream()
    board = setup_board(stdin_stream.receive_message().assert_value())

    logging_observer = LoggingObserver()
    board.add_observer(logging_observer)

    if "red" not in board.live_players:
        print_and_exit("red never played", board)

    for act_pat_json in stdin_stream.message_iterator():
        act_pat = ActionPat.from_json(act_pat_json.assert_value())
        r = board.intermediate_move(
            IntermediateMove(
                tile_pattern_to_tile(act_pat.tile_pat.tile_index,
                                     act_pat.tile_pat.rotation_angle),
                act_pat.player,
            ))
        if r.is_error():
            raise Exception(r.error())
        if logging_observer.entered_loop:
            print_and_exit("infinite", board)

        seen_pos_port: Set[Tuple[BoardPosition, PortID]] = set()
        for (pos, port) in board.live_players.values():
            if (pos, port) in seen_pos_port:
                print_and_exit("collision", board)
            seen_pos_port.add((pos, port))

        if "red" not in board.live_players:
            print_and_exit("red died", board)

    red_pos, red_port = board.live_players["red"]
    til = board.get_board_state().get_tile(red_pos)
    if til is None:
        raise Exception(
            "There is no tile at red's position - this should never happen")
    tile_pat = TilePat(tile_to_index(til), tile_to_rotation_angle(til))
    print_and_exit(
        [
            tile_pat.to_json(),
            "red",
            port_id_to_network_port_id(red_port),
            red_pos.x,
            red_pos.y,
        ],
        board,
    )
Пример #16
0
def test_is_move_suicidal_loop() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightBottom))
    )
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 0))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 1))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(3, 1))
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(4), "red"))
    assert r.is_ok()
    assert r.value()
Пример #17
0
def test_validate_place_tile_forced_loop_or_suicide() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightBottom))
    )
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 0))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 1))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(3, 1))
    r = rc.move_creates_loop(bs, IntermediateMove(index_to_tile(4), "red"))
    assert r.is_ok()
    assert r.value()
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(7), "red"))
    assert r.is_ok()
    assert r.value()

    r2 = rc.validate_move(
        bs,
        [index_to_tile(7), index_to_tile(4)],
        IntermediateMove(index_to_tile(4), "red"),
    )
    assert r2.is_error()
    assert (
        r2.error()
        == "player chose a loopy move when this does not create a loop: Tile(idx=7, edges=[(0, 3), (1, 6), (2, 5), (4, 7)])"
    )

    r3 = rc.validate_move(
        bs,
        [index_to_tile(7), index_to_tile(4)],
        IntermediateMove(index_to_tile(7), "red"),
    )
    assert r3.is_error()
    assert (
        r3.error()
        == "player chose a suicidal move when this does not cause a suicide: Tile(idx=4, edges=[(0, 7), (1, 2), (3, 4), (5, 6)])"
    )
Пример #18
0
def test_wrong_number_tile_choices() -> None:
    rc = RuleChecker()
    bs = BoardState()

    r = rc.validate_initial_move(
        bs,
        [index_to_tile(1)],
        InitialMove(BoardPosition(0, 2), index_to_tile(1), Port.BottomRight, "red"),
    )
    assert r.is_error()
    assert r.error() == "cannot validate move with 1 tile choices (expected 3)"
    r = rc.validate_initial_move(
        bs,
        [index_to_tile(1), index_to_tile(2), index_to_tile(3), index_to_tile(4)],
        InitialMove(BoardPosition(0, 2), index_to_tile(1), Port.BottomRight, "red"),
    )
    assert r.is_error()
    assert r.error() == "cannot validate move with 4 tile choices (expected 3)"

    r = rc.validate_move(
        bs, [index_to_tile(1)], IntermediateMove(index_to_tile(1), "red")
    )
    assert r.is_error()
    assert (
        r.error()
        == "cannot validate move with 1 tile choices (expected 2)"
    )
    r = rc.validate_move(
        bs,
        [index_to_tile(1), index_to_tile(3), index_to_tile(2)],
        IntermediateMove(index_to_tile(1), "red"),
    )
    assert r.is_error()
    assert (
        r.error()
        == "cannot validate move with 3 tile choices (expected 2)"
    )
Пример #19
0
def test_validate_place_tile_unnecessary_suicide() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightTop))
    )
    bs = bs.with_tile(index_to_tile(2), BoardPosition(2, 0))
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(4), "red"))
    assert r.is_ok()
    assert r.value()
    r = rc.is_move_suicidal(bs, IntermediateMove(index_to_tile(3), "red"))
    assert r.is_ok()
    assert not r.value()
    r2 = rc.validate_move(
        bs,
        [index_to_tile(3), index_to_tile(4)],
        IntermediateMove(index_to_tile(4), "red"),
    )
    assert r2.is_error()
    assert (
        r2.error()
        == "player chose a suicidal move when this does not cause a suicide: Tile(idx=3, edges=[(0, 4), (1, 3), (2, 6), (5, 7)])"
    )
Пример #20
0
def test_validate_place_tile_not_in_choices() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.TopRight))
    )
    r = rc.validate_move(
        bs,
        [index_to_tile(2), index_to_tile(3)],
        IntermediateMove(index_to_tile(1), "red"),
    )
    assert r.is_error()
    assert (
        r.error()
        == "tile Tile(idx=1, edges=[(0, 4), (1, 5), (2, 6), (3, 7)]) is not in the list of tiles [Tile(idx=2, edges=[(0, 5), (1, 4), (2, 7), (3, 6)]), Tile(idx=3, edges=[(0, 4), (1, 3), (2, 6), (5, 7)])] the player was given"
    )
Пример #21
0
    def generate_move(self, tiles: List[Tile], board_state: BoardState) -> Result[Tile]:
        """
        Try all tiles in all rotations clockwise starting with the second tile then the first, returning the first legal tile.
        If no tiles orientations are valid, return the second tile without rotation.

        :param tiles:           The list of 2 tile options
        :param board_state:     The state of the current board
        :return:                A result containing the tile that will be placed for the given player
        """
        for tile in reversed(tiles):
            for rot_tile in tile.all_rotations():
                r_illegal = self.rule_checker.is_move_illegal(
                    board_state, IntermediateMove(rot_tile, self.color)
                )
                if not r_illegal.is_error() and not r_illegal.value():
                    return ok(rot_tile)

        return ok(tiles[1])
Пример #22
0
def test_player_calls_observers() -> None:
    bs = BoardState()

    lo = LoggingPlayerObserver()
    p = Player(FirstS())
    p.add_observer(lo)

    p.set_color("red")
    assert lo.set_colors == ["red"]
    p.set_color("green")
    assert lo.set_colors == ["red", "green"]

    assert p.generate_first_move(
        [index_to_tile(3),
         index_to_tile(4),
         index_to_tile(5)], bs).is_ok()
    assert lo.initial_move_offereds == [
        ([index_to_tile(3),
          index_to_tile(4),
          index_to_tile(5)], bs)
    ]
    assert lo.initial_move_playeds == [(
        [index_to_tile(3),
         index_to_tile(4),
         index_to_tile(5)],
        bs,
        InitialMove(BoardPosition(1, 0), index_to_tile(5), Port.RightTop,
                    "green"),
    )]

    assert p.generate_move([index_to_tile(10), index_to_tile(11)], bs).is_ok()
    assert lo.intermediate_move_offereds == [
        ([index_to_tile(10), index_to_tile(11)], bs)
    ]
    assert lo.intermediate_move_playeds == [(
        [index_to_tile(10), index_to_tile(11)],
        bs,
        IntermediateMove(index_to_tile(10), "green"),
    )]

    gr = ([{"red"}, {"black", "green"}], {"white"})
    p.game_result(gr)  # type: ignore
    assert lo.game_results == [gr]
Пример #23
0
def test_both_moves_illegal() -> None:
    rc = RuleChecker()
    bs = BoardState()

    bs = bs.with_tile(index_to_tile(4), BoardPosition(2, 1))
    bs = bs.with_tile(index_to_tile(4), BoardPosition(3, 1))
    bs = bs.with_tile(index_to_tile(24), BoardPosition(2, 0))
    bs = bs.with_tile(index_to_tile(22), BoardPosition(4, 0))
    bs = bs.with_live_players(
        bs.live_players.set("red", (BoardPosition(2, 0), Port.RightBottom)).set(
            "white", (BoardPosition(4, 0), Port.LeftBottom)
        )
    )
    move = IntermediateMove(index_to_tile(24), "white")
    r = rc.validate_move(bs, [index_to_tile(24), index_to_tile(24)], move)
    assert r.is_error()
    assert (
        r.error()
        == "player chose a loopy move when this does not create a loop: Tile(idx=24, edges=[(0, 7), (1, 2), (3, 6), (4, 5)])"
    )
Пример #24
0
def test_ignore_invalid_moves(*_: Any) -> None:
    # Test that giving invalid moves to the observer doesn't crash anything. Mocks out subprocess
    # so that google-chrome isn't opened. Mocks out start_websocket_distributor so that it doesn't
    # start another process.
    b = Board()

    gpo = GraphicalPlayerObserver()
    gpo.set_color("red")
    gpo.set_players(["red", "blue", "green", "black", "white"])

    tile_choices = [index_to_tile(2), index_to_tile(3)]
    gpo.initial_move_played(
        tile_choices,
        b.get_board_state(),
        InitialMove(BoardPosition(2, 3), index_to_tile(2), Port.TopLeft,
                    "red"),
    )

    tile_choices = [index_to_tile(2), index_to_tile(3), index_to_tile(4)]
    gpo.intermediate_move_played(tile_choices, b.get_board_state(),
                                 IntermediateMove(index_to_tile(2), "red"))
Пример #25
0
    def generate_move(self, tiles: List[Tile],
                      board_state: BoardState) -> Result[Tile]:
        """
        Generate a move by choosing from the given list of tiles and returning the move. `set_color`,
        `set_rule_checker`, and `generate_first_move` will be called prior to this method.

        :param tiles:           The set of tile options for the first move
        :param board_state:     The state of the current board
        :return:                A result containing the tile that will be placed for the given player
        """
        for observer in self.observers:
            observer.intermediate_move_offered(tiles, board_state)

        r_move = self.strategy.generate_move(tiles, board_state)
        if r_move.is_error():
            return r_move

        for observer in self.observers:
            observer.intermediate_move_played(
                tiles, board_state, IntermediateMove(r_move.value(),
                                                     self.color))
        return r_move
Пример #26
0
    def generate_move(self, tiles: List[Tile],
                      board_state: BoardState) -> Result[Tile]:
        """
        Try all tiles in all rotations clockwise, returning the first legal tile.
        If no tiles are valid, return the first tile without rotation.

        :param tiles:           The list of tile options
        :param board_state:     The state of the current board
        :return:                A result containing the tile that will be placed for the given player
        """
        if len(tiles) != EXPECTED_TILE_COUNT_INTERMEDIATE_MOVE:
            return error(
                f"Strategy.generate_move given {len(tiles)} (expected {EXPECTED_TILE_COUNT_INTERMEDIATE_MOVE})"
            )

        for tile in tiles:
            for rot_tile in tile.all_rotations():
                r_illegal = self.rule_checker.is_move_illegal(
                    board_state, IntermediateMove(rot_tile, self.color))
                if not r_illegal.is_error() and not r_illegal.value():
                    return ok(rot_tile)

        return ok(tiles[0])
Пример #27
0
def test_create_board_from_moves() -> None:
    assert (Board.create_board_from_moves(
        [], []).assert_value().get_board_state() == BoardState())

    board_r = Board.create_board_from_moves(
        [
            InitialMove(BoardPosition(5, 0), index_to_tile(2),
                        Port.BottomRight, "blue"),
            InitialMove(BoardPosition(9, 2), index_to_tile(3), Port.TopLeft,
                        "white"),
            InitialMove(BoardPosition(9, 4), index_to_tile(4), Port.TopLeft,
                        "green"),
        ],
        [
            IntermediateMove(index_to_tile(5), "blue"),
            IntermediateMove(index_to_tile(6), "white"),
        ],
    )
    assert board_r.is_ok()
    board_state = board_r.value().get_board_state()
    for x in range(MIN_BOARD_COORDINATE, MAX_BOARD_COORDINATE + 1):
        for y in range(MIN_BOARD_COORDINATE, MAX_BOARD_COORDINATE + 1):
            if (x, y) == (5, 0):
                assert board_state.get_tile(BoardPosition(
                    x, y)) == index_to_tile(2)
            elif (x, y) == (5, 1):
                assert board_state.get_tile(BoardPosition(
                    x, y)) == index_to_tile(5)
            elif (x, y) == (9, 1):
                assert board_state.get_tile(BoardPosition(
                    x, y)) == index_to_tile(6)
            elif (x, y) == (9, 2):
                assert board_state.get_tile(BoardPosition(
                    x, y)) == index_to_tile(3)
            elif (x, y) == (9, 4):
                assert board_state.get_tile(BoardPosition(
                    x, y)) == index_to_tile(4)
            else:
                assert board_state.get_tile(BoardPosition(x, y)) is None
    assert board_r.value().live_players == pmap({
        "blue": (BoardPosition(x=5, y=1), 2),
        "green": (BoardPosition(x=9, y=4), 0),
        "white": (BoardPosition(x=9, y=1), 7),
    })

    board_r = Board.create_board_from_moves(
        [
            InitialMove(BoardPosition(5, 0), index_to_tile(2),
                        Port.BottomRight, "blue"),
            InitialMove(BoardPosition(9, 2), index_to_tile(3), Port.TopLeft,
                        "white"),
            InitialMove(BoardPosition(9, 4), index_to_tile(4), Port.TopLeft,
                        "green"),
        ],
        [
            IntermediateMove(index_to_tile(5), "blue"),
            IntermediateMove(index_to_tile(6), "white"),
            # An invalid second move since black never had an initial move
            IntermediateMove(index_to_tile(7), "black"),
        ],
    )
    assert board_r.is_error()
    assert (
        board_r.error() ==
        "failed to create board from moves: cannot place a tile for player black since they are not alive"
    )

    board_r = Board.create_board_from_moves(
        [
            InitialMove(BoardPosition(5, 0), index_to_tile(2),
                        Port.BottomRight, "blue"),
            InitialMove(BoardPosition(9, 2), index_to_tile(3), Port.TopLeft,
                        "white"),
            InitialMove(BoardPosition(9, 4), index_to_tile(4), Port.TopLeft,
                        "green"),
            # An invalid first move since someone placed a tile next to this position
            InitialMove(BoardPosition(9, 3), index_to_tile(7), Port.TopRight,
                        "black"),
        ],
        [
            IntermediateMove(index_to_tile(5), "blue"),
            IntermediateMove(index_to_tile(6), "white"),
        ],
    )
    assert board_r.is_error()
    assert (
        board_r.error() ==
        "failed to create board from set of initial placements: cannot make an initial move at position BoardPosition(x=9, y=3) since the surrounding tiles are not all empty"
    )
Пример #28
0
def test_multi_collision() -> None:
    b = Board()

    logging_observer = LoggingObserver()
    b.add_observer(logging_observer)

    r = b.initial_move(
        InitialMove(BoardPosition(0, 0), index_to_tile(4), Port.BottomRight,
                    "red"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(0, 0)) == index_to_tile(4)
    assert b.live_players["red"] == (BoardPosition(0, 0), Port.BottomRight)

    r = b.initial_move(
        InitialMove(BoardPosition(2, 0), index_to_tile(22), Port.LeftBottom,
                    "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(2, 0)) == index_to_tile(22)
    assert b.live_players["green"] == (BoardPosition(2, 0), Port.LeftBottom)

    r = b.initial_move(
        InitialMove(BoardPosition(4, 0), index_to_tile(22), Port.LeftBottom,
                    "blue"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(4, 0)) == index_to_tile(22)
    assert b.live_players["blue"] == (BoardPosition(4, 0), Port.LeftBottom)

    r = b.initial_move(
        InitialMove(BoardPosition(6, 0), index_to_tile(22), Port.LeftBottom,
                    "white"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(6, 0)) == index_to_tile(22)
    assert b.live_players["white"] == (BoardPosition(6, 0), Port.LeftBottom)

    r = b.initial_move(
        InitialMove(BoardPosition(8, 0), index_to_tile(22), Port.LeftBottom,
                    "black"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(8, 0)) == index_to_tile(22)
    assert b.live_players["black"] == (BoardPosition(8, 0), Port.LeftBottom)

    r = b.intermediate_move(IntermediateMove(index_to_tile(22), "black"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(7, 0)) == index_to_tile(22)

    r = b.intermediate_move(IntermediateMove(index_to_tile(22), "black"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(5, 0)) == index_to_tile(22)

    r = b.intermediate_move(IntermediateMove(index_to_tile(22), "black"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(3, 0)) == index_to_tile(22)

    r = b.intermediate_move(IntermediateMove(index_to_tile(22), "black"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 0)) == index_to_tile(22)

    assert len(b._board_state.live_players) == 5
    assert len(set(b._board_state.live_players.values())) == 1
    assert b.live_players == pmap({
        "black": (BoardPosition(x=0, y=0), 4),
        "blue": (BoardPosition(x=0, y=0), 4),
        "green": (BoardPosition(x=0, y=0), 4),
        "red": (BoardPosition(x=0, y=0), 4),
        "white": (BoardPosition(x=0, y=0), 4),
    })
Пример #29
0
def test_allow_collision() -> None:
    rc = RuleChecker()
    b = Board()

    move = InitialMove(BoardPosition(0, 0), index_to_tile(4), Port.BottomRight, "red")
    assert rc.validate_initial_move(
        b.get_board_state(),
        [index_to_tile(4), index_to_tile(5), index_to_tile(6)],
        move,
    ).is_ok()
    r = b.initial_move(move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(0, 0)) == index_to_tile(4)
    assert b.live_players["red"] == (BoardPosition(0, 0), Port.BottomRight)

    move = InitialMove(BoardPosition(2, 0), index_to_tile(22), Port.LeftBottom, "green")
    assert rc.validate_initial_move(
        b.get_board_state(),
        [index_to_tile(22), index_to_tile(5), index_to_tile(6)],
        move,
    ).is_ok()
    r = b.initial_move(move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(2, 0)) == index_to_tile(22)
    assert b.live_players["green"] == (BoardPosition(2, 0), Port.LeftBottom)

    move = InitialMove(BoardPosition(4, 0), index_to_tile(22), Port.LeftBottom, "blue")
    assert rc.validate_initial_move(
        b.get_board_state(),
        [index_to_tile(22), index_to_tile(5), index_to_tile(6)],
        move,
    ).is_ok()
    r = b.initial_move(move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(4, 0)) == index_to_tile(22)
    assert b.live_players["blue"] == (BoardPosition(4, 0), Port.LeftBottom)

    move = InitialMove(BoardPosition(6, 0), index_to_tile(22), Port.LeftBottom, "white")
    assert rc.validate_initial_move(
        b.get_board_state(),
        [index_to_tile(22), index_to_tile(5), index_to_tile(6)],
        move,
    ).is_ok()
    r = b.initial_move(move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(6, 0)) == index_to_tile(22)
    assert b.live_players["white"] == (BoardPosition(6, 0), Port.LeftBottom)

    move = InitialMove(BoardPosition(8, 0), index_to_tile(22), Port.LeftBottom, "black")
    assert rc.validate_initial_move(
        b.get_board_state(),
        [index_to_tile(22), index_to_tile(5), index_to_tile(6)],
        move,
    ).is_ok()
    r = b.initial_move(move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(8, 0)) == index_to_tile(22)
    assert b.live_players["black"] == (BoardPosition(8, 0), Port.LeftBottom)

    intermediate_move = IntermediateMove(index_to_tile(22), "black")
    assert rc.validate_move(
        b.get_board_state(), [index_to_tile(22), index_to_tile(5)], intermediate_move
    ).is_ok()
    r = b.intermediate_move(intermediate_move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(7, 0)) == index_to_tile(22)

    intermediate_move = IntermediateMove(index_to_tile(22), "black")
    assert rc.validate_move(
        b.get_board_state(), [index_to_tile(22), index_to_tile(5)], intermediate_move
    ).is_ok()
    r = b.intermediate_move(intermediate_move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(5, 0)) == index_to_tile(22)

    intermediate_move = IntermediateMove(index_to_tile(22), "black")
    assert rc.validate_move(
        b.get_board_state(), [index_to_tile(22), index_to_tile(5)], intermediate_move
    ).is_ok()
    r = b.intermediate_move(intermediate_move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(3, 0)) == index_to_tile(22)

    intermediate_move = IntermediateMove(index_to_tile(22), "black")
    assert rc.validate_move(
        b.get_board_state(), [index_to_tile(22), index_to_tile(5)], intermediate_move
    ).is_ok()
    r = b.intermediate_move(intermediate_move)
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 0)) == index_to_tile(22)

    assert len(b._board_state.live_players) == 5
    assert len(set(b._board_state.live_players.values())) == 1
    assert b.live_players == pmap(
        {
            "black": (BoardPosition(x=0, y=0), 4),
            "blue": (BoardPosition(x=0, y=0), 4),
            "green": (BoardPosition(x=0, y=0), 4),
            "red": (BoardPosition(x=0, y=0), 4),
            "white": (BoardPosition(x=0, y=0), 4),
        }
    )