Skip the top menu

Python Game Programming

Snakes and Ladders

Download Code

Learning Outcomes

On completion of this page you will learn how to use tuples, lists and nested loops to navigate around a snakes and ladders board

Introduction

Below is an image of the Snakes and Ladders 'board' that we shall be using for our game example. It is slightly different to the board that you would by in a toy store. The difference is that we have added the yellow section to the right of the main board to show a representation of the dice that would be used in traditional boards. Here we show an image of a dice that we click on. This clicking will result producing a random number between 1 and 5 inclusive; the same as what a physical dice would produce when rolled.

The left hand portion shows an example of a real Snakes and Ladders board.

Fig 1

Snakes and Ladders is a game of pure chance; there is no strategy involved. Success or failure depends on the roll if the dice. The game was initially an allegory of life: the rolls of the dice represent the steps a person takes towards a goal, the ladders represent opportunities encountered and the snakes represent setbacks.

With this little nugget of history at the back of our minds let us look at developing a programme that plays Snakes and Ladders on your computer screen.

Programme Code

Initial Setup

Listing 1-1
                            import pygame, time, random,  math
                            pygame.init()
                            
                            FPS = 60
                            clock = pygame.time.Clock()
                        

As the subtitle above indicates this block of code is where we import our resources and set up our clock. It is not necessary to discuss it as it should be familiar to you alaready.


Go to top

Programme constants

