InternBootcamp/internbootcamp/libs/wordladder/interactive.py
2025-05-23 15:27:15 +08:00

163 lines
6.1 KiB
Python
Executable file

from libs.wordladder.solving.puzzle import Puzzle
from libs.wordladder.solving.solver import Solver
from libs.wordladder.words.dictionary import Dictionary
from libs.wordladder.words.word import Word
from time import perf_counter
APP_NAME = "WordLadder"
PROMPT_PREFIX = APP_NAME + "> "
MINIMUM_WORD_LENGTH = 2
MAXIMUM_WORD_LENGTH = 15
MINIMUM_LADDER_LENGTH = 1
MAXIMUM_LADDER_LENGTH = 20
STEPS = [
"Enter start word: ",
"Enter final word: ",
'Maximum ladder length? [%d-%d, or return]: ' % (MINIMUM_LADDER_LENGTH, MAXIMUM_LADDER_LENGTH)
]
class Interactive(object):
def __init__(self, args):
# not using these yet
self.args = args
self.on_step: int = 0
self.dictionary: Dictionary = None
self.dictionary_load_time: int = 0
self.start_word: Word = None
self.end_word: Word = None
self.maximum_ladder_length: int = -1
def run(self):
again = True
while again:
while self.on_step < len(STEPS):
self._process_input()
self.on_step = 0
self._solve()
print()
# 无须手动again
# inp = input('%sRun again? [y/n]: ' % PROMPT_PREFIX)
# again = inp == "y" or inp == "Y"
# print()
def _process_input(self):
inp = input('%s%s' % (PROMPT_PREFIX, STEPS[self.on_step]))
ok = False
if self.on_step == 0:
ok = self._set_start_word(inp)
elif self.on_step == 1:
ok = self._set_end_word(inp)
elif self.on_step == 2:
ok = self._set_maximum_ladder_length(inp)
if ok:
self.on_step += 1
def _set_start_word(self, inp: str) -> bool:
if len(inp) < MINIMUM_WORD_LENGTH or len(inp) > MAXIMUM_WORD_LENGTH:
print(red(' Please enter a word with between %d and %d characters' % (MINIMUM_WORD_LENGTH, MAXIMUM_WORD_LENGTH)))
return False
self._load_dictionary(len(inp))
self.start_word = self._validate_word(inp)
return self.start_word is not None
def _set_end_word(self, inp: str) -> bool:
if len(inp) != self.dictionary.word_length:
print(red(" Final word length must match start word length!"))
return False
self.end_word = self._validate_word(inp)
return self.end_word is not None
def _validate_word(self, inp: str) -> Word or None: # type: ignore
word: Word = self.dictionary[inp]
if word is None:
print(red(' Word \'%s\' does not exist!' % inp))
return
elif word.is_island:
print(red(' Word \'%s\' is an island word (cannot change single letter to form another word)' % inp))
return
return word
def _set_maximum_ladder_length(self, inp: str) -> bool:
if len(inp) == 0:
print(green(" No answer - assuming auto calc of minimum ladder length"))
self.maximum_ladder_length = -1
return True
try:
inp_int = int(inp)
if inp_int < MINIMUM_LADDER_LENGTH or inp_int > MAXIMUM_LADDER_LENGTH:
raise TypeError
self.maximum_ladder_length = inp_int
return True
except TypeError:
print(red(' Invalid input (please enter a integer between %d and %d)' % (MINIMUM_LADDER_LENGTH, MAXIMUM_LADDER_LENGTH)))
return False
def _load_dictionary(self, word_length: int):
start = perf_counter()
self.dictionary = Dictionary(word_length)
self.dictionary_load_time = (perf_counter() - start) * 1000
def _solve(self):
print('Took %s to load dictionary' % green('%.2fms' % self.dictionary_load_time))
puzzle = Puzzle(self.start_word, self.end_word)
if self.maximum_ladder_length == -1:
start = perf_counter()
min_ladder = puzzle.calculate_minimum_ladder_length()
took = (perf_counter() - start) * 1000
if min_ladder is None:
print(red('Cannot solve \'%s\' to \'%s\' (took %.2fms to determine that)' % (self.start_word, self.end_word, took)))
return
self.maximum_ladder_length = min_ladder
print('Took %s to determine minimum ladder length of %s' % (green('%.2fms' % took), green('%d' % min_ladder)))
solver = Solver(puzzle)
start = perf_counter()
solutions = solver.solve(self.maximum_ladder_length)
took = (perf_counter() - start) * 1000
if len(solutions) == 0:
print(red('Took %.2fms to find no solutions (explored %d solutions)' % (took, solver.explored_count)))
return
print('Took %s to find %s solutions (explored %s solutions)'
% (green('%.2fms' % took), green(len(solutions)), green(solver.explored_count)))
self._display_solutions(solutions)
def _display_solutions(self, solutions):
solutions.sort()
page_start: int = 0
length: int = len(solutions)
while page_start < (length - 1):
inp = input(
'%sList%s solutions? (Enter \'n\' for no, \'y\' or return for next 10, \'all\' for all or how many): '
% (PROMPT_PREFIX, " more" if page_start > 0 else ""))
limit = 10
if inp == "n" or inp == "N":
return
elif inp == "all":
limit = length
elif len(inp) > 0 and inp != "y" and inp != "Y":
try:
inp_int = int(inp)
if inp_int > 0:
limit = inp_int
except TypeError:
limit = 10
for page_start in range(page_start, min(page_start + limit, length)):
print('%d/%d %s' % (page_start + 1, length, green(solutions[page_start])))
TERMINAL_COLOUR_RED = "\u001b[31m"
TERMINAL_COLOUR_GREEN = "\u001b[32m"
TERMINAL_COLOUR_BLACK = "\u001b[0m"
def green(msg):
return TERMINAL_COLOUR_GREEN + str(msg) + TERMINAL_COLOUR_BLACK
def red(msg):
return TERMINAL_COLOUR_RED + str(msg) + TERMINAL_COLOUR_BLACK