Крестики-нолики
Очень полезно в целях изучения языка программирования написать на нем несколько простых программ. Здесь мы представим игру крестики-нолики, которая написана на Python 3 с графическим интерфейсом.
О программе
В этой игре можно помериться силами с компьютером. Первый ход за игроком. Но победить искусственный интеллект в этом противостоянии будет не так уж и просто. Компьютер не делает «зевков» и если у него есть шанс победить, он непременно им воспользуется.

Внизу расположена кнопка для начала новой игры. По её нажатию игровое поле очистится и можно будет начать игру заново.
Игрок ставит крестики, а компьютер нолики. Как всегда, побеждает тот, кто первый составит линию из своих символов: по горизонтали, вертикали или диагонали.
Библиотеки и объявление переменных
Для отображения графики будем использовать стандартную библиотеку Tkinter, которая устанавливается вместе с Python. Так же нам потребуется библиотека random для того, чтобы получать случайные числа, благодаря которым ходы компьютера будут неожиданными.
Это также стандартная библиотека Python. Так что ничего дополнительно устанавливать не надо. Просто подключаем их с помощью import . Создаем окно root , устанавливаем ему заголовок и объявляем необходимые переменные:
- game_run – в эту переменную будем записывать False при завершении игры, чтобы запретить делать ходы когда уже выявлен победитель.
- field – это будет двумерный список, в котором будут храниться кнопки игрового поля. Ходом будет изменение надписи на кнопке на символ «X» или «O».
- cross_count в этой переменной мы будем отслеживать количество крестиков на поле. Чтобы по выставлению пятого крестика, в случае если никто не выиграл фиксировать ничью.
from tkinter import * import random root = Tk() root.title('Criss-cross') game_run = True field = [] cross_count = 0
Обработка нажатия кнопок
Функция new_game будет вызываться при нажатии кнопки начала новой игры. На поле убираются все крестики и нолики. Цвет кнопок делаем бледно-лиловым. Устанавливаем глобальные переменные game_run и cross_count в начальные значения. Это глобальные переменные к которым пытаемся обратиться из функции. Поэтому перед попыткой изменить их значение, в Python надо использовать ключевое слово global .
def new_game(): for row in range(3): for col in range(3): field[row][col]['text'] = ' ' field[row][col]['background'] = 'lavender' global game_run game_run = True global cross_count cross_count = 0
Функция click будет вызываться после нажатия на поле, то есть при попытки поставить крестик. Если игра еще не завершена, то крестик ставится. После этого увеличиваем счетчик количества выставленных крестиков.
Потом проверяем с помощью функции check_win , не победили ли мы этим ходом. Если еще не выявлен победитель и есть еще ходы, то выполняет ход компьютер функцией computer_move , и также после хода идет проверка выигрыша.
def click(row, col): if game_run and field[row][col]['text'] == ' ': field[row][col]['text'] = 'X' global cross_count cross_count += 1 check_win('X') if game_run and cross_count < 5: computer_move() check_win('O')
Проверка победы
Функция check_win осуществляет проверку выигрыша. Она перебирает все возможные комбинации полей, образующих линию и вызывает с ними функцию check_line . Переменная smb – это символ «X» или «O», то есть крестики или нолики. Если задан «O», то проверяется: не победил ли компьютер.
Если зафиксирован выигрыш, то меняем цвет фона кнопок, составляющих линию на розовый. А также записываем в game_run значение False .
def check_win(smb): for n in range(3): check_line(field[n][0], field[n][1], field[n][2], smb) check_line(field[0][n], field[1][n], field[2][n], smb) check_line(field[0][0], field[1][1], field[2][2], smb) check_line(field[2][0], field[1][1], field[0][2], smb) def check_line(a1,a2,a3,smb): if a1['text'] == smb and a2['text'] == smb and a3['text'] == smb: a1['background'] = a2['background'] = a3['background'] = 'pink' global game_run game_run = False
Проверяем все возможные варианты, так как теоретически можно одним ходом составить сразу 2 линии.
Действия компьютера
Ход компьютера рассчитывается в функции computer_move . Алгоритм его действий следующий:
- Проверка возможности победы. Если компьютеру представился шанс победы – он не должен его упустить. Сразу же делает победу.
- Проверка возможной победы противника за один ход. Если игрок выставил два крестика в ряд, компьютер пытается разрушить планы игрока.
- Случайный ход. Так как победить нет возможности и нет угрозы проигрыша, то выбирается случайное свободное поле. В бесконечном цикле wile перебираются случайные числа, пока они не выпадут на не занятое поле.
def can_win(a1,a2,a3,smb): res = False if a1['text'] == smb and a2['text'] == smb and a3['text'] == ' ': a3['text'] = 'O' res = True if a1['text'] == smb and a2['text'] == ' ' and a3['text'] == smb: a2['text'] = 'O' res = True if a1['text'] == ' ' and a2['text'] == smb and a3['text'] == smb: a1['text'] = 'O' res = True return res def computer_move(): for n in range(3): if can_win(field[n][0], field[n][1], field[n][2], 'O'): return if can_win(field[0][n], field[1][n], field[2][n], 'O'): return if can_win(field[0][0], field[1][1], field[2][2], 'O'): return if can_win(field[2][0], field[1][1], field[0][2], 'O'): return for n in range(3): if can_win(field[n][0], field[n][1], field[n][2], 'X'): return if can_win(field[0][n], field[1][n], field[2][n], 'X'): return if can_win(field[0][0], field[1][1], field[2][2], 'X'): return if can_win(field[2][0], field[1][1], field[0][2], 'X'): return while True: row = random.randint(0, 2) col = random.randint(0, 2) if field[row][col]['text'] == ' ': field[row][col]['text'] = 'O' break
Графический интерфейс
Все элементы графического интерфейса мы будем размещать с помощью упаковщика grid . В цикле добавим кнопки игрового поля. Они будут храниться в двумерном список. В языке программирования Python добавляют элементы в список с помощью метода append .
Свойство colorspan у кнопки начала игры выставляем в 3, чтобы он занимал всю ширину таблицы
for row in range(3): line = [] for col in range(3): button = Button(root, text=' ', width=4, height=2, font=('Verdana', 20, 'bold'), background='lavender', command=lambda row=row, col=col: click(row,col)) button.grid(row=row, column=col, sticky='nsew') line.append(button) field.append(line) new_button = Button(root, text='new game', command=new_game) new_button.grid(row=3, column=0, columnspan=3, sticky='nsew') root.mainloop()
Игровое поле мы делали таким же образом как и кнопки в примере с калькулятором, размещённом в отдельной статье.
В целом, реализация игры крестики-нолики неплохо подходит для изучающих программирование на Python 3. Можно немного усложнить задачу, добавив уровни сложности. Например, на простом уровне сложности компьютер делает абсолютно случайные ходы. На более сложном не упускает возможности победить, но все еще может прозевать два крестика выставленных в ряд.
Глава 10.1 Исходный код игры «Крестики-Нолики».
Как всегда, я настоятельно советую набирать программу самостоятельно. Это и практика и программу понимать вы будете лучше. Впрочем, вы всегда можете скачать образец программы на странице «Загрузки«.
Итак, откройте новое окно текстового редактора Python Shell, наберите приведенный ниже код и сохраните в файле с названием ‘TicTacToe.py’ Впрочем, файл можете назвать как захотите. Но если вы потом решите конвертировать программу в *.exe — формат, то могут возникнуть проблемы с символами кириллицы в названии.
1. #Крестики-Нолики 2. 3. import random 4. 5. def drawBoard(board): 6. #Эта функция рисует игровую доску с выполненными ходами 7. 8. #"Доска" является списком из 10 строк которые рисуют доску в символьной графике 9. print(' | |') 10. print(' ' + board[7] + ' | ' + board[8] + ' | ' + board[9]) 11. print(' | |') 12. print('---+---+---') 13. print(' | |') 14. print(' ' + board[4] + ' | ' + board[5] + ' | ' + board[6]) 15. print(' | |') 16. print('---+---+---') 17. print(' | |') 18. print(' ' + board[1] + ' | ' + board[2] + ' | ' + board[3]) 19. print(' | |') 20. 21. def inputPlayerLetter(): 22. #Позволяет игроку выбрать символ, которым он хочет играть 23. #Возвращает список с буквой игрока в качестве первого элемента и буквой компьютера в качестве второго элемента 24. letter = '' 25. while not (letter == 'Х' or letter == 'О'): 26. print('Каким знаком вы будете играть? (Х или О)') 27. letter = input().upper() 28. 29. #Первым элементом возвращаемого списка всегда должен быть знак игрока. 30. if letter == 'Х': 31. return ['Х', 'О'] 32. else: 33. return ['О', 'Х'] 34. 35. def whoGoesFirst(): 36. #Случайно определяется, кто будет ходить первым 37. if random.randint(0, 1) == 0: 38. return 'компьютер' 39. else: 40. return 'игрок' 41. 42. def playAgain(): 43. #Эта функция возвращает True, если игрок хочет сыграть еще раз. Иначе False. 44. print('Вы хотите сыграть еще раз? (да или нет)') 45. return input().lower().startswith('д') 46. 47. def makeMove(board, letter, move): 48. board[move] = letter 49. 50. def isWinner(bo, le): 51. #Функция учитывает позицию на доске и текщий ход игрока. Возвращает True, если игрок выиграл 52. #Мы используем bo вместо доски и le вместо полных имен переменных 53. return ((bo[7] == le and bo[8] == le and bo[9] == le) or #Верхняя линия 54. (bo[4] == le and bo[5] == le and bo[6] == le) or #Средняя линия 55. (bo[1] == le and bo[2] == le and bo[3] == le) or #Нижняя линия 56. (bo[7] == le and bo[4] == le and bo[1] == le) or #Левая вертикальная линия 57. (bo[8] == le and bo[5] == le and bo[2] == le) or #Центральная вертикаль 58. (bo[9] == le and bo[6] == le and bo[3] == le) or #Верхняя линия 59. (bo[7] == le and bo[5] == le and bo[3] == le) or #Диагональ 60. (bo[9] == le and bo[5] == le and bo[1] == le)) #Диагональ 61. 62. def getBoardCopy(board): 63. #Сделаем копию игровой доски и вернем её 64. dupeBoard = [] 65. 66. for i in board: 65. dupeBoard.append(i) 67. 68. return dupeBoard 69. 70. def isSpaceFree(board, move): 71. #Возвращает True если ход возможен 72. return board[move] == ' ' 73. 74. def getPlayerMove(board): 75. #Позволяет игроку выполнить ход 76. move = '' 77. while move not in '1 2 3 4 5 6 7 8 9'.split() or not isSpaceFree(board, int(move)): 78. print('Ваш ход (1-9):') 79. move = input() 80. return int(move) 81. 82. def chooseRandomMoveFromList(board, movesList): 83. #Возвращает случайный ход из полученного списка возможных ходов 84. #Возвращает None если ходов нет 85. possibleMoves = [] 86. for i in movesList: 87. if isSpaceFree(board, i): 88. possibleMoves.append(i) 89. 90. if len(possibleMoves) != 0: 91. return random.choice(possibleMoves) 92. else: 93. return None 94. 95. def getComputerMove(board, computerLetter): 96. #Получает копию содержимого доски и букву, которой ходит компьютер. Исходя из этого определяет куда двигаться и возвращает ход 97. if computerLetter == 'Х': 98. playerLetter = 'О' 99. else: 100. playerLetter = 'Х' 101. 102. #Здесь начинается алгоритм ИИ "Крестики-Нолики" 103. #Первым шагом будет определение возможности победы на следующем ходу 104. for i in range(1, 10): 105. copy = getBoardCopy(board) 106. if isSpaceFree(copy, i): 107. makeMove(copy, computerLetter, i) 108. if isWinner(copy, computerLetter): 109. return i 110 111. #Проверяем, может ли игрок выиграть на следющем ходу, чтобы заблокировать его 112. for i in range(1, 10): 113. copy = getBoardCopy(board) 114. if isSpaceFree(copy, i): 115. makeMove(copy, playerLetter, i) 116. if isWinner(copy, playerLetter): 117. return i 118. 119. #Попытаемся занять один из углов, если они свободны 119. move = chooseRandomMoveFromList(board, [1, 3, 7, 9]) 120. if move != None: 121. return move 122. 123. #Занимаем центр, если он свободен 124. if isSpaceFree(board, 5): 125. return 5 126. 127. #Занимаем одну из боковых клеток 128. return chooseRandomMoveFromList(board, [2, 4, 6, 8]) 129. 130. def isBoardFull(board): 131. #Возвращаем True, если все клетки на доске были заняты. Иначе возвращаем False 132. for i in range(1, 10): 133. if isSpaceFree(board, i): 134. return False 135. return True 136 137. 138. print ('Давай сыграем в "Крестики-Нолики"!') 139 140. while True: 141. #Сбрасываем состояние игровой доски 142. theBoard = [' ']*10 143. playerLetter, computerLetter = inputPlayerLetter() 144. turn = whoGoesFirst() 145. print ('Первым будет ходить '+turn +'\n') 146. gameIsPlaying = True 147. 148. while gameIsPlaying: 149. if turn == 'игрок': 150. #Ход игрока 151. drawBoard(theBoard) 152. move = getPlayerMove(theBoard) 153. makeMove(theBoard, playerLetter, move) 154. 155. if isWinner(theBoard, playerLetter): 156. drawBoard(theBoard) 157. print ('Поздравляю. Вы победили в игре!') 158. gameIsPlaying = False 159. else: 160. if isBoardFull(theBoard): 161. drawBoard(theBoard) 162. print('Ничья. В следующий раз играй лучше') 163. break 164. else: 165. turn = 'компьютер' 166. 167. else: 168. #Ход компьютера 169. move = getComputerMove(theBoard, computerLetter) 170. makeMove(theBoard, computerLetter, move) 171. if isWinner(theBoard, computerLetter): 172. drawBoard(theBoard) 173. print('Компьютер победил! Вы поиграли. ') 174. gameIsPlaying = False 175. else: 176. if isBoardFull(theBoard): 177. drawBoard(theBoard) 178. print('Ничья. В следующий раз играй лучше') 179. break 180. else: 181. turn = 'игрок' 182. 183. if not playAgain(): 185 break
Поделиться ссылкой:
Нравится Загрузка.
Похожее
Автор: axelkidd Рубрика: Урок №10 Отмечено Крестики-Нолики, исходный код, программа, python 3
2 комментария в “ Глава 10.1 Исходный код игры «Крестики-Нолики». ”
Добавь в начало кода
import time
import random
seconds=int(str(time.ctime()).split(‘:’)[2][0:3])
for a in range(seconds):
_random.randint(0,1)
Иначе первым всегда будет ходить один и тот же игрок,
а при добавлении данного кода в начало зависит от того момента, когда юзер открыл игру, сколько игра грузилась (а это уже зависит от компа)
Если вы используете другие функции этого модуля, надо повторять тот же код и для других функций:
import time
import random
seconds=int(str(time.ctime()).split(‘:’)[2][0:3])
for a in range(seconds):
_random.функция(аргументы)
Все это верно, но статья преследует максимально доступно и понятно объяснить концепт человеку, который еще не очень хорошо понимает Python. Кроме того, это, по сути, перевод из книги. Не хотелось ничего менять.
Но все что Вы написали, безусловно верно. И если человеку действительно будет интересно, как можно модифицировать программу, то как раз Ваш комментарий даст ему пищу для размышлений. Все останется как было.
ushchent / Tic tac toe in Jupyter Notebook
Save ushchent/73bb2cf2dec469240e5ef60cf000f0ca to your computer and use it in GitHub Desktop.
Крестики-нолики на питоне
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| # Простейшее и наименее совершенное решение игры в крестики-нолики |
| # по заданию Milestone Project 1 из Complete Python Bootcamp, |
| # см. https://github.com/jmportilla/Complete-Python-Bootcamp |
| game_matrix = [[None, None, None], [None, None, None], [None, None, None]] |
| game_is_on = True |
| while game_is_on: |
| # Крестик - латинская буква X, нолик - латинская буква O |
| # Ходы принимаются в формате [0][0] = "X" или [2][1] = "О" |
| move = input() |
| exec("game_matrix" + move) |
| for row in game_matrix: |
| print(row) |
| reference_matrix = [ |
| game_matrix[0], |
| game_matrix[1], |
| game_matrix[2], |
| [i[0] for i in game_matrix], |
| [i[1] for i in game_matrix], |
| [i[2] for i in game_matrix], |
| [game_matrix[0][0], game_matrix[1][1], game_matrix[2][2]], |
| [game_matrix[0][2], game_matrix[1][1], game_matrix[2][0]] |
| ] |
| for item in reference_matrix: |
| result = list(set(item)) |
| if len(result) == 1 and result[0] != None: |
| print("Game over!") |
| game_is_on = False |
| break |
Крестики-нолики на Python

