/*
Online Java - IDE, Code Editor, Compiler

Online Java is a quick and easy tool that helps you to build, compile, test your programs online.
*/

import java.util.Random;
import java.util.*;

interface Movable
{
    Set <String> outcomes = new HashSet<>();
    
    public void assignSnakesLadders();
    public void setGrid();
    public void checkSnakesLadder();
    public void defineMoves();
    public void diceThrow();
    public boolean performMove();
    public boolean checkValidMove();
    public boolean everyPossibleDieRoll();
    public void getMinimumMoves();
    
}


class SnakesLadders implements Movable
{
    //to hold all enum values....
    PositionSnakesLadders[] psl;
    
    int [][][] moves = new int [100][6][2];
    int minimumMoves[] = new int[1000];
    int gridNumber;
    int m;
    
    int boardsCompleted;
    
    int totalMoves;
    int lengthEnum;
    boolean allPreviousCompletedMoves=true;
    int i;
    int j;
    int currentPosition;
    int newPosition;
    int diceValue;
    int [][] Board;
    int count;
    
    //all information stored here is transactional showing how it traverses the board...
    //the content is not equipped to answer smallest number of turns it takes.....
    StringJoiner sj = new StringJoiner(" ");
    
    int grids=100;
    String [] path = new String[1000];
    
    enum PositionSnakesLadders
    {
        //SNAKE1(16,6), SNAKE2(48,26), SNAKE3(49,11), SNAKE4(56,53), SNAKE5(62,19), SNAKE6(64,60),  SNAKE7(87,24), SNAKE8(93,73), SNAKE9(95,75), SNAKE10(98,78),
        //LADDERS1(1,38), LADDERS2(4,14), LADDERS3(2,31), LADDERS4(3,42), LADDERS5(5,84), LADDERS6(36,44),  LADDERS7(51,67), LADDERS8(71,91), LADDERS9(80,100);
        
        SNAKE1(16,6), SNAKE2(48,26), SNAKE3(49,11), SNAKE4(56,53), SNAKE5(62,19), SNAKE6(64,60),  SNAKE7(87,24), SNAKE8(93,73), SNAKE9(95,75), SNAKE10(98,78),
        LADDERS1(1,38), LADDERS2(4,14), LADDERS3(9,31), LADDERS4(21,42), LADDERS5(28,84), LADDERS6(36,44),  LADDERS7(51,67), LADDERS8(71,91), LADDERS9(80,100);
        
        
        int startPos;
        int endPos;
        
        PositionSnakesLadders(int startPos, int endPos)
        {
            this.startPos=startPos;
            this.endPos=endPos;
        }
        
    }
    
    //this is using logic described below....
    //since if there is snake head or ladder climb at certain grid locations, it is impossible to have
    //moves from this location. It can also be seen in the grid number breakdown...
    //the 2d array populated of moves...
    //the only purpose is to ensure that every grid is landed on and every move completed from each.
    //it is unrelated to the combinations....
    //testing to see if the random algorithm is satisfactory...
    
    public boolean everyPossibleDieRoll()
    {
        
        for (int i=0; i<grids; i++)
        {
            //System.out.println("grid: " + i);
            do{
                //System.out.println("Enum: " + psl[m].startPos);
            
            //if snake or ladder not at this position on grid... 
            if (psl[m].startPos!=i)
            {
                
                //if its not completed this move, everyPossibleDieRoll is false..
                //note it records all dice rolls even if it goes out of bounds...
                
                for (int p=0; p<6; p++)
                {
                if (moves[i][p][0]==0)
                {
                    //System.out.println("endpoint for: " + i + " " + p + "  " + moves[i][p][1]);
                    allPreviousCompletedMoves=false;
                    
                }
                }
            }
            
            
                
             m++;
            }while(m<lengthEnum);
            
            m=0;
            
        }
        
        
        return allPreviousCompletedMoves;
    }
    
    
    public void defineMoves()
    {
        
        //[startPos] [ dicevalue] [ two elements, startPos and endPos ] 
        //this might be startPos on the grid between  1-99 
        //(excluding squares with Snake Head or base of ladder)…
        //{         diceValue{startPos,  endPos},  diceValue{startPos,  endPos}     };
        
    }
    
