GFG App
Open App
Browser
Continue

# Design Snake and Ladder Game using Python OOPS

This article covers designing a complete Snake and Ladder game using object-oriented programming (Python OOP) principles with the following rules and requirements. Below we will discuss the rules of the game:

1. Here we create a board of size 10 and dice of side 6.
2. Each player puts their counter on the board at starting position at 1 and takes turns to roll the dice.
3. Move your counter forward the number of spaces shown on the dice.
4. If your counter lands at the bottom of a ladder, you can move up to the top of the ladder. If your counter lands on the head of a snake, you must slide down to the bottom of the snake.
5. Each player will get a fair chance to roll the dice.
6. On the dice result of 6, the user gets one more chance to roll the dice again. However, the same user can throw the dice a maximum of 3 times.
Note: if the result of the dice is 6,6,6 the user can not throw the dice again as the maximum attempts are over and the next user will get to throw the dice.
7. When the user rolls dice and it leads to an invalid move, the player should remain in the same position.
Ex: when the user is in position 99 and rolling of dice yields any number more than one the user remains in the same position.
8. Print the ranks of users who finished first, second, and so onâ€¦

### Steps to Design Snake and Ladder game using OOPS:

Step 1: Define Game Player Class. This class encapsulates a player’s properties like _id, rank, and its current_position on board.

## Python3

 `class` `GamePlayer:`   `    ``def` `__init__(``self``, _id):` `        ``# initial dummy rank -1` `        ``self``._id ``=` `_id` `        ``# starting position for every player is 1` `        ``self``.rank ``=` `-``1` `        ``self``.position ``=` `1`   `    ``def` `set_position(``self``, pos):` `        ``self``.position ``=` `pos`   `    ``def` `set_rank(``self``, rank):` `        ``self``.rank ``=` `rank`   `    ``def` `get_pos(``self``):` `        ``return` `self``.position`   `    ``def` `get_rank(``self``):` `        ``return` `self``.rank`

Step 2: Define the Moving Entity class. This class can be extended by anything that can move the player to another position, like a Snake or ladder. This is done in order to keep code extensible and abstract common behavior like end position. We can define this class using fixed end_position for a moving entity or can override the get_end_pos() function in order to get dynamic end_pos as well.

## Python3

 `class` `MovingEntity:` `    ``"""` `    ``You can create any moving entity , like snake or ladder or` `    ``wormhole by extending this ` `    ``"""`   `    ``def` `__init__(``self``, end_pos``=``None``):` `        ``self``.end_pos ``=` `end_pos  ``# end pos where player would be send on board` `        ``self``.desc ``=` `None`  `# description of moving entity`   `    ``def` `set_description(``self``, desc):` `        ``self``.desc ``=` `None`   `    ``def` `get_end_pos(``self``):` `        ``if` `self``.end_pos ``is` `None``:` `            ``raise` `Exception(``"no_end_position_defined"``)` `        ``return` `self``.end_pos`

Step 3: Define Snake and Ladder by extending the moving entity.

## Python3

 `class` `Snake(MovingEntity):` `    ``"""Snake entity"""`   `    ``def` `__init__(``self``, end_pos``=``None``):` `        ``super``(Snake, ``self``).__init__(end_pos)` `        ``self``.desc ``=` `"Snake"`     `class` `Ladder(MovingEntity):` `    ``"""Ladder entity"""`   `    ``def` `__init__(``self``, end_pos``=``None``):` `        ``super``(Ladder, ``self``).__init__(end_pos)` `        ``self``.desc ``=` `"Ladder"`

Step 4: Defining board class:

1. The board contains a moving entity that can be set at a specific position using a function(set_moving_entity), for e.g. to add a snake at position 10, we can just do set_moving_entity(10, snake)
2. Board also provides the next position from a given position. In this case, if the player lands on 10, and the board knows there is a snake at 10, it will return the end_pos of the snake. If no moving entity is present on the position, then just return the position itself.

## Python3

 `class` `Board:` `    ``"""` `    ``define board with size and moving entities` `    ``"""`   `    ``def` `__init__(``self``, size):` `        ``self``.size ``=` `size` `        ``self``.board ``=` `{}  ``# instead of using list, we can use map of {pos:moving_entity} to save space`   `    ``def` `get_size(``self``):` `        ``return` `self``.size`   `    ``def` `set_moving_entity(``self``, pos, moving_entity):` `        ``# set moving entity to pos` `        ``self``.board[pos] ``=` `moving_entity`   `    ``def` `get_next_pos(``self``, player_pos):` `        ``# get next pos given a specific position player is on` `        ``if` `player_pos > ``self``.size:` `            ``return` `player_pos` `        ``if` `player_pos ``not` `in` `self``.board:` `            ``return` `player_pos` `        ``return` `self``.board[player_pos].get_end_pos()`   `    ``def` `at_last_pos(``self``, pos):` `        ``if` `pos ``=``=` `self``.size:` `            ``return` `True` `        ``return` `False`

