cool_soccer 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/cool_soccer +20 -8
- data/lib/cool_soccer/soccer_helpers.rb +23 -46
- data/lib/cool_soccer.rb +11 -16
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9c5dd5b67b28fb8a8cb8e6eeced481d22f2a83d8848bd64c0c0ea4a59e91d7e
|
4
|
+
data.tar.gz: 8f7d990484715db577b1da34959bfdade276d66440c2122ed2221b11d8ef8539
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
9
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
82
|
-
|
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
|
-
#
|
85
|
-
sorted_scores = scores.sort_by { |
|
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
|
-
#
|
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
|
-
#
|
142
|
-
#
|
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
|
-
#
|
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
|
-
#
|
150
|
-
#
|
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
|
153
|
-
# param
|
154
|
-
#
|
155
|
-
|
156
|
-
|
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
|
-
#
|
19
|
-
|
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?(
|
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
|
39
|
+
# If so, increment the match day and reset the teams_in_day set
|
47
40
|
@match_day += 1
|
48
|
-
@
|
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.
|
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:
|
106
|
+
version: 3.0.0
|
86
107
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
108
|
requirements:
|
88
109
|
- - ">="
|