import random
from typing import Callable, Optional
def validate(answer: str, guess: str):
"""
使用指定的答案,验证一个猜测。
:returns:
一个二元组,
第一个元素是猜测中位置正确的字符数量,即 A 的数量;
第二个元素是猜测中位置不正确,但存在于答案中的字符数量,即 B 的数量。
"""
a = sum(1 for a, g in zip(answer, guess) if a == g)
b = sum(1 for g in guess if g in answer)
return a, b - a
def solve(
answers: list[str],
get_validation: Callable[[str], tuple[int, int]],
on_progress: Callable[[int], None] = lambda _: None
) -> Optional[str]:
"""
求解一个 1A2B 问题。
:param answers: 要求解的范围,即所有可能的答案。
:param get_validation:
用于获取对指定猜测的验证的函数。
该函数接受一个参数,表示指定的猜测,
并返回一个二元组,表示对该猜测的验证结果。
有关返回的详细信息,请参阅 ``validate``。
:param on_progress:
在求解进度更新时调用的函数。
该函数接受一个参数,表示剩余的可能答案数量。
:returns: 如果输入无误,则为求解的答案;否则为 ``None``。
"""
on_progress(len(answers))
if len(answers) < 2:
return answers[0] if answers else None
guess = random.choice(answers)
validation = get_validation(guess)
return solve(
[
it for it in answers
if validate(it, guess) == validation
],
get_validation,
on_progress
)
def get_validation_interactive(guess: str):
"""交互式验证答案。"""
print('Guess:', guess)
return tuple(map(int, input('Validation: ').split()))
all_4_digits = [
it for it in map(str, range(1023, 9877))
if len(set(it)) == len(it)
]
if __name__ == '__main__':
print('@funnysyc: e.g. "Validation: 1 2" for 1A2B')
answer = solve(
all_4_digits,
get_validation_interactive,
lambda size: print(size, 'answer(s) left')
)
print('Answer:', answer or 'does not exist')
文章评论