Step 5: Define the Dice class as it contains a side and exposes a roll method, which returns a random value from 1 to 6.

## Python3

 `class` `Dice:` `    ``def` `__init__(``self``, sides):` `      ``# no of sides in the dice` `        ``self``.sides ``=` `sides  `   `    ``def` `roll(``self``):` `        ``# return random number between 1 to sides` `        ``import` `random` `        ``ans ``=` `random.randrange(``1``, ``self``.sides ``+` `1``)` `        ``return` `ans`

Step 6: In this step we will define Game class and its functions as below points:

1. The Game class encapsulates everything needed to play the game i.e. players, dice, and board. Here we have last_rank(which is the last rank achieved in the game currently i.e. no of players finished playing), and we have a turn that states which player turn it is.
2. The game can be initialized using the initialize_game() function, which needs a board object, dice sides, and no_of_players.
3. The game can be played by calling the play() function:
1. The game is not finished until the last rank assigned to the player is not equal to the total players, this is checked by can_play().
2. The get_next_player() is used to get the next player to play, which is the player which has the current turn. In case the current turn player has already won, it returns the next player which still has not reached the last position
3. The dice roll is done in order to get the dice result.
4. Using the board we get the next position of the player.
5. If the next position is valid, i.e. within bounds(this is checked by can_move()), we change the player position to the next position
6. The next position for the player is set by move_player(), here we also check if the player is at the last position, so we assign the rank as well.
7. Next, we change the turn using change_turn(), here we will only change the turn if the current dice roll does not deal 6 or we have crossed the consecutive 3 6’s limit
8. We print the game state using print_game_state(), which prints all player and their current positions and move back to step 1.
9. After all, players have finished playing, we print the final game state i.e. ranks of each player using print_game_result()