    public void diceThrow()
    {
        Random rand = new Random();
        
        do
        {
     
        do
        {
        diceValue = (rand.nextInt(6)+1);
        
        System.out.println("\nDie Value thrown:" + diceValue);
        
        //it would write the details here into the 3D array...
        //this is subject to be overwritten if it falls under snakes and ladders category.
        
        
        //due to zero indexing, subtraction of 1 from diceValue
        moves[currentPosition][diceValue-1][0]=currentPosition;
        moves[currentPosition][diceValue-1][1]=currentPosition+diceValue;
        
        //it has to also store the movements in a String since this is the only technique
        //for Set to differentiate if this path has been taken....
        
        sj.add(Integer.toString(diceValue));
        sj.add("{");
        sj.add(Integer.toString(currentPosition));
        sj.add(",");
        
        
        //this method is linked to
        // valid Move...
        
        //slight difficulty since the currentPosition will update as part of the method call performMove
        //and check if interaction with snake or ladder
        
        //this can also be reduced without  performMove()
        }while (performMove() && newPosition!=100);
        
        boardsCompleted++;
        
        System.out.println("Total moves taken: " + totalMoves);
        System.out.println("STARTING AGAIN: " + "Boards complete: " + boardsCompleted);
        
        //NEED TO BE CAREFUL WITH THIS...
        //STRICTLY SPEAKING THE TOTAL MOVES IS FALSE AT THIS POINT
        //SINCE IT IT BIASED TOWARDS THE DIE ROLLS SO FAR...
        //ONLY POSSIBLE TO EXPLORE ONCE ALL MOVES FROM EACH GRID POSITION HAS BEEN DISCOVERED.
        
        sj.add(Integer.toString(totalMoves));
        
        //also need to add the number of moves to the String...
        path[count]=sj.toString();
        
        outcomes.add(path[count]);
        
        
        System.out.println("******PATH*******: " + path[count]);
        System.out.println("Set size: " + outcomes.size());
        
         minimumMoves[count] = totalMoves;
        
        //ready to start again...
        //resets variables...
        sj=new StringJoiner(" ");
        currentPosition=0;
        allPreviousCompletedMoves=true;
        totalMoves=0;
        count=0;
        newPosition=0;
        
    // this loop can become very expansive
    //it can potentially be adjusted to alternative..
    }while (!everyPossibleDieRoll());
    
    //ensures there are 300 solutions...
    //}while (outcomes.size()<300);
    
        // IT was iniitally envisaged to check the landscape of the board from the current position
        // to examine if avoiding a snake and getting maximum six values to get shortest path...
        //but in game of snakes and ladders, it might be beneficial slipping down a snake... and climbing a missed ladder since the differential
        //newPosition might be further than than getting a six.
        //The problem states smallest number of turns, hence it is utilizing the snakes and ladders to advantage.
        //and not necessarily getting a 6 on each throw...
        
        
        //when the combinations aspect is introduced, it needs to ensure there is a valid value that has
        //been inputted into each array position. This is the only way to ensure all possibilities have been explored..
        //from each position on the board..
        //it was decided to actually simulate scenario when using combinations.. since the random number aspect
        //will not satisfy the minimum turns... Random moves was for evaluation of the code...
        
    }
    
    
    public boolean performMove()
    {
        
        //temp=currentPosition;
        newPosition = currentPosition + diceValue;
        System.out.println("Current position on board: " + newPosition);
        
        return checkValidMove();
    }
    
    
    // do not need boolean....
    public boolean checkValidMove()
    {
        
        System.out.println("In valid move");
        
        if (newPosition>100)
        {
            System.out.println("Roll back");
            
            newPosition=currentPosition;   //it performs a roll back
            System.out.println("new position: " + newPosition);
            
            //it has now finalised new position and can close the transaction history...
            //ready to write into path array...
            sj.add(Integer.toString(newPosition));
            sj.add("}");
            
            return true;
        }
        
        if (newPosition==100)
        {
            newPosition=currentPosition+diceValue;
            
            sj.add(Integer.toString(newPosition));
            sj.add("},  ");
            
            return true;
        }
    
    //NOTE in the below code, it can not complete board at value 100...
    //since there is typically never a ladder which takes person directly to 100.
    //in the game, it usually is challenge at the latter stages to hit exact dice value in getting
    //exactly 100...
     
     //due to zero index  (diceValue-1)
     //{         diceValue-1{startPos,  endPos},  diceValue-1{startPos,  endPos}     };
     
     //it is irrelevant if the move has happened before, however it can perform quick check
     
     //if (Board[currentPosition][diceValue-1][0]!=0)
     //{
         //move has been performed since there is an entry...
     //}
         //this is a bit wasteful checking all the entries...
         //but there is no real reference point
         
         //this will go through all enums unconditionally
         //since it does not know which is snakes and ladders, it has to check the startPos and endPos
         
         System.out.println("Checking if new position is a snake or ladder: " + newPosition);
         
         
         for (PositionSnakesLadders enum1: psl)
         {
             //if its a snake, start point is head.
             //if its a ladder, start point is the bottom.
             if (newPosition==enum1.startPos)
             {
                 //this has to be a snake since there is a descent...
                 if (enum1.startPos>enum1.endPos)
                 {
                     newPosition=enum1.endPos;
                     System.out.println("Hit a snake, moving down:" + enum1.endPos);
                     
                     //storing transaction with incurring a snake
                     sj.add(Integer.toString(newPosition));
                     sj.add("},  ");
                     
                     return true;
                     
                 }
             
              //this has to be a ladder since ascend
             if (enum1.startPos<enum1.endPos)
             {
                 System.out.println("Hit a ladder, moving up:" + enum1.endPos);
                 newPosition=enum1.endPos;
                 //storing transaction with incurring a ladder
                 sj.add(Integer.toString(newPosition));
                 sj.add("},  ");
                 
                 return true;
             }
             
             }
             
         }  //end for 
         
         //finalises current position
         currentPosition=newPosition;
         
         //it would only perform this if it has not entered above loops.
         //since it would have hit a return statement above in getting snakes or ladders...
         
         sj.add(Integer.toString(newPosition));
         sj.add("}, ");
         
         
         System.out.println(newPosition + " at end of valid move");
         
         //this is confusing part..
         //the question states smallest number of turns
         //This sounds as if it is referring to valid moves as oppose to moves which would
         //stretch beyond 100
         //it is difficult to interpret smallest number of turns, but for now 
         //taking all scenarios into consideration.
         
         
         totalMoves=totalMoves+1;
         System.out.println("Total moves taken: " + totalMoves);
         
         
        
;         //it would remain true in all circumstances unless validated as false
         return true;
        
    }  //end boolean method
    
    
    
    
    //checking if board is valid...
    //not part of requirements.
    //but it is quite impossible creating other test scenarios without this...
    
