Учим Python, делая крутые игры 2018. Invent your owncomputer gameswith python
![]()
|
Вывод игроку инструкций по игре Функция showInstructions() содержит вызов print() с многострочным вы- водом. 107. def showInstructions(): 108. print('''Инструктаж: 109. Вы - капитан корабля, плывущего за сокровищами. Ваша задача - с помощью -- пропуск-- 154. Нажмите клавишу Enter, чтобы продолжить...''') 155. input() 242 Глава 13 Функция input() позволяет игроку нажать клавишу Enter перед выводом следующей строки. Это связано с тем, что окно IDLE может отображать опре- деленное количество текста за раз, а мы не хотим вынуждать игрока прокру- чивать экран вверх, чтобы прочесть начало текста. После нажатия клавиши Enter, функция возвращается к вызвавшей ее строке кода. Игровой цикл Теперь, когда мы ввели все функции, нужные нашей игре, давайте напи- шем основной код. Первое, что видит игрок после запуска программы, — это название игры, выводимое строкой кода 159. Это — основная часть програм- мы, которая сначала выдает игроку инструкции, а затем присваивает значе- ния переменным, которые будут использоваться в игре. 159. print('Охотник за сокровищами!') 160. print() 161. print('Показать инструктаж? (да/нет)') 162. if input().lower().startswith('д'): 163. showInstructions() 164. 165. while True: 166. # Настройка игры 167. sonarDevices = 20 168. theBoard = getNewBoard() 169. theChests = getRandomChests(3) 170. drawBoard(theBoard) 171. previousMoves = [] Выражение input().lower().startswith('д') позволяет игроку запросить инструкции и принимает значение True, если игрок вводит строку, начинаю- щуюся с буквы 'д' или 'Д'. Например: True input() .lower().startswith('y') 'Y'.lower() .startswith('y') 'y'.startswith('y') input() .lower().startswith('д') 'Д'.lower() .startswith('д') ' д ' . s t a r t s w i t h ( ' д ' ) T r u e Игра «Охотник за сокровищами» 243 Если это условие истинно, в строке кода 163 вызывается функция showInstructions() . Иначе начинается игра. В строках кода с 167 по 171 присваиваются значения нескольким пере- менным; они описаны в таблице 13.1. Таблица 13.1. Переменные, используемые в основном цикле игры Переменная Описание sonarDevices Количество гидролокаторов, оставшихся у игрока theBoard Структура данных игрового поля, используемая в этой игре theChests Список структур данных сундуков. Функция getRandomChests() возвращает список трех сундуков с сокровищами в случайных позициях на игровом поле previousMoves Список всех ходов с координатами x и y, сделанных игроком Ниже мы будем использовать эти переменные, поэтому обязательно про- читайте их описание, прежде чем двигаться дальше! Демонстрация игроку статуса игры До тех пор пока у игрока остаются гидролокаторы, в строке 173 выпол- няется цикл while, сообщающий игроку, сколько гидролокаторов у него еще есть и сколько сундуков с сокровищами осталось на поле. 173. while sonarDevices > 0: 174. # Показать гидролокаторные устройства и сундуки с сокровищами. 175. print('Осталось гидролокаторов: %s. Осталось сундуков с сокровищами: %s.' % (sonarDevices, len(theChests))) После вывода информации о том, сколько осталось устройств, цикл while продолжает выполняться. Обработка хода игрока Код в строке 177 также является частью цикла while и использует мно- жественное присваивание для присвоения переменных x и y двухэлемент- ному списку, который представляет координаты хода игрока, возвращае- мые enterPlayerMove(). Мы добавим переменную previousMoves, чтобы код enterPlayerMove() смог гарантировать, что игрок не повторит предыдущий ход. 177. x, y = enterPlayerMove(previousMoves) 178. previousMoves.append([x, y]) # Мы должны отслеживать все ходы, чтобы гидролокаторы могли обновляться. 179. 244 Глава 13 180. moveResult = makeMove(theBoard, theChests, x, y) 181. if moveResult == False: 182. continue Затем переменные x и y добавляются в конец списка previousMoves. Пере- менная previousMoves представляет собой список координат x и y каждого хода, совершаемого игроком. Этот список позже используется в программе в строках 177 и 186. Переменные x, y, theBoard и theChests передаются функции makeMove() в строке кода 180. Эта функция делает необходимые изменения, чтобы раз- местить гидролокатор на игровом поле. Если функция makeMove() возвращает False, значит, возникла проблема с переданными ей значениями x и y. Оператор continue отбрасывает интер- претатор обратно в начало цикла while в строке кода 173, чтобы снова запро- сить у игрока координаты. Нахождение затонувшего сундука с сокровищами Если функция makeMove() не возвращает значение False, то возвращает строку с результатами этого хода. Если это строка 'Вы нашли сундук с сокро- вищами на затонувшем судне!' , то все гидролокаторы на поле должны обно- вить значения для определения следующего ближайшего сундука с сокро- вищами. 183. else: 184. if moveResult == 'Вы нашли сундук с сокровищами на затонувшем судне!': 185. # Обновить все гидролокаторные устройства, в настоящее время находящиеся на карте. 186. for x, y in previousMoves: 187. makeMove(theBoard, theChests, x, y) 188. drawBoard(theBoard) 189. print(moveResult) Координаты x и y всех гидролокаторов содержатся в переменной previousMoves Перебирая значения previousMoves в строке кода 186, вы можете снова передать все эти координаты x и y функции makeMove(), чтобы обновить зна- чения на поле. Поскольку программа здесь не выводит никакого нового тек- ста, игрок не подозревает, что интерпретатор заново делает все предыдущие ходы. Кажется, что поле обновляется автоматически. Игра «Охотник за сокровищами» 245 Проверка победы игрока Помните, что функция makeMove() изменяет список theChests, который вы ей отправили. Поскольку theChests — список, любые изменения, вне- сенные в него внутри функции, сохраняются после того, как интерпрета- тор из нее возвращается. Функция makeMove() удаляет элементы из списка theChests при обнаружении сундуков с сокровищами, поэтому в конечном итоге (если игрок продолжит правильно угадывать) все сундуки с сокрови- щами будут удалены. (Помните, что под «сундуком с сокровищами» я имею в виду двухэлементные списки из координат x и y внутри списка theChests). 191. if len(theChests) == 0: 192. print('Вы нашли все сундуки с сокровищами на затонувших судах! Поздравляем и приятной игры!') 193. break Когда все сундуки с сокровищами будут обнаружены на поле и удалены из списка theChests, он будет иметь длину 0. Когда это произойдет, програм- ма выведет игроку поздравление, а затем выполнит инструкцию break, чтобы прервать цикл while. Затем интерпретатор перейдет к строке кода 197, первой после блока while. Проверка проигрыша игрока Строка кода 195 — последняя строка цикла while, начавшегося в стро- ке 173. 195. sonarDevices -= 1 Программа уменьшает значение переменной sonarDevices, потому что игрок израсходовал один гидролокатор. Если игрок продолжит упускать сундуки с сокровищами, значение переменной sonarDevices, в конце концов, будет сведено к 0. После этой строки кода интерпретатор переходит к стро- ке 173, чтобы заново оценить условие while (т.е. sonarDevices > 0). Когда значение переменной sonarDevices становится равным 0, это условие становится ложным, и интерпретатор продолжает выполнение за пределами блока while в строке кода 197. Но до тех пор условие останется истинным, а игрок может продолжать делать предположения. 197. if sonarDevices == 0: 198. print('Все гидролокаторы опущены на дно! Придется разворачивать корабль и') 199. print('отправляться домой, в порт! Игра окончена.') 246 Глава 13 200. print('Вы не нашли сундуки в следующих местах:') 201. for x, y in theChests: 202. print(' %s, %s' % (x, y)) Код в строке 197 — первая за пределами цикла while. Когда интерпрета- тор достигает этой точки, игра заканчивается. Если переменная sonarDevices равна 0, то игрок потерпел поражение, так как израсходовал гидролокаторы, не отыскав все сундуки. Код в строках 198–200 сообщит игроку, что тот проиграл. Цикл for в стро- ке 201 переберет сундуки с сокровищами, оставшиеся в списке theChests, и отобразит их местоположение, чтобы игрок мог увидеть, где они скрыва- лись. Завершение работы программы с помощью функции sys.exit() Вне зависимости от исхода игры программа предлагает сыграть еще раз. Если игрок не вводит 'да' или 'Д', либо вводит какую-то другую строку, кото- рая не начинается с буквы д, тогда условие not input().lower().startswith('д') становится истинным, и выполняется функция sys.exit(). Это приводит к за- вершению работы программы. 204. print('Хотите сыграть еще раз? (да или нет)') 205. if not input().lower().startswith('д'): 206. sys.exit() В противном случае интерпретатор переходит к началу цикла while в стро- ке 165, и начинается новая игра. Заключение Помните, как наша игра «Крестики-нолики» пронумеровала участки на игровом поле с 1 по 9? Такое подобие системы координат подходит для поля менее чем с 10 клетками, но ведь у игрового поля «Охотник за сокровища- ми» их 900! Декартова система координат, о которой мы узнали в главе 12, позволяет управлять этими клетками и определять расстояние между двумя позициями на поле. Позиции в играх, использующих декартову систему координат, можно со- хранять в списке списков, в которых под первым индексом содержится значе- ние координаты x, а под вторым — значение y. Это упрощает доступ к коор- динатам с помощью кода board[x][y]. Такие структуры данных (как те, что использовались для океана и место- положений сундуков) позволяют представлять сложные вещи в виде данных, и работа с вашими играми в основном сводится к работе с этими структу- рами. В следующей главе мы будем представлять буквы в виде чисел. Представ- ляя текст в виде чисел, можно выполнять математические операции, позво- ляющие шифровать секретные сообщения. 248 Глава 14 14 ШИФР ЦЕЗАРЯ Программа в этой главе на самом деле не является игрой, но, тем не менее, по- веселит вас. Она превращает обычный русский язык в секретный код, а также может преобразовывать этот секретный код обратно в русский язык. Только тот, кому известен ключ к секретным кодам, сможет понять зашифрованные сообщения. Поскольку эта программа обрабатывает текст, чтобы преобразовать его в секретные сообщения, вам придется выучить несколько новых функций и методов для управления строками. Кроме того, вы узнаете, каким образом программы могут выполнять математические операции с текстовыми стро- ками так же, как с числами. В ЭТОЙ ГЛАВЕ РАССМАТРИВАЮТСЯ СЛЕДУЮЩИЕ ТЕМЫ: • Криптография и шифры • Шифротекст, открытый текст, ключи и символы • Шифрование и расшифровывание • Шифр Цезаря • Строковый метод find() • Криптоанализ • Полный перебор Криптография и шифрование Наука, изучающая создание секретных кодов, называется криптографи- ей. Веками криптография позволяла отправлять секретные сообщения, кото- Шифр Цезаря 249 рые могли прочесть только отправитель и получатель, даже если кто-то пере- хватывал закодированное сообщение. Система секретного кода называется шифром. Шифр, используемый программой в этой главе, называется шифром Цезаря. В криптографии сообщение, которое хотят сохранить в секрете, называ- ют открытым текстом. Допустим, у нас есть сообщение с открытым тек- стом, которое выглядит так: Здесь спрятан ключ от книжного шкафа. Преобразование открытого текста в закодированное сообщение на- зывается шифрованием открытого текста. Открытый текст зашифрован в шифротексте. Шифротекст выглядит как случайные буквы, поэтому мы не можем понять, каким был исходный открытый текст, просто взглянув на шифротекст. Ниже показан предыдущий пример, зашифрованный в шиф- ротекст: Оклшг шцчещжф стею хщ сфпнфхйх ясжыж. Если вам известен шифр, используемый для шифрования сообщения, вы можете расшифровать зашифрованный текст обратно в открытый текст (расшифровывание (дешифровка) — это противоположность шиф- рования). Многие шифры используют ключи , то есть секретные значения, позво- ляющие расшифровать шифротекст, который был зашифрован с помощью конкретного шифра. Думайте о шифре как о дверном замке — его можно от- крыть только с помощью определенного ключа. Как работает шифр Цезаря Шифр Цезаря — один из самых ранних шифров. В этом шифре сообщение шифруется путем замены каждой буквы в нем «сдвинутой» буквой. В крип- тографии зашифрованные буквы называются символами, потому что они мо- гут быть буквами, числами или любыми другими знаками. Если вы сдвинете букву «А» на одну позицию, то получите букву «Б». Если сдвинете букву «А» на две позиции, получите букву «В». На рис. 14.1 показано несколько букв, сдвинутых на три позиции. Чтобы получить все сдвинутые буквы, нарисуйте ряд клеток с каждой буквой алфавита. Затем нарисуйте второй такой же ряд под ним, но начни- 250 Глава 14 те его с буквы через определенное количество позиций. Когда вы дойдете до конца алфавита открытого текста, начните его с начала, с «А». На рис. 14.2 показан пример с буквами, сдвинутыми на три позиции. H I J X Y Z A B C D E F G A B C D E F G . . . Э А В Е Ю Б Г Ё Я В Д Ж А Г Е З Ё И Б Д Рис. 14.1. Шифр Цезаря сдвигает буквы на три позиции. Так, буква «Б» превращается в «Д» R S T A B C D E F G H I J K L M N O P Q X Y Z U V W R S T A B C D E F G H I J K L M N O P Q X Y Z U V W A B C A B C R S T D E F G H I J K L M N O P Q X Y Z U V W R S T D E F G H I J K L M N O P Q X Y Z U V W А А Й Й Е Е О О Ф Ф Б Б К К Ё Ё П П Х Х В В Л Л Ж Ж Р Р Ц Ц Г Г М М З З С С Ч Ч И И Т Т Ш Ш Д Д Н Н У У Рис. 14.2. Целый алфавит сдвинут на три позиции Количество позиций, на которое вы сдвигаете буквы (от 1 до 33) — это ключ в шифре Цезаря. Если вы не знаете ключ (число, используемое для шиф- рования сообщения), то не сможете расшифровать секретный код. В примере на рис. 14.2 показаны преобразования букв для ключа 3. Примечание. Несмотря на то что существуют 33 возможных ключа, шифро- вание вашего сообщения числом 33 образует шифротекст, который будет точно таким же, как и открытый текст! Если вы зашифруете слово открытого текста ПРИВЕТ ключом 3, тогда: • Буква «П» станет «Т». • Буква «Р» станет «У». • Буква «И» станет «Л». • Буква «В» станет «Е». • Буква «Е» станет «З». • Буква «Т» станет «Х». Итак, шифротекст слова ПРИВЕТ с ключом 3 имеет вид ТУЛЕЗХ. Чтобы расшифровать код ТУЛЕЗХ с помощью ключа 3, мы переходим от нижних клеток к верхним. X Y Z a b c d e f g X Y Z a b c d U V W Щ Э Я в Ы Ю а г Ь Я б д Э а в е г ё Ю б Рис. 14.3. Целый алфавит, включающий строчные буквы, сдвинут на три позиции Шифр Цезаря 251 Если вы хотите учесть строчные буквы как отличающиеся от прописных, добавьте еще 33 клетки к тем, которые у вас уже есть, и заполните их 33 строч- ными буквами. Теперь с ключом, равным 3, буква «Ю» становится «б», как по- казано на рис. 14.3. В этом случае шифр работает так же. Вы можете использовать буквы дру- гого языка, заполнив клетки его алфавитом. Таким образом вы получите свой шифр. Пример запуска программы «Шифр Цезаря» Ниже показан пример работы программы «Шифр Цезаря» , шифрующей сообщение: Вы хотите зашифровать или расшифровать текст? зашифровать Введите текст: Если больной очень хочет жить, врачи бессильны. Введите ключ шифрования (1-66) 15 Преобразованный текст: УАъч пэъКьэш эЕуьК ДэЕуБ хчБК, ряоЕч пуААчъКьЙ. Теперь запустите программу и расшифруйте текст, который вы только что зашифровали. Вы хотите зашифровать или расшифровать текст? расшифровать Введите текст: УАъч пэъКьэш эЕуьК ДэЕуБ хчБК, ряоЕч пуААчъКьЙ. Введите ключ шифрования (1-66) 15 Преобразованный текст: Если больной очень хочет жить, врачи бессильны. Если вы выполните расшифровку, используя неправильный ключ, текст не расшифруется должным образом. Вы хотите зашифровать или расшифровать текст? расшифровать Введите текст: 252 Глава 14 УАъч пэъКьэш эЕуьК ДэЕуБ хчБК, ряоЕч пуААчъКьЙ. Введите ключ шифрования (1-66) 13 Преобразованный текст: Жунк грнюпрл рщжпю чрщжф икфю, дтвщк гжуукнюпэ. Исходный код программы «Шифр Цезаря» В редакторе файлов создайте новый файл, вы- брав команду меню File ⇒ New File (Файл ⇒ Но- вый файл). В открывшемся окне введите приве- денный ниже исходный код и сохраните файл под именем cipher.py. Нажмите клавишу F5 и запусти- те программу. Если при выполнении программы возникают ошибки, сравните код, который вы на- брали, с оригинальным кодом с помощью онлайн- инструмента на сайте inventwithpython.com/diff/. 1. # Шифр Цезаря 2. SYMBOLS = 'АБВГДЕЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеежзийклмнопрстуфхцчшщъыьэюя' 3. MAX_KEY_SIZE = len(SYMBOLS) 4. 5. def getMode(): 6. while True: 7. print('Вы хотите зашифровать или расшифровать текст?') 8. mode = input().lower() 9. if mode in ['зашифровать', 'з', 'расшифровать', 'р']: 10. return mode 11. else: 12. print('Введите "зашифровать" или "з" для зашифровки или "расшифровать" или "р" для расшифровки.') 13. 14. def getMessage(): 15. print('Введите текст:') 16. return input() 17. 18. def getKey(): 19. key = 0 20. while True: 21. print('Введите ключ шифрования (1-%s)' % (MAX_KEY_SIZE)) 22. key = int(input()) Make sure you’re using Python 3, not Python 2! УБЕ ДИТЕСЬ, ЧТО ИСПО ЛЬЗУЕТЕ PY THON 3, А НЕ PY THON 2! |