git_game_show 0.1.10 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 716f1e1333d827134e24ece60dd4d14762c72f7c67ba77d4041e3ed020234fc7
4
- data.tar.gz: 94fbdf09cf6588a357ffdcb1722d97bd43afb97872c96904ed272539b4d36a54
3
+ metadata.gz: 3a9bd7f2e076ca47a5bb8205267ce51f3568e9ee9d03acb599c4014ae259278e
4
+ data.tar.gz: 2752fe1f922c36d5ccbcf46c5f45cb13e0377a08f46f6d71629d9eb6cba63d8d
5
5
  SHA512:
6
- metadata.gz: f9ce22d13a247c04acc299bb0d4e988f00db8bddea9c30ff9102196dd2cd8df38accad3fb07368cae4e9deaa5d713729c89d55296fcf21a22afee4157d08b761
7
- data.tar.gz: cd91d7b25ca66d04ff9bf53a399021a5adac70224799426401f284de41659a6a8e6909a9aeb430c0d64201faee881b0919a99ac41a6a1fbf1418b643e8abeb6e
6
+ metadata.gz: b2dccfb2deae7c56d482f0429e5d70d17f781be4d9bce8db78d8c02aa58b50b61465fb55e8a692e9eb93d2d706f7786dd0ae20b68f0957a3050481e05f207b39
7
+ data.tar.gz: ddb0d3486564fbbaaa1086d4705404150096e7975374e8c0accd3294fbc2e0eda855cdc8a04309693fe3281d007308ea1fc9efe46a72d21fe7e81f42cc1f5bda
@@ -165,28 +165,60 @@ module GitGameShow
165
165
  print @cursor.move_to(@main_width + 2, 5)
166
166
  print "Waiting for players...".colorize(:light_black)
167
167
  else
168
+ # Sort players by score (highest first)
169
+ sorted_players = @players.keys.sort_by { |name| -(@scores[name] || 0) }
170
+
168
171
  # Show scrolling indicator if needed
169
172
  if @players.size > max_visible_players
170
173
  print @cursor.move_to(@main_width + 2, 4)
171
174
  print "Showing #{max_visible_players} of #{@players.size}:".colorize(:light_yellow)
172
175
  end
173
176
 
174
- # Determine which players to display (for now, show first N players)
175
- visible_players = @players.keys.take(max_visible_players)
177
+ # Determine which players to display (show top N players by score)
178
+ visible_players = sorted_players.take(max_visible_players)
176
179
 
177
- # Display visible players
180
+ # Display visible players with their scores
178
181
  visible_players.each_with_index do |name, index|
179
182
  print @cursor.move_to(@main_width + 2, 5 + index)
180
- # Truncate long names
181
- truncated_name = name.length > (@sidebar_width - 6) ?
182
- "#{name[0...(@sidebar_width-9)]}..." :
183
- name
184
183
 
185
- if index < 9
186
- print "#{index + 1}. #{truncated_name}".colorize(:light_blue)
184
+ # Get score (default to 0 if not found)
185
+ score = @scores[name] || 0
186
+ score_str = score.to_s
187
+
188
+ # Calculate available space for name and right-justified score
189
+ # Allow space for prefix (like "🥇 " or "10. ") + minimum 3 chars of name + 2 spaces + score
190
+ usable_width = @sidebar_width - 6
191
+ prefix_width = 3 # Account for emoji or number + dot + space
192
+
193
+ # Apply medal emoji for top 3 players when in game
194
+ prefix = ""
195
+ if @game_state == :playing && @scores.any?
196
+ prefix = case index
197
+ when 0 then "🥇 "
198
+ when 1 then "🥈 "
199
+ when 2 then "🥉 "
200
+ else "#{index + 1}. "
201
+ end
187
202
  else
188
- print "#{index + 1}. #{truncated_name}".colorize(:light_blue)
203
+ prefix = "#{index + 1}. "
189
204
  end
