class Match
Model for a tennis match
Overview¶ ↑
-
A match may be a singles match or a doubles match
-
A match has two opponent teams:
-
Doubles teams with four players in total
-
Singles teams with two players in total
-
-
A match has a state:
-
:not_started
-
:in_progress
-
:complete
-
-
A match may have first servers
First server are the players that serve the first game or two. For a singles match, there is a first server for the first game. For a doubles match, there is a first server for the first two games; one from each team.
-
A match has sets (see MatchSet). Sets have games (see SetGame).
-
A match has scoring:
-
:two_six_game_ten_point
-
:one_eight_game
-
:three_six_game
-
-
A complete match has a winner
Schema Information¶ ↑
Table name: matches
id :integer not null, primary key created_at :datetime not null updated_at :datetime not null first_team_id :integer not null second_team_id :integer not null scoring :string not null started :boolean default(FALSE), not null doubles :boolean default(FALSE), not null first_player_server_id :integer second_player_server_id :integer title :string team_winner_id :integer play_version :integer
Public Instance Methods
Indicate if the match is completed.
# File app/models/match.rb, line 149 def completed? team_winner end
Compute the winner of the match based on the number of sets won by each opponent
-
Returns : Team or nil
# File app/models/match.rb, line 256 def compute_team_winner if completed? team_winner else if first_team && second_team if sets_won(first_team) == min_sets_to_play first_team elsif sets_won(second_team) == min_sets_to_play second_team end end end end
Get the first player opponent in a singles match
-
Returns : Player
# File app/models/match.rb, line 155 def first_player singles_player_of_team first_team end
Set the first player opponent in a singles match. A new team may be created to hold the player
# File app/models/match.rb, line 161 def first_player=(player) self.first_team = singles_player_team(player) end
Get the first set of the match
-
Returns : MatchSet
# File app/models/match.rb, line 205 def first_set match_sets.first end
Get the last set of the match
-
Returns : MatchSet
# File app/models/match.rb, line 211 def last_set match_sets.last end
Get the maximum number of sets that will be played in this match. The
maximum is 3 for :three_six_game
scoring, for example
-
Returns : Integer
# File app/models/match.rb, line 227 def max_sets_to_play case scoring.to_sym when :two_six_game_ten_point 3 when :one_eight_game 1 else # :three_six_game 3 end end
Get the minimum number of sets that will be played in this match The
minimum is 2 for :three_six_game
scoring, for example
-
Returns : Integer
# File app/models/match.rb, line 218 def min_sets_to_play max = max_sets_to_play raise Exceptions::ApplicationError, 'Unexpected game count' if max.even? (max + 1) / 2 end
Get the ordinal of the next game to play in the match. The lowest ordinal is 1.
-
Returns : Integer
# File app/models/match.rb, line 241 def next_game_ordinal last_set ? last_set.set_games.count + 1 : 0 end
Execute an action on the match, such as :win_game
-
Args :
-
action
-> the action to execute -
params
-> hash of action parameters
-
-
Raises :
-
UnknownOperation
-> if the action is unknown -
InvalidOperation
-> if the action is not permitted
-
Action¶ ↑
-
:start_play
- Start playing -
:discard_play
- Discard all scoring -
:start_set
- Start the next set -
:start_game
- Start the next game
When starting the first game in a singles match, or the first two games in
a doubles match, params
identifies the player to serve the
game.
-
:start_tiebreak
- Start game tiebreak -
:remove_last_change
- Back up to the previous state -
:start_match_tiebreak
- Start the match tiebreak -
:win_game
- Win the current game -
:win_tiebreak
- Win the current set tiebreak. -
:win_match_tiebreak
- Win the current match tiebreak.
For the 3 :win_*
actions, params
identifies the
doubles team or singles team to win
Params¶ ↑
-
:version
Version number from client.
If provided, it will be compared to the current match play version number. An exception will be raised if the values are not equal. The purpose of the version number is to detect when a client is out of sync.
This options is used with :win_*
actions to identify the
winning team or player. It is also used with the :start_game
action to identify the serving player
# File app/models/match.rb, line 100 def play_match!(action, params = nil) method = play_actions.lookup_method(action) if method ActiveRecord::Base.transaction do version = params[:version] if params version = version.to_i if version if version && version != self.play_version raise Exceptions::InvalidOperation, "Unable to #{action.to_s.tr('_', ' ')}. " + if version < self.play_version 'Client is out of sync.' else 'Unexpected match version number.' end end method[:exec].call params # Version number is used to detect when client has is out of sync self.play_version = next_version_number self.save! end else raise Exceptions::UnknownOperation, "Unknown action: #{action}" end end
Indicate if an action can be executed. :win_game
can be
executed if a game has started, for example.
# File app/models/match.rb, line 127 def play_match?(action) methods = play_actions.lookup_method(action) if methods methods[:query].call else raise Exceptions::UnknownOperation, "Unknown action: #{action}" end end
Get the scoring of a set
-
Args :
-
ordinal
-> set ordinal
-
-
Returns : symbol
-
:six_game
-
:eight_game
-
:ten_point
(tiebreak)
-
# File app/models/match.rb, line 288 def scoring_of_set(ordinal) case scoring.to_sym when :two_six_game_ten_point ordinal == 3 ? :ten_point : :six_game when :one_eight_game :eight_game else # :three_six_game :six_game end end
Get the second player opponent in a singles match
-
Returns : Player
# File app/models/match.rb, line 167 def second_player singles_player_of_team second_team end
Set the second player opponent in a singles match. A new team may be created to hold the player
# File app/models/match.rb, line 173 def second_player=(player) self.second_team = singles_player_team(player) end
Calculate the number of sets won by a team
-
Args :
-
team
-> Team
-
-
Returns : Integer
# File app/models/match.rb, line 196 def sets_won(team) match_sets.reduce(0) do |sum, set| winner = set.compute_team_winner sum + (winner && winner == team ? 1 : 0) end end
Get the state of the match
-
:complete
-
:in_progress
-
:not_started
# File app/models/match.rb, line 181 def state if completed? :complete elsif started :in_progress else :not_started end end
Indicate whether a set is a match tiebreak
-
Args :
-
ordinal
-> Integer
-
-
Returns : Boolean
# File app/models/match.rb, line 249 def tiebreak_set?(ordinal) scoring_of_set(ordinal) == :ten_point end