From ebc56cd941bdc74e3287a2cc52966f4b1ded4765 Mon Sep 17 00:00:00 2001 From: devil Date: Wed, 30 Sep 2026 11:24:31 +0000 Subject: [PATCH 1/2] #1177: split the rounded total between the greeting lines, so they add up --- lib/fbe/award.rb | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/fbe/award.rb b/lib/fbe/award.rb index ae6eeb43..547ce79c 100644 --- a/lib/fbe/award.rb +++ b/lib/fbe/award.rb @@ -396,19 +396,24 @@ 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]}" } - case items.size - when 0 - "You've earned nothing. " - when 1 - "You've earned #{format('%+d', points)} points. " - else - "You've earned #{format('%+d', points)} points for this: #{items.join('; ')}. " - end + return "You've earned nothing. " if @lines.empty? + items = shares.reject { |l| l[:v].zero? }.map { |l| "#{format('%+d', l[:v])} #{l[:t]}" } + return "You've earned #{format('%+d', points)} points. " if @lines.size == 1 || items.empty? + "You've earned #{format('%+d', points)} points for this: #{items.join('; ')}. " end private + # Splits the rounded total between the lines, by the largest remainder. + # + # @return [Array] Lines with whole values that add up to +points+ + def shares + parts = @lines.map { |l| { v: Float(l[:v]).floor, t: l[:t], r: Float(l[:v]) - Float(l[:v]).floor } } + extra = points - parts.sum { |l| l[:v] } + parts.each_with_index.sort_by { |l, i| [-l[:r], i] }.first(extra).each { |l, _| l[:v] += 1 } + parts + end + # Rounds one line value the way the total is rounded. # # @param [Float, Integer] value The value of one line From 6ada3ce8ea03697bc7041150dda4b7665e66b840 Mon Sep 17 00:00:00 2001 From: devil Date: Wed, 30 Sep 2026 11:25:20 +0000 Subject: [PATCH 2/2] #1177: test that the greeting lines add up to the total --- lib/fbe/award.rb | 4 ++-- test/fbe/test_award.rb | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/fbe/award.rb b/lib/fbe/award.rb index 547ce79c..89d0d168 100644 --- a/lib/fbe/award.rb +++ b/lib/fbe/award.rb @@ -397,7 +397,7 @@ def points # bill.greeting #=> "You've earned +75 points for this: +50 for code review; +25 for documentation. " def greeting return "You've earned nothing. " if @lines.empty? - items = shares.reject { |l| l[:v].zero? }.map { |l| "#{format('%+d', l[:v])} #{l[:t]}" } + items = shares.filter_map { |l| "#{format('%+d', l[:v])} #{l[:t]}" unless l[:v].zero? } return "You've earned #{format('%+d', points)} points. " if @lines.size == 1 || items.empty? "You've earned #{format('%+d', points)} points for this: #{items.join('; ')}. " end @@ -410,7 +410,7 @@ def greeting def shares parts = @lines.map { |l| { v: Float(l[:v]).floor, t: l[:t], r: Float(l[:v]) - Float(l[:v]).floor } } extra = points - parts.sum { |l| l[:v] } - parts.each_with_index.sort_by { |l, i| [-l[:r], i] }.first(extra).each { |l, _| l[:v] += 1 } + parts.sort_by.with_index { |l, i| [-l[:r], i] }.first(extra).each { |l| l[:v] += 1 } parts end diff --git a/test/fbe/test_award.rb b/test/fbe/test_award.rb index b13c5c3d..22b5ff82 100644 --- a/test/fbe/test_award.rb +++ b/test/fbe/test_award.rb @@ -81,6 +81,17 @@ def test_some_terms end end + def test_greeting_lines_add_up_to_the_total + { + '(award (give 0.4 "a") (give 0.4 "b") (give 0.4 "c"))' => "You've earned +1 points for this: +1 a. ", + '(award (give 0.5 "a") (give 0.5 "b"))' => "You've earned +1 points for this: +1 a. ", + '(award (give -3.5 "a") (give 1 "b"))' => "You've earned -3 points for this: -4 a; +1 b. ", + '(award (give 0.2 "a") (give 0.2 "b"))' => "You've earned +0 points. " + }.each do |q, v| + assert_equal(v, Fbe::Award.new(q).bill.greeting, q) + end + end + def test_some_greetings { '(award (give (times 7 0.25 "fun")))' => 'You\'ve earned +2 points. ',