Правильный способ повесить мужчину

Проблема:

Я видел несколько вопросов вокруг палача. Обычно это делается очень грубо, что обычно невозможно обобщить. Моя мысль или вопрос о создании мантии , которая является центральной частью в любой игре палача.

Я хочу создать изображение ASCII ниже

 _____
 |   |
 O   |
/|\  |
/ \  |
     |          
 ---------- 

Если одна «конечность» фигуры палки должна появляться каждый раз, когда функция запрашивается.

Я также хотел иметь возможность указать как height , так и width . Однако я не смог масштабировать фигуру палки относительно размера платья. Код приведен ниже, и у меня есть два простых вопроса: =)

Вопрос:

  • Есть ли более чистый способ повесить мужчину? Я чувствую, что мой метод очень варварский (я использовал некоторую черную математику для выпускников вуду, чтобы заставить ее выглядеть правильно).
  • Есть ли способ сделать шкатулку фигуры размером с мантию? (по существу, дает больше догадок).

код:

from math import ceil


def create_gown(width, height):
    gown = []
    gown.append('{:>{}s}'.format('_'*int(width/2), 10))
    gown.append('{:>{}s} {:>{}s}'.format(
        '|', 11 - int(width/2), '|', int(width/2)-2))
    for i in range(height-3):
        gown.append('{:^{}s}'.format('|', 20))
    gown.append('{:^{}s}'.format('-'*width, 20))
    return gown


def wrong_answer(gown, attempt=0):

    height, width = len(gown), len(gown[-1].strip())

    offset1 = int((-width+23)*0.5)+1
    offset3 = int(ceil(0.5*(width-7)))
    if attempt == 0:
        return gown
    elif attempt == 1:
        new_line = '{:>{}s} {:>{}s}'.format('O', offset1-1, '|', offset3+1)
        row = 2
    elif attempt == 2:
        new_line = '{:>{}s} {:>{}s}'.format('|', offset1-1, '|', offset3+1)
        row = 3
    elif attempt == 3:
        new_line = '{:>{}s} {:>{}s}'.format('/| ', offset1, '|', offset3)
        row = 3
    elif attempt == 4:
        new_line = '{:>{}s} {:>{}s}'.format('/|\\', offset1, '|', offset3)
        row = 3
    elif attempt == 5:
        new_line = '{:>{}s} {:>{}s}'.format('/  ', offset1, '|', offset3)
        row = 4
    elif attempt == 6:
        new_line = '{:>{}s} {:>{}s}'.format('/ \\', offset1, '|', offset3)
        row = 4
    else:
        raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
    gown[row] = new_line
    return gown


def print_gown(gown):
    for line in gown:
        print line

if __name__ == '__main__':
    gown = create_gown(10, 7)
    print len(gown)
    for i in range(7):
        gown = wrong_answer(gown, i)
        print_gown(gown)
39 голосов | спросил N3buchadnezzar 7 J0000006Europe/Moscow 2016, 14:26:38

1 ответ


31

Повторения

Все ваши elif разделяют одну и ту же структуру. Вам просто нужно убедиться, что вставленный шаблон всегда 3 ширины, и вы можете отменить корректировки смещений. Следующим логическим шагом будет использование словаря для хранения шаблона и строки для каждой попытки:

_GOWN_MODIFIER = {
    1: (' O ', 2),
    2: (' | ', 3),
    3: ('/| ', 3),
    4: ('/|\\', 3),
    5: ('/  ', 4),
    6: ('/ \\', 4),
}

def wrong_answer(gown, attempt=0):

    height, width = len(gown), len(gown[-1].strip())

    offset1 = int((-width+23)*0.5)+1
    offset3 = int(ceil(0.5*(width-7)))
    if not attempt:
        return gown

    try:
        pattern, row = _GOWN_MODIFIER[attempt]
    except KeyError:
        raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
    else:
        gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)

    return gown

Исчисление

Вам не нужно ceil и int выполнить целочисленное деление на 2. В Python 2 / уже нужен оператор , Вы также можете использовать //, который также является той же операцией. Преимущество последнего над первым состоит в том, что поведение в Python 3 одинаково, тогда как / будет выполнять десятичное деление в Python 3.

_GOWN_MODIFIER = {
    1: (' O ', 2),
    2: (' | ', 3),
    3: ('/| ', 3),
    4: ('/|\\', 3),
    5: ('/  ', 4),
    6: ('/ \\', 4),
}

