Чекер для судоку. 🔢
Мы хотим научить модель решать Судоку и применить для этого
метод Tree of Thoughts
. Для этого нам необходимо написать класс-проверщик
(Checker
), который будет проверять в правильном направлении модель строит
рассуждения или нет.
Для начала посмотрим на промпт для судоку 4 х 4:
sudoku_puzzle = "3,*,*,2|1,*,3,*|*,1,*,3|4,*,*,1"
sudoku_solution = "3,4,1,2|1,2,3,4|2,1,4,3|4,3,2,1"
problem_description = f"""
{sudoku_puzzle}
- Это головоломка Судоку размером 4x4.
- Символ * обозначает ячейку, которую тебе нужно заполнить.
- Символ | разделяет строки.
- На каждом шаге замени одну или несколько * цифрами от 1 до 4.
- Напиши текущее состояние пазла с добавленными цифрами
- Ни в одной строке, столбце или 2x2 подсетке не должно быть одинаковых цифр.
- Сохраняй полученные цифры из предыдущих успешных мыслей на своих местах.
- Каждая мысль может быть промежуточным или окончательным решением.
""".strip()
print(problem_description)
Как можно понять из промпта, мы хотим, чтобы на каждом шаге заменялась одна или несколько звездочек . Но при этом модели нужно понимать в верную ли сторону произошла замена. Может быть предыдущий шаг был неверный, и стоит вернуться с этой ветки размышлений на шаг назад и попробовать заменить другой пропуск или заменить пропуск на другое число. На помощь придут ToTChecker и ThoughtValidity.
Немного пояснений:
Нам нужно взять поледнее рассуждение (мысль), они хранятся в переменной thoughts.
Далее мы проверим соответствует ли предложенная мысль верному ответу. Т.е. если мысль
равна переменной sudoku_solution, значит мы нашли верное решение. В таком случае
чекер должен вернуть ThoughtValidity.VALID_FINAL
.
Если наше решение не содержит ошибок, но при этом в нем есть незаполненные клетки, мы
вернем ThoughtValidity.VALID_INTERMEDIATE
. Проверим, является ли наше
решение частичным с помощью регулярных выражений.
Символы "*" и "|" являются специальными. "*" нам нужно будет заменить на специальный символ в регулярных выражениях, обозначающий любой одиночный символ, а "|" должен восприниматься в регулярном выражении не как специальный символ, а как элемент в нашей строке. Т.е. если мы оставили клетки незаполненными, при этом все значения в заполненных нами клетках совпадают с правильным решением, то наше решение будет матчится регуляркой с правильным решением.
Если в какой-то клетке содержится неверная цифра - мы возвращаем
ThoughtValidity.INVALID
. Мы можем считать, что решение неверное, если оно
не является верным или частичным решением.
Ноутбук с тестами для проверки работы чекера ссылка.
Выберете одну или несколько верных реализаций нашего класса.
A.
class MyChecker(ToTChecker):
def evaluate(
self, problem_description: str, thoughts: Tuple[str, ...] = ()
) -> ThoughtValidity:
if eval(thoughts) == 10:
return ThoughtValidity.VALID_FINAL
elif eval(thoughts) in [9,8,7,6]:
return ThoughtValidity.VALID_INTERMEDIATE
else:
return ThoughtValidity.INVALID
B.
import re
class MyChecker(ToTChecker):
def evaluate(
self, problem_description: str, thoughts: Tuple[str, ...] = ()
) -> ThoughtValidity:
last_thought = thoughts[-1]
clean_solution = last_thought.replace(" ", "").replace('"', "")
regex_solution = clean_solution.replace("*", ".").replace("|", "\\|")
if sudoku_solution in clean_solution:
return ThoughtValidity.VALID_FINAL
elif re.search(regex_solution, sudoku_solution):
return ThoughtValidity.VALID_INTERMEDIATE
else:
return ThoughtValidity.INVALID
C.
import re
class MyChecker(ToTChecker):
def evaluate(
self, problem_description: str, thoughts: Tuple[str, ...] = ()
) -> ThoughtValidity:
solution = thoughts[-1].replace(" ", "").replace('"', "")
if sudoku_solution in solution:
return ThoughtValidity.VALID_FINAL
elif re.search(solution, sudoku_solution):
return ThoughtValidity.VALID_INTERMEDIATE
else:
return ThoughtValidity.INVALID
D.
import re
class MyChecker(ToTChecker):
def evaluate(
self, problem_description: str, thoughts: Tuple[str, ...] = ()
) -> ThoughtValidity:
last_thought = thoughts[-1]
clean_solution = last_thought.replace(" ", "").replace('"', "")
regex_solution = clean_solution.replace("*", ".")
if sudoku_solution in clean_solution:
return ThoughtValidity.VALID_FINAL
elif re.search(regex_solution, sudoku_solution):
return ThoughtValidity.VALID_INTERMEDIATE
else:
return ThoughtValidity.INVALID