git_game_show 0.1.8 → 0.1.10
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/game_server.rb +2 -1
- data/lib/git_game_show/player_client.rb +6 -10
- data/lib/git_game_show/version.rb +1 -1
- data/mini_games/branch_detective.rb +228 -0
- data/mini_games/file_quiz.rb +98 -72
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 716f1e1333d827134e24ece60dd4d14762c72f7c67ba77d4041e3ed020234fc7
|
4
|
+
data.tar.gz: 94fbdf09cf6588a357ffdcb1722d97bd43afb97872c96904ed272539b4d36a54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9ce22d13a247c04acc299bb0d4e988f00db8bddea9c30ff9102196dd2cd8df38accad3fb07368cae4e9deaa5d713729c89d55296fcf21a22afee4157d08b761
|
7
|
+
data.tar.gz: cd91d7b25ca66d04ff9bf53a399021a5adac70224799426401f284de41659a6a8e6909a9aeb430c0d64201faee881b0919a99ac41a6a1fbf1418b643e8abeb6e
|
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),
|
@@ -1594,7 +1594,8 @@ module GitGameShow
|
|
1594
1594
|
GitGameShow::AuthorQuiz,
|
1595
1595
|
GitGameShow::FileQuiz,
|
1596
1596
|
GitGameShow::CommitMessageCompletion,
|
1597
|
-
GitGameShow::DateOrderingQuiz
|
1597
|
+
GitGameShow::DateOrderingQuiz,
|
1598
|
+
GitGameShow::BranchDetective
|
1598
1599
|
]
|
1599
1600
|
end
|
1600
1601
|
end
|
@@ -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,9 +984,9 @@ 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
|
-
puts "│#{'Scoreboard'.center(box_width)}
|
989
|
+
puts "│#{'Scoreboard'.center(box_width)}│".center(@game_width).colorize(:yellow)
|
994
990
|
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:yellow)
|
995
991
|
puts "\n"
|
996
992
|
|
@@ -0,0 +1,228 @@
|
|
1
|
+
module GitGameShow
|
2
|
+
class BranchDetective < MiniGame
|
3
|
+
self.name = "Branch Detective"
|
4
|
+
self.description = "Identify which branch a commit belongs to!"
|
5
|
+
self.questions_per_round = 5
|
6
|
+
|
7
|
+
# Custom timing for this mini-game
|
8
|
+
def self.question_timeout
|
9
|
+
15 # 15 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
|
+
@repo = repo
|
18
|
+
begin
|
19
|
+
# Get all branches (both local and remote)
|
20
|
+
branches = {}
|
21
|
+
|
22
|
+
# Use Git command to get all local and remote branches
|
23
|
+
|
24
|
+
# get all remote branches from the git repository
|
25
|
+
all_remotes_cmd = "cd #{@repo.dir.path} && git branch -r"
|
26
|
+
all_branch_output = `#{all_remotes_cmd}`
|
27
|
+
|
28
|
+
# Parse branch names and clean them up
|
29
|
+
branch_names = all_branch_output.split("\n").map do |branch|
|
30
|
+
branch = branch.gsub(/^\* /, '').strip # Remove the * prefix from current branch
|
31
|
+
|
32
|
+
# Skip special branches like HEAD
|
33
|
+
next if branch == 'HEAD' || branch =~ /HEAD detached/
|
34
|
+
|
35
|
+
branch
|
36
|
+
end.compact.uniq # Remove nils and duplicates
|
37
|
+
|
38
|
+
# Filter out any empty branch names
|
39
|
+
branch_names.reject!(&:empty?)
|
40
|
+
|
41
|
+
# Need at least 3 branches to make interesting questions
|
42
|
+
if branch_names.size < 5
|
43
|
+
return generate_sample_questions
|
44
|
+
end
|
45
|
+
|
46
|
+
branch_names = branch_names.sample(100) if branch_names.size > 100
|
47
|
+
|
48
|
+
branch_names.each do |branch|
|
49
|
+
# Get commits for this branch
|
50
|
+
branches[branch] = get_commits_for_branch(branch)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Generate questions
|
54
|
+
questions = []
|
55
|
+
|
56
|
+
self.class.questions_per_round.times do
|
57
|
+
# Select branches that have commits
|
58
|
+
branch_options = branch_names.sample(4)
|
59
|
+
|
60
|
+
|
61
|
+
# If we don't have 4 valid branches, pad with duplicates and ensure uniqueness later
|
62
|
+
if branch_options.size < 4
|
63
|
+
branch_options = branch_names.dup
|
64
|
+
while branch_options.size < 4
|
65
|
+
branch_options << branch_names.sample
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Choose a random branch as the correct answer
|
70
|
+
correct_branch = branch_options.sample
|
71
|
+
|
72
|
+
# Choose a random commit from this branch
|
73
|
+
commit = branches[correct_branch].sample
|
74
|
+
|
75
|
+
# Create the question
|
76
|
+
commit_date = commit[:date] #.split(" ")[0..2].join(" ") + " " + commit[:date].split(" ")[4]
|
77
|
+
commit_short_sha = commit[:sha][0..6]
|
78
|
+
|
79
|
+
# Format the commit message for display
|
80
|
+
message = commit[:message].lines.first&.strip || "No message"
|
81
|
+
message = message.length > 50 ? "#{message[0...47]}..." : message
|
82
|
+
|
83
|
+
questions << {
|
84
|
+
question: "Which branch was this commit originally made on?\n\n \"#{message}\"",
|
85
|
+
commit_info: "#{commit_short_sha} (by #{commit[:author]} on #{commit_date})",
|
86
|
+
options: branch_options.uniq.shuffle,
|
87
|
+
correct_answer: correct_branch
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
# If we couldn't generate enough questions, fill with sample questions
|
92
|
+
if questions.size < self.class.questions_per_round
|
93
|
+
sample_questions = generate_sample_questions
|
94
|
+
questions += sample_questions[0...(self.class.questions_per_round - questions.size)]
|
95
|
+
end
|
96
|
+
|
97
|
+
questions
|
98
|
+
rescue => e
|
99
|
+
# If any errors occur, fall back to sample questions
|
100
|
+
generate_sample_questions
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Generate sample questions
|
105
|
+
def generate_sample_questions
|
106
|
+
questions = []
|
107
|
+
|
108
|
+
# Sample data with branch names and commits
|
109
|
+
sample_branches = [
|
110
|
+
"main", "develop", "feature/user-auth", "bugfix/login",
|
111
|
+
"feature/payment", "hotfix/security", "release/v2.0", "staging"
|
112
|
+
]
|
113
|
+
|
114
|
+
# Sample commit data
|
115
|
+
sample_commits = [
|
116
|
+
{ message: "[SAMPLE] Add user authentication flow", author: "Jane Doe", date: "2023-05-15 14:30:22", sha: "a1b2c3d" },
|
117
|
+
{ message: "[SAMPLE] Fix login page styling issues", author: "John Smith", date: "2023-05-18 10:15:45", sha: "e4f5g6h" },
|
118
|
+
{ message: "[SAMPLE] Implement password reset functionality", author: "Alice Johnson", date: "2023-05-20 16:45:12", sha: "i7j8k9l" },
|
119
|
+
{ message: "[SAMPLE] Add payment gateway integration", author: "Bob Brown", date: "2023-05-22 09:20:33", sha: "m2n3o4p" },
|
120
|
+
{ message: "[SAMPLE] Update README with API documentation", author: "Charlie Davis", date: "2023-05-25 11:05:56", sha: "q5r6s7t" }
|
121
|
+
]
|
122
|
+
|
123
|
+
# Generate sample questions
|
124
|
+
self.class.questions_per_round.times do |i|
|
125
|
+
# Select a random commit
|
126
|
+
commit = sample_commits[i % sample_commits.size]
|
127
|
+
|
128
|
+
# Select 4 random branch names
|
129
|
+
branch_options = sample_branches.sample(4)
|
130
|
+
|
131
|
+
# Choose a correct branch
|
132
|
+
correct_branch = branch_options.sample
|
133
|
+
|
134
|
+
questions << {
|
135
|
+
question: "Which branch was this commit originally made on?\n\n \"#{commit[:message]}\"",
|
136
|
+
commit_info: "#{commit[:sha]} (by #{commit[:author]} on #{commit[:date]})",
|
137
|
+
options: branch_options,
|
138
|
+
correct_answer: correct_branch
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
questions
|
143
|
+
end
|
144
|
+
|
145
|
+
def evaluate_answers(question, player_answers)
|
146
|
+
results = {}
|
147
|
+
|
148
|
+
player_answers.each do |player_name, answer_data|
|
149
|
+
answered = answer_data[:answered] || false
|
150
|
+
player_answer = answer_data[:answer]
|
151
|
+
time_taken = answer_data[:time_taken] || self.class.question_timeout
|
152
|
+
|
153
|
+
# Check if the answer is correct
|
154
|
+
correct = player_answer == question[:correct_answer]
|
155
|
+
|
156
|
+
# Calculate points
|
157
|
+
points = 0
|
158
|
+
|
159
|
+
# Base points for correct answer
|
160
|
+
if correct
|
161
|
+
points = 10
|
162
|
+
|
163
|
+
# Bonus points for fast answers
|
164
|
+
if time_taken < 5
|
165
|
+
points += 5 # Very fast (under 5 seconds)
|
166
|
+
elsif time_taken < 10
|
167
|
+
points += 3 # Pretty fast (under 10 seconds)
|
168
|
+
elsif time_taken < 12
|
169
|
+
points += 1 # Somewhat fast (under 12 seconds)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Store the results
|
174
|
+
results[player_name] = {
|
175
|
+
answer: player_answer,
|
176
|
+
correct: correct,
|
177
|
+
points: points
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
results
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def get_commits_for_branch branch
|
187
|
+
unique_commits = []
|
188
|
+
|
189
|
+
# Try different ways to reference the branch
|
190
|
+
begin
|
191
|
+
# Try a few different ways to reference the branch
|
192
|
+
got_commits = false
|
193
|
+
|
194
|
+
# First try as a local branch
|
195
|
+
cmd = "cd #{@repo.dir.path} && git log --pretty '#{branch}' --max-count=5 2>/dev/null"
|
196
|
+
commit_output = `#{cmd}`
|
197
|
+
|
198
|
+
# Process commits if we found any
|
199
|
+
commits = commit_output.split("commit ")[1..-1]
|
200
|
+
|
201
|
+
commits.each do |commit|
|
202
|
+
|
203
|
+
# Extract commit info
|
204
|
+
sha = commit.lines[0].split(" ")[0].strip
|
205
|
+
author = commit.lines[1].gsub("Author: ", "").split("<")[0].strip
|
206
|
+
date = commit.lines[2].gsub("Date: ", "").strip
|
207
|
+
message = commit.lines[4..-1].join("\n")
|
208
|
+
|
209
|
+
next unless message.length > 10
|
210
|
+
next if message.include?("Merge pull request")
|
211
|
+
# Store this commit info
|
212
|
+
unique_commits << {
|
213
|
+
sha: sha,
|
214
|
+
message: message,
|
215
|
+
author: author,
|
216
|
+
date: date
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
rescue => e
|
221
|
+
# If we hit any errors with this branch, just skip it
|
222
|
+
[]
|
223
|
+
end
|
224
|
+
|
225
|
+
unique_commits
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
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
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_game_show
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Paulson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -213,6 +213,7 @@ files:
|
|
213
213
|
- lib/git_game_show/updater.rb
|
214
214
|
- lib/git_game_show/version.rb
|
215
215
|
- mini_games/author_quiz.rb
|
216
|
+
- mini_games/branch_detective.rb
|
216
217
|
- mini_games/commit_message_completion.rb
|
217
218
|
- mini_games/date_ordering_quiz.rb
|
218
219
|
- mini_games/file_quiz.rb
|