def wrong_answer(gown, attempt=0):
    if not attempt:
        return gown

    height, width = len(gown), len(gown[-1].strip())
    offset1 = (23 - width) // 2 + 1
    offset3 = (width - 6) // 2

    try:
        pattern, row = _GOWN_MODIFIER[attempt]
    except KeyError:
        raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
    else:
        gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)

    return gown

def create_gown(width, height):
    half_width = width // 2
    gown = []
    gown.append('{:>{}s}'.format('_'*half_width, 10))
    gown.append('{:>{}s} {:>{}s}'.format(
        '|', 11 - half_width, '|', half_width-2))
    for i in range(height-3):
        gown.append('{:^{}s}'.format('|', 20))
    gown.append('{:^{}s}'.format('-'*width, 20))
    return gown

Создание и модификация

Вам известно, что вы можете создавать последовательности повторяющихся элементов, используя seq(element) * count. Вы можете использовать это, чтобы создать свою переменную gown, повторив полюс, а затем изменив верхние два ряда и последний; он должен быть более дружественным к памяти, поскольку пространство, необходимое для хранения списка, будет выделено за один раз:

def create_gown(width, height):
    half_width = width // 2
    gown = ['{:^{}s}'.format('|', 20)] * height
    gown[0] = '{:>{}s}'.format('_' * half_width, 10)
    gown[1] = '{:>{}s} {:>{}s}'.format(
        '|', 11 - half_width, '|', half_width-2)
    gown[-1] = '{:^{}s}'.format('-' * width, 20)
    return gown

При изменении на месте и возврате значения

и немного о печати тоже

Ваш wrong_answer изменяет мантию на место и возвращает измененное значение. Это необязательно, поскольку вызывающий объект все еще должен содержать ссылку на значение (которое было изменено). При вызове кода вы можете:

gown2 = wrong_answer(gown, attempt=3)

, но

>>> gown2 is gown
True

Поэтому нет смысла дублировать ссылки на один и тот же объект. Вместо этого то, что вы можете сделать, это препроцесс задания печати, поэтому вам действительно не нужна функция print_gown:

def wrong_answer(gown, attempt=0):
    height, width = len(gown), len(gown[-1].strip())
    offset1 = (23 - width) // 2 + 1
    offset3 = (width - 6) // 2

    if attempt:
        try:
            pattern, row = _GOWN_MODIFIER[attempt]
        except KeyError:
            raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
        else:
            gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)

    return '\n'.join(gown)

Использование:

print(wrong_answer(gown, attempt=3))

Исключения

Поднятие общей цели Exception - это плохая практика, поскольку она делает предложения except, пытаясь обработать ваш код, способный ловить больше, чем нужно. Вы должны хотя бы использовать более общее исключение (например, ValueError) или определить свой собственный:

class AlreadyHangedError(ValueError):
    pass

def wrong_answer(gown, attempt):
    ...
    raise AlreadyHangedError('...')
    ...

Однако есть что-то странное в том, как вы используете свой wrong_answer: вызывающий абонент должен вызвать эту функцию, используя увеличивающиеся попытки attempts. Это работа длягенератор. Повернув wrong_answer в генератор, вы можете вызвать его, чтобы создать экземпляр генератора, а затем вызвать next в этом экземпляре (или создать цикл for сделайте это для вас), чтобы напечатать следующее платье:

def wrong_answer(width, height):
    gown = create_gown(width, height)
    offset1 = (23 - width) // 2 + 1
    offset3 = (width - 6) // 2

    yield '\n'.join(gown)
    for attempt in range(1, 7):
        pattern, row = _GOWN_MODIFIER[attempt]
        gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)
        yield '\n'.join(gown)

Использование:

for gown in wrong_answer(10, 7):
    print(gown)

или, в более реальном сценарии:

def hangman_game():
    for gown in wrong_answer(10, 7):
        while True:
            status = manage_user_input()
            if status == 'FAILED':  # Whatever
                break
            if status == 'COMPLETED':  # Whatever
                return
        print gown
    print 'You lose'

Магические числа

create_gown, кажется, центрирует платье в 20-строчных строках, но что, если width больше этого? Сначала вы должны определить это значение и дать ему значащее имя, и то же самое для всех значений, которые вытекают из него; и тогда вы можете взять max(20, width) в качестве базы для своего исчисления.

ответил Mathias Ettinger 7 J0000006Europe/Moscow 2016, 16:22:33

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132