205
+
206
+ # Calculate how much space we have for the name
207
+ max_name_length = usable_width - score_str.length - 1 # 2 spaces before score
208
+
209
+ # Truncate long names
210
+ truncated_name = name.length > max_name_length ?
211
+ "#{name[0...(max_name_length-3)]}..." :
212
+ name
213
+
214
+ # Print player name
215
+ print @cursor.move_to(@main_width + 2, 5 + index)
216
+ print "#{prefix}#{truncated_name}".colorize(:light_blue)
217
+
218
+ # Print right-justified score
219
+ score_position = @main_width + usable_width
220
+ print @cursor.move_to(score_position, 5 + index)
221
+ print score_str.colorize(:light_blue)
190
222
  end
191
223
 
192
224
  # If there are more players than can be shown, add an indicator
@@ -391,6 +423,8 @@ module GitGameShow
391
423
  current_question = @round_questions[@current_question_index]
392
424
 
393
425
  # Handle nil answer (timeout) differently
426
+ points = 0
427
+
394
428
  if answer.nil?
395
429
  # For timeouts, set a special "TIMEOUT" answer with 0 points
396
430
  @player_answers[player_name] = {
@@ -398,7 +432,7 @@ module GitGameShow
398
432
  time_taken: time_taken,
399
433
  answered: true,
400
434
  correct: false,
401
- points: 0
435
+ points: points
402
436
  }
403
437
 
404
438
  # Send timeout feedback to the player
@@ -407,7 +441,7 @@ module GitGameShow
407
441
  answer: "TIMEOUT",
408
442
  correct: false,
409
443
  correct_answer: current_question[:correct_answer],
410
- points: 0
444
+ points: points
411
445
  }
412
446
  @players[player_name]&.send(feedback.to_json)
413
447
 
@@ -421,7 +455,8 @@ module GitGameShow
421
455
  if current_question[:question_type] == 'ordering'
422
456
  # Just store the answer and time, points will be calculated in evaluate_answers
423
457
  correct = false # Will be properly set during evaluation
424
- points = 0 # Will be properly set during evaluation
458
+ points = @current_mini_game.evaluate_answers(current_question, {player_name => {answer: answer, time_taken: time_taken}})
459
+ points = points.values.first[:points]
425
460
  else
426
461
  # For regular quizzes, calculate points immediately
427
462
  correct = answer == current_question[:correct_answer]
@@ -575,6 +610,9 @@ module GitGameShow
575
610
  results.each do |player, result|
576
611
  @scores[player] = (@scores[player] || 0) + (result[:points] || 0)
577
612
  end
613
+
614
+ # Update the player list in sidebar to reflect new scores and ranking
615
+ update_player_list
578
616
  rescue => e
579
617
  log_message("Error evaluating answers: #{e.message}", :red)
580
618
  end
@@ -727,8 +765,6 @@ module GitGameShow
727
765
 
728
766
  # Select mini-game for this round with better variety
729
767
  @current_mini_game = select_next_mini_game.new
730
- @round_questions = @current_mini_game.generate_questions(@repo)
731
- @current_question_index = 0
732
768
 
733
769
  # Announce new round
734
770
  broadcast_message({
@@ -736,9 +772,14 @@ module GitGameShow
736
772
  round: @current_round,
737
773
  total_rounds: @rounds,
738
774
  mini_game: @current_mini_game.class.name,
739
- description: @current_mini_game.class.description
775
+ description: @current_mini_game.class.description,
776
+ example: @current_mini_game.class.example
740
777
  })
741
778
 
779
+ @round_questions = @current_mini_game.generate_questions(@repo)
780
+ @current_question_index = 0
781
+
782
+
742
783
  log_message("Starting round #{@current_round}: #{@current_mini_game.class.name}", :cyan)
743
784
 
744
785
  # Start the first question after a short delay
@@ -826,6 +867,11 @@ module GitGameShow
826
867
  question_data[:commit_info] = current_question[:commit_info].to_s
