from copy import deepcopy class Number: def __init__(self, num: int, is_moved: bool) -> None: self.num = num self.is_moved = is_moved class Board: def __init__(self, numbers: list[list[Number]]) -> None: self.board = numbers def __str__(self) -> str: str_ = [] for row in self.board: str_.append(" ".join(map(lambda num_o: str(num_o.num), row))) return "\n".join(str_) def __eq__(self, __o: object) -> bool: for s_row, o_row in zip(self.board, __o.board): for s_num, o_num in zip(s_row, o_row): if s_num.num != o_num.num: return False return True def __ne__(self, __o: object) -> bool: return not self.__eq__(__o) def where_zero(self) -> tuple[int, int]: for row_idx, row in enumerate(self.board): try: return (row_idx, list(map(lambda num: num.num, row)).index(0)) except ValueError: continue raise ValueError def move_up(self): zero_idx = self.where_zero() if zero_idx[0] == 3: return if self.board[zero_idx[0]+1][zero_idx[1]].is_moved: return new_board = deepcopy(self) new_board.board[zero_idx[0]][zero_idx[1]] = Number( self.board[zero_idx[0]+1][zero_idx[1]].num, True) new_board.board[zero_idx[0]+1][zero_idx[1] ] = Number(self.board[zero_idx[0]][zero_idx[1]].num, False) return new_board def move_down(self): zero_idx = self.where_zero() if zero_idx[0] == 0: return if self.board[zero_idx[0]-1][zero_idx[1]].is_moved: return new_board = deepcopy(self) new_board.board[zero_idx[0]][zero_idx[1]] = Number( self.board[zero_idx[0]-1][zero_idx[1]].num, True) new_board.board[zero_idx[0]-1][zero_idx[1] ] = Number(self.board[zero_idx[0]][zero_idx[1]].num, False) return new_board def move_right(self): zero_idx = self.where_zero() if zero_idx[1] == 0: return if self.board[zero_idx[0]][zero_idx[1]-1].is_moved: return new_board = deepcopy(self) new_board.board[zero_idx[0]][zero_idx[1]] = Number( self.board[zero_idx[0]][zero_idx[1]-1].num, True) new_board.board[zero_idx[0]][zero_idx[1]-1 ] = Number(self.board[zero_idx[0]][zero_idx[1]].num, False) return new_board def move_left(self): zero_idx = self.where_zero() if zero_idx[1] == 3: return if self.board[zero_idx[0]][zero_idx[1]+1].is_moved: return new_board = deepcopy(self) new_board.board[zero_idx[0]][zero_idx[1]] = Number( self.board[zero_idx[0]][zero_idx[1]+1].num, True) new_board.board[zero_idx[0]][zero_idx[1]+1 ] = Number(self.board[zero_idx[0]][zero_idx[1]].num, False) return new_board def move_4dir(self): candidate = [self.move_down(), self.move_left(), self.move_right(), self.move_up()] return list(filter(lambda elm: elm is not None, candidate)) def main(): target_board_nums = [list(map(int, input().split())) for _ in range(4)] target_board = Board([list(map(lambda num: Number( num, is_moved=False), target_board_num)) for target_board_num in target_board_nums]) init_board_nums = [list(range(init, init+4)) for init in range(1, 16, 4)] init_board_nums[3][3] = 0 init_board = Board([list(map(lambda num: Number( num, is_moved=False), init_board_num)) for init_board_num in init_board_nums]) stack = [init_board] while stack: current_board = stack.pop() if current_board == target_board: print("Yes") return stack.extend(current_board.move_4dir()) print("No") if __name__ == "__main__": main()