Если вы учитесь программировать, то программирование игр — это очень хороший способ освоить алгоритмы и структуры данных. В этом уроке мы разберем запрограммируем игру крестики нолики на Python. Полный код программы с искусственным интеллектом для игрока-компьютера занимает всего 140 строк. В варианте игры где человек играет против человека — раза в два меньше.
Игра крестики-нолики хороша тем, что ее правила знакомы всем с детства и понятны каждому. Это даст вам возможность сосредоточиться именно на процессе программирования, а не на анализе правил игры. В процессе работы над игрой крестики-нолики мы будем использовать только стандартные функции языка Python без подключения каких либо сторонних библиотек.
Шаг 1. Подбор структуры данных
Программирование любой игры начинается с моделирования ее объектов средствами языка программирования. Когда мы программируем игру крестики нолики нам нужно подумать где в программе хранить поле для игры с ходами которые сделали игроки.
В языке программирования Python наилучшим выбором будет список из 9 значений. Назовем его maps. Первоначально этот список будет заполнен цифрами от 1 до 9. Это сделано для удобства организации диалога с пользователем. Пользователю будет достаточно ввести символ от 1 до 9, что бы мы смогли понять куда он сделал ход. После того как ход сделан, цифру мы заменим на символ нолика или крестика. (Можно было заполнить цифрами от 0 до 8. В этом случает было бы удобнее работать — цифра и есть индекс элемента в списке, но первый нолик будет смущать игроков).
Еще мы создадим второй список victories в котором будем хранить информацию обо всех выигрышных комбинациях. И нам будет нужно создать функцию print_maps, которая будет выводить содержимое нашего списка maps на экран.
# Инициализация карты maps = [1,2,3, 4,5,6, 7,8,9] # Инициализация победных линий victories = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]] # Вывод карты на экран def print_maps(): print(maps[0], end = " ") print(maps[1], end = " ") print(maps[2]) print(maps[3], end = " ") print(maps[4], end = " ") print(maps[5]) print(maps[6], end = " ") print(maps[7], end = " ") print(maps[8])
Со структурами данных разобрались.
Шаг 2. Выполнение очередного хода и проверка на выигрыш
Карта для игры у нас есть, отображать ее мы умеем. Теперь нужно создать две вспомогательные функции, прежде чем мы приступим к программированию основного цикла игры.
Первая функция будет рисовать на поле крестик или нолик, в зависимости от того что в нее передали. Позицию в нее так же нужно будет передавать. Вставлять переданный элемент мы будем по индексу. Индекс определим функцией index (если бы мы пронумеровали от 0 до 8 элементы в maps, то переданное значение и было бы индексом. Можете попробовать — будет на одну строчку кода меньше.)
# Сделать ход в ячейку def step_maps(step,symbol): ind = maps.index(step) maps[ind] = symbol
После каждого ходы мы должны проверять — не победил ли кто то из игроков. Для этого переберем все победные линии из списка victories и проверим нет ли там комбинации из трех крестиков или трех ноликов.
# Получить текущий результат игры def get_result(): win = "" for i in victories: if maps[i[0]] == "X" and maps[i[1]] == "X" and maps[i[2]] == "X": win = "X" if maps[i[0]] == "O" and maps[i[1]] == "O" and maps[i[2]] == "O": win = "O" return win
Эта функция вернет «X» в случае победы крестиков и «O» в случае победы ноликов.
Шаг 3. Основный игровой цикл
Ну вот мы и подошли к созданию основного игрового цикла. Нам предстоит крутиться в цикле до момента пока кто нибудь не выиграет. Игроки будут по очереди делать ходы, мы будем проверять каждый раз не победил ли кто и как только кто то побеждает — выходим из цикла и завершаем программу.
# Основная программа game_over = False player1 = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if player1 == True: symbol = "X" step = int(input("Человек 1, ваш ход: ")) else: symbol = "O" step = int(input("Человек 2, ваш ход: ")) step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False player1 = not(player1) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)
Вот полный код программы крестики-нолики на Python для двух игроков:
# Инициализация карты maps = [1,2,3, 4,5,6, 7,8,9] # Инициализация победных линий victories = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]] # Вывод карты на экран def print_maps(): print(maps[0], end = " ") print(maps[1], end = " ") print(maps[2]) print(maps[3], end = " ") print(maps[4], end = " ") print(maps[5]) print(maps[6], end = " ") print(maps[7], end = " ") print(maps[8]) # Сделать ход в ячейку def step_maps(step,symbol): ind = maps.index(step) maps[ind] = symbol # Получить текущий результат игры def get_result(): win = "" for i in victories: if maps[i[0]] == "X" and maps[i[1]] == "X" and maps[i[2]] == "X": win = "X" if maps[i[0]] == "O" and maps[i[1]] == "O" and maps[i[2]] == "O": win = "O" return win # Основная программа game_over = False player1 = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if player1 == True: symbol = "X" step = int(input("Человек 1, ваш ход: ")) else: symbol = "O" step = int(input("Человек 2, ваш ход: ")) step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False player1 = not(player1) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)
Вот так выглядит процесс игры в крестики-нолики для 2 игроков:
1 2 3 4 5 6 7 8 9 Человек 1, ваш ход: 5 1 2 3 4 X 6 7 8 9 Человек 2, ваш ход: 1 O 2 3 4 X 6 7 8 9 Человек 1, ваш ход: 6 O 2 3 4 X X 7 8 9 Человек 2, ваш ход: 4 O 2 3 O X X 7 8 9 Человек 1, ваш ход: 3 O 2 X O X X 7 8 9 Человек 2, ваш ход: 7 O 2 X O X X O 8 9 Победил O
Шаг 4. Добавление алгоритма для искусственного интеллекта в игре крестики-нолики
А вот теперь мы подобрались к самому интересному моменту в программировании. Нам нужно создать искусственный интеллект, который всегда будет выигрывать или сводить игру к ничьей. На самом деле в игре крестики-нолики такой алгоритм написать совсем не сложно.
Для написания такого алгоритма нам понадобится вспомогательная функция, которая будет проверять все победные линии в игре и подсчитывать в них количество крестиков и ноликов. Если функция находит такую линию, то она возвращает позицию на этой линии куда нужно сделать ход. Например мы будем отслеживать линии где противник поставил два крестика и обязательно поставим нолик, что бы не дать ему выиграть. Вот эта функция:
#Искусственный интеллект: поиск линии с нужным количеством X и O на победных линиях def check_line(sum_O,sum_X): step = "" for line in victories: o = 0 x = 0 for j in range(0,3): if maps[line[j]] == "O": o = o + 1 if maps[line[j]] == "X": x = x + 1 if o == sum_O and x == sum_X: for j in range(0,3): if maps[line[j]] != "O" and maps[line[j]] != "X": step = maps[line[j]] return step
А сейчас мы напишем функцию для поиска очередного лучшего хода для искусственного интеллекта. Запрограммируем такой алгоритм для каждого хода (компьютер играет ноликами):
- Если этим ходом можем выиграть — выигрываем (уже 2 нолика стоят на одной из линий). Иначе идем к шагу 2.
- Если можем помешать выиграть человеку — мешаем ( у человека уже 2 крестика на линии — ставим на нее нолик). Иначе идем к шагу 3.
- Если на линии одна наша фигура — ставим вторую. Если пока ни одной нашей фигуры — идем к шагу 4.
- Ставим нолик в центр. Если центр занят идем к шагу 5.
- Ставим в левый верхний угол.
А вот так все это выглядит в программе на Python:
#Искусственный интеллект: выбор хода def AI(): step = "" # 1) если на какой либо из победных линий 2 свои фигуры и 0 чужих - ставим step = check_line(2,0) # 2) если на какой либо из победных линий 2 чужие фигуры и 0 своих - ставим if step == "": step = check_line(0,2) # 3) если 1 фигура своя и 0 чужих - ставим if step == "": step = check_line(1,0) # 4) центр пуст, то занимаем центр if step == "": if maps[4] != "X" and maps[4] != "O": step = 5 # 5) если центр занят, то занимаем первую ячейку if step == "": if maps[0] != "X" and maps[0] != "O": step = 1 return step
Немного перепишем и основной цикл игры. Теперь вместо человека 2 ход будет делать компьютер. Компьютер каждый раз будет говорить куда делает ход. Если компьютер не дает ответ, значит наметилась ничья — завершаем партию и объявляем ничью. Вот таким станет основной цикл игры, когда мы перепишем программу крестики-нолики под игру против компьютера:
# Основная программа game_over = False human = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if human == True: symbol = "X" step = int(input("Человек, ваш ход: ")) else: print("Компьютер делает ход: ") symbol = "O" step = AI() # 3. Если компьютер нашел куда сделать ход, то играем. Если нет, то ничья. if step != "": step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False else: print("Ничья!") game_over = True win = "дружба" human = not(human) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)
Готовая программа для игры в крестики нолики против компьютера на Python
Вот готовая программа для для игры против искусственного интеллекта.
# Инициализация карты maps = [1,2,3, 4,5,6, 7,8,9] # Инициализация победных линий victories = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]] # Вывод карты на экран def print_maps(): print(maps[0], end = " ") print(maps[1], end = " ") print(maps[2]) print(maps[3], end = " ") print(maps[4], end = " ") print(maps[5]) print(maps[6], end = " ") print(maps[7], end = " ") print(maps[8]) # Сделать ход в ячейку def step_maps(step,symbol): ind = maps.index(step) maps[ind] = symbol # Получить текущий результат игры def get_result(): win = "" for i in victories: if maps[i[0]] == "X" and maps[i[1]] == "X" and maps[i[2]] == "X": win = "X" if maps[i[0]] == "O" and maps[i[1]] == "O" and maps[i[2]] == "O": win = "O" return win #Искусственный интеллект: поиск линии с нужным количеством X и O на победных линиях def check_line(sum_O,sum_X): step = "" for line in victories: o = 0 x = 0 for j in range(0,3): if maps[line[j]] == "O": o = o + 1 if maps[line[j]] == "X": x = x + 1 if o == sum_O and x == sum_X: for j in range(0,3): if maps[line[j]] != "O" and maps[line[j]] != "X": step = maps[line[j]] return step #Искусственный интеллект: выбор хода def AI(): step = "" # 1) если на какой либо из победных линий 2 свои фигуры и 0 чужих - ставим step = check_line(2,0) # 2) если на какой либо из победных линий 2 чужие фигуры и 0 своих - ставим if step == "": step = check_line(0,2) # 3) если 1 фигура своя и 0 чужих - ставим if step == "": step = check_line(1,0) # 4) центр пуст, то занимаем центр if step == "": if maps[4] != "X" and maps[4] != "O": step = 5 # 5) если центр занят, то занимаем первую ячейку if step == "": if maps[0] != "X" and maps[0] != "O": step = 1 return step # Основная программа game_over = False human = True while game_over == False: # 1. Показываем карту print_maps() # 2. Спросим у играющего куда делать ход if human == True: symbol = "X" step = int(input("Человек, ваш ход: ")) else: print("Компьютер делает ход: ") symbol = "O" step = AI() # 3. Если компьютер нашел куда сделать ход, то играем. Если нет, то ничья. if step != "": step_maps(step,symbol) # делаем ход в указанную ячейку win = get_result() # определим победителя if win != "": game_over = True else: game_over = False else: print("Ничья!") game_over = True win = "дружба" human = not(human) # Игра окончена. Покажем карту. Объявим победителя. print_maps() print("Победил", win)
Вот пример игрового процессе. В этой игре победил компьютер:
1 2 3 4 5 6 7 8 9 Человек, ваш ход: 1 X 2 3 4 5 6 7 8 9 Компьютер делает ход: X 2 3 4 O 6 7 8 9 Человек, ваш ход: 2 X X 3 4 O 6 7 8 9 Компьютер делает ход: X X O 4 O 6 7 8 9 Человек, ваш ход: 4 X X O X O 6 7 8 9 Компьютер делает ход: X X O X O 6 O 8 9 Победил O
Вы можете прямо сейчас поиграть в крестики нолики против компьютера или друг против друга. Просто скопируйте текст программы на этой странице и вставьте его в поле программы в этом онлайн эмуляторе Python: https://replit.com/languages/python3 или тут https://www.onlinegdb.com/online_python_compiler
Домашнее задание
У нас получилась довольно беспощадная программа. Ее искусственный интеллект победить не возможно. Человек либо проигрывает, либо играет в ничью. Подумайте как можно дать человеку иногда выигрывать? Возможно вы захотите создать несколько уровней. Например в первом искусственный интеллект очень слаб, дальше он умнет. Или у вас будет на выбор несколько режимов игры на выбор: слабый ИИ, средний и непобедимый.
В общем ваша задача немного ослабить искусственный интеллект в нашей игре крестики-нолики. Сделать его чуть более человечным.