827
868
  end
828
869
  end
870
+
871
+ # Add context if available (for BlameGame)
872
+ if current_question && current_question[:context]
873
+ question_data[:context] = current_question[:context].to_s
874
+ end
829
875
  rescue => e
830
876
  log_message("Error adding additional question data: #{e.message}", :red)
831
877
  # Continue without the additional data
@@ -1595,7 +1641,8 @@ module GitGameShow
1595
1641
  GitGameShow::FileQuiz,
1596
1642
  GitGameShow::CommitMessageCompletion,
1597
1643
  GitGameShow::DateOrderingQuiz,
1598
- GitGameShow::BranchDetective
1644
+ GitGameShow::BranchDetective,
1645
+ GitGameShow::BlameGame
1599
1646
  ]
1600
1647
  end
1601
1648
  end
@@ -5,53 +5,53 @@ module GitGameShow
5
5
  def descendants
6
6
  @descendants ||= []
7
7
  end
8
-
8
+
9
9
  def inherited(subclass)
10
10
  descendants << subclass
11
11
  end
12
-
13
- attr_accessor :name, :description, :questions_per_round
12
+
13
+ attr_accessor :name, :description, :example, :questions_per_round
14
14
  end
15
-
15
+
16
16
  # Default number of questions per round
17
17
  self.questions_per_round = 5
18
-
18
+
19
19
  # Default name and description
20
20
  self.name = "Base Mini Game"
21
21
  self.description = "This is a base mini game class. You should not see this in the actual game."
22
-
22
+
23
23
  # Method to generate questions based on Git repo data
24
24
  # This should be overridden by subclasses
25
25
  def generate_questions(repo)
26
26
  raise NotImplementedError, "#{self.class} must implement #generate_questions"
27
27
  end
28
-
28
+
29
29
  # Method to evaluate player answers and return results
30
30
  # This should be overridden by subclasses
31
31
  def evaluate_answers(question, player_answers)
32
32
  raise NotImplementedError, "#{self.class} must implement #evaluate_answers"
33
33
  end
34
-
34
+
35
35
  # Helper method to get all commits from a repo
36
36
  def get_all_commits(repo)
37
37
  # Get a larger number of commits to ensure more diversity
38
38
  repo.log(1000).each.to_a
39
39
  end
40
-
40
+
41
41
  # Helper method to get unique authors from commits
42
42
  def get_commit_authors(commits)
43
43
  commits.map { |commit| commit.author.name }.uniq
44
44
  end
45
-
45
+
46
46
  # Helper method to get commit messages
47
47
  def get_commit_messages(commits)
48
48
  commits.map(&:message)
49
49
  end
50
-
50
+
51
51
  # Helper method to shuffle an array with the option to exclude certain items
52
52
  def shuffled_excluding(array, exclude = nil)
53
53
  items = exclude ? array.reject { |item| item == exclude } : array.dup
54
54
  items.shuffle
55
55
  end
56
56
  end
57
- end
57
+ end
@@ -115,7 +115,7 @@ module GitGameShow
115
115
  rescue JSON::ParserError => e
116
116
  puts "Invalid message format: #{e.message}".colorize(:red)
117
117
  rescue => e
118
- puts "Error processing message: #{e.message}".colorize(:red)
118
+ puts "Error processing message: #{e.message}\n#{e.backtrace.join("\n")}".colorize(:red)
119
119
  end
120
120
  end
121
121
 
@@ -599,6 +599,7 @@ module GitGameShow
599
599
  total_rounds = data['total_rounds']
600
600
  mini_game = data['mini_game']
601
601
  description = data['description']
602
+ example = data['example']
602
603
 
603
604
  # Box is drawn with exactly 45 "━" characters for the top and bottom borders
604
605
  # The top and bottom including borders are 48 characters wide
@@ -616,7 +617,8 @@ module GitGameShow
616
617
  puts " Mini-game: #{mini_game}".colorize(:light_blue)
