cool_soccer 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b3fd94f126775096201987af7f36fb3d66d9edb50e514b536c1af8d92cd5d68
4
- data.tar.gz: ec3b056a6abe39c7f130d9d306b4e50e031e82b9c0c39ecc0c686cd5eb2d0019
3
+ metadata.gz: f9c5dd5b67b28fb8a8cb8e6eeced481d22f2a83d8848bd64c0c0ea4a59e91d7e
4
+ data.tar.gz: 8f7d990484715db577b1da34959bfdade276d66440c2122ed2221b11d8ef8539
5
5
  SHA512:
6
- metadata.gz: 3854a4121220bc1b298bd1747801a5d087d780a2d37321bb1f9786bc0fd47be11d82faae40baa4288c50945359255197e2e99f09d12859bdcd14b0fa3bb7e547
7
- data.tar.gz: f0725e89a483f9ac95819eb1a32cff5e5b6864caabbb7c35411ff0c40d537c6974d764ce842a539240f540588f3d10dfc35d8a8b471cdeed924fcd4361aaebe4
6
+ metadata.gz: 15e7bf21c7578cf1af656c2279916a48100b56e3ae7bae65456d3fa05ff1d6f5f15a6989192ddd594752e3e5aaecab2f413a08a51530d8c2e221896e25d9c4c8
7
+ data.tar.gz: f34e9ae6c2f8991a5e7d6fdb289f78a39722daf7f7590727269f95fce52c01b22541c53f61fde2543e75aa44add585e0061b383c15f0b92ed3535eadcc208a0c
data/bin/cool_soccer CHANGED
@@ -3,19 +3,31 @@
3
3
 
4
4
  require_relative '../lib/cool_soccer'
5
5
 
6
+ # If there are more than one arguments, exit with an error message.
7
+ if ARGV.length > 1
8
+ puts 'Too many arguments. Expected to be passed a stream, or a path to a single file'
9
+ exit 1
10
+ end
11
+
12
+ # If the file at ARGV[0] does not exist, exit with an error message.
13
+ if ARGV[0] && !File.exist?(ARGV[0])
14
+ puts "The file #{ARGV[0]} does not exist"
15
+ exit 1
16
+ end
17
+
6
18
  app = CoolSoccer.new
7
19
 
8
- if !ARGV.empty?
9
- file_path = ARGV[0]
10
- File.foreach(file_path) do |line|
20
+ if ARGV.empty?
21
+ $stdin.each_line do |line|
11
22
  app.execute(line: line)
12
23
  end
13
24
  else
14
- $stdin.each_line do |line|
25
+ file_path = ARGV[0]
26
+ File.foreach(file_path) do |line|
15
27
  app.execute(line: line)
16
28
  end
17
-
18
- # If we haven't reset the games_today to 0, we stopped before the end of a match day,
19
- # so we need to print out the current top three
20
29
  end
21
- puts app.format_match_day(day: app.match_day, leaderboard: app.leaderboard) if app.games_today.positive?
30
+
31
+ # If the input stops, we should print the final standings as the last match day,
32
+ # since it will be incomplete.
33
+ puts app.format_match_day(day: app.match_day, leaderboard: app.leaderboard)
@@ -16,22 +16,12 @@ module SoccerHelpers
16
16
  # Valid: "San Jose Earthquakes 3, Santa Cruz Slugs 3",
17
17
  # Invalid: "San Jose Earthquakes 3", "San Jose Earthquakes, Santa Cruz Slugs",
18
18
  #
19
+ # We validate the input string with a regular expression, /([a-z-A-Z ]+)(\d+), ([a-zA-Z ]+)(\d+)/
20
+ #
19
21
  # @param result [String] the string to validate
20
22
  # @return [Boolean] is the input string a valid game result string?
21
23
  def valid_game_result?(result:)
