This repository has been archived by the owner on Jan 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
assistant.py
executable file
·365 lines (341 loc) · 15.9 KB
/
assistant.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
#!/usr/bin/env python3
from adaptive_game_module.adaptive_game import Game, NoLevelFoundError
from adaptive_game_module.hint_giver import HintGiver
from adaptive_game_module.level_selector import LevelSelector
from adaptive_game_module.flag_checker import FlagChecker
import subprocess
import os
import sys
import time
def print_help():
"""Print list of arguments that game_loop accepts."""
print("Functions that control the game:")
print("(A)bort - destroys all VMs, resets progress to level 0.")
print("(E)xit - aborts run, then exits the assistant.")
print("(S)tart - starts a new run of the adaptive game, if one isn't in progress.")
print("(N)ext - advances to the next level.")
print("(F)inish - when on the last level, finishes the game and logs end time.")
print("hin(T) - ask for hints, read previously given hints.")
print("(I)nfo - displays info about current game - levels traversed, times...")
print("(L)og - log (save) the information about the game into a file.")
print("Helper functions:")
print("(H)elp - explains all commands on the screen.")
print("(C)heck - checks if prerequisites to run the game are installed.")
def check_prerequisites():
"""Check for the right version of required tools and print the result.
VirtualBox check fails on windows even when VirtualBox is present."""
print("checking Python version:")
if sys.version_info[0] == 3:
if sys.version_info[1] >= 7:
print("OK, Python version higher than 3.7.")
else:
print("NOK, Python version lower than 3.7.")
print("Can't check Vagrant and VirtualBox versions.")
return
else:
print("NOK, Python version lower than 3.") # would sooner crash lol
found = True
print("checking Vagrant version:")
try:
version = subprocess.run(["vagrant", "--version"], capture_output=True, text=True)
except FileNotFoundError:
found = False
print("NOK, Vagrant not found.")
try:
if found:
version_number = version.stdout.split(" ", 1)[1].split(".")
if version_number[0] == "2":
if int(version_number[1]) > 1:
print("OK, Vagrant version higher than 2.2.")
else:
print("NOK, Vagrant version lower than 2.2.")
else:
print("NOK, Vagrant 2 not detected.")
except IndexError:
print("Vagrant was found, but the version string couldn't be parsed.")
print("The string in question: {}".format(version.stdout))
found = False
print("checking Virtualbox version:")
try:
version = subprocess.run(["VBoxManage", "--version"], capture_output=True, text=True)
found = True
except FileNotFoundError:
pass # try no caps
if not found:
try:
version = subprocess.run(["vboxmanage", "--version"], capture_output=True, text=True)
found = True
except FileNotFoundError:
print("Virtualbox not found.")
print("If you are on Windows, this is probably OK.")
print("(You can double check VirtualBox version yourself to be sure)")
print("If you are on Linux, you don't have VirtualBox installed, NOK.")
if found:
version_number = version.stdout.split(".")
if int(version_number[0]) > 5:
print("OK, VirtualBox version higher than 5 detected.")
else:
print("NOK, VirtualBox version lower than 6 detected.")
def write_log(filename, game, hint_giver, flag_checker):
"""Write log from `game` and `hint_giver` to file `filename`
Calls logging methods, which are just yaml dumps."""
try:
game.log_to_file(filename)
hint_giver.log_to_file(filename)
flag_checker.log_to_file(filename)
except OSError:
print("Error encountered while saving game data:")
print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
print("Double check that location {} for the file exists and can be written to.".format(filename))
def give_hint(game, hint_giver):
"""Recap previously given hints, and give player another hint if they want."""
if not game.game_in_progress:
print("Game not started, can't give hints.")
return
if hint_giver.show_taken_hints("level" + str(game.level), game.branch) != []:
print("Hints taken in current level:")
for hint in hint_giver.show_taken_hints("level" + str(game.level), game.branch):
print("{}: {}".format(hint, hint_giver.take_hint("level" + str(game.level), game.branch, hint)))
print("Choose which hint to take (Write a number):")
print("0: (cancel, take no hint)")
i = 1
possible_hints = hint_giver.show_possible_hints("level" + str(game.level), game.branch)
for hint in possible_hints:
print("{}: {}".format(i, hint))
i += 1
hint = input()
try:
hint = int(hint)
if hint == 0:
print("0 selected, no hint taken.")
elif hint >= i:
print("Number too high selected, no hint taken.")
else:
print("{}: {}".format(hint, hint_giver.take_hint(
"level" + str(game.level), game.branch, possible_hints[hint-1])))
except ValueError:
print("Invalid input, no hint taken.")
def starting_quiz(level_selector):
"""Ask the player a few questions, saving known tools to level selector."""
print("Please answer 'yes' if you have ever used a tool or skill before,")
print("or 'no' if you haven't.")
for tool in level_selector.tool_list:
unanswered = True
while (unanswered):
print("Are you familiar with {}?".format(tool))
command = input()
command = command.lower()
if command in ("y", "yes"):
level_selector.add_known_tool(tool)
unanswered = False
elif command in ("n", "no"):
unanswered = False
elif command in ("exit", "stop"):
return False
else:
print("Answer has to be 'yes' or 'no' (or 'exit')")
unanswered = True
return True
def start_game(game, level_selector):
"""Start the game through the game object after doing a starting quiz."""
print("Before the game starts, please fill in a little quiz.")
print("It will help better decide what levels you will play.")
confirmation = starting_quiz(level_selector)
if not confirmation:
print("Game not started, quiz not filled in.")
return
print("Quiz filled in!")
print("Starting the game.")
print("The initial setup may take a while, up to 20 minutes.")
if game.start_game():
print("If you do not see any error messages above,")
print("then the game was started succesfully!")
print("You can start playing now.")
else:
print("Game was not started, it's already in progress!")
print("To start over, please run `abort` first.")
def abort_game(game, hint_giver, flag_checker):
"""Abort the game and reset all progress, log current game in a file."""
try:
if os.path.isdir("logs"):
write_log("logs/aborted_game" + str(time.time()), game, hint_giver, flag_checker)
except OSError as err:
# print("Failed to save game log.")
# print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
pass
print("Aborting game, deleting all VMs.")
game.abort_game()
hint_giver.restart_game()
print("Game aborted, progress reset, VMs deleted.")
def player_logging(game, hint_giver, flag_checker):
"""Player-initiated log of the game. Always saves to logs/game_log.yml"""
if os.path.exists("logs/game_log.yml"):
print("It appears that there is already a saved game log.")
print("Write 'yes' to overwrite it.")
confirmation = input()
confirmation = confirmation.lower()
if (confirmation == "yes"):
print("Overwriting file...")
with open("logs/game_log.yml", 'w'):
pass
write_log("logs/game_log.yml", game, hint_giver, flag_checker)
else:
print("File not overwritten.")
else:
print("Writing file...")
write_log("logs/game_log.yml", game, hint_giver, flag_checker)
def finish_game(game):
"""Mark game as finished, inform player if that's impossible."""
if game.finish_game():
print("Game finished, total time saved!")
elif (not game.game_in_progress and not game.game_finished):
print("Can't finish game, game was not started yet.")
elif (not game.game_in_progress and game.game_finished):
print("Can't finish game, game was already finished earlier.")
else:
print("Could not finish game.")
print("Make sure you are on the last level!")
def try_next_level(game, level_selector):
if game.next_level_exists():
print("Going to set up level {}".format(game.level + 1))
if game.next_level_is_forked():
next_level = level_selector.next_level(game.level, game.running_time())
print("Setting up next level: {}".format(next_level))
game.next_level(next_level)
else:
game.next_level()
print("Level deployed.")
print("If you don't see any errors above, you can continue playing.")
if game.level == 5: # TODO: maybe remove hardcode
print("This is the last level of the game.")
else:
print("No next levels found -- you finished the game!")
print("Make sure to run (F)inish and (L)og your progress before exiting.")
def check_flag(level, flag_checker):
print("To continue, please enter the flag you found:")
print("(case sensitive)")
flag = input()
if flag_checker.check_flag("level"+str(level), flag):
print("Flag is correct!")
return True
print("Flag is incorrect.")
print("Double check your spelling, or use hints if you don't know how to proceed.")
return False
def game_loop():
"""Interactively assist the player with playing the game.
Transform inputs from user into method calls.
Possible inputs
---------------
Functions that control the game:
(A)bort - destroys all VMs, resets progress to level 0.
(E)xit - aborts run, then exits the assistant.
(S)tart - starts a new run of the adaptive game, if one isn't in progress.
(N)ext - advances to the next level.
hin(T) - ask for hints, read previously given hints.
(F)inish - when on the last level, finishes the game and logs end time.
(I)nfo - displays info about current game - levels traversed, times...
(L)og - log (save) the information about the game into a file.
Helper functions:
(H)elp - explains all commands on the screen.
(C)heck - checks if prerequisites to run the game are installed.
"""
if not os.path.isdir("logs"):
try:
os.mkdir("logs")
except FileExistsError:
pass #directory already exists, all OK
except OSError as err:
print("Error encountered while creating the /logs subfolder for save data:")
print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
print("If not fixed, saving game data might not be possible.")
try:
game = Game("resources/levels.yml", "../game")
except OSError as err:
print("Error encountered while setting up the game object.")
print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
print("(Most likely, `resources/levels.yml` file couldn't be read.")
print("Make sure it is in the folder, and readable.")
try:
hint_giver = HintGiver("resources/hints.yml")
except OSError as err:
print("Error encountered while setting up the hint giver.")
print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
print("(Most likely, `resources/hints.yml` file couldn't be read.")
print("Make sure it is in the folder, and readable.")
try:
level_selector = LevelSelector("resources/tools.yml", "resources/level_requirements.yml")
except OSError as err:
print("Error encountered while setting up the level selector.")
print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
print("(Most likely, `resources/level_requirements.yml` file couldn't be read.")
print("Make sure it is in the folder, and readable.")
try:
flag_checker = FlagChecker("resources/level_keys.yml")
except OSError as err:
print("Error encountered while setting up the flag checker.")
print("Error number: {}, Error text: {}".format(err.errno, err.strerror))
# print("(Most likely, `resources/level_requirements.yml` file couldn't be read.")
# print("Make sure it is in the folder, and readable.")
print("Welcome to the adaptive game assistant.")
print("Basic commands are:")
print("(S)tart, (N)ext, (H)elp, (C)heck, (E)xit")
while True:
try:
print("Waiting for your input:")
command = input()
command = command.lower()
if command in ("a", "abort", "(a)bort"):
abort_game(game, hint_giver, flag_checker)
elif command in ("e", "exit"):
abort_game(game, hint_giver, flag_checker)
print("Exiting...")
return
elif command in ("s", "start", "(s)tart"):
start_game(game, level_selector)
elif command in ("n", "next", "(n)ext"):
try:
if game.level == 0:
print("Can't continue, (S)tart the game first!")
elif game.level == 5:
print("Can't continue, you are on the last level!")
print("Make sure to run (F)inish and (L)og your progress before exiting.")
else:
if check_flag(game.level, flag_checker):
try_next_level(game, level_selector)
except NoLevelFoundError as err:
print("Error encountered: {}".format(err))
elif command in ("f", "finish", "(f)inish"):
if (not game.game_in_progress and not game.game_finished):
print("Can't finish game, game was not started yet.")
elif (not game.game_in_progress and game.game_finished):
print("Can't finish game, game was already finished earlier.")
else:
if check_flag(game.level, flag_checker):
finish_game(game)
elif command in ("i", "info", "information", "(i)nfo", "(i)nformation"):
game.print_info()
elif command in ("h", "help", "(h)elp"):
print_help()
elif command in ("c", "check", "(c)heck"):
check_prerequisites()
elif command in ("l", "log", "(l)og"):
player_logging(game, hint_giver, flag_checker)
elif command in ("t", "hint", "hin(t)"):
give_hint(game, hint_giver)
else:
print("Unknown command. Enter another command or try (H)elp.")
except EOFError:
print("Unknown command. Enter another command or try (H)elp.")
print("If you want to exit the assistant, please use the `exit` command.")
except KeyboardInterrupt:
print("You sent a keyboard interrupt to the program.")
print("Would you like to exit?")
print("This will NOT save your log or end the game. yes/no")
confirmation = input()
confirmation = confirmation.lower()
if confirmation in ("y", "yes"):
return
else:
print("Not exiting. Continuing normal operation.")
if __name__ == "__main__":
game_loop()