Как создать игру «Виселица» на Python

Виселица — простая и обучающая текстовая игра, отлично подходящая для практики Python. В этой статье вы найдёте понятное объяснение правил, полный разбор исходного кода, улучшенную реализацию с обработкой ошибок, тест-кейсы, варианты реализации (консоль, GUI, веб) и чек-лист для релиза.
Как играть в Виселицу
Виселица — это игра отгадывания слова по буквам. Программа выбирает слово и показывает набор дефисов или подчёркиваний, по одному на каждую букву. Игрок предлагает буквы по одной. Если буква есть в слове — она открывается во всех позициях. Если её нет — количество оставшихся попыток уменьшается и постепенно рисуется «подвешенный» человечек в ASCII.
Стандартный сценарий:
- Игроку даётся фиксированное число попыток (в примере — 7).
- Если игрок полностью открыл слово до исчерпания попыток — он выигрывает.
- Если попытки кончаются — программа доканчивает рисунок висельника и игрок проигрывает.
Совет: храните список слов в текстовом файле, по одному слову на строку. Файл можно править вручную или генерировать автоматически.
Исходный код — базовая версия
Ниже приведён исходный код из примера. Он показывает основную логику: выбор слова, показ части букв, проверка ввода, отрисовка состояния и основной цикл игры.
import random
def get_random_word_from_wordlist():
wordlist = []
with open("hangman_wordlist.txt", 'r') as file:
wordlist = file.read().split("\n")
word = random.choice(wordlist)
return worddef get_some_letters(word):
letters = []
temp = '_' * len(word)
for char in list(word):
if char not in letters:
letters.append(char)
character = random.choice(letters)
for num, char in enumerate(list(word)):
if char == character:
templist = list(temp)
templist[num] = char
temp = ''.join(templist)
return tempdef draw_hangman(chances):
if chances == 6:
print("________ ")
print("| | ")
print("| ")
print("| ")
print("| ")
print("| ")
elif chances == 5:
print("________ ")
print("| | ")
print("| 0 ")
print("| ")
print("| ")
print("| ")
elif chances == 4:
print("________ ")
print("| | ")
print("| 0 ")
print("| / ")
print("| ")
print("| ")
elif chances == 3:
print("________ ")
print("| | ")
print("| 0 ")
print("| /| ")
print("| ")
print("| ")
elif chances == 2:
print("________ ")
print("| | ")
print("| 0 ")
print("| /|\ ")
print("| ")
print("| ")
elif chances == 1:
print("________ ")
print("| | ")
print("| 0 ")
print("| /|\ ")
print("| / ")
print("| ")
elif chances == 0:
print("________ ")
print("| | ")
print("| 0 ")
print("| /|\ ")
print("| / \ ")
print("| ")def start_hangman_game():
word = get_random_word_from_wordlist()
temp = get_some_letters(word)
chances = 7
found = False
while True:
if chances == 0:
print(f"Sorry! You Lost, the word was: {word}")
print("Better luck next time")
break
print("=== Guess the word ===")
print(temp, end='')
print(f"\t(word has {len(word)} letters)")
print(f"Chances left: {chances}")
character = input("Enter the character you think the word may have: ")
if len(character) > 1 or not character.isalpha():
print("Please enter a single alphabet only")
continue
else:
for num, char in enumerate(list(word)):
if char == character:
templist = list(temp)
templist[num] = char
temp = ''.join(templist)
found = True
if found:
found = False
else:
chances -= 1
if '_' not in temp:
print(f"\nYou Won! The word was: {word}")
print(f"You got it in {7 - chances} guess")
break
else:
draw_hangman(chances)
print()print("===== Welcome to the Hangman Game =====")
while True:
choice = input("Do you wanna play hangman? (yes/no): ")
if 'yes' in choice.lower():
start_hangman_game()
elif 'no' in choice.lower():
print('Quitting the game...')
break
else:
print("Please enter a valid choice.")
print("\n")Пояснение частей кода
- get_random_word_from_wordlist(): читает файл со словами и случайно выбирает одно слово. В исходнике использование split(“\n”) работает, но может привести к пустым строкам в списке — это стоит обработать.
- get_some_letters(word): создаёт строку из подчёркиваний длиной слова и подставляет одну случайно выбранную букву в те позиции, где она встречается.
- draw_hangman(chances): выводит ASCII-рисунок в зависимости от оставшихся попыток.
- start_hangman_game(): основной цикл: ввод буквы, валидация, обновление состояния, проверка победы/поражения.
Улучшения и надёжная версия кода
Исходный код демонстрационный. Ниже — улучшенная реализация с учётом типичных проблем:
- удаление пустых строк из словаря;
- нормализация регистра (работаем в нижнем регистре);
- обработка неанглийских букв и пробелов в слове;
- сохранение уже введённых букв и подсказка пользователю;
- защита от выбора пустого слова.
import random
import sys
WORDLIST_PATH = "hangman_wordlist.txt"
MAX_CHANCES = 7
def load_wordlist(path):
with open(path, 'r', encoding='utf-8') as f:
words = [w.strip() for w in f.readlines()]
# Отфильтровать пустые строки
words = [w for w in words if w]
if not words:
raise ValueError("Wordlist is empty")
return words
def pick_word(words):
return random.choice(words).lower()
def reveal_initial_letters(word, reveal_count=1):
# Собираем уникальные буквы, исключая пробелы
letters = [c for c in dict.fromkeys(word) if c != ' ']
reveal_count = min(len(letters), max(1, reveal_count))
chars = random.sample(letters, reveal_count)
temp = ['_' if c != ' ' else ' ' for c in word]
for i, c in enumerate(word):
if c in chars:
temp[i] = c
return ''.join(temp)
def draw_hangman(chances):
stages = [
("________ \n| | \n| 0 \n| /|\\ \n| / \\ \n| ", 0),
("________ \n| | \n| 0 \n| /|\\ \n| / \n| ", 1),
("________ \n| | \n| 0 \n| /|\\ \n| \n| ", 2),
("________ \n| | \n| 0 \n| /| \n| \n| ", 3),
("________ \n| | \n| 0 \n| / \n| \n| ", 4),
("________ \n| | \n| 0 \n| \n| \n| ", 5),
("________ \n| | \n| \n| \n| \n| ", 6),
]
idx = min(max(0, MAX_CHANCES - 1 - chances), len(stages)-1)
print(stages[idx][0])
def start_hangman_game(words):
word = pick_word(words)
temp = reveal_initial_letters(word, reveal_count=1)
chances = MAX_CHANCES
guessed = set()
while True:
if chances <= 0:
print(f"Вы проиграли. Загаданное слово: {word}")
break
print("=== Отгадайте слово ===")
print(' '.join(temp), f"\t(букв: {len(word)})")
print(f"Оставшиеся попытки: {chances}")
print(f"Угаданные буквы: {', '.join(sorted(guessed))}" if guessed else "Угаданных букв ещё нет")
character = input("Введите букву: ").strip().lower()
if len(character) != 1 or not character.isalpha():
print("Введите одну букву (a–z или локальную букву).")
continue
if character in guessed:
print("Эту букву вы уже вводили.")
continue
guessed.add(character)
if character in word:
temp = ''.join([ch if ch == character or temp[i] != '_' else '_' if ch == ' ' else '_' for i, ch in enumerate(word)])
# Правильнее обновлять по индексам:
templ = list(temp)
for i, ch in enumerate(word):
if ch == character:
templ[i] = ch
temp = ''.join(templ)
else:
chances -= 1
if '_' not in temp:
print(f"Поздравляем! Вы выиграли. Слово: {word}")
print(f"Попыток потрачено: {MAX_CHANCES - chances}")
break
else:
draw_hangman(chances)
print()
if __name__ == '__main__':
try:
words = load_wordlist(WORDLIST_PATH)
except Exception as e:
print(f"Ошибка при загрузке словаря: {e}")
sys.exit(1)
print("===== Добро пожаловать в Виселицу! =====")
while True:
choice = input("Играть? (да/нет): ").strip().lower()
if 'да' in choice:
start_hangman_game(words)
elif 'нет' in choice:
print('Выход...')
break
else:
print("Пожалуйста, ответьте 'да' или 'нет'.")
print()Почему стоит улучшить базовую реализацию — распространённые проблемы
- Пустые строки в файле со словами могут привести к исключениям при выборе слова.
- Разный регистр букв (A vs a) может мешать угадыванию — лучше приводить всё к lower().
- Слова с пробелами и дефисами требуют специальной обработки (не заменять их подчёркиваниями).
- Повторный ввод одной и той же буквы должен предупреждать игрока и не отнимать попытку.
Критерии приёмки
- Игра загружается без ошибок при наличии корректного файла слов.
- Пользователь может вводить буквы в любом регистре; сравнение нечувствительно к регистру.
- Односимвольный ввод проверяется; многосимвольный ввод отклоняется с подсказкой.
- В слове с пробелами пробелы сразу видны и не считаются за буквы для отгадывания.
- Повторный ввод уже названной буквы не уменьшает количество попыток.
Тестовые случаи и сценарии (acceptance)
- Положительный: файл с 100 словами, игрок отгадывает слово за 3 хода.
- Отрицательный: файл пустой — программа прекращает работу с понятным сообщением.
- Граничный: слово из одной буквы — пользователь должен выиграть/проиграть корректно.
- Валидация: ввод цифр, символов или строки длиной >1 — система просит ввести корректную букву.
- Регрессия: повторный ввод буквы — не отнимается попытка.
Альтернативные подходы
- Консольная версия (как выше) — быстро и просто.
- GUI: tkinter или PyQt — удобно для кнопок и кликов, полезно в обучении детей.
- Веб-версия: Flask/FastAPI + HTML/JS — позволяет играть в браузере и делиться ссылкой.
- Telegram-бот: игра в чате через Bot API.
Краткая матрица сравнения:
- Консоль: быстро, минимально зависимостей.
- Tkinter: локальная GUI, просто, встроен в стандартную библиотеку.
- Pygame: гибкость графики, подходит для сложных визуализаций.
- Flask: доступ через сеть, требует HTML/JS.
Роль‑ориентированные чек‑листы
Разработчик:
- Написать модуль загрузки слов и тесты на него.
- Обработать исключения файла.
- Реализовать корректную валидацию ввода.
QA:
- Проверить критерии приёмки.
- Провести ручные и автоматические тесты ввода/вывода.
Преподаватель/лектор:
- Подготовить набор слов разного уровня сложности.
- Дать задания на расширение (GUI, сеть, статистика игроков).
Ментальные модели и эвристики при разработке
- Разделяй ответственность: загрузка слов, логика открывания букв, UI/взаимодействие.
- Минимизируй побочные эффекты в функциях (чистые функции легче тестировать).
- Отталкивайся от малого рабочего прототипа, затем улучшай обработку ошибок.
Галерея пограничных случаев
- Слова с апострофом или дефисом: решите заранее, открываете их или нет.
- Юникодные буквы (например, русские): убедитесь, что файл читается в utf-8.
- Многосимвольные вводы и ввод пробелов: запретить или поддержать подсказкой.
Краткий глоссарий
- Wordlist: файл со словами, по одному слову на строку.
- Reveal: открытие букв в текущей маске слова.
- ASCII-рисунок: последовательность строк, визуально показывающая прогресс проигрыша.
Факты для быстрого ориентирования
- Стандартное число попыток в примерах — 7.
- Рекомендуемый формат файла со словами — UTF-8, одна запись на строку.
- Начальное раскрытие — 1 случайная уникальная буква (можно менять).
Краткий playbook запуска
- Подготовьте файл hangman_wordlist.txt в той же папке и убедитесь в его кодировке UTF-8.
- Запустите скрипт: python hangman.py
- Выберите играть и следуйте подсказкам.
- Для веб/GUI версии — следовать отдельному чек-листу развертывания.
Советы по локализации и адаптации
- Для русскоязычного списка слов убедитесь, что все слова в файле в нижнем регистре и без лишних пробелов.
- Если вы добавляете GUI, подписи кнопок и подсказки переведите и протестируйте на целевой аудитории.
- Для обучения можно сделать уровни сложности: ограничить/увеличить количество попыток или выдавать подсказки.
Безопасность и приватность
Игра работает локально и не собирает личных данных. При добавлении сетевого режима убедитесь, что не логируете персональные данные игроков и применяете базовую проверку входных данных.
Вывод
Виселица — отличная стартовая задача для практики Python и проект для демонстрации принципов модульного кода, валидации ввода и тестирования. Начните с консольной версии, затем расширяйте функционал: лучший опыт достигается через итерации и тестирование.
Wichtig: настройте словарь и правила под вашу аудиторию — дети, взрослые или международная группа требуют разных наборов слов и подсказок.
Похожие материалы
Как экономить мобильные данные в Apple Music
Персональные результаты Google Assistant на блокировке
Настройка уведомлений Outlook: отключить и адаптировать
Добавить дату и время в Google Sheets
Таймер Помодоро на Python с Tkinter