def test_execute_action() -> None: board = init_board(4) new_board = execute_action(board, Disk.LIGHT, Position(1, 3)) config = np.array( [ [0, 0, 0, 0], [0, -1, -1, -1], [0, 1, -1, 0], [0, 0, 0, 0], ] ) assert new_board == Board(config) config = np.array( [ [0, -1, -1, 1], [-1, -1, -1, 1], [1, -1, -1, 1], [0, -1, -1, 1], ], dtype=np.int8, ) board = Board(config) new_board = execute_action(board, Disk.DARK, Position(0, 0)) after_config = np.array( [ [1, 1, 1, 1], [1, 1, -1, 1], [1, -1, 1, 1], [0, -1, -1, 1], ], dtype=np.int8, ) assert new_board == Board(after_config)
def execute_action(board: Board, disk: Disk, position: Position) -> Board: """execute action and return new state board positionは必ずlegalなものを使うこと.この関数ではlegalかのチェックはしない Args: board (Board): 盤 disk (Disk): 石の色 position (Position): 石を置く場所 Returns: Board: 石が置かれた新しい状態の盤 """ flip_position_list = [position] for direction in _DIRECTIONS: adjacent_position = position + direction # 隣の位置がマスをはみ出すか隣の位置のマスの色がdiskの逆でないないなら if (not board.is_in_range(adjacent_position) or board[adjacent_position] != disk.reverse()): continue legal, end_position = _increment_search(board, disk, adjacent_position, direction) while legal and adjacent_position != end_position: flip_position_list.append(adjacent_position) adjacent_position = adjacent_position + direction # flip_position_listが1なら一枚もひっくり返らないのでlegal actionではない # 関数呼び出し側がちゃんとlegal actionとなるように注意する assert len(flip_position_list) > 1 config: npt.NDArray[np.int8] = board.config.copy() for flipped_position in flip_position_list: config[flipped_position] = disk return Board(config)
def test_execute_action() -> None: game = Game.init_game(4) game.execute_action(Position(0, 1)) config: npt.NDArray[np.int8] = np.array( [ [0, 1, 0, 0], [0, 1, 1, 0], [0, 1, -1, 0], [0, 0, 0, 0], ], dtype=np.int8, ) assert game.board == Board(config) assert game.current_disk == Disk.LIGHT assert game.get_legal_actions() == frozenset( [Position(0, 0), Position(0, 2), Position(2, 0)]) assert game.is_game_over() is False config = np.array( [ [1, -1, -1, 1], [0, -1, -1, 1], [1, -1, -1, 1], [1, -1, 1, 1], ], dtype=np.int8, ) game = Game(Board(config), Disk.LIGHT) assert game.get_legal_actions() == frozenset() with pytest.raises(IllegalActionError): game.execute_action(Position(0, 0)) with pytest.raises(IllegalActionError): game.execute_action(Position(1, 0)) game.execute_action(None) assert game.current_disk == Disk.DARK assert game.board == Board(config) with pytest.raises(IllegalActionError): game.execute_action(None) game.execute_action(Position(1, 0)) after_config = np.array( [ [1, -1, -1, 1], [1, 1, 1, 1], [1, 1, -1, 1], [1, -1, 1, 1], ], dtype=np.int8, ) assert game.board == Board(after_config) assert game.get_legal_actions() == frozenset() assert game.is_game_over() is True
def _increment_search(board: Board, disk: Disk, position: Position, direction: Direction) -> Tuple[bool, Optional[Position]]: """diskと同じ色の位置を探す positionからdirectionの方向に進み,空マスに出会わず,diskと同じ色のマスに到達できるか. 到達できたら,その位置も返す Args: board (Board): 盤の状態 disk (Disk): 探す色 position (Position): 調べるマス direction (Direction): 探す方向 Returns: Tuple[bool, Optional[Position]]: diskのマスに到達できたかの真偽値と到達した位置 """ if not board.is_in_range(position): return False, None if board[position] == Square.NULL: return False, None if board[position] == disk: return True, position return _increment_search( board, disk, position + direction, direction, )
def test_obtain_legal_actions() -> None: board = init_board(4) assert obtain_legal_actions(board, Disk.LIGHT) == frozenset( [Position(0, 2), Position(1, 3), Position(2, 0), Position(3, 1)] ) board = Board(np.zeros((4, 4), dtype=np.int8)) assert obtain_legal_actions(board, Disk.LIGHT) == frozenset()
def test_increment_search() -> None: config = np.array( [ [1, -1, -1, 0], [0, 1, -1, 0], [0, 0, 0, 0], [0, 1, -1, 0], ] ) board = Board(config) assert _increment_search(board, Disk.DARK, Position(3, 1), Direction(0, -1)) == ( True, Position(3, 1), ) assert _increment_search(board, Disk.DARK, Position(3, 2), Direction(0, -1)) == ( True, Position(3, 1), ) assert _increment_search(board, Disk.DARK, Position(3, 3), Direction(0, -1)) == ( False, None, ) assert _increment_search(board, Disk.DARK, Position(0, 1), Direction(0, -1)) == ( True, Position(0, 0), ) assert _increment_search(board, Disk.DARK, Position(0, 2), Direction(0, -1)) == ( True, Position(0, 0), ) assert _increment_search(board, Disk.DARK, Position(0, 3), Direction(0, -1)) == ( False, None, )
def test_init_board() -> None: config = np.array( [ [0, 0, 0, 0], [0, -1, 1, 0], [0, 1, -1, 0], [0, 0, 0, 0], ], np.int8, ) assert Board(config) == init_board(4)
def test_greedy_player() -> None: config = np.array( [ [Square.NULL, Square.LIGHT, Square.DARK], [Square.NULL, Square.LIGHT, Square.DARK], [Square.NULL, Square.DARK, Square.DARK], ], dtype=np.int8, ) game = Game(Board(config), Disk.DARK) assert GreedyPlayer().play(game) == Position(0, 0) game.execute_action(Position(0, 0)) assert GreedyPlayer().play(game) is None
def init_board(length: int) -> Board: """initialize board Args: length (int): length of board Returns: Board: [description] """ config: npt.NDArray[np.int8] = np.zeros((length, length), dtype=np.int8) config[length // 2][length // 2] = Square.LIGHT config[length // 2 - 1][length // 2 - 1] = Square.LIGHT config[length // 2][length // 2 - 1] = Square.DARK config[length // 2 - 1][length // 2] = Square.DARK return Board(config)
def test_board() -> None: config = array( [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, -1, 0, 0, 0], [0, 0, 0, -1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], ] ) config_str = ( "--------\n" "--------\n" "--------\n" "---xo---\n" "---ox---\n" "--------\n" "--------\n" "--------" ) board = Board(config) assert Board(np.zeros((4, 4), dtype=np.int8)) == Board( np.zeros((4, 4), dtype=np.int8) ) assert Board(np.zeros((4, 4), dtype=np.int8)) != Board( np.ones((4, 4), dtype=np.int8) ) assert str(board) == config_str # pylint: disable=eval-used assert board == eval(repr(board)) assert board[Position(0, 0)] == Square.NULL # config cannot be replaced. with pytest.raises(AttributeError): board.config = config # type: ignore with pytest.raises(ValueError): board.config[0][0] = Square.DARK # board cannot be updated with pytest.raises(TypeError): # pylint: disable=unsupported-assignment-operation board[Position(0, 0)] = Square.DARK # type: ignore with pytest.raises(IndexError): # pylint: disable=expression-not-assigned board[Position(-1, -1)]
def _is_legal_action(board: Board, disk: Disk, position: Position) -> bool: """legal actionか Args: board (Board): 盤の状態 disk (Disk): 置きたい石 position (Position): 置きたい位置 Returns: bool: True if legal, False if illegal """ if board[position] != Square.NULL: return False # 周囲8マスにdiskの逆がなければfalse # 周囲8マスにdiskの逆がある場合,その方向に進んで,NULLにならずにdiskがあればTrue for direction in _DIRECTIONS: adjacent_position = position + direction if (board.is_in_range(adjacent_position) and board[adjacent_position] == disk.reverse() and _increment_search(board, disk, adjacent_position, direction)[0]): return True return False
def test_game_over() -> None: board = Board(np.zeros((8, 8), dtype=np.int8)) game = Game(board, Disk.LIGHT) assert game.is_game_over() is True