git_game_show 0.1.3 → 0.1.5
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 +175 -123
- data/lib/git_game_show/game_server.rb +610 -233
- data/lib/git_game_show/player_client.rb +307 -292
- data/lib/git_game_show/updater.rb +17 -17
- data/lib/git_game_show/version.rb +1 -1
- data/lib/git_game_show.rb +4 -4
- data/mini_games/date_ordering_quiz.rb +78 -21
- metadata +2 -2
@@ -5,7 +5,7 @@ require 'timeout'
|
|
5
5
|
module GitGameShow
|
6
6
|
class PlayerClient
|
7
7
|
attr_reader :host, :port, :password, :name, :secure
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(host:, port:, password:, name:, secure: false)
|
10
10
|
@host = host
|
11
11
|
@port = port
|
@@ -17,46 +17,48 @@ 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
|
23
24
|
begin
|
24
25
|
client = self # Store reference to the client instance
|
25
|
-
|
26
|
-
#
|
27
|
-
#
|
28
|
-
|
29
|
-
|
26
|
+
|
27
|
+
# Check if the connection should use a secure protocol
|
28
|
+
# For ngrok TCP tunnels, we should use regular ws:// since ngrok tcp doesn't provide SSL termination
|
29
|
+
# Only use wss:// if the secure flag is explicitly set (for configured HTTPS endpoints)
|
30
|
+
protocol = if @secure
|
31
|
+
puts "Using secure WebSocket connection (wss://)".colorize(:light_blue)
|
30
32
|
'wss'
|
31
33
|
else
|
32
34
|
'ws'
|
33
35
|
end
|
34
|
-
|
36
|
+
|
35
37
|
@ws = WebSocket::Client::Simple.connect("#{protocol}://#{host}:#{port}")
|
36
|
-
|
38
|
+
|
37
39
|
@ws.on :open do
|
38
40
|
puts "Connected to server".colorize(:green)
|
39
41
|
# Use the stored client reference
|
40
42
|
client.send_join_request
|
41
43
|
end
|
42
|
-
|
44
|
+
|
43
45
|
@ws.on :message do |msg|
|
44
46
|
client.handle_message(msg)
|
45
47
|
end
|
46
|
-
|
48
|
+
|
47
49
|
@ws.on :error do |e|
|
48
50
|
puts "Error: #{e.message}".colorize(:red)
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
@ws.on :close do |e|
|
52
54
|
puts "Connection closed (#{e.code}: #{e.reason})".colorize(:yellow)
|
53
55
|
exit(1)
|
54
56
|
end
|
55
|
-
|
57
|
+
|
56
58
|
# Keep the client running
|
57
59
|
loop do
|
58
60
|
sleep(1)
|
59
|
-
|
61
|
+
|
60
62
|
# Check if connection is still alive
|
61
63
|
if @ws.nil? || @ws.closed?
|
62
64
|
puts "Connection lost. Exiting...".colorize(:red)
|
@@ -67,7 +69,7 @@ module GitGameShow
|
|
67
69
|
puts "Failed to connect: #{e.message}".colorize(:red)
|
68
70
|
end
|
69
71
|
end
|
70
|
-
|
72
|
+
|
71
73
|
# Make these methods public so they can be called from the WebSocket callbacks
|
72
74
|
def send_join_request
|
73
75
|
send_message({
|
@@ -76,14 +78,14 @@ module GitGameShow
|
|
76
78
|
password: password
|
77
79
|
})
|
78
80
|
end
|
79
|
-
|
81
|
+
|
80
82
|
# Make public for WebSocket callback
|
81
83
|
def handle_message(msg)
|
82
84
|
begin
|
83
85
|
data = JSON.parse(msg.data)
|
84
|
-
|
86
|
+
|
85
87
|
# Remove debug print to reduce console noise
|
86
|
-
|
88
|
+
|
87
89
|
case data['type']
|
88
90
|
when MessageType::JOIN_RESPONSE
|
89
91
|
handle_join_response(data)
|
@@ -116,7 +118,7 @@ module GitGameShow
|
|
116
118
|
puts "Error processing message: #{e.message}".colorize(:red)
|
117
119
|
end
|
118
120
|
end
|
119
|
-
|
121
|
+
|
120
122
|
def handle_join_response(data)
|
121
123
|
if data['success']
|
122
124
|
@players = data['players'] # Get the full player list from server
|
@@ -126,70 +128,70 @@ module GitGameShow
|
|
126
128
|
exit(1)
|
127
129
|
end
|
128
130
|
end
|
129
|
-
|
131
|
+
|
130
132
|
def display_waiting_room
|
131
133
|
clear_screen
|
132
|
-
|
134
|
+
|
133
135
|
# Draw header with fancy box
|
134
|
-
terminal_width = `tput cols`.to_i rescue
|
136
|
+
terminal_width = `tput cols`.to_i rescue @game_width
|
135
137
|
terminal_height = `tput lines`.to_i rescue 24
|
136
|
-
|
138
|
+
|
137
139
|
# Create title box
|
138
|
-
puts "
|
139
|
-
puts "
|
140
|
-
puts "
|
141
|
-
|
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)
|
143
|
+
|
142
144
|
# Left column width (2/3 of terminal) for main content
|
143
145
|
left_width = (terminal_width * 0.65).to_i
|
144
|
-
|
146
|
+
|
145
147
|
# Display instructions and welcome information
|
146
148
|
puts "\n"
|
147
149
|
puts " Welcome to Git Game Show!".colorize(:yellow)
|
148
150
|
puts " Test your knowledge about Git and your team's commits through fun mini-games.".colorize(:light_white)
|
149
151
|
puts "\n"
|
150
|
-
puts " 🔹
|
152
|
+
puts " 🔹 Instructions:".colorize(:light_blue)
|
151
153
|
puts " • The game consists of multiple rounds with different question types".colorize(:light_white)
|
152
154
|
puts " • Each round has a theme based on Git commit history".colorize(:light_white)
|
153
155
|
puts " • Answer questions as quickly as possible for maximum points".colorize(:light_white)
|
154
156
|
puts " • The player with the most points at the end wins!".colorize(:light_white)
|
155
157
|
puts "\n"
|
156
|
-
puts " 🔹
|
158
|
+
puts " 🔹 Status: Waiting for the host to start the game...".colorize(:light_yellow)
|
157
159
|
puts "\n"
|
158
|
-
|
160
|
+
|
159
161
|
# Draw player section in a box
|
160
162
|
player_box_width = terminal_width - 4
|
161
|
-
puts "
|
162
|
-
puts "
|
163
|
-
puts "
|
164
|
-
|
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)
|
166
|
+
|
165
167
|
# Display list of players in a nicer format
|
166
168
|
if @players.empty?
|
167
169
|
puts " (No other players yet)".colorize(:light_black)
|
168
170
|
else
|
169
171
|
# Calculate number of columns based on terminal width and name lengths
|
170
172
|
max_name_length = @players.map(&:length).max + 10 # Extra space for number and "(You)" text
|
171
|
-
|
173
|
+
|
172
174
|
# Add more spacing between players - increase padding from 4 to 10
|
173
175
|
column_width = max_name_length + 12 # More generous spacing
|
174
176
|
num_cols = [terminal_width / column_width, 3].min # Cap at 3 columns max
|
175
177
|
num_cols = 1 if num_cols < 1
|
176
|
-
|
178
|
+
|
177
179
|
# Use fewer columns for better spacing
|
178
180
|
if num_cols > 1 && @players.size > 6
|
179
181
|
# If we have many players, prefer fewer columns with more space
|
180
182
|
num_cols = [num_cols, 2].min
|
181
183
|
end
|
182
|
-
|
184
|
+
|
183
185
|
# Split players into rows for multi-column display
|
184
186
|
player_rows = @players.each_slice(((@players.size + num_cols - 1) / num_cols).ceil).to_a
|
185
|
-
|
187
|
+
|
186
188
|
puts "\n"
|
187
189
|
player_rows.each do |row_players|
|
188
190
|
row_str = " "
|
189
191
|
row_players.each_with_index do |player, idx|
|
190
192
|
col_idx = player_rows.index { |rp| rp.include?(player) }
|
191
193
|
player_num = col_idx * player_rows[0].length + idx + 1
|
192
|
-
|
194
|
+
|
193
195
|
# Apply different color for current player
|
194
196
|
if player == @name
|
195
197
|
row_str += "#{player_num}. #{player} (You)".colorize(:green).ljust(column_width)
|
@@ -202,30 +204,30 @@ module GitGameShow
|
|
202
204
|
puts ""
|
203
205
|
end
|
204
206
|
end
|
205
|
-
|
207
|
+
|
206
208
|
puts "\n"
|
207
209
|
puts " When the game starts, you'll see questions appear automatically.".colorize(:light_black)
|
208
210
|
puts " Get ready to test your Git knowledge!".colorize(:light_yellow)
|
209
211
|
puts "\n"
|
210
212
|
end
|
211
|
-
|
213
|
+
|
212
214
|
def clear_screen
|
213
215
|
# Reset cursor and clear entire screen
|
214
216
|
print "\033[H\033[2J" # Move to home position and clear screen
|
215
217
|
print "\033[3J" # Clear scrollback buffer
|
216
|
-
|
218
|
+
|
217
219
|
# Reserve bottom line for timer status
|
218
220
|
term_height = `tput lines`.to_i rescue 24
|
219
|
-
|
221
|
+
|
220
222
|
# Move to bottom of screen and clear status line
|
221
223
|
print "\e[#{term_height};1H"
|
222
224
|
print "\e[K"
|
223
225
|
print "\e[H" # Move cursor back to home position
|
224
|
-
|
226
|
+
|
225
227
|
STDOUT.flush
|
226
228
|
end
|
227
|
-
|
228
|
-
|
229
|
+
|
230
|
+
|
229
231
|
# Helper method to print a countdown timer status in the window title
|
230
232
|
# This doesn't interfere with the terminal content
|
231
233
|
def update_title_timer(seconds)
|
@@ -234,7 +236,7 @@ module GitGameShow
|
|
234
236
|
print "\033]0;Git Game Show - #{seconds} seconds remaining\007"
|
235
237
|
STDOUT.flush
|
236
238
|
end
|
237
|
-
|
239
|
+
|
238
240
|
# Super simple ordering implementation with minimal screen updates
|
239
241
|
def handle_ordering_question(options, question_text = nil)
|
240
242
|
# Create a copy of the options that we can modify
|
@@ -243,59 +245,56 @@ module GitGameShow
|
|
243
245
|
selected_index = nil
|
244
246
|
num_options = current_order.size
|
245
247
|
question_text ||= "Put these commits in chronological order (oldest to newest)"
|
246
|
-
|
248
|
+
|
247
249
|
# Extract question data if available
|
248
250
|
data = Thread.current[:question_data] || {}
|
249
251
|
question_number = data['question_number']
|
250
252
|
total_questions = data['total_questions']
|
251
|
-
|
253
|
+
|
252
254
|
# Draw the initial screen once
|
253
255
|
# system('clear') || system('cls')
|
254
|
-
|
256
|
+
|
255
257
|
# Draw question header once
|
256
258
|
if question_number && total_questions
|
257
|
-
|
258
|
-
puts "
|
259
|
-
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)
|
260
264
|
end
|
261
|
-
|
265
|
+
|
262
266
|
# Draw the main question text once
|
263
267
|
puts "\n #{question_text}".colorize(:light_blue)
|
264
268
|
puts " Put in order from oldest (1) to newest (#{num_options})".colorize(:light_blue)
|
265
|
-
|
269
|
+
|
266
270
|
# Draw instructions once
|
267
271
|
puts "\n INSTRUCTIONS:".colorize(:yellow)
|
268
272
|
puts " • Use ↑/↓ arrows to move cursor".colorize(:white)
|
269
273
|
puts " • Press ENTER to select/deselect an item to move".colorize(:white)
|
270
274
|
puts " • Selected items move with cursor when you press ↑/↓".colorize(:white)
|
271
275
|
puts " • Navigate to Submit and press ENTER when finished".colorize(:white)
|
272
|
-
|
273
|
-
# Draw the header for the list content once
|
274
|
-
puts "\n CURRENT ORDER:".colorize(:light_blue)
|
275
|
-
|
276
|
+
|
276
277
|
# Calculate where the list content starts on screen
|
277
278
|
content_start_line = question_number ? 15 : 12
|
278
|
-
|
279
|
+
|
279
280
|
# Draw the list content (this will be redrawn repeatedly)
|
280
281
|
draw_ordering_list(current_order, cursor_index, selected_index, content_start_line, num_options)
|
281
|
-
|
282
|
-
# Initialize variables
|
283
|
-
|
282
|
+
|
284
283
|
# Main interaction loop
|
285
284
|
loop do
|
286
285
|
# Read a single keypress
|
287
286
|
char = read_char
|
288
|
-
|
287
|
+
|
289
288
|
# Clear any message on this line
|
290
289
|
move_cursor_to(content_start_line + num_options + 2, 0)
|
291
290
|
print "\r\033[K"
|
292
|
-
|
291
|
+
|
293
292
|
# Check if the timer has expired
|
294
293
|
if @timer_expired
|
295
294
|
# If timer expired, just return the current ordering
|
296
295
|
return current_order
|
297
296
|
end
|
298
|
-
|
297
|
+
|
299
298
|
# Now char is an integer (ASCII code)
|
300
299
|
case char
|
301
300
|
when 13, 10 # Enter key (CR or LF)
|
@@ -317,7 +316,7 @@ module GitGameShow
|
|
317
316
|
# Move cursor up
|
318
317
|
if selected_index == cursor_index && cursor_index > 0
|
319
318
|
# Move the selected item up in the order
|
320
|
-
current_order[cursor_index], current_order[cursor_index - 1] =
|
319
|
+
current_order[cursor_index], current_order[cursor_index - 1] =
|
321
320
|
current_order[cursor_index - 1], current_order[cursor_index]
|
322
321
|
cursor_index -= 1
|
323
322
|
selected_index = cursor_index
|
@@ -328,7 +327,7 @@ module GitGameShow
|
|
328
327
|
when 66, 106, 115 # Down arrow (66='B'), j (106), s (115)
|
329
328
|
if selected_index == cursor_index && cursor_index < num_options - 1
|
330
329
|
# Move the selected item down in the order
|
331
|
-
current_order[cursor_index], current_order[cursor_index + 1] =
|
330
|
+
current_order[cursor_index], current_order[cursor_index + 1] =
|
332
331
|
current_order[cursor_index + 1], current_order[cursor_index]
|
333
332
|
cursor_index += 1
|
334
333
|
selected_index = cursor_index
|
@@ -337,34 +336,34 @@ module GitGameShow
|
|
337
336
|
cursor_index += 1
|
338
337
|
end
|
339
338
|
end
|
340
|
-
|
339
|
+
|
341
340
|
# Redraw just the list portion of the screen
|
342
341
|
draw_ordering_list(current_order, cursor_index, selected_index, content_start_line, num_options)
|
343
342
|
end
|
344
343
|
end
|
345
|
-
|
344
|
+
|
346
345
|
# Helper method to draw just the list portion of the ordering UI
|
347
346
|
def draw_ordering_list(items, cursor_index, selected_index, start_line, num_options)
|
348
347
|
# Clear the line above the list (was used for debugging)
|
349
348
|
debug_line = start_line - 1
|
350
349
|
move_cursor_to(debug_line, 0)
|
351
350
|
print "\r\033[K" # Clear debug line
|
352
|
-
|
351
|
+
|
353
352
|
# Move cursor to the start position for the list
|
354
353
|
move_cursor_to(start_line, 0)
|
355
|
-
|
354
|
+
|
356
355
|
# Clear all lines that will contain list items and the submit button
|
357
356
|
(num_options + 2).times do |i|
|
358
357
|
move_cursor_to(start_line + i, 0)
|
359
358
|
print "\r\033[K" # Clear current line without moving cursor
|
360
359
|
end
|
361
|
-
|
360
|
+
|
362
361
|
# Draw each item with appropriate highlighting
|
363
362
|
items.each_with_index do |item, idx|
|
364
363
|
# Calculate the line for this item
|
365
364
|
item_line = start_line + idx
|
366
365
|
move_cursor_to(item_line, 0)
|
367
|
-
|
366
|
+
|
368
367
|
if selected_index == idx
|
369
368
|
# Selected item (being moved)
|
370
369
|
print " → #{idx + 1}. #{item}".colorize(:light_green)
|
@@ -376,7 +375,7 @@ module GitGameShow
|
|
376
375
|
print " #{idx + 1}. #{item}".colorize(:white)
|
377
376
|
end
|
378
377
|
end
|
379
|
-
|
378
|
+
|
380
379
|
# Add the Submit option at the bottom
|
381
380
|
move_cursor_to(start_line + num_options, 0)
|
382
381
|
if cursor_index == num_options
|
@@ -384,27 +383,27 @@ module GitGameShow
|
|
384
383
|
else
|
385
384
|
print " Submit Answer".colorize(:white)
|
386
385
|
end
|
387
|
-
|
386
|
+
|
388
387
|
# Move cursor after the list
|
389
388
|
move_cursor_to(start_line + num_options + 1, 0)
|
390
|
-
|
389
|
+
|
391
390
|
# Ensure output is visible
|
392
391
|
STDOUT.flush
|
393
392
|
end
|
394
|
-
|
393
|
+
|
395
394
|
# Helper to position cursor at a specific row/column
|
396
395
|
def move_cursor_to(row, col)
|
397
396
|
print "\033[#{row};#{col}H"
|
398
397
|
end
|
399
|
-
|
398
|
+
|
400
399
|
# Simplified key input reader that uses numbers for arrow keys
|
401
400
|
def read_char
|
402
401
|
begin
|
403
402
|
system("stty raw -echo")
|
404
|
-
|
403
|
+
|
405
404
|
# Read a character
|
406
405
|
c = STDIN.getc
|
407
|
-
|
406
|
+
|
408
407
|
# Special handling for escape sequences
|
409
408
|
if c == "\e"
|
410
409
|
# Could be an arrow key - read more
|
@@ -434,14 +433,14 @@ module GitGameShow
|
|
434
433
|
return 27 # ESC key
|
435
434
|
end
|
436
435
|
end
|
437
|
-
|
436
|
+
|
438
437
|
# Just return the ASCII value for the key
|
439
438
|
return c.ord
|
440
439
|
ensure
|
441
440
|
system("stty -raw echo")
|
442
441
|
end
|
443
442
|
end
|
444
|
-
|
443
|
+
|
445
444
|
# Non-blocking key input reader that supports timeouts
|
446
445
|
def read_char_with_timeout
|
447
446
|
begin
|
@@ -449,10 +448,10 @@ module GitGameShow
|
|
449
448
|
if IO.select([STDIN], [], [], 0.1)
|
450
449
|
# Read a character
|
451
450
|
c = STDIN.getc
|
452
|
-
|
451
|
+
|
453
452
|
# Handle nil case (EOF)
|
454
453
|
return nil if c.nil?
|
455
|
-
|
454
|
+
|
456
455
|
# Special handling for escape sequences
|
457
456
|
if c == "\e"
|
458
457
|
# Could be an arrow key - read more
|
@@ -482,11 +481,11 @@ module GitGameShow
|
|
482
481
|
return 27 # ESC key
|
483
482
|
end
|
484
483
|
end
|
485
|
-
|
484
|
+
|
486
485
|
# Just return the ASCII value for the key
|
487
486
|
return c.ord
|
488
487
|
end
|
489
|
-
|
488
|
+
|
490
489
|
# No input available - return nil for timeout
|
491
490
|
return nil
|
492
491
|
rescue => e
|
@@ -494,17 +493,17 @@ module GitGameShow
|
|
494
493
|
return nil
|
495
494
|
end
|
496
495
|
end
|
497
|
-
|
496
|
+
|
498
497
|
# Helper method to display countdown using a status bar at the bottom of the screen
|
499
498
|
def update_countdown_display(seconds, original_seconds)
|
500
499
|
# Get terminal dimensions
|
501
500
|
term_height = `tput lines`.to_i rescue 24
|
502
|
-
|
501
|
+
|
503
502
|
# Calculate a simple progress bar
|
504
503
|
total_width = 30
|
505
504
|
progress_width = ((seconds.to_f / original_seconds) * total_width).to_i
|
506
505
|
remaining_width = total_width - progress_width
|
507
|
-
|
506
|
+
|
508
507
|
# Choose color based on time remaining
|
509
508
|
color = if seconds <= 5
|
510
509
|
:red
|
@@ -513,57 +512,58 @@ module GitGameShow
|
|
513
512
|
else
|
514
513
|
:green
|
515
514
|
end
|
516
|
-
|
515
|
+
|
517
516
|
# Create status bar with progress indicator
|
518
517
|
bar = "[#{"█" * progress_width}#{" " * remaining_width}]"
|
519
518
|
status_text = " ⏱️ Time remaining: #{seconds.to_s.rjust(2)} seconds ".colorize(color) + bar
|
520
|
-
|
519
|
+
|
521
520
|
# Save cursor position
|
522
521
|
print "\e7"
|
523
|
-
|
522
|
+
|
524
523
|
# Move to bottom of screen (status line)
|
525
524
|
print "\e[#{term_height};1H"
|
526
|
-
|
525
|
+
|
527
526
|
# Clear the line
|
528
527
|
print "\e[K"
|
529
|
-
|
528
|
+
|
530
529
|
# Print status bar at bottom of screen
|
531
530
|
print status_text
|
532
|
-
|
531
|
+
|
533
532
|
# Restore cursor position
|
534
533
|
print "\e8"
|
535
534
|
STDOUT.flush
|
536
535
|
end
|
537
|
-
|
536
|
+
|
538
537
|
def handle_game_start(data)
|
539
538
|
@game_state = :playing
|
540
539
|
@players = data['players']
|
541
540
|
@total_rounds = data['rounds']
|
542
|
-
|
541
|
+
|
543
542
|
clear_screen
|
544
|
-
|
543
|
+
|
545
544
|
# Display a fun "Game Starting" animation
|
545
|
+
box_width = 40
|
546
546
|
puts "\n\n"
|
547
|
-
puts "
|
548
|
-
puts "
|
549
|
-
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)
|
550
550
|
puts "\n\n"
|
551
|
-
|
552
|
-
puts " Total rounds: #{@total_rounds}".colorize(:
|
553
|
-
puts " Players: #{@players.join(', ')}".colorize(:
|
551
|
+
|
552
|
+
puts " Total rounds: #{@total_rounds}".colorize(:light_blue)
|
553
|
+
puts " Players: #{@players.join(', ')}".colorize(:light_blue)
|
554
554
|
puts "\n\n"
|
555
555
|
puts " Get ready for the first round!".colorize(:yellow)
|
556
556
|
puts "\n\n"
|
557
557
|
end
|
558
|
-
|
558
|
+
|
559
559
|
def handle_player_update(data)
|
560
560
|
# Update the players list
|
561
561
|
@players = data['players']
|
562
|
-
|
562
|
+
|
563
563
|
if @game_state == :lobby
|
564
564
|
# If we're in the lobby, refresh the waiting room UI with updated player list
|
565
565
|
display_waiting_room
|
566
|
-
|
566
|
+
|
567
567
|
# Show notification at the bottom
|
568
568
|
if data['type'] == 'player_joined'
|
569
569
|
puts "\n 🟢 #{data['name']} has joined the game".colorize(:green)
|
@@ -572,61 +572,52 @@ module GitGameShow
|
|
572
572
|
end
|
573
573
|
else
|
574
574
|
# During gameplay, just show a notification without disrupting the game UI
|
575
|
-
terminal_width = `tput cols`.to_i rescue
|
576
|
-
|
575
|
+
terminal_width = `tput cols`.to_i rescue @game_width
|
576
|
+
|
577
577
|
# Create a notification box that won't interfere with ongoing gameplay
|
578
|
-
puts "
|
579
|
-
|
578
|
+
puts ""
|
579
|
+
puts "╭#{"─" * (terminal_width - 2)}╮".colorize(:light_blue)
|
580
|
+
|
580
581
|
if data['type'] == 'player_joined'
|
581
|
-
puts "
|
582
|
+
puts "│#{" 🟢 #{data['name']} has joined the game ".center(terminal_width - 2)}│".colorize(:green)
|
582
583
|
else
|
583
|
-
puts "
|
584
|
+
puts "│#{" 🔴 #{data['name']} has left the game ".center(terminal_width - 2)}│".colorize(:yellow)
|
584
585
|
end
|
585
|
-
|
586
|
+
|
586
587
|
# Don't show all players during gameplay - can be too disruptive
|
587
588
|
# Just show the total count
|
588
|
-
puts "
|
589
|
-
puts "
|
589
|
+
puts "│#{" Total players: #{data['players'].size} ".center(terminal_width - 2)}│".colorize(:light_blue)
|
590
|
+
puts "╰#{"─" * (terminal_width - 2)}╯".colorize(:light_blue)
|
590
591
|
end
|
591
592
|
end
|
592
|
-
|
593
|
+
|
593
594
|
def handle_round_start(data)
|
594
595
|
clear_screen
|
595
|
-
|
596
|
+
|
596
597
|
# Draw a fancy round header
|
597
598
|
round_num = data['round']
|
598
599
|
total_rounds = data['total_rounds']
|
599
600
|
mini_game = data['mini_game']
|
600
601
|
description = data['description']
|
601
|
-
|
602
|
+
|
602
603
|
puts "\n\n"
|
603
|
-
|
604
|
+
|
604
605
|
# Box is drawn with exactly 45 "━" characters for the top and bottom borders
|
605
606
|
# The top and bottom including borders are 48 characters wide
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
# Find exact box width by measuring the top border
|
613
|
-
box_width = box_top.length # Should be 48 with Unicode characters
|
614
|
-
|
615
|
-
# The inner width is the box width minus the borders
|
616
|
-
inner_width = box_width - (" ┃".length + "┃".length)
|
617
|
-
|
618
|
-
# Simply use Ruby's built-in center method for reliable centering
|
619
|
-
box_middle = " ┃" + round_text.center(inner_width) + "┃"
|
620
|
-
|
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)
|
611
|
+
|
621
612
|
# Output the box
|
622
613
|
puts box_top.colorize(:green)
|
623
614
|
puts box_middle.colorize(:green)
|
624
615
|
puts box_bottom.colorize(:green)
|
625
616
|
puts "\n"
|
626
|
-
puts " Mini-game: #{mini_game}".colorize(:
|
617
|
+
puts " Mini-game: #{mini_game}".colorize(:light_blue)
|
627
618
|
puts " #{description}".colorize(:light_blue)
|
628
619
|
puts "\n"
|
629
|
-
|
620
|
+
|
630
621
|
# Count down to the start - don't sleep here as we're waiting for the server
|
631
622
|
# to send us the questions after a fixed delay
|
632
623
|
puts " Get ready for the first question...".colorize(:yellow)
|
@@ -634,99 +625,92 @@ module GitGameShow
|
|
634
625
|
puts " The host is controlling the timing of all questions.".colorize(:light_blue)
|
635
626
|
puts "\n\n"
|
636
627
|
end
|
637
|
-
|
628
|
+
|
638
629
|
def handle_question(data)
|
639
630
|
# Invalidate any previous timer
|
640
631
|
@current_timer_id = SecureRandom.uuid
|
641
|
-
|
632
|
+
|
642
633
|
# Clear the screen completely
|
643
634
|
clear_screen
|
644
|
-
|
635
|
+
|
645
636
|
question_num = data['question_number']
|
646
637
|
total_questions = data['total_questions']
|
647
638
|
question = data['question']
|
648
639
|
timeout = data['timeout']
|
649
|
-
|
640
|
+
|
650
641
|
# Store question data in thread-local storage for access in other methods
|
651
642
|
Thread.current[:question_data] = data
|
652
|
-
|
643
|
+
|
653
644
|
# No need to reserve space for timer - it will be at the bottom of the screen
|
654
|
-
|
645
|
+
|
655
646
|
# Display question header
|
656
647
|
puts "\n"
|
657
|
-
|
648
|
+
|
658
649
|
# Draw a simple box for the question header
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
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)
|
654
|
+
|
665
655
|
# Output the question box
|
666
|
-
puts box_top.colorize(:
|
667
|
-
puts box_middle.colorize(:
|
668
|
-
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)
|
669
659
|
puts "\n"
|
670
|
-
|
660
|
+
|
671
661
|
# Display question
|
672
662
|
puts " #{question}".colorize(:light_blue)
|
673
|
-
|
663
|
+
|
674
664
|
# Display commit info if available
|
675
665
|
if data['commit_info']
|
676
666
|
puts "\n Commit: #{data['commit_info']}".colorize(:yellow)
|
677
667
|
end
|
678
668
|
puts "\n"
|
679
|
-
|
669
|
+
|
680
670
|
# Create a unique timer ID for this question
|
681
671
|
timer_id = SecureRandom.uuid
|
682
672
|
@current_timer_id = timer_id
|
683
|
-
|
684
|
-
end_time = start_time + timeout
|
685
|
-
|
686
|
-
# Static deadline info
|
687
|
-
puts " Deadline: #{end_time.strftime('%I:%M:%S %p')}".colorize(:light_blue)
|
688
|
-
puts "\n"
|
689
|
-
|
673
|
+
|
690
674
|
# Initialize remaining time for scoring
|
691
675
|
@time_remaining = timeout
|
692
|
-
|
676
|
+
|
693
677
|
# Update the timer display immediately
|
694
678
|
update_countdown_display(timeout, timeout)
|
695
|
-
|
679
|
+
|
696
680
|
# Variable to track if the timer has expired
|
697
681
|
@timer_expired = false
|
698
|
-
|
682
|
+
|
699
683
|
# Start countdown in a background thread with new approach
|
700
684
|
countdown_thread = Thread.new do
|
701
685
|
begin
|
702
686
|
remaining = timeout
|
703
|
-
|
687
|
+
|
704
688
|
while remaining > 0 && @current_timer_id == timer_id
|
705
689
|
# Update both window title and fixed position display
|
706
690
|
update_title_timer(remaining)
|
707
691
|
update_countdown_display(remaining, timeout)
|
708
|
-
|
692
|
+
|
709
693
|
# Sound alert when time is almost up (< 5 seconds)
|
710
694
|
if remaining < 5 && remaining > 0
|
711
695
|
print "\a" if remaining % 2 == 0 # Beep on even seconds
|
712
696
|
end
|
713
|
-
|
697
|
+
|
714
698
|
# Store time for scoring
|
715
699
|
@time_remaining = remaining
|
716
|
-
|
700
|
+
|
717
701
|
# Wait one second
|
718
702
|
sleep 1
|
719
703
|
remaining -= 1
|
720
704
|
end
|
721
|
-
|
705
|
+
|
722
706
|
# Final update when timer reaches zero
|
723
707
|
if @current_timer_id == timer_id
|
724
708
|
update_countdown_display(0, timeout)
|
725
|
-
|
709
|
+
|
726
710
|
# IMPORTANT: Send a timeout answer when time expires
|
727
711
|
# without waiting for user input
|
728
712
|
@timer_expired = true
|
729
|
-
|
713
|
+
|
730
714
|
# Clear the screen to break out of any prompt/UI state
|
731
715
|
clear_screen
|
732
716
|
|
@@ -753,7 +737,7 @@ module GitGameShow
|
|
753
737
|
# Silent failure for robustness
|
754
738
|
end
|
755
739
|
end
|
756
|
-
|
740
|
+
|
757
741
|
# Handle different question types - but wrap in a separate thread
|
758
742
|
# so that timeouts can interrupt the UI
|
759
743
|
input_thread = Thread.new do
|
@@ -786,7 +770,7 @@ module GitGameShow
|
|
786
770
|
end
|
787
771
|
end
|
788
772
|
end
|
789
|
-
|
773
|
+
|
790
774
|
# Wait for input but with timeout
|
791
775
|
answer = nil
|
792
776
|
begin
|
@@ -798,7 +782,7 @@ module GitGameShow
|
|
798
782
|
# If timeout occurs during join, kill the thread
|
799
783
|
input_thread.kill if input_thread.alive?
|
800
784
|
end
|
801
|
-
|
785
|
+
|
802
786
|
# Only send user answer if timer hasn't expired
|
803
787
|
unless @timer_expired
|
804
788
|
# Send answer back to server
|
@@ -808,47 +792,47 @@ module GitGameShow
|
|
808
792
|
answer: answer,
|
809
793
|
question_id: data['question_id']
|
810
794
|
})
|
811
|
-
|
795
|
+
|
812
796
|
puts "\n Answer submitted! Waiting for feedback...".colorize(:green)
|
813
797
|
end
|
814
|
-
|
798
|
+
|
815
799
|
# Stop the timer by invalidating its ID and terminating the thread
|
816
800
|
@current_timer_id = SecureRandom.uuid # Change timer ID to signal thread to stop
|
817
801
|
countdown_thread.kill if countdown_thread.alive? # Force kill the thread
|
818
|
-
|
802
|
+
|
819
803
|
# Reset window title
|
820
804
|
print "\033]0;Git Game Show\007"
|
821
|
-
|
805
|
+
|
822
806
|
# Clear the timer status line at bottom
|
823
807
|
term_height = `tput lines`.to_i rescue 24
|
824
808
|
print "\e7" # Save cursor position
|
825
809
|
print "\e[#{term_height};1H" # Move to bottom line
|
826
810
|
print "\e[K" # Clear line
|
827
811
|
print "\e8" # Restore cursor position
|
828
|
-
|
812
|
+
|
829
813
|
# The server will send ANSWER_FEEDBACK message right away, then we'll see feedback
|
830
814
|
end
|
831
|
-
|
815
|
+
|
832
816
|
# Handle immediate feedback after submitting an answer
|
833
817
|
def handle_answer_feedback(data)
|
834
818
|
# Invalidate any running timer and reset window title
|
835
819
|
@current_timer_id = SecureRandom.uuid
|
836
820
|
print "\033]0;Git Game Show\007" # Reset window title
|
837
|
-
|
821
|
+
|
838
822
|
# Clear the timer status line at bottom
|
839
823
|
term_height = `tput lines`.to_i rescue 24
|
840
824
|
print "\e7" # Save cursor position
|
841
825
|
print "\e[#{term_height};1H" # Move to bottom line
|
842
826
|
print "\e[K" # Clear line
|
843
827
|
print "\e8" # Restore cursor position
|
844
|
-
|
828
|
+
|
845
829
|
# Don't clear screen, just display the feedback under the question
|
846
830
|
# This keeps the context of the question while showing the result
|
847
|
-
|
831
|
+
|
848
832
|
# Add a visual separator
|
849
833
|
puts "\n #{"─" * 40}".colorize(:light_black)
|
850
834
|
puts "\n"
|
851
|
-
|
835
|
+
|
852
836
|
# Show immediate feedback
|
853
837
|
if data['answer'] == "TIMEOUT"
|
854
838
|
# Special handling for timeouts
|
@@ -859,7 +843,7 @@ module GitGameShow
|
|
859
843
|
# Correct answer
|
860
844
|
points_text = data['points'] > 0 ? " (+#{data['points']} points)" : ""
|
861
845
|
puts " ✅ CORRECT! Your answer was correct: #{data['answer']}#{points_text}".colorize(:green)
|
862
|
-
|
846
|
+
|
863
847
|
# Show bonus points details if applicable
|
864
848
|
if data['points'] > 10 # More than base points
|
865
849
|
bonus = data['points'] - 10
|
@@ -870,7 +854,7 @@ module GitGameShow
|
|
870
854
|
puts " ❌ INCORRECT! The correct answer was: #{data['correct_answer']}".colorize(:red)
|
871
855
|
puts " You answered: #{data['answer']} (0 points)".colorize(:yellow)
|
872
856
|
end
|
873
|
-
|
857
|
+
|
874
858
|
puts "\n Waiting for the round to complete. Please wait for the next question...".colorize(:light_blue)
|
875
859
|
end
|
876
860
|
|
@@ -879,43 +863,42 @@ module GitGameShow
|
|
879
863
|
# Invalidate any running timer and reset window title
|
880
864
|
@current_timer_id = SecureRandom.uuid
|
881
865
|
print "\033]0;Git Game Show - Round Results\007" # Reset window title with context
|
882
|
-
|
866
|
+
|
883
867
|
# Start with a clean screen
|
884
868
|
clear_screen
|
885
|
-
|
869
|
+
|
886
870
|
puts "\n"
|
887
|
-
|
871
|
+
|
888
872
|
# Box is drawn with exactly 45 "━" characters for the top and bottom borders
|
889
873
|
# The top and bottom including borders are 48 characters wide
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
# Find exact box width by measuring the top border
|
897
|
-
box_width = box_top.length # Should be 48 with Unicode characters
|
898
|
-
|
899
|
-
# The inner width is the box width minus 2 characters for the borders
|
900
|
-
inner_width = box_width - (" ┃".length + "┃".length)
|
901
|
-
|
902
|
-
# Simply use Ruby's built-in center method for reliable centering
|
903
|
-
box_middle = " ┃" + result_text.center(inner_width) + "┃"
|
904
|
-
|
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)
|
878
|
+
|
905
879
|
# Output the box
|
906
|
-
puts box_top.colorize(:
|
907
|
-
puts box_middle.colorize(:
|
908
|
-
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)
|
909
883
|
puts "\n"
|
910
|
-
|
884
|
+
|
911
885
|
# Show question again
|
912
886
|
puts " Question: #{data['question'][:question]}".colorize(:light_blue)
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
887
|
+
|
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)
|
899
|
+
|
917
900
|
# Debug data temporarily removed
|
918
|
-
|
901
|
+
|
919
902
|
# Handle results based on structure
|
920
903
|
if data['results'].is_a?(Hash)
|
921
904
|
data['results'].each do |player, result|
|
@@ -925,16 +908,38 @@ module GitGameShow
|
|
925
908
|
correct = result[:correct] || result['correct'] || false
|
926
909
|
answer = result[:answer] || result['answer'] || "No answer"
|
927
910
|
points = result[:points] || result['points'] || 0
|
928
|
-
|
911
|
+
|
929
912
|
status = correct ? "✓" : "✗"
|
930
913
|
points_str = "(+#{points} points)"
|
931
914
|
player_str = player == name ? "#{player} (You)" : player
|
932
|
-
|
933
|
-
|
934
|
-
if
|
935
|
-
|
915
|
+
|
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
|
936
935
|
else
|
937
|
-
|
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
|
938
943
|
end
|
939
944
|
else
|
940
945
|
# Fallback for unexpected result format
|
@@ -945,14 +950,14 @@ module GitGameShow
|
|
945
950
|
# Fallback message if results isn't a hash
|
946
951
|
puts " No detailed results available".colorize(:yellow)
|
947
952
|
end
|
948
|
-
|
953
|
+
|
949
954
|
# Display current scoreboard
|
950
955
|
if data['scores']
|
951
956
|
puts "\n Current Standings:".colorize(:yellow)
|
952
957
|
data['scores'].each_with_index do |(player, score), index|
|
953
958
|
player_str = player == name ? "#{player} (You)" : player
|
954
959
|
rank = index + 1
|
955
|
-
|
960
|
+
|
956
961
|
# Add medal emoji for top 3
|
957
962
|
rank_display = case rank
|
958
963
|
when 1 then "🥇"
|
@@ -960,50 +965,51 @@ module GitGameShow
|
|
960
965
|
when 3 then "🥉"
|
961
966
|
else "#{rank}."
|
962
967
|
end
|
963
|
-
|
968
|
+
|
964
969
|
output = " #{rank_display} #{player_str.ljust(20)} #{score} points"
|
965
|
-
|
970
|
+
|
966
971
|
if player == name
|
967
|
-
puts output.colorize(:light_yellow)
|
972
|
+
puts output.colorize(:light_yellow)
|
968
973
|
else
|
969
974
|
puts output.colorize(:light_blue)
|
970
975
|
end
|
971
976
|
end
|
972
977
|
end
|
973
|
-
|
978
|
+
|
974
979
|
puts "\n Next question coming up automatically...".colorize(:yellow)
|
975
980
|
end
|
976
|
-
|
981
|
+
|
977
982
|
def handle_scoreboard(data)
|
978
983
|
# Invalidate any running timer and reset window title
|
979
984
|
@current_timer_id = SecureRandom.uuid
|
980
985
|
print "\033]0;Git Game Show - Scoreboard\007" # Reset window title with context
|
981
|
-
|
986
|
+
|
982
987
|
# Always start with a clean screen for the scoreboard
|
983
988
|
clear_screen
|
984
|
-
|
985
|
-
|
986
|
-
puts "
|
987
|
-
puts "
|
988
|
-
puts "
|
989
|
+
|
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)
|
989
995
|
puts "\n"
|
990
|
-
|
996
|
+
|
991
997
|
# Get player positions
|
992
998
|
position = 1
|
993
999
|
last_score = nil
|
994
|
-
|
1000
|
+
|
995
1001
|
data['scores'].each do |player, score|
|
996
1002
|
# Determine position (handle ties)
|
997
1003
|
position = data['scores'].values.index(score) + 1 if last_score != score
|
998
1004
|
last_score = score
|
999
|
-
|
1005
|
+
|
1000
1006
|
# Highlight current player
|
1001
1007
|
player_str = player == name ? "#{player} (You)" : player
|
1002
|
-
|
1008
|
+
|
1003
1009
|
# Format with position
|
1004
1010
|
position_str = "#{position}."
|
1005
1011
|
score_str = "#{score} points"
|
1006
|
-
|
1012
|
+
|
1007
1013
|
# Add emoji for top 3
|
1008
1014
|
case position
|
1009
1015
|
when 1
|
@@ -1019,94 +1025,103 @@ module GitGameShow
|
|
1019
1025
|
puts " #{position_str.ljust(5)} #{player_str.ljust(25)} #{score_str}"
|
1020
1026
|
end
|
1021
1027
|
end
|
1022
|
-
|
1023
|
-
puts "\n Next round coming up soon...".colorize(:
|
1028
|
+
|
1029
|
+
puts "\n Next round coming up soon...".colorize(:light_blue)
|
1024
1030
|
end
|
1025
|
-
|
1031
|
+
|
1026
1032
|
def handle_game_end(data)
|
1027
1033
|
# Invalidate any running timer and reset window title
|
1028
1034
|
@current_timer_id = SecureRandom.uuid
|
1029
1035
|
print "\033]0;Git Game Show - Game Over\007" # Reset window title with context
|
1030
|
-
|
1036
|
+
|
1031
1037
|
# Clear any timer status line at the bottom
|
1032
1038
|
term_height = `tput lines`.to_i rescue 24
|
1033
1039
|
print "\e7" # Save cursor position
|
1034
1040
|
print "\e[#{term_height};1H" # Move to bottom line
|
1035
1041
|
print "\e[K" # Clear line
|
1036
1042
|
print "\e8" # Restore cursor position
|
1037
|
-
|
1043
|
+
|
1038
1044
|
# Completely clear the screen
|
1039
1045
|
clear_screen
|
1040
1046
|
@game_state = :ended
|
1041
|
-
|
1047
|
+
|
1042
1048
|
winner = data['winner']
|
1043
|
-
|
1049
|
+
|
1044
1050
|
# ASCII trophy art
|
1045
|
-
trophy =
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1051
|
+
trophy = [
|
1052
|
+
"___________",
|
1053
|
+
"'._==_==_=_.'",
|
1054
|
+
".-\\: /-.",
|
1055
|
+
"| (|:. |) |",
|
1056
|
+
"'-|:. |-'",
|
1057
|
+
"\\::. /",
|
1058
|
+
"'::. .'",
|
1059
|
+
") (",
|
1060
|
+
"_.' '._"
|
1061
|
+
]
|
1062
|
+
|
1063
|
+
box_width = 40
|
1057
1064
|
puts "\n\n"
|
1058
|
-
puts
|
1065
|
+
trophy.each{|line| puts line.center(@game_width).colorize(:yellow)}
|
1059
1066
|
puts "\n"
|
1060
|
-
puts "
|
1061
|
-
puts "
|
1062
|
-
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)
|
1063
1070
|
puts "\n"
|
1064
|
-
|
1071
|
+
|
1065
1072
|
winner_is_you = winner == name
|
1066
1073
|
if winner_is_you
|
1067
|
-
puts "
|
1074
|
+
puts "🎉 Congratulations! You won! 🎉".center(@game_width).colorize(:light_yellow)
|
1068
1075
|
else
|
1069
|
-
puts "
|
1076
|
+
puts "Winner: #{winner}! 🏆".center(@game_width).colorize(:light_yellow)
|
1070
1077
|
end
|
1071
|
-
|
1072
|
-
puts "
|
1073
|
-
|
1078
|
+
|
1079
|
+
puts ""
|
1080
|
+
puts "Final Scores".center(@game_width).colorize(:light_blue)
|
1081
|
+
puts ""
|
1082
|
+
|
1074
1083
|
# Get player positions
|
1075
1084
|
position = 1
|
1076
1085
|
last_score = nil
|
1077
|
-
|
1086
|
+
|
1078
1087
|
data['scores'].each do |player, score|
|
1079
1088
|
# Determine position (handle ties)
|
1080
1089
|
position = data['scores'].values.index(score) + 1 if last_score != score
|
1081
1090
|
last_score = score
|
1082
|
-
|
1091
|
+
|
1083
1092
|
# Highlight current player
|
1084
1093
|
player_str = player == name ? "#{player} (You)" : player
|
1085
|
-
|
1094
|
+
|
1086
1095
|
# Format with position
|
1087
1096
|
position_str = "#{position}."
|
1088
1097
|
score_str = "#{score} points"
|
1089
|
-
|
1098
|
+
|
1090
1099
|
# Add emoji for top 3
|
1100
|
+
scores_width = @game_width - 30
|
1091
1101
|
case position
|
1092
1102
|
when 1
|
1093
1103
|
position_str = "🥇 #{position_str}"
|
1094
|
-
|
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)
|
1095
1106
|
when 2
|
1096
1107
|
position_str = "🥈 #{position_str}"
|
1097
|
-
|
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)
|
1098
1110
|
when 3
|
1099
1111
|
position_str = "🥉 #{position_str}"
|
1100
|
-
|
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)
|
1101
1114
|
else
|
1102
|
-
|
1115
|
+
left_string = " " + (position_str.rjust(5) + ' ' + player_str).ljust(scores_width - score_str.length)
|
1116
|
+
puts "#{left_string}#{score_str}".center(@game_width)
|
1103
1117
|
end
|
1104
1118
|
end
|
1105
|
-
|
1106
|
-
puts "\n
|
1107
|
-
puts "
|
1119
|
+
|
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)
|
1108
1123
|
puts " Press Ctrl+C to exit, or wait for the next game".colorize(:light_black)
|
1109
|
-
|
1124
|
+
|
1110
1125
|
# Keep client ready to receive a new game start or reset message
|
1111
1126
|
@game_over_timer = Thread.new do
|
1112
1127
|
begin
|
@@ -1120,31 +1135,31 @@ module GitGameShow
|
|
1120
1135
|
end
|
1121
1136
|
end
|
1122
1137
|
end
|
1123
|
-
|
1138
|
+
|
1124
1139
|
# Add a special method to handle game reset notifications
|
1125
1140
|
def handle_game_reset(data)
|
1126
1141
|
# Stop the game over timer if it's running
|
1127
1142
|
@game_over_timer&.kill if @game_over_timer&.alive?
|
1128
|
-
|
1143
|
+
|
1129
1144
|
# Reset game state
|
1130
1145
|
@game_state = :lobby
|
1131
|
-
|
1146
|
+
|
1132
1147
|
# Clear any lingering state
|
1133
1148
|
@players = @players || [] # Keep existing players list if we have one
|
1134
|
-
|
1149
|
+
|
1135
1150
|
# Show the waiting room again
|
1136
1151
|
clear_screen
|
1137
1152
|
display_waiting_room
|
1138
|
-
|
1153
|
+
|
1139
1154
|
# Show a prominent message that we're back in waiting room mode
|
1140
|
-
puts "\n 🔄 The game has been reset by the host. Waiting for a new game to start...".colorize(:
|
1141
|
-
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)
|
1142
1157
|
end
|
1143
|
-
|
1158
|
+
|
1144
1159
|
def handle_chat(data)
|
1145
1160
|
puts "[#{data['sender']}]: #{data['message']}".colorize(:light_blue)
|
1146
1161
|
end
|
1147
|
-
|
1162
|
+
|
1148
1163
|
def send_message(message)
|
1149
1164
|
begin
|
1150
1165
|
@ws.send(message.to_json)
|
@@ -1153,4 +1168,4 @@ module GitGameShow
|
|
1153
1168
|
end
|
1154
1169
|
end
|
1155
1170
|
end
|
1156
|
-
end
|
1171
|
+
end
|