Статьи
Автор Admin На чтение 5 мин Просмотров 10.8к. Опубликовано 09.11.2022
Введение
В статье напишем игру «Крестики-нолики» на Python.
Крестики-нолики — логическая игра между двумя соперниками на квадратном поле 3×3 клетки, или бо́льшего размера. Один из игроков играет за «крестики», а второй за «нолики».
Рисуем игровое поле
Начнём с того, что нарисуем само игровое поле для игры.
Для начала сгенерируем список с числами от одного, до 9:
board = list(range(1, 10))
Создадим функцию draw_board(), аргументом которой будет board:
def draw_board(board): print("-" * 13) for i in range(3): print("|", board[0 + i * 3], "|", board[1 + i * 3], "|", board[2 + i * 3], "|") print("-" * 13)
В функции выводим первую строку состоящую из 13 символов «тире», после чего, в цикле прорисовываем остальные края поля.
При вызове функции будет следующий вывод:

Принимаем ввод пользователя
Теперь нам нужно создать функцию для приёма ввода.
Создадим функцию take_input() с аргументом player_token:
def take_input(player_token): valid = False while not valid: player_answer = input("Куда поставим " + player_token + "? ") try: player_answer = int(player_answer) except ValueError: print("Некорректный ввод. Вы уверены, что ввели число?") continue if 1
Внутри функции сначала задаётся переменная valid, которая равняется False, после чего идёт цикл while, который не закончится, пока valid не примет значение True. В цикле производится ввод пользователем определённой клетки, в которую будет ставиться крестик, либо нолик. Если же пользователь ввёл, а какой-либо другой символ, выведется ошибка.
Далее в условии проверяется, занята ли введённая клетка. Если клетка занята, то выведется соответствующая ошибка, если же введено число не в диапазоне от 1, до 10 — будет так же выведено соответствующее сообщение.
Проверка, выиграл ли игрок
Создадим функцию check_win(), в которой будем проверять, выиграл ли игрок. Аргументом функции будет board:
def check_win(board): win_coord = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)) for each in win_coord: if board[each[0]] == board[each[1]] == board[each[2]]: return board[each[0]] return False
Внутри функции создаётся кортеж win_coord, в котором хранятся победные комбинации. В цикле производится проверка на победу игрока, если он побеждает, то выводится сообщение о победе, если же нет — возвращается False, и игра продолжается.
Создание главной функции
Теперь создадим функцию main() с аргументом board:
def main(board): counter = 0 win = False while not win: draw_board(board) if counter % 2 == 0: take_input("X") else: take_input("O") counter += 1 tmp = check_win(board) if tmp: print(tmp, "выиграл!") win = True break if counter == 9: print("Ничья!") break draw_board(board)
Внутри функции, после обозначения переменных, создаётся цикл, который закончится после победы одного из игроков, или ничьей. Внутри цикла проводится проверка, какой игрок сходил, после чего вызывается функция take_input() с соответствующим символом игрока. Далее идёт проверка, какой игрок выиграл, или вышла ничья.
Итоговый код игры «Крестики-нолики» на Python
board = list(range(1, 10)) def draw_board(board): print("-" * 13) for i in range(3): print("|", board[0 + i * 3], "|", board[1 + i * 3], "|", board[2 + i * 3], "|") print("-" * 13) def take_input(player_token): valid = False while not valid: player_answer = input("Куда поставим " + player_token + "? ") try: player_answer = int(player_answer) except ValueError: print("Некорректный ввод. Вы уверены, что ввели число?") continue if 1
Заключение
В статье мы с Вами написали игру «Крестики-нолики» на Python! Надеюсь Вам понравилась статья, желаю удачи и успехов!