617
618
  puts " #{description}".colorize(:light_blue)
618
619
  puts "\n"
619
-
620
+ puts " Example: #{example}"
621
+ puts "\n"
620
622
  # Count down to the start - don't sleep here as we're waiting for the server
621
623
  # to send us the questions after a fixed delay
622
624
  puts " Get ready for the first question...".colorize(:yellow)
@@ -662,6 +664,19 @@ module GitGameShow
662
664
  if data['commit_info']
663
665
  puts "\n Commit: #{data['commit_info']}".colorize(:yellow)
664
666
  end
667
+
668
+ # Display code context if available (for BlameGame)
669
+ if data['context']
670
+ puts "\n Code Context:".colorize(:light_blue)
671
+ data['context'].split("\n").each do |line|
672
+ # Check if this is the target line (with the > prefix)
673
+ if line.start_with?('>')
674
+ puts " #{line}".colorize(:yellow) # Highlight the target line
675
+ else
676
+ puts " #{line}".colorize(:white)
677
+ end
678
+ end
679
+ end
665
680
  puts "\n"
666
681
 
667
682
  # Create a unique timer ID for this question
@@ -830,6 +845,8 @@ module GitGameShow
830
845
  puts "\n #{"─" * 40}".colorize(:light_black)
831
846
  puts "\n"
832
847
 
848
+ points_text = "(#{data['points']} points)"
849
+
833
850
  # Show immediate feedback
834
851
  if data['answer'] == "TIMEOUT"
835
852
  # Special handling for timeouts
@@ -838,7 +855,6 @@ module GitGameShow
838
855
  puts " (0 points)".colorize(:light_black)
839
856
  elsif data['correct']
840
857
  # Correct answer
841
- points_text = data['points'] > 0 ? " (+#{data['points']} points)" : ""
842
858
  puts " ✅ CORRECT! Your answer was correct: #{data['answer']}#{points_text}".colorize(:green)
843
859
 
844
860
  # Show bonus points details if applicable
@@ -847,9 +863,21 @@ module GitGameShow
847
863
  puts " 🎉 SPEED BONUS: +#{bonus} points for fast answer!".colorize(:light_yellow)
848
864
  end
849
865
  else
850
- # Incorrect answer
851
- puts " INCORRECT! The correct answer was: #{data['correct_answer']}".colorize(:red)
852
- puts " You answered: #{data['answer']} (0 points)".colorize(:yellow)
866
+ if data['correct_answer'].is_a?(Array)
867
+ puts "Correct Order".rjust(39).colorize(:green) + " Your Order"
868
+ 0.upto(data['correct_answer'].size - 1) do |i|
869
+ answer_color = data['correct_answer'][i] == data['answer'][i] ? :green : :red
870
+ truncated_1 = data['correct_answer'][i].length > 38 ? data['correct_answer'][i][0..35] + "..." : data['correct_answer'][i]
871
+ truncated_2 = data['answer'][i].length > 38 ? data['answer'][i][0..35] + "..." : data['answer'][i]
872
+ puts "#{truncated_1}".rjust(38).colorize(:green) + " #{truncated_2}".colorize(answer_color)
873
+ end
874
+ puts "\n\n"
875
+ puts points_text.center(80).colorize(:yellow)
876
+ else
877
+ # Incorrect answer
878
+ puts " ❌ INCORRECT! The correct answer was: #{data['correct_answer']}".colorize(:red)
879
+ puts " You answered: #{data['answer']} (0 points)".colorize(:yellow)
880
+ end
853
881
  end
854
882
 
855
883
  puts "\n Waiting for the round to complete. Please wait for the next question...".colorize(:light_blue)
@@ -859,7 +887,7 @@ module GitGameShow
859
887
  def handle_round_result(data)
860
888
  # Invalidate any running timer and reset window title
861
889
  @current_timer_id = SecureRandom.uuid
862
- print "\033]0;Git Game Show - Round Results\007" # Reset window title with context
890
+ print "\033]0;Git Game Show - Question Results\007" # Reset window title with context
863
891
 
864
892
  # Start with a clean screen
865
893
  clear_screen
@@ -869,7 +897,7 @@ module GitGameShow
869
897
  box_width = 40
870
898
  box_top = ("╭" + "─" * box_width + "╮").center(@game_width)
871
899
  box_bottom = ("╰" + "─" * box_width + "╯").center(@game_width)
872
- box_middle = "│#{'Round Results'.center(box_width)}│".center(@game_width)
900
+ box_middle = "│#{'Question Results'.center(box_width)}│".center(@game_width)
873
901
 
874
902
  # Output the box
875
903
  puts "\n"
@@ -882,8 +910,8 @@ module GitGameShow
882
910
  puts " Question: #{data['question'][:question]}".colorize(:light_blue)
883
911
 
884
912
  # Handle different display formats for correct answers
885
- if data['question'][:question_type] == 'ordering' && data['correct_answer'].is_a?(Array)
886
- puts " Correct order (oldest to newest):".colorize(:green)
913
+ if data['correct_answer'].is_a?(Array)
914
+ puts " Correct order".colorize(:green)
887
915
  data['correct_answer'].each do |item|
888
916
  puts " #{item}".colorize(:green)
889
917
  end
@@ -901,9 +929,9 @@ module GitGameShow
901
929
  # Ensure result is a hash with the expected keys
902
930
  if result.is_a?(Hash)
903
931
  # Check if 'correct' is a boolean or check string equality if it's a string
904
- correct = result[:correct] || result['correct'] || false
905
- answer = result[:answer] || result['answer'] || "No answer"
906
- points = result[:points] || result['points'] || 0
932
+ correct = result['correct'] || false
933
+ answer = result['answer'].is_a?(Array) ? "" : (result['answer'] || "No answer")
934
+ points = result['points'] || 0
907
935
 
908
936
  status = correct ? "✓" : "✗"
909
937
  points_str = "(+#{points} points)"
@@ -1,4 +1,4 @@
1
1
  module GitGameShow
2
2
  # Current version of Git Game Show
3
- VERSION = "0.1.10"
3
+ VERSION = "0.2.0"
4
4
  end
@@ -2,28 +2,41 @@ module GitGameShow
2
2
  class AuthorQuiz < MiniGame
3
3
  self.name = "Author Quiz"
4
4
  self.description = "Guess which team member made each commit!"
