git_game_show 0.1.8 → 0.1.9
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/lib/git_game_show/cli.rb +1 -0
- data/lib/git_game_show/player_client.rb +5 -9
- data/lib/git_game_show/version.rb +1 -1
- data/mini_games/file_quiz.rb +98 -72
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6dd69e30776e28d63d3fd20db47716c0e957dace72788a5e0479047fe8cbe708
|
4
|
+
data.tar.gz: cd8e8579ec722b25a67ffd1c8268f35a724dcf75cd863420ffebad9025f1ebf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8874ab5bb9a8349e9b22926e8456fef2aa1ce4bcdaf1ca14b170d82d6e0491a68db6c576eda153b92fbca942efa1861dd44d139d1d57e709fe2853e5c2b36fe6
|
7
|
+
data.tar.gz: 37c9292edb46b4e3be1283efbd09b3762c504696a5d875a16c7d8a12fdbc68265319f40b4db992e0c4fd71d42bf420abcce75bd3e62402c741ba38e459c265a2
|
data/lib/git_game_show/cli.rb
CHANGED
@@ -510,6 +510,7 @@ module GitGameShow
|
|
510
510
|
|
511
511
|
def display_ggs
|
512
512
|
clear_screen
|
513
|
+
puts ""
|
513
514
|
lines = [
|
514
515
|
" ██████╗ ".colorize(:red) + " ██████╗ ".colorize(:green) + " █████╗".colorize(:blue),
|
515
516
|
"██╔════╝ ".colorize(:red) + " ██╔════╝ ".colorize(:green) + " ██╔═══╝".colorize(:blue),
|
@@ -543,7 +543,7 @@ module GitGameShow
|
|
543
543
|
|
544
544
|
# Display a fun "Game Starting" animation
|
545
545
|
box_width = 40
|
546
|
-
puts "\n
|
546
|
+
puts "\n"
|
547
547
|
puts ("╭" + "─" * box_width + "╮").center(@game_width).colorize(:green)
|
548
548
|
puts ("│" + "Game starting...".center(box_width) + "│").center(@game_width).colorize(:green)
|
549
549
|
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:green)
|
@@ -600,8 +600,6 @@ module GitGameShow
|
|
600
600
|
mini_game = data['mini_game']
|
601
601
|
description = data['description']
|
602
602
|
|
603
|
-
puts "\n\n"
|
604
|
-
|
605
603
|
# Box is drawn with exactly 45 "━" characters for the top and bottom borders
|
606
604
|
# The top and bottom including borders are 48 characters wide
|
607
605
|
box_width = 42
|
@@ -610,6 +608,7 @@ module GitGameShow
|
|
610
608
|
box_middle = "│#{"Round #{round_num} of #{total_rounds}".center(box_width - 2)}│".center(@game_width)
|
611
609
|
|
612
610
|
# Output the box
|
611
|
+
puts "\n"
|
613
612
|
puts box_top.colorize(:green)
|
614
613
|
puts box_middle.colorize(:green)
|
615
614
|
puts box_bottom.colorize(:green)
|
@@ -643,9 +642,6 @@ module GitGameShow
|
|
643
642
|
|
644
643
|
# No need to reserve space for timer - it will be at the bottom of the screen
|
645
644
|
|
646
|
-
# Display question header
|
647
|
-
puts "\n"
|
648
|
-
|
649
645
|
# Draw a simple box for the question header
|
650
646
|
box_width = 42
|
651
647
|
box_top = ("╭" + "─" * (box_width - 2) + "╮").center(@game_width)
|
@@ -653,6 +649,7 @@ module GitGameShow
|
|
653
649
|
box_middle = "│#{"Question #{question_num} of #{total_questions}".center(box_width - 2)}│".center(@game_width)
|
654
650
|
|
655
651
|
# Output the question box
|
652
|
+
puts "\n"
|
656
653
|
puts box_top.colorize(:light_blue)
|
657
654
|
puts box_middle.colorize(:light_blue)
|
658
655
|
puts box_bottom.colorize(:light_blue)
|
@@ -867,8 +864,6 @@ module GitGameShow
|
|
867
864
|
# Start with a clean screen
|
868
865
|
clear_screen
|
869
866
|
|
870
|
-
puts "\n"
|
871
|
-
|
872
867
|
# Box is drawn with exactly 45 "━" characters for the top and bottom borders
|
873
868
|
# The top and bottom including borders are 48 characters wide
|
874
869
|
box_width = 40
|
@@ -877,6 +872,7 @@ module GitGameShow
|
|
877
872
|
box_middle = "│#{'Round Results'.center(box_width)}│".center(@game_width)
|
878
873
|
|
879
874
|
# Output the box
|
875
|
+
puts "\n"
|
880
876
|
puts box_top.colorize(:light_blue)
|
881
877
|
puts box_middle.colorize(:light_blue)
|
882
878
|
puts box_bottom.colorize(:light_blue)
|
@@ -988,7 +984,7 @@ module GitGameShow
|
|
988
984
|
clear_screen
|
989
985
|
|
990
986
|
box_width = 40
|
991
|
-
puts ""
|
987
|
+
puts "\n"
|
992
988
|
puts ("╭" + "─" * box_width + "╮").center(@game_width).colorize(:yellow)
|
993
989
|
puts "│#{'Scoreboard'.center(box_width)}┃".center(@game_width).colorize(:yellow)
|
994
990
|
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:yellow)
|
data/mini_games/file_quiz.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
module GitGameShow
|
2
|
+
# Disable debug mode for normal operation
|
3
|
+
$FILE_QUIZ_DEBUG = false
|
4
|
+
|
2
5
|
class FileQuiz < MiniGame
|
3
6
|
self.name = "File Quiz"
|
4
7
|
self.description = "Match the commit message to the right changed file!"
|
@@ -15,39 +18,54 @@ module GitGameShow
|
|
15
18
|
|
16
19
|
def generate_questions(repo)
|
17
20
|
begin
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
# Use the same approach as AuthorQuiz - get ALL commits using the helper method
|
22
|
+
commits = get_all_commits(repo)
|
23
|
+
|
24
|
+
# Check if we got any commits at all
|
25
|
+
if commits.nil? || commits.empty?
|
26
|
+
# Silently fall back to sample questions
|
27
|
+
return generate_sample_questions
|
28
|
+
end
|
29
|
+
|
25
30
|
# Shuffle commits for better variety
|
26
31
|
commits.shuffle!
|
27
|
-
|
32
|
+
|
28
33
|
# Process commits to find ones with good file changes
|
29
34
|
valid_commits = []
|
30
|
-
|
35
|
+
|
31
36
|
commits.each do |commit|
|
32
37
|
# Get changed files for this commit
|
33
38
|
changed_files = []
|
34
|
-
|
39
|
+
|
35
40
|
begin
|
36
|
-
#
|
41
|
+
# Use standard Git methods instead of run_command (silently)
|
42
|
+
|
43
|
+
# Use git commands quietly without showing output
|
37
44
|
if commit.parents.empty?
|
38
|
-
#
|
39
|
-
|
40
|
-
|
45
|
+
# For first commit, get files directly
|
46
|
+
cmd = "cd #{repo.dir.path} && git show --name-only --pretty=format: #{commit.sha} 2>/dev/null"
|
47
|
+
diff_output = `#{cmd}`
|
41
48
|
else
|
42
|
-
#
|
43
|
-
|
44
|
-
|
49
|
+
# For normal commits with parents
|
50
|
+
cmd = "cd #{repo.dir.path} && git diff --name-only #{commit.sha}^ #{commit.sha} 2>/dev/null"
|
51
|
+
diff_output = `#{cmd}`
|
52
|
+
|
53
|
+
# If diff returns empty, try using show as fallback
|
54
|
+
if diff_output.strip.empty?
|
55
|
+
cmd = "cd #{repo.dir.path} && git show --name-only --pretty=format: #{commit.sha} 2>/dev/null"
|
56
|
+
diff_output = `#{cmd}`
|
57
|
+
end
|
45
58
|
end
|
46
|
-
|
47
|
-
#
|
59
|
+
|
60
|
+
# Parse the output to get changed files (quietly)
|
61
|
+
changed_files = diff_output.split("\n").reject(&:empty?)
|
62
|
+
|
63
|
+
# Skip if no files changed (can't create a question for this)
|
48
64
|
next if changed_files.empty?
|
49
|
-
|
50
|
-
|
65
|
+
|
66
|
+
# Super relaxed - accept ANY commit with files that changed
|
67
|
+
# No more filtering by number of files
|
68
|
+
|
51
69
|
# Create structure with relevant commit data
|
52
70
|
valid_commits << {
|
53
71
|
sha: commit.sha,
|
@@ -56,29 +74,31 @@ module GitGameShow
|
|
56
74
|
date: commit.date,
|
57
75
|
files: changed_files
|
58
76
|
}
|
59
|
-
|
77
|
+
|
60
78
|
# Once we have enough commits, we can stop
|
61
|
-
|
79
|
+
# Get more commits to have a better selection
|
80
|
+
break if valid_commits.size >= self.class.questions_per_round * 5
|
62
81
|
rescue => e
|
63
|
-
#
|
82
|
+
# Silently skip problematic commits
|
64
83
|
next
|
65
84
|
end
|
66
85
|
end
|
67
|
-
|
68
|
-
#
|
69
|
-
if valid_commits.
|
86
|
+
|
87
|
+
# Never fall back to samples as long as we have at least 1 valid commit
|
88
|
+
if valid_commits.empty?
|
89
|
+
# Silently fall back to sample questions if needed
|
70
90
|
return generate_sample_questions
|
71
91
|
end
|
72
|
-
|
92
|
+
|
73
93
|
# Prioritize commits that modified interesting files (not just .gitignore etc.)
|
74
94
|
prioritized_commits = valid_commits.sort_by do |commit|
|
75
95
|
# Higher score = more interesting commit
|
76
96
|
score = 0
|
77
|
-
|
97
|
+
|
78
98
|
# Prioritize based on file types
|
79
99
|
commit[:files].each do |file|
|
80
100
|
ext = File.extname(file).downcase
|
81
|
-
|
101
|
+
|
82
102
|
case ext
|
83
103
|
when '.rb', '.js', '.py', '.java', '.tsx', '.jsx'
|
84
104
|
score += 3 # Source code is most interesting
|
@@ -90,30 +110,33 @@ module GitGameShow
|
|
90
110
|
score -= 1 # Less interesting files
|
91
111
|
end
|
92
112
|
end
|
93
|
-
|
113
|
+
|
94
114
|
# Prioritize based on commit message length - longer messages are often more descriptive
|
95
115
|
message_length = commit[:message].to_s.strip.length
|
96
116
|
score += [message_length / 20, 5].min
|
97
|
-
|
117
|
+
|
98
118
|
# Return negative score so highest scores come first in sort
|
99
119
|
-score
|
100
120
|
end
|
101
|
-
|
102
|
-
#
|
103
|
-
|
104
|
-
|
121
|
+
|
122
|
+
# Take as many commits as we need for questions
|
123
|
+
needed_commits = [self.class.questions_per_round, prioritized_commits.size].min
|
124
|
+
selected_commits = prioritized_commits.take(needed_commits)
|
125
|
+
|
105
126
|
# Create questions from selected commits
|
106
127
|
questions = []
|
107
|
-
|
128
|
+
|
108
129
|
selected_commits.each do |commit|
|
109
130
|
# Choose the most interesting file as the correct answer
|
110
|
-
# (Sort by extension priority, then by path length to favor shorter paths)
|
111
131
|
files = commit[:files]
|
112
|
-
|
132
|
+
|
133
|
+
# Skip if somehow we got a commit with no files
|
134
|
+
next if files.empty?
|
135
|
+
|
113
136
|
# Score files by interestingness
|
114
137
|
scored_files = files.map do |file|
|
115
138
|
ext = File.extname(file).downcase
|
116
|
-
|
139
|
+
|
117
140
|
# Start with base score by extension
|
118
141
|
score = case ext
|
119
142
|
when '.rb', '.js', '.py', '.java', '.tsx', '.jsx'
|
@@ -129,32 +152,33 @@ module GitGameShow
|
|
129
152
|
else
|
130
153
|
5 # Other files are neutral
|
131
154
|
end
|
132
|
-
|
155
|
+
|
133
156
|
# Shorter paths are usually more recognizable
|
134
157
|
score -= [file.length / 10, 5].min
|
135
|
-
|
158
|
+
|
136
159
|
# Prefer files in main directories (src, lib, app) over deeply nested ones
|
137
160
|
if file.start_with?('src/', 'lib/', 'app/')
|
138
161
|
score += 3
|
139
162
|
end
|
140
|
-
|
163
|
+
|
141
164
|
[file, score]
|
142
165
|
end
|
143
|
-
|
166
|
+
|
144
167
|
# Sort by score (highest first) and select most interesting file
|
145
|
-
|
146
|
-
|
168
|
+
scored_files.sort_by! { |_, score| -score }
|
169
|
+
correct_file = scored_files.first[0]
|
170
|
+
|
147
171
|
# Get incorrect options from other commits
|
148
172
|
other_files = []
|
149
173
|
other_commits = selected_commits - [commit]
|
150
|
-
|
174
|
+
|
151
175
|
# Collect files from other commits
|
152
176
|
other_commits.each do |other_commit|
|
153
177
|
other_commit[:files].each do |file|
|
154
178
|
other_files << file unless files.include?(file)
|
155
179
|
end
|
156
180
|
end
|
157
|
-
|
181
|
+
|
158
182
|
# If we don't have enough other files, use some from sample data
|
159
183
|
if other_files.size < 3
|
160
184
|
sample_files = [
|
@@ -164,19 +188,19 @@ module GitGameShow
|
|
164
188
|
]
|
165
189
|
other_files += sample_files.reject { |f| files.include?(f) }
|
166
190
|
end
|
167
|
-
|
191
|
+
|
168
192
|
# Take up to 3 unique other files, prioritizing diverse ones
|
169
193
|
other_files = other_files.uniq.sample(3)
|
170
|
-
|
194
|
+
|
171
195
|
# Create options array with the correct answer and incorrect ones
|
172
196
|
all_options = ([correct_file] + other_files).shuffle
|
173
|
-
|
197
|
+
|
174
198
|
# Format the commit date nicely
|
175
199
|
nice_date = commit[:date].strftime('%b %d, %Y') rescue "Unknown date"
|
176
|
-
|
200
|
+
|
177
201
|
# Clean up commit message - take first line if multiple lines
|
178
202
|
message = commit[:message].to_s.split("\n").first.strip
|
179
|
-
|
203
|
+
|
180
204
|
# Format consistently with other mini-games
|
181
205
|
questions << {
|
182
206
|
question: "Which file was most likely changed in this commit?\n\n \"#{message}\"",
|
@@ -185,62 +209,66 @@ module GitGameShow
|
|
185
209
|
correct_answer: correct_file
|
186
210
|
}
|
187
211
|
end
|
188
|
-
|
189
|
-
#
|
212
|
+
|
213
|
+
# If we still couldn't create enough questions, use a mix of real and samples
|
190
214
|
if questions.size < self.class.questions_per_round
|
191
|
-
|
215
|
+
# Add sample questions to fill the remaining slots (silently)
|
216
|
+
sample_questions = generate_sample_questions
|
217
|
+
remaining_slots = self.class.questions_per_round - questions.size
|
218
|
+
questions += sample_questions.take(remaining_slots)
|
192
219
|
end
|
193
|
-
|
220
|
+
|
194
221
|
return questions
|
195
222
|
rescue => e
|
196
|
-
# If anything fails, fall back to sample questions
|
223
|
+
# If anything fails, silently fall back to sample questions
|
197
224
|
return generate_sample_questions
|
198
225
|
end
|
199
226
|
end
|
200
227
|
|
201
228
|
def evaluate_answers(question, player_answers)
|
202
229
|
results = {}
|
203
|
-
|
230
|
+
|
204
231
|
player_answers.each do |player_name, answer_data|
|
205
232
|
answered = answer_data[:answered] || false
|
206
233
|
player_answer = answer_data[:answer]
|
207
234
|
correct = player_answer == question[:correct_answer]
|
208
|
-
|
235
|
+
|
209
236
|
points = correct ? 10 : 0
|
210
|
-
|
237
|
+
|
211
238
|
# Bonus points for fast answers (if correct)
|
212
239
|
if correct
|
213
240
|
time_taken = answer_data[:time_taken] || 15
|
214
|
-
|
241
|
+
|
215
242
|
if time_taken < 5
|
216
243
|
points += 5
|
217
244
|
elsif time_taken < 10
|
218
245
|
points += 3
|
219
246
|
end
|
220
247
|
end
|
221
|
-
|
248
|
+
|
222
249
|
results[player_name] = {
|
223
250
|
answer: player_answer,
|
224
251
|
correct: correct,
|
225
252
|
points: points
|
226
253
|
}
|
227
254
|
end
|
228
|
-
|
255
|
+
|
229
256
|
results
|
230
257
|
end
|
231
258
|
|
232
259
|
# Generate sample questions only used when no repository data is available
|
233
260
|
def generate_sample_questions
|
261
|
+
# No debug message in production
|
234
262
|
questions = []
|
235
263
|
|
236
264
|
# Common file options
|
237
265
|
common_files = [
|
238
266
|
"src/main.js",
|
239
267
|
"README.md",
|
240
|
-
"lib/utils.js",
|
268
|
+
"lib/utils.js",
|
241
269
|
"css/styles.css"
|
242
270
|
]
|
243
|
-
|
271
|
+
|
244
272
|
# Common sample commit messages
|
245
273
|
sample_messages = [
|
246
274
|
"Update documentation with new API endpoints",
|
@@ -254,14 +282,14 @@ module GitGameShow
|
|
254
282
|
self.class.questions_per_round.times do |i|
|
255
283
|
# Use modulo to cycle through sample messages
|
256
284
|
message = sample_messages[i % sample_messages.size]
|
257
|
-
|
285
|
+
|
258
286
|
# Different correct answers for each question
|
259
287
|
correct_file = common_files[i % common_files.size]
|
260
|
-
|
288
|
+
|
261
289
|
# Options are all files with the correct one included
|
262
290
|
all_options = common_files.shuffle
|
263
|
-
|
264
|
-
# Create the question
|
291
|
+
|
292
|
+
# Create the question - clearly label as sample data
|
265
293
|
questions << {
|
266
294
|
question: "Which file was most likely changed in this commit?\n\n \"#{message} (SAMPLE)\"",
|
267
295
|
commit_info: "sample#{i} (Demo Question)",
|
@@ -272,7 +300,5 @@ module GitGameShow
|
|
272
300
|
|
273
301
|
questions
|
274
302
|
end
|
275
|
-
|
276
|
-
# No private methods needed anymore
|
277
303
|
end
|
278
304
|
end
|