    public void checkSnakesLadder()
    {
        for (PositionSnakesLadders enum1: psl)
        {
            //System.out.println("current value of i: " + i);
            
            //with the single Enum collection, it is not possible to validate if the endPos is greater than
            //startPos for ladder.
            //and if endPos is less than startPos for snakes...
            //since there is no way to distinguish the constants.....
            //Ignoring this aspect.
            //can potentially partition in future.
            
            j=0;
            for (PositionSnakesLadders enum2: psl)
            {
                //System.out.println("current value of j: " + j);
                
                //ensures no two enums are compared against each other....
                //this is fine....
                if (i==j  && i!=lengthEnum-1)
                {
                    j++;
                    
                    //System.out.println("Jump value of j: " + j);
                }
                
                //this logic ensures it does not exceed boundary of the available enum constants...
                if (j!=(lengthEnum-1))
                {
                    //it will store the enum value of the i loop
                    PositionSnakesLadders current = enum1;
                        
                    //looking at a real life board, it can be seen that no two elements (snake or ladders)
                    //have their ends simultaneously on any grid....
                    //so to ensure that all the logic holds, I will just run few checks for validation of
                    //data entry...
                    
                    //psl.values[0].startPos;
                        
                        //can not compare like this...
                        //since its a for each loop
                        //need to use reference to index in values....
                        
                        if (psl[i].startPos == psl[j].startPos)
                        {
                            System.out.println("Invalid board configuration1: " + current.name()  + " {" + current.startPos+","+ current.endPos+"}");
                            System.out.println("Invalid board configuration2: " + enum2.name()  + " {" + enum2.startPos+","+ enum2.endPos+"}");
                        }
                        
                        if (psl[i].endPos == psl[j].startPos)
                        {
                            System.out.println("Invalid board configuration3: " + current.name()  + " {" + current.startPos+","+ current.endPos+"}");
                            System.out.println("Invalid board configuration4: " + enum2.name()  + " {" + enum2.startPos+","+ enum2.endPos+"}");
                            
                        }
                        
                
                        //if (current.startPos == enum2.startPos)
                        if (psl[i].startPos == psl[j].startPos)
                        {
                            System.out.println("Invalid board configuration5: " + current.name()  + " {" + current.startPos+","+ current.endPos+"}");
                            System.out.println("Invalid board configuration6: " + enum2.name()  + " {" + enum2.startPos+","+ enum2.endPos+"}");
                        }
                        
                        if (psl[i].endPos == psl[j].endPos)
                        //if (current.endPos == enum2.endPos)
                        {
                            System.out.println("Invalid board configuration7: " + current.name()  + " {" + current.startPos+","+ current.endPos+"}");
                            System.out.println("Invalid board configuration8: " + enum2.name()  + " {" + enum2.startPos+","+ enum2.endPos+"}");
                            
                        }
             
                j++; 
            
                }
            }
         
         i++;   
        }
        
    }
    
