Snake and Ladder Problem
Last Updated :
14 Feb, 2023
Given a snake and ladder board, find the minimum number of dice throws required to reach the destination or last cell from the source or 1st cell. Basically, the player has total control over the outcome of the dice throw and wants to find out the minimum number of throws required to reach the last cell.
If the player reaches a cell which is the base of a ladder, the player has to climb up that ladder and if reaches a cell is the mouth of the snake, and has to go down to the tail of the snake without a dice throw.
For example, consider the board shown, the minimum number of dice throws required to reach cell 30 from cell 1 is 3.
Following are the steps:
a) First throw two dice to reach cell number 3 and then ladder to reach 22
b) Then throw 6 to reach 28.
c) Finally through 2 to reach 30.
There can be other solutions as well like (2, 2, 6), (2, 4, 4), (2, 3, 5).. etc.
The idea is to consider the given snake and ladder board as a directed graph with a number of vertices equal to the number of cells in the board. The problem reduces to finding the shortest path in a graph. Every vertex of the graph has an edge to next six vertices if the next 6 vertices do not have a snake or ladder. If any of the next six vertices has a snake or ladder, then the edge from the current vertex goes to the top of the ladder or tail of the snake. Since all edges are of equal weight, we can efficiently find the shortest path using Breadth-First Search of the graph.
Following is the implementation of the above idea. The input is represented by two things, the first is ‘N’ which is a number of cells in the given board, second is an array ‘move[0…N-1]’ of size N. An entry move[i] is -1 if there is no snake and no ladder from i, otherwise move[i] contains index of destination cell for the snake or the ladder at i.
C++
#include <iostream>
#include <queue>
using namespace std;
struct queueEntry {
int v;
int dist;
};
int getMinDiceThrows( int move[], int N)
{
bool * visited = new bool [N];
for ( int i = 0; i < N; i++)
visited[i] = false ;
queue<queueEntry> q;
visited[0] = true ;
queueEntry s
= { 0, 0 };
q.push(s);
queueEntry qe;
while (!q.empty()) {
qe = q.front();
int v = qe.v;
if (v == N - 1)
break ;
q.pop();
for ( int j = v + 1; j <= (v + 6) && j < N; ++j) {
if (!visited[j]) {
queueEntry a;
a.dist = (qe.dist + 1);
visited[j] = true ;
if (move[j] != -1)
a.v = move[j];
else
a.v = j;
q.push(a);
}
}
}
return qe.dist;
}
int main()
{
int N = 30;
int moves[N];
for ( int i = 0; i < N; i++)
moves[i] = -1;
moves[2] = 21;
moves[4] = 7;
moves[10] = 25;
moves[19] = 28;
moves[26] = 0;
moves[20] = 8;
moves[16] = 3;
moves[18] = 6;
cout << "Min Dice throws required is "
<< getMinDiceThrows(moves, N);
return 0;
}
|
Java
import java.util.LinkedList;
import java.util.Queue;
public class SnakesLadder {
static class qentry {
int v;
int dist;
}
static int getMinDiceThrows( int move[], int n)
{
int visited[] = new int [n];
Queue<qentry> q = new LinkedList<>();
qentry qe = new qentry();
qe.v = 0 ;
qe.dist = 0 ;
visited[ 0 ] = 1 ;
q.add(qe);
while (!q.isEmpty()) {
qe = q.remove();
int v = qe.v;
if (v == n - 1 )
break ;
for ( int j = v + 1 ; j <= (v + 6 ) && j < n;
++j) {
if (visited[j] == 0 ) {
qentry a = new qentry();
a.dist = (qe.dist + 1 );
visited[j] = 1 ;
if (move[j] != - 1 )
a.v = move[j];
else
a.v = j;
q.add(a);
}
}
}
return qe.dist;
}
public static void main(String[] args)
{
int N = 30 ;
int moves[] = new int [N];
for ( int i = 0 ; i < N; i++)
moves[i] = - 1 ;
moves[ 2 ] = 21 ;
moves[ 4 ] = 7 ;
moves[ 10 ] = 25 ;
moves[ 19 ] = 28 ;
moves[ 26 ] = 0 ;
moves[ 20 ] = 8 ;
moves[ 16 ] = 3 ;
moves[ 18 ] = 6 ;
System.out.println( "Min Dice throws required is "
+ getMinDiceThrows(moves, N));
}
}
|
Python3
class QueueEntry( object ):
def __init__( self , v = 0 , dist = 0 ):
self .v = v
self .dist = dist
def getMinDiceThrows(move, N):
visited = [ False ] * N
queue = []
visited[ 0 ] = True
queue.append(QueueEntry( 0 , 0 ))
qe = QueueEntry()
while queue:
qe = queue.pop( 0 )
v = qe.v
if v = = N - 1 :
break
j = v + 1
while j < = v + 6 and j < N:
if visited[j] is False :
a = QueueEntry()
a.dist = qe.dist + 1
visited[j] = True
a.v = move[j] if move[j] ! = - 1 else j
queue.append(a)
j + = 1
return qe.dist
N = 30
moves = [ - 1 ] * N
moves[ 2 ] = 21
moves[ 4 ] = 7
moves[ 10 ] = 25
moves[ 19 ] = 28
moves[ 26 ] = 0
moves[ 20 ] = 8
moves[ 16 ] = 3
moves[ 18 ] = 6
print ( "Min Dice throws required is {0}" .
format (getMinDiceThrows(moves, N)))
|
C#
using System;
using System.Collections.Generic;
public class SnakesLadder {
public class qentry {
public int v;
public int
dist;
}
static int getMinDiceThrows( int [] move, int n)
{
int [] visited = new int [n];
Queue<qentry> q = new Queue<qentry>();
qentry qe = new qentry();
qe.v = 0;
qe.dist = 0;
visited[0] = 1;
q.Enqueue(qe);
while (q.Count != 0) {
qe = q.Dequeue();
int v = qe.v;
if (v == n - 1)
break ;
for ( int j = v + 1; j <= (v + 6) && j < n;
++j) {
if (visited[j] == 0) {
qentry a = new qentry();
a.dist = (qe.dist + 1);
visited[j] = 1;
if (move[j] != -1)
a.v = move[j];
else
a.v = j;
q.Enqueue(a);
}
}
}
return qe.dist;
}
public static void Main(String[] args)
{
int N = 30;
int [] moves = new int [N];
for ( int i = 0; i < N; i++)
moves[i] = -1;
moves[2] = 21;
moves[4] = 7;
moves[10] = 25;
moves[19] = 28;
moves[26] = 0;
moves[20] = 8;
moves[16] = 3;
moves[18] = 6;
Console.WriteLine( "Min Dice throws required is "
+ getMinDiceThrows(moves, N));
}
}
|
Javascript
<script>
class qentry
{
constructor()
{
this .v = 0;
this .dist = 0;
}
}
function getMinDiceThrows(move,n)
{
let visited = new Array(n);
for (let i = 0; i < n; i++)
visited[i] = false ;
let q = [];
let qe = new qentry();
qe.v = 0;
qe.dist = 0;
visited[0] = 1;
q.push(qe);
while (q.length != 0)
{
qe = q.shift();
let v = qe.v;
if (v == n - 1)
break ;
for (let j = v + 1; j <= (v + 6) && j < n; ++j)
{
if (visited[j] == 0)
{
let a = new qentry();
a.dist = (qe.dist + 1);
visited[j] = 1;
if (move[j] != -1)
a.v = move[j];
else
a.v = j;
q.push(a);
}
}
}
return qe.dist;
}
let N = 30;
let moves = new Array(N);
for (let i = 0; i < N; i++)
moves[i] = -1;
moves[2] = 21;
moves[4] = 7;
moves[10] = 25;
moves[19] = 28;
moves[26] = 0;
moves[20] = 8;
moves[16] = 3;
moves[18] = 6;
document.write( "Min Dice throws required is " +
getMinDiceThrows(moves, N));
</script>
|
Output
Min Dice throws required is 3
The time complexity of the above solution is O(N) as every cell is added and removed only once from the queue. And a typical enqueue or dequeue operation takes O(1) time.
Auxiliary Space : O(N)
Another approach we can think of is recursion in which we will be going to each block, in this case, which is from 1 to 30, and keeping a count of a minimum number of throws of dice at block i and storing it in an array t.
So, basically, we will:
- Create an array, let’s say ‘t’, and initialize it with -1.
- Now we will call a recursive function from block 1, with variable let’s say ‘i’, and we will be incrementing this.
- In this we will define the base condition as whenever block number reaches 30 or beyond we will return 0 and we will also check if this block has been visited before, this we will do by checking the value of t[i], if this is -1 then it means its not visited and we move forward with the function else its visited and we will return value of t[i].
- After checking base cases we will initialize a variable ‘min’ with a max integer value.
- Now we will initiate a loop from 1 to 6, i.e the values of a dice, now for each iteration we will increase the value of i by the value of dice(eg: i+1,i+2….i+6) and we will check if any increased value has a ladder on it if there is then we will update the value of i to the end of the ladder and then pass the value to the recursive function, if there is no ladder then also we will pass the incremented value of i based on dice value to a recursive function, but if there is a snake then we won’t pass this value to recursive function as we want to reach the end as soon as possible, and the best of doing this would be not to be bitten by a snake. And we would be keep on updating the minimum value for variable ‘min’.
- Finally we will update t[i] with min and return t[i].
Below is the implementation of the above approach:
C++
#include <climits>
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
int t[31];
int sol( int i, unordered_map< int , int >& h)
{
if (i >= 30)
return 0;
else if (t[i] != -1)
return t[i];
int min_value = INT_MAX;
for ( int j = 1; j <= 6; j++) {
int k = i + j;
if (h.count(k) > 0) {
if (h[k] < k)
continue ;
k = h[k];
}
min_value = min(min_value, sol(k, h) + 1);
}
t[i] = min_value;
return t[i];
}
int min_throw( int n, vector< int > arr)
{
for ( int i = 0; i < 31; i++) {
t[i] = -1;
}
unordered_map< int , int > h;
for ( int i = 0; i < 2 * n; i += 2) {
h[arr[i]] = arr[i + 1];
}
return sol(1, h);
}
int main()
{
int N = 8;
vector< int > arr{ 3, 22, 5, 8, 11, 26, 20, 29,
17, 4, 19, 7, 27, 1, 29, 9 };
cout << "Min Dice throws required is "
<< min_throw(N, arr) << endl;
return 0;
}
|
Java
import java.io.*;
import java.util.*;
class GFG {
static int [] t = new int [ 31 ];
static int minThrow( int n, int arr[])
{
for ( int i = 0 ; i < 31 ; i++) {
t[i] = - 1 ;
}
HashMap<Integer, Integer> h = new HashMap<>();
for ( int i = 0 ; i < 2 * n; i = i + 2 ) {
h.put(arr[i], arr[i + 1 ]);
}
return sol( 1 , h);
}
static int sol( int i, HashMap<Integer, Integer> h)
{
if (i >= 30 )
return 0 ;
else if (t[i] != - 1 )
return t[i];
int min = Integer.MAX_VALUE;
for ( int j = 1 ; j <= 6 ; j++) {
int k = i + j;
if (h.containsKey(k)) {
if (h.get(k) < k)
continue ;
k = h.get(k);
}
min = Math.min(min, sol(k, h) + 1 );
}
t[i] = min;
return t[i];
}
public static void main(String[] args)
{
int N = 8 ;
int [] arr = new int [ 2 * N];
arr[ 0 ] = 3 ;
arr[ 1 ] = 22 ;
arr[ 2 ] = 5 ;
arr[ 3 ] = 8 ;
arr[ 4 ] = 11 ;
arr[ 5 ] = 26 ;
arr[ 6 ] = 20 ;
arr[ 7 ] = 29 ;
arr[ 8 ] = 17 ;
arr[ 9 ] = 4 ;
arr[ 10 ] = 19 ;
arr[ 11 ] = 7 ;
arr[ 12 ] = 27 ;
arr[ 13 ] = 1 ;
arr[ 14 ] = 29 ;
arr[ 15 ] = 9 ;
System.out.println( "Min Dice throws required is "
+ minThrow(N, arr));
}
}
|
Python3
from typing import List , Dict
def min_throw(n: int , arr: List [ int ]) - > int :
t = [ - 1 ] * 31
h = {}
for i in range ( 0 , 2 * n, 2 ):
h[arr[i]] = arr[i + 1 ]
return sol( 1 , h, t)
def sol(i: int , h: Dict [ int , int ], t: List [ int ]) - > int :
if i > = 30 :
return 0
elif t[i] ! = - 1 :
return t[i]
min_value = float ( "inf" )
for j in range ( 1 , 7 ):
k = i + j
if k in h:
if h[k] < k:
continue
k = h[k]
min_value = min (min_value, sol(k, h, t) + 1 )
t[i] = min_value
return t[i]
N = 8
arr = [ 3 , 22 , 5 , 8 , 11 , 26 , 20 , 29 , 17 , 4 , 19 , 7 , 27 , 1 , 29 , 9 ]
print ( "Min Dice throws required is" , min_throw(N, arr))
|
C#
using System;
using System.Collections.Generic;
class GFG {
static int [] t= new int [31];
static int sol( int i, Dictionary< int , int > h)
{
if (i >= 30)
return 0;
else if (t[i] != -1)
return t[i];
int min_value =Int32.MaxValue;
;
for ( int j = 1; j <= 6; j++) {
int k = i + j;
if (h.ContainsKey(k)) {
if (h[k] < k)
continue ;
k = h[k];
}
min_value = Math.Min(min_value, sol(k, h) + 1);
}
t[i] = min_value;
return t[i];
}
static int min_throw( int n, List< int > arr)
{
for ( int i = 0; i < 31; i++) {
t[i] = -1;
}
Dictionary< int , int > h= new Dictionary< int , int >();
for ( int i = 0; i < 2 * n; i += 2) {
h.Add(arr[i], arr[i + 1]);
}
return sol(1, h);
}
public static void Main()
{
int N = 8;
List< int > arr= new List< int >{ 3, 22, 5, 8, 11, 26, 20, 29,
17, 4, 19, 7, 27, 1, 29, 9 };
Console.Write( "Min Dice throws required is " + min_throw(N, arr));
}
}
|
Javascript
let t= new Array(31);
function sol(i, h)
{
if (i >= 30)
return 0;
else if (t[i] != -1)
return t[i];
let min_value = Number.MAX_SAFE_INTEGER;
for (let j = 1; j <= 6; j++) {
let k = i + j;
if (h.has(k)) {
if (h.get(k) < k)
continue ;
k = h.get(k);
}
min_value = Math.min(min_value, sol(k, h) + 1);
}
t[i] = min_value;
return t[i];
}
function min_throw(n, arr)
{
for (let i = 0; i < 31; i++) {
t[i] = -1;
}
let h= new Map();
for (let i = 0; i < 2 * n; i += 2) {
h.set(arr[i],arr[i + 1]);
}
return sol(1, h);
}
let N = 8;
let arr=[ 3, 22, 5, 8, 11, 26, 20, 29,
17, 4, 19, 7, 27, 1, 29, 9 ];
console.log( "Min Dice throws required is " + min_throw(N, arr));
|
Output
Min Dice throws required is 3
Time complexity: O(N).
Auxiliary Space O(N)
Like Article
Suggest improvement
Share your thoughts in the comments
Please Login to comment...