Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

15 Puzzle

The goal of the 15 puzzle problem is to complete pieces on $4 \times 4$ cells where one of the cells is empty space.

In this problem, the space is represented by 0 and pieces are represented by integers from 1 to 15 as shown below.

1 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15

You can move a piece toward the empty space at one step. Your goal is to make the pieces the following configuration in the shortest move (fewest steps).

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 0

Write a program which reads an initial state of the puzzle and prints the fewest steps to solve the puzzle.

Input

The $4 \times 4$ integers denoting the pieces or space are given.

Output

Print the fewest steps in a line.

Constraints

The given puzzle is solvable in at most 45 steps.

Sample Input

1 2 3 4
6 7 8 0
5 10 11 12
9 13 14 15

Sample Output

8

Solution in Go (judge verdict: Accepted)

package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
)

var up=[2]int{-1,0}
var down=[2]int{1,0}
var left=[2]int{0,-1}
var right=[2]int{0,1}

const size int =4

const UP int =0
const DOWN int =2
const LEFT int =1
const RIGHT int =3

var targetPoints [size*size][2]int

var movesHistory [100000]int
var ans=0
var tState =[][]int {//aim state
{1 ,2 ,3 ,4 } ,
{5 ,6 ,7 ,8 } ,
{9 ,10,11,12} ,
{13,14,15,0 }}
var STEP_SUM=0
var sState [][]int  //Init state
var blank_row,blank_column int
func main(){
	var in = bufio.NewReader(os.Stdin)
	var out = bufio.NewWriter(os.Stdout)
	sState=make([][]int,size)
	for i,_ := range sState {
		sState[i]=make([]int,size)
	}
	for i := 0; i < size; i++ {
		fmt.Fscanf(in, "%d %d %d %d\n", &sState[i][0], &sState[i][1],&sState[i][2],&sState[i][3])
	}
	getPoint()
	j:=getHeuristic(sState)
	i:=-1
	for ans = j; ; ans++ {
		if solve(sState,blank_row,blank_column,0,i,j) {
			break
		}
	}
	for k:=0;k<ans;k++ {
		move(sState, movesHistory[k])
	}
	fmt.Fprintln(out,STEP_SUM)
	out.Flush()
}
func getPoint(){
	for i:=0;i<size;i++ {
		for j:=0;j<size;j++ {
			if sState[i][j] == 0 {
				blank_row = i;
				blank_column = j;
				break;
			}
		}
	}
	for i:=0;i<size;i++ { //get aim coordinate
		for j:=0;j<size;j++ {
			targetPoints[tState[i][j]][0] = i; //y-coordinate
			targetPoints[tState[i][j]][1] = j; //x-coordinate
		}
	}
}
func getHeuristic(state [][]int) int {
	heuristic := 0;
	for i:=0;i<len(state);i++ {
			for j:=0;j<len(state[i]);j++ {
				if state[i][j] != 0 {
					heuristic = heuristic +	int(math.Abs(float64(targetPoints[state[i][j]][0] - i))+math.Abs(float64(targetPoints[state[i][j]][1] - j)))
				}
			}
	}
	return heuristic;
}

func solve(state [][]int, blank_row, blank_column, dep, d, h int) bool{
	//compare with aim matrix, check same or not, if same mean it's solved.
	isSolved := true
	for i:=0;i<size;i++ {
		for j:=0;j<size;j++ {
			if state[i][j] != tState[i][j] {
				isSolved = false
			}
		}
	}
	if isSolved {
		return true
	}
	if dep == ans {
		return false
	}
	//For express "blank" moved coordinate location
	blank_row1:= blank_row
	blank_column1:= blank_column
	state2 := make([][]int,size)
	for i,_ := range state2 {
		state2[i]=make([]int,size)
	}
	for direction:=0;direction<4;direction++ {
		for i:=0;i<len(state);i++ {
			for j:=0;j<len(state);j++ {
				state2[i][j] = state[i][j]
			}
		}
		//if backtrace same with last time. end this situation.
		if direction != d && (d%2 == direction%2) {
			continue
		}

		if direction == UP {
			blank_row1 = blank_row + up[0]
			blank_column1 = blank_column + up[1]
		} else if direction == DOWN {
			blank_row1 = blank_row + down[0]
			blank_column1 = blank_column + down[1]
		} else if direction == LEFT {
			blank_row1 = blank_row + left[0]
			blank_column1 = blank_column + left[1]
		} else {
			blank_row1 = blank_row + right[0]
			blank_column1 = blank_column + right[1]
		}

		//check the edge
		if blank_column1 < 0 || blank_column1 == size|| blank_row1 < 0 || blank_row1 == size {
			continue
		}

		//swap blank and right now moved locate coordinate
		state2[blank_row][blank_column] = state2[blank_row1][blank_column1]
		state2[blank_row1][blank_column1] = 0
		var h1 int
		//check the blank whether close the aim coordinate
		if direction == DOWN && blank_row1> targetPoints[state[blank_row1][blank_column1]][0] {
			h1 = h - 1
		} else if direction == UP && blank_row1< targetPoints[state[blank_row1][blank_column1]][0] {
			h1 = h - 1
		} else if direction == RIGHT && blank_column1> targetPoints[state[blank_row1][blank_column1]][1] {
			h1 = h - 1
		} else if direction == LEFT && blank_column1< targetPoints[state[blank_row1][blank_column1]][1] {
			h1 = h - 1
		} else {
			//bad situation make the step bigger.
			h1 = h + 1
		}
		if h1+dep+1>ans { //pruning
			continue
		}
		movesHistory[dep] = direction

		if solve(state2, blank_row1, blank_column1, dep+1, direction, h1) {
			return true
		}
		}
	return false
}
func move(state [][]int,direction int)  {
	row := 0
	column := 0
	for i:=0;i<len(state);i++ {
		for j:=0;j<len(state);j++ {
			if state[i][j] == 0 {
				row = i
				column = j
			}
		}
	}
	switch direction {
		case UP:
			state[row][column] = state[row-1][column]
			state[row-1][column] = 0
			break
		case DOWN:
			state[row][column] = state[row+1][column]
			state[row+1][column] = 0
			break
		case LEFT:
			state[row][column] = state[row][column-1]
			state[row][column-1] = 0
			break
		case RIGHT:
			state[row][column] = state[row][column+1]
			state[row][column+1] = 0
			break
	}
	STEP_SUM++
}