Listing 1-2
                            BOARD_SIZE = 800
                            SIDEBAR_LENGTH = BOARD_SIZE//5
                            DICE_SIZE = (SIDEBAR_LENGTH//2, SIDEBAR_LENGTH//2)
                            ENDGAME_SIZE = (BOARD_SIZE,BOARD_SIZE)
                            BACKGROUND_SIZE = (BOARD_SIZE,BOARD_SIZE)
                            BUTTON_SIZE = (BOARD_SIZE//10, BOARD_SIZE//10)
                            FONT_SIZE = int(BOARD_SIZE * 0.032)
                            TRAVELLER_STARTPOINT = (BOARD_SIZE + BOARD_SIZE//20,BOARD_SIZE - BOARD_SIZE//20)
                            CELL_WIDTH = BOARD_SIZE//10
                            CELL_HEIGHT = BOARD_SIZE//10
                        

This is the list of constants that we shall be using in our programme:

  • BOARD_SIZE - this is the length of one sixe of the game board
  • SIDEBAR_LENGTH - this is the length of the short side of the sidebar that contains the image of the dice
  • DICE_SIZE - this is a tuple that has the length and height of the dice image
  • ENDGAME_SIZE - this is a tuple that contains the width and height of the rectangle that appears when the game is over
  • BACKGROUND_SIZE - this is a tuple that contains the widht and height of the gaming board
  • BUTTON_SIZE - this is a tuple that contains the widht and height of the button
  • FONT_SIZE - the name of this constant explains its function
  • TRAVELLER_STARTPOINT - this tuple contains the coordinates of the button's position before the game starts
  • CELL_WIDTH - function is self evident
  • CELL_HEIGHT - function is self evident

Go to top

More Constants

Listing 1-3
                            WHITE = (255, 255, 255)
                            BLACK = (0, 0, 0)
                            YELLOW = (255, 255, 0)
                            MAROON = (128, 0, 0)
                            random_number = 0
                        

Of the code above only the bottom line needs an explanation. As its name implies the variable random_number will hold the random number in the range 1 - 6 inclusive that will be generated when the user clicks the dice image.


Go to top

Images Setup

Listing 1-4
                            gameWindow = pygame.display.set_mode((BOARD_SIZE + SIDEBAR_LENGTH, BOARD_SIZE+4))
                            pygame.display.set_caption("Snakes and Ladders")
                            
                            snakesandladders_image = pygame.image.load("YellowBlueSnakesAndLaddersBoard.png")
                            snakesandladders_image =pygame.transform.scale(snakesandladders_image, BACKGROUND_SIZE)
                            snakesandladders_rect = snakesandladders_image.get_rect()
                            snakesandladders_rect.topleft = (0,0)
                            
                            dice_image = pygame.image.load("dice.png")
                            dice_image =pygame.transform.scale(dice_image, DICE_SIZE)
                            dice_rect = dice_image.get_rect()
                            dice_rect.centerx = (BOARD_SIZE + SIDEBAR_LENGTH//2)
                            dice_rect.centery = BOARD_SIZE//4
                            
                            pygame.draw.rect(gameWindow,(225, 225, 0), pygame.Rect(BOARD_SIZE+2, 0, SIDEBAR_LENGTH, BOARD_SIZE))
                            button_image = pygame.image.load("CoinBackdrop.png")
                            button_image =pygame.transform.scale(button_image, BUTTON_SIZE)
                            button_rect = button_image.get_rect()
                            button_rect.center = TRAVELLER_STARTPOINT
                        

No new concepts are introduced here. Line 24 sets up the game window and line 25 adds a caption to it. The rest of the code involves loading and positioning the .PNG images for the game board, the dice and the button. All of this code is identical to the code for positioning an image on the page Images on the page Startup. The only difference is that the images being loaded above are scaled to fit the requirements of the game.


Go to top

Setting up Text

Listing 1-5
                            #Set up the scoring text
                            fontVerdana = pygame.font.Font("Verdana.ttf",32)
                            random_text = fontVerdana.render(str(random_number),True, MAROON, YELLOW)
                            random_rect = random_text.get_rect()
                            random_rect.center = (BOARD_SIZE+SIDEBAR_LENGTH//2,BOARD_SIZE//2 )
                        

Again no new concepts are introduced here and an explanation of the code can be found the section Text on the page Startup.


Go to top

Setting up sound

Listing 1-6
                            pygame.mixer.Sound("fanfare.mp3")
                            ladder_sound = pygame.mixer.Sound("fanfare.mp3")
                            pygame.mixer.Sound("hissing.mp3")
                            hissing_sound = pygame.mixer.Sound("hissing.mp3")
                            pygame.mixer.Sound("I_won.mp3")
                            I_won_sound = pygame.mixer.Sound("I_won.mp3")
                        

Go to top

Setting up a grid

We have now loaded all of our resources and are ready for the next step, which is building up a map which guides the button through the grid that forms the game board until it reaches the top right hand corner.

Fig 2: the logical layout of the game board

Above we have a 10 X 10 board of the type that our Snakes and Ladders are based on. It is laid out according to the computing version of the Cartesian plane. The x-axis moves from left to right and the y-axes moves from top to bottom, as opposed to mathematics where the y-axis moves from bottom to top.

On a Snakes and Ladders board the button starts at the bottom row and moves from right to left. It then moves to the row above and moves from left to right. It continues with this zigzag movement until it reaches the top right hand corner. This is not compliant with the coordinate system shown in Fig 2. In order to overcome this problem we need to map the movements of the Snakes and Ladders board onto the system shown in Fig 2 above.

Fig 3

Fig 3 above is not a table; it is simply a list of tuples - hence the brackets. The tuples in the list are organised to guide the button to start at the bottom right hand corner, move step by step to the bottom left corner, move up to the next row and move from left to right and continue in this manner until it reaches the top right hand corner.

The code below allows us to populate such a list.

Listing 1-7
                            board_squares = []
                            for row in range(9, -1, -1):
                                if row%2 == 0:
                                    for col in range(0, 10):
                                        board_squares.append((col, row))          
                                else:
                                    for col in range(9,-1,-1):
                                        board_squares.append((col, row))
                            for col in range(8,2,-1):
                                board_squares.append((col, 0))
                            
                            start_point = 0
                        

At line 57 a list board_squares is initialised as being blank. This list will contain the 'map' for navigating the Snakes and Ladders board from start to finish.

The for loop that spans lines 58 - 64 is involved in populating this list.

At line 58 the outer loop starts. The loop counter row moves from 9 to 0 in steps of 1.

The body of the loop is an if..else construct. At line 59 thr counter row is tested for being even. On the first iteration of the loop this will not be the case since 9 is an odd number. Since this is the case lines 60 and 61 are skipped and lines 63 and 64 are executed.

Like line 58 the loop at line 63 iterates from 9 to 0 in units of 1 step. Thus on completion of the first iteration of the main loop the list board_squares will have the following values in it:

(9,0) (8,0) (7,0) (6,0) (5,0) (4,0) (3,0) (2,0) (1,0) (0,0)
Go to top
Listing 1-8
                        active = True
                        while active:
                            for event in pygame.event.get():
                                if event.type == pygame.QUIT:
                                    active = False
                    
Listing 1-9
                                if dice_rect.collidepoint(pygame.mouse.get_pos()):
                                    if event.type == pygame.MOUSEBUTTONDOWN:
                                        random_number = random.randint(1,6)
                                        random_text = fontVerdana.render(str(random_number),True, MAROON, YELLOW)
                                        gameWindow.blit(random_text,random_rect)
                        
                                        end_point = start_point + random_number
                        
                                        for counter in range(start_point,end_point):
                                            item = board_squares[counter]
                                            x_coord = item[0] * CELL_WIDTH
                                            y_coord = item[1] * CELL_HEIGHT
                        
                                            button_rect.topleft = (x_coord,y_coord)
                                            gameWindow.blit(button_image, button_rect)
                        
                                            pygame.display.update()
                                            time.sleep(0.5)
                        
                                            gameWindow.blit(snakesandladders_image, snakesandladders_rect)
                    
Listing 1-10
                                        if end_point == 13:
                                            ladder_sound.play()
                                            top_left = board_squares[91]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 92
                    
Listing 1-11
                                        elif end_point == 16:
                                            ladder_sound.play()
                                            top_left = board_squares[45]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                        
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 46
                        
                                        elif end_point == 18:
                                            ladder_sound.play()
                                            top_left = board_squares[80]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 81
                                        elif end_point == 47:
                                            ladder_sound.play()
                                            top_left = board_squares[94]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 96
                    
Listing 1-12
                                        elif end_point == 98 or end_point == 102:
                                            hissing_sound.play()
                                            top_left = board_squares[9]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 11
                    
Listing 1-13
                                        elif end_point == 71:
                                            hissing_sound.play()
                                            top_left = board_squares[28]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 30
                                        elif end_point == 78:
                                            hissing_sound.play()
                                            top_left = board_squares[3]  
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 5
                                        elif end_point == 65:
                                            hissing_sound.play()
                                            top_left = board_squares[23]
                                            top_left_x = top_left[0] * CELL_WIDTH
                                            top_left_y = top_left[1] * CELL_HEIGHT
                                            button_rect.topleft = (top_left_x,top_left_y)
                                            #print(str(start_point)+"   "+str(end_point))
                                            gameWindow.blit(button_image, button_rect)
                                            start_point = 25
                    
Listing 1-14
                                        else:
                                            if end_point == 100:
                                                gameWindow.blit(snakesandladders_image, snakesandladders_rect)
                                                gameWindow.blit(endgame_image, endgame_rect)
                                                pygame.display.update()
                                                I_won_sound.play()
                                                time.sleep(10)
                                                I_won_sound.play()
                                                active= False
                                            elif end_point > 99:
                                                start_point = 100 - abs(100 - end_point)
                                            else:
                                                start_point += random_number
                    
Listing 1-15
                                gameWindow.blit(snakesandladders_image, snakesandladders_rect)
                                gameWindow.blit(button_image, button_rect)
                                gameWindow.blit(dice_image,dice_rect)
                            pygame.display.update()
                            clock.tick(FPS)
                        pygame.quit()