Snakes and Ladders (OO principles)

Emma Priester
4 min readMar 10, 2021

After today’s pairing session I did not feel quite satisfied yet. We had a bit of a gap between where I ended yesterday and where she ended yesterday, so I didn’t get to do any coding. I decided to get some coding done through katas on code wars and got especially interested in creating a game of ladders and snakes. I am going to assume that you know the rules of the game, if not this is the kata I worked on; https://www.codewars.com/kata/587136ba2eefcb92a9000027/train/ruby.

I got started with a class I called Turns.

describe Turns do
let(:turn) { Turns.new }

describe ‘#count_dice’ do
it ‘should count up the dice’ do
expect(turn.count_dice(3, 3)).to eq 6
end
end

describe ‘#equal?’ do
it ‘should know if the value of both dice is equal’ do
expect(turn.equal?(3, 3)).to eq true
end
it ‘should know if the value is not equal’ do
expect(turn.equal?(4, 2)).to eq false
end
end
end

This would be quite a simple class, but very important for the game. The class can count up the dice, assuming you throw two dice, and it can see if the two dice are the same, or if they are different.

class Turns

attr_reader :die1, :die2

def count_dice(die1, die2)
[die1, die2].sum
end

def equal?(die1, die2)
die1 == die2
end

end

Next, I took a look at a class that I would call Player, this is going to be each player, they can make the moves on the board.

describe Player do
let(:player) { Player.new }

describe ‘#next_player?’ do
it ‘should decide if the player can go again’ do
expect(player.next_player?(1, 1)).to eq false
end
it ‘should know the next player can go’ do
expect(player.next_player?(4, 1 )).to eq true
end
end

describe ‘#location’ do
it ‘should know its current location’ do
expect(player.location).to eq 0
end
end

describe ‘#move’ do
it ‘should ba able ot make a normal move’ do
expect(player.move(1, 4)).to eq 5
end
it ‘should be able to move its location with ladder’ do
player.move(4, 2)
expect(player.location).to eq 6
end
it ‘should be able to move its location with snake’ do
expect(player.move(45, 1)).to eq 25
end
end

describe ‘#finish’ do
it ‘should know when the player has won’ do
expect(player.move(98, 2)).to eq ‘Congratz! You won’
end
end

describe ‘#near_the_end’ do
it ‘should go backwards if gone over 100’ do

expect(player.move(98, 4)).to eq 98
end
end
end

To make the move, we have to know where the snakes and ladders are as well. I gave them to this class as a constant, because all games of snakes and ladders should be the same.

class Player
LOCATION_LADDERS = {
2 => 38, 7 => 14, 8 => 31, 15 => 26, 21 => 42, 36 => 44, 51 => 67, 71 => 91, 78 => 98, 87 => 94
}
LOCATION_SNAKES = {
16 => 6, 46 => 25, 49 => 11, 62 => 19, 64 => 60, 74 => 53, 89 => 68, 92 => 88, 95 => 75, 99 => 80
}

attr_reader :location

def initialize(location = 0)
@location = location
@turn = Turns.new
@moving_back = 0
end

def move(die1, die2)
@location = [@location += @turn.count_dice(die1, die2), LOCATION_LADDERS[@location], LOCATION_SNAKES[@location]].compact.last
near_the_end
congratz
end

def ladders_or_snakes
[@location += @turn.count_dice(die1, die2), LOCATION_LADDERS[@location], LOCATION_SNAKES[@location]].compact.last
end

def next_player?(die1, die2)
!@turn.equal?(die1, die2)
end

def finish
@location == 100
end

def congratz
finish ? “Congratz! You won” : @location
end

def near_the_end
@location >= 100 ? @location -= ((@location — 100) * 2) : @location
end

end

Lastly, I wanted to bring it all together in a SnakesAndLadders class, that would call for all the methods in the right order.

describe SnakesAndLadders do
let(:game) { SnakesAndLadders.new }
let(:player) { Player.new }

describe ‘#player1_’ do
let(:die1) { 1 }
let(:die2) { 1 }
it ‘should roll dice and move a piece for a player’ do
expect(game.player1_(die1, die2)).to eq “Player 1 is on square #{player.move(die1,die2)}”
end
end

describe ‘#player2_’ do
let(:die1) { 4 }
let(:die2) { 1 }
it ‘should roll dice and move player 2’ do
expect(game.player2_(die1, die2)).to eq “Player 2 is on square #{player.move(die1, die2)}”
end
end

describe ‘#switch_turn’ do
it ‘should switch whose turn it is’ do
player1 = Player.new
game.player1_(1, 4)
expect(game.turn)
end
end

describe ‘#game_over’ do
it ‘should say game over if a player has won’ do
game.play(98, 2)
expect(game.play(5, 1)).to eq ‘Game over’
end
end
end

describe Player do
let(:player) { Player.new }

describe ‘#next_player?’ do
it ‘should decide if the player can go again’ do
expect(player.next_player?(1, 1)).to eq false
end
it ‘should know the next player can go’ do
expect(player.next_player?(4, 1 )).to eq true
end
end

describe ‘#location’ do
it ‘should know its current location’ do
expect(player.location).to eq 0
end
end

describe ‘#move’ do
it ‘should ba able ot make a normal move’ do
expect(player.move(1, 4)).to eq 5
end
it ‘should be able to move its location with ladder’ do
player.move(4, 2)
expect(player.location).to eq 6
end
it ‘should be able to move its location with snake’ do
expect(player.move(45, 1)).to eq 25
end
end

describe ‘#finish’ do
it ‘should know when the player has won’ do
expect(player.move(98, 2)).to eq ‘Congratz! You won’
end
end

describe ‘#near_the_end’ do
it ‘should go backwards if gone over 100’ do

expect(player.move(98, 4)).to eq 98
end
end
end

This class will also return to us what location our players are on after each move, and shout game over if a player won, and the other keeps going.

class SnakesAndLadders

attr_reader :turn

def initialize
@player1 = Player.new
@player2 = Player.new
@turn = @player1
end

def play(die1, die2)
@turn == @player1 ? player1_(die1, die2) : player2_(die1, die2)
end

def player1_(die1, die2)
moved = @player1.move(die1, die2)
return ‘Game over’ if @player2.finish
switch_turn(die1, die2)
“Player 1 is on square #{moved}”
end

def player2_(die1, die2)
moved = @player2.move(die1, die2)
return ‘Game over’ if @player1.finish
switch_turn(die1, die2)
“Player 2 is on square #{moved}”
end

def switch_turn(die1, die2)
if @turn == @player1 && @player1.next_player?(die1, die2)
@turn = @player2
elsif @turn == @player2 && @player2.next_player?(die1, die2)
@turn = @player1
end
end

end

This is what I did today. It does not seem to be working perfectly quite yet, but my brain cannot take anything more today. I will update the git once I have the energy to improve it. You can find that here: https://github.com/Emmapr123/snakes_and_ladders

--

--