22
- # Make sure this string has two parts, delimited by a comment
23
- parts = result.split(',')
24
- return false if parts.length != 2
25
-
26
- # For each part, make sure it contains some numeric value, the value is an integer,
27
- # and we have a team name that consists of alphabetic characters.
28
- parts.each do |part|
29
- return false if part.count('0-9').zero?
30
- return false if part.count('a-zA-Z').zero?
31
- end
32
-
33
- # If we have passed all of the above checks, then the string is a valid game result
34
- true
24
+ result.match?(/^([a-zA-Z ]+)(\d+), ([a-zA-Z ]+)(\d+)$/)
35
25
  end
36
26
 
37
27
  # extract_team_and_score answers the question:
@@ -68,7 +58,7 @@ module SoccerHelpers
68
58
  end
69
59
 
70
60
  # Zip the team names and scores together into a hash
71
- Hash[team_names.zip(scores)]
61
+ team_names.zip(scores).to_h
72
62
  end
73
63
 
74
64
  # top_three answers the question: "What are the top three teams in the league?"
@@ -78,13 +68,17 @@ module SoccerHelpers
78
68
  # param scores [Hash] a hash of team names and scores
79
69
  # @return [Array] an array of the top three teams, in the format [team, score]
80
70
  def top_three(scores:)
81
- # Alphabetize by team name
82
- scores = scores.sort_by { |name, _| name }.reverse
71
+ # Each item in scores has a key that corresponds to the team name,
72
+ # and a value that corresponds to the score.
73
+ # We want to sort the scores in descending order,
74
+ # and then take the top three scores.
75
+ #
76
+ # If there are any ties in the score, we want to return the tied teams in alphabetical order.
83
77
 
84
- # Now we need to find the top three teams by score
85
- sorted_scores = scores.sort_by { |_, score| score }.reverse
78
+ # sort_by allows us to do a keyed sort over a hash, with multiple criteria
79
+ sorted_scores = scores.sort_by { |name, score| [-score, name] }
86
80
 
87
- # Return only the top three
81
+ # Just return the top three from the sorted order
88
82
  sorted_scores[0..2]
89
83
  end
90
84
 
@@ -138,35 +132,18 @@ module SoccerHelpers
138
132
  end
139
133
 
140
134
  # match_day_over answers the question: "Is the match day over?"
141
- # There are two scenarios where the match day is over:
142
- # 1. It is match day 1, and the leaderboard already has a key that matches one of the input scores teams
143
- # 2. It is any match day past 1, and the number of games today is equal to num_teams / 2
135
+ # It takes two Sets, teams_in_day, which represents the teams we've seen in a current day,
136
+ # and teams_in_match, which represents the teams from a match string
144
137
  #
145
- # This function takes a lot of parameters, which I don't love,
146
- # but I prefer to call one function, rather than have two similar functions,
147
- # (one for match day 1, and one for match day 2 and beyond).
138
+ # Then it returns whether or not any of the teams in teams_in_match already exist in teams_in_day.
148
139
  #
149
- # Using named params helps make the long parameter list manageable:
150
- # callers will get specific error messages if they pass in the wrong params.
140
+ # There is a different, slightly faster, but less readable solve for this.
141
+ # You can see a different approach in the diffs at https://gitlab.com/coolsoftwaretyler/coding-challenge-soccer/-/merge_requests/1
151
142
  #
152
- # param leaderboard [Hash] the current leaderboard
153
- # param scores [Hash] the scores of the current match day
154
- # param match_day [Integer] the current match day
155
- # param num_teams [Integer] the number of teams in the league
156
- # param games_today [Integer] the number of games played today
157
- # @return [Boolean] is the current match day over?
158
- def match_day_over?(leaderboard:, scores:, match_day:, num_teams:, games_today:)
159
- # If this is the first match day, and the leaderboard already has a key that matches one of the input scores teams,
160
- # then the match day is over
161
- return true if match_day == 1 && leaderboard.key?(scores.keys.first)
162
-
163
- return true if match_day == 1 && leaderboard.key?(scores.keys.last)
164
-
165
- # If this is any match day past 1, and the number of games today is equal to num_leagues / 2,
166
- # then the match day is over
167
- return true if match_day > 1 && games_today == num_teams / 2
168
-
169
- # Otherwise, the match day is not over yet.
170
- false
143
+ # @param teams [Set] the teams in the current match
144
+ # @param teams_in_match [Set] the teams in the current match
145
+ # @return [Boolean] is the match day over?
146
+ def match_day_over?(teams_in_day:, teams_in_match:)
147
+ teams_in_day.intersection(teams_in_match).any?
171
148
  end
