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

  • Проектирование программы

  • Импорт модулей random, sys и math

  • Генерация игрового поля

  • Изображение координат

  • Рисование океана

  • Учим 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
    страница22 из 39
    1   ...   18   19   20   21   22   23   24   25   ...   39

    223
    29. print()
    30.
    31. # Вывести каждый из 15 рядов.
    32. for row in range(15):
    33. # К однозначным числам нужно добавить дополнительный пробел.
    34. if row < 10:
    35. extraSpace = ' '
    36. else:
    37. extraSpace = ''
    38.
    39. # Создать строку для этого ряда на игровом поле.
    40. boardRow = ''
    41. for column in range(60):
    42. boardRow += board[column][row]
    43.
    44. print('%s%s %s %s' % (extraSpace, row, boardRow, row))
    45.
    46. # Вывести числа в нижней части поля.
    47. print()
    48. print(' ' + ('0123456789' * 6))
    49. print(tensDigitsLine)
    50.
    51. def getRandomChests(numChests):
    52. # Создать список структур данных сундука (двухэлементные списки целочисленных координат x и y)).
    53. chests = []
    54. while len(chests) < numChests:
    55. newChest = [random.randint(0, 59), random.randint(0, 14)]
    56. if newChest not in chests: # Убедиться, что сундука здесь еще нет.
    57. chests.append(newChest)
    58. return chests
    59.
    60. def isOnBoard(x, y):
    61. # Возвращать True, если координаты есть на поле; в противном случае возвращать False.
    62. return x >= 0 and x <= 59 and y >= 0 and y <= 14 63.
    64. def makeMove(board, chests, x, y):
    65. # Изменить структуру данных поля, используя символ гидролокатора. Удалить сундуки
    66. # с сокровищами из списка с сундуками, как только их нашли. Вернуть False, если это
    67. # недопустимый ход. В противном случае, вернуть строку с результатом этого хода.
    68. smallestDistance = 100 # Все сундуки будут расположены ближе, чем на расстоянии в 100 единиц.
    69. for cx, cy in chests:
    70. distance = math.sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y))
    71.

    224
    Глава 13 72. if distance < smallestDistance: # Нам нужен ближайший сундук с сокровищами.
    73. smallestDistance = distance
    74.
    75. smallestDistance = round(smallestDistance)
    76.
    77. if smallestDistance == 0:
    78. # Координаты xy попали прямо в сундук с сокровищами!
    79. chests.remove([x, y])
    80. return 'Вы нашли сундук с сокровищами на затонувшем судне!'
    81. else:
    82. if smallestDistance < 10:
    83. board[x][y] = str(smallestDistance)
    84. return 'Сундук с сокровищами обнаружен на расстоянии %s от гидролокатора.' % (smallestDistance)
    85. else:
    86. board[x][y] = 'X'
    87. return 'Гидролокатор ничего не обнаружил. Все сундуки с сокровищами вне пределов досягаемости.'
    88.
    89. def enterPlayerMove(previousMoves):
    90. # Позволить игроку сделать ход. Вернуть двухэлементный список с целыми координатами x и y.
    91. print('Где следует опустить гидролокатор? (координаты: 0-59 0-14) (или введите "выход")')
    92. while True:
    93. move = input()
    94. if move.lower() == 'выход':
    95. print('Спасибо за игру!')
    96. sys.exit()
    97.
    98. move = move.split()
    99. if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isOnBoard(int(move[0]), int(move[1])):
    100. if [int(move[0]), int(move[1])] in previousMoves:
    101. print('Здесь вы уже опускали гидролокатор.')
    102. continue
    103. return [int(move[0]), int(move[1])]
    104.
    105. print('Введите число от 0 до 59, потом пробел, а затем число от 0 до 14.')
    106.
    107. def showInstructions():
    108. print('''Инструктаж:
    109. Вы - капитан корабля, плывущего за сокровищами. Ваша задача - с помощью
    110. гидролокаторов найти три сундука с сокровищами в затонувших судах на дне океана.
    111. Но гидролокаторы очень просты и определяют только расстояние, но не направление.
    112. Введите координаты, чтобы опустить гидролокатор в воду. На карте будет показано
    113. число, обозначающее, на каком расстоянии находится ближайший сундук. Или будет
    114. показана буква Х, обозначающая, что сундук в области действия гидролокатора не

    Игра «Охотник за сокровищами»
    225
    115. обнаружен. На карте ниже метки C - это сундуки.
    116. Цифра 3 обозначает, что ближайший сундук находится на отдалении в 3 единицы.
    117.
    118. 1 2 3 119. 012345678901234567890123456789012 120.
    121. 0

    ``````````````` 0 122. 1 `````````````` 1 123. 2 ``C``3``C`````````` 2 124. 3 ``````````````````````` 3 125. 4 ````C`````````` 4 126.
    127. 012345678901234567890123456789012 128. 1 2 3 129. (Во время игры сундуки на карте не обозначаются!)
    130.
    131. Нажмите клавишу Enter, чтобы продолжить...''')
    132. input()
    133.
    134. print('''Если гидролокатор опущен прямо на сундук, вы сможете поднять
    135. сундук. Другие гидролокаторы обновят данные о расположении ближайшего сундука.
    136. Сундуки ниже находятся вне диапазона локатора, поэтому отображается буква X.
    137.
    138. 1 2 3 139. 012345678901234567890123456789012 140.
    141. 0 ``````````````` 0 142. 1 `````````````` 1 143. 2 ``X``7``C`````````` 2 144. 3 ``````````````````````` 3 145. 4 ````C`````````` 4 146.
    147. 012345678901234567890123456789012 148. 1 2 3 149.
    150. Сундуки с сокровищами не перемещаются. Гидролокаторы определяют сундуки
    151. на расстоянии до 9 единиц. Попробуйте поднять все 3 сундука до того, как все
    152. гидролокаторы будут опущены на дно. Удачи!
    153.
    154. Нажмите клавишу Enter, чтобы продолжить...''')
    155. input()
    156.
    157.

    226
    Глава 13 158.
    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 = []
    172.
    173. while sonarDevices > 0:
    174. # Показать гидролокаторные устройства и сундуки с сокровищами.
    175. print('Осталось гидролокаторов: %s. Осталось сундуков с сокровищами: %s.' % (sonarDevices, len(theChests)))
    176.
    177. x, y = enterPlayerMove(previousMoves)
    178. previousMoves.append([x, y]) # Мы должны отслеживать все ходы, чтобы гидролокаторы могли обновляться.
    179.
    180. moveResult = makeMove(theBoard, theChests, x, y)
    181. if moveResult == False:
    182. continue
    183. else:
    184. if moveResult == 'Вы нашли сундук с сокровищами на затонувшем судне!':
    185. # Обновить все гидролокаторные устройства, в настоящее время находящиеся на карте.
    186. for x, y in previousMoves:
    187. makeMove(theBoard, theChests, x, y)
    188. drawBoard(theBoard)
    189. print(moveResult)
    190.
    191. if len(theChests) == 0:
    192. print('Вы нашли все сундуки с сокровищами на затонувших судах! Поздравляем и приятной игры!')
    193. break
    194.
    195. sonarDevices -= 1 196.
    197. if sonarDevices == 0:
    198. print('Все гидролокаторы опущены на дно! Придется разворачивать корабль и')
    199. print('отправляться домой, в порт! Игра окончена.')

    Игра «Охотник за сокровищами»
    227
    200. print('Вы не нашли сундуки в следующих местах:')
    201. for x, y in theChests:
    202. print(' %s, %s' % (x, y))
    203.
    204. print('Хотите сыграть еще раз? (да или нет)')
    205. if not input().lower().startswith('д'):
    206. sys.exit()
    Проектирование программы
    Прежде чем пытаться понять исходный код, сыграйте в игру несколько раз. Игра «Охотник за сокровищами» использует списки списков и другие сложные переменные, называемые структурами данных . Структуры данных хранят значения в определенном порядке и используются для представле- ния чего-либо. Например, в игре «Крестики-нолики» из главы 10 структура данных игрового поля представляла собой список строк. Строка представ- ляла собой X, O или пустое значение, а индекс строки в списке — позицию на поле. Код игры «Охотник за сокровищами» будет включать аналогичные структуры данных для местоположений сундуков с сокровищами и гидро- локаторов.
    Импорт модулей random, sys и math
    В начале программы импортируем модули random, sys и math .
    1. # Охотник за сокровищами
    2.
    3. import random
    4. import sys
    5. import math
    Модуль sys содержит функцию exit() , которая немедленно завершает работу программы. Ни одна из строк кода не будет выполняться после вы- зова функции sys.exit(); программа просто прекращает работу, как будто интерпретатор достиг конца кода. Эта функция используется в программе позже.
    Модуль math содержит функцию sqrt() , которая используется для вы- числения квадратного корня из числа. Соответствующие вычисления объяс- няются в разделе «Поиск ближайшего сундука с сокровищами» далее в этой главе.

    228
    Глава 13
    Создание поля для новой игры
    Для начала каждой новой игры требуется новая структура данных board, которая создается функцией getNewBoard(). Игровое поле программы «Охот- ник за сокровищами» генерируется с помощью ASCII-символов и координат
    x и y вокруг него.
    Когда мы используем структуру данных board, то хотим получить доступ к ее системе координат таким же образом, как обращаемся к декартовым координатам. Для этого мы будем использовать список списков, вызывая каждую координату на поле следующим образом: board[х][у]. Координата x указывается перед координатой y — для получения строки с координатами
    (26, 12) вы должны использовать код board[26][12], а не board[12][26].
    7. def getNewBoard():
    8. # Создать структуру данных нового игрового поля размером 60х15.
    9. board = []
    10. for x in range(60): # Главный список из 60 списков.
    11. board.append([])
    12. for y in range(15): # Каждый список в главном списке содержит 15 односимвольных строк.
    13. # Для создания океана используем разные символы, чтобы сделать его реалистичнее.
    14. if random.randint(0, 1) == 0:
    15. board[x].append('')
    16. else:
    17. board[x].append('`')
    Структура данных board представляет собой список списков строк. Пер- вый список представляет координату x. Поскольку ширина игрового поля со- ставляет 60 символов, в этом первом списке должно быть 60 списков. В стро- ке кода 10 мы создаем цикл for, который добавит к нему 60 пустых списков.
    Но board — это не просто список из 60 пустых списков. Каждый из этих
    60 списков представляет координату x игрового поля. У поля 15 столбцов, поэтому каждый из этих 60 списков должен содержать 15 строк. Код в стро- ке 12 — еще один цикл for, который добавляет 15 строк из символов, пред- ставляющих океан.
    Океан будет массой случайно выбранных строк '' и '`'. Символы тиль- ды () и кавычки (`), расположенные рядом с клавишей 1 на клавиатуре, бу- дут использоваться для создания океанских волн. Для определения символа, который необходимо использовать, строки кода с 14 по 17 применяют такую логику: если возвращаемое random.randint() значение равно 0, добавляется строка ''; в противном случае — строка '`'. Это придаст океану случайный, изменчивый вид.

    Игра «Охотник за сокровищами»
    229
    В качестве небольшого примера, если списку board присвоено значение
    [['', '', '`'], [['', '', '`'], [['', '', '`'], ['', '`', '`'], ['`',
    '', '`']]
    , тогда поле на экране будет выглядеть так:
    `
    `
    `````
    Наконец, функция возвращает значение в переменной board в строке кода 18.
    18. return board
    Генерация игрового поля
    Затем определим метод drawBoard(), который мы будем вызывать, чтобы создать новое поле:
    20. def drawBoard(board):
    Полностью поле с координатами по его краям выглядит вот так:
    1 2 3 4 5 012345678901234567890123456789012345678901234567890123456789 0 ````````````````````````````` 0 1 ``````````````````````````````````` 1 2 ```````````````````````````` 2 3 `````````````````````````````````` 3 4 ````````````````````````````` 4 5 `````````````````````````````` 5 6 ``````````````````````````````` 6 7 ```````````````````````````` 7 8 ``````````````````````````` 8 9 ```````````````````````````````` 9 10 ````````````````````````````````````` 10 11 `````````````````````````````````` 11 12 ````````````````````````````````` 12 13 ````````````````````````````````` 13 14 ````````````````````````````````` 14 012345678901234567890123456789012345678901234567890123456789 1 2 3 4 5

    230
    Глава 13
    Генерация поля с помощью функции drawBoard() подразумевает четыре этапа:
    1. Создание строковой переменной строки поля с числами 1, 2, 3, 4 и 5, разделенными широкими промежутками. Эти числа обозначают ко- ординаты 10, 20, 30, 40 и 50 по оси x.
    2. Применение этой строки, чтобы отобразить координаты оси x в верх- ней части экрана.
    3. Вывод каждой строки океана вместе с координатами оси y по обе сто- роны экрана.
    4. Повторный вывод оси x внизу. Видя координаты со всех сторон, про- ще понять, где нужно опустить гидролокатор.
    Изображение координат x вдоль верхней части поля
    Первая часть функции drawBoard() выводит ось x в верхней части поля.
    Поскольку клетки поля должны быть одного размера, каждая метка ко- ординат может соответствовать лишь одному символу. Когда нумерация координат достигает 10, число станет двузначным. Для корректного ото- бражения мы помещаем второй знак (показатель десятков) на отдельной строке поля в позиции, как показано на рис. 13.3. Ось x организована таким образом, что первая строка поля показывает разряды десятков, а вторая — единицы.
    +++++++++++++
    1
    +++++++++
    2
    +++++++++
    3
    # First line
    +++
    0123456789012345678901234567890123456789
    # Second line
    +
    0 ```````````````````````` 0
    # Third line
    # Первая строка
    # Вторая строка
    # Третья строка
    Рис. 13.3.
    Расположение символов при выводе верхней части игрового поля
    Строки кода с 22 по 24 создают строку для первой строки поля, которая представляет собой часть оси x с десятками.
    21. # Изобразить структуру данных игрового поля.
    22. tensDigitsLine = ' ' # Создать место для чисел вниз по левой стороне поля.
    23. for i in range(1, 6):
    24. tensDigitsLine += (' ' * 9) + str(i)
    Цифры, обозначающие позиции десятков в первой строке поля, разделе- ны 9 пробелами, а перед цифрой 1 содержится 13 пробелов.
    Строки кода 22–24 создают эту строку поля и сохраняют ее в переменной tensDigitsLine
    :

    Игра «Охотник за сокровищами»
    231
    26. # Вывести числа в верхней части поля.
    27. print(tensDigitsLine)
    28. print(' ' + ('0123456789' * 6))
    29. print()
    Чтобы вывести числа в верхней части игрового поля, сначала выведите содержимое переменной tensDigitsLine.
    Затем в следующей строке кода выведите три пробела (так, чтобы этот ряд выстроился правильно), а затем выведите строку '0123456789' шесть раз:
    ('0123456789' * 6).
    Рисование океана
    Код в строках 32–44 выводит каждый ряд океанских волн, включая числа для обозначения оси y, идущие сверху вниз по бокам поля.
    31. # Вывести каждый из 15 рядов.
    32. for row in range(15):
    33. # К однозначным числам нужно добавить дополнительный пробел.
    34. if row < 10:
    35. extraSpace = ' '
    36. else:
    37. extraSpace = ''
    Цикл for выводит ряды с 0 по 14, а также их номера по обе стороны поля.
    Но у нас та же проблема, что и с осью х. Числа с одной цифрой (например,
    0
    , 1, 2 и т. д.) при выводе занимают только одну позицию, но числа с двумя цифрами (например, 10, 11 и 12) занимают две позиции. Ряды не выстроятся правильно, если координаты будут разного размера. Поле будет выглядеть следующим образом:
    8 ``````````````````````````` 8 9 ```````````````````````````````` 9 10 ````````````````````````````````````` 10 11 `````````````````````````````````` 11

    1   ...   18   19   20   21   22   23   24   25   ...   39


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