Главная страница
Навигация по странице:

  • Установление максимальной длины ключа

  • Выбор между шифрованием и расшифровыванием сообщения

  • Получение сообщения от игрока

  • Шифрование/расшифровывание сообщения

  • Нахождение переданных строк с помощью строчного метода fi nd()

  • Привет, мир!.find(П) 0>>> Привет, мир!.find(и) 2>>> Привет, мир!.find(вет)

  • Привет, мир!.find(пупс)

  • Шифрование/расшифровка каждой буквы

  • ДОБАВЛЕНИЕ НОВЫХ СИМВОЛОВ

  • Добавление режима полного перебора

  • В ЭТОЙ ГЛАВЕ РАССМАТРИВАЮТСЯ СЛЕДУЮЩИЕ ТЕМЫ

  • Учим Python, делая крутые игры 2018. Invent your owncomputer gameswith python


    Скачать 6.56 Mb.
    НазваниеInvent your owncomputer gameswith python
    Дата10.12.2022
    Размер6.56 Mb.
    Формат файлаpdf
    Имя файлаУчим Python, делая крутые игры 2018.pdf
    ТипДокументы
    #837554
    страница25 из 39
    1   ...   21   22   23   24   25   26   27   28   ...   39
    253
    23. if (key >= 1 and key <= MAX_KEY_SIZE):
    24. return key
    25.
    26. def getTranslatedMessage(mode, message, key):
    27. if mode[0] == 'р':
    28. key = -key
    29. translated = ''
    30.
    31. for symbol in message:
    32. symbolIndex = SYMBOLS.find(symbol)
    33. if symbolIndex == -1: # Символ не найден в SYMBOLS.
    34. # Просто добавить этот символ без изменений.
    35. translated += symbol
    36. else:
    37. # Зашифровать или расшифровать
    38. symbolIndex += key
    39.
    40. if symbolIndex >= len(SYMBOLS):
    41. symbolIndex -= len(SYMBOLS)
    42. elif symbolIndex < 0:
    43. symbolIndex += len(SYMBOLS)
    44.
    45. translated += SYMBOLS[symbolIndex]
    46. return translated
    47.
    48. mode = getMode()
    49. message = getMessage()
    50. key = getKey()
    51. print('Преобразованный текст:')
    52. print(getTranslatedMessage(mode, message, key))
    Установление максимальной длины ключа
    Процессы шифрования и расшифровывания — это операции, обратные по отношению друг к другу. В то же время они содержат много общего кода.
    Давайте посмотрим, как работает каждая строка кода программы.
    1. # Шифр Цезаря
    2. SYMBOLS = 'АБВГДЕЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеежзийклмнопрстуфхцчшщъыьэюя'
    3. MAX_KEY_SIZE = len(SYMBOLS)

    254
    Глава 14
    MAX_KEY_SIZE
    — константа, которая хранит длину строки SYMBOLS(66). Эта константа напоминает нам, что в нашей программе значение ключа, исполь- зуемого в шифре, всегда должно быть между 1 и 66.
    Выбор между шифрованием
    и расшифровыванием сообщения
    Функция getMode() позволяет пользователю решить, желает он использо- вать режим шифрования или расшифровывания.
    5. def getMode():
    6. while True:
    7. print('Вы хотите зашифровать или расшифровать текст?')
    8. mode = input().lower()
    9. if mode in ['зашифровать', 'з', 'расшифровать', 'р']:
    10. return mode
    11. else:
    12. print('Введите "зашифровать" или "з" для зашифровки или "расшифровать" или "р" для расшифровки.')
    Код в строке 8 вызывает функцию input(), чтобы пользователь мог вы- брать желаемый режим. Затем метод lower() вызывается в этой строке для возврата строчной версии (нижнего регистра) строки. Значение, возвращае- мое из input().lower(), сохраняется в переменной mode. Условие конструкции if проверяет, существует ли строка, хранящаяся в mode, в списке ['зашифро- вать', 'з', 'расшифровать', 'р']
    Эта функция вернет строку в mode, если mode равен 'зашифровать', 'з', 'рас- шифровать', 'р'
    . Следовательно, функция getMode() вернет строку mode. Если пользователь вводит что-то отличное от 'зашифровать', 'з', 'расшифровать' или 'р'
    , тогда цикл while вновь запросит пользователя ввести правильное зна- чение.
    Получение сообщения от игрока
    Функция getMessage() просто получает от пользователя сообщение (для шифрования или расшифровывания) и возвращает его.
    14. def getMessage():
    15. print('Введите текст:')
    16. return input()

    Шифр Цезаря
    255
    Вызов input() совмещен с return, так что мы используем только одну стро- ку кода вместо двух.
    Получение ключа от игрока
    Функция getKey() позволяет игроку ввести ключ, который будет исполь- зоваться для шифрования или расшифровки сообщения.
    18. def getKey():
    19. key = 0 20. while True:
    21. print('Введите ключ шифрования (1-%s)' % (MAX_KEY_SIZE))
    22. key = int(input())
    23. if (key >= 1 and key <= MAX_KEY_SIZE):
    24. return key
    Цикл while гарантирует, что функция продолжит перебор до тех пор, пока пользователь не введет допустимый ключ. Значение допустимого ключа здесь находится между целочисленными значениями 1 и 66 (помните, что значение переменной MAX_KEY_SIZE равно 66, потому что в переменной SYMBOLS 66 симво- лов). Затем функция getKey() возвращает этот ключ. Код в строке 22 присваи- вает переменной key значение, равное целой части того числа, которое ввел пользователь, поэтому метод getKey() возвращает целое число.
    Шифрование/расшифровывание сообщения
    Функция getTranslatedMessage(), собственно, и выполняет шифрование и расшифровывание.
    26. def getTranslatedMessage(mode, message, key):
    27. if mode[0] == 'р':
    28. key = -key
    29. translated = ''
    Эта функция имеет три параметра:

    mode
    . Этот параметр устанавливает функцию в режим шифрования или расшифровывания;

    message
    . Это открытый текст (или шифротекст), который должен быть зашифрован (или расшифрован);

    key
    . Это ключ, который используется в этом шифре.

    256
    Глава 14
    Код в строке 27 проверяет, является ли первая буква в переменной mode строкой 'р'. Если это так, то программа переходит в режим расшифровыва- ния. Единственное различие между режимами расшифровывания и шифро- вания заключается в том, что в первом ключ является отрицательной версией самого себя. Например, если key — целое число 22, режим расшифровывания превращает его в -22. Причина объясняется в разделе «Шифрование/расшиф- ровка каждой буквы» далее в этой главе.
    Переменная translated будет содержать строку результата: либо шифро- текст (если вы выполняете шифрование), либо открытый текст (если рас- шифровываете). В начале она содержит пустую строку, а затем к ее значению присоединяются зашифрованные или расшифрованные символы. Но пре- жде чем мы сможем начать присоединять символы к значению переменной translated
    , нам нужно зашифровать или расшифровать текст, чем мы и зай- мемся в оставшейся части функции getTranslatedMessage().
    Нахождение переданных строк с помощью
    строчного метода fi nd()
    Чтобы сдвигать буквы, выполняя шифрование или расшифровывание, нам сначала нужно преобразовать их в числа. Числом для каждой буквы в строке SYMBOLS будет индекс, который занимает буква. Поскольку буква «А» занимает индекс SYMBOLS[0], число 0 будет представлять прописную букву «А».
    Если бы мы захотели зашифровать ее с помощью ключа 3, то просто исполь- зовали бы операцию 0 + 3 для получения индекса зашифрованной буквы:
    SYMBOLS[3]
    или 'Г'.
    Мы будем использовать строковый метод find() , который обнаруживает первое вхождение переданной строки в строке, в которой вызывается метод.
    В интерактивной оболочке введите следующие команды:
    >>>
    'Привет, мир!'.find('П')
    0
    >>>
    'Привет, мир!'.find('и')
    2
    >>>
    'Привет, мир!'.find('вет')
    3
    'Привет, мир!.find ('П')
    возвращает 0, потому что 'П' находится под пер- вым индексом в строке 'Привет, мир!. Помните, индексы начинаются с 0, не с 1. Код 'Привет, мир!.find('и') возвращает 2, потому что первое вхождение строчной буквы 'и' находится в середине слова 'Привет'. Метод find() пре- кращает поиск после первого вхождения, поэтому вторая буква 'и' в слове

    Шифр Цезаря
    257
    'мир ' уже не имеет значения. Вы также можете искать строки с более чем од- ним символом. Начало строки 'вет' находится под индексом 3.
    Если переданная строка не может быть найдена, метод find() возвраща- ет -1.
    >>>
    'Привет, мир!'.find('пупс')
    -1
    Вернемся к программе «Шифр Цезаря». Код в строке 31 представляет со- бой цикл for, перебирающий каждый символ в строке message.
    31. for symbol in message:
    32. symbolIndex = SYMBOLS.find(symbol)
    33. if symbolIndex == -1: # Символ не найден в SYMBOLS.
    34. # Просто добавить этот символ без изменений.
    35. translated += symbol
    Метод find() в строке 32 используется для получения индекса строки в symbol. Если метод find() возвращает -1, символ в переменной symbol будет просто присоединен к значению переменной translated без каких-либо изме- нений. Это означает, что любые символы, не являющиеся частью алфавита, такие как запятые и точки, не будут изменены.
    Шифрование/расшифровка каждой буквы
    Как только значение индекса буквы определено, прибавление ключа к это- му значению осуществит смещение и выдаст индекс зашифрованной буквы.
    Код в строке 38 производит это сложение для получения зашифрованной
    (или расшифрованной) буквы.
    36. else:
    37. # Зашифровать или расшифровать
    38. symbolIndex += key
    Помните, что в строке 28 мы сделали целое число в переменной key отри- цательным — для расшифровывания. Код, прибавляющий значение ключа, теперь будет вычитать его, так как прибавление отрицательного числа анало- гично вычитанию.

    258
    Глава 14
    Однако, если это сложение (или вычитание, если значение key отрицатель- но) заставляет переменную symbolIndex проходить последний индекс строки
    SYMBOLS
    , нам нужно вернуть его к 0 в начале списка. Это делается с помощью конструкции if, начинающейся в строке 40.
    40. if symbolIndex >= len(SYMBOLS):
    41. symbolIndex -= len(SYMBOLS)
    42. elif symbolIndex < 0:
    43. symbolIndex += len(SYMBOLS)
    44.
    45. translated += SYMBOLS[symbolIndex]
    Код в строке 40 проверяет, прошла ли переменная symbolIndex последний индекс, сравнивая его с длиной строки SYMBOLS. Если прошла, то код в строке
    41 вычитает длину SYMBOLS из значения переменной symbolIndex. Если значение переменной symbolIndex теперь отрицательное, то расчет индексов должен на- чаться с другого конца строки SYMBOLS. Код в строке 42 проверяет, является ли значение symbolIndex отрицательным после прибавления к нему значения ключа расшифровывания. Если оно отрицательно, код в строке 43 прибавля- ет длину SYMBOLS к значению переменной symbolIndex.
    Переменная symbolIndex теперь содержит индекс правильно зашифро- ванного или расшифрованного символа. Код SYMBOLS[symbolIndex] укажет на конкретный символ для этого индекса, и этот символ будет добавлен в конец значения переменной translated в строке 45.
    Интерпретатор возвращается к строке 31, чтобы повторить то же для сле- дующего символа в переменной message. Как только цикл завершается, функ- ция возвращает зашифрованную (или расшифрованную) строку в translated в строке кода 46.
    46. return translated
    Последняя строка кода функции getTranslatedMessage() возвращает стро- ку translated.
    Запуск программы
    Запуск программы вызывает каждую из трех определенных ранее функ- ций для получения от пользователя всех необходимых данных — режима, со- общения и ключа (mode, message и key).

    Шифр Цезаря
    259
    48. mode = getMode()
    49. message = getMessage()
    50. key = getKey()
    51. print('Преобразованный текст:')
    52. print(getTranslatedMessage(mode, message, key))
    Эти три значения передаются функции getTranslatedMessage(), возвращае- мое значение которой (строка translated) выводится пользователю.
    ДОБАВЛЕНИЕ НОВЫХ СИМВОЛОВ
    Если вы хотите зашифровывать числа, пробелы и знаки препинания, про- сто добавьте их к значению переменной SYMBOLS в строке 2. Например, с помощью программы можно шифровать числа, пробелы и знаки пре- пинания, изменив код в строке 2 на следующий:
    2. SYMBOLS = 'АБВГДЕЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеежзийклмнопрстуфхцчшщъыьэюя
    1234567890!@#$%^&*()'
    Обратите внимание, что значение переменной SYMBOLS содержит про- бел после строчной буквы «я».
    Также можно добавить еще больше символов в этот список. И не нуж- но менять остальную часть вашей программы, так как все строки кода, где необходим список символов, используют константу SYMBOLS.
    Просто убедитесь, что каждый символ содержится в строке только один раз. Кроме того, вам нужно будет расшифровывать сообщения с помощью той же строки SYMBOLS, с помощью которой они были зашифрованы.
    Полный перебор
    Вот и весь шифр Цезаря. Впрочем, несмотря на то, что этот шифр может обмануть кого-то, кто не разбирается в криптографии, он не удержит сооб- щение в секрете от знатоков криптоанализа. В то время как криптография — это наука о создании кодов, криптоанализ изучает их взлом.
    Весь смысл криптографии в том, что если даже зашифрованное сообще- ние попадет в чужие руки, то никто не сможет понять исходный текст. Давай- те представим себя на месте взломщика кодов, и будто все, чем мы располага- ем, — этот зашифрованный текст:

    260
    Глава 14
    Цямхд ъжомуц игъд кмхрмф щшмлр тцпжйцт.
    Полный перебор (метод «грубой силы», от англ. brute force) — это метод последовательного перебора всех возможных ключей вплоть до нахождения правильного. Поскольку существует только 66 возможных ключей, криптоа- налитику будет легко написать программу для взлома, расшифровывающую сообщения с использованием всех возможных ключей. Затем он найдет ключ, который расшифровывает в чистый английский. Давайте добавим в програм- му режим полного перебора
    Добавление режима полного перебора
    Сначала измените код в строках 7, 9 и 12 в функции getMode() так, чтобы они выглядели следующим образом (изменения выделены жирным шрифтом ):
    5. def getMode():
    6. while True:
    7. print('Вы хотите зашифровать, расшифровать
    или взломать текст?')
    8. mode = input().lower()
    9. if mode in ['зашифровать', 'з', 'расшифровать', 'р'
    , 'взломать', 'в']:
    10. return mode
    11. else:
    12. print('Введите "зашифровать" или "з" для зашифровки или "расшифровать" или "р" для расшифровки
    или "взломать" или "в" для взлома.')
    Этот код позволит пользователю выбрать в качестве режима полный пе- ребор (взлом шифра) .
    Затем внесите следующие изменения в основной код программы:
    48. mode = getMode()
    49. message = getMessage()
    50.
    if mode[0] != 'в':
    51. key = getKey()
    52. print('Преобразованный текст:')
    53.
    if mode[0] != 'в':
    54. print(getTranslatedMessage(mode, message, key))
    55.
    else:
    56.
    for key in range(1, MAX_KEY_SIZE + 1):
    57.
    print(key, getTranslatedMessage('расшифровать', message, key))

    Шифр Цезаря
    261
    Если пользователь не выбрал режим полного перебора, программа спра- шивает у него ключ, вызывает функцию getTranslatedMessage() и выводит
    «переведенную» строку. Если же пользователь выбрал режим полного пе- ребора, цикл getTranslatedMessage() выполняет перебор от 1 до MAX_KEY_SIZE
    (то есть до 66). Помните, что функция range() возвращает список целых чи- сел вплоть до второго параметра, но не включая его, вот почему мы добавля- ем + 1. Затем программа выводит каждый возможный «перевод» сообщения
    (включая значение ключа, используемого при преобразовании). Ниже пока- зан пример работы модифицированной программы.
    Вы хотите зашифровать, расшифровать или взломать текст?
    взломать
    Введите текст:
    Цямхд ъжомуц игъд кмхрмф щшмлр тцпжйцт.
    Преобразованный текст:
    1 Хюлфг щенлтх звщг йлфплу шчлкп схоеихс.
    2 Фэкув шемксф жбшв икуокт чцкйо рфнезфр.
    3 Уьйтб чдлйру еачб зйтнйс цхйин пумджуп.
    4 Тыиса цгкипт еЯца жисмир хфизм отлгето.
    5 СъзрЯ хвйзос дЮхЯ езрлзп фузжл нсквесн.
    6 РщжпЮ фбижнр гЭфЮ ежпкжо утжек мрйбдрм.
    7 ПшеоЭ уаземп вЬуЭ деойен тсеей лпиагпл.
    8 ОченЬ тЯжело бЫтЬ гением среди козЯвок.
    9 НцдмЫ сЮедкн аЪсЫ вдмздл рпдгз йнжЮбнй.
    10 МхглЪ рЭегйм ЯЩрЪ бглжгк погвж имеЭами.
    11 ЛфвкЩ пЬдвил ЮШпЩ авкевй онвбе злеЬЯлз.
    12 КубйШ оЫгбзк ЭЧоШ Ябйеби нмбае жкдЫЮкж.
    --
    пропуск--
    Просмотрев каждую строку, вы увидите, что восьмое сообщение не бес- смысленность, а понятный русский текст! Криптоаналитик придет к выводу, что исходный ключ для этого зашифрованного текста был 8. Подобный ме- тод было бы проблематично осуществить в дни Юлия Цезаря и Римской им- перии, но сегодня у нас есть компьютеры, которые могут за короткое время перебрать миллионы или даже миллиарды ключей.
    Заключение
    Компьютеры хороши в математических расчетах. Когда мы создаем си- стему для преобразования некоторой части информации в числа (как с тек-
    стом и порядковыми числительными или с геопозициями и системами ко- ординат), компьютерные программы могут обрабатывать эти числа быстро и эффективно. Большая часть разработки программы заключается в том, как представить информацию, которой вы хотите управлять, в виде понятных
    Python значений.
    При том, что наша программа «Шифр Цезаря» может шифровать сообще- ния, смысл которых останется тайной для людей, вооруженных карандашом и бумагой, программа не сможет сохранить их в секрете от тех, кто знает, как обрабатывать информацию с помощью компьютеров (это подтверждает режим полного перебора).
    В главе 15 мы создадим игру «Реверси» (также известную как «Отелло»).
    Искусственный интеллект в этой игре намного более продвинут, чем в про- грамме «Крестики-нолики» в главе 10. Он будет настолько хорош, что в боль- шинстве случаев вы не сможете его обыграть!

    Игра «Реверси»
    263
    15
    ИГРА «РЕВЕРСИ»
    В этой главе мы создадим игру «Ревер- си», также известную под названием
    «Отелло». В этой настольной игре для двух игроков используется сетка, поэто- му нам пригодится декартова система с координатами x и y. У нашей версии игры будет компьютерный искусственный интеллект
    , более продвинутый, чем ИИ игры «Крестики-нолики» из гла- вы 10. Вообще-то этот ИИ настолько хорош, что он, ско- рее всего, будет побеждать вас почти в каждой игре (во всяком случае он непременно обыгрывает автора).
    В ЭТОЙ ГЛАВЕ РАССМАТРИВАЮТСЯ СЛЕДУЮЩИЕ ТЕМЫ:
    • Как играть в «Реверси»
    • Функция bool()
    • Моделирование ходов на игровом поле
    • Программирование искусственного интеллекта в игре «Реверси»
    Как играть в «Реверси»
    В игре «Реверси» используется поле размером 8×8 клеток и фишки — чер- ные с одной стороны и белые с другой (вместо этого мы будем использовать буквы O и X). В начале игры поле выглядит так, как показано на рис. 15.1.
    Два игрока по очереди выставляют на поле фишки выбранного ими цве- та — черные или белые. Когда игрок помещает фишку на поле, все фишки противника, которые находятся между новой фишкой и остальными фишка- ми игрока, переворачиваются.

    264
    Глава 15 1
    2 3
    4 5
    6 7
    8 1
    2 3
    4 5
    6 7
    8
    Рис. 15.1.
    В начале игры на поле выставляются две белые фишки и две черные
    1 2
    3 4
    5 6
    7 8
    1 2
    3 4
    5 6
    7 8
    Рис. 15.2.
    Игрок белыми ставит новую фишку
    1 2
    3 4
    5 6
    7 8
    1 2
    3 4
    5 6
    7 8
    1   ...   21   22   23   24   25   26   27   28   ...   39


    написать администратору сайта