Автор оригинала: Arun Ravindran.
Недавно Данвер написал о блестящей реализации игры «Палач» всего на трех строках Python. Но сокращенный код может быть трудным для понимания. В этом посте мы перепишем его код в более идиоматическом стиле Python 3.
Предупреждение о спойлере: если вы предпочитаете веселое упражнение, попробуйте сначала сделать его самостоятельно, а затем сравните с моей версией.
Реализация Данвера умело использует системный словарь (предустановленный в Linux и Mac, но мы увидим, как он может работать и в Windows) для выбора случайного слова. Он также отображает аккуратный палач в стиле ASCII на каждом шагу. За исключением нескольких незначительных улучшений, я попытался сохранить суть оригинала Данвера в моем переписанном ниже тексте.
# -*- coding: utf-8 -*-
"""A simple text-based game of hangman
A re-implementation of Hangman 3-liner in more idiomatic Python 3
Original: http://danverbraganza.com/writings/hangman-in-3-lines-of-python
Requirements:
A dictionary file at /usr/share/dict/words
Usage:
$ python hangman.py
Released under the MIT License. (Re)written by Arun Ravindran http://arunrocks.com
"""
import random
DICT '/usr/share/dict/words'
chosen_word random.choice(open(DICT).readlines()).upper().strip()
guesses set()
scaffold """
0:
print("You win!")
else:
print("You lose!\n{}\nHANGED!".format(scaffold.format(*man)))
print("Word was", chosen_word)
Вы также можете просмотреть или загрузить суть на Github .
Как это устроено
Код отлично использует встроенную структуру данных Python set
. и строковая функция format
.
Сразу после утомительного модуля docstring и оператора import
мы инициализируем следующие переменные:
- DICT : путь к файлу словаря.
- selected_word : случайно выбранная строка (также известная как слово) из DICT.
- догадки : набор букв, которые пользователь угадал.
- каркас : палач, нарисованный в кодировке ASCII в виде строки формата.
- man : отсутствуют символы ASCII для
scaffold
- guesses_left : оставшиеся угадывания букв.
Цикл while
, условия выхода которого будут объяснены в ближайшее время, является нашим основным игровым циклом. Первая функция print
напоминает вам уже введенные вами догадки (в алфавитном порядке) и количество оставшихся догадок. Следующая функция print
показывает ASCII-палача с достаточным количеством недостающих частей от man
на основе количества ошибочных предположений.
Наконец, третья функция print
показывает selected_word
, показывая только символы из ваших предположений
и заменяя остальные символы подчеркиванием. В следующей строке мы читаем символ или даже несколько символов от пользователя, прописываем их в верхнем регистре, отфильтровываем неалфавиты и обновляем набор guesses
, все в одной строке:
guesses.update(c.upper() for c in input() if str.isalpha(c))
Далее мы подсчитываем количество ошибочных букв. Как показано на схеме ниже, в этом разница наборов guesses
и selected_words
(на схеме буквы – C, M, T). Мы находим, сколько осталось догадок, вычитая набор букв man
из букв, которые мы ошиблись. Функция max
гарантирует, что мы не опустимся ниже нуля.
В конце концов, вы можете выиграть, если ваши догадки охватывают все правильные буквы. На языке наборов это происходит, когда набор guesses
становится достаточно большим, чтобы превратиться в супернабор набора selected_word
. На это указывает пурпурный пунктирный круг. Следовательно, цикл while
будет продолжаться до тех пор, пока это не произойдет или пока у нас не закончатся оставшиеся предположения.
Наконец, мы показываем сообщение о выигрыше или проигрыше в зависимости от того, закончились ли предположения. В любом случае мы показываем начальное selected_word
.
IMPRO_EMEN_S
Для сравнения, вот оригинальный трехстрочный текст Данвера:
license, chosen_word, guesses, scaffold, man, guesses_left 'https://opensource.org/licenses/MIT', ''.join(filter(str.isalpha, __import__('random').choice(open('/usr/share/dict/words').readlines()).upper())), set(), \n| |\n| {3} {0} {5}\n| {2}{1}{4}\n| {6} {7}\n| {8} {9}\n|', list('OT-\\-//\\||'), 10
while not all(letter in guesses for letter in chosen_word) and guesses_left: _, guesses_left map(guesses.add, filter(str.isalpha, raw_input('%s(%s guesses left)\n%s\n%s:' % (','.join(sorted(guesses)), guesses_left, scaffold.format(*(man[:10-guesses_left] + [' '] * guesses_left)), ' '.join(letter if letter in guesses else '_' for letter in chosen_word))).upper())), max((10 - len(guesses - set(chosen_word))), 0)
print 'You', ['lose!\n' + scaffold.format(*man), 'win!'][bool(guesses_left)], '\nWord was', chosen_word
Понятно? Хорошо, может быть, нужно немного пояснить. Python – это язык с поддержкой пробелов, разработанный для максимальной читаемости. Так что для написания однострочников требуется множество сомнительных уловок, некоторые из которых я сам использовал в своем посте «Однострочные игры на Python». На этот раз мы идем в обратном направлении.
Наверное, наиболее заметными изменениями являются:
- Лицензия : упоминается в строке документа, а не в виде строки.
- Импорт : больше не нужно использовать внутреннюю функцию
__import__
. - Каркас : многострочная строка, а не однострочная.
- Печать : преобразовано из оператора в функцию, как в Python 3.
- Raw_input : заменено на функцию
input
, как в Python 3. - Форматы% s в стиле C : везде заменяется на
str.format
.
Но есть и менее очевидные изменения кода:
- Условие цикла : понимание списка в условии
while
заменено проверкой надмножества. - Отрицательный индекс : не нужен
man [: 10-guesses_left]
, простоman [: - guesses_left]
- Установить обновление : замените
map
/filter
наset.update
, т.е. из этого:
map(guesses.add, filter(str.isalpha, raw_input...
with this:
guesses.update(c.upper() for c in input() if str.isalpha(c))
- Магические числа : число 10 больше не жестко запрограммировано и заменено на
len (man)
.
Как видите, я предпочитаю функции набора, а не понимание списков. Выбор функций набора, таких как my_set.update
, вместо операторов набора, таких как my_set>, был преднамеренным, поскольку первый допускает любую итерацию в качестве аргумента, а не только наборы. В большинстве случаев для повышения эффективности я использовал выражения генератора .
W_Y PYT_ON 3?
Я должен признать, что реальная причина, по которой я переписал Палача, заключалась в том, что он не работал на моем компьютере Arch Linux, который работает на Python 3. За последние несколько месяцев я полностью переключил все свои проекты (и моя книга ) на Python 3. Пока у меня не было причин жаловаться.
Помимо синтаксических различий, вы обнаружите ряд серьезных преимуществ использования Python 3 по сравнению с Python 2:
Файлы Unicode . Поскольку путь к файлу словаря можно изменить, я создал тестовый файл с несколькими малаялам слова в отдельных строках. Версия Python 2 просто завершается с сообщением о выигрыше. Вероятно, это потому, что str.isalpha
Удаляет загадочные формы : больше никаких операторов raw_input
(кто использует старый input
?) или print
Если вы работаете в Windows, вы можете загрузить довольно много любого из словари в Интернете (даже оскорбительные ) и измените путь в DICT
на что-то вроде dict.txt
. Или вы можете создать простой текстовый файл с одним словом в каждой строке. Совет от профессионала: Hangman отлично подходит для изучения иностранных языков, поэтому попробуйте создать файл Unicode с некоторыми иностранными словами.
Прежде чем кто-либо из поклонников Python 2 набросится на меня, позвольте мне заметить, что этот код будет отлично работать на Python 2.7+, если вы замените input
на raw_input
. Ваше здоровье!
FI_AL COMME_T
Этот пост ни в коем случае не является критикой кода Данвера. На самом деле, мне понравилась его реализация, и я совершенно не тратил свои выходные, играя в Палача (27 побед из 30 игр !!!). Было очень весело сопоставить проблему с наборами и внести небольшие улучшения.
Надеюсь, вы нашли новые передовые методы работы с Python или просто потрясающую игру в слова, чтобы убить время!