import java.util.*;

class Position2D {
    int x;
    int y;
    Position2D(int x, int y){
        this.x = x;
        this.y = y;
    }
    public boolean equals(Object obj){
        return obj instanceof Position2D
                && (((Position2D)obj).x == this.x)
                &&(((Position2D)obj).y == this.y);
    }
}

class Config {

    Position2D horseA;
    Position2D horseB;

    char[][] record;
    int[][] isVisited;
    int stepUsed;

    Config(Position2D horseA, Position2D horseB, char[][] record, int[][] isVisited, int stepUsed){
        this.horseA = horseA;
        this.horseB = horseB;
        this.record = record;
        this.isVisited = isVisited;
        this.stepUsed = stepUsed;
    }
}
class Main12{

    public static void main(String[] args){
        char[][] record = {
                {'H','*','*','h','*'},
                {'*','*','h','*','*'},
                {'*','*','H','#','#'},
                {'#','#','*','*','*'},
                {'*','*','*','*','*'}
        };
        int[][] isVisited = new int[record.length][record[0].length];
        isVisited[0][0] = 1;
        isVisited[2][2] = 2;

        System.out.println(minimumJumps(new Position2D(0,0),new Position2D(2,2),
                new Position2D(3,0),new Position2D(2,1),record,isVisited,0));
    }


    static boolean isValid(char[][] record, Position2D ori, Position2D dest, Position2D another){
        //避免边界
        if(dest.x<0||dest.x>=record[0].length||dest.y<0||dest.y>=record.length) return false;
        //避免两马重合
        if(dest.equals(another)) return false;
        //避免障碍物
        if(record[dest.y][dest.x] == '#') return false;

//示意图: 蹩马腿情况示意图 共8种情况, 可以是另一只马('H')或者障碍物('#')
//  0 1 2 3 4 5 6 7 8 横坐标
//  1     1   3
//  2   2       4
//  3       m
//  4   5       8
//  5     6   7         //m可以抵达的位置
//纵坐标
        //case 2
        if(dest.y == ori.y - 1
                && dest.x == ori.x - 2
                && (record[ori.y-1][ori.x-1]=='#'||record[ori.y][ori.x-1]=='#'|| record[ori.y-1][ori.x-1]=='H'||record[ori.y][ori.x-1]=='H'))
            return false;
        //case 1
        if(dest.y == ori.y - 2
                && dest.x == ori.x - 1
                && (record[ori.y-1][ori.x]=='#'||record[ori.y-1][ori.x-1]=='#'|| record[ori.y-1][ori.x]=='H'||record[ori.y-1][ori.x-1]=='H'))
            return false;
        //case 5
        if(dest.y == ori.y + 1
                && dest.x == ori.x - 2
                && (record[ori.y+1][ori.x-1]=='#'||record[ori.y][ori.x-1]=='#'||record[ori.y+1][ori.x-1]=='H'||record[ori.y][ori.x-1]=='H'))
            return false;

        //case 6
        if(dest.y == ori.y + 2
                && dest.x == ori.x - 1
                && (record[ori.y+1][ori.x]=='#'||record[ori.y+1][ori.x-1]=='#'||record[ori.y+1][ori.x]=='H'||record[ori.y+1][ori.x-1]=='H'))
            return false;

        //case 3
        if(dest.y == ori.y - 2
                && dest.x == ori.x + 1
                && (record[ori.y-1][ori.x]=='#'||record[ori.y-1][ori.x+1]=='#'||record[ori.y-1][ori.x]=='H'||record[ori.y-1][ori.x+1]=='H'))
            return false;
        //case 4
        if(dest.y == ori.y - 1
                && dest.x == ori.x + 2
                && (record[ori.y-1][ori.x+1]=='#'||record[ori.y][ori.x+1]=='#'||record[ori.y-1][ori.x+1]=='H'||record[ori.y][ori.x+1]=='H'))
            return false;
        //case 8
        if(dest.y == ori.y + 1
                && dest.x == ori.x + 2
                && (record[ori.y][ori.x+1]=='#'||record[ori.y+1][ori.x+1]=='#'||record[ori.y][ori.x+1]=='H'||record[ori.y+1][ori.x+1]=='H'))
            return false;
        //case 7
        if(dest.y == ori.y + 2
                && dest.x == ori.x + 1
                && (record[ori.y+1][ori.x+1]=='#'||record[ori.y+1][ori.x]=='#'||record[ori.y+1][ori.x+1]=='H'||record[ori.y+1][ori.x]=='H'))
            return false;

        return true;

    }

