Учим Python, делая крутые игры 2018. Invent your owncomputer gameswith python
Скачать 6.56 Mb.
|
Значение None Значение None представляет собой отсутствие значения. None — един- ственное значение типа данных NoneType. Это значение можно использовать, когда необходимо получить ответ типа «не существует» или «ни один из чис- ла, перечисленных выше». Например, в переменной с именем quizAnswer хранится ответ пользовате- ля на какой-нибудь вопрос популярной викторины в формате истина/ложь. То есть переменная возвращает значение True или False, оценивая ответ поль- зователя. Но если пользователь не отвечает на вопрос, значение True/False использовать нельзя, так как это означало бы ответ пользователя. Вместо это- го переменной quizAnswer можно присвоить значение None, если пользователь пропускает ответ. Необходимо отметить, что значение None не выводится в интерактивной оболочке, подобно другим значениям. >>> 2 + 2 4 >>> 'Это строковое значение.' 'Это строковое значение.' >>> None >>> Значения первых двух выражений выводятся на экран в последующих строках, но None не имеет значения, поэтому не выводится ничего. 184 Глава 10 Функция, которая, как кажется, не имеет возвращаемого значения, воз- вращает значение None. Например, print() возвращает значение None. >>> spam = print('Привет, мир!') Привет, мир! >>> spam == None True Здесь мы присваиваем переменной spam значение print('Привет, мир!'). Подобно другим функциям, функция print() возвращает значение. Несмотря на то что print() — функция вывода на экран, при вызове она возвращает значение None. IDLE не показывает значение None в интерактивной оболочке, но можно сказать, что spam присвоено значение None, потому что выражение spam == None истинно. Создание искусственного интеллекта Код ИИ содержится в теле функции getComputerMove(). 83. def getComputerMove(board, computerLetter): 84. # Учитывая заполнение игрового поля и букву компьютера, определяет допустимый ход и возвращает его. 85. if computerLetter == 'Х': 86. playerLetter = 'О' 87. else: 88. playerLetter = 'Х' Первый аргумент, переменная board, — это параметры игрового поля игры «Крестики-нолики». Второй аргумент, переменная letter, — это буква ком- пьютера — 'X' или 'O', которая хранится в переменной computerLetter. Первые несколько строк просто присваивают другую букву переменной playerLetter. Код один и тот же, присвоены компьютеру буквы «Х» или «О». Напомню, как работает алгоритм искусственного интеллекта игры «Крестики-нолики»: 1. Проверить, существует ли ход, сделав который, компьютер победит. Если существует, сделать его. В противном случае перейти к шагу 2. 2. Проверить, существует ли ход, сделав который, победит игрок. Если существует, блокировать его. В противном случае перейти к шагу 3. 3. Проверить, свободна ли одна из угловых клеток (под номером 1, 3, 7 или 9). Если да, то сделать ход в эту клетку. Если нет свободных угло- вых клеток, перейти к шагу 4. Игра «Крестики-нолики» 185 4. Проверить, свободен ли центр. Если свободен, занять его. Если нет, перейти к шагу 5. 5. Сделать ход на одну из боковых клеток (под номером 2, 4, 6 или 8). Если этот шаг выполнен, возможных ходов больше нет. Функция возвращает целочисленные значения в диапазоне от 1 до 9, представляющие собой ходы компьютера. Давайте посмотрим, как каждый из этих шагов реализуется в коде программы. Проверка — сможет ли компьютер победить, сделав ход Прежде всего, если компьютер может победить, сделав следующий ход, его надо сделать немедленно. 90. # Это алгоритм для ИИ "Игры «Крестики-Нолики»": 91. # Сначала проверяем — победим ли мы, сделав следующий ход. 92. for i in range(1, 10): 93. boardCopy = getBoardCopy(board) 94. if isSpaceFree(boardCopy, i): 95. makeMove(boardCopy, computerLetter, i) 96. if isWinner(boardCopy, computerLetter): 97. return i Цикл for, который начинается в строке 92, перебирает все возможные значения ходов от 1 до 9. Код внутри цикла имитирует ситуацию, которая возникнет, если компьютер сделает этот ход. Код в первой строке в цикле (строка 93) создает копию списка board. Та- ким образом, внутри цикла имитируется ход, не изменяя реальных значений игрового поля «Крестики-нолики», которые хранятся в переменной board. Функция getBoardCopy() возвращает другой список, хотя и идентичный спи- ску board. Код в строке 93 проверяет, свободна ли требуемая клетка, и делает ход в копии board. Если в результате компьютер побеждает, функция возвращает этот ход в виде целого значения. Если нет клеток, приводящих к победе, цикл завершается, и выполнение программы продолжается со строки 100. Проверка — сможет ли игрок победить, сделав ход Затем для каждой клетки код имитирует ход человека. 99. # Проверяем — победит ли игрок, сделав следующий ход, и блокируем его. 100. for i in range(1, 10): 186 Глава 10 101. boardCopy = getBoardCopy(board) 102. if isSpaceFree(boardCopy, i): 103. makeMove(boardCopy, playerLetter, i) 104. if isWinner(boardCopy, playerLetter): 105. return i Код такой же, как в цикле со строки 92, исключая букву игрока, поме- щенную в копию board. Если функция isWinner() показывает, что игрок может победить в следующем ходу, компьютер блокирует этот ход, чтобы этого не допустить. Если в следующем ходу победа игрока невозможна, цикл for за- вершается, и выполнение продолжается со строки 108. Проверка угловых, центральной и боковых клеток (в порядке очереди) Если компьютер не может сделать победный ход и нет необходимости блокировать ход игрока, ход совершается в угловую, центральную или боко- вую клетку, в зависимости от того, какие из них свободны. Сначала компьютер пытается сделать ход в одну из угловых клеток. 107. # Пробуем занять один из углов, если есть свободные. 108. move = chooseRandomMoveFromList(board, [1, 3, 7, 9]) 109. if move != None: 110. return move Вызов функции chooseRandomMoveFromList() со списком [1, 3, 7, 9] гаран- тирует, что функция вернет целочисленное значение угловой клетки: 1, 3, 7 или 9. Если все угловые клетки заняты, функция chooseRandomMoveFromList() возвращает значение None, и программа переходит к строке 113. 112. # Пробуем занять центр, если он свободен. 113. if isSpaceFree(board, 5): 114. return 5 Если нет свободных угловых клеток, код в строке 114 совершает ход в центр, если он свободен. Если центр занят, выполняется код в строке 117. 116. # Делаем ход по одной стороне. 117. return chooseRandomMoveFromList(board, [2, 4, 6, 8]) Игра «Крестики-нолики» 187 Этот код тоже вызывает функцию chooseRandomMoveFromList(), только ей передается список [2, 4, 6, 8]. Эта функция не возвращает значения None, потому что боковые клетки — последние из возможных. На этом заканчива- ется выполнение функции getComputerMove() и алгоритма ИИ. Проверка — заполнено ли поле И последняя функция — isBoardFull(). 119. def isBoardFull(board): 120. # Возвращает True, если клетка на игровом поле занята. В противном случае, возвращает False. 121. for i in range(1, 10): 122. if isSpaceFree(board, i): 123. return False 124. return True Эта функция возвращает True, если в переменную board для элементов всех индексов (исключая индекс 0, который просто игнорируется) в качестве аргументов переданы буквы 'X' или 'O'. Цикл for позволяет проверить все индексы списка board, от 1 до 9. Как только в списке board будет найдена сво- бодная клетка (когда isSpaceFree(board, i) вернет True), функция isBoardFull() вернет False. Если произведены все итерации цикла, значит, свободных клеток не оста- лось. Строка 124 вернет значение True. Игровой цикл Строка 127 — первая, которая не принадлежит коду функций, то есть это первая исполняемая строка программы. 127. print('Игра "Крестики-нолики"') Эта строка приветствия перед началом игры. Затем программа переходит к выполнению цикла while в строке 129. 129. while True: 130. # Перезагрузка игрового поля 131. theBoard = [' '] * 10 188 Глава 10 Цикл while выполняется до тех пор, пока управление не будет передано инструкции break. Код в строке 131 сохраняет основное игровое поле игры «Крестики-нолики» в переменной theBoard. Сначала выводится пустое игровое поле, представленное списком из 10 пустых строк. Вместо того чтобы вводить этот список целиком, строка 131 использует репликацию списка. Проще ввести [' '] * 10, чем [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] Выбор буквы игрока и того, кто будет ходить первым Далее функция inputPlayerLetter() предлагает игроку ввести букву, кото- рую он выбирает, «Х» или «О» . 132. playerLetter, computerLetter = inputPlayerLetter() Функция возвращает список из двух элементов, ['X', 'O'] или ['O', 'X']. Операцией множественного присваивания переменной playerLetter присва- ивается значение первого элемента, а переменной computerLetter — второго. Затем функция whoGoesFirst() случайным образом выбирает, кому при- надлежит первый ход, возвращая строку 'Человек' или 'Компьютер', и в стро- ке 137 сообщает игроку о своем выборе. 133. turn = whoGoesFirst() 134. print('' + turn + ' ходит первым.') 135. gameIsPlaying = True Переменная gameIsPlaying хранит информацию о состоянии сеанса игры — продолжается ли игра, или победил один из участников, или вышла ничья. Переменная turn в значении 'Человек' Цикл в строке 137 будет метаться между кодом хода игрока и кодом хода компьютера до тех пор, пока переменной gameIsPlaying присвоено значение True. 137. while gameIsPlaying: 138. if turn == 'Человек': 139. # Ход игрока. 140. drawBoard(theBoard) 141. move = getPlayerMove(theBoard) 142. makeMove(theBoard, playerLetter, move) Игра «Крестики-нолики» 189 Переменной turn уже было присвоено значение 'Человек' или 'Компьютер' при вызове функции whoGoesFirst() в строке 133. Если turn присвоено значе- ние 'Компьютер', то условие в строке 138 ложно и управление переходит к коду в строке 156. Но если условие этой строки истинно, код в строке 140 вызывает функ- цию drawBoard() и передает значение переменной theBoard для вывода на экран в игровом поле Игры «Крестики-нолики». Затем функция getPlayerMove() предлагает игроку сделать ход и проверяет его допустимость. Функция makeMove() добавляет в переменную theBoard букву игрока «Х» или «О». После совершения игроком хода программа должна проверить, привел ли этот ход к победе. 144. if isWinner(theBoard, playerLetter): 145. drawBoard(theBoard) 146. print('Ура! Вы выиграли!') 147. gameIsPlaying = False Если функция isWinner() возвращает значение True, код из блока if выво- дит на экран сообщение о победе игрока. Значение переменной gameIsPlaying становится равным False, и перехода к совершению хода компьютером не происходит. Если игрок не побеждает при последнем своем ходе, то, может быть, поле заполнено с результатом — ничья. Эта вероятность проверяется в инструкции else. 148. else: 149. if isBoardFull(theBoard): 150. drawBoard(theBoard) 151. print('Ничья!') 152. break В блоке else функция isBoardFull() возвращает значение True, если боль- ше нет возможных ходов. В этом случае код из блока if в строке 149 выводит панель с сообщением игроку о ничьей. Программа выходит из цикла while и переходит к строке 173. Если не произошла ни победа игрока, ни ничья, программа переходит к следующей инструкции else. 153. else: 154. turn = 'Компьютер' 190 Глава 10 В строке 154 переменной turn присваивается значение 'Компьютер' и в следу- ющей итерации программа выполняет код, который совершает ход компьютера. Переменная turn в значении 'Компьютер' Если в условии строки 138 значение переменной turn не равно 'Человек', значит, оно должно быть равно 'Компьютер'. Код в этом блоке else такой же, как и для переменной turn в значении 'Человек'. 156. else: 157. # Ход компьютера. 158. move = getComputerMove(theBoard, computerLetter) 159. makeMove(theBoard, computerLetter, move) 160. 161. if isWinner(theBoard, computerLetter): 162. drawBoard(theBoard) 163. print('Компьютер победил! Вы проиграли.') 164. gameIsPlaying = False 165. else: 166. if isBoardFull(theBoard): 167. drawBoard(theBoard) 168. print('Ничья!') 169. break 170. else: 171. turn = 'Человек' Строки 157–171 почти идентичны строкам 139–154. Единственное от- личие в том, что используется буква компьютера и вызывается функция getComputerMove() Если нет победителя или ничьей, код в строке 171 присваивает перемен- ной turn значение 'Человек'. В этом цикле while больше нет строк, поэтому управление передается инструкции while в строке 137. Предложение игроку сыграть заново В финале игры программа спрашивает игрока, хочет ли он сыграть еще раз. 173. print('Сыграем еще раз? (да или нет)') 174. if not input().lower().startswith('д'): 175. break Код в строках 173–175 выполняется сразу после старта цикла в строке 137. Переменной gameIsPlaying присваивается значение False, когда завершается игра. В этот момент программа спрашивает игрока, не хочет ли он сыграть еще раз. Выражение not input().lower().startswith('д') принимает значение True, если игрок введет какую-либо строку, не начинающуюся с буквы 'д'. В этом случае будет выполнена инструкция break. Эта инструкция прерывает вы- полнение цикла while, который был запущен кодом в строке 129. Но так как в программе больше нет строк для выполнения, происходит выход из про- граммы и завершение игры. Заключение Создание программы с искусственным интеллектом сводится к тщатель- ному рассмотрению всех возможных ситуаций, с которыми он может стол- кнуться, и вариантов его реакции на каждую из этих ситуаций. Искусствен- ный интеллект игры «Крестики-нолики» прост, потому что в игре немного возможных ходов, по сравнению, скажем, с шахматами или шашками. Сначала наш компьютерный ИИ проверяет, есть ли у него победный ход. Затем проверяет необходимость блокировки ходов игрока. Далее ИИ просто выбирает свободную угловую клетку, потом центральную, а затем боковую. Это простой алгоритм для совершения хода. Ключ к реализации ИИ заключается в создании копии данных игрового поля и моделирования ходов в этой копии. Таким образом, ИИ может видеть, приведет ли ход к победе или поражению. Затем ИИ совершает ход в реаль- ном поле. Этот тип моделирования эффективен при прогнозировании ре- зультатов предложенного хода. 192 Глава 11 11 ДЕДУКТИВНАЯ ИГРА «ХОЛОДНО-ГОРЯЧО» «Холодно-горячо» — это дедуктивная игра, в которой игрок пытается угадать случайное трехзначное число (без по- вторяющихся цифр), сгенерированное компьютером. После каждой попытки компьютер предоставляет игроку подсказ- ки трех типов: • Холодно. Ни одна цифра не отгадана. • Тепло. Одна цифра отгадана, но не отгадана ее позиция. • Горячо. Одна цифра и ее позиция отгаданы. Компьютер может дать несколько подсказок, сортируемых в алфавитном порядке. Если секретное число 456, а предположение игрока — 546, подсказ- ки будут иметь вид «Горячо Тепло Тепло». Подсказка «Горячо» относится к 6, а «Тепло Тепло» — к 4 и 5. В этой главе вы изучите несколько новых методов и функций, доступных в Python. Вы также узнаете о расширенных операторах присваивания и ин- терполяции строк. Хотя эти возможности и не позволяют делать ничего прин- ципиально нового, это хороший способ сэкономить время, упрощая код. В ЭТОЙ ГЛАВЕ РАССМАТРИВАЮТСЯ СЛЕДУЮЩИЕ ТЕМЫ: • Функция random.shuffle() • Расширенные операторы присваивания, +=, -=, *= и /= • Метод списка sort() • Строковый метод join() • Интерполяция строк • Спецификатор преобразования %s • Вложенные циклы Дедуктивная игра «Холодно-горячо» 193 Пример запуска игры «Холодно-горячо» Вот что видит пользователь, когда запускается программа «Холодно- горячо» . Текст, который вводит игрок, выделен полужирным шрифтом. Я загадаю 3-х значное число, которое вы должны отгадать. Я дам несколько подсказок... Когда я говорю: Это означает: Холодно Ни одна цифра не отгадана. Тепло Одна цифра отгадана, но не отгадана ее позиция. Горячо Одна цифра и ее позиция отгаданы. Итак, я загадал число. У вас есть 10 попыток, чтобы отгадать его. Попытка №1: 123 Тепло Попытка №2: 245 Тепло Попытка №3: 672 Горячо Горячо Попытка №4: 682 Горячо Горячо Попытка №5: 692 Вы угадали! Хотите сыграть еще раз? (да или нет) нет Исходный код игры «Холодно-горячо» В редакторе файлов создайте новый файл, вы- брав команду меню File ⇒ New File (Файл ⇒ Новый файл). В открывшемся окне введите приведенный ниже исходный код и сохраните файл под именем bagels.py. Затем нажмите клавишу F5 и запусти- те программу. Если при выполнении программы возникают ошибки, сравните код, который вы на- брали, с оригинальным кодом с помощью онлайн- инструмента на сайте inventwithpython.com/diff/. Make sure you’re using Python 3, not Python 2! УБЕ ДИТЕСЬ, ЧТО ИСПО ЛЬЗУЕТЕ PY THON 3, А НЕ PY THON 2! 194 Глава 11 bagels.py 1. import random 2. 3. NUM_DIGITS = 3 4. MAX_GUESS = 10 5. 6. def getSecretNum(): 7. # Возвращает строку уникальных случайных цифр, длина которой составляет NUM_DIGITS. 8. numbers = list(range(10)) 9. random.shuffle(numbers) 10. secretNum = '' 11. for i in range(NUM_DIGITS): 12. secretNum += str(numbers[i]) 13. return secretNum 14. 15. def getClues(guess, secretNum): 16. # Возвращает строку с подсказками пользователю "Тепло", "Горячо" и "Холодно". 17. if guess == secretNum: 18. return 'Вы угадали!' 19. 20. clues = [] 21. for i in range(len(guess)): 22. if guess[i] == secretNum[i]: 23. clues.append('Горячо') 24. elif guess[i] in secretNum: 25. clues.append('Тепло') 26. if len(clues) == 0: 27. return 'Холодно' 28. 29. clues.sort() 30. return ' '.join(clues) 31. 32. def isOnlyDigits(num): 33. # Возвращает значение True, если num - строка, состоящая только из цифр. В противном случае возвращает False. 34. if num == '': 35. return False 36. 37. for i in num: 38. if i not in '0 1 2 3 4 5 6 7 8 9'.split(): |