58 lines
1.7 KiB
Ruby
58 lines
1.7 KiB
Ruby
# Copyright (C) 2024 Manuel Bustillo
|
|
|
|
module Tables
|
|
class DiscomfortCalculator
|
|
private attr_reader :table
|
|
def initialize(table:)
|
|
@table = table
|
|
end
|
|
|
|
def calculate
|
|
breakdown.values.sum
|
|
end
|
|
|
|
def breakdown
|
|
@breakdown ||= { table_size_penalty:, cohesion_penalty: }
|
|
end
|
|
|
|
private
|
|
|
|
#
|
|
# Calculates the penalty associated with violating the table size constraints. The penalty is
|
|
# zero when the limits are honored, and it increases linearly as the number of guests deviates
|
|
# from the limits. Overcapacity is penalized more severely than undercapacity.
|
|
#
|
|
# @return [Number] The penalty associated with violating the table size constraints.
|
|
#
|
|
def table_size_penalty
|
|
case table.size
|
|
when 0...table.min_per_table then 5 * (table.min_per_table - table.size)
|
|
when table.min_per_table..table.max_per_table then 0
|
|
else 5 * (table.size - table.max_per_table)
|
|
end
|
|
end
|
|
|
|
def cohesion_penalty
|
|
10 * (cohesion_discomfort * 1.0 / table.size)
|
|
end
|
|
|
|
#
|
|
# Calculates the discomfort of the table based on the cohesion of the guests. The total discomfort
|
|
# is calculated as the sum of the discomfort of each pair of guests. The discomfort of a pair of
|
|
# guests is a rational number between 1 (unrelated groups) and 0 (same group).
|
|
#
|
|
# @return [Number] Total discomfort of the table.
|
|
#
|
|
def cohesion_discomfort
|
|
table.map(&:group_id).tally.to_a.combination(2).sum do |(a, count_a), (b, count_b)|
|
|
distance = AffinityGroupsHierarchy.instance.distance(a, b)
|
|
|
|
next count_a * count_b if distance.nil?
|
|
next 0 if distance.zero?
|
|
|
|
count_a * count_b * Rational(distance, distance + 1)
|
|
end
|
|
end
|
|
end
|
|
end
|