diff --git a/app/services/tables/distribution.rb b/app/services/tables/distribution.rb index 33cb130..9b5f4ba 100644 --- a/app/services/tables/distribution.rb +++ b/app/services/tables/distribution.rb @@ -2,39 +2,66 @@ require_relative '../../extensions/tree_node_extension' module Tables class Distribution - attr_accessor :tables - def initialize(min_per_table:, max_per_table:) @min_per_table = min_per_table @max_per_table = max_per_table - @tables = [] + @tables = {} + end + + def tables + @tables.values.freeze + end + + def deep_freeze + @tables.each_value(&:freeze) + @tables.freeze + freeze + end + + def replace(table) + @tables[table.id] = table + end + + def dup + super.tap do |new_distribution| + new_distribution.instance_variable_set(:@tables, @tables.dup) + end + end + + def <<(array) + Table.new(array).tap do |table| + @tables[table.id] = table + end end def random_distribution(people) - @tables = [] + @tables = {} - @tables << Table.new(people.slice!(0..rand(@min_per_table..@max_per_table))) while people.any? + self << people.slice!(0..rand(@min_per_table..@max_per_table)) while people.any? + + @tables.freeze + freeze end def discomfort - @tables.map do |table| - local_discomfort(table) - end.sum + tables.map(&:discomfort).sum end def inspect - "#{@tables.count} tables, discomfort: #{discomfort}" + "#{tables.count} tables, discomfort: #{discomfort}" end def pretty_print - @tables.map.with_index do |table, i| - "Table #{i + 1} (#{table.count} ppl): (#{local_discomfort(table)}) #{table.map(&:full_name).join(', ')}" + tables.map.with_index do |table, i| + "Table #{i + 1} (#{table.count} ppl): (#{table.discomfort}) #{table.map(&:full_name).join(', ')}" end.join("\n") end def deep_dup self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table).tap do |new_distribution| - new_distribution.tables = @tables.map(&:dup) + tables.each do |table| + new_distribution << table.dup + end end end @@ -55,11 +82,5 @@ module Tables arrangement.update!(discomfort:) end end - - private - - def local_discomfort(table) - table.discomfort ||= DiscomfortCalculator.new(table).calculate - end end end diff --git a/app/services/tables/swap.rb b/app/services/tables/swap.rb index 631b49b..d77e2a4 100644 --- a/app/services/tables/swap.rb +++ b/app/services/tables/swap.rb @@ -8,25 +8,23 @@ module Tables def each @initial_solution.tables.combination(2) do |table_a, table_b| table_a.product(table_b).each do |(person_a, person_b)| - original_discomfort_a = table_a.reset - original_discomfort_b = table_b.reset + new_solution = @initial_solution.dup - table_a.delete(person_a) - table_b.delete(person_b) + new_table_a = table_a.dup + new_table_b = table_b.dup - table_a << person_b - table_b << person_a + new_solution.replace(new_table_a) + new_solution.replace(new_table_b) - yield(@initial_solution) - ensure - table_a.delete(person_b) - table_b.delete(person_a) + new_table_a.delete(person_a) + new_table_b.delete(person_b) - table_a << person_a - table_b << person_b + new_table_a << person_b + new_table_b << person_a - table_a.discomfort = original_discomfort_a - table_b.discomfort = original_discomfort_b + new_solution.freeze + + yield(new_solution) end end end diff --git a/app/services/tables/table.rb b/app/services/tables/table.rb index 1dde7f6..54fd8cc 100644 --- a/app/services/tables/table.rb +++ b/app/services/tables/table.rb @@ -1,15 +1,28 @@ module Tables class Table < Array - attr_accessor :discomfort + attr_writer :discomfort + attr_reader :id + def initialize(*args) super reset + @id = SecureRandom.uuid end def reset - original_discomfort = discomfort + original_discomfort = @discomfort @discomfort = nil original_discomfort end + + def discomfort + @discomfort ||= DiscomfortCalculator.new(self).calculate + end + + def dup + super.tap do |new_table| + new_table.discomfort = nil + end + end end -end \ No newline at end of file +end diff --git a/app/services/vns/engine.rb b/app/services/vns/engine.rb index 5c260aa..d5474b9 100644 --- a/app/services/vns/engine.rb +++ b/app/services/vns/engine.rb @@ -26,7 +26,7 @@ module VNS optimize(perturbation.new(@best_solution)) end - @best_solution + @best_solution end private diff --git a/db/seeds.rb b/db/seeds.rb index 162a80f..194fa88 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,4 +1,4 @@ -NUMBER_OF_GUESTS = 50 +NUMBER_OF_GUESTS = (ENV['SEED_GUEST_COUNT'] || 50).to_i TablesArrangement.delete_all Expense.delete_all @@ -21,7 +21,6 @@ Expense.create!(name: 'Transportation', amount: 3000, pricing_type: 'fixed') Expense.create!(name: 'Invitations', amount: 200, pricing_type: 'fixed') Expense.create!(name: 'Cake', amount: 500, pricing_type: 'fixed') - samples = { close_family_a: 10, close_family_b: 10, diff --git a/spec/services/tables/swap_spec.rb b/spec/services/tables/swap_spec.rb index fff181f..b31779f 100644 --- a/spec/services/tables/swap_spec.rb +++ b/spec/services/tables/swap_spec.rb @@ -14,8 +14,8 @@ module Tables context 'when there are two tables with two people each' do let(:initial_solution) do Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| - distribution.tables << %i[a b].to_table - distribution.tables << %i[c d].to_table + distribution << %i[a b] + distribution << %i[c d] end end @@ -32,8 +32,8 @@ module Tables context 'when there are two tables with three people each' do let(:initial_solution) do Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution| - distribution.tables << %i[a b c].to_table - distribution.tables << %i[d e f].to_table + distribution << %i[a b c] + distribution << %i[d e f] end end @@ -55,9 +55,9 @@ module Tables context 'when there are three tables with two people each' do let(:initial_solution) do Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| - distribution.tables << %i[a b].to_table - distribution.tables << %i[c d].to_table - distribution.tables << %i[e f].to_table + distribution << %i[a b] + distribution << %i[c d] + distribution << %i[e f] end end