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

  • Пример запуска игры «Крестики-нолики»

  • Данные для прорисовки игрового поля

  • Выбор — кто будет ходить первым

  • Размещение меток на игровом поле Функция makeMove() очень проста.36. def makeMove(board, letter, move):37. board[move] = letter 172

  • Учим 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
    страница16 из 39
    1   ...   12   13   14   15   16   17   18   19   ...   39
    Рис. 10.1.
    Клетки игрового поля нумеруются так же, как цифровая клавиатура

    160
    Глава 10
    В ЭТОЙ ГЛАВЕ РАССМАТРИВАЮТСЯ СЛЕДУЮЩИЕ ТЕМЫ:
    • Искусственный интеллект
    • Ссылки на список
    • Вычисление по короткой схеме
    • Значение
    None
    Пример запуска игры «Крестики-нолики»
    Вот что видит пользователь при запуске программы «Крестики-нолики» .
    Текст, который вводит игрок, выделен полужирным шрифтом.
    Игра "Крестики-нолики"
    Вы выбираете Х или О?
    о
    Человек ходит первым.
    | |
    -+-+-
    | |
    -+-+-
    | |
    Ваш следующий ход? (1-9)
    7
    О| |Х
    -+-+-
    | |
    -+-+-
    | |
    Ваш следующий ход? (1-9)
    4
    О| |Х
    -+-+-
    О| |
    -+-+-
    Х| |
    Ваш следующий ход? (1-9)
    5
    О| |Х
    -+-+-
    О|О|

    Игра «Крестики-нолики»
    161
    -+-+-
    Х| |Х
    Ваш следующий ход? (1-9)
    6
    О| |Х
    -+-+-
    О|О|О
    -+-+-
    Х| |Х
    Ура! Вы выиграли!
    Сыграем еще раз? (да или нет)
    нет
    Исходный код игры «Крестики-нолики»
    В редакторе файлов создайте новый файл, вы- брав команду меню File
    New File (Файл ⇒ Но- вый файл). В открывшемся окне введите приве- денный ниже исходный код и сохраните файл под именем tictactoe.py. Затем запустите программу, на- жав клавишу F5. Если при выполнении программы возникают ошибки, сравните код, который вы на- брали, с оригинальным кодом с помощью онлайн- инструмента на сайте inventwithpython.com/diff/.
    tictactoe.py
    1. # Крестики-нолики
    2.
    3. import random
    4.
    5. def drawBoard(board):
    6. # Эта функция выводит на экран игровое поле, клетки которого будут заполняться.
    7.
    8. # "board" — это список из 10 строк, для прорисовки игрового поля (индекс 0 игнорируется).
    9. print(board[7] + '|' + board[8] + '|' + board[9])
    10. print('-+-+-')
    11. print(board[4] + '|' + board[5] + '|' + board[6])
    12. print('-+-+-')
    13. print(board[1] + '|' + board[2] + '|' + board[3])
    14.
    Make sure you’re using Python 3, not Python 2!
    УБЕ ДИТЕСЬ,
    ЧТО ИСПО ЛЬЗУЕТЕ
    PY THON 3,
    А НЕ PY THON 2!

    162
    Глава 10 15. def inputPlayerLetter():
    16. # Разрешение игроку ввести букву, которую он выбирает.
    17. # Возвращает список, в котором буква игрока — первый элемент, а буква компьютера — второй.
    18. letter = ''
    19. while not (letter == 'Х' or letter == 'О'):
    20. print('Вы выбираете Х или О?')
    21. letter = input().upper()
    22.
    23. # Первым элементом списка является буква игрока, вторым — буква компьютера.
    24. if letter == 'Х':
    25. return ['Х', 'О']
    26. else:
    27. return ['О', 'Х']
    28.
    29. def whoGoesFirst():
    30. # Случайный выбор игрока, который ходит первым.
    31. if random.randint(0, 1) == 0:
    32. return 'Компьютер'
    33. else:
    34. return 'Человек'
    35.
    36. def makeMove(board, letter, move):
    37. board[move] = letter
    38.
    39. def isWinner(bo, le):
    40. # Учитывая заполнение игрового поля и буквы игрока, эта функция возвращает True, если игрок выиграл.
    41. # Мы используем "bo" вместо "board" и "le" вместо "letter", поэтому нам не нужно много печатать.
    42. return ((bo[7] == le and bo[8] == le and bo[9] == le) or # across the top
    43. (bo[4] == le and bo[5] == le and bo[6] == le) or # через центр
    44. (bo[1] == le and bo[2] == le and bo[3] == le) or # через низ
    45. (bo[7] == le and bo[4] == le and bo[1] == le) or # вниз по левой стороне
    46. (bo[8] == le and bo[5] == le and bo[2] == le) or # вниз по центру
    47. (bo[9] == le and bo[6] == le and bo[3] == le) or # вниз по правой стороне
    48. (bo[7] == le and bo[5] == le and bo[3] == le) or # по диагонали
    49. (bo[9] == le and bo[5] == le and bo[1] == le)) # по диагонали
    50.
    51. def getBoardCopy(board):
    52. # Создает копию игрового поля и возвращает его.
    53. boardCopy = []
    54. for i in board:
    55. boardCopy.append(i)

    Игра «Крестики-нолики»
    163
    56. return boardCopy
    57.
    58. def isSpaceFree(board, move):
    59. # Возвращает True, если сделан ход в свободную клетку.
    60. return board[move] == ' '
    61.
    62. def getPlayerMove(board):
    63. # Разрешение игроку сделать ход.
    64. move = ' '
    65. while move not in '1 2 3 4 5 6 7 8 9'.split() or not isSpaceFree(board, int(move)):
    66. print('Ваш следующий ход? (1-9)')
    67. move = input()
    68. return int(move)
    69.
    70. def chooseRandomMoveFromList(board, movesList):
    71. # Возвращает допустимый ход, учитывая список сделанных ходов и список заполненных клеток.
    72. # Возвращает значение None, если больше нет допустимых ходов.
    73. possibleMoves = []
    74. for i in movesList:
    75. if isSpaceFree(board, i):
    76. possibleMoves.append(i)
    77.
    78. if len(possibleMoves) != 0:
    79. return random.choice(possibleMoves)
    80. else:
    81. return None
    82.
    83. def getComputerMove(board, computerLetter):
    84. # Учитывая заполнение игрового поля и букву компьютера, определяет допустимый ход и возвращает его.
    85. if computerLetter == 'Х':
    86. playerLetter = 'О'
    87. else:
    88. playerLetter = 'Х'
    89.
    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):

    164
    Глава 10 97. return i
    98.
    99. # Проверяем — победит ли игрок, сделав следующий ход, и блокируем его.
    100. for i in range(1, 10):
    101. boardCopy = getBoardCopy(board)
    102. if isSpaceFree(boardCopy, i):
    103. makeMove(boardCopy, playerLetter, i)
    104. if isWinner(boardCopy, playerLetter):
    105. return i
    106.
    107. # Пробуем занять один из углов, если есть свободные.
    108. move = chooseRandomMoveFromList(board, [1, 3, 7, 9])
    109. if move != None:
    110. return move
    111.
    112. # Пробуем занять центр, если он свободен.
    113. if isSpaceFree(board, 5):
    114. return 5 115.
    116. # Делаем ход по одной стороне.
    117. return chooseRandomMoveFromList(board, [2, 4, 6, 8])
    118.
    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
    125.
    126.
    127. print('Игра "Крестики-нолики"')
    128.
    129. while True:
    130. # Перезагрузка игрового поля
    131. theBoard = [' '] * 10 132. playerLetter, computerLetter = inputPlayerLetter()
    133. turn = whoGoesFirst()
    134. print('' + turn + ' ходит первым.')
    135. gameIsPlaying = True
    136.

    Игра «Крестики-нолики»
    165
    137. while gameIsPlaying:
    138. if turn == 'Человек':
    139. # Ход игрока.
    140. drawBoard(theBoard)
    141. move = getPlayerMove(theBoard)
    142. makeMove(theBoard, playerLetter, move)
    143.
    144. if isWinner(theBoard, playerLetter):
    145. drawBoard(theBoard)
    146. print('Ура! Вы выиграли!')
    147. gameIsPlaying = False
    148. else:
    149. if isBoardFull(theBoard):
    150. drawBoard(theBoard)
    151. print('Ничья!')
    152. break
    153. else:
    154. turn = 'Компьютер'
    155.
    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 = 'Человек'
    172.
    173. print('Сыграем еще раз? (да или нет)')
    174. if not input().lower().startswith('д'):
    175. break

    166
    Глава 10
    Проектирование программы
    На рис. 10.2 показана блок-схема игры «Крестики-нолики». Программа начинается с предложения игроку выбрать букву «Х» или «О». Кому принад- лежит первый ход, выбирается случайно. Затем игрок и компьютер по очере- ди совершают свои ходы.
    Player’s Turn
    Computer’s Turn
    START
    Show the board
    Get player’s move
    Check for tie
    Check for tie
    END
    Ask for player’s letter
    Decide who goes first
    Get computer’s move
    Check if computer won
    Check if player won
    Ask player to play again
    Старт
    Конец
    Показать игровое поле
    Проверить ничью
    Проверить ничью
    Определить ход игрока
    Проверить, выиграл ли игрок
    Спросить игрока, бу- дет ли играть заново
    Проверить, выиграл ли компьютер
    Определить ход компьютера
    Ход компьютера
    Ход игрока
    Запросить у игрока вы- брать букву
    Определить, кто ходит первым
    Рис. 10.2.
    Блок-схема программы «Крестики-нолики»
    Клетки в левой части блок-схемы показывают, что происходит, когда игрок совершает ход. Клетки в правой части показывают, что происходит, когда ход совершает компьютер. После того как игрок или компьютер делают ход, программа проверяет, не выиграл ли кто-нибудь или не получилась ли ничья, далее возможны варианты. По завершении игры программа предлага- ет игроку сыграть заново.
    Данные для прорисовки игрового поля
    Сначала надо определить, как представить игровое поле данными, хра- нящимися в переменных. На бумаге поле для игры в «Крестики-нолики» вычерчивается парой горизонтальных и парой вертикальных линий, а де-

    Игра «Крестики-нолики»
    167
    вять полученных клеток заполняются буквами «Х» и «О». В программе
    «Крестики-нолики» игровое поле представлено списком строк, содержащих
    ASCII-символы, так же, как в игре «Виселица». В каждой строке представле- на одна клетка игрового поля. Есть строки для буквы игрока «Х», для буквы игрока «О», есть строка с единичным пробелом ' ' для пустых клеток.
    Напомню, что размещение клеток игрового поля аналогично размеще- нию клавиш на цифровой клавиатуре компьютера. Таким образом, если спи- сок из 10 строк хранится в переменной с именем board, то board[7] — это клет- ка в левом верхнем углу игрового поля, board[8] — верхняя средняя клетка, board[9]
    — верхняя правая клетка и так далее. Программа игнорирует индекс
    0
    списка. Чтобы сообщить программе, в какую клетку игрок хочет сделать ход, он должен ввести цифру от 1 до 9.
    Стратегия игры ИИ
    ИИ должен быть способен оценить состояние игрового поля и принять решение, в какую клетку сделать ход. Для удобства мы отметим три типа кле- ток: угловые, боковые и центр. Карта на рис. 10.3 показывает, где расположе- на каждая клетка.
    Side
    Side
    Center
    Corner
    Corner
    Side
    Corner
    Corner
    Side
    Сторона
    Центр
    Сторона
    Сторона
    Сторона
    Угол
    Угол
    Угол
    Угол
    Рис. 10.3.
    Расположение сторон, углов и центра клеток на поле
    Стратегия ИИ игры в «Крестики-нолики» подчинена простому алгорит- му — ограниченному числу инструкций, рассчитывающих результат. Одна программа может использовать несколько алгоритмов. Алгоритм можно представить блок-схемой. Алгоритм ИИ для игры в «Крестики-нолики» бу- дет рассчитывать наилучший ход, как показано на рис. 10.4.
    Алгоритм ИИ состоит из следующих шагов:
    1. Проверить существует ли ход, сделав который, компьютер мог бы выиграть. Если существует, сделать его. В противном случае, перейти к шагу 2.
    2. Проверить, существует ли ход, сделав который, игрок мог бы выи- грать. Если существует, сделать ход для блокировки. В противном случае перейти к шагу 3.

    168
    Глава 10 3. Проверить, свободны ли какие-либо угловые клетки (под номером 1,
    3, 7 или 9). Если да, то сделать ход в угол. Если нет свободных углов, перейти к шагу 4.
    4. Проверить, свободен ли центр. Если да, то сделать ход в центр. Если нет, перейти к шагу 5.
    5. Сделать ход в свободную боковую клетку (под номером 2, 4, 6 или 8).
    Если процесс выполнения алгоритма завершил 5-й шаг, возможных ходов больше не существует, так как все боковые клетки заняты.
    1. Make winning move.
    2. Block player’s winning move.
    3. Move on corner.
    4. Move on center.
    5. Move on side.
    1. Сделать потенциально выигрышный ход.
    2. Заблокировать выигрышный ход человека.
    3. Перейти в угол.
    4. Перейти в центр.
    5. Перейти в сторону.
    Рис. 10.4.
    Клетки представляют пять шагов алгоритма «Ход компьютера».
    Стрелки, указывающие налево, соответствуют указанию перейти к клетке
    «Проверить, выиграл ли компьютер» блок-схемы
    Все это происходит в элементе «Определить ход компьютера» блок-схемы на рис. 10.2. Вы можете добавить клетки с этой информацией, изображенные на рис. 10.4, в блок-схему.
    Этот алгоритм реализован в функции getComputerMove() и других, вызы- ваемых ей, функциях.
    Импорт модуля random
    Первая пара строк содержит комментарий и инструкцию импорта моду- ля random. Таким образом, становится возможным вызов функции randint().
    1. # Крестики-нолики
    2.
    3. import random
    С этими двумя понятиями вы уже встречались ранее, поэтому перейдем к следующей части программы.

    Игра «Крестики-нолики»
    169
    Вывод игрового поля на экран
    В следующей части кода определим функцию для прорисовки игрового поля.
    5. def drawBoard(board):
    6. # Эта функция выводит на экран игровое поле, клетки которого будут заполняться.
    7.
    8. # "board" — это список из 10 строк, для прорисовки игрового поля (индекс 0 игнорируется).
    9. print(board[7] + '|' + board[8] + '|' + board[9])
    10. print('-+-+-')
    11. print(board[4] + '|' + board[5] + '|' + board[6])
    12. print('-+-+-')
    13. print(board[1] + '|' + board[2] + '|' + board[3])
    Функция drawBoard() выводит игровое поле, представленное переменной board
    . Напомню, что в переменной board хранится список из 10 строк, в кото- ром строка с индексом 1 соответствует клетке 1 игрового поля «Крестики- нолики» и так далее.
    Строка с индексом 0 игнорируется. Многие функции игры работают, пе- редавая этот список как переменную board.
    Не забудьте указать правильные расстояния в строках, иначе игровое поле будет выглядеть очень забавно при выводе на экран. Ниже продемонстри- ровано несколько примеров вызова функции drawBoard() с переменной board в качестве аргумента и несколько примеров того, что выводится на экран.
    >>>
    drawBoard([' ', ' ', ' ', ' ', 'X', 'O', ' ', 'X', ' ', 'O'])
    X| |
    -+-+-
    X|O|
    -+-+-
    | |
    >>>
    drawBoard([' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '])
    | |
    -+-+-
    | |
    -+-+-
    | |
    Каждую строку программа размещает на игровом поле в порядке, соот- ветствующем порядку цифровой клавиатуры компьютера, как показано на

    170
    Глава 10
    рис. 10.1. Таким образом, первые три строки — это нижний ряд, следующие три строки — середина, и последние три строки — верхний ряд.
    Предоставление игроку выбора
    между «Х» или «О»
    Далее определим функцию назначения игроку буквы «Х» или «О».
    15. def inputPlayerLetter():
    16. # Разрешение игроку ввести букву, которую он выбирает.
    17. # Возвращает список, в котором буква игрока — первый элемент, а буква компьютера — второй.
    18. letter = ''
    19. while not (letter == 'Х' or letter == 'О'):
    20. print('Вы выбираете Х или О?')
    21. letter = input().upper()
    По завершении игры программа предлагает игроку сыграть заново. Усло- вие цикла while содержит круглые скобки, это значит, что выражение в скоб- ках вычисляется первым. Если переменной letter присваивается значение 'X'
    , выражение вычисляется следующим образом:
    not
    (
    letter
    == 'X'
    or letter
    == 'O')
    not (
    'X' == 'X' or
    'X' == 'O'
    )
    not
    (True or False)
    not (True)
    not True
    False
    Если переменной letter присваивается значение 'X' или 'O', то условие цикла становится ложным, что позволяет продолжить выполнение програм- мы после блока while. Если условие истинно, программа продолжит пред- лагать игроку выбрать букву до тех пор, пока игрок не нажмет клавишу Х или О. Код в строке 21 автоматически преобразует возвращаемое строковое значение переменной в прописные буквы. Делается это через вызов функции input()
    с помощью метода upper().

    Игра «Крестики-нолики»
    171
    Следующая функция возвращает список из двух элементов.
    23. # Первым элементом списка является буква игрока, вторым — буква компьютера.
    24. if letter == 'Х':
    25. return ['Х', 'О']
    26. else:
    27. return ['О', 'Х']
    Первый элемент (строковая переменная с индексом 0) — это буква игро- ка, второй элемент (строка с индексом 1) — буква компьютера. Инструкции if и else выбирают соответствующий список.
    Выбор — кто будет ходить первым
    Далее создаем функцию, которая использует randnt(), для выбора того, кто будет ходить первым.
    29. def whoGoesFirst():
    30. # Случайный выбор игрока, который ходит первым.
    31. if random.randint(0, 1) == 0:
    32. return 'Компьютер'
    33. else:
    34. return 'Человек'
    Функция whoGoesFirst() — виртуальный аналог подброшенной монетки, для определения того, кто будет ходить первым. Вместо подбрасываний мо- нетки вызываем функцию random.randint(0, 1). Шансы 50 на 50, что функция вернет 0 или 1. Если эта функция возвращает 0, функция whoGoesFirst() воз- вращает строку 'Компьютер'. В противном случае функция вернет строку 'Че- ловек'
    . Код, вызвавший эту функцию, использует возвращенное значение для определения того, кто сделает первый ход.
    Размещение меток на игровом поле
    Функция makeMove() очень проста.
    36. def makeMove(board, letter, move):
    37. board[move] = letter

    172
    Глава 10
    Параметрами являются переменные board, letter и move. Переменная board
    — это список из 10 строк, представляющих состояние игрового поля.
    Переменная letter — это буква игрока («Х» или «О»). Переменная move — это клетка, на которую игрок пожелал сделать ход (целочисленная переменная в диапазоне от 1 до 9).
    Стоп! Но ведь, кажется, в строке 37 этот код заменяет значение одного из элементов списка board значением переменной letter. Так как этот код при- надлежит функции, то, может быть, изменяемый параметр будет утерян при выходе из функции. Стоит ли вносить изменения, которые будут отменены?
    В действительности это не так, потому что списки, которые передаются в функцию как аргументы, — особенные. Передаются не сами списки, а ссыл-
    ки на них. Рассмотрим разницу между списками и ссылками на списки.
    1   ...   12   13   14   15   16   17   18   19   ...   39


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