git_game_show 0.1.4 → 0.1.6
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 +117 -84
- data/lib/git_game_show/game_server.rb +603 -226
- data/lib/git_game_show/player_client.rb +127 -113
- data/lib/git_game_show/updater.rb +8 -8
- data/lib/git_game_show/version.rb +1 -1
- data/mini_games/date_ordering_quiz.rb +78 -21
- metadata +1 -1
@@ -17,6 +17,7 @@ module GitGameShow
|
|
17
17
|
@players = []
|
18
18
|
@game_state = :lobby # :lobby, :playing, :ended
|
19
19
|
@current_timer_id = nil
|
20
|
+
@game_width = 80
|
20
21
|
end
|
21
22
|
|
22
23
|
def connect
|
@@ -27,7 +28,7 @@ module GitGameShow
|
|
27
28
|
# For ngrok TCP tunnels, we should use regular ws:// since ngrok tcp doesn't provide SSL termination
|
28
29
|
# Only use wss:// if the secure flag is explicitly set (for configured HTTPS endpoints)
|
29
30
|
protocol = if @secure
|
30
|
-
puts "Using secure WebSocket connection (wss://)".colorize(:
|
31
|
+
puts "Using secure WebSocket connection (wss://)".colorize(:light_blue)
|
31
32
|
'wss'
|
32
33
|
else
|
33
34
|
'ws'
|
@@ -132,13 +133,13 @@ module GitGameShow
|
|
132
133
|
clear_screen
|
133
134
|
|
134
135
|
# Draw header with fancy box
|
135
|
-
terminal_width = `tput cols`.to_i rescue
|
136
|
+
terminal_width = `tput cols`.to_i rescue @game_width
|
136
137
|
terminal_height = `tput lines`.to_i rescue 24
|
137
138
|
|
138
139
|
# Create title box
|
139
|
-
puts "
|
140
|
-
puts "
|
141
|
-
puts "
|
140
|
+
puts "╭#{"─" * (terminal_width - 2)}╮".colorize(:green)
|
141
|
+
puts "│#{" Git Game Show - Waiting Room ".center(terminal_width - 2)}│".colorize(:green)
|
142
|
+
puts "╰#{"─" * (terminal_width - 2)}╯".colorize(:green)
|
142
143
|
|
143
144
|
# Left column width (2/3 of terminal) for main content
|
144
145
|
left_width = (terminal_width * 0.65).to_i
|
@@ -148,20 +149,20 @@ module GitGameShow
|
|
148
149
|
puts " Welcome to Git Game Show!".colorize(:yellow)
|
149
150
|
puts " Test your knowledge about Git and your team's commits through fun mini-games.".colorize(:light_white)
|
150
151
|
puts "\n"
|
151
|
-
puts " 🔹
|
152
|
+
puts " 🔹 Instructions:".colorize(:light_blue)
|
152
153
|
puts " • The game consists of multiple rounds with different question types".colorize(:light_white)
|
153
154
|
puts " • Each round has a theme based on Git commit history".colorize(:light_white)
|
154
155
|
puts " • Answer questions as quickly as possible for maximum points".colorize(:light_white)
|
155
156
|
puts " • The player with the most points at the end wins!".colorize(:light_white)
|
156
157
|
puts "\n"
|
157
|
-
puts " 🔹
|
158
|
+
puts " 🔹 Status: Waiting for the host to start the game...".colorize(:light_yellow)
|
158
159
|
puts "\n"
|
159
160
|
|
160
161
|
# Draw player section in a box
|
161
162
|
player_box_width = terminal_width - 4
|
162
|
-
puts "
|
163
|
-
puts "
|
164
|
-
puts "
|
163
|
+
puts ("╭#{"─" * player_box_width}╮").center(terminal_width).colorize(:light_blue)
|
164
|
+
puts ("│#{" Players ".center(player_box_width)}│").center(terminal_width).colorize(:light_blue)
|
165
|
+
puts ("╰#{"─" * player_box_width}╯").center(terminal_width).colorize(:light_blue)
|
165
166
|
|
166
167
|
# Display list of players in a nicer format
|
167
168
|
if @players.empty?
|
@@ -255,9 +256,11 @@ module GitGameShow
|
|
255
256
|
|
256
257
|
# Draw question header once
|
257
258
|
if question_number && total_questions
|
258
|
-
|
259
|
-
puts "
|
260
|
-
puts "
|
259
|
+
box_width = 42
|
260
|
+
puts ""
|
261
|
+
puts ("╭" + "─" * box_width + "╮").center(@game_width).colorize(:light_blue)
|
262
|
+
puts ("│#{'Question #{question_number} of #{total_questions}'.center(box_width-2)}│").center(@game_width).colorize(:light_blue)
|
263
|
+
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:light_blue)
|
261
264
|
end
|
262
265
|
|
263
266
|
# Draw the main question text once
|
@@ -271,17 +274,12 @@ module GitGameShow
|
|
271
274
|
puts " • Selected items move with cursor when you press ↑/↓".colorize(:white)
|
272
275
|
puts " • Navigate to Submit and press ENTER when finished".colorize(:white)
|
273
276
|
|
274
|
-
# Draw the header for the list content once
|
275
|
-
puts "\n CURRENT ORDER:".colorize(:light_blue)
|
276
|
-
|
277
277
|
# Calculate where the list content starts on screen
|
278
278
|
content_start_line = question_number ? 15 : 12
|
279
279
|
|
280
280
|
# Draw the list content (this will be redrawn repeatedly)
|
281
281
|
draw_ordering_list(current_order, cursor_index, selected_index, content_start_line, num_options)
|
282
282
|
|
283
|
-
# Initialize variables
|
284
|
-
|
285
283
|
# Main interaction loop
|
286
284
|
loop do
|
287
285
|
# Read a single keypress
|
@@ -544,14 +542,15 @@ module GitGameShow
|
|
544
542
|
clear_screen
|
545
543
|
|
546
544
|
# Display a fun "Game Starting" animation
|
545
|
+
box_width = 40
|
547
546
|
puts "\n\n"
|
548
|
-
puts "
|
549
|
-
puts "
|
550
|
-
puts "
|
547
|
+
puts ("╭" + "─" * box_width + "╮").center(@game_width).colorize(:green)
|
548
|
+
puts ("│" + "Game starting...".center(box_width) + "│").center(@game_width).colorize(:green)
|
549
|
+
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:green)
|
551
550
|
puts "\n\n"
|
552
551
|
|
553
|
-
puts " Total rounds: #{@total_rounds}".colorize(:
|
554
|
-
puts " Players: #{@players.join(', ')}".colorize(:
|
552
|
+
puts " Total rounds: #{@total_rounds}".colorize(:light_blue)
|
553
|
+
puts " Players: #{@players.join(', ')}".colorize(:light_blue)
|
555
554
|
puts "\n\n"
|
556
555
|
puts " Get ready for the first round!".colorize(:yellow)
|
557
556
|
puts "\n\n"
|
@@ -573,21 +572,22 @@ module GitGameShow
|
|
573
572
|
end
|
574
573
|
else
|
575
574
|
# During gameplay, just show a notification without disrupting the game UI
|
576
|
-
terminal_width = `tput cols`.to_i rescue
|
575
|
+
terminal_width = `tput cols`.to_i rescue @game_width
|
577
576
|
|
578
577
|
# Create a notification box that won't interfere with ongoing gameplay
|
579
|
-
puts "
|
578
|
+
puts ""
|
579
|
+
puts "╭#{"─" * (terminal_width - 2)}╮".colorize(:light_blue)
|
580
580
|
|
581
581
|
if data['type'] == 'player_joined'
|
582
|
-
puts "
|
582
|
+
puts "│#{" 🟢 #{data['name']} has joined the game ".center(terminal_width - 2)}│".colorize(:green)
|
583
583
|
else
|
584
|
-
puts "
|
584
|
+
puts "│#{" 🔴 #{data['name']} has left the game ".center(terminal_width - 2)}│".colorize(:yellow)
|
585
585
|
end
|
586
586
|
|
587
587
|
# Don't show all players during gameplay - can be too disruptive
|
588
588
|
# Just show the total count
|
589
|
-
puts "
|
590
|
-
puts "
|
589
|
+
puts "│#{" Total players: #{data['players'].size} ".center(terminal_width - 2)}│".colorize(:light_blue)
|
590
|
+
puts "╰#{"─" * (terminal_width - 2)}╯".colorize(:light_blue)
|
591
591
|
end
|
592
592
|
end
|
593
593
|
|
@@ -604,27 +604,17 @@ module GitGameShow
|
|
604
604
|
|
605
605
|
# Box is drawn with exactly 45 "━" characters for the top and bottom borders
|
606
606
|
# The top and bottom including borders are 48 characters wide
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
round_text = "ROUND #{round_num} of #{total_rounds}"
|
612
|
-
|
613
|
-
# Find exact box width by measuring the top border
|
614
|
-
box_width = box_top.length # Should be 48 with Unicode characters
|
615
|
-
|
616
|
-
# The inner width is the box width minus the borders
|
617
|
-
inner_width = box_width - (" ┃".length + "┃".length)
|
618
|
-
|
619
|
-
# Simply use Ruby's built-in center method for reliable centering
|
620
|
-
box_middle = " ┃" + round_text.center(inner_width) + "┃"
|
607
|
+
box_width = 42
|
608
|
+
box_top = ("╭" + "─" * (box_width - 2) + "╮").center(@game_width)
|
609
|
+
box_bottom = ("╰" + "─" * (box_width - 2) + "╯").center(@game_width)
|
610
|
+
box_middle = "│#{"Round #{round_num} of #{total_rounds}".center(box_width - 2)}│".center(@game_width)
|
621
611
|
|
622
612
|
# Output the box
|
623
613
|
puts box_top.colorize(:green)
|
624
614
|
puts box_middle.colorize(:green)
|
625
615
|
puts box_bottom.colorize(:green)
|
626
616
|
puts "\n"
|
627
|
-
puts " Mini-game: #{mini_game}".colorize(:
|
617
|
+
puts " Mini-game: #{mini_game}".colorize(:light_blue)
|
628
618
|
puts " #{description}".colorize(:light_blue)
|
629
619
|
puts "\n"
|
630
620
|
|
@@ -657,16 +647,15 @@ module GitGameShow
|
|
657
647
|
puts "\n"
|
658
648
|
|
659
649
|
# Draw a simple box for the question header
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
box_middle = " ┃" + question_text.center(inner_width) + "┃"
|
650
|
+
box_width = 42
|
651
|
+
box_top = ("╭" + "─" * (box_width - 2) + "╮").center(@game_width)
|
652
|
+
box_bottom = ("╰" + "─" * (box_width - 2) + "╯").center(@game_width)
|
653
|
+
box_middle = "│#{"Question #{question_num} of #{total_questions}".center(box_width - 2)}│".center(@game_width)
|
665
654
|
|
666
655
|
# Output the question box
|
667
|
-
puts box_top.colorize(:
|
668
|
-
puts box_middle.colorize(:
|
669
|
-
puts box_bottom.colorize(:
|
656
|
+
puts box_top.colorize(:light_blue)
|
657
|
+
puts box_middle.colorize(:light_blue)
|
658
|
+
puts box_bottom.colorize(:light_blue)
|
670
659
|
puts "\n"
|
671
660
|
|
672
661
|
# Display question
|
@@ -681,12 +670,6 @@ module GitGameShow
|
|
681
670
|
# Create a unique timer ID for this question
|
682
671
|
timer_id = SecureRandom.uuid
|
683
672
|
@current_timer_id = timer_id
|
684
|
-
start_time = Time.now
|
685
|
-
end_time = start_time + timeout
|
686
|
-
|
687
|
-
# Static deadline info
|
688
|
-
puts " Deadline: #{end_time.strftime('%I:%M:%S %p')}".colorize(:light_blue)
|
689
|
-
puts "\n"
|
690
673
|
|
691
674
|
# Initialize remaining time for scoring
|
692
675
|
@time_remaining = timeout
|
@@ -888,32 +871,31 @@ module GitGameShow
|
|
888
871
|
|
889
872
|
# Box is drawn with exactly 45 "━" characters for the top and bottom borders
|
890
873
|
# The top and bottom including borders are 48 characters wide
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
result_text = "ROUND RESULTS"
|
896
|
-
|
897
|
-
# Find exact box width by measuring the top border
|
898
|
-
box_width = box_top.length # Should be 48 with Unicode characters
|
899
|
-
|
900
|
-
# The inner width is the box width minus 2 characters for the borders
|
901
|
-
inner_width = box_width - (" ┃".length + "┃".length)
|
902
|
-
|
903
|
-
# Simply use Ruby's built-in center method for reliable centering
|
904
|
-
box_middle = " ┃" + result_text.center(inner_width) + "┃"
|
874
|
+
box_width = 40
|
875
|
+
box_top = ("╭" + "─" * box_width + "╮").center(@game_width)
|
876
|
+
box_bottom = ("╰" + "─" * box_width + "╯").center(@game_width)
|
877
|
+
box_middle = "│#{'Round Results'.center(box_width)}│".center(@game_width)
|
905
878
|
|
906
879
|
# Output the box
|
907
|
-
puts box_top.colorize(:
|
908
|
-
puts box_middle.colorize(:
|
909
|
-
puts box_bottom.colorize(:
|
880
|
+
puts box_top.colorize(:light_blue)
|
881
|
+
puts box_middle.colorize(:light_blue)
|
882
|
+
puts box_bottom.colorize(:light_blue)
|
910
883
|
puts "\n"
|
911
884
|
|
912
885
|
# Show question again
|
913
886
|
puts " Question: #{data['question'][:question]}".colorize(:light_blue)
|
914
|
-
puts " Correct answer: #{data['correct_answer']}".colorize(:green)
|
915
887
|
|
916
|
-
|
888
|
+
# Handle different display formats for correct answers
|
889
|
+
if data['question'][:question_type] == 'ordering' && data['correct_answer'].is_a?(Array)
|
890
|
+
puts " Correct order (oldest to newest):".colorize(:green)
|
891
|
+
data['correct_answer'].each do |item|
|
892
|
+
puts " #{item}".colorize(:green)
|
893
|
+
end
|
894
|
+
else
|
895
|
+
puts " Correct answer: #{data['correct_answer']}".colorize(:green)
|
896
|
+
end
|
897
|
+
|
898
|
+
puts "\n All player results:".colorize(:light_blue)
|
917
899
|
|
918
900
|
# Debug data temporarily removed
|
919
901
|
|
@@ -931,11 +913,33 @@ module GitGameShow
|
|
931
913
|
points_str = "(+#{points} points)"
|
932
914
|
player_str = player == name ? "#{player} (You)" : player
|
933
915
|
|
934
|
-
|
935
|
-
if
|
936
|
-
|
916
|
+
# For ordering questions with array answers, show them with numbers
|
917
|
+
if data['question'][:question_type] == 'ordering' && answer.is_a?(Array)
|
918
|
+
# First display player name and points
|
919
|
+
header = " #{player_str.ljust(20)} #{points_str.ljust(15)} #{status}"
|
920
|
+
|
921
|
+
# Color according to correctness
|
922
|
+
if correct
|
923
|
+
puts header.colorize(:green)
|
924
|
+
puts " Submitted order:".colorize(:green)
|
925
|
+
answer.each_with_index do |item, idx|
|
926
|
+
puts " #{idx + 1}. #{item}".colorize(:green)
|
927
|
+
end
|
928
|
+
else
|
929
|
+
puts header.colorize(:red)
|
930
|
+
puts " Submitted order:".colorize(:red)
|
931
|
+
answer.each_with_index do |item, idx|
|
932
|
+
puts " #{idx + 1}. #{item}".colorize(:red)
|
933
|
+
end
|
934
|
+
end
|
937
935
|
else
|
938
|
-
|
936
|
+
# Standard display for non-ordering questions
|
937
|
+
player_output = " #{player_str.ljust(20)} #{points_str.ljust(15)} #{answer} #{status}"
|
938
|
+
if correct
|
939
|
+
puts player_output.colorize(:green)
|
940
|
+
else
|
941
|
+
puts player_output.colorize(:red)
|
942
|
+
end
|
939
943
|
end
|
940
944
|
else
|
941
945
|
# Fallback for unexpected result format
|
@@ -983,10 +987,11 @@ module GitGameShow
|
|
983
987
|
# Always start with a clean screen for the scoreboard
|
984
988
|
clear_screen
|
985
989
|
|
986
|
-
|
987
|
-
puts "
|
988
|
-
puts "
|
989
|
-
puts "
|
990
|
+
box_width = 40
|
991
|
+
puts ""
|
992
|
+
puts ("╭" + "─" * box_width + "╮").center(@game_width).colorize(:yellow)
|
993
|
+
puts "│#{'Scoreboard'.center(box_width)}┃".center(@game_width).colorize(:yellow)
|
994
|
+
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:yellow)
|
990
995
|
puts "\n"
|
991
996
|
|
992
997
|
# Get player positions
|
@@ -1021,7 +1026,7 @@ module GitGameShow
|
|
1021
1026
|
end
|
1022
1027
|
end
|
1023
1028
|
|
1024
|
-
puts "\n Next round coming up soon...".colorize(:
|
1029
|
+
puts "\n Next round coming up soon...".colorize(:light_blue)
|
1025
1030
|
end
|
1026
1031
|
|
1027
1032
|
def handle_game_end(data)
|
@@ -1043,34 +1048,37 @@ module GitGameShow
|
|
1043
1048
|
winner = data['winner']
|
1044
1049
|
|
1045
1050
|
# ASCII trophy art
|
1046
|
-
trophy =
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1051
|
+
trophy = [
|
1052
|
+
"___________",
|
1053
|
+
"'._==_==_=_.'",
|
1054
|
+
".-\\: /-.",
|
1055
|
+
"| (|:. |) |",
|
1056
|
+
"'-|:. |-'",
|
1057
|
+
"\\::. /",
|
1058
|
+
"'::. .'",
|
1059
|
+
") (",
|
1060
|
+
"_.' '._"
|
1061
|
+
]
|
1062
|
+
|
1063
|
+
box_width = 40
|
1058
1064
|
puts "\n\n"
|
1059
|
-
puts
|
1065
|
+
trophy.each{|line| puts line.center(@game_width).colorize(:yellow)}
|
1060
1066
|
puts "\n"
|
1061
|
-
puts "
|
1062
|
-
puts "
|
1063
|
-
puts "
|
1067
|
+
puts ("╭" + "─" * box_width + "╮").center(@game_width).colorize(:green)
|
1068
|
+
puts "│#{'Game Over'.center(box_width)}│".center(@game_width).colorize(:green)
|
1069
|
+
puts ("╰" + "─" * box_width + "╯").center(@game_width).colorize(:green)
|
1064
1070
|
puts "\n"
|
1065
1071
|
|
1066
1072
|
winner_is_you = winner == name
|
1067
1073
|
if winner_is_you
|
1068
|
-
puts "
|
1074
|
+
puts "🎉 Congratulations! You won! 🎉".center(@game_width).colorize(:light_yellow)
|
1069
1075
|
else
|
1070
|
-
puts "
|
1076
|
+
puts "Winner: #{winner}! 🏆".center(@game_width).colorize(:light_yellow)
|
1071
1077
|
end
|
1072
1078
|
|
1073
|
-
puts "
|
1079
|
+
puts ""
|
1080
|
+
puts "Final Scores".center(@game_width).colorize(:light_blue)
|
1081
|
+
puts ""
|
1074
1082
|
|
1075
1083
|
# Get player positions
|
1076
1084
|
position = 1
|
@@ -1089,23 +1097,29 @@ module GitGameShow
|
|
1089
1097
|
score_str = "#{score} points"
|
1090
1098
|
|
1091
1099
|
# Add emoji for top 3
|
1100
|
+
scores_width = @game_width - 30
|
1092
1101
|
case position
|
1093
1102
|
when 1
|
1094
1103
|
position_str = "🥇 #{position_str}"
|
1095
|
-
|
1104
|
+
left_string = (position_str.rjust(5) + ' ' + player_str).ljust(scores_width - score_str.length)
|
1105
|
+
puts "#{left_string}#{score_str}".center(@game_width).colorize(:light_yellow)
|
1096
1106
|
when 2
|
1097
1107
|
position_str = "🥈 #{position_str}"
|
1098
|
-
|
1108
|
+
left_string = (position_str.rjust(5) + ' ' + player_str).ljust(scores_width - score_str.length)
|
1109
|
+
puts "#{left_string}#{score_str}".center(@game_width).colorize(:light_blue)
|
1099
1110
|
when 3
|
1100
1111
|
position_str = "🥉 #{position_str}"
|
1101
|
-
|
1112
|
+
left_string = (position_str.rjust(5) + ' ' + player_str).ljust(scores_width - score_str.length)
|
1113
|
+
puts "#{left_string}#{score_str}".center(@game_width).colorize(:light_magenta)
|
1102
1114
|
else
|
1103
|
-
|
1115
|
+
left_string = " " + (position_str.rjust(5) + ' ' + player_str).ljust(scores_width - score_str.length)
|
1116
|
+
puts "#{left_string}#{score_str}".center(@game_width)
|
1104
1117
|
end
|
1105
1118
|
end
|
1106
1119
|
|
1107
|
-
puts "\n
|
1108
|
-
puts "
|
1120
|
+
puts "\n"
|
1121
|
+
puts " Thanks for playing Git Game Show!".colorize(:green)
|
1122
|
+
puts " Waiting for the host to start a new game...".colorize(:light_blue)
|
1109
1123
|
puts " Press Ctrl+C to exit, or wait for the next game".colorize(:light_black)
|
1110
1124
|
|
1111
1125
|
# Keep client ready to receive a new game start or reset message
|
@@ -1138,8 +1152,8 @@ module GitGameShow
|
|
1138
1152
|
display_waiting_room
|
1139
1153
|
|
1140
1154
|
# Show a prominent message that we're back in waiting room mode
|
1141
|
-
puts "\n 🔄 The game has been reset by the host. Waiting for a new game to start...".colorize(:
|
1142
|
-
puts " You can play again or press Ctrl+C to exit.".colorize(:
|
1155
|
+
puts "\n 🔄 The game has been reset by the host. Waiting for a new game to start...".colorize(:light_blue)
|
1156
|
+
puts " You can play again or press Ctrl+C to exit.".colorize(:light_blue)
|
1143
1157
|
end
|
1144
1158
|
|
1145
1159
|
def handle_chat(data)
|
@@ -61,14 +61,14 @@ module GitGameShow
|
|
61
61
|
# Clear the terminal for better visibility
|
62
62
|
puts "\n\n"
|
63
63
|
|
64
|
-
puts "
|
65
|
-
puts "
|
66
|
-
puts "
|
67
|
-
puts "
|
68
|
-
puts "
|
69
|
-
puts "
|
70
|
-
puts "
|
71
|
-
puts "
|
64
|
+
puts "╭―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――╮".colorize(:cyan)
|
65
|
+
puts "│ UPDATE AVAILABLE FOR GIT GAME SHOW │".colorize(:cyan)
|
66
|
+
puts "├―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――┤".colorize(:cyan)
|
67
|
+
puts "│ │".colorize(:cyan)
|
68
|
+
puts "│ Current version: #{current_version.ljust(44)}│".colorize(:cyan)
|
69
|
+
puts "│ Latest version: #{latest_version.ljust(44)}│".colorize(:cyan)
|
70
|
+
puts "│ │".colorize(:cyan)
|
71
|
+
puts "╰―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――╯".colorize(:cyan)
|
72
72
|
puts "\n"
|
73
73
|
|
74
74
|
update_now = prompt.yes?("Would you like to update now?")
|
@@ -179,29 +179,68 @@ module GitGameShow
|
|
179
179
|
def evaluate_answers(question, player_answers)
|
180
180
|
results = {}
|
181
181
|
|
182
|
+
# Safety check for nil or empty responses
|
183
|
+
return results if player_answers.nil? || player_answers.empty?
|
184
|
+
return results unless question && question[:correct_answer]
|
185
|
+
|
186
|
+
# Get total number of items in the correct answer
|
187
|
+
total_items = question[:correct_answer].size
|
188
|
+
|
182
189
|
player_answers.each do |player_name, answer_data|
|
190
|
+
# Skip nil entries
|
191
|
+
next unless player_name && answer_data
|
192
|
+
|
193
|
+
# Extract player's answer with defensive checks
|
183
194
|
player_answer = answer_data[:answer]
|
184
195
|
time_taken = answer_data[:time_taken] || 20
|
185
196
|
|
186
|
-
#
|
187
|
-
|
197
|
+
# Initialize points
|
198
|
+
points = 0
|
188
199
|
|
189
|
-
#
|
190
|
-
|
191
|
-
|
192
|
-
|
200
|
+
# New scoring system: checks positions relative to each other item
|
201
|
+
if player_answer && !player_answer.empty?
|
202
|
+
# Create a mapping from item to its position in player's answer
|
203
|
+
item_positions = {}
|
204
|
+
player_answer.each_with_index do |item, index|
|
205
|
+
item_positions[item] = index if item # Skip nil items
|
206
|
+
end
|
207
|
+
|
208
|
+
# Create mapping from item to correct position
|
209
|
+
correct_positions = {}
|
210
|
+
question[:correct_answer].each_with_index do |item, index|
|
211
|
+
correct_positions[item] = index if item # Skip nil items
|
212
|
+
end
|
213
|
+
|
214
|
+
# For each item, calculate points based on relative positions
|
215
|
+
question[:correct_answer].each_with_index do |item, correct_index|
|
216
|
+
# Skip if the item isn't in the player's answer
|
217
|
+
next unless item && item_positions.key?(item)
|
218
|
+
|
219
|
+
player_index = item_positions[item]
|
220
|
+
|
221
|
+
# Check position relative to other items
|
222
|
+
question[:correct_answer].each_with_index do |other_item, other_correct_index|
|
223
|
+
next if !other_item || item == other_item || !item_positions.key?(other_item)
|
224
|
+
|
225
|
+
other_player_index = item_positions[other_item]
|
226
|
+
|
227
|
+
# If this item should be before the other item
|
228
|
+
if correct_index < other_correct_index
|
229
|
+
points += 1 if player_index < other_player_index
|
230
|
+
# If this item should be after the other item
|
231
|
+
elsif correct_index > other_correct_index
|
232
|
+
points += 1 if player_index > other_player_index
|
233
|
+
end
|
234
|
+
end
|
193
235
|
end
|
194
236
|
end
|
195
237
|
|
196
|
-
#
|
197
|
-
|
198
|
-
|
238
|
+
# Bonus points for perfect answer
|
239
|
+
perfect_score = total_items * (total_items - 1)
|
240
|
+
perfect_score = 1 if perfect_score <= 0 # Prevent division by zero
|
241
|
+
fully_correct = points == perfect_score
|
199
242
|
|
200
|
-
|
201
|
-
if correct_positions == total_items
|
202
|
-
# Base bonus for fully correct answer
|
203
|
-
points += 3
|
204
|
-
|
243
|
+
if fully_correct
|
205
244
|
# Additional time-based bonus (faster answers get more points)
|
206
245
|
if time_taken < 8 # Really fast (under 8 seconds)
|
207
246
|
points += 4
|
@@ -210,17 +249,35 @@ module GitGameShow
|
|
210
249
|
end
|
211
250
|
end
|
212
251
|
|
213
|
-
# Create feedback
|
214
|
-
|
252
|
+
# Create detailed feedback
|
253
|
+
max_possible = total_items * (total_items - 1)
|
254
|
+
max_possible = 1 if max_possible <= 0 # Prevent division by zero
|
255
|
+
feedback = "#{points}/#{max_possible} points (based on relative ordering)"
|
215
256
|
if question[:commit_dates]
|
216
|
-
feedback += "\n\nActual dates
|
257
|
+
feedback += "\n\nActual dates:"
|
258
|
+
# Split by newlines and add them as separate lines for better readability
|
259
|
+
question[:commit_dates].to_s.split("\n").each do |date_line|
|
260
|
+
feedback += "\n • #{date_line}"
|
261
|
+
end
|
217
262
|
end
|
218
263
|
|
264
|
+
# Ensure we return all required fields
|
265
|
+
results[player_name] = {
|
266
|
+
answer: player_answer || [],
|
267
|
+
correct: fully_correct || false,
|
268
|
+
points: points || 0,
|
269
|
+
partial_score: feedback || ""
|
270
|
+
}
|
271
|
+
end
|
272
|
+
|
273
|
+
# Return a default result if somehow we ended up with empty results
|
274
|
+
if results.empty? && !player_answers.empty?
|
275
|
+
player_name = player_answers.keys.first
|
219
276
|
results[player_name] = {
|
220
|
-
answer:
|
221
|
-
correct:
|
222
|
-
points:
|
223
|
-
partial_score:
|
277
|
+
answer: [],
|
278
|
+
correct: false,
|
279
|
+
points: 0,
|
280
|
+
partial_score: "Error calculating score"
|
224
281
|
}
|
225
282
|
end
|
226
283
|
|