172
149
  end
data/lib/cool_soccer.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'cool_soccer/soccer_helpers'
4
+ require 'set'
4
5
 
5
6
  # CoolSoccer is an "imperative shell"
6
7
  # that allows the CLI to read a stream of game results from a soccer league,
@@ -15,39 +16,33 @@ class CoolSoccer
15
16
  @leaderboard = {}
16
17
  # We'll also keep track of the current match day, initialized at 1
17
18
  @match_day = 1
18
- # And we'll want to know how many teams are in the league. At first,
19
- # we'll initialize this at 0.
20
- @num_teams = 0
21
- # Finally, we'll want to know how many games we've seen today,
22
- # which we'll initialize at 0.
23
- @games_today = 0
19
+ # We'll also have a Set that keeps tracks of the teams in a given day
20
+ @teams_in_day = Set.new
24
21
  end
25
22
 
26
23
  def execute(line:)
27
24
  # If the line is invalid, skip it
28
25
  return unless valid_game_result?(result: line)
29
26
 
30
- # Increment the number of games we've played today
31
- @games_today += 1
32
-
33
- # If it's still match day 1, increment the number of teams by 2
34
- @num_teams += 2 if @match_day == 1
35
-
36
27
  # Extract the team names and scores from the line
37
28
  scores = extract_team_and_score(result: line)
38
29
 
30
+ # From the scores, create a new set of team names in the given match
31
+ teams_in_match = Set.new(scores.keys)
32
+
39
33
  # Check if the match day is over
40
- if match_day_over?(leaderboard: @leaderboard, scores: scores, match_day: @match_day, num_teams: @num_teams,
41
- games_today: @games_today)
34
+ if match_day_over?(teams_in_day: @teams_in_day, teams_in_match: teams_in_match)
42
35
 
43
36
  # Print out the current top three
44
37
  puts "#{format_match_day(day: @match_day, leaderboard: @leaderboard)}\n"
45
38
 
46
- # If so, increment the match day and reset the games today counter
39
+ # If so, increment the match day and reset the teams_in_day set
47
40
  @match_day += 1
48
- @games_today = 1
41
+ @teams_in_day = Set.new
49
42
  end
50
43
 
44
+ # Add the teams_in_match to the teams_in_day set
45
+ @teams_in_day.merge(teams_in_match)
51
46
  # Add the scores to the leaderboard
52
47
  @leaderboard = update_leaderboard(leaderboard: @leaderboard, changes: scores)
53
48
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cool_soccer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Williams
@@ -44,6 +44,26 @@ dependencies:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: 1.29.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: rubocop-rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.11'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 2.11.1
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '2.11'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.11.1
47
67
  - !ruby/object:Gem::Dependency
48
68
  name: yard
49
69
  requirement: !ruby/object:Gem::Requirement
@@ -73,7 +93,8 @@ files:
73
93
  homepage: https://rubygems.org/gems/cool_soccer
74
94
  licenses:
75
95
  - MIT
76
- metadata: {}
96
+ metadata:
97
+ rubygems_mfa_required: 'true'
77
98
  post_install_message:
78
99
  rdoc_options: []
79
100
  require_paths:
@@ -82,7 +103,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
103
  requirements:
83
104
  - - ">="
84
105
  - !ruby/object:Gem::Version
85
- version: '2.5'
106
+ version: 3.0.0
86
107
  required_rubygems_version: !ruby/object:Gem::Requirement
87
108
  requirements:
88
109
  - - ">="