generated from Code-Institute-Org/p3-template
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrun.py
540 lines (507 loc) · 21.1 KB
/
run.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# Imports
import gspread
import json
import os
import random
import readchar
import sys
import time
from google.oauth2.service_account import Credentials
from colorama import Fore, Style
from datetime import datetime
# Google drive credentials
SCOPE = [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/drive"
]
# Google sheets credentials
creds = json.load(open('creds.json'))
CREDS = Credentials.from_service_account_info(creds)
SCOPED_CREDS = CREDS.with_scopes(SCOPE)
GSPREAD_CLIENT = gspread.authorize(SCOPED_CREDS)
SHEET = GSPREAD_CLIENT.open('fallout_hangman')
# Emoji list & ASCII art
NUCLEAR_EMOJI = '\u2622'
MAN_EMOJI = '\U0001F468\u200D\U0001F52C'
WOMAN_EMOJI = '\U0001F469\u200D\U0001F52C'
SKULL = '\u2620'
LETTER = '\u2753'
LOGO = r"""
______ _ _ _ ___
| ____| | | | | | |__ \
| |__ __ _| | | ___ _ _| |_ ) |
| __/ _` | | |/ _ \| | | | __| / /
| | | (_| | | | (_) | |_| | |_ / /_
|_| \__,_|_|_|\___/ \__,_|\__| |____|
"""
# Global variables
player_name = ''
perk_inteligence = 0
perk_luck = 0
perk_charisma = 0
alphabet = ('abcdefghijklmnopqrstuvwxyz')
game_winner = True
levels = [1, 2, 3, 4]
launch_time = 0
numbers_picked = []
# max number of rows in worksheet
# if amount of words incresed, this needs to be updated
max_word_number = 50
def random_word_number():
"""
Function generates number 1-max_word_number and checks if not
generated previously.
"""
# Run this loop until generated number not in
# already picked numbers
while True:
generated = random.randint(1, max_word_number)
if generated not in numbers_picked:
numbers_picked.append(generated)
return generated
def display_text(row, column, delay=0.012):
"""
Function displays large portions of text from connected google
sheet with typewriter effect.
Function takes number of line and column as parameter.
"""
# Clear screen
clear_screen()
# Call Google sheets and select column A and row from function parameter.
text_worksheet = SHEET.worksheet("text")
text_to_write = text_worksheet.cell(row, column).value
text_to_write = text_to_write.replace('PLAYER', player_name)
text_to_write = text_to_write.replace('BREAK', '\n')
# Write imported text with 0.012 seconds delay after each character typed.
print(Fore.YELLOW + '┌────────────────────', end='')
print('───────────────────┐' + Style.RESET_ALL)
print(Fore.YELLOW + f'│ {SKULL} Meantime in ', end='')
print(f' Wasteland {SKULL} │' + Style.RESET_ALL)
print(Fore.YELLOW + '└────────────────────', end='')
print('───────────────────┘' + Style.RESET_ALL)
print(Fore.GREEN + '')
for char in text_to_write:
sys.stdout.write(char)
sys.stdout.flush()
time.sleep(delay)
print('' + Style.RESET_ALL)
wait_until_keypressed()
return
def update_history():
"""
Function updates sheet on google drive with players name,
date, time and perk selected by user.
"""
# Open Google worksheet
history_worksheet = SHEET.worksheet("history")
# Get current time and date
start_time = str(datetime.now().time())
date_now = str(datetime.now().date())
# Update history worksheet with name, date, time and used perks.
history_worksheet.append_row([
player_name, date_now, start_time,
perk_inteligence, perk_luck, perk_charisma
])
def word_guess(difficulty, guesses):
"""
Function randomly takes a word from google sheet.
Function takes parameter of difficulty = length of word selected.
Function take parameter of guesses = how many guesses does player have.
"""
# Initial setting for color and value of message returned to player
# after each letter guess.
message_color = 3
message = ''
# Initial setting of the word has not been guessed yet.
word_guessed_correctly = False
letters_guessed = []
# Initial perks settings.
difficulty -= perk_inteligence
guesses += perk_luck
if perk_charisma == 1:
perk_charisma_used = 1
else:
perk_charisma_used = 0
# Open Google worksheet.
words_sheet = SHEET.worksheet("words")
# Generate random number 1-max_word_number and import a word based on
# difficulty = length of word and generated number.
random_number = random_word_number()
word_to_guess = words_sheet.cell(random_number, difficulty).value
# Cycle that runs until word fully guessed or player
# does not run out of guesses.
while word_guessed_correctly is False and guesses > 0:
# Initialize list of progress.
progress_list = []
# If perk charisma is used, reveal one letter each game.
if perk_charisma_used == 1:
charisma_position = random.randint(0, difficulty - 1)
charisma_letter = word_to_guess[charisma_position]
letters_guessed.append(charisma_letter)
perk_charisma_used = 0
# Append correctly guessed letters to progress list.
for each in word_to_guess:
if each in letters_guessed:
progress_list.append(each)
else:
progress_list.append('-')
# Transform progress list to a string
progress_word = ''.join(progress_list)
# If all letters are revealed (progress list has no '-'
# value left), player wins.
if '-' not in progress_list:
clear_screen()
print(Fore.GREEN + 'Great, the hidden word was :', end=' ')
print(f': "{word_to_guess}" !' + Style.RESET_ALL)
wait_until_keypressed()
return True
# Player sees the following information.
clear_screen()
print(Fore.YELLOW + '┌────────────────────', end='')
print('───────────────────┐' + Style.RESET_ALL)
print(Fore.YELLOW + f'│ {LETTER} Guess the ', end='')
print(f' Word {LETTER} │' + Style.RESET_ALL)
print(Fore.YELLOW + '└────────────────────', end='')
print('───────────────────┘' + Style.RESET_ALL)
print(Fore.BLUE + '\n')
print(f'Your word to guess is {difficulty} letters long.')
print(f'Your progress : {progress_word}')
print(f'You already tried this letters : {letters_guessed}')
print(f'Guesses left : {guesses}')
print('\n' + Style.RESET_ALL)
# Statement that changes color of message depends
# on the importance of message.
if message_color == 1:
print(Fore.RED + f'{message}' + Style.RESET_ALL)
elif message_color == 2:
print(Fore.YELLOW + f'{message}' + Style.RESET_ALL)
elif message_color == 3:
print(Fore.GREEN + f'{message}' + Style.RESET_ALL)
# Players input of a letter or the whole word.
player_guess = input('Guess a letter or type the whole word : ')
# If player have inputed only one character.
if len(player_guess) == 1:
# Input is not a letter.
if player_guess == '0':
print_intro()
elif player_guess not in alphabet:
message_color = 1
message = 'Input only letters ...'
# Input was already selected previously.
elif player_guess in letters_guessed:
message_color = 2
message = 'You already tried this letter ...'
# Input is not in the word to guess.
elif player_guess not in word_to_guess:
letters_guessed.append(player_guess)
guesses -= 1
message_color = 1
message = 'The letter is not in the hidden word ...'
# Correct guess.
elif player_guess in word_to_guess:
letters_guessed.append(player_guess)
guesses -= 1
message_color = 3
message = "That's correct ;)"
else:
message_color = 2
message = 'Something went wrong ...'
# Player guessed the whole word and the lenght of word is correct.
elif len(player_guess) == len(word_to_guess):
# Correct word.
if player_guess == word_to_guess:
clear_screen()
print(Fore.GREEN + 'Great, the hidden word was', end=' ')
print(f': "{word_to_guess}" !' + Style.RESET_ALL)
wait_until_keypressed()
return True
# Incorrect word.
else:
message_color = 1
message = 'Thats not the word ...'
guesses -= 1
# Player tries to pass in empty string.
elif len(player_guess) == 0:
message_color = 2
message = 'Your guess cannot be empty ...'
# The length of word to guess and players guess are different.
else:
message_color = 1
message = 'Length of your guess isnt same to the lenght of word.'
clear_screen()
print(Fore.RED + 'The hidden word was', end=' ')
print(f': "{word_to_guess}" !' + Style.RESET_ALL)
wait_until_keypressed()
return False
def create_charater():
"""
Function reads player's name and selection of perk.
Function uses 'readchar' dependency.
Players name can't be empty otherwise error message is returned.
Perks selection can be only 'I' or 'L' or 'C' otherwise
error message is returned.
"""
# Call for global variables.
global player_name, perk_inteligence, perk_luck, perk_charisma
# Reset player name
player_name = ''
# Initial variable of wrong name that's used later in function.
wrong_name = False
# Loop that finishes when correct name entered.
while True:
clear_screen()
print('\n\n')
# Output on display for user.
print(Fore.YELLOW + '┌────────────────────', end='')
print('───────────────────┐' + Style.RESET_ALL)
print(Fore.YELLOW + f'│ {MAN_EMOJI} Create ', end='')
print(f' character {WOMAN_EMOJI} │' + Style.RESET_ALL)
print(Fore.YELLOW + '└────────────────────', end='')
print('───────────────────┘' + Style.RESET_ALL)
# Message to display if entered name is not correct.
if wrong_name is True or player_name.isalpha() is True:
print(Fore.RED + 'The name was invalid !', end=' ')
print('Try again !' + Style.RESET_ALL)
# Players name input
print(Fore.BLUE + "What's your name ? Only letters." + Style.RESET_ALL)
player_name = input('')
# If player tries to pass empty string
if player_name == '0':
return
elif len(player_name) > 0 and player_name.isalpha() is True:
player_name = player_name.capitalize()
wrong_perk = False
# Selection of perk
while True:
clear_screen()
print('\n\n\n')
print(Fore.YELLOW + '┌───────────────────────────────', end='')
print('───────────────────────────────────┐' + Style.RESET_ALL)
print(Fore.YELLOW + '│ ' + Fore.GREEN + 'I', end=' ')
print(Fore.WHITE + '- Intelligent - shortens the leng', end='')
print('th of guesed word by 1 letter' + Fore.YELLOW + ' │')
print(Fore.YELLOW + '│ ' + Fore.GREEN + 'L', end=' ')
print(Fore.WHITE + '- Lucky - adds 5 extra guesses', end=' ')
print('to your guess count' + Fore.YELLOW + ' │')
print(Fore.YELLOW + '│ ' + Fore.GREEN + 'C', end=' ')
print(Fore.WHITE + '- Charismatic - reveals an ext', end='')
print('ra letter in guessed word' + Fore.YELLOW + ' │')
print(Fore.YELLOW + '└──────────────────────────────', end='')
print('────────────────────────────────────┘')
print(Fore.BLUE + f"Hello {player_name},The Chosen O", end='')
print("ne, select your perk.Press i,l or c." + Style.RESET_ALL)
# Message thats displayed if selection of perk was incorrect.
if wrong_perk is True:
print(Fore.RED + 'Your choice of perk was', end=' ')
print('invalid, try again !' + Style.RESET_ALL)
# Reading players selection of perk
perk_choice = readchar.readchar()
# Setting perk based on players selection.
if perk_choice.upper() == '0':
print_intro()
elif perk_choice.upper() == 'I':
perk_inteligence = 1
break
elif perk_choice.upper() == 'L':
perk_luck = 5
break
elif perk_choice.upper() == 'C':
perk_charisma = 1
break
else:
wrong_perk = True
# Calling updating function.
update_history()
# Calling function that starts game.
start_game()
return
# Return back to the loop if name was not correct.
else:
wrong_name = True
def clear_screen():
"""
Function clears the terminal (screen).
"""
os.system('clear')
return
def print_intro():
"""
Function prints menu to terminal and asks user for menu choice.
Menu choice is protected against being an empty string.
Menu choice can be only 'S' or 'H' otherwise error message is returned.
"""
wrong_choice = False
while True:
clear_screen()
# Print logo
print(Fore.YELLOW + LOGO + Style.RESET_ALL)
# Print menu on screen.
print(Fore.YELLOW + '┌──────────────────', end='')
print('─────────────────────┐' + Style.RESET_ALL)
print(Fore.YELLOW + f'│ {NUCLEAR_EMOJI} Welcome to Fallout Mi', end='')
print(f'ni - Hangman {NUCLEAR_EMOJI} │' + Style.RESET_ALL)
print(Fore.YELLOW + '└──────────────────', end='')
print('─────────────────────┘' + Style.RESET_ALL)
print(Fore.WHITE + '┌──────────────────', end='')
print('─────────────────────┐' + Style.RESET_ALL)
print(Fore.WHITE + '│ ' + Fore.GREEN + 'S' + Fore.WHITE + ' -', end='')
print(' Start Game │' + Style.RESET_ALL)
print(Fore.WHITE + '│ ' + Fore.GREEN + 'H' + Fore.WHITE + ' -', end='')
print(' High Scores │' + Style.RESET_ALL)
print(Fore.WHITE + '│ ' + Fore.GREEN + 'E' + Fore.WHITE + ' -', end='')
print(' Exit │' + Style.RESET_ALL)
print(Fore.WHITE + '└──────────────────────', end='')
print('─────────────────┘' + Style.RESET_ALL)
print(Fore.WHITE + 'Input ' + Fore.GREEN + '0 ' + Fore.WHITE, end='')
print('at any stage to return to Main menu\n' + Style.RESET_ALL)
# Message to be displayed if selection was wrong.
if wrong_choice is True:
print(Fore.RED + 'Your choice was invalid !' + Style.RESET_ALL)
print('Please make a menu choice, Press S, H or E.')
# Reading players selection
menu_choice = readchar.readchar()
# calling functions based on players selection.
if menu_choice.upper() == 'S':
create_charater()
break
elif menu_choice.upper() == 'H':
display_highscores()
break
elif menu_choice.upper() == 'E':
clear_screen()
print(Fore.YELLOW + '\nThank you for playing.\n' + Style.RESET_ALL)
print('This project was creted as student portfolio\n', end='')
print('project 3 by Tomas Kubancik in 2023.')
sys.exit()
# Return back to this loop if players selection was wrong.
else:
wrong_choice = True
def update_highscore():
"""
Function updates Google sheet with game time
of winner.
"""
# Read current time
finish_time = time.time()
# Calculate game time
play_time = finish_time - launch_time
# Open Google worksheet
highscore_worksheet = SHEET.worksheet("highscores")
# Update highscores worksheet with name and play_time.
highscore_worksheet.append_row([player_name, play_time])
return
def end_of_game():
"""
This function is called when game is over.
Function displays text based on winning/loosing.
Function calls for update_highscore function if user wins.
"""
clear_screen()
# Message to display if player wins.
if game_winner is True:
update_highscore()
display_text(4, 1)
# Message to display if player looses.
elif game_winner is False:
display_text(5, 1)
return
def wait_until_keypressed():
"""
Function waits for any key to be pressed so
user has time to read the text and continue
when ready.
"""
print('\n')
print(Fore.YELLOW + 'Press anything to continue ...' + Style.RESET_ALL)
# Dummy variable only waiting for any key to be pressed.
pause_var = readchar.readchar()
print(Fore.BLACK + pause_var + Style.RESET_ALL)
return
def start_game():
"""
Function starts the game in loop for different levels and difficulty.
"""
# Call global variable.
global game_winner, launch_time
# Loop to iterate through levels.
launch_time = time.time()
for level in levels:
row = 1
# Display text according level.
display_text(row, level)
row = 2
# Display text according level.
display_text(row, level)
# Call word_guess function with different level params.
round_result = word_guess(level + 3, (level * 2) + 10)
# If round lost = game over.
if round_result is False:
game_winner = False
break
row = 3
# Display text according level.
display_text(row, level)
# Call word_guess function with different level params.
round_result = word_guess(level + 3, (level * 2) + 10)
# If round lost = game over.
if round_result is False:
game_winner = False
break
continue
end_of_game()
return
def display_highscores():
"""
This function reads the worksheet highscores and pulls all values.
Sorts out values by time in ascending order and prints first 10.
If no entries 'No entries yet !' message is displayed.
"""
clear_screen()
print(Fore.GREEN + 'Top 10 players\n' + Style.RESET_ALL)
# Open Google worksheet
highscore_worksheet = SHEET.worksheet("highscores")
# Get all values from worksheet
highscore_data = highscore_worksheet.get_all_values()
if len(highscore_data) == 0:
print('No entries yet !')
# Transform of string time in seconds to float time in seconds
highsc_floats = [
[each[0], float(each[1][:5].replace(',', '.'))]
for each in highscore_data
]
# Sort data from worksheet by the second value + ascending
sorted_highscs = sorted(highsc_floats, key=lambda x: x[1], reverse=False)
# For each entry print name and game_time
# Stop at 10th entry
position = 1
for each in sorted_highscs[0:10]:
# Convert float seconds into minutes with only
# two decimal places
time_rounded = round((each[1]) / 60, 2)
# Print position in leaderboard, name of
# player and time in minutes
print(f'{position}. Player: {each[0]} - Time: {time_rounded} minutes.')
position += 1
wait_until_keypressed()
return
def main():
"""
Main program function.
"""
while True:
# Calling global variables
global player_name, perk_inteligence, perk_luck
global perk_charisma, game_winner, launch_time, numbers_picked
# Redefining variables each time game is started
# to avoid multiple perks and incorrect data used
player_name = ''
perk_inteligence = 0
perk_luck = 0
perk_charisma = 0
game_winner = True
launch_time = 0
numbers_picked = []
print_intro()
if __name__ == '__main__':
main()