Exemplo n.º 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"]
Exemplo n.º 2
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)
Exemplo n.º 3
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",
    ]
Exemplo n.º 4
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,
    )
Exemplo n.º 5
0
    def intermediate_move(self, move: IntermediateMove) -> Result[None]:
        """
        Place the given tile at the given position on this board.

        :param move:        The intermediate move to be placed
        :return:            A result containing an error if the move is invalid or a result containing the value None
                            if there was no error and the tile was placed successfully
        """
        validate_result = self.validate_intermediate_move(move)
        if validate_result.is_error():
            return validate_result

        pos_r = self._board_state.calculate_adjacent_position_of_player(move.player)

        if pos_r.is_error():
            return error(
                "cannot place tile for player %s: %s" % (move.player, pos_r.error())
            )
        pos = pos_r.value()

        if pos is None:
            return error(
                f"cannot place tile for player {move.player} since it would go off the edge of the board (this should "
                f"never happen!)"
            )

        if self._board_state.get_tile(pos) is not None:
            return error(
                f"cannot place for player {move.player} since it would be on top of an existing tile (this should"
                f"never happen!)"
            )

        # Assignment 6 states that if a move causes a loop it is legal (and the board must support it)
        # but that the expected behavior is to remove the players on the loop, not the place tile,
        # and accept the move.
        orig_board_state = deepcopy(self._board_state)
        temp_logging_observer = LoggingObserver()
        self.add_observer(temp_logging_observer)
        self._board_state = self._board_state.with_tile(move.tile, pos)
        r = self._move_all_players_along_paths()
        if r.is_error():
            return r
        if temp_logging_observer.entered_loop:
            # Placing this tile caused someone to enter a loop. So we undo any changes, delete the
            # people that were in the loop, and return ok
            self._board_state = orig_board_state
            for player in temp_logging_observer.entered_loop:
                self.remove_player(player)
        self.remove_observer(temp_logging_observer)
        return ok(None)
Exemplo n.º 6
0
    def move_creates_loop(self, board_state: BoardState,
                          move: IntermediateMove) -> Result[bool]:
        """
        Returns whether or not the given move creates a loop for anyone on the board. Note that this returns False if a
        loop is created that no player is on.

        :param board_state:     The board state to apply the move to
        :param move:            The intermediate move being applied
        :return:                A Result containing whether or not the move creates a loop for anyone on the baord
        """
        logging_observer = LoggingObserver()
        board = Board(deepcopy(board_state))
        board.add_observer(logging_observer)
        r = board.intermediate_move(move)
        if r.is_error():
            return error(r.error())
        return ok(len(logging_observer.entered_loop) > 0)
Exemplo n.º 7
0
    def is_move_suicidal(self, board_state: BoardState,
                         move: IntermediateMove) -> Result[bool]:
        """
        Returns whether the given intermediate move is suicidal. A suicidal move is a move that
        results in the player who placed the move leaving the board but NOT if a loop is caused.

        :param board_state:     The board state the move is being applied to
        :param move:            The intermediate move to check for suicideness
        :return:                A result containing a boolean or an error. If it contains a value
                                the boolean specifies whether or not the move is suicidal.
        """
        logging_observer = LoggingObserver()
        if move.player not in board_state.live_players:
            return error(
                f"player {move.player} is not alive thus the move cannot be suicidal"
            )
        board = Board(deepcopy(board_state))
        board.add_observer(logging_observer)
        r = board.intermediate_move(move)
        if r.is_error():
            return error(r.error())
        return ok(move.player not in board.live_players
                  and len(logging_observer.entered_loop) <= 0)
Exemplo n.º 8
0
def test_logging_observer() -> None:
    lo = LoggingObserver()
    assert lo.entered_loop == []
    assert lo.exited_board == []
    lo.player_entered_loop("red")
    assert lo.entered_loop == ["red"]
    assert lo.exited_board == []
    lo.player_entered_loop("green")
    assert lo.entered_loop == ["red", "green"]
    assert lo.exited_board == []
    lo.player_entered_loop("red")
    assert lo.entered_loop == ["red", "green", "red"]
    assert lo.exited_board == []

    lo.player_exited_board("white")
    assert lo.entered_loop == ["red", "green", "red"]
    assert lo.exited_board == ["white"]
    lo.player_exited_board("black")
    assert lo.entered_loop == ["red", "green", "red"]
    assert lo.exited_board == ["white", "black"]

    assert lo.all_messages() == [
        "entered_loop: red",
        "entered_loop: green",
        "entered_loop: red",
        "exited_board: white",
        "exited_board: black",
    ]
Exemplo n.º 9
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),
    })
Exemplo n.º 10
0
def test_board_initial_move() -> None:
    b = Board()

    logging_observer = LoggingObserver()
    b.add_observer(logging_observer)

    pos = BoardPosition(1, 1)
    r = b.initial_move(
        InitialMove(pos,
                    make_tiles()[0], Port.BottomRight, "red"))
    assert r.is_error()
    assert (
        r.error() ==
        f"cannot make an initial move at position {pos} since it is not on the edge"
    )

    pos = BoardPosition(3, 0)
    port = Port.TopLeft
    r = b.initial_move(InitialMove(pos, make_tiles()[0], port, "red"))
    assert r.is_error()
    assert (
        r.error() ==
        f"cannot make an initial move at position {pos}, port {port} since it does not face the interior of the "
        f"board")

    b.place_tile_at_index_with_scissors(make_tiles()[0], BoardPosition(2, 0))
    r = b.initial_move(
        InitialMove(BoardPosition(2, 0),
                    make_tiles()[1], Port.BottomRight, "blue"))
    assert r.is_error()
    assert (
        r.error() ==
        "cannot place tile at position BoardPosition(x=2, y=0) since there is already a tile at that position"
    )

    r = b.initial_move(
        InitialMove(BoardPosition(3, 0),
                    make_tiles()[1], Port.BottomRight, "blue"))
    assert r.is_error()
    assert (
        r.error() ==
        "cannot make an initial move at position BoardPosition(x=3, y=0) since the surrounding tiles are not all "
        "empty")

    r = b.initial_move(
        InitialMove(BoardPosition(1, 0),
                    make_tiles()[1], Port.BottomRight, "blue"))
    assert r.is_error()
    assert (
        r.error() ==
        "cannot make an initial move at position BoardPosition(x=1, y=0) since the surrounding tiles are not all "
        "empty")

    b._board_state = b._board_state.with_live_players(
        b._board_state.live_players.set(
            "red", (BoardPosition(2, 0), Port.BottomRight)))
    r = b.initial_move(
        InitialMove(BoardPosition(5, 0),
                    make_tiles()[1], Port.BottomRight, "red"))
    assert r.is_error()
    assert (r.error() ==
            "cannot place player red since the player is already on the board")

    r = b.initial_move(
        InitialMove(BoardPosition(5, 0),
                    make_tiles()[2], Port.BottomRight, "blue"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(5, 0)) == make_tiles()[2]
    assert b._board_state.live_players["blue"] == (
        BoardPosition(5, 0),
        Port.BottomRight,
    )

    r = b.initial_move(
        InitialMove(BoardPosition(9, 2),
                    make_tiles()[2], Port.TopLeft, "white"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(9, 2)) == make_tiles()[2]
    b._board_state = b._board_state.with_live_players(
        b._board_state.live_players.set("white",
                                        (BoardPosition(9, 2), Port.TopLeft)))

    r = b.initial_move(
        InitialMove(BoardPosition(9, 4),
                    make_tiles()[2], Port.TopLeft, "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(9, 4)) == make_tiles()[2]
    assert b._board_state.live_players["green"] == (BoardPosition(9, 4),
                                                    Port.TopLeft)

    assert logging_observer.all_messages() == []
Exemplo n.º 11
0
def test_board_intermediate_move() -> 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(0), Port.RightBottom,
                    "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(0, 1)) == index_to_tile(0)

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

    # Add a tile for green
    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,
    )
    assert b._board_state.live_players["red"] == (BoardPosition(0, 3),
                                                  Port.RightBottom)

    # Add another tile for green to get closer to red
    r = b.intermediate_move(
        IntermediateMove(index_to_tile(22).rotate(), "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 2)) == index_to_tile(22)
    assert b._board_state.live_players["green"] == (
        BoardPosition(1, 2),
        Port.BottomLeft,
    )
    assert b._board_state.live_players["red"] == (BoardPosition(0, 3),
                                                  Port.RightBottom)

    # And another tile that will cause green and red to both traverse over the same tile
    r = b.intermediate_move(IntermediateMove(index_to_tile(2), "green"))
    assert r.is_ok()
    assert b._board_state.get_tile(BoardPosition(1, 3)) == index_to_tile(2)
    assert b._board_state.live_players["green"] == (
        BoardPosition(1, 3),
        Port.BottomLeft,
    )
    assert b._board_state.live_players["red"] == (BoardPosition(1, 3),
                                                  Port.RightBottom)

    assert logging_observer.all_messages() == []