git_game_show 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +110 -0
- data/bin/git-game-show +5 -0
- data/lib/git_game_show/cli.rb +537 -0
- data/lib/git_game_show/game_server.rb +1224 -0
- data/lib/git_game_show/mini_game.rb +57 -0
- data/lib/git_game_show/player_client.rb +1145 -0
- data/lib/git_game_show/version.rb +4 -0
- data/lib/git_game_show.rb +49 -0
- data/mini_games/author_quiz.rb +142 -0
- data/mini_games/commit_message_completion.rb +205 -0
- data/mini_games/commit_message_quiz.rb +589 -0
- data/mini_games/date_ordering_quiz.rb +230 -0
- metadata +245 -0
@@ -0,0 +1,230 @@
|
|
1
|
+
module GitGameShow
|
2
|
+
class DateOrderingQuiz < MiniGame
|
3
|
+
self.name = "Commit Timeline"
|
4
|
+
self.description = "Put these commits in chronological order! (20 seconds per question)"
|
5
|
+
self.questions_per_round = 5
|
6
|
+
|
7
|
+
# Custom timing for this mini-game
|
8
|
+
def self.question_timeout
|
9
|
+
20 # 20 seconds per question
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.question_display_time
|
13
|
+
5 # 5 seconds between questions
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_questions(repo)
|
17
|
+
begin
|
18
|
+
# Get commits with valid dates
|
19
|
+
all_commits = get_all_commits(repo)
|
20
|
+
commits = all_commits.select { |commit| commit.date.is_a?(Time) || commit.date.respond_to?(:to_time) }
|
21
|
+
|
22
|
+
# If we don't have enough commits for the game, generate sample questions
|
23
|
+
if commits.size < 10
|
24
|
+
return generate_sample_questions
|
25
|
+
end
|
26
|
+
rescue => e
|
27
|
+
# If any errors occur, fall back to sample questions
|
28
|
+
return generate_sample_questions
|
29
|
+
end
|
30
|
+
|
31
|
+
questions = []
|
32
|
+
|
33
|
+
# Generate questions
|
34
|
+
self.class.questions_per_round.times do
|
35
|
+
begin
|
36
|
+
# Get 4 random commits for each question, ensure they have different dates
|
37
|
+
# to avoid ties in the ordering
|
38
|
+
pool = commits.dup
|
39
|
+
selected_commits = []
|
40
|
+
|
41
|
+
# Select 4 commits with different dates
|
42
|
+
while selected_commits.size < 4 && !pool.empty?
|
43
|
+
commit = pool.sample
|
44
|
+
pool.delete(commit)
|
45
|
+
|
46
|
+
# Check if date is unique among selected commits
|
47
|
+
date_duplicate = selected_commits.any? do |selected|
|
48
|
+
# Convert dates to Time objects for comparison
|
49
|
+
commit_time = commit.date.is_a?(Time) ? commit.date : commit.date.to_time
|
50
|
+
selected_time = selected.date.is_a?(Time) ? selected.date : selected.date.to_time
|
51
|
+
|
52
|
+
# Check if times are within 1 minute of each other (avoid duplicates)
|
53
|
+
(commit_time - selected_time).abs < 60
|
54
|
+
end
|
55
|
+
|
56
|
+
selected_commits << commit unless date_duplicate
|
57
|
+
end
|
58
|
+
|
59
|
+
# If we couldn't get 4 commits with different dates, try again with the full pool
|
60
|
+
if selected_commits.size < 4
|
61
|
+
selected_commits = commits.sample(4)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create the options using commit message and short SHA
|
65
|
+
options = selected_commits.map do |commit|
|
66
|
+
# Get first line of commit message, handle multi-line messages
|
67
|
+
message = commit.message.lines.first&.strip || "No message"
|
68
|
+
# Truncate long messages to keep UI clean
|
69
|
+
message = message.length > 40 ? "#{message[0...37]}..." : message
|
70
|
+
"#{message} (#{commit.sha[0..6]})"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Shuffle options for initial display
|
74
|
+
shuffled_options = options.shuffle
|
75
|
+
|
76
|
+
# Determine correct order by date
|
77
|
+
correct_order = selected_commits.sort_by(&:date).map do |commit|
|
78
|
+
message = commit.message.lines.first&.strip || "No message"
|
79
|
+
message = message.length > 40 ? "#{message[0...37]}..." : message
|
80
|
+
"#{message} (#{commit.sha[0..6]})"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get commit dates for displaying in results
|
84
|
+
commit_dates = selected_commits.map do |commit|
|
85
|
+
date_string = commit.date.strftime('%Y-%m-%d %H:%M:%S')
|
86
|
+
"#{commit.sha[0..6]}: #{date_string}"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Store the question data
|
90
|
+
questions << {
|
91
|
+
question: "Put these commits in chronological order (oldest to newest):",
|
92
|
+
options: shuffled_options,
|
93
|
+
correct_answer: correct_order,
|
94
|
+
question_type: 'ordering',
|
95
|
+
commit_dates: commit_dates.join("\n") # Store dates for feedback
|
96
|
+
}
|
97
|
+
rescue => e
|
98
|
+
# If this question fails, continue to the next one
|
99
|
+
next
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# If we couldn't generate enough questions, fill with sample questions
|
104
|
+
if questions.size < self.class.questions_per_round
|
105
|
+
sample_questions = generate_sample_questions
|
106
|
+
questions += sample_questions[0...(self.class.questions_per_round - questions.size)]
|
107
|
+
end
|
108
|
+
|
109
|
+
questions
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generate sample questions when we don't have enough commits
|
113
|
+
def generate_sample_questions
|
114
|
+
questions = []
|
115
|
+
|
116
|
+
# Sample data sets with commit messages and dates
|
117
|
+
sample_data = [
|
118
|
+
[
|
119
|
+
{ message: "Initial commit", sha: "a1b2c3d", date: "2023-01-01 09:00:00" },
|
120
|
+
{ message: "Add user authentication", sha: "e4f5g6h", date: "2023-01-05 14:30:00" },
|
121
|
+
{ message: "Fix login page styling", sha: "i7j8k9l", date: "2023-01-10 11:15:00" },
|
122
|
+
{ message: "Implement password reset", sha: "m2n3o4p", date: "2023-01-15 16:45:00" }
|
123
|
+
],
|
124
|
+
[
|
125
|
+
{ message: "Create README.md", sha: "q5r6s7t", date: "2023-02-01 10:00:00" },
|
126
|
+
{ message: "Setup test framework", sha: "u8v9w0x", date: "2023-02-07 13:20:00" },
|
127
|
+
{ message: "Add CI pipeline config", sha: "y1z2a3b", date: "2023-02-14 09:45:00" },
|
128
|
+
{ message: "Implement first unit tests", sha: "c4d5e6f", date: "2023-02-20 15:30:00" }
|
129
|
+
],
|
130
|
+
[
|
131
|
+
{ message: "Create database schema", sha: "g7h8i9j", date: "2023-03-05 11:00:00" },
|
132
|
+
{ message: "Add User model", sha: "k1l2m3n", date: "2023-03-10 14:15:00" },
|
133
|
+
{ message: "Implement data validation", sha: "o4p5q6r", date: "2023-03-18 10:30:00" },
|
134
|
+
{ message: "Add database migrations", sha: "s7t8u9v", date: "2023-03-25 16:00:00" }
|
135
|
+
],
|
136
|
+
[
|
137
|
+
{ message: "Initial API endpoints", sha: "w0x1y2z", date: "2023-04-02 09:30:00" },
|
138
|
+
{ message: "Add authentication middleware", sha: "a3b4c5d", date: "2023-04-08 13:45:00" },
|
139
|
+
{ message: "Implement rate limiting", sha: "e6f7g8h", date: "2023-04-15 11:20:00" },
|
140
|
+
{ message: "Add API documentation", sha: "i9j0k1l", date: "2023-04-22 15:10:00" }
|
141
|
+
],
|
142
|
+
[
|
143
|
+
{ message: "Create frontend structure", sha: "m2n3o4p", date: "2023-05-01 10:15:00" },
|
144
|
+
{ message: "Implement login component", sha: "q5r6s7t", date: "2023-05-09 14:00:00" },
|
145
|
+
{ message: "Add dashboard UI", sha: "u8v9w0x", date: "2023-05-17 11:30:00" },
|
146
|
+
{ message: "Fix responsive design issues", sha: "y1z2a3b", date: "2023-05-25 16:45:00" }
|
147
|
+
]
|
148
|
+
]
|
149
|
+
|
150
|
+
# Generate one question from each sample set
|
151
|
+
self.class.questions_per_round.times do |i|
|
152
|
+
# Use modulo to cycle through samples if we have fewer samples than questions
|
153
|
+
sample_set = sample_data[i % sample_data.size]
|
154
|
+
|
155
|
+
# Format the options
|
156
|
+
options = sample_set.map { |item| "#{item[:message]} (#{item[:sha]})" }
|
157
|
+
|
158
|
+
# Determine correct order (they're already in order in our samples)
|
159
|
+
correct_order = options.dup
|
160
|
+
|
161
|
+
# Shuffle options for initial display
|
162
|
+
shuffled_options = options.shuffle
|
163
|
+
|
164
|
+
# Format dates for feedback
|
165
|
+
date_strings = sample_set.map { |item| "#{item[:sha]}: #{item[:date]}" }
|
166
|
+
|
167
|
+
questions << {
|
168
|
+
question: "Put these commits in chronological order (oldest to newest):",
|
169
|
+
options: shuffled_options,
|
170
|
+
correct_answer: correct_order,
|
171
|
+
question_type: 'ordering',
|
172
|
+
commit_dates: date_strings.join("\n")
|
173
|
+
}
|
174
|
+
end
|
175
|
+
|
176
|
+
questions
|
177
|
+
end
|
178
|
+
|
179
|
+
def evaluate_answers(question, player_answers)
|
180
|
+
results = {}
|
181
|
+
|
182
|
+
player_answers.each do |player_name, answer_data|
|
183
|
+
player_answer = answer_data[:answer]
|
184
|
+
time_taken = answer_data[:time_taken] || 20
|
185
|
+
|
186
|
+
# For the ordering quiz, we allow partial credit
|
187
|
+
correct_positions = 0
|
188
|
+
|
189
|
+
# Count how many items are in the correct position
|
190
|
+
question[:correct_answer].each_with_index do |item, index|
|
191
|
+
if player_answer && index < player_answer.size && player_answer[index] == item
|
192
|
+
correct_positions += 1
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Calculate points: full credit for all correct, partial for some correct
|
197
|
+
total_items = question[:correct_answer].size
|
198
|
+
points = (correct_positions.to_f / total_items * 10).round
|
199
|
+
|
200
|
+
# Fully correct gets bonus points
|
201
|
+
if correct_positions == total_items
|
202
|
+
# Base bonus for fully correct answer
|
203
|
+
points += 3
|
204
|
+
|
205
|
+
# Additional time-based bonus (faster answers get more points)
|
206
|
+
if time_taken < 8 # Really fast (under 8 seconds)
|
207
|
+
points += 4
|
208
|
+
elsif time_taken < 14 # Pretty fast (under 14 seconds)
|
209
|
+
points += 2
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Create feedback with date info for displaying in results
|
214
|
+
feedback = "#{correct_positions}/#{total_items} positions correct"
|
215
|
+
if question[:commit_dates]
|
216
|
+
feedback += "\n\nActual dates:\n" + question[:commit_dates]
|
217
|
+
end
|
218
|
+
|
219
|
+
results[player_name] = {
|
220
|
+
answer: player_answer,
|
221
|
+
correct: correct_positions == total_items,
|
222
|
+
points: points,
|
223
|
+
partial_score: feedback
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
results
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
metadata
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git_game_show
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Paulson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-03-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: tty-prompt
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.23'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.23'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tty-table
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.12'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tty-cursor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.7'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: eventmachine
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.2'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: websocket-client-simple
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.6'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.6'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: websocket-eventmachine-server
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: git
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.13'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.13'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: clipboard
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.3'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.3'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubyzip
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '2.3'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '2.3'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rspec
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '3.12'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '3.12'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rubocop
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '1.50'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '1.50'
|
195
|
+
description: Git Game Show is a multiplayer game that tests your team's knowledge
|
196
|
+
of Git with various mini-games like author quizzes, commit message quizzes, and
|
197
|
+
more.
|
198
|
+
email:
|
199
|
+
- justinapaulson@gmail.com
|
200
|
+
executables:
|
201
|
+
- git-game-show
|
202
|
+
extensions: []
|
203
|
+
extra_rdoc_files: []
|
204
|
+
files:
|
205
|
+
- LICENSE
|
206
|
+
- README.md
|
207
|
+
- bin/git-game-show
|
208
|
+
- lib/git_game_show.rb
|
209
|
+
- lib/git_game_show/cli.rb
|
210
|
+
- lib/git_game_show/game_server.rb
|
211
|
+
- lib/git_game_show/mini_game.rb
|
212
|
+
- lib/git_game_show/player_client.rb
|
213
|
+
- lib/git_game_show/version.rb
|
214
|
+
- mini_games/author_quiz.rb
|
215
|
+
- mini_games/commit_message_completion.rb
|
216
|
+
- mini_games/commit_message_quiz.rb
|
217
|
+
- mini_games/date_ordering_quiz.rb
|
218
|
+
homepage: https://github.com/justinpaulson/git_game_show
|
219
|
+
licenses:
|
220
|
+
- MIT
|
221
|
+
metadata:
|
222
|
+
allowed_push_host: https://rubygems.org
|
223
|
+
homepage_uri: https://github.com/justinpaulson/git_game_show
|
224
|
+
source_code_uri: https://github.com/justinpaulson/git_game_show
|
225
|
+
changelog_uri: https://github.com/justinpaulson/git_game_show/blob/main/CHANGELOG.md
|
226
|
+
post_install_message:
|
227
|
+
rdoc_options: []
|
228
|
+
require_paths:
|
229
|
+
- lib
|
230
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
231
|
+
requirements:
|
232
|
+
- - ">="
|
233
|
+
- !ruby/object:Gem::Version
|
234
|
+
version: 2.6.0
|
235
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
236
|
+
requirements:
|
237
|
+
- - ">="
|
238
|
+
- !ruby/object:Gem::Version
|
239
|
+
version: '0'
|
240
|
+
requirements: []
|
241
|
+
rubygems_version: 3.5.11
|
242
|
+
signing_key:
|
243
|
+
specification_version: 4
|
244
|
+
summary: A fun interactive multiplayer game based on Git trivia
|
245
|
+
test_files: []
|