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

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

  • Создание случайных сундуков с сокровищами

  • Отражение хода на игровом поле

  • Поиск ближайшего сундука с сокровищами

  • Удаление значений с помощью метода списка remove()

  • Получение хода игрока Функция enterPlayerMove() получает координаты x и y следующего хода игрока. 240

  • 42.isdigit() True>>> сорок.isdigit() False>>> .isdigit() False>>> привет.isdigit() False>>> x = 10

  • Учим 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
    страница23 из 39
    1   ...   19   20   21   22   23   24   25   26   ...   39
    232
    Глава 13
    Выход прост: добавьте пробел перед каждым однозначным числом. Стро- ки кода 34–37 задают переменную extraSpace равной либо пробелу, либо пу- стой строке. Переменная extraSpace выводится всегда, но символ пробела она содержит только при однозначных номерах рядов, в противном случае при- нимая значение пустой строки. Таким образом мы обеспечим правильный вывод рядов на экран.
    Вывод ряда в океане
    Параметр board — это структура данных, отвечающая за все океан- ские волны. Строки кода 39–44 считывают переменную board и выводят один ряд.
    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))
    В строке кода 40 переменная boardRow сначала принимает значение пу- стой строки. Цикл for в строке кода 32 заставляет переменную row выво- дить текущий ряд океанских волн. Внутри этого цикла в строке кода 41 есть еще один цикл for, который перебирает каждый столбец текущего ряда. В этом цикле мы присваиваем переменной boardRow результат конка- тенации board[column][row], что подразумевает объединение board[0][row], board[1][row]
    , board[2][row] и так далее до board[59][row]. Это связано с тем, что ряд содержит 60 символов, начиная с индекса 0 и заканчивая индек- сом 59.
    Цикл for в строке кода 41 перебирает целые числа от 0 до 59. При каждой такой итерации, следующий символ в структуре данных игрового поля ко- пируется в конец boardRow. К моменту выхода из цикла переменная boardRow содержит полностью нарисованные с помощью ASCII волны ряда. Затем в строке кода 44 выводится строка в переменной boardRow вместе с номерами рядов.
    Изображение координат x вдоль нижней части игрового поля
    Код в строках 46–49 аналогичен строкам кода 26–29.

    Игра «Охотник за сокровищами»
    233
    46. # Вывести числа в нижней части поля.
    47. print()
    48. print(' ' + ('0123456789' * 6))
    49. print(tensDigitsLine)
    Эти строки выводят ось координат x в нижней части поля.
    Создание случайных сундуков с сокровищами
    Код в игре случайным образом размещает скрытые сундуки с сокрови- щами. Сундуки с сокровищами представлены в виде списка списков из двух целых чисел. Эти два числа представляют собой координаты x и y каждого сундука. Например, если структура данных сундука была [[2, 2], [2, 4],
    [10, 0]]
    , то это означало бы наличие трех сундуков с сокровищами, одного в позиции с координатами (2, 2), другого — в позиции (2, 4) и третьего — в позиции (10, 0).
    Функция getRandomChests() создает определенное количество структур данных сундука в случайных координатах.
    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
    Параметр numChests сообщает функции о том, сколько сундуков с со- кровищами нужно сгенерировать. Цикл while в строке кода 54 будет по- вторяться до тех пор, пока всем сундукам не будут назначены координаты.
    Для координат в строке кода 55 выбраны два случайных целых числа. Ко- ордината x может быть любой в диапазоне значений от 0 до 59, а коорди- ната y — в диапазоне от 0 до 14. Выражение [random.randint(0, 59), random.
    randint(0, 14)]
    станет списком наподобие [2, 2],[2, 4] или [10, 0]. Если эти координаты еще не содержатся в списке chests, они присоединяются к нему в строке кода 57.

    234
    Глава 13
    Определение допустимости хода
    Когда игрок вводит координаты x и y позиции, где он желает опустить гидролокатор в воду, нужно убедиться, что введенные числа допустимы.
    Как упоминалось ранее, существуют два условия для того, чтобы ход был допустимым: значение координаты x должно находиться в диапазоне от 0 до
    59, а координаты y — в диапазоне от 0 до 14.
    В коде функции isOnBoard() используются операторы and, чтобы объеди- нить упомянутые условия в одно выражение и удостовериться, что каждая часть выражения истинна.
    60. def isOnBoard(x, y):
    61. # Возвращать True, если координаты есть на поле; в противном случае возвращать False.
    62. return x >= 0 and x <= 59 and y >= 0 and y <= 14
    Поскольку мы используем логический оператор and, все выражение ста- новится ложным, если хотя бы одна из координат недопустима.
    Отражение хода на игровом поле
    В игре «Охотник за сокровищами» игровое поле обновляется, чтобы ото- бразить число, указывающее расстояние от каждого гидролокатора до бли- жайшего сундука с сокровищами. Поэтому, когда игрок делает ход, задавая программе координаты x и y, поле изменяется в зависимости от местонахож- дения сундуков с сокровищами.
    64. def makeMove(board, chests, x, y):
    65. # Изменить структуру данных поля, используя символ гидролокатора. Удалить сундуки
    66. # с сокровищами из списка с сундуками, как только их нашли. Вернуть False, если это
    67. # недопустимый ход. В противном случае, вернуть строку с результатом этого хода.
    Функция makeMove() принимает четыре параметра: структуру данных игрового поля, структуру данных сундука с сокровищами, координату x и ко- ординату y. Функция makeMove() возвращает строковое значение, описываю- щее, что произошло в ответ на ход.
    • Если координаты попадают прямо на сундук с сокровищами, функ- ция makeMove() возвращает 'Вы нашли сундук с сокровищами на затонувшем судне!'

    Игра «Охотник за сокровищами»
    235
    • Если координаты находятся от сундука на расстоянии 9 или менее единиц, функция makeMove() возвращает 'Сундук с сокровищами обна- ружен на расстоянии %s от гидролокатора.'
    (где %s заменяется на целое значение расстояния).

    В противном случае функция makeMove() вернет 'Гидролокатор ничего не обнаружил. Все сундуки с сокровищами вне пределов досягае мости.'
    Имея координаты позиции, в которой игрок хочет опустить в воду ги- дролокатор, и список координат x и y для сундуков с сокровищами, вам по- надобится алгоритм, чтобы узнать, какой сундук с сокровищами находится ближе всего.
    Поиск ближайшего сундука с сокровищами
    Строки кода 68–75 содержат алгоритм определения, какой сундук с со- кровищами находится ближе всего к гидролокатору.
    68. smallestDistance = 100 # Все сундуки будут расположены ближе, чем на расстоянии в 100 единиц.
    69. for cx, cy in chests:
    70. distance = math.sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y))
    71.
    72. if distance < smallestDistance: # Нам нужен ближайший сундук с сокровищами.
    73. smallestDistance = distance
    Параметры x и y являются целыми числами (например, 3 и 5), и в паре они обозначают позицию на игровом поле, которую игрок указал в своем предположении. Переменная chests получит значение типа [[5, 0], [0, 2],
    [4, 2]]
    , представляющее собой расположение трех сундуков с сокровищами.
    На рис. 13.4 это значение представлено в графическом виде.
    Чтобы определить расстояние между гидролокатором и сундуком с со- кровищами, нам нужно будет произвести некоторые арифметические расче- ты и найти расстояние между двумя координатами x и двумя y. Предположим, мы поместили гидролокатор в позицию (3, 5) и хотим определить расстояние до сундука с сокровищами, находящегося в позиции (4, 2).
    Чтобы определить расстояние между двумя наборами координат x и y, мы воспользуемся теоремой Пифагора. Эта теорема применима к прямоугольным
    треугольникам — таким, у которых один угол равен 90 градусам (такие же углы у прямоугольника). В теореме Пифагора сообщается, что диагональную сторону треугольника можно рассчитать исходя из длин горизонтальной и вертикальной сторон.

    236
    Глава 13 0
    0 1
    2 3
    4 5
    6 7
    1 2
    3 4
    5 6
    Рис. 13.4.
    Сундуки с сокровищами, представленные значением [[5, 0], [0, 2], [4, 2]]
    На рис. 13.5 показан прямоугольный треугольник, образованный путем соединения позиций гидролокатора (3, 5) и сундука с сокровищами (4, 2).
    0 0
    1 2
    3 4
    5 6
    7 1
    2 3
    4 5
    6
    a
    b
    c
    0 0
    1 2
    3 4
    5 6
    7 1
    2 3
    4 5
    6
    a
    b
    c
    Рис. 13.5.
    Поле с прямоугольным треугольником, соединяющим гидролокатор и сундук с сокровищами
    Теорема Пифагора выглядит как a
    2
    + b
    2
    = c
    2
    , где a — длина горизонтальной стороны, b — длина вертикальной стороны, c — длина диагональной стороны или гипотенузы. Эти длины возведены в квадрат, то есть числа были умноже- ны сами на себя. Обратная операция называется нахождением квадратного
    корня числа — это то, что мы делаем, чтобы получить c из c
    2
    Давайте воспользуемся теоремой Пифагора, чтобы определить расстоя- ние между гидролокатором (3, 5) и сундуком (4, 2).
    1. Чтобы вычислить
    a, вычтите вторую координату x (4) из первой коор- динаты x (3): 3 − 4 = −1.
    2. Чтобы определить
    a
    2
    , умножьте a на a: −1 × −1 = 1. (Отрицательное чис- ло, умноженное на отрицательное, всегда дает положительное число.)
    3. Чтобы вычислить
    b, вычтите вторую координату y (2) из первой коор- динаты y (5): 5 − 2 = 3.

    Игра «Охотник за сокровищами»
    237
    4. Чтобы вычислить
    b
    2
    , умножьте b на b: 3 × 3 = 9.
    5. Чтобы определить
    c
    2
    , сложите a
    2
    и b
    2
    : 1 + 9 = 10.
    6. Чтобы получить
    c из c
    2
    , вам нужно найти квадратный корень c
    2
    Модуль math, который мы импортировали в строке кода 5, содержит функцию с именем sqrt(), вычисляющую квадратный корень. В интерактив- ной оболочке введите следующие команды:
    >>>
    import math
    >>>
    math.sqrt(10)
    3.1622776601683795
    >>>
    3.1622776601683795 * 3.1622776601683795
    10.000000000000002
    Обратите внимание, что результатом умножения квадратного корня чис- ла на самого себя будет это число. (Дополнительная 2 в конце 10 — результат погрешности нахождения корня числа 10.)
    Передав значение c
    2
    функции sqrt(), можно утверждать, что сундук с со- кровищами находится на расстоянии 3,16 единицы от гидролокатора. Игра округлит это значение до 3.
    Давайте снова взглянем на строки кода с 68 по 70.
    68. smallestDistance = 100 # Все сундуки будут расположены ближе, чем на расстоянии в 100 единиц.
    69. for cx, cy in chests:
    70. distance = math.sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y))
    Код внутри цикла for в строке кода 69 вычисляет расстояние до каждого сундука.
    В начале цикла код в строке 68 присваивает переменной smallestDistance невероятно большое расстояние в 100 единиц, так что по крайней мере один из сундуков с сокровищами, что вы найдете, будет помещен в smallestDistance в строке кода 73.
    Так как cx – x представляет собой горизонтальное расстояние a между сундуком и гидролокатором, то (cx – x) * (cx – x) — это, согласно нашей теореме Пифагора, а
    2
    Значение а
    2
    добавляется к (cy – y) * (cy – y), то есть к b
    2
    . Эта сумма равна
    c
    2
    и передается функции sqrt(), чтобы определить расстояние между сунду- ком и гидролокатором.
    Нам нужно определить расстояние между гидролокатором и ближайшим сундуком, поэтому, если это расстояние меньше, чем минимальное, оно со- храняется как новое минимальное расстояние в строке кода 73.

    238
    Глава 13 72. if distance < smallestDistance: # Нам нужен ближайший сундук с сокровищами.
    73. smallestDistance = distance
    К моменту завершения цикла for вам известно, что переменная smallestDistance содержит наиболее короткое расстояние между гидролока- тором и всеми существующими в игре сундуками с сокровищами.
    Удаление значений с помощью метода списка remove()
    Метод списка remove() удаляет первое вхождение значения, соответству- ющее переданному аргументу. Например, введите следующий код в интерак- тивной оболочке:
    >>>
    x = [42, 5, 10, 42, 15, 42]
    >>>
    x.remove(10)
    >>>
    x
    [42, 5, 42, 15, 42]
    Значение 10 было удалено из списка x.
    Теперь введите в интерактивной оболочке следующее:
    >>>
    x = [42, 5, 10, 42, 15, 42]
    >>>
    x.remove(42)
    >>>
    x
    [5, 10, 42, 15, 42]
    Обратите внимание, что было удалено только первое значение 42, а вто- рое и третье остались на месте. Метод remove() удаляет только первое вхож- дение значения, которое вы ему передаете.
    Если вы попытаетесь удалить значение, которого нет в списке, то получи- те сообщение об ошибке.
    >>>
    x = [5, 42]
    >>>
    x.remove(10)
    Traceback (most recent call last):
    File "", line 1, in
    ValueError: list.remove(x): x not in list

    Игра «Охотник за сокровищами»
    239
    Как и append(), метод remove() вызывается в списке и не возвращает список.
    Вам необходимо использовать код типа x.remove(42), а не x = x.remove(42).
    Давайте вернемся к определению расстояний между гидролокатора- ми и сундуками с сокровищами. Единственный случай, когда переменная smallestDistance принимает значение 0, — когда координаты x и y гидролока- тора совпадают с соответствующими координатами сундука. Это значит, что игрок угадал местонахождение сундука с сокровищами.
    77. if smallestDistance == 0:
    78. # Координаты xy попали прямо в сундук с сокровищами!
    79. chests.remove([x, y])
    80. return 'Вы нашли сундук с сокровищами на затонувшем судне!'
    В этом случае программа с помощью метода списка remove() удаляет из структуры данных chests список из двух чисел, относящийся к этому сунду- ку. Затем функция возвращает строку 'Вы нашли сундук с сокровищами на за- тонувшем судне!'
    Но если переменная smallestDistance не равна 0, это значит, что игрок не угадал точное местоположение сундука с сокровищами, и запускается блок else со строки 81.
    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 'Гидролокатор ничего не обнаружил. Все сундуки с сокровищами вне пределов досягаемости.'
    Если расстояние от гидролокатора до сундука с сокровищами меньше 10, код в строке 83 отмечает поле строковой версией переменной smallestDistance.
    В противном случае на поле выводится символ 'X'.
    Таким образом, игрок знает, насколько близко каждый гидролокатор рас- положен к сундуку с сокровищами. Если игрок видит X, то понимает, что он очень далеко.
    Получение хода игрока
    Функция enterPlayerMove() получает координаты x и y следующего хода игрока.

    240
    Глава 13 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()
    Параметр previousMoves представляет собой список списков с двумя це- лыми числами, обозначающими предыдущие позиции, в которых игрок опу- скал гидролокатор. Это нужно для того, чтобы игрок не сделал повторный ход в одной и той же позиции. Цикл while будет продолжать предлагать игро- ку сделать следующий ход, пока он не введет координаты позиции, где еще нет гидролокатора. Игрок может также ввести 'выход', чтобы выйти из игры.
    В этом случае код в строке 96 вызывает функцию sys.exit(), немедленно за- вершающую работу программы.
    Предполагая, что игрок не ввел 'выход', программа проверяет, что ввод представляет собой два целых числа, разделенных пробелом. Код в строке 98 вызывает метод split() в списке move как новое значение move.
    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.')
    Если игрок ввел значение, подобное '1 2 3', тогда список, возвращаемый функцией split(), будет иметь вид ['1', '2', '3']. В этом случае выраже- ние len(move) == 2 станет ложным (список позиции должен содержать лишь два числа, поскольку он представляет собой координаты), и все выражение немедленно станет ложным. Интерпретатор Python не проверяет остальную часть выражения из-за короткого замыкания (описанного в разделе «Вычис- ление по короткой схеме» главы 10).

    Игра «Охотник за сокровищами»
    241
    Если длина списка равна 2, то два значения будут иметь индексы move[0] и move[1]. Чтобы проверить, числовые ли это значения (как '2' или '17'), вы можете использовать функцию типа isOnlyDigits(), описанную в разделе
    «Проверка на содержание в строке только чисел» главы 11. Но в Python уже есть метод, который делает это.
    Строковый метод isdigit() возвращает True, если строка состоит исклю- чительно из чисел. В противном случае он возвращает значение False. В инте- рактивной оболочке введите следующие команды:
    >>>
    '42'.isdigit()
    True
    >>>
    'сорок'.isdigit()
    False
    >>>
    ''.isdigit()
    False
    >>>
    'привет'.isdigit()
    False
    >>>
    x = '10'
    >>>
    x.isdigit()
    True
    И move[0].isdigit(), и move[1].isdigit() должны принимать значение True, чтобы все условие было истинным. Заключительная часть условия в строке кода 99 вызывает функцию isOnBoard(), чтобы проверить, существуют ли ко- ординаты x и y на игровом поле.
    Если все условие истинно, код в строке 100 проверяет, есть ли данный ход в списке previousMoves. Если есть, тогда инструкция continue в строке 102 воз- вращает интерпретатор к началу цикла while в строке кода 92, а затем снова предлагает игроку сделать ход. Если хода нет в списке, код в строке 103 воз- вращает список с двумя целыми числами — координатами x и y.
    1   ...   19   20   21   22   23   24   25   26   ...   39


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