## Python3

 `class` `Game:`   `    ``def` `__init__(``self``):` `        ``self``.board ``=` `None`  `# game board object` `        ``self``.dice ``=` `None`  `# game dice object` `        ``self``.players ``=` `[]  ``# list of game player objects` `        ``self``.turn ``=` `0`  `# curr turn` `        ``self``.winner ``=` `None` `        ``self``.last_rank ``=` `0`  `# last rank achieved` `        ``self``.consecutive_six ``=` `0`  `# no of consecutive six in one turn, resets every turn`   `    ``def` `initialize_game(``self``, board: Board, dice_sides, players):` `        ``"""` `      ``Initialize game using board, dice and players` `      ``"""` `        ``self``.board ``=` `board` `        ``self``.dice ``=` `Dice(dice_sides)` `        ``self``.players ``=` `[GamePlayer(i) ``for` `i ``in` `range``(players)]`   `    ``def` `can_play(``self``):` `        ``if` `self``.last_rank !``=` `len``(``self``.players):` `            ``return` `True` `        ``return` `False`   `    ``def` `get_next_player(``self``):` `        ``"""` `      ``Return curr_turn player but if it has already won/completed game , return` `      ``next player which is still active` `      ``"""` `        ``while` `True``:` `            ``# if rank is -1 , player is still active so return` `            ``if` `self``.players[``self``.turn].get_rank() ``=``=` `-``1``:` `                ``return` `self``.players[``self``.turn]` `            ``# check next player` `            ``self``.turn ``=` `(``self``.turn ``+` `1``) ``%` `len``(``self``.players)`   `    ``def` `move_player(``self``, curr_player, next_pos):` `        ``# Move player to next_pos` `        ``curr_player.set_position(next_pos)` `        ``if` `self``.board.at_last_pos(curr_player.get_pos()):` `            ``# if at last position set rank` `            ``curr_player.set_rank(``self``.last_rank ``+` `1``)` `            ``self``.last_rank ``+``=` `1`   `    ``def` `can_move(``self``, curr_player, to_move_pos):` `        ``# check if player can move or not ie. between board bound` `        ``if` `to_move_pos <``=` `self``.board.get_size() ``and` `curr_player.get_rank() ``=``=` `-``1``:` `            ``return` `True` `        ``return` `False`   `    ``def` `change_turn(``self``, dice_result):` `        ``# change player turn basis dice result.` `        ``# if it's six, do not change .` `        ``# if it's three consecutive sixes or not a six, change` `        ``self``.consecutive_six ``=` `0` `if` `dice_result !``=` `6` `else` `self``.consecutive_six ``+` `1` `        ``if` `dice_result !``=` `6` `or` `self``.consecutive_six ``=``=` `3``:` `            ``if` `self``.consecutive_six ``=``=` `3``:` `                ``print``(``"Changing turn due to 3 consecutive sixes"``)` `            ``self``.turn ``=` `(``self``.turn ``+` `1``) ``%` `len``(``self``.players)` `        ``else``:` `            ``print``(f``"One more turn for player {self.turn+1} after rolling 6"``)`   `    ``def` `play(``self``):` `        ``"""` `      ``starting point of game` `      ``game will be player until all players have not been assigned a rank` `      ``# get curr player to play` `      ``# roll dice` `      ``# get next pos of player` `      ``# see if pos is valid to move` `      ``# move player to next pos` `      ``# change turn` `      ``Note: Currently everything is automated, ie. dice roll input is not taken from user.` `      ``if required , we can change that to give some control to user.` `      ``"""` `        ``while` `self``.can_play():` `            ``curr_player ``=` `self``.get_next_player()` `            ``player_input ``=` `input``(` `                ``f``"Player {self.turn+1}, Press enter to roll the dice"``)` `            ``dice_result ``=` `self``.dice.roll()` `            ``print``(f``'dice_result: {dice_result}'``)` `            ``_next_pos ``=` `self``.board.get_next_pos(` `                ``curr_player.get_pos() ``+` `dice_result)` `            ``if` `self``.can_move(curr_player, _next_pos):` `                ``self``.move_player(curr_player, _next_pos)` `            ``self``.change_turn(dice_result)` `            ``self``.print_game_state()` `        ``self``.print_game_result()`   `    ``def` `print_game_state(``self``):` `        ``# Print state of game after every turn` `        ``print``(``'-------------game state-------------'``)` `        ``for` `ix, _p ``in` `enumerate``(``self``.players):` `            ``print``(f``'Player: {ix+1} is at pos {_p.get_pos()}'``)` `        ``print``(``'-------------game state-------------\n\n'``)`   `    ``def` `print_game_result(``self``):` `        ``# Print final game result with ranks of each player` `        ``print``(``'-------------final result-------------'``)` `        ``for` `_p ``in` `sorted``(``self``.players, key``=``lambda` `x: x.get_rank()):` `            ``print``(f``'Player: {_p._id+1} , Rank: {_p.get_rank()}'``)`

### Complete Code:

In the code below, there is a sample_run() function defined, which will start the game basis the current configuration defined. We can change the configurations as per our liking and start a new game using any size of the board, any size of dice, and any number of players.

