Python: Задачи и решения (Глава 6. Функции. Игра “Крестики-нолики”).


Продолжаем практиковаться в программировании. После шестой главы в книге: Майкл Доусон “Программируем на Python”, 2014 (Michael Dawson “Python Programming for the Absolute Beginner”, 3rd Edition), где я научилась использовать функции, пора переходить к практике. Сделаем домашнее задание вместе!

Доработка функции ask_number() / hod_number()

Задача: Доработайте функцию ask_number() или hod_number() так, чтобы ее можно было вызывать еще с одним параметром — кратностью (величиной шага). Сделайте шаг по умолчанию равным 1.

Первоначальный вид функции:

def hod_number(low,high):
    otvet=None
    while otvet not in range(low,high):
        otvet=int(input("Делай свой ход - напиши номер поля (0-8): "))
    return otvet

Доработанный вариант функции:

def hod_number(low,high,step=1):
    otvet=None
    while otvet not in range(low,high,step):
        otvet=int(input("Делай свой ход - напиши номер поля (0-8): "))
    return otvet

Доработка игры «Отгадай число»

Задача: Доработайте игру «Отгадай число» из главы 3 так, чтобы в ней нашла применение функция ask_number() она же hod_number().

Исходный код игры «Отгадай число»:

import random 
print('Я загадала целое число от 0 до 100. Угадай с 5 попыток!') 
chislo=random.randrange(101) 
popitka_count=0 
while popitka_count<=4:
    popitka_count+=1
    popitka=int(input('Это число:')) 
    if popitka<chislo: 
        print('Больше!') 
    elif popitka>chislo: 
        print('Меньше!') 
    else:
        print('Ничего себе! Ты отгадал! Это правда',chislo) 
        print('Количество попыток:',popitka_count) 
        break
if popitka_count==5 and popitka!=chislo:
    print('О, ужас! Ты совершенно не умеешь читать мои мысли!\n\
Так и не смог удагать число за 5 попыток :(')

Доработанная программа с функцией hod_number():

import random 

print('Я загадала целое число от 0 до 100. Угадай с 5 попыток!') 
chislo=random.randrange(101)
popitka=None
def hod_number(low=0,high=5):
    popitka_count=0
    while popitka_count in range(low,high):
        popitka=int(input("Это число: "))
        popitka_count+=1
        if popitka<chislo: 
            print('Больше!') 
        elif popitka>chislo: 
            print('Меньше!') 
        elif popitka==chislo:
            print('Ничего себе! Ты отгадал! Это правда',chislo)
            print('Количество попыток:',popitka_count)
            break
    return popitka
hod_number()
if popitka!=chislo:
    print('О, ужас! Ты совершенно не умеешь читать мои мысли!\n\
Так и не смог угадать число за 5 попыток :(')

Запуск доработанной игры «Отгадай число» выглядит так:

