From 664e33a886a70bf1cb9a10e2fad411b834910924 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Mon, 6 May 2024 08:46:17 -0500 Subject: [PATCH 1/2] Switch Model@grid to a 1d array, added Model#grid_cell for getting 2d cell position from 1d array, use an integer (1d grid index) instead of a string for Cell@neighbor key --- lib/wave_function_collapse/cell.rb | 17 ++++++++------- lib/wave_function_collapse/model.rb | 32 +++++++++++++++++++--------- lib/wave_function_collapse/window.rb | 2 +- test/test_model.rb | 2 +- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/lib/wave_function_collapse/cell.rb b/lib/wave_function_collapse/cell.rb index e5b7a0f..9727ae2 100644 --- a/lib/wave_function_collapse/cell.rb +++ b/lib/wave_function_collapse/cell.rb @@ -33,14 +33,15 @@ def collapse self.tiles = [@tiles.max_by { |t| rand**(1.0 / t.probability) }] end - def neighbors(grid) - return if grid.nil? - - @neighbors["#{@x},#{@y}"] ||= begin - up = grid[@x][@y + 1] if grid[@x] && @y < grid[0].length - 1 - down = grid[@x][@y - 1] if grid[@x] && @y.positive? - right = grid[@x + 1][@y] if @x < grid.length - 1 - left = grid[@x - 1][@y] if @x.positive? + 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: up, down: down, right: right, left: left} end end diff --git a/lib/wave_function_collapse/model.rb b/lib/wave_function_collapse/model.rb index 012efa6..16d220d 100644 --- a/lib/wave_function_collapse/model.rb +++ b/lib/wave_function_collapse/model.rb @@ -23,11 +23,17 @@ def initialize(tiles, width, height) @width = width.to_i @height = height.to_i - @grid = Array.new(width) { |x| Array.new(height) { |y| Cell.new(x, y, @tiles.shuffle) } } - @uncollapsed_cells_grid = @grid.flatten.reject(&:collapsed) + # @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) + @grid[@width * y + x] + end + def complete? @uncollapsed_cells_grid.empty? end @@ -53,6 +59,8 @@ def iterate end def prepend_empty_row + raise + x = 0 while x < @width @grid[x].shift @@ -79,18 +87,18 @@ def random_cell def generate_grid x = 0 result = [] - lx = @grid.length - while x < lx + + while x < @width rx = result[x] = [] y = 0 - pgx = @grid[x] - ly = pgx.length - while y < ly - rx[y] = pgx[y].tile + + while y < @height + rx[y] = grid_cell(x, y).tile y += 1 end x += 1 end + result end @@ -110,7 +118,7 @@ def propagate(source_cell) end def evaluate_neighbor(source_cell, evaluation_direction) - neighbor_cell = source_cell.neighbors(@grid)[evaluation_direction] + neighbor_cell = source_cell.neighbors(self)[evaluation_direction] return if neighbor_cell.nil? || neighbor_cell.collapsed original_tile_count = neighbor_cell.tiles.length @@ -122,16 +130,19 @@ def evaluate_neighbor(source_cell, evaluation_direction) neighbor_tiles = neighbor_cell.tiles sci = 0 scil = source_tiles.length + while sci < scil source_tile = source_tiles[sci] sci += 1 source_edge_hash = source_tile.send(evaluation_direction) nci = 0 ncil = neighbor_tiles.length + while nci < ncil tile = neighbor_tiles[nci] nci += 1 - next if new_tile_ids.has_key?(tile.tileid) + next if new_tile_ids[tile.tileid] + tile_edge_hash = tile.send(opposite_direction) if tile_edge_hash == source_edge_hash new_tile_ids[tile.tileid] = true @@ -139,6 +150,7 @@ def evaluate_neighbor(source_cell, evaluation_direction) end end end + neighbor_cell.tiles = new_tiles unless new_tiles.empty? @uncollapsed_cells_grid.delete(neighbor_cell) if neighbor_cell.collapsed diff --git a/lib/wave_function_collapse/window.rb b/lib/wave_function_collapse/window.rb index fb4eecf..ba7ed11 100644 --- a/lib/wave_function_collapse/window.rb +++ b/lib/wave_function_collapse/window.rb @@ -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[x][inverted_y].entropy + entropy = @model.grid_cell(x, inverted_y).entropy if entropy > 1 percent_entropy = (entropy.to_f / @model.max_entropy * 255).round diff --git a/test/test_model.rb b/test/test_model.rb index cbbecff..8d042a5 100644 --- a/test/test_model.rb +++ b/test/test_model.rb @@ -13,7 +13,7 @@ def test_initialize assert_equal 320, model.width assert_equal 240, model.height - assert_equal 320 * 240, model.grid.map(&:size).sum + assert_equal 320 * 240, model.grid.size assert_equal 3, model.max_entropy assert_equal 0, model.percent refute model.complete? From d623b03375dec077d382ababdc7604fe1ce89aa0 Mon Sep 17 00:00:00 2001 From: Piotr Usewicz Date: Mon, 6 May 2024 21:31:40 +0200 Subject: [PATCH 2/2] Cleanup --- lib/wave_function_collapse/cell.rb | 8 ++++---- lib/wave_function_collapse/model.rb | 10 +++------- lib/wave_function_collapse/window.rb | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/wave_function_collapse/cell.rb b/lib/wave_function_collapse/cell.rb index 9727ae2..50afe02 100644 --- a/lib/wave_function_collapse/cell.rb +++ b/lib/wave_function_collapse/cell.rb @@ -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 diff --git a/lib/wave_function_collapse/model.rb b/lib/wave_function_collapse/model.rb index 16d220d..76b3e2d 100644 --- a/lib/wave_function_collapse/model.rb +++ b/lib/wave_function_collapse/model.rb @@ -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 @@ -59,8 +57,6 @@ def iterate end def prepend_empty_row - raise - x = 0 while x < @width @grid[x].shift @@ -93,7 +89,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 @@ -141,7 +137,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 diff --git a/lib/wave_function_collapse/window.rb b/lib/wave_function_collapse/window.rb index ba7ed11..5d3ba72 100644 --- a/lib/wave_function_collapse/window.rb +++ b/lib/wave_function_collapse/window.rb @@ -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