Snakes and Ladders random world generator second implementation The following is an implementation of a random world generator for the classic Snakes and Ladders game. This is a follow up to this question, where I reviewed my own original implementation. I am asking for a review of my review.

The world rules are:


The head of a snake can not be on the last square (you'll never win)
The base of a ladder can not be on the last square (you'll have no where to go)
A square can only have one thing, either the head or base of a ladder or a snake




class World
  attr_reader :size, :snakes, :ladders

  def initialize(size, snakes = 10, ladders = 10)
    @size = size
    @snakes, @ladders = {}, {}
    place_snakes(snakes)
    place_ladders(ladders)
  end

  def place_snakes(count)
    count.times do
      start_point = place_point
      end_point_range = habitable_range.begin..(start_point - 1)

      snakes[start_point] = place_point(end_point_range)
    end
  end

  def place_ladders(count)
    count.times do
      start_point = place_point
      end_point_range = (start_point + 1)..habitable_range.end

      ladders[start_point] = place_point(end_point_range)
    end
  end

private

  def place_point(range = habitable_range)
    begin
      point = rand(range)
    end while occupied?(point)
    point
  end

  def occupied?(number)
    [snakes.keys, snakes.values, ladders.keys, ladders.values].flatten.include?(number)
  end

  def habitable_range
    1..(size - 1)
  end
end
Here's an attempt:

class World
  attr_reader :board

  def initialize(size, snakes_count = 10, ladders_count = 10)
    @board = Array.new(size * size)

    place snakes_count,  :snake
    place ladders_count, :ladder
  end

private

  def free_point
    point = rand(@board.size) until point &amp;&amp; @board[point].nil?
    point
  end

  def place(count, entity)
    count.times do
      start_point = free_point
      end_point   = free_point until end_point &amp;&amp; end_point != start_point &amp;&amp; (entity == :ladder || end_point != @board.size - 1)
      pair = [start_point, end_point].sort
      pair.reverse! if entity == :snake
      @board[pair.first] = @board[pair.last] = pair
    end
  end
end


The idea here is that:


A snake or ladder consists of a point pair. A snake is a desending pair (ie, go back from a higher point to a lower point), while a ladder is an ascending pair (go forward from a lower point to a higher point)
A snake cannot have either of its points be the final point on the board.


The board is represented as a size x size array, so a world of size 12 would produce a board that is 12x12, or 144 squares. Checking for occupation is then as easy as just checking if @board[point].nil?. It also gives you an easy way to iterate the board for rendering or rules checking; as you're playing, if the player lands on square 63, you can check square 63. If it has a value, then check if v[0] > v[1] (snake) or otherwise (ladder).

If desired, you could have #place keep a separate list of snakes and ladders, but it's not strictly necessary, since you can think of them as the same construct (a movement from one point to another), differentiated only by the direction of movement.