    //可能的移动
    static int[][] possibleMove = {{-1,-2},{-2,-1},{1,-2},{2,-1},{-2,1},{-1,2},{1,2},{2,1}};

    //BFS Queue
    static Queue<Config> queue = new LinkedList<>();

    static int minimumJumps(Position2D horseA, Position2D horseB, Position2D d1, Position2D d2, char[][] record, int[][] isVisited, int stepUsed){

        queue.offer(new Config(horseA,horseB,record,isVisited,stepUsed));

        while(!queue.isEmpty()){

            Config current = queue.poll();

            for(int[] pm: possibleMove){
                horseA = current.horseA;
                horseB = current.horseB;
                record = current.record;
                isVisited = current.isVisited;
                stepUsed = current.stepUsed;

                Position2D destinationA = new Position2D(horseA.x+pm[0],horseA.y+pm[1]);
                if(!horseA.equals(d1)
                        &&isValid(record,horseA,destinationA,horseB)
                        &&isVisited[destinationA.y][destinationA.x]!=1&&isVisited[destinationA.y][destinationA.x]!=3){

                    char[][] newRecord =new char[record.length][record[0].length];
                    arrayCopy(record,newRecord);

                    newRecord[horseA.y][horseA.x] = '*';
                    newRecord[destinationA.y][destinationA.x] = 'H';

                    int[][] newIsVisited = new int[isVisited.length][isVisited[0].length];
                    arrayCopy(isVisited,newIsVisited);
                    newIsVisited[destinationA.y][destinationA.x]++;


                    if(newRecord[d1.y][d1.x] == 'H'&&newRecord[d2.y][d2.x] == 'H') return stepUsed + 1;

                    queue.offer(new Config(destinationA,horseB,newRecord,newIsVisited,stepUsed+1));
                }

                Position2D destinationB = new Position2D(horseB.x+pm[0],horseB.y+pm[1]);

                if(!horseB.equals(d2)
                        &&isValid(record,horseB,destinationB,horseA)&&
                        isVisited[destinationB.y][destinationB.x]!=2 && isVisited[destinationB.y][destinationB.x]!=3){

                    char[][] newRecord =new char[record.length][record[0].length];
                    arrayCopy(record,newRecord);

                    newRecord[horseB.y][horseB.x] = '*';
                    newRecord[destinationB.y][destinationB.x] = 'H';

                    int[][] newIsVisited = new int[isVisited.length][isVisited[0].length];
                    arrayCopy(isVisited,newIsVisited);
                    newIsVisited[destinationB.y][destinationB.x]+=2;

                    if(newRecord[d1.y][d1.x] == 'H'&&newRecord[d2.y][d2.x] == 'H') return stepUsed + 1;

                    queue.offer(new Config(horseA,destinationB,newRecord,newIsVisited,stepUsed+1));
                }

            }

        }
        return stepUsed+1;
    }

    public  static void arrayCopy(int[][] aSource, int[][] aDestination) {
        for (int i = 0; i < aSource.length; i++) {
            System.arraycopy(aSource[i], 0, aDestination[i], 0, aSource[i].length);
        }
    }
    public  static void arrayCopy(char[][] aSource, char[][] aDestination) {
        for (int i = 0; i < aSource.length; i++) {
            System.arraycopy(aSource[i], 0, aDestination[i], 0, aSource[i].length);
        }
    }
}