### BUGGY CODE ###

from queue import Queue
from collections import namedtuple


def get_offset(x, y):
    return 16 * y + 4 * x


def get_candidate(pid):
    def check(pid, move):
        if abs(move) == 1:
            return pid // 4 == (pid + move) // 4
        else:
            return 0 <= pid + move < 16

    return tuple(pid + move for move in MOVE_CANDIDATE if check(pid, move))


def get_swap_values(orig):
    return {dest: (1 << OFFSET_MASTER[orig]) - (1 << OFFSET_MASTER[dest]) for dest in SWAP_CANDIDATE[orig]}


MOVE_CANDIDATE = (-4, -1, 1, 4)
SWAP_CANDIDATE = tuple(get_candidate(pid) for pid in range(16))
OFFSET_MASTER = tuple(get_offset(x, y) for y in range(4) for x in range(4))
SWAP_VALUE_MASTER = tuple(get_swap_values(pid) for pid in range(16))


def swap(board, orig, dest):
    dest_val = (board >> OFFSET_MASTER[dest]) & 0b1111
    return board + dest_val * SWAP_VALUE_MASTER[orig][dest]


def put_next_puzzle(is_reverse):
    global puzzles, cache
    puzzle = puzzles[is_reverse].get()
    board, id0, count, prev = puzzle.board, puzzle.id0, puzzle.count, puzzle.prev
    for new_id0 in SWAP_CANDIDATE[id0]:
        if new_id0 == prev:
            continue

        new_board = swap(board, id0, new_id0)
        if new_board in cache[is_reverse]:
            continue
        if new_board in cache[not is_reverse]:
            return cache[not is_reverse][new_board].count + count + 1
        new_puzzle = Puzzle(
                board=new_board,
                count=count + 1,
                id0=new_id0,
                prev=id0,
                trace=puzzle.trace * 16 + new_id0
        )
        cache[is_reverse][new_board] = new_puzzle
        puzzles[is_reverse].put(new_puzzle)
    return -1


CORRECT = 0x0fedcba987654321
Puzzle = namedtuple('Puzzle', 'board count id0 prev trace')
puzzles = (Queue(), Queue())
cache = ({}, {})
board = 0
id0 = -1

for j in range(4):
    for i, v in enumerate(map(int, input().strip().split())):
        board += v << get_offset(i, j)
        if v == 0:
            id0 = 4 * j + i

start = Puzzle(board=board, count=0, id0=id0, prev=-1, trace=0)
goal = Puzzle(board=CORRECT, count=0, id0=15, prev=-1, trace=0)
puzzles[0].put(start)
puzzles[1].put(goal)
cache[0][board] = start
cache[1][CORRECT] = goal
result = None
is_reverse = False

while True:
    result = put_next_puzzle(is_reverse)
    if result > -1:
        break
    is_reverse = not is_reverse

print(result)

### FIXED CODE ###

from heapq import heappop, heappush

manhattan = [[abs((i % 4) - (j % 4)) + abs((i // 4) - (j // 4)) for j in range(16)] for i in range(16)]
movables = [{1, 4}, {0, 2, 5}, {1, 3, 6}, {2, 7}, {0, 5, 8}, {1, 4, 6, 9}, {2, 5, 7, 10}, {3, 6, 11},
            {4, 9, 12}, {5, 8, 10, 13}, {6, 9, 11, 14}, {7, 10, 15}, {8, 13}, {9, 12, 14}, {10, 13, 15}, {11, 14}]
swap_mul = [[(1 << mf) - (1 << mt) for mt in range(0, 64, 4)] for mf in range(0, 64, 4)]
destination = 0xfedcba9876543210

i = 0
board_init = 0
blank_init = 0
for _ in range(4):
    for n in map(int, input().split()):
        if n:
            n -= 1
        else:
            n = 15
            blank_init = i
        board_init += n * 16 ** i
        i += 1

estimation_init = sum(manhattan[i][((board_init >> (4 * i)) & 15)] for i in range(16) if i != blank_init)

queue = [(estimation_init, board_init, blank_init)]
visited = set()

while True:
    estimation, board, blank = heappop(queue)
    if board in visited:
        continue
    elif board == destination:
        print(estimation)
        break
    visited.add(board)
    for new_blank in movables[blank]:
        num = (board >> (4 * new_blank)) & 15
        new_board = board + swap_mul[new_blank][blank] * (15 - num)
        if new_board in visited:
            continue
        new_estimation = estimation + 1 - manhattan[new_blank][num] + manhattan[blank][num]
        if new_estimation > 45:
            continue
        heappush(queue, (new_estimation, new_board, new_blank))