/*
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
{
    
    
    
    public void assignSnakesLadders();
    public void setGrid();
    public void checkSnakesLadder();
    public void defineMoves();
    public void diceThrow();
    public boolean performMove();
    public boolean checkValidMove();
    public boolean everyPossibleDieRoll();
    
}


class SnakesLadders implements Movable
{
    //to hold all enum values....
    PositionSnakesLadders[] psl;
    
    int [][][] moves = new int [100][6][2];
    
    
   //grids = 100;
    
    int boardComplete;
    
    int totalMoves;
    int lengthEnum;
    boolean previousCompletedMove=false;
    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....
    public boolean everyPossibleDieRoll()
    {
        for (int i=0; i<grids; i++)
        {
            if (i<95)
            {
                //more straight forward.
                //if its not completed this move, everyPossibleDieRoll is false..
                if (moves[i][diceValue][0]==0)
                {
                    previousCompletedMove=false;
                    
                }
            }
            
            if (i>=95)
            {
                for (int j=i; j<grids-i; j++)
                {
                    //if there is a 0  (reminder  [i][j][0=startPos  1=endPos])
                    //it means that this die roll has not occured from this situation...
                    //note as reminder of the logic below commented out, it is required
                    //to treat situation differently....
                    //j can not be treated as die value since values are not suitable from 95 upwards..
                    
                    if (moves[i][j][0]==0)
                    {
                        previousCompletedMove=false;
                    }
                }
                
                //the lengths of   [100][0][0]  is expected to be 0
        //the length of    [99][0][0] is expected to be one  {99,100}
        //the length of    [98][0][0] is expected to be two  {98,99},  {98,100}
        //the length of    [97][0][0] is expected to be three  {97,98}, {97,99}, {97,100}
         //the length of   [96][0][0] is expected to be four  {96,97}, {96,98}, {96,99}, {96,100}
         //the length of   [95][0][0] is expected to be five  {95,96}, {95,97}, {95,98}, {95,99}, {95,100}
                
            }
        }
        
        return previousCompletedMove;
    }
    
    
    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("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.
        
        System.out.println(currentPosition);
        System.out.println(diceValue);
        
        //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(",");
       
        
        totalMoves=totalMoves+1;
        
        //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
        }while (performMove() && newPosition!=100);
        
        boardComplete++;
        System.out.println("STARTING AGAIN: " + boardComplete);
        
        //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();
        
        //ready to start again...
        //resets variables...
        sj=new StringJoiner(" ");
        currentPosition=0;
        previousCompletedMove=true;
        totalMoves=0;
        count=0;
        
        
    }while (everyPossibleDieRoll());
    
        // 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...
        
        //this is where it needs another while loop
        //it has to keep going until...
        
        //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..
        
        //the lengths of   [100][0][0]  is expected to be 0
        //the length of    [99][0][0] is expected to be one  {99,100}
        //the length of    [98][0][0] is expected to be two  {98,99},  {98,100}
        //the length of    [97][0][0] is expected to be three  {97,98}, {97,99}, {97,100}
         //the length of   [96][0][0] is expected to be four  {96,97}, {96,98}, {96,99}, {96,100}
         //the length of   [95][0][0] is expected to be five  {95,96}, {95,97}, {95,98}, {95,99}, {95,100}
         
         //FROM HERE ONWARDS, EXPECT lengths to be 6
         //the length of   [94][0][0] is expected to be six  {94,95}, {94,96}, {94,97}, {94,98}, {94,99}, {94,100}
    
    }
    
    
    public boolean performMove()
    {
        
        //temp=currentPosition;
        newPosition = currentPosition + diceValue;
        System.out.println("Current position on board: " + newPosition);
        
        return checkValidMove();
    }
    
    public boolean checkValidMove()
    {
        
        System.out.println("In valid move");
        
        if (newPosition>100)
        {
            newPosition=currentPosition;   //it performs a roll back
            
            //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 false;
        }
        
        if (newPosition==100)
        {
            newPosition=currentPosition+diceValue;
            
            sj.add(Integer.toString(newPosition));
            sj.add("}");
            System.exit(0);
            
            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");
                     
                     //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");
                 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;
         
         System.out.println(newPosition + " at end of valid move\n");
         
         //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 (current.startPos == enum2.endPos)
                        
                        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+","+ current.endPos+"}");
                        }
                        
                        
                        //if (current.endPos == enum2.startPos)
                        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()  + " {" + current.startPos+","+ current.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 class Main
{
    public static void main(String[] args) {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        SnakesLadders sl = new SnakesLadders();
        //`sl.defineMoves();
        sl.diceThrow();
        
       
}
}