5
+ self.example = <<~EXAMPLE
6
+ Who authored this commit?
7
+
8
+ "Add user authentication features"
9
+
10
+ abc123d (Feb 15, 2025)
11
+
12
+ Choose your answer:
13
+ Bob
14
+ \e[0;32;49m> Alice\e[0m
15
+ Charlie
16
+ David
17
+ EXAMPLE
5
18
  self.questions_per_round = 5
6
-
19
+
7
20
  # Custom timing for this mini-game (overrides default config)
8
21
  def self.question_timeout
9
22
  15 # 15 seconds per question
10
23
  end
11
-
24
+
12
25
  def self.question_display_time
13
26
  5 # 5 seconds between questions
14
27
  end
15
-
28
+
16
29
  def generate_questions(repo)
17
30
  # For testing, manually add some questions if we can't get meaningful ones from repo
18
31
  questions = []
19
-
32
+
20
33
  begin
21
34
  # Get commits from the past year
22
35
  one_year_ago = Time.now - (365 * 24 * 60 * 60) # One year in seconds
23
-
36
+
24
37
  # Get all commits
25
38
  all_commits = get_all_commits(repo)
26
-
39
+
27
40
  # Filter commits from the past year, if any
28
41
  commits_from_past_year = all_commits.select do |commit|
29
42
  # Be safe with date parsing
@@ -34,39 +47,39 @@ module GitGameShow
34
47
  false # If we can't parse the date, exclude it
35
48
  end
36
49
  end
37
-
50
+
38
51
  # If we don't have enough commits from the past year, use all commits
39
52
  commits = commits_from_past_year.size >= 10 ? commits_from_past_year : all_commits
40
-
53
+
41
54
  authors = get_commit_authors(commits)
42
-
55
+
43
56
  # We need at least 2 authors for this game to be meaningful
44
57
  if authors.size < 2
45
58
  return generate_sample_questions
46
59
  end
47
-
60
+
48
61
  # Shuffle all commits to ensure randomness, then select commits for questions
49
62
  selected_commits = commits.shuffle.sample(self.class.questions_per_round)
50
-
63
+
51
64
  selected_commits.each do |commit|
52
65
  # Get the real author
53
66
  correct_author = commit.author.name
54
-
67
+
55
68
  # Get incorrect options (other authors)
56
69
  incorrect_authors = shuffled_excluding(authors, correct_author).take(3)
57
-
70
+
58
71
  # Create options array with the correct answer and incorrect ones
59
72
  all_options = ([correct_author] + incorrect_authors).shuffle
60
-
73
+
61
74
  # Extract the commit message, but handle multi-line messages gracefully
62
75
  message_lines = commit.message.lines.reject(&:empty?)
63
76
  message_preview = message_lines.first&.strip || "No message"
64
-
77
+
65
78
  # For longer messages, add an indication that there's more
66
79
  if message_lines.size > 1
67
- message_preview += "..."
80
+ message_preview += "..."
68
81
  end
69
-
82
+
70
83
  # Create a more compact question format to avoid overflows
71
84
  # Add proper indentation to the commit message with spaces
72
85
  questions << {
@@ -80,27 +93,27 @@ module GitGameShow
80
93
  # Silently fail and use sample questions instead
81
94
  return generate_sample_questions
82
95
  end
83
-
96
+
84
97
  # If we couldn't generate enough questions, add sample ones
85
98
  if questions.empty?
86
99
  return generate_sample_questions
87
100
  end
88
-
101
+
89
102
  questions
90
103
  end
91
-
104
+
92
105
  def generate_sample_questions
93
106
  # Create sample questions in case the repo doesn't have enough data
94
107
  sample_authors = ["Alice", "Bob", "Charlie", "David", "Emma"]
95
-
108
+
96
109
  questions = []
97
-
110
+
98
111
  5.times do |i|
99
112
  correct_author = sample_authors.sample
100
113
  incorrect_authors = sample_authors.reject { |a| a == correct_author }.sample(3)
101
-
114
+
102
115
  all_options = ([correct_author] + incorrect_authors).shuffle
103
-
116
+
104
117
  questions << {
105
118
  question: "Who authored this commit?\n\n \"Sample commit message ##{i+1}\"",
106
119
  commit_info: "abc123#{i} (Jan #{i+1}, 2025)",
@@ -108,35 +121,35 @@ module GitGameShow
108
121
  correct_answer: correct_author
109
122
  }
110
123
  end
111
-
124
+
112
125
  questions
113
126
  end
114
-
127
+
115
128
  def evaluate_answers(question, player_answers)
116
129
  results = {}
117
-
130
+
118
131
  player_answers.each do |player_name, answer_data|
119
132
  answered = answer_data[:answered] || false
120
133
  player_answer = answer_data[:answer]
121
134
  correct = player_answer == question[:correct_answer]
122
-
135
+
123
136
  points = correct ? 10 : 0
124
-
137
+
125
138
  # Bonus points for fast answers (if correct)
126
139
  if correct && answer_data[:time_taken] < 5
127
140
  points += 5
128
141
  elsif correct && answer_data[:time_taken] < 10
129
142
  points += 3
130
143
  end
131
-
144
+
132
145
  results[player_name] = {
133
146
  answer: player_answer,
134
147
  correct: correct,
135
148
  points: points
136
149
  }
137
150
  end
138
-
151
+
139
152
  results
140
153
  end
141
154
  end
142
- end
155
+ end