Я загадала целое число от 0 до 100. Угадай с 5 попыток!
Это число: 56
Больше!
Это число: 68
Больше!
Это число: 76
Меньше!
Это число: 70
Больше!
Это число: 74
Меньше!
О, ужас! Ты совершенно не умеешь читать мои мысли!
Так и не смог угадать число за 5 попыток :(
>>> 

Еще один вариант доработанной функции:

def hod_number(LOW=0,HIGH=5):
    popitka=int(input("Это число: "))
    return popitka

Доработка новой версии игры «Отгадай число»

Задача: Доработайте новую версию игры «Отгадай число» (которую вы создали, решая предыдущую задачу) так, чтобы основная часть программы стала функцией main(). Для того чтобы игра началась, не забудьте вызвать эту функцию глобально.

Решение:

def hod_number(LOW=0,HIGH=5):
    popitka=int(input("Это число: "))
    return popitka
def main():
    LOW=0
    HIGH=5
    popitka=0
    popitka_count=0
    print('Я загадала целое число от 0 до 100. Угадай с 5 попыток!')
    chislo=random.randrange(101)
    while popitka_count in range(LOW,HIGH):
        hod_number()
        popitka_count+=1
        if popitka<chislo: 
            print('Больше!') 
        elif popitka>chislo: 
            print('Меньше!') 
        elif popitka==chislo:
            print('Ничего себе! Ты отгадал! Это правда',chislo)
            print('Количество попыток:',popitka_count)
            break
    if popitka!=chislo:
        print('О, ужас! Ты совершенно не умеешь читать мои мысли!\n\
Так и не смог угадать число за 5 попыток :(')
import random
main ()
input('Нажмите Entr, чтобы выйти.')

Запуск программы игры:

Я загадала целое число от 0 до 100. Угадай с 5 попыток!
Это число: 50
Больше!
Это число: 60
Больше!
Это число: 70
Больше!
Это число: 80
Больше!
Это число: 90
Больше!
О, ужас! Ты совершенно не умеешь читать мои мысли!
Так и не смог угадать число за 5 попыток :(
Нажмите Entr, чтобы выйти.
>>>

Непобедимый соперник в Крестики-Нолики

Задача: Напишите такую функцию computer_move() или comp_hod(), которая сделала бы стратегию компьютера безупречной. Проверьте, можно ли создать непобедимого противника.

Исходный код игры «Крестики-нолики»:

X='X'
O='0'
RAZMER_DOSKI=9
HODI=' '
NICHYA='Ничья'

def instrukciya():
    print('''
Привет! Это игра "Крестики-нолики".
Чтобы сделать ход, введи номер клетки,
куда хочешь поставить свой символ:

0 | 1 | 2
---------
3 | 4 | 5
---------
6 | 7 | 8


''')
def nachalo(vopros):
    otvet=None
    while otvet not in ('да','нет'):
        otvet=input(vopros).lower()
    return otvet

def fishki():
    perviy_hod=nachalo("Вы хотите быть первым, кто сделает ход \
(играть крестиками)?  ")
    if perviy_hod=='да':
        print('Окей, ты играешь крестиками!')
        human=X
        comp=O
    else:
        print('ОК, я делаю первый ход крестиками')
        human=O
        comp=X
    return comp, human
        
def hod_number(low,high):
    otvet=None
    while otvet not in range(low,high):
        otvet=int(input("Делай свой ход - напиши номер поля (0-8): "))
    return otvet


def new_doska():
    doska=[]
    for i in range(RAZMER_DOSKI):
        doska.append(HODI)
    return doska

def pokaz_doski(doska):
    print('\n', doska[0], '|', doska[1], '|', doska [2])
    print('---------')
    print('\n'
          , doska[3], '|', doska[4], '|', doska [5])
    print('---------')
    print('\n', doska[6], '|', doska[7], '|', doska [8], '\n')

def dostupnie_hodi(doska):
    dostupnie_hodi=[]
    for i in range(RAZMER_DOSKI):
        if doska[i]== HODI:
            dostupnie_hodi.append(i)
    return dostupnie_hodi

def winner(doska):
    VAR_POBED=((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 i in VAR_POBED:
        if doska[i[0]]==doska[i[1]]==doska[i[2]]!=HODI:
            winner=doska[i[0]]
            return winner
        if HODI not in doska:
            return NICHYA
    return None
def human_hod(doska,human):
    dostupnie=dostupnie_hodi(doska)
    hod=None
    while hod not in dostupnie:
        hod=hod_number(0,RAZMER_DOSKI)
        if hod not in dostupnie:
            print('Поле занято. Напиши другой номер: ')
    print('Супер!')
    return hod
def comp_hod(doska,comp,human):
    doska=doska[:]
    BEST_HODI=(4,0,2,6,8,1,3,5,7)
    print('Мой ход: ')
    for i in dostupnie_hodi(doska):
        doska[i]=comp
        if winner(doska)==comp:
            print(i)
            return i
        doska[i]=HODI
    for j in dostupnie_hodi(doska):
        doska[j]=human
        if winner(doska)==human:
            print(j)
            return j
        doska[j]=HODI
    for k in dostupnie_hodi(doska):
        print(k)
        return k
def next_ochered(ochered):
    if ochered==X:
        return O
    else:
        return X
def pozdrav_pobeditela(pobeditel,comp,human):
    if pobeditel!=NICHYA:
        print('Собрана линия ', pobeditel)
    else:
        print(NICHYA)
    if pobeditel==comp:
        print('Компьютер выиграл!')
    elif pobeditel==human:
        print('Ты победил!')
    elif pobeditel==NICHYA:
        print(NICHYA)
def main():
    instrukciya()
    comp,human=fishki()
    ochered=X
    doska=new_doska()
    pokaz_doski(doska)
    while not winner(doska):
        if ochered==human:
            hod=human_hod(doska,human)
            doska[hod]=human
        else:
            hod=comp_hod(doska,comp,human)
            doska[hod]=comp
        pokaz_doski(doska)
        ochered=next_ochered(ochered)
    pobeditel=winner(doska)
    pozdrav_pobeditela(pobeditel,comp,human)

main()
input('\n Нажми Entr, чтобы выйти')

Исходная функция comp_hod() выглядит так:

def comp_hod(doska,comp,human):
    doska=doska[:]
    BEST_HODI=(4,0,2,6,8,1,3,5,7)
    print('Мой ход: ')
    for i in dostupnie_hodi(doska):
        doska[i]=comp
        if winner(doska)==comp:
            print(i)
            return i
        doska[i]=HODI
    for j in dostupnie_hodi(doska):
        doska[j]=human
        if winner(doska)==human:
            print(j)
            return j
        doska[j]=HODI
    for k in dostupnie_hodi(doska):
        print(k)
        return k

Беспроигрышная стратегия компьютера в крестики-нолики возможна в 4/5 случаев, если его ход компьютера первый. Как выиграть в крестики-нолики если ходишь вторым — вопрос более интересный, ведь в моей программе пользователь сам выбирает, ходить первым или нет. Если зайти в википедию на игру «Крестики-нолики», то можно найти все возможные стратегии выигрыша в этой игре и возможности сыграть в ничью. 

Невозможно создать абсолютно беспроигрышную стратегию для компьютера, ведь уже известны стратегии и для ноликов, и для крестиков, которые гарантированно приводят к ничьей.  Так что выигрыш возможен тогда, когда противник допускает ошибку.

  1. В первую очередь нужно проверить, можно ли выиграть следующим ходом, и так пойти.
  2. Если выиграть не получится, то нужно проверить, не может ли выиграть следующим ходом противник, и пойти туда.

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

  • если компьютер ходит первым (за крестики), то нужно пойти в центр, а затем ходить в такой свободный угол, который располагается дальше всего от последнего хода ноликов, а если так пойти нельзя — то ход выбирается рандомно;
  • если компьютер ходит вторым (играет за нолики), то проверяем куда был сделан первый ход:
    • если крестики пошли в центр, то ходить в любой угол, а если все углы уже заняты, то в любое место;
    • если крестики пошли первым ходом в угол, то сначала занимаем центр, а затем ходим в угол напротив первого хода крестиков, если таких вариантов нет — ходим по сторонам;
    • если крестики сделали первый ход по сторонам поля, ходим в центр,
      • если дальше крестики пошли в угол — ходим в угол на противоположной стороне (крест-на-крест),
      • если дальше крестики пошли в стороны напротив, ходим в любые углы,
      • если дальше крестики пошли по стороне рядом со своим первым ходом — ходим в угол рядом с крестиками.

Теперь осталось записать эту логику в нашу программу. Используем константу BEST_HODI, куда запишем лучшие ходы по приоритетам для компьютера:

def comp_hod(doska,comp,human):
    doska=doska[:]
    BEST_HODI=(0,2,6,8,1,3,5,7)
    print('Мой ход: ')
    for i in dostupnie_hodi(doska):
        doska[i]=comp
        if winner(doska)==comp:
            print(i)
            return i
        doska[i]=HODI
    for j in dostupnie_hodi(doska):
        doska[j]=human
        if winner(doska)==human:
            print(j)
            return j
        doska[j]=HODI
    if comp==X and 4 in dostupnie_hodi(doska):
        print (4)
        return 4
        doska[4]=HODI
    if comp==X and 4 not in dostupnie_hodi(doska):
        for k in BEST_HODI:
            if BEST_HODI[k] in dostupnie_hodi(doska):
                print(k)
                return k

(*Спустя месяц я обнаружила, что этот код не работает. Что так с ним? ХЗ)

Стратегия компьютера сильно улучшилась по сравнению с первоначальным вариантом. Запуск игры выглядит теперь так:

Привет! Это игра "Крестики-нолики".
Чтобы сделать ход, введи номер клетки,
куда хочешь поставить свой символ:

0 | 1 | 2
---------
3 | 4 | 5
---------
6 | 7 | 8



Вы хотите быть первым, кто сделает ход (играть крестиками)?  нет
ОК, я делаю первый ход крестиками

   |   |  
---------

   |   |  
---------

   |   |   

Мой ход: 
4

   |   |  
---------

   | X |  
---------

   |   |   

Делай свой ход - напиши номер поля (0-8): 0
Супер!

 0 |   |  
---------

   | X |  
---------

   |   |   

Мой ход: 
2

 0 |   | X
---------

   | X |  
---------

   |   |   

Делай свой ход - напиши номер поля (0-8): 8
Супер!

 0 |   | X
---------

   | X |  
---------

   |   | 0 

Мой ход: 
6

 0 |   | X
---------

   | X |  
---------

 X |   | 0 

Собрана линия  X
Компьютер выиграл!

 Нажми Entr, чтобы выйти

Как сделать искусственный интеллект нашего компьютерного игрока еще более умным?

Можно использовать идею "вилки" выигрыша. Добавить компьютеру проверку хода на создание следующим ходом два и более варианта выигрыша. Нужно, чтобы компьютер стремился ставить свою фишку так, чтобы создавать вилки. А если такой возможности нет, нужно заблокировать подобную опцию для игрока.

Следующее важное условие для создания непобедимого компьютера — важно, чтобы компьютер предпочитал центр углам.

Таким образом, опишем алгоритм непобедимого компьютера:

  • проверка на наличие хода для выигрыша компьютера;
  • проверка на наличие выигрышного следующего хода противника, чтобы его заблокировать;
  • проверка на возможность создания «вилки», но если есть возможность заблокировать будущие «вилки» противника — сделать это предпочтительно;
  • проверить центр;
  • проверить углы;
  • проверить боковые стороны;
  • в случае, когда можно выбрать любой ход, делать это рандомно.

Чтобы записать этот алгоритм непобедимого соперника в игре «Крестики-Нолики» на языке программирования, для начала нарисуем схему из if-else:

Я человек «из прошлого», так что писать на бумаге мне удобнее и привычнее. По идее эта схема и есть «псевдокод», который осталось просто перенести в Python. Если я ничего не упустила, то эта программа даст нам соперника в лице «искусственного интеллекта», которого ни один не сможет победить. После того, как я записала конечный вариант алгоритма, я так сильно устала от программирования, что забросила это дело на месяц или даже больше.

И вот я вернулась! И я готова еще раз сразиться с этой задачей.

Чтобы не запутаться, я решила взять исходный вариант функции comp_hod

def comp_hod(doska,comp,human): 
	doska=doska[:] 
	BEST_HODI=(4,0,2,6,8,1,3,5,7) 
	print('Мой ход: ') 
	for i in dostupnie_hodi(doska): 
		doska[i]=comp 
		if winner(doska)==comp: 
			print(i) 
			return i 
		doska[i]=HODI 
	for j in dostupnie_hodi(doska): 
		doska[j]=human 
		if winner(doska)==human: 
			print(j) 
			return j 
		doska[j]=HODI 
	for k in dostupnie_hodi(doska):
		print(k) 
		return k

Оставим начало неизменным до строчки for k in dostupnie_hodi(doska):

Эта часть останется как есть:

def comp_hod(doska,comp,human): 
	doska=doska[:] 
	BEST_HODI=(4,0,2,6,8,1,3,5,7) 
	print('Мой ход: ') 
	for i in dostupnie_hodi(doska): 
		doska[i]=comp 
		if winner(doska)==comp: 
			print(i) 
			return i 
		doska[i]=HODI 
	for j in dostupnie_hodi(doska): 
		doska[j]=human 
		if winner(doska)==human: 
			print(j) 
			return j 
		doska[j]=HODI 

Нам осталось расписать все наши if/else в разделе:

for k in dostupnie_hodi(doska):
		print(k) 
		return k

Изначально запишем два основных условия:

for k in dostupnie_hodi(doska):
            if компьютер ходит первым
            else компьютер ходит вторым
		print(k) 
		return k

Далее запишем подусловия:

for k in dostupnie_hodi(doska):
            if компьютер ходит первым
                Блок первого хода
                Блок 2-го и последующих ходов
            else компьютер ходит вторым
                Блок первого хода
                Блок 2-го и последующих ходов
		print(k) 
		return k

Распишем условия и блоки через if/else для случая, когда компьютер ходит первым:

for k in dostupnie_hodi(doska):
            if компьютер ходит первым (Х)
                Блок первого хода
                    if 4 центр свободен:
                        X=4 занять центр
                    else:
                        random ход из доступных
                Блок 2-го и последующих ходов
                    if предыдущий ход O=0:
                        X=8, если занято, то X = 6 или 2,
                        если занято, то random
                    if предыдущий ход O=1:
                        X=6 or 8, если занято, то random
                    if предыдущий ход O=3:
                        X=2 or 8, если занято, то random
                    if предыдущий ход O=5:
                        X=0 or 6, если занято, то random
                    if предыдущий ход O=6:
                        X=2 если занято, то X = 0 или 8,
                        если занято, то random
                    if предыдущий ход O=7:
                        X=0 or 2, если занято, то random
                    if предыдущий ход O=8:
                        X=0 если занято, то X = 6 или 2,
                        если занято, то random
            else компьютер ходит вторым (O)
                Блок первого хода
                Блок 2-го и последующих ходов
		print(k) 
		return k

Распишем условия и блоки через if/else для случая, когда компьютер ходит вторым:

for k in dostupnie_hodi(doska):
            if компьютер ходит первым (Х)
                Блок первого хода
                    if 4 центр свободен:
                        X=4 занять центр
                    else:
                        random ход из доступных
                Блок 2-го и последующих ходов
                    if предыдущий ход O=0:
                        X=8, если занято, то X = 6 или 2,
                        если занято, то random
                    if предыдущий ход O=1:
                        X=6 or 8, если занято, то random
                    if предыдущий ход O=3:
                        X=2 or 8, если занято, то random
                    if предыдущий ход O=5:
                        X=0 or 6, если занято, то random
                    if предыдущий ход O=6:
                        X=2 если занято, то X = 0 или 8,
                        если занято, то random
                    if предыдущий ход O=7:
                        X=0 or 2, если занято, то random
                    if предыдущий ход O=8:
                        X=0 если занято, то X = 6 или 2,
                        если занято, то random
            else компьютер ходит вторым (O)
                Блок первого хода
                    if предыдущий ход противника X=4 (центр):
                        O=[0,2,6,8], если занято, то random
                    if предыдущий ход X=[0,2,6,8]:
                        O=4 (занять центр)
                    if предыдущий ход X=[1,3,5,7]:
                        O=4 (занять центр)
                Блок 2-го и последующих ходов
                    if первый ход X=0:
                        O=8
                    if первый ход X=2:
                        O=6
                    if перывй ход X=6:
                        O=2
                    if первый ход X=8:
                        O=0
                    if первый ход X=1 and второй ход X=[0 или 2]:
                        O=[0 или 2]
                    if первый ход X=1 and второй ход X!=[0;2]:
                        O=[0;2;6;8]
                    if первый ход X=3 and второй ход X=[0;6]:
                        O=[0;6]
                    if первый ход X=3 and второй ход X!=[0;6]:
                        O=[0;2;6;8]
                    if первый ход X=5 and второй ход X=[2;8]:
                        O=[2;8]
                    if первый ход X=5 and второй ход X!=[2;8]:
                        O=[0;2;6;8]
                    if первый ход X=7 and второй ход X=[6;8]:
                        O=[6;8]
                    if первый ход X=7 and второй ход X!=[6;8]:
                        O=[0;2;6;8]
                    else: 0=[1,3,5,7]
		print(k) 
		return k

Это готовый псвдокод абсолютно непобедимого соперника в крестики-нолики. Переведем его в язык программирования?

Чтобы с кодом было проще работать, введем значок #, чтобы закомментировать строки:

for k in dostupnie_hodi(doska):
            if perviy_hod!='да'#компьютер ходит первым (Х)
                #Блок первого хода
                    if #4 центр свободен:
                        #X=4 занять центр
                    else:
                        #random ход из доступных
                #Блок 2-го и последующих ходов
                    if #предыдущий ход O=0:
                        #X=8, если занято, то X = 6 или 2,
                        #если занято, то random
                    if #предыдущий ход O=1:
                        #X=6 or 8, если занято, то random
                    if #предыдущий ход O=3:
                        #X=2 or 8, если занято, то random
                    if #предыдущий ход O=5:
                        #X=0 or 6, если занято, то random
                    if #предыдущий ход O=6:
                        #X=2 если занято, то X = 0 или 8,
                        #если занято, то random
                    if #предыдущий ход O=7:
                        #X=0 or 2, если занято, то random
                    if #предыдущий ход O=8:
                        #X=0 если занято, то X = 6 или 2,
                        #если занято, то random
            else #компьютер ходит вторым (O)
                #Блок первого хода
                    if #предыдущий ход противника X=4 (центр):
                        #O=[0,2,6,8], если занято, то random
                    if #предыдущий ход X=[0,2,6,8]:
                        #O=4 (занять центр)
                    if #предыдущий ход X=[1,3,5,7]:
                        #O=4 (занять центр)
                #Блок 2-го и последующих ходов
                    if #первый ход X=0:
                        #O=8
                    if #первый ход X=2:
                        #O=6
                    if #перывй ход X=6:
                        #O=2
                    if #первый ход X=8:
                        #O=0
                    if #первый ход X=1 and второй ход X=[0 или 2]:
                        #O=[0 или 2]
                    if #первый ход X=1 and второй ход X!=[0;2]:
                        #O=[0;2;6;8]
                    if #первый ход X=3 and второй ход X=[0;6]:
                        #O=[0;6]
                    if #первый ход X=3 and второй ход X!=[0;6]:
                        #O=[0;2;6;8]
                    if #первый ход X=5 and второй ход X=[2;8]:
                        #O=[2;8]
                    if #первый ход X=5 and второй ход X!=[2;8]:
                        #O=[0;2;6;8]
                    if #первый ход X=7 and второй ход X=[6;8]:
                        #O=[6;8]
                    if #первый ход X=7 and второй ход X!=[6;8]:
                        #O=[0;2;6;8]
                    else: #0=[1,3,5,7]
		print(k) 
		return k

Теперь комментарии подсвечиваются красным, это позволяет не отвлекаться и писать код на языке Python.

Поскольку нам нужно использовать значение первого хода — кто ходит первым? (человек или компьютер) — то придется добавить в функцию вывода def fishki() вывод значения perviy_hod:

def fishki():
    perviy_hod=nachalo("Вы хотите быть первым, кто сделает ход \
(играть крестиками)?  ")
    if perviy_hod=='да':
        print('Окей, ты играешь крестиками!')
        human=X
        comp=O
    else:
        print('ОК, я делаю первый ход крестиками')
        human=O
        comp=X
    return comp, human, perviy_hod

Теперь поменяем местами блок «если компьютер ходит первым» и «если компьютер ходит вторым», так как проще записать для варианта #компьютер ходит вторым условие if perviy_hod:

for k in dostupnie_hodi(doska):

#else #компьютер ходит вторым (O)
                if perviy_hod:
                #Блок первого хода
                    if #предыдущий ход противника X=4 (центр):
                        #O=[0,2,6,8], если занято, то random
                    if #предыдущий ход X=[0,2,6,8]:
                        #O=4 (занять центр)
                    if #предыдущий ход X=[1,3,5,7]:
                        #O=4 (занять центр)
                #Блок 2-го и последующих ходов
                    if #первый ход X=0:
                        #O=8
                    if #первый ход X=2:
                        #O=6
                    if #перывй ход X=6:
                        #O=2
                    if #первый ход X=8:
                        #O=0
                    if #первый ход X=1 and второй ход X=[0 или 2]:
                        #O=[0 или 2]
                    if #первый ход X=1 and второй ход X!=[0;2]:
                        #O=[0;2;6;8]
                    if #первый ход X=3 and второй ход X=[0;6]:
                        #O=[0;6]
                    if #первый ход X=3 and второй ход X!=[0;6]:
                        #O=[0;2;6;8]
                    if #первый ход X=5 and второй ход X=[2;8]:
                        #O=[2;8]
                    if #первый ход X=5 and второй ход X!=[2;8]:
                        #O=[0;2;6;8]
                    if #первый ход X=7 and второй ход X=[6;8]:
                        #O=[6;8]
                    if #первый ход X=7 and второй ход X!=[6;8]:
                        #O=[0;2;6;8]
                    else: #0=[1,3,5,7]

            
            #компьютер ходит первым (Х)
            else:
            #Блок первого хода
                    if doska[4] in dostupnie_hodi(doska):#4 центр свободен:
                        doska[4]=comp #X=4 занять центр
                        print(4) 
			return 4 
                    else: #random ход из доступных
                        for i in dostupnie_hodi(doska): 
                        doska[i]=comp
                        print(i) 
			return i 
                #Блок 2-го и последующих ходов
                    if #предыдущий ход O=0:
                        #X=8, если занято, то X = 6 или 2,
                        #если занято, то random
                    if #предыдущий ход O=1:
                        #X=6 or 8, если занято, то random
                    if #предыдущий ход O=3:
                        #X=2 or 8, если занято, то random
                    if #предыдущий ход O=5:
                        #X=0 or 6, если занято, то random
                    if #предыдущий ход O=6:
                        #X=2 если занято, то X = 0 или 8,
                        #если занято, то random
                    if #предыдущий ход O=7:
                        #X=0 or 2, если занято, то random
                    if #предыдущий ход O=8:
                        #X=0 если занято, то X = 6 или 2,
                        #если занято, то random
            
		print(k) 
		return k

Также нам понадобится значение хода человека, так что добавим вводный параметр hod в функцию:

def comp_hod(doska,comp,human,perviy_hod,hod): 

Восстанавливаем логику программы:

      if perviy_hod:
                #Блок первого хода
                    if hod==4: 
#предыдущий ход противника X=4 (центр):
                        for i in (0,2,6,8): 
#O=[0,2,6,8], если занято, то random
                                if i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                        if (0,2,6,8) not in dostupnie_hodi(doska):
                            for j in dostupnie_hodi(doska):
                            doska[j]=comp
                            print(j)
                            return j  

Теперь нужно ввести дополнительное значение «первый ход человека», в имеющейся функции human_hod(doska,human) у нас нет возможности использовать такое значение. Можно попробовать использовать переменную HODI , куда записываются ходы в процессе игры. Так же нужно записать ходы компьютера в базу ходов:

#компьютер ходит вторым (O)
                if perviy_hod:
                #Блок первого хода
                    if hod==4: 
#предыдущий ход противника X=4 (центр):
                        for i in (0,2,6,8): 
#O=[0,2,6,8], если занято, то random
                                if i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                        doska[i]=HODI
                        if (0,2,6,8) not in dostupnie_hodi(doska):
                            for j in dostupnie_hodi(doska):
                            doska[j]=comp
                            print(j)
                            return j
                        doska[j]=HODI
                    elif hod in (0,2,6,8): 
#предыдущий ход X=[0,2,6,8]:
                        doska[4]=comp #O=4 (занять центр)
                        print(4)
                        return 4
                        doska[4]=HODI
                    elif hod in (1,3,5,7): 
#предыдущий ход X=[1,3,5,7]:
                        doska[4]=comp #O=4 (занять центр)
                        print(4)
                        return 4
                        doska[4]=HODI

Блок «если компьютер ходит вторым» выглядит так:

#компьютер ходит вторым (O)
                if perviy_hod:
                #Блок первого хода
                    if hod==4: 
#предыдущий ход противника X=4 (центр):
                        for i in (0,2,6,8): 
#O=[0,2,6,8], если занято, то random
                                if i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                doska[i]=HODI
                        if (0,2,6,8) not in dostupnie_hodi(doska):
                            for j in dostupnie_hodi(doska):
                                doska[j]=comp
                                print(j)
                                return j
                                doska[j]=HODI
                    elif hod in (0,2,6,8): 
#предыдущий ход X=[0,2,6,8]:
                        doska[4]=comp #O=4 (занять центр)
                        print(4)
                        return 4
                        doska[4]=HODI
                    elif hod in (1,3,5,7): 
#предыдущий ход X=[1,3,5,7]:
                        doska[4]=comp #O=4 (занять центр)
                        print(4)
                        return 4
                        doska[4]=HODI
                #Блок 2-го и последующих ходов
                    if HODI[0]==0:#первый ход X=0:
                        doska[8]=comp #O=8
                        print(8)
                        return 8
                        doska[8]=HODI
                    if HODI[0]==2:#первый ход X=2:
                        doska[6]=comp #O=6
                        print(6)
                        return 6
                        doska[6]=HODI
                    if HODI[0]==6: #перывй ход X=6:
                        doska[2]=comp #O=2
                        print(2)
                        return 2
                        doska[2]=HODI
                    if HODI[0]==8: #первый ход X=8:
                        doska[0]=comp #O=0
                        print(0)
                        return 0
                        doska[0]=HODI
                    if HODI[0]==1 and (HODI[1]==0 or HODI[1]==2):
#первый ход X=1 and второй ход X=[0 или 2]:
                        if 0 in dostupnie_hodi(doska): #O=[0 или 2]
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 2 in dostupnie_hodi(doska):
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                    if HODI[0]==1 and (HODI[1]!=0 or HODI[1]!=2):
#первый ход X=1 and второй ход X!=[0;2]:
                        for i in (0,2,6,8): #O=[0;2;6;8]
                            if i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                        doska[i]=HODI
                    if HODI[0]==3 and (HODI[1]==0 or HODI[1]==6):
#первый ход X=3 and второй ход X=[0;6]:
                        if 0 in dostupnie_hodi(doska): #O=[0;6]
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 6 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                    if HODI[0]==3 and (HODI[1]!=0 or HODI[1]!=6): 
#первый ход X=3 and второй ход X!=[0;6]:
                        if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 2 in dostupnie_hodi(doska):
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        elif 6 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                    if HODI[0]==5 and (HODI[1]==2 or HODI[1]==8):
#первый ход X=5 and второй ход X=[2;8]:
                        if 2 in dostupnie_hodi(doska):#O=[2;8]
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                    if HODI[0]==5 and (HODI[1]!=2 or HODI[1]!=8):
#первый ход X=5 and второй ход X!=[2;8]:
                        if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 2 in dostupnie_hodi(doska):
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        elif 6 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                    if HODI[0]==7 and (HODI[1]==6 or HODI[1]==8):
#первый ход X=7 and второй ход X=[6;8]:
                        if 6 in dostupnie_hodi(doska):#O=[6;8]
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                    if HODI[0]==7 and (HODI[1]!=6 or HODI[1]!=8):
#первый ход X=7 and второй ход X!=[6;8]:
                        if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 2 in dostupnie_hodi(doska):
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        elif 6 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                    else:
                        if 1 in dostupnie_hodi(doska):#0=[1,3,5,7]
                            doska[1]=comp
                            print(1)
                            return 1
                            doska[1]=HODI
                        elif 3 in dostupnie_hodi(doska):
                            doska[3]=comp
                            print(3)
                            return 3
                            doska[3]=HODI
                        elif 5 in dostupnie_hodi(doska):
                            doska[5]=comp
                            print(5)
                            return 5
                            doska[5]=HODI
                        elif 7 in dostupnie_hodi(doska):
                            doska[7]=comp
                            print(7)
                            return 7
                            doska[7]=HODI

            
            #компьютер ходит первым (Х)
                else:
            #Блок первого хода
                    if doska[4] in dostupnie_hodi(doska):
#4 центр свободен:
                        doska[4]=comp #X=4 занять центр
                        print(4)
                        return 4
                        doska[4]=HODI
                    else: #random ход из доступных
                        for i in dostupnie_hodi(doska):
                            doska[i]=comp
                            print(i)
                            return i
                            doska[i]=HODI

В блоке «если компьютер ходит первым» нам нужно значение переменной «предыдущий ход противника». Где же его взять? Наверное, можно взять hod, так как это и будет предыдущий ход. 

Как только я дописала код, я столкнулась с ошибкой синтаксиса при попытке запустить его. Где-то были лишние пробелы или табы. Пришлось потратить время, чтобы разобраться.

В итоге у меня получился вот такой код непобедимого соперника в крестики-нолики (который пока не заработал):

X='X'
O='0'
RAZMER_DOSKI=9
HODI=' '
NICHYA='Ничья'

def instrukciya():
    print('''
Привет! Это игра "Крестики-нолики".
Чтобы сделать ход, введи номер клетки,
куда хочешь поставить свой символ:

0 | 1 | 2
---------
3 | 4 | 5
---------
6 | 7 | 8


''')
def nachalo(vopros):
    otvet=None
    while otvet not in ('да','нет'):
        otvet=input(vopros).lower()
    return otvet

def fishki():
    perviy_hod=nachalo("Вы хотите быть первым, кто сделает ход \
(играть крестиками)?  ")
    if perviy_hod=='да':
        print('Окей, ты играешь крестиками!')
        return perviy_hod
        human=X
        comp=O
    else:
        print('ОК, я делаю первый ход крестиками')
        human=O
        comp=X
    return comp, human
        
def hod_number(low,high):
    otvet=None
    while otvet not in range(low,high):
        otvet=int(input("Делай свой ход - напиши номер поля (0-8): "))
    return otvet


def new_doska():
    doska=[]
    for i in range(RAZMER_DOSKI):
        doska.append(HODI)
    return doska

def pokaz_doski(doska):
    print('\n', doska[0], '|', doska[1], '|', doska [2])
    print('---------')
    print('\n'
          , doska[3], '|', doska[4], '|', doska [5])
    print('---------')
    print('\n', doska[6], '|', doska[7], '|', doska [8], '\n')

def dostupnie_hodi(doska):
    dostupnie_hodi=[]
    for i in range(RAZMER_DOSKI):
        if doska[i]== HODI:
            dostupnie_hodi.append(i)
    return dostupnie_hodi

def winner(doska):
    VAR_POBED=((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 i in VAR_POBED:
        if doska[i[0]]==doska[i[1]]==doska[i[2]]!=HODI:
            winner=doska[i[0]]
            return winner
        if HODI not in doska:
            return NICHYA
    return None
def human_hod(doska,human):
    dostupnie=dostupnie_hodi(doska)
    hod=None
    while hod not in dostupnie:
        hod=hod_number(0,RAZMER_DOSKI)
        if hod not in dostupnie:
            print('Поле занято. Напиши другой номер: ')
    print('Супер!')
    return hod

def comp_hod(doska,comp,human,perviy_hod,hod):
    doska=doska[:]
    BEST_HODI=(4,0,2,6,8,1,3,5,7)
    print('Мой ход: ')
    for i in dostupnie_hodi(doska):
        doska[i]=comp
        if winner(doska)==comp:
            print(i)
            return i
        doska[i]=HODI
    for j in dostupnie_hodi(doska):
        doska[j]=human
        if winner(doska)==human:
            print(j)
            return j
        doska[j]=HODI  
    for k in dostupnie_hodi(doska):
            #компьютер ходит вторым (O)
            #Блок первого хода
        if perviy_hod and len(HODI)==1:
            #предыдущий ход противника X=4 (центр):
            if hod==4:
            #O=[0,2,6,8], если занято, то random
                for i in (0,2,6,8):
                    if i in dostupnie_hodi(doska):
                        doska[i]=comp
                        print(i)
                        return i
                        doska[i]=HODI
                    if (0,2,6,8) not in dostupnie_hodi(doska):
                        for j in dostupnie_hodi(doska):
                            doska[j]=comp
                            print(j)
                            return j
                            doska[j]=HODI
            elif hod in (0,2,6,8): #предыдущий ход X=[0,2,6,8]:
                doska[4]=comp #O=4 (занять центр)
                print(4)
                return 4
                doska[4]=HODI
            elif hod in (1,3,5,7): #предыдущий ход X=[1,3,5,7]:
                doska[4]=comp #O=4 (занять центр)
                print(4)
                return 4
                doska[4]=HODI

                #Блок 2-го и последующих ходов
        elif perviy_hod and len(HODI)>1:
            if HODI[0]==0:#первый ход X=0:
                doska[8]=comp #O=8
                print(8)
                return 8
                doska[8]=HODI
            elif HODI[0]==2:#первый ход X=2:
                doska[6]=comp #O=6
                print(6)
                return 6
                doska[6]=HODI
            elif HODI[0]==6: #перывй ход X=6:
                doska[2]=comp #O=2
                print(2)
                return 2
                doska[2]=HODI
            elif HODI[0]==8: #первый ход X=8:
                doska[0]=comp #O=0
                print(0)
                return 0
                doska[0]=HODI
            elif HODI[0]==1 and (HODI[1]==0 or HODI[1]==2):#первый ход X=1 and второй ход X=[0 или 2]:
                if 0 in dostupnie_hodi(doska): #O=[0 или 2]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
            elif HODI[0]==1 and (HODI[1]!=0 or HODI[1]!=2):#первый ход X=1 and второй ход X!=[0;2]:
                for i in (0,2,6,8): #O=[0;2;6;8]
                    if i in dostupnie_hodi(doska):
                        doska[i]=comp
                        print(i)
                        return i
                        doska[i]=HODI
            elif HODI[0]==3 and (HODI[1]==0 or HODI[1]==6):#первый ход X=3 and второй ход X=[0;6]:
                if 0 in dostupnie_hodi(doska): #O=[0;6]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            elif HODI[0]==3 and (HODI[1]!=0 or HODI[1]!=6): #первый ход X=3 and второй ход X!=[0;6]:
                if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            elif HODI[0]==5 and (HODI[1]==2 or HODI[1]==8):#первый ход X=5 and второй ход X=[2;8]:
                if 2 in dostupnie_hodi(doska):#O=[2;8]
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[8]=comp
                    print(8)
                    return 8
                    doska[8]=HODI
            elif HODI[0]==5 and (HODI[1]!=2 or HODI[1]!=8):#первый ход X=5 and второй ход X!=[2;8]:
                if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            elif HODI[0]==7 and (HODI[1]==6 or HODI[1]==8):#первый ход X=7 and второй ход X=[6;8]:
                if 6 in dostupnie_hodi(doska):#O=[6;8]
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[8]=comp
                    print(8)
                    return 8
                    doska[8]=HODI
            elif HODI[0]==7 and (HODI[1]!=6 or HODI[1]!=8):#первый ход X=7 and второй ход X!=[6;8]:
                if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            else:
                if 1 in dostupnie_hodi(doska):#0=[1,3,5,7]
                    doska[1]=comp
                    print(1)
                    return 1
                    doska[1]=HODI
                elif 3 in dostupnie_hodi(doska):
                    doska[3]=comp
                    print(3)
                    return 3
                    doska[3]=HODI
                elif 5 in dostupnie_hodi(doska):
                    doska[5]=comp
                    print(5)
                    return 5
                    doska[5]=HODI
                elif 7 in dostupnie_hodi(doska):
                    doska[7]=comp
                    print(7)
                    return 7
                    doska[7]=HODI

            
            #компьютер ходит первым (Х)
        else:
            #Блок первого хода
                    if doska[4] in dostupnie_hodi(doska):#4 центр свободен:
                        doska[4]=comp #X=4 занять центр
                        print(4)
                        return 4
                        doska[4]=HODI
                    else: #random ход из доступных
                        for i in dostupnie_hodi(doska):
                            doska[i]=comp
                            print(i)
                            return i
                            doska[i]=HODI
                #Блок 2-го и последующих ходов
                    if hod==0:#предыдущий ход противника O=0:
                        if 8 in dostupnie_hodi(doska): #X=8, если занято, то X = 6 или 2,
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                        else:
                            if 6 in dostupnie_hodi(doska):
                                doska[6]=comp
                                print(6)
                                return 6
                                doska[6]=HODI
                            elif 2 in dostupnie_hodi(doska):
                                doska[2]=comp
                                print(2)
                                return 2
                                doska[2]=HODI
                            else: #если занято, то random
                                for i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                    doska[i]=HODI                        
                    if hod==1:#предыдущий ход O=1:
                        if 6 in dostupnie_hodi(doska):#X=6 or 8, если занято, то random
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==3:#предыдущий ход O=3:
                        if 2 in dostupnie_hodi(doska):#X=2 or 8, если занято, то random
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==5:#предыдущий ход O=5:
                        if 0 in dostupnie_hodi(doska):#X=0 or 6, если занято, то random
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 6 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==6:#предыдущий ход O=6:
                        if 2 in dostupnie_hodi(doska):#X=2 
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI 
                        else: #если занято, то X = 0 или 8,
                            if 0 in dostupnie_hodi(doska):
                                doska[0]=comp
                                print(0)
                                return 0
                                doska[0]=HODI
                            elif 8 in dostupnie_hodi(doska):
                                doska[8]=comp
                                print(8)
                                return 8
                                doska[8]=HODI
                            else: #если занято, то random
                                for i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                    doska[i]=HODI
                    if hod==7:#предыдущий ход O=7:
                        if 0 in dostupnie_hodi(doska):#X=0 or 2, если занято, то random
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 2 in dostupnie_hodi(doska):
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==8:#предыдущий ход O=8:
                        if 0 in dostupnie_hodi(doska):
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI 
                        else: #X=0 если занято, то X = 6 или 2,
                            if 0 in dostupnie_hodi(doska):
                                doska[0]=comp
                                print(0)
                                return 0
                                doska[0]=HODI
                            elif 6 in dostupnie_hodi(doska):
                                doska[6]=comp
                                print(6)
                                return 6
                                doska[6]=HODI
                            else:#если занято, то random
                                for i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                    doska[i]=HODI
            #print(k)
            #return k
        #doska[k]=HODI        

def next_ochered(ochered):
    if ochered==X:
        return O
    else:
        return X
def pozdrav_pobeditela(pobeditel,comp,human):
    if pobeditel!=NICHYA:
        print('Собрана линия ', pobeditel)
    else:
        print(NICHYA)
    if pobeditel==comp:
        print('Компьютер выиграл!')
    elif pobeditel==human:
        print('Ты победил!')
    elif pobeditel==NICHYA:
        print(NICHYA)
def main():
    instrukciya()
    comp,human=fishki()
    ochered=X
    doska=new_doska()
    pokaz_doski(doska)
    while not winner(doska):
        if ochered==human:
            hod=human_hod(doska,human)
            doska[hod]=human
        else:
            hod=comp_hod(doska,comp,human)
            doska[hod]=comp
        pokaz_doski(doska)
        ochered=next_ochered(ochered)
    pobeditel=winner(doska)
    pozdrav_pobeditela(pobeditel,comp,human)

main()
input('\n Нажми Entr, чтобы выйти')

И немного доработанный вариант:

X='X'
O='0'
RAZMER_DOSKI=9
HODI=' '
NICHYA='Ничья'

def instrukciya():
    print('''
Привет! Это игра "Крестики-нолики".
Чтобы сделать ход, введи номер клетки,
куда хочешь поставить свой символ:

0 | 1 | 2
---------
3 | 4 | 5
---------
6 | 7 | 8


''')
def nachalo(vopros):
    otvet=None
    while otvet not in ('да','нет'):
        otvet=input(vopros).lower()
    return otvet

def fishki():
    perviy_hod=nachalo("Вы хотите быть первым, кто сделает ход \
(играть крестиками)?  ")
    if perviy_hod=='да':
        print('Окей, ты играешь крестиками!')
        human=X
        comp=O
        return perviy_hod, comp, human
    else:
        print('ОК, я делаю первый ход крестиками')
        human=O
        comp=X
    return comp, human
        
def hod_number(low,high):
    otvet=None
    while otvet not in range(low,high):
        otvet=int(input("Делай свой ход - напиши номер поля (0-8): "))
    return otvet


def new_doska():
    doska=[]
    for i in range(RAZMER_DOSKI):
        doska.append(HODI)
    return doska

def pokaz_doski(doska):
    print('\n', doska[0], '|', doska[1], '|', doska [2])
    print('---------')
    print('\n'
          , doska[3], '|', doska[4], '|', doska [5])
    print('---------')
    print('\n', doska[6], '|', doska[7], '|', doska [8], '\n')

def dostupnie_hodi(doska):
    dostupnie_hodi=[]
    for i in range(RAZMER_DOSKI):
        if doska[i]== HODI:
            dostupnie_hodi.append(i)
    return dostupnie_hodi

def winner(doska):
    VAR_POBED=((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 i in VAR_POBED:
        if doska[i[0]]==doska[i[1]]==doska[i[2]]!=HODI:
            winner=doska[i[0]]
            return winner
        if HODI not in doska:
            return NICHYA
    return None
def human_hod(doska,human):
    dostupnie=dostupnie_hodi(doska)
    hod=None
    while hod not in dostupnie:
        hod=hod_number(0,RAZMER_DOSKI)
        if hod not in dostupnie:
            print('Поле занято. Напиши другой номер: ')
    print('Супер!')
    return hod

def comp_hod(doska,comp,human,hod):
    doska=doska[:]
    BEST_HODI=(4,0,2,6,8,1,3,5,7)
    perviy_hod=fishki()
    print('Мой ход: ')
    for i in dostupnie_hodi(doska):
        doska[i]=comp
        if winner(doska)==comp:
            print(i)
            return i
        doska[i]=HODI
    for j in dostupnie_hodi(doska):
        doska[j]=human
        if winner(doska)==human:
            print(j)
            return j
        doska[j]=HODI  
    for k in dostupnie_hodi(doska):
            #компьютер ходит вторым (O)
            #Блок первого хода
        if perviy_hod and len(HODI)==1:
            #предыдущий ход противника X=4 (центр):
            if hod==4:
            #O=[0,2,6,8], если занято, то random
                for i in (0,2,6,8):
                    if i in dostupnie_hodi(doska):
                        doska[i]=comp
                        print(i)
                        return i
                        doska[i]=HODI
                    if (0,2,6,8) not in dostupnie_hodi(doska):
                        for j in dostupnie_hodi(doska):
                            doska[j]=comp
                            print(j)
                            return j
                            doska[j]=HODI
            elif hod in (0,2,6,8): #предыдущий ход X=[0,2,6,8]:
                doska[4]=comp #O=4 (занять центр)
                print(4)
                return 4
                doska[4]=HODI
            elif hod in (1,3,5,7): #предыдущий ход X=[1,3,5,7]:
                doska[4]=comp #O=4 (занять центр)
                print(4)
                return 4
                doska[4]=HODI

                #Блок 2-го и последующих ходов
        elif perviy_hod and len(HODI)>1:
            if HODI[0]==0:#первый ход X=0:
                doska[8]=comp #O=8
                print(8)
                return 8
                doska[8]=HODI
            elif HODI[0]==2:#первый ход X=2:
                doska[6]=comp #O=6
                print(6)
                return 6
                doska[6]=HODI
            elif HODI[0]==6: #перывй ход X=6:
                doska[2]=comp #O=2
                print(2)
                return 2
                doska[2]=HODI
            elif HODI[0]==8: #первый ход X=8:
                doska[0]=comp #O=0
                print(0)
                return 0
                doska[0]=HODI
            elif HODI[0]==1 and (HODI[1]==0 or HODI[1]==2):#первый ход X=1 and второй ход X=[0 или 2]:
                if 0 in dostupnie_hodi(doska): #O=[0 или 2]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
            elif HODI[0]==1 and (HODI[1]!=0 or HODI[1]!=2):#первый ход X=1 and второй ход X!=[0;2]:
                for i in (0,2,6,8): #O=[0;2;6;8]
                    if i in dostupnie_hodi(doska):
                        doska[i]=comp
                        print(i)
                        return i
                        doska[i]=HODI
            elif HODI[0]==3 and (HODI[1]==0 or HODI[1]==6):#первый ход X=3 and второй ход X=[0;6]:
                if 0 in dostupnie_hodi(doska): #O=[0;6]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            elif HODI[0]==3 and (HODI[1]!=0 or HODI[1]!=6): #первый ход X=3 and второй ход X!=[0;6]:
                if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            elif HODI[0]==5 and (HODI[1]==2 or HODI[1]==8):#первый ход X=5 and второй ход X=[2;8]:
                if 2 in dostupnie_hodi(doska):#O=[2;8]
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[8]=comp
                    print(8)
                    return 8
                    doska[8]=HODI
            elif HODI[0]==5 and (HODI[1]!=2 or HODI[1]!=8):#первый ход X=5 and второй ход X!=[2;8]:
                if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            elif HODI[0]==7 and (HODI[1]==6 or HODI[1]==8):#первый ход X=7 and второй ход X=[6;8]:
                if 6 in dostupnie_hodi(doska):#O=[6;8]
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[8]=comp
                    print(8)
                    return 8
                    doska[8]=HODI
            elif HODI[0]==7 and (HODI[1]!=6 or HODI[1]!=8):#первый ход X=7 and второй ход X!=[6;8]:
                if 0 in dostupnie_hodi(doska):#O=[0;2;6;8]
                    doska[0]=comp
                    print(0)
                    return 0
                    doska[0]=HODI
                elif 2 in dostupnie_hodi(doska):
                    doska[2]=comp
                    print(2)
                    return 2
                    doska[2]=HODI
                elif 6 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
                elif 8 in dostupnie_hodi(doska):
                    doska[6]=comp
                    print(6)
                    return 6
                    doska[6]=HODI
            else:
                if 1 in dostupnie_hodi(doska):#0=[1,3,5,7]
                    doska[1]=comp
                    print(1)
                    return 1
                    doska[1]=HODI
                elif 3 in dostupnie_hodi(doska):
                    doska[3]=comp
                    print(3)
                    return 3
                    doska[3]=HODI
                elif 5 in dostupnie_hodi(doska):
                    doska[5]=comp
                    print(5)
                    return 5
                    doska[5]=HODI
                elif 7 in dostupnie_hodi(doska):
                    doska[7]=comp
                    print(7)
                    return 7
                    doska[7]=HODI

            
            #компьютер ходит первым (Х)
        else:
            #Блок первого хода
                    if doska[4] in dostupnie_hodi(doska):#4 центр свободен:
                        doska[4]=comp #X=4 занять центр
                        print(4)
                        return 4
                        doska[4]=HODI
                    else: #random ход из доступных
                        for i in dostupnie_hodi(doska):
                            doska[i]=comp
                            print(i)
                            return i
                            doska[i]=HODI
                #Блок 2-го и последующих ходов
                    if hod==0:#предыдущий ход противника O=0:
                        if 8 in dostupnie_hodi(doska): #X=8, если занято, то X = 6 или 2,
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                        else:
                            if 6 in dostupnie_hodi(doska):
                                doska[6]=comp
                                print(6)
                                return 6
                                doska[6]=HODI
                            elif 2 in dostupnie_hodi(doska):
                                doska[2]=comp
                                print(2)
                                return 2
                                doska[2]=HODI
                            else: #если занято, то random
                                for i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                    doska[i]=HODI                        
                    if hod==1:#предыдущий ход O=1:
                        if 6 in dostupnie_hodi(doska):#X=6 or 8, если занято, то random
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==3:#предыдущий ход O=3:
                        if 2 in dostupnie_hodi(doska):#X=2 or 8, если занято, то random
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        elif 8 in dostupnie_hodi(doska):
                            doska[8]=comp
                            print(8)
                            return 8
                            doska[8]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==5:#предыдущий ход O=5:
                        if 0 in dostupnie_hodi(doska):#X=0 or 6, если занято, то random
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 6 in dostupnie_hodi(doska):
                            doska[6]=comp
                            print(6)
                            return 6
                            doska[6]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==6:#предыдущий ход O=6:
                        if 2 in dostupnie_hodi(doska):#X=2 
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI 
                        else: #если занято, то X = 0 или 8,
                            if 0 in dostupnie_hodi(doska):
                                doska[0]=comp
                                print(0)
                                return 0
                                doska[0]=HODI
                            elif 8 in dostupnie_hodi(doska):
                                doska[8]=comp
                                print(8)
                                return 8
                                doska[8]=HODI
                            else: #если занято, то random
                                for i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                    doska[i]=HODI
                    if hod==7:#предыдущий ход O=7:
                        if 0 in dostupnie_hodi(doska):#X=0 or 2, если занято, то random
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI
                        elif 2 in dostupnie_hodi(doska):
                            doska[2]=comp
                            print(2)
                            return 2
                            doska[2]=HODI
                        else:
                            for i in dostupnie_hodi(doska):
                                doska[i]=comp
                                print(i)
                                return i
                                doska[i]=HODI
                    if hod==8:#предыдущий ход O=8:
                        if 0 in dostupnie_hodi(doska):
                            doska[0]=comp
                            print(0)
                            return 0
                            doska[0]=HODI 
                        else: #X=0 если занято, то X = 6 или 2,
                            if 0 in dostupnie_hodi(doska):
                                doska[0]=comp
                                print(0)
                                return 0
                                doska[0]=HODI
                            elif 6 in dostupnie_hodi(doska):
                                doska[6]=comp
                                print(6)
                                return 6
                                doska[6]=HODI
                            else:#если занято, то random
                                for i in dostupnie_hodi(doska):
                                    doska[i]=comp
                                    print(i)
                                    return i
                                    doska[i]=HODI
        print(k)
        return k
        doska[k]=HODI        

def next_ochered(ochered):
    if ochered==X:
        return O
    else:
        return X
def pozdrav_pobeditela(pobeditel,comp,human):
    if pobeditel!=NICHYA:
        print('Собрана линия ', pobeditel)
    else:
        print(NICHYA)
    if pobeditel==comp:
        print('Компьютер выиграл!')
    elif pobeditel==human:
        print('Ты победил!')
    elif pobeditel==NICHYA:
        print(NICHYA)
def main():
    instrukciya()
    comp,human,perviy_hod=fishki()
    hod=None
    ochered=X
    doska=new_doska()
    pokaz_doski(doska)
    while not winner(doska):
        if ochered==human:
            hod=human_hod(doska,human)
            doska[hod]=human
        else:
            hod_comp=comp_hod(doska,comp,human,hod)
        next_ochered(ochered)
        pokaz_doski(doska)
    pobeditel=winner(doska)
    pozdrav_pobeditela(pobeditel,comp,human)

main()
input('\n Нажми Entr, чтобы выйти')

Непобедимый соперник через алгоритм Minimax

Есть альтернативный способ сделать непобедимого соперника, используя так называемый математический алгоритм теории игр минимакс — в котором каждый игрок пытается максимизировать выигрыш и минимизировать потери. Для этого используется Utility function, оценивающая ценность каждого хода в дереве возможных ходов. Чтобы выиграть Utility должна быть положительной.

(код Yueyang Ying)

import math
import time
from player import HumanPlayer, RandomComputerPlayer, SmartComputerPlayer


class TicTacToe():
    def __init__(self):
        self.board = self.make_board()
        self.current_winner = None

    @staticmethod
    def make_board():
        return [' ' for _ in range(9)]

    def print_board(self):
        for row in [self.board[i*3:(i+1) * 3] for i in range(3)]:
            print('| ' + ' | '.join(row) + ' |')

    @staticmethod
    def print_board_nums():
        # 0 | 1 | 2
        number_board = [[str(i) for i in range(j*3, (j+1)*3)] for j in range(3)]
        for row in number_board:
            print('| ' + ' | '.join(row) + ' |')

    def make_move(self, square, letter):
        if self.board[square] == ' ':
            self.board[square] = letter
            if self.winner(square, letter):
                self.current_winner = letter
            return True
        return False

    def winner(self, square, letter):
        # check the row
        row_ind = math.floor(square / 3)
        row = self.board[row_ind*3:(row_ind+1)*3]
        # print('row', row)
        if all([s == letter for s in row]):
            return True
        col_ind = square % 3
        column = [self.board[col_ind+i*3] for i in range(3)]
        # print('col', column)
        if all([s == letter for s in column]):
            return True
        if square % 2 == 0:
            diagonal1 = [self.board[i] for i in [0, 4, 8]]
            # print('diag1', diagonal1)
            if all([s == letter for s in diagonal1]):
                return True
            diagonal2 = [self.board[i] for i in [2, 4, 6]]
            # print('diag2', diagonal2)
            if all([s == letter for s in diagonal2]):
                return True
        return False

    def empty_squares(self):
        return ' ' in self.board

    def num_empty_squares(self):
        return self.board.count(' ')

    def available_moves(self):
        return [i for i, x in enumerate(self.board) if x == " "]


def play(game, x_player, o_player, print_game=True):

    if print_game:
        game.print_board_nums()

    letter = 'X'
    while game.empty_squares():
        if letter == 'O':
            square = o_player.get_move(game)
        else:
            square = x_player.get_move(game)
        if game.make_move(square, letter):

            if print_game:
                print(letter + ' makes a move to square {}'.format(square))
                game.print_board()
                print('')

            if game.current_winner:
                if print_game:
                    print(letter + ' wins!')
                return letter  # ends the loop and exits the game
            letter = 'O' if letter == 'X' else 'X'  # switches player

        time.sleep(.8)

    if print_game:
        print('It\'s a tie!')



if __name__ == '__main__':
    x_player = SmartComputerPlayer('X')
    o_player = HumanPlayer('O')
    t = TicTacToe()
    play(t, x_player, o_player, print_game=True)
import math
import random


class Player():
    def __init__(self, letter):
        self.letter = letter

    def get_move(self, game):
        pass


class HumanPlayer(Player):
    def __init__(self, letter):
        super().__init__(letter)

    def get_move(self, game):
        valid_square = False
        val = None
        while not valid_square:
            square = input(self.letter + '\'s turn. Input move (0-9): ')
            try:
                val = int(square)
                if val not in game.available_moves():
                    raise ValueError
                valid_square = True
            except ValueError:
                print('Invalid square. Try again.')
        return val


class RandomComputerPlayer(Player):
    def __init__(self, letter):
        super().__init__(letter)

    def get_move(self, game):
        square = random.choice(game.available_moves())
        return square


class SmartComputerPlayer(Player):
    def __init__(self, letter):
        super().__init__(letter)

    def get_move(self, game):
        if len(game.available_moves()) == 9:
            square = random.choice(game.available_moves())
        else:
            square = self.minimax(game, self.letter)['position']
        return square

    def minimax(self, state, player):
        max_player = self.letter  # yourself
        other_player = 'O' if player == 'X' else 'X'

        # first we want to check if the previous move is a winner
        if state.current_winner == other_player:
            return {'position': None, 'score': 1 * (state.num_empty_squares() + 1) if other_player == max_player else -1 * (
                        state.num_empty_squares() + 1)}
        elif not state.empty_squares():
            return {'position': None, 'score': 0}

        if player == max_player:
            best = {'position': None, 'score': -math.inf}  # each score should maximize
        else:
            best = {'position': None, 'score': math.inf}  # each score should minimize
        for possible_move in state.available_moves():
            state.make_move(possible_move, player)
            sim_score = self.minimax(state, other_player)  # simulate a game after making that move

            # undo move
            state.board[possible_move] = ' '
            state.current_winner = None
            sim_score['position'] = possible_move  # this represents the move optimal next move

            if player == max_player:  # X is max player
                if sim_score['score'] > best['score']:
                    best = sim_score
            else:
                if sim_score['score'] < best['score']:
                    best = sim_score
        return best

 


Запись опубликована в рубрике Python. Добавьте в закладки постоянную ссылку.

4 комментария на «Python: Задачи и решения (Глава 6. Функции. Игра “Крестики-нолики”).»

  1. . говорит:

    Большое спасибо и спасибо за такой проделанный труд

  2. Андрей Л говорит:

    def hod_number(LOW=0,HIGH=5):
    popitka=int(input(«Это число: «))
    return popitka

    Привет, я только начал читать книгу и учить Pyton, и немного бывает туплю, почему этот участок кода в начале

  3. Dar_Morar говорит:

    Доработанная программа с функцией hod_number():
    При угадывании числа за отведённое количество попыток выдаёт такой результат:
    Это число: 72
    Ничего себе! Ты отгадал! Это правда 72
    Количество попыток: 5
    О, ужас! Ты совершенно не умеешь читать мои мысли!
    Так и не смог угадать число за 5 попыток 🙁

    Задваиваются итоги. Или ты угадал, или ты не угадал.

  4. Dar_Morar говорит:

    В доработанной версии программы Отгадай число (с мэйн функцией) содержится в коде ошибка. Нет возможности решить задачу. Пример вывода.
    Я загадала целое число от 0 до 100. Угадай с 5 попыток!
    Это число: 50
    Больше!
    Это число: 75
    Больше!
    Это число: 99
    Больше!
    Это число: 100
    Больше!
    Это число: 101
    Больше!
    О, ужас! Ты совершенно не умеешь читать мои мысли!
    Так и не смог угадать число за 5 попыток 🙁

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

🇬🇧 Attention! Comments with URLs/email are not allowed.
🇷🇺 Комментарии со ссылками/email удаляются автоматически.