## Python3

 `class` `GamePlayer:` `    ``"""` `    ``Encapsulates a player properties` `    ``"""`   `    ``def` `__init__(``self``, _id):` `        ``self``._id ``=` `_id` `        ``# initial dummy rank -1` `        ``self``.rank ``=` `-``1` `        ``# starting position for every player is 1` `        ``self``.position ``=` `1`   `    ``def` `set_position(``self``, pos):` `        ``self``.position ``=` `pos`   `    ``def` `set_rank(``self``, rank):` `        ``self``.rank ``=` `rank`   `    ``def` `get_pos(``self``):` `        ``return` `self``.position`   `    ``def` `get_rank(``self``):` `        ``return` `self``.rank`     `class` `MovingEntity:` `    ``"""` `    ``You can create any moving entity , like snake or ladder or` `    ``wormhole by extending this` `    ``"""`   `    ``def` `__init__(``self``, end_pos``=``None``):` `      ``# end pos where player would be send on board` `        ``self``.end_pos ``=` `end_pos` `        ``# description of moving entity` `        ``self``.desc ``=` `None`   `    ``def` `set_description(``self``, desc):` `        ``self``.desc ``=` `None`   `    ``def` `get_end_pos(``self``):` `        ``if` `self``.end_pos ``is` `None``:` `            ``raise` `Exception(``"no_end_position_defined"``)` `        ``return` `self``.end_pos`     `class` `Snake(MovingEntity):` `    ``"""Snake entity"""`   `    ``def` `__init__(``self``, end_pos``=``None``):` `        ``super``(Snake, ``self``).__init__(end_pos)` `        ``self``.desc ``=` `"Bit by Snake"`     `class` `Ladder(MovingEntity):` `    ``"""Ladder entity"""`   `    ``def` `__init__(``self``, end_pos``=``None``):` `        ``super``(Ladder, ``self``).__init__(end_pos)` `        ``self``.desc ``=` `"Climbed Ladder"`     `class` `Board:` `    ``"""` `    ``define board with size and moving entities` `    ``"""`   `    ``def` `__init__(``self``, size):` `        ``self``.size ``=` `size` `        ``# instead of using list, we can use map of` `        ``# {pos:moving_entity} to save space` `        ``self``.board ``=` `{}`   `    ``def` `get_size(``self``):` `        ``return` `self``.size`   `    ``def` `set_moving_entity(``self``, pos, moving_entity):` `        ``# set moving entity to pos` `        ``self``.board[pos] ``=` `moving_entity`   `    ``def` `get_next_pos(``self``, player_pos):` `        ``# get next pos given a specific position player is on` `        ``if` `player_pos > ``self``.size:` `            ``return` `player_pos` `        ``if` `player_pos ``not` `in` `self``.board:` `            ``return` `player_pos` `        ``print``(f``'{self.board[player_pos].desc} at {player_pos}'``)` `        ``return` `self``.board[player_pos].get_end_pos()`   `    ``def` `at_last_pos(``self``, pos):` `        ``if` `pos ``=``=` `self``.size:` `            ``return` `True` `        ``return` `False`     `class` `Dice:` `    ``def` `__init__(``self``, sides):` `      ``# no of sides in the dice` `        ``self``.sides ``=` `sides`   `    ``def` `roll(``self``):` `        ``# return random number between 1 to sides` `        ``import` `random` `        ``ans ``=` `random.randrange(``1``, ``self``.sides ``+` `1``)` `        ``return` `ans`     `class` `Game:`   `    ``def` `__init__(``self``):` `      ``# game board object` `        ``self``.board ``=` `None` `        ``# game dice object` `        ``self``.dice ``=` `None` `        ``# list of game player objects` `        ``self``.players ``=` `[]` `        ``# curr turn` `        ``self``.turn ``=` `0` `        ``self``.winner ``=` `None` `        ``# last rank achieved` `        ``self``.last_rank ``=` `0` `        ``# no of consecutive six in one turn, resets every turn` `        ``self``.consecutive_six ``=` `0`   `    ``def` `initialize_game(``self``, board: Board, dice_sides, players):` `        ``"""` `      ``Initialize game using board, dice and players` `      ``"""` `        ``self``.board ``=` `board` `        ``self``.dice ``=` `Dice(dice_sides)` `        ``self``.players ``=` `[GamePlayer(i) ``for` `i ``in` `range``(players)]`   `    ``def` `can_play(``self``):` `        ``if` `self``.last_rank !``=` `len``(``self``.players):` `            ``return` `True` `        ``return` `False`   `    ``def` `get_next_player(``self``):` `        ``"""` `      ``Return curr_turn player but if it has already won/completed game , return` `      ``next player which is still active` `      ``"""` `        ``while` `True``:` `            ``# if rank is -1 , player is still active so return` `            ``if` `self``.players[``self``.turn].get_rank() ``=``=` `-``1``:` `                ``return` `self``.players[``self``.turn]` `            ``# check next player` `            ``self``.turn ``=` `(``self``.turn ``+` `1``) ``%` `len``(``self``.players)`   `    ``def` `move_player(``self``, curr_player, next_pos):` `        ``# Move player to next_pos` `        ``curr_player.set_position(next_pos)` `        ``if` `self``.board.at_last_pos(curr_player.get_pos()):` `            ``# if at last position set rank` `            ``curr_player.set_rank(``self``.last_rank ``+` `1``)` `            ``self``.last_rank ``+``=` `1`   `    ``def` `can_move(``self``, curr_player, to_move_pos):` `        ``# check if player can move or not ie. between board bound` `        ``if` `to_move_pos <``=` `self``.board.get_size() ``and` `curr_player.get_rank() ``=``=` `-``1``:` `            ``return` `True` `        ``return` `False`   `    ``def` `change_turn(``self``, dice_result):` `        ``# change player turn basis dice result.` `        ``# if it's six, do not change .` `        ``# if it's three consecutive sixes or not a six, change` `        ``self``.consecutive_six ``=` `0` `if` `dice_result !``=` `6` `else` `self``.consecutive_six ``+` `1` `        ``if` `dice_result !``=` `6` `or` `self``.consecutive_six ``=``=` `3``:` `            ``if` `self``.consecutive_six ``=``=` `3``:` `                ``print``(``"Changing turn due to 3 consecutive sixes"``)` `            ``self``.turn ``=` `(``self``.turn ``+` `1``) ``%` `len``(``self``.players)` `        ``else``:` `            ``print``(f``"One more turn for player {self.turn+1} after rolling 6"``)`   `    ``def` `play(``self``):` `        ``"""` `      ``starting point of game` `      ``game will be player until all players have not been assigned a rank` `      ``# get curr player to play` `      ``# roll dice` `      ``# get next pos of player` `      ``# see if pos is valid to move` `      ``# move player to next pos` `      ``# change turn` `      ``Note: Currently everything is automated, ie. dice roll input is not taken from user.` `      ``if required , we can change that to give some control to user.` `      ``"""` `        ``while` `self``.can_play():` `            ``curr_player ``=` `self``.get_next_player()` `            ``player_input ``=` `input``(` `                ``f``"Player {self.turn+1}, Press enter to roll the dice"``)` `            ``dice_result ``=` `self``.dice.roll()` `            ``print``(f``'dice_result: {dice_result}'``)` `            ``_next_pos ``=` `self``.board.get_next_pos(` `                ``curr_player.get_pos() ``+` `dice_result)` `            ``if` `self``.can_move(curr_player, _next_pos):` `                ``self``.move_player(curr_player, _next_pos)` `            ``self``.change_turn(dice_result)` `            ``self``.print_game_state()` `        ``self``.print_game_result()`   `    ``def` `print_game_state(``self``):` `        ``# Print state of game after every turn` `        ``print``(``'-------------game state-------------'``)` `        ``for` `ix, _p ``in` `enumerate``(``self``.players):` `            ``print``(f``'Player: {ix+1} is at pos {_p.get_pos()}'``)` `        ``print``(``'-------------game state-------------\n\n'``)`   `    ``def` `print_game_result(``self``):` `        ``# Print final game result with ranks of each player` `        ``print``(``'-------------final result-------------'``)` `        ``for` `_p ``in` `sorted``(``self``.players, key``=``lambda` `x: x.get_rank()):` `            ``print``(f``'Player: {_p._id+1} , Rank: {_p.get_rank()}'``)`     `def` `sample_run():` `    ``# a simple flow of the game` `    ``# here we create a board of size 10 and dice of side 6` `    ``# set snake at 5 with end at 2` `    ``# set ladder at 4 with end at 6` `    ``board ``=` `Board(``10``)` `    ``board.set_moving_entity(``7``, Snake(``2``))` `    ``board.set_moving_entity(``4``, Ladder(``6``))` `    ``game ``=` `Game()` `    ``game.initialize_game(board, ``6``, ``2``)` `    ``game.play()`     `sample_run()`

Output:

```Player 1, Press enter to roll the dice
dice_result: 4
-------------game state-------------
Player: 1 is at pos 5
Player: 2 is at pos 1
-------------game state-------------

Player 2, Press enter to roll the dice
dice_result: 2
-------------game state-------------
Player: 1 is at pos 5
Player: 2 is at pos 3
-------------game state-------------

Player 1, Press enter to roll the dice
dice_result: 1
-------------game state-------------
Player: 1 is at pos 6
Player: 2 is at pos 3
-------------game state-------------

Player 2, Press enter to roll the dice
dice_result: 1
-------------game state-------------
Player: 1 is at pos 6
Player: 2 is at pos 6
-------------game state-------------

Player 1, Press enter to roll the dice
dice_result: 5
-------------game state-------------
Player: 1 is at pos 6
Player: 2 is at pos 6
-------------game state-------------

Player 2, Press enter to roll the dice
dice_result: 5
-------------game state-------------
Player: 1 is at pos 6
Player: 2 is at pos 6
-------------game state-------------

Player 1, Press enter to roll the dice
dice_result: 1
Bit by Snake at 7
-------------game state-------------
Player: 1 is at pos 2
Player: 2 is at pos 6
-------------game state-------------

......

Player 2, Press enter to roll the dice
dice_result: 1
-------------game state-------------
Player: 1 is at pos 10
Player: 2 is at pos 10
-------------game state-------------

-------------final result-------------
Player: 1 , Rank: 1
Player: 2 , Rank: 2```

My Personal Notes arrow_drop_up