diff --git a/lib/fbe/award.rb b/lib/fbe/award.rb index ae6eeb43..89d0d168 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.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 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.sort_by.with_index { |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 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. ',