Узнайте, как реализовать валидацию игрового поля для словесных головоломок на Python. Проверяйте смежность, дубликаты и покрытие. Начните прямо сейчас!
Словесные головоломки кажутся простыми: игроки соединяют буквы, составляют слова и очищают поле. Но под капотом скрывается сложная логика. Как проверить, что выбранный путь корректен? Смежны ли плитки? Не используется ли плитка повторно? Составляет ли путь допустимое слово? Можно ли честно завершить поле? В этой статье мы разберём слой валидации для словесных головоломок на Python.
Плитка должна хранить не только букву, но и координаты. Без них невозможно проверить правила перемещения.
# Класс плитки с позицией и буквой
class Tile:
def __init__(self, row: int, col: int, letter: str):
self.row = row
self.col = col
self.letter = letter
def __repr__(self):
return f"Tile({self.row}, {self.col}, '{self.letter}')"Координаты row и col критичны для всех проверок движения.
Первое правило — перемещение. Если разрешены диагонали, плитка может двигаться к любому из восьми соседей.
# Проверка смежности с учётом диагоналей
def is_adjacent(a: Tile, b: Tile) -> bool:
dx = abs(a.row - b.row)
dy = abs(a.col - b.col)
# Возвращаем True, если плитки соседние (включая диагонали)
return dx <= 1 and dy <= 1 and not (dx == 0 and dy == 0)Эта функция отвергает ту же плитку и принимает горизонтальных, вертикальных и диагональных соседей. Если головоломка разрешает только ортогональное движение, правило меняется:
# Проверка только ортогональной смежности
def is_orthogonally_adjacent(a: Tile, b: Tile) -> bool:
dx = abs(a.row - b.row)
dy = abs(a.col - b.col)
# Сумма разниц должна быть равна 1 (только вверх/вниз/влево/вправо)
return dx + dy == 1Разница небольшая, но она кардинально меняет ощущение от головоломки. Диагонали дают больше свободы, ортогональное движение — строгие пути.
В словесных головоломках плитка не может использоваться дважды в одном пути. Простейший способ — хранить координаты в множестве.
# Проверка на дубликаты плиток в пути
def has_duplicate_tiles(path: list) -> bool:
seen = set()
for tile in path:
key = (tile.row, tile.col) # Используем кортеж как ключ
if key in seen:
return True
seen.add(key)
return FalseЭто предотвращает циклы, когда игрок возвращается на уже использованную плитку.
Когда проверки смежности и дубликатов готовы, валидатор пути становится простым.
# Полная валидация пути
def is_valid_path(path: list) -> bool:
if len(path) == 0:
return False
if has_duplicate_tiles(path):
return False
# Проверяем каждую пару соседних плиток
for i in range(1, len(path)):
if not is_adjacent(path[i-1], path[i]):
return False
return TrueЭтот код проверяет только движение. Он не решает, образует ли путь настоящее слово — это отдельная задача.
Путь можно превратить в слово, соединив буквы плиток.
# Преобразование пути в строку
def path_to_word(path: list) -> str:
return ''.join(tile.letter for tile in path)Затем слово проверяется по списку допустимых.
# Проверка, является ли путь принятым словом
def is_accepted_word(path: list, accepted_words: set) -> bool:
word = path_to_word(path).lower()
return word in accepted_wordsДля головоломок я рекомендую использовать курированный список ответов вместо большого словаря. Большой словарь может породить множество случайных допустимых слов, а курированный список даёт дизайнеру контроль над решением.
Некоторые головоломки требуют, чтобы финальное решение покрывало все плитки. Нужно знать, сколько уникальных плиток занято всеми решёнными путями.
# Получение множества покрытых плиток
def get_covered_tiles(paths: list) -> set:
covered = set()
for path in paths:
for tile in path:
covered.add((tile.row, tile.col))
return coveredПроверка завершённости поля:
# Проверка, завершено ли поле
def is_board_complete(paths: list, required_tile_count: int) -> bool:
return len(get_covered_tiles(paths)) == required_tile_countЭто полезно, потому что головоломка может содержать допустимые слова, но оставаться незавершённой. Честное поле не должно оставлять невозможных или запутанных плиток.
Если каждая плитка должна принадлежать только одному финальному ответу, пути не должны пересекаться.
# Проверка на пересечение путей
def paths_overlap(paths: list) -> bool:
used = set()
for path in paths:
for tile in path:
key = (tile.row, tile.col)
if key in used:
return True
used.add(key)
return FalseЭта проверка важна при автоматической генерации головоломок. Генератор может случайно создать поля, где несколько путей борются за одну плитку.
Для первой версии не стоит начинать со случайных букв. Случайные поля легко создавать, но сложно делать честными. Более контролируемый подход:
Это не идеальный генератор, но полезная отправная точка. Для браузерных игр маленький надёжный генератор лучше большого ненадёжного.
Бесконечный случайный режим звучит захватывающе, но контроль качества усложняется. Ежедневные головоломки проще, потому что игра требует только одного хорошего поля в день. Это позволяет добавить дополнительные проверки:
Для первой версии ежедневный режим часто является лучшим выбором.
Сложная часть словесной головоломки — не отрисовка сетки, а валидация. Прежде чем добавлять аккаунты, таблицы лидеров, анимации или функции обмена, убедитесь, что основные правила работают:
Как только эти правила заработают, игру станет гораздо легче улучшать. Я использовал этот подход в своём эксперименте с браузерной головоломкой. Главный урок был прост: сначала создайте валидатор, потом стройте игру вокруг него. Попробуйте реализовать описанные функции на Python прямо сейчас — начните с класса Tile и проверки смежности.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →