Пишем игру крестики-нолики на Python на двоих и против компьютера

Если вы учитесь программировать, то программирование игр — это очень хороший способ освоить алгоритмы и структуры данных. В этом уроке мы разберем запрограммируем игру крестики нолики на 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

А сейчас мы напишем функцию для поиска очередного лучшего хода для искусственного интеллекта. Запрограммируем такой алгоритм для каждого хода (компьютер играет ноликами):

  1. Если этим ходом можем выиграть — выигрываем (уже 2 нолика стоят на одной из линий). Иначе идем к шагу 2.
  2. Если можем помешать выиграть человеку — мешаем ( у человека уже 2 крестика на линии — ставим на нее нолик). Иначе идем к шагу 3.
  3. Если на линии одна наша фигура — ставим вторую. Если пока ни одной нашей фигуры — идем к шагу 4.
  4. Ставим нолик в центр. Если центр занят идем к шагу 5.
  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

Домашнее задание

У нас получилась довольно беспощадная программа. Ее искусственный интеллект победить не возможно. Человек либо проигрывает, либо играет в ничью. Подумайте как можно дать человеку иногда выигрывать? Возможно вы захотите создать несколько уровней. Например в первом искусственный интеллект очень слаб, дальше он умнет. Или у вас будет на выбор несколько режимов игры на выбор: слабый ИИ, средний и непобедимый.

В общем ваша задача немного ослабить искусственный интеллект в нашей игре крестики-нолики. Сделать его чуть более человечным.

Вам также может понравиться

About the Author: Азат Ахметович

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Максимальный размер загружаемого файла: 999 МБ. Вы можете загрузить: изображение, аудио, видео, документ, таблица, интерактив, текст, архив, код, другое. Ссылки на YouTube, Facebook, Twitter и другие сервисы, вставленные в текст комментария, будут автоматически встроены. Drop files here