Created
February 22, 2021 13:46
-
-
Save danbst/7d1841fc9653c6c2a33ed4bd1915aa43 to your computer and use it in GitHub Desktop.
Trainer for powers of 2, inverse powers of two and indexof in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
import random | |
from datetime import datetime | |
from pprint import pprint | |
import pickle | |
import math | |
class PowerTwo: | |
""" | |
Trainer for powers of two | |
""" | |
def __init__(self, stats): | |
self.value = 0 | |
self.complexity = 1 | |
self.stats = stats | |
def make_prompt(self): | |
selection = [0,1,2,3,4] | |
prompts = ["2**{0}"] | |
if self.complexity >= 2: | |
selection.extend([5,6,7,8]*3) | |
prompts.append("2 to power of {0}") | |
if self.complexity >= 3: | |
selection.extend([9,10,11,12]*5) | |
prompts.append("{0} nth power of 2") | |
self.value = random.choice(selection) | |
return random.choice(prompts).format(self.value), self.value | |
def check_result(self, result): | |
try: | |
result = int(result.strip()) | |
except: | |
return False | |
if result == 2**self.value: | |
return True | |
else: | |
return False | |
def update_complexity(self, avgduration, corrects, wrongs): | |
if avgduration < 3000 and len(wrongs) == 0: | |
self.complexity += 1 | |
return True | |
return False | |
class InversePowerTwo: | |
""" | |
Trainer for guessing which power of two gives a number | |
""" | |
def __init__(self, stats): | |
self.value = 0 | |
self.complexity = 1 | |
self.stats = stats | |
def make_prompt(self): | |
selection = [2**x for x in range(5)] | |
prompts = ["which power of 2 gives {0}"] | |
if self.complexity >= 2: | |
selection.extend([2**x for x in range(5, 11)]*3) | |
prompts.append("integer part of log2({0})") | |
if self.complexity >= 3: | |
selection.extend([2**x for x in range(11, 17)]*5) | |
prompts.append("int(math.log({0}, 2))") | |
self.value = random.choice(selection) | |
return random.choice(prompts).format(self.value), self.value | |
def check_result(self, result): | |
try: | |
result = int(result.strip()) | |
except: | |
return False | |
if result == int(math.log2(self.value)): | |
return True | |
else: | |
return False | |
def update_complexity(self, avgduration, corrects, wrongs): | |
if avgduration < 3000 and len(wrongs) == 0: | |
self.complexity += 1 | |
return True | |
return False | |
class IndexCounter: | |
""" | |
Trainer for fast searching of a number's index. Negative indexes are accepted | |
""" | |
def __init__(self, stats): | |
self.value = 0 | |
self.complexity = 1 | |
self.stats = stats | |
def make_prompt(self): | |
listsize = 5 | |
maxnumber = 10 | |
if self.complexity >= 2: | |
listsize = 10 | |
maxnumber = 10 | |
if self.complexity >= 3: | |
listsize = 13 | |
maxnumber = 99 | |
self.value_list = random.sample(range(maxnumber), k=listsize) | |
self.value = self.value_list[random.randrange(0, len(self.value_list))] | |
return "List: {}\nIndex of {}".format(self.value_list, self.value), (self.value_list, self.value) | |
def check_result(self, result): | |
try: | |
result = int(result.strip()) | |
except: | |
return False | |
#print(self.value, self.value_list, result) | |
if self.value == self.value_list[result]: | |
return True | |
else: | |
return False | |
def update_complexity(self, avgduration, corrects, wrongs): | |
if avgduration < 3000 and len(wrongs) == 0: | |
self.complexity += 1 | |
return True | |
return False | |
def clearscr(): | |
print('\n'*50) | |
class GamePlay: | |
def __init__(self): | |
self.filename = 'stats.txt' | |
self.stats = [] | |
try: | |
with open(self.filename, 'rb') as f: | |
self.stats = pickle.load(f) | |
except OSError: | |
pass | |
except EOFError: | |
pass | |
def save_stats(self): | |
with open(self.filename, 'wb') as f: | |
pickle.dump(self.stats, file=f) | |
def play(self, class_): | |
self.current_stats = [x for x in self.stats if x.get('game_class', None) == class_.__name__] | |
game = class_(self.current_stats) | |
count = 0 | |
checkpoint = 10 | |
while True: | |
clearscr() | |
prompt, value = game.make_prompt() | |
start_time = time.time() | |
result = input(f'[{count}/{checkpoint}] ' + prompt + ": ") | |
duration = int((time.time() - start_time)*1000) | |
check = game.check_result(result) | |
stat = {'game_class': class_.__name__, | |
'time': datetime.now(), | |
'duration': duration, | |
'prompt': prompt, | |
'value': value, | |
'answer': result, | |
'correct': check, | |
} | |
self.current_stats.append(stat) | |
self.stats.append(stat) | |
self.save_stats() | |
if check: | |
print("Correct!") | |
time.sleep(1) | |
else: | |
input("Wrong!\n") | |
count += 1 | |
if count >= checkpoint: | |
count = 0 | |
print(".... STATS .....") | |
checkstats = self.current_stats[-checkpoint:] | |
correctstats = [x for x in checkstats if x['correct']] | |
avgduration = sum([x['duration'] for x in correctstats])/len(correctstats) | |
print(f"Average duration for correct stats: {int(avgduration)} ms") | |
wrongstats = [x for x in checkstats if not x['correct']] | |
if wrongstats: | |
print("WRONGS:") | |
for x in [x for x in checkstats if not x['correct']]: | |
print(x['prompt'], x['answer']) | |
if game.update_complexity(avgduration, correctstats, wrongstats): | |
print() | |
print(f"CONGRATULATIONS! Complexity upgraded to {game.complexity}") | |
input() | |
def main(): | |
gameplay = GamePlay() | |
games = [PowerTwo, InversePowerTwo, IndexCounter] | |
while True: | |
clearscr() | |
# Ask which game, Ctrl-C will exit | |
for i, game in enumerate(games): | |
print(f"{i+1}) {game.__doc__}") | |
try: | |
game_class = games[int(input())-1] | |
except KeyboardInterrupt: | |
break | |
# Actually play, Ctrl-C will finish play and ask which game again | |
try: | |
gameplay.play(game_class) | |
except KeyboardInterrupt: | |
pass | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment