loading...

Daily Challenge #207 - Snakes and Ladders

thepracticaldev profile image dev.to staff ・2 min read

Implement a function called SnakesLadders to play a two-player game of Snakes and Ladders. The function should keep track of player turns and player position on the board (of the 100 spaces). SnakesLadders should call a method play(die1, die2) independently of the state of the game or the player turn. These variables are the die thrown in a turn and are both integers between 1 and 6. The player moves the sum of die1 and die 2.

S&L Gameboard

Rules

  1. There are two players and both start off the board on square 0.
  2. Player 1 starts and alternates with player 2.
  3. You follow the numbers up the board in order 1=>100
  4. If the value of both die are the same then that player will have another go.
  5. Climb up ladders. The ladders on the game board allow you to move upwards and get ahead faster. If you land exactly on a square that shows an image of the bottom of a ladder, then you may move the player all the way up to the square at the top of the ladder. (even if you roll a double).
  6. Slide down snakes. Snakes move you back on the board because you have to slide down them. If you land exactly at the top of a snake, slide move the player all the way to the square at the bottom of the snake or chute. (even if you roll a double).
  7. Land exactly on the last square to win. The first person to reach the highest square on the board wins. But there's a twist! If you roll too high, your player "bounces" off the last square and moves back. You can only win by rolling the exact number needed to land on the last square. For example, if you are on square 98 and roll a five, move your game piece to 100 (two moves), then "bounce" back to 99, 98, 97 (three, four then five moves.)

Return Player n Wins!. Where n is winning player that has landed on square 100 without any remaining moves left.

Return Game over! if a player has won and another player tries to play.

Otherwise return Player n is on square x. Where n is the current player and x is the square they are currently on.

Good luck!


This challenge comes from adrian.eyre on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

markdown guide
 

Scala

Instead of saving the state internally, the play function receives a state object and returns a new state object to keep it pure.

object LaddersAndSnakes {
  private val links = Map(
    2 -> 38,
    7 -> 14,
    8 -> 31,
    15 -> 26,
    16 -> 6,
    21 -> 42,
    28 -> 84,
    36 -> 44,
    46 -> 25,
    49 -> 11,
    51 -> 67,
    62 -> 19,
    64 -> 60,
    71 -> 91,
    74 -> 53,
    78 -> 98,
    87 -> 94,
    92 -> 88,
    95 -> 75,
    99 -> 80,
  )

  sealed trait Player
  case object Player1 extends Player
  case object Player2 extends Player

  sealed trait State
  case class Playing(p1: Int, p2: Int, next: Player) extends State {
    val nextPos = next match {
      case Player1 => p1
      case Player2 => p2
    }
  }
  case class Win(p: Player) extends State
  case object GameOver extends State

  object State {
    def start (): State = Playing(1, 1, Player1)
  }

  type Roll = (Int, Int)


  def play (s: State, r: Roll): State = s match {
    case GameOver           => GameOver
    case Win(_)             => GameOver
    case Playing(p1, p2, n) => nextTurn(Playing(p1, p2, n), r)
  }

  private def nextTurn (s: Playing, roll: Roll): State = {
    val Playing(p1, p2, next) = s
    val player = s.nextPos
    val nextPos = updatePosition(player, roll)

    if (nextPos == 100) Win(next)
    else if (isDouble(roll)) next match {
      case Player1 => Playing(nextPos, p2, Player1)
      case Player2 => Playing(p1, nextPos, Player2)
    }
    else next match {
      case Player1 => Playing(nextPos, p2, Player2)
      case Player2 => Playing(p1, nextPos, Player1)
    }
  }

  private def updatePosition (pos: Int, roll: Roll): Int = {
    val (d1, d2) = roll
    val nextPos = clamp(pos + d1 + d2, 100)

    links get nextPos match {
      case None    => nextPos
      case Some(v) => v
    }
  }

  private def clamp (v: Int, limit: Int): Int = {
    if (v <= limit) v
    else limit - (v - limit)
  }

  private def isDouble (r: Roll): Boolean = {
    r._1 == r._2
  }
}
 
 

It was fun

#include <bits/stdc++.h>
using namespace std;

// returns the sum of numbers in die1 and die2 and also whether they are same or not
// Example : die1 = 4, die2 = 5  => return <9, false>
// Example : die1 = 3, die2 = 3  => return <6, true>
pair<int, bool> play(){
    int die1 = rand()%6 + 1;
    int die2 = rand()%6 + 1;

    return make_pair(die1 + die2, die1==die2);
}

// main snake and ladder game
void SnakesLadders(){
    srand(time(0));

    // differnt positions of snake and ladders
    // first one is starting position and second one is ending position
    unordered_map<int, int> snakeLadderPos = {
        {99,80},
        {95,75},
        {92,88},
        {89,68},
        {74,53},
        {64,60},
        {62,19},
        {49,11},
        {46,25},
        {16,6},
        {2,38},
        {7,14},
        {8,31},
        {15,26},
        {21,42},
        {28,84},
        {36,44},
        {51,67},
        {71,91},
        {78,98},
        {87,94}
    };

    int playerPos[2] = {0,0}; // positions of player1 and player2 respectively
    int turn = 0; // 0 if it is player1's turn, 1 if player2's turn

    while(true){
        pair<int, bool> move = play(); // roll the dies
        playerPos[turn] += move.first; // make the move

        // prints the move
        cout << "Player " << turn+1 << "'s turn : " << move.first;
        if(move.second)
            cout << " and double chance";
        cout << "\n";

        // bounce off if move takes player to position greater than 100
        if(playerPos[turn] > 100){
            cout << "Player " << turn+1 << " bounces off from " << playerPos[turn] << " to ";
            playerPos[turn] = 200 - playerPos[turn];
            cout << playerPos[turn] << ".\n";
        }

        // if snake or ladder then go the position indicated
        if(snakeLadderPos.find(playerPos[turn]) != snakeLadderPos.end()){
            if(playerPos[turn] > snakeLadderPos[playerPos[turn]])
                cout << "Player " << turn+1 << " is snaked from " << playerPos[turn] << " to ";
            else
                cout << "Player " << turn+1 << " is laddered from " << playerPos[turn] << " to ";
            playerPos[turn] = snakeLadderPos[playerPos[turn]];
            cout  << playerPos[turn] << "\n";
        }


        // this part handles the case if the player reaches the goal or not
        // if goal is reached then prints the winner and break the loop
        if(playerPos[turn] == 100){
            cout << "Player " << turn+1 << " wins!\nGame over for Player " << 2-turn << ".\n";
            break;
        }

        // if player does not have a second move
        // then its turn for other player
        if(!move.second)
            turn = 1 - turn;
        cout << "Player 1 is on square "<< playerPos[0] << ".\n";
        cout << "Player 2 is on square "<< playerPos[1] << ".\n\n";
        this_thread::sleep_for(chrono::milliseconds(500)); // prints after 0.5sec gap
    }
}

// main function
int main(){
    SnakesLadders();
    return 0;
}
 

Vanilla JS

It was fun doing it. Will appreciate any feedback.