Fastest path on a snakes and ladders board I am not looking for a detailed review of this code as I am aware it is very inefficient. The main reason for posting this is to see if there is a better way to achieve my goal (I am certain there must be)

I have created code to find the fewest number of dice rolls that would permit someone to move from the first to last square on a snakes and ladders board. The player wins if they land on or go beyond the final square on the board. They start off the board at position -1.

My approach uses recursion so is OK for small boards. The moment the board reaches a size of 30+ it takes far too long to generate the solution. 

Is there a better way to solve this problem

"""Module works out fastest way to traverse a snakes and ladders board"""
def roll_dice(position, roll_number, number_squares, snakes, ladders, list_moves=[]):
    """Roll the dice and then work out if the player can climb a ladder / has won"""
    if position in ladders:
        position = ladders[position]
    if position &gt;= number_squares - 1:
        list_moves.append(roll_number)
        return
    for i in range(1, 7): #For each position roll the dice 6 times
        if position + i in snakes:
            continue  # Forbid a dice-roll that lands on a snake
        roll_dice(position + i, roll_number + 1, number_squares, snakes, ladders)
    return list_moves

def minimum_moves(number_squares, snakes={}, ladders={}):
    """Returns the minimum number of moves starting from position 0 for a board of size n
    snakes and ladders are both dictionaries containing the starting point as the key
    and end point as the value"""
    # Initialise board
    # The player starts off the board at position -1
    list_moves = roll_dice(-1, 0, number_squares, snakes, ladders) 
    print(f"Board is traversable in {min(list_moves)} moves")


if __name__ == "__main__":
    NUMBER_SQUARES = 25
    SNAKES = {21:0, 19:9, 14: 2, 18:5}
    LADDERS = {2: 21, 4:9, 10:20, 17:23}
    minimum_moves(NUMBER_SQUARES, SNAKES, LADDERS)
Don't use mutable default arguments. If you need to default to a list then default to None and then change to an empty list.

Take the following example code:

&gt;&gt;&gt; def example_list_builder(value, list_=[]):
    list_.append(value)
    return list_

&gt;&gt;&gt; example_list_builder(1)
[1]
&gt;&gt;&gt; example_list_builder(2)
[1, 2]


This makes list_ a really poorly defined singleton variable, that can't be accessed globally.



Your code looks like it's something like \$O(6^n)\$ where \$n\$ is the size of the board.

You can instead make it \$O(l^2)\$ where \$l\$ is LADDERS and SNAKES.

To improve your code, you should remove the simulation and instead only work with the LADDERS and SNAKES variables.

For each snake and ladder you should traverse it, and then traverse the snake or ladder after this. You should note the distance, there are two ways to do this:


Least tiles visited.
Least turns taken.


For the latter, you'll have to check for collisions with other snakes and ladders.

You should take snakes into account, as the fastest path for the following is 4 turns:

NUMBER_SQUARES = 1000
LADDERS = {1: 502, 501: 999}
SNAKES = {503: 500}