class Worldgen::PlateMap

A map that generates plates within a square map.

Attributes

size[R]

Public Class Methods

new(size) click to toggle source
# File lib/worldgen/platemap.rb, line 87
def initialize size
  @size = size
  @plate_ids = nil
  @plates = nil
end

Public Instance Methods

each_plate_point() { |x, y, plate_ids[y]| ... } click to toggle source

Iterate across the entire map.

Usage:

each_plate_point do |x, y, plate_id|
  # do something with this information
end
# File lib/worldgen/platemap.rb, line 129
def each_plate_point
  (0...@size).each do |x|
    (0...@size).each do |y|
      yield x, y, @plate_ids[x][y]
    end
  end
end
generate_plates!(num_plates, verbose=true) click to toggle source

Generate plates within this map.

Arguments:

  • num_plates - the number of plates to generate

  • verbose (default: true) - output logging while we're generating

# File lib/worldgen/platemap.rb, line 142
def generate_plates! num_plates, verbose=true
  @plate_ids = Array.new(@size) { Array.new(@size) { -1 }}

  # Initialize plates in random spots
  @plates = (0...num_plates).map do |plate_num|
    # Find an unoccupied point
    point = nil
    while point == nil
      x, y = rand(@size), rand(@size)

      point = [x, y] if @plate_ids[x][y] < 0
    end

    x, y = point
    @plate_ids[x][y] = plate_num

    Plate.new.tap do |plate|
      plate.map = @plate_ids
      plate.id = plate_num
      plate.type = rand < 0.5 ? :continent : :ocean
      plate.seed = point
    end
  end

  num_points = self.num_points - num_plates
  valid_plates = @plates.select(&:has_frontier?)

  i = 0
  while valid_plates.length > 0
    if verbose and i % (num_points / 100) == 0
      puts "#{i}/#{num_points} #{(i.fdiv(num_points) * 100).round}%"
    end

    # Find a plate with neighbours
    loop do
      idx = choose_plate valid_plates
      plate = valid_plates[idx]

      # absorb a point from the frontier
      value = plate.absorb_frontier!

      if not value
        valid_plates.delete_at idx
        break if valid_plates.length == 0
      else
        # Did we just consume the last point of this plate?
        valid_plates.delete_at(idx) if not plate.has_frontier?
        break
      end
    end

    i += 1
  end
end
num_points() click to toggle source

Get the number of points in the map.

# File lib/worldgen/platemap.rb, line 94
def num_points
  @size * @size
end
to_height_map(sea_gap) click to toggle source

Convert to a height map - very simple, just give the continents one height and the oceans another height. This is controlled by a single parameter, (({sea_gap})), which will be the difference between the continents height and oceans height.

# File lib/worldgen/platemap.rb, line 102
def to_height_map sea_gap
  raise "Sea gap should be between 0 and 1" if sea_gap < 0 or sea_gap > 1

  plate_heights = @plates.map do |plate|
    if plate.type == :continent
      0.5 + sea_gap / 2
    else
      0.5 - sea_gap / 2
    end
  end

  map = HeightMap.new @size
  
  each_plate_point do |x, y, id|
    map[x, y] = plate_heights[id]
  end

  map
end

Private Instance Methods

choose_plate(plates) click to toggle source

Choose a plate randomly from a list of plates

# File lib/worldgen/platemap.rb, line 200
def choose_plate plates
  # Weighted choice based on frontier length - if we do it perfectly uniform
  # then we end up with plates that "compress" into small remaining places
  # which results in "snaky" plates and weird convergence points between
  # many plates
  
  # TODO: could probably pull weighted randoms into a different module
  total = plates.map(&:frontier_length).inject(&:+)

  # There is the possibility of
  #return 0 if total == 0

  # TODO: using a uniform random generator here gives plates that are all
  # roughly the same size - kinda boring, maybe try using a non-uniform
  # distribution here
  point = rand(total)

  idx = 0
  begin
    while point > plates[idx].frontier_length
      point -= plates[idx].frontier_length
      idx += 1
    end
  rescue
    # TODO: fix this - once in a while we get an out of bounds problem here
    puts $!
    puts $!.backtrace.join("\n")
    puts "Point: #{point}"
    puts "Idx: #{idx}"
    puts "Total: #{total}"
  end

  idx
end