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

  • Рис. 15.5. Второй ход игрока белыми в позицию (3, 6) перевернет две фишки игрока черными 266

  • Пример запуска игры «Реверси»

  • Исходный код игры «Реверси»

  • File ⇒ New File

  • Импорт модулей и создание констант

  • Структура данных игрового поля

  • Отображение на экране структуры данных игрового поля

  • Учим 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
    страница26 из 39
    1   ...   22   23   24   25   26   27   28   29   ...   39
    Рис. 15.3.
    Ход игрока белыми привел к тому, что одна из фишек игрока черными перевернулась

    Игра «Реверси»
    265
    Например, когда игрок белыми помещает новую белую фишку в позицию
    (5, 6), как показано на рис. 15.2, черная фишка в позиции (5, 5) оказывается между двумя белыми, поэтому она переворачивается и тоже становится бе- лой, как показано на рис. 15.3. Цель игры состоит в том, чтобы фишек вашего цвета было больше, чем фишек противника.
    Следом игрок черными мог сделать аналогичный ход, поместив свою фишку в позицию (4, 6) и этим перевернув белую фишку в позиции (4, 5).
    Результатом стала бы ситуация, изображенная на рис. 15.4.
    1 2
    3 4
    5 6
    7 8
    1 2
    3 4
    5 6
    7 8
    Рис. 15.4.
    Игрок черными выставил новую фишку, перевернув одну из фишек игрока белыми
    Фишки во всех направлениях остаются перевернутыми, пока они нахо- дятся между новой фишкой игрока и существующей фишкой такого же цве- та. На рис. 15.5 игрок белыми помещает фишку в позицию (3, 6) и перевора- чивает черные фишки в двух направлениях (обозначены линиями). Результат показан на рис. 15.6.
    1 2
    3 4
    5 6
    7 8
    1 2
    3 4
    5 6
    7 8
    Рис. 15.5.
    Второй ход игрока белыми в позицию (3, 6) перевернет две фишки игрока черными

    266
    Глава 15
    За один или два хода каждый игрок может быстро перевернуть много фишек. Игроки всегда должны делать ход, который переворачивает хотя бы одну фишку. Игра заканчивается либо когда игрок не может сделать ход, либо когда поле заполнено фишками. Побеждает тот игрок, у которого осталось больше фишек своего цвета.
    1 2
    3 4
    5 6
    7 8
    1 2
    3 4
    5 6
    7 8
    Рис. 15.6.
    Поле после второго хода игрока белыми
    Искусственный интеллект, который мы создадим для этой игры, будет искать на поле любые угловые ходы, которые сможет сделать. Если доступных угловых ходов нет, компьютер выберет ход, который перевернет больше всего фишек.
    Пример запуска игры «Реверси»
    Вот что видит пользователь, запустив программу «Реверси». Текст, кото- рый вводит игрок, выделен полужирным шрифтом.
    Приветствуем в игре "Реверси"!
    Вы играете за Х или О?
    х
    Человек ходит первым.
    12345678
    +--------+
    1| |1 2| |2 3| |3 4| ХО |4 5| ОХ |5 6| |6 7| |7 8| |8
    +--------+
    12345678

    Игра «Реверси»
    267
    Ваш счет: 2. Счет компьютера: 2.
    Укажите ход, текст "выход" для завершения игры или "подсказка" для вывода подсказки.
    53
    12345678
    +--------+
    1| |1 2| |2 3| Х |3 4| ХХ |4 5| ОХ |5 6| |6 7| |7 8| |8
    +--------+
    12345678
    Ваш счет: 4. Счет компьютера: 1.
    Нажмите клавишу Enter для просмотра хода компьютера.
    --
    пропуск--
    12345678
    +--------+
    1|ХХХХОООО|1 2|ХХХХХООО|2 3|ХХХХХХОО|3 4|ХХХХХХХО|4 5|ХОХХОХХХ|5 6|ХОХОХОХХ|6 7|ХХОООХХХ|7 8|ХХХХХХХХ|8
    +--------+
    12345678
    X набрал 46 очков. O набрал 18 очков.
    Вы победили компьютер, обогнав его на 28 очков! Поздравления!
    Хотите сыграть еще раз? (да или нет)
    нет

    268
    Глава 15
    Как видно из примера, автор довольно уверенно победил компьютер, 46 очков против 18. Чтобы помочь начинающему игроку, мы добавим в игру подсказки. Игрок может ввести слово подсказка во время своего хода, вклю- чив или выключив режим подсказок. Когда режим подсказок включен, все допустимые ходы игрока отобразятся на игровом поле в виде точек (.), на- пример, так:
    12345678
    +--------+
    1| . |1 2| Х О. |2 3| ХО. |3 4| ХХО. |4 5| ОО. |5 6| ..О |6 7| . |7 8| |8
    +--------+
    12345678
    Как видите, игрок может поместить фишку в позиции (6, 1), (6, 2), (4, 6),
    (7, 7) и несколько других, основываясь на выданных ему подсказках.
    Исходный код игры «Реверси»
    «Реверси» — гигантская программа по сравне- нию с нашими предыдущими играми, она содержит почти 300 строк кода! Но не беспокойтесь: многие из них — это комментарии или пустые строки, оставленные, чтобы разделить код и сделать его бо- лее удобочитаемым.
    Как и в других наших программах, сначала мы создадим несколько функций, которые будут вы- зываться в основном коде игры. Около 250 первых строк кода предназначены для этих вспомогатель- ных функций, а последние 30 строк реализуют саму игру «Реверси».
    В редакторе файлов создайте новый файл, выбрав команду меню
    File
    New File (Файл ⇒ Новый файл). В открывшемся окне введите приве- денный ниже исходный код и сохраните файл под именем reversegam.py. На- жмите клавишу F5 и запустите программу. Если при выполнении программы
    Make sure you’re using Python 3, not Python 2!
    УБЕ ДИТЕСЬ,
    ЧТО ИСПО ЛЬЗУЕТЕ
    PY THON 3,
    А НЕ PY THON 2!

    Игра «Реверси»
    269
    возникают ошибки, сравните код, который вы набрали, с оригинальным ко- дом с помощью онлайн-инструмента на сайте inventwithpython.com/diff/.
    reversegam.py
    1. # "Реверси": клон "Отелло".
    2. import random
    3. import sys
    4. WIDTH = 8 # Игровое поле содержит 8 клеток по ширине.
    5. HEIGHT = 8 # Игровое поле содержит 8 клеток по высоте.
    6. def drawBoard(board):
    7. # Вывести игровое поле, переданное этой функции. Ничего не возвращать.
    8. print(' 12345678')
    9. print(' +--------+')
    10. for y in range(HEIGHT):
    11. print('%s|' % (y+1), end='')
    12. for x in range(WIDTH):
    13. print(board[x][y], end='')
    14. print('|%s' % (y+1))
    15. print(' +--------+')
    16. print(' 12345678')
    17.
    18. def getNewBoard():
    19. # Создать структуру данных нового чистого игрового поля.
    20. board = []
    21. for i in range(WIDTH):
    22. board.append([' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '])
    23. return board
    24.
    25. def isValidMove(board, tile, xstart, ystart):
    26. # Вернуть False, если ход игрока в клетку с координатами xstart, ystart — недопустимый.
    27. # Если это допустимый ход, вернуть список клеток, которые "присвоил" бы игрок, если бы сделал туда ход.
    28. if board[xstart][ystart] != ' ' or not isOnBoard(xstart, ystart):
    29. return False
    30.
    31. if tile == 'Х':
    32. otherTile = 'О'
    33. else:
    34. otherTile = 'Х'
    35.
    36. tilesToFlip = []
    37. for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:

    270
    Глава 15 38. x, y = xstart, ystart
    39. x += xdirection # Первый шаг в направлении x
    40. y += ydirection # Первый шаг в направлении y
    41. while isOnBoard(x, y) and board[x][y] == otherTile:
    42. # Продолжать двигаться в этом направлении x и y.
    43. x += xdirection
    44. y += ydirection
    45. if isOnBoard(x, y) and board[x][y] == tile:
    46. # Есть фишки, которые можно перевернуть. Двигаться в обратном направлении до достижения исходной клетки, отмечая все фишки на этом пути.
    47. while True:
    48. x -= xdirection
    49. y -= ydirection
    50. if x == xstart and y == ystart:
    51. break
    52. tilesToFlip.append([x, y])
    53.
    54. if len(tilesToFlip) == 0: # Если ни одна из фишек не перевернулась, это недопустимый ход.
    55. return False
    56. return tilesToFlip
    57.
    58. def isOnBoard(x, y):
    59. # Вернуть True, если координаты есть на игровом поле.
    60. return x >= 0 and x <= WIDTH - 1 and y >= 0 and y <= HEIGHT — 1 61.
    62. def getBoardWithValidMoves(board, tile):
    63. # Вернуть новое поле с точками, обозначающими допустимые ходы, которые может сделать игрок.
    64. boardCopy = getBoardCopy(board)
    65.
    66. for x, y in getValidMoves(boardCopy, tile):
    67. boardCopy[x][y] = '.'
    68. return boardCopy
    69.
    70. def getValidMoves(board, tile):
    71. # Вернуть список списков с координатами x и y допустимых ходов для данного игрока на данном игровом поле.
    72. validMoves = []
    73. for x in range(WIDTH):
    74. for y in range(HEIGHT):
    75. if isValidMove(board, tile, x, y) != False:
    76. validMoves.append([x, y])
    77. return validMoves
    78.

    Игра «Реверси»
    271
    79. def getScoreOfBoard(board):
    80. # Определить количество очков, подсчитав фишки. Вернуть словарь с ключами 'Х' и 'О'.
    81. xscore = 0 82. oscore = 0 83. for x in range(WIDTH):
    84. for y in range(HEIGHT):
    85. if board[x][y] == 'Х':
    86. xscore += 1 87. if board[x][y] == 'О':
    88. oscore += 1 89. return {'Х':xscore, 'О':oscore}
    90.
    91. def enterPlayerTile():
    92. # Позволить игроку ввести выбранную фишку.
    93. # Возвращает список с фишкой игрока в качестве первого элемента и фишкой компьютера в качестве второго.
    94. tile = ''
    95. while not (tile == 'Х' or tile == 'О'):
    96. print('Вы играете за Х или О?')
    97. tile = input().upper()
    98.
    99. # Первый элемент в списке — фишка игрока, второй элемент — фишка компьютера.
    100. if tile == 'Х':
    101. return ['Х', 'О']
    102. else:
    103. return ['О', 'Х']
    104.
    105. def whoGoesFirst():
    106. # Случайно выбрать, кто ходит первым.
    107. if random.randint(0, 1) == 0:
    108. return 'Компьютер'
    109. else:
    110. return 'Человек'
    111.
    112. def makeMove(board, tile, xstart, ystart):
    113. # Поместить фишку на игровое поле в позицию xstart, ystart и перевернуть какую-либо фишку противника.
    114. # Вернуть False, если это недопустимый ход; вернуть True, если допустимый.
    115. tilesToFlip = isValidMove(board, tile, xstart, ystart)
    116.
    117. if tilesToFlip == False:
    118. return False
    119.
    120. board[xstart][ystart] = tile

    272
    Глава 15 121. for x, y in tilesToFlip:
    122. board[x][y] = tile
    123. return True
    124.
    125. def getBoardCopy(board):
    126. # Сделать копию списка board и вернуть ее.
    127. boardCopy = getNewBoard()
    128.
    129. for x in range(WIDTH):
    130. for y in range(HEIGHT):
    131. boardCopy[x][y] = board[x][y]
    132.
    133. return boardCopy
    134.
    135. def isOnCorner(x, y):
    136. # Вернуть True, если указанная позиция находится в одном из четырех углов.
    137. return (x == 0 or x == WIDTH - 1) and (y == 0 or y == HEIGHT - 1)
    138.
    139. def getPlayerMove(board, playerTile):
    140. # Позволить игроку ввести свой ход.
    141. # Вернуть ход в виде [x, y] (или вернуть строки 'подсказка' или 'выход').
    142. DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split()
    143. while True:
    144. print('Укажите ход, текст "выход" для завершения игры или "подсказка" для вывода подсказки.')
    145. move = input().lower()
    146. if move == 'выход' or move == 'подсказка':
    147. return move
    148.
    149. if len(move) == 2 and move[0] in DIGITS1TO8 and move[1] in DIGITS1TO8:
    150. x = int(move[0]) - 1 151. y = int(move[1]) - 1 152. if isValidMove(board, playerTile, x, y) == False:
    153. continue
    154. else:
    155. break
    156. else:
    157. print('Это недопустимый ход. Введите номер столбца (1-8) и номер ряда (1-8).')
    158. print('К примеру, значение 81 перемещает в верхний правый угол.')
    159.
    160. return [x, y]
    161.
    162. def getComputerMove(board, computerTile):

    Игра «Реверси»
    273
    163. # Учитывая данное игровое поле и данную фишку компьютера, определить,
    164. # куда сделать ход, и вернуть этот ход в виде списка [x, y].
    165. possibleMoves = getValidMoves(board, computerTile)
    166. random.shuffle(possibleMoves) # Сделать случайным порядок ходов
    167.
    168. # Всегда делать ход в угол, если это возможно.
    169. for x, y in possibleMoves:
    170. if isOnCorner(x, y):
    171. return [x, y]
    172.
    173. # Найти ход с наибольшим возможным количеством очков.
    174. bestScore = -1 175. for x, y in possibleMoves:
    176. boardCopy = getBoardCopy(board)
    177. makeMove(boardCopy, computerTile, x, y)
    178. score = getScoreOfBoard(boardCopy)[computerTile]
    179. if score > bestScore:
    180. bestMove = [x, y]
    181. bestScore = score
    182. return bestMove
    183.
    184. def printScore(board, playerTile, computerTile):
    185. scores = getScoreOfBoard(board)
    186. print('Ваш счет: %s. Счет компьютера: %s.' % (scores[playerTile], scores[computerTile]))
    187.
    188. def playGame(playerTile, computerTile):
    189. showHints = False
    190. turn = whoGoesFirst()
    191. print(turn + ' ходит первым.')
    192.
    193. # Очистить игровое поле и выставить стартовые фишки.
    194. board = getNewBoard()
    195. board[3][3] = 'Х'
    196. board[3][4] = 'О'
    197. board[4][3] = 'О'
    198. board[4][4] = 'Х'
    199.
    200. while True:
    201. playerValidMoves = getValidMoves(board, playerTile)
    202. computerValidMoves = getValidMoves(board, computerTile)
    203.
    204. if playerValidMoves == [] and computerValidMoves == []:

    274
    Глава 15 205. return board # Ходов нет ни у кого, так что окончить игру.
    206.
    207. elif turn == 'Человек': # Ход человека
    208. if playerValidMoves != []:
    209. if showHints:
    210. validMovesBoard = getBoardWithValidMoves(board, playerTile)
    211. drawBoard(validMovesBoard)
    212. else:
    213. drawBoard(board)
    214. printScore(board, playerTile, computerTile)
    215.
    216. move = getPlayerMove(board, playerTile)
    217. if move == 'выход':
    218. print('Благодарим за игру!')
    219. sys.exit() # Завершить работу программы.
    220. elif move == 'подсказка':
    221. showHints = not showHints
    222. continue
    223. else:
    224. makeMove(board, playerTile, move[0], move[1])
    225. turn = 'Компьютер'
    226.
    227. elif turn == 'Компьютер': # Ход компьютера
    228. if computerValidMoves != []:
    229. drawBoard(board)
    230. printScore(board, playerTile, computerTile)
    231.
    232. input('Нажмите клавишу Enter для просмотра хода компьютера.')
    233. move = getComputerMove(board, computerTile)
    234. makeMove(board, computerTile, move[0], move[1])
    235. turn = 'Человек'
    236.
    237.
    238.
    239. print('Приветствуем в игре "Реверси"!')
    240.
    241. playerTile, computerTile = enterPlayerTile()
    242.
    243. while True:
    244. finalBoard = playGame(playerTile, computerTile)
    245.
    246. # Отобразить итоговый счет.

    Игра «Реверси»
    275
    247. drawBoard(finalBoard)
    248. scores = getScoreOfBoard(finalBoard)
    249. print('X набрал %s очков. O набрал %s очков.' % (scores['Х'], scores['О']))
    250. if scores[playerTile] > scores[computerTile]:
    251. print('Вы победили компьютер, обогнав его на %s очков! Поздравления!' %
    (scores[playerTile] - scores[computerTile]))
    252. elif scores[playerTile] < scores[computerTile]:
    253. print('Вы проиграли. Компьютер победил вас, обогнав на %s очков.' %
    (scores[computerTile] - scores[playerTile]))
    254. else:
    255. print('Ничья!')
    256.
    257. print('Хотите сыграть еще раз? (да или нет)')
    258. if not input().lower().startswith('д'):
    259. break
    Импорт модулей и создание констант
    Как мы уже научились, начинаем программу с импорта модулей.
    1. # "Реверси": клон "Отелло".
    2. import random
    3. import sys
    4. WIDTH = 8 # Игровое поле содержит 8 клеток по ширине.
    5. HEIGHT = 8 # Игровое поле содержит 8 клеток по высоте.
    Код в строке 2 импортирует модуль random для получения доступа к его функциям randint() и choice(). Код в строке 3 импортирует модуль sys для получения доступа к его функции exit().
    Код в строках 4 и 5 задают две константы, WIDTH и HEIGHT, которые исполь- зуются для настройки игрового поля.
    Структура данных игрового поля
    Давайте разберемся со структурой данных игрового поля. Она представ- ляет собой список списков, точно так же, как в игре «Охотник за сокровища- ми» из главы 13. Список списков создается за тем, чтобы с помощью board[x]
    [y]
    можно было представлять символ, находящийся в клетке, расположенной в позиции x по оси x (перемещение влево/вправо) и в позиции y по оси y
    (вверх/вниз).

    276
    Глава 15
    Этим символом может быть либо ' ' (пробел, представляющий пустую клетку), '.' (точка, указывающая на возможный ход в режиме подсказок) или 'X'
    или 'O' (буквы, представляющие фишки). Параметр board обозначает эту структуру данных вида список списков.
    Важно отметить, что если значения координат x и y игрового поля будут варьироваться от 1 до 8, то индексы структуры данных списка — от 0 до 7.
    Нам придется скорректировать код для учета этого.
    Отображение на экране структуры данных игрового поля
    Структура данных игрового поля — это всего лишь список в Python, но нам нужен более удобный способ представить ее на экране. Функция drawBoard() принимает структуру данных поля и отображает ее на экране, чтобы игрок знал, где находятся фишки.
    6. def drawBoard(board):
    7. # Вывести игровое поле, переданное этой функции. Ничего не возвращать.
    8. print(' 12345678')
    9. print(' +--------+')
    10. for y in range(HEIGHT):
    11. print('%s|' % (y+1), end='')
    12. for x in range(WIDTH):
    13. print(board[x][y], end='')
    14. print('|%s' % (y+1))
    15. print(' +--------+')
    16. print(' 12345678')
    Функция drawBoard() выводит игровое поле в текущем состоянии, осно- вываясь на структуре данных в параметре board.
    Код в строке 8 — первый из вызовов функции print(), выполняемый для каждого игрового поля; он выводит метки для оси x в верхней части поля. Код в строке 9 выводит верхнюю горизонтальную строку поля. Цикл for в строке кода 10 будет выполняться восемь раз, по одному разу для каждого ряда. Код в строке 11 выводит метку для оси y в левой части поля и содержит аргумент — ключевое слово end='', чтобы вместо новой строки поля не выводить ничего.
    Это связано с тем, что другой цикл в строке 12 (который также выпол- няется восемь раз, по одному для каждого столбца в ряде) выводит каждую клетку вместе с символом X, O, . или пробелом в зависимости от того, что хранится в board[x][y]. Вызов функции print() в строке кода 13 внутри это- го цикла также содержит аргумент — ключевое слово end='', чтобы символ

    Игра «Реверси»
    277
    новой строчки поля не выводился. Это создаст на экране одну строку вида '1|XXXXXXXX|1'
    (если каждое из значений board[x][y] было 'X').
    После выполнения внутреннего цикла в строках 15 и 16 вызывается функ- ция print(), которая выводит нижнюю горизонтальную строчку и метки оси x.
    Цикл for в строке 13 формирует игровое поле.
    12345678
    +--------+
    1|XXXXXXXX|1 2|XXXXXXXX|2 3|XXXXXXXX|3 4|XXXXXXXX|4 5|XXXXXXXX|5 6|XXXXXXXX|6 7|XXXXXXXX|7 8|XXXXXXXX|8
    +--------+
    12345678
    Конечно, вместо X некоторые клетки на поле будут отмечены знаком дру- гого игрока (O), точкой (.), если включен режим подсказок, или пробелом для пустых клеток.
    1   ...   22   23   24   25   26   27   28   29   ...   39


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