Рубрики
Без рубрики

Палач в более чем трех строках Python

Автор оригинала: 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 или просто потрясающую игру в слова, чтобы убить время!