lib/tennis.rb in tennis-0.1.7 vs lib/tennis.rb in tennis-0.2.0

- old
+ new

@@ -2,182 +2,80 @@ class Tennis # player 1 is the home player(0) , player 2 is the away player(1) # winner is either 0 or 1 # points is an array: [points for 0 , points for 1] - # sets_lost is an array: [sets lost by 0, sets lost by 1] - # games_lost is an array: [games won by 0, games won by 1] + # sets_won/lost is an array: [sets won/lost by 0, sets won/lost by 1] + # games_won/lost is an array: [games won/lost by 0, games won/lost by 1] - attr_reader :winner, :points, :sets_lost, :games_lost + attr_reader :winner, :sets_won, :sets_lost, :games_won, :games_lost - def initialize(scores) + def initialize(score) # dfh -> default win for home player(0) # dfa -> default win for away player(1) - @winner = :default - @scores = validate_score(scores) - @winner = match_winner if @winner == :default - @points = match_points - unless @scores.is_a? String - @sets_lost = count_sets_lost - @games_lost = count_games_lost - end + process_score(score) end # to_s # return the score in string format def to_s - (0...@scores.length).step(2).map{ |i| [@scores[i], @scores[i+1]].join('-') }.join(', ') + @score.map{|set| set.join('-') }.join(', ') end # flip score ( P1-P2 to P2-P1) # returns the flipped score as a string def flipped - (0...@scores.length).step(2).map{ |i| [@scores[i+1], @scores[i]].join('-') }.join(', ') + @score.map{|set| set.reverse.join('-') }.join(', ') end private - # helper method: to check score validation - def validate_score(scores_string) - set_scores = scores_string.split(/[-,,]/).map(&:to_i) - if set_scores == [0] - # checks bad default string value reported - @winner = (0 if scores == 'dfh') || (1 if scores == 'dfa') || :error - scores_string - else - # check blank input '' - validation_1 = set_scores.any? { |score| score.nil? } - # to check if score for only 1 set has been input - validation_2 = set_scores.length == 2 - # to check if any input > 7 - validation_3 = set_scores.any? { |score| score > 7 } - # to check if one of the input is 7 and the other is not 6 - # bad tie break input - validation_4 = false - set_scores.each_slice(2).each { |r| validation_4 = true if r.any? { |score| score == 7 } && !r.any? { |score| score == 6 || score == 5 } } - @winner = :error if validation_1 || validation_2 || validation_3 || validation_4 - # if set score is not complete eg: 4-6,7-6,4-1 - set_scores.each_slice(2).each {|r| @winner = :incomplete_match if r[0] < 6 && r[1] < 6 } unless @winner == :error - set_scores + def process_score(score, best_of=3) + begin + sets = score.split(/,/) + # only take 2 to 5 sets + raise "invalid number of sets" unless (2..best_of).cover? sets.length + score_plus_winner = map_scores_winners(sets) + @set_winners = score_plus_winner.map{ |sw| sw[1] } + home = @set_winners.count(0) + away = @set_winners.count(1) + raise "nobody won" if home + away == 0 + @winner = home > away ? 0 : away > home ? 1 : raise("no winner") + # sets won and lost + @sets_won, @sets_lost = [[home, away], [away, home]] + # score array + @score = score_plus_winner.map{ |sw| sw[0] } + @games_won = @score.transpose.map{ |games| games.inject{ |sum,x| sum + x } } + @games_lost = @games_won.reverse + # FIXME this is the only thing that assumes 3 sets + raise "too many sets" if @set_winners[0] == @set_winners[1] and sets.size > 2 + rescue => e + raise ArgumentError, "Invalid score '#{score}': #{e}" end end - # returns who won the match - # :incomplete_match (bad input/incomplete match) - # :error (bad input for sure) - # 0 (player-1 won) - # 1 (player-2 won) - def match_winner - @scores.length == 4 ? two_sets : three_sets - end - - # returns an array of points - # returns (points_player_1 , points_player_2) - # returns (0,0) for bad input - def match_points - return [0, 0] if @winner == :error - return [@scores == 'dfh' ? 12 : 0, @scores == 'dfa' ? 12 : 0] if @scores.is_a? String - @winner == 0 || @winner == 1 ? complete_match_points : incomplete_match_points - end - - # returns the number of sets lost by each player - def count_sets_lost - sets = [0, 0] - (0...@scores.length).step(2).each do |i| - @scores[i] > @scores[i + 1] ? sets[1] += 1 : sets[0] += 1 + # return an array of scores and winners for each set + def map_scores_winners(sets) + sets.map do |set| + set.strip! + games = set.split(/-/).map(&:to_i) + raise "uneven games in set '#{set}'" unless games.length == 2 + h, a = games + sw = set_winner(h, a) + raise "no valid winner in set '#{set}'" unless sw + [games, sw] end - sets end - # returns the number of won by each player - def count_games_lost - games = [0, 0] - (0...@scores.length).step(2).each do |i| - games[0] += @scores[i + 1] - games[1] += @scores[i] - end - games + # determine the set winner for home and away, or else return nil + def set_winner(h, a) + # basic range check + return nil if h > 7 or a > 7 or h < 0 or a < 0 + # game went to 7 + return 0 if h == 7 and [5,6].include?(a) + return 1 if a == 7 and [5,6].include?(h) + # there has to be one winner, to 6 + return nil unless (h == 6 and h > a + 1) or (a == 6 and a > h + 1) + h > a ? 0 : 1 end - # returns the number of sets won by each player - def sets_won - sets = [0, 0] - (0...@scores.length).step(2).each do |i| - @scores[0 + i] > @scores[1 + i] ? sets[0] += 1 : sets[1] += 1 - end - sets - end - - # returns the number of won by each player - def games_won - games = [0, 0] - (0...@scores.length).step(2).each do |i| - games[0] += @scores[0 + i] - games[1] += @scores[1 + i] - end - games - end - - # helper method: called by RESULT method for valid matches with 2 sets - def two_sets - set_results = [] - (0...@scores.length).step(2).each do |i| - # tie breaker (assuming a 7 point tie breaker) or a 7-5 scores - if @scores[i] == 7 || @scores[i + 1] == 7 - set_results << (@scores[i] == 7 ? 0 : 1) - # regular set victory - 6 games with a margin of 2 - else - return :incomplete_match if ( @scores[i] - @scores[i + 1] ).abs < 2 - set_results << (@scores[i] == 6 ? 0 : 1) - end - end - # incomplete match e.g: 6-4,5-3 - (set_results[0] if set_results[0] == set_results[1]) || :incomplete_match - end - - # helper method: called by RESULT method for valid matches with 3 sets - def three_sets - set_results = [] - (0...@scores.length).step(2).each do |i| - # tie breaker (assuming a 7 point tie breaker) or a 7-5 score - if @scores[i] == 7 || @scores[i + 1] == 7 - set_results << (@scores[i] == 7 ? 0 : 1) - # regular set victory - 6 games with a margin of 2 - else - return :incomplete_match if (@scores[i] - @scores[i + 1]).abs < 2 - set_results << (@scores[i] == 6 ? 0 : 1) - end - end - # checks if the result has been decided in the first 2 sets - # but the 3rd set is also present in the input - return :error if set_results[0] == set_results[1] - set_results.count(0) == 2 ? 0 : 1 - end - - # helper method: called by POINTS for complete matches - def complete_match_points - points = [0, 0] - points[@winner] = (@scores.length == 6) ? 12 : 14 - runner_up = 1 - @winner - runner_up_points = player_points(runner_up) - points[runner_up] = runner_up_points < 8 ? runner_up_points : 8 - points - end - - # helper method: called by POINTS for incomplete matches - def incomplete_match_points - points = [0, 0] - player_1_points = player_points(0) - player_2_points = player_points(1) - points[0] = player_1_points < 10 ? player_1_points : 10 - points[1] = player_2_points < 10 ? player_2_points : 10 - points - end - - # helper method: returns the POINTS of a player given the player number - def player_points(player) - player_scores = [] - @scores.each_with_index { |score, index| (player_scores << score; player += 2) if index == (player) } - player_scores = player_scores.sort! { |x, y| y <=> x } - player_scores[0] + player_scores[1] - end end