    public SnakesLadders()
    {
        
        psl = PositionSnakesLadders.values();
        lengthEnum = psl.length;
        
        assignSnakesLadders();
        
    }
    
    //this will be used to show end user movement...
    // not possible to put Snakes or ladders on integer array...
    //but it will show end user being displaced in process...
    
    public void setBoard()
    {
        Board = new int[][]  { 
                                        {100,99,98,97,96,95,94,93,92,91},
                                         {90,89,88,87,86,85,84,83,82,81},
                                        { 80,79,78,77,76,75,74,73,72,71},
                                        { 70,69,68,67,66,65,64,63,62,61},
                                        { 60,59,58,57,56,55,54,53,52,51},
                                        { 50,49,48,47,46,45,44,43,42,41},
                                        { 40,39,38,37,36,35,34,33,32,31},
                                        { 30,29,28,27,26,25,24,23,22,21},
                                        { 20,19,18,17,16,15,14,13,12,11},
                                        { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
                                        
                                        };
        
        
    }
    
    
    
    // without converting the board other than int[][], this method will always ensure that
    //all values of the enum
    //since the 
    public void assignSnakesLadders()
    {
        //validates the board....
        checkSnakesLadder();
        
    }
    
     public void setGrid()
    {
        
    }
    
    
    public void getMinimumMoves()
    {
        int min=minimumMoves[0]; 
        
        for (int minimum: minimumMoves)
        {
            if (minimum!=0)
            {
            if (minimum<min)
            {
                min=minimum;
            }
            }
        }
        
        System.out.println("The minimum moves is: " +  min);
        
        System.out.println("*******ANALYSIS OF ALL THE MOVES UNDERTAKEN  [Start Position => End Position]******");
    
        for (int[][] arr : moves) 
        {
            System.out.println("Grid number: " + gridNumber + "   " + Arrays.deepToString(arr));
            gridNumber++;
        }
        
    }
    
}

public class Main
{
    public static void main(String[] args) {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        SnakesLadders sl = new SnakesLadders();
        sl.diceThrow();
        
        System.out.println("\n");
        sl.getMinimumMoves();
        
       
}
}




