Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion lib/fbe/award.rb
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ def line(value, text)
# Calculates the total points in this bill.
#
# @return [Integer] The sum of all point values, rounded to an integer
# @note The sum is rounded once, at the end, not line by line, so that
# a clause worth half a point costs half a point and not a whole one
# @example
# bill = Fbe::Award::Bill.new
# bill.line(42.5, "for answer")
Expand All @@ -396,7 +398,11 @@ def points
# bill.line(25, "for documentation")
# bill.greeting #=> "You've earned +75 points for this: +50 for code review; +25 for documentation. "
def greeting
items = @lines.map { |l| "#{format('%+d', whole(l[:v]))} #{l[:t]}" }
items =
@lines.zip(portions).filter_map do |l, v|
next if v.zero?
"#{format('%+d', v)} #{l[:t]}"
end
case items.size
when 0
"You've earned nothing. "
Expand All @@ -416,6 +422,24 @@ def greeting
def whole(value)
Integer(Float(value).round)
end

# Splits the total between the lines, giving each one a whole number.
#
# Every line is rounded on its own first, and then the difference
# between what that adds up to and the total, which is rounded once,
# is handed to the lines with the largest remainders. So the numbers
# in the greeting still add up to the number in front of it.
#
# @return [Array<Integer>] One whole number per line, summing to +points+
def portions
ints = @lines.map { |l| whole(l[:v]) }
diff = points - ints.sum
return ints if diff.zero?
step = diff.negative? ? -1 : 1
order = (0...ints.size).sort_by { |i| (ints[i] - @lines[i][:v]) * step }
diff.abs.times { |k| ints[order[k % ints.size]] += step }
ints
end
end

# A class for generating human-readable bylaws.
Expand Down
18 changes: 18 additions & 0 deletions test/fbe/test_award.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,24 @@ def test_rounds_the_sum_once
assert_equal(1, bill.points)
end

def test_rounds_the_total_and_not_every_line
b = Fbe::Award.new('(award (give 8 "as a basis") (give -0.5 "for one day of delay"))').bill
assert_equal(8, b.points, b.greeting)
b = Fbe::Award.new('(award (give 8 "as a basis") (give -1.5 "for three days of delay"))').bill
assert_equal(7, b.points, b.greeting)
end

def test_lines_add_up_to_the_total_with_halves
b = Fbe::Award.new('(award (give 8 "as a basis") (give -1.5 "for delay"))').bill
g = b.greeting
assert_equal("You've earned +7 points for this: +8 as a basis; -1 for delay. ", g)
end

def test_skips_a_line_that_is_worth_nothing
g = Fbe::Award.new('(award (give 8 "as a basis") (give -0.4 "for delay"))').bill.greeting
assert_equal("You've earned +8 points. ", g)
end

def test_shorten_when_one_number
g = Fbe::Award.new('(award (give 23 "for love"))').bill.greeting
assert_equal('You\'ve earned +23 points. ', g, g)
Expand Down
Loading