Skip to content

Commit

Permalink
Merge branch 'main' into 1d_array_optimzation
Browse files Browse the repository at this point in the history
  • Loading branch information
pusewicz authored May 6, 2024
2 parents 612c34c + 102a64e commit 4d804df
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 43 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Wave Function Collapse in Ruby

This repository is an example implementation of the [Wave Function Collapse](https://github.com/mxgmn/WaveFunctionCollapse) algorithm using [Ruby](https://www.ruby-lang.org/) and [Gosu](https://www.libgosu.org/).

The algorithm is used to generate the entire map using tiles based on the rules. Rules are defined using the [Tiled](https://www.mapeditor.org/) program and read by the Ruby program from the [TSJ](https://github.com/pusewicz/wave-function-collapse-ruby/blob/main/assets/map.tsj) file.

![Wave Function Collapse in Ruby](assets/screenshot.png)

## Objective
Expand All @@ -14,14 +18,17 @@ Run `bin/run` to run the example.

## Benchmark

Run `ruby test/benchmark.rb` to see how fast the algorithm is.
Run `bin/benchmark` to see how fast the algorithm is.

#### Apple M3 Max 64GB (2023): 12.437260s

$ ruby test/benchmark.rb
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
Running benchmark for Model(grid=20x20 entropy=188)...
12.362356 0.020133 12.382489 ( 12.437260)
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
Run #1: Benchmark for Model(grid=20x20 entropy=188)… Finished in 11.65s
Run #2: Benchmark for Model(grid=20x20 entropy=188)… Finished in 13.46s
Run #3: Benchmark for Model(grid=20x20 entropy=188)… Finished in 11.89s
Average time: 12.33499966666568
Slowest time: 13.458501000000979
Fastest time: 11.65374900000461

## Contributing

Expand Down
41 changes: 41 additions & 0 deletions bin/benchmark
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

$LOAD_PATH.unshift File.expand_path("../lib", __dir__)

require "benchmark"
require "json"
require "wave_function_collapse"

WIDTH = 20
HEIGHT = 20

json = JSON.load_file!("assets/map.tsj")
tiles =
json["wangsets"].last["wangtiles"].map do |tile|
prob = json["tiles"]&.find { |t| t["id"] == tile["tileid"] }&.fetch("probability")
WaveFunctionCollapse::Tile.new(
tileid: tile["tileid"],
wangid: tile["wangid"],
probability: prob
)
end
times = []

puts RUBY_DESCRIPTION

times = 3.times.map { |i|
time = Benchmark.realtime {
model = WaveFunctionCollapse::Model.new(tiles, WIDTH, HEIGHT)
print "Run ##{i + 1}: Benchmark for Model(grid=#{model.width}x#{model.height} entropy=#{model.max_entropy})… "
until model.complete?
model.solve
end
}
puts "Finished in #{time.round(2)}s"
time
}

puts "Average time: #{times.sum / times.size}"
puts "Slowest time: #{times.max}"
puts "Fastest time: #{times.min}"
8 changes: 4 additions & 4 deletions lib/wave_function_collapse/cell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ def neighbors(model)
return if model.nil?

@neighbors[model.width * y + x] ||= begin
up = model.grid_cell(@x, @y + 1) if @y < model.height - 1
down = model.grid_cell(@x, @y - 1) if @y.positive?
right = model.grid_cell(@x + 1, @y) if @x < model.width - 1
left = model.grid_cell(@x - 1, @y) if @x.positive?
up = model.cell_at(@x, @y + 1) if @y < model.height - 1
down = model.cell_at(@x, @y - 1) if @y.positive?
right = model.cell_at(@x + 1, @y) if @x < model.width - 1
left = model.cell_at(@x - 1, @y) if @x.positive?

{up: up, down: down, right: right, left: left}
end
Expand Down
8 changes: 3 additions & 5 deletions lib/wave_function_collapse/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ def initialize(tiles, width, height)
@tiles = tiles
@width = width.to_i
@height = height.to_i

# @grid = Array.new(width) { |x| Array.new(height) { |y| Cell.new(x, y, @tiles.shuffle) } }
@grid = []
@height.times { |y| @width.times { |x| @grid << Cell.new(x, y, @tiles.shuffle) } }
@uncollapsed_cells_grid = @grid.reject(&:collapsed)
@max_entropy = @tiles.length
end

def grid_cell(x, y)
def cell_at(x, y)
@grid[@width * y + x]
end

Expand Down Expand Up @@ -93,7 +91,7 @@ def generate_grid
y = 0

while y < @height
rx[y] = grid_cell(x, y).tile
rx[y] = cell_at(x, y).tile
y += 1
end
x += 1
Expand Down Expand Up @@ -141,7 +139,7 @@ def evaluate_neighbor(source_cell, evaluation_direction)
while nci < ncil
tile = neighbor_tiles[nci]
nci += 1
next if new_tile_ids[tile.tileid]
next if new_tile_ids.has_key?(tile.tileid)

tile_edge_hash = tile.send(opposite_direction)
if tile_edge_hash == source_edge_hash
Expand Down
2 changes: 1 addition & 1 deletion lib/wave_function_collapse/window.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def draw_map
column.reverse.each_with_index do |tile, y|
inverted_y = (y - @model.height + 1).abs

entropy = @model.grid_cell(x, inverted_y).entropy
entropy = @model.cell_at(x, inverted_y).entropy

if entropy > 1
percent_entropy = (entropy.to_f / @model.max_entropy * 255).round
Expand Down
28 changes: 0 additions & 28 deletions test/benchmark.rb

This file was deleted.

0 comments on commit 4d804df

Please sign in to comment.