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 |  |